ip_auth.c revision 55929
162583Sitojun/*
278064Sume * Copyright (C) 1998 by Darren Reed & Guido van Rooij.
362583Sitojun *
455505Sshin * Redistribution and use in source and binary forms are permitted
555505Sshin * provided that this notice is preserved and due credit is given
655505Sshin * to the original author and the contributors.
755505Sshin */
855505Sshin#if !defined(lint)
955505Sshin/*static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.1.2.1 1999/09/28 11:44:04 darrenr Exp $";*/
1055505Sshinstatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 55929 2000-01-13 19:01:33Z guido $";
1155505Sshin#endif
1255505Sshin
1355505Sshin#include <sys/errno.h>
1455505Sshin#include <sys/types.h>
1555505Sshin#include <sys/param.h>
1655505Sshin#include <sys/time.h>
1755505Sshin#include <sys/file.h>
1855505Sshin#if !defined(_KERNEL) && !defined(KERNEL)
1955505Sshin# include <stdio.h>
2055505Sshin# include <stdlib.h>
2155505Sshin# include <string.h>
2255505Sshin#endif
2355505Sshin#if ((defined(KERNEL) && (__FreeBSD_version >= 220000)) || \
2455505Sshin     (defined(_KERNEL) && (__FreeBSD_version >= 40013)))
2555505Sshin# include <sys/filio.h>
2655505Sshin# include <sys/fcntl.h>
2755505Sshin#else
2855505Sshin# include <sys/ioctl.h>
2955505Sshin#endif
3055505Sshin#include <sys/uio.h>
3155505Sshin#ifndef linux
3255505Sshin# include <sys/protosw.h>
3362583Sitojun#endif
3492917Sobrien#include <sys/socket.h>
3555505Sshin#if defined(_KERNEL) && !defined(linux)
3662583Sitojun# include <sys/systm.h>
3762583Sitojun#endif
3862583Sitojun#if !defined(__SVR4) && !defined(__svr4__)
3962583Sitojun# ifndef linux
4062583Sitojun#  include <sys/mbuf.h>
4162583Sitojun# endif
4262583Sitojun#else
4362583Sitojun# include <sys/filio.h>
4462583Sitojun# include <sys/byteorder.h>
4562583Sitojun# ifdef _KERNEL
4662583Sitojun#  include <sys/dditypes.h>
4762583Sitojun# endif
4862583Sitojun# include <sys/stream.h>
4962583Sitojun# include <sys/kmem.h>
5062583Sitojun#endif
5162583Sitojun#if _BSDI_VERSION >= 199802
5262583Sitojun# include <sys/queue.h>
5362583Sitojun#endif
5462583Sitojun#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi)
5562583Sitojun# include <machine/cpu.h>
5662583Sitojun#endif
5762583Sitojun#include <net/if.h>
5862583Sitojun#ifdef sun
5962583Sitojun# include <net/af.h>
6062583Sitojun#endif
6162583Sitojun#include <net/route.h>
6262583Sitojun#include <netinet/in.h>
6362583Sitojun#include <netinet/in_systm.h>
64#include <netinet/ip.h>
65#ifndef	KERNEL
66# define	KERNEL
67# define	NOT_KERNEL
68#endif
69#ifndef linux
70# include <netinet/ip_var.h>
71#endif
72#ifdef	NOT_KERNEL
73# undef	KERNEL
74#endif
75#ifdef __sgi
76# ifdef IFF_DRVRLOCK /* IRIX6 */
77#  include <sys/hashing.h>
78# endif
79#endif
80#include <netinet/tcp.h>
81#if defined(__sgi) && !defined(IFF_DRVRLOCK) /* IRIX < 6 */
82extern struct ifqueue   ipintrq;                /* ip packet input queue */
83#else
84# ifndef linux
85#  if __FreeBSD_version >= 300000
86#   include <net/if_var.h>
87#  endif
88#  include <netinet/in_var.h>
89#  include <netinet/tcp_fsm.h>
90# endif
91#endif
92#include <netinet/udp.h>
93#include <netinet/ip_icmp.h>
94#include "netinet/ip_compat.h"
95#include <netinet/tcpip.h>
96#include "netinet/ip_fil.h"
97#include "netinet/ip_auth.h"
98#if !SOLARIS && !defined(linux)
99# include <net/netisr.h>
100# ifdef __FreeBSD__
101#  include <machine/cpufunc.h>
102# endif
103#endif
104#if (__FreeBSD_version >= 300000)
105# include <sys/malloc.h>
106# if (defined(_KERNEL) || defined(KERNEL)) && !defined(IPFILTER_LKM)
107#  include <sys/libkern.h>
108#  include <sys/systm.h>
109# endif
110#endif
111
112
113
114#if (SOLARIS || defined(__sgi)) && defined(_KERNEL)
115extern KRWLOCK_T ipf_auth;
116extern kmutex_t ipf_authmx;
117# if SOLARIS
118extern kcondvar_t ipfauthwait;
119# endif
120#endif
121#ifdef linux
122static struct wait_queue *ipfauthwait = NULL;
123#endif
124
125int	fr_authsize = FR_NUMAUTH;
126int	fr_authused = 0;
127int	fr_defaultauthage = 600;
128fr_authstat_t	fr_authstats;
129frauth_t fr_auth[FR_NUMAUTH];
130mb_t	*fr_authpkts[FR_NUMAUTH];
131int	fr_authstart = 0, fr_authend = 0, fr_authnext = 0;
132frauthent_t	*fae_list = NULL;
133frentry_t	*ipauth = NULL;
134
135
136/*
137 * Check if a packet has authorization.  If the packet is found to match an
138 * authorization result and that would result in a feedback loop (i.e. it
139 * will end up returning FR_AUTH) then return FR_BLOCK instead.
140 */
141u_32_t fr_checkauth(ip, fin)
142ip_t *ip;
143fr_info_t *fin;
144{
145	u_short id = ip->ip_id;
146	u_32_t pass;
147	int i;
148
149	READ_ENTER(&ipf_auth);
150	for (i = fr_authstart; i != fr_authend; ) {
151		/*
152		 * index becomes -2 only after an SIOCAUTHW.  Check this in
153		 * case the same packet gets sent again and it hasn't yet been
154		 * auth'd.
155		 */
156		if ((fr_auth[i].fra_index == -2) &&
157		    (id == fr_auth[i].fra_info.fin_id) &&
158		    !bcmp((char *)fin,(char *)&fr_auth[i].fra_info,FI_CSIZE)) {
159			/*
160			 * Avoid feedback loop.
161			 */
162			if (!(pass = fr_auth[i].fra_pass) || (pass & FR_AUTH))
163				pass = FR_BLOCK;
164			RWLOCK_EXIT(&ipf_auth);
165			WRITE_ENTER(&ipf_auth);
166			fr_authstats.fas_hits++;
167			fr_auth[i].fra_index = -1;
168			fr_authused--;
169			if (i == fr_authstart) {
170				while (fr_auth[i].fra_index == -1) {
171					i++;
172					if (i == FR_NUMAUTH)
173						i = 0;
174					fr_authstart = i;
175					if (i == fr_authend)
176						break;
177				}
178				if (fr_authstart == fr_authend) {
179					fr_authnext = 0;
180					fr_authstart = fr_authend = 0;
181				}
182			}
183			RWLOCK_EXIT(&ipf_auth);
184			return pass;
185		}
186		i++;
187		if (i == FR_NUMAUTH)
188			i = 0;
189	}
190	fr_authstats.fas_miss++;
191	RWLOCK_EXIT(&ipf_auth);
192	return 0;
193}
194
195
196/*
197 * Check if we have room in the auth array to hold details for another packet.
198 * If we do, store it and wake up any user programs which are waiting to
199 * hear about these events.
200 */
201int fr_newauth(m, fin, ip
202#if defined(_KERNEL) && SOLARIS
203, qif)
204qif_t *qif;
205#else
206)
207#endif
208mb_t *m;
209fr_info_t *fin;
210ip_t *ip;
211{
212	int i;
213
214	WRITE_ENTER(&ipf_auth);
215	if (fr_authstart > fr_authend) {
216		fr_authstats.fas_nospace++;
217		RWLOCK_EXIT(&ipf_auth);
218		return 0;
219	} else {
220		if ((fr_authstart == 0) && (fr_authend == FR_NUMAUTH - 1)) {
221			fr_authstats.fas_nospace++;
222			RWLOCK_EXIT(&ipf_auth);
223			return 0;
224		}
225	}
226
227	fr_authstats.fas_added++;
228	fr_authused++;
229	i = fr_authend++;
230	if (fr_authend == FR_NUMAUTH)
231		fr_authend = 0;
232	RWLOCK_EXIT(&ipf_auth);
233	fr_auth[i].fra_index = i;
234	fr_auth[i].fra_pass = 0;
235	fr_auth[i].fra_age = fr_defaultauthage;
236	bcopy((char *)fin, (char *)&fr_auth[i].fra_info, sizeof(*fin));
237#if !defined(sparc) && !defined(m68k)
238	/*
239	 * No need to copyback here as we want to undo the changes, not keep
240	 * them.
241	 */
242# if SOLARIS && defined(_KERNEL)
243	if (ip == (ip_t *)m->b_rptr)
244# endif
245	{
246		register u_short bo;
247
248		bo = ip->ip_len;
249		ip->ip_len = htons(bo);
250# if !SOLARIS	/* 4.4BSD converts this ip_input.c, but I don't in solaris.c */
251		bo = ip->ip_id;
252		ip->ip_id = htons(bo);
253# endif
254		bo = ip->ip_off;
255		ip->ip_off = htons(bo);
256	}
257#endif
258#if SOLARIS && defined(_KERNEL)
259	m->b_rptr -= qif->qf_off;
260	fr_authpkts[i] = *(mblk_t **)fin->fin_mp;
261	fr_auth[i].fra_q = qif->qf_q;
262	cv_signal(&ipfauthwait);
263#else
264	fr_authpkts[i] = m;
265# if defined(linux) && defined(_KERNEL)
266	wake_up_interruptible(&ipfauthwait);
267# else
268	WAKEUP(&fr_authnext);
269# endif
270#endif
271	return 1;
272}
273
274
275int fr_auth_ioctl(data, cmd, fr, frptr)
276caddr_t data;
277#if defined(__NetBSD__) || defined(__OpenBSD__)
278u_long cmd;
279#else
280int cmd;
281#endif
282frentry_t *fr, **frptr;
283{
284	mb_t *m;
285#if defined(_KERNEL)
286# if !SOLARIS
287	struct ifqueue *ifq;
288	int s;
289# endif
290#endif
291	frauth_t auth, *au = &auth;
292	frauthent_t *fae, **faep;
293	int i, error = 0;
294
295	switch (cmd)
296	{
297	case SIOCINIFR :
298	case SIOCRMIFR :
299	case SIOCADIFR :
300		error = EINVAL;
301		break;
302	case SIOCINAFR :
303	case SIOCRMAFR :
304	case SIOCADAFR :
305		for (faep = &fae_list; (fae = *faep); )
306			if (&fae->fae_fr == fr)
307				break;
308			else
309				faep = &fae->fae_next;
310		if (cmd == SIOCRMAFR) {
311			if (!fae)
312				error = ESRCH;
313			else {
314				WRITE_ENTER(&ipf_auth);
315				*faep = fae->fae_next;
316				*frptr = fr->fr_next;
317				RWLOCK_EXIT(&ipf_auth);
318				KFREE(fae);
319			}
320		} else {
321			KMALLOC(fae, frauthent_t *);
322			if (fae != NULL) {
323				IRCOPY((char *)data, (char *)&fae->fae_fr,
324				       sizeof(fae->fae_fr));
325				WRITE_ENTER(&ipf_auth);
326				fae->fae_age = fr_defaultauthage;
327				fae->fae_fr.fr_hits = 0;
328				fae->fae_fr.fr_next = *frptr;
329				*frptr = &fae->fae_fr;
330				fae->fae_next = *faep;
331				*faep = fae;
332				ipauth = &fae_list->fae_fr;
333				RWLOCK_EXIT(&ipf_auth);
334			} else
335				error = ENOMEM;
336		}
337		break;
338	case SIOCATHST:
339		READ_ENTER(&ipf_auth);
340		fr_authstats.fas_faelist = fae_list;
341		RWLOCK_EXIT(&ipf_auth);
342		IWCOPY((char *)&fr_authstats, data, sizeof(fr_authstats));
343		break;
344	case SIOCAUTHW:
345fr_authioctlloop:
346		READ_ENTER(&ipf_auth);
347		if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) {
348			IWCOPY((char *)&fr_auth[fr_authnext], data,
349			       sizeof(fr_info_t));
350			RWLOCK_EXIT(&ipf_auth);
351			WRITE_ENTER(&ipf_auth);
352			fr_authnext++;
353			if (fr_authnext == FR_NUMAUTH)
354				fr_authnext = 0;
355			RWLOCK_EXIT(&ipf_auth);
356			return 0;
357		}
358#ifdef	_KERNEL
359# if	SOLARIS
360		mutex_enter(&ipf_authmx);
361		if (!cv_wait_sig(&ipfauthwait, &ipf_authmx)) {
362			mutex_exit(&ipf_authmx);
363			return EINTR;
364		}
365		mutex_exit(&ipf_authmx);
366# else
367#  ifdef linux
368		interruptible_sleep_on(&ipfauthwait);
369		if (current->signal & ~current->blocked)
370			error = -EINTR;
371#  else
372		error = SLEEP(&fr_authnext, "fr_authnext");
373# endif
374# endif
375#endif
376		RWLOCK_EXIT(&ipf_auth);
377		if (!error)
378			goto fr_authioctlloop;
379		break;
380	case SIOCAUTHR:
381		IRCOPY(data, (caddr_t)&auth, sizeof(auth));
382		WRITE_ENTER(&ipf_auth);
383		i = au->fra_index;
384		if ((i < 0) || (i > FR_NUMAUTH) ||
385		    (fr_auth[i].fra_info.fin_id != au->fra_info.fin_id)) {
386			RWLOCK_EXIT(&ipf_auth);
387			return EINVAL;
388		}
389		m = fr_authpkts[i];
390		fr_auth[i].fra_index = -2;
391		fr_auth[i].fra_pass = au->fra_pass;
392		fr_authpkts[i] = NULL;
393#ifdef	_KERNEL
394		RWLOCK_EXIT(&ipf_auth);
395		SPL_NET(s);
396# ifndef linux
397		if (m && au->fra_info.fin_out) {
398#  if SOLARIS
399			error = fr_qout(fr_auth[i].fra_q, m);
400#  else /* SOLARIS */
401#   if _BSDI_VERSION >= 199802
402			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL,
403					  NULL);
404#   else
405			error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL);
406#   endif
407#  endif /* SOLARIS */
408			if (error)
409				fr_authstats.fas_sendfail++;
410			else
411				fr_authstats.fas_sendok++;
412		} else if (m) {
413# if SOLARIS
414			error = fr_qin(fr_auth[i].fra_q, m);
415# else /* SOLARIS */
416			ifq = &ipintrq;
417			if (IF_QFULL(ifq)) {
418				IF_DROP(ifq);
419				m_freem(m);
420				error = ENOBUFS;
421			} else {
422				IF_ENQUEUE(ifq, m);
423				schednetisr(NETISR_IP);
424			}
425# endif /* SOLARIS */
426			if (error)
427				fr_authstats.fas_quefail++;
428			else
429				fr_authstats.fas_queok++;
430		} else
431			error = EINVAL;
432# endif
433# if SOLARIS
434		if (error)
435			error = EINVAL;
436# else
437		/*
438		 * If we experience an error which will result in the packet
439		 * not being processed, make sure we advance to the next one.
440		 */
441		if (error == ENOBUFS) {
442			fr_authused--;
443			fr_auth[i].fra_index = -1;
444			fr_auth[i].fra_pass = 0;
445			if (i == fr_authstart) {
446				while (fr_auth[i].fra_index == -1) {
447					i++;
448					if (i == FR_NUMAUTH)
449						i = 0;
450					fr_authstart = i;
451					if (i == fr_authend)
452						break;
453				}
454				if (fr_authstart == fr_authend) {
455					fr_authnext = 0;
456					fr_authstart = fr_authend = 0;
457				}
458			}
459		}
460# endif
461		SPL_X(s);
462#endif /* _KERNEL */
463		break;
464	default :
465		error = EINVAL;
466		break;
467	}
468	return error;
469}
470
471
472#ifdef	_KERNEL
473/*
474 * Free all network buffer memory used to keep saved packets.
475 */
476void fr_authunload()
477{
478	register int i;
479	register frauthent_t *fae, **faep;
480	mb_t *m;
481
482	WRITE_ENTER(&ipf_auth);
483	for (i = 0; i < FR_NUMAUTH; i++) {
484		if ((m = fr_authpkts[i])) {
485			FREE_MB_T(m);
486			fr_authpkts[i] = NULL;
487			fr_auth[i].fra_index = -1;
488		}
489	}
490
491
492	for (faep = &fae_list; (fae = *faep); ) {
493		*faep = fae->fae_next;
494		KFREE(fae);
495	}
496	ipauth = NULL;
497	RWLOCK_EXIT(&ipf_auth);
498}
499
500
501/*
502 * Slowly expire held auth records.  Timeouts are set
503 * in expectation of this being called twice per second.
504 */
505void fr_authexpire()
506{
507	register int i;
508	register frauth_t *fra;
509	register frauthent_t *fae, **faep;
510	mb_t *m;
511#if !SOLARIS
512	int s;
513#endif
514
515	SPL_NET(s);
516	WRITE_ENTER(&ipf_auth);
517	for (i = 0, fra = fr_auth; i < FR_NUMAUTH; i++, fra++) {
518		if ((!--fra->fra_age) && (m = fr_authpkts[i])) {
519			FREE_MB_T(m);
520			fr_authpkts[i] = NULL;
521			fr_auth[i].fra_index = -1;
522			fr_authstats.fas_expire++;
523			fr_authused--;
524		}
525	}
526
527	for (faep = &fae_list; (fae = *faep); ) {
528		if (!--fae->fae_age) {
529			*faep = fae->fae_next;
530			KFREE(fae);
531			fr_authstats.fas_expire++;
532		} else
533			faep = &fae->fae_next;
534	}
535	ipauth = &fae_list->fae_fr;
536	RWLOCK_EXIT(&ipf_auth);
537	SPL_X(s);
538}
539#endif
540