xref: /freebsd/contrib/libpcap/pcap-snit.c (revision dc2c7305)
18cf6c252SPaul Traina /*
28cf6c252SPaul Traina  * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996
38cf6c252SPaul Traina  *	The Regents of the University of California.  All rights reserved.
48cf6c252SPaul Traina  *
58cf6c252SPaul Traina  * Redistribution and use in source and binary forms, with or without
68cf6c252SPaul Traina  * modification, are permitted provided that: (1) source code distributions
78cf6c252SPaul Traina  * retain the above copyright notice and this paragraph in its entirety, (2)
88cf6c252SPaul Traina  * distributions including binary code include the above copyright notice and
98cf6c252SPaul Traina  * this paragraph in its entirety in the documentation or other materials
108cf6c252SPaul Traina  * provided with the distribution, and (3) all advertising materials mentioning
118cf6c252SPaul Traina  * features or use of this software display the following acknowledgement:
128cf6c252SPaul Traina  * ``This product includes software developed by the University of California,
138cf6c252SPaul Traina  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
148cf6c252SPaul Traina  * the University nor the names of its contributors may be used to endorse
158cf6c252SPaul Traina  * or promote products derived from this software without specific prior
168cf6c252SPaul Traina  * written permission.
178cf6c252SPaul Traina  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
188cf6c252SPaul Traina  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
198cf6c252SPaul Traina  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
203052b236SBill Fenner  *
218cf6c252SPaul Traina  * Modifications made to accommodate the new SunOS4.0 NIT facility by
228cf6c252SPaul Traina  * Micky Liu, micky@cunixc.cc.columbia.edu, Columbia University in May, 1989.
238cf6c252SPaul Traina  * This module now handles the STREAMS based NIT.
248cf6c252SPaul Traina  */
258cf6c252SPaul Traina 
263052b236SBill Fenner #ifndef lint
273052b236SBill Fenner static const char rcsid[] =
28dc2c7305SBill Fenner     "@(#) $Header: /tcpdump/master/libpcap/pcap-snit.c,v 1.54 2000/10/28 00:01:30 guy Exp $ (LBL)";
29dc2c7305SBill Fenner #endif
30dc2c7305SBill Fenner 
31dc2c7305SBill Fenner #ifdef HAVE_CONFIG_H
32dc2c7305SBill Fenner #include "config.h"
333052b236SBill Fenner #endif
343052b236SBill Fenner 
358cf6c252SPaul Traina #include <sys/types.h>
368cf6c252SPaul Traina #include <sys/time.h>
378cf6c252SPaul Traina #include <sys/timeb.h>
388cf6c252SPaul Traina #include <sys/dir.h>
398cf6c252SPaul Traina #include <sys/fcntlcom.h>
408cf6c252SPaul Traina #include <sys/file.h>
418cf6c252SPaul Traina #include <sys/ioctl.h>
428cf6c252SPaul Traina #include <sys/socket.h>
438cf6c252SPaul Traina #include <sys/stropts.h>
448cf6c252SPaul Traina 
458cf6c252SPaul Traina #include <net/if.h>
468cf6c252SPaul Traina #include <net/nit.h>
478cf6c252SPaul Traina #include <net/nit_if.h>
488cf6c252SPaul Traina #include <net/nit_pf.h>
498cf6c252SPaul Traina #include <net/nit_buf.h>
508cf6c252SPaul Traina 
518cf6c252SPaul Traina #include <netinet/in.h>
528cf6c252SPaul Traina #include <netinet/in_systm.h>
538cf6c252SPaul Traina #include <netinet/ip.h>
548cf6c252SPaul Traina #include <netinet/if_ether.h>
558cf6c252SPaul Traina #include <netinet/ip_var.h>
568cf6c252SPaul Traina #include <netinet/udp.h>
578cf6c252SPaul Traina #include <netinet/udp_var.h>
588cf6c252SPaul Traina #include <netinet/tcp.h>
598cf6c252SPaul Traina #include <netinet/tcpip.h>
608cf6c252SPaul Traina 
618cf6c252SPaul Traina #include <ctype.h>
628cf6c252SPaul Traina #include <errno.h>
638cf6c252SPaul Traina #include <stdio.h>
648cf6c252SPaul Traina #include <string.h>
658cf6c252SPaul Traina #include <unistd.h>
668cf6c252SPaul Traina 
678cf6c252SPaul Traina #include "pcap-int.h"
688cf6c252SPaul Traina 
698cf6c252SPaul Traina #ifdef HAVE_OS_PROTO_H
708cf6c252SPaul Traina #include "os-proto.h"
718cf6c252SPaul Traina #endif
728cf6c252SPaul Traina 
738cf6c252SPaul Traina /*
748cf6c252SPaul Traina  * The chunk size for NIT.  This is the amount of buffering
758cf6c252SPaul Traina  * done for read calls.
768cf6c252SPaul Traina  */
778cf6c252SPaul Traina #define CHUNKSIZE (2*1024)
788cf6c252SPaul Traina 
798cf6c252SPaul Traina /*
808cf6c252SPaul Traina  * The total buffer space used by NIT.
818cf6c252SPaul Traina  */
828cf6c252SPaul Traina #define BUFSPACE (4*CHUNKSIZE)
838cf6c252SPaul Traina 
848cf6c252SPaul Traina /* Forwards */
858cf6c252SPaul Traina static int nit_setflags(int, int, int, char *);
868cf6c252SPaul Traina 
878cf6c252SPaul Traina int
888cf6c252SPaul Traina pcap_stats(pcap_t *p, struct pcap_stat *ps)
898cf6c252SPaul Traina {
908cf6c252SPaul Traina 
918cf6c252SPaul Traina 	*ps = p->md.stat;
928cf6c252SPaul Traina 	return (0);
938cf6c252SPaul Traina }
948cf6c252SPaul Traina 
958cf6c252SPaul Traina int
968cf6c252SPaul Traina pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
978cf6c252SPaul Traina {
988cf6c252SPaul Traina 	register int cc, n;
998cf6c252SPaul Traina 	register struct bpf_insn *fcode = p->fcode.bf_insns;
1008cf6c252SPaul Traina 	register u_char *bp, *cp, *ep;
1018cf6c252SPaul Traina 	register struct nit_bufhdr *hdrp;
1028cf6c252SPaul Traina 	register struct nit_iftime *ntp;
1038cf6c252SPaul Traina 	register struct nit_iflen *nlp;
1048cf6c252SPaul Traina 	register struct nit_ifdrops *ndp;
1058cf6c252SPaul Traina 	register int caplen;
1068cf6c252SPaul Traina 
1078cf6c252SPaul Traina 	cc = p->cc;
1088cf6c252SPaul Traina 	if (cc == 0) {
1098cf6c252SPaul Traina 		cc = read(p->fd, (char *)p->buffer, p->bufsize);
1108cf6c252SPaul Traina 		if (cc < 0) {
1118cf6c252SPaul Traina 			if (errno == EWOULDBLOCK)
1128cf6c252SPaul Traina 				return (0);
113dc2c7305SBill Fenner 			snprintf(p->errbuf, sizeof(p->errbuf), "pcap_read: %s",
1148cf6c252SPaul Traina 				pcap_strerror(errno));
1158cf6c252SPaul Traina 			return (-1);
1168cf6c252SPaul Traina 		}
1178cf6c252SPaul Traina 		bp = p->buffer;
1188cf6c252SPaul Traina 	} else
1198cf6c252SPaul Traina 		bp = p->bp;
1208cf6c252SPaul Traina 
1218cf6c252SPaul Traina 	/*
1228cf6c252SPaul Traina 	 * loop through each snapshot in the chunk
1238cf6c252SPaul Traina 	 */
1248cf6c252SPaul Traina 	n = 0;
1258cf6c252SPaul Traina 	ep = bp + cc;
1268cf6c252SPaul Traina 	while (bp < ep) {
1278cf6c252SPaul Traina 		++p->md.stat.ps_recv;
1288cf6c252SPaul Traina 		cp = bp;
1298cf6c252SPaul Traina 
1308cf6c252SPaul Traina 		/* get past NIT buffer  */
1318cf6c252SPaul Traina 		hdrp = (struct nit_bufhdr *)cp;
1328cf6c252SPaul Traina 		cp += sizeof(*hdrp);
1338cf6c252SPaul Traina 
1348cf6c252SPaul Traina 		/* get past NIT timer   */
1358cf6c252SPaul Traina 		ntp = (struct nit_iftime *)cp;
1368cf6c252SPaul Traina 		cp += sizeof(*ntp);
1378cf6c252SPaul Traina 
1388cf6c252SPaul Traina 		ndp = (struct nit_ifdrops *)cp;
1398cf6c252SPaul Traina 		p->md.stat.ps_drop = ndp->nh_drops;
1408cf6c252SPaul Traina 		cp += sizeof *ndp;
1418cf6c252SPaul Traina 
1428cf6c252SPaul Traina 		/* get past packet len  */
1438cf6c252SPaul Traina 		nlp = (struct nit_iflen *)cp;
1448cf6c252SPaul Traina 		cp += sizeof(*nlp);
1458cf6c252SPaul Traina 
1468cf6c252SPaul Traina 		/* next snapshot        */
1478cf6c252SPaul Traina 		bp += hdrp->nhb_totlen;
1488cf6c252SPaul Traina 
1498cf6c252SPaul Traina 		caplen = nlp->nh_pktlen;
1508cf6c252SPaul Traina 		if (caplen > p->snapshot)
1518cf6c252SPaul Traina 			caplen = p->snapshot;
1528cf6c252SPaul Traina 
1538cf6c252SPaul Traina 		if (bpf_filter(fcode, cp, nlp->nh_pktlen, caplen)) {
1548cf6c252SPaul Traina 			struct pcap_pkthdr h;
1558cf6c252SPaul Traina 			h.ts = ntp->nh_timestamp;
1568cf6c252SPaul Traina 			h.len = nlp->nh_pktlen;
1578cf6c252SPaul Traina 			h.caplen = caplen;
1588cf6c252SPaul Traina 			(*callback)(user, &h, cp);
1598cf6c252SPaul Traina 			if (++n >= cnt && cnt >= 0) {
1608cf6c252SPaul Traina 				p->cc = ep - bp;
1618cf6c252SPaul Traina 				p->bp = bp;
1628cf6c252SPaul Traina 				return (n);
1638cf6c252SPaul Traina 			}
1648cf6c252SPaul Traina 		}
1658cf6c252SPaul Traina 	}
1668cf6c252SPaul Traina 	p->cc = 0;
1678cf6c252SPaul Traina 	return (n);
1688cf6c252SPaul Traina }
1698cf6c252SPaul Traina 
1708cf6c252SPaul Traina static int
1718cf6c252SPaul Traina nit_setflags(int fd, int promisc, int to_ms, char *ebuf)
1728cf6c252SPaul Traina {
1738cf6c252SPaul Traina 	bpf_u_int32 flags;
1748cf6c252SPaul Traina 	struct strioctl si;
1758cf6c252SPaul Traina 	struct timeval timeout;
1768cf6c252SPaul Traina 
1778cf6c252SPaul Traina 	si.ic_timout = INFTIM;
1788cf6c252SPaul Traina 	if (to_ms != 0) {
1798cf6c252SPaul Traina 		timeout.tv_sec = to_ms / 1000;
1808cf6c252SPaul Traina 		timeout.tv_usec = (to_ms * 1000) % 1000000;
1818cf6c252SPaul Traina 		si.ic_cmd = NIOCSTIME;
1828cf6c252SPaul Traina 		si.ic_len = sizeof(timeout);
1838cf6c252SPaul Traina 		si.ic_dp = (char *)&timeout;
1848cf6c252SPaul Traina 		if (ioctl(fd, I_STR, (char *)&si) < 0) {
185dc2c7305SBill Fenner 			snprintf(ebuf, PCAP_ERRBUF_SIZE, "NIOCSTIME: %s",
186dc2c7305SBill Fenner 			    pcap_strerror(errno));
1878cf6c252SPaul Traina 			return (-1);
1888cf6c252SPaul Traina 		}
1898cf6c252SPaul Traina 	}
1908cf6c252SPaul Traina 	flags = NI_TIMESTAMP | NI_LEN | NI_DROPS;
1918cf6c252SPaul Traina 	if (promisc)
1928cf6c252SPaul Traina 		flags |= NI_PROMISC;
1938cf6c252SPaul Traina 	si.ic_cmd = NIOCSFLAGS;
1948cf6c252SPaul Traina 	si.ic_len = sizeof(flags);
1958cf6c252SPaul Traina 	si.ic_dp = (char *)&flags;
1968cf6c252SPaul Traina 	if (ioctl(fd, I_STR, (char *)&si) < 0) {
197dc2c7305SBill Fenner 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "NIOCSFLAGS: %s",
198dc2c7305SBill Fenner 		    pcap_strerror(errno));
1998cf6c252SPaul Traina 		return (-1);
2008cf6c252SPaul Traina 	}
2018cf6c252SPaul Traina 	return (0);
2028cf6c252SPaul Traina }
2038cf6c252SPaul Traina 
2048cf6c252SPaul Traina pcap_t *
2058cf6c252SPaul Traina pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
2068cf6c252SPaul Traina {
2078cf6c252SPaul Traina 	struct strioctl si;		/* struct for ioctl() */
2088cf6c252SPaul Traina 	struct ifreq ifr;		/* interface request struct */
2098cf6c252SPaul Traina 	int chunksize = CHUNKSIZE;
2108cf6c252SPaul Traina 	int fd;
2118cf6c252SPaul Traina 	static char dev[] = "/dev/nit";
2128cf6c252SPaul Traina 	register pcap_t *p;
2138cf6c252SPaul Traina 
2148cf6c252SPaul Traina 	p = (pcap_t *)malloc(sizeof(*p));
2158cf6c252SPaul Traina 	if (p == NULL) {
216dc2c7305SBill Fenner 		strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE);
2178cf6c252SPaul Traina 		return (NULL);
2188cf6c252SPaul Traina 	}
2198cf6c252SPaul Traina 
2208cf6c252SPaul Traina 	if (snaplen < 96)
2218cf6c252SPaul Traina 		/*
2228cf6c252SPaul Traina 		 * NIT requires a snapshot length of at least 96.
2238cf6c252SPaul Traina 		 */
2248cf6c252SPaul Traina 		snaplen = 96;
2258cf6c252SPaul Traina 
226dc2c7305SBill Fenner 	memset(p, 0, sizeof(*p));
2278cf6c252SPaul Traina 	p->fd = fd = open(dev, O_RDONLY);
2288cf6c252SPaul Traina 	if (fd < 0) {
229dc2c7305SBill Fenner 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s", dev,
230dc2c7305SBill Fenner 		    pcap_strerror(errno));
2318cf6c252SPaul Traina 		goto bad;
2328cf6c252SPaul Traina 	}
2338cf6c252SPaul Traina 
2348cf6c252SPaul Traina 	/* arrange to get discrete messages from the STREAM and use NIT_BUF */
2358cf6c252SPaul Traina 	if (ioctl(fd, I_SRDOPT, (char *)RMSGD) < 0) {
236dc2c7305SBill Fenner 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "I_SRDOPT: %s",
237dc2c7305SBill Fenner 		    pcap_strerror(errno));
2388cf6c252SPaul Traina 		goto bad;
2398cf6c252SPaul Traina 	}
2408cf6c252SPaul Traina 	if (ioctl(fd, I_PUSH, "nbuf") < 0) {
241dc2c7305SBill Fenner 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "push nbuf: %s",
242dc2c7305SBill Fenner 		    pcap_strerror(errno));
2438cf6c252SPaul Traina 		goto bad;
2448cf6c252SPaul Traina 	}
2458cf6c252SPaul Traina 	/* set the chunksize */
2468cf6c252SPaul Traina 	si.ic_cmd = NIOCSCHUNK;
2478cf6c252SPaul Traina 	si.ic_timout = INFTIM;
2488cf6c252SPaul Traina 	si.ic_len = sizeof(chunksize);
2498cf6c252SPaul Traina 	si.ic_dp = (char *)&chunksize;
2508cf6c252SPaul Traina 	if (ioctl(fd, I_STR, (char *)&si) < 0) {
251dc2c7305SBill Fenner 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "NIOCSCHUNK: %s",
252dc2c7305SBill Fenner 		    pcap_strerror(errno));
2538cf6c252SPaul Traina 		goto bad;
2548cf6c252SPaul Traina 	}
2558cf6c252SPaul Traina 
2568cf6c252SPaul Traina 	/* request the interface */
2578cf6c252SPaul Traina 	strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
2588cf6c252SPaul Traina 	ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = ' ';
2598cf6c252SPaul Traina 	si.ic_cmd = NIOCBIND;
2608cf6c252SPaul Traina 	si.ic_len = sizeof(ifr);
2618cf6c252SPaul Traina 	si.ic_dp = (char *)&ifr;
2628cf6c252SPaul Traina 	if (ioctl(fd, I_STR, (char *)&si) < 0) {
263dc2c7305SBill Fenner 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "NIOCBIND: %s: %s",
2648cf6c252SPaul Traina 			ifr.ifr_name, pcap_strerror(errno));
2658cf6c252SPaul Traina 		goto bad;
2668cf6c252SPaul Traina 	}
2678cf6c252SPaul Traina 
2688cf6c252SPaul Traina 	/* set the snapshot length */
2698cf6c252SPaul Traina 	si.ic_cmd = NIOCSSNAP;
2708cf6c252SPaul Traina 	si.ic_len = sizeof(snaplen);
2718cf6c252SPaul Traina 	si.ic_dp = (char *)&snaplen;
2728cf6c252SPaul Traina 	if (ioctl(fd, I_STR, (char *)&si) < 0) {
273dc2c7305SBill Fenner 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "NIOCSSNAP: %s",
274dc2c7305SBill Fenner 		    pcap_strerror(errno));
2758cf6c252SPaul Traina 		goto bad;
2768cf6c252SPaul Traina 	}
2778cf6c252SPaul Traina 	p->snapshot = snaplen;
2788cf6c252SPaul Traina 	if (nit_setflags(p->fd, promisc, to_ms, ebuf) < 0)
2798cf6c252SPaul Traina 		goto bad;
2808cf6c252SPaul Traina 
2818cf6c252SPaul Traina 	(void)ioctl(fd, I_FLUSH, (char *)FLUSHR);
2828cf6c252SPaul Traina 	/*
2838cf6c252SPaul Traina 	 * NIT supports only ethernets.
2848cf6c252SPaul Traina 	 */
2858cf6c252SPaul Traina 	p->linktype = DLT_EN10MB;
2868cf6c252SPaul Traina 
2878cf6c252SPaul Traina 	p->bufsize = BUFSPACE;
2888cf6c252SPaul Traina 	p->buffer = (u_char *)malloc(p->bufsize);
2898cf6c252SPaul Traina 	if (p->buffer == NULL) {
290dc2c7305SBill Fenner 		strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE);
2918cf6c252SPaul Traina 		goto bad;
2928cf6c252SPaul Traina 	}
2938cf6c252SPaul Traina 	return (p);
2948cf6c252SPaul Traina  bad:
2958cf6c252SPaul Traina 	if (fd >= 0)
2968cf6c252SPaul Traina 		close(fd);
2978cf6c252SPaul Traina 	free(p);
2988cf6c252SPaul Traina 	return (NULL);
2998cf6c252SPaul Traina }
3008cf6c252SPaul Traina 
3018cf6c252SPaul Traina int
3028cf6c252SPaul Traina pcap_setfilter(pcap_t *p, struct bpf_program *fp)
3038cf6c252SPaul Traina {
3048cf6c252SPaul Traina 
305dc2c7305SBill Fenner 	if (install_bpf_program(p, fp) < 0)
306dc2c7305SBill Fenner 		return (-1);
3078cf6c252SPaul Traina 	return (0);
3088cf6c252SPaul Traina }
309