xref: /freebsd/sys/netpfil/ipfw/ip_fw_pfil.c (revision 4543ef516683042d46f3bd3bb8a4f3f746e00499)
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
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 (PFIL_PASS);
141 		args.flags |= IPFW_ARGS_REF;
142 	}
143 
144 	args.m = *m0;
145 	args.ifp = ifp;
146 	args.inp = inp;
147 	args.rule.pkt_mark = 0;
148 
149 	ipfw = ipfw_chk(&args);
150 	*m0 = args.m;
151 
152 	KASSERT(*m0 != NULL || ipfw == IP_FW_DENY ||
153 	    ipfw == IP_FW_NAT64, ("%s: m0 is NULL", __func__));
154 
155 	ret = PFIL_PASS;
156 	switch (ipfw) {
157 	case IP_FW_PASS:
158 		/* next_hop may be set by ipfw_chk */
159 		if ((args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR |
160 		    IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) == 0)
161 			break;
162 #if (!defined(INET6) && !defined(INET))
163 		ret = PFIL_DROPPED;
164 #else
165 	    {
166 		void *psa;
167 		size_t len;
168 #ifdef INET
169 		if (args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR)) {
170 			MPASS((args.flags & (IPFW_ARGS_NH4 |
171 			    IPFW_ARGS_NH4PTR)) != (IPFW_ARGS_NH4 |
172 			    IPFW_ARGS_NH4PTR));
173 			MPASS((args.flags & (IPFW_ARGS_NH6 |
174 			    IPFW_ARGS_NH6PTR)) == 0);
175 			len = sizeof(struct sockaddr_in);
176 			psa = (args.flags & IPFW_ARGS_NH4) ?
177 			    &args.hopstore : args.next_hop;
178 			if (in_localip(satosin(psa)->sin_addr))
179 				(*m0)->m_flags |= M_FASTFWD_OURS;
180 			(*m0)->m_flags |= M_IP_NEXTHOP;
181 		}
182 #endif /* INET */
183 #ifdef INET6
184 		if (args.flags & (IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) {
185 			MPASS((args.flags & (IPFW_ARGS_NH6 |
186 			    IPFW_ARGS_NH6PTR)) != (IPFW_ARGS_NH6 |
187 			    IPFW_ARGS_NH6PTR));
188 			MPASS((args.flags & (IPFW_ARGS_NH4 |
189 			    IPFW_ARGS_NH4PTR)) == 0);
190 			len = sizeof(struct sockaddr_in6);
191 			psa = args.next_hop6;
192 			(*m0)->m_flags |= M_IP6_NEXTHOP;
193 		}
194 #endif /* INET6 */
195 		/*
196 		 * Incoming packets should not be tagged so we do not
197 		 * m_tag_find. Outgoing packets may be tagged, so we
198 		 * reuse the tag if present.
199 		 */
200 		tag = (flags & PFIL_IN) ? NULL :
201 			m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
202 		if (tag != NULL) {
203 			m_tag_unlink(*m0, tag);
204 		} else {
205 			tag = m_tag_get(PACKET_TAG_IPFORWARD, len,
206 			    M_NOWAIT);
207 			if (tag == NULL) {
208 				ret = PFIL_DROPPED;
209 				break;
210 			}
211 		}
212 		if ((args.flags & IPFW_ARGS_NH6) == 0)
213 			bcopy(psa, tag + 1, len);
214 		m_tag_prepend(*m0, tag);
215 		ret = PFIL_PASS;
216 #ifdef INET6
217 		/* IPv6 next hop needs additional handling */
218 		if (args.flags & (IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) {
219 			struct sockaddr_in6 *sa6;
220 
221 			sa6 = satosin6(tag + 1);
222 			if (args.flags & IPFW_ARGS_NH6) {
223 				sa6->sin6_family = AF_INET6;
224 				sa6->sin6_len = sizeof(*sa6);
225 				sa6->sin6_addr = args.hopstore6.sin6_addr;
226 				sa6->sin6_port = args.hopstore6.sin6_port;
227 				sa6->sin6_scope_id =
228 				    args.hopstore6.sin6_scope_id;
229 			}
230 			/*
231 			 * If nh6 address is link-local we should convert
232 			 * it to kernel internal form before doing any
233 			 * comparisons.
234 			 */
235 			if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) {
236 				ret = PFIL_DROPPED;
237 				break;
238 			}
239 			if (in6_localip(&sa6->sin6_addr))
240 				(*m0)->m_flags |= M_FASTFWD_OURS;
241 		}
242 #endif /* INET6 */
243 	    }
244 #endif /* INET || INET6 */
245 		break;
246 
247 	case IP_FW_DENY:
248 		ret = PFIL_DROPPED;
249 		break;
250 
251 	case IP_FW_DUMMYNET:
252 		if (ip_dn_io_ptr == NULL) {
253 			ret = PFIL_DROPPED;
254 			break;
255 		}
256 		MPASS(args.flags & IPFW_ARGS_REF);
257 		if (args.flags & (IPFW_ARGS_IP4 | IPFW_ARGS_IP6))
258 			(void )ip_dn_io_ptr(m0, &args);
259 		else {
260 			ret = PFIL_DROPPED;
261 			break;
262 		}
263 		/*
264 		 * XXX should read the return value.
265 		 * dummynet normally eats the packet and sets *m0=NULL
266 		 * unless the packet can be sent immediately. In this
267 		 * case args is updated and we should re-run the
268 		 * check without clearing args.
269 		 */
270 		if (*m0 != NULL)
271 			goto again;
272 		ret = PFIL_CONSUMED;
273 		break;
274 
275 	case IP_FW_TEE:
276 	case IP_FW_DIVERT:
277 		if (ip_divert_ptr == NULL) {
278 			ret = PFIL_DROPPED;
279 			break;
280 		}
281 		MPASS(args.flags & IPFW_ARGS_REF);
282 		(void )ipfw_divert(m0, &args, ipfw == IP_FW_TEE);
283 		/* continue processing for the original packet (tee). */
284 		if (*m0)
285 			goto again;
286 		ret = PFIL_CONSUMED;
287 		break;
288 
289 	case IP_FW_NGTEE:
290 	case IP_FW_NETGRAPH:
291 		if (ng_ipfw_input_p == NULL) {
292 			ret = PFIL_DROPPED;
293 			break;
294 		}
295 		MPASS(args.flags & IPFW_ARGS_REF);
296 		(void )ng_ipfw_input_p(m0, &args, ipfw == IP_FW_NGTEE);
297 		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
298 			goto again;	/* continue with packet */
299 		ret = PFIL_CONSUMED;
300 		break;
301 
302 	case IP_FW_NAT:
303 		/* honor one-pass in case of successful nat */
304 		if (V_fw_one_pass)
305 			break;
306 		goto again;
307 
308 	case IP_FW_REASS:
309 		goto again;		/* continue with packet */
310 
311 	case IP_FW_NAT64:
312 		ret = PFIL_CONSUMED;
313 		break;
314 
315 	default:
316 		KASSERT(0, ("%s: unknown retval", __func__));
317 	}
318 
319 	if (ret != PFIL_PASS) {
320 		if (*m0)
321 			FREE_PKT(*m0);
322 		*m0 = NULL;
323 	}
324 
325 	return (ret);
326 }
327 
328 /*
329  * ipfw processing for ethernet packets (in and out), mbuf version.
330  */
331 static pfil_return_t
332 ipfw_check_frame_mbuf(struct mbuf **m0, struct ifnet *ifp, const int flags,
333     void *ruleset __unused, struct inpcb *inp)
334 {
335 	struct ip_fw_args args = {
336 		.flags = IPFW_ARGS_ETHER |
337 		    ((flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT),
338 		.ifp = ifp,
339 		.inp = inp,
340 	};
341 	struct m_tag *mtag;
342 	pfil_return_t ret;
343 	int ipfw;
344 
345 again:
346 	/*
347 	 * Fetch start point from rule, if any.
348 	 * Remove the tag if present.
349 	 */
350 	mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
351 	if (mtag != NULL) {
352 		args.rule = *((struct ipfw_rule_ref *)(mtag+1));
353 		m_tag_delete(*m0, mtag);
354 		if (args.rule.info & IPFW_ONEPASS)
355 			return (PFIL_PASS);
356 		args.flags |= IPFW_ARGS_REF;
357 	}
358 	args.m = *m0;
359 
360 	ipfw = ipfw_chk(&args);
361 	*m0 = args.m;
362 
363 	ret = PFIL_PASS;
364 	switch (ipfw) {
365 	case IP_FW_PASS:
366 		break;
367 
368 	case IP_FW_DENY:
369 		ret = PFIL_DROPPED;
370 		break;
371 
372 	case IP_FW_DUMMYNET:
373 		if (ip_dn_io_ptr == NULL) {
374 			ret = PFIL_DROPPED;
375 			break;
376 		}
377 		MPASS(args.flags & IPFW_ARGS_REF);
378 		ip_dn_io_ptr(m0, &args);
379 		return (PFIL_CONSUMED);
380 
381 	case IP_FW_NGTEE:
382 	case IP_FW_NETGRAPH:
383 		if (ng_ipfw_input_p == NULL) {
384 			ret = PFIL_DROPPED;
385 			break;
386 		}
387 		MPASS(args.flags & IPFW_ARGS_REF);
388 		(void )ng_ipfw_input_p(m0, &args, ipfw == IP_FW_NGTEE);
389 		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
390 			goto again;	/* continue with packet */
391 		ret = PFIL_CONSUMED;
392 		break;
393 
394 	default:
395 		KASSERT(0, ("%s: unknown retval", __func__));
396 	}
397 
398 	if (ret != PFIL_PASS) {
399 		if (*m0)
400 			FREE_PKT(*m0);
401 		*m0 = NULL;
402 	}
403 
404 	return (ret);
405 }
406 
407 /*
408  * ipfw processing for ethernet packets (in and out), memory pointer version,
409  * two in/out accessors.
410  */
411 static pfil_return_t
412 ipfw_check_frame_mem(void *mem, u_int len, int flags, struct ifnet *ifp,
413     void *ruleset __unused, struct mbuf **m)
414 {
415 	struct ip_fw_args args = {
416 		.flags = len | IPFW_ARGS_ETHER |
417 		    ((flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT),
418 		.ifp = ifp,
419 		.mem = mem,
420 	};
421 	pfil_return_t ret;
422 	int ipfw;
423 
424 	*m = NULL;
425 again:
426 	ipfw = ipfw_chk(&args);
427 
428 	ret = PFIL_PASS;
429 	switch (ipfw) {
430 	case IP_FW_PASS:
431 		break;
432 
433 	case IP_FW_DENY:
434 		ret = PFIL_DROPPED;
435 		break;
436 
437 	case IP_FW_DUMMYNET:
438 		if (ip_dn_io_ptr == NULL) {
439 			ret = PFIL_DROPPED;
440 			break;
441 		}
442 		*m = m_devget(mem, len, 0, ifp, NULL);
443 		if (*m == NULL) {
444 			ret = PFIL_DROPPED;
445 			break;
446 		}
447 		MPASS(args.flags & IPFW_ARGS_REF);
448 		ip_dn_io_ptr(m, &args);
449 		return (PFIL_CONSUMED);
450 
451 	case IP_FW_NGTEE:
452 	case IP_FW_NETGRAPH:
453 		if (ng_ipfw_input_p == NULL) {
454 			ret = PFIL_DROPPED;
455 			break;
456 		}
457 		*m = m_devget(mem, len, 0, ifp, NULL);
458 		if (*m == NULL) {
459 			ret = PFIL_DROPPED;
460 			break;
461 		}
462 		MPASS(args.flags & IPFW_ARGS_REF);
463 		(void )ng_ipfw_input_p(m, &args, ipfw == IP_FW_NGTEE);
464 		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
465 			goto again;	/* continue with packet */
466 		ret = PFIL_CONSUMED;
467 		break;
468 
469 	default:
470 		KASSERT(0, ("%s: unknown retval", __func__));
471 	}
472 
473 	if (*m != NULL && ret == PFIL_PASS)
474 		ret = PFIL_REALLOCED;
475 
476 	return (ret);
477 }
478 
479 /* do the divert, return 1 on error 0 on success */
480 static int
481 ipfw_divert(struct mbuf **m0, struct ip_fw_args *args, bool tee)
482 {
483 	/*
484 	 * ipfw_chk() has already tagged the packet with the divert tag.
485 	 * If tee is set, copy packet and return original.
486 	 * If not tee, consume packet and send it to divert socket.
487 	 */
488 	struct mbuf *clone;
489 	struct ip *ip = mtod(*m0, struct ip *);
490 	struct m_tag *tag;
491 
492 	/* Cloning needed for tee? */
493 	if (tee == false) {
494 		clone = *m0;	/* use the original mbuf */
495 		*m0 = NULL;
496 	} else {
497 		clone = m_dup(*m0, M_NOWAIT);
498 		/* If we cannot duplicate the mbuf, we sacrifice the divert
499 		 * chain and continue with the tee-ed packet.
500 		 */
501 		if (clone == NULL)
502 			return 1;
503 	}
504 
505 	/*
506 	 * Divert listeners can normally handle non-fragmented packets,
507 	 * but we can only reass in the non-tee case.
508 	 * This means that listeners on a tee rule may get fragments,
509 	 * and have to live with that.
510 	 * Note that we now have the 'reass' ipfw option so if we care
511 	 * we can do it before a 'tee'.
512 	 */
513 	if (tee == false) switch (ip->ip_v) {
514 	case IPVERSION:
515 	    if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) {
516 		int hlen;
517 		struct mbuf *reass;
518 
519 		reass = ip_reass(clone); /* Reassemble packet. */
520 		if (reass == NULL)
521 			return 0; /* not an error */
522 		/* if reass = NULL then it was consumed by ip_reass */
523 		/*
524 		 * IP header checksum fixup after reassembly and leave header
525 		 * in network byte order.
526 		 */
527 		ip = mtod(reass, struct ip *);
528 		hlen = ip->ip_hl << 2;
529 		ip->ip_sum = 0;
530 		if (hlen == sizeof(struct ip))
531 			ip->ip_sum = in_cksum_hdr(ip);
532 		else
533 			ip->ip_sum = in_cksum(reass, hlen);
534 		clone = reass;
535 	    }
536 	    break;
537 #ifdef INET6
538 	case IPV6_VERSION >> 4:
539 	    {
540 	    struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *);
541 
542 		if (ip6->ip6_nxt == IPPROTO_FRAGMENT) {
543 			int nxt, off;
544 
545 			off = sizeof(struct ip6_hdr);
546 			nxt = frag6_input(&clone, &off, 0);
547 			if (nxt == IPPROTO_DONE)
548 				return (0);
549 		}
550 		break;
551 	    }
552 #endif
553 	}
554 
555 	/* attach a tag to the packet with the reinject info */
556 	tag = m_tag_alloc(MTAG_IPFW_RULE, 0,
557 		    sizeof(struct ipfw_rule_ref), M_NOWAIT);
558 	if (tag == NULL) {
559 		FREE_PKT(clone);
560 		return 1;
561 	}
562 	*((struct ipfw_rule_ref *)(tag+1)) = args->rule;
563 	m_tag_prepend(clone, tag);
564 
565 	/* Do the dirty job... */
566 	ip_divert_ptr(clone, args->flags & IPFW_ARGS_IN);
567 	return 0;
568 }
569 
570 /*
571  * attach or detach hooks for a given protocol family
572  */
573 VNET_DEFINE_STATIC(pfil_hook_t, ipfw_inet_hook);
574 #define	V_ipfw_inet_hook	VNET(ipfw_inet_hook)
575 #ifdef INET6
576 VNET_DEFINE_STATIC(pfil_hook_t, ipfw_inet6_hook);
577 #define	V_ipfw_inet6_hook	VNET(ipfw_inet6_hook)
578 #endif
579 VNET_DEFINE_STATIC(pfil_hook_t, ipfw_link_hook);
580 #define	V_ipfw_link_hook	VNET(ipfw_link_hook)
581 
582 static void
583 ipfw_hook(int pf)
584 {
585 	struct pfil_hook_args pha = {
586 		.pa_version = PFIL_VERSION,
587 		.pa_flags = PFIL_IN | PFIL_OUT,
588 		.pa_modname = "ipfw",
589 	};
590 	pfil_hook_t *h;
591 
592 	switch (pf) {
593 	case AF_INET:
594 		pha.pa_mbuf_chk = ipfw_check_packet;
595 		pha.pa_type = PFIL_TYPE_IP4;
596 		pha.pa_rulname = "default";
597 		h = &V_ipfw_inet_hook;
598 		break;
599 #ifdef INET6
600 	case AF_INET6:
601 		pha.pa_mbuf_chk = ipfw_check_packet;
602 		pha.pa_type = PFIL_TYPE_IP6;
603 		pha.pa_rulname = "default6";
604 		h = &V_ipfw_inet6_hook;
605 		break;
606 #endif
607 	case AF_LINK:
608 		pha.pa_mbuf_chk = ipfw_check_frame_mbuf;
609 		pha.pa_mem_chk = ipfw_check_frame_mem;
610 		pha.pa_type = PFIL_TYPE_ETHERNET;
611 		pha.pa_rulname = "default-link";
612 		h = &V_ipfw_link_hook;
613 		break;
614 	}
615 
616 	*h = pfil_add_hook(&pha);
617 }
618 
619 static void
620 ipfw_unhook(int pf)
621 {
622 
623 	switch (pf) {
624 	case AF_INET:
625 		pfil_remove_hook(V_ipfw_inet_hook);
626 		break;
627 #ifdef INET6
628 	case AF_INET6:
629 		pfil_remove_hook(V_ipfw_inet6_hook);
630 		break;
631 #endif
632 	case AF_LINK:
633 		pfil_remove_hook(V_ipfw_link_hook);
634 		break;
635 	}
636 }
637 
638 static int
639 ipfw_link(int pf, bool unlink)
640 {
641 	struct pfil_link_args pla;
642 
643 	pla.pa_version = PFIL_VERSION;
644 	pla.pa_flags = PFIL_IN | PFIL_OUT | PFIL_HEADPTR | PFIL_HOOKPTR;
645 	if (unlink)
646 		pla.pa_flags |= PFIL_UNLINK;
647 
648 	switch (pf) {
649 	case AF_INET:
650 		pla.pa_head = V_inet_pfil_head;
651 		pla.pa_hook = V_ipfw_inet_hook;
652 		break;
653 #ifdef INET6
654 	case AF_INET6:
655 		pla.pa_head = V_inet6_pfil_head;
656 		pla.pa_hook = V_ipfw_inet6_hook;
657 		break;
658 #endif
659 	case AF_LINK:
660 		pla.pa_head = V_link_pfil_head;
661 		pla.pa_hook = V_ipfw_link_hook;
662 		break;
663 	}
664 
665 	return (pfil_link(&pla));
666 }
667 
668 int
669 ipfw_attach_hooks(void)
670 {
671 	int error = 0;
672 
673 	ipfw_hook(AF_INET);
674 	TUNABLE_INT_FETCH("net.inet.ip.fw.enable", &V_fw_enable);
675 	if (V_fw_enable && (error = ipfw_link(AF_INET, false)) != 0)
676                 printf("ipfw_hook() error\n");
677 #ifdef INET6
678 	ipfw_hook(AF_INET6);
679 	TUNABLE_INT_FETCH("net.inet6.ip6.fw.enable", &V_fw6_enable);
680 	if (V_fw6_enable && (error = ipfw_link(AF_INET6, false)) != 0)
681                 printf("ipfw6_hook() error\n");
682 #endif
683 	ipfw_hook(AF_LINK);
684 	TUNABLE_INT_FETCH("net.link.ether.ipfw", &V_fwlink_enable);
685 	if (V_fwlink_enable && (error = ipfw_link(AF_LINK, false)) != 0)
686                 printf("ipfw_link_hook() error\n");
687 
688 	return (error);
689 }
690 
691 void
692 ipfw_detach_hooks(void)
693 {
694 
695 	ipfw_unhook(AF_INET);
696 #ifdef INET6
697 	ipfw_unhook(AF_INET6);
698 #endif
699 	ipfw_unhook(AF_LINK);
700 }
701 
702 int
703 ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
704 {
705 	int newval;
706 	int error;
707 	int af;
708 
709 	if (arg1 == &V_fw_enable)
710 		af = AF_INET;
711 #ifdef INET6
712 	else if (arg1 == &V_fw6_enable)
713 		af = AF_INET6;
714 #endif
715 	else if (arg1 == &V_fwlink_enable)
716 		af = AF_LINK;
717 	else
718 		return (EINVAL);
719 
720 	newval = *(int *)arg1;
721 	/* Handle sysctl change */
722 	error = sysctl_handle_int(oidp, &newval, 0, req);
723 
724 	if (error)
725 		return (error);
726 
727 	/* Formalize new value */
728 	newval = (newval) ? 1 : 0;
729 
730 	if (*(int *)arg1 == newval)
731 		return (0);
732 
733 	error = ipfw_link(af, newval == 0 ? true : false);
734 	if (error)
735 		return (error);
736 	*(int *)arg1 = newval;
737 
738 	return (0);
739 }
740 /* end of file */
741