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