1255332Scy/* $FreeBSD$ */
2255332Scy
3254219Scy/*
4254219Scy * Copyright (C) 2012 by Darren Reed.
5254219Scy *
6254219Scy * See the IPFILTER.LICENCE file for details on licencing.
7254219Scy *
8254219Scy */
9254219Scy
10254219Scy#include <sys/param.h>
11254219Scy#include <sys/systm.h>
12254219Scy#include <sys/conf.h>
13254219Scy#include <sys/file.h>
14254219Scy#include <sys/stat.h>
15254219Scy#include <sys/proc.h>
16254219Scy#include <sys/uio.h>
17254219Scy#include <sys/kernel.h>
18254219Scy#include <sys/vnode.h>
19254219Scy#include <sys/namei.h>
20254219Scy#include <sys/malloc.h>
21254219Scy#include <sys/mount.h>
22254219Scy#include <sys/exec.h>
23254219Scy#include <sys/mbuf.h>
24254219Scy#include <net/if.h>
25254219Scy#include <netinet/in_systm.h>
26254219Scy#include <netinet/in.h>
27254219Scy#include <netinet/ip.h>
28254219Scy#include <net/route.h>
29254219Scy#include <netinet/ip_var.h>
30254219Scy#include <netinet/tcp.h>
31254219Scy#include <netinet/tcpip.h>
32254219Scy#include <sys/lkm.h>
33254219Scy#include "ipl.h"
34254219Scy#include "ip_compat.h"
35254219Scy#include "ip_fil.h"
36254219Scy
37254219Scy#define vn_lock(v,f) VOP_LOCK(v)
38254219Scy
39254219Scy#if !defined(VOP_LEASE) && defined(LEASE_CHECK)
40254219Scy#define	VOP_LEASE	LEASE_CHECK
41254219Scy#endif
42254219Scy
43254219Scy
44254219Scyextern	int	lkmenodev __P((void));
45254219Scy
46254219Scy#if OpenBSD >= 200311
47254219Scyint	if_ipf_lkmentry __P((struct lkm_table *, int, int));
48254219Scy#else
49254219Scyint	if_ipf __P((struct lkm_table *, int, int));
50254219Scy#endif
51254219Scystatic	int	ipf_unload __P((void));
52254219Scystatic	int	ipf_load __P((void));
53254219Scystatic	int	ipf_remove __P((void));
54254219Scystatic	int	ipfaction __P((struct lkm_table *, int));
55254219Scystatic	char	*ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME,
56254219Scy				    IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME,
57254219Scy				    IPLOOKUP_NAME, NULL };
58254219Scy
59254219Scy
60254219Scystruct	cdevsw	ipfdevsw =
61254219Scy{
62254219Scy	ipfopen,		/* open */
63254219Scy	ipfclose,		/* close */
64254219Scy	ipfread,		/* read */
65254219Scy	(void *)nullop,		/* write */
66254219Scy	ipfioctl,		/* ioctl */
67254219Scy	(void *)nullop,		/* stop */
68254219Scy	(void *)NULL,		/* tty */
69254219Scy	(void *)nullop,		/* select */
70254219Scy	(void *)nullop,		/* mmap */
71254219Scy	NULL			/* strategy */
72254219Scy};
73254219Scy
74254219Scyint	ipf_major = 0;
75254219Scy
76254219ScyMOD_DEV(IPL_VERSION, LM_DT_CHAR, -1, &ipfdevsw);
77254219Scy
78254219Scyextern int vd_unuseddev __P((void));
79254219Scyextern struct cdevsw cdevsw[];
80254219Scyextern int nchrdev;
81254219Scy
82254219Scy
83254219Scy#if OpenBSD >= 200311
84254219Scyint if_ipf_lkmentry (lkmtp, cmd, ver)
85254219Scy#else
86254219Scyint if_ipf(lkmtp, cmd, ver)
87254219Scy#endif
88254219Scy	struct lkm_table *lkmtp;
89254219Scy	int cmd, ver;
90254219Scy{
91254219Scy	DISPATCH(lkmtp, cmd, ver, ipfaction, ipfaction, ipfaction);
92254219Scy}
93254219Scy
94254219Scyint lkmexists __P((struct lkm_table *)); /* defined in /sys/kern/kern_lkm.c */
95254219Scy
96254219Scystatic int ipfaction(lkmtp, cmd)
97254219Scy	struct lkm_table *lkmtp;
98254219Scy	int cmd;
99254219Scy{
100254219Scy	int i;
101254219Scy	struct lkm_dev *args = lkmtp->private.lkm_dev;
102254219Scy	int err = 0;
103254219Scy
104254219Scy	switch (cmd)
105254219Scy	{
106254219Scy	case LKM_E_LOAD :
107254219Scy		if (lkmexists(lkmtp))
108254219Scy			return EEXIST;
109254219Scy
110254219Scy		for (i = 0; i < nchrdev; i++)
111254219Scy			if (cdevsw[i].d_open == (dev_type_open((*)))lkmenodev ||
112254219Scy			    cdevsw[i].d_open == ipfopen)
113254219Scy				break;
114254219Scy		if (i == nchrdev) {
115254219Scy			printf("IP Filter: No free cdevsw slots\n");
116254219Scy			return ENODEV;
117254219Scy		}
118254219Scy
119254219Scy		ipf_major = i;
120254219Scy		args->lkm_offset = i;   /* slot in cdevsw[] */
121254219Scy		printf("IP Filter: loaded into slot %d\n", ipf_major);
122254219Scy		return ipf_load();
123254219Scy	case LKM_E_UNLOAD :
124254219Scy		err = ipf_unload();
125254219Scy		if (!err)
126254219Scy			printf("IP Filter: unloaded from slot %d\n",
127254219Scy			       ipf_major);
128254219Scy		break;
129254219Scy	case LKM_E_STAT :
130254219Scy		break;
131254219Scy	default:
132254219Scy		err = EIO;
133254219Scy		break;
134254219Scy	}
135254219Scy	return err;
136254219Scy}
137254219Scy
138254219Scy
139254219Scystatic int ipf_remove()
140254219Scy{
141254219Scy	struct nameidata nd;
142254219Scy	int error, i;
143254219Scy	char *name;
144254219Scy
145254219Scy        for (i = 0; (name = ipf_devfiles[i]); i++) {
146254219Scy#if OpenBSD >= 200311
147254219Scy		NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE,
148254219Scy		       name, curproc);
149254219Scy#else
150254219Scy		NDINIT(&nd, DELETE, LOCKPARENT, UIO_SYSSPACE, name, curproc);
151254219Scy#endif
152254219Scy		if ((error = namei(&nd)))
153254219Scy			return (error);
154254219Scy		VOP_LEASE(nd.ni_vp, curproc, curproc->p_ucred, LEASE_WRITE);
155254219Scy#if OpenBSD < 200311
156254219Scy		VOP_LOCK(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY, curproc);
157254219Scy		VOP_LEASE(nd.ni_dvp, curproc, curproc->p_ucred, LEASE_WRITE);
158254219Scy#else
159254219Scy		(void)uvm_vnp_uncache(nd.ni_vp);
160254219Scy
161254219Scy		VOP_LEASE(nd.ni_dvp, curproc, curproc->p_ucred, LEASE_WRITE);
162254219Scy		VOP_LEASE(nd.ni_vp, curproc, curproc->p_ucred, LEASE_WRITE);
163254219Scy#endif
164254219Scy		(void) VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
165254219Scy	}
166254219Scy	return 0;
167254219Scy}
168254219Scy
169254219Scy
170254219Scystatic int ipf_unload()
171254219Scy{
172254219Scy	int error = 0;
173254219Scy
174254219Scy	/*
175254219Scy	 * Unloading - remove the filter rule check from the IP
176254219Scy	 * input/output stream.
177254219Scy	 */
178254219Scy        if (ipf_refcnt)
179254219Scy                error = EBUSY;
180254219Scy	else if (ipf_running >= 0)
181254219Scy		error = ipfdetach();
182254219Scy
183254219Scy	if (error == 0) {
184254219Scy		ipf_running = -2;
185254219Scy		error = ipf_remove();
186254219Scy		printf("%s unloaded\n", ipfilter_version);
187254219Scy	}
188254219Scy	return error;
189254219Scy}
190254219Scy
191254219Scy
192254219Scystatic int ipf_load()
193254219Scy{
194254219Scy	struct nameidata nd;
195254219Scy	struct vattr vattr;
196254219Scy	int error = 0, fmode = S_IFCHR|0600, i;
197254219Scy	char *name;
198254219Scy
199254219Scy	/*
200254219Scy	 * XXX Remove existing device nodes prior to creating new ones
201254219Scy	 * XXX using the assigned LKM device slot's major number.  In a
202254219Scy	 * XXX perfect world we could use the ones specified by cdevsw[].
203254219Scy	 */
204254219Scy	(void)ipf_remove();
205254219Scy
206254219Scy	error = ipfattach();
207254219Scy
208254219Scy	for (i = 0; (error == 0) && (name = ipf_devfiles[i]); i++) {
209254219Scy		NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE, name, curproc);
210254219Scy		if ((error = namei(&nd)))
211254219Scy			break;
212254219Scy		if (nd.ni_vp != NULL) {
213254219Scy			VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
214254219Scy			if (nd.ni_dvp == nd.ni_vp)
215254219Scy				vrele(nd.ni_dvp);
216254219Scy			else
217254219Scy				vput(nd.ni_dvp);
218254219Scy			vrele(nd.ni_vp);
219254219Scy			error = EEXIST;
220254219Scy			break;
221254219Scy		}
222254219Scy		VATTR_NULL(&vattr);
223254219Scy		vattr.va_type = VCHR;
224254219Scy		vattr.va_mode = (fmode & 07777);
225254219Scy		vattr.va_rdev = (ipf_major << 8) | i;
226254219Scy		VOP_LEASE(nd.ni_dvp, curproc, curproc->p_ucred, LEASE_WRITE);
227254219Scy		error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
228254219Scy	}
229254219Scy
230254219Scy	if (error == 0) {
231254219Scy		char *defpass;
232254219Scy
233254219Scy		if (FR_ISPASS(ipf_pass))
234254219Scy			defpass = "pass";
235254219Scy		else if (FR_ISBLOCK(ipf_pass))
236254219Scy			defpass = "block";
237254219Scy		else
238254219Scy			defpass = "no-match -> block";
239254219Scy
240254219Scy		printf("%s initialized.  Default = %s all, Logging = %s%s\n",
241254219Scy			ipfilter_version, defpass,
242254219Scy#ifdef IPFILTER_LOG
243254219Scy			"enabled",
244254219Scy#else
245254219Scy			"disabled",
246254219Scy#endif
247254219Scy#ifdef IPFILTER_COMPILED
248254219Scy			" (COMPILED)"
249254219Scy#else
250254219Scy			""
251254219Scy#endif
252254219Scy			);
253254219Scy		ipf_running = 1;
254254219Scy	}
255254219Scy	return error;
256254219Scy}
257254219Scy
258254219Scy
259254219Scy/*
260254219Scy * routines below for saving IP headers to buffer
261254219Scy */
262254219Scyint
263254219Scyipfopen(dev, flags, devtype, p)
264254219Scy	dev_t dev;
265254219Scy	int flags;
266254219Scy	int devtype;
267254219Scy	struct proc *p;
268254219Scy{
269254219Scy	u_int min = GET_MINOR(dev);
270254219Scy	int error;
271254219Scy
272254219Scy	if (IPL_LOGMAX < min) {
273254219Scy		error = ENXIO;
274254219Scy	} else {
275254219Scy		switch (unit)
276254219Scy		{
277254219Scy		case IPL_LOGIPF :
278254219Scy		case IPL_LOGNAT :
279254219Scy		case IPL_LOGSTATE :
280254219Scy		case IPL_LOGAUTH :
281254219Scy		case IPL_LOGLOOKUP :
282254219Scy		case IPL_LOGSYNC :
283254219Scy#ifdef IPFILTER_SCAN
284254219Scy		case IPL_LOGSCAN :
285254219Scy#endif
286254219Scy			error = 0;
287254219Scy			break;
288254219Scy		default :
289254219Scy			error = ENXIO;
290254219Scy			break;
291254219Scy		}
292254219Scy	}
293254219Scy	return error;
294254219Scy}
295254219Scy
296254219Scy
297254219Scyint
298254219Scyipfclose(dev, flags, devtype, p)
299254219Scy	dev_t dev;
300254219Scy	int flags;
301254219Scy	int devtype;
302254219Scy	struct proc *p;
303254219Scy{
304254219Scy	u_int   min = GET_MINOR(dev);
305254219Scy
306254219Scy	if (IPL_LOGMAX < min)
307254219Scy		min = ENXIO;
308254219Scy	else
309254219Scy		min = 0;
310254219Scy	return min;
311254219Scy}
312254219Scy
313254219Scy
314254219Scy/*
315254219Scy * ipfread/ipflog
316254219Scy * both of these must operate with at least splnet() lest they be
317254219Scy * called during packet processing and cause an inconsistancy to appear in
318254219Scy * the filter lists.
319254219Scy */
320254219Scyint
321254219Scyipfread(dev, uio, ioflag)
322254219Scy	dev_t dev;
323254219Scy	register struct uio *uio;
324254219Scy	int ioflag;
325254219Scy{
326254219Scy
327254219Scy	if (ipf_running < 1)
328254219Scy		return EIO;
329254219Scy
330254219Scy	if (GET_MINOR(dev) == IPL_LOGSYNC)
331254219Scy		return ipfsync_read(uio);
332254219Scy
333254219Scy#ifdef IPFILTER_LOG
334254219Scy	return ipflog_read(GET_MINOR(dev), uio);
335254219Scy#else
336254219Scy	return ENXIO;
337254219Scy#endif
338254219Scy}
339254219Scy
340254219Scy
341254219Scy/*
342254219Scy * ipfwrite
343254219Scy * both of these must operate with at least splnet() lest they be
344254219Scy * called during packet processing and cause an inconsistancy to appear in
345254219Scy * the filter lists.
346254219Scy */
347254219Scyint
348254219Scy#if (BSD >= 199306)
349254219Scyipfwrite(dev, uio, ioflag)
350254219Scy	int ioflag;
351254219Scy#else
352254219Scyipfwrite(dev, uio)
353254219Scy#endif
354254219Scy	dev_t dev;
355254219Scy	register struct uio *uio;
356254219Scy{
357254219Scy
358254219Scy	if (ipf_running < 1)
359254219Scy		return EIO;
360254219Scy
361254219Scy	if (GET_MINOR(dev) == IPL_LOGSYNC)
362254219Scy		return ipfsync_write(uio);
363254219Scy	return ENXIO;
364254219Scy}
365