1190214Srpaulo/*
2190214Srpaulo * Copyright (c) 1993, 1994, 1995, 1996, 1997
3190214Srpaulo *	The Regents of the University of California.  All rights reserved.
4190214Srpaulo *
5190214Srpaulo * Redistribution and use in source and binary forms, with or without
6190214Srpaulo * modification, are permitted provided that: (1) source code distributions
7190214Srpaulo * retain the above copyright notice and this paragraph in its entirety, (2)
8190214Srpaulo * distributions including binary code include the above copyright notice and
9190214Srpaulo * this paragraph in its entirety in the documentation or other materials
10190214Srpaulo * provided with the distribution, and (3) all advertising materials mentioning
11190214Srpaulo * features or use of this software display the following acknowledgement:
12190214Srpaulo * ``This product includes software developed by the University of California,
13190214Srpaulo * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14190214Srpaulo * the University nor the names of its contributors may be used to endorse
15190214Srpaulo * or promote products derived from this software without specific prior
16190214Srpaulo * written permission.
17190214Srpaulo * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18190214Srpaulo * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19190214Srpaulo * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20190214Srpaulo *
21190214Srpaulo * This code contributed by Sagun Shakya (sagun.shakya@sun.com)
22190214Srpaulo */
23190214Srpaulo/*
24190214Srpaulo * Packet capture routines for DLPI using libdlpi under SunOS 5.11.
25190214Srpaulo */
26190214Srpaulo
27190214Srpaulo#ifndef lint
28190214Srpaulostatic const char rcsid[] _U_ =
29214518Srpaulo	"@(#) $Header: /tcpdump/master/libpcap/pcap-libdlpi.c,v 1.6 2008-04-14 20:40:58 guy Exp $ (LBL)";
30190214Srpaulo#endif
31190214Srpaulo
32190214Srpaulo#ifdef HAVE_CONFIG_H
33190214Srpaulo#include "config.h"
34190214Srpaulo#endif
35190214Srpaulo
36190214Srpaulo#include <sys/types.h>
37190214Srpaulo#include <sys/time.h>
38190214Srpaulo#include <sys/bufmod.h>
39190214Srpaulo#include <sys/stream.h>
40190214Srpaulo#include <libdlpi.h>
41190214Srpaulo#include <errno.h>
42190214Srpaulo#include <memory.h>
43190214Srpaulo#include <stropts.h>
44190214Srpaulo#include <stdio.h>
45190214Srpaulo#include <stdlib.h>
46190214Srpaulo#include <string.h>
47190214Srpaulo
48190214Srpaulo#include "pcap-int.h"
49190214Srpaulo#include "dlpisubs.h"
50190214Srpaulo
51190214Srpaulo/* Forwards. */
52235426Sdelphijstatic int dlpromiscon(pcap_t *, bpf_u_int32);
53190214Srpaulostatic int pcap_read_libdlpi(pcap_t *, int, pcap_handler, u_char *);
54190214Srpaulostatic int pcap_inject_libdlpi(pcap_t *, const void *, size_t);
55190214Srpaulostatic void pcap_close_libdlpi(pcap_t *);
56190214Srpaulostatic void pcap_libdlpi_err(const char *, const char *, int, char *);
57214518Srpaulostatic void pcap_cleanup_libdlpi(pcap_t *);
58190214Srpaulo
59190214Srpaulo/*
60190214Srpaulo * list_interfaces() will list all the network links that are
61190214Srpaulo * available on a system.
62190214Srpaulo */
63190214Srpaulostatic boolean_t list_interfaces(const char *, void *);
64190214Srpaulo
65190214Srpaulotypedef struct linknamelist {
66190214Srpaulo	char	linkname[DLPI_LINKNAME_MAX];
67190214Srpaulo	struct linknamelist *lnl_next;
68190214Srpaulo} linknamelist_t;
69190214Srpaulo
70190214Srpaulotypedef struct linkwalk {
71190214Srpaulo	linknamelist_t	*lw_list;
72190214Srpaulo	int		lw_err;
73190214Srpaulo} linkwalk_t;
74190214Srpaulo
75190214Srpaulo/*
76190214Srpaulo * The caller of this function should free the memory allocated
77190214Srpaulo * for each linknamelist_t "entry" allocated.
78190214Srpaulo */
79190214Srpaulostatic boolean_t
80190214Srpaulolist_interfaces(const char *linkname, void *arg)
81190214Srpaulo{
82190214Srpaulo	linkwalk_t	*lwp = arg;
83190214Srpaulo	linknamelist_t	*entry;
84190214Srpaulo
85190214Srpaulo	if ((entry = calloc(1, sizeof(linknamelist_t))) == NULL) {
86190214Srpaulo		lwp->lw_err = ENOMEM;
87190214Srpaulo		return (B_TRUE);
88190214Srpaulo	}
89190214Srpaulo	(void) strlcpy(entry->linkname, linkname, DLPI_LINKNAME_MAX);
90190214Srpaulo
91190214Srpaulo	if (lwp->lw_list == NULL) {
92190214Srpaulo		lwp->lw_list = entry;
93190214Srpaulo	} else {
94190214Srpaulo		entry->lnl_next = lwp->lw_list;
95190214Srpaulo		lwp->lw_list = entry;
96190214Srpaulo	}
97190214Srpaulo
98190214Srpaulo	return (B_FALSE);
99190214Srpaulo}
100190214Srpaulo
101190214Srpaulostatic int
102190214Srpaulopcap_activate_libdlpi(pcap_t *p)
103190214Srpaulo{
104190214Srpaulo	int retv;
105190214Srpaulo	dlpi_handle_t dh;
106190214Srpaulo	dlpi_info_t dlinfo;
107190214Srpaulo	int err = PCAP_ERROR;
108190214Srpaulo
109190214Srpaulo	/*
110190214Srpaulo	 * Enable Solaris raw and passive DLPI extensions;
111190214Srpaulo	 * dlpi_open() will not fail if the underlying link does not support
112190214Srpaulo	 * passive mode. See dlpi(7P) for details.
113190214Srpaulo	 */
114190214Srpaulo	retv = dlpi_open(p->opt.source, &dh, DLPI_RAW|DLPI_PASSIVE);
115190214Srpaulo	if (retv != DLPI_SUCCESS) {
116190214Srpaulo		if (retv == DLPI_ELINKNAMEINVAL || retv == DLPI_ENOLINK)
117190214Srpaulo			err = PCAP_ERROR_NO_SUCH_DEVICE;
118235426Sdelphij		else if (retv == DL_SYSERR &&
119235426Sdelphij		    (errno == EPERM || errno == EACCES))
120190214Srpaulo			err = PCAP_ERROR_PERM_DENIED;
121190214Srpaulo		pcap_libdlpi_err(p->opt.source, "dlpi_open", retv,
122190214Srpaulo		    p->errbuf);
123190214Srpaulo		return (err);
124190214Srpaulo	}
125190214Srpaulo	p->dlpi_hd = dh;
126190214Srpaulo
127190214Srpaulo	if (p->opt.rfmon) {
128190214Srpaulo		/*
129190214Srpaulo		 * This device exists, but we don't support monitor mode
130190214Srpaulo		 * any platforms that support DLPI.
131190214Srpaulo		 */
132190214Srpaulo		err = PCAP_ERROR_RFMON_NOTSUP;
133190214Srpaulo		goto bad;
134190214Srpaulo	}
135190214Srpaulo
136190214Srpaulo	/* Bind with DLPI_ANY_SAP. */
137190214Srpaulo	if ((retv = dlpi_bind(p->dlpi_hd, DLPI_ANY_SAP, 0)) != DLPI_SUCCESS) {
138190214Srpaulo		pcap_libdlpi_err(p->opt.source, "dlpi_bind", retv, p->errbuf);
139190214Srpaulo		goto bad;
140190214Srpaulo	}
141190214Srpaulo
142190214Srpaulo	/* Enable promiscuous mode. */
143190214Srpaulo	if (p->opt.promisc) {
144235426Sdelphij		err = dlpromiscon(p, DL_PROMISC_PHYS);
145235426Sdelphij		if (err < 0) {
146235426Sdelphij			/*
147235426Sdelphij			 * "You don't have permission to capture on
148235426Sdelphij			 * this device" and "you don't have permission
149235426Sdelphij			 * to capture in promiscuous mode on this
150235426Sdelphij			 * device" are different; let the user know,
151235426Sdelphij			 * so if they can't get permission to
152235426Sdelphij			 * capture in promiscuous mode, they can at
153235426Sdelphij			 * least try to capture in non-promiscuous
154235426Sdelphij			 * mode.
155235426Sdelphij			 *
156235426Sdelphij			 * XXX - you might have to capture in
157235426Sdelphij			 * promiscuous mode to see outgoing packets.
158235426Sdelphij			 */
159235426Sdelphij			if (err == PCAP_ERROR_PERM_DENIED)
160235426Sdelphij				err = PCAP_ERROR_PROMISC_PERM_DENIED;
161190214Srpaulo			goto bad;
162190214Srpaulo		}
163190214Srpaulo	} else {
164190214Srpaulo		/* Try to enable multicast. */
165235426Sdelphij		err = dlpromiscon(p, DL_PROMISC_MULTI);
166235426Sdelphij		if (err < 0)
167190214Srpaulo			goto bad;
168190214Srpaulo	}
169190214Srpaulo
170190214Srpaulo	/* Try to enable SAP promiscuity. */
171235426Sdelphij	err = dlpromiscon(p, DL_PROMISC_SAP);
172235426Sdelphij	if (err < 0) {
173235426Sdelphij		/*
174235426Sdelphij		 * Not fatal, since the DL_PROMISC_PHYS mode worked.
175235426Sdelphij		 * Report it as a warning, however.
176235426Sdelphij		 */
177235426Sdelphij		if (p->opt.promisc)
178235426Sdelphij			err = PCAP_WARNING;
179235426Sdelphij		else
180190214Srpaulo			goto bad;
181190214Srpaulo	}
182190214Srpaulo
183190214Srpaulo	/* Determine link type.  */
184190214Srpaulo	if ((retv = dlpi_info(p->dlpi_hd, &dlinfo, 0)) != DLPI_SUCCESS) {
185190214Srpaulo		pcap_libdlpi_err(p->opt.source, "dlpi_info", retv, p->errbuf);
186190214Srpaulo		goto bad;
187190214Srpaulo	}
188190214Srpaulo
189190214Srpaulo	if (pcap_process_mactype(p, dlinfo.di_mactype) != 0)
190190214Srpaulo		goto bad;
191190214Srpaulo
192190214Srpaulo	p->fd = dlpi_fd(p->dlpi_hd);
193190214Srpaulo
194190214Srpaulo	/* Push and configure bufmod. */
195214518Srpaulo	if (pcap_conf_bufmod(p, p->snapshot, p->md.timeout) != 0)
196190214Srpaulo		goto bad;
197190214Srpaulo
198190214Srpaulo	/*
199190214Srpaulo	 * Flush the read side.
200190214Srpaulo	 */
201190214Srpaulo	if (ioctl(p->fd, I_FLUSH, FLUSHR) != 0) {
202190214Srpaulo		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "FLUSHR: %s",
203190214Srpaulo		    pcap_strerror(errno));
204190214Srpaulo		goto bad;
205190214Srpaulo	}
206190214Srpaulo
207190214Srpaulo	/* Allocate data buffer. */
208190214Srpaulo	if (pcap_alloc_databuf(p) != 0)
209190214Srpaulo		goto bad;
210190214Srpaulo
211190214Srpaulo	/*
212190214Srpaulo	 * "p->fd" is a FD for a STREAMS device, so "select()" and
213190214Srpaulo	 * "poll()" should work on it.
214190214Srpaulo	 */
215190214Srpaulo	p->selectable_fd = p->fd;
216190214Srpaulo
217190214Srpaulo	p->read_op = pcap_read_libdlpi;
218190214Srpaulo	p->inject_op = pcap_inject_libdlpi;
219190214Srpaulo	p->setfilter_op = install_bpf_program;	/* No kernel filtering */
220190214Srpaulo	p->setdirection_op = NULL;	/* Not implemented */
221190214Srpaulo	p->set_datalink_op = NULL;	/* Can't change data link type */
222190214Srpaulo	p->getnonblock_op = pcap_getnonblock_fd;
223190214Srpaulo	p->setnonblock_op = pcap_setnonblock_fd;
224190214Srpaulo	p->stats_op = pcap_stats_dlpi;
225190214Srpaulo	p->cleanup_op = pcap_cleanup_libdlpi;
226190214Srpaulo
227190214Srpaulo	return (0);
228190214Srpaulobad:
229190214Srpaulo	pcap_cleanup_libdlpi(p);
230190214Srpaulo	return (err);
231190214Srpaulo}
232190214Srpaulo
233235426Sdelphij#define STRINGIFY(n)	#n
234235426Sdelphij
235235426Sdelphijstatic int
236235426Sdelphijdlpromiscon(pcap_t *p, bpf_u_int32 level)
237235426Sdelphij{
238235426Sdelphij	int err;
239235426Sdelphij
240251129Sdelphij	retv = dlpi_promiscon(p->dlpi_hd, level);
241235426Sdelphij	if (retv != DLPI_SUCCESS) {
242235426Sdelphij		if (retv == DL_SYSERR &&
243235426Sdelphij		    (errno == EPERM || errno == EACCES))
244235426Sdelphij			err = PCAP_ERROR_PERM_DENIED;
245235426Sdelphij		else
246235426Sdelphij			err = PCAP_ERROR;
247235426Sdelphij		pcap_libdlpi_err(p->opt.source, "dlpi_promiscon" STRINGIFY(level),
248235426Sdelphij		    retv, p->errbuf);
249235426Sdelphij		return (err);
250235426Sdelphij	}
251235426Sdelphij	return (0);
252235426Sdelphij}
253235426Sdelphij
254190214Srpaulo/*
255190214Srpaulo * In Solaris, the "standard" mechanism" i.e SIOCGLIFCONF will only find
256190214Srpaulo * network links that are plumbed and are up. dlpi_walk(3DLPI) will find
257190214Srpaulo * additional network links present in the system.
258190214Srpaulo */
259190214Srpauloint
260190214Srpaulopcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf)
261190214Srpaulo{
262190214Srpaulo	int retv = 0;
263190214Srpaulo
264190214Srpaulo	linknamelist_t	*entry, *next;
265190214Srpaulo	linkwalk_t	lw = {NULL, 0};
266190214Srpaulo	int 		save_errno;
267190214Srpaulo
268190214Srpaulo	/* dlpi_walk() for loopback will be added here. */
269190214Srpaulo
270190214Srpaulo	dlpi_walk(list_interfaces, &lw, 0);
271190214Srpaulo
272190214Srpaulo	if (lw.lw_err != 0) {
273190214Srpaulo		snprintf(errbuf, PCAP_ERRBUF_SIZE,
274190214Srpaulo		    "dlpi_walk: %s", pcap_strerror(lw.lw_err));
275190214Srpaulo		retv = -1;
276190214Srpaulo		goto done;
277190214Srpaulo	}
278190214Srpaulo
279190214Srpaulo	/* Add linkname if it does not exist on the list. */
280190214Srpaulo	for (entry = lw.lw_list; entry != NULL; entry = entry->lnl_next) {
281190214Srpaulo		if (pcap_add_if(alldevsp, entry->linkname, 0, NULL, errbuf) < 0)
282190214Srpaulo			retv = -1;
283190214Srpaulo	}
284190214Srpaulodone:
285190214Srpaulo	save_errno = errno;
286190214Srpaulo	for (entry = lw.lw_list; entry != NULL; entry = next) {
287190214Srpaulo		next = entry->lnl_next;
288190214Srpaulo		free(entry);
289190214Srpaulo	}
290190214Srpaulo	errno = save_errno;
291190214Srpaulo
292190214Srpaulo	return (retv);
293190214Srpaulo}
294190214Srpaulo
295190214Srpaulo/*
296190214Srpaulo * Read data received on DLPI handle. Returns -2 if told to terminate, else
297190214Srpaulo * returns the number of packets read.
298190214Srpaulo */
299190214Srpaulostatic int
300190214Srpaulopcap_read_libdlpi(pcap_t *p, int count, pcap_handler callback, u_char *user)
301190214Srpaulo{
302190214Srpaulo	int len;
303190214Srpaulo	u_char *bufp;
304190214Srpaulo	size_t msglen;
305190214Srpaulo	int retv;
306190214Srpaulo
307190214Srpaulo	len = p->cc;
308190214Srpaulo	if (len != 0) {
309190214Srpaulo		bufp = p->bp;
310190214Srpaulo		goto process_pkts;
311190214Srpaulo	}
312190214Srpaulo	do {
313190214Srpaulo		/* Has "pcap_breakloop()" been called? */
314190214Srpaulo		if (p->break_loop) {
315190214Srpaulo			/*
316190214Srpaulo			 * Yes - clear the flag that indicates that it has,
317190214Srpaulo			 * and return -2 to indicate that we were told to
318190214Srpaulo			 * break out of the loop.
319190214Srpaulo			 */
320190214Srpaulo			p->break_loop = 0;
321190214Srpaulo			return (-2);
322190214Srpaulo		}
323190214Srpaulo
324190214Srpaulo		msglen = p->bufsize;
325190214Srpaulo		bufp = p->buffer + p->offset;
326190214Srpaulo
327190214Srpaulo		retv = dlpi_recv(p->dlpi_hd, NULL, NULL, bufp,
328190214Srpaulo		    &msglen, -1, NULL);
329190214Srpaulo		if (retv != DLPI_SUCCESS) {
330190214Srpaulo			/*
331190214Srpaulo			 * This is most likely a call to terminate out of the
332190214Srpaulo			 * loop. So, do not return an error message, instead
333190214Srpaulo			 * check if "pcap_breakloop()" has been called above.
334190214Srpaulo			 */
335190214Srpaulo			if (retv == DL_SYSERR && errno == EINTR) {
336190214Srpaulo				len = 0;
337190214Srpaulo				continue;
338190214Srpaulo			}
339190214Srpaulo			pcap_libdlpi_err(dlpi_linkname(p->dlpi_hd),
340190214Srpaulo			    "dlpi_recv", retv, p->errbuf);
341190214Srpaulo			return (-1);
342190214Srpaulo		}
343190214Srpaulo		len = msglen;
344190214Srpaulo	} while (len == 0);
345190214Srpaulo
346190214Srpauloprocess_pkts:
347190214Srpaulo	return (pcap_process_pkts(p, callback, user, count, bufp, len));
348190214Srpaulo}
349190214Srpaulo
350190214Srpaulostatic int
351190214Srpaulopcap_inject_libdlpi(pcap_t *p, const void *buf, size_t size)
352190214Srpaulo{
353190214Srpaulo	int retv;
354190214Srpaulo
355190214Srpaulo	retv = dlpi_send(p->dlpi_hd, NULL, 0, buf, size, NULL);
356190214Srpaulo	if (retv != DLPI_SUCCESS) {
357190214Srpaulo		pcap_libdlpi_err(dlpi_linkname(p->dlpi_hd), "dlpi_send", retv,
358190214Srpaulo		    p->errbuf);
359190214Srpaulo		return (-1);
360190214Srpaulo	}
361190214Srpaulo	/*
362190214Srpaulo	 * dlpi_send(3DLPI) does not provide a way to return the number of
363190214Srpaulo	 * bytes sent on the wire. Based on the fact that DLPI_SUCCESS was
364190214Srpaulo	 * returned we are assuming 'size' bytes were sent.
365190214Srpaulo	 */
366190214Srpaulo	return (size);
367190214Srpaulo}
368190214Srpaulo
369190214Srpaulo/*
370190214Srpaulo * Close dlpi handle.
371190214Srpaulo */
372190214Srpaulostatic void
373190214Srpaulopcap_cleanup_libdlpi(pcap_t *p)
374190214Srpaulo{
375190214Srpaulo	if (p->dlpi_hd != NULL) {
376190214Srpaulo		dlpi_close(p->dlpi_hd);
377190214Srpaulo		p->dlpi_hd = NULL;
378190214Srpaulo		p->fd = -1;
379190214Srpaulo	}
380190214Srpaulo	pcap_cleanup_live_common(p);
381190214Srpaulo}
382190214Srpaulo
383190214Srpaulo/*
384190214Srpaulo * Write error message to buffer.
385190214Srpaulo */
386190214Srpaulostatic void
387190214Srpaulopcap_libdlpi_err(const char *linkname, const char *func, int err, char *errbuf)
388190214Srpaulo{
389190214Srpaulo	snprintf(errbuf, PCAP_ERRBUF_SIZE, "libpcap: %s failed on %s: %s",
390190214Srpaulo	    func, linkname, dlpi_strerror(err));
391190214Srpaulo}
392190214Srpaulo
393190214Srpaulopcap_t *
394251129Sdelphijpcap_create_interface(const char *device, char *ebuf)
395190214Srpaulo{
396190214Srpaulo	pcap_t *p;
397190214Srpaulo
398190214Srpaulo	p = pcap_create_common(device, ebuf);
399190214Srpaulo	if (p == NULL)
400190214Srpaulo		return (NULL);
401190214Srpaulo
402190214Srpaulo	p->activate_op = pcap_activate_libdlpi;
403190214Srpaulo	return (p);
404190214Srpaulo}
405