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