1145516Sdarrenr/*	$FreeBSD$	*/
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>
36255332Scy# include <sys/mbuf.h>
37255332Scy# include <sys/sockopt.h>
38145516Sdarrenr#if !defined(__hpux)
39145516Sdarrenr# include <sys/mbuf.h>
40145516Sdarrenr#endif
41145516Sdarrenr#include <sys/socket.h>
42170268Sdarrenr# include <sys/selinfo.h>
43195699Srwatson# include <netinet/tcp_var.h>
44145516Sdarrenr
45145516Sdarrenr#include <net/if.h>
46145516Sdarrenr# include <net/if_var.h>
47170268Sdarrenr#  include <net/netisr.h>
48145516Sdarrenr#include <net/route.h>
49145516Sdarrenr#include <netinet/in.h>
50145516Sdarrenr#include <netinet/in_var.h>
51145516Sdarrenr#include <netinet/in_systm.h>
52145516Sdarrenr#include <netinet/ip.h>
53145516Sdarrenr#include <netinet/ip_var.h>
54145516Sdarrenr#include <netinet/tcp.h>
55145516Sdarrenr#if defined(__osf__)
56145516Sdarrenr# include <netinet/tcp_timer.h>
57145516Sdarrenr#endif
58145516Sdarrenr#include <netinet/udp.h>
59145516Sdarrenr#include <netinet/tcpip.h>
60145516Sdarrenr#include <netinet/ip_icmp.h>
61145516Sdarrenr#include "netinet/ip_compat.h"
62145516Sdarrenr#ifdef USE_INET6
63145516Sdarrenr# include <netinet/icmp6.h>
64145516Sdarrenr#endif
65145516Sdarrenr#include "netinet/ip_fil.h"
66145516Sdarrenr#include "netinet/ip_nat.h"
67145516Sdarrenr#include "netinet/ip_frag.h"
68145516Sdarrenr#include "netinet/ip_state.h"
69145516Sdarrenr#include "netinet/ip_proxy.h"
70145516Sdarrenr#include "netinet/ip_auth.h"
71145516Sdarrenr#include "netinet/ip_sync.h"
72255332Scy#include "netinet/ip_lookup.h"
73255332Scy#include "netinet/ip_dstlist.h"
74145516Sdarrenr#ifdef	IPFILTER_SCAN
75145516Sdarrenr#include "netinet/ip_scan.h"
76145516Sdarrenr#endif
77145516Sdarrenr#include "netinet/ip_pool.h"
78145516Sdarrenr# include <sys/malloc.h>
79145516Sdarrenr#include <sys/kernel.h>
80145516Sdarrenr#ifdef CSUM_DATA_VALID
81145516Sdarrenr#include <machine/in_cksum.h>
82145516Sdarrenr#endif
83145516Sdarrenrextern	int	ip_optcopy __P((struct ip *, struct ip *));
84145516Sdarrenr
85145516Sdarrenr
86145516Sdarrenr# ifdef IPFILTER_M_IPFILTER
87151897SrwatsonMALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures");
88145516Sdarrenr# endif
89145516Sdarrenr
90145516Sdarrenr
91255332Scystatic	u_short	ipid = 0;
92255332Scystatic	int	(*ipf_savep) __P((void *, ip_t *, int, void *, int, struct mbuf **));
93255332Scystatic	int	ipf_send_ip __P((fr_info_t *, mb_t *));
94255332Scystatic void	ipf_timer_func __P((void *arg));
95145516Sdarrenrint		ipf_locks_done = 0;
96145516Sdarrenr
97255332Scyipf_main_softc_t ipfmain;
98145516Sdarrenr
99145516Sdarrenr# include <sys/conf.h>
100145516Sdarrenr# if defined(NETBSD_PF)
101145516Sdarrenr#  include <net/pfil.h>
102255332Scy# endif /* NETBSD_PF */
103145516Sdarrenr/*
104255332Scy * We provide the ipf_checkp name just to minimize changes later.
105145516Sdarrenr */
106255332Scyint (*ipf_checkp) __P((void *, ip_t *ip, int hlen, void *ifp, int out, mb_t **mp));
107145516Sdarrenr
108145516Sdarrenr
109153876Sguidostatic eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag;
110153876Sguido
111153876Sguidostatic void ipf_ifevent(void *arg);
112153876Sguido
113153876Sguidostatic void ipf_ifevent(arg)
114255332Scy	void *arg;
115153876Sguido{
116255332Scy        ipf_sync(arg, NULL);
117153876Sguido}
118153876Sguido
119153876Sguido
120145516Sdarrenr
121145516Sdarrenrstatic int
122255332Scyipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
123145516Sdarrenr{
124145516Sdarrenr	struct ip *ip = mtod(*mp, struct ip *);
125255332Scy	int rv;
126255332Scy
127255332Scy	/*
128255332Scy	 * IPFilter expects evreything in network byte order
129255332Scy	 */
130255332Scy#if (__FreeBSD_version < 1000019)
131255332Scy	ip->ip_len = htons(ip->ip_len);
132255332Scy	ip->ip_off = htons(ip->ip_off);
133255332Scy#endif
134255332Scy	rv = ipf_check(&ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT),
135255332Scy		       mp);
136255332Scy#if (__FreeBSD_version < 1000019)
137255332Scy	if ((rv == 0) && (*mp != NULL)) {
138255332Scy		ip = mtod(*mp, struct ip *);
139255332Scy		ip->ip_len = ntohs(ip->ip_len);
140255332Scy		ip->ip_off = ntohs(ip->ip_off);
141255332Scy	}
142255332Scy#endif
143255332Scy	return rv;
144145516Sdarrenr}
145145516Sdarrenr
146145516Sdarrenr# ifdef USE_INET6
147145516Sdarrenr#  include <netinet/ip6.h>
148145516Sdarrenr
149145516Sdarrenrstatic int
150255332Scyipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
151145516Sdarrenr{
152255332Scy	return (ipf_check(&ipfmain, mtod(*mp, struct ip *),
153255332Scy			  sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp));
154145516Sdarrenr}
155145516Sdarrenr# endif
156145516Sdarrenr#if	defined(IPFILTER_LKM)
157255332Scyint ipf_identify(s)
158255332Scy	char *s;
159145516Sdarrenr{
160145516Sdarrenr	if (strcmp(s, "ipl") == 0)
161145516Sdarrenr		return 1;
162145516Sdarrenr	return 0;
163145516Sdarrenr}
164145516Sdarrenr#endif /* IPFILTER_LKM */
165145516Sdarrenr
166145516Sdarrenr
167255332Scystatic void
168255332Scyipf_timer_func(arg)
169255332Scy	void *arg;
170145516Sdarrenr{
171255332Scy	ipf_main_softc_t *softc = arg;
172255332Scy	SPL_INT(s);
173255332Scy
174255332Scy	SPL_NET(s);
175255332Scy	READ_ENTER(&softc->ipf_global);
176255332Scy
177255332Scy        if (softc->ipf_running > 0)
178255332Scy		ipf_slowtimer(softc);
179255332Scy
180255332Scy	if (softc->ipf_running == -1 || softc->ipf_running == 1) {
181255755Scy#if 0
182255332Scy		softc->ipf_slow_ch = timeout(ipf_timer_func, softc, hz/2);
183255332Scy#endif
184255755Scy		callout_init(&softc->ipf_slow_ch, CALLOUT_MPSAFE);
185255755Scy		callout_reset(&softc->ipf_slow_ch,
186255755Scy			(hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT,
187255755Scy			ipf_timer_func, softc);
188255332Scy	}
189255332Scy	RWLOCK_EXIT(&softc->ipf_global);
190255332Scy	SPL_X(s);
191255332Scy}
192255332Scy
193255332Scy
194255332Scyint
195255332Scyipfattach(softc)
196255332Scy	ipf_main_softc_t *softc;
197255332Scy{
198145516Sdarrenr#ifdef USE_SPL
199145516Sdarrenr	int s;
200145516Sdarrenr#endif
201145516Sdarrenr
202145516Sdarrenr	SPL_NET(s);
203255332Scy	if (softc->ipf_running > 0) {
204145516Sdarrenr		SPL_X(s);
205145516Sdarrenr		return EBUSY;
206145516Sdarrenr	}
207145516Sdarrenr
208255332Scy	if (ipf_init_all(softc) < 0) {
209145516Sdarrenr		SPL_X(s);
210145516Sdarrenr		return EIO;
211145516Sdarrenr	}
212145516Sdarrenr
213145516Sdarrenr
214255332Scy	if (ipf_checkp != ipf_check) {
215255332Scy		ipf_savep = ipf_checkp;
216255332Scy		ipf_checkp = ipf_check;
217145516Sdarrenr	}
218145516Sdarrenr
219255332Scy	bzero((char *)ipfmain.ipf_selwait, sizeof(ipfmain.ipf_selwait));
220255332Scy	softc->ipf_running = 1;
221145516Sdarrenr
222255332Scy	if (softc->ipf_control_forwarding & 1)
223181803Sbz		V_ipforwarding = 1;
224145516Sdarrenr
225255332Scy	ipid = 0;
226255332Scy
227145516Sdarrenr	SPL_X(s);
228255755Scy#if 0
229255332Scy	softc->ipf_slow_ch = timeout(ipf_timer_func, softc,
230255332Scy				     (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT);
231255755Scy#endif
232255755Scy	callout_init(&softc->ipf_slow_ch, CALLOUT_MPSAFE);
233255755Scy	callout_reset(&softc->ipf_slow_ch, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT,
234255755Scy		ipf_timer_func, softc);
235145516Sdarrenr	return 0;
236145516Sdarrenr}
237145516Sdarrenr
238145516Sdarrenr
239145516Sdarrenr/*
240145516Sdarrenr * Disable the filter by removing the hooks from the IP input/output
241145516Sdarrenr * stream.
242145516Sdarrenr */
243255332Scyint
244255332Scyipfdetach(softc)
245255332Scy	ipf_main_softc_t *softc;
246145516Sdarrenr{
247145516Sdarrenr#ifdef USE_SPL
248145516Sdarrenr	int s;
249145516Sdarrenr#endif
250255332Scy
251255332Scy	if (softc->ipf_control_forwarding & 2)
252181803Sbz		V_ipforwarding = 0;
253145516Sdarrenr
254145516Sdarrenr	SPL_NET(s);
255145516Sdarrenr
256255755Scy#if 0
257255332Scy	if (softc->ipf_slow_ch.callout != NULL)
258255332Scy		untimeout(ipf_timer_func, softc, softc->ipf_slow_ch);
259255332Scy	bzero(&softc->ipf_slow, sizeof(softc->ipf_slow));
260255755Scy#endif
261255755Scy	callout_drain(&softc->ipf_slow_ch);
262145516Sdarrenr
263145516Sdarrenr#ifndef NETBSD_PF
264255332Scy	if (ipf_checkp != NULL)
265255332Scy		ipf_checkp = ipf_savep;
266255332Scy	ipf_savep = NULL;
267145516Sdarrenr#endif
268145516Sdarrenr
269255332Scy	ipf_fini_all(softc);
270145516Sdarrenr
271255332Scy	softc->ipf_running = -2;
272145516Sdarrenr
273145516Sdarrenr	SPL_X(s);
274145516Sdarrenr
275145516Sdarrenr	return 0;
276145516Sdarrenr}
277145516Sdarrenr
278145516Sdarrenr
279145516Sdarrenr/*
280145516Sdarrenr * Filter ioctl interface.
281145516Sdarrenr */
282255332Scyint
283255332Scyipfioctl(dev, cmd, data, mode
284145516Sdarrenr, p)
285255332Scy	struct thread *p;
286192895Sjamie#    define	p_cred	td_ucred
287170268Sdarrenr#    define	p_uid	td_ucred->cr_ruid
288255332Scy	struct cdev *dev;
289255332Scy	ioctlcmd_t cmd;
290255332Scy	caddr_t data;
291255332Scy	int mode;
292145516Sdarrenr{
293170268Sdarrenr	int error = 0, unit = 0;
294170268Sdarrenr	SPL_INT(s);
295145516Sdarrenr
296255332Scy#if (BSD >= 199306)
297255332Scy        if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE))
298255332Scy	{
299255332Scy		ipfmain.ipf_interror = 130001;
300145516Sdarrenr		return EPERM;
301255332Scy	}
302145516Sdarrenr#endif
303145516Sdarrenr
304145516Sdarrenr	unit = GET_MINOR(dev);
305255332Scy	if ((IPL_LOGMAX < unit) || (unit < 0)) {
306255332Scy		ipfmain.ipf_interror = 130002;
307145516Sdarrenr		return ENXIO;
308255332Scy	}
309145516Sdarrenr
310255332Scy	if (ipfmain.ipf_running <= 0) {
311255332Scy		if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) {
312255332Scy			ipfmain.ipf_interror = 130003;
313145516Sdarrenr			return EIO;
314255332Scy		}
315145516Sdarrenr		if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET &&
316173181Sdarrenr		    cmd != SIOCIPFSET && cmd != SIOCFRENB &&
317255332Scy		    cmd != SIOCGETFS && cmd != SIOCGETFF &&
318255332Scy		    cmd != SIOCIPFINTERROR) {
319255332Scy			ipfmain.ipf_interror = 130004;
320145516Sdarrenr			return EIO;
321255332Scy		}
322145516Sdarrenr	}
323145516Sdarrenr
324145516Sdarrenr	SPL_NET(s);
325145516Sdarrenr
326255332Scy	error = ipf_ioctlswitch(&ipfmain, unit, data, cmd, mode, p->p_uid, p);
327145516Sdarrenr	if (error != -1) {
328145516Sdarrenr		SPL_X(s);
329145516Sdarrenr		return error;
330145516Sdarrenr	}
331145516Sdarrenr
332145516Sdarrenr	SPL_X(s);
333161356Sguido
334145516Sdarrenr	return error;
335145516Sdarrenr}
336145516Sdarrenr
337145516Sdarrenr
338145516Sdarrenr/*
339255332Scy * ipf_send_reset - this could conceivably be a call to tcp_respond(), but that
340145516Sdarrenr * requires a large amount of setting up and isn't any more efficient.
341145516Sdarrenr */
342255332Scyint
343255332Scyipf_send_reset(fin)
344255332Scy	fr_info_t *fin;
345145516Sdarrenr{
346145516Sdarrenr	struct tcphdr *tcp, *tcp2;
347145516Sdarrenr	int tlen = 0, hlen;
348145516Sdarrenr	struct mbuf *m;
349145516Sdarrenr#ifdef USE_INET6
350145516Sdarrenr	ip6_t *ip6;
351145516Sdarrenr#endif
352145516Sdarrenr	ip_t *ip;
353145516Sdarrenr
354145516Sdarrenr	tcp = fin->fin_dp;
355145516Sdarrenr	if (tcp->th_flags & TH_RST)
356145516Sdarrenr		return -1;		/* feedback loop */
357145516Sdarrenr
358255332Scy	if (ipf_checkl4sum(fin) == -1)
359145516Sdarrenr		return -1;
360145516Sdarrenr
361145516Sdarrenr	tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) +
362145516Sdarrenr			((tcp->th_flags & TH_SYN) ? 1 : 0) +
363145516Sdarrenr			((tcp->th_flags & TH_FIN) ? 1 : 0);
364145516Sdarrenr
365145516Sdarrenr#ifdef USE_INET6
366145516Sdarrenr	hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t);
367145516Sdarrenr#else
368145516Sdarrenr	hlen = sizeof(ip_t);
369145516Sdarrenr#endif
370145516Sdarrenr#ifdef MGETHDR
371145516Sdarrenr	MGETHDR(m, M_DONTWAIT, MT_HEADER);
372145516Sdarrenr#else
373145516Sdarrenr	MGET(m, M_DONTWAIT, MT_HEADER);
374145516Sdarrenr#endif
375145516Sdarrenr	if (m == NULL)
376145516Sdarrenr		return -1;
377145516Sdarrenr	if (sizeof(*tcp2) + hlen > MLEN) {
378145516Sdarrenr		MCLGET(m, M_DONTWAIT);
379145516Sdarrenr		if ((m->m_flags & M_EXT) == 0) {
380145516Sdarrenr			FREE_MB_T(m);
381145516Sdarrenr			return -1;
382145516Sdarrenr		}
383145516Sdarrenr	}
384145516Sdarrenr
385145516Sdarrenr	m->m_len = sizeof(*tcp2) + hlen;
386145516Sdarrenr#if (BSD >= 199103)
387145516Sdarrenr	m->m_data += max_linkhdr;
388145516Sdarrenr	m->m_pkthdr.len = m->m_len;
389145516Sdarrenr	m->m_pkthdr.rcvif = (struct ifnet *)0;
390145516Sdarrenr#endif
391145516Sdarrenr	ip = mtod(m, struct ip *);
392145516Sdarrenr	bzero((char *)ip, hlen);
393145516Sdarrenr#ifdef USE_INET6
394145516Sdarrenr	ip6 = (ip6_t *)ip;
395145516Sdarrenr#endif
396145516Sdarrenr	tcp2 = (struct tcphdr *)((char *)ip + hlen);
397145516Sdarrenr	tcp2->th_sport = tcp->th_dport;
398145516Sdarrenr	tcp2->th_dport = tcp->th_sport;
399145516Sdarrenr
400145516Sdarrenr	if (tcp->th_flags & TH_ACK) {
401145516Sdarrenr		tcp2->th_seq = tcp->th_ack;
402145516Sdarrenr		tcp2->th_flags = TH_RST;
403145516Sdarrenr		tcp2->th_ack = 0;
404145516Sdarrenr	} else {
405145516Sdarrenr		tcp2->th_seq = 0;
406145516Sdarrenr		tcp2->th_ack = ntohl(tcp->th_seq);
407145516Sdarrenr		tcp2->th_ack += tlen;
408145516Sdarrenr		tcp2->th_ack = htonl(tcp2->th_ack);
409145516Sdarrenr		tcp2->th_flags = TH_RST|TH_ACK;
410145516Sdarrenr	}
411145516Sdarrenr	TCP_X2_A(tcp2, 0);
412145516Sdarrenr	TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2);
413145516Sdarrenr	tcp2->th_win = tcp->th_win;
414145516Sdarrenr	tcp2->th_sum = 0;
415145516Sdarrenr	tcp2->th_urp = 0;
416145516Sdarrenr
417145516Sdarrenr#ifdef USE_INET6
418145516Sdarrenr	if (fin->fin_v == 6) {
419145516Sdarrenr		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
420145516Sdarrenr		ip6->ip6_plen = htons(sizeof(struct tcphdr));
421145516Sdarrenr		ip6->ip6_nxt = IPPROTO_TCP;
422145516Sdarrenr		ip6->ip6_hlim = 0;
423255332Scy		ip6->ip6_src = fin->fin_dst6.in6;
424255332Scy		ip6->ip6_dst = fin->fin_src6.in6;
425145516Sdarrenr		tcp2->th_sum = in6_cksum(m, IPPROTO_TCP,
426145516Sdarrenr					 sizeof(*ip6), sizeof(*tcp2));
427255332Scy		return ipf_send_ip(fin, m);
428145516Sdarrenr	}
429145516Sdarrenr#endif
430145516Sdarrenr	ip->ip_p = IPPROTO_TCP;
431145516Sdarrenr	ip->ip_len = htons(sizeof(struct tcphdr));
432145516Sdarrenr	ip->ip_src.s_addr = fin->fin_daddr;
433145516Sdarrenr	ip->ip_dst.s_addr = fin->fin_saddr;
434145516Sdarrenr	tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2));
435255332Scy	ip->ip_len = htons(hlen + sizeof(*tcp2));
436255332Scy	return ipf_send_ip(fin, m);
437145516Sdarrenr}
438145516Sdarrenr
439145516Sdarrenr
440255332Scy/*
441255332Scy * ip_len must be in network byte order when called.
442255332Scy */
443255332Scystatic int
444255332Scyipf_send_ip(fin, m)
445255332Scy	fr_info_t *fin;
446255332Scy	mb_t *m;
447145516Sdarrenr{
448145516Sdarrenr	fr_info_t fnew;
449145516Sdarrenr	ip_t *ip, *oip;
450145516Sdarrenr	int hlen;
451145516Sdarrenr
452145516Sdarrenr	ip = mtod(m, ip_t *);
453145516Sdarrenr	bzero((char *)&fnew, sizeof(fnew));
454255332Scy	fnew.fin_main_soft = fin->fin_main_soft;
455145516Sdarrenr
456145516Sdarrenr	IP_V_A(ip, fin->fin_v);
457145516Sdarrenr	switch (fin->fin_v)
458145516Sdarrenr	{
459145516Sdarrenr	case 4 :
460255332Scy		oip = fin->fin_ip;
461255332Scy		hlen = sizeof(*oip);
462145516Sdarrenr		fnew.fin_v = 4;
463255332Scy		fnew.fin_p = ip->ip_p;
464255332Scy		fnew.fin_plen = ntohs(ip->ip_len);
465145516Sdarrenr		IP_HL_A(ip, sizeof(*oip) >> 2);
466145516Sdarrenr		ip->ip_tos = oip->ip_tos;
467145516Sdarrenr		ip->ip_id = fin->fin_ip->ip_id;
468255332Scy#if defined(FreeBSD) && (__FreeBSD_version > 460000)
469255332Scy		ip->ip_off = htons(path_mtu_discovery ? IP_DF : 0);
470145516Sdarrenr#else
471145516Sdarrenr		ip->ip_off = 0;
472145516Sdarrenr#endif
473181803Sbz		ip->ip_ttl = V_ip_defttl;
474145516Sdarrenr		ip->ip_sum = 0;
475145516Sdarrenr		break;
476145516Sdarrenr#ifdef USE_INET6
477145516Sdarrenr	case 6 :
478145516Sdarrenr	{
479145516Sdarrenr		ip6_t *ip6 = (ip6_t *)ip;
480145516Sdarrenr
481145516Sdarrenr		ip6->ip6_vfc = 0x60;
482145516Sdarrenr		ip6->ip6_hlim = IPDEFTTL;
483145516Sdarrenr
484255332Scy		hlen = sizeof(*ip6);
485255332Scy		fnew.fin_p = ip6->ip6_nxt;
486145516Sdarrenr		fnew.fin_v = 6;
487255332Scy		fnew.fin_plen = ntohs(ip6->ip6_plen) + hlen;
488145516Sdarrenr		break;
489145516Sdarrenr	}
490145516Sdarrenr#endif
491145516Sdarrenr	default :
492145516Sdarrenr		return EINVAL;
493145516Sdarrenr	}
494145516Sdarrenr#ifdef IPSEC
495145516Sdarrenr	m->m_pkthdr.rcvif = NULL;
496145516Sdarrenr#endif
497145516Sdarrenr
498145516Sdarrenr	fnew.fin_ifp = fin->fin_ifp;
499145516Sdarrenr	fnew.fin_flx = FI_NOCKSUM;
500145516Sdarrenr	fnew.fin_m = m;
501145516Sdarrenr	fnew.fin_ip = ip;
502255332Scy	fnew.fin_mp = &m;
503145516Sdarrenr	fnew.fin_hlen = hlen;
504145516Sdarrenr	fnew.fin_dp = (char *)ip + hlen;
505255332Scy	(void) ipf_makefrip(hlen, ip, &fnew);
506145516Sdarrenr
507255332Scy	return ipf_fastroute(m, &m, &fnew, NULL);
508145516Sdarrenr}
509145516Sdarrenr
510145516Sdarrenr
511255332Scyint
512255332Scyipf_send_icmp_err(type, fin, dst)
513255332Scy	int type;
514255332Scy	fr_info_t *fin;
515255332Scy	int dst;
516145516Sdarrenr{
517145516Sdarrenr	int err, hlen, xtra, iclen, ohlen, avail, code;
518145516Sdarrenr	struct in_addr dst4;
519145516Sdarrenr	struct icmp *icmp;
520145516Sdarrenr	struct mbuf *m;
521255332Scy	i6addr_t dst6;
522145516Sdarrenr	void *ifp;
523145516Sdarrenr#ifdef USE_INET6
524145516Sdarrenr	ip6_t *ip6;
525145516Sdarrenr#endif
526145516Sdarrenr	ip_t *ip, *ip2;
527145516Sdarrenr
528172776Sdarrenr	if ((type < 0) || (type >= ICMP_MAXTYPE))
529145516Sdarrenr		return -1;
530145516Sdarrenr
531145516Sdarrenr	code = fin->fin_icode;
532145516Sdarrenr#ifdef USE_INET6
533255332Scy#if 0
534255332Scy	/* XXX Fix an off by one error: s/>/>=/
535255332Scy	 was:
536255332Scy	 if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int)))
537255332Scy	 Fix obtained from NetBSD ip_fil_netbsd.c r1.4: */
538255332Scy#endif
539255332Scy	if ((code < 0) || (code >= sizeof(icmptoicmp6unreach)/sizeof(int)))
540145516Sdarrenr		return -1;
541145516Sdarrenr#endif
542145516Sdarrenr
543255332Scy	if (ipf_checkl4sum(fin) == -1)
544145516Sdarrenr		return -1;
545145516Sdarrenr#ifdef MGETHDR
546145516Sdarrenr	MGETHDR(m, M_DONTWAIT, MT_HEADER);
547145516Sdarrenr#else
548145516Sdarrenr	MGET(m, M_DONTWAIT, MT_HEADER);
549145516Sdarrenr#endif
550145516Sdarrenr	if (m == NULL)
551145516Sdarrenr		return -1;
552145516Sdarrenr	avail = MHLEN;
553145516Sdarrenr
554145516Sdarrenr	xtra = 0;
555145516Sdarrenr	hlen = 0;
556145516Sdarrenr	ohlen = 0;
557255332Scy	dst4.s_addr = 0;
558145516Sdarrenr	ifp = fin->fin_ifp;
559145516Sdarrenr	if (fin->fin_v == 4) {
560255332Scy		if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT))
561145516Sdarrenr			switch (ntohs(fin->fin_data[0]) >> 8)
562145516Sdarrenr			{
563145516Sdarrenr			case ICMP_ECHO :
564145516Sdarrenr			case ICMP_TSTAMP :
565145516Sdarrenr			case ICMP_IREQ :
566145516Sdarrenr			case ICMP_MASKREQ :
567145516Sdarrenr				break;
568145516Sdarrenr			default :
569145516Sdarrenr				FREE_MB_T(m);
570145516Sdarrenr				return 0;
571145516Sdarrenr			}
572145516Sdarrenr
573145516Sdarrenr		if (dst == 0) {
574255332Scy			if (ipf_ifpaddr(&ipfmain, 4, FRI_NORMAL, ifp,
575255332Scy					&dst6, NULL) == -1) {
576145516Sdarrenr				FREE_MB_T(m);
577145516Sdarrenr				return -1;
578145516Sdarrenr			}
579255332Scy			dst4 = dst6.in4;
580145516Sdarrenr		} else
581145516Sdarrenr			dst4.s_addr = fin->fin_daddr;
582145516Sdarrenr
583145516Sdarrenr		hlen = sizeof(ip_t);
584145516Sdarrenr		ohlen = fin->fin_hlen;
585255332Scy		iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen;
586145516Sdarrenr		if (fin->fin_hlen < fin->fin_plen)
587145516Sdarrenr			xtra = MIN(fin->fin_dlen, 8);
588145516Sdarrenr		else
589145516Sdarrenr			xtra = 0;
590145516Sdarrenr	}
591145516Sdarrenr
592145516Sdarrenr#ifdef USE_INET6
593145516Sdarrenr	else if (fin->fin_v == 6) {
594145516Sdarrenr		hlen = sizeof(ip6_t);
595145516Sdarrenr		ohlen = sizeof(ip6_t);
596255332Scy		iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen;
597145516Sdarrenr		type = icmptoicmp6types[type];
598145516Sdarrenr		if (type == ICMP6_DST_UNREACH)
599145516Sdarrenr			code = icmptoicmp6unreach[code];
600145516Sdarrenr
601255332Scy		if (iclen + max_linkhdr + fin->fin_plen > avail) {
602145516Sdarrenr			MCLGET(m, M_DONTWAIT);
603145516Sdarrenr			if ((m->m_flags & M_EXT) == 0) {
604145516Sdarrenr				FREE_MB_T(m);
605145516Sdarrenr				return -1;
606145516Sdarrenr			}
607145516Sdarrenr			avail = MCLBYTES;
608145516Sdarrenr		}
609255332Scy		xtra = MIN(fin->fin_plen, avail - iclen - max_linkhdr);
610255332Scy		xtra = MIN(xtra, IPV6_MMTU - iclen);
611145516Sdarrenr		if (dst == 0) {
612255332Scy			if (ipf_ifpaddr(&ipfmain, 6, FRI_NORMAL, ifp,
613255332Scy					&dst6, NULL) == -1) {
614145516Sdarrenr				FREE_MB_T(m);
615145516Sdarrenr				return -1;
616145516Sdarrenr			}
617145516Sdarrenr		} else
618145516Sdarrenr			dst6 = fin->fin_dst6;
619145516Sdarrenr	}
620145516Sdarrenr#endif
621145516Sdarrenr	else {
622145516Sdarrenr		FREE_MB_T(m);
623145516Sdarrenr		return -1;
624145516Sdarrenr	}
625145516Sdarrenr
626145516Sdarrenr	avail -= (max_linkhdr + iclen);
627145516Sdarrenr	if (avail < 0) {
628145516Sdarrenr		FREE_MB_T(m);
629145516Sdarrenr		return -1;
630145516Sdarrenr	}
631145516Sdarrenr	if (xtra > avail)
632145516Sdarrenr		xtra = avail;
633145516Sdarrenr	iclen += xtra;
634145516Sdarrenr	m->m_data += max_linkhdr;
635145516Sdarrenr	m->m_pkthdr.rcvif = (struct ifnet *)0;
636145516Sdarrenr	m->m_pkthdr.len = iclen;
637145516Sdarrenr	m->m_len = iclen;
638145516Sdarrenr	ip = mtod(m, ip_t *);
639145516Sdarrenr	icmp = (struct icmp *)((char *)ip + hlen);
640145516Sdarrenr	ip2 = (ip_t *)&icmp->icmp_ip;
641145516Sdarrenr
642145516Sdarrenr	icmp->icmp_type = type;
643145516Sdarrenr	icmp->icmp_code = fin->fin_icode;
644145516Sdarrenr	icmp->icmp_cksum = 0;
645145516Sdarrenr#ifdef icmp_nextmtu
646255332Scy	if (type == ICMP_UNREACH && fin->fin_icode == ICMP_UNREACH_NEEDFRAG) {
647255332Scy		if (fin->fin_mtu != 0) {
648255332Scy			icmp->icmp_nextmtu = htons(fin->fin_mtu);
649255332Scy
650255332Scy		} else if (ifp != NULL) {
651255332Scy			icmp->icmp_nextmtu = htons(GETIFMTU_4(ifp));
652255332Scy
653255332Scy		} else {	/* make up a number... */
654255332Scy			icmp->icmp_nextmtu = htons(fin->fin_plen - 20);
655255332Scy		}
656255332Scy	}
657145516Sdarrenr#endif
658145516Sdarrenr
659145516Sdarrenr	bcopy((char *)fin->fin_ip, (char *)ip2, ohlen);
660145516Sdarrenr
661145516Sdarrenr#ifdef USE_INET6
662145516Sdarrenr	ip6 = (ip6_t *)ip;
663145516Sdarrenr	if (fin->fin_v == 6) {
664145516Sdarrenr		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
665145516Sdarrenr		ip6->ip6_plen = htons(iclen - hlen);
666145516Sdarrenr		ip6->ip6_nxt = IPPROTO_ICMPV6;
667145516Sdarrenr		ip6->ip6_hlim = 0;
668255332Scy		ip6->ip6_src = dst6.in6;
669255332Scy		ip6->ip6_dst = fin->fin_src6.in6;
670145516Sdarrenr		if (xtra > 0)
671145516Sdarrenr			bcopy((char *)fin->fin_ip + ohlen,
672145516Sdarrenr			      (char *)&icmp->icmp_ip + ohlen, xtra);
673145516Sdarrenr		icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6,
674145516Sdarrenr					     sizeof(*ip6), iclen - hlen);
675145516Sdarrenr	} else
676145516Sdarrenr#endif
677145516Sdarrenr	{
678145516Sdarrenr		ip->ip_p = IPPROTO_ICMP;
679145516Sdarrenr		ip->ip_src.s_addr = dst4.s_addr;
680145516Sdarrenr		ip->ip_dst.s_addr = fin->fin_saddr;
681145516Sdarrenr
682145516Sdarrenr		if (xtra > 0)
683145516Sdarrenr			bcopy((char *)fin->fin_ip + ohlen,
684145516Sdarrenr			      (char *)&icmp->icmp_ip + ohlen, xtra);
685145516Sdarrenr		icmp->icmp_cksum = ipf_cksum((u_short *)icmp,
686145516Sdarrenr					     sizeof(*icmp) + 8);
687255332Scy		ip->ip_len = htons(iclen);
688145516Sdarrenr		ip->ip_p = IPPROTO_ICMP;
689145516Sdarrenr	}
690255332Scy	err = ipf_send_ip(fin, m);
691145516Sdarrenr	return err;
692145516Sdarrenr}
693145516Sdarrenr
694145516Sdarrenr
695145516Sdarrenr
696145516Sdarrenr
697173181Sdarrenr/*
698173181Sdarrenr * m0 - pointer to mbuf where the IP packet starts
699173181Sdarrenr * mpp - pointer to the mbuf pointer that is the start of the mbuf chain
700173181Sdarrenr */
701255332Scyint
702255332Scyipf_fastroute(m0, mpp, fin, fdp)
703255332Scy	mb_t *m0, **mpp;
704255332Scy	fr_info_t *fin;
705255332Scy	frdest_t *fdp;
706145516Sdarrenr{
707145516Sdarrenr	register struct ip *ip, *mhip;
708173181Sdarrenr	register struct mbuf *m = *mpp;
709145516Sdarrenr	register struct route *ro;
710145516Sdarrenr	int len, off, error = 0, hlen, code;
711145516Sdarrenr	struct ifnet *ifp, *sifp;
712145516Sdarrenr	struct sockaddr_in *dst;
713145516Sdarrenr	struct route iproute;
714145516Sdarrenr	u_short ip_off;
715255332Scy	frdest_t node;
716145516Sdarrenr	frentry_t *fr;
717145516Sdarrenr
718161356Sguido	ro = NULL;
719161356Sguido
720145516Sdarrenr#ifdef M_WRITABLE
721145516Sdarrenr	/*
722145516Sdarrenr	* HOT FIX/KLUDGE:
723145516Sdarrenr	*
724145516Sdarrenr	* If the mbuf we're about to send is not writable (because of
725145516Sdarrenr	* a cluster reference, for example) we'll need to make a copy
726145516Sdarrenr	* of it since this routine modifies the contents.
727145516Sdarrenr	*
728145516Sdarrenr	* If you have non-crappy network hardware that can transmit data
729145516Sdarrenr	* from the mbuf, rather than making a copy, this is gonna be a
730145516Sdarrenr	* problem.
731145516Sdarrenr	*/
732145516Sdarrenr	if (M_WRITABLE(m) == 0) {
733161356Sguido		m0 = m_dup(m, M_DONTWAIT);
734161356Sguido		if (m0 != 0) {
735145516Sdarrenr			FREE_MB_T(m);
736145516Sdarrenr			m = m0;
737145516Sdarrenr			*mpp = m;
738145516Sdarrenr		} else {
739145516Sdarrenr			error = ENOBUFS;
740145516Sdarrenr			FREE_MB_T(m);
741161356Sguido			goto done;
742145516Sdarrenr		}
743145516Sdarrenr	}
744145516Sdarrenr#endif
745145516Sdarrenr
746145516Sdarrenr#ifdef USE_INET6
747145516Sdarrenr	if (fin->fin_v == 6) {
748145516Sdarrenr		/*
749145516Sdarrenr		 * currently "to <if>" and "to <if>:ip#" are not supported
750145516Sdarrenr		 * for IPv6
751145516Sdarrenr		 */
752255332Scy		return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
753145516Sdarrenr	}
754145516Sdarrenr#endif
755145516Sdarrenr
756145516Sdarrenr	hlen = fin->fin_hlen;
757145516Sdarrenr	ip = mtod(m0, struct ip *);
758255332Scy	ifp = NULL;
759145516Sdarrenr
760145516Sdarrenr	/*
761145516Sdarrenr	 * Route packet.
762145516Sdarrenr	 */
763145516Sdarrenr	ro = &iproute;
764255332Scy	bzero(ro, sizeof (*ro));
765145516Sdarrenr	dst = (struct sockaddr_in *)&ro->ro_dst;
766145516Sdarrenr	dst->sin_family = AF_INET;
767145516Sdarrenr	dst->sin_addr = ip->ip_dst;
768145516Sdarrenr
769145516Sdarrenr	fr = fin->fin_fr;
770255332Scy	if ((fr != NULL) && !(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) &&
771255332Scy	    (fdp->fd_type == FRD_DSTLIST)) {
772255332Scy		if (ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node) == 0)
773255332Scy			fdp = &node;
774255332Scy	}
775255332Scy
776145516Sdarrenr	if (fdp != NULL)
777255332Scy		ifp = fdp->fd_ptr;
778145516Sdarrenr	else
779145516Sdarrenr		ifp = fin->fin_ifp;
780145516Sdarrenr
781255332Scy	if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) {
782145516Sdarrenr		error = -2;
783145516Sdarrenr		goto bad;
784145516Sdarrenr	}
785145516Sdarrenr
786161356Sguido	if ((fdp != NULL) && (fdp->fd_ip.s_addr != 0))
787161356Sguido		dst->sin_addr = fdp->fd_ip;
788145516Sdarrenr
789145516Sdarrenr	dst->sin_len = sizeof(*dst);
790178888Sjulian	in_rtalloc(ro, 0);
791145516Sdarrenr
792145516Sdarrenr	if ((ifp == NULL) && (ro->ro_rt != NULL))
793145516Sdarrenr		ifp = ro->ro_rt->rt_ifp;
794145516Sdarrenr
795145516Sdarrenr	if ((ro->ro_rt == NULL) || (ifp == NULL)) {
796145516Sdarrenr		if (in_localaddr(ip->ip_dst))
797145516Sdarrenr			error = EHOSTUNREACH;
798145516Sdarrenr		else
799145516Sdarrenr			error = ENETUNREACH;
800145516Sdarrenr		goto bad;
801145516Sdarrenr	}
802145516Sdarrenr	if (ro->ro_rt->rt_flags & RTF_GATEWAY)
803145516Sdarrenr		dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
804145516Sdarrenr	if (ro->ro_rt)
805145516Sdarrenr		ro->ro_rt->rt_use++;
806145516Sdarrenr
807145516Sdarrenr	/*
808145516Sdarrenr	 * For input packets which are being "fastrouted", they won't
809145516Sdarrenr	 * go back through output filtering and miss their chance to get
810170268Sdarrenr	 * NAT'd and counted.  Duplicated packets aren't considered to be
811170268Sdarrenr	 * part of the normal packet stream, so do not NAT them or pass
812170268Sdarrenr	 * them through stateful checking, etc.
813145516Sdarrenr	 */
814170268Sdarrenr	if ((fdp != &fr->fr_dif) && (fin->fin_out == 0)) {
815145516Sdarrenr		sifp = fin->fin_ifp;
816145516Sdarrenr		fin->fin_ifp = ifp;
817145516Sdarrenr		fin->fin_out = 1;
818255332Scy		(void) ipf_acctpkt(fin, NULL);
819145516Sdarrenr		fin->fin_fr = NULL;
820145516Sdarrenr		if (!fr || !(fr->fr_flags & FR_RETMASK)) {
821145516Sdarrenr			u_32_t pass;
822145516Sdarrenr
823255332Scy			(void) ipf_state_check(fin, &pass);
824145516Sdarrenr		}
825145516Sdarrenr
826255332Scy		switch (ipf_nat_checkout(fin, NULL))
827145516Sdarrenr		{
828145516Sdarrenr		case 0 :
829145516Sdarrenr			break;
830145516Sdarrenr		case 1 :
831145516Sdarrenr			ip->ip_sum = 0;
832145516Sdarrenr			break;
833145516Sdarrenr		case -1 :
834145516Sdarrenr			error = -1;
835173181Sdarrenr			goto bad;
836145516Sdarrenr			break;
837145516Sdarrenr		}
838145516Sdarrenr
839145516Sdarrenr		fin->fin_ifp = sifp;
840145516Sdarrenr		fin->fin_out = 0;
841145516Sdarrenr	} else
842145516Sdarrenr		ip->ip_sum = 0;
843145516Sdarrenr	/*
844145516Sdarrenr	 * If small enough for interface, can just send directly.
845145516Sdarrenr	 */
846255332Scy	if (ntohs(ip->ip_len) <= ifp->if_mtu) {
847145516Sdarrenr		if (!ip->ip_sum)
848145516Sdarrenr			ip->ip_sum = in_cksum(m, hlen);
849145516Sdarrenr		error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst,
850255332Scy			    ro
851255332Scy			);
852145516Sdarrenr		goto done;
853145516Sdarrenr	}
854145516Sdarrenr	/*
855145516Sdarrenr	 * Too large for interface; fragment if possible.
856145516Sdarrenr	 * Must be able to put at least 8 bytes per fragment.
857145516Sdarrenr	 */
858145516Sdarrenr	ip_off = ntohs(ip->ip_off);
859145516Sdarrenr	if (ip_off & IP_DF) {
860145516Sdarrenr		error = EMSGSIZE;
861145516Sdarrenr		goto bad;
862145516Sdarrenr	}
863145516Sdarrenr	len = (ifp->if_mtu - hlen) &~ 7;
864145516Sdarrenr	if (len < 8) {
865145516Sdarrenr		error = EMSGSIZE;
866145516Sdarrenr		goto bad;
867145516Sdarrenr	}
868145516Sdarrenr
869145516Sdarrenr    {
870145516Sdarrenr	int mhlen, firstlen = len;
871145516Sdarrenr	struct mbuf **mnext = &m->m_act;
872145516Sdarrenr
873145516Sdarrenr	/*
874145516Sdarrenr	 * Loop through length of segment after first fragment,
875145516Sdarrenr	 * make new header and copy data of each part and link onto chain.
876145516Sdarrenr	 */
877145516Sdarrenr	m0 = m;
878145516Sdarrenr	mhlen = sizeof (struct ip);
879255332Scy	for (off = hlen + len; off < ntohs(ip->ip_len); off += len) {
880145516Sdarrenr#ifdef MGETHDR
881145516Sdarrenr		MGETHDR(m, M_DONTWAIT, MT_HEADER);
882145516Sdarrenr#else
883145516Sdarrenr		MGET(m, M_DONTWAIT, MT_HEADER);
884145516Sdarrenr#endif
885145516Sdarrenr		if (m == 0) {
886145516Sdarrenr			m = m0;
887145516Sdarrenr			error = ENOBUFS;
888145516Sdarrenr			goto bad;
889145516Sdarrenr		}
890145516Sdarrenr		m->m_data += max_linkhdr;
891145516Sdarrenr		mhip = mtod(m, struct ip *);
892145516Sdarrenr		bcopy((char *)ip, (char *)mhip, sizeof(*ip));
893145516Sdarrenr		if (hlen > sizeof (struct ip)) {
894145516Sdarrenr			mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
895145516Sdarrenr			IP_HL_A(mhip, mhlen >> 2);
896145516Sdarrenr		}
897145516Sdarrenr		m->m_len = mhlen;
898145516Sdarrenr		mhip->ip_off = ((off - hlen) >> 3) + ip_off;
899255332Scy		if (off + len >= ntohs(ip->ip_len))
900255332Scy			len = ntohs(ip->ip_len) - off;
901145516Sdarrenr		else
902145516Sdarrenr			mhip->ip_off |= IP_MF;
903145516Sdarrenr		mhip->ip_len = htons((u_short)(len + mhlen));
904161356Sguido		*mnext = m;
905145516Sdarrenr		m->m_next = m_copy(m0, off, len);
906145516Sdarrenr		if (m->m_next == 0) {
907145516Sdarrenr			error = ENOBUFS;	/* ??? */
908145516Sdarrenr			goto sendorfree;
909145516Sdarrenr		}
910145516Sdarrenr		m->m_pkthdr.len = mhlen + len;
911145516Sdarrenr		m->m_pkthdr.rcvif = NULL;
912145516Sdarrenr		mhip->ip_off = htons((u_short)mhip->ip_off);
913145516Sdarrenr		mhip->ip_sum = 0;
914145516Sdarrenr		mhip->ip_sum = in_cksum(m, mhlen);
915145516Sdarrenr		mnext = &m->m_act;
916145516Sdarrenr	}
917145516Sdarrenr	/*
918145516Sdarrenr	 * Update first fragment by trimming what's been copied out
919145516Sdarrenr	 * and updating header, then send each fragment (in order).
920145516Sdarrenr	 */
921145516Sdarrenr	m_adj(m0, hlen + firstlen - ip->ip_len);
922145516Sdarrenr	ip->ip_len = htons((u_short)(hlen + firstlen));
923145516Sdarrenr	ip->ip_off = htons((u_short)IP_MF);
924145516Sdarrenr	ip->ip_sum = 0;
925145516Sdarrenr	ip->ip_sum = in_cksum(m0, hlen);
926145516Sdarrenrsendorfree:
927145516Sdarrenr	for (m = m0; m; m = m0) {
928145516Sdarrenr		m0 = m->m_act;
929145516Sdarrenr		m->m_act = 0;
930145516Sdarrenr		if (error == 0)
931145516Sdarrenr			error = (*ifp->if_output)(ifp, m,
932255332Scy			    (struct sockaddr *)dst,
933255332Scy			    ro
934255332Scy			    );
935145516Sdarrenr		else
936145516Sdarrenr			FREE_MB_T(m);
937145516Sdarrenr	}
938255332Scy    }
939145516Sdarrenrdone:
940145516Sdarrenr	if (!error)
941255332Scy		ipfmain.ipf_frouteok[0]++;
942145516Sdarrenr	else
943255332Scy		ipfmain.ipf_frouteok[1]++;
944145516Sdarrenr
945161356Sguido	if ((ro != NULL) && (ro->ro_rt != NULL)) {
946145516Sdarrenr		RTFREE(ro->ro_rt);
947145516Sdarrenr	}
948145516Sdarrenr	return 0;
949145516Sdarrenrbad:
950145516Sdarrenr	if (error == EMSGSIZE) {
951145516Sdarrenr		sifp = fin->fin_ifp;
952145516Sdarrenr		code = fin->fin_icode;
953145516Sdarrenr		fin->fin_icode = ICMP_UNREACH_NEEDFRAG;
954145516Sdarrenr		fin->fin_ifp = ifp;
955255332Scy		(void) ipf_send_icmp_err(ICMP_UNREACH, fin, 1);
956145516Sdarrenr		fin->fin_ifp = sifp;
957145516Sdarrenr		fin->fin_icode = code;
958145516Sdarrenr	}
959145516Sdarrenr	FREE_MB_T(m);
960145516Sdarrenr	goto done;
961145516Sdarrenr}
962145516Sdarrenr
963145516Sdarrenr
964255332Scyint
965255332Scyipf_verifysrc(fin)
966255332Scy	fr_info_t *fin;
967145516Sdarrenr{
968145516Sdarrenr	struct sockaddr_in *dst;
969145516Sdarrenr	struct route iproute;
970145516Sdarrenr
971145516Sdarrenr	bzero((char *)&iproute, sizeof(iproute));
972145516Sdarrenr	dst = (struct sockaddr_in *)&iproute.ro_dst;
973145516Sdarrenr	dst->sin_len = sizeof(*dst);
974145516Sdarrenr	dst->sin_family = AF_INET;
975145516Sdarrenr	dst->sin_addr = fin->fin_src;
976178888Sjulian	in_rtalloc(&iproute, 0);
977145516Sdarrenr	if (iproute.ro_rt == NULL)
978145516Sdarrenr		return 0;
979145516Sdarrenr	return (fin->fin_ifp == iproute.ro_rt->rt_ifp);
980145516Sdarrenr}
981145516Sdarrenr
982145516Sdarrenr
983145516Sdarrenr/*
984145516Sdarrenr * return the first IP Address associated with an interface
985145516Sdarrenr */
986255332Scyint
987255332Scyipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask)
988255332Scy	ipf_main_softc_t *softc;
989255332Scy	int v, atype;
990255332Scy	void *ifptr;
991255332Scy	i6addr_t *inp, *inpmask;
992145516Sdarrenr{
993145516Sdarrenr#ifdef USE_INET6
994145516Sdarrenr	struct in6_addr *inp6 = NULL;
995145516Sdarrenr#endif
996145516Sdarrenr	struct sockaddr *sock, *mask;
997145516Sdarrenr	struct sockaddr_in *sin;
998145516Sdarrenr	struct ifaddr *ifa;
999145516Sdarrenr	struct ifnet *ifp;
1000145516Sdarrenr
1001145516Sdarrenr	if ((ifptr == NULL) || (ifptr == (void *)-1))
1002145516Sdarrenr		return -1;
1003145516Sdarrenr
1004145516Sdarrenr	sin = NULL;
1005145516Sdarrenr	ifp = ifptr;
1006145516Sdarrenr
1007145516Sdarrenr	if (v == 4)
1008255332Scy		inp->in4.s_addr = 0;
1009145516Sdarrenr#ifdef USE_INET6
1010145516Sdarrenr	else if (v == 6)
1011255332Scy		bzero((char *)inp, sizeof(*inp));
1012145516Sdarrenr#endif
1013145516Sdarrenr	ifa = TAILQ_FIRST(&ifp->if_addrhead);
1014145516Sdarrenr
1015145516Sdarrenr	sock = ifa->ifa_addr;
1016145516Sdarrenr	while (sock != NULL && ifa != NULL) {
1017145516Sdarrenr		sin = (struct sockaddr_in *)sock;
1018145516Sdarrenr		if ((v == 4) && (sin->sin_family == AF_INET))
1019145516Sdarrenr			break;
1020145516Sdarrenr#ifdef USE_INET6
1021145516Sdarrenr		if ((v == 6) && (sin->sin_family == AF_INET6)) {
1022145516Sdarrenr			inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr;
1023145516Sdarrenr			if (!IN6_IS_ADDR_LINKLOCAL(inp6) &&
1024145516Sdarrenr			    !IN6_IS_ADDR_LOOPBACK(inp6))
1025145516Sdarrenr				break;
1026145516Sdarrenr		}
1027145516Sdarrenr#endif
1028145516Sdarrenr		ifa = TAILQ_NEXT(ifa, ifa_link);
1029145516Sdarrenr		if (ifa != NULL)
1030145516Sdarrenr			sock = ifa->ifa_addr;
1031145516Sdarrenr	}
1032145516Sdarrenr
1033145516Sdarrenr	if (ifa == NULL || sin == NULL)
1034145516Sdarrenr		return -1;
1035145516Sdarrenr
1036145516Sdarrenr	mask = ifa->ifa_netmask;
1037145516Sdarrenr	if (atype == FRI_BROADCAST)
1038145516Sdarrenr		sock = ifa->ifa_broadaddr;
1039145516Sdarrenr	else if (atype == FRI_PEERADDR)
1040145516Sdarrenr		sock = ifa->ifa_dstaddr;
1041145516Sdarrenr
1042161356Sguido	if (sock == NULL)
1043161356Sguido		return -1;
1044161356Sguido
1045145516Sdarrenr#ifdef USE_INET6
1046145516Sdarrenr	if (v == 6) {
1047255332Scy		return ipf_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock,
1048255332Scy					 (struct sockaddr_in6 *)mask,
1049255332Scy					 inp, inpmask);
1050145516Sdarrenr	}
1051145516Sdarrenr#endif
1052255332Scy	return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)sock,
1053255332Scy				 (struct sockaddr_in *)mask,
1054255332Scy				 &inp->in4, &inpmask->in4);
1055145516Sdarrenr}
1056145516Sdarrenr
1057145516Sdarrenr
1058255332Scyu_32_t
1059255332Scyipf_newisn(fin)
1060255332Scy	fr_info_t *fin;
1061145516Sdarrenr{
1062145516Sdarrenr	u_32_t newiss;
1063145516Sdarrenr	newiss = arc4random();
1064145516Sdarrenr	return newiss;
1065145516Sdarrenr}
1066145516Sdarrenr
1067145516Sdarrenr
1068145516Sdarrenr/* ------------------------------------------------------------------------ */
1069255332Scy/* Function:    ipf_nextipid                                                */
1070145516Sdarrenr/* Returns:     int - 0 == success, -1 == error (packet should be droppped) */
1071145516Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
1072145516Sdarrenr/*                                                                          */
1073145516Sdarrenr/* Returns the next IPv4 ID to use for this packet.                         */
1074145516Sdarrenr/* ------------------------------------------------------------------------ */
1075255332Scyu_short
1076255332Scyipf_nextipid(fin)
1077255332Scy	fr_info_t *fin;
1078145516Sdarrenr{
1079145516Sdarrenr	u_short id;
1080145516Sdarrenr
1081255332Scy#ifndef	RANDOM_IP_ID
1082255332Scy	MUTEX_ENTER(&ipfmain.ipf_rw);
1083145516Sdarrenr	id = ipid++;
1084255332Scy	MUTEX_EXIT(&ipfmain.ipf_rw);
1085145516Sdarrenr#else
1086145516Sdarrenr	id = ip_randomid();
1087145516Sdarrenr#endif
1088145516Sdarrenr
1089145516Sdarrenr	return id;
1090145516Sdarrenr}
1091145516Sdarrenr
1092145516Sdarrenr
1093255332ScyINLINE int
1094255332Scyipf_checkv4sum(fin)
1095255332Scy	fr_info_t *fin;
1096145516Sdarrenr{
1097145516Sdarrenr#ifdef CSUM_DATA_VALID
1098145516Sdarrenr	int manual = 0;
1099145516Sdarrenr	u_short sum;
1100145516Sdarrenr	ip_t *ip;
1101145516Sdarrenr	mb_t *m;
1102145516Sdarrenr
1103145516Sdarrenr	if ((fin->fin_flx & FI_NOCKSUM) != 0)
1104255332Scy		return 0;
1105145516Sdarrenr
1106255332Scy	if ((fin->fin_flx & FI_SHORT) != 0)
1107255332Scy		return 1;
1108172776Sdarrenr
1109255332Scy	if (fin->fin_cksum != FI_CK_NEEDED)
1110255332Scy		return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1;
1111255332Scy
1112145516Sdarrenr	m = fin->fin_m;
1113145516Sdarrenr	if (m == NULL) {
1114145516Sdarrenr		manual = 1;
1115145516Sdarrenr		goto skipauto;
1116145516Sdarrenr	}
1117145516Sdarrenr	ip = fin->fin_ip;
1118145516Sdarrenr
1119255332Scy	if ((m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)) ==
1120255332Scy	    CSUM_IP_CHECKED) {
1121255332Scy		fin->fin_cksum = FI_CK_BAD;
1122255332Scy		fin->fin_flx |= FI_BAD;
1123255332Scy		return -1;
1124255332Scy	}
1125145516Sdarrenr	if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
1126145516Sdarrenr		if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
1127145516Sdarrenr			sum = m->m_pkthdr.csum_data;
1128145516Sdarrenr		else
1129145516Sdarrenr			sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
1130145516Sdarrenr					htonl(m->m_pkthdr.csum_data +
1131255332Scy					fin->fin_dlen + fin->fin_p));
1132145516Sdarrenr		sum ^= 0xffff;
1133172776Sdarrenr		if (sum != 0) {
1134255332Scy			fin->fin_cksum = FI_CK_BAD;
1135145516Sdarrenr			fin->fin_flx |= FI_BAD;
1136172776Sdarrenr		} else {
1137255332Scy			fin->fin_cksum = FI_CK_SUMOK;
1138255332Scy			return 0;
1139172776Sdarrenr		}
1140255332Scy	} else {
1141255332Scy		if (m->m_pkthdr.csum_flags == CSUM_DELAY_DATA) {
1142255332Scy			fin->fin_cksum = FI_CK_L4FULL;
1143255332Scy			return 0;
1144255332Scy		} else if (m->m_pkthdr.csum_flags == CSUM_TCP ||
1145255332Scy			   m->m_pkthdr.csum_flags == CSUM_UDP) {
1146255332Scy			fin->fin_cksum = FI_CK_L4PART;
1147255332Scy			return 0;
1148255332Scy		} else if (m->m_pkthdr.csum_flags == CSUM_IP) {
1149255332Scy			fin->fin_cksum = FI_CK_L4PART;
1150255332Scy			return 0;
1151255332Scy		} else {
1152255332Scy			manual = 1;
1153255332Scy		}
1154255332Scy	}
1155145516Sdarrenrskipauto:
1156255332Scy	if (manual != 0) {
1157255332Scy		if (ipf_checkl4sum(fin) == -1) {
1158145516Sdarrenr			fin->fin_flx |= FI_BAD;
1159255332Scy			return -1;
1160255332Scy		}
1161255332Scy	}
1162145516Sdarrenr#else
1163255332Scy	if (ipf_checkl4sum(fin) == -1) {
1164145516Sdarrenr		fin->fin_flx |= FI_BAD;
1165255332Scy		return -1;
1166255332Scy	}
1167145516Sdarrenr#endif
1168255332Scy	return 0;
1169145516Sdarrenr}
1170145516Sdarrenr
1171145516Sdarrenr
1172145516Sdarrenr#ifdef USE_INET6
1173255332ScyINLINE int
1174255332Scyipf_checkv6sum(fin)
1175255332Scy	fr_info_t *fin;
1176145516Sdarrenr{
1177255332Scy	if ((fin->fin_flx & FI_NOCKSUM) != 0)
1178255332Scy		return 0;
1179255332Scy
1180255332Scy	if ((fin->fin_flx & FI_SHORT) != 0)
1181255332Scy		return 1;
1182255332Scy
1183255332Scy	if (fin->fin_cksum != FI_CK_NEEDED)
1184255332Scy		return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1;
1185255332Scy
1186255332Scy	if (ipf_checkl4sum(fin) == -1) {
1187145516Sdarrenr		fin->fin_flx |= FI_BAD;
1188255332Scy		return -1;
1189255332Scy	}
1190255332Scy	return 0;
1191145516Sdarrenr}
1192145516Sdarrenr#endif /* USE_INET6 */
1193145516Sdarrenr
1194145516Sdarrenr
1195255332Scysize_t
1196255332Scymbufchainlen(m0)
1197255332Scy	struct mbuf *m0;
1198255332Scy	{
1199145516Sdarrenr	size_t len;
1200145516Sdarrenr
1201145516Sdarrenr	if ((m0->m_flags & M_PKTHDR) != 0) {
1202145516Sdarrenr		len = m0->m_pkthdr.len;
1203145516Sdarrenr	} else {
1204145516Sdarrenr		struct mbuf *m;
1205145516Sdarrenr
1206145516Sdarrenr		for (m = m0, len = 0; m != NULL; m = m->m_next)
1207145516Sdarrenr			len += m->m_len;
1208145516Sdarrenr	}
1209145516Sdarrenr	return len;
1210145516Sdarrenr}
1211145516Sdarrenr
1212145516Sdarrenr
1213145516Sdarrenr/* ------------------------------------------------------------------------ */
1214255332Scy/* Function:    ipf_pullup                                                  */
1215145516Sdarrenr/* Returns:     NULL == pullup failed, else pointer to protocol header      */
1216255332Scy/* Parameters:  xmin(I)- pointer to buffer where data packet starts         */
1217145516Sdarrenr/*              fin(I) - pointer to packet information                      */
1218145516Sdarrenr/*              len(I) - number of bytes to pullup                          */
1219145516Sdarrenr/*                                                                          */
1220145516Sdarrenr/* Attempt to move at least len bytes (from the start of the buffer) into a */
1221145516Sdarrenr/* single buffer for ease of access.  Operating system native functions are */
1222145516Sdarrenr/* used to manage buffers - if necessary.  If the entire packet ends up in  */
1223255332Scy/* a single buffer, set the FI_COALESCE flag even though ipf_coalesce() has */
1224145516Sdarrenr/* not been called.  Both fin_ip and fin_dp are updated before exiting _IF_ */
1225145516Sdarrenr/* and ONLY if the pullup succeeds.                                         */
1226145516Sdarrenr/*                                                                          */
1227255332Scy/* We assume that 'xmin' is a pointer to a buffer that is part of the chain */
1228145516Sdarrenr/* of buffers that starts at *fin->fin_mp.                                  */
1229145516Sdarrenr/* ------------------------------------------------------------------------ */
1230255332Scyvoid *
1231255332Scyipf_pullup(xmin, fin, len)
1232255332Scy	mb_t *xmin;
1233255332Scy	fr_info_t *fin;
1234255332Scy	int len;
1235145516Sdarrenr{
1236255332Scy	int dpoff, ipoff;
1237255332Scy	mb_t *m = xmin;
1238145516Sdarrenr	char *ip;
1239145516Sdarrenr
1240145516Sdarrenr	if (m == NULL)
1241145516Sdarrenr		return NULL;
1242145516Sdarrenr
1243145516Sdarrenr	ip = (char *)fin->fin_ip;
1244145516Sdarrenr	if ((fin->fin_flx & FI_COALESCE) != 0)
1245145516Sdarrenr		return ip;
1246145516Sdarrenr
1247145516Sdarrenr	ipoff = fin->fin_ipoff;
1248145516Sdarrenr	if (fin->fin_dp != NULL)
1249145516Sdarrenr		dpoff = (char *)fin->fin_dp - (char *)ip;
1250145516Sdarrenr	else
1251145516Sdarrenr		dpoff = 0;
1252145516Sdarrenr
1253145516Sdarrenr	if (M_LEN(m) < len) {
1254255332Scy		mb_t *n = *fin->fin_mp;
1255145516Sdarrenr		/*
1256145516Sdarrenr		 * Assume that M_PKTHDR is set and just work with what is left
1257145516Sdarrenr		 * rather than check..
1258145516Sdarrenr		 * Should not make any real difference, anyway.
1259145516Sdarrenr		 */
1260255332Scy		if (m != n) {
1261255332Scy			/*
1262255332Scy			 * Record the mbuf that points to the mbuf that we're
1263255332Scy			 * about to go to work on so that we can update the
1264255332Scy			 * m_next appropriately later.
1265255332Scy			 */
1266255332Scy			for (; n->m_next != m; n = n->m_next)
1267255332Scy				;
1268255332Scy		} else {
1269255332Scy			n = NULL;
1270255332Scy		}
1271255332Scy
1272255332Scy#ifdef MHLEN
1273145516Sdarrenr		if (len > MHLEN)
1274145516Sdarrenr#else
1275145516Sdarrenr		if (len > MLEN)
1276145516Sdarrenr#endif
1277145516Sdarrenr		{
1278145516Sdarrenr#ifdef HAVE_M_PULLDOWN
1279145516Sdarrenr			if (m_pulldown(m, 0, len, NULL) == NULL)
1280145516Sdarrenr				m = NULL;
1281145516Sdarrenr#else
1282145516Sdarrenr			FREE_MB_T(*fin->fin_mp);
1283145516Sdarrenr			m = NULL;
1284255332Scy			n = NULL;
1285145516Sdarrenr#endif
1286145516Sdarrenr		} else
1287145516Sdarrenr		{
1288145516Sdarrenr			m = m_pullup(m, len);
1289145516Sdarrenr		}
1290255332Scy		if (n != NULL)
1291255332Scy			n->m_next = m;
1292145516Sdarrenr		if (m == NULL) {
1293255332Scy			/*
1294255332Scy			 * When n is non-NULL, it indicates that m pointed to
1295255332Scy			 * a sub-chain (tail) of the mbuf and that the head
1296255332Scy			 * of this chain has not yet been free'd.
1297255332Scy			 */
1298255332Scy			if (n != NULL) {
1299255332Scy				FREE_MB_T(*fin->fin_mp);
1300255332Scy			}
1301255332Scy
1302255332Scy			*fin->fin_mp = NULL;
1303172776Sdarrenr			fin->fin_m = NULL;
1304145516Sdarrenr			return NULL;
1305145516Sdarrenr		}
1306172776Sdarrenr
1307255332Scy		if (n == NULL)
1308255332Scy			*fin->fin_mp = m;
1309255332Scy
1310172776Sdarrenr		while (M_LEN(m) == 0) {
1311172776Sdarrenr			m = m->m_next;
1312172776Sdarrenr		}
1313172776Sdarrenr		fin->fin_m = m;
1314145516Sdarrenr		ip = MTOD(m, char *) + ipoff;
1315255332Scy
1316255332Scy		fin->fin_ip = (ip_t *)ip;
1317255332Scy		if (fin->fin_dp != NULL)
1318255332Scy			fin->fin_dp = (char *)fin->fin_ip + dpoff;
1319255332Scy		if (fin->fin_fraghdr != NULL)
1320255332Scy			fin->fin_fraghdr = (char *)ip +
1321255332Scy					   ((char *)fin->fin_fraghdr -
1322255332Scy					    (char *)fin->fin_ip);
1323145516Sdarrenr	}
1324145516Sdarrenr
1325145516Sdarrenr	if (len == fin->fin_plen)
1326145516Sdarrenr		fin->fin_flx |= FI_COALESCE;
1327145516Sdarrenr	return ip;
1328145516Sdarrenr}
1329170268Sdarrenr
1330170268Sdarrenr
1331255332Scyint
1332255332Scyipf_inject(fin, m)
1333255332Scy	fr_info_t *fin;
1334255332Scy	mb_t *m;
1335170268Sdarrenr{
1336170268Sdarrenr	int error = 0;
1337170268Sdarrenr
1338170268Sdarrenr	if (fin->fin_out == 0) {
1339170268Sdarrenr		netisr_dispatch(NETISR_IP, m);
1340170268Sdarrenr	} else {
1341173931Sdarrenr		fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len);
1342173931Sdarrenr		fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off);
1343170268Sdarrenr		error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
1344170268Sdarrenr	}
1345170268Sdarrenr
1346170268Sdarrenr	return error;
1347170268Sdarrenr}
1348172776Sdarrenr
1349172776Sdarrenrint ipf_pfil_unhook(void) {
1350172776Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011)
1351172776Sdarrenr	struct pfil_head *ph_inet;
1352172776Sdarrenr#  ifdef USE_INET6
1353172776Sdarrenr	struct pfil_head *ph_inet6;
1354172776Sdarrenr#  endif
1355172776Sdarrenr#endif
1356172776Sdarrenr
1357172776Sdarrenr#ifdef NETBSD_PF
1358172776Sdarrenr	ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
1359172776Sdarrenr	if (ph_inet != NULL)
1360255332Scy		pfil_remove_hook((void *)ipf_check_wrapper, NULL,
1361172776Sdarrenr		    PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet);
1362172776Sdarrenr# ifdef USE_INET6
1363172776Sdarrenr	ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
1364172776Sdarrenr	if (ph_inet6 != NULL)
1365255332Scy		pfil_remove_hook((void *)ipf_check_wrapper6, NULL,
1366172776Sdarrenr		    PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6);
1367172776Sdarrenr# endif
1368172776Sdarrenr#endif
1369172776Sdarrenr
1370172776Sdarrenr	return (0);
1371172776Sdarrenr}
1372172776Sdarrenr
1373172776Sdarrenrint ipf_pfil_hook(void) {
1374172776Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011)
1375172776Sdarrenr	struct pfil_head *ph_inet;
1376172776Sdarrenr#  ifdef USE_INET6
1377172776Sdarrenr	struct pfil_head *ph_inet6;
1378172776Sdarrenr#  endif
1379172776Sdarrenr#endif
1380172776Sdarrenr
1381172776Sdarrenr# ifdef NETBSD_PF
1382172776Sdarrenr	ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
1383172776Sdarrenr#    ifdef USE_INET6
1384172776Sdarrenr	ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
1385172776Sdarrenr#    endif
1386172776Sdarrenr	if (ph_inet == NULL
1387172776Sdarrenr#    ifdef USE_INET6
1388172776Sdarrenr	    && ph_inet6 == NULL
1389172776Sdarrenr#    endif
1390255332Scy	   ) {
1391172776Sdarrenr		return ENODEV;
1392255332Scy	}
1393172776Sdarrenr
1394172776Sdarrenr	if (ph_inet != NULL)
1395255332Scy		pfil_add_hook((void *)ipf_check_wrapper, NULL,
1396172776Sdarrenr		    PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet);
1397172776Sdarrenr#  ifdef USE_INET6
1398172776Sdarrenr	if (ph_inet6 != NULL)
1399255332Scy		pfil_add_hook((void *)ipf_check_wrapper6, NULL,
1400172776Sdarrenr				      PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6);
1401172776Sdarrenr#  endif
1402172776Sdarrenr# endif
1403172776Sdarrenr	return (0);
1404172776Sdarrenr}
1405172776Sdarrenr
1406172776Sdarrenrvoid
1407172776Sdarrenripf_event_reg(void)
1408172776Sdarrenr{
1409255332Scy	ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \
1410255332Scy					       ipf_ifevent, &ipfmain, \
1411172776Sdarrenr					       EVENTHANDLER_PRI_ANY);
1412255332Scy	ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \
1413255332Scy					       ipf_ifevent, &ipfmain, \
1414172776Sdarrenr					       EVENTHANDLER_PRI_ANY);
1415255332Scy	ipf_clonetag  = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \
1416255332Scy					       &ipfmain, EVENTHANDLER_PRI_ANY);
1417172776Sdarrenr}
1418172776Sdarrenr
1419172776Sdarrenrvoid
1420172776Sdarrenripf_event_dereg(void)
1421172776Sdarrenr{
1422172776Sdarrenr	if (ipf_arrivetag != NULL) {
1423172776Sdarrenr		EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ipf_arrivetag);
1424172776Sdarrenr	}
1425172776Sdarrenr	if (ipf_departtag != NULL) {
1426172776Sdarrenr		EVENTHANDLER_DEREGISTER(ifnet_departure_event, ipf_departtag);
1427172776Sdarrenr	}
1428172776Sdarrenr	if (ipf_clonetag != NULL) {
1429172776Sdarrenr		EVENTHANDLER_DEREGISTER(if_clone_event, ipf_clonetag);
1430172776Sdarrenr	}
1431172776Sdarrenr}
1432255332Scy
1433255332Scy
1434255332Scyu_32_t
1435255332Scyipf_random()
1436255332Scy{
1437255332Scy	return arc4random();
1438255332Scy}
1439255332Scy
1440255332Scy
1441255332Scyu_int
1442255332Scyipf_pcksum(fin, hlen, sum)
1443255332Scy	fr_info_t *fin;
1444255332Scy	int hlen;
1445255332Scy	u_int sum;
1446255332Scy{
1447255332Scy	struct mbuf *m;
1448255332Scy	u_int sum2;
1449255332Scy	int off;
1450255332Scy
1451255332Scy	m = fin->fin_m;
1452255332Scy	off = (char *)fin->fin_dp - (char *)fin->fin_ip;
1453255332Scy	m->m_data += hlen;
1454255332Scy	m->m_len -= hlen;
1455255332Scy	sum2 = in_cksum(fin->fin_m, fin->fin_plen - off);
1456255332Scy	m->m_len += hlen;
1457255332Scy	m->m_data -= hlen;
1458255332Scy
1459255332Scy	/*
1460255332Scy	 * Both sum and sum2 are partial sums, so combine them together.
1461255332Scy	 */
1462255332Scy	sum += ~sum2 & 0xffff;
1463255332Scy	while (sum > 0xffff)
1464255332Scy		sum = (sum & 0xffff) + (sum >> 16);
1465255332Scy	sum2 = ~sum & 0xffff;
1466255332Scy	return sum2;
1467255332Scy}
1468