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