xref: /titanic_52/usr/src/uts/common/inet/sockmods/sockmod_pfp.c (revision 3b4315d3f6ce29d16d3f8e2c62b2f9c24192c3a8)
10a0e9771SDarren Reed /*
20a0e9771SDarren Reed  * CDDL HEADER START
30a0e9771SDarren Reed  *
40a0e9771SDarren Reed  * The contents of this file are subject to the terms of the
50a0e9771SDarren Reed  * Common Development and Distribution License (the "License").
60a0e9771SDarren Reed  * You may not use this file except in compliance with the License.
70a0e9771SDarren Reed  *
80a0e9771SDarren Reed  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90a0e9771SDarren Reed  * or http://www.opensolaris.org/os/licensing.
100a0e9771SDarren Reed  * See the License for the specific language governing permissions
110a0e9771SDarren Reed  * and limitations under the License.
120a0e9771SDarren Reed  *
130a0e9771SDarren Reed  * When distributing Covered Code, include this CDDL HEADER in each
140a0e9771SDarren Reed  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150a0e9771SDarren Reed  * If applicable, add the following below this CDDL HEADER, with the
160a0e9771SDarren Reed  * fields enclosed by brackets "[]" replaced with your own identifying
170a0e9771SDarren Reed  * information: Portions Copyright [yyyy] [name of copyright owner]
180a0e9771SDarren Reed  *
190a0e9771SDarren Reed  * CDDL HEADER END
200a0e9771SDarren Reed  */
210a0e9771SDarren Reed 
220a0e9771SDarren Reed /*
23d2b5b2d3SAnders Persson  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24336069c2SPatrick Mooney  * Copyright 2015 Joyent, Inc. All rights reserved.
250a0e9771SDarren Reed  */
260a0e9771SDarren Reed 
270a0e9771SDarren Reed #include <sys/types.h>
280a0e9771SDarren Reed #include <sys/param.h>
290a0e9771SDarren Reed #include <sys/systm.h>
300a0e9771SDarren Reed #include <sys/stropts.h>
310a0e9771SDarren Reed #include <sys/socket.h>
320a0e9771SDarren Reed #include <sys/socketvar.h>
330a0e9771SDarren Reed #include <sys/socket_proto.h>
340a0e9771SDarren Reed #include <sys/sockio.h>
350a0e9771SDarren Reed #include <sys/strsun.h>
360a0e9771SDarren Reed #include <sys/kstat.h>
370a0e9771SDarren Reed #include <sys/modctl.h>
380a0e9771SDarren Reed #include <sys/policy.h>
390a0e9771SDarren Reed #include <sys/priv_const.h>
400a0e9771SDarren Reed #include <sys/tihdr.h>
410a0e9771SDarren Reed #include <sys/zone.h>
420a0e9771SDarren Reed #include <sys/time.h>
43a6911619SDarren Reed #include <sys/ethernet.h>
44a6911619SDarren Reed #include <sys/llc1.h>
450a0e9771SDarren Reed #include <fs/sockfs/sockcommon.h>
460a0e9771SDarren Reed #include <net/if.h>
47a6911619SDarren Reed #include <inet/ip_arp.h>
480a0e9771SDarren Reed 
490a0e9771SDarren Reed #include <sys/dls.h>
500a0e9771SDarren Reed #include <sys/mac.h>
510a0e9771SDarren Reed #include <sys/mac_client.h>
520a0e9771SDarren Reed #include <sys/mac_provider.h>
530a0e9771SDarren Reed #include <sys/mac_client_priv.h>
540a0e9771SDarren Reed 
550a0e9771SDarren Reed #include <netpacket/packet.h>
560a0e9771SDarren Reed 
570a0e9771SDarren Reed static void pfp_close(mac_handle_t, mac_client_handle_t);
580a0e9771SDarren Reed static int pfp_dl_to_arphrd(int);
590a0e9771SDarren Reed static int pfp_getpacket_sockopt(sock_lower_handle_t, int, void *,
600a0e9771SDarren Reed     socklen_t *);
61336069c2SPatrick Mooney static int pfp_ifreq_getlinkid(intptr_t, struct ifreq *, datalink_id_t *, int);
62336069c2SPatrick Mooney static int pfp_lifreq_getlinkid(intptr_t, struct lifreq *, datalink_id_t *,
63336069c2SPatrick Mooney     int);
640a0e9771SDarren Reed static int pfp_open_index(int, mac_handle_t *, mac_client_handle_t *,
650a0e9771SDarren Reed     cred_t *);
660a0e9771SDarren Reed static void pfp_packet(void *, mac_resource_handle_t, mblk_t *, boolean_t);
670a0e9771SDarren Reed static void pfp_release_bpf(struct pfpsock *);
680a0e9771SDarren Reed static int pfp_set_promisc(struct pfpsock *, mac_client_promisc_type_t);
690a0e9771SDarren Reed static int pfp_setsocket_sockopt(sock_lower_handle_t, int, const void *,
700a0e9771SDarren Reed     socklen_t);
710a0e9771SDarren Reed static int pfp_setpacket_sockopt(sock_lower_handle_t, int, const void *,
720a0e9771SDarren Reed     socklen_t);
730a0e9771SDarren Reed 
740a0e9771SDarren Reed /*
750a0e9771SDarren Reed  * PFP sockfs operations
760a0e9771SDarren Reed  * Most are currently no-ops because they have no meaning for a connectionless
770a0e9771SDarren Reed  * socket.
780a0e9771SDarren Reed  */
790a0e9771SDarren Reed static void sdpfp_activate(sock_lower_handle_t, sock_upper_handle_t,
800a0e9771SDarren Reed     sock_upcalls_t *, int, struct cred *);
810a0e9771SDarren Reed static int sdpfp_bind(sock_lower_handle_t, struct sockaddr *, socklen_t,
820a0e9771SDarren Reed     struct cred *);
830a0e9771SDarren Reed static int sdpfp_close(sock_lower_handle_t, int, struct cred *);
840a0e9771SDarren Reed static void sdpfp_clr_flowctrl(sock_lower_handle_t);
850a0e9771SDarren Reed static int sdpfp_getsockopt(sock_lower_handle_t, int, int, void *,
860a0e9771SDarren Reed     socklen_t *, struct cred *);
870a0e9771SDarren Reed static int sdpfp_ioctl(sock_lower_handle_t, int, intptr_t, int, int32_t *,
880a0e9771SDarren Reed     struct cred *);
890a0e9771SDarren Reed static int sdpfp_senduio(sock_lower_handle_t, struct uio *, struct nmsghdr *,
900a0e9771SDarren Reed     struct cred *);
910a0e9771SDarren Reed static int sdpfp_setsockopt(sock_lower_handle_t, int, int, const void *,
920a0e9771SDarren Reed     socklen_t, struct cred *);
930a0e9771SDarren Reed 
940a0e9771SDarren Reed static sock_lower_handle_t sockpfp_create(int, int, int, sock_downcalls_t **,
950a0e9771SDarren Reed     uint_t *, int *, int, cred_t *);
960a0e9771SDarren Reed 
970a0e9771SDarren Reed static int sockpfp_init(void);
980a0e9771SDarren Reed static void sockpfp_fini(void);
990a0e9771SDarren Reed 
1000a0e9771SDarren Reed static kstat_t *pfp_ksp;
1010a0e9771SDarren Reed static pfp_kstats_t ks_stats;
1020a0e9771SDarren Reed static pfp_kstats_t pfp_kstats = {
1030a0e9771SDarren Reed 	/*
1040a0e9771SDarren Reed 	 * Each one of these kstats is a different return path in handling
1050a0e9771SDarren Reed 	 * a packet received from the mac layer.
1060a0e9771SDarren Reed 	 */
1070a0e9771SDarren Reed 	{ "recvMacHeaderFail",	KSTAT_DATA_UINT64 },
1080a0e9771SDarren Reed 	{ "recvBadProtocol",	KSTAT_DATA_UINT64 },
1090a0e9771SDarren Reed 	{ "recvAllocbFail",	KSTAT_DATA_UINT64 },
1100a0e9771SDarren Reed 	{ "recvOk",		KSTAT_DATA_UINT64 },
1110a0e9771SDarren Reed 	{ "recvFail",		KSTAT_DATA_UINT64 },
1120a0e9771SDarren Reed 	{ "recvFiltered",	KSTAT_DATA_UINT64 },
1130a0e9771SDarren Reed 	{ "recvFlowControl",	KSTAT_DATA_UINT64 },
1140a0e9771SDarren Reed 	/*
1150a0e9771SDarren Reed 	 * A global set of counters is maintained to track the behaviour
1160a0e9771SDarren Reed 	 * of the system (kernel & applications) in sending packets.
1170a0e9771SDarren Reed 	 */
1180a0e9771SDarren Reed 	{ "sendUnbound",	KSTAT_DATA_UINT64 },
1190a0e9771SDarren Reed 	{ "sendFailed",		KSTAT_DATA_UINT64 },
1200a0e9771SDarren Reed 	{ "sendTooBig",		KSTAT_DATA_UINT64 },
1210a0e9771SDarren Reed 	{ "sendAllocFail",	KSTAT_DATA_UINT64 },
1220a0e9771SDarren Reed 	{ "sendUiomoveFail",	KSTAT_DATA_UINT64 },
1230a0e9771SDarren Reed 	{ "sendNoMemory",	KSTAT_DATA_UINT64 },
1240a0e9771SDarren Reed 	{ "sendOpenFail",	KSTAT_DATA_UINT64 },
1250a0e9771SDarren Reed 	{ "sendWrongFamily",	KSTAT_DATA_UINT64 },
1260a0e9771SDarren Reed 	{ "sendShortMsg",	KSTAT_DATA_UINT64 },
1270a0e9771SDarren Reed 	{ "sendOk",		KSTAT_DATA_UINT64 }
1280a0e9771SDarren Reed };
1290a0e9771SDarren Reed 
1300a0e9771SDarren Reed sock_downcalls_t pfp_downcalls = {
1310a0e9771SDarren Reed 	sdpfp_activate,
1320a0e9771SDarren Reed 	sock_accept_notsupp,
1330a0e9771SDarren Reed 	sdpfp_bind,
1340a0e9771SDarren Reed 	sock_listen_notsupp,
1350a0e9771SDarren Reed 	sock_connect_notsupp,
1360a0e9771SDarren Reed 	sock_getpeername_notsupp,
1370a0e9771SDarren Reed 	sock_getsockname_notsupp,
1380a0e9771SDarren Reed 	sdpfp_getsockopt,
1390a0e9771SDarren Reed 	sdpfp_setsockopt,
1400a0e9771SDarren Reed 	sock_send_notsupp,
1410a0e9771SDarren Reed 	sdpfp_senduio,
1420a0e9771SDarren Reed 	NULL,
1430a0e9771SDarren Reed 	sock_poll_notsupp,
1440a0e9771SDarren Reed 	sock_shutdown_notsupp,
1450a0e9771SDarren Reed 	sdpfp_clr_flowctrl,
1460a0e9771SDarren Reed 	sdpfp_ioctl,
1470a0e9771SDarren Reed 	sdpfp_close,
1480a0e9771SDarren Reed };
1490a0e9771SDarren Reed 
1500a0e9771SDarren Reed static smod_reg_t sinfo = {
1510a0e9771SDarren Reed 	SOCKMOD_VERSION,
1520a0e9771SDarren Reed 	"sockpfp",
1530a0e9771SDarren Reed 	SOCK_UC_VERSION,
1540a0e9771SDarren Reed 	SOCK_DC_VERSION,
1550a0e9771SDarren Reed 	sockpfp_create,
1560a0e9771SDarren Reed 	NULL
1570a0e9771SDarren Reed };
1580a0e9771SDarren Reed 
159a6911619SDarren Reed static int accepted_protos[3][2] = {
160a6911619SDarren Reed 	{ ETH_P_ALL,	0 },
161a6911619SDarren Reed 	{ ETH_P_802_2,	LLC_SNAP_SAP },
162a6911619SDarren Reed 	{ ETH_P_803_3,	0 },
163a6911619SDarren Reed };
164a6911619SDarren Reed 
1650a0e9771SDarren Reed /*
166336069c2SPatrick Mooney  * This sets an upper bound on the size of the receive buffer for a PF_PACKET
167336069c2SPatrick Mooney  * socket. More properly, this should be controlled through ipadm, ala TCP, UDP,
168336069c2SPatrick Mooney  * SCTP, etc. Until that's done, this provides a hard cap of 4 MB and allows an
169336069c2SPatrick Mooney  * opportunity for it to be changed, should it be needed.
170336069c2SPatrick Mooney  */
171336069c2SPatrick Mooney int sockmod_pfp_rcvbuf_max = 1024 * 1024 * 4;
172336069c2SPatrick Mooney 
173336069c2SPatrick Mooney /*
1740a0e9771SDarren Reed  * Module linkage information for the kernel.
1750a0e9771SDarren Reed  */
1760a0e9771SDarren Reed static struct modlsockmod modlsockmod = {
1770a0e9771SDarren Reed 	&mod_sockmodops, "PF Packet socket module", &sinfo
1780a0e9771SDarren Reed };
1790a0e9771SDarren Reed 
1800a0e9771SDarren Reed static struct modlinkage modlinkage = {
1810a0e9771SDarren Reed 	MODREV_1,
1820a0e9771SDarren Reed 	&modlsockmod,
1830a0e9771SDarren Reed 	NULL
1840a0e9771SDarren Reed };
1850a0e9771SDarren Reed 
1860a0e9771SDarren Reed int
1870a0e9771SDarren Reed _init(void)
1880a0e9771SDarren Reed {
1890a0e9771SDarren Reed 	int error;
1900a0e9771SDarren Reed 
1910a0e9771SDarren Reed 	error = sockpfp_init();
1920a0e9771SDarren Reed 	if (error != 0)
1930a0e9771SDarren Reed 		return (error);
1940a0e9771SDarren Reed 
1950a0e9771SDarren Reed 	error = mod_install(&modlinkage);
1960a0e9771SDarren Reed 	if (error != 0)
1970a0e9771SDarren Reed 		sockpfp_fini();
1980a0e9771SDarren Reed 
1990a0e9771SDarren Reed 	return (error);
2000a0e9771SDarren Reed }
2010a0e9771SDarren Reed 
2020a0e9771SDarren Reed int
2030a0e9771SDarren Reed _fini(void)
2040a0e9771SDarren Reed {
2050a0e9771SDarren Reed 	int error;
2060a0e9771SDarren Reed 
2070a0e9771SDarren Reed 	error = mod_remove(&modlinkage);
2080a0e9771SDarren Reed 	if (error == 0)
2090a0e9771SDarren Reed 		sockpfp_fini();
2100a0e9771SDarren Reed 
2110a0e9771SDarren Reed 	return (error);
2120a0e9771SDarren Reed }
2130a0e9771SDarren Reed 
2140a0e9771SDarren Reed int
2150a0e9771SDarren Reed _info(struct modinfo *modinfop)
2160a0e9771SDarren Reed {
2170a0e9771SDarren Reed 	return (mod_info(&modlinkage, modinfop));
2180a0e9771SDarren Reed }
2190a0e9771SDarren Reed 
2200a0e9771SDarren Reed /*
2210a0e9771SDarren Reed  * sockpfp_init: called as part of the initialisation of the module when
2220a0e9771SDarren Reed  * loaded into the kernel.
2230a0e9771SDarren Reed  *
2240a0e9771SDarren Reed  * Being able to create and record the kstats data in the kernel is not
2250a0e9771SDarren Reed  * considered to be vital to the operation of this kernel module, thus
2260a0e9771SDarren Reed  * its failure is tolerated.
2270a0e9771SDarren Reed  */
2280a0e9771SDarren Reed static int
2290a0e9771SDarren Reed sockpfp_init(void)
2300a0e9771SDarren Reed {
2310a0e9771SDarren Reed 	(void) memset(&ks_stats, 0, sizeof (ks_stats));
2320a0e9771SDarren Reed 
2330a0e9771SDarren Reed 	(void) memcpy(&ks_stats, &pfp_kstats, sizeof (pfp_kstats));
2340a0e9771SDarren Reed 
2350a0e9771SDarren Reed 	pfp_ksp = kstat_create("pfpacket", 0, "global", "misc",
2360a0e9771SDarren Reed 	    KSTAT_TYPE_NAMED, sizeof (pfp_kstats) / sizeof (kstat_named_t),
2370a0e9771SDarren Reed 	    KSTAT_FLAG_VIRTUAL);
2380a0e9771SDarren Reed 	if (pfp_ksp != NULL) {
2390a0e9771SDarren Reed 		pfp_ksp->ks_data = &ks_stats;
2400a0e9771SDarren Reed 		kstat_install(pfp_ksp);
2410a0e9771SDarren Reed 	}
2420a0e9771SDarren Reed 
2430a0e9771SDarren Reed 	return (0);
2440a0e9771SDarren Reed }
2450a0e9771SDarren Reed 
2460a0e9771SDarren Reed /*
2470a0e9771SDarren Reed  * sockpfp_fini: called when the operating system wants to unload the
2480a0e9771SDarren Reed  * socket module from the kernel.
2490a0e9771SDarren Reed  */
2500a0e9771SDarren Reed static void
2510a0e9771SDarren Reed sockpfp_fini(void)
2520a0e9771SDarren Reed {
2530a0e9771SDarren Reed 	if (pfp_ksp != NULL)
2540a0e9771SDarren Reed 		kstat_delete(pfp_ksp);
2550a0e9771SDarren Reed }
2560a0e9771SDarren Reed 
2570a0e9771SDarren Reed /*
2580a0e9771SDarren Reed  * Due to sockets being created read-write by default, all PF_PACKET sockets
2590a0e9771SDarren Reed  * therefore require the NET_RAWACCESS priviliege, even if the socket is only
2600a0e9771SDarren Reed  * being used for reading packets from.
2610a0e9771SDarren Reed  *
2620a0e9771SDarren Reed  * This create function enforces this module only being used with PF_PACKET
263d2b5b2d3SAnders Persson  * sockets and the policy that we support via the config file in sock2path.d:
2640a0e9771SDarren Reed  * PF_PACKET sockets must be either SOCK_DGRAM or SOCK_RAW.
2650a0e9771SDarren Reed  */
2660a0e9771SDarren Reed /* ARGSUSED */
2670a0e9771SDarren Reed static sock_lower_handle_t
2680a0e9771SDarren Reed sockpfp_create(int family, int type, int proto,
2690a0e9771SDarren Reed     sock_downcalls_t **sock_downcalls, uint_t *smodep, int *errorp,
2700a0e9771SDarren Reed     int sflags, cred_t *cred)
2710a0e9771SDarren Reed {
2720a0e9771SDarren Reed 	struct pfpsock *ps;
2730a0e9771SDarren Reed 	int kmflags;
274a6911619SDarren Reed 	int newproto;
275a6911619SDarren Reed 	int i;
2760a0e9771SDarren Reed 
2770a0e9771SDarren Reed 	if (secpolicy_net_rawaccess(cred) != 0) {
2780a0e9771SDarren Reed 		*errorp = EACCES;
2790a0e9771SDarren Reed 		return (NULL);
2800a0e9771SDarren Reed 	}
2810a0e9771SDarren Reed 
2820a0e9771SDarren Reed 	if (family != AF_PACKET) {
2830a0e9771SDarren Reed 		*errorp = EAFNOSUPPORT;
2840a0e9771SDarren Reed 		return (NULL);
2850a0e9771SDarren Reed 	}
2860a0e9771SDarren Reed 
2870a0e9771SDarren Reed 	if ((type != SOCK_RAW) && (type != SOCK_DGRAM)) {
2880a0e9771SDarren Reed 		*errorp = ESOCKTNOSUPPORT;
2890a0e9771SDarren Reed 		return (NULL);
2900a0e9771SDarren Reed 	}
2910a0e9771SDarren Reed 
292a6911619SDarren Reed 	/*
293a6911619SDarren Reed 	 * First check to see if the protocol number passed in via the socket
294a6911619SDarren Reed 	 * creation should be mapped to a different number for internal use.
295a6911619SDarren Reed 	 */
296a6911619SDarren Reed 	for (i = 0, newproto = -1;
297a6911619SDarren Reed 	    i < sizeof (accepted_protos)/ sizeof (accepted_protos[0]); i++) {
298a6911619SDarren Reed 		if (accepted_protos[i][0] == proto) {
299a6911619SDarren Reed 			newproto = accepted_protos[i][1];
300a6911619SDarren Reed 			break;
301a6911619SDarren Reed 		}
302a6911619SDarren Reed 	}
303a6911619SDarren Reed 
304a6911619SDarren Reed 	/*
305a6911619SDarren Reed 	 * If the mapping of the protocol that was under 0x800 failed to find
306a6911619SDarren Reed 	 * a local equivalent then fail the socket creation. If the protocol
307a6911619SDarren Reed 	 * for the socket is over 0x800 and it was not found in the mapping
308a6911619SDarren Reed 	 * table above, then use the value as is.
309a6911619SDarren Reed 	 */
310a6911619SDarren Reed 	if (newproto == -1) {
311a6911619SDarren Reed 		if (proto < 0x800) {
312a6911619SDarren Reed 			*errorp = ENOPROTOOPT;
313a6911619SDarren Reed 			return (NULL);
314a6911619SDarren Reed 		}
315a6911619SDarren Reed 		newproto = proto;
316a6911619SDarren Reed 	}
317a6911619SDarren Reed 	proto = newproto;
318a6911619SDarren Reed 
3190a0e9771SDarren Reed 	kmflags = (sflags & SOCKET_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP;
3200a0e9771SDarren Reed 	ps = kmem_zalloc(sizeof (*ps), kmflags);
3210a0e9771SDarren Reed 	if (ps == NULL) {
3220a0e9771SDarren Reed 		*errorp = ENOMEM;
3230a0e9771SDarren Reed 		return (NULL);
3240a0e9771SDarren Reed 	}
3250a0e9771SDarren Reed 
3260a0e9771SDarren Reed 	ps->ps_type = type;
3270a0e9771SDarren Reed 	ps->ps_proto = proto;
3280a0e9771SDarren Reed 	rw_init(&ps->ps_bpflock, NULL, RW_DRIVER, NULL);
3290a0e9771SDarren Reed 	mutex_init(&ps->ps_lock, NULL, MUTEX_DRIVER, NULL);
3300a0e9771SDarren Reed 
3310a0e9771SDarren Reed 	*sock_downcalls = &pfp_downcalls;
3320a0e9771SDarren Reed 	/*
3330a0e9771SDarren Reed 	 * Setting this causes bytes from a packet that do not fit into the
3340a0e9771SDarren Reed 	 * destination user buffer to be discarded. Thus the API is one
3350a0e9771SDarren Reed 	 * packet per receive and callers are required to use a buffer large
3360a0e9771SDarren Reed 	 * enough for the biggest packet that the interface can provide.
3370a0e9771SDarren Reed 	 */
3380a0e9771SDarren Reed 	*smodep = SM_ATOMIC;
3390a0e9771SDarren Reed 
3400a0e9771SDarren Reed 	return ((sock_lower_handle_t)ps);
3410a0e9771SDarren Reed }
3420a0e9771SDarren Reed 
3430a0e9771SDarren Reed /* ************************************************************************* */
3440a0e9771SDarren Reed 
3450a0e9771SDarren Reed /*
3460a0e9771SDarren Reed  * pfp_packet is the callback function that is given to the mac layer for
3470a0e9771SDarren Reed  * PF_PACKET to receive packets with. One packet at a time is passed into
3480a0e9771SDarren Reed  * this function from the mac layer. Each packet is a private copy given
3490a0e9771SDarren Reed  * to PF_PACKET to modify or free as it wishes and does not harm the original
3500a0e9771SDarren Reed  * packet from which it was cloned.
3510a0e9771SDarren Reed  */
3520a0e9771SDarren Reed /* ARGSUSED */
3530a0e9771SDarren Reed static void
3540a0e9771SDarren Reed pfp_packet(void *arg, mac_resource_handle_t mrh, mblk_t *mp, boolean_t flag)
3550a0e9771SDarren Reed {
3560a0e9771SDarren Reed 	struct T_unitdata_ind *tunit;
3570a0e9771SDarren Reed 	struct sockaddr_ll *sll;
3580a0e9771SDarren Reed 	struct sockaddr_ll *sol;
3590a0e9771SDarren Reed 	mac_header_info_t hdr;
3600a0e9771SDarren Reed 	struct pfpsock *ps;
3610a0e9771SDarren Reed 	size_t tusz;
3620a0e9771SDarren Reed 	mblk_t *mp0;
3630a0e9771SDarren Reed 	int error;
3640a0e9771SDarren Reed 
3650a0e9771SDarren Reed 	if (mp == NULL)
3660a0e9771SDarren Reed 		return;
3670a0e9771SDarren Reed 
3680a0e9771SDarren Reed 	ps = arg;
3690a0e9771SDarren Reed 	if (ps->ps_flow_ctrld) {
3700a0e9771SDarren Reed 		ps->ps_flow_ctrl_drops++;
3710a0e9771SDarren Reed 		ps->ps_stats.tp_drops++;
3720a0e9771SDarren Reed 		ks_stats.kp_recv_flow_cntrld.value.ui64++;
3730a0e9771SDarren Reed 		freemsg(mp);
3740a0e9771SDarren Reed 		return;
3750a0e9771SDarren Reed 	}
3760a0e9771SDarren Reed 
3770a0e9771SDarren Reed 	if (mac_header_info(ps->ps_mh, mp, &hdr) != 0) {
3780a0e9771SDarren Reed 		/*
3790a0e9771SDarren Reed 		 * Can't decode the packet header information so drop it.
3800a0e9771SDarren Reed 		 */
3810a0e9771SDarren Reed 		ps->ps_stats.tp_drops++;
3820a0e9771SDarren Reed 		ks_stats.kp_recv_mac_hdr_fail.value.ui64++;
3830a0e9771SDarren Reed 		freemsg(mp);
3840a0e9771SDarren Reed 		return;
3850a0e9771SDarren Reed 	}
3860a0e9771SDarren Reed 
3870a0e9771SDarren Reed 	if (mac_type(ps->ps_mh) == DL_ETHER &&
3880a0e9771SDarren Reed 	    hdr.mhi_bindsap == ETHERTYPE_VLAN) {
3890a0e9771SDarren Reed 		struct ether_vlan_header *evhp;
3900a0e9771SDarren Reed 		struct ether_vlan_header evh;
3910a0e9771SDarren Reed 
3920a0e9771SDarren Reed 		hdr.mhi_hdrsize = sizeof (struct ether_vlan_header);
3930a0e9771SDarren Reed 		hdr.mhi_istagged = B_TRUE;
3940a0e9771SDarren Reed 
3950a0e9771SDarren Reed 		if (MBLKL(mp) >= sizeof (*evhp)) {
3960a0e9771SDarren Reed 			evhp = (struct ether_vlan_header *)mp->b_rptr;
3970a0e9771SDarren Reed 		} else {
3980a0e9771SDarren Reed 			int sz = sizeof (*evhp);
3990a0e9771SDarren Reed 			char *s = (char *)&evh;
4000a0e9771SDarren Reed 			mblk_t *tmp;
4010a0e9771SDarren Reed 			int len;
4020a0e9771SDarren Reed 
4030a0e9771SDarren Reed 			for (tmp = mp; sz > 0 && tmp != NULL;
4040a0e9771SDarren Reed 			    tmp = tmp->b_cont) {
4050a0e9771SDarren Reed 				len = min(sz, MBLKL(tmp));
4060a0e9771SDarren Reed 				bcopy(tmp->b_rptr, s, len);
4070a0e9771SDarren Reed 				sz -= len;
4080a0e9771SDarren Reed 			}
4090a0e9771SDarren Reed 			evhp = &evh;
4100a0e9771SDarren Reed 		}
4110a0e9771SDarren Reed 		hdr.mhi_tci = ntohs(evhp->ether_tci);
4120a0e9771SDarren Reed 		hdr.mhi_bindsap = ntohs(evhp->ether_type);
4130a0e9771SDarren Reed 	}
4140a0e9771SDarren Reed 
4150a0e9771SDarren Reed 	if ((ps->ps_proto != 0) && (ps->ps_proto != hdr.mhi_bindsap)) {
4160a0e9771SDarren Reed 		/*
4170a0e9771SDarren Reed 		 * The packet is not of interest to this socket so
4180a0e9771SDarren Reed 		 * drop it on the floor. Here the SAP is being used
4190a0e9771SDarren Reed 		 * as a very course filter.
4200a0e9771SDarren Reed 		 */
4210a0e9771SDarren Reed 		ps->ps_stats.tp_drops++;
4220a0e9771SDarren Reed 		ks_stats.kp_recv_bad_proto.value.ui64++;
4230a0e9771SDarren Reed 		freemsg(mp);
4240a0e9771SDarren Reed 		return;
4250a0e9771SDarren Reed 	}
4260a0e9771SDarren Reed 
4270a0e9771SDarren Reed 	/*
4280a0e9771SDarren Reed 	 * This field is not often set, even for ethernet,
4290a0e9771SDarren Reed 	 * by mac_header_info, so compute it if it is 0.
4300a0e9771SDarren Reed 	 */
4310a0e9771SDarren Reed 	if (hdr.mhi_pktsize == 0)
4320a0e9771SDarren Reed 		hdr.mhi_pktsize = msgdsize(mp);
4330a0e9771SDarren Reed 
4340a0e9771SDarren Reed 	/*
4350a0e9771SDarren Reed 	 * If a BPF filter is present, pass the raw packet into that.
4360a0e9771SDarren Reed 	 * A failed match will result in zero being returned, indicating
4370a0e9771SDarren Reed 	 * that this socket is not interested in the packet.
4380a0e9771SDarren Reed 	 */
4390a0e9771SDarren Reed 	if (ps->ps_bpf.bf_len != 0) {
4400a0e9771SDarren Reed 		uchar_t *buffer;
4410a0e9771SDarren Reed 		int buflen;
4420a0e9771SDarren Reed 
4430a0e9771SDarren Reed 		buflen = MBLKL(mp);
4440a0e9771SDarren Reed 		if (hdr.mhi_pktsize == buflen) {
4450a0e9771SDarren Reed 			buffer = mp->b_rptr;
4460a0e9771SDarren Reed 		} else {
4470a0e9771SDarren Reed 			buflen = 0;
4480a0e9771SDarren Reed 			buffer = (uchar_t *)mp;
4490a0e9771SDarren Reed 		}
4500a0e9771SDarren Reed 		rw_enter(&ps->ps_bpflock, RW_READER);
4510a0e9771SDarren Reed 		if (bpf_filter(ps->ps_bpf.bf_insns, buffer,
4520a0e9771SDarren Reed 		    hdr.mhi_pktsize, buflen) == 0) {
4530a0e9771SDarren Reed 			rw_exit(&ps->ps_bpflock);
4540a0e9771SDarren Reed 			ps->ps_stats.tp_drops++;
4550a0e9771SDarren Reed 			ks_stats.kp_recv_filtered.value.ui64++;
4560a0e9771SDarren Reed 			freemsg(mp);
4570a0e9771SDarren Reed 			return;
4580a0e9771SDarren Reed 		}
4590a0e9771SDarren Reed 		rw_exit(&ps->ps_bpflock);
4600a0e9771SDarren Reed 	}
4610a0e9771SDarren Reed 
4620a0e9771SDarren Reed 	if (ps->ps_type == SOCK_DGRAM) {
4630a0e9771SDarren Reed 		/*
4640a0e9771SDarren Reed 		 * SOCK_DGRAM socket expect a "layer 3" packet, so advance
4650a0e9771SDarren Reed 		 * past the link layer header.
4660a0e9771SDarren Reed 		 */
4670a0e9771SDarren Reed 		mp->b_rptr += hdr.mhi_hdrsize;
4680a0e9771SDarren Reed 		hdr.mhi_pktsize -= hdr.mhi_hdrsize;
4690a0e9771SDarren Reed 	}
4700a0e9771SDarren Reed 
4710a0e9771SDarren Reed 	tusz = sizeof (struct T_unitdata_ind) + sizeof (struct sockaddr_ll);
4720a0e9771SDarren Reed 	if (ps->ps_auxdata) {
4730a0e9771SDarren Reed 		tusz += _TPI_ALIGN_TOPT(sizeof (struct tpacket_auxdata));
4740a0e9771SDarren Reed 		tusz += _TPI_ALIGN_TOPT(sizeof (struct T_opthdr));
4750a0e9771SDarren Reed 	}
4760a0e9771SDarren Reed 
4770a0e9771SDarren Reed 	/*
4780a0e9771SDarren Reed 	 * It is tempting to think that this could be optimised by having
4790a0e9771SDarren Reed 	 * the base mblk_t allocated and hung off the pfpsock structure,
4800a0e9771SDarren Reed 	 * except that then another one would need to be allocated for the
4810a0e9771SDarren Reed 	 * sockaddr_ll that is included. Even creating a template to copy
4820a0e9771SDarren Reed 	 * from is of questionable value, as read-write from one structure
4830a0e9771SDarren Reed 	 * to the other is going to be slower than all of the initialisation.
4840a0e9771SDarren Reed 	 */
4850a0e9771SDarren Reed 	mp0 = allocb(tusz, BPRI_HI);
4860a0e9771SDarren Reed 	if (mp0 == NULL) {
4870a0e9771SDarren Reed 		ps->ps_stats.tp_drops++;
4880a0e9771SDarren Reed 		ks_stats.kp_recv_alloc_fail.value.ui64++;
4890a0e9771SDarren Reed 		freemsg(mp);
4900a0e9771SDarren Reed 		return;
4910a0e9771SDarren Reed 	}
4920a0e9771SDarren Reed 
4930a0e9771SDarren Reed 	(void) memset(mp0->b_rptr, 0, tusz);
4940a0e9771SDarren Reed 
4950a0e9771SDarren Reed 	mp0->b_datap->db_type = M_PROTO;
4960a0e9771SDarren Reed 	mp0->b_wptr = mp0->b_rptr + tusz;
4970a0e9771SDarren Reed 
4980a0e9771SDarren Reed 	tunit = (struct T_unitdata_ind *)mp0->b_rptr;
4990a0e9771SDarren Reed 	tunit->PRIM_type = T_UNITDATA_IND;
5000a0e9771SDarren Reed 	tunit->SRC_length = sizeof (struct sockaddr);
5010a0e9771SDarren Reed 	tunit->SRC_offset = sizeof (*tunit);
5020a0e9771SDarren Reed 
503*3b4315d3SPatrick Mooney 	sol = &ps->ps_sock;
5040a0e9771SDarren Reed 	sll = (struct sockaddr_ll *)(mp0->b_rptr + sizeof (*tunit));
5050a0e9771SDarren Reed 	sll->sll_ifindex = sol->sll_ifindex;
5060a0e9771SDarren Reed 	sll->sll_hatype = (uint16_t)hdr.mhi_origsap;
5070a0e9771SDarren Reed 	sll->sll_halen = sol->sll_halen;
5080a0e9771SDarren Reed 	if (hdr.mhi_saddr != NULL)
5090a0e9771SDarren Reed 		(void) memcpy(sll->sll_addr, hdr.mhi_saddr, sll->sll_halen);
5100a0e9771SDarren Reed 
5110a0e9771SDarren Reed 	switch (hdr.mhi_dsttype) {
5120a0e9771SDarren Reed 	case MAC_ADDRTYPE_MULTICAST :
5130a0e9771SDarren Reed 		sll->sll_pkttype = PACKET_MULTICAST;
5140a0e9771SDarren Reed 		break;
5150a0e9771SDarren Reed 	case MAC_ADDRTYPE_BROADCAST :
5160a0e9771SDarren Reed 		sll->sll_pkttype = PACKET_BROADCAST;
5170a0e9771SDarren Reed 		break;
5180a0e9771SDarren Reed 	case MAC_ADDRTYPE_UNICAST :
5190a0e9771SDarren Reed 		if (memcmp(sol->sll_addr, hdr.mhi_daddr, sol->sll_halen) == 0)
5200a0e9771SDarren Reed 			sll->sll_pkttype = PACKET_HOST;
5210a0e9771SDarren Reed 		else
5220a0e9771SDarren Reed 			sll->sll_pkttype = PACKET_OTHERHOST;
5230a0e9771SDarren Reed 		break;
5240a0e9771SDarren Reed 	}
5250a0e9771SDarren Reed 
5260a0e9771SDarren Reed 	if (ps->ps_auxdata) {
5270a0e9771SDarren Reed 		struct tpacket_auxdata *aux;
5280a0e9771SDarren Reed 		struct T_opthdr *topt;
5290a0e9771SDarren Reed 
5300a0e9771SDarren Reed 		tunit->OPT_offset = _TPI_ALIGN_TOPT(tunit->SRC_offset +
5310a0e9771SDarren Reed 		    sizeof (struct sockaddr_ll));
5320a0e9771SDarren Reed 		tunit->OPT_length = _TPI_ALIGN_TOPT(sizeof (struct T_opthdr)) +
5330a0e9771SDarren Reed 		    _TPI_ALIGN_TOPT(sizeof (struct tpacket_auxdata));
5340a0e9771SDarren Reed 
5350a0e9771SDarren Reed 		topt = (struct T_opthdr *)(mp0->b_rptr + tunit->OPT_offset);
5360a0e9771SDarren Reed 		aux = (struct tpacket_auxdata *)
5370a0e9771SDarren Reed 		    ((char *)topt + _TPI_ALIGN_TOPT(sizeof (*topt)));
5380a0e9771SDarren Reed 
5390a0e9771SDarren Reed 		topt->len = tunit->OPT_length;
5400a0e9771SDarren Reed 		topt->level = SOL_PACKET;
5410a0e9771SDarren Reed 		topt->name = PACKET_AUXDATA;
5420a0e9771SDarren Reed 		topt->status = 0;
5430a0e9771SDarren Reed 		/*
5440a0e9771SDarren Reed 		 * libpcap doesn't seem to use any other field,
5450a0e9771SDarren Reed 		 * so it isn't clear how they should be filled in.
5460a0e9771SDarren Reed 		 */
5470a0e9771SDarren Reed 		aux->tp_vlan_vci = hdr.mhi_tci;
5480a0e9771SDarren Reed 	}
5490a0e9771SDarren Reed 
5500a0e9771SDarren Reed 	linkb(mp0, mp);
5510a0e9771SDarren Reed 
552336069c2SPatrick Mooney 	(void) gethrestime(&ps->ps_timestamp);
553336069c2SPatrick Mooney 
5540a0e9771SDarren Reed 	ps->ps_upcalls->su_recv(ps->ps_upper, mp0, hdr.mhi_pktsize, 0,
5550a0e9771SDarren Reed 	    &error, NULL);
5560a0e9771SDarren Reed 
5570a0e9771SDarren Reed 	if (error == 0) {
5580a0e9771SDarren Reed 		ps->ps_stats.tp_packets++;
5590a0e9771SDarren Reed 		ks_stats.kp_recv_ok.value.ui64++;
5600a0e9771SDarren Reed 	} else {
5610a0e9771SDarren Reed 		mutex_enter(&ps->ps_lock);
5620a0e9771SDarren Reed 		if (error == ENOSPC) {
5630a0e9771SDarren Reed 			ps->ps_upcalls->su_recv(ps->ps_upper, NULL, 0, 0,
5640a0e9771SDarren Reed 			    &error, NULL);
5650a0e9771SDarren Reed 			if (error == ENOSPC)
5660a0e9771SDarren Reed 				ps->ps_flow_ctrld = B_TRUE;
5670a0e9771SDarren Reed 		}
5680a0e9771SDarren Reed 		mutex_exit(&ps->ps_lock);
5690a0e9771SDarren Reed 		ps->ps_stats.tp_drops++;
5700a0e9771SDarren Reed 		ks_stats.kp_recv_fail.value.ui64++;
5710a0e9771SDarren Reed 	}
5720a0e9771SDarren Reed }
5730a0e9771SDarren Reed 
5740a0e9771SDarren Reed /*
5750a0e9771SDarren Reed  * Bind a PF_PACKET socket to a network interface.
5760a0e9771SDarren Reed  *
5770a0e9771SDarren Reed  * The default operation of this bind() is to place the socket (and thus the
5780a0e9771SDarren Reed  * network interface) into promiscuous mode. It is then up to the application
5790a0e9771SDarren Reed  * to turn that down by issuing the relevant ioctls, if desired.
5800a0e9771SDarren Reed  */
5810a0e9771SDarren Reed static int
5820a0e9771SDarren Reed sdpfp_bind(sock_lower_handle_t handle, struct sockaddr *addr,
5830a0e9771SDarren Reed     socklen_t addrlen, struct cred *cred)
5840a0e9771SDarren Reed {
5850a0e9771SDarren Reed 	struct sockaddr_ll *addr_ll, *sol;
5860a0e9771SDarren Reed 	mac_client_handle_t mch;
5870a0e9771SDarren Reed 	struct pfpsock *ps;
5880a0e9771SDarren Reed 	mac_handle_t mh;
5890a0e9771SDarren Reed 	int error;
5900a0e9771SDarren Reed 
5910a0e9771SDarren Reed 	ps = (struct pfpsock *)handle;
5920a0e9771SDarren Reed 	if (ps->ps_bound)
5930a0e9771SDarren Reed 		return (EINVAL);
5940a0e9771SDarren Reed 
595*3b4315d3SPatrick Mooney 	if (addrlen < sizeof (struct sockaddr_ll) || addr == NULL)
596*3b4315d3SPatrick Mooney 		return (EINVAL);
597*3b4315d3SPatrick Mooney 
5980a0e9771SDarren Reed 	addr_ll = (struct sockaddr_ll *)addr;
5990a0e9771SDarren Reed 
6000a0e9771SDarren Reed 	error = pfp_open_index(addr_ll->sll_ifindex, &mh, &mch, cred);
6010a0e9771SDarren Reed 	if (error != 0)
6020a0e9771SDarren Reed 		return (error);
6030a0e9771SDarren Reed 	/*
6040a0e9771SDarren Reed 	 * Ensure that each socket is only bound once.
6050a0e9771SDarren Reed 	 */
6060a0e9771SDarren Reed 	mutex_enter(&ps->ps_lock);
6070a0e9771SDarren Reed 	if (ps->ps_mh != 0) {
6080a0e9771SDarren Reed 		mutex_exit(&ps->ps_lock);
6090a0e9771SDarren Reed 		pfp_close(mh, mch);
6100a0e9771SDarren Reed 		return (EADDRINUSE);
6110a0e9771SDarren Reed 	}
6120a0e9771SDarren Reed 	ps->ps_mh = mh;
6130a0e9771SDarren Reed 	ps->ps_mch = mch;
6140a0e9771SDarren Reed 	mutex_exit(&ps->ps_lock);
6150a0e9771SDarren Reed 
6160a0e9771SDarren Reed 	/*
6170a0e9771SDarren Reed 	 * Cache all of the information from bind so that it's in an easy
6180a0e9771SDarren Reed 	 * place to get at when packets are received.
6190a0e9771SDarren Reed 	 */
620*3b4315d3SPatrick Mooney 	sol = &ps->ps_sock;
6210a0e9771SDarren Reed 	sol->sll_family = AF_PACKET;
6220a0e9771SDarren Reed 	sol->sll_ifindex = addr_ll->sll_ifindex;
6230a0e9771SDarren Reed 	sol->sll_protocol = addr_ll->sll_protocol;
6240a0e9771SDarren Reed 	sol->sll_halen = mac_addr_len(ps->ps_mh);
6250a0e9771SDarren Reed 	mac_unicast_primary_get(ps->ps_mh, sol->sll_addr);
6260a0e9771SDarren Reed 	mac_sdu_get(ps->ps_mh, NULL, &ps->ps_max_sdu);
6270a0e9771SDarren Reed 	ps->ps_linkid = addr_ll->sll_ifindex;
6280a0e9771SDarren Reed 
6290a0e9771SDarren Reed 	error = mac_promisc_add(ps->ps_mch, MAC_CLIENT_PROMISC_ALL,
6300a0e9771SDarren Reed 	    pfp_packet, ps, &ps->ps_phd, MAC_PROMISC_FLAGS_VLAN_TAG_STRIP);
6310a0e9771SDarren Reed 	if (error == 0) {
6320a0e9771SDarren Reed 		ps->ps_promisc = MAC_CLIENT_PROMISC_ALL;
6330a0e9771SDarren Reed 		ps->ps_bound = B_TRUE;
6340a0e9771SDarren Reed 	}
6350a0e9771SDarren Reed 
6360a0e9771SDarren Reed 	return (error);
6370a0e9771SDarren Reed }
6380a0e9771SDarren Reed 
6390a0e9771SDarren Reed /* ARGSUSED */
6400a0e9771SDarren Reed static void
6410a0e9771SDarren Reed sdpfp_activate(sock_lower_handle_t lower, sock_upper_handle_t upper,
6420a0e9771SDarren Reed     sock_upcalls_t *upcalls, int flags, cred_t *cred)
6430a0e9771SDarren Reed {
6440a0e9771SDarren Reed 	struct pfpsock *ps;
6450a0e9771SDarren Reed 
6460a0e9771SDarren Reed 	ps = (struct pfpsock *)lower;
6470a0e9771SDarren Reed 	ps->ps_upper = upper;
6480a0e9771SDarren Reed 	ps->ps_upcalls = upcalls;
6490a0e9771SDarren Reed }
6500a0e9771SDarren Reed 
6510a0e9771SDarren Reed /*
6520a0e9771SDarren Reed  * This module only implements getting socket options for the new socket
6530a0e9771SDarren Reed  * option level (SOL_PACKET) that it introduces. All other requests are
6540a0e9771SDarren Reed  * passed back to the sockfs layer.
6550a0e9771SDarren Reed  */
6560a0e9771SDarren Reed /* ARGSUSED */
6570a0e9771SDarren Reed static int
6580a0e9771SDarren Reed sdpfp_getsockopt(sock_lower_handle_t handle, int level, int option_name,
6590a0e9771SDarren Reed     void *optval, socklen_t *optlenp, struct cred *cred)
6600a0e9771SDarren Reed {
661336069c2SPatrick Mooney 	struct pfpsock *ps;
6620a0e9771SDarren Reed 	int error = 0;
6630a0e9771SDarren Reed 
664336069c2SPatrick Mooney 	ps = (struct pfpsock *)handle;
665336069c2SPatrick Mooney 
6660a0e9771SDarren Reed 	switch (level) {
6670a0e9771SDarren Reed 	case SOL_PACKET :
6680a0e9771SDarren Reed 		error = pfp_getpacket_sockopt(handle, option_name, optval,
6690a0e9771SDarren Reed 		    optlenp);
6700a0e9771SDarren Reed 		break;
671336069c2SPatrick Mooney 
672336069c2SPatrick Mooney 	case SOL_SOCKET :
673336069c2SPatrick Mooney 		if (option_name == SO_RCVBUF) {
674336069c2SPatrick Mooney 			if (*optlenp < sizeof (int32_t))
675336069c2SPatrick Mooney 				return (EINVAL);
676336069c2SPatrick Mooney 			*((int32_t *)optval) = ps->ps_rcvbuf;
677336069c2SPatrick Mooney 			*optlenp = sizeof (int32_t);
678336069c2SPatrick Mooney 		} else {
679336069c2SPatrick Mooney 			error = ENOPROTOOPT;
680336069c2SPatrick Mooney 		}
681336069c2SPatrick Mooney 		break;
682336069c2SPatrick Mooney 
6830a0e9771SDarren Reed 	default :
6840a0e9771SDarren Reed 		/*
6850a0e9771SDarren Reed 		 * If sockfs code receives this error in return from the
6860a0e9771SDarren Reed 		 * getsockopt downcall it handles the option locally, if
687336069c2SPatrick Mooney 		 * it can.
6880a0e9771SDarren Reed 		 */
6890a0e9771SDarren Reed 		error = ENOPROTOOPT;
6900a0e9771SDarren Reed 		break;
6910a0e9771SDarren Reed 	}
6920a0e9771SDarren Reed 
6930a0e9771SDarren Reed 	return (error);
6940a0e9771SDarren Reed }
6950a0e9771SDarren Reed 
6960a0e9771SDarren Reed /*
6970a0e9771SDarren Reed  * PF_PACKET supports setting socket options at only two levels:
6980a0e9771SDarren Reed  * SOL_SOCKET and SOL_PACKET.
6990a0e9771SDarren Reed  */
7000a0e9771SDarren Reed /* ARGSUSED */
7010a0e9771SDarren Reed static int
7020a0e9771SDarren Reed sdpfp_setsockopt(sock_lower_handle_t handle, int level, int option_name,
7030a0e9771SDarren Reed     const void *optval, socklen_t optlen, struct cred *cred)
7040a0e9771SDarren Reed {
7050a0e9771SDarren Reed 	int error = 0;
7060a0e9771SDarren Reed 
7070a0e9771SDarren Reed 	switch (level) {
7080a0e9771SDarren Reed 	case SOL_SOCKET :
7090a0e9771SDarren Reed 		error = pfp_setsocket_sockopt(handle, option_name, optval,
7100a0e9771SDarren Reed 		    optlen);
7110a0e9771SDarren Reed 		break;
7120a0e9771SDarren Reed 	case SOL_PACKET :
7130a0e9771SDarren Reed 		error = pfp_setpacket_sockopt(handle, option_name, optval,
7140a0e9771SDarren Reed 		    optlen);
7150a0e9771SDarren Reed 		break;
7160a0e9771SDarren Reed 	default :
7170a0e9771SDarren Reed 		error = EINVAL;
7180a0e9771SDarren Reed 		break;
7190a0e9771SDarren Reed 	}
7200a0e9771SDarren Reed 
7210a0e9771SDarren Reed 	return (error);
7220a0e9771SDarren Reed }
7230a0e9771SDarren Reed 
7240a0e9771SDarren Reed /*
7250a0e9771SDarren Reed  * This function is incredibly inefficient for sending any packet that
7260a0e9771SDarren Reed  * comes with a msghdr asking to be sent to an interface to which the
7270a0e9771SDarren Reed  * socket has not been bound. Some possibilities here are keeping a
7280a0e9771SDarren Reed  * cache of all open mac's and mac_client's, for the purpose of sending,
7290a0e9771SDarren Reed  * and closing them after some amount of inactivity. Clearly, applications
7300a0e9771SDarren Reed  * should not be written to use one socket for multiple interfaces if
7310a0e9771SDarren Reed  * performance is desired with the code as is.
7320a0e9771SDarren Reed  */
7330a0e9771SDarren Reed /* ARGSUSED */
7340a0e9771SDarren Reed static int
7350a0e9771SDarren Reed sdpfp_senduio(sock_lower_handle_t handle, struct uio *uiop,
7360a0e9771SDarren Reed     struct nmsghdr *msg, struct cred *cred)
7370a0e9771SDarren Reed {
7380a0e9771SDarren Reed 	struct sockaddr_ll *sol;
7390a0e9771SDarren Reed 	mac_client_handle_t mch;
7400a0e9771SDarren Reed 	struct pfpsock *ps;
7410a0e9771SDarren Reed 	boolean_t new_open;
7420a0e9771SDarren Reed 	mac_handle_t mh;
7430a0e9771SDarren Reed 	size_t mpsize;
7440a0e9771SDarren Reed 	uint_t maxsdu;
7450a0e9771SDarren Reed 	mblk_t *mp0;
7460a0e9771SDarren Reed 	mblk_t *mp;
7470a0e9771SDarren Reed 	int error;
7480a0e9771SDarren Reed 
7490a0e9771SDarren Reed 	mp = NULL;
7500a0e9771SDarren Reed 	mp0 = NULL;
7510a0e9771SDarren Reed 	new_open = B_FALSE;
7520a0e9771SDarren Reed 	ps = (struct pfpsock *)handle;
7530a0e9771SDarren Reed 	mh = ps->ps_mh;
7540a0e9771SDarren Reed 	mch = ps->ps_mch;
7550a0e9771SDarren Reed 	maxsdu = ps->ps_max_sdu;
7560a0e9771SDarren Reed 
7570a0e9771SDarren Reed 	sol = (struct sockaddr_ll *)msg->msg_name;
7580a0e9771SDarren Reed 	if (sol == NULL) {
7590a0e9771SDarren Reed 		/*
7600a0e9771SDarren Reed 		 * If no sockaddr_ll has been provided with the send call,
7610a0e9771SDarren Reed 		 * use the one constructed when the socket was bound to an
7620a0e9771SDarren Reed 		 * interface and fail if it hasn't been bound.
7630a0e9771SDarren Reed 		 */
7640a0e9771SDarren Reed 		if (!ps->ps_bound) {
7650a0e9771SDarren Reed 			ks_stats.kp_send_unbound.value.ui64++;
7660a0e9771SDarren Reed 			return (EPROTO);
7670a0e9771SDarren Reed 		}
768*3b4315d3SPatrick Mooney 		sol = &ps->ps_sock;
7690a0e9771SDarren Reed 	} else {
7700a0e9771SDarren Reed 		/*
7710a0e9771SDarren Reed 		 * Verify the sockaddr_ll message passed down before using
7720a0e9771SDarren Reed 		 * it to send a packet out with. If it refers to an interface
7730a0e9771SDarren Reed 		 * that has not been bound, it is necessary to open it.
7740a0e9771SDarren Reed 		 */
7750a0e9771SDarren Reed 		struct sockaddr_ll *sll;
7760a0e9771SDarren Reed 
7770a0e9771SDarren Reed 		if (msg->msg_namelen < sizeof (struct sockaddr_ll)) {
7780a0e9771SDarren Reed 			ks_stats.kp_send_short_msg.value.ui64++;
7790a0e9771SDarren Reed 			return (EINVAL);
7800a0e9771SDarren Reed 		}
7810a0e9771SDarren Reed 
7820a0e9771SDarren Reed 		if (sol->sll_family != AF_PACKET) {
7830a0e9771SDarren Reed 			ks_stats.kp_send_wrong_family.value.ui64++;
7840a0e9771SDarren Reed 			return (EAFNOSUPPORT);
7850a0e9771SDarren Reed 		}
7860a0e9771SDarren Reed 
787*3b4315d3SPatrick Mooney 		sll = &ps->ps_sock;
7880a0e9771SDarren Reed 		if (sol->sll_ifindex != sll->sll_ifindex) {
7890a0e9771SDarren Reed 			error = pfp_open_index(sol->sll_ifindex, &mh, &mch,
7900a0e9771SDarren Reed 			    cred);
7910a0e9771SDarren Reed 			if (error != 0) {
7920a0e9771SDarren Reed 				ks_stats.kp_send_open_fail.value.ui64++;
7930a0e9771SDarren Reed 				return (error);
7940a0e9771SDarren Reed 			}
7950a0e9771SDarren Reed 			mac_sdu_get(mh, NULL, &maxsdu);
7960a0e9771SDarren Reed 			new_open = B_TRUE;
7970a0e9771SDarren Reed 		}
7980a0e9771SDarren Reed 	}
7990a0e9771SDarren Reed 
8000a0e9771SDarren Reed 	mpsize = uiop->uio_resid;
8010a0e9771SDarren Reed 	if (mpsize > maxsdu) {
8020a0e9771SDarren Reed 		ks_stats.kp_send_too_big.value.ui64++;
8030a0e9771SDarren Reed 		error = EMSGSIZE;
8040a0e9771SDarren Reed 		goto done;
8050a0e9771SDarren Reed 	}
8060a0e9771SDarren Reed 
8070a0e9771SDarren Reed 	if ((mp = allocb(mpsize, BPRI_HI)) == NULL) {
8080a0e9771SDarren Reed 		ks_stats.kp_send_alloc_fail.value.ui64++;
8090a0e9771SDarren Reed 		error = ENOBUFS;
8100a0e9771SDarren Reed 		goto done;
8110a0e9771SDarren Reed 	}
8120a0e9771SDarren Reed 
8130a0e9771SDarren Reed 	mp->b_wptr = mp->b_rptr + mpsize;
8140a0e9771SDarren Reed 	error = uiomove(mp->b_rptr, mpsize, UIO_WRITE, uiop);
8150a0e9771SDarren Reed 	if (error != 0) {
8160a0e9771SDarren Reed 		ks_stats.kp_send_uiomove_fail.value.ui64++;
8170a0e9771SDarren Reed 		goto done;
8180a0e9771SDarren Reed 	}
8190a0e9771SDarren Reed 
8200a0e9771SDarren Reed 	if (ps->ps_type == SOCK_DGRAM) {
8210a0e9771SDarren Reed 		mp0 = mac_header(mh, sol->sll_addr, sol->sll_protocol, mp, 0);
8220a0e9771SDarren Reed 		if (mp0 == NULL) {
8230a0e9771SDarren Reed 			ks_stats.kp_send_no_memory.value.ui64++;
8240a0e9771SDarren Reed 			error = ENOBUFS;
8250a0e9771SDarren Reed 			goto done;
8260a0e9771SDarren Reed 		}
8270a0e9771SDarren Reed 		linkb(mp0, mp);
8280a0e9771SDarren Reed 		mp = mp0;
8290a0e9771SDarren Reed 	}
8300a0e9771SDarren Reed 
8310a0e9771SDarren Reed 	/*
8320a0e9771SDarren Reed 	 * As this is sending datagrams and no promise is made about
8330a0e9771SDarren Reed 	 * how or if a packet will be sent/delivered, no effort is to
8340a0e9771SDarren Reed 	 * be expended in recovering from a situation where the packet
8350a0e9771SDarren Reed 	 * cannot be sent - it is just dropped.
8360a0e9771SDarren Reed 	 */
8370a0e9771SDarren Reed 	error = mac_tx(mch, mp, 0, MAC_DROP_ON_NO_DESC, NULL);
8380a0e9771SDarren Reed 	if (error == 0) {
8390a0e9771SDarren Reed 		mp = NULL;
8400a0e9771SDarren Reed 		ks_stats.kp_send_ok.value.ui64++;
8410a0e9771SDarren Reed 	} else {
8420a0e9771SDarren Reed 		ks_stats.kp_send_failed.value.ui64++;
8430a0e9771SDarren Reed 	}
8440a0e9771SDarren Reed 
8450a0e9771SDarren Reed done:
8460a0e9771SDarren Reed 
8470a0e9771SDarren Reed 	if (new_open) {
8480a0e9771SDarren Reed 		ASSERT(mch != ps->ps_mch);
8490a0e9771SDarren Reed 		ASSERT(mh != ps->ps_mh);
8500a0e9771SDarren Reed 		pfp_close(mh, mch);
8510a0e9771SDarren Reed 	}
8520a0e9771SDarren Reed 	if (mp != NULL)
8530a0e9771SDarren Reed 		freemsg(mp);
8540a0e9771SDarren Reed 
8550a0e9771SDarren Reed 	return (error);
8560a0e9771SDarren Reed 
8570a0e9771SDarren Reed }
8580a0e9771SDarren Reed 
8590a0e9771SDarren Reed /*
8600a0e9771SDarren Reed  * There's no use of a lock here, or at the bottom of pfp_packet() where
8610a0e9771SDarren Reed  * ps_flow_ctrld is set to true, because in a situation where these two
8620a0e9771SDarren Reed  * are racing to set the flag one way or the other, the end result is
8630a0e9771SDarren Reed  * going to be ultimately determined by the scheduler anyway - which of
8640a0e9771SDarren Reed  * the two threads gets the lock first? In such an operational environment,
8650a0e9771SDarren Reed  * we've got packets arriving too fast to be delt with so packets are going
8660a0e9771SDarren Reed  * to be dropped. Grabbing a lock just makes the drop more expensive.
8670a0e9771SDarren Reed  */
8680a0e9771SDarren Reed static void
8690a0e9771SDarren Reed sdpfp_clr_flowctrl(sock_lower_handle_t handle)
8700a0e9771SDarren Reed {
8710a0e9771SDarren Reed 	struct pfpsock *ps;
8720a0e9771SDarren Reed 
8730a0e9771SDarren Reed 	ps = (struct pfpsock *)handle;
8740a0e9771SDarren Reed 
8750a0e9771SDarren Reed 	mutex_enter(&ps->ps_lock);
8760a0e9771SDarren Reed 	ps->ps_flow_ctrld = B_FALSE;
8770a0e9771SDarren Reed 	mutex_exit(&ps->ps_lock);
8780a0e9771SDarren Reed }
8790a0e9771SDarren Reed 
8800a0e9771SDarren Reed /*
8810a0e9771SDarren Reed  * The implementation of this ioctl() handler is intended to function
8820a0e9771SDarren Reed  * in the absence of a bind() being made before it is called. Thus the
8830a0e9771SDarren Reed  * function calls mac_open() itself to provide a handle
8840a0e9771SDarren Reed  * This function is structured like this:
8850a0e9771SDarren Reed  * - determine the linkid for the interface being targetted
8860a0e9771SDarren Reed  * - open the interface with said linkid
8870a0e9771SDarren Reed  * - perform ioctl
8880a0e9771SDarren Reed  * - copy results back to caller
8890a0e9771SDarren Reed  *
8900a0e9771SDarren Reed  * The ioctls that interact with interface flags have been implented below
8910a0e9771SDarren Reed  * to assume that the interface is always up and running (IFF_RUNNING) and
8920a0e9771SDarren Reed  * to use the state of this socket to determine whether or not the network
8930a0e9771SDarren Reed  * interface is in promiscuous mode. Thus an ioctl to get the interface flags
8940a0e9771SDarren Reed  * of an interface that has been put in promiscuous mode by another socket
8950a0e9771SDarren Reed  * (in the same program or different), will not report that status.
8960a0e9771SDarren Reed  */
8970a0e9771SDarren Reed /* ARGSUSED */
8980a0e9771SDarren Reed static int
8990a0e9771SDarren Reed sdpfp_ioctl(sock_lower_handle_t handle, int cmd, intptr_t arg, int mod,
9000a0e9771SDarren Reed     int32_t *rval, struct cred *cr)
9010a0e9771SDarren Reed {
9020a0e9771SDarren Reed 	struct timeval tival;
9030a0e9771SDarren Reed 	mac_client_promisc_type_t mtype;
904a6911619SDarren Reed 	struct sockaddr_dl *sock;
9050a0e9771SDarren Reed 	datalink_id_t linkid;
9060a0e9771SDarren Reed 	struct lifreq lifreq;
9070a0e9771SDarren Reed 	struct ifreq ifreq;
9080a0e9771SDarren Reed 	struct pfpsock *ps;
9090a0e9771SDarren Reed 	mac_handle_t mh;
9100a0e9771SDarren Reed 	int error;
9110a0e9771SDarren Reed 
912336069c2SPatrick Mooney 	ps = (struct pfpsock *)handle;
913336069c2SPatrick Mooney 
9140a0e9771SDarren Reed 	switch (cmd) {
9150a0e9771SDarren Reed 	/*
9160a0e9771SDarren Reed 	 * ioctls that work on "struct lifreq"
9170a0e9771SDarren Reed 	 */
9180a0e9771SDarren Reed 	case SIOCSLIFFLAGS :
9190a0e9771SDarren Reed 	case SIOCGLIFINDEX :
9200a0e9771SDarren Reed 	case SIOCGLIFFLAGS :
9210a0e9771SDarren Reed 	case SIOCGLIFMTU :
922a6911619SDarren Reed 	case SIOCGLIFHWADDR :
923336069c2SPatrick Mooney 		error = pfp_lifreq_getlinkid(arg, &lifreq, &linkid, mod);
9240a0e9771SDarren Reed 		if (error != 0)
9250a0e9771SDarren Reed 			return (error);
9260a0e9771SDarren Reed 		break;
9270a0e9771SDarren Reed 
9280a0e9771SDarren Reed 	/*
9290a0e9771SDarren Reed 	 * ioctls that work on "struct ifreq".
9300a0e9771SDarren Reed 	 * Not all of these have a "struct lifreq" partner, for example
9310a0e9771SDarren Reed 	 * SIOCGIFHWADDR, for the simple reason that the logical interface
9320a0e9771SDarren Reed 	 * does not have a hardware address.
9330a0e9771SDarren Reed 	 */
9340a0e9771SDarren Reed 	case SIOCSIFFLAGS :
9350a0e9771SDarren Reed 	case SIOCGIFINDEX :
9360a0e9771SDarren Reed 	case SIOCGIFFLAGS :
9370a0e9771SDarren Reed 	case SIOCGIFMTU :
9380a0e9771SDarren Reed 	case SIOCGIFHWADDR :
939336069c2SPatrick Mooney 		error = pfp_ifreq_getlinkid(arg, &ifreq, &linkid, mod);
9400a0e9771SDarren Reed 		if (error != 0)
9410a0e9771SDarren Reed 			return (error);
9420a0e9771SDarren Reed 		break;
943336069c2SPatrick Mooney 
944336069c2SPatrick Mooney 	case SIOCGSTAMP :
945336069c2SPatrick Mooney 		tival.tv_sec = (time_t)ps->ps_timestamp.tv_sec;
946336069c2SPatrick Mooney 		tival.tv_usec = ps->ps_timestamp.tv_nsec / 1000;
947336069c2SPatrick Mooney 		if (get_udatamodel() == DATAMODEL_NATIVE) {
948336069c2SPatrick Mooney 			error = ddi_copyout(&tival, (void *)arg,
949336069c2SPatrick Mooney 			    sizeof (tival), mod);
950336069c2SPatrick Mooney 		}
951336069c2SPatrick Mooney #ifdef _SYSCALL32_IMPL
952336069c2SPatrick Mooney 		else {
953336069c2SPatrick Mooney 			struct timeval32 tv32;
954336069c2SPatrick Mooney 			TIMEVAL_TO_TIMEVAL32(&tv32, &tival);
955336069c2SPatrick Mooney 			error = ddi_copyout(&tv32, (void *)arg,
956336069c2SPatrick Mooney 			    sizeof (tv32), mod);
957336069c2SPatrick Mooney 		}
958336069c2SPatrick Mooney #endif
959336069c2SPatrick Mooney 		return (error);
9600a0e9771SDarren Reed 	}
9610a0e9771SDarren Reed 
9620a0e9771SDarren Reed 	error =  mac_open_by_linkid(linkid, &mh);
9630a0e9771SDarren Reed 	if (error != 0)
9640a0e9771SDarren Reed 		return (error);
9650a0e9771SDarren Reed 
9660a0e9771SDarren Reed 	switch (cmd) {
9670a0e9771SDarren Reed 	case SIOCGLIFINDEX :
9680a0e9771SDarren Reed 		lifreq.lifr_index = linkid;
9690a0e9771SDarren Reed 		break;
9700a0e9771SDarren Reed 
9710a0e9771SDarren Reed 	case SIOCGIFINDEX :
9720a0e9771SDarren Reed 		ifreq.ifr_index = linkid;
9730a0e9771SDarren Reed 		break;
9740a0e9771SDarren Reed 
9750a0e9771SDarren Reed 	case SIOCGIFFLAGS :
9760a0e9771SDarren Reed 		ifreq.ifr_flags = IFF_RUNNING;
9770a0e9771SDarren Reed 		if (ps->ps_promisc == MAC_CLIENT_PROMISC_ALL)
9780a0e9771SDarren Reed 			ifreq.ifr_flags |= IFF_PROMISC;
9790a0e9771SDarren Reed 		break;
9800a0e9771SDarren Reed 
9810a0e9771SDarren Reed 	case SIOCGLIFFLAGS :
9820a0e9771SDarren Reed 		lifreq.lifr_flags = IFF_RUNNING;
9830a0e9771SDarren Reed 		if (ps->ps_promisc == MAC_CLIENT_PROMISC_ALL)
9840a0e9771SDarren Reed 			lifreq.lifr_flags |= IFF_PROMISC;
9850a0e9771SDarren Reed 		break;
9860a0e9771SDarren Reed 
9870a0e9771SDarren Reed 	case SIOCSIFFLAGS :
9880a0e9771SDarren Reed 		if (linkid != ps->ps_linkid) {
9890a0e9771SDarren Reed 			error = EINVAL;
9900a0e9771SDarren Reed 		} else {
9910a0e9771SDarren Reed 			if ((ifreq.ifr_flags & IFF_PROMISC) != 0)
9920a0e9771SDarren Reed 				mtype = MAC_CLIENT_PROMISC_ALL;
9930a0e9771SDarren Reed 			else
9940a0e9771SDarren Reed 				mtype = MAC_CLIENT_PROMISC_FILTERED;
9950a0e9771SDarren Reed 			error = pfp_set_promisc(ps, mtype);
9960a0e9771SDarren Reed 		}
9970a0e9771SDarren Reed 		break;
9980a0e9771SDarren Reed 
9990a0e9771SDarren Reed 	case SIOCSLIFFLAGS :
10000a0e9771SDarren Reed 		if (linkid != ps->ps_linkid) {
10010a0e9771SDarren Reed 			error = EINVAL;
10020a0e9771SDarren Reed 		} else {
10030a0e9771SDarren Reed 			if ((lifreq.lifr_flags & IFF_PROMISC) != 0)
10040a0e9771SDarren Reed 				mtype = MAC_CLIENT_PROMISC_ALL;
10050a0e9771SDarren Reed 			else
10060a0e9771SDarren Reed 				mtype = MAC_CLIENT_PROMISC_FILTERED;
10070a0e9771SDarren Reed 			error = pfp_set_promisc(ps, mtype);
10080a0e9771SDarren Reed 		}
10090a0e9771SDarren Reed 		break;
10100a0e9771SDarren Reed 
10110a0e9771SDarren Reed 	case SIOCGIFMTU :
10120a0e9771SDarren Reed 		mac_sdu_get(mh, NULL, &ifreq.ifr_mtu);
10130a0e9771SDarren Reed 		break;
10140a0e9771SDarren Reed 
10150a0e9771SDarren Reed 	case SIOCGLIFMTU :
10160a0e9771SDarren Reed 		mac_sdu_get(mh, NULL, &lifreq.lifr_mtu);
10170a0e9771SDarren Reed 		break;
10180a0e9771SDarren Reed 
10190a0e9771SDarren Reed 	case SIOCGIFHWADDR :
1020a6911619SDarren Reed 		if (mac_addr_len(mh) > sizeof (ifreq.ifr_addr.sa_data)) {
1021a6911619SDarren Reed 			error = EPFNOSUPPORT;
1022a6911619SDarren Reed 			break;
1023a6911619SDarren Reed 		}
1024a6911619SDarren Reed 
1025a6911619SDarren Reed 		if (mac_addr_len(mh) == 0) {
1026a6911619SDarren Reed 			(void) memset(ifreq.ifr_addr.sa_data, 0,
1027a6911619SDarren Reed 			    sizeof (ifreq.ifr_addr.sa_data));
1028a6911619SDarren Reed 		} else {
1029a6911619SDarren Reed 			mac_unicast_primary_get(mh,
1030a6911619SDarren Reed 			    (uint8_t *)ifreq.ifr_addr.sa_data);
1031a6911619SDarren Reed 		}
1032a6911619SDarren Reed 
1033a6911619SDarren Reed 		/*
1034a6911619SDarren Reed 		 * The behaviour here in setting sa_family is consistent
1035a6911619SDarren Reed 		 * with what applications such as tcpdump would expect
1036a6911619SDarren Reed 		 * for a Linux PF_PACKET socket.
1037a6911619SDarren Reed 		 */
10380a0e9771SDarren Reed 		ifreq.ifr_addr.sa_family = pfp_dl_to_arphrd(mac_type(mh));
10390a0e9771SDarren Reed 		break;
10400a0e9771SDarren Reed 
1041a6911619SDarren Reed 	case SIOCGLIFHWADDR :
1042a6911619SDarren Reed 		lifreq.lifr_type = 0;
1043a6911619SDarren Reed 		sock = (struct sockaddr_dl *)&lifreq.lifr_addr;
1044a6911619SDarren Reed 
1045a6911619SDarren Reed 		if (mac_addr_len(mh) > sizeof (sock->sdl_data)) {
1046a6911619SDarren Reed 			error = EPFNOSUPPORT;
1047a6911619SDarren Reed 			break;
1048a6911619SDarren Reed 		}
1049a6911619SDarren Reed 
1050a6911619SDarren Reed 		/*
1051a6911619SDarren Reed 		 * Fill in the sockaddr_dl with link layer details. Of note,
1052a6911619SDarren Reed 		 * the index is returned as 0 for a couple of reasons:
1053a6911619SDarren Reed 		 * (1) there is no public API that uses or requires it
1054a6911619SDarren Reed 		 * (2) the MAC index is currently 32bits and sdl_index is 16.
1055a6911619SDarren Reed 		 */
1056a6911619SDarren Reed 		sock->sdl_family = AF_LINK;
1057a6911619SDarren Reed 		sock->sdl_index = 0;
1058a6911619SDarren Reed 		sock->sdl_type = mac_type(mh);
1059a6911619SDarren Reed 		sock->sdl_nlen = 0;
1060a6911619SDarren Reed 		sock->sdl_alen = mac_addr_len(mh);
1061a6911619SDarren Reed 		sock->sdl_slen = 0;
1062a6911619SDarren Reed 		if (mac_addr_len(mh) == 0) {
1063a6911619SDarren Reed 			(void) memset(sock->sdl_data, 0,
1064a6911619SDarren Reed 			    sizeof (sock->sdl_data));
1065a6911619SDarren Reed 		} else {
1066a6911619SDarren Reed 			mac_unicast_primary_get(mh, (uint8_t *)sock->sdl_data);
1067a6911619SDarren Reed 		}
1068a6911619SDarren Reed 		break;
1069a6911619SDarren Reed 
10700a0e9771SDarren Reed 	default :
10710a0e9771SDarren Reed 		break;
10720a0e9771SDarren Reed 	}
10730a0e9771SDarren Reed 
10740a0e9771SDarren Reed 	mac_close(mh);
10750a0e9771SDarren Reed 
10760a0e9771SDarren Reed 	if (error == 0) {
10770a0e9771SDarren Reed 		/*
10780a0e9771SDarren Reed 		 * Only the "GET" ioctls need to copy data back to userace.
10790a0e9771SDarren Reed 		 */
10800a0e9771SDarren Reed 		switch (cmd) {
10810a0e9771SDarren Reed 		case SIOCGLIFINDEX :
10820a0e9771SDarren Reed 		case SIOCGLIFFLAGS :
10830a0e9771SDarren Reed 		case SIOCGLIFMTU :
1084a6911619SDarren Reed 		case SIOCGLIFHWADDR :
10850a0e9771SDarren Reed 			error = ddi_copyout(&lifreq, (void *)arg,
1086336069c2SPatrick Mooney 			    sizeof (lifreq), mod);
10870a0e9771SDarren Reed 			break;
10880a0e9771SDarren Reed 
10890a0e9771SDarren Reed 		case SIOCGIFINDEX :
10900a0e9771SDarren Reed 		case SIOCGIFFLAGS :
10910a0e9771SDarren Reed 		case SIOCGIFMTU :
10920a0e9771SDarren Reed 		case SIOCGIFHWADDR :
10930a0e9771SDarren Reed 			error = ddi_copyout(&ifreq, (void *)arg,
1094336069c2SPatrick Mooney 			    sizeof (ifreq), mod);
10950a0e9771SDarren Reed 			break;
10960a0e9771SDarren Reed 		default :
10970a0e9771SDarren Reed 			break;
10980a0e9771SDarren Reed 		}
10990a0e9771SDarren Reed 	}
11000a0e9771SDarren Reed 
11010a0e9771SDarren Reed 	return (error);
11020a0e9771SDarren Reed }
11030a0e9771SDarren Reed 
11040a0e9771SDarren Reed /*
11050a0e9771SDarren Reed  * Closing the socket requires that all open references to network
11060a0e9771SDarren Reed  * interfaces be closed.
11070a0e9771SDarren Reed  */
11080a0e9771SDarren Reed /* ARGSUSED */
11090a0e9771SDarren Reed static int
11100a0e9771SDarren Reed sdpfp_close(sock_lower_handle_t handle, int flag, struct cred *cr)
11110a0e9771SDarren Reed {
11120a0e9771SDarren Reed 	struct pfpsock *ps = (struct pfpsock *)handle;
11130a0e9771SDarren Reed 
11140a0e9771SDarren Reed 	if (ps->ps_phd != 0) {
11150a0e9771SDarren Reed 		mac_promisc_remove(ps->ps_phd);
11160a0e9771SDarren Reed 		ps->ps_phd = 0;
11170a0e9771SDarren Reed 	}
11180a0e9771SDarren Reed 
11190a0e9771SDarren Reed 	if (ps->ps_mch != 0) {
11200a0e9771SDarren Reed 		mac_client_close(ps->ps_mch, 0);
11210a0e9771SDarren Reed 		ps->ps_mch = 0;
11220a0e9771SDarren Reed 	}
11230a0e9771SDarren Reed 
11240a0e9771SDarren Reed 	if (ps->ps_mh != 0) {
11250a0e9771SDarren Reed 		mac_close(ps->ps_mh);
11260a0e9771SDarren Reed 		ps->ps_mh = 0;
11270a0e9771SDarren Reed 	}
11280a0e9771SDarren Reed 
11290a0e9771SDarren Reed 	kmem_free(ps, sizeof (*ps));
11300a0e9771SDarren Reed 
11310a0e9771SDarren Reed 	return (0);
11320a0e9771SDarren Reed }
11330a0e9771SDarren Reed 
11340a0e9771SDarren Reed /* ************************************************************************* */
11350a0e9771SDarren Reed 
11360a0e9771SDarren Reed /*
11370a0e9771SDarren Reed  * Given a pointer (arg) to a "struct ifreq" (potentially in user space),
11380a0e9771SDarren Reed  * determine the linkid for the interface name stored in that structure.
11390a0e9771SDarren Reed  * name is used as a buffer so that we can ensure a trailing \0 is appended
11400a0e9771SDarren Reed  * to the name safely.
11410a0e9771SDarren Reed  */
11420a0e9771SDarren Reed static int
11430a0e9771SDarren Reed pfp_ifreq_getlinkid(intptr_t arg, struct ifreq *ifreqp,
1144336069c2SPatrick Mooney     datalink_id_t *linkidp, int mode)
11450a0e9771SDarren Reed {
11460a0e9771SDarren Reed 	char name[IFNAMSIZ + 1];
11470a0e9771SDarren Reed 	int error;
11480a0e9771SDarren Reed 
1149336069c2SPatrick Mooney 	if (ddi_copyin((void *)arg, ifreqp, sizeof (*ifreqp), mode) != 0)
11500a0e9771SDarren Reed 		return (EFAULT);
11510a0e9771SDarren Reed 
11520a0e9771SDarren Reed 	(void) strlcpy(name, ifreqp->ifr_name, sizeof (name));
11530a0e9771SDarren Reed 
11540a0e9771SDarren Reed 	error = dls_mgmt_get_linkid(name, linkidp);
11550a0e9771SDarren Reed 	if (error != 0)
11560a0e9771SDarren Reed 		error = dls_devnet_macname2linkid(name, linkidp);
11570a0e9771SDarren Reed 
11580a0e9771SDarren Reed 	return (error);
11590a0e9771SDarren Reed }
11600a0e9771SDarren Reed 
11610a0e9771SDarren Reed /*
11620a0e9771SDarren Reed  * Given a pointer (arg) to a "struct lifreq" (potentially in user space),
11630a0e9771SDarren Reed  * determine the linkid for the interface name stored in that structure.
11640a0e9771SDarren Reed  * name is used as a buffer so that we can ensure a trailing \0 is appended
11650a0e9771SDarren Reed  * to the name safely.
11660a0e9771SDarren Reed  */
11670a0e9771SDarren Reed static int
11680a0e9771SDarren Reed pfp_lifreq_getlinkid(intptr_t arg, struct lifreq *lifreqp,
1169336069c2SPatrick Mooney     datalink_id_t *linkidp, int mode)
11700a0e9771SDarren Reed {
11710a0e9771SDarren Reed 	char name[LIFNAMSIZ + 1];
11720a0e9771SDarren Reed 	int error;
11730a0e9771SDarren Reed 
1174336069c2SPatrick Mooney 	if (ddi_copyin((void *)arg, lifreqp, sizeof (*lifreqp), mode) != 0)
11750a0e9771SDarren Reed 		return (EFAULT);
11760a0e9771SDarren Reed 
11770a0e9771SDarren Reed 	(void) strlcpy(name, lifreqp->lifr_name, sizeof (name));
11780a0e9771SDarren Reed 
11790a0e9771SDarren Reed 	error = dls_mgmt_get_linkid(name, linkidp);
11800a0e9771SDarren Reed 	if (error != 0)
11810a0e9771SDarren Reed 		error = dls_devnet_macname2linkid(name, linkidp);
11820a0e9771SDarren Reed 
11830a0e9771SDarren Reed 	return (error);
11840a0e9771SDarren Reed }
11850a0e9771SDarren Reed 
11860a0e9771SDarren Reed /*
11870a0e9771SDarren Reed  * Although there are several new SOL_PACKET options that can be set and
11880a0e9771SDarren Reed  * are specific to this implementation of PF_PACKET, the current API does
11890a0e9771SDarren Reed  * not support doing a get on them to retrieve accompanying status. Thus
11900a0e9771SDarren Reed  * it is only currently possible to use SOL_PACKET with getsockopt to
11910a0e9771SDarren Reed  * retrieve statistical information. This remains consistant with the
11920a0e9771SDarren Reed  * Linux API at the time of writing.
11930a0e9771SDarren Reed  */
11940a0e9771SDarren Reed static int
11950a0e9771SDarren Reed pfp_getpacket_sockopt(sock_lower_handle_t handle, int option_name,
11960a0e9771SDarren Reed     void *optval, socklen_t *optlenp)
11970a0e9771SDarren Reed {
11980a0e9771SDarren Reed 	struct pfpsock *ps;
1199336069c2SPatrick Mooney 	struct tpacket_stats_short tpss;
12000a0e9771SDarren Reed 	int error = 0;
12010a0e9771SDarren Reed 
12020a0e9771SDarren Reed 	ps = (struct pfpsock *)handle;
12030a0e9771SDarren Reed 
12040a0e9771SDarren Reed 	switch (option_name) {
12050a0e9771SDarren Reed 	case PACKET_STATISTICS :
12060a0e9771SDarren Reed 		if (*optlenp < sizeof (ps->ps_stats)) {
12070a0e9771SDarren Reed 			error = EINVAL;
12080a0e9771SDarren Reed 			break;
12090a0e9771SDarren Reed 		}
12100a0e9771SDarren Reed 		*optlenp = sizeof (ps->ps_stats);
12110a0e9771SDarren Reed 		bcopy(&ps->ps_stats, optval, sizeof (ps->ps_stats));
12120a0e9771SDarren Reed 		break;
1213336069c2SPatrick Mooney 	case PACKET_STATISTICS_SHORT :
1214336069c2SPatrick Mooney 		if (*optlenp < sizeof (tpss)) {
1215336069c2SPatrick Mooney 			error = EINVAL;
1216336069c2SPatrick Mooney 			break;
1217336069c2SPatrick Mooney 		}
1218336069c2SPatrick Mooney 		*optlenp = sizeof (tpss);
1219336069c2SPatrick Mooney 		tpss.tp_packets = ps->ps_stats.tp_packets;
1220336069c2SPatrick Mooney 		tpss.tp_drops = ps->ps_stats.tp_drops;
1221336069c2SPatrick Mooney 		bcopy(&tpss, optval, sizeof (tpss));
1222336069c2SPatrick Mooney 		break;
12230a0e9771SDarren Reed 	default :
12240a0e9771SDarren Reed 		error = EINVAL;
12250a0e9771SDarren Reed 		break;
12260a0e9771SDarren Reed 	}
12270a0e9771SDarren Reed 
12280a0e9771SDarren Reed 	return (error);
12290a0e9771SDarren Reed }
12300a0e9771SDarren Reed 
12310a0e9771SDarren Reed /*
12320a0e9771SDarren Reed  * The SOL_PACKET level for socket options supports three options,
12330a0e9771SDarren Reed  * PACKET_ADD_MEMBERSHIP, PACKET_DROP_MEMBERSHIP and PACKET_AUXDATA.
12340a0e9771SDarren Reed  * This function is responsible for mapping the two socket options
12350a0e9771SDarren Reed  * that manage multicast membership into the appropriate internal
12360a0e9771SDarren Reed  * function calls to bring the option into effect. Whilst direct
12370a0e9771SDarren Reed  * changes to the multicast membership (ADD/DROP) groups is handled
12380a0e9771SDarren Reed  * by calls directly into the mac module, changes to the promiscuos
12390a0e9771SDarren Reed  * mode are vectored through pfp_set_promisc() so that the logic for
12400a0e9771SDarren Reed  * managing the promiscuous mode is in one place.
12410a0e9771SDarren Reed  */
12420a0e9771SDarren Reed /* ARGSUSED */
12430a0e9771SDarren Reed static int
12440a0e9771SDarren Reed pfp_setpacket_sockopt(sock_lower_handle_t handle, int option_name,
12450a0e9771SDarren Reed     const void *optval, socklen_t optlen)
12460a0e9771SDarren Reed {
12470a0e9771SDarren Reed 	struct packet_mreq mreq;
12480a0e9771SDarren Reed 	struct pfpsock *ps;
12490a0e9771SDarren Reed 	int error = 0;
12500a0e9771SDarren Reed 	int opt;
12510a0e9771SDarren Reed 
12520a0e9771SDarren Reed 	ps = (struct pfpsock *)handle;
12530a0e9771SDarren Reed 	if (!ps->ps_bound)
12540a0e9771SDarren Reed 		return (EPROTO);
12550a0e9771SDarren Reed 
12560a0e9771SDarren Reed 	if ((option_name == PACKET_ADD_MEMBERSHIP) ||
12570a0e9771SDarren Reed 	    (option_name == PACKET_DROP_MEMBERSHIP)) {
12580a0e9771SDarren Reed 		if (!ps->ps_bound)
12590a0e9771SDarren Reed 			return (EPROTO);
12600a0e9771SDarren Reed 		bcopy(optval, &mreq, sizeof (mreq));
12610a0e9771SDarren Reed 		if (ps->ps_linkid != mreq.mr_ifindex)
12620a0e9771SDarren Reed 			return (EINVAL);
12630a0e9771SDarren Reed 	}
12640a0e9771SDarren Reed 
12650a0e9771SDarren Reed 	switch (option_name) {
12660a0e9771SDarren Reed 	case PACKET_ADD_MEMBERSHIP :
12670a0e9771SDarren Reed 		switch (mreq.mr_type) {
12680a0e9771SDarren Reed 		case PACKET_MR_MULTICAST :
1269*3b4315d3SPatrick Mooney 			if (mreq.mr_alen != ps->ps_sock.sll_halen)
1270a6911619SDarren Reed 				return (EINVAL);
1271a6911619SDarren Reed 
12720a0e9771SDarren Reed 			error = mac_multicast_add(ps->ps_mch, mreq.mr_address);
12730a0e9771SDarren Reed 			break;
12740a0e9771SDarren Reed 
12750a0e9771SDarren Reed 		case PACKET_MR_PROMISC :
12760a0e9771SDarren Reed 			error = pfp_set_promisc(ps, MAC_CLIENT_PROMISC_ALL);
12770a0e9771SDarren Reed 			break;
12780a0e9771SDarren Reed 
12790a0e9771SDarren Reed 		case PACKET_MR_ALLMULTI :
12800a0e9771SDarren Reed 			error = pfp_set_promisc(ps, MAC_CLIENT_PROMISC_MULTI);
12810a0e9771SDarren Reed 			break;
12820a0e9771SDarren Reed 		}
12830a0e9771SDarren Reed 		break;
12840a0e9771SDarren Reed 
12850a0e9771SDarren Reed 	case PACKET_DROP_MEMBERSHIP :
12860a0e9771SDarren Reed 		switch (mreq.mr_type) {
12870a0e9771SDarren Reed 		case PACKET_MR_MULTICAST :
1288*3b4315d3SPatrick Mooney 			if (mreq.mr_alen != ps->ps_sock.sll_halen)
1289a6911619SDarren Reed 				return (EINVAL);
1290a6911619SDarren Reed 
12910a0e9771SDarren Reed 			mac_multicast_remove(ps->ps_mch, mreq.mr_address);
12920a0e9771SDarren Reed 			break;
12930a0e9771SDarren Reed 
12940a0e9771SDarren Reed 		case PACKET_MR_PROMISC :
12950a0e9771SDarren Reed 			if (ps->ps_promisc != MAC_CLIENT_PROMISC_ALL)
12960a0e9771SDarren Reed 				return (EINVAL);
12970a0e9771SDarren Reed 			error = pfp_set_promisc(ps,
12980a0e9771SDarren Reed 			    MAC_CLIENT_PROMISC_FILTERED);
12990a0e9771SDarren Reed 			break;
13000a0e9771SDarren Reed 
13010a0e9771SDarren Reed 		case PACKET_MR_ALLMULTI :
13020a0e9771SDarren Reed 			if (ps->ps_promisc != MAC_CLIENT_PROMISC_MULTI)
13030a0e9771SDarren Reed 				return (EINVAL);
13040a0e9771SDarren Reed 			error = pfp_set_promisc(ps,
13050a0e9771SDarren Reed 			    MAC_CLIENT_PROMISC_FILTERED);
13060a0e9771SDarren Reed 			break;
13070a0e9771SDarren Reed 		}
13080a0e9771SDarren Reed 		break;
13090a0e9771SDarren Reed 
13100a0e9771SDarren Reed 	case PACKET_AUXDATA :
13110a0e9771SDarren Reed 		if (optlen == sizeof (int)) {
13120a0e9771SDarren Reed 			opt = *(int *)optval;
13130a0e9771SDarren Reed 			ps->ps_auxdata = (opt != 0);
13140a0e9771SDarren Reed 		} else {
13150a0e9771SDarren Reed 			error = EINVAL;
13160a0e9771SDarren Reed 		}
13170a0e9771SDarren Reed 		break;
13180a0e9771SDarren Reed 	default :
13190a0e9771SDarren Reed 		error = EINVAL;
13200a0e9771SDarren Reed 		break;
13210a0e9771SDarren Reed 	}
13220a0e9771SDarren Reed 
13230a0e9771SDarren Reed 	return (error);
13240a0e9771SDarren Reed }
13250a0e9771SDarren Reed 
13260a0e9771SDarren Reed /*
13270a0e9771SDarren Reed  * There are only two special setsockopt's for SOL_SOCKET with PF_PACKET:
1328336069c2SPatrick Mooney  * SO_ATTACH_FILTER and SO_DETACH_FILTER.
13290a0e9771SDarren Reed  *
13300a0e9771SDarren Reed  * Both of these setsockopt values are candidates for being handled by the
13310a0e9771SDarren Reed  * socket layer itself in future, however this requires understanding how
13320a0e9771SDarren Reed  * they would interact with all other sockets.
13330a0e9771SDarren Reed  */
13340a0e9771SDarren Reed static int
13350a0e9771SDarren Reed pfp_setsocket_sockopt(sock_lower_handle_t handle, int option_name,
13360a0e9771SDarren Reed     const void *optval, socklen_t optlen)
13370a0e9771SDarren Reed {
13380a0e9771SDarren Reed 	struct bpf_program prog;
13390a0e9771SDarren Reed 	struct bpf_insn *fcode;
13400a0e9771SDarren Reed 	struct pfpsock *ps;
1341336069c2SPatrick Mooney 	struct sock_proto_props sopp;
13420a0e9771SDarren Reed 	int error = 0;
13430a0e9771SDarren Reed 	int size;
13440a0e9771SDarren Reed 
13450a0e9771SDarren Reed 	ps = (struct pfpsock *)handle;
13460a0e9771SDarren Reed 
13470a0e9771SDarren Reed 	switch (option_name) {
13480a0e9771SDarren Reed 	case SO_ATTACH_FILTER :
13490a0e9771SDarren Reed #ifdef _LP64
13500a0e9771SDarren Reed 		if (optlen == sizeof (struct bpf_program32)) {
13510a0e9771SDarren Reed 			struct bpf_program32 prog32;
13520a0e9771SDarren Reed 
13530a0e9771SDarren Reed 			bcopy(optval, &prog32, sizeof (prog32));
13540a0e9771SDarren Reed 			prog.bf_len = prog32.bf_len;
13550a0e9771SDarren Reed 			prog.bf_insns = (void *)(uint64_t)prog32.bf_insns;
13560a0e9771SDarren Reed 		} else
13570a0e9771SDarren Reed #endif
13580a0e9771SDarren Reed 		if (optlen == sizeof (struct bpf_program)) {
13590a0e9771SDarren Reed 			bcopy(optval, &prog, sizeof (prog));
13600a0e9771SDarren Reed 		} else if (optlen != sizeof (struct bpf_program)) {
13610a0e9771SDarren Reed 			return (EINVAL);
13620a0e9771SDarren Reed 		}
1363336069c2SPatrick Mooney 		if (prog.bf_len > BPF_MAXINSNS)
1364336069c2SPatrick Mooney 			return (EINVAL);
13650a0e9771SDarren Reed 
13660a0e9771SDarren Reed 		size = prog.bf_len * sizeof (*prog.bf_insns);
13670a0e9771SDarren Reed 		fcode = kmem_alloc(size, KM_SLEEP);
13680a0e9771SDarren Reed 		if (ddi_copyin(prog.bf_insns, fcode, size, 0) != 0) {
13690a0e9771SDarren Reed 			kmem_free(fcode, size);
13700a0e9771SDarren Reed 			return (EFAULT);
13710a0e9771SDarren Reed 		}
13720a0e9771SDarren Reed 
13730a0e9771SDarren Reed 		if (bpf_validate(fcode, (int)prog.bf_len)) {
13740a0e9771SDarren Reed 			rw_enter(&ps->ps_bpflock, RW_WRITER);
13750a0e9771SDarren Reed 			pfp_release_bpf(ps);
13760a0e9771SDarren Reed 			ps->ps_bpf.bf_insns = fcode;
13770a0e9771SDarren Reed 			ps->ps_bpf.bf_len = size;
13780a0e9771SDarren Reed 			rw_exit(&ps->ps_bpflock);
13790a0e9771SDarren Reed 
13800a0e9771SDarren Reed 			return (0);
13810a0e9771SDarren Reed 		}
13820a0e9771SDarren Reed 		kmem_free(fcode, size);
13830a0e9771SDarren Reed 		error = EINVAL;
13840a0e9771SDarren Reed 		break;
13850a0e9771SDarren Reed 
13860a0e9771SDarren Reed 	case SO_DETACH_FILTER :
13870a0e9771SDarren Reed 		pfp_release_bpf(ps);
13880a0e9771SDarren Reed 		break;
1389336069c2SPatrick Mooney 
1390336069c2SPatrick Mooney 	case SO_RCVBUF :
1391336069c2SPatrick Mooney 		size = *(int32_t *)optval;
1392336069c2SPatrick Mooney 		if (size > sockmod_pfp_rcvbuf_max || size < 0)
1393336069c2SPatrick Mooney 			return (ENOBUFS);
1394336069c2SPatrick Mooney 		sopp.sopp_flags = SOCKOPT_RCVHIWAT;
1395336069c2SPatrick Mooney 		sopp.sopp_rxhiwat = size;
1396336069c2SPatrick Mooney 		ps->ps_upcalls->su_set_proto_props(ps->ps_upper, &sopp);
1397336069c2SPatrick Mooney 		ps->ps_rcvbuf = size;
1398336069c2SPatrick Mooney 		break;
1399336069c2SPatrick Mooney 
14000a0e9771SDarren Reed 	default :
14010a0e9771SDarren Reed 		error = ENOPROTOOPT;
14020a0e9771SDarren Reed 		break;
14030a0e9771SDarren Reed 	}
14040a0e9771SDarren Reed 
14050a0e9771SDarren Reed 	return (error);
14060a0e9771SDarren Reed }
14070a0e9771SDarren Reed 
14080a0e9771SDarren Reed /*
14090a0e9771SDarren Reed  * pfp_open_index is an internal function used to open a MAC device by
14100a0e9771SDarren Reed  * its index. Both a mac_handle_t and mac_client_handle_t are acquired
14110a0e9771SDarren Reed  * because some of the interfaces provided by the mac layer require either
14120a0e9771SDarren Reed  * only the mac_handle_t or both it and mac_handle_t.
14130a0e9771SDarren Reed  *
14140a0e9771SDarren Reed  * Whilst inside the kernel we can access data structures supporting any
14150a0e9771SDarren Reed  * zone, access to interfaces from non-global zones is restricted to those
14160a0e9771SDarren Reed  * interfaces (if any) that are exclusively assigned to a zone.
14170a0e9771SDarren Reed  */
14180a0e9771SDarren Reed static int
14190a0e9771SDarren Reed pfp_open_index(int index, mac_handle_t *mhp, mac_client_handle_t *mcip,
14200a0e9771SDarren Reed     cred_t *cred)
14210a0e9771SDarren Reed {
14220a0e9771SDarren Reed 	mac_client_handle_t mch;
14230a0e9771SDarren Reed 	zoneid_t ifzoneid;
14240a0e9771SDarren Reed 	mac_handle_t mh;
14250a0e9771SDarren Reed 	zoneid_t zoneid;
14260a0e9771SDarren Reed 	int error;
14270a0e9771SDarren Reed 
14280a0e9771SDarren Reed 	mh = 0;
14290a0e9771SDarren Reed 	mch = 0;
14300a0e9771SDarren Reed 	error = mac_open_by_linkid(index, &mh);
14310a0e9771SDarren Reed 	if (error != 0)
14320a0e9771SDarren Reed 		goto bad_open;
14330a0e9771SDarren Reed 
14340a0e9771SDarren Reed 	error = mac_client_open(mh, &mch, NULL,
14350a0e9771SDarren Reed 	    MAC_OPEN_FLAGS_USE_DATALINK_NAME);
14360a0e9771SDarren Reed 	if (error != 0)
14370a0e9771SDarren Reed 		goto bad_open;
14380a0e9771SDarren Reed 
14390a0e9771SDarren Reed 	zoneid = crgetzoneid(cred);
14400a0e9771SDarren Reed 	if (zoneid != GLOBAL_ZONEID) {
14410a0e9771SDarren Reed 		mac_perim_handle_t perim;
14420a0e9771SDarren Reed 
14430a0e9771SDarren Reed 		mac_perim_enter_by_mh(mh, &perim);
1444336069c2SPatrick Mooney 		error = dls_link_getzid(mac_name(mh), &ifzoneid);
14450a0e9771SDarren Reed 		mac_perim_exit(perim);
14460a0e9771SDarren Reed 		if (error != 0)
14470a0e9771SDarren Reed 			goto bad_open;
14480a0e9771SDarren Reed 		if (ifzoneid != zoneid) {
14490a0e9771SDarren Reed 			error = EACCES;
14500a0e9771SDarren Reed 			goto bad_open;
14510a0e9771SDarren Reed 		}
14520a0e9771SDarren Reed 	}
14530a0e9771SDarren Reed 
14540a0e9771SDarren Reed 	*mcip = mch;
14550a0e9771SDarren Reed 	*mhp = mh;
14560a0e9771SDarren Reed 
14570a0e9771SDarren Reed 	return (0);
14580a0e9771SDarren Reed bad_open:
14590a0e9771SDarren Reed 	if (mch != 0)
14600a0e9771SDarren Reed 		mac_client_close(mch, 0);
14610a0e9771SDarren Reed 	if (mh != 0)
14620a0e9771SDarren Reed 		mac_close(mh);
14630a0e9771SDarren Reed 	return (error);
14640a0e9771SDarren Reed }
14650a0e9771SDarren Reed 
14660a0e9771SDarren Reed static void
14670a0e9771SDarren Reed pfp_close(mac_handle_t mh, mac_client_handle_t mch)
14680a0e9771SDarren Reed {
14690a0e9771SDarren Reed 	mac_client_close(mch, 0);
14700a0e9771SDarren Reed 	mac_close(mh);
14710a0e9771SDarren Reed }
14720a0e9771SDarren Reed 
14730a0e9771SDarren Reed /*
14740a0e9771SDarren Reed  * The purpose of this function is to provide a single place where we free
14750a0e9771SDarren Reed  * the loaded BPF program and reset all pointers/counters associated with
14760a0e9771SDarren Reed  * it.
14770a0e9771SDarren Reed  */
14780a0e9771SDarren Reed static void
14790a0e9771SDarren Reed pfp_release_bpf(struct pfpsock *ps)
14800a0e9771SDarren Reed {
14810a0e9771SDarren Reed 	if (ps->ps_bpf.bf_len != 0) {
14820a0e9771SDarren Reed 		kmem_free(ps->ps_bpf.bf_insns, ps->ps_bpf.bf_len);
14830a0e9771SDarren Reed 		ps->ps_bpf.bf_len = 0;
14840a0e9771SDarren Reed 		ps->ps_bpf.bf_insns = NULL;
14850a0e9771SDarren Reed 	}
14860a0e9771SDarren Reed }
14870a0e9771SDarren Reed 
14880a0e9771SDarren Reed /*
14890a0e9771SDarren Reed  * Set the promiscuous mode of a network interface.
14900a0e9771SDarren Reed  * This function only calls the mac layer when there is a change to the
14910a0e9771SDarren Reed  * status of a network interface's promiscous mode. Tracking of how many
14920a0e9771SDarren Reed  * sockets have the network interface in promiscuous mode, and thus the
14930a0e9771SDarren Reed  * control over the physical device's status, is left to the mac layer.
14940a0e9771SDarren Reed  */
14950a0e9771SDarren Reed static int
14960a0e9771SDarren Reed pfp_set_promisc(struct pfpsock *ps, mac_client_promisc_type_t turnon)
14970a0e9771SDarren Reed {
14980a0e9771SDarren Reed 	int error = 0;
14990a0e9771SDarren Reed 	int flags;
15000a0e9771SDarren Reed 
15010a0e9771SDarren Reed 	/*
15020a0e9771SDarren Reed 	 * There are 4 combinations of turnon/ps_promisc.
15030a0e9771SDarren Reed 	 * This if handles 2 (both false, both true) and the if() below
15040a0e9771SDarren Reed 	 * handles the remaining one - when change is required.
15050a0e9771SDarren Reed 	 */
15060a0e9771SDarren Reed 	if (turnon == ps->ps_promisc)
15070a0e9771SDarren Reed 		return (error);
15080a0e9771SDarren Reed 
15090a0e9771SDarren Reed 	if (ps->ps_phd != 0) {
15100a0e9771SDarren Reed 		mac_promisc_remove(ps->ps_phd);
15110a0e9771SDarren Reed 		ps->ps_phd = 0;
15120a0e9771SDarren Reed 
15130a0e9771SDarren Reed 		/*
15140a0e9771SDarren Reed 		 * ps_promisc is set here in case the call to mac_promisc_add
15150a0e9771SDarren Reed 		 * fails: leaving it to indicate that the interface is still
15160a0e9771SDarren Reed 		 * in some sort of promiscuous mode is false.
15170a0e9771SDarren Reed 		 */
15180a0e9771SDarren Reed 		if (ps->ps_promisc != MAC_CLIENT_PROMISC_FILTERED) {
15190a0e9771SDarren Reed 			ps->ps_promisc = MAC_CLIENT_PROMISC_FILTERED;
15200a0e9771SDarren Reed 			flags = MAC_PROMISC_FLAGS_NO_PHYS;
15210a0e9771SDarren Reed 		} else {
15220a0e9771SDarren Reed 			flags = 0;
15230a0e9771SDarren Reed 		}
15240a0e9771SDarren Reed 		flags |= MAC_PROMISC_FLAGS_VLAN_TAG_STRIP;
15250a0e9771SDarren Reed 	}
15260a0e9771SDarren Reed 
15270a0e9771SDarren Reed 	error = mac_promisc_add(ps->ps_mch, turnon, pfp_packet, ps,
15280a0e9771SDarren Reed 	    &ps->ps_phd, flags);
15290a0e9771SDarren Reed 	if (error == 0)
15300a0e9771SDarren Reed 		ps->ps_promisc = turnon;
15310a0e9771SDarren Reed 
15320a0e9771SDarren Reed 	return (error);
15330a0e9771SDarren Reed }
15340a0e9771SDarren Reed 
15350a0e9771SDarren Reed /*
15360a0e9771SDarren Reed  * This table maps the MAC types in Solaris to the ARPHRD_* values used
1537a6911619SDarren Reed  * on Linux. This is used with the SIOCGIFHWADDR/SIOCGLIFHWADDR ioctl.
1538a6911619SDarren Reed  *
1539a6911619SDarren Reed  * The symbols in this table are *not* pulled in from <net/if_arp.h>,
1540a6911619SDarren Reed  * they are pulled from <netpacket/packet.h>, thus it acts as a source
1541a6911619SDarren Reed  * of supplementary information to the ARP table.
15420a0e9771SDarren Reed  */
15430a0e9771SDarren Reed static uint_t arphrd_to_dl[][2] = {
15440a0e9771SDarren Reed 	{ ARPHRD_IEEE80211,	DL_WIFI },
1545a6911619SDarren Reed 	{ ARPHRD_TUNNEL,	DL_IPV4 },
1546a6911619SDarren Reed 	{ ARPHRD_TUNNEL,	DL_IPV6 },
1547a6911619SDarren Reed 	{ ARPHRD_TUNNEL,	DL_6TO4 },
1548a6911619SDarren Reed 	{ ARPHRD_AX25,		DL_X25 },
1549a6911619SDarren Reed 	{ ARPHRD_ATM,		DL_ATM },
15500a0e9771SDarren Reed 	{ 0,			0 }
15510a0e9771SDarren Reed };
15520a0e9771SDarren Reed 
15530a0e9771SDarren Reed static int
15540a0e9771SDarren Reed pfp_dl_to_arphrd(int dltype)
15550a0e9771SDarren Reed {
15560a0e9771SDarren Reed 	int i;
15570a0e9771SDarren Reed 
15580a0e9771SDarren Reed 	for (i = 0; arphrd_to_dl[i][0] != 0; i++)
15590a0e9771SDarren Reed 		if (arphrd_to_dl[i][1] == dltype)
15600a0e9771SDarren Reed 			return (arphrd_to_dl[i][0]);
1561a6911619SDarren Reed 	return (arp_hw_type(dltype));
15620a0e9771SDarren Reed }
1563