summaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_fw.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net/ipv6/ip6_fw.c
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net/ipv6/ip6_fw.c')
-rw-r--r--net/ipv6/ip6_fw.c378
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