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