1190214Srpaulo/*
2190214Srpaulo * This code is derived from code formerly in pcap-dlpi.c, originally
3190214Srpaulo * contributed by Atanu Ghosh (atanu@cs.ucl.ac.uk), University College
4190214Srpaulo * London, and subsequently modified by Guy Harris (guy@alum.mit.edu),
5190214Srpaulo * Mark Pizzolato <List-tcpdump-workers@subscriptions.pizzolato.net>,
6190214Srpaulo * Mark C. Brown (mbrown@hp.com), and Sagun Shakya <Sagun.Shakya@Sun.COM>.
7190214Srpaulo */
8190214Srpaulo
9190214Srpaulo/*
10190214Srpaulo * This file contains dlpi/libdlpi related common functions used
11190214Srpaulo * by pcap-[dlpi,libdlpi].c.
12190214Srpaulo */
13190214Srpaulo#ifndef lint
14190214Srpaulostatic const char rcsid[] _U_ =
15214518Srpaulo	"@(#) $Header: /tcpdump/master/libpcap/dlpisubs.c,v 1.3 2008-12-02 16:40:19 guy Exp $ (LBL)";
16190214Srpaulo#endif
17190214Srpaulo
18190214Srpaulo#ifdef HAVE_CONFIG_H
19190214Srpaulo#include "config.h"
20190214Srpaulo#endif
21190214Srpaulo
22214518Srpaulo#ifndef DL_IPATM
23214518Srpaulo#define DL_IPATM	0x12	/* ATM Classical IP interface */
24214518Srpaulo#endif
25214518Srpaulo
26190214Srpaulo#ifdef HAVE_SYS_BUFMOD_H
27190214Srpaulo	/*
28190214Srpaulo	 * Size of a bufmod chunk to pass upstream; that appears to be the
29190214Srpaulo	 * biggest value to which you can set it, and setting it to that value
30190214Srpaulo	 * (which is bigger than what appears to be the Solaris default of 8192)
31190214Srpaulo	 * reduces the number of packet drops.
32190214Srpaulo	 */
33190214Srpaulo#define	CHUNKSIZE	65536
34190214Srpaulo
35190214Srpaulo	/*
36190214Srpaulo	 * Size of the buffer to allocate for packet data we read; it must be
37190214Srpaulo	 * large enough to hold a chunk.
38190214Srpaulo	 */
39190214Srpaulo#define	PKTBUFSIZE	CHUNKSIZE
40190214Srpaulo
41190214Srpaulo#else /* HAVE_SYS_BUFMOD_H */
42190214Srpaulo
43190214Srpaulo	/*
44190214Srpaulo	 * Size of the buffer to allocate for packet data we read; this is
45190214Srpaulo	 * what the value used to be - there's no particular reason why it
46190214Srpaulo	 * should be tied to MAXDLBUF, but we'll leave it as this for now.
47190214Srpaulo	 */
48214518Srpaulo#define	MAXDLBUF	8192
49190214Srpaulo#define	PKTBUFSIZE	(MAXDLBUF * sizeof(bpf_u_int32))
50190214Srpaulo
51190214Srpaulo#endif
52190214Srpaulo
53190214Srpaulo#include <sys/types.h>
54190214Srpaulo#include <sys/time.h>
55190214Srpaulo#ifdef HAVE_SYS_BUFMOD_H
56190214Srpaulo#include <sys/bufmod.h>
57190214Srpaulo#endif
58190214Srpaulo#include <sys/dlpi.h>
59190214Srpaulo#include <sys/stream.h>
60190214Srpaulo
61190214Srpaulo#include <errno.h>
62190214Srpaulo#include <memory.h>
63190214Srpaulo#include <stdio.h>
64190214Srpaulo#include <stdlib.h>
65190214Srpaulo#include <string.h>
66190214Srpaulo#include <stropts.h>
67190214Srpaulo#include <unistd.h>
68190214Srpaulo
69190214Srpaulo#include "pcap-int.h"
70190214Srpaulo#include "dlpisubs.h"
71190214Srpaulo
72214518Srpaulo#ifdef HAVE_SYS_BUFMOD_H
73190214Srpaulostatic void pcap_stream_err(const char *, int, char *);
74214518Srpaulo#endif
75190214Srpaulo
76190214Srpaulo/*
77190214Srpaulo * Get the packet statistics.
78190214Srpaulo */
79190214Srpauloint
80190214Srpaulopcap_stats_dlpi(pcap_t *p, struct pcap_stat *ps)
81190214Srpaulo{
82190214Srpaulo
83190214Srpaulo	/*
84190214Srpaulo	 * "ps_recv" counts packets handed to the filter, not packets
85190214Srpaulo	 * that passed the filter.  As filtering is done in userland,
86190214Srpaulo	 * this would not include packets dropped because we ran out
87190214Srpaulo	 * of buffer space; in order to make this more like other
88190214Srpaulo	 * platforms (Linux 2.4 and later, BSDs with BPF), where the
89190214Srpaulo	 * "packets received" count includes packets received but dropped
90190214Srpaulo	 * due to running out of buffer space, and to keep from confusing
91190214Srpaulo	 * applications that, for example, compute packet drop percentages,
92190214Srpaulo	 * we also make it count packets dropped by "bufmod" (otherwise we
93190214Srpaulo	 * might run the risk of the packet drop count being bigger than
94190214Srpaulo	 * the received-packet count).
95190214Srpaulo	 *
96190214Srpaulo	 * "ps_drop" counts packets dropped by "bufmod" because of
97190214Srpaulo	 * flow control requirements or resource exhaustion; it doesn't
98190214Srpaulo	 * count packets dropped by the interface driver, or packets
99190214Srpaulo	 * dropped upstream.  As filtering is done in userland, it counts
100190214Srpaulo	 * packets regardless of whether they would've passed the filter.
101190214Srpaulo	 *
102190214Srpaulo	 * These statistics don't include packets not yet read from
103190214Srpaulo	 * the kernel by libpcap, but they may include packets not
104190214Srpaulo	 * yet read from libpcap by the application.
105190214Srpaulo	 */
106190214Srpaulo	*ps = p->md.stat;
107190214Srpaulo
108190214Srpaulo	/*
109190214Srpaulo	 * Add in the drop count, as per the above comment.
110190214Srpaulo	 */
111190214Srpaulo	ps->ps_recv += ps->ps_drop;
112190214Srpaulo	return (0);
113190214Srpaulo}
114190214Srpaulo
115190214Srpaulo/*
116190214Srpaulo * Loop through the packets and call the callback for each packet.
117190214Srpaulo * Return the number of packets read.
118190214Srpaulo */
119190214Srpauloint
120190214Srpaulopcap_process_pkts(pcap_t *p, pcap_handler callback, u_char *user,
121190214Srpaulo	int count, u_char *bufp, int len)
122190214Srpaulo{
123190214Srpaulo	int n, caplen, origlen;
124190214Srpaulo	u_char *ep, *pk;
125190214Srpaulo	struct pcap_pkthdr pkthdr;
126190214Srpaulo#ifdef HAVE_SYS_BUFMOD_H
127190214Srpaulo	struct sb_hdr *sbp;
128190214Srpaulo#ifdef LBL_ALIGN
129190214Srpaulo	struct sb_hdr sbhdr;
130190214Srpaulo#endif
131190214Srpaulo#endif
132190214Srpaulo
133190214Srpaulo	/* Loop through packets */
134190214Srpaulo	ep = bufp + len;
135190214Srpaulo	n = 0;
136190214Srpaulo
137190214Srpaulo#ifdef HAVE_SYS_BUFMOD_H
138190214Srpaulo	while (bufp < ep) {
139190214Srpaulo		/*
140190214Srpaulo		 * Has "pcap_breakloop()" been called?
141190214Srpaulo		 * If so, return immediately - if we haven't read any
142190214Srpaulo		 * packets, clear the flag and return -2 to indicate
143190214Srpaulo		 * that we were told to break out of the loop, otherwise
144190214Srpaulo		 * leave the flag set, so that the *next* call will break
145190214Srpaulo		 * out of the loop without having read any packets, and
146190214Srpaulo		 * return the number of packets we've processed so far.
147190214Srpaulo		 */
148190214Srpaulo		if (p->break_loop) {
149190214Srpaulo			if (n == 0) {
150190214Srpaulo				p->break_loop = 0;
151190214Srpaulo				return (-2);
152190214Srpaulo			} else {
153190214Srpaulo				p->bp = bufp;
154190214Srpaulo				p->cc = ep - bufp;
155190214Srpaulo				return (n);
156190214Srpaulo			}
157190214Srpaulo		}
158190214Srpaulo#ifdef LBL_ALIGN
159190214Srpaulo		if ((long)bufp & 3) {
160190214Srpaulo			sbp = &sbhdr;
161190214Srpaulo			memcpy(sbp, bufp, sizeof(*sbp));
162190214Srpaulo		} else
163190214Srpaulo#endif
164190214Srpaulo			sbp = (struct sb_hdr *)bufp;
165190214Srpaulo		p->md.stat.ps_drop = sbp->sbh_drops;
166190214Srpaulo		pk = bufp + sizeof(*sbp);
167190214Srpaulo		bufp += sbp->sbh_totlen;
168190214Srpaulo		origlen = sbp->sbh_origlen;
169190214Srpaulo		caplen = sbp->sbh_msglen;
170190214Srpaulo#else
171190214Srpaulo		origlen = len;
172190214Srpaulo		caplen = min(p->snapshot, len);
173190214Srpaulo		pk = bufp;
174190214Srpaulo		bufp += caplen;
175190214Srpaulo#endif
176190214Srpaulo		++p->md.stat.ps_recv;
177190214Srpaulo		if (bpf_filter(p->fcode.bf_insns, pk, origlen, caplen)) {
178190214Srpaulo#ifdef HAVE_SYS_BUFMOD_H
179190214Srpaulo			pkthdr.ts.tv_sec = sbp->sbh_timestamp.tv_sec;
180190214Srpaulo			pkthdr.ts.tv_usec = sbp->sbh_timestamp.tv_usec;
181190214Srpaulo#else
182190214Srpaulo			(void) gettimeofday(&pkthdr.ts, NULL);
183190214Srpaulo#endif
184190214Srpaulo			pkthdr.len = origlen;
185190214Srpaulo			pkthdr.caplen = caplen;
186190214Srpaulo			/* Insure caplen does not exceed snapshot */
187190214Srpaulo			if (pkthdr.caplen > p->snapshot)
188190214Srpaulo				pkthdr.caplen = p->snapshot;
189190214Srpaulo			(*callback)(user, &pkthdr, pk);
190190214Srpaulo			if (++n >= count && count >= 0) {
191190214Srpaulo				p->cc = ep - bufp;
192190214Srpaulo				p->bp = bufp;
193190214Srpaulo				return (n);
194190214Srpaulo			}
195190214Srpaulo		}
196190214Srpaulo#ifdef HAVE_SYS_BUFMOD_H
197190214Srpaulo	}
198190214Srpaulo#endif
199190214Srpaulo	p->cc = 0;
200190214Srpaulo	return (n);
201190214Srpaulo}
202190214Srpaulo
203190214Srpaulo/*
204190214Srpaulo * Process the mac type. Returns -1 if no matching mac type found, otherwise 0.
205190214Srpaulo */
206190214Srpauloint
207190214Srpaulopcap_process_mactype(pcap_t *p, u_int mactype)
208190214Srpaulo{
209190214Srpaulo	int retv = 0;
210190214Srpaulo
211190214Srpaulo	switch (mactype) {
212190214Srpaulo
213190214Srpaulo	case DL_CSMACD:
214190214Srpaulo	case DL_ETHER:
215190214Srpaulo		p->linktype = DLT_EN10MB;
216190214Srpaulo		p->offset = 2;
217190214Srpaulo		/*
218190214Srpaulo		 * This is (presumably) a real Ethernet capture; give it a
219190214Srpaulo		 * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so
220190214Srpaulo		 * that an application can let you choose it, in case you're
221190214Srpaulo		 * capturing DOCSIS traffic that a Cisco Cable Modem
222190214Srpaulo		 * Termination System is putting out onto an Ethernet (it
223190214Srpaulo		 * doesn't put an Ethernet header onto the wire, it puts raw
224190214Srpaulo		 * DOCSIS frames out on the wire inside the low-level
225190214Srpaulo		 * Ethernet framing).
226190214Srpaulo		 */
227190214Srpaulo		p->dlt_list = (u_int *)malloc(sizeof(u_int) * 2);
228190214Srpaulo		/*
229190214Srpaulo		 * If that fails, just leave the list empty.
230190214Srpaulo		 */
231190214Srpaulo		if (p->dlt_list != NULL) {
232190214Srpaulo			p->dlt_list[0] = DLT_EN10MB;
233190214Srpaulo			p->dlt_list[1] = DLT_DOCSIS;
234190214Srpaulo			p->dlt_count = 2;
235190214Srpaulo		}
236190214Srpaulo		break;
237190214Srpaulo
238190214Srpaulo	case DL_FDDI:
239190214Srpaulo		p->linktype = DLT_FDDI;
240190214Srpaulo		p->offset = 3;
241190214Srpaulo		break;
242190214Srpaulo
243190214Srpaulo	case DL_TPR:
244190214Srpaulo		/* XXX - what about DL_TPB?  Is that Token Bus?  */
245190214Srpaulo		p->linktype = DLT_IEEE802;
246190214Srpaulo		p->offset = 2;
247190214Srpaulo		break;
248190214Srpaulo
249190214Srpaulo#ifdef HAVE_SOLARIS
250190214Srpaulo	case DL_IPATM:
251190214Srpaulo		p->linktype = DLT_SUNATM;
252190214Srpaulo		p->offset = 0;  /* works for LANE and LLC encapsulation */
253190214Srpaulo		break;
254190214Srpaulo#endif
255190214Srpaulo
256190214Srpaulo	default:
257190214Srpaulo		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown mactype %u",
258190214Srpaulo		    mactype);
259190214Srpaulo		retv = -1;
260190214Srpaulo	}
261190214Srpaulo
262190214Srpaulo	return (retv);
263190214Srpaulo}
264190214Srpaulo
265190214Srpaulo#ifdef HAVE_SYS_BUFMOD_H
266190214Srpaulo/*
267190214Srpaulo * Push and configure the buffer module. Returns -1 for error, otherwise 0.
268190214Srpaulo */
269190214Srpauloint
270190214Srpaulopcap_conf_bufmod(pcap_t *p, int snaplen, int timeout)
271190214Srpaulo{
272190214Srpaulo	int retv = 0;
273190214Srpaulo
274190214Srpaulo	bpf_u_int32 ss, chunksize;
275190214Srpaulo
276190214Srpaulo	/* Non-standard call to get the data nicely buffered. */
277190214Srpaulo	if (ioctl(p->fd, I_PUSH, "bufmod") != 0) {
278190214Srpaulo		pcap_stream_err("I_PUSH bufmod", errno, p->errbuf);
279190214Srpaulo		retv = -1;
280190214Srpaulo	}
281190214Srpaulo
282190214Srpaulo	ss = snaplen;
283190214Srpaulo	if (ss > 0 &&
284190214Srpaulo	    strioctl(p->fd, SBIOCSSNAP, sizeof(ss), (char *)&ss) != 0) {
285190214Srpaulo		pcap_stream_err("SBIOCSSNAP", errno, p->errbuf);
286190214Srpaulo		retv = -1;
287190214Srpaulo	}
288190214Srpaulo
289190214Srpaulo	/* Set up the bufmod timeout. */
290190214Srpaulo	if (timeout != 0) {
291190214Srpaulo		struct timeval to;
292190214Srpaulo
293190214Srpaulo		to.tv_sec = timeout / 1000;
294190214Srpaulo		to.tv_usec = (timeout * 1000) % 1000000;
295190214Srpaulo		if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) {
296190214Srpaulo			pcap_stream_err("SBIOCSTIME", errno, p->errbuf);
297190214Srpaulo			retv = -1;
298190214Srpaulo		}
299190214Srpaulo	}
300190214Srpaulo
301190214Srpaulo	/* Set the chunk length. */
302190214Srpaulo	chunksize = CHUNKSIZE;
303190214Srpaulo	if (strioctl(p->fd, SBIOCSCHUNK, sizeof(chunksize), (char *)&chunksize)
304190214Srpaulo	    != 0) {
305190214Srpaulo		pcap_stream_err("SBIOCSCHUNKP", errno, p->errbuf);
306190214Srpaulo		retv = -1;
307190214Srpaulo	}
308190214Srpaulo
309190214Srpaulo	return (retv);
310190214Srpaulo}
311190214Srpaulo#endif /* HAVE_SYS_BUFMOD_H */
312190214Srpaulo
313190214Srpaulo/*
314190214Srpaulo * Allocate data buffer. Returns -1 if memory allocation fails, else 0.
315190214Srpaulo */
316190214Srpauloint
317190214Srpaulopcap_alloc_databuf(pcap_t *p)
318190214Srpaulo{
319190214Srpaulo	p->bufsize = PKTBUFSIZE;
320190214Srpaulo	p->buffer = (u_char *)malloc(p->bufsize + p->offset);
321190214Srpaulo	if (p->buffer == NULL) {
322190214Srpaulo		strlcpy(p->errbuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE);
323190214Srpaulo		return (-1);
324190214Srpaulo	}
325190214Srpaulo
326190214Srpaulo	return (0);
327190214Srpaulo}
328190214Srpaulo
329190214Srpaulo/*
330190214Srpaulo * Issue a STREAMS I_STR ioctl. Returns -1 on error, otherwise
331190214Srpaulo * length of returned data on success.
332190214Srpaulo */
333190214Srpauloint
334190214Srpaulostrioctl(int fd, int cmd, int len, char *dp)
335190214Srpaulo{
336190214Srpaulo	struct strioctl str;
337190214Srpaulo	int retv;
338190214Srpaulo
339190214Srpaulo	str.ic_cmd = cmd;
340190214Srpaulo	str.ic_timout = -1;
341190214Srpaulo	str.ic_len = len;
342190214Srpaulo	str.ic_dp = dp;
343190214Srpaulo	if ((retv = ioctl(fd, I_STR, &str)) < 0)
344190214Srpaulo		return (retv);
345190214Srpaulo
346190214Srpaulo	return (str.ic_len);
347190214Srpaulo}
348190214Srpaulo
349214518Srpaulo#ifdef HAVE_SYS_BUFMOD_H
350190214Srpaulo/*
351190214Srpaulo * Write stream error message to errbuf.
352190214Srpaulo */
353190214Srpaulostatic void
354190214Srpaulopcap_stream_err(const char *func, int err, char *errbuf)
355190214Srpaulo{
356190214Srpaulo	snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", func, pcap_strerror(err));
357190214Srpaulo}
358214518Srpaulo#endif
359