ip_log.c revision 95563
153642Sguido/*
280482Sdarrenr * Copyright (C) 1997-2001 by Darren Reed.
353642Sguido *
480482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
553642Sguido *
663523Sdarrenr * $Id: ip_log.c,v 2.5.2.1 2000/07/19 13:11:47 darrenr Exp $
757126Sguido * $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_log.c 95563 2002-04-27 16:56:25Z darrenr $
853642Sguido */
953642Sguido#include <sys/param.h>
1053642Sguido#if defined(KERNEL) && !defined(_KERNEL)
1153642Sguido# define       _KERNEL
1253642Sguido#endif
1395563Sdarrenr#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
1495563Sdarrenr    defined(_KERNEL)
1553642Sguido# include "opt_ipfilter_log.h"
1653642Sguido#endif
1753642Sguido#ifdef  __FreeBSD__
1860925Sdarrenr# if defined(IPFILTER_LKM) || defined(_KERNEL)
1960925Sdarrenr#  if !defined(__FreeBSD_version)
2060925Sdarrenr#   include <sys/osreldate.h>
2153642Sguido#  endif
2260925Sdarrenr#  if !defined(IPFILTER_LKM)
2360925Sdarrenr#   if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
2460925Sdarrenr#    include "opt_ipfilter.h"
2560925Sdarrenr#   endif
2660925Sdarrenr#  endif
2760857Sdarrenr# else
2863523Sdarrenr#  ifdef KLD_MODULE
2980482Sdarrenr#   ifndef __FreeBSD_cc_version
3080482Sdarrenr#    include <osreldate.h>
3180482Sdarrenr#   else
3280482Sdarrenr#    if __FreeBSD_cc_version < 430000
3380482Sdarrenr#     include <osreldate.h>
3480482Sdarrenr#    endif
3580482Sdarrenr#   endif
3663523Sdarrenr#  endif
3753642Sguido# endif
3853642Sguido#endif
3960925Sdarrenr#ifdef  IPFILTER_LOG
4053642Sguido# ifndef SOLARIS
4153642Sguido#  define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4)))
4253642Sguido# endif
4353642Sguido# ifndef _KERNEL
4453642Sguido#  include <stdio.h>
4553642Sguido#  include <string.h>
4653642Sguido#  include <stdlib.h>
4753642Sguido#  include <ctype.h>
4853642Sguido# endif
4953642Sguido# include <sys/errno.h>
5053642Sguido# include <sys/types.h>
5153642Sguido# include <sys/file.h>
5253642Sguido# if __FreeBSD_version >= 220000 && defined(_KERNEL)
5353642Sguido#  include <sys/fcntl.h>
5453642Sguido#  include <sys/filio.h>
5553642Sguido# else
5653642Sguido#  include <sys/ioctl.h>
5753642Sguido# endif
5853642Sguido# include <sys/time.h>
5980482Sdarrenr# if defined(_KERNEL)
6053642Sguido#  include <sys/systm.h>
6153642Sguido# endif
6253642Sguido# if !SOLARIS
6353642Sguido#  if (NetBSD > 199609) || (OpenBSD > 199603) || (__FreeBSD_version >= 300000)
6453642Sguido#   include <sys/dirent.h>
6553642Sguido#  else
6653642Sguido#   include <sys/dir.h>
6753642Sguido#  endif
6880482Sdarrenr#  include <sys/mbuf.h>
6953642Sguido# else
7053642Sguido#  include <sys/filio.h>
7153642Sguido#  include <sys/cred.h>
7253642Sguido#  include <sys/kmem.h>
7392685Sdarrenr#  ifdef _KERNEL
7492685Sdarrenr#   include <sys/ddi.h>
7592685Sdarrenr#   include <sys/sunddi.h>
7692685Sdarrenr#   include <sys/ksynch.h>
7792685Sdarrenr#   include <sys/dditypes.h>
7892685Sdarrenr#   include <sys/cmn_err.h>
7992685Sdarrenr#  endif
8053642Sguido# endif
8180482Sdarrenr# include <sys/protosw.h>
8253642Sguido# include <sys/socket.h>
8353642Sguido
8453642Sguido# include <net/if.h>
8553642Sguido# ifdef sun
8653642Sguido#  include <net/af.h>
8753642Sguido# endif
8853642Sguido# if __FreeBSD_version >= 300000
8953642Sguido#  include <net/if_var.h>
9053642Sguido# endif
9153642Sguido# include <net/route.h>
9253642Sguido# include <netinet/in.h>
9353642Sguido# ifdef __sgi
9495418Sdarrenr#  define _KMEMUSER
9553642Sguido#  include <sys/ddi.h>
9653642Sguido#  ifdef IFF_DRVRLOCK /* IRIX6 */
9753642Sguido#   include <sys/hashing.h>
9853642Sguido#  endif
9953642Sguido# endif
10080482Sdarrenr# if !(defined(__sgi) && !defined(IFF_DRVRLOCK)) /*IRIX<6*/
10153642Sguido#  include <netinet/in_var.h>
10253642Sguido# endif
10353642Sguido# include <netinet/in_systm.h>
10453642Sguido# include <netinet/ip.h>
10553642Sguido# include <netinet/tcp.h>
10653642Sguido# include <netinet/udp.h>
10753642Sguido# include <netinet/ip_icmp.h>
10880482Sdarrenr# include <netinet/ip_var.h>
10953642Sguido# ifndef _KERNEL
11053642Sguido#  include <syslog.h>
11153642Sguido# endif
11253642Sguido# include "netinet/ip_compat.h"
11353642Sguido# include <netinet/tcpip.h>
11453642Sguido# include "netinet/ip_fil.h"
11553642Sguido# if (__FreeBSD_version >= 300000)
11653642Sguido#  include <sys/malloc.h>
11753642Sguido# endif
11853642Sguido
11953642Sguido# ifndef MIN
12053642Sguido#  define	MIN(a,b)	(((a)<(b))?(a):(b))
12153642Sguido# endif
12292685Sdarrenr# ifdef IPFILTER_LOGSIZE
12392685Sdarrenr#  undef IPLLOGSIZE
12492685Sdarrenr#  define IPLLOGSIZE IPFILTER_LOGSIZE
12592685Sdarrenr# endif
12653642Sguido
12753642Sguido
12853642Sguido# if SOLARIS || defined(__sgi)
12953642Sguidoextern	kmutex_t	ipl_mutex;
13053642Sguido#  if SOLARIS
13153642Sguidoextern	kcondvar_t	iplwait;
13253642Sguido#  endif
13353642Sguido# endif
13453642Sguido
13553642Sguidoiplog_t	**iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1], *ipll[IPL_LOGMAX+1];
13653642Sguidosize_t	iplused[IPL_LOGMAX+1];
13760857Sdarrenrstatic fr_info_t	iplcrc[IPL_LOGMAX+1];
13853642Sguido
13953642Sguido
14053642Sguido/*
14153642Sguido * Initialise log buffers & pointers.  Also iniialised the CRC to a local
14253642Sguido * secret for use in calculating the "last log checksum".
14353642Sguido */
14453642Sguidovoid ipflog_init()
14553642Sguido{
14653642Sguido	int	i;
14753642Sguido
14853642Sguido	for (i = IPL_LOGMAX; i >= 0; i--) {
14953642Sguido		iplt[i] = NULL;
15053642Sguido		ipll[i] = NULL;
15153642Sguido		iplh[i] = &iplt[i];
15253642Sguido		iplused[i] = 0;
15353642Sguido		bzero((char *)&iplcrc[i], sizeof(iplcrc[i]));
15453642Sguido	}
15553642Sguido}
15653642Sguido
15753642Sguido
15853642Sguido/*
15953642Sguido * ipflog
16053642Sguido * Create a log record for a packet given that it has been triggered by a
16153642Sguido * rule (or the default setting).  Calculate the transport protocol header
16253642Sguido * size using predetermined size of a couple of popular protocols and thus
16353642Sguido * how much data to copy into the log, including part of the data body if
16453642Sguido * requested.
16553642Sguido */
16653642Sguidoint ipflog(flags, ip, fin, m)
16753642Sguidou_int flags;
16853642Sguidoip_t *ip;
16953642Sguidofr_info_t *fin;
17053642Sguidomb_t *m;
17153642Sguido{
17253642Sguido	ipflog_t ipfl;
17353642Sguido	register size_t mlen, hlen;
17453642Sguido	size_t sizes[2];
17553642Sguido	void *ptrs[2];
17653642Sguido	int types[2];
17760857Sdarrenr	u_char p;
17892685Sdarrenr# if SOLARIS && defined(_KERNEL)
17953642Sguido	ill_t *ifp = fin->fin_ifp;
18053642Sguido# else
18153642Sguido	struct ifnet *ifp = fin->fin_ifp;
18253642Sguido# endif
18353642Sguido
18453642Sguido	/*
18553642Sguido	 * calculate header size.
18653642Sguido	 */
18753642Sguido	hlen = fin->fin_hlen;
18860857Sdarrenr	if (fin->fin_off == 0) {
18960857Sdarrenr		p = fin->fin_fi.fi_p;
19060857Sdarrenr		if (p == IPPROTO_TCP)
19153642Sguido			hlen += MIN(sizeof(tcphdr_t), fin->fin_dlen);
19260857Sdarrenr		else if (p == IPPROTO_UDP)
19353642Sguido			hlen += MIN(sizeof(udphdr_t), fin->fin_dlen);
19460857Sdarrenr		else if (p == IPPROTO_ICMP) {
19560857Sdarrenr			struct icmp *icmp;
19653642Sguido
19760857Sdarrenr			icmp = (struct icmp *)fin->fin_dp;
19853642Sguido
19953642Sguido			/*
20053642Sguido			 * For ICMP, if the packet is an error packet, also
20153642Sguido			 * include the information about the packet which
20253642Sguido			 * caused the error.
20353642Sguido			 */
20453642Sguido			switch (icmp->icmp_type)
20553642Sguido			{
20653642Sguido			case ICMP_UNREACH :
20753642Sguido			case ICMP_SOURCEQUENCH :
20853642Sguido			case ICMP_REDIRECT :
20953642Sguido			case ICMP_TIMXCEED :
21053642Sguido			case ICMP_PARAMPROB :
21153642Sguido				hlen += MIN(sizeof(struct icmp) + 8,
21253642Sguido					    fin->fin_dlen);
21353642Sguido				break;
21453642Sguido			default :
21553642Sguido				hlen += MIN(sizeof(struct icmp),
21653642Sguido					    fin->fin_dlen);
21753642Sguido				break;
21853642Sguido			}
21953642Sguido		}
22053642Sguido	}
22153642Sguido	/*
22253642Sguido	 * Get the interface number and name to which this packet is
22353642Sguido	 * currently associated.
22453642Sguido	 */
22592685Sdarrenr	bzero((char *)ipfl.fl_ifname, sizeof(ipfl.fl_ifname));
22692685Sdarrenr# if SOLARIS && defined(_KERNEL)
22753642Sguido	ipfl.fl_unit = (u_char)ifp->ill_ppa;
22892685Sdarrenr	bcopy(ifp->ill_name, ipfl.fl_ifname,
22992685Sdarrenr	      MIN(ifp->ill_name_length, sizeof(ipfl.fl_ifname)));
23053642Sguido	mlen = (flags & FR_LOGBODY) ? MIN(msgdsize(m) - hlen, 128) : 0;
23153642Sguido# else
23253642Sguido#  if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \
23353642Sguido	(defined(OpenBSD) && (OpenBSD >= 199603))
23453642Sguido	strncpy(ipfl.fl_ifname, ifp->if_xname, IFNAMSIZ);
23553642Sguido#  else
23653642Sguido	ipfl.fl_unit = (u_char)ifp->if_unit;
23792685Sdarrenr	strncpy(ipfl.fl_ifname, ifp->if_name, MIN(sizeof(ipfl.fl_ifname),
23892685Sdarrenr						  sizeof(ifp->if_name)));
23953642Sguido#  endif
24060857Sdarrenr	mlen = (flags & FR_LOGBODY) ? MIN(fin->fin_plen - hlen, 128) : 0;
24153642Sguido# endif
24253642Sguido	ipfl.fl_plen = (u_char)mlen;
24353642Sguido	ipfl.fl_hlen = (u_char)hlen;
24453642Sguido	ipfl.fl_rule = fin->fin_rule;
24553642Sguido	ipfl.fl_group = fin->fin_group;
24653642Sguido	if (fin->fin_fr != NULL)
24753642Sguido		ipfl.fl_loglevel = fin->fin_fr->fr_loglevel;
24853642Sguido	else
24953642Sguido		ipfl.fl_loglevel = 0xffff;
25053642Sguido	ipfl.fl_flags = flags;
25192685Sdarrenr	ipfl.fl_dir = fin->fin_out;
25253642Sguido	ptrs[0] = (void *)&ipfl;
25353642Sguido	sizes[0] = sizeof(ipfl);
25453642Sguido	types[0] = 0;
25592685Sdarrenr# if SOLARIS && defined(_KERNEL)
25653642Sguido	/*
25753642Sguido	 * Are we copied from the mblk or an aligned array ?
25853642Sguido	 */
25953642Sguido	if (ip == (ip_t *)m->b_rptr) {
26053642Sguido		ptrs[1] = m;
26153642Sguido		sizes[1] = hlen + mlen;
26253642Sguido		types[1] = 1;
26353642Sguido	} else {
26453642Sguido		ptrs[1] = ip;
26553642Sguido		sizes[1] = hlen + mlen;
26653642Sguido		types[1] = 0;
26753642Sguido	}
26853642Sguido# else
26953642Sguido	ptrs[1] = m;
27053642Sguido	sizes[1] = hlen + mlen;
27153642Sguido	types[1] = 1;
27253642Sguido# endif
27353642Sguido	return ipllog(IPL_LOGIPF, fin, ptrs, sizes, types, 2);
27453642Sguido}
27553642Sguido
27653642Sguido
27753642Sguido/*
27853642Sguido * ipllog
27953642Sguido */
28053642Sguidoint ipllog(dev, fin, items, itemsz, types, cnt)
28153642Sguidoint dev;
28253642Sguidofr_info_t *fin;
28353642Sguidovoid **items;
28453642Sguidosize_t *itemsz;
28553642Sguidoint *types, cnt;
28653642Sguido{
28753642Sguido	caddr_t buf, s;
28853642Sguido	iplog_t *ipl;
28953642Sguido	size_t len;
29053642Sguido	int i;
29153642Sguido
29253642Sguido	/*
29353642Sguido	 * Check to see if this log record has a CRC which matches the last
29453642Sguido	 * record logged.  If it does, just up the count on the previous one
29553642Sguido	 * rather than create a new one.
29653642Sguido	 */
29753642Sguido	MUTEX_ENTER(&ipl_mutex);
29853642Sguido	if (fin != NULL) {
29953642Sguido		if ((ipll[dev] != NULL) &&
30092685Sdarrenr		    bcmp((char *)fin, (char *)&iplcrc[dev], FI_LCSIZE) == 0) {
30153642Sguido			ipll[dev]->ipl_count++;
30253642Sguido			MUTEX_EXIT(&ipl_mutex);
30353642Sguido			return 1;
30453642Sguido		}
30592685Sdarrenr		bcopy((char *)fin, (char *)&iplcrc[dev], FI_LCSIZE);
30653642Sguido	} else
30792685Sdarrenr		bzero((char *)&iplcrc[dev], FI_LCSIZE);
30853642Sguido	MUTEX_EXIT(&ipl_mutex);
30953642Sguido
31053642Sguido	/*
31153642Sguido	 * Get the total amount of data to be logged.
31253642Sguido	 */
31392685Sdarrenr	for (i = 0, len = IPLOG_SIZE; i < cnt; i++)
31453642Sguido		len += itemsz[i];
31553642Sguido
31653642Sguido	/*
31753642Sguido	 * check that we have space to record this information and can
31853642Sguido	 * allocate that much.
31953642Sguido	 */
32053642Sguido	KMALLOCS(buf, caddr_t, len);
32153642Sguido	if (!buf)
32253642Sguido		return 0;
32353642Sguido	MUTEX_ENTER(&ipl_mutex);
32453642Sguido	if ((iplused[dev] + len) > IPLLOGSIZE) {
32553642Sguido		MUTEX_EXIT(&ipl_mutex);
32653642Sguido		KFREES(buf, len);
32753642Sguido		return 0;
32853642Sguido	}
32953642Sguido	iplused[dev] += len;
33053642Sguido	MUTEX_EXIT(&ipl_mutex);
33153642Sguido
33253642Sguido	/*
33353642Sguido	 * advance the log pointer to the next empty record and deduct the
33453642Sguido	 * amount of space we're going to use.
33553642Sguido	 */
33653642Sguido	ipl = (iplog_t *)buf;
33753642Sguido	ipl->ipl_magic = IPL_MAGIC;
33853642Sguido	ipl->ipl_count = 1;
33953642Sguido	ipl->ipl_next = NULL;
34053642Sguido	ipl->ipl_dsize = len;
34192685Sdarrenr# ifdef _KERNEL
34292685Sdarrenr#  if SOLARIS || defined(sun)
34353642Sguido	uniqtime((struct timeval *)&ipl->ipl_sec);
34492685Sdarrenr#  else
34592685Sdarrenr#   if BSD >= 199306 || defined(__FreeBSD__) || defined(__sgi)
34653642Sguido	microtime((struct timeval *)&ipl->ipl_sec);
34792685Sdarrenr#   endif
34853642Sguido#  endif
34992685Sdarrenr# else
35092685Sdarrenr	ipl->ipl_sec = 0;
35192685Sdarrenr	ipl->ipl_usec = 0;
35253642Sguido# endif
35353642Sguido
35453642Sguido	/*
35553642Sguido	 * Loop through all the items to be logged, copying each one to the
35653642Sguido	 * buffer.  Use bcopy for normal data or the mb_t copyout routine.
35753642Sguido	 */
35892685Sdarrenr	for (i = 0, s = buf + IPLOG_SIZE; i < cnt; i++) {
35953642Sguido		if (types[i] == 0)
36053642Sguido			bcopy(items[i], s, itemsz[i]);
36153642Sguido		else if (types[i] == 1) {
36292685Sdarrenr# if SOLARIS && defined(_KERNEL)
36353642Sguido			copyout_mblk(items[i], 0, itemsz[i], s);
36453642Sguido# else
36553642Sguido			m_copydata(items[i], 0, itemsz[i], s);
36653642Sguido# endif
36753642Sguido		}
36853642Sguido		s += itemsz[i];
36953642Sguido	}
37053642Sguido	MUTEX_ENTER(&ipl_mutex);
37153642Sguido	ipll[dev] = ipl;
37253642Sguido	*iplh[dev] = ipl;
37353642Sguido	iplh[dev] = &ipl->ipl_next;
37492685Sdarrenr# if SOLARIS && defined(_KERNEL)
37553642Sguido	cv_signal(&iplwait);
37653642Sguido	mutex_exit(&ipl_mutex);
37753642Sguido# else
37853642Sguido	MUTEX_EXIT(&ipl_mutex);
37992685Sdarrenr	WAKEUP(&iplh[dev]);
38053642Sguido# endif
38153642Sguido	return 1;
38253642Sguido}
38353642Sguido
38453642Sguido
38553642Sguidoint ipflog_read(unit, uio)
38653642Sguidominor_t unit;
38753642Sguidostruct uio *uio;
38853642Sguido{
38953642Sguido	size_t dlen, copied;
39053642Sguido	int error = 0;
39153642Sguido	iplog_t *ipl;
39253642Sguido# if defined(_KERNEL) && !SOLARIS
39353642Sguido	int s;
39453642Sguido# endif
39553642Sguido
39653642Sguido	/*
39753642Sguido	 * Sanity checks.  Make sure the minor # is valid and we're copying
39853642Sguido	 * a valid chunk of data.
39953642Sguido	 */
40053642Sguido	if (IPL_LOGMAX < unit)
40153642Sguido		return ENXIO;
40253642Sguido	if (!uio->uio_resid)
40353642Sguido		return 0;
40492685Sdarrenr	if (uio->uio_resid < IPLOG_SIZE)
40553642Sguido		return EINVAL;
40653642Sguido
40753642Sguido	/*
40853642Sguido	 * Lock the log so we can snapshot the variables.  Wait for a signal
40953642Sguido	 * if the log is empty.
41053642Sguido	 */
41153642Sguido	SPL_NET(s);
41253642Sguido	MUTEX_ENTER(&ipl_mutex);
41353642Sguido
41453642Sguido	while (!iplused[unit] || !iplt[unit]) {
41553642Sguido# if SOLARIS && defined(_KERNEL)
41653642Sguido		if (!cv_wait_sig(&iplwait, &ipl_mutex)) {
41753642Sguido			MUTEX_EXIT(&ipl_mutex);
41853642Sguido			return EINTR;
41953642Sguido		}
42053642Sguido# else
42153642Sguido		MUTEX_EXIT(&ipl_mutex);
42253642Sguido		error = SLEEP(&iplh[unit], "ipl sleep");
42380482Sdarrenr		if (error) {
42480482Sdarrenr			SPL_X(s);
42553642Sguido			return error;
42680482Sdarrenr		}
42753642Sguido		MUTEX_ENTER(&ipl_mutex);
42853642Sguido# endif /* SOLARIS */
42953642Sguido	}
43053642Sguido
43153642Sguido# if BSD >= 199306 || defined(__FreeBSD__)
43253642Sguido	uio->uio_rw = UIO_READ;
43353642Sguido# endif
43453642Sguido
43553642Sguido	for (copied = 0; (ipl = iplt[unit]); copied += dlen) {
43653642Sguido		dlen = ipl->ipl_dsize;
43753642Sguido		if (dlen > uio->uio_resid)
43853642Sguido			break;
43953642Sguido		/*
44053642Sguido		 * Don't hold the mutex over the uiomove call.
44153642Sguido		 */
44253642Sguido		iplt[unit] = ipl->ipl_next;
44353642Sguido		iplused[unit] -= dlen;
44453642Sguido		MUTEX_EXIT(&ipl_mutex);
44553642Sguido		error = UIOMOVE((caddr_t)ipl, dlen, UIO_READ, uio);
44692685Sdarrenr		MUTEX_ENTER(&ipl_mutex);
44753642Sguido		if (error) {
44853642Sguido			ipl->ipl_next = iplt[unit];
44953642Sguido			iplt[unit] = ipl;
45053642Sguido			iplused[unit] += dlen;
45153642Sguido			break;
45253642Sguido		}
45353642Sguido		KFREES((caddr_t)ipl, dlen);
45453642Sguido	}
45553642Sguido	if (!iplt[unit]) {
45653642Sguido		iplused[unit] = 0;
45753642Sguido		iplh[unit] = &iplt[unit];
45853642Sguido		ipll[unit] = NULL;
45953642Sguido	}
46053642Sguido
46153642Sguido	MUTEX_EXIT(&ipl_mutex);
46253642Sguido	SPL_X(s);
46353642Sguido	return error;
46453642Sguido}
46553642Sguido
46653642Sguido
46753642Sguidoint ipflog_clear(unit)
46853642Sguidominor_t unit;
46953642Sguido{
47053642Sguido	iplog_t *ipl;
47153642Sguido	int used;
47253642Sguido
47353642Sguido	MUTEX_ENTER(&ipl_mutex);
47453642Sguido	while ((ipl = iplt[unit])) {
47553642Sguido		iplt[unit] = ipl->ipl_next;
47653642Sguido		KFREES((caddr_t)ipl, ipl->ipl_dsize);
47753642Sguido	}
47853642Sguido	iplh[unit] = &iplt[unit];
47953642Sguido	ipll[unit] = NULL;
48053642Sguido	used = iplused[unit];
48153642Sguido	iplused[unit] = 0;
48292685Sdarrenr	bzero((char *)&iplcrc[unit], FI_LCSIZE);
48353642Sguido	MUTEX_EXIT(&ipl_mutex);
48453642Sguido	return used;
48553642Sguido}
48653642Sguido#endif /* IPFILTER_LOG */
487