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