ip_auth.c revision 145522
1145522Sdarrenr/* $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 145522 2005-04-25 18:43:14Z darrenr $ */ 2145522Sdarrenr 353642Sguido/* 4145522Sdarrenr * Copyright (C) 1998-2003 by Darren Reed & Guido van Rooij. 553642Sguido * 680482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 753642Sguido */ 8145522Sdarrenr#if defined(KERNEL) || defined(_KERNEL) 9145522Sdarrenr# undef KERNEL 10145522Sdarrenr# undef _KERNEL 11145522Sdarrenr# define KERNEL 1 12145522Sdarrenr# define _KERNEL 1 1392685Sdarrenr#endif 1453642Sguido#include <sys/errno.h> 1553642Sguido#include <sys/types.h> 1653642Sguido#include <sys/param.h> 1753642Sguido#include <sys/time.h> 1853642Sguido#include <sys/file.h> 19145522Sdarrenr#if !defined(_KERNEL) 2053642Sguido# include <stdio.h> 2153642Sguido# include <stdlib.h> 2253642Sguido# include <string.h> 23145522Sdarrenr# define _KERNEL 24145522Sdarrenr# ifdef __OpenBSD__ 25145522Sdarrenrstruct file; 26145522Sdarrenr# endif 27145522Sdarrenr# include <sys/uio.h> 28145522Sdarrenr# undef _KERNEL 2953642Sguido#endif 30145522Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 220000) 3153642Sguido# include <sys/filio.h> 3253642Sguido# include <sys/fcntl.h> 3353642Sguido#else 3453642Sguido# include <sys/ioctl.h> 3553642Sguido#endif 36145522Sdarrenr#if !defined(linux) 3753642Sguido# include <sys/protosw.h> 3853642Sguido#endif 3953642Sguido#include <sys/socket.h> 40145522Sdarrenr#if defined(_KERNEL) 4153642Sguido# include <sys/systm.h> 42145522Sdarrenr# if !defined(__SVR4) && !defined(__svr4__) && !defined(linux) 4353642Sguido# include <sys/mbuf.h> 4453642Sguido# endif 45145522Sdarrenr#endif 46145522Sdarrenr#if defined(__SVR4) || defined(__svr4__) 4753642Sguido# include <sys/filio.h> 4853642Sguido# include <sys/byteorder.h> 4953642Sguido# ifdef _KERNEL 5053642Sguido# include <sys/dditypes.h> 5153642Sguido# endif 5253642Sguido# include <sys/stream.h> 5353642Sguido# include <sys/kmem.h> 5453642Sguido#endif 5564105Sroberto#if (_BSDI_VERSION >= 199802) || (__FreeBSD_version >= 400000) 5653642Sguido# include <sys/queue.h> 5753642Sguido#endif 5853642Sguido#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi) 5953642Sguido# include <machine/cpu.h> 6053642Sguido#endif 61145522Sdarrenr#if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) 62145522Sdarrenr# include <sys/proc.h> 63145522Sdarrenr#endif 6453642Sguido#include <net/if.h> 6553642Sguido#ifdef sun 6653642Sguido# include <net/af.h> 6753642Sguido#endif 6853642Sguido#include <net/route.h> 6953642Sguido#include <netinet/in.h> 7053642Sguido#include <netinet/in_systm.h> 7153642Sguido#include <netinet/ip.h> 72145522Sdarrenr#if !defined(_KERNEL) && !defined(__osf__) && !defined(__sgi) 7353642Sguido# define KERNEL 74145522Sdarrenr# define _KERNEL 7553642Sguido# define NOT_KERNEL 7653642Sguido#endif 77145522Sdarrenr#if !defined(linux) 7853642Sguido# include <netinet/ip_var.h> 7953642Sguido#endif 8053642Sguido#ifdef NOT_KERNEL 81145522Sdarrenr# undef _KERNEL 8253642Sguido# undef KERNEL 8353642Sguido#endif 8453642Sguido#include <netinet/tcp.h> 85145522Sdarrenr#if defined(IRIX) && (IRIX < 60516) /* IRIX < 6 */ 8680482Sdarrenrextern struct ifqueue ipintrq; /* ip packet input queue */ 8753642Sguido#else 88145522Sdarrenr# if !defined(__hpux) && !defined(linux) 8953642Sguido# if __FreeBSD_version >= 300000 9053642Sguido# include <net/if_var.h> 91145522Sdarrenr# if __FreeBSD_version >= 500042 92145522Sdarrenr# define IF_QFULL _IF_QFULL 93145522Sdarrenr# define IF_DROP _IF_DROP 94145522Sdarrenr# endif /* __FreeBSD_version >= 500042 */ 9553642Sguido# endif 9653642Sguido# include <netinet/in_var.h> 9753642Sguido# include <netinet/tcp_fsm.h> 9853642Sguido# endif 9953642Sguido#endif 10053642Sguido#include <netinet/udp.h> 10153642Sguido#include <netinet/ip_icmp.h> 10253642Sguido#include "netinet/ip_compat.h" 10353642Sguido#include <netinet/tcpip.h> 10453642Sguido#include "netinet/ip_fil.h" 10553642Sguido#include "netinet/ip_auth.h" 106145522Sdarrenr#if !defined(MENTAT) && !defined(linux) 10753642Sguido# include <net/netisr.h> 10853642Sguido# ifdef __FreeBSD__ 10953642Sguido# include <machine/cpufunc.h> 11053642Sguido# endif 11153642Sguido#endif 11253642Sguido#if (__FreeBSD_version >= 300000) 11353642Sguido# include <sys/malloc.h> 114145522Sdarrenr# if defined(_KERNEL) && !defined(IPFILTER_LKM) 11553642Sguido# include <sys/libkern.h> 11653642Sguido# include <sys/systm.h> 11753642Sguido# endif 11853642Sguido#endif 119145522Sdarrenr/* END OF INCLUDES */ 12053642Sguido 12180482Sdarrenr#if !defined(lint) 12280482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 145522 2005-04-25 18:43:14Z darrenr $"; 123145522Sdarrenrstatic const char rcsid[] = "@(#)Id: ip_auth.c,v 2.73.2.3 2004/08/26 11:25:21 darrenr Exp"; 12480482Sdarrenr#endif 12553642Sguido 12653642Sguido 127145522Sdarrenr#if SOLARIS 12853642Sguidoextern kcondvar_t ipfauthwait; 129145522Sdarrenr#endif /* SOLARIS */ 130145522Sdarrenr#if defined(linux) && defined(_KERNEL) 131145522Sdarrenrwait_queue_head_t fr_authnext_linux; 13253642Sguido#endif 13353642Sguido 13453642Sguidoint fr_authsize = FR_NUMAUTH; 13553642Sguidoint fr_authused = 0; 13653642Sguidoint fr_defaultauthage = 600; 13760850Sdarrenrint fr_auth_lock = 0; 138145522Sdarrenrint fr_auth_init = 0; 13953642Sguidofr_authstat_t fr_authstats; 140145522Sdarrenrstatic frauth_t *fr_auth = NULL; 141145522Sdarrenrmb_t **fr_authpkts = NULL; 142145522Sdarrenrint fr_authstart = 0, fr_authend = 0, fr_authnext = 0; 143145522Sdarrenrfrauthent_t *fae_list = NULL; 14480482Sdarrenrfrentry_t *ipauth = NULL, 14580482Sdarrenr *fr_authlist = NULL; 14653642Sguido 14753642Sguido 148145522Sdarrenrint fr_authinit() 149145522Sdarrenr{ 150145522Sdarrenr KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth)); 151145522Sdarrenr if (fr_auth != NULL) 152145522Sdarrenr bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth)); 153145522Sdarrenr else 154145522Sdarrenr return -1; 155145522Sdarrenr 156145522Sdarrenr KMALLOCS(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts)); 157145522Sdarrenr if (fr_authpkts != NULL) 158145522Sdarrenr bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts)); 159145522Sdarrenr else 160145522Sdarrenr return -2; 161145522Sdarrenr 162145522Sdarrenr MUTEX_INIT(&ipf_authmx, "ipf auth log mutex"); 163145522Sdarrenr RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock"); 164145522Sdarrenr#if SOLARIS && defined(_KERNEL) 165145522Sdarrenr cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL); 166145522Sdarrenr#endif 167145522Sdarrenr#if defined(linux) && defined(_KERNEL) 168145522Sdarrenr init_waitqueue_head(&fr_authnext_linux); 169145522Sdarrenr#endif 170145522Sdarrenr 171145522Sdarrenr fr_auth_init = 1; 172145522Sdarrenr 173145522Sdarrenr return 0; 174145522Sdarrenr} 175145522Sdarrenr 176145522Sdarrenr 17753642Sguido/* 17853642Sguido * Check if a packet has authorization. If the packet is found to match an 17953642Sguido * authorization result and that would result in a feedback loop (i.e. it 18053642Sguido * will end up returning FR_AUTH) then return FR_BLOCK instead. 18153642Sguido */ 182145522Sdarrenrfrentry_t *fr_checkauth(fin, passp) 18353642Sguidofr_info_t *fin; 184145522Sdarrenru_32_t *passp; 18553642Sguido{ 18680482Sdarrenr frentry_t *fr; 18780482Sdarrenr frauth_t *fra; 18853642Sguido u_32_t pass; 189145522Sdarrenr u_short id; 190145522Sdarrenr ip_t *ip; 19153642Sguido int i; 19253642Sguido 19380482Sdarrenr if (fr_auth_lock || !fr_authused) 194145522Sdarrenr return NULL; 19560850Sdarrenr 196145522Sdarrenr ip = fin->fin_ip; 197145522Sdarrenr id = ip->ip_id; 198145522Sdarrenr 19953642Sguido READ_ENTER(&ipf_auth); 20053642Sguido for (i = fr_authstart; i != fr_authend; ) { 20153642Sguido /* 20253642Sguido * index becomes -2 only after an SIOCAUTHW. Check this in 20353642Sguido * case the same packet gets sent again and it hasn't yet been 20453642Sguido * auth'd. 20553642Sguido */ 20680482Sdarrenr fra = fr_auth + i; 20780482Sdarrenr if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) && 20880482Sdarrenr !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) { 20953642Sguido /* 21053642Sguido * Avoid feedback loop. 21153642Sguido */ 212145522Sdarrenr if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass))) 21353642Sguido pass = FR_BLOCK; 21480482Sdarrenr /* 21580482Sdarrenr * Create a dummy rule for the stateful checking to 21680482Sdarrenr * use and return. Zero out any values we don't 21780482Sdarrenr * trust from userland! 21880482Sdarrenr */ 21980482Sdarrenr if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) && 220145522Sdarrenr (fin->fin_flx & FI_FRAG))) { 22180482Sdarrenr KMALLOC(fr, frentry_t *); 22280482Sdarrenr if (fr) { 22380482Sdarrenr bcopy((char *)fra->fra_info.fin_fr, 224145522Sdarrenr (char *)fr, sizeof(*fr)); 22580482Sdarrenr fr->fr_grp = NULL; 22680482Sdarrenr fr->fr_ifa = fin->fin_ifp; 22780482Sdarrenr fr->fr_func = NULL; 22880482Sdarrenr fr->fr_ref = 1; 22980482Sdarrenr fr->fr_flags = pass; 230145522Sdarrenr fr->fr_ifas[1] = NULL; 231145522Sdarrenr fr->fr_ifas[2] = NULL; 232145522Sdarrenr fr->fr_ifas[3] = NULL; 23380482Sdarrenr } 23480482Sdarrenr } else 23580482Sdarrenr fr = fra->fra_info.fin_fr; 23680482Sdarrenr fin->fin_fr = fr; 23753642Sguido RWLOCK_EXIT(&ipf_auth); 23853642Sguido WRITE_ENTER(&ipf_auth); 239145522Sdarrenr if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) { 24080482Sdarrenr fr->fr_next = fr_authlist; 24180482Sdarrenr fr_authlist = fr; 24280482Sdarrenr } 24353642Sguido fr_authstats.fas_hits++; 24480482Sdarrenr fra->fra_index = -1; 24553642Sguido fr_authused--; 24653642Sguido if (i == fr_authstart) { 24780482Sdarrenr while (fra->fra_index == -1) { 24853642Sguido i++; 24980482Sdarrenr fra++; 250145522Sdarrenr if (i == fr_authsize) { 25153642Sguido i = 0; 25280482Sdarrenr fra = fr_auth; 25380482Sdarrenr } 25453642Sguido fr_authstart = i; 25553642Sguido if (i == fr_authend) 25653642Sguido break; 25753642Sguido } 25853642Sguido if (fr_authstart == fr_authend) { 25953642Sguido fr_authnext = 0; 26053642Sguido fr_authstart = fr_authend = 0; 26153642Sguido } 26253642Sguido } 26353642Sguido RWLOCK_EXIT(&ipf_auth); 264145522Sdarrenr if (passp != NULL) 265145522Sdarrenr *passp = pass; 266145522Sdarrenr ATOMIC_INC64(fr_authstats.fas_hits); 267145522Sdarrenr return fr; 26853642Sguido } 26953642Sguido i++; 270145522Sdarrenr if (i == fr_authsize) 27153642Sguido i = 0; 27253642Sguido } 27353642Sguido fr_authstats.fas_miss++; 27453642Sguido RWLOCK_EXIT(&ipf_auth); 275145522Sdarrenr ATOMIC_INC64(fr_authstats.fas_miss); 276145522Sdarrenr return NULL; 27753642Sguido} 27853642Sguido 27953642Sguido 28053642Sguido/* 28153642Sguido * Check if we have room in the auth array to hold details for another packet. 28253642Sguido * If we do, store it and wake up any user programs which are waiting to 28353642Sguido * hear about these events. 28453642Sguido */ 285145522Sdarrenrint fr_newauth(m, fin) 28653642Sguidomb_t *m; 28753642Sguidofr_info_t *fin; 28853642Sguido{ 289145522Sdarrenr#if defined(_KERNEL) && defined(MENTAT) 290145522Sdarrenr qpktinfo_t *qpi = fin->fin_qpi; 29160850Sdarrenr#endif 29280482Sdarrenr frauth_t *fra; 293145522Sdarrenr#if !defined(sparc) && !defined(m68k) 294145522Sdarrenr ip_t *ip; 295145522Sdarrenr#endif 29653642Sguido int i; 29753642Sguido 29860850Sdarrenr if (fr_auth_lock) 29960850Sdarrenr return 0; 30060850Sdarrenr 30153642Sguido WRITE_ENTER(&ipf_auth); 30253642Sguido if (fr_authstart > fr_authend) { 30353642Sguido fr_authstats.fas_nospace++; 30453642Sguido RWLOCK_EXIT(&ipf_auth); 30553642Sguido return 0; 30653642Sguido } else { 307145522Sdarrenr if (fr_authused == fr_authsize) { 30853642Sguido fr_authstats.fas_nospace++; 30953642Sguido RWLOCK_EXIT(&ipf_auth); 31053642Sguido return 0; 31153642Sguido } 31253642Sguido } 31353642Sguido 31453642Sguido fr_authstats.fas_added++; 31553642Sguido fr_authused++; 31653642Sguido i = fr_authend++; 317145522Sdarrenr if (fr_authend == fr_authsize) 31853642Sguido fr_authend = 0; 31953642Sguido RWLOCK_EXIT(&ipf_auth); 320145522Sdarrenr 32180482Sdarrenr fra = fr_auth + i; 32280482Sdarrenr fra->fra_index = i; 32380482Sdarrenr fra->fra_pass = 0; 32480482Sdarrenr fra->fra_age = fr_defaultauthage; 32580482Sdarrenr bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin)); 326145522Sdarrenr#if !defined(sparc) && !defined(m68k) 32753642Sguido /* 32853642Sguido * No need to copyback here as we want to undo the changes, not keep 32953642Sguido * them. 33053642Sguido */ 331145522Sdarrenr ip = fin->fin_ip; 332145522Sdarrenr# if defined(MENTAT) && defined(_KERNEL) 333145522Sdarrenr if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4)) 334145522Sdarrenr# endif 33553642Sguido { 33653642Sguido register u_short bo; 33753642Sguido 33853642Sguido bo = ip->ip_len; 33953642Sguido ip->ip_len = htons(bo); 34053642Sguido bo = ip->ip_off; 34153642Sguido ip->ip_off = htons(bo); 34253642Sguido } 343145522Sdarrenr#endif 344145522Sdarrenr#if SOLARIS && defined(_KERNEL) 345145522Sdarrenr m->b_rptr -= qpi->qpi_off; 34653642Sguido fr_authpkts[i] = *(mblk_t **)fin->fin_mp; 347145522Sdarrenr fra->fra_q = qpi->qpi_q; /* The queue can disappear! */ 34853642Sguido cv_signal(&ipfauthwait); 34953642Sguido#else 35080482Sdarrenr# if defined(BSD) && !defined(sparc) && (BSD >= 199306) 351145522Sdarrenr if (!fin->fin_out) { 352110915Sdarrenr ip->ip_len = htons(ip->ip_len); 353110915Sdarrenr ip->ip_off = htons(ip->ip_off); 35480482Sdarrenr } 35580482Sdarrenr# endif 35653642Sguido fr_authpkts[i] = m; 357145522Sdarrenr WAKEUP(&fr_authnext,0); 35853642Sguido#endif 35953642Sguido return 1; 36053642Sguido} 36153642Sguido 36253642Sguido 363145522Sdarrenrint fr_auth_ioctl(data, cmd, mode) 36453642Sguidocaddr_t data; 365145522Sdarrenrioctlcmd_t cmd; 36680482Sdarrenrint mode; 36753642Sguido{ 36853642Sguido mb_t *m; 369145522Sdarrenr#if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \ 370145522Sdarrenr (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000)) 371130886Sdarrenr struct ifqueue *ifq; 372145522Sdarrenr# ifdef USE_SPL 37380482Sdarrenr int s; 374145522Sdarrenr# endif /* USE_SPL */ 37580482Sdarrenr#endif 37680482Sdarrenr frauth_t auth, *au = &auth, *fra; 377145522Sdarrenr int i, error = 0, len; 378145522Sdarrenr char *t; 37953642Sguido 38053642Sguido switch (cmd) 38153642Sguido { 38260850Sdarrenr case SIOCSTLCK : 383110915Sdarrenr if (!(mode & FWRITE)) { 384110915Sdarrenr error = EPERM; 385110915Sdarrenr break; 386110915Sdarrenr } 387145522Sdarrenr fr_lock(data, &fr_auth_lock); 38860850Sdarrenr break; 389145522Sdarrenr 39053642Sguido case SIOCATHST: 39153642Sguido fr_authstats.fas_faelist = fae_list; 392145522Sdarrenr error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT); 39353642Sguido break; 394145522Sdarrenr 395145522Sdarrenr case SIOCIPFFL: 396145522Sdarrenr SPL_NET(s); 397145522Sdarrenr WRITE_ENTER(&ipf_auth); 398145522Sdarrenr i = fr_authflush(); 399145522Sdarrenr RWLOCK_EXIT(&ipf_auth); 400145522Sdarrenr SPL_X(s); 401145522Sdarrenr error = copyoutptr((char *)&i, data, sizeof(i)); 402145522Sdarrenr break; 403145522Sdarrenr 40453642Sguido case SIOCAUTHW: 40553642Sguidofr_authioctlloop: 406145522Sdarrenr error = fr_inobj(data, au, IPFOBJ_FRAUTH); 40753642Sguido READ_ENTER(&ipf_auth); 40853642Sguido if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) { 409145522Sdarrenr error = fr_outobj(data, &fr_auth[fr_authnext], 410145522Sdarrenr IPFOBJ_FRAUTH); 411145522Sdarrenr if (auth.fra_len != 0 && auth.fra_buf != NULL) { 412145522Sdarrenr /* 413145522Sdarrenr * Copy packet contents out to user space if 414145522Sdarrenr * requested. Bail on an error. 415145522Sdarrenr */ 416145522Sdarrenr m = fr_authpkts[fr_authnext]; 417145522Sdarrenr len = MSGDSIZE(m); 418145522Sdarrenr if (len > auth.fra_len) 419145522Sdarrenr len = auth.fra_len; 420145522Sdarrenr auth.fra_len = len; 421145522Sdarrenr for (t = auth.fra_buf; m && (len > 0); ) { 422145522Sdarrenr i = MIN(M_LEN(m), len); 423145522Sdarrenr error = copyoutptr(MTOD(m, char *), 424145522Sdarrenr t, i); 425145522Sdarrenr len -= i; 426145522Sdarrenr t += i; 427145522Sdarrenr if (error != 0) 428145522Sdarrenr break; 429145522Sdarrenr } 430145522Sdarrenr } 43153642Sguido RWLOCK_EXIT(&ipf_auth); 432145522Sdarrenr if (error != 0) 43360850Sdarrenr break; 434145522Sdarrenr SPL_NET(s); 43553642Sguido WRITE_ENTER(&ipf_auth); 43653642Sguido fr_authnext++; 437145522Sdarrenr if (fr_authnext == fr_authsize) 43853642Sguido fr_authnext = 0; 439145522Sdarrenr RWLOCK_EXIT(&ipf_auth); 44080482Sdarrenr SPL_X(s); 44153642Sguido return 0; 44253642Sguido } 44395418Sdarrenr RWLOCK_EXIT(&ipf_auth); 444145522Sdarrenr /* 445145522Sdarrenr * We exit ipf_global here because a program that enters in 446145522Sdarrenr * here will have a lock on it and goto sleep having this lock. 447145522Sdarrenr * If someone were to do an 'ipf -D' the system would then 448145522Sdarrenr * deadlock. The catch with releasing it here is that the 449145522Sdarrenr * caller of this function expects it to be held when we 450145522Sdarrenr * return so we have to reacquire it in here. 451145522Sdarrenr */ 452145522Sdarrenr RWLOCK_EXIT(&ipf_global); 453145522Sdarrenr 454145522Sdarrenr MUTEX_ENTER(&ipf_authmx); 45553642Sguido#ifdef _KERNEL 45653642Sguido# if SOLARIS 457145522Sdarrenr error = 0; 458145522Sdarrenr if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk)) 459145522Sdarrenr error = EINTR; 460145522Sdarrenr# else /* SOLARIS */ 461145522Sdarrenr# ifdef __hpux 462145522Sdarrenr { 463145522Sdarrenr lock_t *l; 464145522Sdarrenr 465145522Sdarrenr l = get_sleep_lock(&fr_authnext); 466145522Sdarrenr error = sleep(&fr_authnext, PZERO+1); 467145522Sdarrenr spinunlock(l); 46853642Sguido } 469145522Sdarrenr# else 470145522Sdarrenr# ifdef __osf__ 471145522Sdarrenr error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0, 472145522Sdarrenr &ipf_authmx, MS_LOCK_SIMPLE); 473145522Sdarrenr# else 47453642Sguido error = SLEEP(&fr_authnext, "fr_authnext"); 475145522Sdarrenr# endif /* __osf__ */ 476145522Sdarrenr# endif /* __hpux */ 477145522Sdarrenr# endif /* SOLARIS */ 47853642Sguido#endif 479145522Sdarrenr MUTEX_EXIT(&ipf_authmx); 480145522Sdarrenr READ_ENTER(&ipf_global); 481145522Sdarrenr if (error == 0) { 482145522Sdarrenr READ_ENTER(&ipf_auth); 48353642Sguido goto fr_authioctlloop; 484145522Sdarrenr } 48553642Sguido break; 486145522Sdarrenr 48753642Sguido case SIOCAUTHR: 488145522Sdarrenr error = fr_inobj(data, &auth, IPFOBJ_FRAUTH); 489145522Sdarrenr if (error != 0) 49060850Sdarrenr return error; 491145522Sdarrenr SPL_NET(s); 49253642Sguido WRITE_ENTER(&ipf_auth); 49360857Sdarrenr i = au->fra_index; 49480482Sdarrenr fra = fr_auth + i; 495145522Sdarrenr if ((i < 0) || (i >= fr_authsize) || 49680482Sdarrenr (fra->fra_info.fin_id != au->fra_info.fin_id)) { 497145522Sdarrenr RWLOCK_EXIT(&ipf_auth); 49880482Sdarrenr SPL_X(s); 499145522Sdarrenr return ESRCH; 50053642Sguido } 50153642Sguido m = fr_authpkts[i]; 50280482Sdarrenr fra->fra_index = -2; 50380482Sdarrenr fra->fra_pass = au->fra_pass; 50453642Sguido fr_authpkts[i] = NULL; 50580482Sdarrenr RWLOCK_EXIT(&ipf_auth); 50653642Sguido#ifdef _KERNEL 507145522Sdarrenr if ((m != NULL) && (au->fra_info.fin_out != 0)) { 508145522Sdarrenr# ifdef MENTAT 509145522Sdarrenr error = !putq(fra->fra_q, m); 510145522Sdarrenr# else /* MENTAT */ 511145522Sdarrenr# ifdef linux 512145522Sdarrenr# else 513145522Sdarrenr# if (_BSDI_VERSION >= 199802) || defined(__OpenBSD__) || \ 514145522Sdarrenr (defined(__sgi) && (IRIX >= 60500) || \ 515145522Sdarrenr (defined(__FreeBSD__) && (__FreeBSD_version >= 470102))) 516145522Sdarrenr error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, 51753642Sguido NULL); 518145522Sdarrenr# else 519145522Sdarrenr error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL); 520145522Sdarrenr# endif 521145522Sdarrenr# endif /* Linux */ 522145522Sdarrenr# endif /* MENTAT */ 523145522Sdarrenr if (error != 0) 52453642Sguido fr_authstats.fas_sendfail++; 52553642Sguido else 52653642Sguido fr_authstats.fas_sendok++; 52753642Sguido } else if (m) { 528145522Sdarrenr# ifdef MENTAT 529145522Sdarrenr error = !putq(fra->fra_q, m); 530145522Sdarrenr# else /* MENTAT */ 531145522Sdarrenr# ifdef linux 532145522Sdarrenr# else 533145522Sdarrenr# if __FreeBSD_version >= 501000 534145522Sdarrenr netisr_dispatch(NETISR_IP, m); 535145522Sdarrenr# else 536145522Sdarrenr# if IRIX >= 60516 537145522Sdarrenr ifq = &((struct ifnet *)fra->fra_info.fin_ifp)->if_snd; 538145522Sdarrenr# else 539145522Sdarrenr ifq = &ipintrq; 540145522Sdarrenr# endif 541145522Sdarrenr if (IF_QFULL(ifq)) { 542145522Sdarrenr IF_DROP(ifq); 543145522Sdarrenr FREE_MB_T(m); 54453642Sguido error = ENOBUFS; 545145522Sdarrenr } else { 546145522Sdarrenr IF_ENQUEUE(ifq, m); 547145522Sdarrenr# if IRIX < 60500 548145522Sdarrenr schednetisr(NETISR_IP); 549145522Sdarrenr# endif 550145522Sdarrenr } 551145522Sdarrenr# endif 552145522Sdarrenr# endif /* Linux */ 553145522Sdarrenr# endif /* MENTAT */ 554145522Sdarrenr if (error != 0) 55553642Sguido fr_authstats.fas_quefail++; 55653642Sguido else 55753642Sguido fr_authstats.fas_queok++; 55853642Sguido } else 55953642Sguido error = EINVAL; 560145522Sdarrenr# ifdef MENTAT 561145522Sdarrenr if (error != 0) 56253642Sguido error = EINVAL; 563145522Sdarrenr# else /* MENTAT */ 56453642Sguido /* 56553642Sguido * If we experience an error which will result in the packet 56653642Sguido * not being processed, make sure we advance to the next one. 567145522Sdarrenr */ 56853642Sguido if (error == ENOBUFS) { 56953642Sguido fr_authused--; 57080482Sdarrenr fra->fra_index = -1; 57180482Sdarrenr fra->fra_pass = 0; 57253642Sguido if (i == fr_authstart) { 57380482Sdarrenr while (fra->fra_index == -1) { 57453642Sguido i++; 575145522Sdarrenr if (i == fr_authsize) 57653642Sguido i = 0; 57753642Sguido fr_authstart = i; 57853642Sguido if (i == fr_authend) 57953642Sguido break; 58053642Sguido } 58153642Sguido if (fr_authstart == fr_authend) { 58253642Sguido fr_authnext = 0; 58353642Sguido fr_authstart = fr_authend = 0; 58453642Sguido } 58553642Sguido } 58653642Sguido } 587145522Sdarrenr# endif /* MENTAT */ 58853642Sguido#endif /* _KERNEL */ 58980482Sdarrenr SPL_X(s); 59053642Sguido break; 591145522Sdarrenr 59253642Sguido default : 59353642Sguido error = EINVAL; 59453642Sguido break; 59553642Sguido } 59653642Sguido return error; 59753642Sguido} 59853642Sguido 59953642Sguido 60053642Sguido/* 60153642Sguido * Free all network buffer memory used to keep saved packets. 60253642Sguido */ 60353642Sguidovoid fr_authunload() 60453642Sguido{ 60553642Sguido register int i; 60653642Sguido register frauthent_t *fae, **faep; 60780482Sdarrenr frentry_t *fr, **frp; 60853642Sguido mb_t *m; 60953642Sguido 610145522Sdarrenr if (fr_auth != NULL) { 611145522Sdarrenr KFREES(fr_auth, fr_authsize * sizeof(*fr_auth)); 612145522Sdarrenr fr_auth = NULL; 613145522Sdarrenr } 614145522Sdarrenr 615145522Sdarrenr if (fr_authpkts != NULL) { 616145522Sdarrenr for (i = 0; i < fr_authsize; i++) { 617145522Sdarrenr m = fr_authpkts[i]; 618145522Sdarrenr if (m != NULL) { 619145522Sdarrenr FREE_MB_T(m); 620145522Sdarrenr fr_authpkts[i] = NULL; 621145522Sdarrenr } 62253642Sguido } 623145522Sdarrenr KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts)); 624145522Sdarrenr fr_authpkts = NULL; 62553642Sguido } 62653642Sguido 627145522Sdarrenr faep = &fae_list; 628145522Sdarrenr while ((fae = *faep) != NULL) { 62953642Sguido *faep = fae->fae_next; 63053642Sguido KFREE(fae); 63153642Sguido } 63253642Sguido ipauth = NULL; 63380482Sdarrenr 634145522Sdarrenr if (fr_authlist != NULL) { 635145522Sdarrenr for (frp = &fr_authlist; ((fr = *frp) != NULL); ) { 63680482Sdarrenr if (fr->fr_ref == 1) { 63780482Sdarrenr *frp = fr->fr_next; 63880482Sdarrenr KFREE(fr); 63980482Sdarrenr } else 64080482Sdarrenr frp = &fr->fr_next; 64180482Sdarrenr } 64280482Sdarrenr } 643145522Sdarrenr 644145522Sdarrenr if (fr_auth_init == 1) { 645145522Sdarrenr# if SOLARIS && defined(_KERNEL) 646145522Sdarrenr cv_destroy(&ipfauthwait); 647145522Sdarrenr# endif 648145522Sdarrenr MUTEX_DESTROY(&ipf_authmx); 649145522Sdarrenr RW_DESTROY(&ipf_auth); 650145522Sdarrenr 651145522Sdarrenr fr_auth_init = 0; 652145522Sdarrenr } 65353642Sguido} 65453642Sguido 65553642Sguido 65653642Sguido/* 65753642Sguido * Slowly expire held auth records. Timeouts are set 65853642Sguido * in expectation of this being called twice per second. 65953642Sguido */ 66053642Sguidovoid fr_authexpire() 66153642Sguido{ 66253642Sguido register int i; 66353642Sguido register frauth_t *fra; 66453642Sguido register frauthent_t *fae, **faep; 66580482Sdarrenr register frentry_t *fr, **frp; 66653642Sguido mb_t *m; 667145522Sdarrenr# if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL) 66853642Sguido int s; 669145522Sdarrenr# endif 67053642Sguido 67160850Sdarrenr if (fr_auth_lock) 67260850Sdarrenr return; 67360850Sdarrenr 67453642Sguido SPL_NET(s); 67553642Sguido WRITE_ENTER(&ipf_auth); 676145522Sdarrenr for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) { 677145522Sdarrenr fra->fra_age--; 678145522Sdarrenr if ((fra->fra_age == 0) && (m = fr_authpkts[i])) { 67953642Sguido FREE_MB_T(m); 68053642Sguido fr_authpkts[i] = NULL; 68153642Sguido fr_auth[i].fra_index = -1; 68253642Sguido fr_authstats.fas_expire++; 68353642Sguido fr_authused--; 68453642Sguido } 68553642Sguido } 68653642Sguido 687145522Sdarrenr for (faep = &fae_list; ((fae = *faep) != NULL); ) { 688145522Sdarrenr fae->fae_age--; 689145522Sdarrenr if (fae->fae_age == 0) { 69053642Sguido *faep = fae->fae_next; 69153642Sguido KFREE(fae); 69253642Sguido fr_authstats.fas_expire++; 69353642Sguido } else 69453642Sguido faep = &fae->fae_next; 69553642Sguido } 69698004Sdarrenr if (fae_list != NULL) 69798004Sdarrenr ipauth = &fae_list->fae_fr; 69898004Sdarrenr else 69998004Sdarrenr ipauth = NULL; 70080482Sdarrenr 701145522Sdarrenr for (frp = &fr_authlist; ((fr = *frp) != NULL); ) { 70280482Sdarrenr if (fr->fr_ref == 1) { 70380482Sdarrenr *frp = fr->fr_next; 70480482Sdarrenr KFREE(fr); 70580482Sdarrenr } else 70680482Sdarrenr frp = &fr->fr_next; 70780482Sdarrenr } 70853642Sguido RWLOCK_EXIT(&ipf_auth); 70953642Sguido SPL_X(s); 71053642Sguido} 711110915Sdarrenr 712110915Sdarrenrint fr_preauthcmd(cmd, fr, frptr) 713145522Sdarrenrioctlcmd_t cmd; 714110915Sdarrenrfrentry_t *fr, **frptr; 715110915Sdarrenr{ 716110915Sdarrenr frauthent_t *fae, **faep; 717110915Sdarrenr int error = 0; 718145522Sdarrenr# if !defined(MENAT) && defined(_KERNEL) && defined(USE_SPL) 719110915Sdarrenr int s; 720110915Sdarrenr#endif 721110915Sdarrenr 722145522Sdarrenr if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR)) 723110915Sdarrenr return EIO; 724110915Sdarrenr 725145522Sdarrenr for (faep = &fae_list; ((fae = *faep) != NULL); ) { 726110915Sdarrenr if (&fae->fae_fr == fr) 727110915Sdarrenr break; 728110915Sdarrenr else 729110915Sdarrenr faep = &fae->fae_next; 730145522Sdarrenr } 731145522Sdarrenr 732145522Sdarrenr if (cmd == (ioctlcmd_t)SIOCRMAFR) { 733145522Sdarrenr if (fr == NULL || frptr == NULL) 734110915Sdarrenr error = EINVAL; 735145522Sdarrenr else if (fae == NULL) 736110915Sdarrenr error = ESRCH; 737110915Sdarrenr else { 738145522Sdarrenr SPL_NET(s); 739110915Sdarrenr WRITE_ENTER(&ipf_auth); 740110915Sdarrenr *faep = fae->fae_next; 741145522Sdarrenr if (ipauth == &fae->fae_fr) 742145522Sdarrenr ipauth = fae_list ? &fae_list->fae_fr : NULL; 743145522Sdarrenr RWLOCK_EXIT(&ipf_auth); 744110915Sdarrenr SPL_X(s); 745145522Sdarrenr 746110915Sdarrenr KFREE(fae); 747110915Sdarrenr } 748145522Sdarrenr } else if (fr != NULL && frptr != NULL) { 749110915Sdarrenr KMALLOC(fae, frauthent_t *); 750110915Sdarrenr if (fae != NULL) { 751110915Sdarrenr bcopy((char *)fr, (char *)&fae->fae_fr, 752110915Sdarrenr sizeof(*fr)); 753145522Sdarrenr SPL_NET(s); 754110915Sdarrenr WRITE_ENTER(&ipf_auth); 755110915Sdarrenr fae->fae_age = fr_defaultauthage; 756110915Sdarrenr fae->fae_fr.fr_hits = 0; 757110915Sdarrenr fae->fae_fr.fr_next = *frptr; 758110915Sdarrenr *frptr = &fae->fae_fr; 759110915Sdarrenr fae->fae_next = *faep; 760110915Sdarrenr *faep = fae; 761110915Sdarrenr ipauth = &fae_list->fae_fr; 762145522Sdarrenr RWLOCK_EXIT(&ipf_auth); 763110915Sdarrenr SPL_X(s); 764110915Sdarrenr } else 765110915Sdarrenr error = ENOMEM; 766110915Sdarrenr } else 767110915Sdarrenr error = EINVAL; 768110915Sdarrenr return error; 769110915Sdarrenr} 770145522Sdarrenr 771145522Sdarrenr 772145522Sdarrenr/* 773145522Sdarrenr * Flush held packets. 774145522Sdarrenr * Must already be properly SPL'ed and Locked on &ipf_auth. 775145522Sdarrenr * 776145522Sdarrenr */ 777145522Sdarrenrint fr_authflush() 778145522Sdarrenr{ 779145522Sdarrenr register int i, num_flushed; 780145522Sdarrenr mb_t *m; 781145522Sdarrenr 782145522Sdarrenr if (fr_auth_lock) 783145522Sdarrenr return -1; 784145522Sdarrenr 785145522Sdarrenr num_flushed = 0; 786145522Sdarrenr 787145522Sdarrenr for (i = 0 ; i < fr_authsize; i++) { 788145522Sdarrenr m = fr_authpkts[i]; 789145522Sdarrenr if (m != NULL) { 790145522Sdarrenr FREE_MB_T(m); 791145522Sdarrenr fr_authpkts[i] = NULL; 792145522Sdarrenr fr_auth[i].fra_index = -1; 793145522Sdarrenr /* perhaps add & use a flush counter inst.*/ 794145522Sdarrenr fr_authstats.fas_expire++; 795145522Sdarrenr fr_authused--; 796145522Sdarrenr num_flushed++; 797145522Sdarrenr } 798145522Sdarrenr } 799145522Sdarrenr 800145522Sdarrenr fr_authstart = 0; 801145522Sdarrenr fr_authend = 0; 802145522Sdarrenr fr_authnext = 0; 803145522Sdarrenr 804145522Sdarrenr return num_flushed; 805145522Sdarrenr} 806