ip_fw_pfil.c revision 196423
1139823Simp/*-
2133920Sandre * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG
3133920Sandre * All rights reserved.
4133920Sandre *
5133920Sandre * Redistribution and use in source and binary forms, with or without
6133920Sandre * modification, are permitted provided that the following conditions
7133920Sandre * are met:
8133920Sandre * 1. Redistributions of source code must retain the above copyright
9133920Sandre *    notice, this list of conditions and the following disclaimer.
10133920Sandre * 2. Redistributions in binary form must reproduce the above copyright
11133920Sandre *    notice, this list of conditions and the following disclaimer in the
12133920Sandre *    documentation and/or other materials provided with the distribution.
13133920Sandre *
14133920Sandre * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15133920Sandre * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16133920Sandre * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17133920Sandre * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18133920Sandre * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19133920Sandre * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20133920Sandre * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21133920Sandre * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22133920Sandre * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23133920Sandre * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24133920Sandre * SUCH DAMAGE.
25133920Sandre */
26133920Sandre
27172467Ssilby#include <sys/cdefs.h>
28172467Ssilby__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_pfil.c 196423 2009-08-21 11:20:10Z julian $");
29172467Ssilby
30134346Sru#if !defined(KLD_MODULE)
31133920Sandre#include "opt_ipfw.h"
32133920Sandre#include "opt_ipdn.h"
33133920Sandre#include "opt_inet.h"
34133920Sandre#ifndef INET
35133920Sandre#error IPFIREWALL requires INET.
36133920Sandre#endif /* INET */
37134383Sandre#endif /* KLD_MODULE */
38152928Sume#include "opt_inet6.h"
39133920Sandre
40133920Sandre#include <sys/param.h>
41133920Sandre#include <sys/systm.h>
42133920Sandre#include <sys/malloc.h>
43133920Sandre#include <sys/mbuf.h>
44133920Sandre#include <sys/module.h>
45133920Sandre#include <sys/kernel.h>
46185895Szec#include <sys/lock.h>
47185895Szec#include <sys/rwlock.h>
48133920Sandre#include <sys/socket.h>
49133920Sandre#include <sys/socketvar.h>
50133920Sandre#include <sys/sysctl.h>
51133920Sandre#include <sys/ucred.h>
52133920Sandre
53133920Sandre#include <net/if.h>
54133920Sandre#include <net/route.h>
55133920Sandre#include <net/pfil.h>
56196423Sjulian#include <net/vnet.h>
57133920Sandre
58133920Sandre#include <netinet/in.h>
59133920Sandre#include <netinet/in_systm.h>
60133920Sandre#include <netinet/ip.h>
61133920Sandre#include <netinet/ip_var.h>
62133920Sandre#include <netinet/ip_fw.h>
63133920Sandre#include <netinet/ip_divert.h>
64133920Sandre#include <netinet/ip_dummynet.h>
65133920Sandre
66141351Sglebius#include <netgraph/ng_ipfw.h>
67141351Sglebius
68133920Sandre#include <machine/in_cksum.h>
69133920Sandre
70195699SrwatsonVNET_DEFINE(int, fw_enable) = 1;
71158470Smlaier#ifdef INET6
72195699SrwatsonVNET_DEFINE(int, fw6_enable) = 1;
73158470Smlaier#endif
74133920Sandre
75158470Smlaierint ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
76158470Smlaier
77136714Sandre/* Divert hooks. */
78136714Sandreip_divert_packet_t *ip_divert_ptr = NULL;
79136714Sandre
80141351Sglebius/* ng_ipfw hooks. */
81141351Sglebiusng_ipfw_input_t *ng_ipfw_input_p = NULL;
82141351Sglebius
83136714Sandre/* Forward declarations. */
84136714Sandrestatic int	ipfw_divert(struct mbuf **, int, int);
85133920Sandre#define	DIV_DIR_IN	1
86133920Sandre#define	DIV_DIR_OUT	0
87133920Sandre
88133920Sandreint
89135920Smlaieripfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
90135920Smlaier    struct inpcb *inp)
91133920Sandre{
92133920Sandre	struct ip_fw_args args;
93141351Sglebius	struct ng_ipfw_tag *ng_tag;
94133920Sandre	struct m_tag *dn_tag;
95133920Sandre	int ipfw = 0;
96133920Sandre	int divert;
97140224Sglebius	int tee;
98133920Sandre#ifdef IPFIREWALL_FORWARD
99133920Sandre	struct m_tag *fwd_tag;
100133920Sandre#endif
101133920Sandre
102133920Sandre	KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
103133920Sandre
104133920Sandre	bzero(&args, sizeof(args));
105133920Sandre
106173399Soleg	ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
107173399Soleg	    NULL);
108173399Soleg	if (ng_tag != NULL) {
109173399Soleg		KASSERT(ng_tag->dir == NG_IPFW_IN,
110173399Soleg		    ("ng_ipfw tag with wrong direction"));
111173399Soleg		args.rule = ng_tag->rule;
112193859Soleg		args.rule_id = ng_tag->rule_id;
113193859Soleg		args.chain_id = ng_tag->chain_id;
114173399Soleg		m_tag_delete(*m0, (struct m_tag *)ng_tag);
115173399Soleg	}
116173399Soleg
117173399Solegagain:
118138652Sglebius	dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
119138652Sglebius	if (dn_tag != NULL){
120133920Sandre		struct dn_pkt_tag *dt;
121133920Sandre
122133920Sandre		dt = (struct dn_pkt_tag *)(dn_tag+1);
123133920Sandre		args.rule = dt->rule;
124193859Soleg		args.rule_id = dt->rule_id;
125193859Soleg		args.chain_id = dt->chain_id;
126133920Sandre
127133920Sandre		m_tag_delete(*m0, dn_tag);
128133920Sandre	}
129133920Sandre
130133920Sandre	args.m = *m0;
131135920Smlaier	args.inp = inp;
132140224Sglebius	tee = 0;
133133920Sandre
134191570Soleg	if (V_fw_one_pass == 0 || args.rule == NULL) {
135191570Soleg		ipfw = ipfw_chk(&args);
136191570Soleg		*m0 = args.m;
137191570Soleg	} else
138191570Soleg		ipfw = IP_FW_PASS;
139191570Soleg
140140224Sglebius	KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
141140224Sglebius	    __func__));
142133920Sandre
143140224Sglebius	switch (ipfw) {
144140224Sglebius	case IP_FW_PASS:
145140224Sglebius		if (args.next_hop == NULL)
146140224Sglebius			goto pass;
147133920Sandre
148133920Sandre#ifdef IPFIREWALL_FORWARD
149133920Sandre		fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
150133920Sandre				sizeof(struct sockaddr_in), M_NOWAIT);
151133920Sandre		if (fwd_tag == NULL)
152133920Sandre			goto drop;
153133920Sandre		bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
154133920Sandre		m_tag_prepend(*m0, fwd_tag);
155133920Sandre
156133920Sandre		if (in_localip(args.next_hop->sin_addr))
157133920Sandre			(*m0)->m_flags |= M_FASTFWD_OURS;
158133920Sandre		goto pass;
159133920Sandre#endif
160140224Sglebius		break;			/* not reached */
161133920Sandre
162140224Sglebius	case IP_FW_DENY:
163140224Sglebius		goto drop;
164140224Sglebius		break;			/* not reached */
165140224Sglebius
166140224Sglebius	case IP_FW_DUMMYNET:
167193502Sluigi		if (ip_dn_io_ptr == NULL)
168140224Sglebius			goto drop;
169145246Sbrooks		if (mtod(*m0, struct ip *)->ip_v == 4)
170173399Soleg			ip_dn_io_ptr(m0, DN_TO_IP_IN, &args);
171145246Sbrooks		else if (mtod(*m0, struct ip *)->ip_v == 6)
172173399Soleg			ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args);
173173399Soleg		if (*m0 != NULL)
174173399Soleg			goto again;
175140224Sglebius		return 0;		/* packet consumed */
176140224Sglebius
177140224Sglebius	case IP_FW_TEE:
178140224Sglebius		tee = 1;
179140224Sglebius		/* fall through */
180140224Sglebius
181140224Sglebius	case IP_FW_DIVERT:
182140224Sglebius		divert = ipfw_divert(m0, DIV_DIR_IN, tee);
183140224Sglebius		if (divert) {
184140224Sglebius			*m0 = NULL;
185140224Sglebius			return 0;	/* packet consumed */
186144712Sglebius		} else {
187144712Sglebius			args.rule = NULL;
188140224Sglebius			goto again;	/* continue with packet */
189144712Sglebius		}
190140224Sglebius
191141351Sglebius	case IP_FW_NGTEE:
192141351Sglebius		if (!NG_IPFW_LOADED)
193141351Sglebius			goto drop;
194141351Sglebius		(void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1);
195141351Sglebius		goto again;		/* continue with packet */
196141351Sglebius
197141351Sglebius	case IP_FW_NETGRAPH:
198141351Sglebius		if (!NG_IPFW_LOADED)
199141351Sglebius			goto drop;
200141351Sglebius		return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
201165648Spiso
202165648Spiso	case IP_FW_NAT:
203165648Spiso		goto again;		/* continue with packet */
204141351Sglebius
205190633Spiso	case IP_FW_REASS:
206190633Spiso		goto again;
207190633Spiso
208140224Sglebius	default:
209140224Sglebius		KASSERT(0, ("%s: unknown retval", __func__));
210140224Sglebius	}
211140224Sglebius
212133920Sandredrop:
213133920Sandre	if (*m0)
214133920Sandre		m_freem(*m0);
215133920Sandre	*m0 = NULL;
216133920Sandre	return (EACCES);
217133920Sandrepass:
218133920Sandre	return 0;	/* not filtered */
219133920Sandre}
220133920Sandre
221133920Sandreint
222135920Smlaieripfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
223135920Smlaier    struct inpcb *inp)
224133920Sandre{
225133920Sandre	struct ip_fw_args args;
226141351Sglebius	struct ng_ipfw_tag *ng_tag;
227133920Sandre	struct m_tag *dn_tag;
228133920Sandre	int ipfw = 0;
229133920Sandre	int divert;
230140224Sglebius	int tee;
231133920Sandre#ifdef IPFIREWALL_FORWARD
232133920Sandre	struct m_tag *fwd_tag;
233133920Sandre#endif
234133920Sandre
235133920Sandre	KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
236133920Sandre
237133920Sandre	bzero(&args, sizeof(args));
238133920Sandre
239173399Soleg	ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
240173399Soleg	    NULL);
241173399Soleg	if (ng_tag != NULL) {
242173399Soleg		KASSERT(ng_tag->dir == NG_IPFW_OUT,
243173399Soleg		    ("ng_ipfw tag with wrong direction"));
244173399Soleg		args.rule = ng_tag->rule;
245193859Soleg		args.rule_id = ng_tag->rule_id;
246193859Soleg		args.chain_id = ng_tag->chain_id;
247173399Soleg		m_tag_delete(*m0, (struct m_tag *)ng_tag);
248173399Soleg	}
249173399Soleg
250173399Solegagain:
251138652Sglebius	dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
252138652Sglebius	if (dn_tag != NULL) {
253133920Sandre		struct dn_pkt_tag *dt;
254133920Sandre
255133920Sandre		dt = (struct dn_pkt_tag *)(dn_tag+1);
256133920Sandre		args.rule = dt->rule;
257193859Soleg		args.rule_id = dt->rule_id;
258193859Soleg		args.chain_id = dt->chain_id;
259133920Sandre
260133920Sandre		m_tag_delete(*m0, dn_tag);
261133920Sandre	}
262133920Sandre
263133920Sandre	args.m = *m0;
264133920Sandre	args.oif = ifp;
265135920Smlaier	args.inp = inp;
266140224Sglebius	tee = 0;
267133920Sandre
268191570Soleg	if (V_fw_one_pass == 0 || args.rule == NULL) {
269191570Soleg		ipfw = ipfw_chk(&args);
270191570Soleg		*m0 = args.m;
271191570Soleg	} else
272191570Soleg		ipfw = IP_FW_PASS;
273191570Soleg
274140224Sglebius	KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
275140224Sglebius	    __func__));
276133920Sandre
277140224Sglebius	switch (ipfw) {
278140224Sglebius	case IP_FW_PASS:
279140224Sglebius                if (args.next_hop == NULL)
280140224Sglebius                        goto pass;
281133920Sandre#ifdef IPFIREWALL_FORWARD
282133920Sandre		/* Overwrite existing tag. */
283133920Sandre		fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
284135167Sandre		if (fwd_tag == NULL) {
285133920Sandre			fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
286133920Sandre				sizeof(struct sockaddr_in), M_NOWAIT);
287135167Sandre			if (fwd_tag == NULL)
288135167Sandre				goto drop;
289135167Sandre		} else
290135167Sandre			m_tag_unlink(*m0, fwd_tag);
291133920Sandre		bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
292133920Sandre		m_tag_prepend(*m0, fwd_tag);
293133920Sandre
294133920Sandre		if (in_localip(args.next_hop->sin_addr))
295133920Sandre			(*m0)->m_flags |= M_FASTFWD_OURS;
296133920Sandre		goto pass;
297133920Sandre#endif
298140224Sglebius		break;			/* not reached */
299133920Sandre
300140224Sglebius	case IP_FW_DENY:
301140224Sglebius		goto drop;
302140224Sglebius		break;  		/* not reached */
303140224Sglebius
304140224Sglebius	case IP_FW_DUMMYNET:
305193502Sluigi		if (ip_dn_io_ptr == NULL)
306140224Sglebius			break;
307145246Sbrooks		if (mtod(*m0, struct ip *)->ip_v == 4)
308173399Soleg			ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args);
309145246Sbrooks		else if (mtod(*m0, struct ip *)->ip_v == 6)
310173399Soleg			ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args);
311173399Soleg		if (*m0 != NULL)
312173399Soleg			goto again;
313140224Sglebius		return 0;		/* packet consumed */
314140224Sglebius
315140224Sglebius		break;
316140224Sglebius
317140224Sglebius	case IP_FW_TEE:
318140224Sglebius		tee = 1;
319140224Sglebius		/* fall through */
320140224Sglebius
321140224Sglebius	case IP_FW_DIVERT:
322140224Sglebius		divert = ipfw_divert(m0, DIV_DIR_OUT, tee);
323140224Sglebius		if (divert) {
324140224Sglebius			*m0 = NULL;
325140224Sglebius			return 0;	/* packet consumed */
326144712Sglebius		} else {
327144712Sglebius			args.rule = NULL;
328140224Sglebius			goto again;	/* continue with packet */
329144712Sglebius		}
330140224Sglebius
331141351Sglebius	case IP_FW_NGTEE:
332141351Sglebius		if (!NG_IPFW_LOADED)
333141351Sglebius			goto drop;
334141351Sglebius		(void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1);
335141351Sglebius		goto again;		/* continue with packet */
336141351Sglebius
337141351Sglebius	case IP_FW_NETGRAPH:
338141351Sglebius		if (!NG_IPFW_LOADED)
339141351Sglebius			goto drop;
340141351Sglebius		return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
341141351Sglebius
342165648Spiso	case IP_FW_NAT:
343165648Spiso		goto again;		/* continue with packet */
344165648Spiso
345190633Spiso	case IP_FW_REASS:
346190633Spiso		goto again;
347190633Spiso
348140224Sglebius	default:
349140224Sglebius		KASSERT(0, ("%s: unknown retval", __func__));
350140224Sglebius	}
351140224Sglebius
352133920Sandredrop:
353133920Sandre	if (*m0)
354133920Sandre		m_freem(*m0);
355133920Sandre	*m0 = NULL;
356133920Sandre	return (EACCES);
357133920Sandrepass:
358133920Sandre	return 0;	/* not filtered */
359133920Sandre}
360133920Sandre
361133920Sandrestatic int
362133920Sandreipfw_divert(struct mbuf **m, int incoming, int tee)
363133920Sandre{
364133920Sandre	/*
365135154Sandre	 * ipfw_chk() has already tagged the packet with the divert tag.
366133920Sandre	 * If tee is set, copy packet and return original.
367133920Sandre	 * If not tee, consume packet and send it to divert socket.
368133920Sandre	 */
369133920Sandre	struct mbuf *clone, *reass;
370133920Sandre	struct ip *ip;
371133920Sandre	int hlen;
372133920Sandre
373133920Sandre	reass = NULL;
374133920Sandre
375136714Sandre	/* Is divert module loaded? */
376136714Sandre	if (ip_divert_ptr == NULL)
377136714Sandre		goto nodivert;
378136714Sandre
379133920Sandre	/* Cloning needed for tee? */
380133920Sandre	if (tee)
381133920Sandre		clone = m_dup(*m, M_DONTWAIT);
382133920Sandre	else
383133920Sandre		clone = *m;
384133920Sandre
385133920Sandre	/* In case m_dup was unable to allocate mbufs. */
386133920Sandre	if (clone == NULL)
387133920Sandre		goto teeout;
388133920Sandre
389133920Sandre	/*
390133920Sandre	 * Divert listeners can only handle non-fragmented packets.
391133920Sandre	 * However when tee is set we will *not* de-fragment the packets;
392133920Sandre	 * Doing do would put the reassembly into double-jeopardy.  On top
393133920Sandre	 * of that someone doing a tee will probably want to get the packet
394133920Sandre	 * in its original form.
395133920Sandre	 */
396133920Sandre	ip = mtod(clone, struct ip *);
397133920Sandre	if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
398133920Sandre
399133920Sandre		/* Reassemble packet. */
400133920Sandre		reass = ip_reass(clone);
401133920Sandre
402133920Sandre		/*
403133920Sandre		 * IP header checksum fixup after reassembly and leave header
404133920Sandre		 * in network byte order.
405133920Sandre		 */
406133920Sandre		if (reass != NULL) {
407133920Sandre			ip = mtod(reass, struct ip *);
408133920Sandre			hlen = ip->ip_hl << 2;
409133920Sandre			ip->ip_len = htons(ip->ip_len);
410133920Sandre			ip->ip_off = htons(ip->ip_off);
411133920Sandre			ip->ip_sum = 0;
412133920Sandre			if (hlen == sizeof(struct ip))
413133920Sandre				ip->ip_sum = in_cksum_hdr(ip);
414133920Sandre			else
415133920Sandre				ip->ip_sum = in_cksum(reass, hlen);
416133920Sandre			clone = reass;
417133920Sandre		} else
418133920Sandre			clone = NULL;
419133920Sandre	} else {
420133920Sandre		/* Convert header to network byte order. */
421133920Sandre		ip->ip_len = htons(ip->ip_len);
422133920Sandre		ip->ip_off = htons(ip->ip_off);
423133920Sandre	}
424133920Sandre
425133920Sandre	/* Do the dirty job... */
426136714Sandre	if (clone && ip_divert_ptr != NULL)
427136714Sandre		ip_divert_ptr(clone, incoming);
428133920Sandre
429133920Sandreteeout:
430135154Sandre	/*
431135154Sandre	 * For tee we leave the divert tag attached to original packet.
432135154Sandre	 * It will then continue rule evaluation after the tee rule.
433135154Sandre	 */
434135154Sandre	if (tee)
435135154Sandre		return 0;
436133920Sandre
437133920Sandre	/* Packet diverted and consumed */
438133920Sandre	return 1;
439136714Sandre
440136714Sandrenodivert:
441133920Sandre	m_freem(*m);
442133920Sandre	return 1;
443133920Sandre}
444133920Sandre
445196423Sjulianint
446133920Sandreipfw_hook(void)
447133920Sandre{
448133920Sandre	struct pfil_head *pfh_inet;
449133920Sandre
450133920Sandre	pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
451133920Sandre	if (pfh_inet == NULL)
452133920Sandre		return ENOENT;
453133920Sandre
454186180Srwatson	(void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
455186180Srwatson	    pfh_inet);
456186180Srwatson	(void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
457186180Srwatson	    pfh_inet);
458133920Sandre
459133920Sandre	return 0;
460133920Sandre}
461133920Sandre
462196423Sjulianint
463133920Sandreipfw_unhook(void)
464133920Sandre{
465133920Sandre	struct pfil_head *pfh_inet;
466133920Sandre
467133920Sandre	pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
468133920Sandre	if (pfh_inet == NULL)
469133920Sandre		return ENOENT;
470158470Smlaier
471186180Srwatson	(void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
472186180Srwatson	    pfh_inet);
473186180Srwatson	(void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
474186180Srwatson	    pfh_inet);
475158470Smlaier
476158470Smlaier	return 0;
477158470Smlaier}
478158470Smlaier
479145246Sbrooks#ifdef INET6
480196423Sjulianint
481158470Smlaieripfw6_hook(void)
482158470Smlaier{
483158470Smlaier	struct pfil_head *pfh_inet6;
484158470Smlaier
485145246Sbrooks	pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
486145246Sbrooks	if (pfh_inet6 == NULL)
487145246Sbrooks		return ENOENT;
488133920Sandre
489186180Srwatson	(void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
490186180Srwatson	    pfh_inet6);
491186180Srwatson	(void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
492186180Srwatson	    pfh_inet6);
493158470Smlaier
494158470Smlaier	return 0;
495158470Smlaier}
496158470Smlaier
497196423Sjulianint
498158470Smlaieripfw6_unhook(void)
499158470Smlaier{
500158470Smlaier	struct pfil_head *pfh_inet6;
501158470Smlaier
502158470Smlaier	pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
503158470Smlaier	if (pfh_inet6 == NULL)
504158470Smlaier		return ENOENT;
505158470Smlaier
506186180Srwatson	(void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
507186180Srwatson	    pfh_inet6);
508186180Srwatson	(void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
509186180Srwatson	    pfh_inet6);
510133920Sandre
511133920Sandre	return 0;
512133920Sandre}
513158470Smlaier#endif /* INET6 */
514133920Sandre
515158470Smlaierint
516158470Smlaieripfw_chg_hook(SYSCTL_HANDLER_ARGS)
517158470Smlaier{
518158470Smlaier	int enable = *(int *)arg1;
519158470Smlaier	int error;
520158470Smlaier
521196423Sjulian#ifdef VIMAGE /* Since enabling is global, only let base do it. */
522196423Sjulian	if (! IS_DEFAULT_VNET(curvnet))
523196423Sjulian		return (EPERM);
524196423Sjulian#endif
525158470Smlaier	error = sysctl_handle_int(oidp, &enable, 0, req);
526158470Smlaier	if (error)
527158470Smlaier		return (error);
528158470Smlaier
529158470Smlaier	enable = (enable) ? 1 : 0;
530158470Smlaier
531158470Smlaier	if (enable == *(int *)arg1)
532158470Smlaier		return (0);
533158470Smlaier
534181803Sbz	if (arg1 == &V_fw_enable) {
535158470Smlaier		if (enable)
536158470Smlaier			error = ipfw_hook();
537158470Smlaier		else
538158470Smlaier			error = ipfw_unhook();
539158470Smlaier	}
540158470Smlaier#ifdef INET6
541181803Sbz	if (arg1 == &V_fw6_enable) {
542158470Smlaier		if (enable)
543158470Smlaier			error = ipfw6_hook();
544158470Smlaier		else
545158470Smlaier			error = ipfw6_unhook();
546158470Smlaier	}
547158470Smlaier#endif
548158470Smlaier
549158470Smlaier	if (error)
550158470Smlaier		return (error);
551158470Smlaier
552158470Smlaier	*(int *)arg1 = enable;
553158470Smlaier
554158470Smlaier	return (0);
555158470Smlaier}
556158470Smlaier
557