11539Srgrimes/*
21539Srgrimes * Copyright (c) 1993, 1994, 1995, 1996, 1997
31539Srgrimes *	The Regents of the University of California.  All rights reserved.
41539Srgrimes *
51539Srgrimes * Redistribution and use in source and binary forms, with or without
61539Srgrimes * modification, are permitted provided that: (1) source code distributions
71539Srgrimes * retain the above copyright notice and this paragraph in its entirety, (2)
81539Srgrimes * distributions including binary code include the above copyright notice and
91539Srgrimes * this paragraph in its entirety in the documentation or other materials
101539Srgrimes * provided with the distribution, and (3) all advertising materials mentioning
111539Srgrimes * features or use of this software display the following acknowledgement:
121539Srgrimes * ``This product includes software developed by the University of California,
131539Srgrimes * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
141539Srgrimes * the University nor the names of its contributors may be used to endorse
151539Srgrimes * or promote products derived from this software without specific prior
161539Srgrimes * written permission.
171539Srgrimes * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
181539Srgrimes * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
191539Srgrimes * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
201539Srgrimes *
211539Srgrimes * This code contributed by Sagun Shakya (sagun.shakya@sun.com)
221539Srgrimes */
231539Srgrimes/*
241539Srgrimes * Packet capture routines for DLPI using libdlpi under SunOS 5.11.
251539Srgrimes */
261539Srgrimes
271539Srgrimes#ifdef HAVE_CONFIG_H
281539Srgrimes#include <config.h>
291539Srgrimes#endif
301539Srgrimes
311539Srgrimes#include <sys/types.h>
321539Srgrimes#include <sys/time.h>
331539Srgrimes#include <sys/bufmod.h>
341539Srgrimes#include <sys/stream.h>
351539Srgrimes#include <libdlpi.h>
361539Srgrimes#include <errno.h>
371539Srgrimes#include <memory.h>
381539Srgrimes#include <stropts.h>
391539Srgrimes#include <stdio.h>
401539Srgrimes#include <stdlib.h>
4175033Sphk#include <string.h>
4275033Sphk
4375033Sphk#include "pcap-int.h"
4475033Sphk#include "dlpisubs.h"
451539Srgrimes
461539Srgrimes/* Forwards. */
471539Srgrimesstatic int dlpromiscon(pcap_t *, bpf_u_int32);
4898269Swollmanstatic int pcap_read_libdlpi(pcap_t *, int, pcap_handler, u_char *);
49123257Smarcelstatic int pcap_inject_libdlpi(pcap_t *, const void *, int);
50102227Smikestatic void pcap_libdlpi_err(const char *, const char *, int, char *);
51102227Smikestatic void pcap_cleanup_libdlpi(pcap_t *);
5298312Swollman
536164Sbde/*
546164Sbde * list_interfaces() will list all the network links that are
5598269Swollman * available on a system.
566164Sbde */
57102874Smikestatic boolean_t list_interfaces(const char *, void *);
586164Sbde
596164Sbdetypedef struct linknamelist {
606164Sbde	char	linkname[DLPI_LINKNAME_MAX];
61102874Smike	struct linknamelist *lnl_next;
626164Sbde} linknamelist_t;
63102227Smike
64102227Smiketypedef struct linkwalk {
65102227Smike	linknamelist_t	*lw_list;
661539Srgrimes	int		lw_err;
671539Srgrimes} linkwalk_t;
68102227Smike
69102227Smike/*
70102227Smike * The caller of this function should free the memory allocated
711539Srgrimes * for each linknamelist_t "entry" allocated.
721539Srgrimes */
73102227Smikestatic boolean_t
74102227Smikelist_interfaces(const char *linkname, void *arg)
75102227Smike{
7625773Speter	linkwalk_t	*lwp = arg;
7725773Speter	linknamelist_t	*entry;
7898269Swollman
7925773Speter	if ((entry = calloc(1, sizeof(linknamelist_t))) == NULL) {
8025773Speter		lwp->lw_err = ENOMEM;
8125773Speter		return (B_TRUE);
82102227Smike	}
83102227Smike	(void) pcap_strlcpy(entry->linkname, linkname, DLPI_LINKNAME_MAX);
84102227Smike
8525769Sache	if (lwp->lw_list == NULL) {
8625769Sache		lwp->lw_list = entry;
87102227Smike	} else {
88102227Smike		entry->lnl_next = lwp->lw_list;
89102227Smike		lwp->lw_list = entry;
901539Srgrimes	}
911539Srgrimes
9298269Swollman	return (B_FALSE);
9398269Swollman}
9425773Speter
95144529Sdasstatic int
96144529Sdaspcap_activate_libdlpi(pcap_t *p)
97144529Sdas{
98144529Sdas	struct pcap_dlpi *pd = p->priv;
99144529Sdas	int status = 0;
100144529Sdas	int retv;
101144529Sdas	dlpi_handle_t dh;
102144529Sdas	dlpi_info_t dlinfo;
103157766Sjb
104157766Sjb	/*
105157766Sjb	 * Enable Solaris raw and passive DLPI extensions;
106157766Sjb	 * dlpi_open() will not fail if the underlying link does not support
107157766Sjb	 * passive mode. See dlpi(7P) for details.
108157766Sjb	 */
109157766Sjb	retv = dlpi_open(p->opt.device, &dh, DLPI_RAW|DLPI_PASSIVE);
110157766Sjb	if (retv != DLPI_SUCCESS) {
111144529Sdas		if (retv == DLPI_ELINKNAMEINVAL || retv == DLPI_ENOLINK) {
112144529Sdas			/*
113144529Sdas			 * There's nothing more to say, so clear the
114144529Sdas			 * error message.
115144529Sdas			 */
116144529Sdas			status = PCAP_ERROR_NO_SUCH_DEVICE;
117144529Sdas			p->errbuf[0] = '\0';
118144529Sdas		} else if (retv == DL_SYSERR &&
119144529Sdas		    (errno == EPERM || errno == EACCES)) {
1201539Srgrimes			status = PCAP_ERROR_PERM_DENIED;
1211539Srgrimes			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
1221539Srgrimes			    "Attempt to open DLPI device failed with %s - root privilege may be required",
1231539Srgrimes			    (errno == EPERM) ? "EPERM" : "EACCES");
1241539Srgrimes		} else {
1251539Srgrimes			status = PCAP_ERROR;
1261539Srgrimes			pcap_libdlpi_err(p->opt.device, "dlpi_open", retv,
1271539Srgrimes			    p->errbuf);
1281539Srgrimes		}
1291539Srgrimes		return (status);
13075033Sphk	}
1311539Srgrimes	pd->dlpi_hd = dh;
1321539Srgrimes
1331539Srgrimes	if (p->opt.rfmon) {
13498269Swollman		/*
13538464Sjkoshy		 * This device exists, but we don't support monitor mode
13638464Sjkoshy		 * any platforms that support DLPI.
13738464Sjkoshy		 */
1381539Srgrimes		status = PCAP_ERROR_RFMON_NOTSUP;
13993032Simp		goto bad;
14093032Simp	}
14193032Simp
14293032Simp	/* Bind with DLPI_ANY_SAP. */
143144529Sdas	if ((retv = dlpi_bind(pd->dlpi_hd, DLPI_ANY_SAP, 0)) != DLPI_SUCCESS) {
14493032Simp		status = PCAP_ERROR;
14593032Simp		pcap_libdlpi_err(p->opt.device, "dlpi_bind", retv, p->errbuf);
14693032Simp		goto bad;
147103012Stjr	}
148103012Stjr
14993032Simp	/*
150151870Sdavidxu	 * Turn a negative snapshot value (invalid), a snapshot value of
151151870Sdavidxu	 * 0 (unspecified), or a value bigger than the normal maximum
152151870Sdavidxu	 * value, into the maximum allowed value.
153151870Sdavidxu	 *
154151870Sdavidxu	 * If some application really *needs* a bigger snapshot
155151870Sdavidxu	 * length, we should just increase MAXIMUM_SNAPLEN.
156151870Sdavidxu	 */
157151870Sdavidxu	if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
158151870Sdavidxu		p->snapshot = MAXIMUM_SNAPLEN;
15998269Swollman
16093032Simp	/* Enable promiscuous mode. */
16198269Swollman	if (p->opt.promisc) {
16298269Swollman		retv = dlpromiscon(p, DL_PROMISC_PHYS);
16398923Swollman		if (retv < 0) {
16498923Swollman			/*
16598923Swollman			 * "You don't have permission to capture on
16698923Swollman			 * this device" and "you don't have permission
167144529Sdas			 * to capture in promiscuous mode on this
16898923Swollman			 * device" are different; let the user know,
16998923Swollman			 * so if they can't get permission to
17098923Swollman			 * capture in promiscuous mode, they can at
17198930Swollman			 * least try to capture in non-promiscuous
17298930Swollman			 * mode.
17398930Swollman			 *
17498930Swollman			 * XXX - you might have to capture in
17598930Swollman			 * promiscuous mode to see outgoing packets.
17698930Swollman			 */
17798930Swollman			if (retv == PCAP_ERROR_PERM_DENIED)
17898269Swollman				status = PCAP_ERROR_PROMISC_PERM_DENIED;
179103012Stjr			else
180103012Stjr				status = retv;
18198269Swollman			goto bad;
18298269Swollman		}
18398269Swollman	} else {
184144529Sdas		/* Try to enable multicast. */
18593032Simp		retv = dlpromiscon(p, DL_PROMISC_MULTI);
18693032Simp		if (retv < 0) {
18793032Simp			status = retv;
18898269Swollman			goto bad;
1891539Srgrimes		}
1901539Srgrimes	}
1911539Srgrimes
192	/* Try to enable SAP promiscuity. */
193	retv = dlpromiscon(p, DL_PROMISC_SAP);
194	if (retv < 0) {
195		/*
196		 * Not fatal, since the DL_PROMISC_PHYS mode worked.
197		 * Report it as a warning, however.
198		 */
199		if (p->opt.promisc)
200			status = PCAP_WARNING;
201		else {
202			status = retv;
203			goto bad;
204		}
205	}
206
207	/* Determine link type.  */
208	if ((retv = dlpi_info(pd->dlpi_hd, &dlinfo, 0)) != DLPI_SUCCESS) {
209		status = PCAP_ERROR;
210		pcap_libdlpi_err(p->opt.device, "dlpi_info", retv, p->errbuf);
211		goto bad;
212	}
213
214	if (pcap_process_mactype(p, dlinfo.di_mactype) != 0) {
215		status = PCAP_ERROR;
216		goto bad;
217	}
218
219	p->fd = dlpi_fd(pd->dlpi_hd);
220
221	/* Push and configure bufmod. */
222	if (pcap_conf_bufmod(p, p->snapshot) != 0) {
223		status = PCAP_ERROR;
224		goto bad;
225	}
226
227	/*
228	 * Flush the read side.
229	 */
230	if (ioctl(p->fd, I_FLUSH, FLUSHR) != 0) {
231		status = PCAP_ERROR;
232		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
233		    errno, "FLUSHR");
234		goto bad;
235	}
236
237	/* Allocate data buffer. */
238	if (pcap_alloc_databuf(p) != 0) {
239		status = PCAP_ERROR;
240		goto bad;
241	}
242
243	/*
244	 * "p->fd" is a FD for a STREAMS device, so "select()" and
245	 * "poll()" should work on it.
246	 */
247	p->selectable_fd = p->fd;
248
249	p->read_op = pcap_read_libdlpi;
250	p->inject_op = pcap_inject_libdlpi;
251	p->setfilter_op = install_bpf_program;	/* No kernel filtering */
252	p->setdirection_op = NULL;	/* Not implemented */
253	p->set_datalink_op = NULL;	/* Can't change data link type */
254	p->getnonblock_op = pcap_getnonblock_fd;
255	p->setnonblock_op = pcap_setnonblock_fd;
256	p->stats_op = pcap_stats_dlpi;
257	p->cleanup_op = pcap_cleanup_libdlpi;
258
259	return (status);
260bad:
261	pcap_cleanup_libdlpi(p);
262	return (status);
263}
264
265#define STRINGIFY(n)	#n
266
267static int
268dlpromiscon(pcap_t *p, bpf_u_int32 level)
269{
270	struct pcap_dlpi *pd = p->priv;
271	int retv;
272	int err;
273
274	retv = dlpi_promiscon(pd->dlpi_hd, level);
275	if (retv != DLPI_SUCCESS) {
276		if (retv == DL_SYSERR &&
277		    (errno == EPERM || errno == EACCES)) {
278			if (level == DL_PROMISC_PHYS) {
279				err = PCAP_ERROR_PROMISC_PERM_DENIED;
280				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
281				    "Attempt to set promiscuous mode failed with %s - root privilege may be required",
282				    (errno == EPERM) ? "EPERM" : "EACCES");
283			} else {
284				err = PCAP_ERROR_PERM_DENIED;
285				snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
286				    "Attempt to set %s mode failed with %s - root privilege may be required",
287				    (level == DL_PROMISC_MULTI) ? "multicast" : "SAP promiscuous",
288				    (errno == EPERM) ? "EPERM" : "EACCES");
289			}
290		} else {
291			err = PCAP_ERROR;
292			pcap_libdlpi_err(p->opt.device,
293			    "dlpi_promiscon" STRINGIFY(level),
294			    retv, p->errbuf);
295		}
296		return (err);
297	}
298	return (0);
299}
300
301/*
302 * Presumably everything returned by dlpi_walk() is a DLPI device,
303 * so there's no work to be done here to check whether name refers
304 * to a DLPI device.
305 */
306static int
307is_dlpi_interface(const char *name _U_)
308{
309	return (1);
310}
311
312static int
313get_if_flags(const char *name _U_, bpf_u_int32 *flags _U_, char *errbuf _U_)
314{
315	/*
316	 * Nothing we can do other than mark loopback devices as "the
317	 * connected/disconnected status doesn't apply".
318	 *
319	 * XXX - on Solaris, can we do what the dladm command does,
320	 * i.e. get a connected/disconnected indication from a kstat?
321	 * (Note that you can also get the link speed, and possibly
322	 * other information, from a kstat as well.)
323	 */
324	if (*flags & PCAP_IF_LOOPBACK) {
325		/*
326		 * Loopback devices aren't wireless, and "connected"/
327		 * "disconnected" doesn't apply to them.
328		 */
329		*flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
330		return (0);
331	}
332	return (0);
333}
334
335/*
336 * In Solaris, the "standard" mechanism" i.e SIOCGLIFCONF will only find
337 * network links that are plumbed and are up. dlpi_walk(3DLPI) will find
338 * additional network links present in the system.
339 */
340int
341pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
342{
343	int retv = 0;
344
345	linknamelist_t	*entry, *next;
346	linkwalk_t	lw = {NULL, 0};
347	int		save_errno;
348
349	/*
350	 * Get the list of regular interfaces first.
351	 */
352	if (pcap_findalldevs_interfaces(devlistp, errbuf,
353	    is_dlpi_interface, get_if_flags) == -1)
354		return (-1);	/* failure */
355
356	/* dlpi_walk() for loopback will be added here. */
357
358	/*
359	 * Find all DLPI devices in the current zone.
360	 *
361	 * XXX - will pcap_findalldevs_interfaces() find any devices
362	 * outside the current zone?  If not, the only reason to call
363	 * it would be to get the interface addresses.
364	 */
365	dlpi_walk(list_interfaces, &lw, 0);
366
367	if (lw.lw_err != 0) {
368		pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
369		    lw.lw_err, "dlpi_walk");
370		retv = -1;
371		goto done;
372	}
373
374	/* Add linkname if it does not exist on the list. */
375	for (entry = lw.lw_list; entry != NULL; entry = entry->lnl_next) {
376		/*
377		 * If it isn't already in the list of devices, try to
378		 * add it.
379		 */
380		if (find_or_add_dev(devlistp, entry->linkname, 0, get_if_flags,
381		    NULL, errbuf) == NULL)
382			retv = -1;
383	}
384done:
385	save_errno = errno;
386	for (entry = lw.lw_list; entry != NULL; entry = next) {
387		next = entry->lnl_next;
388		free(entry);
389	}
390	errno = save_errno;
391
392	return (retv);
393}
394
395/*
396 * Read data received on DLPI handle. Returns -2 if told to terminate, else
397 * returns the number of packets read.
398 */
399static int
400pcap_read_libdlpi(pcap_t *p, int count, pcap_handler callback, u_char *user)
401{
402	struct pcap_dlpi *pd = p->priv;
403	int len;
404	u_char *bufp;
405	size_t msglen;
406	int retv;
407
408	len = p->cc;
409	if (len != 0) {
410		bufp = p->bp;
411		goto process_pkts;
412	}
413	do {
414		/* Has "pcap_breakloop()" been called? */
415		if (p->break_loop) {
416			/*
417			 * Yes - clear the flag that indicates that it has,
418			 * and return -2 to indicate that we were told to
419			 * break out of the loop.
420			 */
421			p->break_loop = 0;
422			return (-2);
423		}
424
425		msglen = p->bufsize;
426		bufp = (u_char *)p->buffer + p->offset;
427
428		retv = dlpi_recv(pd->dlpi_hd, NULL, NULL, bufp,
429		    &msglen, -1, NULL);
430		if (retv != DLPI_SUCCESS) {
431			/*
432			 * This is most likely a call to terminate out of the
433			 * loop. So, do not return an error message, instead
434			 * check if "pcap_breakloop()" has been called above.
435			 */
436			if (retv == DL_SYSERR && errno == EINTR) {
437				len = 0;
438				continue;
439			}
440			pcap_libdlpi_err(dlpi_linkname(pd->dlpi_hd),
441			    "dlpi_recv", retv, p->errbuf);
442			return (-1);
443		}
444		len = msglen;
445	} while (len == 0);
446
447process_pkts:
448	return (pcap_process_pkts(p, callback, user, count, bufp, len));
449}
450
451static int
452pcap_inject_libdlpi(pcap_t *p, const void *buf, int size)
453{
454	struct pcap_dlpi *pd = p->priv;
455	int retv;
456
457	retv = dlpi_send(pd->dlpi_hd, NULL, 0, buf, size, NULL);
458	if (retv != DLPI_SUCCESS) {
459		pcap_libdlpi_err(dlpi_linkname(pd->dlpi_hd), "dlpi_send", retv,
460		    p->errbuf);
461		return (-1);
462	}
463	/*
464	 * dlpi_send(3DLPI) does not provide a way to return the number of
465	 * bytes sent on the wire. Based on the fact that DLPI_SUCCESS was
466	 * returned we are assuming 'size' bytes were sent.
467	 */
468	return (size);
469}
470
471/*
472 * Close dlpi handle.
473 */
474static void
475pcap_cleanup_libdlpi(pcap_t *p)
476{
477	struct pcap_dlpi *pd = p->priv;
478
479	if (pd->dlpi_hd != NULL) {
480		dlpi_close(pd->dlpi_hd);
481		pd->dlpi_hd = NULL;
482		p->fd = -1;
483	}
484	pcap_cleanup_live_common(p);
485}
486
487/*
488 * Write error message to buffer.
489 */
490static void
491pcap_libdlpi_err(const char *linkname, const char *func, int err, char *errbuf)
492{
493	snprintf(errbuf, PCAP_ERRBUF_SIZE, "libpcap: %s failed on %s: %s",
494	    func, linkname, dlpi_strerror(err));
495}
496
497pcap_t *
498pcap_create_interface(const char *device _U_, char *ebuf)
499{
500	pcap_t *p;
501
502	p = PCAP_CREATE_COMMON(ebuf, struct pcap_dlpi);
503	if (p == NULL)
504		return (NULL);
505
506	p->activate_op = pcap_activate_libdlpi;
507	return (p);
508}
509
510/*
511 * Libpcap version string.
512 */
513const char *
514pcap_lib_version(void)
515{
516	return (PCAP_VERSION_STRING);
517}
518