pcap-pf.c revision 17683
1/* 2 * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that: (1) source code distributions 7 * retain the above copyright notice and this paragraph in its entirety, (2) 8 * distributions including binary code include the above copyright notice and 9 * this paragraph in its entirety in the documentation or other materials 10 * provided with the distribution, and (3) all advertising materials mentioning 11 * features or use of this software display the following acknowledgement: 12 * ``This product includes software developed by the University of California, 13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 14 * the University nor the names of its contributors may be used to endorse 15 * or promote products derived from this software without specific prior 16 * written permission. 17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 20 */ 21#ifndef lint 22static char rcsid[] = 23 "@(#)$Header: pcap-pf.c,v 1.50 96/07/16 14:30:28 vern Exp $ (LBL)"; 24#endif 25 26/* 27 * packet filter subroutines for tcpdump 28 * Extraction/creation by Jeffrey Mogul, DECWRL 29 * 30 * Extracted from tcpdump.c. 31 */ 32 33#include <sys/types.h> 34#include <sys/time.h> 35#include <sys/timeb.h> 36#include <sys/socket.h> 37#include <sys/file.h> 38#include <sys/ioctl.h> 39#include <net/pfilt.h> 40 41#if __STDC__ 42struct mbuf; 43struct rtentry; 44#endif 45 46#include <net/if.h> 47 48#include <netinet/in.h> 49#include <netinet/in_systm.h> 50#include <netinet/ip.h> 51#include <netinet/if_ether.h> 52#include <netinet/ip_var.h> 53#include <netinet/udp.h> 54#include <netinet/udp_var.h> 55#include <netinet/tcp.h> 56#include <netinet/tcpip.h> 57 58#include <ctype.h> 59#include <errno.h> 60#include <netdb.h> 61#include <stdio.h> 62#include <stdlib.h> 63#include <string.h> 64#include <unistd.h> 65 66#include "pcap-int.h" 67 68#include "gnuc.h" 69#ifdef HAVE_OS_PROTO_H 70#include "os-proto.h" 71#endif 72 73/* 74 * BUFSPACE is the size in bytes of the packet read buffer. Most tcpdump 75 * applications aren't going to need more than 200 bytes of packet header 76 * and the read shouldn't return more packets than packetfilter's internal 77 * queue limit (bounded at 256). 78 */ 79#define BUFSPACE (200 * 256) 80 81int 82pcap_read(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) 83{ 84 register u_char *p, *bp; 85 struct bpf_insn *fcode; 86 register int cc, n, buflen, inc; 87 register struct enstamp *sp; 88#ifdef LBL_ALIGN 89 struct enstamp stamp; 90#endif 91#ifdef PCAP_FDDIPAD 92 register int pad; 93#endif 94 95 fcode = pc->md.use_bpf ? NULL : pc->fcode.bf_insns; 96 again: 97 cc = pc->cc; 98 if (cc == 0) { 99 cc = read(pc->fd, (char *)pc->buffer + pc->offset, pc->bufsize); 100 if (cc < 0) { 101 if (errno == EWOULDBLOCK) 102 return (0); 103 if (errno == EINVAL && 104 lseek(pc->fd, 0L, SEEK_CUR) + pc->bufsize < 0) { 105 /* 106 * Due to a kernel bug, after 2^31 bytes, 107 * the kernel file offset overflows and 108 * read fails with EINVAL. The lseek() 109 * to 0 will fix things. 110 */ 111 (void)lseek(pc->fd, 0L, SEEK_SET); 112 goto again; 113 } 114 sprintf(pc->errbuf, "pf read: %s", 115 pcap_strerror(errno)); 116 return (-1); 117 } 118 bp = pc->buffer + pc->offset; 119 } else 120 bp = pc->bp; 121 /* 122 * Loop through each packet. 123 */ 124 n = 0; 125#ifdef PCAP_FDDIPAD 126 if (pc->linktype == DLT_FDDI) 127 pad = pcap_fddipad; 128 else 129 pad = 0; 130#endif 131 while (cc > 0) { 132 if (cc < sizeof(*sp)) { 133 sprintf(pc->errbuf, "pf short read (%d)", cc); 134 return (-1); 135 } 136#ifdef LBL_ALIGN 137 if ((long)bp & 3) { 138 sp = &stamp; 139 memcpy((char *)sp, (char *)bp, sizeof(*sp)); 140 } else 141#endif 142 sp = (struct enstamp *)bp; 143 if (sp->ens_stamplen != sizeof(*sp)) { 144 sprintf(pc->errbuf, "pf short stamplen (%d)", 145 sp->ens_stamplen); 146 return (-1); 147 } 148 149 p = bp + sp->ens_stamplen; 150 buflen = sp->ens_count; 151 if (buflen > pc->snapshot) 152 buflen = pc->snapshot; 153 154 /* Calculate inc before possible pad update */ 155 inc = ENALIGN(buflen + sp->ens_stamplen); 156 cc -= inc; 157 bp += inc; 158#ifdef PCAP_FDDIPAD 159 p += pad; 160 buflen -= pad; 161#endif 162 pc->md.TotPkts++; 163 pc->md.TotDrops += sp->ens_dropped; 164 pc->md.TotMissed = sp->ens_ifoverflows; 165 if (pc->md.OrigMissed < 0) 166 pc->md.OrigMissed = pc->md.TotMissed; 167 168 /* 169 * Short-circuit evaluation: if using BPF filter 170 * in kernel, no need to do it now. 171 */ 172 if (fcode == NULL || 173 bpf_filter(fcode, p, sp->ens_count, buflen)) { 174 struct pcap_pkthdr h; 175 pc->md.TotAccepted++; 176 h.ts = sp->ens_tstamp; 177#ifdef PCAP_FDDIPAD 178 h.len = sp->ens_count - pad; 179#else 180 h.len = sp->ens_count; 181#endif 182 h.caplen = buflen; 183 (*callback)(user, &h, p); 184 if (++n >= cnt && cnt > 0) { 185 pc->cc = cc; 186 pc->bp = bp; 187 return (n); 188 } 189 } 190 } 191 pc->cc = 0; 192 return (n); 193} 194 195int 196pcap_stats(pcap_t *p, struct pcap_stat *ps) 197{ 198 199 ps->ps_recv = p->md.TotAccepted; 200 ps->ps_drop = p->md.TotDrops; 201 ps->ps_ifdrop = p->md.TotMissed - p->md.OrigMissed; 202 return (0); 203} 204 205pcap_t * 206pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) 207{ 208 pcap_t *p; 209 short enmode; 210 int backlog = -1; /* request the most */ 211 struct enfilter Filter; 212 struct endevp devparams; 213 214 p = (pcap_t *)malloc(sizeof(*p)); 215 if (p == NULL) { 216 sprintf(ebuf, "pcap_open_live: %s", pcap_strerror(errno)); 217 return (0); 218 } 219 bzero((char *)p, sizeof(*p)); 220 p->fd = pfopen(device, O_RDONLY); 221 if (p->fd < 0) { 222 sprintf(ebuf, "pf open: %s: %s\n\ 223your system may not be properly configured; see \"man packetfilter(4)\"\n", 224 device, pcap_strerror(errno)); 225 goto bad; 226 } 227 p->md.OrigMissed = -1; 228 enmode = ENTSTAMP|ENBATCH|ENNONEXCL; 229 if (promisc) 230 enmode |= ENPROMISC; 231 if (ioctl(p->fd, EIOCMBIS, (caddr_t)&enmode) < 0) { 232 sprintf(ebuf, "EIOCMBIS: %s", pcap_strerror(errno)); 233 goto bad; 234 } 235#ifdef ENCOPYALL 236 /* Try to set COPYALL mode so that we see packets to ourself */ 237 enmode = ENCOPYALL; 238 (void)ioctl(p->fd, EIOCMBIS, (caddr_t)&enmode);/* OK if this fails */ 239#endif 240 /* set the backlog */ 241 if (ioctl(p->fd, EIOCSETW, (caddr_t)&backlog) < 0) { 242 sprintf(ebuf, "EIOCSETW: %s", pcap_strerror(errno)); 243 goto bad; 244 } 245 /* discover interface type */ 246 if (ioctl(p->fd, EIOCDEVP, (caddr_t)&devparams) < 0) { 247 sprintf(ebuf, "EIOCDEVP: %s", pcap_strerror(errno)); 248 goto bad; 249 } 250 /* HACK: to compile prior to Ultrix 4.2 */ 251#ifndef ENDT_FDDI 252#define ENDT_FDDI 4 253#endif 254 switch (devparams.end_dev_type) { 255 256 case ENDT_10MB: 257 p->linktype = DLT_EN10MB; 258 p->offset = 2; 259 break; 260 261 case ENDT_FDDI: 262 p->linktype = DLT_FDDI; 263 break; 264 265 default: 266 /* 267 * XXX 268 * Currently, the Ultrix packet filter supports only 269 * Ethernet and FDDI. Eventually, support for SLIP and PPP 270 * (and possibly others: T1?) should be added. 271 */ 272#ifdef notdef 273 warning( 274 "Packet filter data-link type %d unknown, assuming Ethernet", 275 devparams.end_dev_type); 276#endif 277 p->linktype = DLT_EN10MB; 278 p->offset = 2; 279 break; 280 } 281 /* set truncation */ 282#ifdef PCAP_FDDIPAD 283 if (p->linktype == DLT_FDDI) 284 /* packetfilter includes the padding in the snapshot */ 285 snaplen += pcap_fddipad; 286#endif 287 if (ioctl(p->fd, EIOCTRUNCATE, (caddr_t)&snaplen) < 0) { 288 sprintf(ebuf, "EIOCTRUNCATE: %s", pcap_strerror(errno)); 289 goto bad; 290 } 291 p->snapshot = snaplen; 292 /* accept all packets */ 293 Filter.enf_Priority = 37; /* anything > 2 */ 294 Filter.enf_FilterLen = 0; /* means "always true" */ 295 if (ioctl(p->fd, EIOCSETF, (caddr_t)&Filter) < 0) { 296 sprintf(ebuf, "EIOCSETF: %s", pcap_strerror(errno)); 297 goto bad; 298 } 299 300 if (to_ms != 0) { 301 struct timeval timeout; 302 timeout.tv_sec = to_ms / 1000; 303 timeout.tv_usec = (to_ms * 1000) % 1000000; 304 if (ioctl(p->fd, EIOCSRTIMEOUT, (caddr_t)&timeout) < 0) { 305 sprintf(ebuf, "EIOCSRTIMEOUT: %s", 306 pcap_strerror(errno)); 307 goto bad; 308 } 309 } 310 p->bufsize = BUFSPACE; 311 p->buffer = (u_char*)malloc(p->bufsize + p->offset); 312 313 return (p); 314 bad: 315 free(p); 316 return (NULL); 317} 318 319int 320pcap_setfilter(pcap_t *p, struct bpf_program *fp) 321{ 322 /* 323 * See if BIOCSETF works. If it does, the kernel supports 324 * BPF-style filters, and we do not need to do post-filtering. 325 */ 326 p->md.use_bpf = (ioctl(p->fd, BIOCSETF, (caddr_t)fp) >= 0); 327 if (p->md.use_bpf) { 328 struct bpf_version bv; 329 330 if (ioctl(p->fd, BIOCVERSION, (caddr_t)&bv) < 0) { 331 sprintf(p->errbuf, "BIOCVERSION: %s", 332 pcap_strerror(errno)); 333 return (-1); 334 } 335 else if (bv.bv_major != BPF_MAJOR_VERSION || 336 bv.bv_minor < BPF_MINOR_VERSION) { 337 fprintf(stderr, 338 "requires bpf language %d.%d or higher; kernel is %d.%d", 339 BPF_MAJOR_VERSION, BPF_MINOR_VERSION, 340 bv.bv_major, bv.bv_minor); 341 /* don't give up, just be inefficient */ 342 p->md.use_bpf = 0; 343 } 344 } else 345 p->fcode = *fp; 346 347 /*XXX this goes in tcpdump*/ 348 if (p->md.use_bpf) 349 fprintf(stderr, "tcpdump: Using kernel BPF filter\n"); 350 else 351 fprintf(stderr, "tcpdump: Filtering in user process\n"); 352 return (0); 353} 354