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