1/*
2 * This code is derived from code formerly in pcap-dlpi.c, originally
3 * contributed by Atanu Ghosh (atanu@cs.ucl.ac.uk), University College
4 * London, and subsequently modified by Guy Harris (guy@alum.mit.edu),
5 * Mark Pizzolato <List-tcpdump-workers@subscriptions.pizzolato.net>,
6 * Mark C. Brown (mbrown@hp.com), and Sagun Shakya <Sagun.Shakya@Sun.COM>.
7 */
8
9/*
10 * This file contains dlpi/libdlpi related common functions used
11 * by pcap-[dlpi,libdlpi].c.
12 */
13#ifndef lint
14static const char rcsid[] _U_ =
15	"@(#) $Header: /tcpdump/master/libpcap/dlpisubs.c,v 1.3 2008-12-02 16:40:19 guy Exp $ (LBL)";
16#endif
17
18#ifdef HAVE_CONFIG_H
19#include "config.h"
20#endif
21
22#ifndef DL_IPATM
23#define DL_IPATM	0x12	/* ATM Classical IP interface */
24#endif
25
26#ifdef HAVE_SYS_BUFMOD_H
27	/*
28	 * Size of a bufmod chunk to pass upstream; that appears to be the
29	 * biggest value to which you can set it, and setting it to that value
30	 * (which is bigger than what appears to be the Solaris default of 8192)
31	 * reduces the number of packet drops.
32	 */
33#define	CHUNKSIZE	65536
34
35	/*
36	 * Size of the buffer to allocate for packet data we read; it must be
37	 * large enough to hold a chunk.
38	 */
39#define	PKTBUFSIZE	CHUNKSIZE
40
41#else /* HAVE_SYS_BUFMOD_H */
42
43	/*
44	 * Size of the buffer to allocate for packet data we read; this is
45	 * what the value used to be - there's no particular reason why it
46	 * should be tied to MAXDLBUF, but we'll leave it as this for now.
47	 */
48#define	MAXDLBUF	8192
49#define	PKTBUFSIZE	(MAXDLBUF * sizeof(bpf_u_int32))
50
51#endif
52
53#include <sys/types.h>
54#include <sys/time.h>
55#ifdef HAVE_SYS_BUFMOD_H
56#include <sys/bufmod.h>
57#endif
58#include <sys/dlpi.h>
59#include <sys/stream.h>
60
61#include <errno.h>
62#include <memory.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66#include <stropts.h>
67#include <unistd.h>
68
69#include "pcap-int.h"
70#include "dlpisubs.h"
71
72#ifdef HAVE_SYS_BUFMOD_H
73static void pcap_stream_err(const char *, int, char *);
74#endif
75
76/*
77 * Get the packet statistics.
78 */
79int
80pcap_stats_dlpi(pcap_t *p, struct pcap_stat *ps)
81{
82
83	/*
84	 * "ps_recv" counts packets handed to the filter, not packets
85	 * that passed the filter.  As filtering is done in userland,
86	 * this would not include packets dropped because we ran out
87	 * of buffer space; in order to make this more like other
88	 * platforms (Linux 2.4 and later, BSDs with BPF), where the
89	 * "packets received" count includes packets received but dropped
90	 * due to running out of buffer space, and to keep from confusing
91	 * applications that, for example, compute packet drop percentages,
92	 * we also make it count packets dropped by "bufmod" (otherwise we
93	 * might run the risk of the packet drop count being bigger than
94	 * the received-packet count).
95	 *
96	 * "ps_drop" counts packets dropped by "bufmod" because of
97	 * flow control requirements or resource exhaustion; it doesn't
98	 * count packets dropped by the interface driver, or packets
99	 * dropped upstream.  As filtering is done in userland, it counts
100	 * packets regardless of whether they would've passed the filter.
101	 *
102	 * These statistics don't include packets not yet read from
103	 * the kernel by libpcap, but they may include packets not
104	 * yet read from libpcap by the application.
105	 */
106	*ps = p->md.stat;
107
108	/*
109	 * Add in the drop count, as per the above comment.
110	 */
111	ps->ps_recv += ps->ps_drop;
112	return (0);
113}
114
115/*
116 * Loop through the packets and call the callback for each packet.
117 * Return the number of packets read.
118 */
119int
120pcap_process_pkts(pcap_t *p, pcap_handler callback, u_char *user,
121	int count, u_char *bufp, int len)
122{
123	int n, caplen, origlen;
124	u_char *ep, *pk;
125	struct pcap_pkthdr pkthdr;
126#ifdef HAVE_SYS_BUFMOD_H
127	struct sb_hdr *sbp;
128#ifdef LBL_ALIGN
129	struct sb_hdr sbhdr;
130#endif
131#endif
132
133	/* Loop through packets */
134	ep = bufp + len;
135	n = 0;
136
137#ifdef HAVE_SYS_BUFMOD_H
138	while (bufp < ep) {
139		/*
140		 * Has "pcap_breakloop()" been called?
141		 * If so, return immediately - if we haven't read any
142		 * packets, clear the flag and return -2 to indicate
143		 * that we were told to break out of the loop, otherwise
144		 * leave the flag set, so that the *next* call will break
145		 * out of the loop without having read any packets, and
146		 * return the number of packets we've processed so far.
147		 */
148		if (p->break_loop) {
149			if (n == 0) {
150				p->break_loop = 0;
151				return (-2);
152			} else {
153				p->bp = bufp;
154				p->cc = ep - bufp;
155				return (n);
156			}
157		}
158#ifdef LBL_ALIGN
159		if ((long)bufp & 3) {
160			sbp = &sbhdr;
161			memcpy(sbp, bufp, sizeof(*sbp));
162		} else
163#endif
164			sbp = (struct sb_hdr *)bufp;
165		p->md.stat.ps_drop = sbp->sbh_drops;
166		pk = bufp + sizeof(*sbp);
167		bufp += sbp->sbh_totlen;
168		origlen = sbp->sbh_origlen;
169		caplen = sbp->sbh_msglen;
170#else
171		origlen = len;
172		caplen = min(p->snapshot, len);
173		pk = bufp;
174		bufp += caplen;
175#endif
176		++p->md.stat.ps_recv;
177		if (bpf_filter(p->fcode.bf_insns, pk, origlen, caplen)) {
178#ifdef HAVE_SYS_BUFMOD_H
179			pkthdr.ts.tv_sec = sbp->sbh_timestamp.tv_sec;
180			pkthdr.ts.tv_usec = sbp->sbh_timestamp.tv_usec;
181#else
182			(void) gettimeofday(&pkthdr.ts, NULL);
183#endif
184			pkthdr.len = origlen;
185			pkthdr.caplen = caplen;
186			/* Insure caplen does not exceed snapshot */
187			if (pkthdr.caplen > p->snapshot)
188				pkthdr.caplen = p->snapshot;
189			(*callback)(user, &pkthdr, pk);
190			if (++n >= count && count >= 0) {
191				p->cc = ep - bufp;
192				p->bp = bufp;
193				return (n);
194			}
195		}
196#ifdef HAVE_SYS_BUFMOD_H
197	}
198#endif
199	p->cc = 0;
200	return (n);
201}
202
203/*
204 * Process the mac type. Returns -1 if no matching mac type found, otherwise 0.
205 */
206int
207pcap_process_mactype(pcap_t *p, u_int mactype)
208{
209	int retv = 0;
210
211	switch (mactype) {
212
213	case DL_CSMACD:
214	case DL_ETHER:
215		p->linktype = DLT_EN10MB;
216		p->offset = 2;
217		/*
218		 * This is (presumably) a real Ethernet capture; give it a
219		 * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so
220		 * that an application can let you choose it, in case you're
221		 * capturing DOCSIS traffic that a Cisco Cable Modem
222		 * Termination System is putting out onto an Ethernet (it
223		 * doesn't put an Ethernet header onto the wire, it puts raw
224		 * DOCSIS frames out on the wire inside the low-level
225		 * Ethernet framing).
226		 */
227		p->dlt_list = (u_int *)malloc(sizeof(u_int) * 2);
228		/*
229		 * If that fails, just leave the list empty.
230		 */
231		if (p->dlt_list != NULL) {
232			p->dlt_list[0] = DLT_EN10MB;
233			p->dlt_list[1] = DLT_DOCSIS;
234			p->dlt_count = 2;
235		}
236		break;
237
238	case DL_FDDI:
239		p->linktype = DLT_FDDI;
240		p->offset = 3;
241		break;
242
243	case DL_TPR:
244		/* XXX - what about DL_TPB?  Is that Token Bus?  */
245		p->linktype = DLT_IEEE802;
246		p->offset = 2;
247		break;
248
249#ifdef HAVE_SOLARIS
250	case DL_IPATM:
251		p->linktype = DLT_SUNATM;
252		p->offset = 0;  /* works for LANE and LLC encapsulation */
253		break;
254#endif
255
256	default:
257		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown mactype %u",
258		    mactype);
259		retv = -1;
260	}
261
262	return (retv);
263}
264
265#ifdef HAVE_SYS_BUFMOD_H
266/*
267 * Push and configure the buffer module. Returns -1 for error, otherwise 0.
268 */
269int
270pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout)
271{
272	int retv = 0;
273
274	bpf_u_int32 ss, chunksize;
275
276	/* Non-standard call to get the data nicely buffered. */
277	if (ioctl(p->fd, I_PUSH, "bufmod") != 0) {
278		pcap_stream_err("I_PUSH bufmod", errno, p->errbuf);
279		retv = -1;
280	}
281
282	ss = snaplen;
283	if (ss > 0 &&
284	    strioctl(p->fd, SBIOCSSNAP, sizeof(ss), (char *)&ss) != 0) {
285		pcap_stream_err("SBIOCSSNAP", errno, p->errbuf);
286		retv = -1;
287	}
288
289	/* Set up the bufmod timeout. */
290	if (timeout != 0) {
291		struct timeval to;
292
293		to.tv_sec = timeout / 1000;
294		to.tv_usec = (timeout * 1000) % 1000000;
295		if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) {
296			pcap_stream_err("SBIOCSTIME", errno, p->errbuf);
297			retv = -1;
298		}
299	}
300
301	/* Set the chunk length. */
302	chunksize = CHUNKSIZE;
303	if (strioctl(p->fd, SBIOCSCHUNK, sizeof(chunksize), (char *)&chunksize)
304	    != 0) {
305		pcap_stream_err("SBIOCSCHUNKP", errno, p->errbuf);
306		retv = -1;
307	}
308
309	return (retv);
310}
311#endif /* HAVE_SYS_BUFMOD_H */
312
313/*
314 * Allocate data buffer. Returns -1 if memory allocation fails, else 0.
315 */
316int
317pcap_alloc_databuf(pcap_t *p)
318{
319	p->bufsize = PKTBUFSIZE;
320	p->buffer = (u_char *)malloc(p->bufsize + p->offset);
321	if (p->buffer == NULL) {
322		strlcpy(p->errbuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE);
323		return (-1);
324	}
325
326	return (0);
327}
328
329/*
330 * Issue a STREAMS I_STR ioctl. Returns -1 on error, otherwise
331 * length of returned data on success.
332 */
333int
334strioctl(int fd, int cmd, int len, char *dp)
335{
336	struct strioctl str;
337	int retv;
338
339	str.ic_cmd = cmd;
340	str.ic_timout = -1;
341	str.ic_len = len;
342	str.ic_dp = dp;
343	if ((retv = ioctl(fd, I_STR, &str)) < 0)
344		return (retv);
345
346	return (str.ic_len);
347}
348
349#ifdef HAVE_SYS_BUFMOD_H
350/*
351 * Write stream error message to errbuf.
352 */
353static void
354pcap_stream_err(const char *func, int err, char *errbuf)
355{
356	snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", func, pcap_strerror(err));
357}
358#endif
359