diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
commit | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch) | |
tree | 40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net/ipv6/ip6_fw.c | |
parent | 7206675c40394c78a90e74812bbdbf8cf3cca1be (diff) |
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net/ipv6/ip6_fw.c')
-rw-r--r-- | net/ipv6/ip6_fw.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/net/ipv6/ip6_fw.c b/net/ipv6/ip6_fw.c new file mode 100644 index 000000000..f6e7f8da4 --- /dev/null +++ b/net/ipv6/ip6_fw.c @@ -0,0 +1,378 @@ +/* + * IPv6 Firewall + * Linux INET6 implementation + * + * Authors: + * Pedro Roque <roque@di.fc.ul.pt> + * + * $Id: ip6_fw.c,v 1.4 1997/03/18 18:24:34 davem Exp $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/net.h> +#include <linux/route.h> +#include <linux/netdevice.h> +#include <linux/in6.h> +#include <linux/udp.h> + +#include <net/ipv6.h> +#include <net/ip6_route.h> +#include <net/ip6_fw.h> +#include <net/netlink.h> + +static unsigned long ip6_fw_rule_cnt; +static struct ip6_fw_rule ip6_fw_rule_list = { + {0}, + NULL, NULL, + {0}, + IP6_FW_REJECT +}; + +static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args); + +struct flow_rule_ops ip6_fw_ops = { + ip6_fw_accept +}; + + +static struct rt6_info ip6_fw_null_entry = { + {{NULL, 0, 0, NULL, + 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL, + ip6_pkt_discard, ip6_pkt_discard, NULL}}, + NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL, + 0, &ip6_fw_rule_list, {{{{0}}}, 128}, {{{{0}}}, 128} +}; + +static struct fib6_node ip6_fw_fib = { + NULL, NULL, NULL, NULL, + &ip6_fw_null_entry, + 0, RTN_ROOT|RTN_TL_ROOT, 0 +}; + +static void ip6_rule_add(struct ip6_fw_rule *rl) +{ + struct ip6_fw_rule *next; + + start_bh_atomic(); + ip6_fw_rule_cnt++; + next = &ip6_fw_rule_list; + rl->next = next; + rl->prev = next->prev; + rl->prev->next = rl; + next->prev = rl; + end_bh_atomic(); +} + +static void ip6_rule_del(struct ip6_fw_rule *rl) +{ + struct ip6_fw_rule *next, *prev; + + start_bh_atomic(); + ip6_fw_rule_cnt--; + next = rl->next; + prev = rl->prev; + next->prev = prev; + prev->next = next; + end_bh_atomic(); +} + +static __inline__ struct ip6_fw_rule * ip6_fwrule_alloc(void) +{ + struct ip6_fw_rule *rl; + + rl = kmalloc(sizeof(struct ip6_fw_rule), GFP_ATOMIC); + + memset(rl, 0, sizeof(struct ip6_fw_rule)); + + if (rl) + rl->flowr.ops = &ip6_fw_ops; + + return rl; +} + +static __inline__ void ip6_fwrule_free(struct ip6_fw_rule * rl) +{ + kfree(rl); +} + +static __inline__ int port_match(int rl_port, int fl_port) +{ + int res = 0; + if (rl_port == 0 || (rl_port == fl_port)) + res = 1; + return res; +} + +static int ip6_fw_accept_trans(struct ip6_fw_rule *rl, + struct fl_acc_args *args) +{ + int res = FLOWR_NODECISION; + int proto = 0; + int sport = 0; + int dport = 0; + + switch (args->type) { + case FL_ARG_FORWARD: + { + struct sk_buff *skb = args->fl_u.skb; + struct ipv6hdr *hdr = skb->nh.ipv6h; + int len; + + len = skb->len - sizeof(struct ipv6hdr); + + proto = hdr->nexthdr; + + switch (proto) { + case IPPROTO_TCP: + { + struct tcphdr *th; + + if (len < sizeof(struct tcphdr)) { + res = FLOWR_ERROR; + goto out; + } + th = (struct tcphdr *)(hdr + 1); + sport = th->source; + dport = th->dest; + break; + } + case IPPROTO_UDP: + { + struct udphdr *uh; + + if (len < sizeof(struct udphdr)) { + res = FLOWR_ERROR; + goto out; + } + uh = (struct udphdr *)(hdr + 1); + sport = uh->source; + dport = uh->dest; + break; + } + default: + goto out; + }; + break; + } + + case FL_ARG_ORIGIN: + { + proto = args->fl_u.fl_o.flow->proto; + + if (proto == IPPROTO_ICMPV6) { + goto out; + } else { + sport = args->fl_u.fl_o.flow->uli_u.ports.sport; + dport = args->fl_u.fl_o.flow->uli_u.ports.dport; + } + break; + } + + if (proto == rl->info.proto && + port_match(args->fl_u.fl_o.flow->uli_u.ports.sport, sport) && + port_match(args->fl_u.fl_o.flow->uli_u.ports.dport, dport)) { + if (rl->policy & IP6_FW_REJECT) + res = FLOWR_SELECT; + else + res = FLOWR_CLEAR; + } + + default: +#if IP6_FW_DEBUG >= 1 + printk(KERN_DEBUG "ip6_fw_accept: unknown arg type\n"); +#endif + goto out; + }; + +out: + return res; +} + +static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args) +{ + struct rt6_info *rt; + struct ip6_fw_rule *rl; + int proto; + int res = FLOWR_NODECISION; + + rt = (struct rt6_info *) dst; + rl = (struct ip6_fw_rule *) rt->rt6i_flowr; + + proto = rl->info.proto; + + switch (proto) { + case 0: + if (rl->policy & IP6_FW_REJECT) + res = FLOWR_SELECT; + else + res = FLOWR_CLEAR; + break; + case IPPROTO_TCP: + case IPPROTO_UDP: + res = ip6_fw_accept_trans(rl, args); + break; + case IPPROTO_ICMPV6: + }; + + return res; +} + +static struct dst_entry * ip6_fw_dup(struct dst_entry *frule, + struct dst_entry *rt, + struct fl_acc_args *args) +{ + struct ip6_fw_rule *rl; + struct rt6_info *nrt; + struct rt6_info *frt; + + frt = (struct rt6_info *) frule; + + rl = (struct ip6_fw_rule *) frt->rt6i_flowr; + + nrt = ip6_rt_copy((struct rt6_info *) rt); + + if (nrt) { + nrt->u.dst.input = frule->input; + nrt->u.dst.output = frule->output; + + nrt->rt6i_flowr = flow_clone(frt->rt6i_flowr); + + nrt->rt6i_flags |= RTF_CACHE; + nrt->rt6i_tstamp = jiffies; + } + + return (struct dst_entry *) nrt; +} + +int ip6_fw_reject(struct sk_buff *skb) +{ +#if IP6_FW_DEBUG >= 1 + printk(KERN_DEBUG "packet rejected: \n"); +#endif + + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADM_PROHIBITED, 0, + skb->dev); + /* + * send it via netlink, as (rule, skb) + */ + + kfree_skb(skb, FREE_READ); + return 0; +} + +int ip6_fw_discard(struct sk_buff *skb) +{ + printk(KERN_DEBUG "ip6_fw: BUG fw_reject called\n"); + kfree_skb(skb, FREE_READ); + return 0; +} + +int ip6_fw_msg_add(struct ip6_fw_msg *msg) +{ + struct in6_rtmsg rtmsg; + struct ip6_fw_rule *rl; + struct rt6_info *rt; + int err; + + ipv6_addr_copy(&rtmsg.rtmsg_dst, &msg->dst); + ipv6_addr_copy(&rtmsg.rtmsg_src, &msg->src); + rtmsg.rtmsg_dst_len = msg->dst_len; + rtmsg.rtmsg_src_len = msg->src_len; + rtmsg.rtmsg_metric = IP6_RT_PRIO_FW; + + rl = ip6_fwrule_alloc(); + + if (rl == NULL) + return -ENOMEM; + + rl->policy = msg->policy; + rl->info.proto = msg->proto; + rl->info.uli_u.data = msg->u.data; + + rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_POLICY; + rt = ip6_route_add(&rtmsg, &err); + + if (rt == NULL) { + ip6_fwrule_free(rl); + return -ENOMEM; + } + + rt->u.dst.error = -EPERM; + + if (msg->policy == IP6_FW_ACCEPT) { + /* + * Accept rules are never selected + * (i.e. packets use normal forwarding) + */ + rt->u.dst.input = ip6_fw_discard; + rt->u.dst.output = ip6_fw_discard; + } else { + rt->u.dst.input = ip6_fw_reject; + rt->u.dst.output = ip6_fw_reject; + } + + ip6_rule_add(rl); + + rt->rt6i_flowr = flow_clone((struct flow_rule *)rl); + + return 0; +} + +static int ip6_fw_msgrcv(int unit, struct sk_buff *skb) +{ + int count = 0; + + while (skb->len) { + struct ip6_fw_msg *msg; + + if (skb->len < sizeof(struct ip6_fw_msg)) { + count = -EINVAL; + break; + } + + msg = (struct ip6_fw_msg *) skb->data; + skb_pull(skb, sizeof(struct ip6_fw_msg)); + count += sizeof(struct ip6_fw_msg); + + switch (msg->action) { + case IP6_FW_MSG_ADD: + ip6_fw_msg_add(msg); + break; + case IP6_FW_MSG_DEL: + break; + default: + return -EINVAL; + }; + } + + return count; +} + +static void ip6_fw_destroy(struct flow_rule *rl) +{ + ip6_fwrule_free((struct ip6_fw_rule *)rl); +} + +#ifdef MODULE +#define ip6_fw_init module_init +#endif + +void ip6_fw_init(void) +{ + netlink_attach(NETLINK_IP6_FW, ip6_fw_msgrcv); +} + +#ifdef MODULE +void module_cleanup(void) +{ + netlink_detach(NETLINK_IP6_FW); +} +#endif |