patch-2.1.30 linux/net/ipv6/ip6_fw.c

Next file: linux/net/ipv6/ip6_input.c
Previous file: linux/net/ipv6/ip6_fib.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ip6_fw.c linux/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

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov