1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 #include "opt_ipfw.h"
31 #include "opt_inet.h"
32 #include "opt_inet6.h"
33 #ifndef INET
34 #error IPFIREWALL requires INET.
35 #endif /* INET */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/module.h>
42 #include <sys/kernel.h>
43 #include <sys/lock.h>
44 #include <sys/rwlock.h>
45 #include <sys/socket.h>
46 #include <sys/sysctl.h>
47
48 #include <net/if.h>
49 #include <net/if_var.h>
50 #include <net/route.h>
51 #include <net/ethernet.h>
52 #include <net/pfil.h>
53 #include <net/vnet.h>
54
55 #include <netinet/in.h>
56 #include <netinet/in_systm.h>
57 #include <netinet/ip.h>
58 #include <netinet/ip_var.h>
59 #include <netinet/ip_fw.h>
60 #ifdef INET6
61 #include <netinet/ip6.h>
62 #include <netinet6/ip6_var.h>
63 #include <netinet6/scope6_var.h>
64 #endif
65
66 #include <netgraph/ng_ipfw.h>
67
68 #include <netpfil/ipfw/ip_fw_private.h>
69
70 #include <machine/in_cksum.h>
71
72 VNET_DEFINE_STATIC(int, fw_enable) = 1;
73 #define V_fw_enable VNET(fw_enable)
74
75 #ifdef INET6
76 VNET_DEFINE_STATIC(int, fw6_enable) = 1;
77 #define V_fw6_enable VNET(fw6_enable)
78 #endif
79
80 VNET_DEFINE_STATIC(int, fwlink_enable) = 0;
81 #define V_fwlink_enable VNET(fwlink_enable)
82
83 int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
84
85 /* Forward declarations. */
86 static int ipfw_divert(struct mbuf **, struct ip_fw_args *, bool);
87
88 #ifdef SYSCTL_NODE
89
90 SYSBEGIN(f1)
91
92 SYSCTL_DECL(_net_inet_ip_fw);
93 SYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, enable,
94 CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_SECURE3 |
95 CTLFLAG_NEEDGIANT, &VNET_NAME(fw_enable), 0, ipfw_chg_hook, "I",
96 "Enable ipfw");
97 #ifdef INET6
98 SYSCTL_DECL(_net_inet6_ip6_fw);
99 SYSCTL_PROC(_net_inet6_ip6_fw, OID_AUTO, enable,
100 CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_SECURE3 |
101 CTLFLAG_NEEDGIANT, &VNET_NAME(fw6_enable), 0, ipfw_chg_hook, "I",
102 "Enable ipfw+6");
103 #endif /* INET6 */
104
105 SYSCTL_DECL(_net_link_ether);
106 SYSCTL_PROC(_net_link_ether, OID_AUTO, ipfw,
107 CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_SECURE3 |
108 CTLFLAG_NEEDGIANT, &VNET_NAME(fwlink_enable), 0, ipfw_chg_hook, "I",
109 "Pass ether pkts through firewall");
110
111 SYSEND
112
113 #endif /* SYSCTL_NODE */
114
115 /*
116 * The pfilter hook to pass packets to ipfw_chk and then to
117 * dummynet, divert, netgraph or other modules.
118 * The packet may be consumed.
119 */
120 static pfil_return_t
ipfw_check_packet(struct mbuf ** m0,struct ifnet * ifp,int flags,void * ruleset __unused,struct inpcb * inp)121 ipfw_check_packet(struct mbuf **m0, struct ifnet *ifp, int flags,
122 void *ruleset __unused, struct inpcb *inp)
123 {
124 struct ip_fw_args args;
125 struct m_tag *tag;
126 pfil_return_t ret;
127 int ipfw;
128
129 args.flags = (flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT;
130 again:
131 /*
132 * extract and remove the tag if present. If we are left
133 * with onepass, optimize the outgoing path.
134 */
135 tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
136 if (tag != NULL) {
137 args.rule = *((struct ipfw_rule_ref *)(tag+1));
138 m_tag_delete(*m0, tag);
139 if (args.rule.info & IPFW_ONEPASS)
140 return (0);
141 args.flags |= IPFW_ARGS_REF;
142 }
143
144 args.m = *m0;
145 args.ifp = ifp;
146 args.inp = inp;
147
148 ipfw = ipfw_chk(&args);
149 *m0 = args.m;
150
151 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY ||
152 ipfw == IP_FW_NAT64, ("%s: m0 is NULL", __func__));
153
154 ret = PFIL_PASS;
155 switch (ipfw) {
156 case IP_FW_PASS:
157 /* next_hop may be set by ipfw_chk */
158 if ((args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR |
159 IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) == 0)
160 break;
161 #if (!defined(INET6) && !defined(INET))
162 ret = PFIL_DROPPED;
163 #else
164 {
165 void *psa;
166 size_t len;
167 #ifdef INET
168 if (args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR)) {
169 MPASS((args.flags & (IPFW_ARGS_NH4 |
170 IPFW_ARGS_NH4PTR)) != (IPFW_ARGS_NH4 |
171 IPFW_ARGS_NH4PTR));
172 MPASS((args.flags & (IPFW_ARGS_NH6 |
173 IPFW_ARGS_NH6PTR)) == 0);
174 len = sizeof(struct sockaddr_in);
175 psa = (args.flags & IPFW_ARGS_NH4) ?
176 &args.hopstore : args.next_hop;
177 if (in_localip(satosin(psa)->sin_addr))
178 (*m0)->m_flags |= M_FASTFWD_OURS;
179 (*m0)->m_flags |= M_IP_NEXTHOP;
180 }
181 #endif /* INET */
182 #ifdef INET6
183 if (args.flags & (IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) {
184 MPASS((args.flags & (IPFW_ARGS_NH6 |
185 IPFW_ARGS_NH6PTR)) != (IPFW_ARGS_NH6 |
186 IPFW_ARGS_NH6PTR));
187 MPASS((args.flags & (IPFW_ARGS_NH4 |
188 IPFW_ARGS_NH4PTR)) == 0);
189 len = sizeof(struct sockaddr_in6);
190 psa = args.next_hop6;
191 (*m0)->m_flags |= M_IP6_NEXTHOP;
192 }
193 #endif /* INET6 */
194 /*
195 * Incoming packets should not be tagged so we do not
196 * m_tag_find. Outgoing packets may be tagged, so we
197 * reuse the tag if present.
198 */
199 tag = (flags & PFIL_IN) ? NULL :
200 m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
201 if (tag != NULL) {
202 m_tag_unlink(*m0, tag);
203 } else {
204 tag = m_tag_get(PACKET_TAG_IPFORWARD, len,
205 M_NOWAIT);
206 if (tag == NULL) {
207 ret = PFIL_DROPPED;
208 break;
209 }
210 }
211 if ((args.flags & IPFW_ARGS_NH6) == 0)
212 bcopy(psa, tag + 1, len);
213 m_tag_prepend(*m0, tag);
214 ret = 0;
215 #ifdef INET6
216 /* IPv6 next hop needs additional handling */
217 if (args.flags & (IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) {
218 struct sockaddr_in6 *sa6;
219
220 sa6 = satosin6(tag + 1);
221 if (args.flags & IPFW_ARGS_NH6) {
222 sa6->sin6_family = AF_INET6;
223 sa6->sin6_len = sizeof(*sa6);
224 sa6->sin6_addr = args.hopstore6.sin6_addr;
225 sa6->sin6_port = args.hopstore6.sin6_port;
226 sa6->sin6_scope_id =
227 args.hopstore6.sin6_scope_id;
228 }
229 /*
230 * If nh6 address is link-local we should convert
231 * it to kernel internal form before doing any
232 * comparisons.
233 */
234 if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) {
235 ret = PFIL_DROPPED;
236 break;
237 }
238 if (in6_localip(&sa6->sin6_addr))
239 (*m0)->m_flags |= M_FASTFWD_OURS;
240 }
241 #endif /* INET6 */
242 }
243 #endif /* INET || INET6 */
244 break;
245
246 case IP_FW_DENY:
247 ret = PFIL_DROPPED;
248 break;
249
250 case IP_FW_DUMMYNET:
251 if (ip_dn_io_ptr == NULL) {
252 ret = PFIL_DROPPED;
253 break;
254 }
255 MPASS(args.flags & IPFW_ARGS_REF);
256 if (args.flags & (IPFW_ARGS_IP4 | IPFW_ARGS_IP6))
257 (void )ip_dn_io_ptr(m0, &args);
258 else {
259 ret = PFIL_DROPPED;
260 break;
261 }
262 /*
263 * XXX should read the return value.
264 * dummynet normally eats the packet and sets *m0=NULL
265 * unless the packet can be sent immediately. In this
266 * case args is updated and we should re-run the
267 * check without clearing args.
268 */
269 if (*m0 != NULL)
270 goto again;
271 ret = PFIL_CONSUMED;
272 break;
273
274 case IP_FW_TEE:
275 case IP_FW_DIVERT:
276 if (ip_divert_ptr == NULL) {
277 ret = PFIL_DROPPED;
278 break;
279 }
280 MPASS(args.flags & IPFW_ARGS_REF);
281 (void )ipfw_divert(m0, &args, ipfw == IP_FW_TEE);
282 /* continue processing for the original packet (tee). */
283 if (*m0)
284 goto again;
285 ret = PFIL_CONSUMED;
286 break;
287
288 case IP_FW_NGTEE:
289 case IP_FW_NETGRAPH:
290 if (ng_ipfw_input_p == NULL) {
291 ret = PFIL_DROPPED;
292 break;
293 }
294 MPASS(args.flags & IPFW_ARGS_REF);
295 (void )ng_ipfw_input_p(m0, &args, ipfw == IP_FW_NGTEE);
296 if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
297 goto again; /* continue with packet */
298 ret = PFIL_CONSUMED;
299 break;
300
301 case IP_FW_NAT:
302 /* honor one-pass in case of successful nat */
303 if (V_fw_one_pass)
304 break;
305 goto again;
306
307 case IP_FW_REASS:
308 goto again; /* continue with packet */
309
310 case IP_FW_NAT64:
311 ret = PFIL_CONSUMED;
312 break;
313
314 default:
315 KASSERT(0, ("%s: unknown retval", __func__));
316 }
317
318 if (ret != PFIL_PASS) {
319 if (*m0)
320 FREE_PKT(*m0);
321 *m0 = NULL;
322 }
323
324 return (ret);
325 }
326
327 /*
328 * ipfw processing for ethernet packets (in and out).
329 */
330 static pfil_return_t
ipfw_check_frame(pfil_packet_t p,struct ifnet * ifp,int flags,void * ruleset __unused,struct inpcb * inp)331 ipfw_check_frame(pfil_packet_t p, struct ifnet *ifp, int flags,
332 void *ruleset __unused, struct inpcb *inp)
333 {
334 struct ip_fw_args args;
335 pfil_return_t ret;
336 bool mem, realloc;
337 int ipfw;
338
339 if (flags & PFIL_MEMPTR) {
340 mem = true;
341 realloc = false;
342 args.flags = PFIL_LENGTH(flags) | IPFW_ARGS_ETHER;
343 args.mem = p.mem;
344 } else {
345 mem = realloc = false;
346 args.flags = IPFW_ARGS_ETHER;
347 }
348 args.flags |= (flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT;
349 args.ifp = ifp;
350 args.inp = inp;
351
352 again:
353 if (!mem) {
354 /*
355 * Fetch start point from rule, if any.
356 * Remove the tag if present.
357 */
358 struct m_tag *mtag;
359
360 mtag = m_tag_locate(*p.m, MTAG_IPFW_RULE, 0, NULL);
361 if (mtag != NULL) {
362 args.rule = *((struct ipfw_rule_ref *)(mtag+1));
363 m_tag_delete(*p.m, mtag);
364 if (args.rule.info & IPFW_ONEPASS)
365 return (PFIL_PASS);
366 args.flags |= IPFW_ARGS_REF;
367 }
368 args.m = *p.m;
369 }
370
371 ipfw = ipfw_chk(&args);
372 if (!mem)
373 *p.m = args.m;
374
375 ret = PFIL_PASS;
376 switch (ipfw) {
377 case IP_FW_PASS:
378 break;
379
380 case IP_FW_DENY:
381 ret = PFIL_DROPPED;
382 break;
383
384 case IP_FW_DUMMYNET:
385 if (ip_dn_io_ptr == NULL) {
386 ret = PFIL_DROPPED;
387 break;
388 }
389 if (mem) {
390 if (pfil_realloc(&p, flags, ifp) != 0) {
391 ret = PFIL_DROPPED;
392 break;
393 }
394 mem = false;
395 realloc = true;
396 }
397 MPASS(args.flags & IPFW_ARGS_REF);
398 ip_dn_io_ptr(p.m, &args);
399 return (PFIL_CONSUMED);
400
401 case IP_FW_NGTEE:
402 case IP_FW_NETGRAPH:
403 if (ng_ipfw_input_p == NULL) {
404 ret = PFIL_DROPPED;
405 break;
406 }
407 if (mem) {
408 if (pfil_realloc(&p, flags, ifp) != 0) {
409 ret = PFIL_DROPPED;
410 break;
411 }
412 mem = false;
413 realloc = true;
414 }
415 MPASS(args.flags & IPFW_ARGS_REF);
416 (void )ng_ipfw_input_p(p.m, &args, ipfw == IP_FW_NGTEE);
417 if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
418 goto again; /* continue with packet */
419 ret = PFIL_CONSUMED;
420 break;
421
422 default:
423 KASSERT(0, ("%s: unknown retval", __func__));
424 }
425
426 if (!mem && ret != PFIL_PASS) {
427 if (*p.m)
428 FREE_PKT(*p.m);
429 *p.m = NULL;
430 }
431
432 if (realloc && ret == PFIL_PASS)
433 ret = PFIL_REALLOCED;
434
435 return (ret);
436 }
437
438 /* do the divert, return 1 on error 0 on success */
439 static int
ipfw_divert(struct mbuf ** m0,struct ip_fw_args * args,bool tee)440 ipfw_divert(struct mbuf **m0, struct ip_fw_args *args, bool tee)
441 {
442 /*
443 * ipfw_chk() has already tagged the packet with the divert tag.
444 * If tee is set, copy packet and return original.
445 * If not tee, consume packet and send it to divert socket.
446 */
447 struct mbuf *clone;
448 struct ip *ip = mtod(*m0, struct ip *);
449 struct m_tag *tag;
450
451 /* Cloning needed for tee? */
452 if (tee == false) {
453 clone = *m0; /* use the original mbuf */
454 *m0 = NULL;
455 } else {
456 clone = m_dup(*m0, M_NOWAIT);
457 /* If we cannot duplicate the mbuf, we sacrifice the divert
458 * chain and continue with the tee-ed packet.
459 */
460 if (clone == NULL)
461 return 1;
462 }
463
464 /*
465 * Divert listeners can normally handle non-fragmented packets,
466 * but we can only reass in the non-tee case.
467 * This means that listeners on a tee rule may get fragments,
468 * and have to live with that.
469 * Note that we now have the 'reass' ipfw option so if we care
470 * we can do it before a 'tee'.
471 */
472 if (tee == false) switch (ip->ip_v) {
473 case IPVERSION:
474 if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) {
475 int hlen;
476 struct mbuf *reass;
477
478 reass = ip_reass(clone); /* Reassemble packet. */
479 if (reass == NULL)
480 return 0; /* not an error */
481 /* if reass = NULL then it was consumed by ip_reass */
482 /*
483 * IP header checksum fixup after reassembly and leave header
484 * in network byte order.
485 */
486 ip = mtod(reass, struct ip *);
487 hlen = ip->ip_hl << 2;
488 ip->ip_sum = 0;
489 if (hlen == sizeof(struct ip))
490 ip->ip_sum = in_cksum_hdr(ip);
491 else
492 ip->ip_sum = in_cksum(reass, hlen);
493 clone = reass;
494 }
495 break;
496 #ifdef INET6
497 case IPV6_VERSION >> 4:
498 {
499 struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *);
500
501 if (ip6->ip6_nxt == IPPROTO_FRAGMENT) {
502 int nxt, off;
503
504 off = sizeof(struct ip6_hdr);
505 nxt = frag6_input(&clone, &off, 0);
506 if (nxt == IPPROTO_DONE)
507 return (0);
508 }
509 break;
510 }
511 #endif
512 }
513
514 /* attach a tag to the packet with the reinject info */
515 tag = m_tag_alloc(MTAG_IPFW_RULE, 0,
516 sizeof(struct ipfw_rule_ref), M_NOWAIT);
517 if (tag == NULL) {
518 FREE_PKT(clone);
519 return 1;
520 }
521 *((struct ipfw_rule_ref *)(tag+1)) = args->rule;
522 m_tag_prepend(clone, tag);
523
524 /* Do the dirty job... */
525 ip_divert_ptr(clone, args->flags & IPFW_ARGS_IN);
526 return 0;
527 }
528
529 /*
530 * attach or detach hooks for a given protocol family
531 */
532 VNET_DEFINE_STATIC(pfil_hook_t, ipfw_inet_hook);
533 #define V_ipfw_inet_hook VNET(ipfw_inet_hook)
534 #ifdef INET6
535 VNET_DEFINE_STATIC(pfil_hook_t, ipfw_inet6_hook);
536 #define V_ipfw_inet6_hook VNET(ipfw_inet6_hook)
537 #endif
538 VNET_DEFINE_STATIC(pfil_hook_t, ipfw_link_hook);
539 #define V_ipfw_link_hook VNET(ipfw_link_hook)
540
541 static void
ipfw_hook(int pf)542 ipfw_hook(int pf)
543 {
544 struct pfil_hook_args pha;
545 pfil_hook_t *h;
546
547 pha.pa_version = PFIL_VERSION;
548 pha.pa_flags = PFIL_IN | PFIL_OUT;
549 pha.pa_modname = "ipfw";
550 pha.pa_ruleset = NULL;
551
552 switch (pf) {
553 case AF_INET:
554 pha.pa_func = ipfw_check_packet;
555 pha.pa_type = PFIL_TYPE_IP4;
556 pha.pa_rulname = "default";
557 h = &V_ipfw_inet_hook;
558 break;
559 #ifdef INET6
560 case AF_INET6:
561 pha.pa_func = ipfw_check_packet;
562 pha.pa_type = PFIL_TYPE_IP6;
563 pha.pa_rulname = "default6";
564 h = &V_ipfw_inet6_hook;
565 break;
566 #endif
567 case AF_LINK:
568 pha.pa_func = ipfw_check_frame;
569 pha.pa_type = PFIL_TYPE_ETHERNET;
570 pha.pa_rulname = "default-link";
571 pha.pa_flags |= PFIL_MEMPTR;
572 h = &V_ipfw_link_hook;
573 break;
574 }
575
576 *h = pfil_add_hook(&pha);
577 }
578
579 static void
ipfw_unhook(int pf)580 ipfw_unhook(int pf)
581 {
582
583 switch (pf) {
584 case AF_INET:
585 pfil_remove_hook(V_ipfw_inet_hook);
586 break;
587 #ifdef INET6
588 case AF_INET6:
589 pfil_remove_hook(V_ipfw_inet6_hook);
590 break;
591 #endif
592 case AF_LINK:
593 pfil_remove_hook(V_ipfw_link_hook);
594 break;
595 }
596 }
597
598 static int
ipfw_link(int pf,bool unlink)599 ipfw_link(int pf, bool unlink)
600 {
601 struct pfil_link_args pla;
602
603 pla.pa_version = PFIL_VERSION;
604 pla.pa_flags = PFIL_IN | PFIL_OUT | PFIL_HEADPTR | PFIL_HOOKPTR;
605 if (unlink)
606 pla.pa_flags |= PFIL_UNLINK;
607
608 switch (pf) {
609 case AF_INET:
610 pla.pa_head = V_inet_pfil_head;
611 pla.pa_hook = V_ipfw_inet_hook;
612 break;
613 #ifdef INET6
614 case AF_INET6:
615 pla.pa_head = V_inet6_pfil_head;
616 pla.pa_hook = V_ipfw_inet6_hook;
617 break;
618 #endif
619 case AF_LINK:
620 pla.pa_head = V_link_pfil_head;
621 pla.pa_hook = V_ipfw_link_hook;
622 break;
623 }
624
625 return (pfil_link(&pla));
626 }
627
628 int
ipfw_attach_hooks(void)629 ipfw_attach_hooks(void)
630 {
631 int error = 0;
632
633 ipfw_hook(AF_INET);
634 TUNABLE_INT_FETCH("net.inet.ip.fw.enable", &V_fw_enable);
635 if (V_fw_enable && (error = ipfw_link(AF_INET, false)) != 0)
636 printf("ipfw_hook() error\n");
637 #ifdef INET6
638 ipfw_hook(AF_INET6);
639 TUNABLE_INT_FETCH("net.inet6.ip6.fw.enable", &V_fw6_enable);
640 if (V_fw6_enable && (error = ipfw_link(AF_INET6, false)) != 0)
641 printf("ipfw6_hook() error\n");
642 #endif
643 ipfw_hook(AF_LINK);
644 TUNABLE_INT_FETCH("net.link.ether.ipfw", &V_fwlink_enable);
645 if (V_fwlink_enable && (error = ipfw_link(AF_LINK, false)) != 0)
646 printf("ipfw_link_hook() error\n");
647
648 return (error);
649 }
650
651 void
ipfw_detach_hooks(void)652 ipfw_detach_hooks(void)
653 {
654
655 ipfw_unhook(AF_INET);
656 #ifdef INET6
657 ipfw_unhook(AF_INET6);
658 #endif
659 ipfw_unhook(AF_LINK);
660 }
661
662 int
ipfw_chg_hook(SYSCTL_HANDLER_ARGS)663 ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
664 {
665 int newval;
666 int error;
667 int af;
668
669 if (arg1 == &V_fw_enable)
670 af = AF_INET;
671 #ifdef INET6
672 else if (arg1 == &V_fw6_enable)
673 af = AF_INET6;
674 #endif
675 else if (arg1 == &V_fwlink_enable)
676 af = AF_LINK;
677 else
678 return (EINVAL);
679
680 newval = *(int *)arg1;
681 /* Handle sysctl change */
682 error = sysctl_handle_int(oidp, &newval, 0, req);
683
684 if (error)
685 return (error);
686
687 /* Formalize new value */
688 newval = (newval) ? 1 : 0;
689
690 if (*(int *)arg1 == newval)
691 return (0);
692
693 error = ipfw_link(af, newval == 0 ? true : false);
694 if (error)
695 return (error);
696 *(int *)arg1 = newval;
697
698 return (0);
699 }
700 /* end of file */
701