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 22 static 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__ 42 struct mbuf; 43 struct 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 81 int 82 pcap_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 195 int 196 pcap_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 205 pcap_t * 206 pcap_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\ 223 your 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 319 int 320 pcap_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