1/* $FreeBSD$ */
2
3/*
4 * Copyright (C) 2012 by Darren Reed.
5 *
6 * See the IPFILTER.LICENCE file for details on licencing.
7 *
8 */
9
10#include <sys/param.h>
11#include <sys/systm.h>
12#include <sys/conf.h>
13#include <sys/file.h>
14#include <sys/stat.h>
15#include <sys/proc.h>
16#include <sys/uio.h>
17#include <sys/kernel.h>
18#include <sys/vnode.h>
19#include <sys/namei.h>
20#include <sys/malloc.h>
21#include <sys/mount.h>
22#include <sys/exec.h>
23#include <sys/mbuf.h>
24#include <net/if.h>
25#include <netinet/in_systm.h>
26#include <netinet/in.h>
27#include <netinet/ip.h>
28#include <net/route.h>
29#include <netinet/ip_var.h>
30#include <netinet/tcp.h>
31#include <netinet/tcpip.h>
32#include <sys/lkm.h>
33#include "ipl.h"
34#include "ip_compat.h"
35#include "ip_fil.h"
36
37#define vn_lock(v,f) VOP_LOCK(v)
38
39#if !defined(VOP_LEASE) && defined(LEASE_CHECK)
40#define	VOP_LEASE	LEASE_CHECK
41#endif
42
43
44extern	int	lkmenodev __P((void));
45
46#if OpenBSD >= 200311
47int	if_ipf_lkmentry __P((struct lkm_table *, int, int));
48#else
49int	if_ipf __P((struct lkm_table *, int, int));
50#endif
51static	int	ipf_unload __P((void));
52static	int	ipf_load __P((void));
53static	int	ipf_remove __P((void));
54static	int	ipfaction __P((struct lkm_table *, int));
55static	char	*ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME,
56				    IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME,
57				    IPLOOKUP_NAME, NULL };
58
59
60struct	cdevsw	ipfdevsw =
61{
62	ipfopen,		/* open */
63	ipfclose,		/* close */
64	ipfread,		/* read */
65	(void *)nullop,		/* write */
66	ipfioctl,		/* ioctl */
67	(void *)nullop,		/* stop */
68	(void *)NULL,		/* tty */
69	(void *)nullop,		/* select */
70	(void *)nullop,		/* mmap */
71	NULL			/* strategy */
72};
73
74int	ipf_major = 0;
75
76MOD_DEV(IPL_VERSION, LM_DT_CHAR, -1, &ipfdevsw);
77
78extern int vd_unuseddev __P((void));
79extern struct cdevsw cdevsw[];
80extern int nchrdev;
81
82
83#if OpenBSD >= 200311
84int if_ipf_lkmentry (lkmtp, cmd, ver)
85#else
86int if_ipf(lkmtp, cmd, ver)
87#endif
88	struct lkm_table *lkmtp;
89	int cmd, ver;
90{
91	DISPATCH(lkmtp, cmd, ver, ipfaction, ipfaction, ipfaction);
92}
93
94int lkmexists __P((struct lkm_table *)); /* defined in /sys/kern/kern_lkm.c */
95
96static int ipfaction(lkmtp, cmd)
97	struct lkm_table *lkmtp;
98	int cmd;
99{
100	int i;
101	struct lkm_dev *args = lkmtp->private.lkm_dev;
102	int err = 0;
103
104	switch (cmd)
105	{
106	case LKM_E_LOAD :
107		if (lkmexists(lkmtp))
108			return EEXIST;
109
110		for (i = 0; i < nchrdev; i++)
111			if (cdevsw[i].d_open == (dev_type_open((*)))lkmenodev ||
112			    cdevsw[i].d_open == ipfopen)
113				break;
114		if (i == nchrdev) {
115			printf("IP Filter: No free cdevsw slots\n");
116			return ENODEV;
117		}
118
119		ipf_major = i;
120		args->lkm_offset = i;   /* slot in cdevsw[] */
121		printf("IP Filter: loaded into slot %d\n", ipf_major);
122		return ipf_load();
123	case LKM_E_UNLOAD :
124		err = ipf_unload();
125		if (!err)
126			printf("IP Filter: unloaded from slot %d\n",
127			       ipf_major);
128		break;
129	case LKM_E_STAT :
130		break;
131	default:
132		err = EIO;
133		break;
134	}
135	return err;
136}
137
138
139static int ipf_remove()
140{
141	struct nameidata nd;
142	int error, i;
143	char *name;
144
145        for (i = 0; (name = ipf_devfiles[i]); i++) {
146#if OpenBSD >= 200311
147		NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE,
148		       name, curproc);
149#else
150		NDINIT(&nd, DELETE, LOCKPARENT, UIO_SYSSPACE, name, curproc);
151#endif
152		if ((error = namei(&nd)))
153			return (error);
154		VOP_LEASE(nd.ni_vp, curproc, curproc->p_ucred, LEASE_WRITE);
155#if OpenBSD < 200311
156		VOP_LOCK(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY, curproc);
157		VOP_LEASE(nd.ni_dvp, curproc, curproc->p_ucred, LEASE_WRITE);
158#else
159		(void)uvm_vnp_uncache(nd.ni_vp);
160
161		VOP_LEASE(nd.ni_dvp, curproc, curproc->p_ucred, LEASE_WRITE);
162		VOP_LEASE(nd.ni_vp, curproc, curproc->p_ucred, LEASE_WRITE);
163#endif
164		(void) VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
165	}
166	return 0;
167}
168
169
170static int ipf_unload()
171{
172	int error = 0;
173
174	/*
175	 * Unloading - remove the filter rule check from the IP
176	 * input/output stream.
177	 */
178        if (ipf_refcnt)
179                error = EBUSY;
180	else if (ipf_running >= 0)
181		error = ipfdetach();
182
183	if (error == 0) {
184		ipf_running = -2;
185		error = ipf_remove();
186		printf("%s unloaded\n", ipfilter_version);
187	}
188	return error;
189}
190
191
192static int ipf_load()
193{
194	struct nameidata nd;
195	struct vattr vattr;
196	int error = 0, fmode = S_IFCHR|0600, i;
197	char *name;
198
199	/*
200	 * XXX Remove existing device nodes prior to creating new ones
201	 * XXX using the assigned LKM device slot's major number.  In a
202	 * XXX perfect world we could use the ones specified by cdevsw[].
203	 */
204	(void)ipf_remove();
205
206	error = ipfattach();
207
208	for (i = 0; (error == 0) && (name = ipf_devfiles[i]); i++) {
209		NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE, name, curproc);
210		if ((error = namei(&nd)))
211			break;
212		if (nd.ni_vp != NULL) {
213			VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
214			if (nd.ni_dvp == nd.ni_vp)
215				vrele(nd.ni_dvp);
216			else
217				vput(nd.ni_dvp);
218			vrele(nd.ni_vp);
219			error = EEXIST;
220			break;
221		}
222		VATTR_NULL(&vattr);
223		vattr.va_type = VCHR;
224		vattr.va_mode = (fmode & 07777);
225		vattr.va_rdev = (ipf_major << 8) | i;
226		VOP_LEASE(nd.ni_dvp, curproc, curproc->p_ucred, LEASE_WRITE);
227		error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
228	}
229
230	if (error == 0) {
231		char *defpass;
232
233		if (FR_ISPASS(ipf_pass))
234			defpass = "pass";
235		else if (FR_ISBLOCK(ipf_pass))
236			defpass = "block";
237		else
238			defpass = "no-match -> block";
239
240		printf("%s initialized.  Default = %s all, Logging = %s%s\n",
241			ipfilter_version, defpass,
242#ifdef IPFILTER_LOG
243			"enabled",
244#else
245			"disabled",
246#endif
247#ifdef IPFILTER_COMPILED
248			" (COMPILED)"
249#else
250			""
251#endif
252			);
253		ipf_running = 1;
254	}
255	return error;
256}
257
258
259/*
260 * routines below for saving IP headers to buffer
261 */
262int
263ipfopen(dev, flags, devtype, p)
264	dev_t dev;
265	int flags;
266	int devtype;
267	struct proc *p;
268{
269	u_int min = GET_MINOR(dev);
270	int error;
271
272	if (IPL_LOGMAX < min) {
273		error = ENXIO;
274	} else {
275		switch (unit)
276		{
277		case IPL_LOGIPF :
278		case IPL_LOGNAT :
279		case IPL_LOGSTATE :
280		case IPL_LOGAUTH :
281		case IPL_LOGLOOKUP :
282		case IPL_LOGSYNC :
283#ifdef IPFILTER_SCAN
284		case IPL_LOGSCAN :
285#endif
286			error = 0;
287			break;
288		default :
289			error = ENXIO;
290			break;
291		}
292	}
293	return error;
294}
295
296
297int
298ipfclose(dev, flags, devtype, p)
299	dev_t dev;
300	int flags;
301	int devtype;
302	struct proc *p;
303{
304	u_int   min = GET_MINOR(dev);
305
306	if (IPL_LOGMAX < min)
307		min = ENXIO;
308	else
309		min = 0;
310	return min;
311}
312
313
314/*
315 * ipfread/ipflog
316 * both of these must operate with at least splnet() lest they be
317 * called during packet processing and cause an inconsistancy to appear in
318 * the filter lists.
319 */
320int
321ipfread(dev, uio, ioflag)
322	dev_t dev;
323	register struct uio *uio;
324	int ioflag;
325{
326
327	if (ipf_running < 1)
328		return EIO;
329
330	if (GET_MINOR(dev) == IPL_LOGSYNC)
331		return ipfsync_read(uio);
332
333#ifdef IPFILTER_LOG
334	return ipflog_read(GET_MINOR(dev), uio);
335#else
336	return ENXIO;
337#endif
338}
339
340
341/*
342 * ipfwrite
343 * both of these must operate with at least splnet() lest they be
344 * called during packet processing and cause an inconsistancy to appear in
345 * the filter lists.
346 */
347int
348#if (BSD >= 199306)
349ipfwrite(dev, uio, ioflag)
350	int ioflag;
351#else
352ipfwrite(dev, uio)
353#endif
354	dev_t dev;
355	register struct uio *uio;
356{
357
358	if (ipf_running < 1)
359		return EIO;
360
361	if (GET_MINOR(dev) == IPL_LOGSYNC)
362		return ipfsync_write(uio);
363	return ENXIO;
364}
365