mln_ipl.c revision 259128
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 * 29/12/94 Added code from Marc Huber <huber@fzi.de> to allow it to allocate
11 * its own major char number! Way cool patch!
12 */
13
14
15#include <sys/param.h>
16
17/*
18 * Post NetBSD 1.2 has the PFIL interface for packet filters.  This turns
19 * on those hooks.  We don't need any special mods with this!
20 */
21#if (defined(NetBSD) && (NetBSD > 199609) && (NetBSD <= 1991011)) || \
22    (defined(NetBSD1_2) && NetBSD1_2 > 1)
23# define NETBSD_PF
24#endif
25
26#include <sys/systm.h>
27#include <sys/conf.h>
28#include <sys/file.h>
29#include <sys/stat.h>
30#include <sys/proc.h>
31#include <sys/uio.h>
32#include <sys/kernel.h>
33#include <sys/vnode.h>
34#include <sys/namei.h>
35#include <sys/malloc.h>
36#include <sys/mount.h>
37#include <sys/exec.h>
38#include <sys/mbuf.h>
39#include <net/if.h>
40#include <netinet/in_systm.h>
41#include <netinet/in.h>
42#include <netinet/ip.h>
43#include <net/route.h>
44#include <netinet/ip_var.h>
45#include <netinet/tcp.h>
46#include <netinet/tcpip.h>
47#include <sys/lkm.h>
48#include <sys/poll.h>
49#include <sys/select.h>
50#include "ipl.h"
51#include "ip_compat.h"
52#include "ip_fil.h"
53#include "ip_auth.h"
54#include "ip_state.h"
55#include "ip_nat.h"
56#include "ip_sync.h"
57
58#if !defined(__NetBSD_Version__) || __NetBSD_Version__ < 103050000
59#define vn_lock(v,f) VOP_LOCK(v)
60#endif
61
62#if !defined(VOP_LEASE) && defined(LEASE_CHECK)
63#define	VOP_LEASE	LEASE_CHECK
64#endif
65
66
67extern	int	lkmenodev __P((void));
68
69#if NetBSD >= 199706
70int	ipflkm_lkmentry __P((struct lkm_table *, int, int));
71#else
72int	xxxinit __P((struct lkm_table *, int, int));
73#endif
74static	int	ipf_unload __P((void));
75static	int	ipf_load __P((void));
76static	int	ipf_remove __P((void));
77static	int	ipfaction __P((struct lkm_table *, int));
78static	char	*ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME,
79				    IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME,
80				    IPLOOKUP_NAME, NULL };
81
82int				ipf_major = 0;
83extern	ipf_main_softc_t	ipfmain;
84extern	const struct cdevsw ipl_cdevsw;
85
86#if defined(__NetBSD__) && (__NetBSD_Version__ >= 106080000)
87MOD_DEV(IPL_VERSION, "ipf", NULL, -1, &ipl_cdevsw, -1);
88#else
89MOD_DEV(IPL_VERSION, LM_DT_CHAR, -1, &ipldevsw);
90#endif
91
92extern int vd_unuseddev __P((void));
93extern struct cdevsw cdevsw[];
94extern int nchrdev;
95
96
97int
98#if NetBSD >= 199706
99ipflkm_lkmentry(lkmtp, cmd, ver)
100#else
101xxxinit(lkmtp, cmd, ver)
102#endif
103	struct lkm_table *lkmtp;
104	int cmd, ver;
105{
106	DISPATCH(lkmtp, cmd, ver, ipfaction, ipfaction, ipfaction);
107}
108
109
110static int
111ipfaction(lkmtp, cmd)
112	struct lkm_table *lkmtp;
113	int cmd;
114{
115#if !defined(__NetBSD__) || (__NetBSD_Version__ < 106080000)
116	int i;
117#endif
118	struct lkm_dev *args = lkmtp->private.lkm_dev;
119	int err = 0;
120
121	switch (cmd)
122	{
123	case LKM_E_LOAD :
124		if (lkmexists(lkmtp))
125			return EEXIST;
126
127#if defined(__NetBSD__) && (__NetBSD_Version__ >= 106080000)
128# if (__NetBSD_Version__ < 200000000)
129		err = devsw_attach(args->lkm_devname,
130				   args->lkm_bdev, &args->lkm_bdevmaj,
131				   args->lkm_cdev, &args->lkm_cdevmaj);
132		if (err != 0)
133			return (err);
134# endif
135		ipf_major = args->lkm_cdevmaj;
136#else
137		for (i = 0; i < nchrdev; i++)
138			if (cdevsw[i].d_open == (dev_type_open((*)))lkmenodev ||
139			    cdevsw[i].d_open == ipfopen)
140				break;
141		if (i == nchrdev) {
142			printf("IP Filter: No free cdevsw slots\n");
143			return ENODEV;
144		}
145
146		ipf_major = i;
147		args->lkm_offset = i;   /* slot in cdevsw[] */
148#endif
149		printf("IP Filter: loaded into slot %d\n", ipf_major);
150		return ipf_load();
151	case LKM_E_UNLOAD :
152#if defined(__NetBSD__) && (__NetBSD_Version__ >= 106080000)
153		devsw_detach(args->lkm_bdev, args->lkm_cdev);
154		args->lkm_bdevmaj = -1;
155		args->lkm_cdevmaj = -1;
156#endif
157		err = ipf_unload();
158		if (!err)
159			printf("IP Filter: unloaded from slot %d\n",
160			       ipf_major);
161		break;
162	case LKM_E_STAT :
163		break;
164	default:
165		err = EIO;
166		break;
167	}
168	return err;
169}
170
171
172static int
173ipf_remove()
174{
175	char *name;
176	struct nameidata nd;
177	int error, i;
178
179        for (i = 0; (name = ipf_devfiles[i]); i++) {
180#if (__NetBSD_Version__ > 106009999)
181# if (__NetBSD_Version__ > 399001400)
182#  if (__NetBSD_Version__ > 499001400)
183		NDINIT(&nd, DELETE, LOCKPARENT|LOCKLEAF, UIO_SYSSPACE,
184		       name);
185#  else
186		NDINIT(&nd, DELETE, LOCKPARENT|LOCKLEAF, UIO_SYSSPACE,
187		       name, curlwp);
188#  endif
189# else
190		NDINIT(&nd, DELETE, LOCKPARENT|LOCKLEAF, UIO_SYSSPACE,
191		       name, curproc);
192# endif
193#else
194		NDINIT(&nd, DELETE, LOCKPARENT, UIO_SYSSPACE, name, curproc);
195#endif
196		if ((error = namei(&nd)))
197			return (error);
198#if (__NetBSD_Version__ > 399001400)
199# if (__NetBSD_Version__ > 399002000)
200#  if (__NetBSD_Version__ < 499001400)
201		VOP_LEASE(nd.ni_dvp, curlwp, curlwp->l_cred, LEASE_WRITE);
202#  endif
203# else
204		VOP_LEASE(nd.ni_dvp, curlwp, curlwp->l_proc->p_ucred, LEASE_WRITE);
205# endif
206#else
207		VOP_LEASE(nd.ni_dvp, curproc, curproc->p_ucred, LEASE_WRITE);
208#endif
209#if !defined(__NetBSD_Version__) || (__NetBSD_Version__ < 106000000)
210		vn_lock(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY);
211#endif
212#if (__NetBSD_Version__ >= 399002000)
213# if (__NetBSD_Version__ < 499001400)
214		VOP_LEASE(nd.ni_vp, curlwp, curlwp->l_cred, LEASE_WRITE);
215# endif
216#else
217# if (__NetBSD_Version__ > 399001400)
218		VOP_LEASE(nd.ni_vp, curlwp, curlwp->l_proc->p_ucred, LEASE_WRITE);
219# else
220		VOP_LEASE(nd.ni_vp, curproc, curproc->p_ucred, LEASE_WRITE);
221# endif
222#endif
223		(void) VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
224	}
225	return 0;
226}
227
228
229static int
230ipf_unload()
231{
232	int error = 0;
233
234	/*
235	 * Unloading - remove the filter rule check from the IP
236	 * input/output stream.
237	 */
238	if (ipfmain.ipf_refcnt)
239		error = EBUSY;
240	else if (ipfmain.ipf_running >= 0) {
241		error = ipfdetach(&ipfmain);
242		if (error == 0) {
243			ipf_destroy_all(&ipfmain);
244			ipf_unload_all();
245		}
246	}
247
248	if (error == 0) {
249		ipfmain.ipf_running = -2;
250		error = ipf_remove();
251		printf("%s unloaded\n", ipfilter_version);
252	}
253	return error;
254}
255
256
257static int
258ipf_load()
259{
260	struct nameidata nd;
261	struct vattr vattr;
262	int error = 0, fmode = S_IFCHR|0600, i;
263	char *name;
264
265	/*
266	 * XXX Remove existing device nodes prior to creating new ones
267	 * XXX using the assigned LKM device slot's major number.  In a
268	 * XXX perfect world we could use the ones specified by cdevsw[].
269	 */
270	(void)ipf_remove();
271
272	bzero((char *)&ipfmain, sizeof(ipfmain));
273        error = ipf_load_all();
274	if (error != 0)
275		return error;
276	if (ipf_create_all(&ipfmain) == NULL) {
277		ipf_unload_all();
278		return EIO;
279	}
280
281	error = ipfattach(&ipfmain);
282	if (error != 0) {
283		(void) ipf_unload();
284		return error;
285	}
286
287	for (i = 0; (error == 0) && (name = ipf_devfiles[i]); i++) {
288#if (__NetBSD_Version__ > 399001400)
289# if (__NetBSD_Version__ > 499001400)
290		NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE, name);
291# else
292		NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE, name, curlwp);
293# endif
294#else
295		NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE, name, curproc);
296#endif
297		if ((error = namei(&nd)))
298			break;
299		if (nd.ni_vp != NULL) {
300			VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
301			if (nd.ni_dvp == nd.ni_vp)
302				vrele(nd.ni_dvp);
303			else
304				vput(nd.ni_dvp);
305			vrele(nd.ni_vp);
306			error = EEXIST;
307			break;
308		}
309		VATTR_NULL(&vattr);
310		vattr.va_type = VCHR;
311		vattr.va_mode = (fmode & 07777);
312		vattr.va_rdev = (ipf_major << 8) | i;
313#if (__NetBSD_Version__ > 399001400)
314# if (__NetBSD_Version__ >= 399002000)
315#  if (__NetBSD_Version__ < 499001400)
316		VOP_LEASE(nd.ni_dvp, curlwp, curlwp->l_cred, LEASE_WRITE);
317#  endif
318# else
319		VOP_LEASE(nd.ni_dvp, curlwp, curlwp->l_proc->p_ucred, LEASE_WRITE);
320# endif
321#else
322		VOP_LEASE(nd.ni_dvp, curproc, curproc->p_ucred, LEASE_WRITE);
323#endif
324		error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
325		if (error == 0)
326			vput(nd.ni_vp);
327	}
328
329	if (error == 0) {
330		char *defpass;
331
332		if (FR_ISPASS(ipfmain.ipf_pass))
333			defpass = "pass";
334		else if (FR_ISBLOCK(ipfmain.ipf_pass))
335			defpass = "block";
336		else
337			defpass = "no-match -> block";
338
339		printf("%s initialized.  Default = %s all, Logging = %s%s\n",
340			ipfilter_version, defpass,
341#ifdef IPFILTER_LOG
342			"enabled",
343#else
344			"disabled",
345#endif
346#ifdef IPFILTER_COMPILED
347			" (COMPILED)"
348#else
349			""
350#endif
351			);
352		ipfmain.ipf_running = 1;
353	}
354	return error;
355}
356