ip_fil_freebsd.c revision 153876
1145516Sdarrenr/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c 153876 2005-12-30 11:32:23Z guido $	*/
2145516Sdarrenr
3145516Sdarrenr/*
4145516Sdarrenr * Copyright (C) 1993-2003 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";
10153876Sguidostatic const char rcsid[] = "@(#)$Id: ip_fil_freebsd.c,v 2.53.2.27 2005/08/20 13:48:19 darrenr Exp $";
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#if defined(__FreeBSD__) && !defined(__FreeBSD_version)
29145516Sdarrenr# if defined(IPFILTER_LKM)
30145516Sdarrenr#  ifndef __FreeBSD_cc_version
31145516Sdarrenr#   include <osreldate.h>
32145516Sdarrenr#  else
33145516Sdarrenr#   if __FreeBSD_cc_version < 430000
34145516Sdarrenr#    include <osreldate.h>
35145516Sdarrenr#   endif
36145516Sdarrenr#  endif
37145516Sdarrenr# endif
38145516Sdarrenr#endif
39145516Sdarrenr#include <sys/errno.h>
40145516Sdarrenr#include <sys/types.h>
41145516Sdarrenr#include <sys/file.h>
42145516Sdarrenr#if __FreeBSD_version >= 220000
43145516Sdarrenr# include <sys/fcntl.h>
44145516Sdarrenr# include <sys/filio.h>
45145516Sdarrenr#else
46145516Sdarrenr# include <sys/ioctl.h>
47145516Sdarrenr#endif
48145516Sdarrenr#include <sys/time.h>
49145516Sdarrenr#include <sys/systm.h>
50145516Sdarrenr#if (__FreeBSD_version >= 300000)
51145516Sdarrenr# include <sys/dirent.h>
52145516Sdarrenr#else
53145516Sdarrenr# include <sys/dir.h>
54145516Sdarrenr#endif
55145516Sdarrenr#if !defined(__hpux)
56145516Sdarrenr# include <sys/mbuf.h>
57145516Sdarrenr#endif
58145516Sdarrenr#include <sys/protosw.h>
59145516Sdarrenr#include <sys/socket.h>
60145516Sdarrenr
61145516Sdarrenr#include <net/if.h>
62145516Sdarrenr#if __FreeBSD_version >= 300000
63145516Sdarrenr# include <net/if_var.h>
64145516Sdarrenr# if !defined(IPFILTER_LKM)
65145516Sdarrenr#  include "opt_ipfilter.h"
66145516Sdarrenr# endif
67145516Sdarrenr#endif
68145516Sdarrenr#include <net/route.h>
69145516Sdarrenr#include <netinet/in.h>
70145516Sdarrenr#include <netinet/in_var.h>
71145516Sdarrenr#include <netinet/in_systm.h>
72145516Sdarrenr#include <netinet/ip.h>
73145516Sdarrenr#include <netinet/ip_var.h>
74145516Sdarrenr#include <netinet/tcp.h>
75145516Sdarrenr#if defined(__osf__)
76145516Sdarrenr# include <netinet/tcp_timer.h>
77145516Sdarrenr#endif
78145516Sdarrenr#include <netinet/udp.h>
79145516Sdarrenr#include <netinet/tcpip.h>
80145516Sdarrenr#include <netinet/ip_icmp.h>
81145516Sdarrenr#ifndef _KERNEL
82145516Sdarrenr# include "netinet/ipf.h"
83145516Sdarrenr#endif
84145516Sdarrenr#include "netinet/ip_compat.h"
85145516Sdarrenr#ifdef USE_INET6
86145516Sdarrenr# include <netinet/icmp6.h>
87145516Sdarrenr#endif
88145516Sdarrenr#include "netinet/ip_fil.h"
89145516Sdarrenr#include "netinet/ip_nat.h"
90145516Sdarrenr#include "netinet/ip_frag.h"
91145516Sdarrenr#include "netinet/ip_state.h"
92145516Sdarrenr#include "netinet/ip_proxy.h"
93145516Sdarrenr#include "netinet/ip_auth.h"
94145516Sdarrenr#ifdef	IPFILTER_SYNC
95145516Sdarrenr#include "netinet/ip_sync.h"
96145516Sdarrenr#endif
97145516Sdarrenr#ifdef	IPFILTER_SCAN
98145516Sdarrenr#include "netinet/ip_scan.h"
99145516Sdarrenr#endif
100145516Sdarrenr#include "netinet/ip_pool.h"
101145516Sdarrenr#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
102145516Sdarrenr# include <sys/malloc.h>
103145516Sdarrenr#endif
104145516Sdarrenr#include <sys/kernel.h>
105145516Sdarrenr#ifdef CSUM_DATA_VALID
106145516Sdarrenr#include <machine/in_cksum.h>
107145516Sdarrenr#endif
108145516Sdarrenrextern	int	ip_optcopy __P((struct ip *, struct ip *));
109145516Sdarrenr
110145516Sdarrenr#if (__FreeBSD_version > 460000)
111145516Sdarrenrextern	int	path_mtu_discovery;
112145516Sdarrenr#endif
113145516Sdarrenr
114145516Sdarrenr# ifdef IPFILTER_M_IPFILTER
115151897SrwatsonMALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures");
116145516Sdarrenr# endif
117145516Sdarrenr
118145516Sdarrenr
119145516Sdarrenr#if !defined(__osf__)
120145516Sdarrenrextern	struct	protosw	inetsw[];
121145516Sdarrenr#endif
122145516Sdarrenr
123145516Sdarrenrstatic	int	(*fr_savep) __P((ip_t *, int, void *, int, struct mbuf **));
124145516Sdarrenrstatic	int	fr_send_ip __P((fr_info_t *, mb_t *, mb_t **));
125145516Sdarrenr# ifdef USE_MUTEXES
126145516Sdarrenripfmutex_t	ipl_mutex, ipf_authmx, ipf_rw, ipf_stinsert;
127145516Sdarrenripfmutex_t	ipf_nat_new, ipf_natio, ipf_timeoutlock;
128153876Sguidoipfrwlock_t	ipf_mutex, ipf_global, ipf_ipidfrag, ipf_frcache;
129145516Sdarrenripfrwlock_t	ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth;
130145516Sdarrenr# endif
131145516Sdarrenrint		ipf_locks_done = 0;
132145516Sdarrenr
133145516Sdarrenr#if (__FreeBSD_version >= 300000)
134145516Sdarrenrstruct callout_handle fr_slowtimer_ch;
135145516Sdarrenr#endif
136145516Sdarrenr
137145516Sdarrenr#if (__FreeBSD_version >= 500011)
138145516Sdarrenr# include <sys/conf.h>
139145516Sdarrenr# if defined(NETBSD_PF)
140145516Sdarrenr#  include <net/pfil.h>
141145516Sdarrenr#  include <netinet/ipprotosw.h>
142145516Sdarrenr/*
143145516Sdarrenr * We provide the fr_checkp name just to minimize changes later.
144145516Sdarrenr */
145145516Sdarrenrint (*fr_checkp) __P((ip_t *ip, int hlen, void *ifp, int out, mb_t **mp));
146145516Sdarrenr# endif /* NETBSD_PF */
147145516Sdarrenr#endif /* __FreeBSD_version >= 500011 */
148145516Sdarrenr
149145516Sdarrenr
150153876Sguido#if (__FreeBSD_version >= 502103)
151153876Sguidostatic eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag;
152153876Sguido
153153876Sguidostatic void ipf_ifevent(void *arg);
154153876Sguido
155153876Sguidostatic void ipf_ifevent(arg)
156153876Sguidovoid *arg;
157153876Sguido{
158153876Sguido        frsync(NULL);
159153876Sguido}
160153876Sguido#endif
161153876Sguido
162153876Sguido
163145516Sdarrenr#if (__FreeBSD_version >= 501108) && defined(_KERNEL)
164145516Sdarrenr
165145516Sdarrenrstatic int
166145516Sdarrenrfr_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
167145516Sdarrenr{
168145516Sdarrenr	struct ip *ip = mtod(*mp, struct ip *);
169145516Sdarrenr	return fr_check(ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), mp);
170145516Sdarrenr}
171145516Sdarrenr
172145516Sdarrenr# ifdef USE_INET6
173145516Sdarrenr#  include <netinet/ip6.h>
174145516Sdarrenr
175145516Sdarrenrstatic int
176145516Sdarrenrfr_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
177145516Sdarrenr{
178145516Sdarrenr	return (fr_check(mtod(*mp, struct ip *), sizeof(struct ip6_hdr),
179145516Sdarrenr	    ifp, (dir == PFIL_OUT), mp));
180145516Sdarrenr}
181145516Sdarrenr# endif
182145516Sdarrenr#endif /* __FreeBSD_version >= 501108 */
183145516Sdarrenr#if	defined(IPFILTER_LKM)
184145516Sdarrenrint iplidentify(s)
185145516Sdarrenrchar *s;
186145516Sdarrenr{
187145516Sdarrenr	if (strcmp(s, "ipl") == 0)
188145516Sdarrenr		return 1;
189145516Sdarrenr	return 0;
190145516Sdarrenr}
191145516Sdarrenr#endif /* IPFILTER_LKM */
192145516Sdarrenr
193145516Sdarrenr
194145516Sdarrenrint iplattach()
195145516Sdarrenr{
196145516Sdarrenr#ifdef USE_SPL
197145516Sdarrenr	int s;
198145516Sdarrenr#endif
199145516Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011)
200145516Sdarrenr	int error = 0;
201145516Sdarrenr# if __FreeBSD_version >= 501108
202145516Sdarrenr	struct pfil_head *ph_inet;
203145516Sdarrenr#  ifdef USE_INET6
204145516Sdarrenr	struct pfil_head *ph_inet6;
205145516Sdarrenr#  endif
206145516Sdarrenr# endif
207145516Sdarrenr#endif
208145516Sdarrenr
209145516Sdarrenr	SPL_NET(s);
210145516Sdarrenr	if (fr_running > 0) {
211145516Sdarrenr		SPL_X(s);
212145516Sdarrenr		return EBUSY;
213145516Sdarrenr	}
214145516Sdarrenr
215145516Sdarrenr	MUTEX_INIT(&ipf_rw, "ipf rw mutex");
216145516Sdarrenr	RWLOCK_INIT(&ipf_global, "ipf filter load/unload mutex");
217145516Sdarrenr	MUTEX_INIT(&ipf_timeoutlock, "ipf timeout queue mutex");
218145516Sdarrenr	RWLOCK_INIT(&ipf_mutex, "ipf filter rwlock");
219153876Sguido	RWLOCK_INIT(&ipf_frcache, "ipf cache rwlock");
220145516Sdarrenr	RWLOCK_INIT(&ipf_ipidfrag, "ipf IP NAT-Frag rwlock");
221145516Sdarrenr	ipf_locks_done = 1;
222145516Sdarrenr
223145516Sdarrenr	if (fr_initialise() < 0) {
224145516Sdarrenr		SPL_X(s);
225145516Sdarrenr		return EIO;
226145516Sdarrenr	}
227145516Sdarrenr
228145516Sdarrenr
229145516Sdarrenr# ifdef NETBSD_PF
230145516Sdarrenr#  if __FreeBSD_version >= 500011
231145516Sdarrenr#   if __FreeBSD_version >= 501108
232145516Sdarrenr	ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
233145516Sdarrenr#    ifdef USE_INET6
234145516Sdarrenr	ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
235145516Sdarrenr#    endif
236145516Sdarrenr	if (ph_inet == NULL
237145516Sdarrenr#    ifdef USE_INET6
238145516Sdarrenr	    && ph_inet6 == NULL
239145516Sdarrenr#    endif
240145516Sdarrenr	   )
241145516Sdarrenr		return ENODEV;
242145516Sdarrenr
243145516Sdarrenr	if (ph_inet != NULL)
244145516Sdarrenr		error = pfil_add_hook((void *)fr_check_wrapper, NULL,
245145516Sdarrenr				      PFIL_IN|PFIL_OUT, ph_inet);
246145516Sdarrenr	else
247145516Sdarrenr		error = 0;
248145516Sdarrenr#  else
249145516Sdarrenr	error = pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT,
250145516Sdarrenr			      &inetsw[ip_protox[IPPROTO_IP]].pr_pfh);
251145516Sdarrenr#  endif
252145516Sdarrenr	if (error) {
253145516Sdarrenr#   ifdef USE_INET6
254145516Sdarrenr		goto pfil_error;
255145516Sdarrenr#   else
256145516Sdarrenr		fr_deinitialise();
257145516Sdarrenr		SPL_X(s);
258145516Sdarrenr		return error;
259145516Sdarrenr#   endif
260145516Sdarrenr	}
261145516Sdarrenr#  else
262145516Sdarrenr	pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT);
263145516Sdarrenr#  endif
264145516Sdarrenr#  ifdef USE_INET6
265145516Sdarrenr#   if __FreeBSD_version >= 501108
266145516Sdarrenr	if (ph_inet6 != NULL)
267145516Sdarrenr		error = pfil_add_hook((void *)fr_check_wrapper6, NULL,
268145516Sdarrenr				      PFIL_IN|PFIL_OUT, ph_inet6);
269145516Sdarrenr	else
270145516Sdarrenr		error = 0;
271145516Sdarrenr	if (error) {
272145516Sdarrenr		pfil_remove_hook((void *)fr_check_wrapper6, NULL,
273145516Sdarrenr				 PFIL_IN|PFIL_OUT, ph_inet6);
274145516Sdarrenr#   else
275145516Sdarrenr	error = pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT,
276145516Sdarrenr			      &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh);
277145516Sdarrenr	if (error) {
278145516Sdarrenr		pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT,
279145516Sdarrenr				 &inetsw[ip_protox[IPPROTO_IP]].pr_pfh);
280145516Sdarrenr#   endif
281145516Sdarrenrpfil_error:
282145516Sdarrenr		fr_deinitialise();
283145516Sdarrenr		SPL_X(s);
284145516Sdarrenr		return error;
285145516Sdarrenr	}
286145516Sdarrenr#  endif
287145516Sdarrenr# endif
288153876Sguido
289153876Sguido#if (__FreeBSD_version >= 502103)
290153876Sguido	ipf_arrivetag =  EVENTHANDLER_REGISTER(ifnet_arrival_event, \
291153876Sguido					       ipf_ifevent, NULL, \
292153876Sguido					       EVENTHANDLER_PRI_ANY);
293153876Sguido	ipf_departtag =  EVENTHANDLER_REGISTER(ifnet_departure_event, \
294153876Sguido					       ipf_ifevent, NULL, \
295153876Sguido					       EVENTHANDLER_PRI_ANY);
296153876Sguido	ipf_clonetag =  EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \
297153876Sguido					      NULL, EVENTHANDLER_PRI_ANY);
298153876Sguido#endif
299153876Sguido
300145516Sdarrenr	if (fr_checkp != fr_check) {
301145516Sdarrenr		fr_savep = fr_checkp;
302145516Sdarrenr		fr_checkp = fr_check;
303145516Sdarrenr	}
304145516Sdarrenr
305145516Sdarrenr	bzero((char *)frcache, sizeof(frcache));
306145516Sdarrenr	fr_running = 1;
307145516Sdarrenr
308145516Sdarrenr	if (fr_control_forwarding & 1)
309145516Sdarrenr		ipforwarding = 1;
310145516Sdarrenr
311145516Sdarrenr	SPL_X(s);
312145516Sdarrenr#if (__FreeBSD_version >= 300000)
313145516Sdarrenr	fr_slowtimer_ch = timeout(fr_slowtimer, NULL,
314145516Sdarrenr				    (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT);
315145516Sdarrenr#else
316145516Sdarrenr	timeout(fr_slowtimer, NULL, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT);
317145516Sdarrenr#endif
318145516Sdarrenr	return 0;
319145516Sdarrenr}
320145516Sdarrenr
321145516Sdarrenr
322145516Sdarrenr/*
323145516Sdarrenr * Disable the filter by removing the hooks from the IP input/output
324145516Sdarrenr * stream.
325145516Sdarrenr */
326145516Sdarrenrint ipldetach()
327145516Sdarrenr{
328145516Sdarrenr#ifdef USE_SPL
329145516Sdarrenr	int s;
330145516Sdarrenr#endif
331145516Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011)
332145516Sdarrenr	int error = 0;
333145516Sdarrenr# if __FreeBSD_version >= 501108
334145516Sdarrenr	struct pfil_head *ph_inet;
335145516Sdarrenr#  ifdef USE_INET6
336145516Sdarrenr	struct pfil_head *ph_inet6;
337145516Sdarrenr#  endif
338145516Sdarrenr# endif
339145516Sdarrenr#endif
340145516Sdarrenr
341145516Sdarrenr	if (fr_control_forwarding & 2)
342145516Sdarrenr		ipforwarding = 0;
343145516Sdarrenr
344153876Sguido#if (__FreeBSD_version >= 502103)
345153876Sguido	if (ipf_arrivetag != NULL) {
346153876Sguido		EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ipf_arrivetag);
347153876Sguido	}
348153876Sguido	if (ipf_departtag != NULL) {
349153876Sguido		EVENTHANDLER_DEREGISTER(ifnet_departure_event, ipf_departtag);
350153876Sguido	}
351153876Sguido	if (ipf_clonetag != NULL) {
352153876Sguido		EVENTHANDLER_DEREGISTER(if_clone_event, ipf_clonetag);
353153876Sguido	}
354153876Sguido#endif
355153876Sguido
356145516Sdarrenr	SPL_NET(s);
357145516Sdarrenr
358145516Sdarrenr#if (__FreeBSD_version >= 300000)
359145516Sdarrenr	if (fr_slowtimer_ch.callout != NULL)
360145516Sdarrenr		untimeout(fr_slowtimer, NULL, fr_slowtimer_ch);
361145516Sdarrenr	bzero(&fr_slowtimer_ch, sizeof(fr_slowtimer_ch));
362145516Sdarrenr#else
363145516Sdarrenr	untimeout(fr_slowtimer, NULL);
364145516Sdarrenr#endif /* FreeBSD */
365145516Sdarrenr
366145516Sdarrenr#ifndef NETBSD_PF
367145516Sdarrenr	if (fr_checkp != NULL)
368145516Sdarrenr		fr_checkp = fr_savep;
369145516Sdarrenr	fr_savep = NULL;
370145516Sdarrenr#endif
371145516Sdarrenr
372145516Sdarrenr#ifdef NETBSD_PF
373145516Sdarrenr# if (__FreeBSD_version >= 500011)
374145516Sdarrenr#  if (__FreeBSD_version >= 501108)
375145516Sdarrenr	ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
376145516Sdarrenr	if (ph_inet != NULL)
377145516Sdarrenr		error = pfil_remove_hook((void *)fr_check_wrapper, NULL,
378145516Sdarrenr					 PFIL_IN|PFIL_OUT, ph_inet);
379145516Sdarrenr	else
380145516Sdarrenr		error = 0;
381145516Sdarrenr#  else
382145516Sdarrenr	error = pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT,
383145516Sdarrenr				 &inetsw[ip_protox[IPPROTO_IP]].pr_pfh);
384145516Sdarrenr#  endif
385145516Sdarrenr	if (error) {
386145516Sdarrenr		SPL_X(s);
387145516Sdarrenr		return error;
388145516Sdarrenr	}
389145516Sdarrenr# else
390145516Sdarrenr	pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT);
391145516Sdarrenr# endif
392145516Sdarrenr# ifdef USE_INET6
393145516Sdarrenr#  if (__FreeBSD_version >= 501108)
394145516Sdarrenr	ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
395145516Sdarrenr	if (ph_inet6 != NULL)
396145516Sdarrenr		error = pfil_remove_hook((void *)fr_check_wrapper6, NULL,
397145516Sdarrenr					 PFIL_IN|PFIL_OUT, ph_inet6);
398145516Sdarrenr	else
399145516Sdarrenr		error = 0;
400145516Sdarrenr#  else
401145516Sdarrenr	error = pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT,
402145516Sdarrenr				 &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh);
403145516Sdarrenr#  endif
404145516Sdarrenr	if (error) {
405145516Sdarrenr		SPL_X(s);
406145516Sdarrenr		return error;
407145516Sdarrenr	}
408145516Sdarrenr# endif
409145516Sdarrenr#endif
410145516Sdarrenr	fr_deinitialise();
411145516Sdarrenr
412145516Sdarrenr	fr_running = -2;
413145516Sdarrenr
414145516Sdarrenr	(void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE);
415145516Sdarrenr	(void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE);
416145516Sdarrenr
417145516Sdarrenr	if (ipf_locks_done == 1) {
418145516Sdarrenr		MUTEX_DESTROY(&ipf_timeoutlock);
419145516Sdarrenr		MUTEX_DESTROY(&ipf_rw);
420145516Sdarrenr		RW_DESTROY(&ipf_mutex);
421153876Sguido		RW_DESTROY(&ipf_frcache);
422145516Sdarrenr		RW_DESTROY(&ipf_ipidfrag);
423145516Sdarrenr		RW_DESTROY(&ipf_global);
424145516Sdarrenr		ipf_locks_done = 0;
425145516Sdarrenr	}
426145516Sdarrenr
427145516Sdarrenr	SPL_X(s);
428145516Sdarrenr
429145516Sdarrenr	return 0;
430145516Sdarrenr}
431145516Sdarrenr
432145516Sdarrenr
433145516Sdarrenr/*
434145516Sdarrenr * Filter ioctl interface.
435145516Sdarrenr */
436145516Sdarrenrint iplioctl(dev, cmd, data, mode
437145516Sdarrenr# if defined(_KERNEL) && ((BSD >= 199506) || (__FreeBSD_version >= 220000))
438145516Sdarrenr, p)
439145516Sdarrenr#  if (__FreeBSD_version >= 500024)
440145516Sdarrenrstruct thread *p;
441145516Sdarrenr#  else
442145516Sdarrenrstruct proc *p;
443145516Sdarrenr#  endif /* __FreeBSD_version >= 500024 */
444145516Sdarrenr# else
445145516Sdarrenr)
446145516Sdarrenr# endif
447145516Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 502116)
448145516Sdarrenrstruct cdev *dev;
449145516Sdarrenr#else
450145516Sdarrenrdev_t dev;
451145516Sdarrenr#endif
452145516Sdarrenrioctlcmd_t cmd;
453145516Sdarrenrcaddr_t data;
454145516Sdarrenrint mode;
455145516Sdarrenr{
456145516Sdarrenr#ifdef USE_SPL
457145516Sdarrenr	int s;
458145516Sdarrenr#endif
459145516Sdarrenr	int error = 0, unit = 0, tmp;
460145516Sdarrenr	friostat_t fio;
461145516Sdarrenr
462145516Sdarrenr#if (BSD >= 199306) && defined(_KERNEL)
463153876Sguido	if ((securelevel >= 3) && (mode & FWRITE))
464145516Sdarrenr		return EPERM;
465145516Sdarrenr#endif
466145516Sdarrenr
467145516Sdarrenr	unit = GET_MINOR(dev);
468145516Sdarrenr	if ((IPL_LOGMAX < unit) || (unit < 0))
469145516Sdarrenr		return ENXIO;
470145516Sdarrenr
471145516Sdarrenr	if (fr_running <= 0) {
472145516Sdarrenr		if (unit != IPL_LOGIPF)
473145516Sdarrenr			return EIO;
474145516Sdarrenr		if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET &&
475145516Sdarrenr		    cmd != SIOCIPFSET && cmd != SIOCFRENB &&
476145516Sdarrenr		    cmd != SIOCGETFS && cmd != SIOCGETFF)
477145516Sdarrenr			return EIO;
478145516Sdarrenr	}
479145516Sdarrenr
480145516Sdarrenr	SPL_NET(s);
481145516Sdarrenr
482145516Sdarrenr	error = fr_ioctlswitch(unit, data, cmd, mode);
483145516Sdarrenr	if (error != -1) {
484145516Sdarrenr		SPL_X(s);
485145516Sdarrenr		return error;
486145516Sdarrenr	}
487145516Sdarrenr	error = 0;
488145516Sdarrenr
489145516Sdarrenr	switch (cmd)
490145516Sdarrenr	{
491145516Sdarrenr	case FIONREAD :
492145516Sdarrenr#ifdef IPFILTER_LOG
493145516Sdarrenr		BCOPYOUT(&iplused[IPL_LOGIPF], (caddr_t)data,
494145516Sdarrenr			 sizeof(iplused[IPL_LOGIPF]));
495145516Sdarrenr#endif
496145516Sdarrenr		break;
497145516Sdarrenr	case SIOCFRENB :
498145516Sdarrenr		if (!(mode & FWRITE))
499145516Sdarrenr			error = EPERM;
500145516Sdarrenr		else {
501145516Sdarrenr			BCOPYIN(data, &tmp, sizeof(tmp));
502145516Sdarrenr			if (tmp) {
503145516Sdarrenr				if (fr_running > 0)
504145516Sdarrenr					error = 0;
505145516Sdarrenr				else
506145516Sdarrenr					error = iplattach();
507145516Sdarrenr				if (error == 0)
508145516Sdarrenr					fr_running = 1;
509145516Sdarrenr				else
510145516Sdarrenr					(void) ipldetach();
511145516Sdarrenr			} else {
512145516Sdarrenr				error = ipldetach();
513145516Sdarrenr				if (error == 0)
514145516Sdarrenr					fr_running = -1;
515145516Sdarrenr			}
516145516Sdarrenr		}
517145516Sdarrenr		break;
518145516Sdarrenr	case SIOCIPFSET :
519145516Sdarrenr		if (!(mode & FWRITE)) {
520145516Sdarrenr			error = EPERM;
521145516Sdarrenr			break;
522145516Sdarrenr		}
523145516Sdarrenr	case SIOCIPFGETNEXT :
524145516Sdarrenr	case SIOCIPFGET :
525145516Sdarrenr		error = fr_ipftune(cmd, data);
526145516Sdarrenr		break;
527145516Sdarrenr	case SIOCSETFF :
528145516Sdarrenr		if (!(mode & FWRITE))
529145516Sdarrenr			error = EPERM;
530145516Sdarrenr		else
531145516Sdarrenr			BCOPYIN(data, &fr_flags, sizeof(fr_flags));
532145516Sdarrenr		break;
533145516Sdarrenr	case SIOCGETFF :
534145516Sdarrenr		BCOPYOUT(&fr_flags, data, sizeof(fr_flags));
535145516Sdarrenr		break;
536145516Sdarrenr	case SIOCFUNCL :
537145516Sdarrenr		error = fr_resolvefunc(data);
538145516Sdarrenr		break;
539145516Sdarrenr	case SIOCINAFR :
540145516Sdarrenr	case SIOCRMAFR :
541145516Sdarrenr	case SIOCADAFR :
542145516Sdarrenr	case SIOCZRLST :
543145516Sdarrenr		if (!(mode & FWRITE))
544145516Sdarrenr			error = EPERM;
545145516Sdarrenr		else
546145516Sdarrenr			error = frrequest(unit, cmd, data, fr_active, 1);
547145516Sdarrenr		break;
548145516Sdarrenr	case SIOCINIFR :
549145516Sdarrenr	case SIOCRMIFR :
550145516Sdarrenr	case SIOCADIFR :
551145516Sdarrenr		if (!(mode & FWRITE))
552145516Sdarrenr			error = EPERM;
553145516Sdarrenr		else
554145516Sdarrenr			error = frrequest(unit, cmd, data, 1 - fr_active, 1);
555145516Sdarrenr		break;
556145516Sdarrenr	case SIOCSWAPA :
557145516Sdarrenr		if (!(mode & FWRITE))
558145516Sdarrenr			error = EPERM;
559145516Sdarrenr		else {
560145516Sdarrenr			bzero((char *)frcache, sizeof(frcache[0]) * 2);
561145516Sdarrenr			*(u_int *)data = fr_active;
562145516Sdarrenr			fr_active = 1 - fr_active;
563145516Sdarrenr		}
564145516Sdarrenr		break;
565145516Sdarrenr	case SIOCGETFS :
566145516Sdarrenr		fr_getstat(&fio);
567145516Sdarrenr		error = fr_outobj(data, &fio, IPFOBJ_IPFSTAT);
568145516Sdarrenr		break;
569145516Sdarrenr	case SIOCFRZST :
570145516Sdarrenr		if (!(mode & FWRITE))
571145516Sdarrenr			error = EPERM;
572145516Sdarrenr		else
573145516Sdarrenr			error = fr_zerostats(data);
574145516Sdarrenr		break;
575145516Sdarrenr	case SIOCIPFFL :
576145516Sdarrenr		if (!(mode & FWRITE))
577145516Sdarrenr			error = EPERM;
578145516Sdarrenr		else {
579145516Sdarrenr			BCOPYIN(data, &tmp, sizeof(tmp));
580145516Sdarrenr			tmp = frflush(unit, 4, tmp);
581145516Sdarrenr			BCOPYOUT(&tmp, data, sizeof(tmp));
582145516Sdarrenr		}
583145516Sdarrenr		break;
584145516Sdarrenr#ifdef USE_INET6
585145516Sdarrenr	case SIOCIPFL6 :
586145516Sdarrenr		if (!(mode & FWRITE))
587145516Sdarrenr			error = EPERM;
588145516Sdarrenr		else {
589145516Sdarrenr			BCOPYIN(data, &tmp, sizeof(tmp));
590145516Sdarrenr			tmp = frflush(unit, 6, tmp);
591145516Sdarrenr			BCOPYOUT(&tmp, data, sizeof(tmp));
592145516Sdarrenr		}
593145516Sdarrenr		break;
594145516Sdarrenr#endif
595145516Sdarrenr	case SIOCSTLCK :
596145516Sdarrenr		BCOPYIN(data, &tmp, sizeof(tmp));
597145516Sdarrenr		fr_state_lock = tmp;
598145516Sdarrenr		fr_nat_lock = tmp;
599145516Sdarrenr		fr_frag_lock = tmp;
600145516Sdarrenr		fr_auth_lock = tmp;
601145516Sdarrenr		break;
602145516Sdarrenr#ifdef IPFILTER_LOG
603145516Sdarrenr	case SIOCIPFFB :
604145516Sdarrenr		if (!(mode & FWRITE))
605145516Sdarrenr			error = EPERM;
606145516Sdarrenr		else
607145516Sdarrenr			*(int *)data = ipflog_clear(unit);
608145516Sdarrenr		break;
609145516Sdarrenr#endif /* IPFILTER_LOG */
610145516Sdarrenr	case SIOCGFRST :
611145516Sdarrenr		error = fr_outobj(data, fr_fragstats(), IPFOBJ_FRAGSTAT);
612145516Sdarrenr		break;
613145516Sdarrenr	case SIOCFRSYN :
614145516Sdarrenr		if (!(mode & FWRITE))
615145516Sdarrenr			error = EPERM;
616145516Sdarrenr		else {
617145516Sdarrenr			frsync(NULL);
618145516Sdarrenr		}
619145516Sdarrenr		break;
620145516Sdarrenr	default :
621145516Sdarrenr		error = EINVAL;
622145516Sdarrenr		break;
623145516Sdarrenr	}
624145516Sdarrenr	SPL_X(s);
625145516Sdarrenr	return error;
626145516Sdarrenr}
627145516Sdarrenr
628145516Sdarrenr
629145516Sdarrenr#if 0
630145516Sdarrenrvoid fr_forgetifp(ifp)
631145516Sdarrenrvoid *ifp;
632145516Sdarrenr{
633145516Sdarrenr	register frentry_t *f;
634145516Sdarrenr
635145516Sdarrenr	WRITE_ENTER(&ipf_mutex);
636145516Sdarrenr	for (f = ipacct[0][fr_active]; (f != NULL); f = f->fr_next)
637145516Sdarrenr		if (f->fr_ifa == ifp)
638145516Sdarrenr			f->fr_ifa = (void *)-1;
639145516Sdarrenr	for (f = ipacct[1][fr_active]; (f != NULL); f = f->fr_next)
640145516Sdarrenr		if (f->fr_ifa == ifp)
641145516Sdarrenr			f->fr_ifa = (void *)-1;
642145516Sdarrenr	for (f = ipfilter[0][fr_active]; (f != NULL); f = f->fr_next)
643145516Sdarrenr		if (f->fr_ifa == ifp)
644145516Sdarrenr			f->fr_ifa = (void *)-1;
645145516Sdarrenr	for (f = ipfilter[1][fr_active]; (f != NULL); f = f->fr_next)
646145516Sdarrenr		if (f->fr_ifa == ifp)
647145516Sdarrenr			f->fr_ifa = (void *)-1;
648145516Sdarrenr#ifdef USE_INET6
649145516Sdarrenr	for (f = ipacct6[0][fr_active]; (f != NULL); f = f->fr_next)
650145516Sdarrenr		if (f->fr_ifa == ifp)
651145516Sdarrenr			f->fr_ifa = (void *)-1;
652145516Sdarrenr	for (f = ipacct6[1][fr_active]; (f != NULL); f = f->fr_next)
653145516Sdarrenr		if (f->fr_ifa == ifp)
654145516Sdarrenr			f->fr_ifa = (void *)-1;
655145516Sdarrenr	for (f = ipfilter6[0][fr_active]; (f != NULL); f = f->fr_next)
656145516Sdarrenr		if (f->fr_ifa == ifp)
657145516Sdarrenr			f->fr_ifa = (void *)-1;
658145516Sdarrenr	for (f = ipfilter6[1][fr_active]; (f != NULL); f = f->fr_next)
659145516Sdarrenr		if (f->fr_ifa == ifp)
660145516Sdarrenr			f->fr_ifa = (void *)-1;
661145516Sdarrenr#endif
662145516Sdarrenr	RWLOCK_EXIT(&ipf_mutex);
663145516Sdarrenr	fr_natsync(ifp);
664145516Sdarrenr}
665145516Sdarrenr#endif
666145516Sdarrenr
667145516Sdarrenr
668145516Sdarrenr/*
669145516Sdarrenr * routines below for saving IP headers to buffer
670145516Sdarrenr */
671145516Sdarrenrint iplopen(dev, flags
672145516Sdarrenr#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL)
673145516Sdarrenr, devtype, p)
674145516Sdarrenrint devtype;
675145516Sdarrenr# if (__FreeBSD_version >= 500024)
676145516Sdarrenrstruct thread *p;
677145516Sdarrenr# else
678145516Sdarrenrstruct proc *p;
679145516Sdarrenr# endif /* __FreeBSD_version >= 500024 */
680145516Sdarrenr#else
681145516Sdarrenr)
682145516Sdarrenr#endif
683145516Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 502116)
684145516Sdarrenrstruct cdev *dev;
685145516Sdarrenr#else
686145516Sdarrenrdev_t dev;
687145516Sdarrenr#endif
688145516Sdarrenrint flags;
689145516Sdarrenr{
690145516Sdarrenr	u_int min = GET_MINOR(dev);
691145516Sdarrenr
692145516Sdarrenr	if (IPL_LOGMAX < min)
693145516Sdarrenr		min = ENXIO;
694145516Sdarrenr	else
695145516Sdarrenr		min = 0;
696145516Sdarrenr	return min;
697145516Sdarrenr}
698145516Sdarrenr
699145516Sdarrenr
700145516Sdarrenrint iplclose(dev, flags
701145516Sdarrenr#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL)
702145516Sdarrenr, devtype, p)
703145516Sdarrenrint devtype;
704145516Sdarrenr# if (__FreeBSD_version >= 500024)
705145516Sdarrenrstruct thread *p;
706145516Sdarrenr# else
707145516Sdarrenrstruct proc *p;
708145516Sdarrenr# endif /* __FreeBSD_version >= 500024 */
709145516Sdarrenr#else
710145516Sdarrenr)
711145516Sdarrenr#endif
712145516Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 502116)
713145516Sdarrenrstruct cdev *dev;
714145516Sdarrenr#else
715145516Sdarrenrdev_t dev;
716145516Sdarrenr#endif
717145516Sdarrenrint flags;
718145516Sdarrenr{
719145516Sdarrenr	u_int	min = GET_MINOR(dev);
720145516Sdarrenr
721145516Sdarrenr	if (IPL_LOGMAX < min)
722145516Sdarrenr		min = ENXIO;
723145516Sdarrenr	else
724145516Sdarrenr		min = 0;
725145516Sdarrenr	return min;
726145516Sdarrenr}
727145516Sdarrenr
728145516Sdarrenr/*
729145516Sdarrenr * iplread/ipllog
730145516Sdarrenr * both of these must operate with at least splnet() lest they be
731145516Sdarrenr * called during packet processing and cause an inconsistancy to appear in
732145516Sdarrenr * the filter lists.
733145516Sdarrenr */
734145516Sdarrenr#if (BSD >= 199306)
735145516Sdarrenrint iplread(dev, uio, ioflag)
736145516Sdarrenrint ioflag;
737145516Sdarrenr#else
738145516Sdarrenrint iplread(dev, uio)
739145516Sdarrenr#endif
740145516Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 502116)
741145516Sdarrenrstruct cdev *dev;
742145516Sdarrenr#else
743145516Sdarrenrdev_t dev;
744145516Sdarrenr#endif
745145516Sdarrenrregister struct uio *uio;
746145516Sdarrenr{
747145516Sdarrenr
748145516Sdarrenr# ifdef	IPFILTER_SYNC
749145516Sdarrenr	if (GET_MINOR(dev) == IPL_LOGSYNC)
750145516Sdarrenr		return ipfsync_read(uio);
751145516Sdarrenr# endif
752145516Sdarrenr
753145516Sdarrenr#ifdef IPFILTER_LOG
754145516Sdarrenr	return ipflog_read(GET_MINOR(dev), uio);
755145516Sdarrenr#else
756145516Sdarrenr	return ENXIO;
757145516Sdarrenr#endif
758145516Sdarrenr}
759145516Sdarrenr
760145516Sdarrenr
761145516Sdarrenr/*
762145516Sdarrenr * iplwrite
763145516Sdarrenr * both of these must operate with at least splnet() lest they be
764145516Sdarrenr * called during packet processing and cause an inconsistancy to appear in
765145516Sdarrenr * the filter lists.
766145516Sdarrenr */
767145516Sdarrenr#if (BSD >= 199306)
768145516Sdarrenrint iplwrite(dev, uio, ioflag)
769145516Sdarrenrint ioflag;
770145516Sdarrenr#else
771145516Sdarrenrint iplwrite(dev, uio)
772145516Sdarrenr#endif
773145516Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 502116)
774145516Sdarrenrstruct cdev *dev;
775145516Sdarrenr#else
776145516Sdarrenrdev_t dev;
777145516Sdarrenr#endif
778145516Sdarrenrregister struct uio *uio;
779145516Sdarrenr{
780145516Sdarrenr
781145516Sdarrenr#ifdef	IPFILTER_SYNC
782145516Sdarrenr	if (GET_MINOR(dev) == IPL_LOGSYNC)
783145516Sdarrenr		return ipfsync_write(uio);
784145516Sdarrenr#endif
785145516Sdarrenr	return ENXIO;
786145516Sdarrenr}
787145516Sdarrenr
788145516Sdarrenr
789145516Sdarrenr/*
790145516Sdarrenr * fr_send_reset - this could conceivably be a call to tcp_respond(), but that
791145516Sdarrenr * requires a large amount of setting up and isn't any more efficient.
792145516Sdarrenr */
793145516Sdarrenrint fr_send_reset(fin)
794145516Sdarrenrfr_info_t *fin;
795145516Sdarrenr{
796145516Sdarrenr	struct tcphdr *tcp, *tcp2;
797145516Sdarrenr	int tlen = 0, hlen;
798145516Sdarrenr	struct mbuf *m;
799145516Sdarrenr#ifdef USE_INET6
800145516Sdarrenr	ip6_t *ip6;
801145516Sdarrenr#endif
802145516Sdarrenr	ip_t *ip;
803145516Sdarrenr
804145516Sdarrenr	tcp = fin->fin_dp;
805145516Sdarrenr	if (tcp->th_flags & TH_RST)
806145516Sdarrenr		return -1;		/* feedback loop */
807145516Sdarrenr
808145516Sdarrenr#ifndef	IPFILTER_CKSUM
809145516Sdarrenr	if (fr_checkl4sum(fin) == -1)
810145516Sdarrenr		return -1;
811145516Sdarrenr#endif
812145516Sdarrenr
813145516Sdarrenr	tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) +
814145516Sdarrenr			((tcp->th_flags & TH_SYN) ? 1 : 0) +
815145516Sdarrenr			((tcp->th_flags & TH_FIN) ? 1 : 0);
816145516Sdarrenr
817145516Sdarrenr#ifdef USE_INET6
818145516Sdarrenr	hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t);
819145516Sdarrenr#else
820145516Sdarrenr	hlen = sizeof(ip_t);
821145516Sdarrenr#endif
822145516Sdarrenr#ifdef MGETHDR
823145516Sdarrenr	MGETHDR(m, M_DONTWAIT, MT_HEADER);
824145516Sdarrenr#else
825145516Sdarrenr	MGET(m, M_DONTWAIT, MT_HEADER);
826145516Sdarrenr#endif
827145516Sdarrenr	if (m == NULL)
828145516Sdarrenr		return -1;
829145516Sdarrenr	if (sizeof(*tcp2) + hlen > MLEN) {
830145516Sdarrenr		MCLGET(m, M_DONTWAIT);
831145516Sdarrenr		if ((m->m_flags & M_EXT) == 0) {
832145516Sdarrenr			FREE_MB_T(m);
833145516Sdarrenr			return -1;
834145516Sdarrenr		}
835145516Sdarrenr	}
836145516Sdarrenr
837145516Sdarrenr	m->m_len = sizeof(*tcp2) + hlen;
838145516Sdarrenr#if (BSD >= 199103)
839145516Sdarrenr	m->m_data += max_linkhdr;
840145516Sdarrenr	m->m_pkthdr.len = m->m_len;
841145516Sdarrenr	m->m_pkthdr.rcvif = (struct ifnet *)0;
842145516Sdarrenr#endif
843145516Sdarrenr	ip = mtod(m, struct ip *);
844145516Sdarrenr	bzero((char *)ip, hlen);
845145516Sdarrenr#ifdef USE_INET6
846145516Sdarrenr	ip6 = (ip6_t *)ip;
847145516Sdarrenr#endif
848145516Sdarrenr	tcp2 = (struct tcphdr *)((char *)ip + hlen);
849145516Sdarrenr	tcp2->th_sport = tcp->th_dport;
850145516Sdarrenr	tcp2->th_dport = tcp->th_sport;
851145516Sdarrenr
852145516Sdarrenr	if (tcp->th_flags & TH_ACK) {
853145516Sdarrenr		tcp2->th_seq = tcp->th_ack;
854145516Sdarrenr		tcp2->th_flags = TH_RST;
855145516Sdarrenr		tcp2->th_ack = 0;
856145516Sdarrenr	} else {
857145516Sdarrenr		tcp2->th_seq = 0;
858145516Sdarrenr		tcp2->th_ack = ntohl(tcp->th_seq);
859145516Sdarrenr		tcp2->th_ack += tlen;
860145516Sdarrenr		tcp2->th_ack = htonl(tcp2->th_ack);
861145516Sdarrenr		tcp2->th_flags = TH_RST|TH_ACK;
862145516Sdarrenr	}
863145516Sdarrenr	TCP_X2_A(tcp2, 0);
864145516Sdarrenr	TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2);
865145516Sdarrenr	tcp2->th_win = tcp->th_win;
866145516Sdarrenr	tcp2->th_sum = 0;
867145516Sdarrenr	tcp2->th_urp = 0;
868145516Sdarrenr
869145516Sdarrenr#ifdef USE_INET6
870145516Sdarrenr	if (fin->fin_v == 6) {
871145516Sdarrenr		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
872145516Sdarrenr		ip6->ip6_plen = htons(sizeof(struct tcphdr));
873145516Sdarrenr		ip6->ip6_nxt = IPPROTO_TCP;
874145516Sdarrenr		ip6->ip6_hlim = 0;
875145516Sdarrenr		ip6->ip6_src = fin->fin_dst6;
876145516Sdarrenr		ip6->ip6_dst = fin->fin_src6;
877145516Sdarrenr		tcp2->th_sum = in6_cksum(m, IPPROTO_TCP,
878145516Sdarrenr					 sizeof(*ip6), sizeof(*tcp2));
879145516Sdarrenr		return fr_send_ip(fin, m, &m);
880145516Sdarrenr	}
881145516Sdarrenr#endif
882145516Sdarrenr	ip->ip_p = IPPROTO_TCP;
883145516Sdarrenr	ip->ip_len = htons(sizeof(struct tcphdr));
884145516Sdarrenr	ip->ip_src.s_addr = fin->fin_daddr;
885145516Sdarrenr	ip->ip_dst.s_addr = fin->fin_saddr;
886145516Sdarrenr	tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2));
887145516Sdarrenr	ip->ip_len = hlen + sizeof(*tcp2);
888145516Sdarrenr	return fr_send_ip(fin, m, &m);
889145516Sdarrenr}
890145516Sdarrenr
891145516Sdarrenr
892145516Sdarrenrstatic int fr_send_ip(fin, m, mpp)
893145516Sdarrenrfr_info_t *fin;
894145516Sdarrenrmb_t *m, **mpp;
895145516Sdarrenr{
896145516Sdarrenr	fr_info_t fnew;
897145516Sdarrenr	ip_t *ip, *oip;
898145516Sdarrenr	int hlen;
899145516Sdarrenr
900145516Sdarrenr	ip = mtod(m, ip_t *);
901145516Sdarrenr	bzero((char *)&fnew, sizeof(fnew));
902145516Sdarrenr
903145516Sdarrenr	IP_V_A(ip, fin->fin_v);
904145516Sdarrenr	switch (fin->fin_v)
905145516Sdarrenr	{
906145516Sdarrenr	case 4 :
907145516Sdarrenr		fnew.fin_v = 4;
908145516Sdarrenr		oip = fin->fin_ip;
909145516Sdarrenr		IP_HL_A(ip, sizeof(*oip) >> 2);
910145516Sdarrenr		ip->ip_tos = oip->ip_tos;
911145516Sdarrenr		ip->ip_id = fin->fin_ip->ip_id;
912145516Sdarrenr#if (__FreeBSD_version > 460000)
913145516Sdarrenr		ip->ip_off = path_mtu_discovery ? IP_DF : 0;
914145516Sdarrenr#else
915145516Sdarrenr		ip->ip_off = 0;
916145516Sdarrenr#endif
917145516Sdarrenr		ip->ip_ttl = ip_defttl;
918145516Sdarrenr		ip->ip_sum = 0;
919145516Sdarrenr		hlen = sizeof(*oip);
920145516Sdarrenr		break;
921145516Sdarrenr#ifdef USE_INET6
922145516Sdarrenr	case 6 :
923145516Sdarrenr	{
924145516Sdarrenr		ip6_t *ip6 = (ip6_t *)ip;
925145516Sdarrenr
926145516Sdarrenr		ip6->ip6_vfc = 0x60;
927145516Sdarrenr		ip6->ip6_hlim = IPDEFTTL;
928145516Sdarrenr
929145516Sdarrenr		fnew.fin_v = 6;
930145516Sdarrenr		hlen = sizeof(*ip6);
931145516Sdarrenr		break;
932145516Sdarrenr	}
933145516Sdarrenr#endif
934145516Sdarrenr	default :
935145516Sdarrenr		return EINVAL;
936145516Sdarrenr	}
937145516Sdarrenr#ifdef IPSEC
938145516Sdarrenr	m->m_pkthdr.rcvif = NULL;
939145516Sdarrenr#endif
940145516Sdarrenr
941145516Sdarrenr	fnew.fin_ifp = fin->fin_ifp;
942145516Sdarrenr	fnew.fin_flx = FI_NOCKSUM;
943145516Sdarrenr	fnew.fin_m = m;
944145516Sdarrenr	fnew.fin_ip = ip;
945145516Sdarrenr	fnew.fin_mp = mpp;
946145516Sdarrenr	fnew.fin_hlen = hlen;
947145516Sdarrenr	fnew.fin_dp = (char *)ip + hlen;
948145516Sdarrenr	(void) fr_makefrip(hlen, ip, &fnew);
949145516Sdarrenr
950145516Sdarrenr	return fr_fastroute(m, mpp, &fnew, NULL);
951145516Sdarrenr}
952145516Sdarrenr
953145516Sdarrenr
954145516Sdarrenrint fr_send_icmp_err(type, fin, dst)
955145516Sdarrenrint type;
956145516Sdarrenrfr_info_t *fin;
957145516Sdarrenrint dst;
958145516Sdarrenr{
959145516Sdarrenr	int err, hlen, xtra, iclen, ohlen, avail, code;
960145516Sdarrenr	struct in_addr dst4;
961145516Sdarrenr	struct icmp *icmp;
962145516Sdarrenr	struct mbuf *m;
963145516Sdarrenr	void *ifp;
964145516Sdarrenr#ifdef USE_INET6
965145516Sdarrenr	ip6_t *ip6;
966145516Sdarrenr	struct in6_addr dst6;
967145516Sdarrenr#endif
968145516Sdarrenr	ip_t *ip, *ip2;
969145516Sdarrenr
970145516Sdarrenr	if ((type < 0) || (type > ICMP_MAXTYPE))
971145516Sdarrenr		return -1;
972145516Sdarrenr
973145516Sdarrenr	code = fin->fin_icode;
974145516Sdarrenr#ifdef USE_INET6
975145516Sdarrenr	if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int)))
976145516Sdarrenr		return -1;
977145516Sdarrenr#endif
978145516Sdarrenr
979145516Sdarrenr#ifndef	IPFILTER_CKSUM
980145516Sdarrenr	if (fr_checkl4sum(fin) == -1)
981145516Sdarrenr		return -1;
982145516Sdarrenr#endif
983145516Sdarrenr#ifdef MGETHDR
984145516Sdarrenr	MGETHDR(m, M_DONTWAIT, MT_HEADER);
985145516Sdarrenr#else
986145516Sdarrenr	MGET(m, M_DONTWAIT, MT_HEADER);
987145516Sdarrenr#endif
988145516Sdarrenr	if (m == NULL)
989145516Sdarrenr		return -1;
990145516Sdarrenr	avail = MHLEN;
991145516Sdarrenr
992145516Sdarrenr	xtra = 0;
993145516Sdarrenr	hlen = 0;
994145516Sdarrenr	ohlen = 0;
995145516Sdarrenr	ifp = fin->fin_ifp;
996145516Sdarrenr	if (fin->fin_v == 4) {
997145516Sdarrenr		if ((fin->fin_p == IPPROTO_ICMP) &&
998145516Sdarrenr		    !(fin->fin_flx & FI_SHORT))
999145516Sdarrenr			switch (ntohs(fin->fin_data[0]) >> 8)
1000145516Sdarrenr			{
1001145516Sdarrenr			case ICMP_ECHO :
1002145516Sdarrenr			case ICMP_TSTAMP :
1003145516Sdarrenr			case ICMP_IREQ :
1004145516Sdarrenr			case ICMP_MASKREQ :
1005145516Sdarrenr				break;
1006145516Sdarrenr			default :
1007145516Sdarrenr				FREE_MB_T(m);
1008145516Sdarrenr				return 0;
1009145516Sdarrenr			}
1010145516Sdarrenr
1011145516Sdarrenr		if (dst == 0) {
1012145516Sdarrenr			if (fr_ifpaddr(4, FRI_NORMAL, ifp,
1013145516Sdarrenr				       &dst4, NULL) == -1) {
1014145516Sdarrenr				FREE_MB_T(m);
1015145516Sdarrenr				return -1;
1016145516Sdarrenr			}
1017145516Sdarrenr		} else
1018145516Sdarrenr			dst4.s_addr = fin->fin_daddr;
1019145516Sdarrenr
1020145516Sdarrenr		hlen = sizeof(ip_t);
1021145516Sdarrenr		ohlen = fin->fin_hlen;
1022145516Sdarrenr		if (fin->fin_hlen < fin->fin_plen)
1023145516Sdarrenr			xtra = MIN(fin->fin_dlen, 8);
1024145516Sdarrenr		else
1025145516Sdarrenr			xtra = 0;
1026145516Sdarrenr	}
1027145516Sdarrenr
1028145516Sdarrenr#ifdef USE_INET6
1029145516Sdarrenr	else if (fin->fin_v == 6) {
1030145516Sdarrenr		hlen = sizeof(ip6_t);
1031145516Sdarrenr		ohlen = sizeof(ip6_t);
1032145516Sdarrenr		type = icmptoicmp6types[type];
1033145516Sdarrenr		if (type == ICMP6_DST_UNREACH)
1034145516Sdarrenr			code = icmptoicmp6unreach[code];
1035145516Sdarrenr
1036145516Sdarrenr		if (hlen + sizeof(*icmp) + max_linkhdr +
1037145516Sdarrenr		    fin->fin_plen > avail) {
1038145516Sdarrenr			MCLGET(m, M_DONTWAIT);
1039145516Sdarrenr			if ((m->m_flags & M_EXT) == 0) {
1040145516Sdarrenr				FREE_MB_T(m);
1041145516Sdarrenr				return -1;
1042145516Sdarrenr			}
1043145516Sdarrenr			avail = MCLBYTES;
1044145516Sdarrenr		}
1045145516Sdarrenr		xtra = MIN(fin->fin_plen,
1046145516Sdarrenr			   avail - hlen - sizeof(*icmp) - max_linkhdr);
1047145516Sdarrenr		if (dst == 0) {
1048145516Sdarrenr			if (fr_ifpaddr(6, FRI_NORMAL, ifp,
1049145516Sdarrenr				       (struct in_addr *)&dst6, NULL) == -1) {
1050145516Sdarrenr				FREE_MB_T(m);
1051145516Sdarrenr				return -1;
1052145516Sdarrenr			}
1053145516Sdarrenr		} else
1054145516Sdarrenr			dst6 = fin->fin_dst6;
1055145516Sdarrenr	}
1056145516Sdarrenr#endif
1057145516Sdarrenr	else {
1058145516Sdarrenr		FREE_MB_T(m);
1059145516Sdarrenr		return -1;
1060145516Sdarrenr	}
1061145516Sdarrenr
1062145516Sdarrenr	iclen = hlen + sizeof(*icmp);
1063145516Sdarrenr	avail -= (max_linkhdr + iclen);
1064145516Sdarrenr	if (avail < 0) {
1065145516Sdarrenr		FREE_MB_T(m);
1066145516Sdarrenr		return -1;
1067145516Sdarrenr	}
1068145516Sdarrenr	if (xtra > avail)
1069145516Sdarrenr		xtra = avail;
1070145516Sdarrenr	iclen += xtra;
1071145516Sdarrenr	m->m_data += max_linkhdr;
1072145516Sdarrenr	m->m_pkthdr.rcvif = (struct ifnet *)0;
1073145516Sdarrenr	m->m_pkthdr.len = iclen;
1074145516Sdarrenr	m->m_len = iclen;
1075145516Sdarrenr	ip = mtod(m, ip_t *);
1076145516Sdarrenr	icmp = (struct icmp *)((char *)ip + hlen);
1077145516Sdarrenr	ip2 = (ip_t *)&icmp->icmp_ip;
1078145516Sdarrenr
1079145516Sdarrenr	icmp->icmp_type = type;
1080145516Sdarrenr	icmp->icmp_code = fin->fin_icode;
1081145516Sdarrenr	icmp->icmp_cksum = 0;
1082145516Sdarrenr#ifdef icmp_nextmtu
1083145516Sdarrenr	if (type == ICMP_UNREACH &&
1084145516Sdarrenr	    fin->fin_icode == ICMP_UNREACH_NEEDFRAG && ifp)
1085145516Sdarrenr		icmp->icmp_nextmtu = htons(((struct ifnet *)ifp)->if_mtu);
1086145516Sdarrenr#endif
1087145516Sdarrenr
1088145516Sdarrenr	bcopy((char *)fin->fin_ip, (char *)ip2, ohlen);
1089145516Sdarrenr
1090145516Sdarrenr#ifdef USE_INET6
1091145516Sdarrenr	ip6 = (ip6_t *)ip;
1092145516Sdarrenr	if (fin->fin_v == 6) {
1093145516Sdarrenr		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
1094145516Sdarrenr		ip6->ip6_plen = htons(iclen - hlen);
1095145516Sdarrenr		ip6->ip6_nxt = IPPROTO_ICMPV6;
1096145516Sdarrenr		ip6->ip6_hlim = 0;
1097145516Sdarrenr		ip6->ip6_src = dst6;
1098145516Sdarrenr		ip6->ip6_dst = fin->fin_src6;
1099145516Sdarrenr		if (xtra > 0)
1100145516Sdarrenr			bcopy((char *)fin->fin_ip + ohlen,
1101145516Sdarrenr			      (char *)&icmp->icmp_ip + ohlen, xtra);
1102145516Sdarrenr		icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6,
1103145516Sdarrenr					     sizeof(*ip6), iclen - hlen);
1104145516Sdarrenr	} else
1105145516Sdarrenr#endif
1106145516Sdarrenr	{
1107145516Sdarrenr		ip2->ip_len = htons(ip2->ip_len);
1108145516Sdarrenr		ip2->ip_off = htons(ip2->ip_off);
1109145516Sdarrenr		ip->ip_p = IPPROTO_ICMP;
1110145516Sdarrenr		ip->ip_src.s_addr = dst4.s_addr;
1111145516Sdarrenr		ip->ip_dst.s_addr = fin->fin_saddr;
1112145516Sdarrenr
1113145516Sdarrenr		if (xtra > 0)
1114145516Sdarrenr			bcopy((char *)fin->fin_ip + ohlen,
1115145516Sdarrenr			      (char *)&icmp->icmp_ip + ohlen, xtra);
1116145516Sdarrenr		icmp->icmp_cksum = ipf_cksum((u_short *)icmp,
1117145516Sdarrenr					     sizeof(*icmp) + 8);
1118145516Sdarrenr		ip->ip_len = iclen;
1119145516Sdarrenr		ip->ip_p = IPPROTO_ICMP;
1120145516Sdarrenr	}
1121145516Sdarrenr	err = fr_send_ip(fin, m, &m);
1122145516Sdarrenr	return err;
1123145516Sdarrenr}
1124145516Sdarrenr
1125145516Sdarrenr
1126145516Sdarrenr#if !defined(IPFILTER_LKM) && (__FreeBSD_version < 300000)
1127145516Sdarrenr# if	(BSD < 199306)
1128145516Sdarrenrint iplinit __P((void));
1129145516Sdarrenr
1130145516Sdarrenrint
1131145516Sdarrenr# else
1132145516Sdarrenrvoid iplinit __P((void));
1133145516Sdarrenr
1134145516Sdarrenrvoid
1135145516Sdarrenr# endif
1136145516Sdarrenriplinit()
1137145516Sdarrenr{
1138145516Sdarrenr	if (iplattach() != 0)
1139145516Sdarrenr		printf("IP Filter failed to attach\n");
1140145516Sdarrenr	ip_init();
1141145516Sdarrenr}
1142145516Sdarrenr#endif /* __FreeBSD_version < 300000 */
1143145516Sdarrenr
1144145516Sdarrenr
1145145516Sdarrenrint fr_fastroute(m0, mpp, fin, fdp)
1146145516Sdarrenrmb_t *m0, **mpp;
1147145516Sdarrenrfr_info_t *fin;
1148145516Sdarrenrfrdest_t *fdp;
1149145516Sdarrenr{
1150145516Sdarrenr	register struct ip *ip, *mhip;
1151145516Sdarrenr	register struct mbuf *m = m0;
1152145516Sdarrenr	register struct route *ro;
1153145516Sdarrenr	int len, off, error = 0, hlen, code;
1154145516Sdarrenr	struct ifnet *ifp, *sifp;
1155145516Sdarrenr	struct sockaddr_in *dst;
1156145516Sdarrenr	struct route iproute;
1157145516Sdarrenr	u_short ip_off;
1158145516Sdarrenr	frentry_t *fr;
1159145516Sdarrenr
1160145516Sdarrenr#ifdef M_WRITABLE
1161145516Sdarrenr	/*
1162145516Sdarrenr	* HOT FIX/KLUDGE:
1163145516Sdarrenr	*
1164145516Sdarrenr	* If the mbuf we're about to send is not writable (because of
1165145516Sdarrenr	* a cluster reference, for example) we'll need to make a copy
1166145516Sdarrenr	* of it since this routine modifies the contents.
1167145516Sdarrenr	*
1168145516Sdarrenr	* If you have non-crappy network hardware that can transmit data
1169145516Sdarrenr	* from the mbuf, rather than making a copy, this is gonna be a
1170145516Sdarrenr	* problem.
1171145516Sdarrenr	*/
1172145516Sdarrenr	if (M_WRITABLE(m) == 0) {
1173145516Sdarrenr		if ((m0 = m_dup(m, M_DONTWAIT)) != 0) {
1174145516Sdarrenr			FREE_MB_T(m);
1175145516Sdarrenr			m = m0;
1176145516Sdarrenr			*mpp = m;
1177145516Sdarrenr		} else {
1178145516Sdarrenr			error = ENOBUFS;
1179145516Sdarrenr			FREE_MB_T(m);
1180145516Sdarrenr			*mpp = NULL;
1181145516Sdarrenr			fr_frouteok[1]++;
1182145516Sdarrenr		}
1183145516Sdarrenr	}
1184145516Sdarrenr#endif
1185145516Sdarrenr
1186145516Sdarrenr#ifdef USE_INET6
1187145516Sdarrenr	if (fin->fin_v == 6) {
1188145516Sdarrenr		/*
1189145516Sdarrenr		 * currently "to <if>" and "to <if>:ip#" are not supported
1190145516Sdarrenr		 * for IPv6
1191145516Sdarrenr		 */
1192145516Sdarrenr#if  (__FreeBSD_version >= 490000)
1193145516Sdarrenr		return ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL);
1194145516Sdarrenr#else
1195145516Sdarrenr		return ip6_output(m0, NULL, NULL, 0, NULL, NULL);
1196145516Sdarrenr#endif
1197145516Sdarrenr	}
1198145516Sdarrenr#endif
1199145516Sdarrenr
1200145516Sdarrenr	hlen = fin->fin_hlen;
1201145516Sdarrenr	ip = mtod(m0, struct ip *);
1202145516Sdarrenr
1203145516Sdarrenr	/*
1204145516Sdarrenr	 * Route packet.
1205145516Sdarrenr	 */
1206145516Sdarrenr	ro = &iproute;
1207145516Sdarrenr	bzero((caddr_t)ro, sizeof (*ro));
1208145516Sdarrenr	dst = (struct sockaddr_in *)&ro->ro_dst;
1209145516Sdarrenr	dst->sin_family = AF_INET;
1210145516Sdarrenr	dst->sin_addr = ip->ip_dst;
1211145516Sdarrenr
1212145516Sdarrenr	fr = fin->fin_fr;
1213145516Sdarrenr	if (fdp != NULL)
1214145516Sdarrenr		ifp = fdp->fd_ifp;
1215145516Sdarrenr	else
1216145516Sdarrenr		ifp = fin->fin_ifp;
1217145516Sdarrenr
1218145516Sdarrenr	if ((ifp == NULL) && (!fr || !(fr->fr_flags & FR_FASTROUTE))) {
1219145516Sdarrenr		error = -2;
1220145516Sdarrenr		goto bad;
1221145516Sdarrenr	}
1222145516Sdarrenr
1223145516Sdarrenr	/*
1224145516Sdarrenr	 * In case we're here due to "to <if>" being used with "keep state",
1225145516Sdarrenr	 * check that we're going in the correct direction.
1226145516Sdarrenr	 */
1227145516Sdarrenr	if ((fr != NULL) && (fin->fin_rev != 0)) {
1228145516Sdarrenr		if ((ifp != NULL) && (fdp == &fr->fr_tif))
1229145516Sdarrenr			return -1;
1230145516Sdarrenr	}
1231145516Sdarrenr	if (fdp != NULL) {
1232145516Sdarrenr		if (fdp->fd_ip.s_addr != 0)
1233145516Sdarrenr			dst->sin_addr = fdp->fd_ip;
1234145516Sdarrenr	}
1235145516Sdarrenr
1236145516Sdarrenr	dst->sin_len = sizeof(*dst);
1237145516Sdarrenr	rtalloc(ro);
1238145516Sdarrenr
1239145516Sdarrenr	if ((ifp == NULL) && (ro->ro_rt != NULL))
1240145516Sdarrenr		ifp = ro->ro_rt->rt_ifp;
1241145516Sdarrenr
1242145516Sdarrenr	if ((ro->ro_rt == NULL) || (ifp == NULL)) {
1243145516Sdarrenr		if (in_localaddr(ip->ip_dst))
1244145516Sdarrenr			error = EHOSTUNREACH;
1245145516Sdarrenr		else
1246145516Sdarrenr			error = ENETUNREACH;
1247145516Sdarrenr		goto bad;
1248145516Sdarrenr	}
1249145516Sdarrenr	if (ro->ro_rt->rt_flags & RTF_GATEWAY)
1250145516Sdarrenr		dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
1251145516Sdarrenr	if (ro->ro_rt)
1252145516Sdarrenr		ro->ro_rt->rt_use++;
1253145516Sdarrenr
1254145516Sdarrenr	/*
1255145516Sdarrenr	 * For input packets which are being "fastrouted", they won't
1256145516Sdarrenr	 * go back through output filtering and miss their chance to get
1257145516Sdarrenr	 * NAT'd and counted.
1258145516Sdarrenr	 */
1259145516Sdarrenr	if (fin->fin_out == 0) {
1260145516Sdarrenr		sifp = fin->fin_ifp;
1261145516Sdarrenr		fin->fin_ifp = ifp;
1262145516Sdarrenr		fin->fin_out = 1;
1263145516Sdarrenr		(void) fr_acctpkt(fin, NULL);
1264145516Sdarrenr		fin->fin_fr = NULL;
1265145516Sdarrenr		if (!fr || !(fr->fr_flags & FR_RETMASK)) {
1266145516Sdarrenr			u_32_t pass;
1267145516Sdarrenr
1268145516Sdarrenr			(void) fr_checkstate(fin, &pass);
1269145516Sdarrenr		}
1270145516Sdarrenr
1271145516Sdarrenr		switch (fr_checknatout(fin, NULL))
1272145516Sdarrenr		{
1273145516Sdarrenr		case 0 :
1274145516Sdarrenr			break;
1275145516Sdarrenr		case 1 :
1276145516Sdarrenr			ip->ip_sum = 0;
1277145516Sdarrenr			break;
1278145516Sdarrenr		case -1 :
1279145516Sdarrenr			error = -1;
1280145516Sdarrenr			goto done;
1281145516Sdarrenr			break;
1282145516Sdarrenr		}
1283145516Sdarrenr
1284145516Sdarrenr		fin->fin_ifp = sifp;
1285145516Sdarrenr		fin->fin_out = 0;
1286145516Sdarrenr	} else
1287145516Sdarrenr		ip->ip_sum = 0;
1288145516Sdarrenr	/*
1289145516Sdarrenr	 * If small enough for interface, can just send directly.
1290145516Sdarrenr	 */
1291145516Sdarrenr	if (ip->ip_len <= ifp->if_mtu) {
1292145516Sdarrenr		ip->ip_len = htons(ip->ip_len);
1293145516Sdarrenr		ip->ip_off = htons(ip->ip_off);
1294145516Sdarrenr
1295145516Sdarrenr		if (!ip->ip_sum)
1296145516Sdarrenr			ip->ip_sum = in_cksum(m, hlen);
1297145516Sdarrenr		error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst,
1298145516Sdarrenr					  ro->ro_rt);
1299145516Sdarrenr		goto done;
1300145516Sdarrenr	}
1301145516Sdarrenr	/*
1302145516Sdarrenr	 * Too large for interface; fragment if possible.
1303145516Sdarrenr	 * Must be able to put at least 8 bytes per fragment.
1304145516Sdarrenr	 */
1305145516Sdarrenr	ip_off = ntohs(ip->ip_off);
1306145516Sdarrenr	if (ip_off & IP_DF) {
1307145516Sdarrenr		error = EMSGSIZE;
1308145516Sdarrenr		goto bad;
1309145516Sdarrenr	}
1310145516Sdarrenr	len = (ifp->if_mtu - hlen) &~ 7;
1311145516Sdarrenr	if (len < 8) {
1312145516Sdarrenr		error = EMSGSIZE;
1313145516Sdarrenr		goto bad;
1314145516Sdarrenr	}
1315145516Sdarrenr
1316145516Sdarrenr    {
1317145516Sdarrenr	int mhlen, firstlen = len;
1318145516Sdarrenr	struct mbuf **mnext = &m->m_act;
1319145516Sdarrenr
1320145516Sdarrenr	/*
1321145516Sdarrenr	 * Loop through length of segment after first fragment,
1322145516Sdarrenr	 * make new header and copy data of each part and link onto chain.
1323145516Sdarrenr	 */
1324145516Sdarrenr	m0 = m;
1325145516Sdarrenr	mhlen = sizeof (struct ip);
1326145516Sdarrenr	for (off = hlen + len; off < ip->ip_len; off += len) {
1327145516Sdarrenr#ifdef MGETHDR
1328145516Sdarrenr		MGETHDR(m, M_DONTWAIT, MT_HEADER);
1329145516Sdarrenr#else
1330145516Sdarrenr		MGET(m, M_DONTWAIT, MT_HEADER);
1331145516Sdarrenr#endif
1332145516Sdarrenr		if (m == 0) {
1333145516Sdarrenr			m = m0;
1334145516Sdarrenr			error = ENOBUFS;
1335145516Sdarrenr			goto bad;
1336145516Sdarrenr		}
1337145516Sdarrenr		m->m_data += max_linkhdr;
1338145516Sdarrenr		mhip = mtod(m, struct ip *);
1339145516Sdarrenr		bcopy((char *)ip, (char *)mhip, sizeof(*ip));
1340145516Sdarrenr		if (hlen > sizeof (struct ip)) {
1341145516Sdarrenr			mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
1342145516Sdarrenr			IP_HL_A(mhip, mhlen >> 2);
1343145516Sdarrenr		}
1344145516Sdarrenr		m->m_len = mhlen;
1345145516Sdarrenr		mhip->ip_off = ((off - hlen) >> 3) + ip_off;
1346145516Sdarrenr		if (off + len >= ip->ip_len)
1347145516Sdarrenr			len = ip->ip_len - off;
1348145516Sdarrenr		else
1349145516Sdarrenr			mhip->ip_off |= IP_MF;
1350145516Sdarrenr		mhip->ip_len = htons((u_short)(len + mhlen));
1351145516Sdarrenr		m->m_next = m_copy(m0, off, len);
1352145516Sdarrenr		if (m->m_next == 0) {
1353145516Sdarrenr			error = ENOBUFS;	/* ??? */
1354145516Sdarrenr			goto sendorfree;
1355145516Sdarrenr		}
1356145516Sdarrenr		m->m_pkthdr.len = mhlen + len;
1357145516Sdarrenr		m->m_pkthdr.rcvif = NULL;
1358145516Sdarrenr		mhip->ip_off = htons((u_short)mhip->ip_off);
1359145516Sdarrenr		mhip->ip_sum = 0;
1360145516Sdarrenr		mhip->ip_sum = in_cksum(m, mhlen);
1361145516Sdarrenr		*mnext = m;
1362145516Sdarrenr		mnext = &m->m_act;
1363145516Sdarrenr	}
1364145516Sdarrenr	/*
1365145516Sdarrenr	 * Update first fragment by trimming what's been copied out
1366145516Sdarrenr	 * and updating header, then send each fragment (in order).
1367145516Sdarrenr	 */
1368145516Sdarrenr	m_adj(m0, hlen + firstlen - ip->ip_len);
1369145516Sdarrenr	ip->ip_len = htons((u_short)(hlen + firstlen));
1370145516Sdarrenr	ip->ip_off = htons((u_short)IP_MF);
1371145516Sdarrenr	ip->ip_sum = 0;
1372145516Sdarrenr	ip->ip_sum = in_cksum(m0, hlen);
1373145516Sdarrenrsendorfree:
1374145516Sdarrenr	for (m = m0; m; m = m0) {
1375145516Sdarrenr		m0 = m->m_act;
1376145516Sdarrenr		m->m_act = 0;
1377145516Sdarrenr		if (error == 0)
1378145516Sdarrenr			error = (*ifp->if_output)(ifp, m,
1379145516Sdarrenr			    (struct sockaddr *)dst, ro->ro_rt);
1380145516Sdarrenr		else
1381145516Sdarrenr			FREE_MB_T(m);
1382145516Sdarrenr	}
1383145516Sdarrenr    }
1384145516Sdarrenrdone:
1385145516Sdarrenr	if (!error)
1386145516Sdarrenr		fr_frouteok[0]++;
1387145516Sdarrenr	else
1388145516Sdarrenr		fr_frouteok[1]++;
1389145516Sdarrenr
1390145516Sdarrenr	if (ro->ro_rt) {
1391145516Sdarrenr		RTFREE(ro->ro_rt);
1392145516Sdarrenr	}
1393145516Sdarrenr	*mpp = NULL;
1394145516Sdarrenr	return 0;
1395145516Sdarrenrbad:
1396145516Sdarrenr	if (error == EMSGSIZE) {
1397145516Sdarrenr		sifp = fin->fin_ifp;
1398145516Sdarrenr		code = fin->fin_icode;
1399145516Sdarrenr		fin->fin_icode = ICMP_UNREACH_NEEDFRAG;
1400145516Sdarrenr		fin->fin_ifp = ifp;
1401145516Sdarrenr		(void) fr_send_icmp_err(ICMP_UNREACH, fin, 1);
1402145516Sdarrenr		fin->fin_ifp = sifp;
1403145516Sdarrenr		fin->fin_icode = code;
1404145516Sdarrenr	}
1405145516Sdarrenr	FREE_MB_T(m);
1406145516Sdarrenr	goto done;
1407145516Sdarrenr}
1408145516Sdarrenr
1409145516Sdarrenr
1410145516Sdarrenrint fr_verifysrc(fin)
1411145516Sdarrenrfr_info_t *fin;
1412145516Sdarrenr{
1413145516Sdarrenr	struct sockaddr_in *dst;
1414145516Sdarrenr	struct route iproute;
1415145516Sdarrenr
1416145516Sdarrenr	bzero((char *)&iproute, sizeof(iproute));
1417145516Sdarrenr	dst = (struct sockaddr_in *)&iproute.ro_dst;
1418145516Sdarrenr	dst->sin_len = sizeof(*dst);
1419145516Sdarrenr	dst->sin_family = AF_INET;
1420145516Sdarrenr	dst->sin_addr = fin->fin_src;
1421145516Sdarrenr	rtalloc(&iproute);
1422145516Sdarrenr	if (iproute.ro_rt == NULL)
1423145516Sdarrenr		return 0;
1424145516Sdarrenr	return (fin->fin_ifp == iproute.ro_rt->rt_ifp);
1425145516Sdarrenr}
1426145516Sdarrenr
1427145516Sdarrenr
1428145516Sdarrenr/*
1429145516Sdarrenr * return the first IP Address associated with an interface
1430145516Sdarrenr */
1431145516Sdarrenrint fr_ifpaddr(v, atype, ifptr, inp, inpmask)
1432145516Sdarrenrint v, atype;
1433145516Sdarrenrvoid *ifptr;
1434145516Sdarrenrstruct in_addr *inp, *inpmask;
1435145516Sdarrenr{
1436145516Sdarrenr#ifdef USE_INET6
1437145516Sdarrenr	struct in6_addr *inp6 = NULL;
1438145516Sdarrenr#endif
1439145516Sdarrenr	struct sockaddr *sock, *mask;
1440145516Sdarrenr	struct sockaddr_in *sin;
1441145516Sdarrenr	struct ifaddr *ifa;
1442145516Sdarrenr	struct ifnet *ifp;
1443145516Sdarrenr
1444145516Sdarrenr	if ((ifptr == NULL) || (ifptr == (void *)-1))
1445145516Sdarrenr		return -1;
1446145516Sdarrenr
1447145516Sdarrenr	sin = NULL;
1448145516Sdarrenr	ifp = ifptr;
1449145516Sdarrenr
1450145516Sdarrenr	if (v == 4)
1451145516Sdarrenr		inp->s_addr = 0;
1452145516Sdarrenr#ifdef USE_INET6
1453145516Sdarrenr	else if (v == 6)
1454145516Sdarrenr		bzero((char *)inp, sizeof(struct in6_addr));
1455145516Sdarrenr#endif
1456145516Sdarrenr#if  (__FreeBSD_version >= 300000)
1457145516Sdarrenr	ifa = TAILQ_FIRST(&ifp->if_addrhead);
1458145516Sdarrenr#else
1459145516Sdarrenr	ifa = ifp->if_addrlist;
1460145516Sdarrenr#endif /* __FreeBSD_version >= 300000 */
1461145516Sdarrenr
1462145516Sdarrenr	sock = ifa->ifa_addr;
1463145516Sdarrenr	while (sock != NULL && ifa != NULL) {
1464145516Sdarrenr		sin = (struct sockaddr_in *)sock;
1465145516Sdarrenr		if ((v == 4) && (sin->sin_family == AF_INET))
1466145516Sdarrenr			break;
1467145516Sdarrenr#ifdef USE_INET6
1468145516Sdarrenr		if ((v == 6) && (sin->sin_family == AF_INET6)) {
1469145516Sdarrenr			inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr;
1470145516Sdarrenr			if (!IN6_IS_ADDR_LINKLOCAL(inp6) &&
1471145516Sdarrenr			    !IN6_IS_ADDR_LOOPBACK(inp6))
1472145516Sdarrenr				break;
1473145516Sdarrenr		}
1474145516Sdarrenr#endif
1475145516Sdarrenr#if (__FreeBSD_version >= 300000)
1476145516Sdarrenr		ifa = TAILQ_NEXT(ifa, ifa_link);
1477145516Sdarrenr#else
1478145516Sdarrenr		ifa = ifa->ifa_next;
1479145516Sdarrenr#endif /* __FreeBSD_version >= 300000 */
1480145516Sdarrenr		if (ifa != NULL)
1481145516Sdarrenr			sock = ifa->ifa_addr;
1482145516Sdarrenr	}
1483145516Sdarrenr
1484145516Sdarrenr	if (ifa == NULL || sin == NULL)
1485145516Sdarrenr		return -1;
1486145516Sdarrenr
1487145516Sdarrenr	mask = ifa->ifa_netmask;
1488145516Sdarrenr	if (atype == FRI_BROADCAST)
1489145516Sdarrenr		sock = ifa->ifa_broadaddr;
1490145516Sdarrenr	else if (atype == FRI_PEERADDR)
1491145516Sdarrenr		sock = ifa->ifa_dstaddr;
1492145516Sdarrenr
1493145516Sdarrenr#ifdef USE_INET6
1494145516Sdarrenr	if (v == 6) {
1495145516Sdarrenr		return fr_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock,
1496145516Sdarrenr					(struct sockaddr_in6 *)mask,
1497145516Sdarrenr					inp, inpmask);
1498145516Sdarrenr	}
1499145516Sdarrenr#endif
1500145516Sdarrenr	return fr_ifpfillv4addr(atype, (struct sockaddr_in *)sock,
1501145516Sdarrenr				(struct sockaddr_in *)mask, inp, inpmask);
1502145516Sdarrenr}
1503145516Sdarrenr
1504145516Sdarrenr
1505145516Sdarrenru_32_t fr_newisn(fin)
1506145516Sdarrenrfr_info_t *fin;
1507145516Sdarrenr{
1508145516Sdarrenr	u_32_t newiss;
1509145516Sdarrenr#if  (__FreeBSD_version >= 400000)
1510145516Sdarrenr	newiss = arc4random();
1511145516Sdarrenr#else
1512145516Sdarrenr	static iss_seq_off = 0;
1513145516Sdarrenr	u_char hash[16];
1514145516Sdarrenr	MD5_CTX ctx;
1515145516Sdarrenr
1516145516Sdarrenr	/*
1517145516Sdarrenr	 * Compute the base value of the ISS.  It is a hash
1518145516Sdarrenr	 * of (saddr, sport, daddr, dport, secret).
1519145516Sdarrenr	 */
1520145516Sdarrenr	MD5Init(&ctx);
1521145516Sdarrenr
1522145516Sdarrenr	MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_src,
1523145516Sdarrenr		  sizeof(fin->fin_fi.fi_src));
1524145516Sdarrenr	MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_dst,
1525145516Sdarrenr		  sizeof(fin->fin_fi.fi_dst));
1526145516Sdarrenr	MD5Update(&ctx, (u_char *) &fin->fin_dat, sizeof(fin->fin_dat));
1527145516Sdarrenr
1528145516Sdarrenr	MD5Update(&ctx, ipf_iss_secret, sizeof(ipf_iss_secret));
1529145516Sdarrenr
1530145516Sdarrenr	MD5Final(hash, &ctx);
1531145516Sdarrenr
1532145516Sdarrenr	memcpy(&newiss, hash, sizeof(newiss));
1533145516Sdarrenr
1534145516Sdarrenr	/*
1535145516Sdarrenr	 * Now increment our "timer", and add it in to
1536145516Sdarrenr	 * the computed value.
1537145516Sdarrenr	 *
1538145516Sdarrenr	 * XXX Use `addin'?
1539145516Sdarrenr	 * XXX TCP_ISSINCR too large to use?
1540145516Sdarrenr	 */
1541145516Sdarrenr	iss_seq_off += 0x00010000;
1542145516Sdarrenr	newiss += iss_seq_off;
1543145516Sdarrenr#endif
1544145516Sdarrenr	return newiss;
1545145516Sdarrenr}
1546145516Sdarrenr
1547145516Sdarrenr
1548145516Sdarrenr/* ------------------------------------------------------------------------ */
1549145516Sdarrenr/* Function:    fr_nextipid                                                 */
1550145516Sdarrenr/* Returns:     int - 0 == success, -1 == error (packet should be droppped) */
1551145516Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
1552145516Sdarrenr/*                                                                          */
1553145516Sdarrenr/* Returns the next IPv4 ID to use for this packet.                         */
1554145516Sdarrenr/* ------------------------------------------------------------------------ */
1555145516Sdarrenru_short fr_nextipid(fin)
1556145516Sdarrenrfr_info_t *fin;
1557145516Sdarrenr{
1558145516Sdarrenr#ifndef	RANDOM_IP_ID
1559145516Sdarrenr	static u_short ipid = 0;
1560145516Sdarrenr	u_short id;
1561145516Sdarrenr
1562145516Sdarrenr	MUTEX_ENTER(&ipf_rw);
1563145516Sdarrenr	id = ipid++;
1564145516Sdarrenr	MUTEX_EXIT(&ipf_rw);
1565145516Sdarrenr#else
1566145516Sdarrenr	u_short id;
1567145516Sdarrenr
1568145516Sdarrenr	id = ip_randomid();
1569145516Sdarrenr#endif
1570145516Sdarrenr
1571145516Sdarrenr	return id;
1572145516Sdarrenr}
1573145516Sdarrenr
1574145516Sdarrenr
1575145516SdarrenrINLINE void fr_checkv4sum(fin)
1576145516Sdarrenrfr_info_t *fin;
1577145516Sdarrenr{
1578145516Sdarrenr#ifdef CSUM_DATA_VALID
1579145516Sdarrenr	int manual = 0;
1580145516Sdarrenr	u_short sum;
1581145516Sdarrenr	ip_t *ip;
1582145516Sdarrenr	mb_t *m;
1583145516Sdarrenr
1584145516Sdarrenr	if ((fin->fin_flx & FI_NOCKSUM) != 0)
1585145516Sdarrenr		return;
1586145516Sdarrenr
1587145516Sdarrenr	m = fin->fin_m;
1588145516Sdarrenr	if (m == NULL) {
1589145516Sdarrenr		manual = 1;
1590145516Sdarrenr		goto skipauto;
1591145516Sdarrenr	}
1592145516Sdarrenr	ip = fin->fin_ip;
1593145516Sdarrenr
1594145516Sdarrenr	if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
1595145516Sdarrenr		if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
1596145516Sdarrenr			sum = m->m_pkthdr.csum_data;
1597145516Sdarrenr		else
1598145516Sdarrenr			sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
1599145516Sdarrenr					htonl(m->m_pkthdr.csum_data +
1600145516Sdarrenr					fin->fin_ip->ip_len + fin->fin_p));
1601145516Sdarrenr		sum ^= 0xffff;
1602145516Sdarrenr		if (sum != 0)
1603145516Sdarrenr			fin->fin_flx |= FI_BAD;
1604145516Sdarrenr	} else
1605145516Sdarrenr		manual = 1;
1606145516Sdarrenrskipauto:
1607145516Sdarrenr# ifdef IPFILTER_CKSUM
1608145516Sdarrenr	if (manual != 0)
1609145516Sdarrenr		if (fr_checkl4sum(fin) == -1)
1610145516Sdarrenr			fin->fin_flx |= FI_BAD;
1611145516Sdarrenr# else
1612145516Sdarrenr	;
1613145516Sdarrenr# endif
1614145516Sdarrenr#else
1615145516Sdarrenr# ifdef IPFILTER_CKSUM
1616145516Sdarrenr	if (fr_checkl4sum(fin) == -1)
1617145516Sdarrenr		fin->fin_flx |= FI_BAD;
1618145516Sdarrenr# endif
1619145516Sdarrenr#endif
1620145516Sdarrenr}
1621145516Sdarrenr
1622145516Sdarrenr
1623145516Sdarrenr#ifdef USE_INET6
1624145516SdarrenrINLINE void fr_checkv6sum(fin)
1625145516Sdarrenrfr_info_t *fin;
1626145516Sdarrenr{
1627145516Sdarrenr# ifdef IPFILTER_CKSUM
1628145516Sdarrenr	if (fr_checkl4sum(fin) == -1)
1629145516Sdarrenr		fin->fin_flx |= FI_BAD;
1630145516Sdarrenr# endif
1631145516Sdarrenr}
1632145516Sdarrenr#endif /* USE_INET6 */
1633145516Sdarrenr
1634145516Sdarrenr
1635145516Sdarrenrsize_t mbufchainlen(m0)
1636145516Sdarrenrstruct mbuf *m0;
1637145516Sdarrenr{
1638145516Sdarrenr	size_t len;
1639145516Sdarrenr
1640145516Sdarrenr	if ((m0->m_flags & M_PKTHDR) != 0) {
1641145516Sdarrenr		len = m0->m_pkthdr.len;
1642145516Sdarrenr	} else {
1643145516Sdarrenr		struct mbuf *m;
1644145516Sdarrenr
1645145516Sdarrenr		for (m = m0, len = 0; m != NULL; m = m->m_next)
1646145516Sdarrenr			len += m->m_len;
1647145516Sdarrenr	}
1648145516Sdarrenr	return len;
1649145516Sdarrenr}
1650145516Sdarrenr
1651145516Sdarrenr
1652145516Sdarrenr/* ------------------------------------------------------------------------ */
1653145516Sdarrenr/* Function:    fr_pullup                                                   */
1654145516Sdarrenr/* Returns:     NULL == pullup failed, else pointer to protocol header      */
1655145516Sdarrenr/* Parameters:  m(I)   - pointer to buffer where data packet starts         */
1656145516Sdarrenr/*              fin(I) - pointer to packet information                      */
1657145516Sdarrenr/*              len(I) - number of bytes to pullup                          */
1658145516Sdarrenr/*                                                                          */
1659145516Sdarrenr/* Attempt to move at least len bytes (from the start of the buffer) into a */
1660145516Sdarrenr/* single buffer for ease of access.  Operating system native functions are */
1661145516Sdarrenr/* used to manage buffers - if necessary.  If the entire packet ends up in  */
1662145516Sdarrenr/* a single buffer, set the FI_COALESCE flag even though fr_coalesce() has  */
1663145516Sdarrenr/* not been called.  Both fin_ip and fin_dp are updated before exiting _IF_ */
1664145516Sdarrenr/* and ONLY if the pullup succeeds.                                         */
1665145516Sdarrenr/*                                                                          */
1666145516Sdarrenr/* We assume that 'min' is a pointer to a buffer that is part of the chain  */
1667145516Sdarrenr/* of buffers that starts at *fin->fin_mp.                                  */
1668145516Sdarrenr/* ------------------------------------------------------------------------ */
1669145516Sdarrenrvoid *fr_pullup(min, fin, len)
1670145516Sdarrenrmb_t *min;
1671145516Sdarrenrfr_info_t *fin;
1672145516Sdarrenrint len;
1673145516Sdarrenr{
1674145516Sdarrenr	int out = fin->fin_out, dpoff, ipoff;
1675145516Sdarrenr	mb_t *m = min;
1676145516Sdarrenr	char *ip;
1677145516Sdarrenr
1678145516Sdarrenr	if (m == NULL)
1679145516Sdarrenr		return NULL;
1680145516Sdarrenr
1681145516Sdarrenr	ip = (char *)fin->fin_ip;
1682145516Sdarrenr	if ((fin->fin_flx & FI_COALESCE) != 0)
1683145516Sdarrenr		return ip;
1684145516Sdarrenr
1685145516Sdarrenr	ipoff = fin->fin_ipoff;
1686145516Sdarrenr	if (fin->fin_dp != NULL)
1687145516Sdarrenr		dpoff = (char *)fin->fin_dp - (char *)ip;
1688145516Sdarrenr	else
1689145516Sdarrenr		dpoff = 0;
1690145516Sdarrenr
1691145516Sdarrenr	if (M_LEN(m) < len) {
1692145516Sdarrenr#ifdef MHLEN
1693145516Sdarrenr		/*
1694145516Sdarrenr		 * Assume that M_PKTHDR is set and just work with what is left
1695145516Sdarrenr		 * rather than check..
1696145516Sdarrenr		 * Should not make any real difference, anyway.
1697145516Sdarrenr		 */
1698145516Sdarrenr		if (len > MHLEN)
1699145516Sdarrenr#else
1700145516Sdarrenr		if (len > MLEN)
1701145516Sdarrenr#endif
1702145516Sdarrenr		{
1703145516Sdarrenr#ifdef HAVE_M_PULLDOWN
1704145516Sdarrenr			if (m_pulldown(m, 0, len, NULL) == NULL)
1705145516Sdarrenr				m = NULL;
1706145516Sdarrenr#else
1707145516Sdarrenr			FREE_MB_T(*fin->fin_mp);
1708145516Sdarrenr			m = NULL;
1709145516Sdarrenr#endif
1710145516Sdarrenr		} else
1711145516Sdarrenr		{
1712145516Sdarrenr			m = m_pullup(m, len);
1713145516Sdarrenr		}
1714145516Sdarrenr		*fin->fin_mp = m;
1715145516Sdarrenr		fin->fin_m = m;
1716145516Sdarrenr		if (m == NULL) {
1717145516Sdarrenr			ATOMIC_INCL(frstats[out].fr_pull[1]);
1718145516Sdarrenr			return NULL;
1719145516Sdarrenr		}
1720145516Sdarrenr		ip = MTOD(m, char *) + ipoff;
1721145516Sdarrenr	}
1722145516Sdarrenr
1723145516Sdarrenr	ATOMIC_INCL(frstats[out].fr_pull[0]);
1724145516Sdarrenr	fin->fin_ip = (ip_t *)ip;
1725145516Sdarrenr	if (fin->fin_dp != NULL)
1726145516Sdarrenr		fin->fin_dp = (char *)fin->fin_ip + dpoff;
1727145516Sdarrenr
1728145516Sdarrenr	if (len == fin->fin_plen)
1729145516Sdarrenr		fin->fin_flx |= FI_COALESCE;
1730145516Sdarrenr	return ip;
1731145516Sdarrenr}
1732