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