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