0da190408f6a6dbbf34bc4ed379de01eafd5aff7
[ipfw.git] / dummynet / ipfw2_mod.c
1 /*
2  * Copyright (C) 2009 Luigi Rizzo, Marta Carbone, Universita` di Pisa
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 /*
27  * $Id$
28  *
29  * The main interface to build ipfw+dummynet as a linux module.
30  * (and possibly as a windows module as well, though that part
31  * is not complete yet).
32  *
33  * The control interface uses the sockopt mechanism
34  * on a socket(AF_INET, SOCK_RAW, IPPROTO_RAW).
35  *
36  * The data interface uses the netfilter interface, at the moment
37  * hooked to the PRE_ROUTING and POST_ROUTING hooks.
38  * Unfortunately the netfilter interface is a moving target,
39  * so we need a set of macros to adapt to the various cases.
40  *
41  * In the netfilter hook we just mark packet as 'QUEUE' and then
42  * let the queue handler to do the whole work (filtering and
43  * possibly emulation).
44  * As we receive packets, we wrap them with an mbuf descriptor
45  * so the existing ipfw+dummynet code runs unmodified.
46  */
47
48 #include <sys/cdefs.h>
49 #include <sys/mbuf.h>                   /* sizeof struct mbuf */
50
51 #ifdef __linux__
52 #include <linux/module.h>
53 #include <linux/kernel.h>
54 #include <linux/netfilter.h>
55 #include <linux/netfilter_ipv4.h>       /* NF_IP_PRI_FILTER */
56
57 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
58 #include <net/netfilter/nf_queue.h>     /* nf_queue */
59 #endif
60
61 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
62 #define __read_mostly
63 #endif
64
65 #endif /* !__linux__ */
66
67 #include <netinet/in.h>                 /* in_addr */
68 #include <netinet/ip_fw.h>              /* ip_fw_ctl_t, ip_fw_chk_t */
69 #include <netinet/ip_dummynet.h>        /* ip_dn_ctl_t, ip_dn_io_t */
70 #include <net/pfil.h>   /* PFIL_IN, PFIL_OUT */
71
72 /*
73  * Here we allocate some global variables used in the firewall.
74  */
75 ip_dn_ctl_t    *ip_dn_ctl_ptr;
76 ip_fw_ctl_t    *ip_fw_ctl_ptr;
77
78 ip_dn_io_t     *ip_dn_io_ptr;
79 ip_fw_chk_t    *ip_fw_chk_ptr;
80
81 void            (*bridge_dn_p)(struct mbuf *, struct ifnet *);
82
83 /*
84  * Glue code to implement the registration of children with the parent.
85  * Each child should call my_mod_register() when linking, so that
86  * module_init() and module_exit() can call init_children() and
87  * fini_children() to provide the necessary initialization.
88  */
89 #include <sys/module.h>
90 struct mod_args {
91         struct moduledata *mod;
92         const char *name;
93         int order;
94 };
95
96 static unsigned int mod_idx;
97 static struct mod_args mods[10];        /* hard limit to 10 modules */
98
99 /*
100  * my_mod_register should be called automatically as the init
101  * functions in the submodules. Unfortunately this compiler/linker
102  * trick is not supported yet so we call it manually.
103  */
104 int
105 my_mod_register(struct moduledata *mod, const char *name, int order)
106 {
107         struct mod_args m = { mod, name, order };
108
109         printf("%s %s called\n", __FUNCTION__, name);
110         if (mod_idx < sizeof(mods) / sizeof(mods[0]))
111                 mods[mod_idx++] = m;
112         return 0;
113 }
114
115 static void
116 init_children(void)
117 {
118         unsigned int i;
119
120         /* Call the functions registered at init time. */
121         printf("%s mod_idx value %d\n", __FUNCTION__, mod_idx);
122         for (i = 0; i < mod_idx; i++) {
123                 printf("+++ start module %d %s %s at %p order 0x%x\n",
124                         i, mods[i].name, mods[i].mod->name,
125                         mods[i].mod, mods[i].order);
126                 mods[i].mod->evhand(NULL, MOD_LOAD, mods[i].mod->priv);
127         }
128 }
129
130 static void
131 fini_children(void)
132 {
133         int i;
134
135         /* Call the functions registered at init time. */
136         for (i = mod_idx - 1; i >= 0; i--) {
137                 printf("+++ end module %d %s %s at %p order 0x%x\n",
138                         i, mods[i].name, mods[i].mod->name,
139                         mods[i].mod, mods[i].order);
140                 mods[i].mod->evhand(NULL, MOD_UNLOAD, mods[i].mod->priv);
141         }
142 }
143 /* end of module bindinghelper functions */
144
145 /*
146  * Control hooks:
147  * ipfw_ctl_h() is a wrapper for linux to FreeBSD sockopt call convention.
148  * then call the ipfw handler in order to manage requests.
149  * In turn this is called by the linux set/get handlers.
150  */
151 static int
152 ipfw_ctl_h(struct sockopt *s, int cmd, int dir, int len, void __user *user)
153 {
154         struct thread t;
155         int ret = EINVAL;
156
157         memset(s, 0, sizeof(s));
158         s->sopt_name = cmd;
159         s->sopt_dir = dir;
160         s->sopt_valsize = len;
161         s->sopt_val = user;
162
163         /* sopt_td is not used but it is referenced */
164         memset(&t, 0, sizeof(t));
165         s->sopt_td = &t;
166         
167         printf("%s called with cmd %d len %d\n", __FUNCTION__, cmd, len);
168
169         if (cmd < IP_DUMMYNET_CONFIGURE && ip_fw_ctl_ptr)
170                 ret = ip_fw_ctl_ptr(s);
171         else if (cmd >= IP_DUMMYNET_CONFIGURE && ip_dn_ctl_ptr)
172                 ret = ip_dn_ctl_ptr(s);
173
174         return -ret;    /* errors are < 0 on linux */
175 }
176
177 #ifdef _WIN32
178
179 void
180 netisr_dispatch(int __unused num, struct mbuf *m)
181 {
182 }
183
184 int
185 ip_output(struct mbuf *m, struct mbuf __unused *opt,
186         struct route __unused *ro, int __unused flags,
187     struct ip_moptions __unused *imo, struct inpcb __unused *inp)
188 {
189         netisr_dispatch(0, m);
190         return 0;
191 }
192
193 #else /* this is the linux glue */
194 /*
195  * setsockopt hook has no return value other than the error code.
196  */
197 static int
198 do_ipfw_set_ctl(struct sock __unused *sk, int cmd,
199         void __user *user, unsigned int len)
200 {
201         struct sockopt s;       /* pass arguments */
202
203         return ipfw_ctl_h(&s, cmd, SOPT_SET, len, user);
204 }
205
206 /*
207  * getsockopt can can return a block of data in response.
208  */
209 static int
210 do_ipfw_get_ctl(struct sock __unused *sk,
211         int cmd, void __user *user, int *len)
212 {
213         struct sockopt s;       /* pass arguments */
214         int ret = ipfw_ctl_h(&s, cmd, SOPT_GET, *len, user);
215
216         *len = s.sopt_valsize;  /* return lenght back to the caller */
217         return ret;
218 }
219
220 /*
221  * declare our [get|set]sockopt hooks
222  */
223 static struct nf_sockopt_ops ipfw_sockopts = {
224         .pf             = PF_INET,
225         .set_optmin     = _IPFW_SOCKOPT_BASE,
226         .set_optmax     = _IPFW_SOCKOPT_END,
227         .set            = do_ipfw_set_ctl,
228         .get_optmin     = _IPFW_SOCKOPT_BASE,
229         .get_optmax     = _IPFW_SOCKOPT_END,
230         .get            = do_ipfw_get_ctl,
231 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
232         .owner          = THIS_MODULE,
233 #endif
234 };
235
236 /*
237  * declare hook to grab packets from the netfilter interface.
238  * The NF_* names change in different versions of linux, in some
239  * cases they are #defines, in others they are enum, so we
240  * need to adapt.
241  */
242 #ifndef NF_IP_PRE_ROUTING
243 #define NF_IP_PRE_ROUTING       NF_INET_PRE_ROUTING
244 #endif
245 #ifndef NF_IP_POST_ROUTING
246 #define NF_IP_POST_ROUTING      NF_INET_POST_ROUTING
247 #endif
248
249 /*
250  * The main netfilter hook.
251  * To make life simple, we queue everything and then do all the
252  * decision in the queue handler.
253  *
254  * XXX note that in 2.4 the skbuf is passed as sk_buff**
255  */
256 static unsigned int
257 call_ipfw(unsigned int __unused hooknum,
258 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) // in 2.6.22 we have **
259         struct sk_buff  __unused **skb,
260 #else
261         struct sk_buff  __unused *skb,
262 #endif
263         const struct net_device  __unused *in,
264         const struct net_device  __unused *out,
265         int __unused (*okfn)(struct sk_buff *))
266 {
267         return NF_QUEUE;
268 }
269
270 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
271 #define NF_STOP         NF_ACCEPT
272 #endif
273
274 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
275
276 #define nf_queue_entry  nf_info /* for simplicity */
277 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)  /* unsure on the exact boundary */
278 /* on 2.4 we use nf_info */
279 #define QH_ARGS         struct sk_buff *skb, struct nf_info *info, void *data
280 #else   /* 2.6.1.. 2.6.24 */
281 #define QH_ARGS         struct sk_buff *skb, struct nf_info *info, unsigned int qnum, void *data
282 #endif
283
284 #define DEFINE_SKB      /* nothing, already an argument */
285 #define REINJECT(_inf, _verd)   nf_reinject(skb, _inf, _verd)
286
287 #else   /* 2.6.25 and above */
288
289 #define QH_ARGS         struct nf_queue_entry *info, unsigned int queuenum
290 #define DEFINE_SKB      struct sk_buff *skb = info->skb;
291 #define REINJECT(_inf, _verd)   nf_reinject(_inf, _verd)
292 #endif
293
294 /*
295  * used by dummynet when dropping packets
296  * XXX use dummynet_send()
297  */
298 void
299 reinject_drop(struct mbuf* m)
300 {
301 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) /* unsure on the exact boundary */
302         struct sk_buff *skb = (struct sk_buff *)m;
303 #endif
304         REINJECT(m->queue_entry, NF_DROP);
305 }
306
307 /*
308  * The real call to the firewall. nf_queue_entry points to the skbuf,
309  * and eventually we need to return both through nf_reinject().
310  */
311 static int
312 ipfw2_queue_handler(QH_ARGS)
313 {
314         DEFINE_SKB      /* no semicolon here, goes in the macro */
315         int ret = 0;    /* return value */
316         struct mbuf *m;
317
318 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
319         if (skb->nh.iph == NULL) {
320                 printf("null dp, len %d reinject now\n", skb->len);
321                 REINJECT(info, NF_ACCEPT);
322                 return 0;
323         }
324 #endif
325         m = malloc(sizeof(*m), 0, 0);
326         if (m == NULL) {
327                 printf("malloc fail, len %d reinject now\n", skb->len);
328                 REINJECT(info, NF_ACCEPT);
329                 return 0;
330         }
331
332         m->m_skb = skb;
333         m->m_len = skb->len;            /* len in this skbuf */
334         m->m_pkthdr.len = skb->len;     /* total packet len */
335         m->m_pkthdr.rcvif = info->indev;
336         m->queue_entry = info;
337 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
338         m->m_data = skb->nh.iph;
339 #else
340         m->m_data = skb_network_header(skb);
341 #endif
342         /* XXX add the interface */
343         if (info->hook == NF_IP_PRE_ROUTING) {
344                 ret = ipfw_check_in(NULL, &m, info->indev, PFIL_IN, NULL);
345         } else {
346                 ret = ipfw_check_out(NULL, &m, info->outdev, PFIL_OUT, NULL);
347         }
348
349         if (m != NULL) {        /* Accept. reinject and free the mbuf */
350                 REINJECT(info, NF_STOP);
351                 m_freem(m);
352         } else if (ret == 0) {
353                 /* dummynet has kept the packet, will reinject later. */
354         } else {
355                 /*
356                  * Packet dropped by ipfw or dummynet, reinject as NF_DROP
357                  * mbuf already released by ipfw itself
358                  */
359                 REINJECT(info, NF_DROP);
360         }
361         return 0;
362 }
363
364 struct route;
365 struct ip_moptions;
366 struct inpcb;
367
368
369 /* XXX should include prototypes for netisr_dispatch and ip_output */
370 /*
371  * The reinjection routine after a packet comes out from dummynet.
372  * We must update the skb timestamp so ping reports the right time.
373  */
374 void
375 netisr_dispatch(int num, struct mbuf *m)
376 {
377         struct nf_queue_entry *info = m->queue_entry;
378         struct sk_buff *skb = m->m_skb; /* always used */
379
380         m_freem(m);
381         KASSERT((info != NULL), ("%s info null!\n", __FUNCTION__));
382 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)        // XXX above 2.6.x ?
383         __net_timestamp(skb);   /* update timestamp */
384 #endif
385
386         /* XXX to obey one-pass, possibly call the queue handler here */
387         REINJECT(info, ((num == -1)?NF_DROP:NF_STOP));  /* accept but no more firewall */
388 }
389
390 int
391 ip_output(struct mbuf *m, struct mbuf __unused *opt,
392         struct route __unused *ro, int __unused flags,
393     struct ip_moptions __unused *imo, struct inpcb __unused *inp)
394 {
395         netisr_dispatch(0, m);
396         return 0;
397 }
398
399
400 /*
401  * Now prepare to hook the various functions.
402  * Linux 2.4 has a different API so we need some adaptation
403  * for register and unregister hooks
404  *
405  * the unregister function changed arguments between 2.6.22 and 2.6.24
406  */
407 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
408 static int
409 nf_register_hooks(struct nf_hook_ops *ops, int n)
410 {
411         int i, ret = 0;
412         for (i = 0; i < n; i++) {
413                 ret = nf_register_hook(ops + i);
414                 if (ret < 0)
415                         break;
416         }
417         return ret;
418 }
419
420 static void
421 nf_unregister_hooks(struct nf_hook_ops *ops, int n)
422 {
423         int i;
424         for (i = 0; i < n; i++) {
425                 nf_unregister_hook(ops + i);
426         }
427 }
428 #define REG_QH_ARG(fn)  fn, NULL        /* argument for nf_[un]register_queue_handler */
429 #define UNREG_QH_ARG(fn) //fn   /* argument for nf_[un]register_queue_handler */
430 #define SET_MOD_OWNER
431
432 #else /* linux >= 2.6.0 */
433
434 struct nf_queue_handler ipfw2_queue_handler_desc = {
435         .outfn = ipfw2_queue_handler,
436         .name = "ipfw2 dummynet queue",
437 };
438 #define REG_QH_ARG(fn)  &(fn ## _desc)
439
440 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
441 #define UNREG_QH_ARG(fn) //fn   /* argument for nf_[un]register_queue_handler */
442 #else
443 #define UNREG_QH_ARG(fn)        , &(fn ## _desc)
444 #endif /* 2.6.0 < LINUX > 2.6.24 */
445
446 #define SET_MOD_OWNER   .owner = THIS_MODULE,
447
448 #endif  /* !LINUX < 2.6.0 */
449
450 static struct nf_hook_ops ipfw_ops[] __read_mostly = {
451         {
452                 .hook           = call_ipfw,
453                 .pf             = PF_INET,
454                 .hooknum        = NF_IP_PRE_ROUTING,
455                 .priority       = NF_IP_PRI_FILTER,
456                 SET_MOD_OWNER
457         },
458         {
459                 .hook           = call_ipfw,
460                 .pf             = PF_INET,
461                 .hooknum        = NF_IP_POST_ROUTING,
462                 .priority       = NF_IP_PRI_FILTER,
463                 SET_MOD_OWNER
464         },
465 };
466 #endif /* !__linux__ */
467
468 /* descriptors for the children */
469 extern moduledata_t *moddesc_ipfw;
470 extern moduledata_t *moddesc_dummynet;
471
472 /*
473  * Module glue - init and exit function.
474  */
475 static int __init
476 ipfw_module_init(void)
477 {
478         int ret = 0;
479
480         printf("%s called\n", __FUNCTION__);
481
482         my_mod_register(moddesc_ipfw, "ipfw",  1);
483         my_mod_register(moddesc_dummynet, "dummynet",  2);
484         init_children();
485
486 #ifdef _WIN32
487         return ret;
488
489 #else  /* linux hook */
490         /* sockopt register, in order to talk with user space */
491         ret = nf_register_sockopt(&ipfw_sockopts);
492         if (ret < 0) {
493                 printf("error %d in nf_register_sockopt\n", ret);
494                 goto clean_modules;
495         }
496
497         /* queue handler registration, in order to get network
498          * packet under a private queue */
499         ret = nf_register_queue_handler(PF_INET, REG_QH_ARG(ipfw2_queue_handler) );
500         if (ret < 0)    /* queue busy */
501                 goto unregister_sockopt;
502
503         ret = nf_register_hooks(ipfw_ops, ARRAY_SIZE(ipfw_ops));
504         if (ret < 0)
505                 goto unregister_sockopt;
506
507         printf("%s loaded\n", __FUNCTION__);
508         return 0;
509
510
511 /* handle errors on load */
512 unregister_sockopt:
513         nf_unregister_queue_handler(PF_INET  UNREG_QH_ARG(ipfw2_queue_handler) );
514         nf_unregister_sockopt(&ipfw_sockopts);
515
516 clean_modules:
517         fini_children();
518         printf("%s error\n", __FUNCTION__);
519
520         return ret;
521 #endif  /* linux */
522 }
523
524 /* module shutdown */
525 static void __exit
526 ipfw_module_exit(void)
527 {
528 #ifdef _WIN32
529 #else  /* linux hook */
530         nf_unregister_hooks(ipfw_ops, ARRAY_SIZE(ipfw_ops));
531         /* maybe drain the queue before unregistering ? */
532         nf_unregister_queue_handler(PF_INET  UNREG_QH_ARG(ipfw2_queue_handler) );
533         nf_unregister_sockopt(&ipfw_sockopts);
534 #endif  /* linux */
535
536         fini_children();
537
538         printf("%s unloaded\n", __FUNCTION__);
539 }
540
541 #ifdef __linux__
542 module_init(ipfw_module_init)
543 module_exit(ipfw_module_exit)
544 MODULE_LICENSE("GPL"); /* mandatory */
545 #endif