pcap-nit.c revision 147894
147133Sobrien/* 247133Sobrien * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996 347133Sobrien * The Regents of the University of California. All rights reserved. 447133Sobrien * 547133Sobrien * Redistribution and use in source and binary forms, with or without 647133Sobrien * modification, are permitted provided that: (1) source code distributions 747133Sobrien * retain the above copyright notice and this paragraph in its entirety, (2) 847133Sobrien * distributions including binary code include the above copyright notice and 947133Sobrien * this paragraph in its entirety in the documentation or other materials 1047133Sobrien * provided with the distribution, and (3) all advertising materials mentioning 1147133Sobrien * features or use of this software display the following acknowledgement: 1247133Sobrien * ``This product includes software developed by the University of California, 1347133Sobrien * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 1447133Sobrien * the University nor the names of its contributors may be used to endorse 1547133Sobrien * or promote products derived from this software without specific prior 1647133Sobrien * written permission. 1747133Sobrien * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 1847133Sobrien * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 1947133Sobrien * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 2047133Sobrien */ 2147133Sobrien#ifndef lint 2247133Sobrienstatic const char rcsid[] _U_ = 2347133Sobrien "@(#) $Header: /tcpdump/master/libpcap/pcap-nit.c,v 1.57.2.1 2005/05/03 18:54:37 guy Exp $ (LBL)"; 2447133Sobrien#endif 2547133Sobrien 2651594Speter#ifdef HAVE_CONFIG_H 2750477Speter#include "config.h" 2847133Sobrien#endif 2955723Simp 3055723Simp#include <sys/types.h> 3147133Sobrien#include <sys/time.h> 3247133Sobrien#include <sys/timeb.h> 3348114Sobrien#include <sys/file.h> 3448114Sobrien#include <sys/ioctl.h> 3548114Sobrien#include <sys/socket.h> 3648114Sobrien 3748114Sobrien#include <net/if.h> 3848114Sobrien#include <net/nit.h> 3948114Sobrien 4048114Sobrien#include <netinet/in.h> 4148114Sobrien#include <netinet/in_systm.h> 4248114Sobrien#include <netinet/ip.h> 4348114Sobrien#include <netinet/if_ether.h> 4448114Sobrien#include <netinet/ip_var.h> 4548114Sobrien#include <netinet/udp.h> 4648114Sobrien#include <netinet/udp_var.h> 4748114Sobrien#include <netinet/tcp.h> 4848114Sobrien#include <netinet/tcpip.h> 4948114Sobrien 5048114Sobrien#include <ctype.h> 5148114Sobrien#include <errno.h> 5248114Sobrien#include <stdio.h> 5348114Sobrien 5448114Sobrien#include "pcap-int.h" 5548114Sobrien 5648114Sobrien#ifdef HAVE_OS_PROTO_H 5748114Sobrien#include "os-proto.h" 5848114Sobrien#endif 5948114Sobrien 6048114Sobrien/* 6148114Sobrien * The chunk size for NIT. This is the amount of buffering 6248114Sobrien * done for read calls. 6348114Sobrien */ 6448114Sobrien#define CHUNKSIZE (2*1024) 6548114Sobrien 6648114Sobrien/* 6748114Sobrien * The total buffer space used by NIT. 6848114Sobrien */ 6947133Sobrien#define BUFSPACE (4*CHUNKSIZE) 7047133Sobrien 7148114Sobrien/* Forwards */ 7248114Sobrienstatic int nit_setflags(int, int, int, char *); 7348114Sobrien 7448114Sobrienstatic int 7547133Sobrienpcap_stats_nit(pcap_t *p, struct pcap_stat *ps) 7648114Sobrien{ 7748114Sobrien 7848114Sobrien /* 7947133Sobrien * "ps_recv" counts packets handed to the filter, not packets 8048114Sobrien * that passed the filter. As filtering is done in userland, 8148114Sobrien * this does not include packets dropped because we ran out 8248114Sobrien * of buffer space. 8348114Sobrien * 8447133Sobrien * "ps_drop" presumably counts packets dropped by the socket 8548114Sobrien * because of flow control requirements or resource exhaustion; 8647133Sobrien * it doesn't count packets dropped by the interface driver. 8748114Sobrien * As filtering is done in userland, it counts packets regardless 8847133Sobrien * of whether they would've passed the filter. 8948114Sobrien * 9048114Sobrien * These statistics don't include packets not yet read from the 9148114Sobrien * kernel by libpcap or packets not yet read from libpcap by the 9248114Sobrien * application. 9347133Sobrien */ 9448114Sobrien *ps = p->md.stat; 9548114Sobrien return (0); 9648114Sobrien} 9748114Sobrien 9848114Sobrienstatic int 9948114Sobrienpcap_read_nit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 10048114Sobrien{ 10148114Sobrien register int cc, n; 10248114Sobrien register struct bpf_insn *fcode = p->fcode.bf_insns; 10348114Sobrien register u_char *bp, *cp, *ep; 10448114Sobrien register struct nit_hdr *nh; 10548114Sobrien register int caplen; 10648114Sobrien 10748114Sobrien cc = p->cc; 10848114Sobrien if (cc == 0) { 10947133Sobrien cc = read(p->fd, (char *)p->buffer, p->bufsize); 11048114Sobrien if (cc < 0) { 11148114Sobrien if (errno == EWOULDBLOCK) 11247133Sobrien return (0); 11347133Sobrien snprintf(p->errbuf, sizeof(p->errbuf), "pcap_read: %s", 11448114Sobrien pcap_strerror(errno)); 11547133Sobrien return (-1); 11648114Sobrien } 11747133Sobrien bp = p->buffer; 11848114Sobrien } else 11948114Sobrien bp = p->bp; 12048114Sobrien 12148114Sobrien /* 12248114Sobrien * Loop through each packet. The increment expression 12347133Sobrien * rounds up to the next int boundary past the end of 12448114Sobrien * the previous packet. 12548114Sobrien */ 12648114Sobrien n = 0; 12748114Sobrien ep = bp + cc; 12848114Sobrien while (bp < ep) { 12948114Sobrien /* 13048114Sobrien * Has "pcap_breakloop()" been called? 13148114Sobrien * If so, return immediately - if we haven't read any 13247133Sobrien * packets, clear the flag and return -2 to indicate 13348114Sobrien * that we were told to break out of the loop, otherwise 13448114Sobrien * leave the flag set, so that the *next* call will break 13548114Sobrien * out of the loop without having read any packets, and 13648114Sobrien * return the number of packets we've processed so far. 13748114Sobrien */ 13848114Sobrien if (p->break_loop) { 13948114Sobrien if (n == 0) { 14048114Sobrien p->break_loop = 0; 14148114Sobrien return (-2); 14248114Sobrien } else { 14348114Sobrien p->cc = ep - bp; 14448114Sobrien p->bp = bp; 14548114Sobrien return (n); 14648114Sobrien } 14748114Sobrien } 14847133Sobrien 14948114Sobrien nh = (struct nit_hdr *)bp; 15048114Sobrien cp = bp + sizeof(*nh); 15148114Sobrien 15247133Sobrien switch (nh->nh_state) { 15347133Sobrien 15448114Sobrien case NIT_CATCH: 15547133Sobrien break; 15648114Sobrien 15747133Sobrien case NIT_NOMBUF: 15848114Sobrien case NIT_NOCLUSTER: 15948114Sobrien case NIT_NOSPACE: 16048114Sobrien p->md.stat.ps_drop = nh->nh_dropped; 16148114Sobrien continue; 16248114Sobrien 16347133Sobrien case NIT_SEQNO: 16448114Sobrien continue; 16548114Sobrien 16648114Sobrien default: 16748114Sobrien snprintf(p->errbuf, sizeof(p->errbuf), 16848114Sobrien "bad nit state %d", nh->nh_state); 16948114Sobrien return (-1); 17048114Sobrien } 17148114Sobrien ++p->md.stat.ps_recv; 17248114Sobrien bp += ((sizeof(struct nit_hdr) + nh->nh_datalen + 17347133Sobrien sizeof(int) - 1) & ~(sizeof(int) - 1)); 17448114Sobrien 17548114Sobrien caplen = nh->nh_wirelen; 17648114Sobrien if (caplen > p->snapshot) 17748114Sobrien caplen = p->snapshot; 17848114Sobrien if (bpf_filter(fcode, cp, nh->nh_wirelen, caplen)) { 17947133Sobrien struct pcap_pkthdr h; 18048114Sobrien h.ts = nh->nh_timestamp; 18148114Sobrien h.len = nh->nh_wirelen; 18247133Sobrien h.caplen = caplen; 18347133Sobrien (*callback)(user, &h, cp); 18447133Sobrien if (++n >= cnt && cnt >= 0) { 18548114Sobrien p->cc = ep - bp; 18648114Sobrien p->bp = bp; 18747133Sobrien return (n); 18847133Sobrien } 18947133Sobrien } 19048114Sobrien } 19148114Sobrien p->cc = 0; 19248114Sobrien return (n); 19348114Sobrien} 19448114Sobrien 19547133Sobrienstatic int 19648114Sobrienpcap_inject_nit(pcap_t *p, const void *buf, size_t size) 19748114Sobrien{ 19848114Sobrien struct sockaddr sa; 19948114Sobrien int ret; 20048114Sobrien 20148114Sobrien memset(&sa, 0, sizeof(sa)); 20248114Sobrien strncpy(sa.sa_data, device, sizeof(sa.sa_data)); 20348114Sobrien ret = sendto(p->fd, buf, size, 0, &sa, sizeof(sa)); 20448114Sobrien if (ret == -1) { 20548114Sobrien snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s", 20648114Sobrien pcap_strerror(errno)); 20748114Sobrien return (-1); 20848114Sobrien } 20948114Sobrien return (ret); 21048114Sobrien} 21148114Sobrien 21248114Sobrienstatic int 21348114Sobriennit_setflags(int fd, int promisc, int to_ms, char *ebuf) 21448114Sobrien{ 21548114Sobrien struct nit_ioc nioc; 21648114Sobrien 21748114Sobrien memset(&nioc, 0, sizeof(nioc)); 21848114Sobrien nioc.nioc_bufspace = BUFSPACE; 21948114Sobrien nioc.nioc_chunksize = CHUNKSIZE; 22048114Sobrien nioc.nioc_typetomatch = NT_ALLTYPES; 22148114Sobrien nioc.nioc_snaplen = p->snapshot; 22248114Sobrien nioc.nioc_bufalign = sizeof(int); 22348114Sobrien nioc.nioc_bufoffset = 0; 22448114Sobrien 22548114Sobrien if (to_ms != 0) { 22648114Sobrien nioc.nioc_flags |= NF_TIMEOUT; 22748114Sobrien nioc.nioc_timeout.tv_sec = to_ms / 1000; 22848114Sobrien nioc.nioc_timeout.tv_usec = (to_ms * 1000) % 1000000; 22948114Sobrien } 23048114Sobrien if (promisc) 23148114Sobrien nioc.nioc_flags |= NF_PROMISC; 23248114Sobrien 23348114Sobrien if (ioctl(fd, SIOCSNIT, &nioc) < 0) { 23448114Sobrien snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCSNIT: %s", 23548114Sobrien pcap_strerror(errno)); 23648114Sobrien return (-1); 23748114Sobrien } 23848114Sobrien return (0); 23948114Sobrien} 24048114Sobrien 24148114Sobrienstatic void 24248114Sobrienpcap_close_nit(pcap_t *p) 24348114Sobrien{ 24448114Sobrien pcap_close_common(p); 24548114Sobrien if (p->device != NULL) 24648114Sobrien free(p->device); 24748114Sobrien} 24848114Sobrien 24948114Sobrienpcap_t * 25048114Sobrienpcap_open_live(const char *device, int snaplen, int promisc, int to_ms, 25148114Sobrien char *ebuf) 25248114Sobrien{ 25348114Sobrien int fd; 25448114Sobrien struct sockaddr_nit snit; 25548114Sobrien register pcap_t *p; 25648114Sobrien 25748114Sobrien p = (pcap_t *)malloc(sizeof(*p)); 25848114Sobrien if (p == NULL) { 25948114Sobrien strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); 26048114Sobrien return (NULL); 26148114Sobrien } 26248114Sobrien 26348114Sobrien if (snaplen < 96) 26448114Sobrien /* 26548114Sobrien * NIT requires a snapshot length of at least 96. 26648114Sobrien */ 26748114Sobrien snaplen = 96; 26848114Sobrien 26948114Sobrien memset(p, 0, sizeof(*p)); 27048114Sobrien p->fd = fd = socket(AF_NIT, SOCK_RAW, NITPROTO_RAW); 27148114Sobrien if (fd < 0) { 27248114Sobrien snprintf(ebuf, PCAP_ERRBUF_SIZE, 27348114Sobrien "socket: %s", pcap_strerror(errno)); 27448114Sobrien goto bad; 27548114Sobrien } 27648114Sobrien snit.snit_family = AF_NIT; 27748114Sobrien (void)strncpy(snit.snit_ifname, device, NITIFSIZ); 27848114Sobrien 27948114Sobrien if (bind(fd, (struct sockaddr *)&snit, sizeof(snit))) { 28048114Sobrien snprintf(ebuf, PCAP_ERRBUF_SIZE, 28148114Sobrien "bind: %s: %s", snit.snit_ifname, pcap_strerror(errno)); 28248114Sobrien goto bad; 28348114Sobrien } 28448114Sobrien p->snapshot = snaplen; 28548114Sobrien nit_setflags(p->fd, promisc, to_ms, ebuf); 28648114Sobrien 28748114Sobrien /* 28848114Sobrien * NIT supports only ethernets. 28948114Sobrien */ 29048114Sobrien p->linktype = DLT_EN10MB; 29148114Sobrien 29248114Sobrien p->bufsize = BUFSPACE; 29348114Sobrien p->buffer = (u_char *)malloc(p->bufsize); 29448114Sobrien if (p->buffer == NULL) { 29548114Sobrien strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); 29648114Sobrien goto bad; 29748114Sobrien } 29848114Sobrien 29948114Sobrien /* 30048114Sobrien * We need the device name in order to send packets. 30148114Sobrien */ 30248114Sobrien p->device = strdup(device); 30347133Sobrien if (p->device == NULL) { 30447133Sobrien strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); 30548114Sobrien free(p->buffer); 30648114Sobrien goto bad; 30748114Sobrien } 30848114Sobrien 30948114Sobrien /* 31047133Sobrien * "p->fd" is a socket, so "select()" should work on it. 31148114Sobrien */ 31248114Sobrien p->selectable_fd = p->fd; 31348114Sobrien 31448114Sobrien /* 31548114Sobrien * This is (presumably) a real Ethernet capture; give it a 31648114Sobrien * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so 31748114Sobrien * that an application can let you choose it, in case you're 31848114Sobrien * capturing DOCSIS traffic that a Cisco Cable Modem 31948114Sobrien * Termination System is putting out onto an Ethernet (it 32048114Sobrien * doesn't put an Ethernet header onto the wire, it puts raw 32148114Sobrien * DOCSIS frames out on the wire inside the low-level 32247133Sobrien * Ethernet framing). 32348114Sobrien */ 32448114Sobrien p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); 32548114Sobrien /* 32648114Sobrien * If that fails, just leave the list empty. 32748114Sobrien */ 32848114Sobrien if (p->dlt_list != NULL) { 32948114Sobrien p->dlt_list[0] = DLT_EN10MB; 33048114Sobrien p->dlt_list[1] = DLT_DOCSIS; 33148114Sobrien p->dlt_count = 2; 33248114Sobrien } 33348114Sobrien 33448114Sobrien p->read_op = pcap_read_nit; 33548114Sobrien p->inject_op = pcap_inject_nit; 33648114Sobrien p->setfilter_op = install_bpf_program; /* no kernel filtering */ 33748114Sobrien p->setdirection_op = NULL; /* Not implemented. */ 33848114Sobrien p->set_datalink_op = NULL; /* can't change data link type */ 33948114Sobrien p->getnonblock_op = pcap_getnonblock_fd; 34048114Sobrien p->setnonblock_op = pcap_setnonblock_fd; 34148114Sobrien p->stats_op = pcap_stats_nit; 34248114Sobrien p->close_op = pcap_close_nit; 34348114Sobrien 34448114Sobrien return (p); 34548114Sobrien bad: 34648114Sobrien if (fd >= 0) 34748114Sobrien close(fd); 34848114Sobrien free(p); 34948114Sobrien return (NULL); 35048114Sobrien} 35147133Sobrien 35248114Sobrienint 35348114Sobrienpcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) 35448114Sobrien{ 35548114Sobrien return (0); 35648114Sobrien} 35748114Sobrien