xref: /freebsd/contrib/libpcap/pcap-snit.c (revision 3052b236)
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[] =
283052b236SBill Fenner     "@(#) $Header: pcap-snit.c,v 1.45 96/12/10 23:15:01 leres Exp $ (LBL)";
293052b236SBill Fenner #endif
303052b236SBill Fenner 
318cf6c252SPaul Traina #include <sys/types.h>
328cf6c252SPaul Traina #include <sys/time.h>
338cf6c252SPaul Traina #include <sys/timeb.h>
348cf6c252SPaul Traina #include <sys/dir.h>
358cf6c252SPaul Traina #include <sys/fcntlcom.h>
368cf6c252SPaul Traina #include <sys/file.h>
378cf6c252SPaul Traina #include <sys/ioctl.h>
388cf6c252SPaul Traina #include <sys/socket.h>
398cf6c252SPaul Traina #include <sys/stropts.h>
408cf6c252SPaul Traina 
418cf6c252SPaul Traina #include <net/if.h>
428cf6c252SPaul Traina #include <net/nit.h>
438cf6c252SPaul Traina #include <net/nit_if.h>
448cf6c252SPaul Traina #include <net/nit_pf.h>
458cf6c252SPaul Traina #include <net/nit_buf.h>
468cf6c252SPaul Traina 
478cf6c252SPaul Traina #include <netinet/in.h>
488cf6c252SPaul Traina #include <netinet/in_systm.h>
498cf6c252SPaul Traina #include <netinet/ip.h>
508cf6c252SPaul Traina #include <netinet/if_ether.h>
518cf6c252SPaul Traina #include <netinet/ip_var.h>
528cf6c252SPaul Traina #include <netinet/udp.h>
538cf6c252SPaul Traina #include <netinet/udp_var.h>
548cf6c252SPaul Traina #include <netinet/tcp.h>
558cf6c252SPaul Traina #include <netinet/tcpip.h>
568cf6c252SPaul Traina 
578cf6c252SPaul Traina #include <ctype.h>
588cf6c252SPaul Traina #include <errno.h>
598cf6c252SPaul Traina #ifdef HAVE_MALLOC_H
608cf6c252SPaul Traina #include <malloc.h>
618cf6c252SPaul Traina #endif
628cf6c252SPaul Traina #include <stdio.h>
638cf6c252SPaul Traina #include <string.h>
648cf6c252SPaul Traina #include <unistd.h>
658cf6c252SPaul Traina 
668cf6c252SPaul Traina #include "pcap-int.h"
678cf6c252SPaul Traina 
688cf6c252SPaul Traina #include "gnuc.h"
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);
1138cf6c252SPaul Traina 			sprintf(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) {
1858cf6c252SPaul Traina 			sprintf(ebuf, "NIOCSTIME: %s", pcap_strerror(errno));
1868cf6c252SPaul Traina 			return (-1);
1878cf6c252SPaul Traina 		}
1888cf6c252SPaul Traina 	}
1898cf6c252SPaul Traina 	flags = NI_TIMESTAMP | NI_LEN | NI_DROPS;
1908cf6c252SPaul Traina 	if (promisc)
1918cf6c252SPaul Traina 		flags |= NI_PROMISC;
1928cf6c252SPaul Traina 	si.ic_cmd = NIOCSFLAGS;
1938cf6c252SPaul Traina 	si.ic_len = sizeof(flags);
1948cf6c252SPaul Traina 	si.ic_dp = (char *)&flags;
1958cf6c252SPaul Traina 	if (ioctl(fd, I_STR, (char *)&si) < 0) {
1968cf6c252SPaul Traina 		sprintf(ebuf, "NIOCSFLAGS: %s", pcap_strerror(errno));
1978cf6c252SPaul Traina 		return (-1);
1988cf6c252SPaul Traina 	}
1998cf6c252SPaul Traina 	return (0);
2008cf6c252SPaul Traina }
2018cf6c252SPaul Traina 
2028cf6c252SPaul Traina pcap_t *
2038cf6c252SPaul Traina pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
2048cf6c252SPaul Traina {
2058cf6c252SPaul Traina 	struct strioctl si;		/* struct for ioctl() */
2068cf6c252SPaul Traina 	struct ifreq ifr;		/* interface request struct */
2078cf6c252SPaul Traina 	int chunksize = CHUNKSIZE;
2088cf6c252SPaul Traina 	int fd;
2098cf6c252SPaul Traina 	static char dev[] = "/dev/nit";
2108cf6c252SPaul Traina 	register pcap_t *p;
2118cf6c252SPaul Traina 
2128cf6c252SPaul Traina 	p = (pcap_t *)malloc(sizeof(*p));
2138cf6c252SPaul Traina 	if (p == NULL) {
2148cf6c252SPaul Traina 		strcpy(ebuf, pcap_strerror(errno));
2158cf6c252SPaul Traina 		return (NULL);
2168cf6c252SPaul Traina 	}
2178cf6c252SPaul Traina 
2188cf6c252SPaul Traina 	if (snaplen < 96)
2198cf6c252SPaul Traina 		/*
2208cf6c252SPaul Traina 		 * NIT requires a snapshot length of at least 96.
2218cf6c252SPaul Traina 		 */
2228cf6c252SPaul Traina 		snaplen = 96;
2238cf6c252SPaul Traina 
2248cf6c252SPaul Traina 	bzero(p, sizeof(*p));
2258cf6c252SPaul Traina 	p->fd = fd = open(dev, O_RDONLY);
2268cf6c252SPaul Traina 	if (fd < 0) {
2278cf6c252SPaul Traina 		sprintf(ebuf, "%s: %s", dev, pcap_strerror(errno));
2288cf6c252SPaul Traina 		goto bad;
2298cf6c252SPaul Traina 	}
2308cf6c252SPaul Traina 
2318cf6c252SPaul Traina 	/* arrange to get discrete messages from the STREAM and use NIT_BUF */
2328cf6c252SPaul Traina 	if (ioctl(fd, I_SRDOPT, (char *)RMSGD) < 0) {
2338cf6c252SPaul Traina 		sprintf(ebuf, "I_SRDOPT: %s", pcap_strerror(errno));
2348cf6c252SPaul Traina 		goto bad;
2358cf6c252SPaul Traina 	}
2368cf6c252SPaul Traina 	if (ioctl(fd, I_PUSH, "nbuf") < 0) {
2378cf6c252SPaul Traina 		sprintf(ebuf, "push nbuf: %s", pcap_strerror(errno));
2388cf6c252SPaul Traina 		goto bad;
2398cf6c252SPaul Traina 	}
2408cf6c252SPaul Traina 	/* set the chunksize */
2418cf6c252SPaul Traina 	si.ic_cmd = NIOCSCHUNK;
2428cf6c252SPaul Traina 	si.ic_timout = INFTIM;
2438cf6c252SPaul Traina 	si.ic_len = sizeof(chunksize);
2448cf6c252SPaul Traina 	si.ic_dp = (char *)&chunksize;
2458cf6c252SPaul Traina 	if (ioctl(fd, I_STR, (char *)&si) < 0) {
2468cf6c252SPaul Traina 		sprintf(ebuf, "NIOCSCHUNK: %s", pcap_strerror(errno));
2478cf6c252SPaul Traina 		goto bad;
2488cf6c252SPaul Traina 	}
2498cf6c252SPaul Traina 
2508cf6c252SPaul Traina 	/* request the interface */
2518cf6c252SPaul Traina 	strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
2528cf6c252SPaul Traina 	ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = ' ';
2538cf6c252SPaul Traina 	si.ic_cmd = NIOCBIND;
2548cf6c252SPaul Traina 	si.ic_len = sizeof(ifr);
2558cf6c252SPaul Traina 	si.ic_dp = (char *)&ifr;
2568cf6c252SPaul Traina 	if (ioctl(fd, I_STR, (char *)&si) < 0) {
2578cf6c252SPaul Traina 		sprintf(ebuf, "NIOCBIND: %s: %s",
2588cf6c252SPaul Traina 			ifr.ifr_name, pcap_strerror(errno));
2598cf6c252SPaul Traina 		goto bad;
2608cf6c252SPaul Traina 	}
2618cf6c252SPaul Traina 
2628cf6c252SPaul Traina 	/* set the snapshot length */
2638cf6c252SPaul Traina 	si.ic_cmd = NIOCSSNAP;
2648cf6c252SPaul Traina 	si.ic_len = sizeof(snaplen);
2658cf6c252SPaul Traina 	si.ic_dp = (char *)&snaplen;
2668cf6c252SPaul Traina 	if (ioctl(fd, I_STR, (char *)&si) < 0) {
2678cf6c252SPaul Traina 		sprintf(ebuf, "NIOCSSNAP: %s", pcap_strerror(errno));
2688cf6c252SPaul Traina 		goto bad;
2698cf6c252SPaul Traina 	}
2708cf6c252SPaul Traina 	p->snapshot = snaplen;
2718cf6c252SPaul Traina 	if (nit_setflags(p->fd, promisc, to_ms, ebuf) < 0)
2728cf6c252SPaul Traina 		goto bad;
2738cf6c252SPaul Traina 
2748cf6c252SPaul Traina 	(void)ioctl(fd, I_FLUSH, (char *)FLUSHR);
2758cf6c252SPaul Traina 	/*
2768cf6c252SPaul Traina 	 * NIT supports only ethernets.
2778cf6c252SPaul Traina 	 */
2788cf6c252SPaul Traina 	p->linktype = DLT_EN10MB;
2798cf6c252SPaul Traina 
2808cf6c252SPaul Traina 	p->bufsize = BUFSPACE;
2818cf6c252SPaul Traina 	p->buffer = (u_char *)malloc(p->bufsize);
2828cf6c252SPaul Traina 	if (p->buffer == NULL) {
2838cf6c252SPaul Traina 		strcpy(ebuf, pcap_strerror(errno));
2848cf6c252SPaul Traina 		goto bad;
2858cf6c252SPaul Traina 	}
2868cf6c252SPaul Traina 	return (p);
2878cf6c252SPaul Traina  bad:
2888cf6c252SPaul Traina 	if (fd >= 0)
2898cf6c252SPaul Traina 		close(fd);
2908cf6c252SPaul Traina 	free(p);
2918cf6c252SPaul Traina 	return (NULL);
2928cf6c252SPaul Traina }
2938cf6c252SPaul Traina 
2948cf6c252SPaul Traina int
2958cf6c252SPaul Traina pcap_setfilter(pcap_t *p, struct bpf_program *fp)
2968cf6c252SPaul Traina {
2978cf6c252SPaul Traina 
2988cf6c252SPaul Traina 	p->fcode = *fp;
2998cf6c252SPaul Traina 	return (0);
3008cf6c252SPaul Traina }
301