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