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