1335640Shselasky/*
2335640Shselasky * This code is derived from code formerly in pcap-dlpi.c, originally
3335640Shselasky * contributed by Atanu Ghosh (atanu@cs.ucl.ac.uk), University College
4335640Shselasky * London, and subsequently modified by Guy Harris (guy@alum.mit.edu),
5335640Shselasky * Mark Pizzolato <List-tcpdump-workers@subscriptions.pizzolato.net>,
6335640Shselasky * Mark C. Brown (mbrown@hp.com), and Sagun Shakya <Sagun.Shakya@Sun.COM>.
7335640Shselasky */
8335640Shselasky
9335640Shselasky/*
10335640Shselasky * This file contains dlpi/libdlpi related common functions used
11335640Shselasky * by pcap-[dlpi,libdlpi].c.
12335640Shselasky */
13335640Shselasky
14335640Shselasky#ifdef HAVE_CONFIG_H
15335640Shselasky#include <config.h>
16335640Shselasky#endif
17335640Shselasky
18335640Shselasky#ifndef DL_IPATM
19335640Shselasky#define DL_IPATM	0x12	/* ATM Classical IP interface */
20335640Shselasky#endif
21335640Shselasky
22335640Shselasky#ifdef HAVE_SYS_BUFMOD_H
23335640Shselasky	/*
24335640Shselasky	 * Size of a bufmod chunk to pass upstream; that appears to be the
25335640Shselasky	 * biggest value to which you can set it, and setting it to that value
26335640Shselasky	 * (which is bigger than what appears to be the Solaris default of 8192)
27335640Shselasky	 * reduces the number of packet drops.
28335640Shselasky	 */
29335640Shselasky#define	CHUNKSIZE	65536
30335640Shselasky
31335640Shselasky	/*
32335640Shselasky	 * Size of the buffer to allocate for packet data we read; it must be
33335640Shselasky	 * large enough to hold a chunk.
34335640Shselasky	 */
35335640Shselasky#define	PKTBUFSIZE	CHUNKSIZE
36335640Shselasky
37335640Shselasky#else /* HAVE_SYS_BUFMOD_H */
38335640Shselasky
39335640Shselasky	/*
40335640Shselasky	 * Size of the buffer to allocate for packet data we read; this is
41335640Shselasky	 * what the value used to be - there's no particular reason why it
42335640Shselasky	 * should be tied to MAXDLBUF, but we'll leave it as this for now.
43335640Shselasky	 */
44335640Shselasky#define	MAXDLBUF	8192
45335640Shselasky#define	PKTBUFSIZE	(MAXDLBUF * sizeof(bpf_u_int32))
46335640Shselasky
47335640Shselasky#endif
48335640Shselasky
49335640Shselasky#include <sys/types.h>
50335640Shselasky#include <sys/time.h>
51335640Shselasky#ifdef HAVE_SYS_BUFMOD_H
52335640Shselasky#include <sys/bufmod.h>
53335640Shselasky#endif
54335640Shselasky#include <sys/dlpi.h>
55335640Shselasky#include <sys/stream.h>
56335640Shselasky
57335640Shselasky#include <errno.h>
58335640Shselasky#include <memory.h>
59335640Shselasky#include <stdio.h>
60335640Shselasky#include <stdlib.h>
61335640Shselasky#include <string.h>
62335640Shselasky#include <stropts.h>
63335640Shselasky#include <unistd.h>
64335640Shselasky
65335640Shselasky#ifdef HAVE_LIBDLPI
66335640Shselasky#include <libdlpi.h>
67335640Shselasky#endif
68335640Shselasky
69335640Shselasky#include "pcap-int.h"
70335640Shselasky#include "dlpisubs.h"
71335640Shselasky
72335640Shselasky#ifdef HAVE_SYS_BUFMOD_H
73335640Shselaskystatic void pcap_stream_err(const char *, int, char *);
74335640Shselasky#endif
75335640Shselasky
76335640Shselasky/*
77335640Shselasky * Get the packet statistics.
78335640Shselasky */
79335640Shselaskyint
80335640Shselaskypcap_stats_dlpi(pcap_t *p, struct pcap_stat *ps)
81335640Shselasky{
82335640Shselasky	struct pcap_dlpi *pd = p->priv;
83335640Shselasky
84335640Shselasky	/*
85335640Shselasky	 * "ps_recv" counts packets handed to the filter, not packets
86335640Shselasky	 * that passed the filter.  As filtering is done in userland,
87335640Shselasky	 * this would not include packets dropped because we ran out
88335640Shselasky	 * of buffer space; in order to make this more like other
89335640Shselasky	 * platforms (Linux 2.4 and later, BSDs with BPF), where the
90335640Shselasky	 * "packets received" count includes packets received but dropped
91335640Shselasky	 * due to running out of buffer space, and to keep from confusing
92335640Shselasky	 * applications that, for example, compute packet drop percentages,
93335640Shselasky	 * we also make it count packets dropped by "bufmod" (otherwise we
94335640Shselasky	 * might run the risk of the packet drop count being bigger than
95335640Shselasky	 * the received-packet count).
96335640Shselasky	 *
97335640Shselasky	 * "ps_drop" counts packets dropped by "bufmod" because of
98335640Shselasky	 * flow control requirements or resource exhaustion; it doesn't
99335640Shselasky	 * count packets dropped by the interface driver, or packets
100335640Shselasky	 * dropped upstream.  As filtering is done in userland, it counts
101335640Shselasky	 * packets regardless of whether they would've passed the filter.
102335640Shselasky	 *
103335640Shselasky	 * These statistics don't include packets not yet read from
104335640Shselasky	 * the kernel by libpcap, but they may include packets not
105335640Shselasky	 * yet read from libpcap by the application.
106335640Shselasky	 */
107335640Shselasky	*ps = pd->stat;
108335640Shselasky
109335640Shselasky	/*
110335640Shselasky	 * Add in the drop count, as per the above comment.
111335640Shselasky	 */
112335640Shselasky	ps->ps_recv += ps->ps_drop;
113335640Shselasky	return (0);
114335640Shselasky}
115335640Shselasky
116335640Shselasky/*
117335640Shselasky * Loop through the packets and call the callback for each packet.
118335640Shselasky * Return the number of packets read.
119335640Shselasky */
120335640Shselaskyint
121335640Shselaskypcap_process_pkts(pcap_t *p, pcap_handler callback, u_char *user,
122335640Shselasky	int count, u_char *bufp, int len)
123335640Shselasky{
124335640Shselasky	struct pcap_dlpi *pd = p->priv;
125335640Shselasky	int n, caplen, origlen;
126335640Shselasky	u_char *ep, *pk;
127335640Shselasky	struct pcap_pkthdr pkthdr;
128335640Shselasky#ifdef HAVE_SYS_BUFMOD_H
129335640Shselasky	struct sb_hdr *sbp;
130335640Shselasky#ifdef LBL_ALIGN
131335640Shselasky	struct sb_hdr sbhdr;
132335640Shselasky#endif
133335640Shselasky#endif
134335640Shselasky
135335640Shselasky	/* Loop through packets */
136335640Shselasky	ep = bufp + len;
137335640Shselasky	n = 0;
138335640Shselasky
139335640Shselasky#ifdef HAVE_SYS_BUFMOD_H
140335640Shselasky	while (bufp < ep) {
141335640Shselasky		/*
142335640Shselasky		 * Has "pcap_breakloop()" been called?
143335640Shselasky		 * If so, return immediately - if we haven't read any
144335640Shselasky		 * packets, clear the flag and return -2 to indicate
145335640Shselasky		 * that we were told to break out of the loop, otherwise
146335640Shselasky		 * leave the flag set, so that the *next* call will break
147335640Shselasky		 * out of the loop without having read any packets, and
148335640Shselasky		 * return the number of packets we've processed so far.
149335640Shselasky		 */
150335640Shselasky		if (p->break_loop) {
151335640Shselasky			if (n == 0) {
152335640Shselasky				p->break_loop = 0;
153335640Shselasky				return (-2);
154335640Shselasky			} else {
155335640Shselasky				p->bp = bufp;
156335640Shselasky				p->cc = ep - bufp;
157335640Shselasky				return (n);
158335640Shselasky			}
159335640Shselasky		}
160335640Shselasky#ifdef LBL_ALIGN
161335640Shselasky		if ((long)bufp & 3) {
162335640Shselasky			sbp = &sbhdr;
163335640Shselasky			memcpy(sbp, bufp, sizeof(*sbp));
164335640Shselasky		} else
165335640Shselasky#endif
166335640Shselasky			sbp = (struct sb_hdr *)bufp;
167335640Shselasky		pd->stat.ps_drop = sbp->sbh_drops;
168335640Shselasky		pk = bufp + sizeof(*sbp);
169335640Shselasky		bufp += sbp->sbh_totlen;
170335640Shselasky		origlen = sbp->sbh_origlen;
171335640Shselasky		caplen = sbp->sbh_msglen;
172335640Shselasky#else
173335640Shselasky		origlen = len;
174335640Shselasky		caplen = min(p->snapshot, len);
175335640Shselasky		pk = bufp;
176335640Shselasky		bufp += caplen;
177335640Shselasky#endif
178335640Shselasky		++pd->stat.ps_recv;
179335640Shselasky		if (bpf_filter(p->fcode.bf_insns, pk, origlen, caplen)) {
180335640Shselasky#ifdef HAVE_SYS_BUFMOD_H
181335640Shselasky			pkthdr.ts.tv_sec = sbp->sbh_timestamp.tv_sec;
182335640Shselasky			pkthdr.ts.tv_usec = sbp->sbh_timestamp.tv_usec;
183335640Shselasky#else
184335640Shselasky			(void) gettimeofday(&pkthdr.ts, NULL);
185335640Shselasky#endif
186335640Shselasky			pkthdr.len = origlen;
187335640Shselasky			pkthdr.caplen = caplen;
188335640Shselasky			/* Insure caplen does not exceed snapshot */
189335640Shselasky			if (pkthdr.caplen > (bpf_u_int32)p->snapshot)
190335640Shselasky				pkthdr.caplen = (bpf_u_int32)p->snapshot;
191335640Shselasky			(*callback)(user, &pkthdr, pk);
192335640Shselasky			if (++n >= count && !PACKET_COUNT_IS_UNLIMITED(count)) {
193335640Shselasky				p->cc = ep - bufp;
194335640Shselasky				p->bp = bufp;
195335640Shselasky				return (n);
196335640Shselasky			}
197335640Shselasky		}
198335640Shselasky#ifdef HAVE_SYS_BUFMOD_H
199335640Shselasky	}
200335640Shselasky#endif
201335640Shselasky	p->cc = 0;
202335640Shselasky	return (n);
203335640Shselasky}
204335640Shselasky
205335640Shselasky/*
206335640Shselasky * Process the mac type. Returns -1 if no matching mac type found, otherwise 0.
207335640Shselasky */
208335640Shselaskyint
209335640Shselaskypcap_process_mactype(pcap_t *p, u_int mactype)
210335640Shselasky{
211335640Shselasky	int retv = 0;
212335640Shselasky
213335640Shselasky	switch (mactype) {
214335640Shselasky
215335640Shselasky	case DL_CSMACD:
216335640Shselasky	case DL_ETHER:
217335640Shselasky		p->linktype = DLT_EN10MB;
218335640Shselasky		p->offset = 2;
219335640Shselasky		/*
220335640Shselasky		 * This is (presumably) a real Ethernet capture; give it a
221335640Shselasky		 * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so
222335640Shselasky		 * that an application can let you choose it, in case you're
223335640Shselasky		 * capturing DOCSIS traffic that a Cisco Cable Modem
224335640Shselasky		 * Termination System is putting out onto an Ethernet (it
225335640Shselasky		 * doesn't put an Ethernet header onto the wire, it puts raw
226335640Shselasky		 * DOCSIS frames out on the wire inside the low-level
227335640Shselasky		 * Ethernet framing).
228335640Shselasky		 */
229335640Shselasky		p->dlt_list = (u_int *)malloc(sizeof(u_int) * 2);
230335640Shselasky		/*
231335640Shselasky		 * If that fails, just leave the list empty.
232335640Shselasky		 */
233335640Shselasky		if (p->dlt_list != NULL) {
234335640Shselasky			p->dlt_list[0] = DLT_EN10MB;
235335640Shselasky			p->dlt_list[1] = DLT_DOCSIS;
236335640Shselasky			p->dlt_count = 2;
237335640Shselasky		}
238335640Shselasky		break;
239335640Shselasky
240335640Shselasky	case DL_FDDI:
241335640Shselasky		p->linktype = DLT_FDDI;
242335640Shselasky		p->offset = 3;
243335640Shselasky		break;
244335640Shselasky
245335640Shselasky	case DL_TPR:
246335640Shselasky		/* XXX - what about DL_TPB?  Is that Token Bus?  */
247335640Shselasky		p->linktype = DLT_IEEE802;
248335640Shselasky		p->offset = 2;
249335640Shselasky		break;
250335640Shselasky
251335640Shselasky#ifdef HAVE_SOLARIS
252335640Shselasky	case DL_IPATM:
253335640Shselasky		p->linktype = DLT_SUNATM;
254335640Shselasky		p->offset = 0;  /* works for LANE and LLC encapsulation */
255335640Shselasky		break;
256335640Shselasky#endif
257335640Shselasky
258335640Shselasky#ifdef DL_IPV4
259335640Shselasky	case DL_IPV4:
260335640Shselasky		p->linktype = DLT_IPV4;
261335640Shselasky		p->offset = 0;
262335640Shselasky		break;
263335640Shselasky#endif
264335640Shselasky
265335640Shselasky#ifdef DL_IPV6
266335640Shselasky	case DL_IPV6:
267335640Shselasky		p->linktype = DLT_IPV6;
268335640Shselasky		p->offset = 0;
269335640Shselasky		break;
270335640Shselasky#endif
271335640Shselasky
272335640Shselasky#ifdef DL_IPNET
273335640Shselasky	case DL_IPNET:
274335640Shselasky		/*
275335640Shselasky		 * XXX - DL_IPNET devices default to "raw IP" rather than
276335640Shselasky		 * "IPNET header"; see
277335640Shselasky		 *
278335640Shselasky		 *    http://seclists.org/tcpdump/2009/q1/202
279335640Shselasky		 *
280335640Shselasky		 * We'd have to do DL_IOC_IPNET_INFO to enable getting
281335640Shselasky		 * the IPNET header.
282335640Shselasky		 */
283335640Shselasky		p->linktype = DLT_RAW;
284335640Shselasky		p->offset = 0;
285335640Shselasky		break;
286335640Shselasky#endif
287335640Shselasky
288335640Shselasky	default:
289335640Shselasky		pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown mactype 0x%x",
290335640Shselasky		    mactype);
291335640Shselasky		retv = -1;
292335640Shselasky	}
293335640Shselasky
294335640Shselasky	return (retv);
295335640Shselasky}
296335640Shselasky
297335640Shselasky#ifdef HAVE_SYS_BUFMOD_H
298335640Shselasky/*
299335640Shselasky * Push and configure the buffer module. Returns -1 for error, otherwise 0.
300335640Shselasky */
301335640Shselaskyint
302335640Shselaskypcap_conf_bufmod(pcap_t *p, int snaplen)
303335640Shselasky{
304335640Shselasky	struct timeval to;
305335640Shselasky	bpf_u_int32 ss, chunksize;
306335640Shselasky
307335640Shselasky	/* Non-standard call to get the data nicely buffered. */
308335640Shselasky	if (ioctl(p->fd, I_PUSH, "bufmod") != 0) {
309335640Shselasky		pcap_stream_err("I_PUSH bufmod", errno, p->errbuf);
310335640Shselasky		return (-1);
311335640Shselasky	}
312335640Shselasky
313335640Shselasky	ss = snaplen;
314335640Shselasky	if (ss > 0 &&
315335640Shselasky	    strioctl(p->fd, SBIOCSSNAP, sizeof(ss), (char *)&ss) != 0) {
316335640Shselasky		pcap_stream_err("SBIOCSSNAP", errno, p->errbuf);
317335640Shselasky		return (-1);
318335640Shselasky	}
319335640Shselasky
320335640Shselasky	if (p->opt.immediate) {
321335640Shselasky		/* Set the timeout to zero, for immediate delivery. */
322335640Shselasky		to.tv_sec = 0;
323335640Shselasky		to.tv_usec = 0;
324335640Shselasky		if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) {
325335640Shselasky			pcap_stream_err("SBIOCSTIME", errno, p->errbuf);
326335640Shselasky			return (-1);
327335640Shselasky		}
328335640Shselasky	} else {
329335640Shselasky		/* Set up the bufmod timeout. */
330335640Shselasky		if (p->opt.timeout != 0) {
331335640Shselasky			to.tv_sec = p->opt.timeout / 1000;
332335640Shselasky			to.tv_usec = (p->opt.timeout * 1000) % 1000000;
333335640Shselasky			if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) {
334335640Shselasky				pcap_stream_err("SBIOCSTIME", errno, p->errbuf);
335335640Shselasky				return (-1);
336335640Shselasky			}
337335640Shselasky		}
338335640Shselasky
339335640Shselasky		/* Set the chunk length. */
340335640Shselasky		chunksize = CHUNKSIZE;
341335640Shselasky		if (strioctl(p->fd, SBIOCSCHUNK, sizeof(chunksize), (char *)&chunksize)
342335640Shselasky		    != 0) {
343335640Shselasky			pcap_stream_err("SBIOCSCHUNKP", errno, p->errbuf);
344335640Shselasky			return (-1);
345335640Shselasky		}
346335640Shselasky	}
347335640Shselasky
348335640Shselasky	return (0);
349335640Shselasky}
350335640Shselasky#endif /* HAVE_SYS_BUFMOD_H */
351335640Shselasky
352335640Shselasky/*
353335640Shselasky * Allocate data buffer. Returns -1 if memory allocation fails, else 0.
354335640Shselasky */
355335640Shselaskyint
356335640Shselaskypcap_alloc_databuf(pcap_t *p)
357335640Shselasky{
358335640Shselasky	p->bufsize = PKTBUFSIZE;
359335640Shselasky	p->buffer = malloc(p->bufsize + p->offset);
360335640Shselasky	if (p->buffer == NULL) {
361335640Shselasky		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
362335640Shselasky		    errno, "malloc");
363335640Shselasky		return (-1);
364335640Shselasky	}
365335640Shselasky
366335640Shselasky	return (0);
367335640Shselasky}
368335640Shselasky
369335640Shselasky/*
370335640Shselasky * Issue a STREAMS I_STR ioctl. Returns -1 on error, otherwise
371335640Shselasky * length of returned data on success.
372335640Shselasky */
373335640Shselaskyint
374335640Shselaskystrioctl(int fd, int cmd, int len, char *dp)
375335640Shselasky{
376335640Shselasky	struct strioctl str;
377335640Shselasky	int retv;
378335640Shselasky
379335640Shselasky	str.ic_cmd = cmd;
380335640Shselasky	str.ic_timout = -1;
381335640Shselasky	str.ic_len = len;
382335640Shselasky	str.ic_dp = dp;
383335640Shselasky	if ((retv = ioctl(fd, I_STR, &str)) < 0)
384335640Shselasky		return (retv);
385335640Shselasky
386335640Shselasky	return (str.ic_len);
387335640Shselasky}
388335640Shselasky
389335640Shselasky#ifdef HAVE_SYS_BUFMOD_H
390335640Shselasky/*
391335640Shselasky * Write stream error message to errbuf.
392335640Shselasky */
393335640Shselaskystatic void
394335640Shselaskypcap_stream_err(const char *func, int err, char *errbuf)
395335640Shselasky{
396335640Shselasky	pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, err, "%s", func);
397335640Shselasky}
398335640Shselasky#endif
399