1/*
2 *	IPv6 Firewall
3 *	Linux INET6 implementation
4 *
5 *	Authors:
6 *	Pedro Roque		<roque@di.fc.ul.pt>
7 *
8 *	$Id: ip6_fw.c,v 1.1.1.1 2008/10/15 03:27:34 james26_jang Exp $
9 *
10 *	This program is free software; you can redistribute it and/or
11 *      modify it under the terms of the GNU General Public License
12 *      as published by the Free Software Foundation; either version
13 *      2 of the License, or (at your option) any later version.
14 */
15
16#include <linux/config.h>
17#include <linux/errno.h>
18#include <linux/types.h>
19#include <linux/string.h>
20#include <linux/socket.h>
21#include <linux/sockios.h>
22#include <linux/net.h>
23#include <linux/route.h>
24#include <linux/netdevice.h>
25#include <linux/in6.h>
26#include <linux/udp.h>
27#include <linux/init.h>
28
29#include <net/ipv6.h>
30#include <net/ip6_route.h>
31#include <net/ip6_fw.h>
32#include <net/netlink.h>
33
34static unsigned long ip6_fw_rule_cnt;
35static struct ip6_fw_rule ip6_fw_rule_list = {
36	{0},
37	NULL, NULL,
38	{0},
39	IP6_FW_REJECT
40};
41
42static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args);
43
44struct flow_rule_ops ip6_fw_ops = {
45	ip6_fw_accept
46};
47
48
49static struct rt6_info ip6_fw_null_entry = {
50	{{NULL, 0, 0, NULL,
51	  0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
52	  ip6_pkt_discard, ip6_pkt_discard, NULL}},
53	NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL,
54	0, &ip6_fw_rule_list, {{{{0}}}, 128}, {{{{0}}}, 128}
55};
56
57static struct fib6_node ip6_fw_fib = {
58	NULL, NULL, NULL, NULL,
59	&ip6_fw_null_entry,
60	0, RTN_ROOT|RTN_TL_ROOT, 0
61};
62
63rwlock_t ip6_fw_lock = RW_LOCK_UNLOCKED;
64
65
66static void ip6_rule_add(struct ip6_fw_rule *rl)
67{
68	struct ip6_fw_rule *next;
69
70	write_lock_bh(&ip6_fw_lock);
71	ip6_fw_rule_cnt++;
72	next = &ip6_fw_rule_list;
73	rl->next = next;
74	rl->prev = next->prev;
75	rl->prev->next = rl;
76	next->prev = rl;
77	write_unlock_bh(&ip6_fw_lock);
78}
79
80static void ip6_rule_del(struct ip6_fw_rule *rl)
81{
82	struct ip6_fw_rule *next, *prev;
83
84	write_lock_bh(&ip6_fw_lock);
85	ip6_fw_rule_cnt--;
86	next = rl->next;
87	prev = rl->prev;
88	next->prev = prev;
89	prev->next = next;
90	write_unlock_bh(&ip6_fw_lock);
91}
92
93static __inline__ struct ip6_fw_rule * ip6_fwrule_alloc(void)
94{
95	struct ip6_fw_rule *rl;
96
97	rl = kmalloc(sizeof(struct ip6_fw_rule), GFP_ATOMIC);
98	if (rl)
99	{
100		memset(rl, 0, sizeof(struct ip6_fw_rule));
101		rl->flowr.ops = &ip6_fw_ops;
102	}
103	return rl;
104}
105
106static __inline__ void ip6_fwrule_free(struct ip6_fw_rule * rl)
107{
108	kfree(rl);
109}
110
111static __inline__ int port_match(int rl_port, int fl_port)
112{
113	int res = 0;
114	if (rl_port == 0 || (rl_port == fl_port))
115		res = 1;
116	return res;
117}
118
119static int ip6_fw_accept_trans(struct ip6_fw_rule *rl,
120			       struct fl_acc_args *args)
121{
122	int res = FLOWR_NODECISION;
123	int proto = 0;
124	int sport = 0;
125	int dport = 0;
126
127	switch (args->type) {
128	case FL_ARG_FORWARD:
129	{
130		struct sk_buff *skb = args->fl_u.skb;
131		struct ipv6hdr *hdr = skb->nh.ipv6h;
132		int len;
133
134		len = skb->len - sizeof(struct ipv6hdr);
135
136		proto = hdr->nexthdr;
137
138		switch (proto) {
139		case IPPROTO_TCP:
140		{
141			struct tcphdr *th;
142
143			if (len < sizeof(struct tcphdr)) {
144				res = FLOWR_ERROR;
145				goto out;
146			}
147			th = (struct tcphdr *)(hdr + 1);
148			sport = th->source;
149			dport = th->dest;
150			break;
151		}
152		case IPPROTO_UDP:
153		{
154			struct udphdr *uh;
155
156			if (len < sizeof(struct udphdr)) {
157				res = FLOWR_ERROR;
158				goto out;
159			}
160			uh = (struct udphdr *)(hdr + 1);
161			sport = uh->source;
162			dport = uh->dest;
163			break;
164		}
165		default:
166			goto out;
167		};
168		break;
169	}
170
171	case FL_ARG_ORIGIN:
172	{
173		proto = args->fl_u.fl_o.flow->proto;
174
175		if (proto == IPPROTO_ICMPV6) {
176			goto out;
177		} else {
178			sport = args->fl_u.fl_o.flow->uli_u.ports.sport;
179			dport = args->fl_u.fl_o.flow->uli_u.ports.dport;
180		}
181		break;
182	}
183
184	if (proto == rl->info.proto &&
185	    port_match(args->fl_u.fl_o.flow->uli_u.ports.sport, sport) &&
186	    port_match(args->fl_u.fl_o.flow->uli_u.ports.dport, dport)) {
187		if (rl->policy & IP6_FW_REJECT)
188			res = FLOWR_SELECT;
189		else
190			res = FLOWR_CLEAR;
191	}
192
193	default:
194#if IP6_FW_DEBUG >= 1
195		printk(KERN_DEBUG "ip6_fw_accept: unknown arg type\n");
196#endif
197		goto out;
198	};
199
200out:
201	return res;
202}
203
204static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args)
205{
206	struct rt6_info *rt;
207	struct ip6_fw_rule *rl;
208	int proto;
209	int res = FLOWR_NODECISION;
210
211	rt = (struct rt6_info *) dst;
212	rl = (struct ip6_fw_rule *) rt->rt6i_flowr;
213
214	proto = rl->info.proto;
215
216	switch (proto) {
217	case 0:
218		if (rl->policy & IP6_FW_REJECT)
219			res = FLOWR_SELECT;
220		else
221			res = FLOWR_CLEAR;
222		break;
223	case IPPROTO_TCP:
224	case IPPROTO_UDP:
225		res = ip6_fw_accept_trans(rl, args);
226		break;
227	case IPPROTO_ICMPV6:
228	};
229
230	return res;
231}
232
233static struct dst_entry * ip6_fw_dup(struct dst_entry *frule,
234				     struct dst_entry *rt,
235				     struct fl_acc_args *args)
236{
237	struct ip6_fw_rule *rl;
238	struct rt6_info *nrt;
239	struct rt6_info *frt;
240
241	frt = (struct rt6_info *) frule;
242
243	rl = (struct ip6_fw_rule *) frt->rt6i_flowr;
244
245	nrt = ip6_rt_copy((struct rt6_info *) rt);
246
247	if (nrt) {
248		nrt->u.dst.input = frule->input;
249		nrt->u.dst.output = frule->output;
250
251		nrt->rt6i_flowr = flow_clone(frt->rt6i_flowr);
252
253		nrt->rt6i_flags |= RTF_CACHE;
254		nrt->rt6i_tstamp = jiffies;
255	}
256
257	return (struct dst_entry *) nrt;
258}
259
260int ip6_fw_reject(struct sk_buff *skb)
261{
262#if IP6_FW_DEBUG >= 1
263	printk(KERN_DEBUG "packet rejected: \n");
264#endif
265
266	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADM_PROHIBITED, 0,
267		    skb->dev);
268	/*
269	 *	send it via netlink, as (rule, skb)
270	 */
271
272	kfree_skb(skb);
273	return 0;
274}
275
276int ip6_fw_discard(struct sk_buff *skb)
277{
278	printk(KERN_DEBUG "ip6_fw: BUG fw_reject called\n");
279	kfree_skb(skb);
280	return 0;
281}
282
283int ip6_fw_msg_add(struct ip6_fw_msg *msg)
284{
285	struct in6_rtmsg rtmsg;
286	struct ip6_fw_rule *rl;
287	struct rt6_info *rt;
288	int err;
289
290	ipv6_addr_copy(&rtmsg.rtmsg_dst, &msg->dst);
291	ipv6_addr_copy(&rtmsg.rtmsg_src, &msg->src);
292	rtmsg.rtmsg_dst_len = msg->dst_len;
293	rtmsg.rtmsg_src_len = msg->src_len;
294	rtmsg.rtmsg_metric = IP6_RT_PRIO_FW;
295
296	rl = ip6_fwrule_alloc();
297
298	if (rl == NULL)
299		return -ENOMEM;
300
301	rl->policy = msg->policy;
302	rl->info.proto = msg->proto;
303	rl->info.uli_u.data = msg->u.data;
304
305	rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_POLICY;
306	err = ip6_route_add(&rtmsg);
307
308	if (err) {
309		ip6_fwrule_free(rl);
310		return err;
311	}
312
313	/* The rest will not work for now. --ABK (989725) */
314
315#ifndef notdef
316	ip6_fwrule_free(rl);
317	return -EPERM;
318#else
319	rt->u.dst.error = -EPERM;
320
321	if (msg->policy == IP6_FW_ACCEPT) {
322		/*
323		 *	Accept rules are never selected
324		 *	(i.e. packets use normal forwarding)
325		 */
326		rt->u.dst.input = ip6_fw_discard;
327		rt->u.dst.output = ip6_fw_discard;
328	} else {
329		rt->u.dst.input = ip6_fw_reject;
330		rt->u.dst.output = ip6_fw_reject;
331	}
332
333	ip6_rule_add(rl);
334
335	rt->rt6i_flowr = flow_clone((struct flow_rule *)rl);
336
337	return 0;
338#endif
339}
340
341static int ip6_fw_msgrcv(int unit, struct sk_buff *skb)
342{
343	int count = 0;
344
345	while (skb->len) {
346		struct ip6_fw_msg *msg;
347
348		if (skb->len < sizeof(struct ip6_fw_msg)) {
349			count = -EINVAL;
350			break;
351		}
352
353		msg = (struct ip6_fw_msg *) skb->data;
354		skb_pull(skb, sizeof(struct ip6_fw_msg));
355		count += sizeof(struct ip6_fw_msg);
356
357		switch (msg->action) {
358		case IP6_FW_MSG_ADD:
359			ip6_fw_msg_add(msg);
360			break;
361		case IP6_FW_MSG_DEL:
362			break;
363		default:
364			return -EINVAL;
365		};
366	}
367
368	return count;
369}
370
371static void ip6_fw_destroy(struct flow_rule *rl)
372{
373	ip6_fwrule_free((struct ip6_fw_rule *)rl);
374}
375
376#ifdef MODULE
377#define ip6_fw_init module_init
378#endif
379
380void __init ip6_fw_init(void)
381{
382	netlink_attach(NETLINK_IP6_FW, ip6_fw_msgrcv);
383}
384
385#ifdef MODULE
386void cleanup_module(void)
387{
388	netlink_detach(NETLINK_IP6_FW);
389}
390#endif
391