pcap-libdlpi.c revision 356341
1/*
2 * Copyright (c) 1993, 1994, 1995, 1996, 1997
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 *
21 * This code contributed by Sagun Shakya (sagun.shakya@sun.com)
22 */
23/*
24 * Packet capture routines for DLPI using libdlpi under SunOS 5.11.
25 */
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31#include <sys/types.h>
32#include <sys/time.h>
33#include <sys/bufmod.h>
34#include <sys/stream.h>
35#include <libdlpi.h>
36#include <errno.h>
37#include <memory.h>
38#include <stropts.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42
43#include "pcap-int.h"
44#include "dlpisubs.h"
45
46/* Forwards. */
47static int dlpromiscon(pcap_t *, bpf_u_int32);
48static int pcap_read_libdlpi(pcap_t *, int, pcap_handler, u_char *);
49static int pcap_inject_libdlpi(pcap_t *, const void *, size_t);
50static void pcap_libdlpi_err(const char *, const char *, int, char *);
51static void pcap_cleanup_libdlpi(pcap_t *);
52
53/*
54 * list_interfaces() will list all the network links that are
55 * available on a system.
56 */
57static boolean_t list_interfaces(const char *, void *);
58
59typedef struct linknamelist {
60	char	linkname[DLPI_LINKNAME_MAX];
61	struct linknamelist *lnl_next;
62} linknamelist_t;
63
64typedef struct linkwalk {
65	linknamelist_t	*lw_list;
66	int		lw_err;
67} linkwalk_t;
68
69/*
70 * The caller of this function should free the memory allocated
71 * for each linknamelist_t "entry" allocated.
72 */
73static boolean_t
74list_interfaces(const char *linkname, void *arg)
75{
76	linkwalk_t	*lwp = arg;
77	linknamelist_t	*entry;
78
79	if ((entry = calloc(1, sizeof(linknamelist_t))) == NULL) {
80		lwp->lw_err = ENOMEM;
81		return (B_TRUE);
82	}
83	(void) pcap_strlcpy(entry->linkname, linkname, DLPI_LINKNAME_MAX);
84
85	if (lwp->lw_list == NULL) {
86		lwp->lw_list = entry;
87	} else {
88		entry->lnl_next = lwp->lw_list;
89		lwp->lw_list = entry;
90	}
91
92	return (B_FALSE);
93}
94
95static int
96pcap_activate_libdlpi(pcap_t *p)
97{
98	struct pcap_dlpi *pd = p->priv;
99	int status = 0;
100	int retv;
101	dlpi_handle_t dh;
102	dlpi_info_t dlinfo;
103
104	/*
105	 * Enable Solaris raw and passive DLPI extensions;
106	 * dlpi_open() will not fail if the underlying link does not support
107	 * passive mode. See dlpi(7P) for details.
108	 */
109	retv = dlpi_open(p->opt.device, &dh, DLPI_RAW|DLPI_PASSIVE);
110	if (retv != DLPI_SUCCESS) {
111		if (retv == DLPI_ELINKNAMEINVAL || retv == DLPI_ENOLINK)
112			status = PCAP_ERROR_NO_SUCH_DEVICE;
113		else if (retv == DL_SYSERR &&
114		    (errno == EPERM || errno == EACCES))
115			status = PCAP_ERROR_PERM_DENIED;
116		else
117			status = PCAP_ERROR;
118		pcap_libdlpi_err(p->opt.device, "dlpi_open", retv,
119		    p->errbuf);
120		return (status);
121	}
122	pd->dlpi_hd = dh;
123
124	if (p->opt.rfmon) {
125		/*
126		 * This device exists, but we don't support monitor mode
127		 * any platforms that support DLPI.
128		 */
129		status = PCAP_ERROR_RFMON_NOTSUP;
130		goto bad;
131	}
132
133	/* Bind with DLPI_ANY_SAP. */
134	if ((retv = dlpi_bind(pd->dlpi_hd, DLPI_ANY_SAP, 0)) != DLPI_SUCCESS) {
135		status = PCAP_ERROR;
136		pcap_libdlpi_err(p->opt.device, "dlpi_bind", retv, p->errbuf);
137		goto bad;
138	}
139
140	/*
141	 * Turn a negative snapshot value (invalid), a snapshot value of
142	 * 0 (unspecified), or a value bigger than the normal maximum
143	 * value, into the maximum allowed value.
144	 *
145	 * If some application really *needs* a bigger snapshot
146	 * length, we should just increase MAXIMUM_SNAPLEN.
147	 */
148	if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
149		p->snapshot = MAXIMUM_SNAPLEN;
150
151	/* Enable promiscuous mode. */
152	if (p->opt.promisc) {
153		retv = dlpromiscon(p, DL_PROMISC_PHYS);
154		if (retv < 0) {
155			/*
156			 * "You don't have permission to capture on
157			 * this device" and "you don't have permission
158			 * to capture in promiscuous mode on this
159			 * device" are different; let the user know,
160			 * so if they can't get permission to
161			 * capture in promiscuous mode, they can at
162			 * least try to capture in non-promiscuous
163			 * mode.
164			 *
165			 * XXX - you might have to capture in
166			 * promiscuous mode to see outgoing packets.
167			 */
168			if (retv == PCAP_ERROR_PERM_DENIED)
169				status = PCAP_ERROR_PROMISC_PERM_DENIED;
170			else
171				status = retv;
172			goto bad;
173		}
174	} else {
175		/* Try to enable multicast. */
176		retv = dlpromiscon(p, DL_PROMISC_MULTI);
177		if (retv < 0) {
178			status = retv;
179			goto bad;
180		}
181	}
182
183	/* Try to enable SAP promiscuity. */
184	retv = dlpromiscon(p, DL_PROMISC_SAP);
185	if (retv < 0) {
186		/*
187		 * Not fatal, since the DL_PROMISC_PHYS mode worked.
188		 * Report it as a warning, however.
189		 */
190		if (p->opt.promisc)
191			status = PCAP_WARNING;
192		else {
193			status = retv;
194			goto bad;
195		}
196	}
197
198	/* Determine link type.  */
199	if ((retv = dlpi_info(pd->dlpi_hd, &dlinfo, 0)) != DLPI_SUCCESS) {
200		status = PCAP_ERROR;
201		pcap_libdlpi_err(p->opt.device, "dlpi_info", retv, p->errbuf);
202		goto bad;
203	}
204
205	if (pcap_process_mactype(p, dlinfo.di_mactype) != 0) {
206		status = PCAP_ERROR;
207		goto bad;
208	}
209
210	p->fd = dlpi_fd(pd->dlpi_hd);
211
212	/* Push and configure bufmod. */
213	if (pcap_conf_bufmod(p, p->snapshot) != 0) {
214		status = PCAP_ERROR;
215		goto bad;
216	}
217
218	/*
219	 * Flush the read side.
220	 */
221	if (ioctl(p->fd, I_FLUSH, FLUSHR) != 0) {
222		status = PCAP_ERROR;
223		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
224		    errno, "FLUSHR");
225		goto bad;
226	}
227
228	/* Allocate data buffer. */
229	if (pcap_alloc_databuf(p) != 0) {
230		status = PCAP_ERROR;
231		goto bad;
232	}
233
234	/*
235	 * "p->fd" is a FD for a STREAMS device, so "select()" and
236	 * "poll()" should work on it.
237	 */
238	p->selectable_fd = p->fd;
239
240	p->read_op = pcap_read_libdlpi;
241	p->inject_op = pcap_inject_libdlpi;
242	p->setfilter_op = install_bpf_program;	/* No kernel filtering */
243	p->setdirection_op = NULL;	/* Not implemented */
244	p->set_datalink_op = NULL;	/* Can't change data link type */
245	p->getnonblock_op = pcap_getnonblock_fd;
246	p->setnonblock_op = pcap_setnonblock_fd;
247	p->stats_op = pcap_stats_dlpi;
248	p->cleanup_op = pcap_cleanup_libdlpi;
249
250	return (status);
251bad:
252	pcap_cleanup_libdlpi(p);
253	return (status);
254}
255
256#define STRINGIFY(n)	#n
257
258static int
259dlpromiscon(pcap_t *p, bpf_u_int32 level)
260{
261	struct pcap_dlpi *pd = p->priv;
262	int retv;
263	int err;
264
265	retv = dlpi_promiscon(pd->dlpi_hd, level);
266	if (retv != DLPI_SUCCESS) {
267		if (retv == DL_SYSERR &&
268		    (errno == EPERM || errno == EACCES))
269			err = PCAP_ERROR_PERM_DENIED;
270		else
271			err = PCAP_ERROR;
272		pcap_libdlpi_err(p->opt.device, "dlpi_promiscon" STRINGIFY(level),
273		    retv, p->errbuf);
274		return (err);
275	}
276	return (0);
277}
278
279/*
280 * Presumably everything returned by dlpi_walk() is a DLPI device,
281 * so there's no work to be done here to check whether name refers
282 * to a DLPI device.
283 */
284static int
285is_dlpi_interface(const char *name _U_)
286{
287	return (1);
288}
289
290static int
291get_if_flags(const char *name _U_, bpf_u_int32 *flags _U_, char *errbuf _U_)
292{
293	/*
294	 * Nothing we can do other than mark loopback devices as "the
295	 * connected/disconnected status doesn't apply".
296	 *
297	 * XXX - on Solaris, can we do what the dladm command does,
298	 * i.e. get a connected/disconnected indication from a kstat?
299	 * (Note that you can also get the link speed, and possibly
300	 * other information, from a kstat as well.)
301	 */
302	if (*flags & PCAP_IF_LOOPBACK) {
303		/*
304		 * Loopback devices aren't wireless, and "connected"/
305		 * "disconnected" doesn't apply to them.
306		 */
307		*flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
308		return (0);
309	}
310	return (0);
311}
312
313/*
314 * In Solaris, the "standard" mechanism" i.e SIOCGLIFCONF will only find
315 * network links that are plumbed and are up. dlpi_walk(3DLPI) will find
316 * additional network links present in the system.
317 */
318int
319pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
320{
321	int retv = 0;
322
323	linknamelist_t	*entry, *next;
324	linkwalk_t	lw = {NULL, 0};
325	int 		save_errno;
326
327	/*
328	 * Get the list of regular interfaces first.
329	 */
330	if (pcap_findalldevs_interfaces(devlistp, errbuf,
331	    is_dlpi_interface, get_if_flags) == -1)
332		return (-1);	/* failure */
333
334	/* dlpi_walk() for loopback will be added here. */
335
336	/*
337	 * Find all DLPI devices in the current zone.
338	 *
339	 * XXX - will pcap_findalldevs_interfaces() find any devices
340	 * outside the current zone?  If not, the only reason to call
341	 * it would be to get the interface addresses.
342	 */
343	dlpi_walk(list_interfaces, &lw, 0);
344
345	if (lw.lw_err != 0) {
346		pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
347		    lw.lw_err, "dlpi_walk");
348		retv = -1;
349		goto done;
350	}
351
352	/* Add linkname if it does not exist on the list. */
353	for (entry = lw.lw_list; entry != NULL; entry = entry->lnl_next) {
354		/*
355		 * If it isn't already in the list of devices, try to
356		 * add it.
357		 */
358		if (find_or_add_dev(devlistp, entry->linkname, 0, get_if_flags,
359		    NULL, errbuf) == NULL)
360			retv = -1;
361	}
362done:
363	save_errno = errno;
364	for (entry = lw.lw_list; entry != NULL; entry = next) {
365		next = entry->lnl_next;
366		free(entry);
367	}
368	errno = save_errno;
369
370	return (retv);
371}
372
373/*
374 * Read data received on DLPI handle. Returns -2 if told to terminate, else
375 * returns the number of packets read.
376 */
377static int
378pcap_read_libdlpi(pcap_t *p, int count, pcap_handler callback, u_char *user)
379{
380	struct pcap_dlpi *pd = p->priv;
381	int len;
382	u_char *bufp;
383	size_t msglen;
384	int retv;
385
386	len = p->cc;
387	if (len != 0) {
388		bufp = p->bp;
389		goto process_pkts;
390	}
391	do {
392		/* Has "pcap_breakloop()" been called? */
393		if (p->break_loop) {
394			/*
395			 * Yes - clear the flag that indicates that it has,
396			 * and return -2 to indicate that we were told to
397			 * break out of the loop.
398			 */
399			p->break_loop = 0;
400			return (-2);
401		}
402
403		msglen = p->bufsize;
404		bufp = (u_char *)p->buffer + p->offset;
405
406		retv = dlpi_recv(pd->dlpi_hd, NULL, NULL, bufp,
407		    &msglen, -1, NULL);
408		if (retv != DLPI_SUCCESS) {
409			/*
410			 * This is most likely a call to terminate out of the
411			 * loop. So, do not return an error message, instead
412			 * check if "pcap_breakloop()" has been called above.
413			 */
414			if (retv == DL_SYSERR && errno == EINTR) {
415				len = 0;
416				continue;
417			}
418			pcap_libdlpi_err(dlpi_linkname(pd->dlpi_hd),
419			    "dlpi_recv", retv, p->errbuf);
420			return (-1);
421		}
422		len = msglen;
423	} while (len == 0);
424
425process_pkts:
426	return (pcap_process_pkts(p, callback, user, count, bufp, len));
427}
428
429static int
430pcap_inject_libdlpi(pcap_t *p, const void *buf, size_t size)
431{
432	struct pcap_dlpi *pd = p->priv;
433	int retv;
434
435	retv = dlpi_send(pd->dlpi_hd, NULL, 0, buf, size, NULL);
436	if (retv != DLPI_SUCCESS) {
437		pcap_libdlpi_err(dlpi_linkname(pd->dlpi_hd), "dlpi_send", retv,
438		    p->errbuf);
439		return (-1);
440	}
441	/*
442	 * dlpi_send(3DLPI) does not provide a way to return the number of
443	 * bytes sent on the wire. Based on the fact that DLPI_SUCCESS was
444	 * returned we are assuming 'size' bytes were sent.
445	 */
446	return (size);
447}
448
449/*
450 * Close dlpi handle.
451 */
452static void
453pcap_cleanup_libdlpi(pcap_t *p)
454{
455	struct pcap_dlpi *pd = p->priv;
456
457	if (pd->dlpi_hd != NULL) {
458		dlpi_close(pd->dlpi_hd);
459		pd->dlpi_hd = NULL;
460		p->fd = -1;
461	}
462	pcap_cleanup_live_common(p);
463}
464
465/*
466 * Write error message to buffer.
467 */
468static void
469pcap_libdlpi_err(const char *linkname, const char *func, int err, char *errbuf)
470{
471	pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "libpcap: %s failed on %s: %s",
472	    func, linkname, dlpi_strerror(err));
473}
474
475pcap_t *
476pcap_create_interface(const char *device _U_, char *ebuf)
477{
478	pcap_t *p;
479
480	p = pcap_create_common(ebuf, sizeof (struct pcap_dlpi));
481	if (p == NULL)
482		return (NULL);
483
484	p->activate_op = pcap_activate_libdlpi;
485	return (p);
486}
487
488/*
489 * Libpcap version string.
490 */
491const char *
492pcap_lib_version(void)
493{
494	return (PCAP_VERSION_STRING);
495}
496