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