ip_fil_freebsd.c revision 298030
1145516Sdarrenr/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c 298030 2016-04-15 03:43:16Z cy $	*/
2145516Sdarrenr
3145516Sdarrenr/*
4255332Scy * Copyright (C) 2012 by Darren Reed.
5145516Sdarrenr *
6145516Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
7145516Sdarrenr */
8145516Sdarrenr#if !defined(lint)
9145516Sdarrenrstatic const char sccsid[] = "@(#)ip_fil.c	2.41 6/5/96 (C) 1993-2000 Darren Reed";
10255332Scystatic const char rcsid[] = "@(#)$Id$";
11145516Sdarrenr#endif
12145516Sdarrenr
13145516Sdarrenr#if defined(KERNEL) || defined(_KERNEL)
14145516Sdarrenr# undef KERNEL
15145516Sdarrenr# undef _KERNEL
16145516Sdarrenr# define	KERNEL	1
17145516Sdarrenr# define	_KERNEL	1
18145516Sdarrenr#endif
19145516Sdarrenr#if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \
20145516Sdarrenr    !defined(KLD_MODULE) && !defined(IPFILTER_LKM)
21145516Sdarrenr# include "opt_inet6.h"
22145516Sdarrenr#endif
23145516Sdarrenr#if defined(__FreeBSD_version) && (__FreeBSD_version >= 440000) && \
24145516Sdarrenr    !defined(KLD_MODULE) && !defined(IPFILTER_LKM)
25145516Sdarrenr# include "opt_random_ip_id.h"
26145516Sdarrenr#endif
27145516Sdarrenr#include <sys/param.h>
28145516Sdarrenr#include <sys/errno.h>
29145516Sdarrenr#include <sys/types.h>
30145516Sdarrenr#include <sys/file.h>
31145516Sdarrenr# include <sys/fcntl.h>
32145516Sdarrenr# include <sys/filio.h>
33145516Sdarrenr#include <sys/time.h>
34145516Sdarrenr#include <sys/systm.h>
35145516Sdarrenr# include <sys/dirent.h>
36274744Srodrigc#if defined(__FreeBSD_version) && (__FreeBSD_version >= 800000)
37274744Srodrigc#include <sys/jail.h>
38274744Srodrigc#endif
39295126Sglebius# include <sys/malloc.h>
40255332Scy# include <sys/mbuf.h>
41255332Scy# include <sys/sockopt.h>
42145516Sdarrenr#if !defined(__hpux)
43145516Sdarrenr# include <sys/mbuf.h>
44145516Sdarrenr#endif
45145516Sdarrenr#include <sys/socket.h>
46170268Sdarrenr# include <sys/selinfo.h>
47195699Srwatson# include <netinet/tcp_var.h>
48145516Sdarrenr
49145516Sdarrenr#include <net/if.h>
50145516Sdarrenr# include <net/if_var.h>
51170268Sdarrenr#  include <net/netisr.h>
52145516Sdarrenr#include <net/route.h>
53145516Sdarrenr#include <netinet/in.h>
54293628Smelifaro#include <netinet/in_fib.h>
55145516Sdarrenr#include <netinet/in_var.h>
56145516Sdarrenr#include <netinet/in_systm.h>
57145516Sdarrenr#include <netinet/ip.h>
58145516Sdarrenr#include <netinet/ip_var.h>
59145516Sdarrenr#include <netinet/tcp.h>
60274744Srodrigc#if defined(__FreeBSD_version) && (__FreeBSD_version >= 800000)
61274744Srodrigc#include <net/vnet.h>
62274744Srodrigc#else
63274744Srodrigc#define CURVNET_SET(arg)
64274744Srodrigc#define CURVNET_RESTORE()
65274744Srodrigc#endif
66145516Sdarrenr#if defined(__osf__)
67145516Sdarrenr# include <netinet/tcp_timer.h>
68145516Sdarrenr#endif
69145516Sdarrenr#include <netinet/udp.h>
70145516Sdarrenr#include <netinet/tcpip.h>
71145516Sdarrenr#include <netinet/ip_icmp.h>
72145516Sdarrenr#include "netinet/ip_compat.h"
73145516Sdarrenr#ifdef USE_INET6
74145516Sdarrenr# include <netinet/icmp6.h>
75145516Sdarrenr#endif
76145516Sdarrenr#include "netinet/ip_fil.h"
77145516Sdarrenr#include "netinet/ip_nat.h"
78145516Sdarrenr#include "netinet/ip_frag.h"
79145516Sdarrenr#include "netinet/ip_state.h"
80145516Sdarrenr#include "netinet/ip_proxy.h"
81145516Sdarrenr#include "netinet/ip_auth.h"
82145516Sdarrenr#include "netinet/ip_sync.h"
83255332Scy#include "netinet/ip_lookup.h"
84255332Scy#include "netinet/ip_dstlist.h"
85145516Sdarrenr#ifdef	IPFILTER_SCAN
86145516Sdarrenr#include "netinet/ip_scan.h"
87145516Sdarrenr#endif
88145516Sdarrenr#include "netinet/ip_pool.h"
89145516Sdarrenr# include <sys/malloc.h>
90145516Sdarrenr#include <sys/kernel.h>
91145516Sdarrenr#ifdef CSUM_DATA_VALID
92145516Sdarrenr#include <machine/in_cksum.h>
93145516Sdarrenr#endif
94145516Sdarrenrextern	int	ip_optcopy __P((struct ip *, struct ip *));
95145516Sdarrenr
96145516Sdarrenr
97145516Sdarrenr# ifdef IPFILTER_M_IPFILTER
98151897SrwatsonMALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures");
99145516Sdarrenr# endif
100145516Sdarrenr
101145516Sdarrenr
102255332Scystatic	int	(*ipf_savep) __P((void *, ip_t *, int, void *, int, struct mbuf **));
103255332Scystatic	int	ipf_send_ip __P((fr_info_t *, mb_t *));
104255332Scystatic void	ipf_timer_func __P((void *arg));
105145516Sdarrenrint		ipf_locks_done = 0;
106145516Sdarrenr
107255332Scyipf_main_softc_t ipfmain;
108145516Sdarrenr
109145516Sdarrenr# include <sys/conf.h>
110145516Sdarrenr# if defined(NETBSD_PF)
111145516Sdarrenr#  include <net/pfil.h>
112255332Scy# endif /* NETBSD_PF */
113145516Sdarrenr/*
114255332Scy * We provide the ipf_checkp name just to minimize changes later.
115145516Sdarrenr */
116255332Scyint (*ipf_checkp) __P((void *, ip_t *ip, int hlen, void *ifp, int out, mb_t **mp));
117145516Sdarrenr
118145516Sdarrenr
119153876Sguidostatic eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag;
120153876Sguido
121153876Sguidostatic void ipf_ifevent(void *arg);
122153876Sguido
123153876Sguidostatic void ipf_ifevent(arg)
124255332Scy	void *arg;
125153876Sguido{
126255332Scy        ipf_sync(arg, NULL);
127153876Sguido}
128153876Sguido
129153876Sguido
130145516Sdarrenr
131145516Sdarrenrstatic int
132255332Scyipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
133145516Sdarrenr{
134145516Sdarrenr	struct ip *ip = mtod(*mp, struct ip *);
135255332Scy	int rv;
136255332Scy
137255332Scy	/*
138255332Scy	 * IPFilter expects evreything in network byte order
139255332Scy	 */
140255332Scy#if (__FreeBSD_version < 1000019)
141255332Scy	ip->ip_len = htons(ip->ip_len);
142255332Scy	ip->ip_off = htons(ip->ip_off);
143255332Scy#endif
144255332Scy	rv = ipf_check(&ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT),
145255332Scy		       mp);
146255332Scy#if (__FreeBSD_version < 1000019)
147255332Scy	if ((rv == 0) && (*mp != NULL)) {
148255332Scy		ip = mtod(*mp, struct ip *);
149255332Scy		ip->ip_len = ntohs(ip->ip_len);
150255332Scy		ip->ip_off = ntohs(ip->ip_off);
151255332Scy	}
152255332Scy#endif
153255332Scy	return rv;
154145516Sdarrenr}
155145516Sdarrenr
156145516Sdarrenr# ifdef USE_INET6
157145516Sdarrenr#  include <netinet/ip6.h>
158145516Sdarrenr
159145516Sdarrenrstatic int
160255332Scyipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
161145516Sdarrenr{
162255332Scy	return (ipf_check(&ipfmain, mtod(*mp, struct ip *),
163255332Scy			  sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp));
164145516Sdarrenr}
165145516Sdarrenr# endif
166145516Sdarrenr#if	defined(IPFILTER_LKM)
167255332Scyint ipf_identify(s)
168255332Scy	char *s;
169145516Sdarrenr{
170145516Sdarrenr	if (strcmp(s, "ipl") == 0)
171145516Sdarrenr		return 1;
172145516Sdarrenr	return 0;
173145516Sdarrenr}
174145516Sdarrenr#endif /* IPFILTER_LKM */
175145516Sdarrenr
176145516Sdarrenr
177255332Scystatic void
178255332Scyipf_timer_func(arg)
179255332Scy	void *arg;
180145516Sdarrenr{
181255332Scy	ipf_main_softc_t *softc = arg;
182255332Scy	SPL_INT(s);
183255332Scy
184255332Scy	SPL_NET(s);
185255332Scy	READ_ENTER(&softc->ipf_global);
186255332Scy
187255332Scy        if (softc->ipf_running > 0)
188255332Scy		ipf_slowtimer(softc);
189255332Scy
190255332Scy	if (softc->ipf_running == -1 || softc->ipf_running == 1) {
191255755Scy#if 0
192255332Scy		softc->ipf_slow_ch = timeout(ipf_timer_func, softc, hz/2);
193255332Scy#endif
194283291Sjkim		callout_init(&softc->ipf_slow_ch, 1);
195255755Scy		callout_reset(&softc->ipf_slow_ch,
196255755Scy			(hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT,
197255755Scy			ipf_timer_func, softc);
198255332Scy	}
199255332Scy	RWLOCK_EXIT(&softc->ipf_global);
200255332Scy	SPL_X(s);
201255332Scy}
202255332Scy
203255332Scy
204255332Scyint
205255332Scyipfattach(softc)
206255332Scy	ipf_main_softc_t *softc;
207255332Scy{
208145516Sdarrenr#ifdef USE_SPL
209145516Sdarrenr	int s;
210145516Sdarrenr#endif
211145516Sdarrenr
212145516Sdarrenr	SPL_NET(s);
213255332Scy	if (softc->ipf_running > 0) {
214145516Sdarrenr		SPL_X(s);
215145516Sdarrenr		return EBUSY;
216145516Sdarrenr	}
217145516Sdarrenr
218255332Scy	if (ipf_init_all(softc) < 0) {
219145516Sdarrenr		SPL_X(s);
220145516Sdarrenr		return EIO;
221145516Sdarrenr	}
222145516Sdarrenr
223145516Sdarrenr
224255332Scy	if (ipf_checkp != ipf_check) {
225255332Scy		ipf_savep = ipf_checkp;
226255332Scy		ipf_checkp = ipf_check;
227145516Sdarrenr	}
228145516Sdarrenr
229255332Scy	bzero((char *)ipfmain.ipf_selwait, sizeof(ipfmain.ipf_selwait));
230255332Scy	softc->ipf_running = 1;
231145516Sdarrenr
232255332Scy	if (softc->ipf_control_forwarding & 1)
233181803Sbz		V_ipforwarding = 1;
234145516Sdarrenr
235145516Sdarrenr	SPL_X(s);
236255755Scy#if 0
237255332Scy	softc->ipf_slow_ch = timeout(ipf_timer_func, softc,
238255332Scy				     (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT);
239255755Scy#endif
240283291Sjkim	callout_init(&softc->ipf_slow_ch, 1);
241255755Scy	callout_reset(&softc->ipf_slow_ch, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT,
242255755Scy		ipf_timer_func, softc);
243145516Sdarrenr	return 0;
244145516Sdarrenr}
245145516Sdarrenr
246145516Sdarrenr
247145516Sdarrenr/*
248145516Sdarrenr * Disable the filter by removing the hooks from the IP input/output
249145516Sdarrenr * stream.
250145516Sdarrenr */
251255332Scyint
252255332Scyipfdetach(softc)
253255332Scy	ipf_main_softc_t *softc;
254145516Sdarrenr{
255145516Sdarrenr#ifdef USE_SPL
256145516Sdarrenr	int s;
257145516Sdarrenr#endif
258255332Scy
259255332Scy	if (softc->ipf_control_forwarding & 2)
260181803Sbz		V_ipforwarding = 0;
261145516Sdarrenr
262145516Sdarrenr	SPL_NET(s);
263145516Sdarrenr
264255755Scy#if 0
265255332Scy	if (softc->ipf_slow_ch.callout != NULL)
266255332Scy		untimeout(ipf_timer_func, softc, softc->ipf_slow_ch);
267255332Scy	bzero(&softc->ipf_slow, sizeof(softc->ipf_slow));
268255755Scy#endif
269255755Scy	callout_drain(&softc->ipf_slow_ch);
270145516Sdarrenr
271145516Sdarrenr#ifndef NETBSD_PF
272255332Scy	if (ipf_checkp != NULL)
273255332Scy		ipf_checkp = ipf_savep;
274255332Scy	ipf_savep = NULL;
275145516Sdarrenr#endif
276145516Sdarrenr
277255332Scy	ipf_fini_all(softc);
278145516Sdarrenr
279255332Scy	softc->ipf_running = -2;
280145516Sdarrenr
281145516Sdarrenr	SPL_X(s);
282145516Sdarrenr
283145516Sdarrenr	return 0;
284145516Sdarrenr}
285145516Sdarrenr
286145516Sdarrenr
287145516Sdarrenr/*
288145516Sdarrenr * Filter ioctl interface.
289145516Sdarrenr */
290255332Scyint
291255332Scyipfioctl(dev, cmd, data, mode
292145516Sdarrenr, p)
293255332Scy	struct thread *p;
294192895Sjamie#    define	p_cred	td_ucred
295170268Sdarrenr#    define	p_uid	td_ucred->cr_ruid
296255332Scy	struct cdev *dev;
297255332Scy	ioctlcmd_t cmd;
298255332Scy	caddr_t data;
299255332Scy	int mode;
300145516Sdarrenr{
301170268Sdarrenr	int error = 0, unit = 0;
302170268Sdarrenr	SPL_INT(s);
303145516Sdarrenr
304255332Scy#if (BSD >= 199306)
305255332Scy        if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE))
306255332Scy	{
307255332Scy		ipfmain.ipf_interror = 130001;
308145516Sdarrenr		return EPERM;
309255332Scy	}
310145516Sdarrenr#endif
311145516Sdarrenr
312145516Sdarrenr	unit = GET_MINOR(dev);
313255332Scy	if ((IPL_LOGMAX < unit) || (unit < 0)) {
314255332Scy		ipfmain.ipf_interror = 130002;
315145516Sdarrenr		return ENXIO;
316255332Scy	}
317145516Sdarrenr
318255332Scy	if (ipfmain.ipf_running <= 0) {
319255332Scy		if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) {
320255332Scy			ipfmain.ipf_interror = 130003;
321145516Sdarrenr			return EIO;
322255332Scy		}
323145516Sdarrenr		if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET &&
324173181Sdarrenr		    cmd != SIOCIPFSET && cmd != SIOCFRENB &&
325255332Scy		    cmd != SIOCGETFS && cmd != SIOCGETFF &&
326255332Scy		    cmd != SIOCIPFINTERROR) {
327255332Scy			ipfmain.ipf_interror = 130004;
328145516Sdarrenr			return EIO;
329255332Scy		}
330145516Sdarrenr	}
331145516Sdarrenr
332145516Sdarrenr	SPL_NET(s);
333145516Sdarrenr
334274744Srodrigc	CURVNET_SET(TD_TO_VNET(p));
335255332Scy	error = ipf_ioctlswitch(&ipfmain, unit, data, cmd, mode, p->p_uid, p);
336274744Srodrigc	CURVNET_RESTORE();
337145516Sdarrenr	if (error != -1) {
338145516Sdarrenr		SPL_X(s);
339145516Sdarrenr		return error;
340145516Sdarrenr	}
341145516Sdarrenr
342145516Sdarrenr	SPL_X(s);
343161356Sguido
344145516Sdarrenr	return error;
345145516Sdarrenr}
346145516Sdarrenr
347145516Sdarrenr
348145516Sdarrenr/*
349255332Scy * ipf_send_reset - this could conceivably be a call to tcp_respond(), but that
350145516Sdarrenr * requires a large amount of setting up and isn't any more efficient.
351145516Sdarrenr */
352255332Scyint
353255332Scyipf_send_reset(fin)
354255332Scy	fr_info_t *fin;
355145516Sdarrenr{
356145516Sdarrenr	struct tcphdr *tcp, *tcp2;
357145516Sdarrenr	int tlen = 0, hlen;
358145516Sdarrenr	struct mbuf *m;
359145516Sdarrenr#ifdef USE_INET6
360145516Sdarrenr	ip6_t *ip6;
361145516Sdarrenr#endif
362145516Sdarrenr	ip_t *ip;
363145516Sdarrenr
364145516Sdarrenr	tcp = fin->fin_dp;
365145516Sdarrenr	if (tcp->th_flags & TH_RST)
366145516Sdarrenr		return -1;		/* feedback loop */
367145516Sdarrenr
368255332Scy	if (ipf_checkl4sum(fin) == -1)
369145516Sdarrenr		return -1;
370145516Sdarrenr
371145516Sdarrenr	tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) +
372145516Sdarrenr			((tcp->th_flags & TH_SYN) ? 1 : 0) +
373145516Sdarrenr			((tcp->th_flags & TH_FIN) ? 1 : 0);
374145516Sdarrenr
375145516Sdarrenr#ifdef USE_INET6
376145516Sdarrenr	hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t);
377145516Sdarrenr#else
378145516Sdarrenr	hlen = sizeof(ip_t);
379145516Sdarrenr#endif
380145516Sdarrenr#ifdef MGETHDR
381260715Sglebius	MGETHDR(m, M_NOWAIT, MT_HEADER);
382145516Sdarrenr#else
383260715Sglebius	MGET(m, M_NOWAIT, MT_HEADER);
384145516Sdarrenr#endif
385145516Sdarrenr	if (m == NULL)
386145516Sdarrenr		return -1;
387145516Sdarrenr	if (sizeof(*tcp2) + hlen > MLEN) {
388276750Srwatson		if (!(MCLGET(m, M_NOWAIT))) {
389145516Sdarrenr			FREE_MB_T(m);
390145516Sdarrenr			return -1;
391145516Sdarrenr		}
392145516Sdarrenr	}
393145516Sdarrenr
394145516Sdarrenr	m->m_len = sizeof(*tcp2) + hlen;
395145516Sdarrenr#if (BSD >= 199103)
396145516Sdarrenr	m->m_data += max_linkhdr;
397145516Sdarrenr	m->m_pkthdr.len = m->m_len;
398145516Sdarrenr	m->m_pkthdr.rcvif = (struct ifnet *)0;
399145516Sdarrenr#endif
400145516Sdarrenr	ip = mtod(m, struct ip *);
401145516Sdarrenr	bzero((char *)ip, hlen);
402145516Sdarrenr#ifdef USE_INET6
403145516Sdarrenr	ip6 = (ip6_t *)ip;
404145516Sdarrenr#endif
405145516Sdarrenr	tcp2 = (struct tcphdr *)((char *)ip + hlen);
406145516Sdarrenr	tcp2->th_sport = tcp->th_dport;
407145516Sdarrenr	tcp2->th_dport = tcp->th_sport;
408145516Sdarrenr
409145516Sdarrenr	if (tcp->th_flags & TH_ACK) {
410145516Sdarrenr		tcp2->th_seq = tcp->th_ack;
411145516Sdarrenr		tcp2->th_flags = TH_RST;
412145516Sdarrenr		tcp2->th_ack = 0;
413145516Sdarrenr	} else {
414145516Sdarrenr		tcp2->th_seq = 0;
415145516Sdarrenr		tcp2->th_ack = ntohl(tcp->th_seq);
416145516Sdarrenr		tcp2->th_ack += tlen;
417145516Sdarrenr		tcp2->th_ack = htonl(tcp2->th_ack);
418145516Sdarrenr		tcp2->th_flags = TH_RST|TH_ACK;
419145516Sdarrenr	}
420145516Sdarrenr	TCP_X2_A(tcp2, 0);
421145516Sdarrenr	TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2);
422145516Sdarrenr	tcp2->th_win = tcp->th_win;
423145516Sdarrenr	tcp2->th_sum = 0;
424145516Sdarrenr	tcp2->th_urp = 0;
425145516Sdarrenr
426145516Sdarrenr#ifdef USE_INET6
427145516Sdarrenr	if (fin->fin_v == 6) {
428145516Sdarrenr		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
429145516Sdarrenr		ip6->ip6_plen = htons(sizeof(struct tcphdr));
430145516Sdarrenr		ip6->ip6_nxt = IPPROTO_TCP;
431145516Sdarrenr		ip6->ip6_hlim = 0;
432255332Scy		ip6->ip6_src = fin->fin_dst6.in6;
433255332Scy		ip6->ip6_dst = fin->fin_src6.in6;
434145516Sdarrenr		tcp2->th_sum = in6_cksum(m, IPPROTO_TCP,
435145516Sdarrenr					 sizeof(*ip6), sizeof(*tcp2));
436255332Scy		return ipf_send_ip(fin, m);
437145516Sdarrenr	}
438145516Sdarrenr#endif
439145516Sdarrenr	ip->ip_p = IPPROTO_TCP;
440145516Sdarrenr	ip->ip_len = htons(sizeof(struct tcphdr));
441145516Sdarrenr	ip->ip_src.s_addr = fin->fin_daddr;
442145516Sdarrenr	ip->ip_dst.s_addr = fin->fin_saddr;
443145516Sdarrenr	tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2));
444255332Scy	ip->ip_len = htons(hlen + sizeof(*tcp2));
445255332Scy	return ipf_send_ip(fin, m);
446145516Sdarrenr}
447145516Sdarrenr
448145516Sdarrenr
449255332Scy/*
450255332Scy * ip_len must be in network byte order when called.
451255332Scy */
452255332Scystatic int
453255332Scyipf_send_ip(fin, m)
454255332Scy	fr_info_t *fin;
455255332Scy	mb_t *m;
456145516Sdarrenr{
457145516Sdarrenr	fr_info_t fnew;
458145516Sdarrenr	ip_t *ip, *oip;
459145516Sdarrenr	int hlen;
460145516Sdarrenr
461145516Sdarrenr	ip = mtod(m, ip_t *);
462145516Sdarrenr	bzero((char *)&fnew, sizeof(fnew));
463255332Scy	fnew.fin_main_soft = fin->fin_main_soft;
464145516Sdarrenr
465145516Sdarrenr	IP_V_A(ip, fin->fin_v);
466145516Sdarrenr	switch (fin->fin_v)
467145516Sdarrenr	{
468145516Sdarrenr	case 4 :
469255332Scy		oip = fin->fin_ip;
470255332Scy		hlen = sizeof(*oip);
471145516Sdarrenr		fnew.fin_v = 4;
472255332Scy		fnew.fin_p = ip->ip_p;
473255332Scy		fnew.fin_plen = ntohs(ip->ip_len);
474145516Sdarrenr		IP_HL_A(ip, sizeof(*oip) >> 2);
475145516Sdarrenr		ip->ip_tos = oip->ip_tos;
476145516Sdarrenr		ip->ip_id = fin->fin_ip->ip_id;
477255332Scy#if defined(FreeBSD) && (__FreeBSD_version > 460000)
478255332Scy		ip->ip_off = htons(path_mtu_discovery ? IP_DF : 0);
479145516Sdarrenr#else
480145516Sdarrenr		ip->ip_off = 0;
481145516Sdarrenr#endif
482181803Sbz		ip->ip_ttl = V_ip_defttl;
483145516Sdarrenr		ip->ip_sum = 0;
484145516Sdarrenr		break;
485145516Sdarrenr#ifdef USE_INET6
486145516Sdarrenr	case 6 :
487145516Sdarrenr	{
488145516Sdarrenr		ip6_t *ip6 = (ip6_t *)ip;
489145516Sdarrenr
490145516Sdarrenr		ip6->ip6_vfc = 0x60;
491145516Sdarrenr		ip6->ip6_hlim = IPDEFTTL;
492145516Sdarrenr
493255332Scy		hlen = sizeof(*ip6);
494255332Scy		fnew.fin_p = ip6->ip6_nxt;
495145516Sdarrenr		fnew.fin_v = 6;
496255332Scy		fnew.fin_plen = ntohs(ip6->ip6_plen) + hlen;
497145516Sdarrenr		break;
498145516Sdarrenr	}
499145516Sdarrenr#endif
500145516Sdarrenr	default :
501145516Sdarrenr		return EINVAL;
502145516Sdarrenr	}
503145516Sdarrenr#ifdef IPSEC
504145516Sdarrenr	m->m_pkthdr.rcvif = NULL;
505145516Sdarrenr#endif
506145516Sdarrenr
507145516Sdarrenr	fnew.fin_ifp = fin->fin_ifp;
508145516Sdarrenr	fnew.fin_flx = FI_NOCKSUM;
509145516Sdarrenr	fnew.fin_m = m;
510145516Sdarrenr	fnew.fin_ip = ip;
511255332Scy	fnew.fin_mp = &m;
512145516Sdarrenr	fnew.fin_hlen = hlen;
513145516Sdarrenr	fnew.fin_dp = (char *)ip + hlen;
514255332Scy	(void) ipf_makefrip(hlen, ip, &fnew);
515145516Sdarrenr
516255332Scy	return ipf_fastroute(m, &m, &fnew, NULL);
517145516Sdarrenr}
518145516Sdarrenr
519145516Sdarrenr
520255332Scyint
521255332Scyipf_send_icmp_err(type, fin, dst)
522255332Scy	int type;
523255332Scy	fr_info_t *fin;
524255332Scy	int dst;
525145516Sdarrenr{
526145516Sdarrenr	int err, hlen, xtra, iclen, ohlen, avail, code;
527145516Sdarrenr	struct in_addr dst4;
528145516Sdarrenr	struct icmp *icmp;
529145516Sdarrenr	struct mbuf *m;
530255332Scy	i6addr_t dst6;
531145516Sdarrenr	void *ifp;
532145516Sdarrenr#ifdef USE_INET6
533145516Sdarrenr	ip6_t *ip6;
534145516Sdarrenr#endif
535145516Sdarrenr	ip_t *ip, *ip2;
536145516Sdarrenr
537172776Sdarrenr	if ((type < 0) || (type >= ICMP_MAXTYPE))
538145516Sdarrenr		return -1;
539145516Sdarrenr
540145516Sdarrenr	code = fin->fin_icode;
541145516Sdarrenr#ifdef USE_INET6
542255332Scy#if 0
543255332Scy	/* XXX Fix an off by one error: s/>/>=/
544255332Scy	 was:
545255332Scy	 if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int)))
546255332Scy	 Fix obtained from NetBSD ip_fil_netbsd.c r1.4: */
547255332Scy#endif
548255332Scy	if ((code < 0) || (code >= sizeof(icmptoicmp6unreach)/sizeof(int)))
549145516Sdarrenr		return -1;
550145516Sdarrenr#endif
551145516Sdarrenr
552255332Scy	if (ipf_checkl4sum(fin) == -1)
553145516Sdarrenr		return -1;
554145516Sdarrenr#ifdef MGETHDR
555260715Sglebius	MGETHDR(m, M_NOWAIT, MT_HEADER);
556145516Sdarrenr#else
557260715Sglebius	MGET(m, M_NOWAIT, MT_HEADER);
558145516Sdarrenr#endif
559145516Sdarrenr	if (m == NULL)
560145516Sdarrenr		return -1;
561145516Sdarrenr	avail = MHLEN;
562145516Sdarrenr
563145516Sdarrenr	xtra = 0;
564145516Sdarrenr	hlen = 0;
565145516Sdarrenr	ohlen = 0;
566255332Scy	dst4.s_addr = 0;
567145516Sdarrenr	ifp = fin->fin_ifp;
568145516Sdarrenr	if (fin->fin_v == 4) {
569255332Scy		if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT))
570145516Sdarrenr			switch (ntohs(fin->fin_data[0]) >> 8)
571145516Sdarrenr			{
572145516Sdarrenr			case ICMP_ECHO :
573145516Sdarrenr			case ICMP_TSTAMP :
574145516Sdarrenr			case ICMP_IREQ :
575145516Sdarrenr			case ICMP_MASKREQ :
576145516Sdarrenr				break;
577145516Sdarrenr			default :
578145516Sdarrenr				FREE_MB_T(m);
579145516Sdarrenr				return 0;
580145516Sdarrenr			}
581145516Sdarrenr
582145516Sdarrenr		if (dst == 0) {
583255332Scy			if (ipf_ifpaddr(&ipfmain, 4, FRI_NORMAL, ifp,
584255332Scy					&dst6, NULL) == -1) {
585145516Sdarrenr				FREE_MB_T(m);
586145516Sdarrenr				return -1;
587145516Sdarrenr			}
588255332Scy			dst4 = dst6.in4;
589145516Sdarrenr		} else
590145516Sdarrenr			dst4.s_addr = fin->fin_daddr;
591145516Sdarrenr
592145516Sdarrenr		hlen = sizeof(ip_t);
593145516Sdarrenr		ohlen = fin->fin_hlen;
594255332Scy		iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen;
595145516Sdarrenr		if (fin->fin_hlen < fin->fin_plen)
596145516Sdarrenr			xtra = MIN(fin->fin_dlen, 8);
597145516Sdarrenr		else
598145516Sdarrenr			xtra = 0;
599145516Sdarrenr	}
600145516Sdarrenr
601145516Sdarrenr#ifdef USE_INET6
602145516Sdarrenr	else if (fin->fin_v == 6) {
603145516Sdarrenr		hlen = sizeof(ip6_t);
604145516Sdarrenr		ohlen = sizeof(ip6_t);
605255332Scy		iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen;
606145516Sdarrenr		type = icmptoicmp6types[type];
607145516Sdarrenr		if (type == ICMP6_DST_UNREACH)
608145516Sdarrenr			code = icmptoicmp6unreach[code];
609145516Sdarrenr
610255332Scy		if (iclen + max_linkhdr + fin->fin_plen > avail) {
611276750Srwatson			if (!(MCLGET(m, M_NOWAIT))) {
612145516Sdarrenr				FREE_MB_T(m);
613145516Sdarrenr				return -1;
614145516Sdarrenr			}
615145516Sdarrenr			avail = MCLBYTES;
616145516Sdarrenr		}
617255332Scy		xtra = MIN(fin->fin_plen, avail - iclen - max_linkhdr);
618255332Scy		xtra = MIN(xtra, IPV6_MMTU - iclen);
619145516Sdarrenr		if (dst == 0) {
620255332Scy			if (ipf_ifpaddr(&ipfmain, 6, FRI_NORMAL, ifp,
621255332Scy					&dst6, NULL) == -1) {
622145516Sdarrenr				FREE_MB_T(m);
623145516Sdarrenr				return -1;
624145516Sdarrenr			}
625145516Sdarrenr		} else
626145516Sdarrenr			dst6 = fin->fin_dst6;
627145516Sdarrenr	}
628145516Sdarrenr#endif
629145516Sdarrenr	else {
630145516Sdarrenr		FREE_MB_T(m);
631145516Sdarrenr		return -1;
632145516Sdarrenr	}
633145516Sdarrenr
634145516Sdarrenr	avail -= (max_linkhdr + iclen);
635145516Sdarrenr	if (avail < 0) {
636145516Sdarrenr		FREE_MB_T(m);
637145516Sdarrenr		return -1;
638145516Sdarrenr	}
639145516Sdarrenr	if (xtra > avail)
640145516Sdarrenr		xtra = avail;
641145516Sdarrenr	iclen += xtra;
642145516Sdarrenr	m->m_data += max_linkhdr;
643145516Sdarrenr	m->m_pkthdr.rcvif = (struct ifnet *)0;
644145516Sdarrenr	m->m_pkthdr.len = iclen;
645145516Sdarrenr	m->m_len = iclen;
646145516Sdarrenr	ip = mtod(m, ip_t *);
647145516Sdarrenr	icmp = (struct icmp *)((char *)ip + hlen);
648145516Sdarrenr	ip2 = (ip_t *)&icmp->icmp_ip;
649145516Sdarrenr
650145516Sdarrenr	icmp->icmp_type = type;
651145516Sdarrenr	icmp->icmp_code = fin->fin_icode;
652145516Sdarrenr	icmp->icmp_cksum = 0;
653145516Sdarrenr#ifdef icmp_nextmtu
654255332Scy	if (type == ICMP_UNREACH && fin->fin_icode == ICMP_UNREACH_NEEDFRAG) {
655255332Scy		if (fin->fin_mtu != 0) {
656255332Scy			icmp->icmp_nextmtu = htons(fin->fin_mtu);
657255332Scy
658255332Scy		} else if (ifp != NULL) {
659255332Scy			icmp->icmp_nextmtu = htons(GETIFMTU_4(ifp));
660255332Scy
661255332Scy		} else {	/* make up a number... */
662255332Scy			icmp->icmp_nextmtu = htons(fin->fin_plen - 20);
663255332Scy		}
664255332Scy	}
665145516Sdarrenr#endif
666145516Sdarrenr
667145516Sdarrenr	bcopy((char *)fin->fin_ip, (char *)ip2, ohlen);
668145516Sdarrenr
669145516Sdarrenr#ifdef USE_INET6
670145516Sdarrenr	ip6 = (ip6_t *)ip;
671145516Sdarrenr	if (fin->fin_v == 6) {
672145516Sdarrenr		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
673145516Sdarrenr		ip6->ip6_plen = htons(iclen - hlen);
674145516Sdarrenr		ip6->ip6_nxt = IPPROTO_ICMPV6;
675145516Sdarrenr		ip6->ip6_hlim = 0;
676255332Scy		ip6->ip6_src = dst6.in6;
677255332Scy		ip6->ip6_dst = fin->fin_src6.in6;
678145516Sdarrenr		if (xtra > 0)
679145516Sdarrenr			bcopy((char *)fin->fin_ip + ohlen,
680145516Sdarrenr			      (char *)&icmp->icmp_ip + ohlen, xtra);
681145516Sdarrenr		icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6,
682145516Sdarrenr					     sizeof(*ip6), iclen - hlen);
683145516Sdarrenr	} else
684145516Sdarrenr#endif
685145516Sdarrenr	{
686145516Sdarrenr		ip->ip_p = IPPROTO_ICMP;
687145516Sdarrenr		ip->ip_src.s_addr = dst4.s_addr;
688145516Sdarrenr		ip->ip_dst.s_addr = fin->fin_saddr;
689145516Sdarrenr
690145516Sdarrenr		if (xtra > 0)
691145516Sdarrenr			bcopy((char *)fin->fin_ip + ohlen,
692145516Sdarrenr			      (char *)&icmp->icmp_ip + ohlen, xtra);
693145516Sdarrenr		icmp->icmp_cksum = ipf_cksum((u_short *)icmp,
694145516Sdarrenr					     sizeof(*icmp) + 8);
695255332Scy		ip->ip_len = htons(iclen);
696145516Sdarrenr		ip->ip_p = IPPROTO_ICMP;
697145516Sdarrenr	}
698255332Scy	err = ipf_send_ip(fin, m);
699145516Sdarrenr	return err;
700145516Sdarrenr}
701145516Sdarrenr
702145516Sdarrenr
703145516Sdarrenr
704145516Sdarrenr
705173181Sdarrenr/*
706173181Sdarrenr * m0 - pointer to mbuf where the IP packet starts
707173181Sdarrenr * mpp - pointer to the mbuf pointer that is the start of the mbuf chain
708173181Sdarrenr */
709255332Scyint
710255332Scyipf_fastroute(m0, mpp, fin, fdp)
711255332Scy	mb_t *m0, **mpp;
712255332Scy	fr_info_t *fin;
713255332Scy	frdest_t *fdp;
714145516Sdarrenr{
715145516Sdarrenr	register struct ip *ip, *mhip;
716173181Sdarrenr	register struct mbuf *m = *mpp;
717145516Sdarrenr	int len, off, error = 0, hlen, code;
718145516Sdarrenr	struct ifnet *ifp, *sifp;
719293628Smelifaro	struct sockaddr_in dst;
720293628Smelifaro	struct nhop4_extended nh4;
721293628Smelifaro	int has_nhop = 0;
722293628Smelifaro	u_long fibnum = 0;
723145516Sdarrenr	u_short ip_off;
724255332Scy	frdest_t node;
725145516Sdarrenr	frentry_t *fr;
726145516Sdarrenr
727145516Sdarrenr#ifdef M_WRITABLE
728145516Sdarrenr	/*
729145516Sdarrenr	* HOT FIX/KLUDGE:
730145516Sdarrenr	*
731145516Sdarrenr	* If the mbuf we're about to send is not writable (because of
732145516Sdarrenr	* a cluster reference, for example) we'll need to make a copy
733145516Sdarrenr	* of it since this routine modifies the contents.
734145516Sdarrenr	*
735145516Sdarrenr	* If you have non-crappy network hardware that can transmit data
736145516Sdarrenr	* from the mbuf, rather than making a copy, this is gonna be a
737145516Sdarrenr	* problem.
738145516Sdarrenr	*/
739145516Sdarrenr	if (M_WRITABLE(m) == 0) {
740260715Sglebius		m0 = m_dup(m, M_NOWAIT);
741298030Scy		if (m0 != NULL) {
742145516Sdarrenr			FREE_MB_T(m);
743145516Sdarrenr			m = m0;
744145516Sdarrenr			*mpp = m;
745145516Sdarrenr		} else {
746145516Sdarrenr			error = ENOBUFS;
747145516Sdarrenr			FREE_MB_T(m);
748161356Sguido			goto done;
749145516Sdarrenr		}
750145516Sdarrenr	}
751145516Sdarrenr#endif
752145516Sdarrenr
753145516Sdarrenr#ifdef USE_INET6
754145516Sdarrenr	if (fin->fin_v == 6) {
755145516Sdarrenr		/*
756145516Sdarrenr		 * currently "to <if>" and "to <if>:ip#" are not supported
757145516Sdarrenr		 * for IPv6
758145516Sdarrenr		 */
759255332Scy		return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
760145516Sdarrenr	}
761145516Sdarrenr#endif
762145516Sdarrenr
763145516Sdarrenr	hlen = fin->fin_hlen;
764145516Sdarrenr	ip = mtod(m0, struct ip *);
765255332Scy	ifp = NULL;
766145516Sdarrenr
767145516Sdarrenr	/*
768145516Sdarrenr	 * Route packet.
769145516Sdarrenr	 */
770293628Smelifaro	bzero(&dst, sizeof (dst));
771293628Smelifaro	dst.sin_family = AF_INET;
772293628Smelifaro	dst.sin_addr = ip->ip_dst;
773293628Smelifaro	dst.sin_len = sizeof(dst);
774145516Sdarrenr
775145516Sdarrenr	fr = fin->fin_fr;
776255332Scy	if ((fr != NULL) && !(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) &&
777255332Scy	    (fdp->fd_type == FRD_DSTLIST)) {
778255332Scy		if (ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node) == 0)
779255332Scy			fdp = &node;
780255332Scy	}
781255332Scy
782145516Sdarrenr	if (fdp != NULL)
783255332Scy		ifp = fdp->fd_ptr;
784145516Sdarrenr	else
785145516Sdarrenr		ifp = fin->fin_ifp;
786145516Sdarrenr
787255332Scy	if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) {
788145516Sdarrenr		error = -2;
789145516Sdarrenr		goto bad;
790145516Sdarrenr	}
791145516Sdarrenr
792161356Sguido	if ((fdp != NULL) && (fdp->fd_ip.s_addr != 0))
793293628Smelifaro		dst.sin_addr = fdp->fd_ip;
794145516Sdarrenr
795293628Smelifaro	fibnum = M_GETFIB(m0);
796293628Smelifaro	if (fib4_lookup_nh_ext(fibnum, dst.sin_addr, NHR_REF, 0, &nh4) != 0) {
797145516Sdarrenr		if (in_localaddr(ip->ip_dst))
798145516Sdarrenr			error = EHOSTUNREACH;
799145516Sdarrenr		else
800145516Sdarrenr			error = ENETUNREACH;
801145516Sdarrenr		goto bad;
802145516Sdarrenr	}
803145516Sdarrenr
804293628Smelifaro	has_nhop = 1;
805293628Smelifaro	if (ifp == NULL)
806293628Smelifaro		ifp = nh4.nh_ifp;
807293628Smelifaro	if (nh4.nh_flags & NHF_GATEWAY)
808293628Smelifaro		dst.sin_addr = nh4.nh_addr;
809293628Smelifaro
810145516Sdarrenr	/*
811145516Sdarrenr	 * For input packets which are being "fastrouted", they won't
812145516Sdarrenr	 * go back through output filtering and miss their chance to get
813170268Sdarrenr	 * NAT'd and counted.  Duplicated packets aren't considered to be
814170268Sdarrenr	 * part of the normal packet stream, so do not NAT them or pass
815170268Sdarrenr	 * them through stateful checking, etc.
816145516Sdarrenr	 */
817170268Sdarrenr	if ((fdp != &fr->fr_dif) && (fin->fin_out == 0)) {
818145516Sdarrenr		sifp = fin->fin_ifp;
819145516Sdarrenr		fin->fin_ifp = ifp;
820145516Sdarrenr		fin->fin_out = 1;
821255332Scy		(void) ipf_acctpkt(fin, NULL);
822145516Sdarrenr		fin->fin_fr = NULL;
823145516Sdarrenr		if (!fr || !(fr->fr_flags & FR_RETMASK)) {
824145516Sdarrenr			u_32_t pass;
825145516Sdarrenr
826255332Scy			(void) ipf_state_check(fin, &pass);
827145516Sdarrenr		}
828145516Sdarrenr
829255332Scy		switch (ipf_nat_checkout(fin, NULL))
830145516Sdarrenr		{
831145516Sdarrenr		case 0 :
832145516Sdarrenr			break;
833145516Sdarrenr		case 1 :
834145516Sdarrenr			ip->ip_sum = 0;
835145516Sdarrenr			break;
836145516Sdarrenr		case -1 :
837145516Sdarrenr			error = -1;
838173181Sdarrenr			goto bad;
839145516Sdarrenr			break;
840145516Sdarrenr		}
841145516Sdarrenr
842145516Sdarrenr		fin->fin_ifp = sifp;
843145516Sdarrenr		fin->fin_out = 0;
844145516Sdarrenr	} else
845145516Sdarrenr		ip->ip_sum = 0;
846145516Sdarrenr	/*
847145516Sdarrenr	 * If small enough for interface, can just send directly.
848145516Sdarrenr	 */
849255332Scy	if (ntohs(ip->ip_len) <= ifp->if_mtu) {
850145516Sdarrenr		if (!ip->ip_sum)
851145516Sdarrenr			ip->ip_sum = in_cksum(m, hlen);
852293628Smelifaro		error = (*ifp->if_output)(ifp, m, (struct sockaddr *)&dst,
853293628Smelifaro			    NULL
854255332Scy			);
855145516Sdarrenr		goto done;
856145516Sdarrenr	}
857145516Sdarrenr	/*
858145516Sdarrenr	 * Too large for interface; fragment if possible.
859145516Sdarrenr	 * Must be able to put at least 8 bytes per fragment.
860145516Sdarrenr	 */
861145516Sdarrenr	ip_off = ntohs(ip->ip_off);
862145516Sdarrenr	if (ip_off & IP_DF) {
863145516Sdarrenr		error = EMSGSIZE;
864145516Sdarrenr		goto bad;
865145516Sdarrenr	}
866145516Sdarrenr	len = (ifp->if_mtu - hlen) &~ 7;
867145516Sdarrenr	if (len < 8) {
868145516Sdarrenr		error = EMSGSIZE;
869145516Sdarrenr		goto bad;
870145516Sdarrenr	}
871145516Sdarrenr
872145516Sdarrenr    {
873145516Sdarrenr	int mhlen, firstlen = len;
874145516Sdarrenr	struct mbuf **mnext = &m->m_act;
875145516Sdarrenr
876145516Sdarrenr	/*
877145516Sdarrenr	 * Loop through length of segment after first fragment,
878145516Sdarrenr	 * make new header and copy data of each part and link onto chain.
879145516Sdarrenr	 */
880145516Sdarrenr	m0 = m;
881145516Sdarrenr	mhlen = sizeof (struct ip);
882255332Scy	for (off = hlen + len; off < ntohs(ip->ip_len); off += len) {
883145516Sdarrenr#ifdef MGETHDR
884260715Sglebius		MGETHDR(m, M_NOWAIT, MT_HEADER);
885145516Sdarrenr#else
886260715Sglebius		MGET(m, M_NOWAIT, MT_HEADER);
887145516Sdarrenr#endif
888298030Scy		if (m == NULL) {
889145516Sdarrenr			m = m0;
890145516Sdarrenr			error = ENOBUFS;
891145516Sdarrenr			goto bad;
892145516Sdarrenr		}
893145516Sdarrenr		m->m_data += max_linkhdr;
894145516Sdarrenr		mhip = mtod(m, struct ip *);
895145516Sdarrenr		bcopy((char *)ip, (char *)mhip, sizeof(*ip));
896145516Sdarrenr		if (hlen > sizeof (struct ip)) {
897145516Sdarrenr			mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
898145516Sdarrenr			IP_HL_A(mhip, mhlen >> 2);
899145516Sdarrenr		}
900145516Sdarrenr		m->m_len = mhlen;
901145516Sdarrenr		mhip->ip_off = ((off - hlen) >> 3) + ip_off;
902255332Scy		if (off + len >= ntohs(ip->ip_len))
903255332Scy			len = ntohs(ip->ip_len) - off;
904145516Sdarrenr		else
905145516Sdarrenr			mhip->ip_off |= IP_MF;
906145516Sdarrenr		mhip->ip_len = htons((u_short)(len + mhlen));
907161356Sguido		*mnext = m;
908145516Sdarrenr		m->m_next = m_copy(m0, off, len);
909145516Sdarrenr		if (m->m_next == 0) {
910145516Sdarrenr			error = ENOBUFS;	/* ??? */
911145516Sdarrenr			goto sendorfree;
912145516Sdarrenr		}
913145516Sdarrenr		m->m_pkthdr.len = mhlen + len;
914145516Sdarrenr		m->m_pkthdr.rcvif = NULL;
915145516Sdarrenr		mhip->ip_off = htons((u_short)mhip->ip_off);
916145516Sdarrenr		mhip->ip_sum = 0;
917145516Sdarrenr		mhip->ip_sum = in_cksum(m, mhlen);
918145516Sdarrenr		mnext = &m->m_act;
919145516Sdarrenr	}
920145516Sdarrenr	/*
921145516Sdarrenr	 * Update first fragment by trimming what's been copied out
922145516Sdarrenr	 * and updating header, then send each fragment (in order).
923145516Sdarrenr	 */
924145516Sdarrenr	m_adj(m0, hlen + firstlen - ip->ip_len);
925145516Sdarrenr	ip->ip_len = htons((u_short)(hlen + firstlen));
926145516Sdarrenr	ip->ip_off = htons((u_short)IP_MF);
927145516Sdarrenr	ip->ip_sum = 0;
928145516Sdarrenr	ip->ip_sum = in_cksum(m0, hlen);
929145516Sdarrenrsendorfree:
930145516Sdarrenr	for (m = m0; m; m = m0) {
931145516Sdarrenr		m0 = m->m_act;
932145516Sdarrenr		m->m_act = 0;
933145516Sdarrenr		if (error == 0)
934145516Sdarrenr			error = (*ifp->if_output)(ifp, m,
935293628Smelifaro			    (struct sockaddr *)&dst,
936293628Smelifaro			    NULL
937255332Scy			    );
938145516Sdarrenr		else
939145516Sdarrenr			FREE_MB_T(m);
940145516Sdarrenr	}
941255332Scy    }
942145516Sdarrenrdone:
943145516Sdarrenr	if (!error)
944255332Scy		ipfmain.ipf_frouteok[0]++;
945145516Sdarrenr	else
946255332Scy		ipfmain.ipf_frouteok[1]++;
947145516Sdarrenr
948293628Smelifaro	if (has_nhop)
949293628Smelifaro		fib4_free_nh_ext(fibnum, &nh4);
950293628Smelifaro
951145516Sdarrenr	return 0;
952145516Sdarrenrbad:
953145516Sdarrenr	if (error == EMSGSIZE) {
954145516Sdarrenr		sifp = fin->fin_ifp;
955145516Sdarrenr		code = fin->fin_icode;
956145516Sdarrenr		fin->fin_icode = ICMP_UNREACH_NEEDFRAG;
957145516Sdarrenr		fin->fin_ifp = ifp;
958255332Scy		(void) ipf_send_icmp_err(ICMP_UNREACH, fin, 1);
959145516Sdarrenr		fin->fin_ifp = sifp;
960145516Sdarrenr		fin->fin_icode = code;
961145516Sdarrenr	}
962145516Sdarrenr	FREE_MB_T(m);
963145516Sdarrenr	goto done;
964145516Sdarrenr}
965145516Sdarrenr
966145516Sdarrenr
967255332Scyint
968255332Scyipf_verifysrc(fin)
969255332Scy	fr_info_t *fin;
970145516Sdarrenr{
971293628Smelifaro	struct nhop4_basic nh4;
972145516Sdarrenr
973293628Smelifaro	if (fib4_lookup_nh_basic(0, fin->fin_src, 0, 0, &nh4) != 0)
974293628Smelifaro		return (0);
975293628Smelifaro	return (fin->fin_ifp == nh4.nh_ifp);
976145516Sdarrenr}
977145516Sdarrenr
978145516Sdarrenr
979145516Sdarrenr/*
980145516Sdarrenr * return the first IP Address associated with an interface
981145516Sdarrenr */
982255332Scyint
983255332Scyipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask)
984255332Scy	ipf_main_softc_t *softc;
985255332Scy	int v, atype;
986255332Scy	void *ifptr;
987255332Scy	i6addr_t *inp, *inpmask;
988145516Sdarrenr{
989145516Sdarrenr#ifdef USE_INET6
990145516Sdarrenr	struct in6_addr *inp6 = NULL;
991145516Sdarrenr#endif
992145516Sdarrenr	struct sockaddr *sock, *mask;
993145516Sdarrenr	struct sockaddr_in *sin;
994145516Sdarrenr	struct ifaddr *ifa;
995145516Sdarrenr	struct ifnet *ifp;
996145516Sdarrenr
997145516Sdarrenr	if ((ifptr == NULL) || (ifptr == (void *)-1))
998145516Sdarrenr		return -1;
999145516Sdarrenr
1000145516Sdarrenr	sin = NULL;
1001145516Sdarrenr	ifp = ifptr;
1002145516Sdarrenr
1003145516Sdarrenr	if (v == 4)
1004255332Scy		inp->in4.s_addr = 0;
1005145516Sdarrenr#ifdef USE_INET6
1006145516Sdarrenr	else if (v == 6)
1007255332Scy		bzero((char *)inp, sizeof(*inp));
1008145516Sdarrenr#endif
1009145516Sdarrenr	ifa = TAILQ_FIRST(&ifp->if_addrhead);
1010145516Sdarrenr
1011145516Sdarrenr	sock = ifa->ifa_addr;
1012145516Sdarrenr	while (sock != NULL && ifa != NULL) {
1013145516Sdarrenr		sin = (struct sockaddr_in *)sock;
1014145516Sdarrenr		if ((v == 4) && (sin->sin_family == AF_INET))
1015145516Sdarrenr			break;
1016145516Sdarrenr#ifdef USE_INET6
1017145516Sdarrenr		if ((v == 6) && (sin->sin_family == AF_INET6)) {
1018145516Sdarrenr			inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr;
1019145516Sdarrenr			if (!IN6_IS_ADDR_LINKLOCAL(inp6) &&
1020145516Sdarrenr			    !IN6_IS_ADDR_LOOPBACK(inp6))
1021145516Sdarrenr				break;
1022145516Sdarrenr		}
1023145516Sdarrenr#endif
1024145516Sdarrenr		ifa = TAILQ_NEXT(ifa, ifa_link);
1025145516Sdarrenr		if (ifa != NULL)
1026145516Sdarrenr			sock = ifa->ifa_addr;
1027145516Sdarrenr	}
1028145516Sdarrenr
1029145516Sdarrenr	if (ifa == NULL || sin == NULL)
1030145516Sdarrenr		return -1;
1031145516Sdarrenr
1032145516Sdarrenr	mask = ifa->ifa_netmask;
1033145516Sdarrenr	if (atype == FRI_BROADCAST)
1034145516Sdarrenr		sock = ifa->ifa_broadaddr;
1035145516Sdarrenr	else if (atype == FRI_PEERADDR)
1036145516Sdarrenr		sock = ifa->ifa_dstaddr;
1037145516Sdarrenr
1038161356Sguido	if (sock == NULL)
1039161356Sguido		return -1;
1040161356Sguido
1041145516Sdarrenr#ifdef USE_INET6
1042145516Sdarrenr	if (v == 6) {
1043255332Scy		return ipf_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock,
1044255332Scy					 (struct sockaddr_in6 *)mask,
1045255332Scy					 inp, inpmask);
1046145516Sdarrenr	}
1047145516Sdarrenr#endif
1048255332Scy	return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)sock,
1049255332Scy				 (struct sockaddr_in *)mask,
1050255332Scy				 &inp->in4, &inpmask->in4);
1051145516Sdarrenr}
1052145516Sdarrenr
1053145516Sdarrenr
1054255332Scyu_32_t
1055255332Scyipf_newisn(fin)
1056255332Scy	fr_info_t *fin;
1057145516Sdarrenr{
1058145516Sdarrenr	u_32_t newiss;
1059145516Sdarrenr	newiss = arc4random();
1060145516Sdarrenr	return newiss;
1061145516Sdarrenr}
1062145516Sdarrenr
1063145516Sdarrenr
1064255332ScyINLINE int
1065255332Scyipf_checkv4sum(fin)
1066255332Scy	fr_info_t *fin;
1067145516Sdarrenr{
1068145516Sdarrenr#ifdef CSUM_DATA_VALID
1069145516Sdarrenr	int manual = 0;
1070145516Sdarrenr	u_short sum;
1071145516Sdarrenr	ip_t *ip;
1072145516Sdarrenr	mb_t *m;
1073145516Sdarrenr
1074145516Sdarrenr	if ((fin->fin_flx & FI_NOCKSUM) != 0)
1075255332Scy		return 0;
1076145516Sdarrenr
1077255332Scy	if ((fin->fin_flx & FI_SHORT) != 0)
1078255332Scy		return 1;
1079172776Sdarrenr
1080255332Scy	if (fin->fin_cksum != FI_CK_NEEDED)
1081255332Scy		return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1;
1082255332Scy
1083145516Sdarrenr	m = fin->fin_m;
1084145516Sdarrenr	if (m == NULL) {
1085145516Sdarrenr		manual = 1;
1086145516Sdarrenr		goto skipauto;
1087145516Sdarrenr	}
1088145516Sdarrenr	ip = fin->fin_ip;
1089145516Sdarrenr
1090255332Scy	if ((m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)) ==
1091255332Scy	    CSUM_IP_CHECKED) {
1092255332Scy		fin->fin_cksum = FI_CK_BAD;
1093255332Scy		fin->fin_flx |= FI_BAD;
1094297632Scy		DT2(ipf_fi_bad_checkv4sum_csum_ip_checked, fr_info_t *, fin, u_int, m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID));
1095255332Scy		return -1;
1096255332Scy	}
1097145516Sdarrenr	if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
1098288910Scy		/* Depending on the driver, UDP may have zero checksum */
1099288910Scy		if (fin->fin_p == IPPROTO_UDP && (fin->fin_flx &
1100288910Scy		    (FI_FRAG|FI_SHORT|FI_BAD)) == 0) {
1101288910Scy			udphdr_t *udp = fin->fin_dp;
1102288910Scy			if (udp->uh_sum == 0) {
1103288910Scy				/*
1104288910Scy				 * we're good no matter what the hardware
1105288910Scy				 * checksum flags and csum_data say (handling
1106288910Scy				 * of csum_data for zero UDP checksum is not
1107288910Scy				 * consistent across all drivers)
1108288910Scy				 */
1109288910Scy				fin->fin_cksum = 1;
1110288910Scy				return 0;
1111288910Scy			}
1112288910Scy		}
1113288910Scy
1114145516Sdarrenr		if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
1115145516Sdarrenr			sum = m->m_pkthdr.csum_data;
1116145516Sdarrenr		else
1117145516Sdarrenr			sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
1118145516Sdarrenr					htonl(m->m_pkthdr.csum_data +
1119255332Scy					fin->fin_dlen + fin->fin_p));
1120145516Sdarrenr		sum ^= 0xffff;
1121172776Sdarrenr		if (sum != 0) {
1122255332Scy			fin->fin_cksum = FI_CK_BAD;
1123145516Sdarrenr			fin->fin_flx |= FI_BAD;
1124297632Scy			DT2(ipf_fi_bad_checkv4sum_sum, fr_info_t *, fin, u_int, sum);
1125172776Sdarrenr		} else {
1126255332Scy			fin->fin_cksum = FI_CK_SUMOK;
1127255332Scy			return 0;
1128172776Sdarrenr		}
1129255332Scy	} else {
1130255332Scy		if (m->m_pkthdr.csum_flags == CSUM_DELAY_DATA) {
1131255332Scy			fin->fin_cksum = FI_CK_L4FULL;
1132255332Scy			return 0;
1133255332Scy		} else if (m->m_pkthdr.csum_flags == CSUM_TCP ||
1134255332Scy			   m->m_pkthdr.csum_flags == CSUM_UDP) {
1135255332Scy			fin->fin_cksum = FI_CK_L4PART;
1136255332Scy			return 0;
1137255332Scy		} else if (m->m_pkthdr.csum_flags == CSUM_IP) {
1138255332Scy			fin->fin_cksum = FI_CK_L4PART;
1139255332Scy			return 0;
1140255332Scy		} else {
1141255332Scy			manual = 1;
1142255332Scy		}
1143255332Scy	}
1144145516Sdarrenrskipauto:
1145255332Scy	if (manual != 0) {
1146255332Scy		if (ipf_checkl4sum(fin) == -1) {
1147145516Sdarrenr			fin->fin_flx |= FI_BAD;
1148297632Scy			DT2(ipf_fi_bad_checkv4sum_manual, fr_info_t *, fin, u_int, manual);
1149255332Scy			return -1;
1150255332Scy		}
1151255332Scy	}
1152145516Sdarrenr#else
1153255332Scy	if (ipf_checkl4sum(fin) == -1) {
1154145516Sdarrenr		fin->fin_flx |= FI_BAD;
1155297632Scy		DT2(ipf_fi_bad_checkv4sum_checkl4sum, fr_info_t *, fin, u_int, -1);
1156255332Scy		return -1;
1157255332Scy	}
1158145516Sdarrenr#endif
1159255332Scy	return 0;
1160145516Sdarrenr}
1161145516Sdarrenr
1162145516Sdarrenr
1163145516Sdarrenr#ifdef USE_INET6
1164255332ScyINLINE int
1165255332Scyipf_checkv6sum(fin)
1166255332Scy	fr_info_t *fin;
1167145516Sdarrenr{
1168255332Scy	if ((fin->fin_flx & FI_NOCKSUM) != 0)
1169297632Scy		DT(ipf_checkv6sum_fi_nocksum);
1170255332Scy		return 0;
1171255332Scy
1172255332Scy	if ((fin->fin_flx & FI_SHORT) != 0)
1173297632Scy		DT(ipf_checkv6sum_fi_short);
1174255332Scy		return 1;
1175255332Scy
1176255332Scy	if (fin->fin_cksum != FI_CK_NEEDED)
1177297632Scy		DT(ipf_checkv6sum_fi_ck_needed);
1178255332Scy		return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1;
1179255332Scy
1180255332Scy	if (ipf_checkl4sum(fin) == -1) {
1181145516Sdarrenr		fin->fin_flx |= FI_BAD;
1182297632Scy		DT2(ipf_fi_bad_checkv6sum_checkl4sum, fr_info_t *, fin, u_int, -1);
1183255332Scy		return -1;
1184255332Scy	}
1185255332Scy	return 0;
1186145516Sdarrenr}
1187145516Sdarrenr#endif /* USE_INET6 */
1188145516Sdarrenr
1189145516Sdarrenr
1190255332Scysize_t
1191255332Scymbufchainlen(m0)
1192255332Scy	struct mbuf *m0;
1193255332Scy	{
1194145516Sdarrenr	size_t len;
1195145516Sdarrenr
1196145516Sdarrenr	if ((m0->m_flags & M_PKTHDR) != 0) {
1197145516Sdarrenr		len = m0->m_pkthdr.len;
1198145516Sdarrenr	} else {
1199145516Sdarrenr		struct mbuf *m;
1200145516Sdarrenr
1201145516Sdarrenr		for (m = m0, len = 0; m != NULL; m = m->m_next)
1202145516Sdarrenr			len += m->m_len;
1203145516Sdarrenr	}
1204145516Sdarrenr	return len;
1205145516Sdarrenr}
1206145516Sdarrenr
1207145516Sdarrenr
1208145516Sdarrenr/* ------------------------------------------------------------------------ */
1209255332Scy/* Function:    ipf_pullup                                                  */
1210145516Sdarrenr/* Returns:     NULL == pullup failed, else pointer to protocol header      */
1211255332Scy/* Parameters:  xmin(I)- pointer to buffer where data packet starts         */
1212145516Sdarrenr/*              fin(I) - pointer to packet information                      */
1213145516Sdarrenr/*              len(I) - number of bytes to pullup                          */
1214145516Sdarrenr/*                                                                          */
1215145516Sdarrenr/* Attempt to move at least len bytes (from the start of the buffer) into a */
1216145516Sdarrenr/* single buffer for ease of access.  Operating system native functions are */
1217145516Sdarrenr/* used to manage buffers - if necessary.  If the entire packet ends up in  */
1218255332Scy/* a single buffer, set the FI_COALESCE flag even though ipf_coalesce() has */
1219145516Sdarrenr/* not been called.  Both fin_ip and fin_dp are updated before exiting _IF_ */
1220145516Sdarrenr/* and ONLY if the pullup succeeds.                                         */
1221145516Sdarrenr/*                                                                          */
1222255332Scy/* We assume that 'xmin' is a pointer to a buffer that is part of the chain */
1223145516Sdarrenr/* of buffers that starts at *fin->fin_mp.                                  */
1224145516Sdarrenr/* ------------------------------------------------------------------------ */
1225255332Scyvoid *
1226255332Scyipf_pullup(xmin, fin, len)
1227255332Scy	mb_t *xmin;
1228255332Scy	fr_info_t *fin;
1229255332Scy	int len;
1230145516Sdarrenr{
1231255332Scy	int dpoff, ipoff;
1232255332Scy	mb_t *m = xmin;
1233145516Sdarrenr	char *ip;
1234145516Sdarrenr
1235145516Sdarrenr	if (m == NULL)
1236145516Sdarrenr		return NULL;
1237145516Sdarrenr
1238145516Sdarrenr	ip = (char *)fin->fin_ip;
1239145516Sdarrenr	if ((fin->fin_flx & FI_COALESCE) != 0)
1240145516Sdarrenr		return ip;
1241145516Sdarrenr
1242145516Sdarrenr	ipoff = fin->fin_ipoff;
1243145516Sdarrenr	if (fin->fin_dp != NULL)
1244145516Sdarrenr		dpoff = (char *)fin->fin_dp - (char *)ip;
1245145516Sdarrenr	else
1246145516Sdarrenr		dpoff = 0;
1247145516Sdarrenr
1248145516Sdarrenr	if (M_LEN(m) < len) {
1249255332Scy		mb_t *n = *fin->fin_mp;
1250145516Sdarrenr		/*
1251145516Sdarrenr		 * Assume that M_PKTHDR is set and just work with what is left
1252145516Sdarrenr		 * rather than check..
1253145516Sdarrenr		 * Should not make any real difference, anyway.
1254145516Sdarrenr		 */
1255255332Scy		if (m != n) {
1256255332Scy			/*
1257255332Scy			 * Record the mbuf that points to the mbuf that we're
1258255332Scy			 * about to go to work on so that we can update the
1259255332Scy			 * m_next appropriately later.
1260255332Scy			 */
1261255332Scy			for (; n->m_next != m; n = n->m_next)
1262255332Scy				;
1263255332Scy		} else {
1264255332Scy			n = NULL;
1265255332Scy		}
1266255332Scy
1267255332Scy#ifdef MHLEN
1268145516Sdarrenr		if (len > MHLEN)
1269145516Sdarrenr#else
1270145516Sdarrenr		if (len > MLEN)
1271145516Sdarrenr#endif
1272145516Sdarrenr		{
1273145516Sdarrenr#ifdef HAVE_M_PULLDOWN
1274145516Sdarrenr			if (m_pulldown(m, 0, len, NULL) == NULL)
1275145516Sdarrenr				m = NULL;
1276145516Sdarrenr#else
1277145516Sdarrenr			FREE_MB_T(*fin->fin_mp);
1278145516Sdarrenr			m = NULL;
1279255332Scy			n = NULL;
1280145516Sdarrenr#endif
1281145516Sdarrenr		} else
1282145516Sdarrenr		{
1283145516Sdarrenr			m = m_pullup(m, len);
1284145516Sdarrenr		}
1285255332Scy		if (n != NULL)
1286255332Scy			n->m_next = m;
1287145516Sdarrenr		if (m == NULL) {
1288255332Scy			/*
1289255332Scy			 * When n is non-NULL, it indicates that m pointed to
1290255332Scy			 * a sub-chain (tail) of the mbuf and that the head
1291255332Scy			 * of this chain has not yet been free'd.
1292255332Scy			 */
1293255332Scy			if (n != NULL) {
1294255332Scy				FREE_MB_T(*fin->fin_mp);
1295255332Scy			}
1296255332Scy
1297255332Scy			*fin->fin_mp = NULL;
1298172776Sdarrenr			fin->fin_m = NULL;
1299145516Sdarrenr			return NULL;
1300145516Sdarrenr		}
1301172776Sdarrenr
1302255332Scy		if (n == NULL)
1303255332Scy			*fin->fin_mp = m;
1304255332Scy
1305172776Sdarrenr		while (M_LEN(m) == 0) {
1306172776Sdarrenr			m = m->m_next;
1307172776Sdarrenr		}
1308172776Sdarrenr		fin->fin_m = m;
1309145516Sdarrenr		ip = MTOD(m, char *) + ipoff;
1310255332Scy
1311255332Scy		fin->fin_ip = (ip_t *)ip;
1312255332Scy		if (fin->fin_dp != NULL)
1313255332Scy			fin->fin_dp = (char *)fin->fin_ip + dpoff;
1314255332Scy		if (fin->fin_fraghdr != NULL)
1315255332Scy			fin->fin_fraghdr = (char *)ip +
1316255332Scy					   ((char *)fin->fin_fraghdr -
1317255332Scy					    (char *)fin->fin_ip);
1318145516Sdarrenr	}
1319145516Sdarrenr
1320145516Sdarrenr	if (len == fin->fin_plen)
1321145516Sdarrenr		fin->fin_flx |= FI_COALESCE;
1322145516Sdarrenr	return ip;
1323145516Sdarrenr}
1324170268Sdarrenr
1325170268Sdarrenr
1326255332Scyint
1327255332Scyipf_inject(fin, m)
1328255332Scy	fr_info_t *fin;
1329255332Scy	mb_t *m;
1330170268Sdarrenr{
1331170268Sdarrenr	int error = 0;
1332170268Sdarrenr
1333170268Sdarrenr	if (fin->fin_out == 0) {
1334170268Sdarrenr		netisr_dispatch(NETISR_IP, m);
1335170268Sdarrenr	} else {
1336173931Sdarrenr		fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len);
1337173931Sdarrenr		fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off);
1338170268Sdarrenr		error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
1339170268Sdarrenr	}
1340170268Sdarrenr
1341170268Sdarrenr	return error;
1342170268Sdarrenr}
1343172776Sdarrenr
1344172776Sdarrenrint ipf_pfil_unhook(void) {
1345172776Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011)
1346172776Sdarrenr	struct pfil_head *ph_inet;
1347172776Sdarrenr#  ifdef USE_INET6
1348172776Sdarrenr	struct pfil_head *ph_inet6;
1349172776Sdarrenr#  endif
1350172776Sdarrenr#endif
1351172776Sdarrenr
1352172776Sdarrenr#ifdef NETBSD_PF
1353172776Sdarrenr	ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
1354172776Sdarrenr	if (ph_inet != NULL)
1355255332Scy		pfil_remove_hook((void *)ipf_check_wrapper, NULL,
1356172776Sdarrenr		    PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet);
1357172776Sdarrenr# ifdef USE_INET6
1358172776Sdarrenr	ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
1359172776Sdarrenr	if (ph_inet6 != NULL)
1360255332Scy		pfil_remove_hook((void *)ipf_check_wrapper6, NULL,
1361172776Sdarrenr		    PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6);
1362172776Sdarrenr# endif
1363172776Sdarrenr#endif
1364172776Sdarrenr
1365172776Sdarrenr	return (0);
1366172776Sdarrenr}
1367172776Sdarrenr
1368172776Sdarrenrint ipf_pfil_hook(void) {
1369172776Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011)
1370172776Sdarrenr	struct pfil_head *ph_inet;
1371172776Sdarrenr#  ifdef USE_INET6
1372172776Sdarrenr	struct pfil_head *ph_inet6;
1373172776Sdarrenr#  endif
1374172776Sdarrenr#endif
1375172776Sdarrenr
1376172776Sdarrenr# ifdef NETBSD_PF
1377172776Sdarrenr	ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
1378172776Sdarrenr#    ifdef USE_INET6
1379172776Sdarrenr	ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
1380172776Sdarrenr#    endif
1381172776Sdarrenr	if (ph_inet == NULL
1382172776Sdarrenr#    ifdef USE_INET6
1383172776Sdarrenr	    && ph_inet6 == NULL
1384172776Sdarrenr#    endif
1385255332Scy	   ) {
1386172776Sdarrenr		return ENODEV;
1387255332Scy	}
1388172776Sdarrenr
1389172776Sdarrenr	if (ph_inet != NULL)
1390255332Scy		pfil_add_hook((void *)ipf_check_wrapper, NULL,
1391172776Sdarrenr		    PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet);
1392172776Sdarrenr#  ifdef USE_INET6
1393172776Sdarrenr	if (ph_inet6 != NULL)
1394255332Scy		pfil_add_hook((void *)ipf_check_wrapper6, NULL,
1395172776Sdarrenr				      PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6);
1396172776Sdarrenr#  endif
1397172776Sdarrenr# endif
1398172776Sdarrenr	return (0);
1399172776Sdarrenr}
1400172776Sdarrenr
1401172776Sdarrenrvoid
1402172776Sdarrenripf_event_reg(void)
1403172776Sdarrenr{
1404255332Scy	ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \
1405255332Scy					       ipf_ifevent, &ipfmain, \
1406172776Sdarrenr					       EVENTHANDLER_PRI_ANY);
1407255332Scy	ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \
1408255332Scy					       ipf_ifevent, &ipfmain, \
1409172776Sdarrenr					       EVENTHANDLER_PRI_ANY);
1410255332Scy	ipf_clonetag  = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \
1411255332Scy					       &ipfmain, EVENTHANDLER_PRI_ANY);
1412172776Sdarrenr}
1413172776Sdarrenr
1414172776Sdarrenrvoid
1415172776Sdarrenripf_event_dereg(void)
1416172776Sdarrenr{
1417172776Sdarrenr	if (ipf_arrivetag != NULL) {
1418172776Sdarrenr		EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ipf_arrivetag);
1419172776Sdarrenr	}
1420172776Sdarrenr	if (ipf_departtag != NULL) {
1421172776Sdarrenr		EVENTHANDLER_DEREGISTER(ifnet_departure_event, ipf_departtag);
1422172776Sdarrenr	}
1423172776Sdarrenr	if (ipf_clonetag != NULL) {
1424172776Sdarrenr		EVENTHANDLER_DEREGISTER(if_clone_event, ipf_clonetag);
1425172776Sdarrenr	}
1426172776Sdarrenr}
1427255332Scy
1428255332Scy
1429255332Scyu_32_t
1430255332Scyipf_random()
1431255332Scy{
1432255332Scy	return arc4random();
1433255332Scy}
1434255332Scy
1435255332Scy
1436255332Scyu_int
1437255332Scyipf_pcksum(fin, hlen, sum)
1438255332Scy	fr_info_t *fin;
1439255332Scy	int hlen;
1440255332Scy	u_int sum;
1441255332Scy{
1442255332Scy	struct mbuf *m;
1443255332Scy	u_int sum2;
1444255332Scy	int off;
1445255332Scy
1446255332Scy	m = fin->fin_m;
1447255332Scy	off = (char *)fin->fin_dp - (char *)fin->fin_ip;
1448255332Scy	m->m_data += hlen;
1449255332Scy	m->m_len -= hlen;
1450255332Scy	sum2 = in_cksum(fin->fin_m, fin->fin_plen - off);
1451255332Scy	m->m_len += hlen;
1452255332Scy	m->m_data -= hlen;
1453255332Scy
1454255332Scy	/*
1455255332Scy	 * Both sum and sum2 are partial sums, so combine them together.
1456255332Scy	 */
1457255332Scy	sum += ~sum2 & 0xffff;
1458255332Scy	while (sum > 0xffff)
1459255332Scy		sum = (sum & 0xffff) + (sum >> 16);
1460255332Scy	sum2 = ~sum & 0xffff;
1461255332Scy	return sum2;
1462255332Scy}
1463