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