4e3568aa5be2e75ebcf788699b0735c9fee73873
[ipfw.git] / dummynet / ip_fw_pfil.c
1 /*-
2  * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD: src/sys/netinet/ip_fw_pfil.c,v 1.25.2.2 2008/04/25 10:26:30 oleg Exp $");
29
30 #if !defined(KLD_MODULE)
31 #include "opt_ipfw.h"
32 #include "opt_ipdn.h"
33 #include "opt_inet.h"
34 #ifndef INET
35 #error IPFIREWALL requires INET.
36 #endif /* INET */
37 #endif /* KLD_MODULE */
38 #include "opt_inet6.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/malloc.h>
43 #include <sys/mbuf.h>
44 #include <sys/module.h>
45 #include <sys/kernel.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>
48 #include <sys/sysctl.h>
49
50 #include <net/if.h>
51 #include <net/pfil.h>
52 #include <net/vnet.h>
53
54 #include <netinet/in.h>
55 #include <netinet/ip.h>
56 #include <netinet/ip_var.h>
57 #include <netinet/ip_fw.h>
58 #include <netinet/ip_divert.h>
59 #include <netinet/ip_dummynet.h>
60
61 #include <netgraph/ng_ipfw.h>
62
63 #include <machine/in_cksum.h>
64
65 #include "missing.h"
66
67 int fw_enable = 1;
68 #ifdef INET6
69 int fw6_enable = 1;
70 #endif
71
72 int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
73
74 /* Divert hooks. */
75 ip_divert_packet_t *ip_divert_ptr = NULL;
76
77 /* ng_ipfw hooks. */
78 ng_ipfw_input_t *ng_ipfw_input_p = NULL;
79
80 /* Forward declarations. */
81 static int      ipfw_divert(struct mbuf **, int, int);
82 #define DIV_DIR_IN      1
83 #define DIV_DIR_OUT     0
84
85 int
86 ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
87     struct inpcb *inp)
88 {
89         struct ip_fw_args args;
90         struct ng_ipfw_tag *ng_tag;
91         struct m_tag *dn_tag;
92         int ipfw = 0;
93         int divert;
94         int tee;
95 #ifdef IPFIREWALL_FORWARD
96         struct m_tag *fwd_tag;
97 #endif
98
99         KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
100
101         bzero(&args, sizeof(args));
102
103         ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
104             NULL);
105         if (ng_tag != NULL) {
106                 KASSERT(ng_tag->dir == NG_IPFW_IN,
107                     ("ng_ipfw tag with wrong direction"));
108                 args.rule = ng_tag->rule;
109                 args.rule_id = ng_tag->rule_id;
110                 args.chain_id = ng_tag->chain_id;
111                 m_tag_delete(*m0, (struct m_tag *)ng_tag);
112         }
113
114 again:
115         dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
116         if (dn_tag != NULL){
117                 struct dn_pkt_tag *dt;
118
119                 dt = (struct dn_pkt_tag *)(dn_tag+1);
120                 args.rule = dt->rule;
121                 args.rule_id = dt->rule_id;
122                 args.chain_id = dt->chain_id;
123
124                 m_tag_delete(*m0, dn_tag);
125         }
126
127         args.m = *m0;
128         args.inp = inp;
129         ipfw = ipfw_chk(&args);
130         *m0 = args.m;   /* args.m can be modified by ipfw_chk */
131         tee = 0;
132
133         KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
134             __func__));
135
136         switch (ipfw) {
137         case IP_FW_PASS:
138                 if (args.next_hop == NULL)
139                         goto pass;
140
141 #ifdef IPFIREWALL_FORWARD
142                 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
143                                 sizeof(struct sockaddr_in), M_NOWAIT);
144                 if (fwd_tag == NULL)
145                         goto drop;
146                 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
147                 m_tag_prepend(*m0, fwd_tag);
148
149                 if (in_localip(args.next_hop->sin_addr))
150                         (*m0)->m_flags |= M_FASTFWD_OURS;
151                 goto pass;
152 #endif
153                 break;                  /* not reached */
154
155         case IP_FW_DENY:
156                 goto drop;
157                 break;                  /* not reached */
158
159         /* here packets come after the ipfw classification */
160         case IP_FW_DUMMYNET:
161                 if (ip_dn_io_ptr == NULL)
162                         goto drop;
163                 if (mtod(*m0, struct ip *)->ip_v == 4)
164                         ip_dn_io_ptr(m0, DN_TO_IP_IN, &args);
165                 else if (mtod(*m0, struct ip *)->ip_v == 6)
166                         ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args);
167                 if (*m0 != NULL)
168                         goto again;
169                 return 0;               /* packet consumed */
170
171         case IP_FW_TEE:
172                 tee = 1;
173                 /* fall through */
174
175         case IP_FW_DIVERT:
176                 divert = ipfw_divert(m0, DIV_DIR_IN, tee);
177                 if (divert) {
178                         *m0 = NULL;
179                         return 0;       /* packet consumed */
180                 } else {
181                         args.rule = NULL;
182                         goto again;     /* continue with packet */
183                 }
184
185         case IP_FW_NGTEE:
186                 if (!NG_IPFW_LOADED)
187                         goto drop;
188                 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1);
189                 goto again;             /* continue with packet */
190
191         case IP_FW_NETGRAPH:
192                 if (!NG_IPFW_LOADED)
193                         goto drop;
194                 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
195                 
196         case IP_FW_NAT:
197                 goto again;             /* continue with packet */
198
199         case IP_FW_REASS:
200                 goto again;
201
202         default:
203                 KASSERT(0, ("%s: unknown retval", __func__));
204         }
205
206 drop:
207         if (*m0)
208                 m_freem(*m0);
209         *m0 = NULL;
210         return (EACCES);
211 pass:
212         return 0;       /* not filtered */
213 }
214
215 int
216 ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
217     struct inpcb *inp)
218 {
219         struct ip_fw_args args;
220         struct ng_ipfw_tag *ng_tag;
221         struct m_tag *dn_tag;
222         int ipfw = 0;
223         int divert;
224         int tee;
225 #ifdef IPFIREWALL_FORWARD
226         struct m_tag *fwd_tag;
227 #endif
228
229         KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
230
231         bzero(&args, sizeof(args));
232
233         ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
234             NULL);
235         if (ng_tag != NULL) {
236                 KASSERT(ng_tag->dir == NG_IPFW_OUT,
237                     ("ng_ipfw tag with wrong direction"));
238                 args.rule = ng_tag->rule;
239                 args.rule_id = ng_tag->rule_id;
240                 args.chain_id = ng_tag->chain_id;
241                 m_tag_delete(*m0, (struct m_tag *)ng_tag);
242         }
243
244 again:
245         dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
246         if (dn_tag != NULL) {
247                 struct dn_pkt_tag *dt;
248
249                 dt = (struct dn_pkt_tag *)(dn_tag+1);
250                 args.rule = dt->rule;
251                 args.rule_id = dt->rule_id;
252                 args.chain_id = dt->chain_id;
253
254                 m_tag_delete(*m0, dn_tag);
255         }
256
257         args.m = *m0;
258         args.oif = ifp;
259         args.inp = inp;
260         ipfw = ipfw_chk(&args);
261         *m0 = args.m;   /* args.m can be modified by ipfw_chk */
262         tee = 0;
263
264         KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
265             __func__));
266
267         switch (ipfw) {
268         case IP_FW_PASS:
269                 if (args.next_hop == NULL)
270                         goto pass;
271 #ifdef IPFIREWALL_FORWARD
272                 /* Overwrite existing tag. */
273                 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
274                 if (fwd_tag == NULL) {
275                         fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
276                                 sizeof(struct sockaddr_in), M_NOWAIT);
277                         if (fwd_tag == NULL)
278                                 goto drop;
279                 } else
280                         m_tag_unlink(*m0, fwd_tag);
281                 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
282                 m_tag_prepend(*m0, fwd_tag);
283
284                 if (in_localip(args.next_hop->sin_addr))
285                         (*m0)->m_flags |= M_FASTFWD_OURS;
286                 goto pass;
287 #endif
288                 break;                  /* not reached */
289
290         case IP_FW_DENY:
291                 goto drop;
292                 break;                  /* not reached */
293
294         case IP_FW_DUMMYNET:
295                 if (ip_dn_io_ptr == NULL)
296                         break;
297                 if (mtod(*m0, struct ip *)->ip_v == 4)
298                         ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args);
299                 else if (mtod(*m0, struct ip *)->ip_v == 6)
300                         ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args);
301                 if (*m0 != NULL)
302                         goto again;
303                 return 0;               /* packet consumed */
304
305                 break;
306
307         case IP_FW_TEE:
308                 tee = 1;
309                 /* fall through */
310
311         case IP_FW_DIVERT:
312                 divert = ipfw_divert(m0, DIV_DIR_OUT, tee);
313                 if (divert) {
314                         *m0 = NULL;
315                         return 0;       /* packet consumed */
316                 } else {
317                         args.rule = NULL;
318                         goto again;     /* continue with packet */
319                 }
320
321         case IP_FW_NGTEE:
322                 if (!NG_IPFW_LOADED)
323                         goto drop;
324                 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1);
325                 goto again;             /* continue with packet */
326
327         case IP_FW_NETGRAPH:
328                 if (!NG_IPFW_LOADED)
329                         goto drop;
330                 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
331
332         case IP_FW_NAT:
333                 goto again;             /* continue with packet */
334                 
335         case IP_FW_REASS:
336                 goto again;     
337         
338         default:
339                 KASSERT(0, ("%s: unknown retval", __func__));
340         }
341
342 drop:
343         if (*m0)
344                 m_freem(*m0);
345         *m0 = NULL;
346         return (EACCES);
347 pass:
348         return 0;       /* not filtered */
349 }
350
351 static int
352 ipfw_divert(struct mbuf **m, int incoming, int tee)
353 {
354         /*
355          * ipfw_chk() has already tagged the packet with the divert tag.
356          * If tee is set, copy packet and return original.
357          * If not tee, consume packet and send it to divert socket.
358          */
359         struct mbuf *clone, *reass;
360         struct ip *ip;
361         int hlen;
362
363         reass = NULL;
364
365         /* Is divert module loaded? */
366         if (ip_divert_ptr == NULL)
367                 goto nodivert;
368
369         /* Cloning needed for tee? */
370         if (tee)
371                 clone = m_dup(*m, M_DONTWAIT);
372         else
373                 clone = *m;
374
375         /* In case m_dup was unable to allocate mbufs. */
376         if (clone == NULL)
377                 goto teeout;
378
379         /*
380          * Divert listeners can only handle non-fragmented packets.
381          * However when tee is set we will *not* de-fragment the packets;
382          * Doing do would put the reassembly into double-jeopardy.  On top
383          * of that someone doing a tee will probably want to get the packet
384          * in its original form.
385          */
386         ip = mtod(clone, struct ip *);
387         if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
388
389                 /* Reassemble packet. */
390                 reass = ip_reass(clone);
391
392                 /*
393                  * IP header checksum fixup after reassembly and leave header
394                  * in network byte order.
395                  */
396                 if (reass != NULL) {
397                         ip = mtod(reass, struct ip *);
398                         hlen = ip->ip_hl << 2;
399                         ip->ip_len = htons(ip->ip_len);
400                         ip->ip_off = htons(ip->ip_off);
401                         ip->ip_sum = 0;
402                         if (hlen == sizeof(struct ip))
403                                 ip->ip_sum = in_cksum_hdr(ip);
404                         else
405                                 ip->ip_sum = in_cksum(reass, hlen);
406                         clone = reass;
407                 } else
408                         clone = NULL;
409         } else {
410                 /* Convert header to network byte order. */
411                 ip->ip_len = htons(ip->ip_len);
412                 ip->ip_off = htons(ip->ip_off);
413         }
414
415         /* Do the dirty job... */
416         if (clone && ip_divert_ptr != NULL)
417                 ip_divert_ptr(clone, incoming);
418
419 teeout:
420         /*
421          * For tee we leave the divert tag attached to original packet.
422          * It will then continue rule evaluation after the tee rule.
423          */
424         if (tee)
425                 return 0;
426
427         /* Packet diverted and consumed */
428         return 1;
429
430 nodivert:
431         m_freem(*m);
432         return 1;
433 }
434
435 static int
436 ipfw_hook(void)
437 {
438         struct pfil_head *pfh_inet;
439
440         pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
441         if (pfh_inet == NULL)
442                 return ENOENT;
443
444         (void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
445                 pfh_inet);
446         (void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
447                 pfh_inet);
448
449         return 0;
450 }
451
452 static int
453 ipfw_unhook(void)
454 {
455         struct pfil_head *pfh_inet;
456
457         pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
458         if (pfh_inet == NULL)
459                 return ENOENT;
460
461         (void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
462                 pfh_inet);
463         (void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
464                 pfh_inet);
465
466         return 0;
467 }
468
469 #ifdef INET6
470 static int
471 ipfw6_hook(void)
472 {
473         struct pfil_head *pfh_inet6;
474
475         pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
476         if (pfh_inet6 == NULL)
477                 return ENOENT;
478
479         (void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
480                 pfh_inet6);
481         (void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
482                 pfh_inet6);
483
484         return 0;
485 }
486
487 static int
488 ipfw6_unhook(void)
489 {
490         struct pfil_head *pfh_inet6;
491
492         pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
493         if (pfh_inet6 == NULL)
494                 return ENOENT;
495
496         (void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
497                 pfh_inet6);
498         (void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
499                 pfh_inet6);
500
501         return 0;
502 }
503 #endif /* INET6 */
504
505 int
506 ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
507 {
508         int enable = *(int *)arg1;
509         int error;
510
511         error = sysctl_handle_int(oidp, &enable, 0, req);
512         if (error)
513                 return (error);
514
515         enable = (enable) ? 1 : 0;
516
517         if (enable == *(int *)arg1)
518                 return (0);
519
520         if (arg1 == &fw_enable) {
521                 if (enable)
522                         error = ipfw_hook();
523                 else
524                         error = ipfw_unhook();
525         }
526 #ifdef INET6
527         if (arg1 == &fw6_enable) {
528                 if (enable)
529                         error = ipfw6_hook();
530                 else
531                         error = ipfw6_unhook();
532         }
533 #endif
534
535         if (error)
536                 return (error);
537
538         *(int *)arg1 = enable;
539
540         return (0);
541 }
542
543 static int
544 ipfw_modevent(module_t mod, int type, void *unused)
545 {
546         int err = 0;
547
548         switch (type) {
549         case MOD_LOAD:
550                 if ((err = ipfw_init()) != 0) {
551                         printf("ipfw_init() error\n");
552                         break;
553                 }
554                 if ((err = ipfw_hook()) != 0) {
555                         printf("ipfw_hook() error\n");
556                         break;
557                 }
558 #ifdef INET6
559                 if ((err = ipfw6_hook()) != 0) {
560                         printf("ipfw_hook() error\n");
561                         break;
562                 }
563 #endif
564                 break;
565
566         case MOD_UNLOAD:
567                 if ((err = ipfw_unhook()) > 0)
568                         break;
569 #ifdef INET6
570                 if ((err = ipfw6_unhook()) > 0)
571                         break;
572 #endif
573                 ipfw_destroy();
574                 break;
575
576         default:
577                 return EOPNOTSUPP;
578                 break;
579         }
580         return err;
581 }
582
583 static moduledata_t ipfwmod = {
584         "ipfw",
585         ipfw_modevent,
586         0
587 };
588 DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY - 256);
589 MODULE_VERSION(ipfw, 2);