port more changes to make PCI work
[linux-2.4.git] / net / ipv6 / ip6_fw.c
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.16 2001/10/31 08:17:58 davem 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
34 static unsigned long ip6_fw_rule_cnt;
35 static struct ip6_fw_rule ip6_fw_rule_list = {
36         {0},
37         NULL, NULL,
38         {0},
39         IP6_FW_REJECT
40 };
41
42 static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args);
43
44 struct flow_rule_ops ip6_fw_ops = {
45         ip6_fw_accept
46 };
47
48
49 static 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
57 static 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
63 rwlock_t ip6_fw_lock = RW_LOCK_UNLOCKED;
64
65
66 static 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
80 static 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
93 static __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
106 static __inline__ void ip6_fwrule_free(struct ip6_fw_rule * rl)
107 {
108         kfree(rl);
109 }
110
111 static __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
119 static 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
200 out:
201         return res;
202 }
203
204 static 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
233 static 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
260 int 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
276 int 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
283 int 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
341 static 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
371 static 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
380 void __init ip6_fw_init(void)
381 {
382         netlink_attach(NETLINK_IP6_FW, ip6_fw_msgrcv);
383 }
384
385 #ifdef MODULE
386 void cleanup_module(void)
387 {
388         netlink_detach(NETLINK_IP6_FW);
389 }
390 #endif