]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sys/netinet/ipfw/ip_fw_pfil.c
Copy head to stable/8 as part of 8.0 Release cycle.
[FreeBSD/stable/8.git] / sys / netinet / ipfw / 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$");
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/lock.h>
47 #include <sys/rwlock.h>
48 #include <sys/socket.h>
49 #include <sys/socketvar.h>
50 #include <sys/sysctl.h>
51 #include <sys/ucred.h>
52
53 #include <net/if.h>
54 #include <net/route.h>
55 #include <net/pfil.h>
56
57 #include <netinet/in.h>
58 #include <netinet/in_systm.h>
59 #include <netinet/ip.h>
60 #include <netinet/ip_var.h>
61 #include <netinet/ip_fw.h>
62 #include <netinet/ip_divert.h>
63 #include <netinet/ip_dummynet.h>
64
65 #include <netgraph/ng_ipfw.h>
66
67 #include <machine/in_cksum.h>
68
69 VNET_DEFINE(int, fw_enable) = 1;
70 #ifdef INET6
71 VNET_DEFINE(int, fw6_enable) = 1;
72 #endif
73
74 int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
75
76 /* Divert hooks. */
77 ip_divert_packet_t *ip_divert_ptr = NULL;
78
79 /* ng_ipfw hooks. */
80 ng_ipfw_input_t *ng_ipfw_input_p = NULL;
81
82 /* Forward declarations. */
83 static int      ipfw_divert(struct mbuf **, int, int);
84 #define DIV_DIR_IN      1
85 #define DIV_DIR_OUT     0
86
87 int
88 ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
89     struct inpcb *inp)
90 {
91         struct ip_fw_args args;
92         struct ng_ipfw_tag *ng_tag;
93         struct m_tag *dn_tag;
94         int ipfw = 0;
95         int divert;
96         int tee;
97 #ifdef IPFIREWALL_FORWARD
98         struct m_tag *fwd_tag;
99 #endif
100
101         KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
102
103         bzero(&args, sizeof(args));
104
105         ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
106             NULL);
107         if (ng_tag != NULL) {
108                 KASSERT(ng_tag->dir == NG_IPFW_IN,
109                     ("ng_ipfw tag with wrong direction"));
110                 args.rule = ng_tag->rule;
111                 args.rule_id = ng_tag->rule_id;
112                 args.chain_id = ng_tag->chain_id;
113                 m_tag_delete(*m0, (struct m_tag *)ng_tag);
114         }
115
116 again:
117         dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
118         if (dn_tag != NULL){
119                 struct dn_pkt_tag *dt;
120
121                 dt = (struct dn_pkt_tag *)(dn_tag+1);
122                 args.rule = dt->rule;
123                 args.rule_id = dt->rule_id;
124                 args.chain_id = dt->chain_id;
125
126                 m_tag_delete(*m0, dn_tag);
127         }
128
129         args.m = *m0;
130         args.inp = inp;
131         tee = 0;
132
133         if (V_fw_one_pass == 0 || args.rule == NULL) {
134                 ipfw = ipfw_chk(&args);
135                 *m0 = args.m;
136         } else
137                 ipfw = IP_FW_PASS;
138                 
139         KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
140             __func__));
141
142         switch (ipfw) {
143         case IP_FW_PASS:
144                 if (args.next_hop == NULL)
145                         goto pass;
146
147 #ifdef IPFIREWALL_FORWARD
148                 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
149                                 sizeof(struct sockaddr_in), M_NOWAIT);
150                 if (fwd_tag == NULL)
151                         goto drop;
152                 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
153                 m_tag_prepend(*m0, fwd_tag);
154
155                 if (in_localip(args.next_hop->sin_addr))
156                         (*m0)->m_flags |= M_FASTFWD_OURS;
157                 goto pass;
158 #endif
159                 break;                  /* not reached */
160
161         case IP_FW_DENY:
162                 goto drop;
163                 break;                  /* not reached */
164
165         case IP_FW_DUMMYNET:
166                 if (ip_dn_io_ptr == NULL)
167                         goto drop;
168                 if (mtod(*m0, struct ip *)->ip_v == 4)
169                         ip_dn_io_ptr(m0, DN_TO_IP_IN, &args);
170                 else if (mtod(*m0, struct ip *)->ip_v == 6)
171                         ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args);
172                 if (*m0 != NULL)
173                         goto again;
174                 return 0;               /* packet consumed */
175
176         case IP_FW_TEE:
177                 tee = 1;
178                 /* fall through */
179
180         case IP_FW_DIVERT:
181                 divert = ipfw_divert(m0, DIV_DIR_IN, tee);
182                 if (divert) {
183                         *m0 = NULL;
184                         return 0;       /* packet consumed */
185                 } else {
186                         args.rule = NULL;
187                         goto again;     /* continue with packet */
188                 }
189
190         case IP_FW_NGTEE:
191                 if (!NG_IPFW_LOADED)
192                         goto drop;
193                 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1);
194                 goto again;             /* continue with packet */
195
196         case IP_FW_NETGRAPH:
197                 if (!NG_IPFW_LOADED)
198                         goto drop;
199                 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
200                 
201         case IP_FW_NAT:
202                 goto again;             /* continue with packet */
203
204         case IP_FW_REASS:
205                 goto again;
206
207         default:
208                 KASSERT(0, ("%s: unknown retval", __func__));
209         }
210
211 drop:
212         if (*m0)
213                 m_freem(*m0);
214         *m0 = NULL;
215         return (EACCES);
216 pass:
217         return 0;       /* not filtered */
218 }
219
220 int
221 ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
222     struct inpcb *inp)
223 {
224         struct ip_fw_args args;
225         struct ng_ipfw_tag *ng_tag;
226         struct m_tag *dn_tag;
227         int ipfw = 0;
228         int divert;
229         int tee;
230 #ifdef IPFIREWALL_FORWARD
231         struct m_tag *fwd_tag;
232 #endif
233
234         KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
235
236         bzero(&args, sizeof(args));
237
238         ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
239             NULL);
240         if (ng_tag != NULL) {
241                 KASSERT(ng_tag->dir == NG_IPFW_OUT,
242                     ("ng_ipfw tag with wrong direction"));
243                 args.rule = ng_tag->rule;
244                 args.rule_id = ng_tag->rule_id;
245                 args.chain_id = ng_tag->chain_id;
246                 m_tag_delete(*m0, (struct m_tag *)ng_tag);
247         }
248
249 again:
250         dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
251         if (dn_tag != NULL) {
252                 struct dn_pkt_tag *dt;
253
254                 dt = (struct dn_pkt_tag *)(dn_tag+1);
255                 args.rule = dt->rule;
256                 args.rule_id = dt->rule_id;
257                 args.chain_id = dt->chain_id;
258
259                 m_tag_delete(*m0, dn_tag);
260         }
261
262         args.m = *m0;
263         args.oif = ifp;
264         args.inp = inp;
265         tee = 0;
266
267         if (V_fw_one_pass == 0 || args.rule == NULL) {
268                 ipfw = ipfw_chk(&args);
269                 *m0 = args.m;
270         } else
271                 ipfw = IP_FW_PASS;
272
273         KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
274             __func__));
275
276         switch (ipfw) {
277         case IP_FW_PASS:
278                 if (args.next_hop == NULL)
279                         goto pass;
280 #ifdef IPFIREWALL_FORWARD
281                 /* Overwrite existing tag. */
282                 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
283                 if (fwd_tag == NULL) {
284                         fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
285                                 sizeof(struct sockaddr_in), M_NOWAIT);
286                         if (fwd_tag == NULL)
287                                 goto drop;
288                 } else
289                         m_tag_unlink(*m0, fwd_tag);
290                 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
291                 m_tag_prepend(*m0, fwd_tag);
292
293                 if (in_localip(args.next_hop->sin_addr))
294                         (*m0)->m_flags |= M_FASTFWD_OURS;
295                 goto pass;
296 #endif
297                 break;                  /* not reached */
298
299         case IP_FW_DENY:
300                 goto drop;
301                 break;                  /* not reached */
302
303         case IP_FW_DUMMYNET:
304                 if (ip_dn_io_ptr == NULL)
305                         break;
306                 if (mtod(*m0, struct ip *)->ip_v == 4)
307                         ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args);
308                 else if (mtod(*m0, struct ip *)->ip_v == 6)
309                         ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args);
310                 if (*m0 != NULL)
311                         goto again;
312                 return 0;               /* packet consumed */
313
314                 break;
315
316         case IP_FW_TEE:
317                 tee = 1;
318                 /* fall through */
319
320         case IP_FW_DIVERT:
321                 divert = ipfw_divert(m0, DIV_DIR_OUT, tee);
322                 if (divert) {
323                         *m0 = NULL;
324                         return 0;       /* packet consumed */
325                 } else {
326                         args.rule = NULL;
327                         goto again;     /* continue with packet */
328                 }
329
330         case IP_FW_NGTEE:
331                 if (!NG_IPFW_LOADED)
332                         goto drop;
333                 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1);
334                 goto again;             /* continue with packet */
335
336         case IP_FW_NETGRAPH:
337                 if (!NG_IPFW_LOADED)
338                         goto drop;
339                 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
340
341         case IP_FW_NAT:
342                 goto again;             /* continue with packet */
343                 
344         case IP_FW_REASS:
345                 goto again;     
346         
347         default:
348                 KASSERT(0, ("%s: unknown retval", __func__));
349         }
350
351 drop:
352         if (*m0)
353                 m_freem(*m0);
354         *m0 = NULL;
355         return (EACCES);
356 pass:
357         return 0;       /* not filtered */
358 }
359
360 static int
361 ipfw_divert(struct mbuf **m, int incoming, int tee)
362 {
363         /*
364          * ipfw_chk() has already tagged the packet with the divert tag.
365          * If tee is set, copy packet and return original.
366          * If not tee, consume packet and send it to divert socket.
367          */
368         struct mbuf *clone, *reass;
369         struct ip *ip;
370         int hlen;
371
372         reass = NULL;
373
374         /* Is divert module loaded? */
375         if (ip_divert_ptr == NULL)
376                 goto nodivert;
377
378         /* Cloning needed for tee? */
379         if (tee)
380                 clone = m_dup(*m, M_DONTWAIT);
381         else
382                 clone = *m;
383
384         /* In case m_dup was unable to allocate mbufs. */
385         if (clone == NULL)
386                 goto teeout;
387
388         /*
389          * Divert listeners can only handle non-fragmented packets.
390          * However when tee is set we will *not* de-fragment the packets;
391          * Doing do would put the reassembly into double-jeopardy.  On top
392          * of that someone doing a tee will probably want to get the packet
393          * in its original form.
394          */
395         ip = mtod(clone, struct ip *);
396         if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
397
398                 /* Reassemble packet. */
399                 reass = ip_reass(clone);
400
401                 /*
402                  * IP header checksum fixup after reassembly and leave header
403                  * in network byte order.
404                  */
405                 if (reass != NULL) {
406                         ip = mtod(reass, struct ip *);
407                         hlen = ip->ip_hl << 2;
408                         ip->ip_len = htons(ip->ip_len);
409                         ip->ip_off = htons(ip->ip_off);
410                         ip->ip_sum = 0;
411                         if (hlen == sizeof(struct ip))
412                                 ip->ip_sum = in_cksum_hdr(ip);
413                         else
414                                 ip->ip_sum = in_cksum(reass, hlen);
415                         clone = reass;
416                 } else
417                         clone = NULL;
418         } else {
419                 /* Convert header to network byte order. */
420                 ip->ip_len = htons(ip->ip_len);
421                 ip->ip_off = htons(ip->ip_off);
422         }
423
424         /* Do the dirty job... */
425         if (clone && ip_divert_ptr != NULL)
426                 ip_divert_ptr(clone, incoming);
427
428 teeout:
429         /*
430          * For tee we leave the divert tag attached to original packet.
431          * It will then continue rule evaluation after the tee rule.
432          */
433         if (tee)
434                 return 0;
435
436         /* Packet diverted and consumed */
437         return 1;
438
439 nodivert:
440         m_freem(*m);
441         return 1;
442 }
443
444 static int
445 ipfw_hook(void)
446 {
447         struct pfil_head *pfh_inet;
448
449         pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
450         if (pfh_inet == NULL)
451                 return ENOENT;
452
453         (void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
454             pfh_inet);
455         (void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
456             pfh_inet);
457
458         return 0;
459 }
460
461 static int
462 ipfw_unhook(void)
463 {
464         struct pfil_head *pfh_inet;
465
466         pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
467         if (pfh_inet == NULL)
468                 return ENOENT;
469
470         (void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
471             pfh_inet);
472         (void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
473             pfh_inet);
474
475         return 0;
476 }
477
478 #ifdef INET6
479 static int
480 ipfw6_hook(void)
481 {
482         struct pfil_head *pfh_inet6;
483
484         pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
485         if (pfh_inet6 == NULL)
486                 return ENOENT;
487
488         (void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
489             pfh_inet6);
490         (void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
491             pfh_inet6);
492
493         return 0;
494 }
495
496 static int
497 ipfw6_unhook(void)
498 {
499         struct pfil_head *pfh_inet6;
500
501         pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
502         if (pfh_inet6 == NULL)
503                 return ENOENT;
504
505         (void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
506             pfh_inet6);
507         (void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
508             pfh_inet6);
509
510         return 0;
511 }
512 #endif /* INET6 */
513
514 int
515 ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
516 {
517         int enable = *(int *)arg1;
518         int error;
519
520         error = sysctl_handle_int(oidp, &enable, 0, req);
521         if (error)
522                 return (error);
523
524         enable = (enable) ? 1 : 0;
525
526         if (enable == *(int *)arg1)
527                 return (0);
528
529         if (arg1 == &V_fw_enable) {
530                 if (enable)
531                         error = ipfw_hook();
532                 else
533                         error = ipfw_unhook();
534         }
535 #ifdef INET6
536         if (arg1 == &V_fw6_enable) {
537                 if (enable)
538                         error = ipfw6_hook();
539                 else
540                         error = ipfw6_unhook();
541         }
542 #endif
543
544         if (error)
545                 return (error);
546
547         *(int *)arg1 = enable;
548
549         return (0);
550 }
551
552 static int
553 ipfw_modevent(module_t mod, int type, void *unused)
554 {
555         int err = 0;
556
557         switch (type) {
558         case MOD_LOAD:
559                 if ((err = ipfw_init()) != 0) {
560                         printf("ipfw_init() error\n");
561                         break;
562                 }
563                 if ((err = ipfw_hook()) != 0) {
564                         printf("ipfw_hook() error\n");
565                         break;
566                 }
567 #ifdef INET6
568                 if ((err = ipfw6_hook()) != 0) {
569                         printf("ipfw_hook() error\n");
570                         break;
571                 }
572 #endif
573                 break;
574
575         case MOD_UNLOAD:
576                 if ((err = ipfw_unhook()) > 0)
577                         break;
578 #ifdef INET6
579                 if ((err = ipfw6_unhook()) > 0)
580                         break;
581 #endif
582                 ipfw_destroy();
583                 break;
584
585         default:
586                 return EOPNOTSUPP;
587                 break;
588         }
589         return err;
590 }
591
592 static moduledata_t ipfwmod = {
593         "ipfw",
594         ipfw_modevent,
595         0
596 };
597 DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY - 256);
598 MODULE_VERSION(ipfw, 2);