ip_log.c revision 80482
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 80482 2001-07-28 11:58:26Z 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# include <sys/uio.h>
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/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# 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#  include <sys/ddi.h>
94#  ifdef IFF_DRVRLOCK /* IRIX6 */
95#   include <sys/hashing.h>
96#  endif
97# endif
98# if !(defined(__sgi) && !defined(IFF_DRVRLOCK)) /*IRIX<6*/
99#  include <netinet/in_var.h>
100# endif
101# include <netinet/in_systm.h>
102# include <netinet/ip.h>
103# include <netinet/tcp.h>
104# include <netinet/udp.h>
105# include <netinet/ip_icmp.h>
106# include <netinet/ip_var.h>
107# ifndef _KERNEL
108#  include <syslog.h>
109# endif
110# include "netinet/ip_compat.h"
111# include <netinet/tcpip.h>
112# include "netinet/ip_fil.h"
113# include "netinet/ip_proxy.h"
114# include "netinet/ip_nat.h"
115# include "netinet/ip_frag.h"
116# include "netinet/ip_state.h"
117# include "netinet/ip_auth.h"
118# if (__FreeBSD_version >= 300000)
119#  include <sys/malloc.h>
120# endif
121
122# ifndef MIN
123#  define	MIN(a,b)	(((a)<(b))?(a):(b))
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
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# if SOLARIS
225	ipfl.fl_unit = (u_char)ifp->ill_ppa;
226	bcopy(ifp->ill_name, ipfl.fl_ifname, MIN(ifp->ill_name_length, 4));
227	mlen = (flags & FR_LOGBODY) ? MIN(msgdsize(m) - hlen, 128) : 0;
228# else
229#  if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \
230	(defined(OpenBSD) && (OpenBSD >= 199603))
231	strncpy(ipfl.fl_ifname, ifp->if_xname, IFNAMSIZ);
232#  else
233	ipfl.fl_unit = (u_char)ifp->if_unit;
234	if ((ipfl.fl_ifname[0] = ifp->if_name[0]))
235		if ((ipfl.fl_ifname[1] = ifp->if_name[1]))
236			if ((ipfl.fl_ifname[2] = ifp->if_name[2]))
237				ipfl.fl_ifname[3] = ifp->if_name[3];
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	ptrs[0] = (void *)&ipfl;
251	sizes[0] = sizeof(ipfl);
252	types[0] = 0;
253# if SOLARIS
254	/*
255	 * Are we copied from the mblk or an aligned array ?
256	 */
257	if (ip == (ip_t *)m->b_rptr) {
258		ptrs[1] = m;
259		sizes[1] = hlen + mlen;
260		types[1] = 1;
261	} else {
262		ptrs[1] = ip;
263		sizes[1] = hlen + mlen;
264		types[1] = 0;
265	}
266# else
267	ptrs[1] = m;
268	sizes[1] = hlen + mlen;
269	types[1] = 1;
270# endif
271	return ipllog(IPL_LOGIPF, fin, ptrs, sizes, types, 2);
272}
273
274
275/*
276 * ipllog
277 */
278int ipllog(dev, fin, items, itemsz, types, cnt)
279int dev;
280fr_info_t *fin;
281void **items;
282size_t *itemsz;
283int *types, cnt;
284{
285	caddr_t buf, s;
286	iplog_t *ipl;
287	size_t len;
288	int i;
289
290	/*
291	 * Check to see if this log record has a CRC which matches the last
292	 * record logged.  If it does, just up the count on the previous one
293	 * rather than create a new one.
294	 */
295	MUTEX_ENTER(&ipl_mutex);
296	if (fin != NULL) {
297		if ((ipll[dev] != NULL) &&
298		    bcmp((char *)fin, (char *)&iplcrc[dev], FI_CSIZE) == 0) {
299			ipll[dev]->ipl_count++;
300			MUTEX_EXIT(&ipl_mutex);
301			return 1;
302		}
303		bcopy((char *)fin, (char *)&iplcrc[dev], FI_CSIZE);
304	} else
305		bzero((char *)&iplcrc[dev], FI_CSIZE);
306	MUTEX_EXIT(&ipl_mutex);
307
308	/*
309	 * Get the total amount of data to be logged.
310	 */
311	for (i = 0, len = sizeof(iplog_t); i < cnt; i++)
312		len += itemsz[i];
313
314	/*
315	 * check that we have space to record this information and can
316	 * allocate that much.
317	 */
318	KMALLOCS(buf, caddr_t, len);
319	if (!buf)
320		return 0;
321	MUTEX_ENTER(&ipl_mutex);
322	if ((iplused[dev] + len) > IPLLOGSIZE) {
323		MUTEX_EXIT(&ipl_mutex);
324		KFREES(buf, len);
325		return 0;
326	}
327	iplused[dev] += len;
328	MUTEX_EXIT(&ipl_mutex);
329
330	/*
331	 * advance the log pointer to the next empty record and deduct the
332	 * amount of space we're going to use.
333	 */
334	ipl = (iplog_t *)buf;
335	ipl->ipl_magic = IPL_MAGIC;
336	ipl->ipl_count = 1;
337	ipl->ipl_next = NULL;
338	ipl->ipl_dsize = len;
339# if SOLARIS || defined(sun)
340	uniqtime((struct timeval *)&ipl->ipl_sec);
341# else
342#  if BSD >= 199306 || defined(__FreeBSD__) || defined(__sgi)
343	microtime((struct timeval *)&ipl->ipl_sec);
344#  endif
345# endif
346
347	/*
348	 * Loop through all the items to be logged, copying each one to the
349	 * buffer.  Use bcopy for normal data or the mb_t copyout routine.
350	 */
351	for (i = 0, s = buf + sizeof(*ipl); i < cnt; i++) {
352		if (types[i] == 0)
353			bcopy(items[i], s, itemsz[i]);
354		else if (types[i] == 1) {
355# if SOLARIS
356			copyout_mblk(items[i], 0, itemsz[i], s);
357# else
358			m_copydata(items[i], 0, itemsz[i], s);
359# endif
360		}
361		s += itemsz[i];
362	}
363	MUTEX_ENTER(&ipl_mutex);
364	ipll[dev] = ipl;
365	*iplh[dev] = ipl;
366	iplh[dev] = &ipl->ipl_next;
367# if SOLARIS
368	cv_signal(&iplwait);
369	mutex_exit(&ipl_mutex);
370# else
371	MUTEX_EXIT(&ipl_mutex);
372	wakeup(&iplh[dev]);
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		return EINVAL;
399
400	/*
401	 * Lock the log so we can snapshot the variables.  Wait for a signal
402	 * if the log is empty.
403	 */
404	SPL_NET(s);
405	MUTEX_ENTER(&ipl_mutex);
406
407	while (!iplused[unit] || !iplt[unit]) {
408# if SOLARIS && defined(_KERNEL)
409		if (!cv_wait_sig(&iplwait, &ipl_mutex)) {
410			MUTEX_EXIT(&ipl_mutex);
411			return EINTR;
412		}
413# else
414		MUTEX_EXIT(&ipl_mutex);
415		error = SLEEP(&iplh[unit], "ipl sleep");
416		if (error) {
417			SPL_X(s);
418			return error;
419		}
420		MUTEX_ENTER(&ipl_mutex);
421# endif /* SOLARIS */
422	}
423
424# if BSD >= 199306 || defined(__FreeBSD__)
425	uio->uio_rw = UIO_READ;
426# endif
427
428	for (copied = 0; (ipl = iplt[unit]); copied += dlen) {
429		dlen = ipl->ipl_dsize;
430		if (dlen > uio->uio_resid)
431			break;
432		/*
433		 * Don't hold the mutex over the uiomove call.
434		 */
435		iplt[unit] = ipl->ipl_next;
436		iplused[unit] -= dlen;
437		MUTEX_EXIT(&ipl_mutex);
438		error = UIOMOVE((caddr_t)ipl, dlen, UIO_READ, uio);
439		if (error) {
440			MUTEX_ENTER(&ipl_mutex);
441			ipl->ipl_next = iplt[unit];
442			iplt[unit] = ipl;
443			iplused[unit] += dlen;
444			break;
445		}
446		KFREES((caddr_t)ipl, dlen);
447		MUTEX_ENTER(&ipl_mutex);
448	}
449	if (!iplt[unit]) {
450		iplused[unit] = 0;
451		iplh[unit] = &iplt[unit];
452		ipll[unit] = NULL;
453	}
454
455	MUTEX_EXIT(&ipl_mutex);
456	SPL_X(s);
457	return error;
458}
459
460
461int ipflog_clear(unit)
462minor_t unit;
463{
464	iplog_t *ipl;
465	int used;
466
467	MUTEX_ENTER(&ipl_mutex);
468	while ((ipl = iplt[unit])) {
469		iplt[unit] = ipl->ipl_next;
470		KFREES((caddr_t)ipl, ipl->ipl_dsize);
471	}
472	iplh[unit] = &iplt[unit];
473	ipll[unit] = NULL;
474	used = iplused[unit];
475	iplused[unit] = 0;
476	bzero((char *)&iplcrc[unit], FI_CSIZE);
477	MUTEX_EXIT(&ipl_mutex);
478	return used;
479}
480#endif /* IPFILTER_LOG */
481