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