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$");
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 */
162242079Sae#if (!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;
202242463Sae			(*m0)->m_flags |= M_IP6_NEXTHOP;
203225044Sbz		}
204225044Sbz#endif
205225044Sbz#ifdef INET
206225044Sbz		if (args.next_hop != NULL) {
207225044Sbz			bcopy(args.next_hop, (fwd_tag+1), len);
208225044Sbz			if (in_localip(args.next_hop->sin_addr))
209225044Sbz				(*m0)->m_flags |= M_FASTFWD_OURS;
210242463Sae			(*m0)->m_flags |= M_IP_NEXTHOP;
211225044Sbz		}
212225044Sbz#endif
213133920Sandre		m_tag_prepend(*m0, fwd_tag);
214201527Sluigi	    }
215242079Sae#endif /* INET || INET6 */
216201122Sluigi		break;
217133920Sandre
218140224Sglebius	case IP_FW_DENY:
219201122Sluigi		ret = EACCES;
220201122Sluigi		break; /* i.e. drop */
221140224Sglebius
222140224Sglebius	case IP_FW_DUMMYNET:
223201122Sluigi		ret = EACCES;
224193502Sluigi		if (ip_dn_io_ptr == NULL)
225201122Sluigi			break; /* i.e. drop */
226145246Sbrooks		if (mtod(*m0, struct ip *)->ip_v == 4)
227201122Sluigi			ret = ip_dn_io_ptr(m0, dir, &args);
228145246Sbrooks		else if (mtod(*m0, struct ip *)->ip_v == 6)
229201122Sluigi			ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args);
230201122Sluigi		else
231201122Sluigi			break; /* drop it */
232201122Sluigi		/*
233201122Sluigi		 * XXX should read the return value.
234201122Sluigi		 * dummynet normally eats the packet and sets *m0=NULL
235201122Sluigi		 * unless the packet can be sent immediately. In this
236201122Sluigi		 * case args is updated and we should re-run the
237201122Sluigi		 * check without clearing args.
238201122Sluigi		 */
239173399Soleg		if (*m0 != NULL)
240173399Soleg			goto again;
241140224Sglebius		break;
242140224Sglebius
243140224Sglebius	case IP_FW_TEE:
244140224Sglebius	case IP_FW_DIVERT:
245201122Sluigi		if (ip_divert_ptr == NULL) {
246201122Sluigi			ret = EACCES;
247201122Sluigi			break; /* i.e. drop */
248201122Sluigi		}
249201527Sluigi		ret = ipfw_divert(m0, dir, &args.rule,
250201527Sluigi			(ipfw == IP_FW_TEE) ? 1 : 0);
251201527Sluigi		/* continue processing for the original packet (tee). */
252201527Sluigi		if (*m0)
253201527Sluigi			goto again;
254201122Sluigi		break;
255140224Sglebius
256141351Sglebius	case IP_FW_NGTEE:
257141351Sglebius	case IP_FW_NETGRAPH:
258201732Sluigi		if (ng_ipfw_input_p == NULL) {
259201122Sluigi			ret = EACCES;
260201122Sluigi			break; /* i.e. drop */
261201122Sluigi		}
262201122Sluigi		ret = ng_ipfw_input_p(m0, dir, &args,
263201122Sluigi			(ipfw == IP_FW_NGTEE) ? 1 : 0);
264201122Sluigi		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
265201122Sluigi			goto again;	/* continue with packet */
266201122Sluigi		break;
267141351Sglebius
268165648Spiso	case IP_FW_NAT:
269213254Sluigi		/* honor one-pass in case of successful nat */
270213254Sluigi		if (V_fw_one_pass)
271213254Sluigi			break; /* ret is already 0 */
272213254Sluigi		goto again;
273213254Sluigi
274201122Sluigi	case IP_FW_REASS:
275165648Spiso		goto again;		/* continue with packet */
276190633Spiso
277140224Sglebius	default:
278140224Sglebius		KASSERT(0, ("%s: unknown retval", __func__));
279140224Sglebius	}
280140224Sglebius
281201122Sluigi	if (ret != 0) {
282201122Sluigi		if (*m0)
283201527Sluigi			FREE_PKT(*m0);
284201122Sluigi		*m0 = NULL;
285201122Sluigi	}
286241245Sglebius
287201122Sluigi	return ret;
288133920Sandre}
289133920Sandre
290240099Smelifaro/*
291240099Smelifaro * ipfw processing for ethernet packets (in and out).
292240099Smelifaro * Inteface is NULL from ether_demux, and ifp from
293240099Smelifaro * ether_output_frame.
294240099Smelifaro */
295240099Smelifarostatic int
296240099Smelifaroipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir,
297240099Smelifaro    struct inpcb *inp)
298240099Smelifaro{
299240099Smelifaro	struct ether_header *eh;
300240099Smelifaro	struct ether_header save_eh;
301240099Smelifaro	struct mbuf *m;
302240099Smelifaro	int i, ret;
303240099Smelifaro	struct ip_fw_args args;
304240099Smelifaro	struct m_tag *mtag;
305240099Smelifaro
306240099Smelifaro	/* fetch start point from rule, if any */
307240099Smelifaro	mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
308240099Smelifaro	if (mtag == NULL) {
309240099Smelifaro		args.rule.slot = 0;
310240099Smelifaro	} else {
311240099Smelifaro		/* dummynet packet, already partially processed */
312240099Smelifaro		struct ipfw_rule_ref *r;
313240099Smelifaro
314240099Smelifaro		/* XXX can we free it after use ? */
315240099Smelifaro		mtag->m_tag_id = PACKET_TAG_NONE;
316240099Smelifaro		r = (struct ipfw_rule_ref *)(mtag + 1);
317240099Smelifaro		if (r->info & IPFW_ONEPASS)
318240099Smelifaro			return (0);
319240099Smelifaro		args.rule = *r;
320240099Smelifaro	}
321240099Smelifaro
322240099Smelifaro	/* I need some amt of data to be contiguous */
323240099Smelifaro	m = *m0;
324240099Smelifaro	i = min(m->m_pkthdr.len, max_protohdr);
325240099Smelifaro	if (m->m_len < i) {
326240099Smelifaro		m = m_pullup(m, i);
327240099Smelifaro		if (m == NULL) {
328240099Smelifaro			*m0 = m;
329240099Smelifaro			return (0);
330240099Smelifaro		}
331240099Smelifaro	}
332240099Smelifaro	eh = mtod(m, struct ether_header *);
333240099Smelifaro	save_eh = *eh;			/* save copy for restore below */
334240099Smelifaro	m_adj(m, ETHER_HDR_LEN);	/* strip ethernet header */
335240099Smelifaro
336240099Smelifaro	args.m = m;		/* the packet we are looking at		*/
337264813Sae	args.oif = dir == PFIL_OUT ? dst: NULL;	/* destination, if any	*/
338240099Smelifaro	args.next_hop = NULL;	/* we do not support forward yet	*/
339240099Smelifaro	args.next_hop6 = NULL;	/* we do not support forward yet	*/
340240099Smelifaro	args.eh = &save_eh;	/* MAC header for bridged/MAC packets	*/
341240099Smelifaro	args.inp = NULL;	/* used by ipfw uid/gid/jail rules	*/
342240099Smelifaro	i = ipfw_chk(&args);
343240099Smelifaro	m = args.m;
344240099Smelifaro	if (m != NULL) {
345240099Smelifaro		/*
346240099Smelifaro		 * Restore Ethernet header, as needed, in case the
347240099Smelifaro		 * mbuf chain was replaced by ipfw.
348240099Smelifaro		 */
349243882Sglebius		M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
350240099Smelifaro		if (m == NULL) {
351240099Smelifaro			*m0 = NULL;
352240099Smelifaro			return (0);
353240099Smelifaro		}
354240099Smelifaro		if (eh != mtod(m, struct ether_header *))
355240099Smelifaro			bcopy(&save_eh, mtod(m, struct ether_header *),
356240099Smelifaro				ETHER_HDR_LEN);
357240099Smelifaro	}
358240099Smelifaro	*m0 = m;
359240099Smelifaro
360240099Smelifaro	ret = 0;
361240099Smelifaro	/* Check result of ipfw_chk() */
362240099Smelifaro	switch (i) {
363240099Smelifaro	case IP_FW_PASS:
364240099Smelifaro		break;
365240099Smelifaro
366240099Smelifaro	case IP_FW_DENY:
367240099Smelifaro		ret = EACCES;
368240099Smelifaro		break; /* i.e. drop */
369240099Smelifaro
370240099Smelifaro	case IP_FW_DUMMYNET:
371240099Smelifaro		ret = EACCES;
372240099Smelifaro		int dir;
373240099Smelifaro
374240099Smelifaro		if (ip_dn_io_ptr == NULL)
375240099Smelifaro			break; /* i.e. drop */
376240099Smelifaro
377240099Smelifaro		*m0 = NULL;
378240099Smelifaro		dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN);
379240099Smelifaro		ip_dn_io_ptr(&m, dir, &args);
380240099Smelifaro		return 0;
381240099Smelifaro
382240099Smelifaro	default:
383240099Smelifaro		KASSERT(0, ("%s: unknown retval", __func__));
384240099Smelifaro	}
385240099Smelifaro
386240099Smelifaro	if (ret != 0) {
387240099Smelifaro		if (*m0)
388240099Smelifaro			FREE_PKT(*m0);
389240099Smelifaro		*m0 = NULL;
390240099Smelifaro	}
391240099Smelifaro
392240099Smelifaro	return ret;
393240099Smelifaro}
394240099Smelifaro
395201527Sluigi/* do the divert, return 1 on error 0 on success */
396201527Sluigistatic int
397201527Sluigiipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
398201527Sluigi	int tee)
399133920Sandre{
400133920Sandre	/*
401135154Sandre	 * ipfw_chk() has already tagged the packet with the divert tag.
402133920Sandre	 * If tee is set, copy packet and return original.
403133920Sandre	 * If not tee, consume packet and send it to divert socket.
404133920Sandre	 */
405201122Sluigi	struct mbuf *clone;
406223593Sglebius	struct ip *ip = mtod(*m0, struct ip *);
407201527Sluigi	struct m_tag *tag;
408133920Sandre
409133920Sandre	/* Cloning needed for tee? */
410201122Sluigi	if (tee == 0) {
411201122Sluigi		clone = *m0;	/* use the original mbuf */
412201122Sluigi		*m0 = NULL;
413201122Sluigi	} else {
414243882Sglebius		clone = m_dup(*m0, M_NOWAIT);
415201122Sluigi		/* If we cannot duplicate the mbuf, we sacrifice the divert
416201122Sluigi		 * chain and continue with the tee-ed packet.
417201122Sluigi		 */
418201122Sluigi		if (clone == NULL)
419201527Sluigi			return 1;
420201122Sluigi	}
421133920Sandre
422133920Sandre	/*
423201122Sluigi	 * Divert listeners can normally handle non-fragmented packets,
424201122Sluigi	 * but we can only reass in the non-tee case.
425201122Sluigi	 * This means that listeners on a tee rule may get fragments,
426201122Sluigi	 * and have to live with that.
427201122Sluigi	 * Note that we now have the 'reass' ipfw option so if we care
428201122Sluigi	 * we can do it before a 'tee'.
429133920Sandre	 */
430223593Sglebius	if (!tee) switch (ip->ip_v) {
431223593Sglebius	case IPVERSION:
432223593Sglebius	    if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) {
433201122Sluigi		int hlen;
434201122Sluigi		struct mbuf *reass;
435133920Sandre
436201122Sluigi		reass = ip_reass(clone); /* Reassemble packet. */
437201122Sluigi		if (reass == NULL)
438201527Sluigi			return 0; /* not an error */
439201122Sluigi		/* if reass = NULL then it was consumed by ip_reass */
440133920Sandre		/*
441133920Sandre		 * IP header checksum fixup after reassembly and leave header
442133920Sandre		 * in network byte order.
443133920Sandre		 */
444201122Sluigi		ip = mtod(reass, struct ip *);
445201122Sluigi		hlen = ip->ip_hl << 2;
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 newval;
540158470Smlaier	int error;
541201122Sluigi	int af;
542158470Smlaier
543263680Sglebius	if (arg1 == &V_fw_enable)
544201122Sluigi		af = AF_INET;
545197952Sjulian#ifdef INET6
546263680Sglebius	else if (arg1 == &V_fw6_enable)
547201122Sluigi		af = AF_INET6;
548196423Sjulian#endif
549263680Sglebius	else if (arg1 == &V_fwlink_enable)
550240099Smelifaro		af = AF_LINK;
551197952Sjulian	else
552197952Sjulian		return (EINVAL);
553197952Sjulian
554263680Sglebius	newval = *(int *)arg1;
555240099Smelifaro	/* Handle sysctl change */
556240099Smelifaro	error = sysctl_handle_int(oidp, &newval, 0, req);
557197952Sjulian
558158470Smlaier	if (error)
559158470Smlaier		return (error);
560158470Smlaier
561240099Smelifaro	/* Formalize new value */
562240099Smelifaro	newval = (newval) ? 1 : 0;
563158470Smlaier
564263680Sglebius	if (*(int *)arg1 == newval)
565158470Smlaier		return (0);
566158470Smlaier
567240099Smelifaro	error = ipfw_hook(newval, af);
568201122Sluigi	if (error)
569201122Sluigi		return (error);
570263680Sglebius	*(int *)arg1 = newval;
571158470Smlaier
572158470Smlaier	return (0);
573158470Smlaier}
574200601Sluigi/* end of file */
575