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