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