1145516Sdarrenr/*	$FreeBSD: releng/10.3/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c 289210 2015-10-13 04:19:49Z 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>
36275213Scy#if defined(__FreeBSD_version) && (__FreeBSD_version >= 800000)
37275213Scy#include <sys/jail.h>
38275213Scy#endif
39255332Scy# include <sys/mbuf.h>
40255332Scy# include <sys/sockopt.h>
41145516Sdarrenr#if !defined(__hpux)
42145516Sdarrenr# include <sys/mbuf.h>
43145516Sdarrenr#endif
44145516Sdarrenr#include <sys/socket.h>
45170268Sdarrenr# include <sys/selinfo.h>
46195699Srwatson# include <netinet/tcp_var.h>
47145516Sdarrenr
48145516Sdarrenr#include <net/if.h>
49145516Sdarrenr# include <net/if_var.h>
50170268Sdarrenr#  include <net/netisr.h>
51145516Sdarrenr#include <net/route.h>
52145516Sdarrenr#include <netinet/in.h>
53145516Sdarrenr#include <netinet/in_var.h>
54145516Sdarrenr#include <netinet/in_systm.h>
55145516Sdarrenr#include <netinet/ip.h>
56145516Sdarrenr#include <netinet/ip_var.h>
57145516Sdarrenr#include <netinet/tcp.h>
58275213Scy#if defined(__FreeBSD_version) && (__FreeBSD_version >= 800000)
59275213Scy#include <net/vnet.h>
60275213Scy#else
61275213Scy#define CURVNET_SET(arg)
62275213Scy#define CURVNET_RESTORE()
63275213Scy#endif
64145516Sdarrenr#if defined(__osf__)
65145516Sdarrenr# include <netinet/tcp_timer.h>
66145516Sdarrenr#endif
67145516Sdarrenr#include <netinet/udp.h>
68145516Sdarrenr#include <netinet/tcpip.h>
69145516Sdarrenr#include <netinet/ip_icmp.h>
70145516Sdarrenr#include "netinet/ip_compat.h"
71145516Sdarrenr#ifdef USE_INET6
72145516Sdarrenr# include <netinet/icmp6.h>
73145516Sdarrenr#endif
74145516Sdarrenr#include "netinet/ip_fil.h"
75145516Sdarrenr#include "netinet/ip_nat.h"
76145516Sdarrenr#include "netinet/ip_frag.h"
77145516Sdarrenr#include "netinet/ip_state.h"
78145516Sdarrenr#include "netinet/ip_proxy.h"
79145516Sdarrenr#include "netinet/ip_auth.h"
80145516Sdarrenr#include "netinet/ip_sync.h"
81255332Scy#include "netinet/ip_lookup.h"
82255332Scy#include "netinet/ip_dstlist.h"
83145516Sdarrenr#ifdef	IPFILTER_SCAN
84145516Sdarrenr#include "netinet/ip_scan.h"
85145516Sdarrenr#endif
86145516Sdarrenr#include "netinet/ip_pool.h"
87145516Sdarrenr# include <sys/malloc.h>
88145516Sdarrenr#include <sys/kernel.h>
89145516Sdarrenr#ifdef CSUM_DATA_VALID
90145516Sdarrenr#include <machine/in_cksum.h>
91145516Sdarrenr#endif
92145516Sdarrenrextern	int	ip_optcopy __P((struct ip *, struct ip *));
93145516Sdarrenr
94145516Sdarrenr
95145516Sdarrenr# ifdef IPFILTER_M_IPFILTER
96151897SrwatsonMALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures");
97145516Sdarrenr# endif
98145516Sdarrenr
99145516Sdarrenr
100255332Scystatic	u_short	ipid = 0;
101255332Scystatic	int	(*ipf_savep) __P((void *, ip_t *, int, void *, int, struct mbuf **));
102255332Scystatic	int	ipf_send_ip __P((fr_info_t *, mb_t *));
103255332Scystatic void	ipf_timer_func __P((void *arg));
104145516Sdarrenrint		ipf_locks_done = 0;
105145516Sdarrenr
106255332Scyipf_main_softc_t ipfmain;
107145516Sdarrenr
108145516Sdarrenr# include <sys/conf.h>
109145516Sdarrenr# if defined(NETBSD_PF)
110145516Sdarrenr#  include <net/pfil.h>
111255332Scy# endif /* NETBSD_PF */
112145516Sdarrenr/*
113255332Scy * We provide the ipf_checkp name just to minimize changes later.
114145516Sdarrenr */
115255332Scyint (*ipf_checkp) __P((void *, ip_t *ip, int hlen, void *ifp, int out, mb_t **mp));
116145516Sdarrenr
117145516Sdarrenr
118153876Sguidostatic eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag;
119153876Sguido
120153876Sguidostatic void ipf_ifevent(void *arg);
121153876Sguido
122153876Sguidostatic void ipf_ifevent(arg)
123255332Scy	void *arg;
124153876Sguido{
125255332Scy        ipf_sync(arg, NULL);
126153876Sguido}
127153876Sguido
128153876Sguido
129145516Sdarrenr
130145516Sdarrenrstatic int
131255332Scyipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
132145516Sdarrenr{
133145516Sdarrenr	struct ip *ip = mtod(*mp, struct ip *);
134255332Scy	int rv;
135255332Scy
136255332Scy	/*
137255332Scy	 * IPFilter expects evreything in network byte order
138255332Scy	 */
139255332Scy#if (__FreeBSD_version < 1000019)
140255332Scy	ip->ip_len = htons(ip->ip_len);
141255332Scy	ip->ip_off = htons(ip->ip_off);
142255332Scy#endif
143255332Scy	rv = ipf_check(&ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT),
144255332Scy		       mp);
145255332Scy#if (__FreeBSD_version < 1000019)
146255332Scy	if ((rv == 0) && (*mp != NULL)) {
147255332Scy		ip = mtod(*mp, struct ip *);
148255332Scy		ip->ip_len = ntohs(ip->ip_len);
149255332Scy		ip->ip_off = ntohs(ip->ip_off);
150255332Scy	}
151255332Scy#endif
152255332Scy	return rv;
153145516Sdarrenr}
154145516Sdarrenr
155145516Sdarrenr# ifdef USE_INET6
156145516Sdarrenr#  include <netinet/ip6.h>
157145516Sdarrenr
158145516Sdarrenrstatic int
159255332Scyipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
160145516Sdarrenr{
161255332Scy	return (ipf_check(&ipfmain, mtod(*mp, struct ip *),
162255332Scy			  sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp));
163145516Sdarrenr}
164145516Sdarrenr# endif
165145516Sdarrenr#if	defined(IPFILTER_LKM)
166255332Scyint ipf_identify(s)
167255332Scy	char *s;
168145516Sdarrenr{
169145516Sdarrenr	if (strcmp(s, "ipl") == 0)
170145516Sdarrenr		return 1;
171145516Sdarrenr	return 0;
172145516Sdarrenr}
173145516Sdarrenr#endif /* IPFILTER_LKM */
174145516Sdarrenr
175145516Sdarrenr
176255332Scystatic void
177255332Scyipf_timer_func(arg)
178255332Scy	void *arg;
179145516Sdarrenr{
180255332Scy	ipf_main_softc_t *softc = arg;
181255332Scy	SPL_INT(s);
182255332Scy
183255332Scy	SPL_NET(s);
184255332Scy	READ_ENTER(&softc->ipf_global);
185255332Scy
186255332Scy        if (softc->ipf_running > 0)
187255332Scy		ipf_slowtimer(softc);
188255332Scy
189255332Scy	if (softc->ipf_running == -1 || softc->ipf_running == 1) {
190255755Scy#if 0
191255332Scy		softc->ipf_slow_ch = timeout(ipf_timer_func, softc, hz/2);
192255332Scy#endif
193255755Scy		callout_init(&softc->ipf_slow_ch, CALLOUT_MPSAFE);
194255755Scy		callout_reset(&softc->ipf_slow_ch,
195255755Scy			(hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT,
196255755Scy			ipf_timer_func, softc);
197255332Scy	}
198255332Scy	RWLOCK_EXIT(&softc->ipf_global);
199255332Scy	SPL_X(s);
200255332Scy}
201255332Scy
202255332Scy
203255332Scyint
204255332Scyipfattach(softc)
205255332Scy	ipf_main_softc_t *softc;
206255332Scy{
207145516Sdarrenr#ifdef USE_SPL
208145516Sdarrenr	int s;
209145516Sdarrenr#endif
210145516Sdarrenr
211145516Sdarrenr	SPL_NET(s);
212255332Scy	if (softc->ipf_running > 0) {
213145516Sdarrenr		SPL_X(s);
214145516Sdarrenr		return EBUSY;
215145516Sdarrenr	}
216145516Sdarrenr
217255332Scy	if (ipf_init_all(softc) < 0) {
218145516Sdarrenr		SPL_X(s);
219145516Sdarrenr		return EIO;
220145516Sdarrenr	}
221145516Sdarrenr
222145516Sdarrenr
223255332Scy	if (ipf_checkp != ipf_check) {
224255332Scy		ipf_savep = ipf_checkp;
225255332Scy		ipf_checkp = ipf_check;
226145516Sdarrenr	}
227145516Sdarrenr
228255332Scy	bzero((char *)ipfmain.ipf_selwait, sizeof(ipfmain.ipf_selwait));
229255332Scy	softc->ipf_running = 1;
230145516Sdarrenr
231255332Scy	if (softc->ipf_control_forwarding & 1)
232181803Sbz		V_ipforwarding = 1;
233145516Sdarrenr
234255332Scy	ipid = 0;
235255332Scy
236145516Sdarrenr	SPL_X(s);
237255755Scy#if 0
238255332Scy	softc->ipf_slow_ch = timeout(ipf_timer_func, softc,
239255332Scy				     (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT);
240255755Scy#endif
241255755Scy	callout_init(&softc->ipf_slow_ch, CALLOUT_MPSAFE);
242255755Scy	callout_reset(&softc->ipf_slow_ch, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT,
243255755Scy		ipf_timer_func, softc);
244145516Sdarrenr	return 0;
245145516Sdarrenr}
246145516Sdarrenr
247145516Sdarrenr
248145516Sdarrenr/*
249145516Sdarrenr * Disable the filter by removing the hooks from the IP input/output
250145516Sdarrenr * stream.
251145516Sdarrenr */
252255332Scyint
253255332Scyipfdetach(softc)
254255332Scy	ipf_main_softc_t *softc;
255145516Sdarrenr{
256145516Sdarrenr#ifdef USE_SPL
257145516Sdarrenr	int s;
258145516Sdarrenr#endif
259255332Scy
260255332Scy	if (softc->ipf_control_forwarding & 2)
261181803Sbz		V_ipforwarding = 0;
262145516Sdarrenr
263145516Sdarrenr	SPL_NET(s);
264145516Sdarrenr
265255755Scy#if 0
266255332Scy	if (softc->ipf_slow_ch.callout != NULL)
267255332Scy		untimeout(ipf_timer_func, softc, softc->ipf_slow_ch);
268255332Scy	bzero(&softc->ipf_slow, sizeof(softc->ipf_slow));
269255755Scy#endif
270255755Scy	callout_drain(&softc->ipf_slow_ch);
271145516Sdarrenr
272145516Sdarrenr#ifndef NETBSD_PF
273255332Scy	if (ipf_checkp != NULL)
274255332Scy		ipf_checkp = ipf_savep;
275255332Scy	ipf_savep = NULL;
276145516Sdarrenr#endif
277145516Sdarrenr
278255332Scy	ipf_fini_all(softc);
279145516Sdarrenr
280255332Scy	softc->ipf_running = -2;
281145516Sdarrenr
282145516Sdarrenr	SPL_X(s);
283145516Sdarrenr
284145516Sdarrenr	return 0;
285145516Sdarrenr}
286145516Sdarrenr
287145516Sdarrenr
288145516Sdarrenr/*
289145516Sdarrenr * Filter ioctl interface.
290145516Sdarrenr */
291255332Scyint
292255332Scyipfioctl(dev, cmd, data, mode
293145516Sdarrenr, p)
294255332Scy	struct thread *p;
295192895Sjamie#    define	p_cred	td_ucred
296170268Sdarrenr#    define	p_uid	td_ucred->cr_ruid
297255332Scy	struct cdev *dev;
298255332Scy	ioctlcmd_t cmd;
299255332Scy	caddr_t data;
300255332Scy	int mode;
301145516Sdarrenr{
302170268Sdarrenr	int error = 0, unit = 0;
303170268Sdarrenr	SPL_INT(s);
304145516Sdarrenr
305255332Scy#if (BSD >= 199306)
306255332Scy        if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE))
307255332Scy	{
308255332Scy		ipfmain.ipf_interror = 130001;
309145516Sdarrenr		return EPERM;
310255332Scy	}
311145516Sdarrenr#endif
312145516Sdarrenr
313145516Sdarrenr	unit = GET_MINOR(dev);
314255332Scy	if ((IPL_LOGMAX < unit) || (unit < 0)) {
315255332Scy		ipfmain.ipf_interror = 130002;
316145516Sdarrenr		return ENXIO;
317255332Scy	}
318145516Sdarrenr
319255332Scy	if (ipfmain.ipf_running <= 0) {
320255332Scy		if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) {
321255332Scy			ipfmain.ipf_interror = 130003;
322145516Sdarrenr			return EIO;
323255332Scy		}
324145516Sdarrenr		if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET &&
325173181Sdarrenr		    cmd != SIOCIPFSET && cmd != SIOCFRENB &&
326255332Scy		    cmd != SIOCGETFS && cmd != SIOCGETFF &&
327255332Scy		    cmd != SIOCIPFINTERROR) {
328255332Scy			ipfmain.ipf_interror = 130004;
329145516Sdarrenr			return EIO;
330255332Scy		}
331145516Sdarrenr	}
332145516Sdarrenr
333145516Sdarrenr	SPL_NET(s);
334145516Sdarrenr
335275213Scy	CURVNET_SET(TD_TO_VNET(p));
336255332Scy	error = ipf_ioctlswitch(&ipfmain, unit, data, cmd, mode, p->p_uid, p);
337275213Scy	CURVNET_RESTORE();
338145516Sdarrenr	if (error != -1) {
339145516Sdarrenr		SPL_X(s);
340145516Sdarrenr		return error;
341145516Sdarrenr	}
342145516Sdarrenr
343145516Sdarrenr	SPL_X(s);
344161356Sguido
345145516Sdarrenr	return error;
346145516Sdarrenr}
347145516Sdarrenr
348145516Sdarrenr
349145516Sdarrenr/*
350255332Scy * ipf_send_reset - this could conceivably be a call to tcp_respond(), but that
351145516Sdarrenr * requires a large amount of setting up and isn't any more efficient.
352145516Sdarrenr */
353255332Scyint
354255332Scyipf_send_reset(fin)
355255332Scy	fr_info_t *fin;
356145516Sdarrenr{
357145516Sdarrenr	struct tcphdr *tcp, *tcp2;
358145516Sdarrenr	int tlen = 0, hlen;
359145516Sdarrenr	struct mbuf *m;
360145516Sdarrenr#ifdef USE_INET6
361145516Sdarrenr	ip6_t *ip6;
362145516Sdarrenr#endif
363145516Sdarrenr	ip_t *ip;
364145516Sdarrenr
365145516Sdarrenr	tcp = fin->fin_dp;
366145516Sdarrenr	if (tcp->th_flags & TH_RST)
367145516Sdarrenr		return -1;		/* feedback loop */
368145516Sdarrenr
369255332Scy	if (ipf_checkl4sum(fin) == -1)
370145516Sdarrenr		return -1;
371145516Sdarrenr
372145516Sdarrenr	tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) +
373145516Sdarrenr			((tcp->th_flags & TH_SYN) ? 1 : 0) +
374145516Sdarrenr			((tcp->th_flags & TH_FIN) ? 1 : 0);
375145516Sdarrenr
376145516Sdarrenr#ifdef USE_INET6
377145516Sdarrenr	hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t);
378145516Sdarrenr#else
379145516Sdarrenr	hlen = sizeof(ip_t);
380145516Sdarrenr#endif
381145516Sdarrenr#ifdef MGETHDR
382145516Sdarrenr	MGETHDR(m, M_DONTWAIT, MT_HEADER);
383145516Sdarrenr#else
384145516Sdarrenr	MGET(m, M_DONTWAIT, MT_HEADER);
385145516Sdarrenr#endif
386145516Sdarrenr	if (m == NULL)
387145516Sdarrenr		return -1;
388145516Sdarrenr	if (sizeof(*tcp2) + hlen > MLEN) {
389145516Sdarrenr		MCLGET(m, M_DONTWAIT);
390145516Sdarrenr		if ((m->m_flags & M_EXT) == 0) {
391145516Sdarrenr			FREE_MB_T(m);
392145516Sdarrenr			return -1;
393145516Sdarrenr		}
394145516Sdarrenr	}
395145516Sdarrenr
396145516Sdarrenr	m->m_len = sizeof(*tcp2) + hlen;
397145516Sdarrenr#if (BSD >= 199103)
398145516Sdarrenr	m->m_data += max_linkhdr;
399145516Sdarrenr	m->m_pkthdr.len = m->m_len;
400145516Sdarrenr	m->m_pkthdr.rcvif = (struct ifnet *)0;
401145516Sdarrenr#endif
402145516Sdarrenr	ip = mtod(m, struct ip *);
403145516Sdarrenr	bzero((char *)ip, hlen);
404145516Sdarrenr#ifdef USE_INET6
405145516Sdarrenr	ip6 = (ip6_t *)ip;
406145516Sdarrenr#endif
407145516Sdarrenr	tcp2 = (struct tcphdr *)((char *)ip + hlen);
408145516Sdarrenr	tcp2->th_sport = tcp->th_dport;
409145516Sdarrenr	tcp2->th_dport = tcp->th_sport;
410145516Sdarrenr
411145516Sdarrenr	if (tcp->th_flags & TH_ACK) {
412145516Sdarrenr		tcp2->th_seq = tcp->th_ack;
413145516Sdarrenr		tcp2->th_flags = TH_RST;
414145516Sdarrenr		tcp2->th_ack = 0;
415145516Sdarrenr	} else {
416145516Sdarrenr		tcp2->th_seq = 0;
417145516Sdarrenr		tcp2->th_ack = ntohl(tcp->th_seq);
418145516Sdarrenr		tcp2->th_ack += tlen;
419145516Sdarrenr		tcp2->th_ack = htonl(tcp2->th_ack);
420145516Sdarrenr		tcp2->th_flags = TH_RST|TH_ACK;
421145516Sdarrenr	}
422145516Sdarrenr	TCP_X2_A(tcp2, 0);
423145516Sdarrenr	TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2);
424145516Sdarrenr	tcp2->th_win = tcp->th_win;
425145516Sdarrenr	tcp2->th_sum = 0;
426145516Sdarrenr	tcp2->th_urp = 0;
427145516Sdarrenr
428145516Sdarrenr#ifdef USE_INET6
429145516Sdarrenr	if (fin->fin_v == 6) {
430145516Sdarrenr		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
431145516Sdarrenr		ip6->ip6_plen = htons(sizeof(struct tcphdr));
432145516Sdarrenr		ip6->ip6_nxt = IPPROTO_TCP;
433145516Sdarrenr		ip6->ip6_hlim = 0;
434255332Scy		ip6->ip6_src = fin->fin_dst6.in6;
435255332Scy		ip6->ip6_dst = fin->fin_src6.in6;
436145516Sdarrenr		tcp2->th_sum = in6_cksum(m, IPPROTO_TCP,
437145516Sdarrenr					 sizeof(*ip6), sizeof(*tcp2));
438255332Scy		return ipf_send_ip(fin, m);
439145516Sdarrenr	}
440145516Sdarrenr#endif
441145516Sdarrenr	ip->ip_p = IPPROTO_TCP;
442145516Sdarrenr	ip->ip_len = htons(sizeof(struct tcphdr));
443145516Sdarrenr	ip->ip_src.s_addr = fin->fin_daddr;
444145516Sdarrenr	ip->ip_dst.s_addr = fin->fin_saddr;
445145516Sdarrenr	tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2));
446255332Scy	ip->ip_len = htons(hlen + sizeof(*tcp2));
447255332Scy	return ipf_send_ip(fin, m);
448145516Sdarrenr}
449145516Sdarrenr
450145516Sdarrenr
451255332Scy/*
452255332Scy * ip_len must be in network byte order when called.
453255332Scy */
454255332Scystatic int
455255332Scyipf_send_ip(fin, m)
456255332Scy	fr_info_t *fin;
457255332Scy	mb_t *m;
458145516Sdarrenr{
459145516Sdarrenr	fr_info_t fnew;
460145516Sdarrenr	ip_t *ip, *oip;
461145516Sdarrenr	int hlen;
462145516Sdarrenr
463145516Sdarrenr	ip = mtod(m, ip_t *);
464145516Sdarrenr	bzero((char *)&fnew, sizeof(fnew));
465255332Scy	fnew.fin_main_soft = fin->fin_main_soft;
466145516Sdarrenr
467145516Sdarrenr	IP_V_A(ip, fin->fin_v);
468145516Sdarrenr	switch (fin->fin_v)
469145516Sdarrenr	{
470145516Sdarrenr	case 4 :
471255332Scy		oip = fin->fin_ip;
472255332Scy		hlen = sizeof(*oip);
473145516Sdarrenr		fnew.fin_v = 4;
474255332Scy		fnew.fin_p = ip->ip_p;
475255332Scy		fnew.fin_plen = ntohs(ip->ip_len);
476145516Sdarrenr		IP_HL_A(ip, sizeof(*oip) >> 2);
477145516Sdarrenr		ip->ip_tos = oip->ip_tos;
478145516Sdarrenr		ip->ip_id = fin->fin_ip->ip_id;
479255332Scy#if defined(FreeBSD) && (__FreeBSD_version > 460000)
480255332Scy		ip->ip_off = htons(path_mtu_discovery ? IP_DF : 0);
481145516Sdarrenr#else
482145516Sdarrenr		ip->ip_off = 0;
483145516Sdarrenr#endif
484181803Sbz		ip->ip_ttl = V_ip_defttl;
485145516Sdarrenr		ip->ip_sum = 0;
486145516Sdarrenr		break;
487145516Sdarrenr#ifdef USE_INET6
488145516Sdarrenr	case 6 :
489145516Sdarrenr	{
490145516Sdarrenr		ip6_t *ip6 = (ip6_t *)ip;
491145516Sdarrenr
492145516Sdarrenr		ip6->ip6_vfc = 0x60;
493145516Sdarrenr		ip6->ip6_hlim = IPDEFTTL;
494145516Sdarrenr
495255332Scy		hlen = sizeof(*ip6);
496255332Scy		fnew.fin_p = ip6->ip6_nxt;
497145516Sdarrenr		fnew.fin_v = 6;
498255332Scy		fnew.fin_plen = ntohs(ip6->ip6_plen) + hlen;
499145516Sdarrenr		break;
500145516Sdarrenr	}
501145516Sdarrenr#endif
502145516Sdarrenr	default :
503145516Sdarrenr		return EINVAL;
504145516Sdarrenr	}
505145516Sdarrenr#ifdef IPSEC
506145516Sdarrenr	m->m_pkthdr.rcvif = NULL;
507145516Sdarrenr#endif
508145516Sdarrenr
509145516Sdarrenr	fnew.fin_ifp = fin->fin_ifp;
510145516Sdarrenr	fnew.fin_flx = FI_NOCKSUM;
511145516Sdarrenr	fnew.fin_m = m;
512145516Sdarrenr	fnew.fin_ip = ip;
513255332Scy	fnew.fin_mp = &m;
514145516Sdarrenr	fnew.fin_hlen = hlen;
515145516Sdarrenr	fnew.fin_dp = (char *)ip + hlen;
516255332Scy	(void) ipf_makefrip(hlen, ip, &fnew);
517145516Sdarrenr
518255332Scy	return ipf_fastroute(m, &m, &fnew, NULL);
519145516Sdarrenr}
520145516Sdarrenr
521145516Sdarrenr
522255332Scyint
523255332Scyipf_send_icmp_err(type, fin, dst)
524255332Scy	int type;
525255332Scy	fr_info_t *fin;
526255332Scy	int dst;
527145516Sdarrenr{
528145516Sdarrenr	int err, hlen, xtra, iclen, ohlen, avail, code;
529145516Sdarrenr	struct in_addr dst4;
530145516Sdarrenr	struct icmp *icmp;
531145516Sdarrenr	struct mbuf *m;
532255332Scy	i6addr_t dst6;
533145516Sdarrenr	void *ifp;
534145516Sdarrenr#ifdef USE_INET6
535145516Sdarrenr	ip6_t *ip6;
536145516Sdarrenr#endif
537145516Sdarrenr	ip_t *ip, *ip2;
538145516Sdarrenr
539172776Sdarrenr	if ((type < 0) || (type >= ICMP_MAXTYPE))
540145516Sdarrenr		return -1;
541145516Sdarrenr
542145516Sdarrenr	code = fin->fin_icode;
543145516Sdarrenr#ifdef USE_INET6
544255332Scy#if 0
545255332Scy	/* XXX Fix an off by one error: s/>/>=/
546255332Scy	 was:
547255332Scy	 if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int)))
548255332Scy	 Fix obtained from NetBSD ip_fil_netbsd.c r1.4: */
549255332Scy#endif
550255332Scy	if ((code < 0) || (code >= sizeof(icmptoicmp6unreach)/sizeof(int)))
551145516Sdarrenr		return -1;
552145516Sdarrenr#endif
553145516Sdarrenr
554255332Scy	if (ipf_checkl4sum(fin) == -1)
555145516Sdarrenr		return -1;
556145516Sdarrenr#ifdef MGETHDR
557145516Sdarrenr	MGETHDR(m, M_DONTWAIT, MT_HEADER);
558145516Sdarrenr#else
559145516Sdarrenr	MGET(m, M_DONTWAIT, MT_HEADER);
560145516Sdarrenr#endif
561145516Sdarrenr	if (m == NULL)
562145516Sdarrenr		return -1;
563145516Sdarrenr	avail = MHLEN;
564145516Sdarrenr
565145516Sdarrenr	xtra = 0;
566145516Sdarrenr	hlen = 0;
567145516Sdarrenr	ohlen = 0;
568255332Scy	dst4.s_addr = 0;
569145516Sdarrenr	ifp = fin->fin_ifp;
570145516Sdarrenr	if (fin->fin_v == 4) {
571255332Scy		if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT))
572145516Sdarrenr			switch (ntohs(fin->fin_data[0]) >> 8)
573145516Sdarrenr			{
574145516Sdarrenr			case ICMP_ECHO :
575145516Sdarrenr			case ICMP_TSTAMP :
576145516Sdarrenr			case ICMP_IREQ :
577145516Sdarrenr			case ICMP_MASKREQ :
578145516Sdarrenr				break;
579145516Sdarrenr			default :
580145516Sdarrenr				FREE_MB_T(m);
581145516Sdarrenr				return 0;
582145516Sdarrenr			}
583145516Sdarrenr
584145516Sdarrenr		if (dst == 0) {
585255332Scy			if (ipf_ifpaddr(&ipfmain, 4, FRI_NORMAL, ifp,
586255332Scy					&dst6, NULL) == -1) {
587145516Sdarrenr				FREE_MB_T(m);
588145516Sdarrenr				return -1;
589145516Sdarrenr			}
590255332Scy			dst4 = dst6.in4;
591145516Sdarrenr		} else
592145516Sdarrenr			dst4.s_addr = fin->fin_daddr;
593145516Sdarrenr
594145516Sdarrenr		hlen = sizeof(ip_t);
595145516Sdarrenr		ohlen = fin->fin_hlen;
596255332Scy		iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen;
597145516Sdarrenr		if (fin->fin_hlen < fin->fin_plen)
598145516Sdarrenr			xtra = MIN(fin->fin_dlen, 8);
599145516Sdarrenr		else
600145516Sdarrenr			xtra = 0;
601145516Sdarrenr	}
602145516Sdarrenr
603145516Sdarrenr#ifdef USE_INET6
604145516Sdarrenr	else if (fin->fin_v == 6) {
605145516Sdarrenr		hlen = sizeof(ip6_t);
606145516Sdarrenr		ohlen = sizeof(ip6_t);
607255332Scy		iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen;
608145516Sdarrenr		type = icmptoicmp6types[type];
609145516Sdarrenr		if (type == ICMP6_DST_UNREACH)
610145516Sdarrenr			code = icmptoicmp6unreach[code];
611145516Sdarrenr
612255332Scy		if (iclen + max_linkhdr + fin->fin_plen > avail) {
613145516Sdarrenr			MCLGET(m, M_DONTWAIT);
614145516Sdarrenr			if ((m->m_flags & M_EXT) == 0) {
615145516Sdarrenr				FREE_MB_T(m);
616145516Sdarrenr				return -1;
617145516Sdarrenr			}
618145516Sdarrenr			avail = MCLBYTES;
619145516Sdarrenr		}
620255332Scy		xtra = MIN(fin->fin_plen, avail - iclen - max_linkhdr);
621255332Scy		xtra = MIN(xtra, IPV6_MMTU - iclen);
622145516Sdarrenr		if (dst == 0) {
623255332Scy			if (ipf_ifpaddr(&ipfmain, 6, FRI_NORMAL, ifp,
624255332Scy					&dst6, NULL) == -1) {
625145516Sdarrenr				FREE_MB_T(m);
626145516Sdarrenr				return -1;
627145516Sdarrenr			}
628145516Sdarrenr		} else
629145516Sdarrenr			dst6 = fin->fin_dst6;
630145516Sdarrenr	}
631145516Sdarrenr#endif
632145516Sdarrenr	else {
633145516Sdarrenr		FREE_MB_T(m);
634145516Sdarrenr		return -1;
635145516Sdarrenr	}
636145516Sdarrenr
637145516Sdarrenr	avail -= (max_linkhdr + iclen);
638145516Sdarrenr	if (avail < 0) {
639145516Sdarrenr		FREE_MB_T(m);
640145516Sdarrenr		return -1;
641145516Sdarrenr	}
642145516Sdarrenr	if (xtra > avail)
643145516Sdarrenr		xtra = avail;
644145516Sdarrenr	iclen += xtra;
645145516Sdarrenr	m->m_data += max_linkhdr;
646145516Sdarrenr	m->m_pkthdr.rcvif = (struct ifnet *)0;
647145516Sdarrenr	m->m_pkthdr.len = iclen;
648145516Sdarrenr	m->m_len = iclen;
649145516Sdarrenr	ip = mtod(m, ip_t *);
650145516Sdarrenr	icmp = (struct icmp *)((char *)ip + hlen);
651145516Sdarrenr	ip2 = (ip_t *)&icmp->icmp_ip;
652145516Sdarrenr
653145516Sdarrenr	icmp->icmp_type = type;
654145516Sdarrenr	icmp->icmp_code = fin->fin_icode;
655145516Sdarrenr	icmp->icmp_cksum = 0;
656145516Sdarrenr#ifdef icmp_nextmtu
657255332Scy	if (type == ICMP_UNREACH && fin->fin_icode == ICMP_UNREACH_NEEDFRAG) {
658255332Scy		if (fin->fin_mtu != 0) {
659255332Scy			icmp->icmp_nextmtu = htons(fin->fin_mtu);
660255332Scy
661255332Scy		} else if (ifp != NULL) {
662255332Scy			icmp->icmp_nextmtu = htons(GETIFMTU_4(ifp));
663255332Scy
664255332Scy		} else {	/* make up a number... */
665255332Scy			icmp->icmp_nextmtu = htons(fin->fin_plen - 20);
666255332Scy		}
667255332Scy	}
668145516Sdarrenr#endif
669145516Sdarrenr
670145516Sdarrenr	bcopy((char *)fin->fin_ip, (char *)ip2, ohlen);
671145516Sdarrenr
672145516Sdarrenr#ifdef USE_INET6
673145516Sdarrenr	ip6 = (ip6_t *)ip;
674145516Sdarrenr	if (fin->fin_v == 6) {
675145516Sdarrenr		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
676145516Sdarrenr		ip6->ip6_plen = htons(iclen - hlen);
677145516Sdarrenr		ip6->ip6_nxt = IPPROTO_ICMPV6;
678145516Sdarrenr		ip6->ip6_hlim = 0;
679255332Scy		ip6->ip6_src = dst6.in6;
680255332Scy		ip6->ip6_dst = fin->fin_src6.in6;
681145516Sdarrenr		if (xtra > 0)
682145516Sdarrenr			bcopy((char *)fin->fin_ip + ohlen,
683145516Sdarrenr			      (char *)&icmp->icmp_ip + ohlen, xtra);
684145516Sdarrenr		icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6,
685145516Sdarrenr					     sizeof(*ip6), iclen - hlen);
686145516Sdarrenr	} else
687145516Sdarrenr#endif
688145516Sdarrenr	{
689145516Sdarrenr		ip->ip_p = IPPROTO_ICMP;
690145516Sdarrenr		ip->ip_src.s_addr = dst4.s_addr;
691145516Sdarrenr		ip->ip_dst.s_addr = fin->fin_saddr;
692145516Sdarrenr
693145516Sdarrenr		if (xtra > 0)
694145516Sdarrenr			bcopy((char *)fin->fin_ip + ohlen,
695145516Sdarrenr			      (char *)&icmp->icmp_ip + ohlen, xtra);
696145516Sdarrenr		icmp->icmp_cksum = ipf_cksum((u_short *)icmp,
697145516Sdarrenr					     sizeof(*icmp) + 8);
698255332Scy		ip->ip_len = htons(iclen);
699145516Sdarrenr		ip->ip_p = IPPROTO_ICMP;
700145516Sdarrenr	}
701255332Scy	err = ipf_send_ip(fin, m);
702145516Sdarrenr	return err;
703145516Sdarrenr}
704145516Sdarrenr
705145516Sdarrenr
706145516Sdarrenr
707145516Sdarrenr
708173181Sdarrenr/*
709173181Sdarrenr * m0 - pointer to mbuf where the IP packet starts
710173181Sdarrenr * mpp - pointer to the mbuf pointer that is the start of the mbuf chain
711173181Sdarrenr */
712255332Scyint
713255332Scyipf_fastroute(m0, mpp, fin, fdp)
714255332Scy	mb_t *m0, **mpp;
715255332Scy	fr_info_t *fin;
716255332Scy	frdest_t *fdp;
717145516Sdarrenr{
718145516Sdarrenr	register struct ip *ip, *mhip;
719173181Sdarrenr	register struct mbuf *m = *mpp;
720145516Sdarrenr	register struct route *ro;
721145516Sdarrenr	int len, off, error = 0, hlen, code;
722145516Sdarrenr	struct ifnet *ifp, *sifp;
723145516Sdarrenr	struct sockaddr_in *dst;
724145516Sdarrenr	struct route iproute;
725145516Sdarrenr	u_short ip_off;
726255332Scy	frdest_t node;
727145516Sdarrenr	frentry_t *fr;
728145516Sdarrenr
729161356Sguido	ro = NULL;
730161356Sguido
731145516Sdarrenr#ifdef M_WRITABLE
732145516Sdarrenr	/*
733145516Sdarrenr	* HOT FIX/KLUDGE:
734145516Sdarrenr	*
735145516Sdarrenr	* If the mbuf we're about to send is not writable (because of
736145516Sdarrenr	* a cluster reference, for example) we'll need to make a copy
737145516Sdarrenr	* of it since this routine modifies the contents.
738145516Sdarrenr	*
739145516Sdarrenr	* If you have non-crappy network hardware that can transmit data
740145516Sdarrenr	* from the mbuf, rather than making a copy, this is gonna be a
741145516Sdarrenr	* problem.
742145516Sdarrenr	*/
743145516Sdarrenr	if (M_WRITABLE(m) == 0) {
744161356Sguido		m0 = m_dup(m, M_DONTWAIT);
745161356Sguido		if (m0 != 0) {
746145516Sdarrenr			FREE_MB_T(m);
747145516Sdarrenr			m = m0;
748145516Sdarrenr			*mpp = m;
749145516Sdarrenr		} else {
750145516Sdarrenr			error = ENOBUFS;
751145516Sdarrenr			FREE_MB_T(m);
752161356Sguido			goto done;
753145516Sdarrenr		}
754145516Sdarrenr	}
755145516Sdarrenr#endif
756145516Sdarrenr
757145516Sdarrenr#ifdef USE_INET6
758145516Sdarrenr	if (fin->fin_v == 6) {
759145516Sdarrenr		/*
760145516Sdarrenr		 * currently "to <if>" and "to <if>:ip#" are not supported
761145516Sdarrenr		 * for IPv6
762145516Sdarrenr		 */
763255332Scy		return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
764145516Sdarrenr	}
765145516Sdarrenr#endif
766145516Sdarrenr
767145516Sdarrenr	hlen = fin->fin_hlen;
768145516Sdarrenr	ip = mtod(m0, struct ip *);
769255332Scy	ifp = NULL;
770145516Sdarrenr
771145516Sdarrenr	/*
772145516Sdarrenr	 * Route packet.
773145516Sdarrenr	 */
774145516Sdarrenr	ro = &iproute;
775255332Scy	bzero(ro, sizeof (*ro));
776145516Sdarrenr	dst = (struct sockaddr_in *)&ro->ro_dst;
777145516Sdarrenr	dst->sin_family = AF_INET;
778145516Sdarrenr	dst->sin_addr = ip->ip_dst;
779145516Sdarrenr
780145516Sdarrenr	fr = fin->fin_fr;
781255332Scy	if ((fr != NULL) && !(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) &&
782255332Scy	    (fdp->fd_type == FRD_DSTLIST)) {
783255332Scy		if (ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node) == 0)
784255332Scy			fdp = &node;
785255332Scy	}
786255332Scy
787145516Sdarrenr	if (fdp != NULL)
788255332Scy		ifp = fdp->fd_ptr;
789145516Sdarrenr	else
790145516Sdarrenr		ifp = fin->fin_ifp;
791145516Sdarrenr
792255332Scy	if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) {
793145516Sdarrenr		error = -2;
794145516Sdarrenr		goto bad;
795145516Sdarrenr	}
796145516Sdarrenr
797161356Sguido	if ((fdp != NULL) && (fdp->fd_ip.s_addr != 0))
798161356Sguido		dst->sin_addr = fdp->fd_ip;
799145516Sdarrenr
800145516Sdarrenr	dst->sin_len = sizeof(*dst);
801267868Scy	in_rtalloc(ro, M_GETFIB(m0));
802145516Sdarrenr
803145516Sdarrenr	if ((ifp == NULL) && (ro->ro_rt != NULL))
804145516Sdarrenr		ifp = ro->ro_rt->rt_ifp;
805145516Sdarrenr
806145516Sdarrenr	if ((ro->ro_rt == NULL) || (ifp == NULL)) {
807145516Sdarrenr		if (in_localaddr(ip->ip_dst))
808145516Sdarrenr			error = EHOSTUNREACH;
809145516Sdarrenr		else
810145516Sdarrenr			error = ENETUNREACH;
811145516Sdarrenr		goto bad;
812145516Sdarrenr	}
813145516Sdarrenr	if (ro->ro_rt->rt_flags & RTF_GATEWAY)
814145516Sdarrenr		dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
815145516Sdarrenr	if (ro->ro_rt)
816263478Sglebius		counter_u64_add(ro->ro_rt->rt_pksent, 1);
817145516Sdarrenr
818145516Sdarrenr	/*
819145516Sdarrenr	 * For input packets which are being "fastrouted", they won't
820145516Sdarrenr	 * go back through output filtering and miss their chance to get
821170268Sdarrenr	 * NAT'd and counted.  Duplicated packets aren't considered to be
822170268Sdarrenr	 * part of the normal packet stream, so do not NAT them or pass
823170268Sdarrenr	 * them through stateful checking, etc.
824145516Sdarrenr	 */
825170268Sdarrenr	if ((fdp != &fr->fr_dif) && (fin->fin_out == 0)) {
826145516Sdarrenr		sifp = fin->fin_ifp;
827145516Sdarrenr		fin->fin_ifp = ifp;
828145516Sdarrenr		fin->fin_out = 1;
829255332Scy		(void) ipf_acctpkt(fin, NULL);
830145516Sdarrenr		fin->fin_fr = NULL;
831145516Sdarrenr		if (!fr || !(fr->fr_flags & FR_RETMASK)) {
832145516Sdarrenr			u_32_t pass;
833145516Sdarrenr
834255332Scy			(void) ipf_state_check(fin, &pass);
835145516Sdarrenr		}
836145516Sdarrenr
837255332Scy		switch (ipf_nat_checkout(fin, NULL))
838145516Sdarrenr		{
839145516Sdarrenr		case 0 :
840145516Sdarrenr			break;
841145516Sdarrenr		case 1 :
842145516Sdarrenr			ip->ip_sum = 0;
843145516Sdarrenr			break;
844145516Sdarrenr		case -1 :
845145516Sdarrenr			error = -1;
846173181Sdarrenr			goto bad;
847145516Sdarrenr			break;
848145516Sdarrenr		}
849145516Sdarrenr
850145516Sdarrenr		fin->fin_ifp = sifp;
851145516Sdarrenr		fin->fin_out = 0;
852145516Sdarrenr	} else
853145516Sdarrenr		ip->ip_sum = 0;
854145516Sdarrenr	/*
855145516Sdarrenr	 * If small enough for interface, can just send directly.
856145516Sdarrenr	 */
857255332Scy	if (ntohs(ip->ip_len) <= ifp->if_mtu) {
858145516Sdarrenr		if (!ip->ip_sum)
859145516Sdarrenr			ip->ip_sum = in_cksum(m, hlen);
860145516Sdarrenr		error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst,
861255332Scy			    ro
862255332Scy			);
863145516Sdarrenr		goto done;
864145516Sdarrenr	}
865145516Sdarrenr	/*
866145516Sdarrenr	 * Too large for interface; fragment if possible.
867145516Sdarrenr	 * Must be able to put at least 8 bytes per fragment.
868145516Sdarrenr	 */
869145516Sdarrenr	ip_off = ntohs(ip->ip_off);
870145516Sdarrenr	if (ip_off & IP_DF) {
871145516Sdarrenr		error = EMSGSIZE;
872145516Sdarrenr		goto bad;
873145516Sdarrenr	}
874145516Sdarrenr	len = (ifp->if_mtu - hlen) &~ 7;
875145516Sdarrenr	if (len < 8) {
876145516Sdarrenr		error = EMSGSIZE;
877145516Sdarrenr		goto bad;
878145516Sdarrenr	}
879145516Sdarrenr
880145516Sdarrenr    {
881145516Sdarrenr	int mhlen, firstlen = len;
882145516Sdarrenr	struct mbuf **mnext = &m->m_act;
883145516Sdarrenr
884145516Sdarrenr	/*
885145516Sdarrenr	 * Loop through length of segment after first fragment,
886145516Sdarrenr	 * make new header and copy data of each part and link onto chain.
887145516Sdarrenr	 */
888145516Sdarrenr	m0 = m;
889145516Sdarrenr	mhlen = sizeof (struct ip);
890255332Scy	for (off = hlen + len; off < ntohs(ip->ip_len); off += len) {
891145516Sdarrenr#ifdef MGETHDR
892145516Sdarrenr		MGETHDR(m, M_DONTWAIT, MT_HEADER);
893145516Sdarrenr#else
894145516Sdarrenr		MGET(m, M_DONTWAIT, MT_HEADER);
895145516Sdarrenr#endif
896145516Sdarrenr		if (m == 0) {
897145516Sdarrenr			m = m0;
898145516Sdarrenr			error = ENOBUFS;
899145516Sdarrenr			goto bad;
900145516Sdarrenr		}
901145516Sdarrenr		m->m_data += max_linkhdr;
902145516Sdarrenr		mhip = mtod(m, struct ip *);
903145516Sdarrenr		bcopy((char *)ip, (char *)mhip, sizeof(*ip));
904145516Sdarrenr		if (hlen > sizeof (struct ip)) {
905145516Sdarrenr			mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
906145516Sdarrenr			IP_HL_A(mhip, mhlen >> 2);
907145516Sdarrenr		}
908145516Sdarrenr		m->m_len = mhlen;
909145516Sdarrenr		mhip->ip_off = ((off - hlen) >> 3) + ip_off;
910255332Scy		if (off + len >= ntohs(ip->ip_len))
911255332Scy			len = ntohs(ip->ip_len) - off;
912145516Sdarrenr		else
913145516Sdarrenr			mhip->ip_off |= IP_MF;
914145516Sdarrenr		mhip->ip_len = htons((u_short)(len + mhlen));
915161356Sguido		*mnext = m;
916145516Sdarrenr		m->m_next = m_copy(m0, off, len);
917145516Sdarrenr		if (m->m_next == 0) {
918145516Sdarrenr			error = ENOBUFS;	/* ??? */
919145516Sdarrenr			goto sendorfree;
920145516Sdarrenr		}
921145516Sdarrenr		m->m_pkthdr.len = mhlen + len;
922145516Sdarrenr		m->m_pkthdr.rcvif = NULL;
923145516Sdarrenr		mhip->ip_off = htons((u_short)mhip->ip_off);
924145516Sdarrenr		mhip->ip_sum = 0;
925145516Sdarrenr		mhip->ip_sum = in_cksum(m, mhlen);
926145516Sdarrenr		mnext = &m->m_act;
927145516Sdarrenr	}
928145516Sdarrenr	/*
929145516Sdarrenr	 * Update first fragment by trimming what's been copied out
930145516Sdarrenr	 * and updating header, then send each fragment (in order).
931145516Sdarrenr	 */
932145516Sdarrenr	m_adj(m0, hlen + firstlen - ip->ip_len);
933145516Sdarrenr	ip->ip_len = htons((u_short)(hlen + firstlen));
934145516Sdarrenr	ip->ip_off = htons((u_short)IP_MF);
935145516Sdarrenr	ip->ip_sum = 0;
936145516Sdarrenr	ip->ip_sum = in_cksum(m0, hlen);
937145516Sdarrenrsendorfree:
938145516Sdarrenr	for (m = m0; m; m = m0) {
939145516Sdarrenr		m0 = m->m_act;
940145516Sdarrenr		m->m_act = 0;
941145516Sdarrenr		if (error == 0)
942145516Sdarrenr			error = (*ifp->if_output)(ifp, m,
943255332Scy			    (struct sockaddr *)dst,
944255332Scy			    ro
945255332Scy			    );
946145516Sdarrenr		else
947145516Sdarrenr			FREE_MB_T(m);
948145516Sdarrenr	}
949255332Scy    }
950145516Sdarrenrdone:
951145516Sdarrenr	if (!error)
952255332Scy		ipfmain.ipf_frouteok[0]++;
953145516Sdarrenr	else
954255332Scy		ipfmain.ipf_frouteok[1]++;
955145516Sdarrenr
956161356Sguido	if ((ro != NULL) && (ro->ro_rt != NULL)) {
957145516Sdarrenr		RTFREE(ro->ro_rt);
958145516Sdarrenr	}
959145516Sdarrenr	return 0;
960145516Sdarrenrbad:
961145516Sdarrenr	if (error == EMSGSIZE) {
962145516Sdarrenr		sifp = fin->fin_ifp;
963145516Sdarrenr		code = fin->fin_icode;
964145516Sdarrenr		fin->fin_icode = ICMP_UNREACH_NEEDFRAG;
965145516Sdarrenr		fin->fin_ifp = ifp;
966255332Scy		(void) ipf_send_icmp_err(ICMP_UNREACH, fin, 1);
967145516Sdarrenr		fin->fin_ifp = sifp;
968145516Sdarrenr		fin->fin_icode = code;
969145516Sdarrenr	}
970145516Sdarrenr	FREE_MB_T(m);
971145516Sdarrenr	goto done;
972145516Sdarrenr}
973145516Sdarrenr
974145516Sdarrenr
975255332Scyint
976255332Scyipf_verifysrc(fin)
977255332Scy	fr_info_t *fin;
978145516Sdarrenr{
979145516Sdarrenr	struct sockaddr_in *dst;
980145516Sdarrenr	struct route iproute;
981145516Sdarrenr
982145516Sdarrenr	bzero((char *)&iproute, sizeof(iproute));
983145516Sdarrenr	dst = (struct sockaddr_in *)&iproute.ro_dst;
984145516Sdarrenr	dst->sin_len = sizeof(*dst);
985145516Sdarrenr	dst->sin_family = AF_INET;
986145516Sdarrenr	dst->sin_addr = fin->fin_src;
987178888Sjulian	in_rtalloc(&iproute, 0);
988145516Sdarrenr	if (iproute.ro_rt == NULL)
989145516Sdarrenr		return 0;
990145516Sdarrenr	return (fin->fin_ifp == iproute.ro_rt->rt_ifp);
991145516Sdarrenr}
992145516Sdarrenr
993145516Sdarrenr
994145516Sdarrenr/*
995145516Sdarrenr * return the first IP Address associated with an interface
996145516Sdarrenr */
997255332Scyint
998255332Scyipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask)
999255332Scy	ipf_main_softc_t *softc;
1000255332Scy	int v, atype;
1001255332Scy	void *ifptr;
1002255332Scy	i6addr_t *inp, *inpmask;
1003145516Sdarrenr{
1004145516Sdarrenr#ifdef USE_INET6
1005145516Sdarrenr	struct in6_addr *inp6 = NULL;
1006145516Sdarrenr#endif
1007145516Sdarrenr	struct sockaddr *sock, *mask;
1008145516Sdarrenr	struct sockaddr_in *sin;
1009145516Sdarrenr	struct ifaddr *ifa;
1010145516Sdarrenr	struct ifnet *ifp;
1011145516Sdarrenr
1012145516Sdarrenr	if ((ifptr == NULL) || (ifptr == (void *)-1))
1013145516Sdarrenr		return -1;
1014145516Sdarrenr
1015145516Sdarrenr	sin = NULL;
1016145516Sdarrenr	ifp = ifptr;
1017145516Sdarrenr
1018145516Sdarrenr	if (v == 4)
1019255332Scy		inp->in4.s_addr = 0;
1020145516Sdarrenr#ifdef USE_INET6
1021145516Sdarrenr	else if (v == 6)
1022255332Scy		bzero((char *)inp, sizeof(*inp));
1023145516Sdarrenr#endif
1024145516Sdarrenr	ifa = TAILQ_FIRST(&ifp->if_addrhead);
1025145516Sdarrenr
1026145516Sdarrenr	sock = ifa->ifa_addr;
1027145516Sdarrenr	while (sock != NULL && ifa != NULL) {
1028145516Sdarrenr		sin = (struct sockaddr_in *)sock;
1029145516Sdarrenr		if ((v == 4) && (sin->sin_family == AF_INET))
1030145516Sdarrenr			break;
1031145516Sdarrenr#ifdef USE_INET6
1032145516Sdarrenr		if ((v == 6) && (sin->sin_family == AF_INET6)) {
1033145516Sdarrenr			inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr;
1034145516Sdarrenr			if (!IN6_IS_ADDR_LINKLOCAL(inp6) &&
1035145516Sdarrenr			    !IN6_IS_ADDR_LOOPBACK(inp6))
1036145516Sdarrenr				break;
1037145516Sdarrenr		}
1038145516Sdarrenr#endif
1039145516Sdarrenr		ifa = TAILQ_NEXT(ifa, ifa_link);
1040145516Sdarrenr		if (ifa != NULL)
1041145516Sdarrenr			sock = ifa->ifa_addr;
1042145516Sdarrenr	}
1043145516Sdarrenr
1044145516Sdarrenr	if (ifa == NULL || sin == NULL)
1045145516Sdarrenr		return -1;
1046145516Sdarrenr
1047145516Sdarrenr	mask = ifa->ifa_netmask;
1048145516Sdarrenr	if (atype == FRI_BROADCAST)
1049145516Sdarrenr		sock = ifa->ifa_broadaddr;
1050145516Sdarrenr	else if (atype == FRI_PEERADDR)
1051145516Sdarrenr		sock = ifa->ifa_dstaddr;
1052145516Sdarrenr
1053161356Sguido	if (sock == NULL)
1054161356Sguido		return -1;
1055161356Sguido
1056145516Sdarrenr#ifdef USE_INET6
1057145516Sdarrenr	if (v == 6) {
1058255332Scy		return ipf_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock,
1059255332Scy					 (struct sockaddr_in6 *)mask,
1060255332Scy					 inp, inpmask);
1061145516Sdarrenr	}
1062145516Sdarrenr#endif
1063255332Scy	return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)sock,
1064255332Scy				 (struct sockaddr_in *)mask,
1065255332Scy				 &inp->in4, &inpmask->in4);
1066145516Sdarrenr}
1067145516Sdarrenr
1068145516Sdarrenr
1069255332Scyu_32_t
1070255332Scyipf_newisn(fin)
1071255332Scy	fr_info_t *fin;
1072145516Sdarrenr{
1073145516Sdarrenr	u_32_t newiss;
1074145516Sdarrenr	newiss = arc4random();
1075145516Sdarrenr	return newiss;
1076145516Sdarrenr}
1077145516Sdarrenr
1078145516Sdarrenr
1079145516Sdarrenr/* ------------------------------------------------------------------------ */
1080255332Scy/* Function:    ipf_nextipid                                                */
1081145516Sdarrenr/* Returns:     int - 0 == success, -1 == error (packet should be droppped) */
1082145516Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
1083145516Sdarrenr/*                                                                          */
1084145516Sdarrenr/* Returns the next IPv4 ID to use for this packet.                         */
1085145516Sdarrenr/* ------------------------------------------------------------------------ */
1086255332Scyu_short
1087255332Scyipf_nextipid(fin)
1088255332Scy	fr_info_t *fin;
1089145516Sdarrenr{
1090145516Sdarrenr	u_short id;
1091145516Sdarrenr
1092255332Scy#ifndef	RANDOM_IP_ID
1093255332Scy	MUTEX_ENTER(&ipfmain.ipf_rw);
1094145516Sdarrenr	id = ipid++;
1095255332Scy	MUTEX_EXIT(&ipfmain.ipf_rw);
1096145516Sdarrenr#else
1097145516Sdarrenr	id = ip_randomid();
1098145516Sdarrenr#endif
1099145516Sdarrenr
1100145516Sdarrenr	return id;
1101145516Sdarrenr}
1102145516Sdarrenr
1103145516Sdarrenr
1104255332ScyINLINE int
1105255332Scyipf_checkv4sum(fin)
1106255332Scy	fr_info_t *fin;
1107145516Sdarrenr{
1108145516Sdarrenr#ifdef CSUM_DATA_VALID
1109145516Sdarrenr	int manual = 0;
1110145516Sdarrenr	u_short sum;
1111145516Sdarrenr	ip_t *ip;
1112145516Sdarrenr	mb_t *m;
1113145516Sdarrenr
1114145516Sdarrenr	if ((fin->fin_flx & FI_NOCKSUM) != 0)
1115255332Scy		return 0;
1116145516Sdarrenr
1117255332Scy	if ((fin->fin_flx & FI_SHORT) != 0)
1118255332Scy		return 1;
1119172776Sdarrenr
1120255332Scy	if (fin->fin_cksum != FI_CK_NEEDED)
1121255332Scy		return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1;
1122255332Scy
1123145516Sdarrenr	m = fin->fin_m;
1124145516Sdarrenr	if (m == NULL) {
1125145516Sdarrenr		manual = 1;
1126145516Sdarrenr		goto skipauto;
1127145516Sdarrenr	}
1128145516Sdarrenr	ip = fin->fin_ip;
1129145516Sdarrenr
1130255332Scy	if ((m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)) ==
1131255332Scy	    CSUM_IP_CHECKED) {
1132255332Scy		fin->fin_cksum = FI_CK_BAD;
1133255332Scy		fin->fin_flx |= FI_BAD;
1134255332Scy		return -1;
1135255332Scy	}
1136145516Sdarrenr	if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
1137289210Scy		/* Depending on the driver, UDP may have zero checksum */
1138289210Scy		if (fin->fin_p == IPPROTO_UDP && (fin->fin_flx &
1139289210Scy		    (FI_FRAG|FI_SHORT|FI_BAD)) == 0) {
1140289210Scy			udphdr_t *udp = fin->fin_dp;
1141289210Scy			if (udp->uh_sum == 0) {
1142289210Scy				/*
1143289210Scy				 * we're good no matter what the hardware
1144289210Scy				 * checksum flags and csum_data say (handling
1145289210Scy				 * of csum_data for zero UDP checksum is not
1146289210Scy				 * consistent across all drivers)
1147289210Scy				 */
1148289210Scy				fin->fin_cksum = 1;
1149289210Scy				return 0;
1150289210Scy			}
1151289210Scy		}
1152289210Scy
1153145516Sdarrenr		if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
1154145516Sdarrenr			sum = m->m_pkthdr.csum_data;
1155145516Sdarrenr		else
1156145516Sdarrenr			sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
1157145516Sdarrenr					htonl(m->m_pkthdr.csum_data +
1158255332Scy					fin->fin_dlen + fin->fin_p));
1159145516Sdarrenr		sum ^= 0xffff;
1160172776Sdarrenr		if (sum != 0) {
1161255332Scy			fin->fin_cksum = FI_CK_BAD;
1162145516Sdarrenr			fin->fin_flx |= FI_BAD;
1163172776Sdarrenr		} else {
1164255332Scy			fin->fin_cksum = FI_CK_SUMOK;
1165255332Scy			return 0;
1166172776Sdarrenr		}
1167255332Scy	} else {
1168255332Scy		if (m->m_pkthdr.csum_flags == CSUM_DELAY_DATA) {
1169255332Scy			fin->fin_cksum = FI_CK_L4FULL;
1170255332Scy			return 0;
1171255332Scy		} else if (m->m_pkthdr.csum_flags == CSUM_TCP ||
1172255332Scy			   m->m_pkthdr.csum_flags == CSUM_UDP) {
1173255332Scy			fin->fin_cksum = FI_CK_L4PART;
1174255332Scy			return 0;
1175255332Scy		} else if (m->m_pkthdr.csum_flags == CSUM_IP) {
1176255332Scy			fin->fin_cksum = FI_CK_L4PART;
1177255332Scy			return 0;
1178255332Scy		} else {
1179255332Scy			manual = 1;
1180255332Scy		}
1181255332Scy	}
1182145516Sdarrenrskipauto:
1183255332Scy	if (manual != 0) {
1184255332Scy		if (ipf_checkl4sum(fin) == -1) {
1185145516Sdarrenr			fin->fin_flx |= FI_BAD;
1186255332Scy			return -1;
1187255332Scy		}
1188255332Scy	}
1189145516Sdarrenr#else
1190255332Scy	if (ipf_checkl4sum(fin) == -1) {
1191145516Sdarrenr		fin->fin_flx |= FI_BAD;
1192255332Scy		return -1;
1193255332Scy	}
1194145516Sdarrenr#endif
1195255332Scy	return 0;
1196145516Sdarrenr}
1197145516Sdarrenr
1198145516Sdarrenr
1199145516Sdarrenr#ifdef USE_INET6
1200255332ScyINLINE int
1201255332Scyipf_checkv6sum(fin)
1202255332Scy	fr_info_t *fin;
1203145516Sdarrenr{
1204255332Scy	if ((fin->fin_flx & FI_NOCKSUM) != 0)
1205255332Scy		return 0;
1206255332Scy
1207255332Scy	if ((fin->fin_flx & FI_SHORT) != 0)
1208255332Scy		return 1;
1209255332Scy
1210255332Scy	if (fin->fin_cksum != FI_CK_NEEDED)
1211255332Scy		return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1;
1212255332Scy
1213255332Scy	if (ipf_checkl4sum(fin) == -1) {
1214145516Sdarrenr		fin->fin_flx |= FI_BAD;
1215255332Scy		return -1;
1216255332Scy	}
1217255332Scy	return 0;
1218145516Sdarrenr}
1219145516Sdarrenr#endif /* USE_INET6 */
1220145516Sdarrenr
1221145516Sdarrenr
1222255332Scysize_t
1223255332Scymbufchainlen(m0)
1224255332Scy	struct mbuf *m0;
1225255332Scy	{
1226145516Sdarrenr	size_t len;
1227145516Sdarrenr
1228145516Sdarrenr	if ((m0->m_flags & M_PKTHDR) != 0) {
1229145516Sdarrenr		len = m0->m_pkthdr.len;
1230145516Sdarrenr	} else {
1231145516Sdarrenr		struct mbuf *m;
1232145516Sdarrenr
1233145516Sdarrenr		for (m = m0, len = 0; m != NULL; m = m->m_next)
1234145516Sdarrenr			len += m->m_len;
1235145516Sdarrenr	}
1236145516Sdarrenr	return len;
1237145516Sdarrenr}
1238145516Sdarrenr
1239145516Sdarrenr
1240145516Sdarrenr/* ------------------------------------------------------------------------ */
1241255332Scy/* Function:    ipf_pullup                                                  */
1242145516Sdarrenr/* Returns:     NULL == pullup failed, else pointer to protocol header      */
1243255332Scy/* Parameters:  xmin(I)- pointer to buffer where data packet starts         */
1244145516Sdarrenr/*              fin(I) - pointer to packet information                      */
1245145516Sdarrenr/*              len(I) - number of bytes to pullup                          */
1246145516Sdarrenr/*                                                                          */
1247145516Sdarrenr/* Attempt to move at least len bytes (from the start of the buffer) into a */
1248145516Sdarrenr/* single buffer for ease of access.  Operating system native functions are */
1249145516Sdarrenr/* used to manage buffers - if necessary.  If the entire packet ends up in  */
1250255332Scy/* a single buffer, set the FI_COALESCE flag even though ipf_coalesce() has */
1251145516Sdarrenr/* not been called.  Both fin_ip and fin_dp are updated before exiting _IF_ */
1252145516Sdarrenr/* and ONLY if the pullup succeeds.                                         */
1253145516Sdarrenr/*                                                                          */
1254255332Scy/* We assume that 'xmin' is a pointer to a buffer that is part of the chain */
1255145516Sdarrenr/* of buffers that starts at *fin->fin_mp.                                  */
1256145516Sdarrenr/* ------------------------------------------------------------------------ */
1257255332Scyvoid *
1258255332Scyipf_pullup(xmin, fin, len)
1259255332Scy	mb_t *xmin;
1260255332Scy	fr_info_t *fin;
1261255332Scy	int len;
1262145516Sdarrenr{
1263255332Scy	int dpoff, ipoff;
1264255332Scy	mb_t *m = xmin;
1265145516Sdarrenr	char *ip;
1266145516Sdarrenr
1267145516Sdarrenr	if (m == NULL)
1268145516Sdarrenr		return NULL;
1269145516Sdarrenr
1270145516Sdarrenr	ip = (char *)fin->fin_ip;
1271145516Sdarrenr	if ((fin->fin_flx & FI_COALESCE) != 0)
1272145516Sdarrenr		return ip;
1273145516Sdarrenr
1274145516Sdarrenr	ipoff = fin->fin_ipoff;
1275145516Sdarrenr	if (fin->fin_dp != NULL)
1276145516Sdarrenr		dpoff = (char *)fin->fin_dp - (char *)ip;
1277145516Sdarrenr	else
1278145516Sdarrenr		dpoff = 0;
1279145516Sdarrenr
1280145516Sdarrenr	if (M_LEN(m) < len) {
1281255332Scy		mb_t *n = *fin->fin_mp;
1282145516Sdarrenr		/*
1283145516Sdarrenr		 * Assume that M_PKTHDR is set and just work with what is left
1284145516Sdarrenr		 * rather than check..
1285145516Sdarrenr		 * Should not make any real difference, anyway.
1286145516Sdarrenr		 */
1287255332Scy		if (m != n) {
1288255332Scy			/*
1289255332Scy			 * Record the mbuf that points to the mbuf that we're
1290255332Scy			 * about to go to work on so that we can update the
1291255332Scy			 * m_next appropriately later.
1292255332Scy			 */
1293255332Scy			for (; n->m_next != m; n = n->m_next)
1294255332Scy				;
1295255332Scy		} else {
1296255332Scy			n = NULL;
1297255332Scy		}
1298255332Scy
1299255332Scy#ifdef MHLEN
1300145516Sdarrenr		if (len > MHLEN)
1301145516Sdarrenr#else
1302145516Sdarrenr		if (len > MLEN)
1303145516Sdarrenr#endif
1304145516Sdarrenr		{
1305145516Sdarrenr#ifdef HAVE_M_PULLDOWN
1306145516Sdarrenr			if (m_pulldown(m, 0, len, NULL) == NULL)
1307145516Sdarrenr				m = NULL;
1308145516Sdarrenr#else
1309145516Sdarrenr			FREE_MB_T(*fin->fin_mp);
1310145516Sdarrenr			m = NULL;
1311255332Scy			n = NULL;
1312145516Sdarrenr#endif
1313145516Sdarrenr		} else
1314145516Sdarrenr		{
1315145516Sdarrenr			m = m_pullup(m, len);
1316145516Sdarrenr		}
1317255332Scy		if (n != NULL)
1318255332Scy			n->m_next = m;
1319145516Sdarrenr		if (m == NULL) {
1320255332Scy			/*
1321255332Scy			 * When n is non-NULL, it indicates that m pointed to
1322255332Scy			 * a sub-chain (tail) of the mbuf and that the head
1323255332Scy			 * of this chain has not yet been free'd.
1324255332Scy			 */
1325255332Scy			if (n != NULL) {
1326255332Scy				FREE_MB_T(*fin->fin_mp);
1327255332Scy			}
1328255332Scy
1329255332Scy			*fin->fin_mp = NULL;
1330172776Sdarrenr			fin->fin_m = NULL;
1331145516Sdarrenr			return NULL;
1332145516Sdarrenr		}
1333172776Sdarrenr
1334255332Scy		if (n == NULL)
1335255332Scy			*fin->fin_mp = m;
1336255332Scy
1337172776Sdarrenr		while (M_LEN(m) == 0) {
1338172776Sdarrenr			m = m->m_next;
1339172776Sdarrenr		}
1340172776Sdarrenr		fin->fin_m = m;
1341145516Sdarrenr		ip = MTOD(m, char *) + ipoff;
1342255332Scy
1343255332Scy		fin->fin_ip = (ip_t *)ip;
1344255332Scy		if (fin->fin_dp != NULL)
1345255332Scy			fin->fin_dp = (char *)fin->fin_ip + dpoff;
1346255332Scy		if (fin->fin_fraghdr != NULL)
1347255332Scy			fin->fin_fraghdr = (char *)ip +
1348255332Scy					   ((char *)fin->fin_fraghdr -
1349255332Scy					    (char *)fin->fin_ip);
1350145516Sdarrenr	}
1351145516Sdarrenr
1352145516Sdarrenr	if (len == fin->fin_plen)
1353145516Sdarrenr		fin->fin_flx |= FI_COALESCE;
1354145516Sdarrenr	return ip;
1355145516Sdarrenr}
1356170268Sdarrenr
1357170268Sdarrenr
1358255332Scyint
1359255332Scyipf_inject(fin, m)
1360255332Scy	fr_info_t *fin;
1361255332Scy	mb_t *m;
1362170268Sdarrenr{
1363170268Sdarrenr	int error = 0;
1364170268Sdarrenr
1365170268Sdarrenr	if (fin->fin_out == 0) {
1366170268Sdarrenr		netisr_dispatch(NETISR_IP, m);
1367170268Sdarrenr	} else {
1368173931Sdarrenr		fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len);
1369173931Sdarrenr		fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off);
1370170268Sdarrenr		error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
1371170268Sdarrenr	}
1372170268Sdarrenr
1373170268Sdarrenr	return error;
1374170268Sdarrenr}
1375172776Sdarrenr
1376172776Sdarrenrint ipf_pfil_unhook(void) {
1377172776Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011)
1378172776Sdarrenr	struct pfil_head *ph_inet;
1379172776Sdarrenr#  ifdef USE_INET6
1380172776Sdarrenr	struct pfil_head *ph_inet6;
1381172776Sdarrenr#  endif
1382172776Sdarrenr#endif
1383172776Sdarrenr
1384172776Sdarrenr#ifdef NETBSD_PF
1385172776Sdarrenr	ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
1386172776Sdarrenr	if (ph_inet != NULL)
1387255332Scy		pfil_remove_hook((void *)ipf_check_wrapper, NULL,
1388172776Sdarrenr		    PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet);
1389172776Sdarrenr# ifdef USE_INET6
1390172776Sdarrenr	ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
1391172776Sdarrenr	if (ph_inet6 != NULL)
1392255332Scy		pfil_remove_hook((void *)ipf_check_wrapper6, NULL,
1393172776Sdarrenr		    PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6);
1394172776Sdarrenr# endif
1395172776Sdarrenr#endif
1396172776Sdarrenr
1397172776Sdarrenr	return (0);
1398172776Sdarrenr}
1399172776Sdarrenr
1400172776Sdarrenrint ipf_pfil_hook(void) {
1401172776Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011)
1402172776Sdarrenr	struct pfil_head *ph_inet;
1403172776Sdarrenr#  ifdef USE_INET6
1404172776Sdarrenr	struct pfil_head *ph_inet6;
1405172776Sdarrenr#  endif
1406172776Sdarrenr#endif
1407172776Sdarrenr
1408172776Sdarrenr# ifdef NETBSD_PF
1409172776Sdarrenr	ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
1410172776Sdarrenr#    ifdef USE_INET6
1411172776Sdarrenr	ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
1412172776Sdarrenr#    endif
1413172776Sdarrenr	if (ph_inet == NULL
1414172776Sdarrenr#    ifdef USE_INET6
1415172776Sdarrenr	    && ph_inet6 == NULL
1416172776Sdarrenr#    endif
1417255332Scy	   ) {
1418172776Sdarrenr		return ENODEV;
1419255332Scy	}
1420172776Sdarrenr
1421172776Sdarrenr	if (ph_inet != NULL)
1422255332Scy		pfil_add_hook((void *)ipf_check_wrapper, NULL,
1423172776Sdarrenr		    PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet);
1424172776Sdarrenr#  ifdef USE_INET6
1425172776Sdarrenr	if (ph_inet6 != NULL)
1426255332Scy		pfil_add_hook((void *)ipf_check_wrapper6, NULL,
1427172776Sdarrenr				      PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6);
1428172776Sdarrenr#  endif
1429172776Sdarrenr# endif
1430172776Sdarrenr	return (0);
1431172776Sdarrenr}
1432172776Sdarrenr
1433172776Sdarrenrvoid
1434172776Sdarrenripf_event_reg(void)
1435172776Sdarrenr{
1436255332Scy	ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \
1437255332Scy					       ipf_ifevent, &ipfmain, \
1438172776Sdarrenr					       EVENTHANDLER_PRI_ANY);
1439255332Scy	ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \
1440255332Scy					       ipf_ifevent, &ipfmain, \
1441172776Sdarrenr					       EVENTHANDLER_PRI_ANY);
1442255332Scy	ipf_clonetag  = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \
1443255332Scy					       &ipfmain, EVENTHANDLER_PRI_ANY);
1444172776Sdarrenr}
1445172776Sdarrenr
1446172776Sdarrenrvoid
1447172776Sdarrenripf_event_dereg(void)
1448172776Sdarrenr{
1449172776Sdarrenr	if (ipf_arrivetag != NULL) {
1450172776Sdarrenr		EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ipf_arrivetag);
1451172776Sdarrenr	}
1452172776Sdarrenr	if (ipf_departtag != NULL) {
1453172776Sdarrenr		EVENTHANDLER_DEREGISTER(ifnet_departure_event, ipf_departtag);
1454172776Sdarrenr	}
1455172776Sdarrenr	if (ipf_clonetag != NULL) {
1456172776Sdarrenr		EVENTHANDLER_DEREGISTER(if_clone_event, ipf_clonetag);
1457172776Sdarrenr	}
1458172776Sdarrenr}
1459255332Scy
1460255332Scy
1461255332Scyu_32_t
1462255332Scyipf_random()
1463255332Scy{
1464255332Scy	return arc4random();
1465255332Scy}
1466255332Scy
1467255332Scy
1468255332Scyu_int
1469255332Scyipf_pcksum(fin, hlen, sum)
1470255332Scy	fr_info_t *fin;
1471255332Scy	int hlen;
1472255332Scy	u_int sum;
1473255332Scy{
1474255332Scy	struct mbuf *m;
1475255332Scy	u_int sum2;
1476255332Scy	int off;
1477255332Scy
1478255332Scy	m = fin->fin_m;
1479255332Scy	off = (char *)fin->fin_dp - (char *)fin->fin_ip;
1480255332Scy	m->m_data += hlen;
1481255332Scy	m->m_len -= hlen;
1482255332Scy	sum2 = in_cksum(fin->fin_m, fin->fin_plen - off);
1483255332Scy	m->m_len += hlen;
1484255332Scy	m->m_data -= hlen;
1485255332Scy
1486255332Scy	/*
1487255332Scy	 * Both sum and sum2 are partial sums, so combine them together.
1488255332Scy	 */
1489255332Scy	sum += ~sum2 & 0xffff;
1490255332Scy	while (sum > 0xffff)
1491255332Scy		sum = (sum & 0xffff) + (sum >> 16);
1492255332Scy	sum2 = ~sum & 0xffff;
1493255332Scy	return sum2;
1494255332Scy}
1495