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