ip_log.c revision 63523
1/*
2 * Copyright (C) 1997-2000 by Darren Reed.
3 *
4 * Redistribution and use in source and binary forms are permitted
5 * provided that this notice is preserved and due credit is given
6 * to the original author and the contributors.
7 *
8 * $Id: ip_log.c,v 2.5.2.1 2000/07/19 13:11:47 darrenr Exp $
9 * $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_log.c 63523 2000-07-19 14:02:09Z darrenr $
10 */
11#include <sys/param.h>
12#if defined(KERNEL) && !defined(_KERNEL)
13# define       _KERNEL
14#endif
15#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM)
16# include "opt_ipfilter_log.h"
17#endif
18#ifdef  __FreeBSD__
19# if defined(IPFILTER_LKM) || defined(_KERNEL)
20#  if !defined(__FreeBSD_version)
21#   include <sys/osreldate.h>
22#  endif
23#  if !defined(IPFILTER_LKM)
24#   if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
25#    include "opt_ipfilter.h"
26#   endif
27#  endif
28# else
29#  ifdef KLD_MODULE
30#   include <sys/osreldate.h>
31#  else
32#   include <osreldate.h>
33#  endif
34# endif
35#endif
36#ifdef  IPFILTER_LOG
37# ifndef SOLARIS
38#  define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4)))
39# endif
40# ifndef _KERNEL
41#  include <stdio.h>
42#  include <string.h>
43#  include <stdlib.h>
44#  include <ctype.h>
45# endif
46# include <sys/errno.h>
47# include <sys/types.h>
48# include <sys/file.h>
49# if __FreeBSD_version >= 220000 && defined(_KERNEL)
50#  include <sys/fcntl.h>
51#  include <sys/filio.h>
52# else
53#  include <sys/ioctl.h>
54# endif
55# include <sys/time.h>
56# if defined(_KERNEL) && !defined(linux)
57#  include <sys/systm.h>
58# endif
59# include <sys/uio.h>
60# if !SOLARIS
61#  if (NetBSD > 199609) || (OpenBSD > 199603) || (__FreeBSD_version >= 300000)
62#   include <sys/dirent.h>
63#  else
64#   include <sys/dir.h>
65#  endif
66#  ifndef linux
67#   include <sys/mbuf.h>
68#  endif
69# else
70#  include <sys/filio.h>
71#  include <sys/cred.h>
72#  include <sys/ddi.h>
73#  include <sys/sunddi.h>
74#  include <sys/ksynch.h>
75#  include <sys/kmem.h>
76#  include <sys/mkdev.h>
77#  include <sys/dditypes.h>
78#  include <sys/cmn_err.h>
79# endif
80# ifndef linux
81#  include <sys/protosw.h>
82# endif
83# include <sys/socket.h>
84
85# include <net/if.h>
86# ifdef sun
87#  include <net/af.h>
88# endif
89# if __FreeBSD_version >= 300000
90#  include <net/if_var.h>
91# endif
92# include <net/route.h>
93# include <netinet/in.h>
94# ifdef __sgi
95#  include <sys/ddi.h>
96#  ifdef IFF_DRVRLOCK /* IRIX6 */
97#   include <sys/hashing.h>
98#  endif
99# endif
100# if !defined(linux) && !(defined(__sgi) && !defined(IFF_DRVRLOCK)) /*IRIX<6*/
101#  include <netinet/in_var.h>
102# endif
103# include <netinet/in_systm.h>
104# include <netinet/ip.h>
105# include <netinet/tcp.h>
106# include <netinet/udp.h>
107# include <netinet/ip_icmp.h>
108# ifndef linux
109#  include <netinet/ip_var.h>
110# endif
111# ifndef _KERNEL
112#  include <syslog.h>
113# endif
114# include "netinet/ip_compat.h"
115# include <netinet/tcpip.h>
116# include "netinet/ip_fil.h"
117# include "netinet/ip_proxy.h"
118# include "netinet/ip_nat.h"
119# include "netinet/ip_frag.h"
120# include "netinet/ip_state.h"
121# include "netinet/ip_auth.h"
122# if (__FreeBSD_version >= 300000)
123#  include <sys/malloc.h>
124# endif
125
126# ifndef MIN
127#  define	MIN(a,b)	(((a)<(b))?(a):(b))
128# endif
129
130
131# if SOLARIS || defined(__sgi)
132extern	kmutex_t	ipl_mutex;
133#  if SOLARIS
134extern	kcondvar_t	iplwait;
135#  endif
136# endif
137
138iplog_t	**iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1], *ipll[IPL_LOGMAX+1];
139size_t	iplused[IPL_LOGMAX+1];
140static fr_info_t	iplcrc[IPL_LOGMAX+1];
141# ifdef	linux
142static struct wait_queue *iplwait[IPL_LOGMAX+1];
143# endif
144
145
146/*
147 * Initialise log buffers & pointers.  Also iniialised the CRC to a local
148 * secret for use in calculating the "last log checksum".
149 */
150void ipflog_init()
151{
152	int	i;
153
154	for (i = IPL_LOGMAX; i >= 0; i--) {
155		iplt[i] = NULL;
156		ipll[i] = NULL;
157		iplh[i] = &iplt[i];
158		iplused[i] = 0;
159		bzero((char *)&iplcrc[i], sizeof(iplcrc[i]));
160	}
161}
162
163
164/*
165 * ipflog
166 * Create a log record for a packet given that it has been triggered by a
167 * rule (or the default setting).  Calculate the transport protocol header
168 * size using predetermined size of a couple of popular protocols and thus
169 * how much data to copy into the log, including part of the data body if
170 * requested.
171 */
172int ipflog(flags, ip, fin, m)
173u_int flags;
174ip_t *ip;
175fr_info_t *fin;
176mb_t *m;
177{
178	ipflog_t ipfl;
179	register size_t mlen, hlen;
180	size_t sizes[2];
181	void *ptrs[2];
182	int types[2];
183	u_char p;
184# if SOLARIS
185	ill_t *ifp = fin->fin_ifp;
186# else
187	struct ifnet *ifp = fin->fin_ifp;
188# endif
189
190	/*
191	 * calculate header size.
192	 */
193	hlen = fin->fin_hlen;
194	if (fin->fin_off == 0) {
195		p = fin->fin_fi.fi_p;
196		if (p == IPPROTO_TCP)
197			hlen += MIN(sizeof(tcphdr_t), fin->fin_dlen);
198		else if (p == IPPROTO_UDP)
199			hlen += MIN(sizeof(udphdr_t), fin->fin_dlen);
200		else if (p == IPPROTO_ICMP) {
201			struct icmp *icmp;
202
203			icmp = (struct icmp *)fin->fin_dp;
204
205			/*
206			 * For ICMP, if the packet is an error packet, also
207			 * include the information about the packet which
208			 * caused the error.
209			 */
210			switch (icmp->icmp_type)
211			{
212			case ICMP_UNREACH :
213			case ICMP_SOURCEQUENCH :
214			case ICMP_REDIRECT :
215			case ICMP_TIMXCEED :
216			case ICMP_PARAMPROB :
217				hlen += MIN(sizeof(struct icmp) + 8,
218					    fin->fin_dlen);
219				break;
220			default :
221				hlen += MIN(sizeof(struct icmp),
222					    fin->fin_dlen);
223				break;
224			}
225		}
226	}
227	/*
228	 * Get the interface number and name to which this packet is
229	 * currently associated.
230	 */
231# if SOLARIS
232	ipfl.fl_unit = (u_char)ifp->ill_ppa;
233	bcopy(ifp->ill_name, ipfl.fl_ifname, MIN(ifp->ill_name_length, 4));
234	mlen = (flags & FR_LOGBODY) ? MIN(msgdsize(m) - hlen, 128) : 0;
235# else
236#  if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \
237	(defined(OpenBSD) && (OpenBSD >= 199603))
238	strncpy(ipfl.fl_ifname, ifp->if_xname, IFNAMSIZ);
239#  else
240#   ifndef linux
241	ipfl.fl_unit = (u_char)ifp->if_unit;
242#   endif
243	if ((ipfl.fl_ifname[0] = ifp->if_name[0]))
244		if ((ipfl.fl_ifname[1] = ifp->if_name[1]))
245			if ((ipfl.fl_ifname[2] = ifp->if_name[2]))
246				ipfl.fl_ifname[3] = ifp->if_name[3];
247#  endif
248	mlen = (flags & FR_LOGBODY) ? MIN(fin->fin_plen - hlen, 128) : 0;
249# endif
250	ipfl.fl_plen = (u_char)mlen;
251	ipfl.fl_hlen = (u_char)hlen;
252	ipfl.fl_rule = fin->fin_rule;
253	ipfl.fl_group = fin->fin_group;
254	if (fin->fin_fr != NULL)
255		ipfl.fl_loglevel = fin->fin_fr->fr_loglevel;
256	else
257		ipfl.fl_loglevel = 0xffff;
258	ipfl.fl_flags = flags;
259	ptrs[0] = (void *)&ipfl;
260	sizes[0] = sizeof(ipfl);
261	types[0] = 0;
262# if SOLARIS
263	/*
264	 * Are we copied from the mblk or an aligned array ?
265	 */
266	if (ip == (ip_t *)m->b_rptr) {
267		ptrs[1] = m;
268		sizes[1] = hlen + mlen;
269		types[1] = 1;
270	} else {
271		ptrs[1] = ip;
272		sizes[1] = hlen + mlen;
273		types[1] = 0;
274	}
275# else
276	ptrs[1] = m;
277	sizes[1] = hlen + mlen;
278	types[1] = 1;
279# endif
280	return ipllog(IPL_LOGIPF, fin, ptrs, sizes, types, 2);
281}
282
283
284/*
285 * ipllog
286 */
287int ipllog(dev, fin, items, itemsz, types, cnt)
288int dev;
289fr_info_t *fin;
290void **items;
291size_t *itemsz;
292int *types, cnt;
293{
294	caddr_t buf, s;
295	iplog_t *ipl;
296	size_t len;
297	int i;
298
299	/*
300	 * Check to see if this log record has a CRC which matches the last
301	 * record logged.  If it does, just up the count on the previous one
302	 * rather than create a new one.
303	 */
304	MUTEX_ENTER(&ipl_mutex);
305	if (fin != NULL) {
306		if ((ipll[dev] != NULL) &&
307		    bcmp((char *)fin, (char *)&iplcrc[dev], FI_CSIZE) == 0) {
308			ipll[dev]->ipl_count++;
309			MUTEX_EXIT(&ipl_mutex);
310			return 1;
311		}
312		bcopy((char *)fin, (char *)&iplcrc[dev], FI_CSIZE);
313	} else
314		bzero((char *)&iplcrc[dev], FI_CSIZE);
315	MUTEX_EXIT(&ipl_mutex);
316
317	/*
318	 * Get the total amount of data to be logged.
319	 */
320	for (i = 0, len = sizeof(iplog_t); i < cnt; i++)
321		len += itemsz[i];
322
323	/*
324	 * check that we have space to record this information and can
325	 * allocate that much.
326	 */
327	KMALLOCS(buf, caddr_t, len);
328	if (!buf)
329		return 0;
330	MUTEX_ENTER(&ipl_mutex);
331	if ((iplused[dev] + len) > IPLLOGSIZE) {
332		MUTEX_EXIT(&ipl_mutex);
333		KFREES(buf, len);
334		return 0;
335	}
336	iplused[dev] += len;
337	MUTEX_EXIT(&ipl_mutex);
338
339	/*
340	 * advance the log pointer to the next empty record and deduct the
341	 * amount of space we're going to use.
342	 */
343	ipl = (iplog_t *)buf;
344	ipl->ipl_magic = IPL_MAGIC;
345	ipl->ipl_count = 1;
346	ipl->ipl_next = NULL;
347	ipl->ipl_dsize = len;
348# if SOLARIS || defined(sun) || defined(linux)
349	uniqtime((struct timeval *)&ipl->ipl_sec);
350# else
351#  if BSD >= 199306 || defined(__FreeBSD__) || defined(__sgi)
352	microtime((struct timeval *)&ipl->ipl_sec);
353#  endif
354# endif
355
356	/*
357	 * Loop through all the items to be logged, copying each one to the
358	 * buffer.  Use bcopy for normal data or the mb_t copyout routine.
359	 */
360	for (i = 0, s = buf + sizeof(*ipl); i < cnt; i++) {
361		if (types[i] == 0)
362			bcopy(items[i], s, itemsz[i]);
363		else if (types[i] == 1) {
364# if SOLARIS
365			copyout_mblk(items[i], 0, itemsz[i], s);
366# else
367			m_copydata(items[i], 0, itemsz[i], s);
368# endif
369		}
370		s += itemsz[i];
371	}
372	MUTEX_ENTER(&ipl_mutex);
373	ipll[dev] = ipl;
374	*iplh[dev] = ipl;
375	iplh[dev] = &ipl->ipl_next;
376# if SOLARIS
377	cv_signal(&iplwait);
378	mutex_exit(&ipl_mutex);
379# else
380	MUTEX_EXIT(&ipl_mutex);
381#  ifdef linux
382	wake_up_interruptible(&iplwait[dev]);
383#  else
384	wakeup(&iplh[dev]);
385#  endif
386# endif
387	return 1;
388}
389
390
391int ipflog_read(unit, uio)
392minor_t unit;
393struct uio *uio;
394{
395	size_t dlen, copied;
396	int error = 0;
397	iplog_t *ipl;
398# if defined(_KERNEL) && !SOLARIS
399	int s;
400# endif
401
402	/*
403	 * Sanity checks.  Make sure the minor # is valid and we're copying
404	 * a valid chunk of data.
405	 */
406	if (IPL_LOGMAX < unit)
407		return ENXIO;
408	if (!uio->uio_resid)
409		return 0;
410	if ((uio->uio_resid < sizeof(iplog_t)) ||
411	    (uio->uio_resid > IPLLOGSIZE))
412		return EINVAL;
413
414	/*
415	 * Lock the log so we can snapshot the variables.  Wait for a signal
416	 * if the log is empty.
417	 */
418	SPL_NET(s);
419	MUTEX_ENTER(&ipl_mutex);
420
421	while (!iplused[unit] || !iplt[unit]) {
422# if SOLARIS && defined(_KERNEL)
423		if (!cv_wait_sig(&iplwait, &ipl_mutex)) {
424			MUTEX_EXIT(&ipl_mutex);
425			return EINTR;
426		}
427# else
428#  ifdef linux
429		interruptible_sleep_on(&iplwait[unit]);
430		if (current->signal & ~current->blocked)
431			return -EINTR;
432#  else
433		MUTEX_EXIT(&ipl_mutex);
434		SPL_X(s);
435		error = SLEEP(&iplh[unit], "ipl sleep");
436		if (error)
437			return error;
438		SPL_NET(s);
439		MUTEX_ENTER(&ipl_mutex);
440#  endif /* linux */
441# endif /* SOLARIS */
442	}
443
444# if BSD >= 199306 || defined(__FreeBSD__)
445	uio->uio_rw = UIO_READ;
446# endif
447
448	for (copied = 0; (ipl = iplt[unit]); copied += dlen) {
449		dlen = ipl->ipl_dsize;
450		if (dlen > uio->uio_resid)
451			break;
452		/*
453		 * Don't hold the mutex over the uiomove call.
454		 */
455		iplt[unit] = ipl->ipl_next;
456		iplused[unit] -= dlen;
457		MUTEX_EXIT(&ipl_mutex);
458		SPL_X(s);
459		error = UIOMOVE((caddr_t)ipl, dlen, UIO_READ, uio);
460		if (error) {
461			SPL_NET(s);
462			MUTEX_ENTER(&ipl_mutex);
463			ipl->ipl_next = iplt[unit];
464			iplt[unit] = ipl;
465			iplused[unit] += dlen;
466			break;
467		}
468		KFREES((caddr_t)ipl, dlen);
469		SPL_NET(s);
470		MUTEX_ENTER(&ipl_mutex);
471	}
472	if (!iplt[unit]) {
473		iplused[unit] = 0;
474		iplh[unit] = &iplt[unit];
475		ipll[unit] = NULL;
476	}
477
478	MUTEX_EXIT(&ipl_mutex);
479	SPL_X(s);
480# ifdef 	linux
481	if (!error)
482		return (int)copied;
483	return -error;
484# else
485	return error;
486# endif
487}
488
489
490int ipflog_clear(unit)
491minor_t unit;
492{
493	iplog_t *ipl;
494	int used;
495
496	MUTEX_ENTER(&ipl_mutex);
497	while ((ipl = iplt[unit])) {
498		iplt[unit] = ipl->ipl_next;
499		KFREES((caddr_t)ipl, ipl->ipl_dsize);
500	}
501	iplh[unit] = &iplt[unit];
502	ipll[unit] = NULL;
503	used = iplused[unit];
504	iplused[unit] = 0;
505	bzero((char *)&iplcrc[unit], FI_CSIZE);
506	MUTEX_EXIT(&ipl_mutex);
507	return used;
508}
509#endif /* IPFILTER_LOG */
510