ip_fw_pfil.c revision 241359
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/netpfil/ipfw/ip_fw_pfil.c 241359 2012-10-08 22:58:28Z glebius $");
29172467Ssilby
30225518Sjhb#include "opt_ipfw.h"
31133920Sandre#include "opt_inet.h"
32225793Sbz#include "opt_inet6.h"
33133920Sandre#ifndef INET
34133920Sandre#error IPFIREWALL requires INET.
35133920Sandre#endif /* INET */
36133920Sandre
37133920Sandre#include <sys/param.h>
38133920Sandre#include <sys/systm.h>
39133920Sandre#include <sys/malloc.h>
40133920Sandre#include <sys/mbuf.h>
41133920Sandre#include <sys/module.h>
42133920Sandre#include <sys/kernel.h>
43185895Szec#include <sys/lock.h>
44185895Szec#include <sys/rwlock.h>
45133920Sandre#include <sys/socket.h>
46133920Sandre#include <sys/sysctl.h>
47133920Sandre
48133920Sandre#include <net/if.h>
49133920Sandre#include <net/route.h>
50240099Smelifaro#include <net/ethernet.h>
51133920Sandre#include <net/pfil.h>
52196423Sjulian#include <net/vnet.h>
53133920Sandre
54133920Sandre#include <netinet/in.h>
55133920Sandre#include <netinet/in_systm.h>
56133920Sandre#include <netinet/ip.h>
57133920Sandre#include <netinet/ip_var.h>
58133920Sandre#include <netinet/ip_fw.h>
59223593Sglebius#ifdef INET6
60223593Sglebius#include <netinet/ip6.h>
61223593Sglebius#include <netinet6/ip6_var.h>
62223593Sglebius#endif
63240494Sglebius
64201124Sluigi#include <netgraph/ng_ipfw.h>
65133920Sandre
66240494Sglebius#include <netpfil/ipfw/ip_fw_private.h>
67240494Sglebius
68133920Sandre#include <machine/in_cksum.h>
69133920Sandre
70215701Sdimstatic VNET_DEFINE(int, fw_enable) = 1;
71200601Sluigi#define V_fw_enable	VNET(fw_enable)
72200601Sluigi
73158470Smlaier#ifdef INET6
74215701Sdimstatic VNET_DEFINE(int, fw6_enable) = 1;
75200601Sluigi#define V_fw6_enable	VNET(fw6_enable)
76158470Smlaier#endif
77133920Sandre
78240099Smelifarostatic VNET_DEFINE(int, fwlink_enable) = 0;
79240099Smelifaro#define V_fwlink_enable	VNET(fwlink_enable)
80240099Smelifaro
81158470Smlaierint ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
82158470Smlaier
83136714Sandre/* Forward declarations. */
84201527Sluigistatic int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int);
85240099Smelifarostatic int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int,
86240099Smelifaro	struct inpcb *);
87240099Smelifarostatic int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int,
88240099Smelifaro	struct inpcb *);
89133920Sandre
90200601Sluigi#ifdef SYSCTL_NODE
91204591Sluigi
92204591SluigiSYSBEGIN(f1)
93204591Sluigi
94200601SluigiSYSCTL_DECL(_net_inet_ip_fw);
95200601SluigiSYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, enable,
96200601Sluigi    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw_enable), 0,
97200601Sluigi    ipfw_chg_hook, "I", "Enable ipfw");
98200601Sluigi#ifdef INET6
99200601SluigiSYSCTL_DECL(_net_inet6_ip6_fw);
100200601SluigiSYSCTL_VNET_PROC(_net_inet6_ip6_fw, OID_AUTO, enable,
101200601Sluigi    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw6_enable), 0,
102200601Sluigi    ipfw_chg_hook, "I", "Enable ipfw+6");
103200601Sluigi#endif /* INET6 */
104204591Sluigi
105240099SmelifaroSYSCTL_DECL(_net_link_ether);
106240099SmelifaroSYSCTL_VNET_PROC(_net_link_ether, OID_AUTO, ipfw,
107240099Smelifaro    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fwlink_enable), 0,
108240099Smelifaro    ipfw_chg_hook, "I", "Pass ether pkts through firewall");
109240099Smelifaro
110204591SluigiSYSEND
111204591Sluigi
112200601Sluigi#endif /* SYSCTL_NODE */
113200601Sluigi
114201122Sluigi/*
115201122Sluigi * The pfilter hook to pass packets to ipfw_chk and then to
116201122Sluigi * dummynet, divert, netgraph or other modules.
117201122Sluigi * The packet may be consumed.
118201122Sluigi */
119240099Smelifarostatic int
120240099Smelifaroipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
121135920Smlaier    struct inpcb *inp)
122133920Sandre{
123133920Sandre	struct ip_fw_args args;
124201527Sluigi	struct m_tag *tag;
125201122Sluigi	int ipfw;
126201122Sluigi	int ret;
127133920Sandre
128201122Sluigi	/* convert dir to IPFW values */
129201122Sluigi	dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT;
130133920Sandre	bzero(&args, sizeof(args));
131133920Sandre
132173399Solegagain:
133201527Sluigi	/*
134201527Sluigi	 * extract and remove the tag if present. If we are left
135201527Sluigi	 * with onepass, optimize the outgoing path.
136201527Sluigi	 */
137201527Sluigi	tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
138201527Sluigi	if (tag != NULL) {
139201527Sluigi		args.rule = *((struct ipfw_rule_ref *)(tag+1));
140201527Sluigi		m_tag_delete(*m0, tag);
141241359Sglebius		if (args.rule.info & IPFW_ONEPASS)
142223358Sae			return (0);
143133920Sandre	}
144133920Sandre
145133920Sandre	args.m = *m0;
146201122Sluigi	args.oif = dir == DIR_OUT ? ifp : NULL;
147135920Smlaier	args.inp = inp;
148133920Sandre
149201527Sluigi	ipfw = ipfw_chk(&args);
150201527Sluigi	*m0 = args.m;
151191570Soleg
152140224Sglebius	KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
153140224Sglebius	    __func__));
154133920Sandre
155201122Sluigi	/* breaking out of the switch means drop */
156201122Sluigi	ret = 0;	/* default return value for pass */
157140224Sglebius	switch (ipfw) {
158140224Sglebius	case IP_FW_PASS:
159201122Sluigi		/* next_hop may be set by ipfw_chk */
160225044Sbz		if (args.next_hop == NULL && args.next_hop6 == NULL)
161206845Sluigi			break; /* pass */
162225793Sbz#if !defined(IPFIREWALL_FORWARD) || (!defined(INET6) && !defined(INET))
163201122Sluigi		ret = EACCES;
164201122Sluigi#else
165201527Sluigi	    {
166201527Sluigi		struct m_tag *fwd_tag;
167225044Sbz		size_t len;
168201527Sluigi
169225044Sbz		KASSERT(args.next_hop == NULL || args.next_hop6 == NULL,
170225044Sbz		    ("%s: both next_hop=%p and next_hop6=%p not NULL", __func__,
171225044Sbz		     args.next_hop, args.next_hop6));
172225044Sbz#ifdef INET6
173225044Sbz		if (args.next_hop6 != NULL)
174225044Sbz			len = sizeof(struct sockaddr_in6);
175225044Sbz#endif
176225044Sbz#ifdef INET
177225044Sbz		if (args.next_hop != NULL)
178225044Sbz			len = sizeof(struct sockaddr_in);
179225044Sbz#endif
180225044Sbz
181201122Sluigi		/* Incoming packets should not be tagged so we do not
182201122Sluigi		 * m_tag_find. Outgoing packets may be tagged, so we
183201122Sluigi		 * reuse the tag if present.
184201122Sluigi		 */
185201122Sluigi		fwd_tag = (dir == DIR_IN) ? NULL :
186201122Sluigi			m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
187201122Sluigi		if (fwd_tag != NULL) {
188201122Sluigi			m_tag_unlink(*m0, fwd_tag);
189201122Sluigi		} else {
190225044Sbz			fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len,
191225044Sbz			    M_NOWAIT);
192201122Sluigi			if (fwd_tag == NULL) {
193201122Sluigi				ret = EACCES;
194201122Sluigi				break; /* i.e. drop */
195201122Sluigi			}
196201122Sluigi		}
197225044Sbz#ifdef INET6
198225044Sbz		if (args.next_hop6 != NULL) {
199225044Sbz			bcopy(args.next_hop6, (fwd_tag+1), len);
200225044Sbz			if (in6_localip(&args.next_hop6->sin6_addr))
201225044Sbz				(*m0)->m_flags |= M_FASTFWD_OURS;
202225044Sbz		}
203225044Sbz#endif
204225044Sbz#ifdef INET
205225044Sbz		if (args.next_hop != NULL) {
206225044Sbz			bcopy(args.next_hop, (fwd_tag+1), len);
207225044Sbz			if (in_localip(args.next_hop->sin_addr))
208225044Sbz				(*m0)->m_flags |= M_FASTFWD_OURS;
209225044Sbz		}
210225044Sbz#endif
211133920Sandre		m_tag_prepend(*m0, fwd_tag);
212201527Sluigi	    }
213225793Sbz#endif /* IPFIREWALL_FORWARD */
214201122Sluigi		break;
215133920Sandre
216140224Sglebius	case IP_FW_DENY:
217201122Sluigi		ret = EACCES;
218201122Sluigi		break; /* i.e. drop */
219140224Sglebius
220140224Sglebius	case IP_FW_DUMMYNET:
221201122Sluigi		ret = EACCES;
222193502Sluigi		if (ip_dn_io_ptr == NULL)
223201122Sluigi			break; /* i.e. drop */
224145246Sbrooks		if (mtod(*m0, struct ip *)->ip_v == 4)
225201122Sluigi			ret = ip_dn_io_ptr(m0, dir, &args);
226145246Sbrooks		else if (mtod(*m0, struct ip *)->ip_v == 6)
227201122Sluigi			ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args);
228201122Sluigi		else
229201122Sluigi			break; /* drop it */
230201122Sluigi		/*
231201122Sluigi		 * XXX should read the return value.
232201122Sluigi		 * dummynet normally eats the packet and sets *m0=NULL
233201122Sluigi		 * unless the packet can be sent immediately. In this
234201122Sluigi		 * case args is updated and we should re-run the
235201122Sluigi		 * check without clearing args.
236201122Sluigi		 */
237173399Soleg		if (*m0 != NULL)
238173399Soleg			goto again;
239140224Sglebius		break;
240140224Sglebius
241140224Sglebius	case IP_FW_TEE:
242140224Sglebius	case IP_FW_DIVERT:
243201122Sluigi		if (ip_divert_ptr == NULL) {
244201122Sluigi			ret = EACCES;
245201122Sluigi			break; /* i.e. drop */
246201122Sluigi		}
247201527Sluigi		ret = ipfw_divert(m0, dir, &args.rule,
248201527Sluigi			(ipfw == IP_FW_TEE) ? 1 : 0);
249201527Sluigi		/* continue processing for the original packet (tee). */
250201527Sluigi		if (*m0)
251201527Sluigi			goto again;
252201122Sluigi		break;
253140224Sglebius
254141351Sglebius	case IP_FW_NGTEE:
255141351Sglebius	case IP_FW_NETGRAPH:
256201732Sluigi		if (ng_ipfw_input_p == NULL) {
257201122Sluigi			ret = EACCES;
258201122Sluigi			break; /* i.e. drop */
259201122Sluigi		}
260201122Sluigi		ret = ng_ipfw_input_p(m0, dir, &args,
261201122Sluigi			(ipfw == IP_FW_NGTEE) ? 1 : 0);
262201122Sluigi		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
263201122Sluigi			goto again;	/* continue with packet */
264201122Sluigi		break;
265141351Sglebius
266165648Spiso	case IP_FW_NAT:
267213254Sluigi		/* honor one-pass in case of successful nat */
268213254Sluigi		if (V_fw_one_pass)
269213254Sluigi			break; /* ret is already 0 */
270213254Sluigi		goto again;
271213254Sluigi
272201122Sluigi	case IP_FW_REASS:
273165648Spiso		goto again;		/* continue with packet */
274190633Spiso
275140224Sglebius	default:
276140224Sglebius		KASSERT(0, ("%s: unknown retval", __func__));
277140224Sglebius	}
278140224Sglebius
279201122Sluigi	if (ret != 0) {
280201122Sluigi		if (*m0)
281201527Sluigi			FREE_PKT(*m0);
282201122Sluigi		*m0 = NULL;
283201122Sluigi	}
284241245Sglebius
285201122Sluigi	return ret;
286133920Sandre}
287133920Sandre
288240099Smelifaro/*
289240099Smelifaro * ipfw processing for ethernet packets (in and out).
290240099Smelifaro * Inteface is NULL from ether_demux, and ifp from
291240099Smelifaro * ether_output_frame.
292240099Smelifaro */
293240099Smelifarostatic int
294240099Smelifaroipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir,
295240099Smelifaro    struct inpcb *inp)
296240099Smelifaro{
297240099Smelifaro	struct ether_header *eh;
298240099Smelifaro	struct ether_header save_eh;
299240099Smelifaro	struct mbuf *m;
300240099Smelifaro	int i, ret;
301240099Smelifaro	struct ip_fw_args args;
302240099Smelifaro	struct m_tag *mtag;
303240099Smelifaro
304240099Smelifaro	/* fetch start point from rule, if any */
305240099Smelifaro	mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
306240099Smelifaro	if (mtag == NULL) {
307240099Smelifaro		args.rule.slot = 0;
308240099Smelifaro	} else {
309240099Smelifaro		/* dummynet packet, already partially processed */
310240099Smelifaro		struct ipfw_rule_ref *r;
311240099Smelifaro
312240099Smelifaro		/* XXX can we free it after use ? */
313240099Smelifaro		mtag->m_tag_id = PACKET_TAG_NONE;
314240099Smelifaro		r = (struct ipfw_rule_ref *)(mtag + 1);
315240099Smelifaro		if (r->info & IPFW_ONEPASS)
316240099Smelifaro			return (0);
317240099Smelifaro		args.rule = *r;
318240099Smelifaro	}
319240099Smelifaro
320240099Smelifaro	/* I need some amt of data to be contiguous */
321240099Smelifaro	m = *m0;
322240099Smelifaro	i = min(m->m_pkthdr.len, max_protohdr);
323240099Smelifaro	if (m->m_len < i) {
324240099Smelifaro		m = m_pullup(m, i);
325240099Smelifaro		if (m == NULL) {
326240099Smelifaro			*m0 = m;
327240099Smelifaro			return (0);
328240099Smelifaro		}
329240099Smelifaro	}
330240099Smelifaro	eh = mtod(m, struct ether_header *);
331240099Smelifaro	save_eh = *eh;			/* save copy for restore below */
332240099Smelifaro	m_adj(m, ETHER_HDR_LEN);	/* strip ethernet header */
333240099Smelifaro
334240099Smelifaro	args.m = m;		/* the packet we are looking at		*/
335240099Smelifaro	args.oif = dst;		/* destination, if any			*/
336240099Smelifaro	args.next_hop = NULL;	/* we do not support forward yet	*/
337240099Smelifaro	args.next_hop6 = NULL;	/* we do not support forward yet	*/
338240099Smelifaro	args.eh = &save_eh;	/* MAC header for bridged/MAC packets	*/
339240099Smelifaro	args.inp = NULL;	/* used by ipfw uid/gid/jail rules	*/
340240099Smelifaro	i = ipfw_chk(&args);
341240099Smelifaro	m = args.m;
342240099Smelifaro	if (m != NULL) {
343240099Smelifaro		/*
344240099Smelifaro		 * Restore Ethernet header, as needed, in case the
345240099Smelifaro		 * mbuf chain was replaced by ipfw.
346240099Smelifaro		 */
347240099Smelifaro		M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT);
348240099Smelifaro		if (m == NULL) {
349240099Smelifaro			*m0 = NULL;
350240099Smelifaro			return (0);
351240099Smelifaro		}
352240099Smelifaro		if (eh != mtod(m, struct ether_header *))
353240099Smelifaro			bcopy(&save_eh, mtod(m, struct ether_header *),
354240099Smelifaro				ETHER_HDR_LEN);
355240099Smelifaro	}
356240099Smelifaro	*m0 = m;
357240099Smelifaro
358240099Smelifaro	ret = 0;
359240099Smelifaro	/* Check result of ipfw_chk() */
360240099Smelifaro	switch (i) {
361240099Smelifaro	case IP_FW_PASS:
362240099Smelifaro		break;
363240099Smelifaro
364240099Smelifaro	case IP_FW_DENY:
365240099Smelifaro		ret = EACCES;
366240099Smelifaro		break; /* i.e. drop */
367240099Smelifaro
368240099Smelifaro	case IP_FW_DUMMYNET:
369240099Smelifaro		ret = EACCES;
370240099Smelifaro		int dir;
371240099Smelifaro
372240099Smelifaro		if (ip_dn_io_ptr == NULL)
373240099Smelifaro			break; /* i.e. drop */
374240099Smelifaro
375240099Smelifaro		*m0 = NULL;
376240099Smelifaro		dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN);
377240099Smelifaro		ip_dn_io_ptr(&m, dir, &args);
378240099Smelifaro		return 0;
379240099Smelifaro
380240099Smelifaro	default:
381240099Smelifaro		KASSERT(0, ("%s: unknown retval", __func__));
382240099Smelifaro	}
383240099Smelifaro
384240099Smelifaro	if (ret != 0) {
385240099Smelifaro		if (*m0)
386240099Smelifaro			FREE_PKT(*m0);
387240099Smelifaro		*m0 = NULL;
388240099Smelifaro	}
389240099Smelifaro
390240099Smelifaro	return ret;
391240099Smelifaro}
392240099Smelifaro
393201527Sluigi/* do the divert, return 1 on error 0 on success */
394201527Sluigistatic int
395201527Sluigiipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
396201527Sluigi	int tee)
397133920Sandre{
398133920Sandre	/*
399135154Sandre	 * ipfw_chk() has already tagged the packet with the divert tag.
400133920Sandre	 * If tee is set, copy packet and return original.
401133920Sandre	 * If not tee, consume packet and send it to divert socket.
402133920Sandre	 */
403201122Sluigi	struct mbuf *clone;
404223593Sglebius	struct ip *ip = mtod(*m0, struct ip *);
405201527Sluigi	struct m_tag *tag;
406133920Sandre
407133920Sandre	/* Cloning needed for tee? */
408201122Sluigi	if (tee == 0) {
409201122Sluigi		clone = *m0;	/* use the original mbuf */
410201122Sluigi		*m0 = NULL;
411201122Sluigi	} else {
412201122Sluigi		clone = m_dup(*m0, M_DONTWAIT);
413201122Sluigi		/* If we cannot duplicate the mbuf, we sacrifice the divert
414201122Sluigi		 * chain and continue with the tee-ed packet.
415201122Sluigi		 */
416201122Sluigi		if (clone == NULL)
417201527Sluigi			return 1;
418201122Sluigi	}
419133920Sandre
420133920Sandre	/*
421201122Sluigi	 * Divert listeners can normally handle non-fragmented packets,
422201122Sluigi	 * but we can only reass in the non-tee case.
423201122Sluigi	 * This means that listeners on a tee rule may get fragments,
424201122Sluigi	 * and have to live with that.
425201122Sluigi	 * Note that we now have the 'reass' ipfw option so if we care
426201122Sluigi	 * we can do it before a 'tee'.
427133920Sandre	 */
428223593Sglebius	if (!tee) switch (ip->ip_v) {
429223593Sglebius	case IPVERSION:
430223593Sglebius	    if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) {
431201122Sluigi		int hlen;
432201122Sluigi		struct mbuf *reass;
433133920Sandre
434201527Sluigi		SET_HOST_IPLEN(ip); /* ip_reass wants host order */
435201122Sluigi		reass = ip_reass(clone); /* Reassemble packet. */
436201122Sluigi		if (reass == NULL)
437201527Sluigi			return 0; /* not an error */
438201122Sluigi		/* if reass = NULL then it was consumed by ip_reass */
439133920Sandre		/*
440133920Sandre		 * IP header checksum fixup after reassembly and leave header
441133920Sandre		 * in network byte order.
442133920Sandre		 */
443201122Sluigi		ip = mtod(reass, struct ip *);
444201122Sluigi		hlen = ip->ip_hl << 2;
445201122Sluigi		SET_NET_IPLEN(ip);
446201122Sluigi		ip->ip_sum = 0;
447201122Sluigi		if (hlen == sizeof(struct ip))
448201122Sluigi			ip->ip_sum = in_cksum_hdr(ip);
449201122Sluigi		else
450201122Sluigi			ip->ip_sum = in_cksum(reass, hlen);
451201122Sluigi		clone = reass;
452223593Sglebius	    }
453223593Sglebius	    break;
454223593Sglebius#ifdef INET6
455223593Sglebius	case IPV6_VERSION >> 4:
456223593Sglebius	    {
457223593Sglebius	    struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *);
458223593Sglebius
459223593Sglebius		if (ip6->ip6_nxt == IPPROTO_FRAGMENT) {
460223593Sglebius			int nxt, off;
461223593Sglebius
462223593Sglebius			off = sizeof(struct ip6_hdr);
463223593Sglebius			nxt = frag6_input(&clone, &off, 0);
464223593Sglebius			if (nxt == IPPROTO_DONE)
465223593Sglebius				return (0);
466223593Sglebius		}
467223593Sglebius		break;
468223593Sglebius	    }
469223593Sglebius#endif
470133920Sandre	}
471223593Sglebius
472201527Sluigi	/* attach a tag to the packet with the reinject info */
473201527Sluigi	tag = m_tag_alloc(MTAG_IPFW_RULE, 0,
474201527Sluigi		    sizeof(struct ipfw_rule_ref), M_NOWAIT);
475201527Sluigi	if (tag == NULL) {
476201527Sluigi		FREE_PKT(clone);
477201527Sluigi		return 1;
478201527Sluigi	}
479201527Sluigi	*((struct ipfw_rule_ref *)(tag+1)) = *rule;
480201527Sluigi	m_tag_prepend(clone, tag);
481133920Sandre
482133920Sandre	/* Do the dirty job... */
483201122Sluigi	ip_divert_ptr(clone, incoming);
484201527Sluigi	return 0;
485133920Sandre}
486133920Sandre
487201122Sluigi/*
488201122Sluigi * attach or detach hooks for a given protocol family
489201122Sluigi */
490200601Sluigistatic int
491201122Sluigiipfw_hook(int onoff, int pf)
492133920Sandre{
493201122Sluigi	struct pfil_head *pfh;
494240099Smelifaro	void *hook_func;
495133920Sandre
496201122Sluigi	pfh = pfil_head_get(PFIL_TYPE_AF, pf);
497201122Sluigi	if (pfh == NULL)
498133920Sandre		return ENOENT;
499133920Sandre
500240099Smelifaro	hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet;
501240099Smelifaro
502201527Sluigi	(void) (onoff ? pfil_add_hook : pfil_remove_hook)
503240099Smelifaro	    (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh);
504133920Sandre
505133920Sandre	return 0;
506133920Sandre}
507133920Sandre
508196423Sjulianint
509201122Sluigiipfw_attach_hooks(int arg)
510133920Sandre{
511200601Sluigi	int error = 0;
512200601Sluigi
513201122Sluigi	if (arg == 0) /* detach */
514201122Sluigi		ipfw_hook(0, AF_INET);
515206845Sluigi	else if (V_fw_enable && ipfw_hook(1, AF_INET) != 0) {
516200601Sluigi                error = ENOENT; /* see ip_fw_pfil.c::ipfw_hook() */
517200601Sluigi                printf("ipfw_hook() error\n");
518200601Sluigi        }
519200601Sluigi#ifdef INET6
520201122Sluigi	if (arg == 0) /* detach */
521201122Sluigi		ipfw_hook(0, AF_INET6);
522206845Sluigi	else if (V_fw6_enable && ipfw_hook(1, AF_INET6) != 0) {
523200601Sluigi                error = ENOENT;
524200601Sluigi                printf("ipfw6_hook() error\n");
525200601Sluigi        }
526200601Sluigi#endif
527240099Smelifaro	if (arg == 0) /* detach */
528240099Smelifaro		ipfw_hook(0, AF_LINK);
529240099Smelifaro	else if (V_fwlink_enable && ipfw_hook(1, AF_LINK) != 0) {
530240099Smelifaro                error = ENOENT;
531240099Smelifaro                printf("ipfw_link_hook() error\n");
532240099Smelifaro        }
533200601Sluigi	return error;
534200601Sluigi}
535200601Sluigi
536200601Sluigiint
537158470Smlaieripfw_chg_hook(SYSCTL_HANDLER_ARGS)
538158470Smlaier{
539240099Smelifaro	int *enable;
540240099Smelifaro	int newval;
541158470Smlaier	int error;
542201122Sluigi	int af;
543158470Smlaier
544197952Sjulian	if (arg1 == &VNET_NAME(fw_enable)) {
545240099Smelifaro		enable = &V_fw_enable;
546201122Sluigi		af = AF_INET;
547197952Sjulian	}
548197952Sjulian#ifdef INET6
549197952Sjulian	else if (arg1 == &VNET_NAME(fw6_enable)) {
550240099Smelifaro		enable = &V_fw6_enable;
551201122Sluigi		af = AF_INET6;
552197952Sjulian	}
553196423Sjulian#endif
554240099Smelifaro	else if (arg1 == &VNET_NAME(fwlink_enable)) {
555240099Smelifaro		enable = &V_fwlink_enable;
556240099Smelifaro		af = AF_LINK;
557240099Smelifaro	}
558197952Sjulian	else
559197952Sjulian		return (EINVAL);
560197952Sjulian
561240099Smelifaro	newval = *enable;
562197952Sjulian
563240099Smelifaro	/* Handle sysctl change */
564240099Smelifaro	error = sysctl_handle_int(oidp, &newval, 0, req);
565197952Sjulian
566158470Smlaier	if (error)
567158470Smlaier		return (error);
568158470Smlaier
569240099Smelifaro	/* Formalize new value */
570240099Smelifaro	newval = (newval) ? 1 : 0;
571158470Smlaier
572240099Smelifaro	if (*enable == newval)
573158470Smlaier		return (0);
574158470Smlaier
575240099Smelifaro	error = ipfw_hook(newval, af);
576201122Sluigi	if (error)
577201122Sluigi		return (error);
578240099Smelifaro	*enable = newval;
579158470Smlaier
580158470Smlaier	return (0);
581158470Smlaier}
582200601Sluigi/* end of file */
583