1335640Shselasky/*
2335640Shselasky * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996
3335640Shselasky *	The Regents of the University of California.  All rights reserved.
4335640Shselasky *
5335640Shselasky * Redistribution and use in source and binary forms, with or without
6335640Shselasky * modification, are permitted provided that: (1) source code distributions
7335640Shselasky * retain the above copyright notice and this paragraph in its entirety, (2)
8335640Shselasky * distributions including binary code include the above copyright notice and
9335640Shselasky * this paragraph in its entirety in the documentation or other materials
10335640Shselasky * provided with the distribution, and (3) all advertising materials mentioning
11335640Shselasky * features or use of this software display the following acknowledgement:
12335640Shselasky * ``This product includes software developed by the University of California,
13335640Shselasky * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14335640Shselasky * the University nor the names of its contributors may be used to endorse
15335640Shselasky * or promote products derived from this software without specific prior
16335640Shselasky * written permission.
17335640Shselasky * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18335640Shselasky * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19335640Shselasky * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20335640Shselasky */
21335640Shselasky
22335640Shselasky#ifdef HAVE_CONFIG_H
23335640Shselasky#include <config.h>
24335640Shselasky#endif
25335640Shselasky
26335640Shselasky#include <sys/types.h>
27335640Shselasky#include <sys/time.h>
28335640Shselasky#include <sys/timeb.h>
29335640Shselasky#include <sys/file.h>
30335640Shselasky#include <sys/ioctl.h>
31335640Shselasky#include <sys/socket.h>
32335640Shselasky
33335640Shselasky#include <net/if.h>
34335640Shselasky#include <net/nit.h>
35335640Shselasky
36335640Shselasky#include <netinet/in.h>
37335640Shselasky#include <netinet/in_systm.h>
38335640Shselasky#include <netinet/ip.h>
39335640Shselasky#include <netinet/if_ether.h>
40335640Shselasky#include <netinet/ip_var.h>
41335640Shselasky#include <netinet/udp.h>
42335640Shselasky#include <netinet/udp_var.h>
43335640Shselasky#include <netinet/tcp.h>
44335640Shselasky#include <netinet/tcpip.h>
45335640Shselasky
46335640Shselasky#include <ctype.h>
47335640Shselasky#include <errno.h>
48335640Shselasky#include <stdio.h>
49335640Shselasky
50335640Shselasky#include "pcap-int.h"
51335640Shselasky
52335640Shselasky#ifdef HAVE_OS_PROTO_H
53335640Shselasky#include "os-proto.h"
54335640Shselasky#endif
55335640Shselasky
56335640Shselasky/*
57335640Shselasky * The chunk size for NIT.  This is the amount of buffering
58335640Shselasky * done for read calls.
59335640Shselasky */
60335640Shselasky#define CHUNKSIZE (2*1024)
61335640Shselasky
62335640Shselasky/*
63335640Shselasky * The total buffer space used by NIT.
64335640Shselasky */
65335640Shselasky#define BUFSPACE (4*CHUNKSIZE)
66335640Shselasky
67335640Shselasky/* Forwards */
68335640Shselaskystatic int nit_setflags(int, int, int, char *);
69335640Shselasky
70335640Shselasky/*
71335640Shselasky * Private data for capturing on NIT devices.
72335640Shselasky */
73335640Shselaskystruct pcap_nit {
74335640Shselasky	struct pcap_stat stat;
75335640Shselasky};
76335640Shselasky
77335640Shselaskystatic int
78335640Shselaskypcap_stats_nit(pcap_t *p, struct pcap_stat *ps)
79335640Shselasky{
80335640Shselasky	struct pcap_nit *pn = p->priv;
81335640Shselasky
82335640Shselasky	/*
83335640Shselasky	 * "ps_recv" counts packets handed to the filter, not packets
84335640Shselasky	 * that passed the filter.  As filtering is done in userland,
85335640Shselasky	 * this does not include packets dropped because we ran out
86335640Shselasky	 * of buffer space.
87335640Shselasky	 *
88335640Shselasky	 * "ps_drop" presumably counts packets dropped by the socket
89335640Shselasky	 * because of flow control requirements or resource exhaustion;
90335640Shselasky	 * it doesn't count packets dropped by the interface driver.
91335640Shselasky	 * As filtering is done in userland, it counts packets regardless
92335640Shselasky	 * of whether they would've passed the filter.
93335640Shselasky	 *
94335640Shselasky	 * These statistics don't include packets not yet read from the
95335640Shselasky	 * kernel by libpcap or packets not yet read from libpcap by the
96335640Shselasky	 * application.
97335640Shselasky	 */
98335640Shselasky	*ps = pn->stat;
99335640Shselasky	return (0);
100335640Shselasky}
101335640Shselasky
102335640Shselaskystatic int
103335640Shselaskypcap_read_nit(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
104335640Shselasky{
105335640Shselasky	struct pcap_nit *pn = p->priv;
106335640Shselasky	register int cc, n;
107335640Shselasky	register u_char *bp, *cp, *ep;
108335640Shselasky	register struct nit_hdr *nh;
109335640Shselasky	register int caplen;
110335640Shselasky
111335640Shselasky	cc = p->cc;
112335640Shselasky	if (cc == 0) {
113335640Shselasky		cc = read(p->fd, (char *)p->buffer, p->bufsize);
114335640Shselasky		if (cc < 0) {
115335640Shselasky			if (errno == EWOULDBLOCK)
116335640Shselasky				return (0);
117335640Shselasky			pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf),
118335640Shselasky			    errno, "pcap_read");
119335640Shselasky			return (-1);
120335640Shselasky		}
121335640Shselasky		bp = (u_char *)p->buffer;
122335640Shselasky	} else
123335640Shselasky		bp = p->bp;
124335640Shselasky
125335640Shselasky	/*
126335640Shselasky	 * Loop through each packet.  The increment expression
127335640Shselasky	 * rounds up to the next int boundary past the end of
128335640Shselasky	 * the previous packet.
129335640Shselasky	 */
130335640Shselasky	n = 0;
131335640Shselasky	ep = bp + cc;
132335640Shselasky	while (bp < ep) {
133335640Shselasky		/*
134335640Shselasky		 * Has "pcap_breakloop()" been called?
135335640Shselasky		 * If so, return immediately - if we haven't read any
136335640Shselasky		 * packets, clear the flag and return -2 to indicate
137335640Shselasky		 * that we were told to break out of the loop, otherwise
138335640Shselasky		 * leave the flag set, so that the *next* call will break
139335640Shselasky		 * out of the loop without having read any packets, and
140335640Shselasky		 * return the number of packets we've processed so far.
141335640Shselasky		 */
142335640Shselasky		if (p->break_loop) {
143335640Shselasky			if (n == 0) {
144335640Shselasky				p->break_loop = 0;
145335640Shselasky				return (-2);
146335640Shselasky			} else {
147335640Shselasky				p->cc = ep - bp;
148335640Shselasky				p->bp = bp;
149335640Shselasky				return (n);
150335640Shselasky			}
151335640Shselasky		}
152335640Shselasky
153335640Shselasky		nh = (struct nit_hdr *)bp;
154335640Shselasky		cp = bp + sizeof(*nh);
155335640Shselasky
156335640Shselasky		switch (nh->nh_state) {
157335640Shselasky
158335640Shselasky		case NIT_CATCH:
159335640Shselasky			break;
160335640Shselasky
161335640Shselasky		case NIT_NOMBUF:
162335640Shselasky		case NIT_NOCLUSTER:
163335640Shselasky		case NIT_NOSPACE:
164335640Shselasky			pn->stat.ps_drop = nh->nh_dropped;
165335640Shselasky			continue;
166335640Shselasky
167335640Shselasky		case NIT_SEQNO:
168335640Shselasky			continue;
169335640Shselasky
170335640Shselasky		default:
171335640Shselasky			pcap_snprintf(p->errbuf, sizeof(p->errbuf),
172335640Shselasky			    "bad nit state %d", nh->nh_state);
173335640Shselasky			return (-1);
174335640Shselasky		}
175335640Shselasky		++pn->stat.ps_recv;
176335640Shselasky		bp += ((sizeof(struct nit_hdr) + nh->nh_datalen +
177335640Shselasky		    sizeof(int) - 1) & ~(sizeof(int) - 1));
178335640Shselasky
179335640Shselasky		caplen = nh->nh_wirelen;
180335640Shselasky		if (caplen > p->snapshot)
181335640Shselasky			caplen = p->snapshot;
182335640Shselasky		if (bpf_filter(p->fcode.bf_insns, cp, nh->nh_wirelen, caplen)) {
183335640Shselasky			struct pcap_pkthdr h;
184335640Shselasky			h.ts = nh->nh_timestamp;
185335640Shselasky			h.len = nh->nh_wirelen;
186335640Shselasky			h.caplen = caplen;
187335640Shselasky			(*callback)(user, &h, cp);
188335640Shselasky			if (++n >= cnt && !PACKET_COUNT_IS_UNLIMITED(cnt)) {
189335640Shselasky				p->cc = ep - bp;
190335640Shselasky				p->bp = bp;
191335640Shselasky				return (n);
192335640Shselasky			}
193335640Shselasky		}
194335640Shselasky	}
195335640Shselasky	p->cc = 0;
196335640Shselasky	return (n);
197335640Shselasky}
198335640Shselasky
199335640Shselaskystatic int
200335640Shselaskypcap_inject_nit(pcap_t *p, const void *buf, size_t size)
201335640Shselasky{
202335640Shselasky	struct sockaddr sa;
203335640Shselasky	int ret;
204335640Shselasky
205335640Shselasky	memset(&sa, 0, sizeof(sa));
206335640Shselasky	strncpy(sa.sa_data, device, sizeof(sa.sa_data));
207335640Shselasky	ret = sendto(p->fd, buf, size, 0, &sa, sizeof(sa));
208335640Shselasky	if (ret == -1) {
209335640Shselasky		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
210335640Shselasky		    errno, "send");
211335640Shselasky		return (-1);
212335640Shselasky	}
213335640Shselasky	return (ret);
214335640Shselasky}
215335640Shselasky
216335640Shselaskystatic int
217335640Shselaskynit_setflags(pcap_t *p)
218335640Shselasky{
219335640Shselasky	struct nit_ioc nioc;
220335640Shselasky
221335640Shselasky	memset(&nioc, 0, sizeof(nioc));
222335640Shselasky	nioc.nioc_typetomatch = NT_ALLTYPES;
223335640Shselasky	nioc.nioc_snaplen = p->snapshot;
224335640Shselasky	nioc.nioc_bufalign = sizeof(int);
225335640Shselasky	nioc.nioc_bufoffset = 0;
226335640Shselasky
227335640Shselasky	if (p->opt.buffer_size != 0)
228335640Shselasky		nioc.nioc_bufspace = p->opt.buffer_size;
229335640Shselasky	else {
230335640Shselasky		/* Default buffer size */
231335640Shselasky		nioc.nioc_bufspace = BUFSPACE;
232335640Shselasky	}
233335640Shselasky
234335640Shselasky	if (p->opt.immediate) {
235335640Shselasky		/*
236335640Shselasky		 * XXX - will this cause packets to be delivered immediately?
237335640Shselasky		 * XXX - given that this is for SunOS prior to 4.0, do
238335640Shselasky		 * we care?
239335640Shselasky		 */
240335640Shselasky		nioc.nioc_chunksize = 0;
241335640Shselasky	} else
242335640Shselasky		nioc.nioc_chunksize = CHUNKSIZE;
243335640Shselasky	if (p->opt.timeout != 0) {
244335640Shselasky		nioc.nioc_flags |= NF_TIMEOUT;
245335640Shselasky		nioc.nioc_timeout.tv_sec = p->opt.timeout / 1000;
246335640Shselasky		nioc.nioc_timeout.tv_usec = (p->opt.timeout * 1000) % 1000000;
247335640Shselasky	}
248335640Shselasky	if (p->opt.promisc)
249335640Shselasky		nioc.nioc_flags |= NF_PROMISC;
250335640Shselasky
251335640Shselasky	if (ioctl(p->fd, SIOCSNIT, &nioc) < 0) {
252335640Shselasky		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
253335640Shselasky		    errno, "SIOCSNIT");
254335640Shselasky		return (-1);
255335640Shselasky	}
256335640Shselasky	return (0);
257335640Shselasky}
258335640Shselasky
259335640Shselaskystatic int
260335640Shselaskypcap_activate_nit(pcap_t *p)
261335640Shselasky{
262335640Shselasky	int fd;
263335640Shselasky	struct sockaddr_nit snit;
264335640Shselasky
265335640Shselasky	if (p->opt.rfmon) {
266335640Shselasky		/*
267335640Shselasky		 * No monitor mode on SunOS 3.x or earlier (no
268335640Shselasky		 * Wi-Fi *devices* for the hardware that supported
269335640Shselasky		 * them!).
270335640Shselasky		 */
271335640Shselasky		return (PCAP_ERROR_RFMON_NOTSUP);
272335640Shselasky	}
273335640Shselasky
274335640Shselasky	/*
275335640Shselasky	 * Turn a negative snapshot value (invalid), a snapshot value of
276335640Shselasky	 * 0 (unspecified), or a value bigger than the normal maximum
277335640Shselasky	 * value, into the maximum allowed value.
278335640Shselasky	 *
279335640Shselasky	 * If some application really *needs* a bigger snapshot
280335640Shselasky	 * length, we should just increase MAXIMUM_SNAPLEN.
281335640Shselasky	 */
282335640Shselasky	if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
283335640Shselasky		p->snapshot = MAXIMUM_SNAPLEN;
284335640Shselasky
285335640Shselasky	if (p->snapshot < 96)
286335640Shselasky		/*
287335640Shselasky		 * NIT requires a snapshot length of at least 96.
288335640Shselasky		 */
289335640Shselasky		p->snapshot = 96;
290335640Shselasky
291335640Shselasky	memset(p, 0, sizeof(*p));
292335640Shselasky	p->fd = fd = socket(AF_NIT, SOCK_RAW, NITPROTO_RAW);
293335640Shselasky	if (fd < 0) {
294335640Shselasky		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
295335640Shselasky		    errno, "socket");
296335640Shselasky		goto bad;
297335640Shselasky	}
298335640Shselasky	snit.snit_family = AF_NIT;
299335640Shselasky	(void)strncpy(snit.snit_ifname, p->opt.device, NITIFSIZ);
300335640Shselasky
301335640Shselasky	if (bind(fd, (struct sockaddr *)&snit, sizeof(snit))) {
302335640Shselasky		/*
303335640Shselasky		 * XXX - there's probably a particular bind error that
304335640Shselasky		 * means "there's no such device" and a particular bind
305335640Shselasky		 * error that means "that device doesn't support NIT";
306335640Shselasky		 * they might be the same error, if they both end up
307335640Shselasky		 * meaning "NIT doesn't know about that device".
308335640Shselasky		 */
309335640Shselasky		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
310335640Shselasky		    errno, "bind: %s", snit.snit_ifname);
311335640Shselasky		goto bad;
312335640Shselasky	}
313335640Shselasky	if (nit_setflags(p) < 0)
314335640Shselasky		goto bad;
315335640Shselasky
316335640Shselasky	/*
317335640Shselasky	 * NIT supports only ethernets.
318335640Shselasky	 */
319335640Shselasky	p->linktype = DLT_EN10MB;
320335640Shselasky
321335640Shselasky	p->bufsize = BUFSPACE;
322335640Shselasky	p->buffer = malloc(p->bufsize);
323335640Shselasky	if (p->buffer == NULL) {
324335640Shselasky		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
325335640Shselasky		    errno, "malloc");
326335640Shselasky		goto bad;
327335640Shselasky	}
328335640Shselasky
329335640Shselasky	/*
330335640Shselasky	 * "p->fd" is a socket, so "select()" should work on it.
331335640Shselasky	 */
332335640Shselasky	p->selectable_fd = p->fd;
333335640Shselasky
334335640Shselasky	/*
335335640Shselasky	 * This is (presumably) a real Ethernet capture; give it a
336335640Shselasky	 * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so
337335640Shselasky	 * that an application can let you choose it, in case you're
338335640Shselasky	 * capturing DOCSIS traffic that a Cisco Cable Modem
339335640Shselasky	 * Termination System is putting out onto an Ethernet (it
340335640Shselasky	 * doesn't put an Ethernet header onto the wire, it puts raw
341335640Shselasky	 * DOCSIS frames out on the wire inside the low-level
342335640Shselasky	 * Ethernet framing).
343335640Shselasky	 */
344335640Shselasky	p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
345335640Shselasky	/*
346335640Shselasky	 * If that fails, just leave the list empty.
347335640Shselasky	 */
348335640Shselasky	if (p->dlt_list != NULL) {
349335640Shselasky		p->dlt_list[0] = DLT_EN10MB;
350335640Shselasky		p->dlt_list[1] = DLT_DOCSIS;
351335640Shselasky		p->dlt_count = 2;
352335640Shselasky	}
353335640Shselasky
354335640Shselasky	p->read_op = pcap_read_nit;
355335640Shselasky	p->inject_op = pcap_inject_nit;
356335640Shselasky	p->setfilter_op = install_bpf_program;	/* no kernel filtering */
357335640Shselasky	p->setdirection_op = NULL;	/* Not implemented. */
358335640Shselasky	p->set_datalink_op = NULL;	/* can't change data link type */
359335640Shselasky	p->getnonblock_op = pcap_getnonblock_fd;
360335640Shselasky	p->setnonblock_op = pcap_setnonblock_fd;
361335640Shselasky	p->stats_op = pcap_stats_nit;
362335640Shselasky
363335640Shselasky	return (0);
364335640Shselasky bad:
365335640Shselasky	pcap_cleanup_live_common(p);
366335640Shselasky	return (PCAP_ERROR);
367335640Shselasky}
368335640Shselasky
369335640Shselaskypcap_t *
370335640Shselaskypcap_create_interface(const char *device _U_, char *ebuf)
371335640Shselasky{
372335640Shselasky	pcap_t *p;
373335640Shselasky
374335640Shselasky	p = pcap_create_common(ebuf, sizeof (struct pcap_nit));
375335640Shselasky	if (p == NULL)
376335640Shselasky		return (NULL);
377335640Shselasky
378335640Shselasky	p->activate_op = pcap_activate_nit;
379335640Shselasky	return (p);
380335640Shselasky}
381335640Shselasky
382335640Shselasky/*
383335640Shselasky * XXX - there's probably a particular bind error that means "that device
384335640Shselasky * doesn't support NIT"; if so, we should try a bind and use that.
385335640Shselasky */
386335640Shselaskystatic int
387335640Shselaskycan_be_bound(const char *name _U_)
388335640Shselasky{
389335640Shselasky	return (1);
390335640Shselasky}
391335640Shselasky
392335640Shselaskystatic int
393335640Shselaskyget_if_flags(const char *name _U_, bpf_u_int32 *flags _U_, char *errbuf _U_)
394335640Shselasky{
395335640Shselasky	/*
396335640Shselasky	 * Nothing we can do.
397335640Shselasky	 * XXX - is there a way to find out whether an adapter has
398335640Shselasky	 * something plugged into it?
399335640Shselasky	 */
400335640Shselasky	return (0);
401335640Shselasky}
402335640Shselasky
403335640Shselaskyint
404335640Shselaskypcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
405335640Shselasky{
406335640Shselasky	return (pcap_findalldevs_interfaces(devlistp, errbuf, can_be_bound,
407335640Shselasky	    get_if_flags));
408335640Shselasky}
409335640Shselasky
410335640Shselasky/*
411335640Shselasky * Libpcap version string.
412335640Shselasky */
413335640Shselaskyconst char *
414335640Shselaskypcap_lib_version(void)
415335640Shselasky{
416335640Shselasky	return (PCAP_VERSION_STRING);
417335640Shselasky}
418