pcap-pf.c revision 26175
117683Spst/* 217683Spst * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996 317683Spst * The Regents of the University of California. All rights reserved. 417683Spst * 517683Spst * Redistribution and use in source and binary forms, with or without 617683Spst * modification, are permitted provided that: (1) source code distributions 717683Spst * retain the above copyright notice and this paragraph in its entirety, (2) 817683Spst * distributions including binary code include the above copyright notice and 917683Spst * this paragraph in its entirety in the documentation or other materials 1017683Spst * provided with the distribution, and (3) all advertising materials mentioning 1117683Spst * features or use of this software display the following acknowledgement: 1217683Spst * ``This product includes software developed by the University of California, 1317683Spst * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 1417683Spst * the University nor the names of its contributors may be used to endorse 1517683Spst * or promote products derived from this software without specific prior 1617683Spst * written permission. 1717683Spst * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 1817683Spst * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 1917683Spst * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 2026175Sfenner * 2126175Sfenner * packet filter subroutines for tcpdump 2226175Sfenner * Extraction/creation by Jeffrey Mogul, DECWRL 2317683Spst */ 2426175Sfenner 2517683Spst#ifndef lint 2626175Sfennerstatic const char rcsid[] = 2726175Sfenner "@(#) $Header: pcap-pf.c,v 1.54 96/12/10 23:15:01 leres Exp $ (LBL)"; 2817683Spst#endif 2917683Spst 3017683Spst#include <sys/types.h> 3117683Spst#include <sys/time.h> 3217683Spst#include <sys/timeb.h> 3317683Spst#include <sys/socket.h> 3417683Spst#include <sys/file.h> 3517683Spst#include <sys/ioctl.h> 3617683Spst#include <net/pfilt.h> 3717683Spst 3817683Spst#if __STDC__ 3917683Spststruct mbuf; 4017683Spststruct rtentry; 4117683Spst#endif 4217683Spst 4317683Spst#include <net/if.h> 4417683Spst 4517683Spst#include <netinet/in.h> 4617683Spst#include <netinet/in_systm.h> 4717683Spst#include <netinet/ip.h> 4817683Spst#include <netinet/if_ether.h> 4917683Spst#include <netinet/ip_var.h> 5017683Spst#include <netinet/udp.h> 5117683Spst#include <netinet/udp_var.h> 5217683Spst#include <netinet/tcp.h> 5317683Spst#include <netinet/tcpip.h> 5417683Spst 5517683Spst#include <ctype.h> 5617683Spst#include <errno.h> 5717683Spst#include <netdb.h> 5817683Spst#include <stdio.h> 5917683Spst#include <stdlib.h> 6017683Spst#include <string.h> 6117683Spst#include <unistd.h> 6217683Spst 6317683Spst#include "pcap-int.h" 6417683Spst 6517683Spst#include "gnuc.h" 6617683Spst#ifdef HAVE_OS_PROTO_H 6717683Spst#include "os-proto.h" 6817683Spst#endif 6917683Spst 7017683Spst/* 7117683Spst * BUFSPACE is the size in bytes of the packet read buffer. Most tcpdump 7217683Spst * applications aren't going to need more than 200 bytes of packet header 7317683Spst * and the read shouldn't return more packets than packetfilter's internal 7417683Spst * queue limit (bounded at 256). 7517683Spst */ 7617683Spst#define BUFSPACE (200 * 256) 7717683Spst 7817683Spstint 7917683Spstpcap_read(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) 8017683Spst{ 8117683Spst register u_char *p, *bp; 8217683Spst struct bpf_insn *fcode; 8317683Spst register int cc, n, buflen, inc; 8417683Spst register struct enstamp *sp; 8517683Spst#ifdef LBL_ALIGN 8617683Spst struct enstamp stamp; 8717683Spst#endif 8817683Spst#ifdef PCAP_FDDIPAD 8917683Spst register int pad; 9017683Spst#endif 9117683Spst 9217683Spst fcode = pc->md.use_bpf ? NULL : pc->fcode.bf_insns; 9317683Spst again: 9417683Spst cc = pc->cc; 9517683Spst if (cc == 0) { 9617683Spst cc = read(pc->fd, (char *)pc->buffer + pc->offset, pc->bufsize); 9717683Spst if (cc < 0) { 9817683Spst if (errno == EWOULDBLOCK) 9917683Spst return (0); 10017683Spst if (errno == EINVAL && 10117683Spst lseek(pc->fd, 0L, SEEK_CUR) + pc->bufsize < 0) { 10217683Spst /* 10317683Spst * Due to a kernel bug, after 2^31 bytes, 10417683Spst * the kernel file offset overflows and 10517683Spst * read fails with EINVAL. The lseek() 10617683Spst * to 0 will fix things. 10717683Spst */ 10817683Spst (void)lseek(pc->fd, 0L, SEEK_SET); 10917683Spst goto again; 11017683Spst } 11117683Spst sprintf(pc->errbuf, "pf read: %s", 11217683Spst pcap_strerror(errno)); 11317683Spst return (-1); 11417683Spst } 11517683Spst bp = pc->buffer + pc->offset; 11617683Spst } else 11717683Spst bp = pc->bp; 11817683Spst /* 11917683Spst * Loop through each packet. 12017683Spst */ 12117683Spst n = 0; 12217683Spst#ifdef PCAP_FDDIPAD 12317683Spst if (pc->linktype == DLT_FDDI) 12417683Spst pad = pcap_fddipad; 12517683Spst else 12617683Spst pad = 0; 12717683Spst#endif 12817683Spst while (cc > 0) { 12917683Spst if (cc < sizeof(*sp)) { 13017683Spst sprintf(pc->errbuf, "pf short read (%d)", cc); 13117683Spst return (-1); 13217683Spst } 13317683Spst#ifdef LBL_ALIGN 13417683Spst if ((long)bp & 3) { 13517683Spst sp = &stamp; 13617683Spst memcpy((char *)sp, (char *)bp, sizeof(*sp)); 13717683Spst } else 13817683Spst#endif 13917683Spst sp = (struct enstamp *)bp; 14017683Spst if (sp->ens_stamplen != sizeof(*sp)) { 14117683Spst sprintf(pc->errbuf, "pf short stamplen (%d)", 14217683Spst sp->ens_stamplen); 14317683Spst return (-1); 14417683Spst } 14517683Spst 14617683Spst p = bp + sp->ens_stamplen; 14717683Spst buflen = sp->ens_count; 14817683Spst if (buflen > pc->snapshot) 14917683Spst buflen = pc->snapshot; 15017683Spst 15117683Spst /* Calculate inc before possible pad update */ 15217683Spst inc = ENALIGN(buflen + sp->ens_stamplen); 15317683Spst cc -= inc; 15417683Spst bp += inc; 15517683Spst#ifdef PCAP_FDDIPAD 15617683Spst p += pad; 15717683Spst buflen -= pad; 15817683Spst#endif 15917683Spst pc->md.TotPkts++; 16017683Spst pc->md.TotDrops += sp->ens_dropped; 16117683Spst pc->md.TotMissed = sp->ens_ifoverflows; 16217683Spst if (pc->md.OrigMissed < 0) 16317683Spst pc->md.OrigMissed = pc->md.TotMissed; 16417683Spst 16517683Spst /* 16617683Spst * Short-circuit evaluation: if using BPF filter 16717683Spst * in kernel, no need to do it now. 16817683Spst */ 16917683Spst if (fcode == NULL || 17017683Spst bpf_filter(fcode, p, sp->ens_count, buflen)) { 17117683Spst struct pcap_pkthdr h; 17217683Spst pc->md.TotAccepted++; 17317683Spst h.ts = sp->ens_tstamp; 17417683Spst#ifdef PCAP_FDDIPAD 17517683Spst h.len = sp->ens_count - pad; 17617683Spst#else 17717683Spst h.len = sp->ens_count; 17817683Spst#endif 17917683Spst h.caplen = buflen; 18017683Spst (*callback)(user, &h, p); 18117683Spst if (++n >= cnt && cnt > 0) { 18217683Spst pc->cc = cc; 18317683Spst pc->bp = bp; 18417683Spst return (n); 18517683Spst } 18617683Spst } 18717683Spst } 18817683Spst pc->cc = 0; 18917683Spst return (n); 19017683Spst} 19117683Spst 19217683Spstint 19317683Spstpcap_stats(pcap_t *p, struct pcap_stat *ps) 19417683Spst{ 19517683Spst 19617683Spst ps->ps_recv = p->md.TotAccepted; 19717683Spst ps->ps_drop = p->md.TotDrops; 19817683Spst ps->ps_ifdrop = p->md.TotMissed - p->md.OrigMissed; 19917683Spst return (0); 20017683Spst} 20117683Spst 20217683Spstpcap_t * 20317683Spstpcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) 20417683Spst{ 20517683Spst pcap_t *p; 20617683Spst short enmode; 20717683Spst int backlog = -1; /* request the most */ 20817683Spst struct enfilter Filter; 20917683Spst struct endevp devparams; 21017683Spst 21117683Spst p = (pcap_t *)malloc(sizeof(*p)); 21217683Spst if (p == NULL) { 21317683Spst sprintf(ebuf, "pcap_open_live: %s", pcap_strerror(errno)); 21417683Spst return (0); 21517683Spst } 21617683Spst bzero((char *)p, sizeof(*p)); 21717683Spst p->fd = pfopen(device, O_RDONLY); 21817683Spst if (p->fd < 0) { 21917683Spst sprintf(ebuf, "pf open: %s: %s\n\ 22017683Spstyour system may not be properly configured; see \"man packetfilter(4)\"\n", 22117683Spst device, pcap_strerror(errno)); 22217683Spst goto bad; 22317683Spst } 22417683Spst p->md.OrigMissed = -1; 22517683Spst enmode = ENTSTAMP|ENBATCH|ENNONEXCL; 22617683Spst if (promisc) 22717683Spst enmode |= ENPROMISC; 22817683Spst if (ioctl(p->fd, EIOCMBIS, (caddr_t)&enmode) < 0) { 22917683Spst sprintf(ebuf, "EIOCMBIS: %s", pcap_strerror(errno)); 23017683Spst goto bad; 23117683Spst } 23217683Spst#ifdef ENCOPYALL 23317683Spst /* Try to set COPYALL mode so that we see packets to ourself */ 23417683Spst enmode = ENCOPYALL; 23517683Spst (void)ioctl(p->fd, EIOCMBIS, (caddr_t)&enmode);/* OK if this fails */ 23617683Spst#endif 23717683Spst /* set the backlog */ 23817683Spst if (ioctl(p->fd, EIOCSETW, (caddr_t)&backlog) < 0) { 23917683Spst sprintf(ebuf, "EIOCSETW: %s", pcap_strerror(errno)); 24017683Spst goto bad; 24117683Spst } 24217683Spst /* discover interface type */ 24317683Spst if (ioctl(p->fd, EIOCDEVP, (caddr_t)&devparams) < 0) { 24417683Spst sprintf(ebuf, "EIOCDEVP: %s", pcap_strerror(errno)); 24517683Spst goto bad; 24617683Spst } 24717683Spst /* HACK: to compile prior to Ultrix 4.2 */ 24817683Spst#ifndef ENDT_FDDI 24917683Spst#define ENDT_FDDI 4 25017683Spst#endif 25117683Spst switch (devparams.end_dev_type) { 25217683Spst 25317683Spst case ENDT_10MB: 25417683Spst p->linktype = DLT_EN10MB; 25517683Spst p->offset = 2; 25617683Spst break; 25717683Spst 25817683Spst case ENDT_FDDI: 25917683Spst p->linktype = DLT_FDDI; 26017683Spst break; 26117683Spst 26217683Spst default: 26317683Spst /* 26417683Spst * XXX 26517683Spst * Currently, the Ultrix packet filter supports only 26617683Spst * Ethernet and FDDI. Eventually, support for SLIP and PPP 26717683Spst * (and possibly others: T1?) should be added. 26817683Spst */ 26917683Spst#ifdef notdef 27017683Spst warning( 27117683Spst "Packet filter data-link type %d unknown, assuming Ethernet", 27217683Spst devparams.end_dev_type); 27317683Spst#endif 27417683Spst p->linktype = DLT_EN10MB; 27517683Spst p->offset = 2; 27617683Spst break; 27717683Spst } 27817683Spst /* set truncation */ 27917683Spst#ifdef PCAP_FDDIPAD 28017683Spst if (p->linktype == DLT_FDDI) 28117683Spst /* packetfilter includes the padding in the snapshot */ 28217683Spst snaplen += pcap_fddipad; 28317683Spst#endif 28417683Spst if (ioctl(p->fd, EIOCTRUNCATE, (caddr_t)&snaplen) < 0) { 28517683Spst sprintf(ebuf, "EIOCTRUNCATE: %s", pcap_strerror(errno)); 28617683Spst goto bad; 28717683Spst } 28817683Spst p->snapshot = snaplen; 28917683Spst /* accept all packets */ 29026175Sfenner bzero((char *)&Filter, sizeof(Filter)); 29117683Spst Filter.enf_Priority = 37; /* anything > 2 */ 29217683Spst Filter.enf_FilterLen = 0; /* means "always true" */ 29317683Spst if (ioctl(p->fd, EIOCSETF, (caddr_t)&Filter) < 0) { 29417683Spst sprintf(ebuf, "EIOCSETF: %s", pcap_strerror(errno)); 29517683Spst goto bad; 29617683Spst } 29717683Spst 29817683Spst if (to_ms != 0) { 29917683Spst struct timeval timeout; 30017683Spst timeout.tv_sec = to_ms / 1000; 30117683Spst timeout.tv_usec = (to_ms * 1000) % 1000000; 30217683Spst if (ioctl(p->fd, EIOCSRTIMEOUT, (caddr_t)&timeout) < 0) { 30317683Spst sprintf(ebuf, "EIOCSRTIMEOUT: %s", 30417683Spst pcap_strerror(errno)); 30517683Spst goto bad; 30617683Spst } 30717683Spst } 30817683Spst p->bufsize = BUFSPACE; 30917683Spst p->buffer = (u_char*)malloc(p->bufsize + p->offset); 31017683Spst 31117683Spst return (p); 31217683Spst bad: 31317683Spst free(p); 31417683Spst return (NULL); 31517683Spst} 31617683Spst 31717683Spstint 31817683Spstpcap_setfilter(pcap_t *p, struct bpf_program *fp) 31917683Spst{ 32017683Spst /* 32117683Spst * See if BIOCSETF works. If it does, the kernel supports 32217683Spst * BPF-style filters, and we do not need to do post-filtering. 32317683Spst */ 32417683Spst p->md.use_bpf = (ioctl(p->fd, BIOCSETF, (caddr_t)fp) >= 0); 32517683Spst if (p->md.use_bpf) { 32617683Spst struct bpf_version bv; 32717683Spst 32817683Spst if (ioctl(p->fd, BIOCVERSION, (caddr_t)&bv) < 0) { 32917683Spst sprintf(p->errbuf, "BIOCVERSION: %s", 33017683Spst pcap_strerror(errno)); 33117683Spst return (-1); 33217683Spst } 33317683Spst else if (bv.bv_major != BPF_MAJOR_VERSION || 33417683Spst bv.bv_minor < BPF_MINOR_VERSION) { 33517683Spst fprintf(stderr, 33617683Spst "requires bpf language %d.%d or higher; kernel is %d.%d", 33717683Spst BPF_MAJOR_VERSION, BPF_MINOR_VERSION, 33817683Spst bv.bv_major, bv.bv_minor); 33917683Spst /* don't give up, just be inefficient */ 34017683Spst p->md.use_bpf = 0; 34117683Spst } 34217683Spst } else 34317683Spst p->fcode = *fp; 34417683Spst 34517683Spst /*XXX this goes in tcpdump*/ 34617683Spst if (p->md.use_bpf) 34717683Spst fprintf(stderr, "tcpdump: Using kernel BPF filter\n"); 34817683Spst else 34917683Spst fprintf(stderr, "tcpdump: Filtering in user process\n"); 35017683Spst return (0); 35117683Spst} 352