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