xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_arp.c (revision 13095:eb7af425d949)
111042SErik.Nordmark@Sun.COM /*
211042SErik.Nordmark@Sun.COM  * CDDL HEADER START
311042SErik.Nordmark@Sun.COM  *
411042SErik.Nordmark@Sun.COM  * The contents of this file are subject to the terms of the
511042SErik.Nordmark@Sun.COM  * Common Development and Distribution License (the "License").
611042SErik.Nordmark@Sun.COM  * You may not use this file except in compliance with the License.
711042SErik.Nordmark@Sun.COM  *
811042SErik.Nordmark@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911042SErik.Nordmark@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011042SErik.Nordmark@Sun.COM  * See the License for the specific language governing permissions
1111042SErik.Nordmark@Sun.COM  * and limitations under the License.
1211042SErik.Nordmark@Sun.COM  *
1311042SErik.Nordmark@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411042SErik.Nordmark@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511042SErik.Nordmark@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611042SErik.Nordmark@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711042SErik.Nordmark@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811042SErik.Nordmark@Sun.COM  *
1911042SErik.Nordmark@Sun.COM  * CDDL HEADER END
2011042SErik.Nordmark@Sun.COM  */
2111042SErik.Nordmark@Sun.COM 
2211042SErik.Nordmark@Sun.COM /*
2312069SPeter.Memishian@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2411042SErik.Nordmark@Sun.COM  */
2511042SErik.Nordmark@Sun.COM 
2611042SErik.Nordmark@Sun.COM #include <inet/ip_arp.h>
2711042SErik.Nordmark@Sun.COM #include <inet/ip_ndp.h>
2811042SErik.Nordmark@Sun.COM #include <net/if_arp.h>
2911042SErik.Nordmark@Sun.COM #include <netinet/if_ether.h>
3011042SErik.Nordmark@Sun.COM #include <sys/strsubr.h>
3111042SErik.Nordmark@Sun.COM #include <inet/ip6.h>
3211042SErik.Nordmark@Sun.COM #include <inet/ip.h>
3311042SErik.Nordmark@Sun.COM #include <inet/ip_ire.h>
3411042SErik.Nordmark@Sun.COM #include <inet/ip_if.h>
3511042SErik.Nordmark@Sun.COM #include <sys/dlpi.h>
3611042SErik.Nordmark@Sun.COM #include <sys/sunddi.h>
3711042SErik.Nordmark@Sun.COM #include <sys/strsun.h>
3811042SErik.Nordmark@Sun.COM #include <sys/sdt.h>
3911042SErik.Nordmark@Sun.COM #include <inet/mi.h>
4011042SErik.Nordmark@Sun.COM #include <inet/arp.h>
4111042SErik.Nordmark@Sun.COM #include <inet/ipdrop.h>
4211042SErik.Nordmark@Sun.COM #include <sys/sockio.h>
4311042SErik.Nordmark@Sun.COM #include <inet/ip_impl.h>
4411042SErik.Nordmark@Sun.COM #include <sys/policy.h>
4511042SErik.Nordmark@Sun.COM 
4611042SErik.Nordmark@Sun.COM #define	ARL_LL_ADDR_OFFSET(arl)	(((arl)->arl_sap_length) < 0 ? \
4711042SErik.Nordmark@Sun.COM 	(sizeof (dl_unitdata_req_t)) : \
4811042SErik.Nordmark@Sun.COM 	((sizeof (dl_unitdata_req_t)) + (ABS((arl)->arl_sap_length))))
4911042SErik.Nordmark@Sun.COM 
5011042SErik.Nordmark@Sun.COM /*
5111042SErik.Nordmark@Sun.COM  * MAC-specific intelligence.  Shouldn't be needed, but the DL_INFO_ACK
5211042SErik.Nordmark@Sun.COM  * doesn't quite do it for us.
5311042SErik.Nordmark@Sun.COM  */
5411042SErik.Nordmark@Sun.COM typedef struct arp_m_s {
5511042SErik.Nordmark@Sun.COM 	t_uscalar_t	arp_mac_type;
5611042SErik.Nordmark@Sun.COM 	uint32_t	arp_mac_arp_hw_type;
5711042SErik.Nordmark@Sun.COM 	t_scalar_t	arp_mac_sap_length;
5811042SErik.Nordmark@Sun.COM 	uint32_t	arp_mac_hw_addr_length;
5911042SErik.Nordmark@Sun.COM } arp_m_t;
6011042SErik.Nordmark@Sun.COM 
6111042SErik.Nordmark@Sun.COM static int arp_close(queue_t *, int);
6211042SErik.Nordmark@Sun.COM static void arp_rput(queue_t *, mblk_t *);
6311042SErik.Nordmark@Sun.COM static void arp_wput(queue_t *, mblk_t *);
6411042SErik.Nordmark@Sun.COM static arp_m_t	*arp_m_lookup(t_uscalar_t mac_type);
6511042SErik.Nordmark@Sun.COM static void arp_notify(ipaddr_t, mblk_t *, uint32_t, ip_recv_attr_t *,
6611042SErik.Nordmark@Sun.COM 	ncec_t *);
6711042SErik.Nordmark@Sun.COM static int arp_output(ill_t *, uint32_t, const uchar_t *, const uchar_t *,
6811042SErik.Nordmark@Sun.COM 	const uchar_t *, const uchar_t *, uchar_t *);
6911042SErik.Nordmark@Sun.COM static int  arp_modclose(arl_t *);
7011042SErik.Nordmark@Sun.COM static void  arp_mod_close_tail(arl_t *);
7111042SErik.Nordmark@Sun.COM static mblk_t *arl_unbind(arl_t *);
7211042SErik.Nordmark@Sun.COM static void arp_process_packet(ill_t *, mblk_t *);
7311042SErik.Nordmark@Sun.COM static void arp_excl(ipsq_t *, queue_t *, mblk_t *, void *);
7411042SErik.Nordmark@Sun.COM static void arp_drop_packet(const char *str, mblk_t *, ill_t *);
7511042SErik.Nordmark@Sun.COM static int arp_open(queue_t *, dev_t *, int, int, cred_t *);
7611042SErik.Nordmark@Sun.COM static int ip_sioctl_ifunitsel_arp(queue_t *, int *);
7711042SErik.Nordmark@Sun.COM static int ip_sioctl_slifname_arp(queue_t *, void *);
7811042SErik.Nordmark@Sun.COM static void arp_dlpi_send(arl_t *, mblk_t *);
7911042SErik.Nordmark@Sun.COM static void arl_defaults_common(arl_t *, mblk_t *);
8011042SErik.Nordmark@Sun.COM static int arp_modopen(queue_t *, dev_t *, int, int, cred_t *);
8111042SErik.Nordmark@Sun.COM static void arp_ifname_notify(arl_t *);
8211042SErik.Nordmark@Sun.COM static void arp_rput_dlpi_writer(ipsq_t *, queue_t *, mblk_t *, void *);
8311042SErik.Nordmark@Sun.COM static arl_t *ill_to_arl(ill_t *);
8411042SErik.Nordmark@Sun.COM 
8511042SErik.Nordmark@Sun.COM #define	DL_PRIM(mp)	(((union DL_primitives *)(mp)->b_rptr)->dl_primitive)
8611042SErik.Nordmark@Sun.COM #define	IS_DLPI_DATA(mp)						\
8711042SErik.Nordmark@Sun.COM 	((DB_TYPE(mp) == M_PROTO) &&					\
8811042SErik.Nordmark@Sun.COM 	MBLKL(mp) >= sizeof (dl_unitdata_ind_t) &&			\
8911042SErik.Nordmark@Sun.COM 	(DL_PRIM(mp) == DL_UNITDATA_IND))
9011042SErik.Nordmark@Sun.COM 
9111042SErik.Nordmark@Sun.COM #define	AR_NOTFOUND	1	/* No matching ace found in cache */
9211042SErik.Nordmark@Sun.COM #define	AR_MERGED	2	/* Matching ace updated (RFC 826 Merge_flag) */
9311042SErik.Nordmark@Sun.COM #define	AR_LOOPBACK	3	/* Our own arp packet was received */
9411042SErik.Nordmark@Sun.COM #define	AR_BOGON	4	/* Another host has our IP addr. */
9511042SErik.Nordmark@Sun.COM #define	AR_FAILED	5	/* Duplicate Address Detection has failed */
9611042SErik.Nordmark@Sun.COM #define	AR_CHANGED	6	/* Address has changed; tell IP (and merged) */
9711042SErik.Nordmark@Sun.COM 
9811042SErik.Nordmark@Sun.COM boolean_t arp_no_defense;
9911042SErik.Nordmark@Sun.COM 
10011042SErik.Nordmark@Sun.COM struct module_info arp_mod_info = {
10111166SNitin.Hande@Sun.COM 	IP_MOD_ID, "arp", 1, INFPSZ, 65536, 1024
10211042SErik.Nordmark@Sun.COM };
10311042SErik.Nordmark@Sun.COM static struct qinit rinit_arp = {
10411042SErik.Nordmark@Sun.COM 	(pfi_t)arp_rput, NULL, arp_open, arp_close, NULL, &arp_mod_info
10511042SErik.Nordmark@Sun.COM };
10611042SErik.Nordmark@Sun.COM static struct qinit winit_arp = {
10711042SErik.Nordmark@Sun.COM 	(pfi_t)arp_wput, NULL, arp_open, arp_close, NULL,
10811042SErik.Nordmark@Sun.COM 	&arp_mod_info
10911042SErik.Nordmark@Sun.COM };
11011042SErik.Nordmark@Sun.COM struct streamtab arpinfo = {
11111042SErik.Nordmark@Sun.COM 	&rinit_arp, &winit_arp
11211042SErik.Nordmark@Sun.COM };
11311042SErik.Nordmark@Sun.COM #define	ARH_FIXED_LEN	8
11411042SErik.Nordmark@Sun.COM #define	AR_LL_HDR_SLACK	32
11511042SErik.Nordmark@Sun.COM 
11611042SErik.Nordmark@Sun.COM /*
11711042SErik.Nordmark@Sun.COM  * pfhooks for ARP.
11811042SErik.Nordmark@Sun.COM  */
11911042SErik.Nordmark@Sun.COM #define	ARP_HOOK_IN(_hook, _event, _ilp, _hdr, _fm, _m, ipst)		\
12011042SErik.Nordmark@Sun.COM 									\
12111042SErik.Nordmark@Sun.COM 	if ((_hook).he_interested) {                       		\
12211042SErik.Nordmark@Sun.COM 		hook_pkt_event_t info;                          	\
12311042SErik.Nordmark@Sun.COM 									\
12411042SErik.Nordmark@Sun.COM 		info.hpe_protocol = ipst->ips_arp_net_data;		\
12511042SErik.Nordmark@Sun.COM 		info.hpe_ifp = _ilp;                       		\
12611042SErik.Nordmark@Sun.COM 		info.hpe_ofp = 0;                       		\
12711042SErik.Nordmark@Sun.COM 		info.hpe_hdr = _hdr;                            	\
12811042SErik.Nordmark@Sun.COM 		info.hpe_mp = &(_fm);                           	\
12911042SErik.Nordmark@Sun.COM 		info.hpe_mb = _m;                               	\
13011042SErik.Nordmark@Sun.COM 		if (hook_run(ipst->ips_arp_net_data->netd_hooks,	\
13111042SErik.Nordmark@Sun.COM 		    _event, (hook_data_t)&info) != 0) {			\
13211042SErik.Nordmark@Sun.COM 			if (_fm != NULL) {                      	\
13311042SErik.Nordmark@Sun.COM 				freemsg(_fm);                   	\
13411042SErik.Nordmark@Sun.COM 				_fm = NULL;                     	\
13511042SErik.Nordmark@Sun.COM 			}                                       	\
13611042SErik.Nordmark@Sun.COM 			_hdr = NULL;                            	\
13711042SErik.Nordmark@Sun.COM 			_m = NULL;                              	\
13811042SErik.Nordmark@Sun.COM 		} else {                                        	\
13911042SErik.Nordmark@Sun.COM 			_hdr = info.hpe_hdr;                    	\
14011042SErik.Nordmark@Sun.COM 			_m = info.hpe_mb;                       	\
14111042SErik.Nordmark@Sun.COM 		}                                               	\
14211042SErik.Nordmark@Sun.COM 	}
14311042SErik.Nordmark@Sun.COM 
14411042SErik.Nordmark@Sun.COM #define	ARP_HOOK_OUT(_hook, _event, _olp, _hdr, _fm, _m, ipst)		\
14511042SErik.Nordmark@Sun.COM 									\
14611042SErik.Nordmark@Sun.COM 	if ((_hook).he_interested) {                       		\
14711042SErik.Nordmark@Sun.COM 		hook_pkt_event_t info;                          	\
14811042SErik.Nordmark@Sun.COM 									\
14911042SErik.Nordmark@Sun.COM 		info.hpe_protocol = ipst->ips_arp_net_data;		\
15011042SErik.Nordmark@Sun.COM 		info.hpe_ifp = 0;                       		\
15111042SErik.Nordmark@Sun.COM 		info.hpe_ofp = _olp;                       		\
15211042SErik.Nordmark@Sun.COM 		info.hpe_hdr = _hdr;                            	\
15311042SErik.Nordmark@Sun.COM 		info.hpe_mp = &(_fm);                           	\
15411042SErik.Nordmark@Sun.COM 		info.hpe_mb = _m;                               	\
15511042SErik.Nordmark@Sun.COM 		if (hook_run(ipst->ips_arp_net_data->netd_hooks,	\
15611042SErik.Nordmark@Sun.COM 		    _event, (hook_data_t)&info) != 0) {			\
15711042SErik.Nordmark@Sun.COM 			if (_fm != NULL) {                      	\
15811042SErik.Nordmark@Sun.COM 				freemsg(_fm);                   	\
15911042SErik.Nordmark@Sun.COM 				_fm = NULL;                     	\
16011042SErik.Nordmark@Sun.COM 			}                                       	\
16111042SErik.Nordmark@Sun.COM 			_hdr = NULL;                            	\
16211042SErik.Nordmark@Sun.COM 			_m = NULL;                              	\
16311042SErik.Nordmark@Sun.COM 		} else {                                        	\
16411042SErik.Nordmark@Sun.COM 			_hdr = info.hpe_hdr;                    	\
16511042SErik.Nordmark@Sun.COM 			_m = info.hpe_mb;                       	\
16611042SErik.Nordmark@Sun.COM 		}                                               	\
16711042SErik.Nordmark@Sun.COM 	}
16811042SErik.Nordmark@Sun.COM 
16911042SErik.Nordmark@Sun.COM static arp_m_t	arp_m_tbl[] = {
17011042SErik.Nordmark@Sun.COM 	{ DL_CSMACD,	ARPHRD_ETHER,	-2,	6},	/* 802.3 */
17111042SErik.Nordmark@Sun.COM 	{ DL_TPB,	ARPHRD_IEEE802,	-2,	6},	/* 802.4 */
17211042SErik.Nordmark@Sun.COM 	{ DL_TPR,	ARPHRD_IEEE802,	-2,	6},	/* 802.5 */
17311042SErik.Nordmark@Sun.COM 	{ DL_METRO,	ARPHRD_IEEE802,	-2,	6},	/* 802.6 */
17411042SErik.Nordmark@Sun.COM 	{ DL_ETHER,	ARPHRD_ETHER,	-2,	6},	/* Ethernet */
17511042SErik.Nordmark@Sun.COM 	{ DL_FDDI,	ARPHRD_ETHER,	-2,	6},	/* FDDI */
17611042SErik.Nordmark@Sun.COM 	{ DL_IB,	ARPHRD_IB,	-2,	20},	/* Infiniband */
17711042SErik.Nordmark@Sun.COM 	{ DL_OTHER,	ARPHRD_ETHER,	-2,	6}	/* unknown */
17811042SErik.Nordmark@Sun.COM };
17911042SErik.Nordmark@Sun.COM 
18011042SErik.Nordmark@Sun.COM static void
arl_refhold_locked(arl_t * arl)18111042SErik.Nordmark@Sun.COM arl_refhold_locked(arl_t *arl)
18211042SErik.Nordmark@Sun.COM {
18311042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&arl->arl_lock));
18411042SErik.Nordmark@Sun.COM 	arl->arl_refcnt++;
18511042SErik.Nordmark@Sun.COM 	ASSERT(arl->arl_refcnt != 0);
18611042SErik.Nordmark@Sun.COM }
18711042SErik.Nordmark@Sun.COM 
18811042SErik.Nordmark@Sun.COM static void
arl_refrele(arl_t * arl)18911042SErik.Nordmark@Sun.COM arl_refrele(arl_t *arl)
19011042SErik.Nordmark@Sun.COM {
19111042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
19211042SErik.Nordmark@Sun.COM 	ASSERT(arl->arl_refcnt != 0);
19311042SErik.Nordmark@Sun.COM 	arl->arl_refcnt--;
19411042SErik.Nordmark@Sun.COM 	if (arl->arl_refcnt > 1) {
19511042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
19611042SErik.Nordmark@Sun.COM 		return;
19711042SErik.Nordmark@Sun.COM 	}
19811042SErik.Nordmark@Sun.COM 
19911042SErik.Nordmark@Sun.COM 	/* ill_close or arp_unbind_complete may be waiting */
20011042SErik.Nordmark@Sun.COM 	cv_broadcast(&arl->arl_cv);
20111042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
20211042SErik.Nordmark@Sun.COM }
20311042SErik.Nordmark@Sun.COM 
20411042SErik.Nordmark@Sun.COM /*
20511042SErik.Nordmark@Sun.COM  * wake up any pending ip ioctls.
20611042SErik.Nordmark@Sun.COM  */
20711042SErik.Nordmark@Sun.COM static void
arp_cmd_done(ill_t * ill,int err,t_uscalar_t lastprim)20811042SErik.Nordmark@Sun.COM arp_cmd_done(ill_t *ill, int err, t_uscalar_t lastprim)
20911042SErik.Nordmark@Sun.COM {
21011042SErik.Nordmark@Sun.COM 	if (lastprim == DL_UNBIND_REQ && ill->ill_replumbing)
21111042SErik.Nordmark@Sun.COM 		arp_replumb_done(ill, 0);
21211042SErik.Nordmark@Sun.COM 	else
21311042SErik.Nordmark@Sun.COM 		arp_bringup_done(ill, err);
21411042SErik.Nordmark@Sun.COM }
21511042SErik.Nordmark@Sun.COM 
21611042SErik.Nordmark@Sun.COM static int
ip_nce_resolve_all(ill_t * ill,uchar_t * src_haddr,uint32_t hlen,const in_addr_t * src_paddr,ncec_t ** sncec,int op)21711042SErik.Nordmark@Sun.COM ip_nce_resolve_all(ill_t *ill, uchar_t *src_haddr, uint32_t hlen,
21811042SErik.Nordmark@Sun.COM     const in_addr_t *src_paddr, ncec_t **sncec, int op)
21911042SErik.Nordmark@Sun.COM {
22011042SErik.Nordmark@Sun.COM 	int retv;
22111042SErik.Nordmark@Sun.COM 	ncec_t *ncec;
22211042SErik.Nordmark@Sun.COM 	boolean_t ll_changed;
22311042SErik.Nordmark@Sun.COM 	uchar_t *lladdr = NULL;
22411042SErik.Nordmark@Sun.COM 	int new_state;
22511042SErik.Nordmark@Sun.COM 
22611042SErik.Nordmark@Sun.COM 	ASSERT(ill != NULL);
22711042SErik.Nordmark@Sun.COM 
22811042SErik.Nordmark@Sun.COM 	ncec = ncec_lookup_illgrp_v4(ill, src_paddr);
22911042SErik.Nordmark@Sun.COM 	*sncec = ncec;
23011042SErik.Nordmark@Sun.COM 
23111042SErik.Nordmark@Sun.COM 	if (ncec == NULL) {
23211042SErik.Nordmark@Sun.COM 		retv = AR_NOTFOUND;
23311042SErik.Nordmark@Sun.COM 		goto done;
23411042SErik.Nordmark@Sun.COM 	}
23511042SErik.Nordmark@Sun.COM 
23611042SErik.Nordmark@Sun.COM 	mutex_enter(&ncec->ncec_lock);
23711042SErik.Nordmark@Sun.COM 	/*
23811042SErik.Nordmark@Sun.COM 	 * IP addr and hardware address match what we already
23911042SErik.Nordmark@Sun.COM 	 * have, then this is a broadcast packet emitted by one of our
24011042SErik.Nordmark@Sun.COM 	 * interfaces, reflected by the switch and received on another
24111042SErik.Nordmark@Sun.COM 	 * interface.  We return AR_LOOPBACK.
24211042SErik.Nordmark@Sun.COM 	 */
24311042SErik.Nordmark@Sun.COM 	lladdr = ncec->ncec_lladdr;
24411042SErik.Nordmark@Sun.COM 	if (NCE_MYADDR(ncec) && hlen == ncec->ncec_ill->ill_phys_addr_length &&
24511042SErik.Nordmark@Sun.COM 	    bcmp(lladdr, src_haddr, hlen) == 0) {
24611042SErik.Nordmark@Sun.COM 		mutex_exit(&ncec->ncec_lock);
24711042SErik.Nordmark@Sun.COM 		retv = AR_LOOPBACK;
24811042SErik.Nordmark@Sun.COM 		goto done;
24911042SErik.Nordmark@Sun.COM 	}
25011042SErik.Nordmark@Sun.COM 	/*
25111042SErik.Nordmark@Sun.COM 	 * If the entry is unverified, then we've just verified that
25211042SErik.Nordmark@Sun.COM 	 * someone else already owns this address, because this is a
25311042SErik.Nordmark@Sun.COM 	 * message with the same protocol address but different
25411042SErik.Nordmark@Sun.COM 	 * hardware address.
25511042SErik.Nordmark@Sun.COM 	 */
25611042SErik.Nordmark@Sun.COM 	if (ncec->ncec_flags & NCE_F_UNVERIFIED) {
25711042SErik.Nordmark@Sun.COM 		mutex_exit(&ncec->ncec_lock);
25811042SErik.Nordmark@Sun.COM 		ncec_delete(ncec);
25911042SErik.Nordmark@Sun.COM 		ncec_refrele(ncec);
26011042SErik.Nordmark@Sun.COM 		*sncec = NULL;
26111042SErik.Nordmark@Sun.COM 		retv = AR_FAILED;
26211042SErik.Nordmark@Sun.COM 		goto done;
26311042SErik.Nordmark@Sun.COM 	}
26411042SErik.Nordmark@Sun.COM 
26511042SErik.Nordmark@Sun.COM 	/*
26611042SErik.Nordmark@Sun.COM 	 * If the IP address matches ours and we're authoritative for
26711042SErik.Nordmark@Sun.COM 	 * this entry, then some other node is using our IP addr, so
26811042SErik.Nordmark@Sun.COM 	 * return AR_BOGON.  Also reset the transmit count to zero so
26911042SErik.Nordmark@Sun.COM 	 * that, if we're currently in initial announcement mode, we
27011042SErik.Nordmark@Sun.COM 	 * switch back to the lazier defense mode.  Knowing that
27111042SErik.Nordmark@Sun.COM 	 * there's at least one duplicate out there, we ought not
27211042SErik.Nordmark@Sun.COM 	 * blindly announce.
27311042SErik.Nordmark@Sun.COM 	 *
27411042SErik.Nordmark@Sun.COM 	 * NCE_F_AUTHORITY is set in one of two ways:
27511042SErik.Nordmark@Sun.COM 	 * 1. /sbin/arp told us so, via the "permanent" flag.
27611042SErik.Nordmark@Sun.COM 	 * 2. This is one of my addresses.
27711042SErik.Nordmark@Sun.COM 	 */
27811042SErik.Nordmark@Sun.COM 	if (ncec->ncec_flags & NCE_F_AUTHORITY) {
27911042SErik.Nordmark@Sun.COM 		ncec->ncec_unsolicit_count = 0;
28011042SErik.Nordmark@Sun.COM 		mutex_exit(&ncec->ncec_lock);
28111042SErik.Nordmark@Sun.COM 		retv = AR_BOGON;
28211042SErik.Nordmark@Sun.COM 		goto done;
28311042SErik.Nordmark@Sun.COM 	}
28411042SErik.Nordmark@Sun.COM 
28511042SErik.Nordmark@Sun.COM 	/*
28611042SErik.Nordmark@Sun.COM 	 * No address conflict was detected, and we are getting
28711042SErik.Nordmark@Sun.COM 	 * ready to update the ncec's hwaddr. The nce MUST NOT be on an
28811042SErik.Nordmark@Sun.COM 	 * under interface, because all dynamic nce's are created on the
28911042SErik.Nordmark@Sun.COM 	 * native interface (in the non-IPMP case) or on the IPMP
29011042SErik.Nordmark@Sun.COM 	 * meta-interface (in the IPMP case)
29111042SErik.Nordmark@Sun.COM 	 */
29211042SErik.Nordmark@Sun.COM 	ASSERT(!IS_UNDER_IPMP(ncec->ncec_ill));
29311042SErik.Nordmark@Sun.COM 
29411042SErik.Nordmark@Sun.COM 	/*
29511042SErik.Nordmark@Sun.COM 	 * update ncec with src_haddr, hlen.
29611042SErik.Nordmark@Sun.COM 	 *
29711042SErik.Nordmark@Sun.COM 	 * We are trying to resolve this ncec_addr/src_paddr and we
29811042SErik.Nordmark@Sun.COM 	 * got a REQUEST/RESPONSE from the ncec_addr/src_paddr.
29911042SErik.Nordmark@Sun.COM 	 * So the new_state is at least "STALE". If, in addition,
30011042SErik.Nordmark@Sun.COM 	 * this a solicited, unicast ARP_RESPONSE, we can transition
30111042SErik.Nordmark@Sun.COM 	 * to REACHABLE.
30211042SErik.Nordmark@Sun.COM 	 */
30311042SErik.Nordmark@Sun.COM 	new_state = ND_STALE;
30411042SErik.Nordmark@Sun.COM 	ip1dbg(("got info for ncec %p from addr %x\n",
30511042SErik.Nordmark@Sun.COM 	    (void *)ncec, *src_paddr));
30611042SErik.Nordmark@Sun.COM 	retv = AR_MERGED;
30711042SErik.Nordmark@Sun.COM 	if (ncec->ncec_state == ND_INCOMPLETE ||
30811042SErik.Nordmark@Sun.COM 	    ncec->ncec_state == ND_INITIAL) {
30911042SErik.Nordmark@Sun.COM 		ll_changed = B_TRUE;
31011042SErik.Nordmark@Sun.COM 	} else {
31111042SErik.Nordmark@Sun.COM 		ll_changed = nce_cmp_ll_addr(ncec, src_haddr, hlen);
31211042SErik.Nordmark@Sun.COM 		if (!ll_changed)
31311042SErik.Nordmark@Sun.COM 			new_state = ND_UNCHANGED;
31411042SErik.Nordmark@Sun.COM 		else
31511042SErik.Nordmark@Sun.COM 			retv = AR_CHANGED;
31611042SErik.Nordmark@Sun.COM 	}
31711042SErik.Nordmark@Sun.COM 	/*
31811042SErik.Nordmark@Sun.COM 	 * We don't have the equivalent of the IPv6 'S' flag indicating
31911042SErik.Nordmark@Sun.COM 	 * a solicited response, so we assume that if we are in
32011042SErik.Nordmark@Sun.COM 	 * INCOMPLETE, or got back an unchanged lladdr in PROBE state,
32111042SErik.Nordmark@Sun.COM 	 * and this is an ARP_RESPONSE, it must be a
32211042SErik.Nordmark@Sun.COM 	 * solicited response allowing us to transtion to REACHABLE.
32311042SErik.Nordmark@Sun.COM 	 */
32411042SErik.Nordmark@Sun.COM 	if (op == ARP_RESPONSE) {
32511042SErik.Nordmark@Sun.COM 		switch (ncec->ncec_state) {
32611042SErik.Nordmark@Sun.COM 		case ND_PROBE:
32711042SErik.Nordmark@Sun.COM 			new_state = (ll_changed ? ND_STALE : ND_REACHABLE);
32811042SErik.Nordmark@Sun.COM 			break;
32911042SErik.Nordmark@Sun.COM 		case ND_INCOMPLETE:
33011042SErik.Nordmark@Sun.COM 			new_state = ND_REACHABLE;
33111042SErik.Nordmark@Sun.COM 			break;
33211042SErik.Nordmark@Sun.COM 		}
33311042SErik.Nordmark@Sun.COM 	}
33411042SErik.Nordmark@Sun.COM 	/*
33511042SErik.Nordmark@Sun.COM 	 * Call nce_update() to refresh fastpath information on any
33611042SErik.Nordmark@Sun.COM 	 * dependent nce_t entries.
33711042SErik.Nordmark@Sun.COM 	 */
33811042SErik.Nordmark@Sun.COM 	nce_update(ncec, new_state, (ll_changed ? src_haddr : NULL));
33911042SErik.Nordmark@Sun.COM 	mutex_exit(&ncec->ncec_lock);
34011042SErik.Nordmark@Sun.COM 	nce_resolv_ok(ncec);
34111042SErik.Nordmark@Sun.COM done:
34211042SErik.Nordmark@Sun.COM 	return (retv);
34311042SErik.Nordmark@Sun.COM }
34411042SErik.Nordmark@Sun.COM 
34511042SErik.Nordmark@Sun.COM /* Find an entry for a particular MAC type in the arp_m_tbl. */
34611042SErik.Nordmark@Sun.COM static arp_m_t	*
arp_m_lookup(t_uscalar_t mac_type)34711042SErik.Nordmark@Sun.COM arp_m_lookup(t_uscalar_t mac_type)
34811042SErik.Nordmark@Sun.COM {
34911042SErik.Nordmark@Sun.COM 	arp_m_t	*arm;
35011042SErik.Nordmark@Sun.COM 
35111042SErik.Nordmark@Sun.COM 	for (arm = arp_m_tbl; arm < A_END(arp_m_tbl); arm++) {
35211042SErik.Nordmark@Sun.COM 		if (arm->arp_mac_type == mac_type)
35311042SErik.Nordmark@Sun.COM 			return (arm);
35411042SErik.Nordmark@Sun.COM 	}
35511042SErik.Nordmark@Sun.COM 	return (NULL);
35611042SErik.Nordmark@Sun.COM }
35711042SErik.Nordmark@Sun.COM 
358*13095SDarren.Reed@Oracle.COM uint32_t
arp_hw_type(t_uscalar_t mactype)35911042SErik.Nordmark@Sun.COM arp_hw_type(t_uscalar_t mactype)
36011042SErik.Nordmark@Sun.COM {
36111042SErik.Nordmark@Sun.COM 	arp_m_t *arm;
36211042SErik.Nordmark@Sun.COM 
36311042SErik.Nordmark@Sun.COM 	if ((arm = arp_m_lookup(mactype)) == NULL)
36411042SErik.Nordmark@Sun.COM 		arm = arp_m_lookup(DL_OTHER);
36511042SErik.Nordmark@Sun.COM 	return (arm->arp_mac_arp_hw_type);
36611042SErik.Nordmark@Sun.COM }
36711042SErik.Nordmark@Sun.COM 
36811042SErik.Nordmark@Sun.COM /*
36911042SErik.Nordmark@Sun.COM  * Called when an DLPI control message has been acked; send down the next
37011042SErik.Nordmark@Sun.COM  * queued message (if any).
37111042SErik.Nordmark@Sun.COM  * The DLPI messages of interest being bind, attach and unbind since
37211042SErik.Nordmark@Sun.COM  * these are the only ones sent by ARP via arp_dlpi_send.
37311042SErik.Nordmark@Sun.COM  */
37411042SErik.Nordmark@Sun.COM static void
arp_dlpi_done(arl_t * arl,ill_t * ill)37511042SErik.Nordmark@Sun.COM arp_dlpi_done(arl_t *arl, ill_t *ill)
37611042SErik.Nordmark@Sun.COM {
37711042SErik.Nordmark@Sun.COM 	mblk_t *mp;
37811042SErik.Nordmark@Sun.COM 	int err;
37911042SErik.Nordmark@Sun.COM 	t_uscalar_t prim;
38011042SErik.Nordmark@Sun.COM 
38111042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
38211042SErik.Nordmark@Sun.COM 	prim = arl->arl_dlpi_pending;
38311042SErik.Nordmark@Sun.COM 
38411042SErik.Nordmark@Sun.COM 	if ((mp = arl->arl_dlpi_deferred) == NULL) {
38511042SErik.Nordmark@Sun.COM 		arl->arl_dlpi_pending = DL_PRIM_INVAL;
38611042SErik.Nordmark@Sun.COM 		if (arl->arl_state_flags & ARL_LL_DOWN)
38711042SErik.Nordmark@Sun.COM 			err = ENETDOWN;
38811042SErik.Nordmark@Sun.COM 		else
38911042SErik.Nordmark@Sun.COM 			err = 0;
39011042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
39111042SErik.Nordmark@Sun.COM 
39211042SErik.Nordmark@Sun.COM 		mutex_enter(&ill->ill_lock);
39311042SErik.Nordmark@Sun.COM 		ill->ill_arl_dlpi_pending = 0;
39411042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_lock);
39511042SErik.Nordmark@Sun.COM 		arp_cmd_done(ill, err, prim);
39611042SErik.Nordmark@Sun.COM 		return;
39711042SErik.Nordmark@Sun.COM 	}
39811042SErik.Nordmark@Sun.COM 
39911042SErik.Nordmark@Sun.COM 	arl->arl_dlpi_deferred = mp->b_next;
40011042SErik.Nordmark@Sun.COM 	mp->b_next = NULL;
40111042SErik.Nordmark@Sun.COM 
40211042SErik.Nordmark@Sun.COM 	ASSERT(DB_TYPE(mp) == M_PROTO || DB_TYPE(mp) == M_PCPROTO);
40311042SErik.Nordmark@Sun.COM 
40411042SErik.Nordmark@Sun.COM 	arl->arl_dlpi_pending = DL_PRIM(mp);
40511042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
40611042SErik.Nordmark@Sun.COM 
40711042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_lock);
40811042SErik.Nordmark@Sun.COM 	ill->ill_arl_dlpi_pending = 1;
40911042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_lock);
41011042SErik.Nordmark@Sun.COM 
41111042SErik.Nordmark@Sun.COM 	putnext(arl->arl_wq, mp);
41211042SErik.Nordmark@Sun.COM }
41311042SErik.Nordmark@Sun.COM 
41411042SErik.Nordmark@Sun.COM /*
41511042SErik.Nordmark@Sun.COM  * This routine is called during module initialization when the DL_INFO_ACK
41611042SErik.Nordmark@Sun.COM  * comes back from the device.	We set up defaults for all the device dependent
41711042SErik.Nordmark@Sun.COM  * doo-dads we are going to need.  This will leave us ready to roll if we are
41811042SErik.Nordmark@Sun.COM  * attempting auto-configuration.  Alternatively, these defaults can be
41911042SErik.Nordmark@Sun.COM  * overridden by initialization procedures possessing higher intelligence.
42011042SErik.Nordmark@Sun.COM  *
42111042SErik.Nordmark@Sun.COM  * Caller will free the mp.
42211042SErik.Nordmark@Sun.COM  */
42311042SErik.Nordmark@Sun.COM static void
arp_ll_set_defaults(arl_t * arl,mblk_t * mp)42411042SErik.Nordmark@Sun.COM arp_ll_set_defaults(arl_t *arl, mblk_t *mp)
42511042SErik.Nordmark@Sun.COM {
42611042SErik.Nordmark@Sun.COM 	arp_m_t		*arm;
42711042SErik.Nordmark@Sun.COM 	dl_info_ack_t	*dlia = (dl_info_ack_t *)mp->b_rptr;
42811042SErik.Nordmark@Sun.COM 
42911042SErik.Nordmark@Sun.COM 	if ((arm = arp_m_lookup(dlia->dl_mac_type)) == NULL)
43011042SErik.Nordmark@Sun.COM 		arm = arp_m_lookup(DL_OTHER);
43111042SErik.Nordmark@Sun.COM 	ASSERT(arm != NULL);
43211042SErik.Nordmark@Sun.COM 
43311042SErik.Nordmark@Sun.COM 	/*
43411042SErik.Nordmark@Sun.COM 	 * We initialize based on parameters in the (currently) not too
43511042SErik.Nordmark@Sun.COM 	 * exhaustive arp_m_tbl.
43611042SErik.Nordmark@Sun.COM 	 */
43711042SErik.Nordmark@Sun.COM 	if (dlia->dl_version == DL_VERSION_2) {
43811042SErik.Nordmark@Sun.COM 		arl->arl_sap_length = dlia->dl_sap_length;
43911042SErik.Nordmark@Sun.COM 		arl->arl_phys_addr_length = dlia->dl_brdcst_addr_length;
44011042SErik.Nordmark@Sun.COM 		if (dlia->dl_provider_style == DL_STYLE2)
44111042SErik.Nordmark@Sun.COM 			arl->arl_needs_attach = 1;
44211042SErik.Nordmark@Sun.COM 	} else {
44311042SErik.Nordmark@Sun.COM 		arl->arl_sap_length = arm->arp_mac_sap_length;
44411042SErik.Nordmark@Sun.COM 		arl->arl_phys_addr_length = arm->arp_mac_hw_addr_length;
44511042SErik.Nordmark@Sun.COM 	}
44611042SErik.Nordmark@Sun.COM 	/*
44711042SErik.Nordmark@Sun.COM 	 * Note: the arp_hw_type in the arp header may be derived from
44811042SErik.Nordmark@Sun.COM 	 * the ill_mac_type and arp_m_lookup().
44911042SErik.Nordmark@Sun.COM 	 */
45011042SErik.Nordmark@Sun.COM 	arl->arl_sap = ETHERTYPE_ARP;
45111042SErik.Nordmark@Sun.COM 	arl_defaults_common(arl, mp);
45211042SErik.Nordmark@Sun.COM }
45311042SErik.Nordmark@Sun.COM 
45411042SErik.Nordmark@Sun.COM static void
arp_wput(queue_t * q,mblk_t * mp)45511042SErik.Nordmark@Sun.COM arp_wput(queue_t *q, mblk_t *mp)
45611042SErik.Nordmark@Sun.COM {
45711042SErik.Nordmark@Sun.COM 	int err = EINVAL;
45811042SErik.Nordmark@Sun.COM 	struct iocblk *ioc;
45911042SErik.Nordmark@Sun.COM 	mblk_t *mp1;
46011042SErik.Nordmark@Sun.COM 
46111042SErik.Nordmark@Sun.COM 	switch (DB_TYPE(mp)) {
46211042SErik.Nordmark@Sun.COM 	case M_IOCTL:
46311042SErik.Nordmark@Sun.COM 		ASSERT(q->q_next != NULL);
46411042SErik.Nordmark@Sun.COM 		ioc = (struct iocblk *)mp->b_rptr;
46511042SErik.Nordmark@Sun.COM 		if (ioc->ioc_cmd != SIOCSLIFNAME &&
46611042SErik.Nordmark@Sun.COM 		    ioc->ioc_cmd != IF_UNITSEL) {
46711042SErik.Nordmark@Sun.COM 			DTRACE_PROBE4(arl__dlpi, char *, "arp_wput",
46811042SErik.Nordmark@Sun.COM 			    char *, "<some ioctl>", char *, "-",
46911042SErik.Nordmark@Sun.COM 			    arl_t *, (arl_t *)q->q_ptr);
47011042SErik.Nordmark@Sun.COM 			putnext(q, mp);
47111042SErik.Nordmark@Sun.COM 			return;
47211042SErik.Nordmark@Sun.COM 		}
47311042SErik.Nordmark@Sun.COM 		if ((mp1 = mp->b_cont) == 0)
47411042SErik.Nordmark@Sun.COM 			err = EINVAL;
47511042SErik.Nordmark@Sun.COM 		else if (ioc->ioc_cmd == SIOCSLIFNAME)
47611042SErik.Nordmark@Sun.COM 			err = ip_sioctl_slifname_arp(q, mp1->b_rptr);
47711042SErik.Nordmark@Sun.COM 		else if (ioc->ioc_cmd == IF_UNITSEL)
47811042SErik.Nordmark@Sun.COM 			err = ip_sioctl_ifunitsel_arp(q, (int *)mp1->b_rptr);
47911042SErik.Nordmark@Sun.COM 		if (err == 0)
48011042SErik.Nordmark@Sun.COM 			miocack(q, mp, 0, 0);
48111042SErik.Nordmark@Sun.COM 		else
48211042SErik.Nordmark@Sun.COM 			miocnak(q, mp, 0, err);
48311042SErik.Nordmark@Sun.COM 		return;
48411042SErik.Nordmark@Sun.COM 	default:
48511042SErik.Nordmark@Sun.COM 		DTRACE_PROBE4(arl__dlpi, char *, "arp_wput default",
48611042SErik.Nordmark@Sun.COM 		    char *, "default mblk", char *, "-",
48711042SErik.Nordmark@Sun.COM 		    arl_t *, (arl_t *)q->q_ptr);
48811042SErik.Nordmark@Sun.COM 		putnext(q, mp);
48911042SErik.Nordmark@Sun.COM 		return;
49011042SErik.Nordmark@Sun.COM 	}
49111042SErik.Nordmark@Sun.COM }
49211042SErik.Nordmark@Sun.COM 
49311042SErik.Nordmark@Sun.COM /*
49411042SErik.Nordmark@Sun.COM  * similar to ill_dlpi_pending(): verify that the received DLPI response
49511042SErik.Nordmark@Sun.COM  * matches the one that is pending for the arl.
49611042SErik.Nordmark@Sun.COM  */
49711042SErik.Nordmark@Sun.COM static boolean_t
arl_dlpi_pending(arl_t * arl,t_uscalar_t prim)49811042SErik.Nordmark@Sun.COM arl_dlpi_pending(arl_t *arl, t_uscalar_t prim)
49911042SErik.Nordmark@Sun.COM {
50011042SErik.Nordmark@Sun.COM 	t_uscalar_t pending;
50111042SErik.Nordmark@Sun.COM 
50211042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
50311042SErik.Nordmark@Sun.COM 	if (arl->arl_dlpi_pending == prim) {
50411042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
50511042SErik.Nordmark@Sun.COM 		return (B_TRUE);
50611042SErik.Nordmark@Sun.COM 	}
50711042SErik.Nordmark@Sun.COM 
50811042SErik.Nordmark@Sun.COM 	if (arl->arl_state_flags & ARL_CONDEMNED) {
50911042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
51011042SErik.Nordmark@Sun.COM 		return (B_FALSE);
51111042SErik.Nordmark@Sun.COM 	}
51211042SErik.Nordmark@Sun.COM 	pending = arl->arl_dlpi_pending;
51311042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
51411042SErik.Nordmark@Sun.COM 
51511042SErik.Nordmark@Sun.COM 	if (pending == DL_PRIM_INVAL) {
51611042SErik.Nordmark@Sun.COM 		ip0dbg(("arl_dlpi_pending unsolicited ack for %s on %s",
51711042SErik.Nordmark@Sun.COM 		    dl_primstr(prim), arl->arl_name));
51811042SErik.Nordmark@Sun.COM 	} else {
51911042SErik.Nordmark@Sun.COM 		ip0dbg(("arl_dlpi_pending ack for %s on %s expect %s",
52011042SErik.Nordmark@Sun.COM 		    dl_primstr(prim), arl->arl_name, dl_primstr(pending)));
52111042SErik.Nordmark@Sun.COM 	}
52211042SErik.Nordmark@Sun.COM 	return (B_FALSE);
52311042SErik.Nordmark@Sun.COM }
52411042SErik.Nordmark@Sun.COM 
52511042SErik.Nordmark@Sun.COM /* DLPI messages, other than DL_UNITDATA_IND are handled here. */
52611042SErik.Nordmark@Sun.COM static void
arp_rput_dlpi(queue_t * q,mblk_t * mp)52711042SErik.Nordmark@Sun.COM arp_rput_dlpi(queue_t *q, mblk_t *mp)
52811042SErik.Nordmark@Sun.COM {
52911042SErik.Nordmark@Sun.COM 	arl_t		*arl = (arl_t *)q->q_ptr;
53011042SErik.Nordmark@Sun.COM 	union DL_primitives *dlp;
53111042SErik.Nordmark@Sun.COM 	t_uscalar_t	prim;
53211042SErik.Nordmark@Sun.COM 	t_uscalar_t	reqprim = DL_PRIM_INVAL;
53311042SErik.Nordmark@Sun.COM 	ill_t		*ill;
53411042SErik.Nordmark@Sun.COM 
53511042SErik.Nordmark@Sun.COM 	if ((mp->b_wptr - mp->b_rptr) < sizeof (dlp->dl_primitive)) {
53611042SErik.Nordmark@Sun.COM 		putnext(q, mp);
53711042SErik.Nordmark@Sun.COM 		return;
53811042SErik.Nordmark@Sun.COM 	}
53911042SErik.Nordmark@Sun.COM 	dlp = (union DL_primitives *)mp->b_rptr;
54011042SErik.Nordmark@Sun.COM 	prim = dlp->dl_primitive;
54111042SErik.Nordmark@Sun.COM 
54211042SErik.Nordmark@Sun.COM 	/*
54311042SErik.Nordmark@Sun.COM 	 * If we received an ACK but didn't send a request for it, then it
54411042SErik.Nordmark@Sun.COM 	 * can't be part of any pending operation; discard up-front.
54511042SErik.Nordmark@Sun.COM 	 */
54611042SErik.Nordmark@Sun.COM 	switch (prim) {
54711042SErik.Nordmark@Sun.COM 	case DL_ERROR_ACK:
54811042SErik.Nordmark@Sun.COM 		/*
54911042SErik.Nordmark@Sun.COM 		 * ce is confused about how DLPI works, so we have to interpret
55011042SErik.Nordmark@Sun.COM 		 * an "error" on DL_NOTIFY_ACK (which we never could have sent)
55111042SErik.Nordmark@Sun.COM 		 * as really meaning an error on DL_NOTIFY_REQ.
55211042SErik.Nordmark@Sun.COM 		 *
55311042SErik.Nordmark@Sun.COM 		 * Note that supporting DL_NOTIFY_REQ is optional, so printing
55411042SErik.Nordmark@Sun.COM 		 * out an error message on the console isn't warranted except
55511042SErik.Nordmark@Sun.COM 		 * for debug.
55611042SErik.Nordmark@Sun.COM 		 */
55711042SErik.Nordmark@Sun.COM 		if (dlp->error_ack.dl_error_primitive == DL_NOTIFY_ACK ||
55811042SErik.Nordmark@Sun.COM 		    dlp->error_ack.dl_error_primitive == DL_NOTIFY_REQ) {
55911042SErik.Nordmark@Sun.COM 			reqprim = DL_NOTIFY_REQ;
56011042SErik.Nordmark@Sun.COM 		} else {
56111042SErik.Nordmark@Sun.COM 			reqprim = dlp->error_ack.dl_error_primitive;
56211042SErik.Nordmark@Sun.COM 		}
56311042SErik.Nordmark@Sun.COM 		break;
56411042SErik.Nordmark@Sun.COM 	case DL_INFO_ACK:
56511042SErik.Nordmark@Sun.COM 		reqprim = DL_INFO_REQ;
56611042SErik.Nordmark@Sun.COM 		break;
56711042SErik.Nordmark@Sun.COM 	case DL_OK_ACK:
56811042SErik.Nordmark@Sun.COM 		reqprim = dlp->ok_ack.dl_correct_primitive;
56911042SErik.Nordmark@Sun.COM 		break;
57011042SErik.Nordmark@Sun.COM 	case DL_BIND_ACK:
57111042SErik.Nordmark@Sun.COM 		reqprim = DL_BIND_REQ;
57211042SErik.Nordmark@Sun.COM 		break;
57311042SErik.Nordmark@Sun.COM 	default:
57411042SErik.Nordmark@Sun.COM 		DTRACE_PROBE2(rput_dl_badprim, arl_t *, arl,
57511042SErik.Nordmark@Sun.COM 		    union DL_primitives *, dlp);
57611042SErik.Nordmark@Sun.COM 		putnext(q, mp);
57711042SErik.Nordmark@Sun.COM 		return;
57811042SErik.Nordmark@Sun.COM 	}
57911042SErik.Nordmark@Sun.COM 	if (reqprim == DL_PRIM_INVAL || !arl_dlpi_pending(arl, reqprim)) {
58011042SErik.Nordmark@Sun.COM 		freemsg(mp);
58111042SErik.Nordmark@Sun.COM 		return;
58211042SErik.Nordmark@Sun.COM 	}
58311042SErik.Nordmark@Sun.COM 	DTRACE_PROBE4(arl__dlpi, char *, "arp_rput_dlpi received",
58411042SErik.Nordmark@Sun.COM 	    char *, dl_primstr(prim), char *, dl_primstr(reqprim),
58511042SErik.Nordmark@Sun.COM 	    arl_t *, arl);
58611042SErik.Nordmark@Sun.COM 
58711042SErik.Nordmark@Sun.COM 	ASSERT(prim != DL_NOTIFY_IND);
58811042SErik.Nordmark@Sun.COM 
58911042SErik.Nordmark@Sun.COM 	ill = arl_to_ill(arl);
59011042SErik.Nordmark@Sun.COM 
59111042SErik.Nordmark@Sun.COM 	switch (reqprim) {
59211042SErik.Nordmark@Sun.COM 	case DL_INFO_REQ:
59311042SErik.Nordmark@Sun.COM 		/*
59411042SErik.Nordmark@Sun.COM 		 * ill has not been set up yet for this case. This is the
59511042SErik.Nordmark@Sun.COM 		 * DL_INFO_ACK for the first DL_INFO_REQ sent from
59611042SErik.Nordmark@Sun.COM 		 * arp_modopen(). There should be no other arl_dlpi_deferred
59711042SErik.Nordmark@Sun.COM 		 * messages pending. We initialize the arl here.
59811042SErik.Nordmark@Sun.COM 		 */
59911042SErik.Nordmark@Sun.COM 		ASSERT(!arl->arl_dlpi_style_set);
60011042SErik.Nordmark@Sun.COM 		ASSERT(arl->arl_dlpi_pending == DL_INFO_REQ);
60111042SErik.Nordmark@Sun.COM 		ASSERT(arl->arl_dlpi_deferred == NULL);
60211042SErik.Nordmark@Sun.COM 		arl->arl_dlpi_pending = DL_PRIM_INVAL;
60311042SErik.Nordmark@Sun.COM 		arp_ll_set_defaults(arl, mp);
60411042SErik.Nordmark@Sun.COM 		freemsg(mp);
60511042SErik.Nordmark@Sun.COM 		return;
60611042SErik.Nordmark@Sun.COM 	case DL_UNBIND_REQ:
60711042SErik.Nordmark@Sun.COM 		mutex_enter(&arl->arl_lock);
60811042SErik.Nordmark@Sun.COM 		arl->arl_state_flags &= ~ARL_DL_UNBIND_IN_PROGRESS;
60911042SErik.Nordmark@Sun.COM 		/*
61011042SErik.Nordmark@Sun.COM 		 * This is not an error, so we don't set ARL_LL_DOWN
61111042SErik.Nordmark@Sun.COM 		 */
61211042SErik.Nordmark@Sun.COM 		arl->arl_state_flags &= ~ARL_LL_UP;
61311042SErik.Nordmark@Sun.COM 		arl->arl_state_flags |= ARL_LL_UNBOUND;
61411042SErik.Nordmark@Sun.COM 		if (arl->arl_state_flags & ARL_CONDEMNED) {
61511042SErik.Nordmark@Sun.COM 			/*
61611042SErik.Nordmark@Sun.COM 			 * if this is part of the unplumb the arl may
61711042SErik.Nordmark@Sun.COM 			 * vaporize any moment after we cv_signal the
61811042SErik.Nordmark@Sun.COM 			 * arl_cv so we reset arl_dlpi_pending here.
61911042SErik.Nordmark@Sun.COM 			 * All other cases (including replumb) will
62011042SErik.Nordmark@Sun.COM 			 * have the arl_dlpi_pending reset in
62111042SErik.Nordmark@Sun.COM 			 * arp_dlpi_done.
62211042SErik.Nordmark@Sun.COM 			 */
62311042SErik.Nordmark@Sun.COM 			arl->arl_dlpi_pending = DL_PRIM_INVAL;
62411042SErik.Nordmark@Sun.COM 		}
62511042SErik.Nordmark@Sun.COM 		cv_signal(&arl->arl_cv);
62611042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
62711042SErik.Nordmark@Sun.COM 		break;
62811042SErik.Nordmark@Sun.COM 	}
62911042SErik.Nordmark@Sun.COM 	if (ill != NULL) {
63011042SErik.Nordmark@Sun.COM 		/*
63111042SErik.Nordmark@Sun.COM 		 * ill ref obtained by arl_to_ill()  will be released
63211042SErik.Nordmark@Sun.COM 		 * by qwriter_ip()
63311042SErik.Nordmark@Sun.COM 		 */
63411042SErik.Nordmark@Sun.COM 		qwriter_ip(ill, ill->ill_wq, mp, arp_rput_dlpi_writer,
63511042SErik.Nordmark@Sun.COM 		    CUR_OP, B_TRUE);
63611042SErik.Nordmark@Sun.COM 		return;
63711042SErik.Nordmark@Sun.COM 	}
63811042SErik.Nordmark@Sun.COM 	freemsg(mp);
63911042SErik.Nordmark@Sun.COM }
64011042SErik.Nordmark@Sun.COM 
64111042SErik.Nordmark@Sun.COM /*
64211042SErik.Nordmark@Sun.COM  * Handling of DLPI messages that require exclusive access to the ipsq.
64311042SErik.Nordmark@Sun.COM  */
64411042SErik.Nordmark@Sun.COM /* ARGSUSED */
64511042SErik.Nordmark@Sun.COM static void
arp_rput_dlpi_writer(ipsq_t * ipsq,queue_t * q,mblk_t * mp,void * dummy_arg)64611042SErik.Nordmark@Sun.COM arp_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
64711042SErik.Nordmark@Sun.COM {
64811042SErik.Nordmark@Sun.COM 	union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr;
64911042SErik.Nordmark@Sun.COM 	ill_t		*ill = (ill_t *)q->q_ptr;
65011042SErik.Nordmark@Sun.COM 	arl_t		*arl = ill_to_arl(ill);
65111042SErik.Nordmark@Sun.COM 
65211042SErik.Nordmark@Sun.COM 	if (arl == NULL) {
65311042SErik.Nordmark@Sun.COM 		/*
65411042SErik.Nordmark@Sun.COM 		 * happens as a result arp_modclose triggering unbind.
65511042SErik.Nordmark@Sun.COM 		 * arp_rput_dlpi will cv_signal the arl_cv and the modclose
65611042SErik.Nordmark@Sun.COM 		 * will complete, but when it does ipsq_exit, the waiting
65711042SErik.Nordmark@Sun.COM 		 * qwriter_ip gets into the ipsq but will find the arl null.
65811042SErik.Nordmark@Sun.COM 		 * There should be no deferred messages in this case, so
65911042SErik.Nordmark@Sun.COM 		 * just complete and exit.
66011042SErik.Nordmark@Sun.COM 		 */
66111042SErik.Nordmark@Sun.COM 		arp_cmd_done(ill, 0, DL_UNBIND_REQ);
66211042SErik.Nordmark@Sun.COM 		freemsg(mp);
66311042SErik.Nordmark@Sun.COM 		return;
66411042SErik.Nordmark@Sun.COM 	}
66511042SErik.Nordmark@Sun.COM 	switch (dlp->dl_primitive) {
66611042SErik.Nordmark@Sun.COM 	case DL_ERROR_ACK:
66711042SErik.Nordmark@Sun.COM 		switch (dlp->error_ack.dl_error_primitive) {
66811042SErik.Nordmark@Sun.COM 		case DL_UNBIND_REQ:
66911042SErik.Nordmark@Sun.COM 			mutex_enter(&arl->arl_lock);
67011042SErik.Nordmark@Sun.COM 			arl->arl_state_flags &= ~ARL_DL_UNBIND_IN_PROGRESS;
67111042SErik.Nordmark@Sun.COM 			arl->arl_state_flags &= ~ARL_LL_UP;
67211042SErik.Nordmark@Sun.COM 			arl->arl_state_flags |= ARL_LL_UNBOUND;
67311042SErik.Nordmark@Sun.COM 			arl->arl_state_flags |= ARL_LL_DOWN;
67411042SErik.Nordmark@Sun.COM 			cv_signal(&arl->arl_cv);
67511042SErik.Nordmark@Sun.COM 			mutex_exit(&arl->arl_lock);
67611042SErik.Nordmark@Sun.COM 			break;
67711042SErik.Nordmark@Sun.COM 		case DL_BIND_REQ:
67811042SErik.Nordmark@Sun.COM 			mutex_enter(&arl->arl_lock);
67911042SErik.Nordmark@Sun.COM 			arl->arl_state_flags &= ~ARL_LL_UP;
68011042SErik.Nordmark@Sun.COM 			arl->arl_state_flags |= ARL_LL_DOWN;
68111042SErik.Nordmark@Sun.COM 			arl->arl_state_flags |= ARL_LL_UNBOUND;
68211042SErik.Nordmark@Sun.COM 			cv_signal(&arl->arl_cv);
68311042SErik.Nordmark@Sun.COM 			mutex_exit(&arl->arl_lock);
68411042SErik.Nordmark@Sun.COM 			break;
68511042SErik.Nordmark@Sun.COM 		case DL_ATTACH_REQ:
68611042SErik.Nordmark@Sun.COM 			break;
68711042SErik.Nordmark@Sun.COM 		default:
68811042SErik.Nordmark@Sun.COM 			/* If it's anything else, we didn't send it. */
68911042SErik.Nordmark@Sun.COM 			arl_refrele(arl);
69011042SErik.Nordmark@Sun.COM 			putnext(q, mp);
69111042SErik.Nordmark@Sun.COM 			return;
69211042SErik.Nordmark@Sun.COM 		}
69311042SErik.Nordmark@Sun.COM 		break;
69411042SErik.Nordmark@Sun.COM 	case DL_OK_ACK:
69511042SErik.Nordmark@Sun.COM 		DTRACE_PROBE4(arl__dlpi, char *, "arp_rput_dlpi_writer ok",
69611042SErik.Nordmark@Sun.COM 		    char *, dl_primstr(dlp->ok_ack.dl_correct_primitive),
69711042SErik.Nordmark@Sun.COM 		    char *, dl_primstr(dlp->ok_ack.dl_correct_primitive),
69811042SErik.Nordmark@Sun.COM 		    arl_t *, arl);
69911042SErik.Nordmark@Sun.COM 		mutex_enter(&arl->arl_lock);
70011042SErik.Nordmark@Sun.COM 		switch (dlp->ok_ack.dl_correct_primitive) {
70111042SErik.Nordmark@Sun.COM 		case DL_UNBIND_REQ:
70211042SErik.Nordmark@Sun.COM 		case DL_ATTACH_REQ:
70311042SErik.Nordmark@Sun.COM 			break;
70411042SErik.Nordmark@Sun.COM 		default:
70511042SErik.Nordmark@Sun.COM 			ip0dbg(("Dropping unrecognized DL_OK_ACK for %s",
70611042SErik.Nordmark@Sun.COM 			    dl_primstr(dlp->ok_ack.dl_correct_primitive)));
70711042SErik.Nordmark@Sun.COM 			mutex_exit(&arl->arl_lock);
70811042SErik.Nordmark@Sun.COM 			arl_refrele(arl);
70911042SErik.Nordmark@Sun.COM 			freemsg(mp);
71011042SErik.Nordmark@Sun.COM 			return;
71111042SErik.Nordmark@Sun.COM 		}
71211042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
71311042SErik.Nordmark@Sun.COM 		break;
71411042SErik.Nordmark@Sun.COM 	case DL_BIND_ACK:
71511042SErik.Nordmark@Sun.COM 		DTRACE_PROBE2(rput_dl_bind, arl_t *, arl,
71611042SErik.Nordmark@Sun.COM 		    dl_bind_ack_t *, &dlp->bind_ack);
71711042SErik.Nordmark@Sun.COM 
71811042SErik.Nordmark@Sun.COM 		mutex_enter(&arl->arl_lock);
71911042SErik.Nordmark@Sun.COM 		ASSERT(arl->arl_state_flags & ARL_LL_BIND_PENDING);
72011042SErik.Nordmark@Sun.COM 		arl->arl_state_flags &=
72111042SErik.Nordmark@Sun.COM 		    ~(ARL_LL_BIND_PENDING|ARL_LL_DOWN|ARL_LL_UNBOUND);
72211042SErik.Nordmark@Sun.COM 		arl->arl_state_flags |= ARL_LL_UP;
72311042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
72411042SErik.Nordmark@Sun.COM 		break;
72511042SErik.Nordmark@Sun.COM 	case DL_UDERROR_IND:
72611042SErik.Nordmark@Sun.COM 		DTRACE_PROBE2(rput_dl_uderror, arl_t *, arl,
72711042SErik.Nordmark@Sun.COM 		    dl_uderror_ind_t *, &dlp->uderror_ind);
72811042SErik.Nordmark@Sun.COM 		arl_refrele(arl);
72911042SErik.Nordmark@Sun.COM 		putnext(q, mp);
73011042SErik.Nordmark@Sun.COM 		return;
73111042SErik.Nordmark@Sun.COM 	default:
73211042SErik.Nordmark@Sun.COM 		DTRACE_PROBE2(rput_dl_badprim, arl_t *, arl,
73311042SErik.Nordmark@Sun.COM 		    union DL_primitives *, dlp);
73411042SErik.Nordmark@Sun.COM 		arl_refrele(arl);
73511042SErik.Nordmark@Sun.COM 		putnext(q, mp);
73611042SErik.Nordmark@Sun.COM 		return;
73711042SErik.Nordmark@Sun.COM 	}
73811042SErik.Nordmark@Sun.COM 	arp_dlpi_done(arl, ill);
73911042SErik.Nordmark@Sun.COM 	arl_refrele(arl);
74011042SErik.Nordmark@Sun.COM 	freemsg(mp);
74111042SErik.Nordmark@Sun.COM }
74211042SErik.Nordmark@Sun.COM 
74311042SErik.Nordmark@Sun.COM void
arp_rput(queue_t * q,mblk_t * mp)74411042SErik.Nordmark@Sun.COM arp_rput(queue_t *q, mblk_t *mp)
74511042SErik.Nordmark@Sun.COM {
74611042SErik.Nordmark@Sun.COM 	arl_t		*arl = q->q_ptr;
74711042SErik.Nordmark@Sun.COM 	boolean_t	need_refrele = B_FALSE;
74811042SErik.Nordmark@Sun.COM 
74911042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
75011042SErik.Nordmark@Sun.COM 	if (((arl->arl_state_flags &
75111042SErik.Nordmark@Sun.COM 	    (ARL_CONDEMNED | ARL_LL_REPLUMBING)) != 0)) {
75211042SErik.Nordmark@Sun.COM 		/*
75311042SErik.Nordmark@Sun.COM 		 * Only allow high priority DLPI messages during unplumb or
75411042SErik.Nordmark@Sun.COM 		 * replumb, and we don't take an arl_refcnt for that case.
75511042SErik.Nordmark@Sun.COM 		 */
75611042SErik.Nordmark@Sun.COM 		if (DB_TYPE(mp) != M_PCPROTO) {
75711042SErik.Nordmark@Sun.COM 			mutex_exit(&arl->arl_lock);
75811042SErik.Nordmark@Sun.COM 			freemsg(mp);
75911042SErik.Nordmark@Sun.COM 			return;
76011042SErik.Nordmark@Sun.COM 		}
76111042SErik.Nordmark@Sun.COM 	} else {
76211042SErik.Nordmark@Sun.COM 		arl_refhold_locked(arl);
76311042SErik.Nordmark@Sun.COM 		need_refrele = B_TRUE;
76411042SErik.Nordmark@Sun.COM 	}
76511042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
76611042SErik.Nordmark@Sun.COM 
76711042SErik.Nordmark@Sun.COM 	switch (DB_TYPE(mp)) {
76811042SErik.Nordmark@Sun.COM 	case M_PCPROTO:
76911042SErik.Nordmark@Sun.COM 	case M_PROTO: {
77011042SErik.Nordmark@Sun.COM 		ill_t *ill;
77111042SErik.Nordmark@Sun.COM 
77211042SErik.Nordmark@Sun.COM 		/*
77311042SErik.Nordmark@Sun.COM 		 * could be one of
77411042SErik.Nordmark@Sun.COM 		 * (i)   real message from the wire, (DLPI_DATA)
77511042SErik.Nordmark@Sun.COM 		 * (ii)  DLPI message
77611042SErik.Nordmark@Sun.COM 		 * Take a ref on the ill associated with this arl to
77711042SErik.Nordmark@Sun.COM 		 * prevent the ill from being unplumbed until this thread
77811042SErik.Nordmark@Sun.COM 		 * is done.
77911042SErik.Nordmark@Sun.COM 		 */
78011042SErik.Nordmark@Sun.COM 		if (IS_DLPI_DATA(mp)) {
78111042SErik.Nordmark@Sun.COM 			ill = arl_to_ill(arl);
78211042SErik.Nordmark@Sun.COM 			if (ill == NULL) {
78311042SErik.Nordmark@Sun.COM 				arp_drop_packet("No ill", mp, ill);
78411042SErik.Nordmark@Sun.COM 				break;
78511042SErik.Nordmark@Sun.COM 			}
78611042SErik.Nordmark@Sun.COM 			arp_process_packet(ill, mp);
78711042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
78811042SErik.Nordmark@Sun.COM 			break;
78911042SErik.Nordmark@Sun.COM 		}
79011042SErik.Nordmark@Sun.COM 		/* Miscellaneous DLPI messages get shuffled off. */
79111042SErik.Nordmark@Sun.COM 		arp_rput_dlpi(q, mp);
79211042SErik.Nordmark@Sun.COM 		break;
79311042SErik.Nordmark@Sun.COM 	}
79411042SErik.Nordmark@Sun.COM 	case M_ERROR:
79511042SErik.Nordmark@Sun.COM 	case M_HANGUP:
79611042SErik.Nordmark@Sun.COM 		if (mp->b_rptr < mp->b_wptr)
79711042SErik.Nordmark@Sun.COM 			arl->arl_error = (int)(*mp->b_rptr & 0xFF);
79811042SErik.Nordmark@Sun.COM 		if (arl->arl_error == 0)
79911042SErik.Nordmark@Sun.COM 			arl->arl_error = ENXIO;
80011042SErik.Nordmark@Sun.COM 		freemsg(mp);
80111042SErik.Nordmark@Sun.COM 		break;
80211042SErik.Nordmark@Sun.COM 	default:
80311042SErik.Nordmark@Sun.COM 		ip1dbg(("arp_rput other db type %x\n", DB_TYPE(mp)));
80411042SErik.Nordmark@Sun.COM 		putnext(q, mp);
80511042SErik.Nordmark@Sun.COM 		break;
80611042SErik.Nordmark@Sun.COM 	}
80711042SErik.Nordmark@Sun.COM 	if (need_refrele)
80811042SErik.Nordmark@Sun.COM 		arl_refrele(arl);
80911042SErik.Nordmark@Sun.COM }
81011042SErik.Nordmark@Sun.COM 
81111042SErik.Nordmark@Sun.COM static void
arp_process_packet(ill_t * ill,mblk_t * mp)81211042SErik.Nordmark@Sun.COM arp_process_packet(ill_t *ill, mblk_t *mp)
81311042SErik.Nordmark@Sun.COM {
81411042SErik.Nordmark@Sun.COM 	mblk_t 		*mp1;
81511042SErik.Nordmark@Sun.COM 	arh_t		*arh;
81611042SErik.Nordmark@Sun.COM 	in_addr_t	src_paddr, dst_paddr;
81711042SErik.Nordmark@Sun.COM 	uint32_t	hlen, plen;
81811042SErik.Nordmark@Sun.COM 	boolean_t	is_probe;
81911042SErik.Nordmark@Sun.COM 	int		op;
82011042SErik.Nordmark@Sun.COM 	ncec_t		*dst_ncec, *src_ncec = NULL;
82111042SErik.Nordmark@Sun.COM 	uchar_t		*src_haddr, *arhp, *dst_haddr, *dp, *sp;
82211042SErik.Nordmark@Sun.COM 	int		err;
82311042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst;
82411042SErik.Nordmark@Sun.COM 	boolean_t	need_ill_refrele = B_FALSE;
82511042SErik.Nordmark@Sun.COM 	nce_t		*nce;
82611042SErik.Nordmark@Sun.COM 	uchar_t		*src_lladdr;
82711042SErik.Nordmark@Sun.COM 	dl_unitdata_ind_t *dlui;
82811042SErik.Nordmark@Sun.COM 	ip_recv_attr_t	iras;
82911042SErik.Nordmark@Sun.COM 
83011042SErik.Nordmark@Sun.COM 	ASSERT(ill != NULL);
83111042SErik.Nordmark@Sun.COM 	if (ill->ill_flags & ILLF_NOARP) {
83211042SErik.Nordmark@Sun.COM 		arp_drop_packet("Interface does not support ARP", mp, ill);
83311042SErik.Nordmark@Sun.COM 		return;
83411042SErik.Nordmark@Sun.COM 	}
83511042SErik.Nordmark@Sun.COM 	ipst = ill->ill_ipst;
83611042SErik.Nordmark@Sun.COM 	/*
83711042SErik.Nordmark@Sun.COM 	 * What we should have at this point is a DL_UNITDATA_IND message
83811042SErik.Nordmark@Sun.COM 	 * followed by an ARP packet.  We do some initial checks and then
83911042SErik.Nordmark@Sun.COM 	 * get to work.
84011042SErik.Nordmark@Sun.COM 	 */
84111042SErik.Nordmark@Sun.COM 	dlui = (dl_unitdata_ind_t *)mp->b_rptr;
84211042SErik.Nordmark@Sun.COM 	if (dlui->dl_group_address == 1) {
84311042SErik.Nordmark@Sun.COM 		/*
84411042SErik.Nordmark@Sun.COM 		 * multicast or broadcast  packet. Only accept on the ipmp
84511042SErik.Nordmark@Sun.COM 		 * nominated interface for multicasts ('cast_ill').
84611042SErik.Nordmark@Sun.COM 		 * If we have no cast_ill we are liberal and accept everything.
84711042SErik.Nordmark@Sun.COM 		 */
84811042SErik.Nordmark@Sun.COM 		if (IS_UNDER_IPMP(ill)) {
84911042SErik.Nordmark@Sun.COM 			/* For an under ill_grp can change under lock */
85011042SErik.Nordmark@Sun.COM 			rw_enter(&ipst->ips_ill_g_lock, RW_READER);
85111042SErik.Nordmark@Sun.COM 			if (!ill->ill_nom_cast && ill->ill_grp != NULL &&
85211042SErik.Nordmark@Sun.COM 			    ill->ill_grp->ig_cast_ill != NULL) {
85311042SErik.Nordmark@Sun.COM 				rw_exit(&ipst->ips_ill_g_lock);
85411042SErik.Nordmark@Sun.COM 				arp_drop_packet("Interface is not nominated "
85511042SErik.Nordmark@Sun.COM 				    "for multicast sends and receives",
85611042SErik.Nordmark@Sun.COM 				    mp, ill);
85711042SErik.Nordmark@Sun.COM 				return;
85811042SErik.Nordmark@Sun.COM 			}
85911042SErik.Nordmark@Sun.COM 			rw_exit(&ipst->ips_ill_g_lock);
86011042SErik.Nordmark@Sun.COM 		}
86111042SErik.Nordmark@Sun.COM 	}
86211042SErik.Nordmark@Sun.COM 	mp1 = mp->b_cont;
86311042SErik.Nordmark@Sun.COM 	if (mp1 == NULL) {
86411042SErik.Nordmark@Sun.COM 		arp_drop_packet("Missing ARP packet", mp, ill);
86511042SErik.Nordmark@Sun.COM 		return;
86611042SErik.Nordmark@Sun.COM 	}
86711042SErik.Nordmark@Sun.COM 	if (mp1->b_cont != NULL) {
86811042SErik.Nordmark@Sun.COM 		/* No fooling around with funny messages. */
86911042SErik.Nordmark@Sun.COM 		if (!pullupmsg(mp1, -1)) {
87011042SErik.Nordmark@Sun.COM 			arp_drop_packet("Funny message: pullup failed",
87111042SErik.Nordmark@Sun.COM 			    mp, ill);
87211042SErik.Nordmark@Sun.COM 			return;
87311042SErik.Nordmark@Sun.COM 		}
87411042SErik.Nordmark@Sun.COM 	}
87511042SErik.Nordmark@Sun.COM 	arh = (arh_t *)mp1->b_rptr;
87611042SErik.Nordmark@Sun.COM 	hlen = arh->arh_hlen;
87711042SErik.Nordmark@Sun.COM 	plen = arh->arh_plen;
87811042SErik.Nordmark@Sun.COM 	if (MBLKL(mp1) < ARH_FIXED_LEN + 2 * hlen + 2 * plen) {
87911042SErik.Nordmark@Sun.COM 		arp_drop_packet("mblk len too small", mp, ill);
88011042SErik.Nordmark@Sun.COM 		return;
88111042SErik.Nordmark@Sun.COM 	}
88211042SErik.Nordmark@Sun.COM 	/*
88311042SErik.Nordmark@Sun.COM 	 * hlen 0 is used for RFC 1868 UnARP.
88411042SErik.Nordmark@Sun.COM 	 *
88511042SErik.Nordmark@Sun.COM 	 * Note that the rest of the code checks that hlen is what we expect
88611042SErik.Nordmark@Sun.COM 	 * for this hardware address type, so might as well discard packets
88711042SErik.Nordmark@Sun.COM 	 * here that don't match.
88811042SErik.Nordmark@Sun.COM 	 */
88911042SErik.Nordmark@Sun.COM 	if ((hlen > 0 && hlen != ill->ill_phys_addr_length) || plen == 0) {
89011042SErik.Nordmark@Sun.COM 		DTRACE_PROBE2(rput_bogus, ill_t *, ill, mblk_t *, mp1);
89111042SErik.Nordmark@Sun.COM 		arp_drop_packet("Bogus hlen or plen", mp, ill);
89211042SErik.Nordmark@Sun.COM 		return;
89311042SErik.Nordmark@Sun.COM 	}
89411042SErik.Nordmark@Sun.COM 	/*
89511042SErik.Nordmark@Sun.COM 	 * Historically, Solaris has been lenient about hardware type numbers.
89611042SErik.Nordmark@Sun.COM 	 * We should check here, but don't.
89711042SErik.Nordmark@Sun.COM 	 */
89811042SErik.Nordmark@Sun.COM 	DTRACE_PROBE3(arp__physical__in__start, ill_t *, ill, arh_t *, arh,
89911042SErik.Nordmark@Sun.COM 	    mblk_t *, mp);
90011042SErik.Nordmark@Sun.COM 	/*
90111042SErik.Nordmark@Sun.COM 	 * If ill is in an ipmp group, it will be the under ill. If we want
90211042SErik.Nordmark@Sun.COM 	 * to report the packet as coming up the IPMP interface, we should
90311042SErik.Nordmark@Sun.COM 	 * convert it to the ipmp ill.
90411042SErik.Nordmark@Sun.COM 	 */
90511042SErik.Nordmark@Sun.COM 	ARP_HOOK_IN(ipst->ips_arp_physical_in_event, ipst->ips_arp_physical_in,
90611042SErik.Nordmark@Sun.COM 	    ill->ill_phyint->phyint_ifindex, arh, mp, mp1, ipst);
90711042SErik.Nordmark@Sun.COM 	DTRACE_PROBE1(arp__physical__in__end, mblk_t *, mp);
90811042SErik.Nordmark@Sun.COM 	if (mp == NULL)
90911042SErik.Nordmark@Sun.COM 		return;
91011042SErik.Nordmark@Sun.COM 	arhp = (uchar_t *)arh + ARH_FIXED_LEN;
91111042SErik.Nordmark@Sun.COM 	src_haddr = arhp;			/* ar$sha */
91211042SErik.Nordmark@Sun.COM 	arhp += hlen;
91311042SErik.Nordmark@Sun.COM 	bcopy(arhp, &src_paddr, IP_ADDR_LEN);	/* ar$spa */
91411042SErik.Nordmark@Sun.COM 	sp = arhp;
91511042SErik.Nordmark@Sun.COM 	arhp += IP_ADDR_LEN;
91611042SErik.Nordmark@Sun.COM 	dst_haddr = arhp;			/* ar$dha */
91711042SErik.Nordmark@Sun.COM 	arhp += hlen;
91811042SErik.Nordmark@Sun.COM 	bcopy(arhp, &dst_paddr, IP_ADDR_LEN);	/* ar$tpa */
91911042SErik.Nordmark@Sun.COM 	dp = arhp;
92011042SErik.Nordmark@Sun.COM 	op = BE16_TO_U16(arh->arh_operation);
92111042SErik.Nordmark@Sun.COM 
92211042SErik.Nordmark@Sun.COM 	DTRACE_PROBE2(ip__arp__input, (in_addr_t), src_paddr,
92311042SErik.Nordmark@Sun.COM 	    (in_addr_t), dst_paddr);
92411042SErik.Nordmark@Sun.COM 
92511042SErik.Nordmark@Sun.COM 	/* Determine if this is just a probe */
92611042SErik.Nordmark@Sun.COM 	is_probe = (src_paddr == INADDR_ANY);
92711042SErik.Nordmark@Sun.COM 
92811042SErik.Nordmark@Sun.COM 	/*
92912038SSowmini.Varadhan@Sun.COM 	 * The following test for loopback is faster than
93012038SSowmini.Varadhan@Sun.COM 	 * IP_LOOPBACK_ADDR(), because it avoids any bitwise
93112038SSowmini.Varadhan@Sun.COM 	 * operations.
93212038SSowmini.Varadhan@Sun.COM 	 * Note that these addresses are always in network byte order
93312038SSowmini.Varadhan@Sun.COM 	 */
93412038SSowmini.Varadhan@Sun.COM 	if ((*(uint8_t *)&src_paddr) == IN_LOOPBACKNET ||
93512038SSowmini.Varadhan@Sun.COM 	    (*(uint8_t *)&dst_paddr) == IN_LOOPBACKNET ||
93612135SSowmini.Varadhan@Sun.COM 	    CLASSD(src_paddr) || CLASSD(dst_paddr)) {
93712038SSowmini.Varadhan@Sun.COM 		arp_drop_packet("Martian IP addr", mp, ill);
93812038SSowmini.Varadhan@Sun.COM 		return;
93912038SSowmini.Varadhan@Sun.COM 	}
94012038SSowmini.Varadhan@Sun.COM 
94112038SSowmini.Varadhan@Sun.COM 	/*
94211042SErik.Nordmark@Sun.COM 	 * ira_ill is the only field used down the arp_notify path.
94311042SErik.Nordmark@Sun.COM 	 */
94411042SErik.Nordmark@Sun.COM 	bzero(&iras, sizeof (iras));
94511042SErik.Nordmark@Sun.COM 	iras.ira_ill = iras.ira_rill = ill;
94611042SErik.Nordmark@Sun.COM 	/*
94711042SErik.Nordmark@Sun.COM 	 * RFC 826: first check if the <protocol, sender protocol address> is
94811042SErik.Nordmark@Sun.COM 	 * in the cache, if there is a sender protocol address.  Note that this
94911042SErik.Nordmark@Sun.COM 	 * step also handles resolutions based on source.
95011042SErik.Nordmark@Sun.COM 	 */
95111042SErik.Nordmark@Sun.COM 	/* Note: after here we need to freeb(mp) and freemsg(mp1) separately */
95211042SErik.Nordmark@Sun.COM 	mp->b_cont = NULL;
95311042SErik.Nordmark@Sun.COM 	if (is_probe) {
95411042SErik.Nordmark@Sun.COM 		err = AR_NOTFOUND;
95511042SErik.Nordmark@Sun.COM 	} else {
95611042SErik.Nordmark@Sun.COM 		if (plen != 4) {
95711042SErik.Nordmark@Sun.COM 			arp_drop_packet("bad protocol len", mp, ill);
95811042SErik.Nordmark@Sun.COM 			return;
95911042SErik.Nordmark@Sun.COM 		}
96011042SErik.Nordmark@Sun.COM 		err = ip_nce_resolve_all(ill, src_haddr, hlen, &src_paddr,
96111042SErik.Nordmark@Sun.COM 		    &src_ncec, op);
96211042SErik.Nordmark@Sun.COM 		switch (err) {
96311042SErik.Nordmark@Sun.COM 		case AR_BOGON:
96411042SErik.Nordmark@Sun.COM 			ASSERT(src_ncec != NULL);
96511042SErik.Nordmark@Sun.COM 			arp_notify(src_paddr, mp1, AR_CN_BOGON,
96611042SErik.Nordmark@Sun.COM 			    &iras, src_ncec);
96711042SErik.Nordmark@Sun.COM 			break;
96811042SErik.Nordmark@Sun.COM 		case AR_FAILED:
96911042SErik.Nordmark@Sun.COM 			arp_notify(src_paddr, mp1, AR_CN_FAILED, &iras,
97011042SErik.Nordmark@Sun.COM 			    src_ncec);
97111042SErik.Nordmark@Sun.COM 			break;
97211042SErik.Nordmark@Sun.COM 		case AR_LOOPBACK:
97311042SErik.Nordmark@Sun.COM 			DTRACE_PROBE2(rput_loopback, ill_t *, ill, arh_t *,
97411042SErik.Nordmark@Sun.COM 			    arh);
97511042SErik.Nordmark@Sun.COM 			freemsg(mp1);
97611042SErik.Nordmark@Sun.COM 			break;
97711042SErik.Nordmark@Sun.COM 		default:
97811042SErik.Nordmark@Sun.COM 			goto update;
97911042SErik.Nordmark@Sun.COM 		}
98011042SErik.Nordmark@Sun.COM 		freemsg(mp);
98111042SErik.Nordmark@Sun.COM 		if (src_ncec != NULL)
98211042SErik.Nordmark@Sun.COM 			ncec_refrele(src_ncec);
98311042SErik.Nordmark@Sun.COM 		return;
98411042SErik.Nordmark@Sun.COM 	}
98511042SErik.Nordmark@Sun.COM update:
98611042SErik.Nordmark@Sun.COM 	/*
98711042SErik.Nordmark@Sun.COM 	 * Now look up the destination address.  By RFC 826, we ignore the
98811042SErik.Nordmark@Sun.COM 	 * packet at this step if the target isn't one of our addresses (i.e.,
98911042SErik.Nordmark@Sun.COM 	 * one we have been asked to PUBLISH).  This is true even if the
99011042SErik.Nordmark@Sun.COM 	 * target is something we're trying to resolve and the packet
99111042SErik.Nordmark@Sun.COM 	 * is a response.
99211042SErik.Nordmark@Sun.COM 	 */
99311042SErik.Nordmark@Sun.COM 	dst_ncec = ncec_lookup_illgrp_v4(ill, &dst_paddr);
99411042SErik.Nordmark@Sun.COM 	if (dst_ncec == NULL || !NCE_PUBLISH(dst_ncec)) {
99511042SErik.Nordmark@Sun.COM 		/*
99611042SErik.Nordmark@Sun.COM 		 * Let the client know if the source mapping has changed, even
99711042SErik.Nordmark@Sun.COM 		 * if the destination provides no useful information for the
99811042SErik.Nordmark@Sun.COM 		 * client.
99911042SErik.Nordmark@Sun.COM 		 */
100011042SErik.Nordmark@Sun.COM 		if (err == AR_CHANGED) {
100111042SErik.Nordmark@Sun.COM 			arp_notify(src_paddr, mp1, AR_CN_ANNOUNCE, &iras,
100211042SErik.Nordmark@Sun.COM 			    NULL);
100311042SErik.Nordmark@Sun.COM 			freemsg(mp);
100411042SErik.Nordmark@Sun.COM 		} else {
100511042SErik.Nordmark@Sun.COM 			freemsg(mp);
100611042SErik.Nordmark@Sun.COM 			arp_drop_packet("Target is not interesting", mp1, ill);
100711042SErik.Nordmark@Sun.COM 		}
100811042SErik.Nordmark@Sun.COM 		if (dst_ncec != NULL)
100911042SErik.Nordmark@Sun.COM 			ncec_refrele(dst_ncec);
101011042SErik.Nordmark@Sun.COM 		if (src_ncec != NULL)
101111042SErik.Nordmark@Sun.COM 			ncec_refrele(src_ncec);
101211042SErik.Nordmark@Sun.COM 		return;
101311042SErik.Nordmark@Sun.COM 	}
101411042SErik.Nordmark@Sun.COM 
101511042SErik.Nordmark@Sun.COM 	if (dst_ncec->ncec_flags & NCE_F_UNVERIFIED) {
101611042SErik.Nordmark@Sun.COM 		/*
101711042SErik.Nordmark@Sun.COM 		 * Check for a reflection.  Some misbehaving bridges will
101811042SErik.Nordmark@Sun.COM 		 * reflect our own transmitted packets back to us.
101911042SErik.Nordmark@Sun.COM 		 */
102011042SErik.Nordmark@Sun.COM 		ASSERT(NCE_PUBLISH(dst_ncec));
102111042SErik.Nordmark@Sun.COM 		if (hlen != dst_ncec->ncec_ill->ill_phys_addr_length) {
102211042SErik.Nordmark@Sun.COM 			ncec_refrele(dst_ncec);
102311042SErik.Nordmark@Sun.COM 			if (src_ncec != NULL)
102411042SErik.Nordmark@Sun.COM 				ncec_refrele(src_ncec);
102511042SErik.Nordmark@Sun.COM 			freemsg(mp);
102611042SErik.Nordmark@Sun.COM 			arp_drop_packet("bad arh_len", mp1, ill);
102711042SErik.Nordmark@Sun.COM 			return;
102811042SErik.Nordmark@Sun.COM 		}
102911042SErik.Nordmark@Sun.COM 		if (!nce_cmp_ll_addr(dst_ncec, src_haddr, hlen)) {
103011042SErik.Nordmark@Sun.COM 			DTRACE_PROBE3(rput_probe_reflected, ill_t *, ill,
103111042SErik.Nordmark@Sun.COM 			    arh_t *, arh, ncec_t *, dst_ncec);
103211042SErik.Nordmark@Sun.COM 			ncec_refrele(dst_ncec);
103311042SErik.Nordmark@Sun.COM 			if (src_ncec != NULL)
103411042SErik.Nordmark@Sun.COM 				ncec_refrele(src_ncec);
103511042SErik.Nordmark@Sun.COM 			freemsg(mp);
103611042SErik.Nordmark@Sun.COM 			arp_drop_packet("Reflected probe", mp1, ill);
103711042SErik.Nordmark@Sun.COM 			return;
103811042SErik.Nordmark@Sun.COM 		}
103911042SErik.Nordmark@Sun.COM 		/*
104011042SErik.Nordmark@Sun.COM 		 * Responses targeting our HW address that are not responses to
104111042SErik.Nordmark@Sun.COM 		 * our DAD probe must be ignored as they are related to requests
104211042SErik.Nordmark@Sun.COM 		 * sent before DAD was restarted.
104311042SErik.Nordmark@Sun.COM 		 */
104411042SErik.Nordmark@Sun.COM 		if (op == ARP_RESPONSE &&
104511042SErik.Nordmark@Sun.COM 		    (nce_cmp_ll_addr(dst_ncec, dst_haddr, hlen) == 0)) {
104611042SErik.Nordmark@Sun.COM 			ncec_refrele(dst_ncec);
104711042SErik.Nordmark@Sun.COM 			if (src_ncec != NULL)
104811042SErik.Nordmark@Sun.COM 				ncec_refrele(src_ncec);
104911042SErik.Nordmark@Sun.COM 			freemsg(mp);
105011042SErik.Nordmark@Sun.COM 			arp_drop_packet(
105111042SErik.Nordmark@Sun.COM 			    "Response to request that was sent before DAD",
105211042SErik.Nordmark@Sun.COM 			    mp1, ill);
105311042SErik.Nordmark@Sun.COM 			return;
105411042SErik.Nordmark@Sun.COM 		}
105511042SErik.Nordmark@Sun.COM 		/*
105611042SErik.Nordmark@Sun.COM 		 * Responses targeted to HW addresses which are not ours but
105711042SErik.Nordmark@Sun.COM 		 * sent to our unverified proto address are also conflicts.
105811042SErik.Nordmark@Sun.COM 		 * These may be reported by a proxy rather than the interface
105911042SErik.Nordmark@Sun.COM 		 * with the conflicting address, dst_paddr is in conflict
106011042SErik.Nordmark@Sun.COM 		 * rather than src_paddr. To ensure IP can locate the correct
106111042SErik.Nordmark@Sun.COM 		 * ipif to take down, it is necessary to copy dst_paddr to
106211042SErik.Nordmark@Sun.COM 		 * the src_paddr field before sending it to IP. The same is
106311042SErik.Nordmark@Sun.COM 		 * required for probes, where src_paddr will be INADDR_ANY.
106411042SErik.Nordmark@Sun.COM 		 */
106511042SErik.Nordmark@Sun.COM 		if (is_probe || op == ARP_RESPONSE) {
106611042SErik.Nordmark@Sun.COM 			bcopy(dp, sp, plen);
106711042SErik.Nordmark@Sun.COM 			arp_notify(src_paddr, mp1, AR_CN_FAILED, &iras,
106811042SErik.Nordmark@Sun.COM 			    NULL);
106911042SErik.Nordmark@Sun.COM 			ncec_delete(dst_ncec);
107011042SErik.Nordmark@Sun.COM 		} else if (err == AR_CHANGED) {
107111042SErik.Nordmark@Sun.COM 			arp_notify(src_paddr, mp1, AR_CN_ANNOUNCE, &iras,
107211042SErik.Nordmark@Sun.COM 			    NULL);
107311042SErik.Nordmark@Sun.COM 		} else {
107411042SErik.Nordmark@Sun.COM 			DTRACE_PROBE3(rput_request_unverified,
107511042SErik.Nordmark@Sun.COM 			    ill_t *, ill, arh_t *, arh, ncec_t *, dst_ncec);
107611042SErik.Nordmark@Sun.COM 			arp_drop_packet("Unverified request", mp1, ill);
107711042SErik.Nordmark@Sun.COM 		}
107811042SErik.Nordmark@Sun.COM 		freemsg(mp);
107911042SErik.Nordmark@Sun.COM 		ncec_refrele(dst_ncec);
108011042SErik.Nordmark@Sun.COM 		if (src_ncec != NULL)
108111042SErik.Nordmark@Sun.COM 			ncec_refrele(src_ncec);
108211042SErik.Nordmark@Sun.COM 		return;
108311042SErik.Nordmark@Sun.COM 	}
108411042SErik.Nordmark@Sun.COM 	/*
108511042SErik.Nordmark@Sun.COM 	 * If it's a request, then we reply to this, and if we think the
108611042SErik.Nordmark@Sun.COM 	 * sender's unknown, then we create an entry to avoid unnecessary ARPs.
108711042SErik.Nordmark@Sun.COM 	 * The design assumption is that someone ARPing us is likely to send us
108811042SErik.Nordmark@Sun.COM 	 * a packet soon, and that we'll want to reply to it.
108911042SErik.Nordmark@Sun.COM 	 */
109011042SErik.Nordmark@Sun.COM 	if (op == ARP_REQUEST) {
109111042SErik.Nordmark@Sun.COM 		const uchar_t *nce_hwaddr;
109211042SErik.Nordmark@Sun.COM 		struct in_addr nce_paddr;
109311042SErik.Nordmark@Sun.COM 		clock_t now;
109411042SErik.Nordmark@Sun.COM 		ill_t *under_ill = ill;
109511042SErik.Nordmark@Sun.COM 		boolean_t send_unicast = B_TRUE;
109611042SErik.Nordmark@Sun.COM 
109711042SErik.Nordmark@Sun.COM 		ASSERT(NCE_PUBLISH(dst_ncec));
109811042SErik.Nordmark@Sun.COM 
109911042SErik.Nordmark@Sun.COM 		if ((dst_ncec->ncec_flags & (NCE_F_BCAST|NCE_F_MCAST)) != 0) {
110011042SErik.Nordmark@Sun.COM 			/*
110111042SErik.Nordmark@Sun.COM 			 * Ignore senders who are deliberately or accidentally
110211042SErik.Nordmark@Sun.COM 			 * confused.
110311042SErik.Nordmark@Sun.COM 			 */
110411042SErik.Nordmark@Sun.COM 			goto bail;
110511042SErik.Nordmark@Sun.COM 		}
110611042SErik.Nordmark@Sun.COM 
110711042SErik.Nordmark@Sun.COM 		if (!is_probe && err == AR_NOTFOUND) {
110811042SErik.Nordmark@Sun.COM 			ASSERT(src_ncec == NULL);
110911042SErik.Nordmark@Sun.COM 
111011042SErik.Nordmark@Sun.COM 			if (IS_UNDER_IPMP(under_ill)) {
111111042SErik.Nordmark@Sun.COM 				/*
111211042SErik.Nordmark@Sun.COM 				 * create the ncec for the sender on ipmp_ill.
111311042SErik.Nordmark@Sun.COM 				 * We pass in the ipmp_ill itself to avoid
111411042SErik.Nordmark@Sun.COM 				 * creating an nce_t on the under_ill.
111511042SErik.Nordmark@Sun.COM 				 */
111611042SErik.Nordmark@Sun.COM 				ill = ipmp_ill_hold_ipmp_ill(under_ill);
111711042SErik.Nordmark@Sun.COM 				if (ill == NULL)
111811042SErik.Nordmark@Sun.COM 					ill = under_ill;
111911042SErik.Nordmark@Sun.COM 				else
112011042SErik.Nordmark@Sun.COM 					need_ill_refrele = B_TRUE;
112111042SErik.Nordmark@Sun.COM 			}
112211042SErik.Nordmark@Sun.COM 
112311042SErik.Nordmark@Sun.COM 			err = nce_lookup_then_add_v4(ill, src_haddr, hlen,
112411042SErik.Nordmark@Sun.COM 			    &src_paddr, 0, ND_STALE, &nce);
112511042SErik.Nordmark@Sun.COM 
112611042SErik.Nordmark@Sun.COM 			switch (err) {
112711042SErik.Nordmark@Sun.COM 			case 0:
112811042SErik.Nordmark@Sun.COM 			case EEXIST:
112911042SErik.Nordmark@Sun.COM 				ip1dbg(("added ncec %p in state %d ill %s\n",
113011042SErik.Nordmark@Sun.COM 				    (void *)src_ncec, src_ncec->ncec_state,
113111042SErik.Nordmark@Sun.COM 				    ill->ill_name));
113211042SErik.Nordmark@Sun.COM 				src_ncec = nce->nce_common;
113311042SErik.Nordmark@Sun.COM 				break;
113411042SErik.Nordmark@Sun.COM 			default:
113511042SErik.Nordmark@Sun.COM 				/*
113611042SErik.Nordmark@Sun.COM 				 * Either no memory, or the outgoing interface
113711042SErik.Nordmark@Sun.COM 				 * is in the process of down/unplumb. In the
113811042SErik.Nordmark@Sun.COM 				 * latter case, we will fail the send anyway,
113911042SErik.Nordmark@Sun.COM 				 * and in the former case, we should try to send
114011042SErik.Nordmark@Sun.COM 				 * the ARP response.
114111042SErik.Nordmark@Sun.COM 				 */
114211042SErik.Nordmark@Sun.COM 				src_lladdr = src_haddr;
114311042SErik.Nordmark@Sun.COM 				goto send_response;
114411042SErik.Nordmark@Sun.COM 			}
114511042SErik.Nordmark@Sun.COM 			ncec_refhold(src_ncec);
114611042SErik.Nordmark@Sun.COM 			nce_refrele(nce);
114711042SErik.Nordmark@Sun.COM 			/* set up cleanup interval on ncec */
114811042SErik.Nordmark@Sun.COM 		}
114911042SErik.Nordmark@Sun.COM 
115011042SErik.Nordmark@Sun.COM 		/*
115111042SErik.Nordmark@Sun.COM 		 * This implements periodic address defense based on a modified
115211042SErik.Nordmark@Sun.COM 		 * version of the RFC 3927 requirements.  Instead of sending a
115311042SErik.Nordmark@Sun.COM 		 * broadcasted reply every time, as demanded by the RFC, we
115411042SErik.Nordmark@Sun.COM 		 * send at most one broadcast reply per arp_broadcast_interval.
115511042SErik.Nordmark@Sun.COM 		 */
115611042SErik.Nordmark@Sun.COM 		now = ddi_get_lbolt();
115711042SErik.Nordmark@Sun.COM 		if ((now - dst_ncec->ncec_last_time_defended) >
115811042SErik.Nordmark@Sun.COM 		    MSEC_TO_TICK(ipst->ips_ipv4_dad_announce_interval)) {
115911042SErik.Nordmark@Sun.COM 			dst_ncec->ncec_last_time_defended = now;
116011042SErik.Nordmark@Sun.COM 			/*
116111042SErik.Nordmark@Sun.COM 			 * If this is one of the long-suffering entries,
116211042SErik.Nordmark@Sun.COM 			 * pull it out now.  It no longer needs separate
116311042SErik.Nordmark@Sun.COM 			 * defense, because we're now doing that with this
116411042SErik.Nordmark@Sun.COM 			 * broadcasted reply.
116511042SErik.Nordmark@Sun.COM 			 */
116611042SErik.Nordmark@Sun.COM 			dst_ncec->ncec_flags &= ~NCE_F_DELAYED;
116711042SErik.Nordmark@Sun.COM 			send_unicast = B_FALSE;
116811042SErik.Nordmark@Sun.COM 		}
116911042SErik.Nordmark@Sun.COM 		if (src_ncec != NULL && send_unicast) {
117011042SErik.Nordmark@Sun.COM 			src_lladdr = src_ncec->ncec_lladdr;
117111042SErik.Nordmark@Sun.COM 		} else {
117211042SErik.Nordmark@Sun.COM 			src_lladdr = under_ill->ill_bcast_mp->b_rptr +
117311042SErik.Nordmark@Sun.COM 			    NCE_LL_ADDR_OFFSET(under_ill);
117411042SErik.Nordmark@Sun.COM 		}
117511042SErik.Nordmark@Sun.COM send_response:
117611042SErik.Nordmark@Sun.COM 		nce_hwaddr = dst_ncec->ncec_lladdr;
117711042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_INADDR(&dst_ncec->ncec_addr, &nce_paddr);
117811042SErik.Nordmark@Sun.COM 
117911042SErik.Nordmark@Sun.COM 		(void) arp_output(under_ill, ARP_RESPONSE,
118011042SErik.Nordmark@Sun.COM 		    nce_hwaddr, (uchar_t *)&nce_paddr, src_haddr,
118111042SErik.Nordmark@Sun.COM 		    (uchar_t *)&src_paddr, src_lladdr);
118211042SErik.Nordmark@Sun.COM 	}
118311042SErik.Nordmark@Sun.COM bail:
118411042SErik.Nordmark@Sun.COM 	if (dst_ncec != NULL) {
118511042SErik.Nordmark@Sun.COM 		ncec_refrele(dst_ncec);
118611042SErik.Nordmark@Sun.COM 	}
118711042SErik.Nordmark@Sun.COM 	if (src_ncec != NULL) {
118811042SErik.Nordmark@Sun.COM 		ncec_refrele(src_ncec);
118911042SErik.Nordmark@Sun.COM 	}
119011042SErik.Nordmark@Sun.COM 	if (err == AR_CHANGED) {
119111042SErik.Nordmark@Sun.COM 		mp->b_cont = NULL;
119211042SErik.Nordmark@Sun.COM 		arp_notify(src_paddr, mp1, AR_CN_ANNOUNCE, &iras, NULL);
119311042SErik.Nordmark@Sun.COM 		mp1 = NULL;
119411042SErik.Nordmark@Sun.COM 	}
119511042SErik.Nordmark@Sun.COM 	if (need_ill_refrele)
119611042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
119711042SErik.Nordmark@Sun.COM done:
119811042SErik.Nordmark@Sun.COM 	freemsg(mp);
119911042SErik.Nordmark@Sun.COM 	freemsg(mp1);
120011042SErik.Nordmark@Sun.COM }
120111042SErik.Nordmark@Sun.COM 
120211042SErik.Nordmark@Sun.COM /*
120311042SErik.Nordmark@Sun.COM  * Basic initialization of the arl_t and the arl_common structure shared with
120411042SErik.Nordmark@Sun.COM  * the ill_t that is done after SLIFNAME/IF_UNITSEL.
120511042SErik.Nordmark@Sun.COM  */
120611042SErik.Nordmark@Sun.COM static int
arl_ill_init(arl_t * arl,char * ill_name)120711042SErik.Nordmark@Sun.COM arl_ill_init(arl_t *arl, char *ill_name)
120811042SErik.Nordmark@Sun.COM {
120911042SErik.Nordmark@Sun.COM 	ill_t *ill;
121011042SErik.Nordmark@Sun.COM 	arl_ill_common_t *ai;
121111042SErik.Nordmark@Sun.COM 
121211042SErik.Nordmark@Sun.COM 	ill = ill_lookup_on_name(ill_name, B_FALSE, B_FALSE, B_FALSE,
121311042SErik.Nordmark@Sun.COM 	    arl->arl_ipst);
121411042SErik.Nordmark@Sun.COM 
121511042SErik.Nordmark@Sun.COM 	if (ill == NULL)
121611042SErik.Nordmark@Sun.COM 		return (ENXIO);
121711042SErik.Nordmark@Sun.COM 
121811042SErik.Nordmark@Sun.COM 	/*
121911042SErik.Nordmark@Sun.COM 	 * By the time we set up the arl, we expect the ETHERTYPE_IP
122011042SErik.Nordmark@Sun.COM 	 * stream to be fully bound and attached. So we copy/verify
122111042SErik.Nordmark@Sun.COM 	 * relevant information as possible from/against the ill.
122211042SErik.Nordmark@Sun.COM 	 *
122311042SErik.Nordmark@Sun.COM 	 * The following should have been set up in arp_ll_set_defaults()
122411042SErik.Nordmark@Sun.COM 	 * after the first DL_INFO_ACK was received.
122511042SErik.Nordmark@Sun.COM 	 */
122611042SErik.Nordmark@Sun.COM 	ASSERT(arl->arl_phys_addr_length == ill->ill_phys_addr_length);
122711042SErik.Nordmark@Sun.COM 	ASSERT(arl->arl_sap == ETHERTYPE_ARP);
122811042SErik.Nordmark@Sun.COM 	ASSERT(arl->arl_mactype == ill->ill_mactype);
122911042SErik.Nordmark@Sun.COM 	ASSERT(arl->arl_sap_length == ill->ill_sap_length);
123011042SErik.Nordmark@Sun.COM 
123111042SErik.Nordmark@Sun.COM 	ai =  kmem_zalloc(sizeof (*ai), KM_SLEEP);
123211042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_lock);
123311042SErik.Nordmark@Sun.COM 	/* First ensure that the ill is not CONDEMNED.  */
123411042SErik.Nordmark@Sun.COM 	if (ill->ill_state_flags & ILL_CONDEMNED) {
123511042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_lock);
123611042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
123711042SErik.Nordmark@Sun.COM 		kmem_free(ai, sizeof (*ai));
123811042SErik.Nordmark@Sun.COM 		return (ENXIO);
123911042SErik.Nordmark@Sun.COM 	}
124011042SErik.Nordmark@Sun.COM 	if (ill->ill_common != NULL || arl->arl_common != NULL) {
124111042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_lock);
124211042SErik.Nordmark@Sun.COM 		ip0dbg(("%s: PPA already exists", ill->ill_name));
124311042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
124411042SErik.Nordmark@Sun.COM 		kmem_free(ai, sizeof (*ai));
124511042SErik.Nordmark@Sun.COM 		return (EEXIST);
124611042SErik.Nordmark@Sun.COM 	}
124711042SErik.Nordmark@Sun.COM 	mutex_init(&ai->ai_lock, NULL, MUTEX_DEFAULT, NULL);
124811042SErik.Nordmark@Sun.COM 	ai->ai_arl = arl;
124911042SErik.Nordmark@Sun.COM 	ai->ai_ill = ill;
125011042SErik.Nordmark@Sun.COM 	ill->ill_common = ai;
125111042SErik.Nordmark@Sun.COM 	arl->arl_common = ai;
125211042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_lock);
125311042SErik.Nordmark@Sun.COM 	(void) strlcpy(arl->arl_name, ill->ill_name, LIFNAMSIZ);
125411042SErik.Nordmark@Sun.COM 	arl->arl_name_length = ill->ill_name_length;
125511042SErik.Nordmark@Sun.COM 	ill_refrele(ill);
125611042SErik.Nordmark@Sun.COM 	arp_ifname_notify(arl);
125711042SErik.Nordmark@Sun.COM 	return (0);
125811042SErik.Nordmark@Sun.COM }
125911042SErik.Nordmark@Sun.COM 
126011042SErik.Nordmark@Sun.COM /* Allocate and do common initializations for DLPI messages. */
126111042SErik.Nordmark@Sun.COM static mblk_t *
ip_ar_dlpi_comm(t_uscalar_t prim,size_t size)126211042SErik.Nordmark@Sun.COM ip_ar_dlpi_comm(t_uscalar_t prim, size_t size)
126311042SErik.Nordmark@Sun.COM {
126411042SErik.Nordmark@Sun.COM 	mblk_t  *mp;
126511042SErik.Nordmark@Sun.COM 
126611042SErik.Nordmark@Sun.COM 	if ((mp = allocb(size, BPRI_HI)) == NULL)
126711042SErik.Nordmark@Sun.COM 		return (NULL);
126811042SErik.Nordmark@Sun.COM 
126911042SErik.Nordmark@Sun.COM 	/*
127011042SErik.Nordmark@Sun.COM 	 * DLPIv2 says that DL_INFO_REQ and DL_TOKEN_REQ (the latter
127111042SErik.Nordmark@Sun.COM 	 * of which we don't seem to use) are sent with M_PCPROTO, and
127211042SErik.Nordmark@Sun.COM 	 * that other DLPI are M_PROTO.
127311042SErik.Nordmark@Sun.COM 	 */
127411042SErik.Nordmark@Sun.COM 	DB_TYPE(mp) = (prim == DL_INFO_REQ) ? M_PCPROTO : M_PROTO;
127511042SErik.Nordmark@Sun.COM 
127611042SErik.Nordmark@Sun.COM 	mp->b_wptr = mp->b_rptr + size;
127711042SErik.Nordmark@Sun.COM 	bzero(mp->b_rptr, size);
127811042SErik.Nordmark@Sun.COM 	DL_PRIM(mp) = prim;
127911042SErik.Nordmark@Sun.COM 	return (mp);
128011042SErik.Nordmark@Sun.COM }
128111042SErik.Nordmark@Sun.COM 
128211042SErik.Nordmark@Sun.COM 
128311042SErik.Nordmark@Sun.COM int
ip_sioctl_ifunitsel_arp(queue_t * q,int * ppa)128411042SErik.Nordmark@Sun.COM ip_sioctl_ifunitsel_arp(queue_t *q, int *ppa)
128511042SErik.Nordmark@Sun.COM {
128611042SErik.Nordmark@Sun.COM 	arl_t *arl;
128711042SErik.Nordmark@Sun.COM 	char *cp, ill_name[LIFNAMSIZ];
128811042SErik.Nordmark@Sun.COM 
128911042SErik.Nordmark@Sun.COM 	if (q->q_next == NULL)
129011042SErik.Nordmark@Sun.COM 		return (EINVAL);
129111042SErik.Nordmark@Sun.COM 
129211042SErik.Nordmark@Sun.COM 	do {
129311042SErik.Nordmark@Sun.COM 		q = q->q_next;
129411042SErik.Nordmark@Sun.COM 	} while (q->q_next != NULL);
129511042SErik.Nordmark@Sun.COM 	cp = q->q_qinfo->qi_minfo->mi_idname;
129611042SErik.Nordmark@Sun.COM 
129711042SErik.Nordmark@Sun.COM 	arl = (arl_t *)q->q_ptr;
129811042SErik.Nordmark@Sun.COM 	(void) snprintf(ill_name, sizeof (ill_name), "%s%d", cp, *ppa);
129911042SErik.Nordmark@Sun.COM 	arl->arl_ppa = *ppa;
130011042SErik.Nordmark@Sun.COM 	return (arl_ill_init(arl, ill_name));
130111042SErik.Nordmark@Sun.COM }
130211042SErik.Nordmark@Sun.COM 
130311042SErik.Nordmark@Sun.COM int
ip_sioctl_slifname_arp(queue_t * q,void * lifreq)130411042SErik.Nordmark@Sun.COM ip_sioctl_slifname_arp(queue_t *q, void *lifreq)
130511042SErik.Nordmark@Sun.COM {
130611042SErik.Nordmark@Sun.COM 	arl_t *arl;
130711042SErik.Nordmark@Sun.COM 	struct lifreq *lifr = lifreq;
130811042SErik.Nordmark@Sun.COM 
130911042SErik.Nordmark@Sun.COM 	/* ioctl not valid when IP opened as a device */
131011042SErik.Nordmark@Sun.COM 	if (q->q_next == NULL)
131111042SErik.Nordmark@Sun.COM 		return (EINVAL);
131211042SErik.Nordmark@Sun.COM 
131311042SErik.Nordmark@Sun.COM 	arl = (arl_t *)q->q_ptr;
131411042SErik.Nordmark@Sun.COM 	arl->arl_ppa = lifr->lifr_ppa;
131511042SErik.Nordmark@Sun.COM 	return (arl_ill_init(arl, lifr->lifr_name));
131611042SErik.Nordmark@Sun.COM }
131711042SErik.Nordmark@Sun.COM 
131811042SErik.Nordmark@Sun.COM arl_t *
ill_to_arl(ill_t * ill)131911042SErik.Nordmark@Sun.COM ill_to_arl(ill_t *ill)
132011042SErik.Nordmark@Sun.COM {
132111042SErik.Nordmark@Sun.COM 	arl_ill_common_t *ai = ill->ill_common;
132211042SErik.Nordmark@Sun.COM 	arl_t *arl = NULL;
132311042SErik.Nordmark@Sun.COM 
132411042SErik.Nordmark@Sun.COM 	if (ai == NULL)
132511042SErik.Nordmark@Sun.COM 		return (NULL);
132611042SErik.Nordmark@Sun.COM 	/*
132711042SErik.Nordmark@Sun.COM 	 * Find the arl_t that corresponds to this ill_t from the shared
132811042SErik.Nordmark@Sun.COM 	 * ill_common structure. We can safely access the ai here as it
132911042SErik.Nordmark@Sun.COM 	 * will only be freed in arp_modclose() after we have become
133011042SErik.Nordmark@Sun.COM 	 * single-threaded.
133111042SErik.Nordmark@Sun.COM 	 */
133211042SErik.Nordmark@Sun.COM 	mutex_enter(&ai->ai_lock);
133311042SErik.Nordmark@Sun.COM 	if ((arl = ai->ai_arl) != NULL) {
133411042SErik.Nordmark@Sun.COM 		mutex_enter(&arl->arl_lock);
133511042SErik.Nordmark@Sun.COM 		if (!(arl->arl_state_flags & ARL_CONDEMNED)) {
133611042SErik.Nordmark@Sun.COM 			arl_refhold_locked(arl);
133711042SErik.Nordmark@Sun.COM 			mutex_exit(&arl->arl_lock);
133811042SErik.Nordmark@Sun.COM 		} else {
133911042SErik.Nordmark@Sun.COM 			mutex_exit(&arl->arl_lock);
134011042SErik.Nordmark@Sun.COM 			arl = NULL;
134111042SErik.Nordmark@Sun.COM 		}
134211042SErik.Nordmark@Sun.COM 	}
134311042SErik.Nordmark@Sun.COM 	mutex_exit(&ai->ai_lock);
134411042SErik.Nordmark@Sun.COM 	return (arl);
134511042SErik.Nordmark@Sun.COM }
134611042SErik.Nordmark@Sun.COM 
134711042SErik.Nordmark@Sun.COM ill_t *
arl_to_ill(arl_t * arl)134811042SErik.Nordmark@Sun.COM arl_to_ill(arl_t *arl)
134911042SErik.Nordmark@Sun.COM {
135011042SErik.Nordmark@Sun.COM 	arl_ill_common_t *ai = arl->arl_common;
135111042SErik.Nordmark@Sun.COM 	ill_t *ill = NULL;
135211042SErik.Nordmark@Sun.COM 
135311042SErik.Nordmark@Sun.COM 	if (ai == NULL) {
135411042SErik.Nordmark@Sun.COM 		/*
135511042SErik.Nordmark@Sun.COM 		 * happens when the arp stream is just being opened, and
135611042SErik.Nordmark@Sun.COM 		 * arl_ill_init has not been executed yet.
135711042SErik.Nordmark@Sun.COM 		 */
135811042SErik.Nordmark@Sun.COM 		return (NULL);
135911042SErik.Nordmark@Sun.COM 	}
136011042SErik.Nordmark@Sun.COM 	/*
136111042SErik.Nordmark@Sun.COM 	 * Find the ill_t that corresponds to this arl_t from the shared
136211042SErik.Nordmark@Sun.COM 	 * arl_common structure. We can safely access the ai here as it
136311042SErik.Nordmark@Sun.COM 	 * will only be freed in arp_modclose() after we have become
136411042SErik.Nordmark@Sun.COM 	 * single-threaded.
136511042SErik.Nordmark@Sun.COM 	 */
136611042SErik.Nordmark@Sun.COM 	mutex_enter(&ai->ai_lock);
136711042SErik.Nordmark@Sun.COM 	if ((ill = ai->ai_ill) != NULL) {
136811042SErik.Nordmark@Sun.COM 		mutex_enter(&ill->ill_lock);
136911042SErik.Nordmark@Sun.COM 		if (!ILL_IS_CONDEMNED(ill)) {
137011042SErik.Nordmark@Sun.COM 			ill_refhold_locked(ill);
137111042SErik.Nordmark@Sun.COM 			mutex_exit(&ill->ill_lock);
137211042SErik.Nordmark@Sun.COM 		} else {
137311042SErik.Nordmark@Sun.COM 			mutex_exit(&ill->ill_lock);
137411042SErik.Nordmark@Sun.COM 			ill = NULL;
137511042SErik.Nordmark@Sun.COM 		}
137611042SErik.Nordmark@Sun.COM 	}
137711042SErik.Nordmark@Sun.COM 	mutex_exit(&ai->ai_lock);
137811042SErik.Nordmark@Sun.COM 	return (ill);
137911042SErik.Nordmark@Sun.COM }
138011042SErik.Nordmark@Sun.COM 
138111042SErik.Nordmark@Sun.COM int
arp_ll_up(ill_t * ill)138211042SErik.Nordmark@Sun.COM arp_ll_up(ill_t *ill)
138311042SErik.Nordmark@Sun.COM {
138411042SErik.Nordmark@Sun.COM 	mblk_t	*attach_mp = NULL;
138511042SErik.Nordmark@Sun.COM 	mblk_t	*bind_mp = NULL;
138611042SErik.Nordmark@Sun.COM 	mblk_t	*unbind_mp = NULL;
138711042SErik.Nordmark@Sun.COM 	arl_t 	*arl;
138811042SErik.Nordmark@Sun.COM 
138911042SErik.Nordmark@Sun.COM 	ASSERT(IAM_WRITER_ILL(ill));
139011042SErik.Nordmark@Sun.COM 	arl = ill_to_arl(ill);
139111042SErik.Nordmark@Sun.COM 
139211042SErik.Nordmark@Sun.COM 	DTRACE_PROBE2(ill__downup, char *, "arp_ll_up", ill_t *, ill);
139311042SErik.Nordmark@Sun.COM 	if (arl == NULL)
139411042SErik.Nordmark@Sun.COM 		return (ENXIO);
139511042SErik.Nordmark@Sun.COM 	DTRACE_PROBE2(arl__downup, char *, "arp_ll_up", arl_t *, arl);
139611042SErik.Nordmark@Sun.COM 	if ((arl->arl_state_flags & ARL_LL_UP) != 0) {
139711042SErik.Nordmark@Sun.COM 		arl_refrele(arl);
139811042SErik.Nordmark@Sun.COM 		return (0);
139911042SErik.Nordmark@Sun.COM 	}
140011042SErik.Nordmark@Sun.COM 	if (arl->arl_needs_attach) { /* DL_STYLE2 */
140111042SErik.Nordmark@Sun.COM 		attach_mp =
140211042SErik.Nordmark@Sun.COM 		    ip_ar_dlpi_comm(DL_ATTACH_REQ, sizeof (dl_attach_req_t));
140311042SErik.Nordmark@Sun.COM 		if (attach_mp == NULL)
140411042SErik.Nordmark@Sun.COM 			goto bad;
140511042SErik.Nordmark@Sun.COM 		((dl_attach_req_t *)attach_mp->b_rptr)->dl_ppa = arl->arl_ppa;
140611042SErik.Nordmark@Sun.COM 	}
140711042SErik.Nordmark@Sun.COM 
140811042SErik.Nordmark@Sun.COM 	/* Allocate and initialize a bind message. */
140911042SErik.Nordmark@Sun.COM 	bind_mp = ip_ar_dlpi_comm(DL_BIND_REQ, sizeof (dl_bind_req_t));
141011042SErik.Nordmark@Sun.COM 	if (bind_mp == NULL)
141111042SErik.Nordmark@Sun.COM 		goto bad;
141211042SErik.Nordmark@Sun.COM 	((dl_bind_req_t *)bind_mp->b_rptr)->dl_sap = ETHERTYPE_ARP;
141311042SErik.Nordmark@Sun.COM 	((dl_bind_req_t *)bind_mp->b_rptr)->dl_service_mode = DL_CLDLS;
141411042SErik.Nordmark@Sun.COM 
141511042SErik.Nordmark@Sun.COM 	unbind_mp = ip_ar_dlpi_comm(DL_UNBIND_REQ, sizeof (dl_unbind_req_t));
141611042SErik.Nordmark@Sun.COM 	if (unbind_mp == NULL)
141711042SErik.Nordmark@Sun.COM 		goto bad;
141811042SErik.Nordmark@Sun.COM 	if (arl->arl_needs_attach) {
141911042SErik.Nordmark@Sun.COM 		arp_dlpi_send(arl, attach_mp);
142011042SErik.Nordmark@Sun.COM 	}
142111042SErik.Nordmark@Sun.COM 	arl->arl_unbind_mp = unbind_mp;
142211042SErik.Nordmark@Sun.COM 
142311042SErik.Nordmark@Sun.COM 	arl->arl_state_flags |= ARL_LL_BIND_PENDING;
142411042SErik.Nordmark@Sun.COM 	arp_dlpi_send(arl, bind_mp);
142511042SErik.Nordmark@Sun.COM 	arl_refrele(arl);
142611042SErik.Nordmark@Sun.COM 	return (EINPROGRESS);
142711042SErik.Nordmark@Sun.COM 
142811042SErik.Nordmark@Sun.COM bad:
142911042SErik.Nordmark@Sun.COM 	freemsg(attach_mp);
143011042SErik.Nordmark@Sun.COM 	freemsg(bind_mp);
143111042SErik.Nordmark@Sun.COM 	freemsg(unbind_mp);
143211042SErik.Nordmark@Sun.COM 	arl_refrele(arl);
143311042SErik.Nordmark@Sun.COM 	return (ENOMEM);
143411042SErik.Nordmark@Sun.COM }
143511042SErik.Nordmark@Sun.COM 
143611042SErik.Nordmark@Sun.COM /*
143711042SErik.Nordmark@Sun.COM  * consumes/frees mp
143811042SErik.Nordmark@Sun.COM  */
143911042SErik.Nordmark@Sun.COM static void
arp_notify(in_addr_t src,mblk_t * mp,uint32_t arcn_code,ip_recv_attr_t * ira,ncec_t * ncec)144011042SErik.Nordmark@Sun.COM arp_notify(in_addr_t src, mblk_t *mp, uint32_t arcn_code,
144111042SErik.Nordmark@Sun.COM     ip_recv_attr_t *ira, ncec_t *ncec)
144211042SErik.Nordmark@Sun.COM {
144311042SErik.Nordmark@Sun.COM 	char		hbuf[MAC_STR_LEN];
144411042SErik.Nordmark@Sun.COM 	char		sbuf[INET_ADDRSTRLEN];
144511042SErik.Nordmark@Sun.COM 	ill_t		*ill = ira->ira_ill;
144611042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = ill->ill_ipst;
144711042SErik.Nordmark@Sun.COM 	arh_t		*arh = (arh_t *)mp->b_rptr;
144811042SErik.Nordmark@Sun.COM 
144911042SErik.Nordmark@Sun.COM 	switch (arcn_code) {
145011042SErik.Nordmark@Sun.COM 	case AR_CN_BOGON:
145111042SErik.Nordmark@Sun.COM 		/*
145211042SErik.Nordmark@Sun.COM 		 * Someone is sending ARP packets with a source protocol
145311042SErik.Nordmark@Sun.COM 		 * address that we have published and for which we believe our
145411042SErik.Nordmark@Sun.COM 		 * entry is authoritative and verified to be unique on
145511042SErik.Nordmark@Sun.COM 		 * the network.
145611042SErik.Nordmark@Sun.COM 		 *
145711042SErik.Nordmark@Sun.COM 		 * arp_process_packet() sends AR_CN_FAILED for the case when
145811042SErik.Nordmark@Sun.COM 		 * a DAD probe is received and the hardware address of a
145911042SErik.Nordmark@Sun.COM 		 * non-authoritative entry has changed. Thus, AR_CN_BOGON
146011042SErik.Nordmark@Sun.COM 		 * indicates a real conflict, and we have to do resolution.
146111042SErik.Nordmark@Sun.COM 		 *
146211042SErik.Nordmark@Sun.COM 		 * We back away quickly from the address if it's from DHCP or
146311042SErik.Nordmark@Sun.COM 		 * otherwise temporary and hasn't been used recently (or at
146411042SErik.Nordmark@Sun.COM 		 * all).  We'd like to include "deprecated" addresses here as
146511042SErik.Nordmark@Sun.COM 		 * well (as there's no real reason to defend something we're
146611042SErik.Nordmark@Sun.COM 		 * discarding), but IPMP "reuses" this flag to mean something
146711042SErik.Nordmark@Sun.COM 		 * other than the standard meaning.
146811042SErik.Nordmark@Sun.COM 		 */
146911042SErik.Nordmark@Sun.COM 		if (ip_nce_conflict(mp, ira, ncec)) {
147011042SErik.Nordmark@Sun.COM 			(void) mac_colon_addr((uint8_t *)(arh + 1),
147111042SErik.Nordmark@Sun.COM 			    arh->arh_hlen, hbuf, sizeof (hbuf));
147211042SErik.Nordmark@Sun.COM 			(void) ip_dot_addr(src, sbuf);
147311042SErik.Nordmark@Sun.COM 			cmn_err(CE_WARN,
147411042SErik.Nordmark@Sun.COM 			    "proxy ARP problem?  Node '%s' is using %s on %s",
147511042SErik.Nordmark@Sun.COM 			    hbuf, sbuf, ill->ill_name);
147611042SErik.Nordmark@Sun.COM 			if (!arp_no_defense)
147711042SErik.Nordmark@Sun.COM 				(void) arp_announce(ncec);
147811042SErik.Nordmark@Sun.COM 			/*
147911042SErik.Nordmark@Sun.COM 			 * ncec_last_time_defended has been adjusted in
148011042SErik.Nordmark@Sun.COM 			 * ip_nce_conflict.
148111042SErik.Nordmark@Sun.COM 			 */
148211042SErik.Nordmark@Sun.COM 		} else {
148311042SErik.Nordmark@Sun.COM 			ncec_delete(ncec);
148411042SErik.Nordmark@Sun.COM 		}
148511042SErik.Nordmark@Sun.COM 		freemsg(mp);
148611042SErik.Nordmark@Sun.COM 		break;
148711042SErik.Nordmark@Sun.COM 	case AR_CN_ANNOUNCE: {
148811042SErik.Nordmark@Sun.COM 		nce_hw_map_t hwm;
148911042SErik.Nordmark@Sun.COM 		/*
149011042SErik.Nordmark@Sun.COM 		 * ARP gives us a copy of any packet where it thinks
149111042SErik.Nordmark@Sun.COM 		 * the address has changed, so that we can update our
149211042SErik.Nordmark@Sun.COM 		 * caches.  We're responsible for caching known answers
149311042SErik.Nordmark@Sun.COM 		 * in the current design.  We check whether the
149411042SErik.Nordmark@Sun.COM 		 * hardware address really has changed in all of our
149511042SErik.Nordmark@Sun.COM 		 * entries that have cached this mapping, and if so, we
149611042SErik.Nordmark@Sun.COM 		 * blow them away.  This way we will immediately pick
149711042SErik.Nordmark@Sun.COM 		 * up the rare case of a host changing hardware
149811042SErik.Nordmark@Sun.COM 		 * address.
149911042SErik.Nordmark@Sun.COM 		 */
150011042SErik.Nordmark@Sun.COM 		if (src == 0) {
150111042SErik.Nordmark@Sun.COM 			freemsg(mp);
150211042SErik.Nordmark@Sun.COM 			break;
150311042SErik.Nordmark@Sun.COM 		}
150411042SErik.Nordmark@Sun.COM 		hwm.hwm_addr = src;
150511042SErik.Nordmark@Sun.COM 		hwm.hwm_hwlen = arh->arh_hlen;
150611042SErik.Nordmark@Sun.COM 		hwm.hwm_hwaddr = (uchar_t *)(arh + 1);
150711042SErik.Nordmark@Sun.COM 		hwm.hwm_flags = 0;
150811042SErik.Nordmark@Sun.COM 		ncec_walk_common(ipst->ips_ndp4, NULL,
150911042SErik.Nordmark@Sun.COM 		    (pfi_t)nce_update_hw_changed, &hwm, B_TRUE);
151011042SErik.Nordmark@Sun.COM 		freemsg(mp);
151111042SErik.Nordmark@Sun.COM 		break;
151211042SErik.Nordmark@Sun.COM 	}
151311042SErik.Nordmark@Sun.COM 	case AR_CN_FAILED:
151411042SErik.Nordmark@Sun.COM 		if (arp_no_defense) {
151511042SErik.Nordmark@Sun.COM 			(void) mac_colon_addr((uint8_t *)(arh + 1),
151611042SErik.Nordmark@Sun.COM 			    arh->arh_hlen, hbuf, sizeof (hbuf));
151711042SErik.Nordmark@Sun.COM 			(void) ip_dot_addr(src, sbuf);
151811042SErik.Nordmark@Sun.COM 
151911042SErik.Nordmark@Sun.COM 			cmn_err(CE_WARN,
152011042SErik.Nordmark@Sun.COM 			    "node %s is using our IP address %s on %s",
152111042SErik.Nordmark@Sun.COM 			    hbuf, sbuf, ill->ill_name);
152211042SErik.Nordmark@Sun.COM 			freemsg(mp);
152311042SErik.Nordmark@Sun.COM 			break;
152411042SErik.Nordmark@Sun.COM 		}
152511042SErik.Nordmark@Sun.COM 		/*
152611042SErik.Nordmark@Sun.COM 		 * mp will be freed by arp_excl.
152711042SErik.Nordmark@Sun.COM 		 */
152811042SErik.Nordmark@Sun.COM 		ill_refhold(ill);
152911042SErik.Nordmark@Sun.COM 		qwriter_ip(ill, ill->ill_rq, mp, arp_excl, NEW_OP, B_FALSE);
153011042SErik.Nordmark@Sun.COM 		return;
153111042SErik.Nordmark@Sun.COM 	default:
153211042SErik.Nordmark@Sun.COM 		ASSERT(0);
153311042SErik.Nordmark@Sun.COM 		freemsg(mp);
153411042SErik.Nordmark@Sun.COM 		break;
153511042SErik.Nordmark@Sun.COM 	}
153611042SErik.Nordmark@Sun.COM }
153711042SErik.Nordmark@Sun.COM 
153811042SErik.Nordmark@Sun.COM /*
153911042SErik.Nordmark@Sun.COM  * arp_output is called to transmit an ARP Request or Response. The mapping
154011042SErik.Nordmark@Sun.COM  * to RFC 826 variables is:
154111042SErik.Nordmark@Sun.COM  *   haddr1 == ar$sha
154211042SErik.Nordmark@Sun.COM  *   paddr1 == ar$spa
154311042SErik.Nordmark@Sun.COM  *   haddr2 == ar$tha
154411042SErik.Nordmark@Sun.COM  *   paddr2 == ar$tpa
154511042SErik.Nordmark@Sun.COM  * The ARP frame is sent to the ether_dst in dst_lladdr.
154611042SErik.Nordmark@Sun.COM  */
154711042SErik.Nordmark@Sun.COM static int
arp_output(ill_t * ill,uint32_t operation,const uchar_t * haddr1,const uchar_t * paddr1,const uchar_t * haddr2,const uchar_t * paddr2,uchar_t * dst_lladdr)154811042SErik.Nordmark@Sun.COM arp_output(ill_t *ill, uint32_t operation,
154911042SErik.Nordmark@Sun.COM     const uchar_t *haddr1, const uchar_t *paddr1, const uchar_t *haddr2,
155011042SErik.Nordmark@Sun.COM     const uchar_t *paddr2, uchar_t *dst_lladdr)
155111042SErik.Nordmark@Sun.COM {
155211042SErik.Nordmark@Sun.COM 	arh_t	*arh;
155311042SErik.Nordmark@Sun.COM 	uint8_t	*cp;
155411042SErik.Nordmark@Sun.COM 	uint_t	hlen;
155511042SErik.Nordmark@Sun.COM 	uint32_t plen = IPV4_ADDR_LEN; /* ar$pln from RFC 826 */
155611042SErik.Nordmark@Sun.COM 	uint32_t proto = IP_ARP_PROTO_TYPE;
155711042SErik.Nordmark@Sun.COM 	mblk_t *mp;
155811042SErik.Nordmark@Sun.COM 	arl_t *arl;
155911042SErik.Nordmark@Sun.COM 
156011042SErik.Nordmark@Sun.COM 	ASSERT(dst_lladdr != NULL);
156111042SErik.Nordmark@Sun.COM 	hlen = ill->ill_phys_addr_length; /* ar$hln from RFC 826 */
156211042SErik.Nordmark@Sun.COM 	mp = ill_dlur_gen(dst_lladdr, hlen, ETHERTYPE_ARP, ill->ill_sap_length);
156311042SErik.Nordmark@Sun.COM 
156411042SErik.Nordmark@Sun.COM 	if (mp == NULL)
156511042SErik.Nordmark@Sun.COM 		return (ENOMEM);
156611042SErik.Nordmark@Sun.COM 
156711042SErik.Nordmark@Sun.COM 	/* IFF_NOARP flag is set or link down: do not send arp messages */
156811042SErik.Nordmark@Sun.COM 	if ((ill->ill_flags & ILLF_NOARP) || !ill->ill_dl_up) {
156911042SErik.Nordmark@Sun.COM 		freemsg(mp);
157011042SErik.Nordmark@Sun.COM 		return (ENXIO);
157111042SErik.Nordmark@Sun.COM 	}
157211042SErik.Nordmark@Sun.COM 
157311042SErik.Nordmark@Sun.COM 	mp->b_cont = allocb(AR_LL_HDR_SLACK + ARH_FIXED_LEN + (hlen * 4) +
157411042SErik.Nordmark@Sun.COM 	    plen + plen, BPRI_MED);
157511042SErik.Nordmark@Sun.COM 	if (mp->b_cont == NULL) {
157611042SErik.Nordmark@Sun.COM 		freeb(mp);
157711042SErik.Nordmark@Sun.COM 		return (ENOMEM);
157811042SErik.Nordmark@Sun.COM 	}
157911042SErik.Nordmark@Sun.COM 
158011042SErik.Nordmark@Sun.COM 	/* Fill in the ARP header. */
158111042SErik.Nordmark@Sun.COM 	cp = mp->b_cont->b_rptr + (AR_LL_HDR_SLACK + hlen + hlen);
158211042SErik.Nordmark@Sun.COM 	mp->b_cont->b_rptr = cp;
158311042SErik.Nordmark@Sun.COM 	arh = (arh_t *)cp;
158411042SErik.Nordmark@Sun.COM 	U16_TO_BE16(arp_hw_type(ill->ill_mactype), arh->arh_hardware);
158511042SErik.Nordmark@Sun.COM 	U16_TO_BE16(proto, arh->arh_proto);
158611042SErik.Nordmark@Sun.COM 	arh->arh_hlen = (uint8_t)hlen;
158711042SErik.Nordmark@Sun.COM 	arh->arh_plen = (uint8_t)plen;
158811042SErik.Nordmark@Sun.COM 	U16_TO_BE16(operation, arh->arh_operation);
158911042SErik.Nordmark@Sun.COM 	cp += ARH_FIXED_LEN;
159011042SErik.Nordmark@Sun.COM 	bcopy(haddr1, cp, hlen);
159111042SErik.Nordmark@Sun.COM 	cp += hlen;
159211042SErik.Nordmark@Sun.COM 	if (paddr1 == NULL)
159311042SErik.Nordmark@Sun.COM 		bzero(cp, plen);
159411042SErik.Nordmark@Sun.COM 	else
159511042SErik.Nordmark@Sun.COM 		bcopy(paddr1, cp, plen);
159611042SErik.Nordmark@Sun.COM 	cp += plen;
159711042SErik.Nordmark@Sun.COM 	if (haddr2 == NULL)
159811042SErik.Nordmark@Sun.COM 		bzero(cp, hlen);
159911042SErik.Nordmark@Sun.COM 	else
160011042SErik.Nordmark@Sun.COM 		bcopy(haddr2, cp, hlen);
160111042SErik.Nordmark@Sun.COM 	cp += hlen;
160211042SErik.Nordmark@Sun.COM 	bcopy(paddr2, cp, plen);
160311042SErik.Nordmark@Sun.COM 	cp += plen;
160411042SErik.Nordmark@Sun.COM 	mp->b_cont->b_wptr = cp;
160511042SErik.Nordmark@Sun.COM 
160611042SErik.Nordmark@Sun.COM 	DTRACE_PROBE3(arp__physical__out__start,
160711042SErik.Nordmark@Sun.COM 	    ill_t *, ill, arh_t *, arh, mblk_t *, mp);
160811042SErik.Nordmark@Sun.COM 	ARP_HOOK_OUT(ill->ill_ipst->ips_arp_physical_out_event,
160911042SErik.Nordmark@Sun.COM 	    ill->ill_ipst->ips_arp_physical_out,
161011042SErik.Nordmark@Sun.COM 	    ill->ill_phyint->phyint_ifindex, arh, mp, mp->b_cont,
161111042SErik.Nordmark@Sun.COM 	    ill->ill_ipst);
161211042SErik.Nordmark@Sun.COM 	DTRACE_PROBE1(arp__physical__out__end, mblk_t *, mp);
161311042SErik.Nordmark@Sun.COM 	if (mp == NULL)
161411042SErik.Nordmark@Sun.COM 		return (0);
161511042SErik.Nordmark@Sun.COM 
161611042SErik.Nordmark@Sun.COM 	/* Ship it out. */
161711042SErik.Nordmark@Sun.COM 	arl = ill_to_arl(ill);
161811042SErik.Nordmark@Sun.COM 	if (arl == NULL) {
161911042SErik.Nordmark@Sun.COM 		freemsg(mp);
162011042SErik.Nordmark@Sun.COM 		return (0);
162111042SErik.Nordmark@Sun.COM 	}
162211042SErik.Nordmark@Sun.COM 	if (canputnext(arl->arl_wq))
162311042SErik.Nordmark@Sun.COM 		putnext(arl->arl_wq, mp);
162411042SErik.Nordmark@Sun.COM 	else
162511042SErik.Nordmark@Sun.COM 		freemsg(mp);
162611042SErik.Nordmark@Sun.COM 	arl_refrele(arl);
162711042SErik.Nordmark@Sun.COM 	return (0);
162811042SErik.Nordmark@Sun.COM }
162911042SErik.Nordmark@Sun.COM 
163011042SErik.Nordmark@Sun.COM /*
163111042SErik.Nordmark@Sun.COM  * Process resolve requests.
163211042SErik.Nordmark@Sun.COM  * If we are not yet reachable then we check and decrease ncec_rcnt; otherwise
163311042SErik.Nordmark@Sun.COM  * we leave it alone (the caller will check and manage ncec_pcnt in those
163411042SErik.Nordmark@Sun.COM  * cases.)
163511042SErik.Nordmark@Sun.COM  */
163611042SErik.Nordmark@Sun.COM int
arp_request(ncec_t * ncec,in_addr_t sender,ill_t * ill)163711042SErik.Nordmark@Sun.COM arp_request(ncec_t *ncec, in_addr_t sender, ill_t *ill)
163811042SErik.Nordmark@Sun.COM {
163911042SErik.Nordmark@Sun.COM 	int err;
164011042SErik.Nordmark@Sun.COM 	const uchar_t *target_hwaddr;
164111042SErik.Nordmark@Sun.COM 	struct in_addr nce_paddr;
164211042SErik.Nordmark@Sun.COM 	uchar_t *dst_lladdr;
164311042SErik.Nordmark@Sun.COM 	boolean_t use_rcnt = !NCE_ISREACHABLE(ncec);
164411042SErik.Nordmark@Sun.COM 
164511042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&ncec->ncec_lock));
164611042SErik.Nordmark@Sun.COM 	ASSERT(!IS_IPMP(ill));
164711042SErik.Nordmark@Sun.COM 
164811042SErik.Nordmark@Sun.COM 	if (use_rcnt && ncec->ncec_rcnt == 0) {
164911042SErik.Nordmark@Sun.COM 		/* not allowed any more retransmits. */
165011042SErik.Nordmark@Sun.COM 		return (0);
165111042SErik.Nordmark@Sun.COM 	}
165211042SErik.Nordmark@Sun.COM 
165311042SErik.Nordmark@Sun.COM 	if ((ill->ill_flags & ILLF_NOARP) != 0)
165411042SErik.Nordmark@Sun.COM 		return (0);
165511042SErik.Nordmark@Sun.COM 
165611042SErik.Nordmark@Sun.COM 	IN6_V4MAPPED_TO_INADDR(&ncec->ncec_addr, &nce_paddr);
165711042SErik.Nordmark@Sun.COM 
165811042SErik.Nordmark@Sun.COM 	target_hwaddr =
165911042SErik.Nordmark@Sun.COM 	    ill->ill_bcast_mp->b_rptr + NCE_LL_ADDR_OFFSET(ill);
166011042SErik.Nordmark@Sun.COM 
166111042SErik.Nordmark@Sun.COM 	if (NCE_ISREACHABLE(ncec)) {
166211042SErik.Nordmark@Sun.COM 		dst_lladdr =  ncec->ncec_lladdr;
166311042SErik.Nordmark@Sun.COM 	} else {
166411042SErik.Nordmark@Sun.COM 		dst_lladdr =  ill->ill_bcast_mp->b_rptr +
166511042SErik.Nordmark@Sun.COM 		    NCE_LL_ADDR_OFFSET(ill);
166611042SErik.Nordmark@Sun.COM 	}
166711042SErik.Nordmark@Sun.COM 
166811042SErik.Nordmark@Sun.COM 	mutex_exit(&ncec->ncec_lock);
166911042SErik.Nordmark@Sun.COM 	err = arp_output(ill, ARP_REQUEST,
167011042SErik.Nordmark@Sun.COM 	    ill->ill_phys_addr, (uchar_t *)&sender, target_hwaddr,
167111042SErik.Nordmark@Sun.COM 	    (uchar_t *)&nce_paddr, dst_lladdr);
167211042SErik.Nordmark@Sun.COM 	mutex_enter(&ncec->ncec_lock);
167311042SErik.Nordmark@Sun.COM 
167411042SErik.Nordmark@Sun.COM 	if (err != 0) {
167511042SErik.Nordmark@Sun.COM 		/*
167611042SErik.Nordmark@Sun.COM 		 * Some transient error such as ENOMEM or a down link was
167711042SErik.Nordmark@Sun.COM 		 * encountered. If the link has been taken down permanently,
167811042SErik.Nordmark@Sun.COM 		 * the ncec will eventually be cleaned up (ipif_down_tail()
167911042SErik.Nordmark@Sun.COM 		 * will call ipif_nce_down() and flush the ncec), to terminate
168011042SErik.Nordmark@Sun.COM 		 * recurring attempts to send ARP requests. In all other cases,
168111042SErik.Nordmark@Sun.COM 		 * allow the caller another chance at success next time.
168211042SErik.Nordmark@Sun.COM 		 */
168311042SErik.Nordmark@Sun.COM 		return (ncec->ncec_ill->ill_reachable_retrans_time);
168411042SErik.Nordmark@Sun.COM 	}
168511042SErik.Nordmark@Sun.COM 
168611042SErik.Nordmark@Sun.COM 	if (use_rcnt)
168711042SErik.Nordmark@Sun.COM 		ncec->ncec_rcnt--;
168811042SErik.Nordmark@Sun.COM 
168911042SErik.Nordmark@Sun.COM 	return (ncec->ncec_ill->ill_reachable_retrans_time);
169011042SErik.Nordmark@Sun.COM }
169111042SErik.Nordmark@Sun.COM 
169211042SErik.Nordmark@Sun.COM /* return B_TRUE if dropped */
169311042SErik.Nordmark@Sun.COM boolean_t
arp_announce(ncec_t * ncec)169411042SErik.Nordmark@Sun.COM arp_announce(ncec_t *ncec)
169511042SErik.Nordmark@Sun.COM {
169611042SErik.Nordmark@Sun.COM 	ill_t *ill;
169711042SErik.Nordmark@Sun.COM 	int err;
169811042SErik.Nordmark@Sun.COM 	uchar_t *sphys_addr, *bcast_addr;
169911042SErik.Nordmark@Sun.COM 	struct in_addr ncec_addr;
170011042SErik.Nordmark@Sun.COM 	boolean_t need_refrele = B_FALSE;
170111042SErik.Nordmark@Sun.COM 
170211042SErik.Nordmark@Sun.COM 	ASSERT((ncec->ncec_flags & NCE_F_BCAST) == 0);
170311042SErik.Nordmark@Sun.COM 	ASSERT((ncec->ncec_flags & NCE_F_MCAST) == 0);
170411042SErik.Nordmark@Sun.COM 
170511042SErik.Nordmark@Sun.COM 	if (IS_IPMP(ncec->ncec_ill)) {
170611042SErik.Nordmark@Sun.COM 		/* sent on the cast_ill */
170712069SPeter.Memishian@Sun.COM 		ill = ipmp_ill_hold_xmit_ill(ncec->ncec_ill, B_FALSE);
170811042SErik.Nordmark@Sun.COM 		if (ill == NULL)
170911042SErik.Nordmark@Sun.COM 			return (B_TRUE);
171011042SErik.Nordmark@Sun.COM 		need_refrele = B_TRUE;
171111042SErik.Nordmark@Sun.COM 	} else {
171211042SErik.Nordmark@Sun.COM 		ill = ncec->ncec_ill;
171311042SErik.Nordmark@Sun.COM 	}
171411042SErik.Nordmark@Sun.COM 
171511042SErik.Nordmark@Sun.COM 	/*
171611042SErik.Nordmark@Sun.COM 	 * broadcast an announce to ill_bcast address.
171711042SErik.Nordmark@Sun.COM 	 */
171811042SErik.Nordmark@Sun.COM 	IN6_V4MAPPED_TO_INADDR(&ncec->ncec_addr, &ncec_addr);
171911042SErik.Nordmark@Sun.COM 
172011042SErik.Nordmark@Sun.COM 	sphys_addr = ncec->ncec_lladdr;
172111042SErik.Nordmark@Sun.COM 	bcast_addr = ill->ill_bcast_mp->b_rptr + NCE_LL_ADDR_OFFSET(ill);
172211042SErik.Nordmark@Sun.COM 
172311042SErik.Nordmark@Sun.COM 	err = arp_output(ill, ARP_REQUEST,
172411042SErik.Nordmark@Sun.COM 	    sphys_addr, (uchar_t *)&ncec_addr, bcast_addr,
172511042SErik.Nordmark@Sun.COM 	    (uchar_t *)&ncec_addr, bcast_addr);
172611042SErik.Nordmark@Sun.COM 
172711042SErik.Nordmark@Sun.COM 	if (need_refrele)
172811042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
172911042SErik.Nordmark@Sun.COM 	return (err != 0);
173011042SErik.Nordmark@Sun.COM }
173111042SErik.Nordmark@Sun.COM 
173211042SErik.Nordmark@Sun.COM /* return B_TRUE if dropped */
173311042SErik.Nordmark@Sun.COM boolean_t
arp_probe(ncec_t * ncec)173411042SErik.Nordmark@Sun.COM arp_probe(ncec_t *ncec)
173511042SErik.Nordmark@Sun.COM {
173611042SErik.Nordmark@Sun.COM 	ill_t *ill;
173711042SErik.Nordmark@Sun.COM 	int err;
173811042SErik.Nordmark@Sun.COM 	struct in_addr ncec_addr;
173911042SErik.Nordmark@Sun.COM 	uchar_t *sphys_addr, *dst_lladdr;
174011042SErik.Nordmark@Sun.COM 
174111042SErik.Nordmark@Sun.COM 	if (IS_IPMP(ncec->ncec_ill)) {
174212069SPeter.Memishian@Sun.COM 		ill = ipmp_ill_hold_xmit_ill(ncec->ncec_ill, B_FALSE);
174311042SErik.Nordmark@Sun.COM 		if (ill == NULL)
174411042SErik.Nordmark@Sun.COM 			return (B_TRUE);
174511042SErik.Nordmark@Sun.COM 	} else {
174611042SErik.Nordmark@Sun.COM 		ill = ncec->ncec_ill;
174711042SErik.Nordmark@Sun.COM 	}
174811042SErik.Nordmark@Sun.COM 
174911042SErik.Nordmark@Sun.COM 	IN6_V4MAPPED_TO_INADDR(&ncec->ncec_addr, &ncec_addr);
175011042SErik.Nordmark@Sun.COM 
175111042SErik.Nordmark@Sun.COM 	sphys_addr = ncec->ncec_lladdr;
175211042SErik.Nordmark@Sun.COM 	dst_lladdr = ill->ill_bcast_mp->b_rptr + NCE_LL_ADDR_OFFSET(ill);
175311042SErik.Nordmark@Sun.COM 	err = arp_output(ill, ARP_REQUEST,
175411042SErik.Nordmark@Sun.COM 	    sphys_addr, NULL, NULL, (uchar_t *)&ncec_addr, dst_lladdr);
175511042SErik.Nordmark@Sun.COM 
175611042SErik.Nordmark@Sun.COM 	if (IS_IPMP(ncec->ncec_ill))
175711042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
175811042SErik.Nordmark@Sun.COM 	return (err != 0);
175911042SErik.Nordmark@Sun.COM }
176011042SErik.Nordmark@Sun.COM 
176111042SErik.Nordmark@Sun.COM static mblk_t *
arl_unbind(arl_t * arl)176211042SErik.Nordmark@Sun.COM arl_unbind(arl_t *arl)
176311042SErik.Nordmark@Sun.COM {
176411042SErik.Nordmark@Sun.COM 	mblk_t *mp;
176511042SErik.Nordmark@Sun.COM 
176611042SErik.Nordmark@Sun.COM 	if ((mp = arl->arl_unbind_mp) != NULL) {
176711042SErik.Nordmark@Sun.COM 		arl->arl_unbind_mp = NULL;
176811042SErik.Nordmark@Sun.COM 		arl->arl_state_flags |= ARL_DL_UNBIND_IN_PROGRESS;
176911042SErik.Nordmark@Sun.COM 	}
177011042SErik.Nordmark@Sun.COM 	return (mp);
177111042SErik.Nordmark@Sun.COM }
177211042SErik.Nordmark@Sun.COM 
177311042SErik.Nordmark@Sun.COM int
arp_ll_down(ill_t * ill)177411042SErik.Nordmark@Sun.COM arp_ll_down(ill_t *ill)
177511042SErik.Nordmark@Sun.COM {
177611042SErik.Nordmark@Sun.COM 	arl_t 	*arl;
177711042SErik.Nordmark@Sun.COM 	mblk_t *unbind_mp;
177811042SErik.Nordmark@Sun.COM 	int err = 0;
177911042SErik.Nordmark@Sun.COM 	boolean_t replumb = (ill->ill_replumbing == 1);
178011042SErik.Nordmark@Sun.COM 
178111042SErik.Nordmark@Sun.COM 	DTRACE_PROBE2(ill__downup, char *, "arp_ll_down", ill_t *, ill);
178211042SErik.Nordmark@Sun.COM 	if ((arl = ill_to_arl(ill)) == NULL)
178311042SErik.Nordmark@Sun.COM 		return (ENXIO);
178411042SErik.Nordmark@Sun.COM 	DTRACE_PROBE2(arl__downup, char *, "arp_ll_down", arl_t *, arl);
178511042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
178611042SErik.Nordmark@Sun.COM 	unbind_mp = arl_unbind(arl);
178711042SErik.Nordmark@Sun.COM 	if (unbind_mp != NULL) {
178811042SErik.Nordmark@Sun.COM 		ASSERT(arl->arl_state_flags & ARL_DL_UNBIND_IN_PROGRESS);
178911042SErik.Nordmark@Sun.COM 		DTRACE_PROBE2(arp__unbinding, mblk_t *, unbind_mp,
179011042SErik.Nordmark@Sun.COM 		    arl_t *, arl);
179111042SErik.Nordmark@Sun.COM 		err = EINPROGRESS;
179211042SErik.Nordmark@Sun.COM 		if (replumb)
179311042SErik.Nordmark@Sun.COM 			arl->arl_state_flags |= ARL_LL_REPLUMBING;
179411042SErik.Nordmark@Sun.COM 	}
179511042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
179611042SErik.Nordmark@Sun.COM 	if (unbind_mp != NULL)
179711042SErik.Nordmark@Sun.COM 		arp_dlpi_send(arl, unbind_mp);
179811042SErik.Nordmark@Sun.COM 	arl_refrele(arl);
179911042SErik.Nordmark@Sun.COM 	return (err);
180011042SErik.Nordmark@Sun.COM }
180111042SErik.Nordmark@Sun.COM 
180211042SErik.Nordmark@Sun.COM /* ARGSUSED */
180311042SErik.Nordmark@Sun.COM int
arp_close(queue_t * q,int flags)180411042SErik.Nordmark@Sun.COM arp_close(queue_t *q, int flags)
180511042SErik.Nordmark@Sun.COM {
180611042SErik.Nordmark@Sun.COM 	if (WR(q)->q_next != NULL) {
180711042SErik.Nordmark@Sun.COM 		/* This is a module close */
180811042SErik.Nordmark@Sun.COM 		return (arp_modclose(q->q_ptr));
180911042SErik.Nordmark@Sun.COM 	}
181011042SErik.Nordmark@Sun.COM 	qprocsoff(q);
181111042SErik.Nordmark@Sun.COM 	q->q_ptr = WR(q)->q_ptr = NULL;
181211042SErik.Nordmark@Sun.COM 	return (0);
181311042SErik.Nordmark@Sun.COM }
181411042SErik.Nordmark@Sun.COM 
181511042SErik.Nordmark@Sun.COM static int
arp_modclose(arl_t * arl)181611042SErik.Nordmark@Sun.COM arp_modclose(arl_t *arl)
181711042SErik.Nordmark@Sun.COM {
181811042SErik.Nordmark@Sun.COM 	arl_ill_common_t *ai = arl->arl_common;
181911042SErik.Nordmark@Sun.COM 	ill_t		*ill;
182011042SErik.Nordmark@Sun.COM 	queue_t		*q = arl->arl_rq;
182111042SErik.Nordmark@Sun.COM 	mblk_t		*mp, *nextmp;
182211042SErik.Nordmark@Sun.COM 	ipsq_t		*ipsq = NULL;
182311042SErik.Nordmark@Sun.COM 
182411042SErik.Nordmark@Sun.COM 	ill = arl_to_ill(arl);
182511042SErik.Nordmark@Sun.COM 	if (ill != NULL) {
182611042SErik.Nordmark@Sun.COM 		if (!ill_waiter_inc(ill)) {
182711042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
182811042SErik.Nordmark@Sun.COM 		} else {
182911042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
183011042SErik.Nordmark@Sun.COM 			if (ipsq_enter(ill, B_FALSE, NEW_OP))
183111042SErik.Nordmark@Sun.COM 				ipsq = ill->ill_phyint->phyint_ipsq;
183211042SErik.Nordmark@Sun.COM 			ill_waiter_dcr(ill);
183311042SErik.Nordmark@Sun.COM 		}
183411042SErik.Nordmark@Sun.COM 		if (ipsq == NULL) {
183511042SErik.Nordmark@Sun.COM 			/*
183611042SErik.Nordmark@Sun.COM 			 * could not enter the ipsq because ill is already
183711042SErik.Nordmark@Sun.COM 			 * marked CONDEMNED.
183811042SErik.Nordmark@Sun.COM 			 */
183911042SErik.Nordmark@Sun.COM 			ill = NULL;
184011042SErik.Nordmark@Sun.COM 		}
184111042SErik.Nordmark@Sun.COM 	}
184211042SErik.Nordmark@Sun.COM 	if (ai != NULL && ipsq == NULL) {
184311042SErik.Nordmark@Sun.COM 		/*
184411042SErik.Nordmark@Sun.COM 		 * Either we did not get an ill because it was marked CONDEMNED
184511042SErik.Nordmark@Sun.COM 		 * or we could not enter the ipsq because it was unplumbing.
184611042SErik.Nordmark@Sun.COM 		 * In both cases, wait for the ill to complete ip_modclose().
184711042SErik.Nordmark@Sun.COM 		 *
184811042SErik.Nordmark@Sun.COM 		 * If the arp_modclose happened even before SLIFNAME, the ai
184911042SErik.Nordmark@Sun.COM 		 * itself would be NULL, in which case we can complete the close
185011042SErik.Nordmark@Sun.COM 		 * without waiting.
185111042SErik.Nordmark@Sun.COM 		 */
185211042SErik.Nordmark@Sun.COM 		mutex_enter(&ai->ai_lock);
185311042SErik.Nordmark@Sun.COM 		while (ai->ai_ill != NULL)
185411042SErik.Nordmark@Sun.COM 			cv_wait(&ai->ai_ill_unplumb_done, &ai->ai_lock);
185511042SErik.Nordmark@Sun.COM 		mutex_exit(&ai->ai_lock);
185611042SErik.Nordmark@Sun.COM 	}
185711042SErik.Nordmark@Sun.COM 	ASSERT(ill == NULL || IAM_WRITER_ILL(ill));
185811042SErik.Nordmark@Sun.COM 
185911042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
186011042SErik.Nordmark@Sun.COM 	/*
186111042SErik.Nordmark@Sun.COM 	 * If the ill had completed unplumbing before arp_modclose(), there
186211042SErik.Nordmark@Sun.COM 	 * would be no ill (and therefore, no ipsq) to serialize arp_modclose()
186311042SErik.Nordmark@Sun.COM 	 * so that we need to explicitly check for ARL_CONDEMNED and back off
186411042SErik.Nordmark@Sun.COM 	 * if it is set.
186511042SErik.Nordmark@Sun.COM 	 */
186611042SErik.Nordmark@Sun.COM 	if ((arl->arl_state_flags & ARL_CONDEMNED) != 0) {
186711042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
186811042SErik.Nordmark@Sun.COM 		ASSERT(ipsq == NULL);
186911042SErik.Nordmark@Sun.COM 		return (0);
187011042SErik.Nordmark@Sun.COM 	}
187111042SErik.Nordmark@Sun.COM 	arl->arl_state_flags |= ARL_CONDEMNED;
187211042SErik.Nordmark@Sun.COM 
187311042SErik.Nordmark@Sun.COM 	/*
187411042SErik.Nordmark@Sun.COM 	 * send out all pending dlpi messages, don't wait for the ack (which
187511042SErik.Nordmark@Sun.COM 	 * will be ignored in arp_rput when CONDEMNED is set)
187611042SErik.Nordmark@Sun.COM 	 *
187711042SErik.Nordmark@Sun.COM 	 * We have to check for pending DL_UNBIND_REQ because, in the case
187811042SErik.Nordmark@Sun.COM 	 * that ip_modclose() executed before arp_modclose(), the call to
187911042SErik.Nordmark@Sun.COM 	 * ill_delete_tail->ipif_arp_down() would have triggered a
188011042SErik.Nordmark@Sun.COM 	 * DL_UNBIND_REQ. When arp_modclose() executes ipsq_enter() will fail
188111042SErik.Nordmark@Sun.COM 	 * (since ip_modclose() is in the ipsq) but the DL_UNBIND_ACK may not
188211042SErik.Nordmark@Sun.COM 	 * have been processed yet. In this scenario, we cannot reset
188311042SErik.Nordmark@Sun.COM 	 * arl_dlpi_pending, because the setting/clearing of arl_state_flags
188411042SErik.Nordmark@Sun.COM 	 * related to unbind, and the associated cv_waits must be allowed to
188511042SErik.Nordmark@Sun.COM 	 * continue.
188611042SErik.Nordmark@Sun.COM 	 */
188711042SErik.Nordmark@Sun.COM 	if (arl->arl_dlpi_pending != DL_UNBIND_REQ)
188811042SErik.Nordmark@Sun.COM 		arl->arl_dlpi_pending = DL_PRIM_INVAL;
188911042SErik.Nordmark@Sun.COM 	mp = arl->arl_dlpi_deferred;
189011042SErik.Nordmark@Sun.COM 	arl->arl_dlpi_deferred = NULL;
189111042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
189211042SErik.Nordmark@Sun.COM 
189311042SErik.Nordmark@Sun.COM 	for (; mp != NULL; mp = nextmp) {
189411042SErik.Nordmark@Sun.COM 		nextmp = mp->b_next;
189511042SErik.Nordmark@Sun.COM 		mp->b_next = NULL;
189611042SErik.Nordmark@Sun.COM 		putnext(arl->arl_wq, mp);
189711042SErik.Nordmark@Sun.COM 	}
189811042SErik.Nordmark@Sun.COM 
189911042SErik.Nordmark@Sun.COM 	/* Wait for data paths to quiesce */
190011042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
190111042SErik.Nordmark@Sun.COM 	while (arl->arl_refcnt != 0)
190211042SErik.Nordmark@Sun.COM 		cv_wait(&arl->arl_cv, &arl->arl_lock);
190311042SErik.Nordmark@Sun.COM 
190411042SErik.Nordmark@Sun.COM 	/*
190511042SErik.Nordmark@Sun.COM 	 * unbind, so that nothing else can come up from driver.
190611042SErik.Nordmark@Sun.COM 	 */
190711042SErik.Nordmark@Sun.COM 	mp = arl_unbind(arl);
190811042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
190911042SErik.Nordmark@Sun.COM 	if (mp != NULL)
191011042SErik.Nordmark@Sun.COM 		arp_dlpi_send(arl, mp);
191111042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
191211042SErik.Nordmark@Sun.COM 
191311042SErik.Nordmark@Sun.COM 	/* wait for unbind ack  */
191411042SErik.Nordmark@Sun.COM 	while (arl->arl_state_flags & ARL_DL_UNBIND_IN_PROGRESS)
191511042SErik.Nordmark@Sun.COM 		cv_wait(&arl->arl_cv, &arl->arl_lock);
191611042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
191711042SErik.Nordmark@Sun.COM 
191811042SErik.Nordmark@Sun.COM 	qprocsoff(q);
191911042SErik.Nordmark@Sun.COM 
192011042SErik.Nordmark@Sun.COM 	if (ill != NULL) {
192111042SErik.Nordmark@Sun.COM 		mutex_enter(&ill->ill_lock);
192211042SErik.Nordmark@Sun.COM 		ill->ill_arl_dlpi_pending = 0;
192311042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_lock);
192411042SErik.Nordmark@Sun.COM 	}
192511042SErik.Nordmark@Sun.COM 
192611042SErik.Nordmark@Sun.COM 	if (ai != NULL) {
192711042SErik.Nordmark@Sun.COM 		mutex_enter(&ai->ai_lock);
192811042SErik.Nordmark@Sun.COM 		ai->ai_arl = NULL;
192911042SErik.Nordmark@Sun.COM 		if (ai->ai_ill == NULL) {
193011042SErik.Nordmark@Sun.COM 			mutex_destroy(&ai->ai_lock);
193111042SErik.Nordmark@Sun.COM 			kmem_free(ai, sizeof (*ai));
193211042SErik.Nordmark@Sun.COM 		} else {
193311042SErik.Nordmark@Sun.COM 			mutex_exit(&ai->ai_lock);
193411042SErik.Nordmark@Sun.COM 		}
193511042SErik.Nordmark@Sun.COM 	}
193611042SErik.Nordmark@Sun.COM 
193711042SErik.Nordmark@Sun.COM 	/* free up the rest */
193811042SErik.Nordmark@Sun.COM 	arp_mod_close_tail(arl);
193911042SErik.Nordmark@Sun.COM 
194011042SErik.Nordmark@Sun.COM 	q->q_ptr = WR(q)->q_ptr = NULL;
194111042SErik.Nordmark@Sun.COM 
194211042SErik.Nordmark@Sun.COM 	if (ipsq != NULL)
194311042SErik.Nordmark@Sun.COM 		ipsq_exit(ipsq);
194411042SErik.Nordmark@Sun.COM 
194511042SErik.Nordmark@Sun.COM 	return (0);
194611042SErik.Nordmark@Sun.COM }
194711042SErik.Nordmark@Sun.COM 
194811042SErik.Nordmark@Sun.COM static void
arp_mod_close_tail(arl_t * arl)194911042SErik.Nordmark@Sun.COM arp_mod_close_tail(arl_t *arl)
195011042SErik.Nordmark@Sun.COM {
195111042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = arl->arl_ipst;
195211042SErik.Nordmark@Sun.COM 	mblk_t		**mpp;
195311042SErik.Nordmark@Sun.COM 
195411042SErik.Nordmark@Sun.COM 	mutex_enter(&ipst->ips_ip_mi_lock);
195511042SErik.Nordmark@Sun.COM 	mi_close_unlink(&ipst->ips_arp_g_head, (IDP)arl);
195611042SErik.Nordmark@Sun.COM 	mutex_exit(&ipst->ips_ip_mi_lock);
195711042SErik.Nordmark@Sun.COM 
195811042SErik.Nordmark@Sun.COM 	/*
195911042SErik.Nordmark@Sun.COM 	 * credp could be null if the open didn't succeed and ip_modopen
196011042SErik.Nordmark@Sun.COM 	 * itself calls ip_close.
196111042SErik.Nordmark@Sun.COM 	 */
196211042SErik.Nordmark@Sun.COM 	if (arl->arl_credp != NULL)
196311042SErik.Nordmark@Sun.COM 		crfree(arl->arl_credp);
196411042SErik.Nordmark@Sun.COM 
196511042SErik.Nordmark@Sun.COM 	/* Free all retained control messages. */
196611042SErik.Nordmark@Sun.COM 	mpp = &arl->arl_first_mp_to_free;
196711042SErik.Nordmark@Sun.COM 	do {
196811042SErik.Nordmark@Sun.COM 		while (mpp[0]) {
196911042SErik.Nordmark@Sun.COM 			mblk_t  *mp;
197011042SErik.Nordmark@Sun.COM 			mblk_t  *mp1;
197111042SErik.Nordmark@Sun.COM 
197211042SErik.Nordmark@Sun.COM 			mp = mpp[0];
197311042SErik.Nordmark@Sun.COM 			mpp[0] = mp->b_next;
197411042SErik.Nordmark@Sun.COM 			for (mp1 = mp; mp1 != NULL; mp1 = mp1->b_cont) {
197511042SErik.Nordmark@Sun.COM 				mp1->b_next = NULL;
197611042SErik.Nordmark@Sun.COM 				mp1->b_prev = NULL;
197711042SErik.Nordmark@Sun.COM 			}
197811042SErik.Nordmark@Sun.COM 			freemsg(mp);
197911042SErik.Nordmark@Sun.COM 		}
198011042SErik.Nordmark@Sun.COM 	} while (mpp++ != &arl->arl_last_mp_to_free);
198111042SErik.Nordmark@Sun.COM 
198211042SErik.Nordmark@Sun.COM 	netstack_rele(ipst->ips_netstack);
198311042SErik.Nordmark@Sun.COM 	mi_free(arl->arl_name);
198411042SErik.Nordmark@Sun.COM 	mi_close_free((IDP)arl);
198511042SErik.Nordmark@Sun.COM }
198611042SErik.Nordmark@Sun.COM 
198711042SErik.Nordmark@Sun.COM /*
198811042SErik.Nordmark@Sun.COM  * DAD failed. Tear down ipifs with the specified srce address. Note that
198911042SErik.Nordmark@Sun.COM  * tearing down the ipif also meas deleting the ncec through ipif_down,
199011042SErik.Nordmark@Sun.COM  * so it is not possible to use nce_timer for recovery. Instead we start
199111042SErik.Nordmark@Sun.COM  * a timer on the ipif. Caller has to free the mp.
199211042SErik.Nordmark@Sun.COM  */
199311042SErik.Nordmark@Sun.COM void
arp_failure(mblk_t * mp,ip_recv_attr_t * ira)199411042SErik.Nordmark@Sun.COM arp_failure(mblk_t *mp, ip_recv_attr_t *ira)
199511042SErik.Nordmark@Sun.COM {
199611042SErik.Nordmark@Sun.COM 	ill_t *ill = ira->ira_ill;
199711042SErik.Nordmark@Sun.COM 
199811042SErik.Nordmark@Sun.COM 	if ((mp = copymsg(mp)) != NULL) {
199911042SErik.Nordmark@Sun.COM 		ill_refhold(ill);
200011042SErik.Nordmark@Sun.COM 		qwriter_ip(ill, ill->ill_rq, mp, arp_excl, NEW_OP, B_FALSE);
200111042SErik.Nordmark@Sun.COM 	}
200211042SErik.Nordmark@Sun.COM }
200311042SErik.Nordmark@Sun.COM 
200411042SErik.Nordmark@Sun.COM /*
200511042SErik.Nordmark@Sun.COM  * This is for exclusive changes due to ARP.  Tear down an interface due
200611042SErik.Nordmark@Sun.COM  * to AR_CN_FAILED and AR_CN_BOGON.
200711042SErik.Nordmark@Sun.COM  */
200811042SErik.Nordmark@Sun.COM /* ARGSUSED */
200911042SErik.Nordmark@Sun.COM static void
arp_excl(ipsq_t * ipsq,queue_t * rq,mblk_t * mp,void * dummy_arg)201011042SErik.Nordmark@Sun.COM arp_excl(ipsq_t *ipsq, queue_t *rq, mblk_t *mp, void *dummy_arg)
201111042SErik.Nordmark@Sun.COM {
201211042SErik.Nordmark@Sun.COM 	ill_t	*ill = rq->q_ptr;
201311042SErik.Nordmark@Sun.COM 	arh_t *arh;
201411042SErik.Nordmark@Sun.COM 	ipaddr_t src;
201511042SErik.Nordmark@Sun.COM 	ipif_t	*ipif;
201611042SErik.Nordmark@Sun.COM 	ip_stack_t *ipst = ill->ill_ipst;
201711042SErik.Nordmark@Sun.COM 	uchar_t	*haddr;
201811042SErik.Nordmark@Sun.COM 	uint_t	haddrlen;
201911042SErik.Nordmark@Sun.COM 
202011042SErik.Nordmark@Sun.COM 	/* first try src = ar$spa */
202111042SErik.Nordmark@Sun.COM 	arh = (arh_t *)mp->b_rptr;
202211042SErik.Nordmark@Sun.COM 	bcopy((char *)&arh[1] + arh->arh_hlen, &src, IP_ADDR_LEN);
202311042SErik.Nordmark@Sun.COM 
202411042SErik.Nordmark@Sun.COM 	haddrlen = arh->arh_hlen;
202511042SErik.Nordmark@Sun.COM 	haddr = (uint8_t *)(arh + 1);
202611042SErik.Nordmark@Sun.COM 
202711042SErik.Nordmark@Sun.COM 	if (haddrlen == ill->ill_phys_addr_length) {
202811042SErik.Nordmark@Sun.COM 		/*
202911042SErik.Nordmark@Sun.COM 		 * Ignore conflicts generated by misbehaving switches that
203011042SErik.Nordmark@Sun.COM 		 * just reflect our own messages back to us.  For IPMP, we may
203111042SErik.Nordmark@Sun.COM 		 * see reflections across any ill in the illgrp.
203211042SErik.Nordmark@Sun.COM 		 */
203311042SErik.Nordmark@Sun.COM 		/* For an under ill_grp can change under lock */
203411042SErik.Nordmark@Sun.COM 		rw_enter(&ipst->ips_ill_g_lock, RW_READER);
203511042SErik.Nordmark@Sun.COM 		if (bcmp(haddr, ill->ill_phys_addr, haddrlen) == 0 ||
203611042SErik.Nordmark@Sun.COM 		    IS_UNDER_IPMP(ill) && ill->ill_grp != NULL &&
203711042SErik.Nordmark@Sun.COM 		    ipmp_illgrp_find_ill(ill->ill_grp, haddr,
203811042SErik.Nordmark@Sun.COM 		    haddrlen) != NULL) {
203911042SErik.Nordmark@Sun.COM 			rw_exit(&ipst->ips_ill_g_lock);
204011042SErik.Nordmark@Sun.COM 			goto ignore_conflict;
204111042SErik.Nordmark@Sun.COM 		}
204211042SErik.Nordmark@Sun.COM 		rw_exit(&ipst->ips_ill_g_lock);
204311042SErik.Nordmark@Sun.COM 	}
204411042SErik.Nordmark@Sun.COM 
204511042SErik.Nordmark@Sun.COM 	/*
204611042SErik.Nordmark@Sun.COM 	 * Look up the appropriate ipif.
204711042SErik.Nordmark@Sun.COM 	 */
204811042SErik.Nordmark@Sun.COM 	ipif = ipif_lookup_addr(src, ill, ALL_ZONES, ipst);
204911042SErik.Nordmark@Sun.COM 	if (ipif == NULL)
205011042SErik.Nordmark@Sun.COM 		goto ignore_conflict;
205111042SErik.Nordmark@Sun.COM 
205211042SErik.Nordmark@Sun.COM 	/* Reload the ill to match the ipif */
205311042SErik.Nordmark@Sun.COM 	ill = ipif->ipif_ill;
205411042SErik.Nordmark@Sun.COM 
205511042SErik.Nordmark@Sun.COM 	/* If it's already duplicate or ineligible, then don't do anything. */
205611042SErik.Nordmark@Sun.COM 	if (ipif->ipif_flags & (IPIF_POINTOPOINT|IPIF_DUPLICATE)) {
205711042SErik.Nordmark@Sun.COM 		ipif_refrele(ipif);
205811042SErik.Nordmark@Sun.COM 		goto ignore_conflict;
205911042SErik.Nordmark@Sun.COM 	}
206011042SErik.Nordmark@Sun.COM 
206111042SErik.Nordmark@Sun.COM 	/*
206211042SErik.Nordmark@Sun.COM 	 * If we failed on a recovery probe, then restart the timer to
206311042SErik.Nordmark@Sun.COM 	 * try again later.
206411042SErik.Nordmark@Sun.COM 	 */
206511042SErik.Nordmark@Sun.COM 	if (!ipif->ipif_was_dup) {
206611042SErik.Nordmark@Sun.COM 		char hbuf[MAC_STR_LEN];
206711042SErik.Nordmark@Sun.COM 		char sbuf[INET_ADDRSTRLEN];
206811042SErik.Nordmark@Sun.COM 		char ibuf[LIFNAMSIZ];
206911042SErik.Nordmark@Sun.COM 
207011042SErik.Nordmark@Sun.COM 		(void) mac_colon_addr(haddr, haddrlen, hbuf, sizeof (hbuf));
207111042SErik.Nordmark@Sun.COM 		(void) ip_dot_addr(src, sbuf);
207211042SErik.Nordmark@Sun.COM 		ipif_get_name(ipif, ibuf, sizeof (ibuf));
207311042SErik.Nordmark@Sun.COM 
207411042SErik.Nordmark@Sun.COM 		cmn_err(CE_WARN, "%s has duplicate address %s (in use by %s);"
207511042SErik.Nordmark@Sun.COM 		    " disabled", ibuf, sbuf, hbuf);
207611042SErik.Nordmark@Sun.COM 	}
207711042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_lock);
207811042SErik.Nordmark@Sun.COM 	ASSERT(!(ipif->ipif_flags & IPIF_DUPLICATE));
207911042SErik.Nordmark@Sun.COM 	ipif->ipif_flags |= IPIF_DUPLICATE;
208011042SErik.Nordmark@Sun.COM 	ill->ill_ipif_dup_count++;
208111042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_lock);
208211042SErik.Nordmark@Sun.COM 	(void) ipif_down(ipif, NULL, NULL);
208311042SErik.Nordmark@Sun.COM 	(void) ipif_down_tail(ipif);
208411042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_lock);
208511042SErik.Nordmark@Sun.COM 	if (!(ipif->ipif_flags & (IPIF_DHCPRUNNING|IPIF_TEMPORARY)) &&
208611042SErik.Nordmark@Sun.COM 	    ill->ill_net_type == IRE_IF_RESOLVER &&
208711042SErik.Nordmark@Sun.COM 	    !(ipif->ipif_state_flags & IPIF_CONDEMNED) &&
208811042SErik.Nordmark@Sun.COM 	    ipst->ips_ip_dup_recovery > 0) {
208911042SErik.Nordmark@Sun.COM 		ASSERT(ipif->ipif_recovery_id == 0);
209011042SErik.Nordmark@Sun.COM 		ipif->ipif_recovery_id = timeout(ipif_dup_recovery,
209111042SErik.Nordmark@Sun.COM 		    ipif, MSEC_TO_TICK(ipst->ips_ip_dup_recovery));
209211042SErik.Nordmark@Sun.COM 	}
209311042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_lock);
209411042SErik.Nordmark@Sun.COM 	ipif_refrele(ipif);
209511042SErik.Nordmark@Sun.COM 
209611042SErik.Nordmark@Sun.COM ignore_conflict:
209711042SErik.Nordmark@Sun.COM 	freemsg(mp);
209811042SErik.Nordmark@Sun.COM }
209911042SErik.Nordmark@Sun.COM 
210011042SErik.Nordmark@Sun.COM /*
210111042SErik.Nordmark@Sun.COM  * This is a place for a dtrace hook.
210211042SErik.Nordmark@Sun.COM  * Note that mp can be either the DL_UNITDATA_IND with a b_cont payload,
210311042SErik.Nordmark@Sun.COM  * or just the ARP packet payload as an M_DATA.
210411042SErik.Nordmark@Sun.COM  */
210511042SErik.Nordmark@Sun.COM /* ARGSUSED */
210611042SErik.Nordmark@Sun.COM static void
arp_drop_packet(const char * str,mblk_t * mp,ill_t * ill)210711042SErik.Nordmark@Sun.COM arp_drop_packet(const char *str, mblk_t *mp, ill_t *ill)
210811042SErik.Nordmark@Sun.COM {
210911042SErik.Nordmark@Sun.COM 	freemsg(mp);
211011042SErik.Nordmark@Sun.COM }
211111042SErik.Nordmark@Sun.COM 
211211042SErik.Nordmark@Sun.COM static boolean_t
arp_over_driver(queue_t * q)211311042SErik.Nordmark@Sun.COM arp_over_driver(queue_t *q)
211411042SErik.Nordmark@Sun.COM {
211511042SErik.Nordmark@Sun.COM 	queue_t *qnext = STREAM(q)->sd_wrq->q_next;
211611042SErik.Nordmark@Sun.COM 
211711042SErik.Nordmark@Sun.COM 	/*
211811042SErik.Nordmark@Sun.COM 	 * check if first module below stream head is IP or UDP.
211911042SErik.Nordmark@Sun.COM 	 */
212011042SErik.Nordmark@Sun.COM 	ASSERT(qnext != NULL);
212111042SErik.Nordmark@Sun.COM 	if (strcmp(Q2NAME(qnext), "ip") != 0 &&
212211042SErik.Nordmark@Sun.COM 	    strcmp(Q2NAME(qnext), "udp") != 0) {
212311042SErik.Nordmark@Sun.COM 		/*
212411042SErik.Nordmark@Sun.COM 		 * module below is not ip or udp, so arp has been pushed
212511042SErik.Nordmark@Sun.COM 		 * on the driver.
212611042SErik.Nordmark@Sun.COM 		 */
212711042SErik.Nordmark@Sun.COM 		return (B_TRUE);
212811042SErik.Nordmark@Sun.COM 	}
212911042SErik.Nordmark@Sun.COM 	return (B_FALSE);
213011042SErik.Nordmark@Sun.COM }
213111042SErik.Nordmark@Sun.COM 
213211042SErik.Nordmark@Sun.COM static int
arp_open(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * credp)213311042SErik.Nordmark@Sun.COM arp_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
213411042SErik.Nordmark@Sun.COM {
213511042SErik.Nordmark@Sun.COM 	int err;
213611042SErik.Nordmark@Sun.COM 
213711042SErik.Nordmark@Sun.COM 	ASSERT(sflag & MODOPEN);
213811042SErik.Nordmark@Sun.COM 	if (!arp_over_driver(q)) {
213911042SErik.Nordmark@Sun.COM 		q->q_qinfo = dummymodinfo.st_rdinit;
214011042SErik.Nordmark@Sun.COM 		WR(q)->q_qinfo = dummymodinfo.st_wrinit;
214111042SErik.Nordmark@Sun.COM 		return ((*dummymodinfo.st_rdinit->qi_qopen)(q, devp, flag,
214211042SErik.Nordmark@Sun.COM 		    sflag, credp));
214311042SErik.Nordmark@Sun.COM 	}
214411042SErik.Nordmark@Sun.COM 	err = arp_modopen(q, devp, flag, sflag, credp);
214511042SErik.Nordmark@Sun.COM 	return (err);
214611042SErik.Nordmark@Sun.COM }
214711042SErik.Nordmark@Sun.COM 
214811042SErik.Nordmark@Sun.COM /*
214911042SErik.Nordmark@Sun.COM  * In most cases we must be a writer on the IP stream before coming to
215011042SErik.Nordmark@Sun.COM  * arp_dlpi_send(), to serialize DLPI sends to the driver. The exceptions
215111042SErik.Nordmark@Sun.COM  * when we are not a writer are very early duing initialization (in
215211042SErik.Nordmark@Sun.COM  * arl_init, before the arl has done a SLIFNAME, so that we don't yet know
215311042SErik.Nordmark@Sun.COM  * the associated ill) or during arp_mod_close, when we could not enter the
215411042SErik.Nordmark@Sun.COM  * ipsq because the ill has already unplumbed.
215511042SErik.Nordmark@Sun.COM  */
215611042SErik.Nordmark@Sun.COM static void
arp_dlpi_send(arl_t * arl,mblk_t * mp)215711042SErik.Nordmark@Sun.COM arp_dlpi_send(arl_t *arl, mblk_t *mp)
215811042SErik.Nordmark@Sun.COM {
215911042SErik.Nordmark@Sun.COM 	mblk_t **mpp;
216011042SErik.Nordmark@Sun.COM 	t_uscalar_t prim;
216111042SErik.Nordmark@Sun.COM 	arl_ill_common_t *ai;
216211042SErik.Nordmark@Sun.COM 
216311042SErik.Nordmark@Sun.COM 	ASSERT(DB_TYPE(mp) == M_PROTO || DB_TYPE(mp) == M_PCPROTO);
216411042SErik.Nordmark@Sun.COM 
216511042SErik.Nordmark@Sun.COM #ifdef DEBUG
216611042SErik.Nordmark@Sun.COM 	ai = arl->arl_common;
216711042SErik.Nordmark@Sun.COM 	if (ai != NULL) {
216811042SErik.Nordmark@Sun.COM 		mutex_enter(&ai->ai_lock);
216911042SErik.Nordmark@Sun.COM 		if (ai->ai_ill != NULL)
217011042SErik.Nordmark@Sun.COM 			ASSERT(IAM_WRITER_ILL(ai->ai_ill));
217111042SErik.Nordmark@Sun.COM 		mutex_exit(&ai->ai_lock);
217211042SErik.Nordmark@Sun.COM 	}
217311042SErik.Nordmark@Sun.COM #endif /* DEBUG */
217411042SErik.Nordmark@Sun.COM 
217511042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
217611042SErik.Nordmark@Sun.COM 	if (arl->arl_dlpi_pending != DL_PRIM_INVAL) {
217711042SErik.Nordmark@Sun.COM 		/* Must queue message. Tail insertion */
217811042SErik.Nordmark@Sun.COM 		mpp = &arl->arl_dlpi_deferred;
217911042SErik.Nordmark@Sun.COM 		while (*mpp != NULL)
218011042SErik.Nordmark@Sun.COM 			mpp = &((*mpp)->b_next);
218111042SErik.Nordmark@Sun.COM 
218211042SErik.Nordmark@Sun.COM 		*mpp = mp;
218311042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
218411042SErik.Nordmark@Sun.COM 		return;
218511042SErik.Nordmark@Sun.COM 	}
218611042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
218711042SErik.Nordmark@Sun.COM 	if ((prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive)
218811042SErik.Nordmark@Sun.COM 	    == DL_BIND_REQ) {
218911042SErik.Nordmark@Sun.COM 		ASSERT((arl->arl_state_flags & ARL_DL_UNBIND_IN_PROGRESS) == 0);
219011042SErik.Nordmark@Sun.COM 	}
219111042SErik.Nordmark@Sun.COM 	/*
219211042SErik.Nordmark@Sun.COM 	 * No need to take the arl_lock to examine ARL_CONDEMNED at this point
219311042SErik.Nordmark@Sun.COM 	 * because the only thread that can see ARL_CONDEMNED here is the
219411042SErik.Nordmark@Sun.COM 	 * closing arp_modclose() thread which sets the flag after becoming a
219511042SErik.Nordmark@Sun.COM 	 * writer on the ipsq. Threads from IP must have finished and
219611042SErik.Nordmark@Sun.COM 	 * cannot be active now.
219711042SErik.Nordmark@Sun.COM 	 */
219811042SErik.Nordmark@Sun.COM 	if (!(arl->arl_state_flags & ARL_CONDEMNED) ||
219911042SErik.Nordmark@Sun.COM 	    (prim == DL_UNBIND_REQ)) {
220011042SErik.Nordmark@Sun.COM 		if (prim != DL_NOTIFY_CONF) {
220111042SErik.Nordmark@Sun.COM 			ill_t *ill = arl_to_ill(arl);
220211042SErik.Nordmark@Sun.COM 
220311042SErik.Nordmark@Sun.COM 			arl->arl_dlpi_pending = prim;
220411042SErik.Nordmark@Sun.COM 			if (ill != NULL) {
220511042SErik.Nordmark@Sun.COM 				mutex_enter(&ill->ill_lock);
220611042SErik.Nordmark@Sun.COM 				ill->ill_arl_dlpi_pending = 1;
220711042SErik.Nordmark@Sun.COM 				mutex_exit(&ill->ill_lock);
220811042SErik.Nordmark@Sun.COM 				ill_refrele(ill);
220911042SErik.Nordmark@Sun.COM 			}
221011042SErik.Nordmark@Sun.COM 		}
221111042SErik.Nordmark@Sun.COM 	}
221211042SErik.Nordmark@Sun.COM 	DTRACE_PROBE4(arl__dlpi, char *, "arp_dlpi_send",
221311042SErik.Nordmark@Sun.COM 	    char *, dl_primstr(prim), char *, "-",  arl_t *, arl);
221411042SErik.Nordmark@Sun.COM 	putnext(arl->arl_wq, mp);
221511042SErik.Nordmark@Sun.COM }
221611042SErik.Nordmark@Sun.COM 
221711042SErik.Nordmark@Sun.COM static void
arl_defaults_common(arl_t * arl,mblk_t * mp)221811042SErik.Nordmark@Sun.COM arl_defaults_common(arl_t *arl, mblk_t *mp)
221911042SErik.Nordmark@Sun.COM {
222011042SErik.Nordmark@Sun.COM 	dl_info_ack_t	*dlia = (dl_info_ack_t *)mp->b_rptr;
222111042SErik.Nordmark@Sun.COM 	/*
222211042SErik.Nordmark@Sun.COM 	 * Till the ill is fully up  the ill is not globally visible.
222311042SErik.Nordmark@Sun.COM 	 * So no need for a lock.
222411042SErik.Nordmark@Sun.COM 	 */
222511042SErik.Nordmark@Sun.COM 	arl->arl_mactype = dlia->dl_mac_type;
222611042SErik.Nordmark@Sun.COM 	arl->arl_sap_length = dlia->dl_sap_length;
222711042SErik.Nordmark@Sun.COM 
222811042SErik.Nordmark@Sun.COM 	if (!arl->arl_dlpi_style_set) {
222911042SErik.Nordmark@Sun.COM 		if (dlia->dl_provider_style == DL_STYLE2)
223011042SErik.Nordmark@Sun.COM 			arl->arl_needs_attach = 1;
223111042SErik.Nordmark@Sun.COM 		mutex_enter(&arl->arl_lock);
223211042SErik.Nordmark@Sun.COM 		ASSERT(arl->arl_dlpi_style_set == 0);
223311042SErik.Nordmark@Sun.COM 		arl->arl_dlpi_style_set = 1;
223411042SErik.Nordmark@Sun.COM 		arl->arl_state_flags &= ~ARL_LL_SUBNET_PENDING;
223511042SErik.Nordmark@Sun.COM 		cv_broadcast(&arl->arl_cv);
223611042SErik.Nordmark@Sun.COM 		mutex_exit(&arl->arl_lock);
223711042SErik.Nordmark@Sun.COM 	}
223811042SErik.Nordmark@Sun.COM }
223911042SErik.Nordmark@Sun.COM 
224011042SErik.Nordmark@Sun.COM int
arl_init(queue_t * q,arl_t * arl)224111042SErik.Nordmark@Sun.COM arl_init(queue_t *q, arl_t *arl)
224211042SErik.Nordmark@Sun.COM {
224311042SErik.Nordmark@Sun.COM 	mblk_t *info_mp;
224411042SErik.Nordmark@Sun.COM 	dl_info_req_t   *dlir;
224511042SErik.Nordmark@Sun.COM 
224611042SErik.Nordmark@Sun.COM 	/* subset of ill_init */
224711042SErik.Nordmark@Sun.COM 	mutex_init(&arl->arl_lock, NULL, MUTEX_DEFAULT, 0);
224811042SErik.Nordmark@Sun.COM 
224911042SErik.Nordmark@Sun.COM 	arl->arl_rq = q;
225011042SErik.Nordmark@Sun.COM 	arl->arl_wq = WR(q);
225111042SErik.Nordmark@Sun.COM 
225211042SErik.Nordmark@Sun.COM 	info_mp = allocb(MAX(sizeof (dl_info_req_t), sizeof (dl_info_ack_t)),
225311042SErik.Nordmark@Sun.COM 	    BPRI_HI);
225411042SErik.Nordmark@Sun.COM 	if (info_mp == NULL)
225511042SErik.Nordmark@Sun.COM 		return (ENOMEM);
225611042SErik.Nordmark@Sun.COM 	/*
225711042SErik.Nordmark@Sun.COM 	 * allocate sufficient space to contain device name.
225811042SErik.Nordmark@Sun.COM 	 */
225911042SErik.Nordmark@Sun.COM 	arl->arl_name = (char *)(mi_zalloc(2 * LIFNAMSIZ));
226011042SErik.Nordmark@Sun.COM 	arl->arl_ppa = UINT_MAX;
226111042SErik.Nordmark@Sun.COM 	arl->arl_state_flags |= (ARL_LL_SUBNET_PENDING | ARL_LL_UNBOUND);
226211042SErik.Nordmark@Sun.COM 
226311042SErik.Nordmark@Sun.COM 	/* Send down the Info Request to the driver. */
226411042SErik.Nordmark@Sun.COM 	info_mp->b_datap->db_type = M_PCPROTO;
226511042SErik.Nordmark@Sun.COM 	dlir = (dl_info_req_t *)info_mp->b_rptr;
226611042SErik.Nordmark@Sun.COM 	info_mp->b_wptr = (uchar_t *)&dlir[1];
226711042SErik.Nordmark@Sun.COM 	dlir->dl_primitive = DL_INFO_REQ;
226811042SErik.Nordmark@Sun.COM 	arl->arl_dlpi_pending = DL_PRIM_INVAL;
226911042SErik.Nordmark@Sun.COM 	qprocson(q);
227011042SErik.Nordmark@Sun.COM 
227111042SErik.Nordmark@Sun.COM 	arp_dlpi_send(arl, info_mp);
227211042SErik.Nordmark@Sun.COM 	return (0);
227311042SErik.Nordmark@Sun.COM }
227411042SErik.Nordmark@Sun.COM 
227511042SErik.Nordmark@Sun.COM int
arl_wait_for_info_ack(arl_t * arl)227611042SErik.Nordmark@Sun.COM arl_wait_for_info_ack(arl_t *arl)
227711042SErik.Nordmark@Sun.COM {
227811042SErik.Nordmark@Sun.COM 	int err;
227911042SErik.Nordmark@Sun.COM 
228011042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
228111042SErik.Nordmark@Sun.COM 	while (arl->arl_state_flags & ARL_LL_SUBNET_PENDING) {
228211042SErik.Nordmark@Sun.COM 		/*
228311042SErik.Nordmark@Sun.COM 		 * Return value of 0 indicates a pending signal.
228411042SErik.Nordmark@Sun.COM 		 */
228511042SErik.Nordmark@Sun.COM 		err = cv_wait_sig(&arl->arl_cv, &arl->arl_lock);
228611042SErik.Nordmark@Sun.COM 		if (err == 0) {
228711042SErik.Nordmark@Sun.COM 			mutex_exit(&arl->arl_lock);
228811042SErik.Nordmark@Sun.COM 			return (EINTR);
228911042SErik.Nordmark@Sun.COM 		}
229011042SErik.Nordmark@Sun.COM 	}
229111042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
229211042SErik.Nordmark@Sun.COM 	/*
229311042SErik.Nordmark@Sun.COM 	 * ip_rput_other could have set an error  in ill_error on
229411042SErik.Nordmark@Sun.COM 	 * receipt of M_ERROR.
229511042SErik.Nordmark@Sun.COM 	 */
229611042SErik.Nordmark@Sun.COM 	return (arl->arl_error);
229711042SErik.Nordmark@Sun.COM }
229811042SErik.Nordmark@Sun.COM 
229911042SErik.Nordmark@Sun.COM void
arl_set_muxid(ill_t * ill,int muxid)230011042SErik.Nordmark@Sun.COM arl_set_muxid(ill_t *ill, int muxid)
230111042SErik.Nordmark@Sun.COM {
230211042SErik.Nordmark@Sun.COM 	arl_t *arl;
230311042SErik.Nordmark@Sun.COM 
230411042SErik.Nordmark@Sun.COM 	arl = ill_to_arl(ill);
230511042SErik.Nordmark@Sun.COM 	if (arl != NULL) {
230611042SErik.Nordmark@Sun.COM 		arl->arl_muxid = muxid;
230711042SErik.Nordmark@Sun.COM 		arl_refrele(arl);
230811042SErik.Nordmark@Sun.COM 	}
230911042SErik.Nordmark@Sun.COM }
231011042SErik.Nordmark@Sun.COM 
231111042SErik.Nordmark@Sun.COM int
arl_get_muxid(ill_t * ill)231211042SErik.Nordmark@Sun.COM arl_get_muxid(ill_t *ill)
231311042SErik.Nordmark@Sun.COM {
231411042SErik.Nordmark@Sun.COM 	arl_t *arl;
231511042SErik.Nordmark@Sun.COM 	int muxid = 0;
231611042SErik.Nordmark@Sun.COM 
231711042SErik.Nordmark@Sun.COM 	arl = ill_to_arl(ill);
231811042SErik.Nordmark@Sun.COM 	if (arl != NULL) {
231911042SErik.Nordmark@Sun.COM 		muxid = arl->arl_muxid;
232011042SErik.Nordmark@Sun.COM 		arl_refrele(arl);
232111042SErik.Nordmark@Sun.COM 	}
232211042SErik.Nordmark@Sun.COM 	return (muxid);
232311042SErik.Nordmark@Sun.COM }
232411042SErik.Nordmark@Sun.COM 
232511042SErik.Nordmark@Sun.COM static int
arp_modopen(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * credp)232611042SErik.Nordmark@Sun.COM arp_modopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
232711042SErik.Nordmark@Sun.COM {
232811042SErik.Nordmark@Sun.COM 	int	err;
232911042SErik.Nordmark@Sun.COM 	zoneid_t zoneid;
233011042SErik.Nordmark@Sun.COM 	netstack_t *ns;
233111042SErik.Nordmark@Sun.COM 	ip_stack_t *ipst;
233211042SErik.Nordmark@Sun.COM 	arl_t	*arl = NULL;
233311042SErik.Nordmark@Sun.COM 
233411042SErik.Nordmark@Sun.COM 	/*
233511042SErik.Nordmark@Sun.COM 	 * Prevent unprivileged processes from pushing IP so that
233611042SErik.Nordmark@Sun.COM 	 * they can't send raw IP.
233711042SErik.Nordmark@Sun.COM 	 */
233811042SErik.Nordmark@Sun.COM 	if (secpolicy_net_rawaccess(credp) != 0)
233911042SErik.Nordmark@Sun.COM 		return (EPERM);
234011042SErik.Nordmark@Sun.COM 
234111042SErik.Nordmark@Sun.COM 	ns = netstack_find_by_cred(credp);
234211042SErik.Nordmark@Sun.COM 	ASSERT(ns != NULL);
234311042SErik.Nordmark@Sun.COM 	ipst = ns->netstack_ip;
234411042SErik.Nordmark@Sun.COM 	ASSERT(ipst != NULL);
234511042SErik.Nordmark@Sun.COM 
234611042SErik.Nordmark@Sun.COM 	/*
234711042SErik.Nordmark@Sun.COM 	 * For exclusive stacks we set the zoneid to zero
234811042SErik.Nordmark@Sun.COM 	 * to make IP operate as if in the global zone.
234911042SErik.Nordmark@Sun.COM 	 */
235011042SErik.Nordmark@Sun.COM 	if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
235111042SErik.Nordmark@Sun.COM 		zoneid = GLOBAL_ZONEID;
235211042SErik.Nordmark@Sun.COM 	else
235311042SErik.Nordmark@Sun.COM 		zoneid = crgetzoneid(credp);
235411042SErik.Nordmark@Sun.COM 
235511042SErik.Nordmark@Sun.COM 	arl = (arl_t *)mi_open_alloc_sleep(sizeof (arl_t));
235611042SErik.Nordmark@Sun.COM 	q->q_ptr = WR(q)->q_ptr = arl;
235711042SErik.Nordmark@Sun.COM 	arl->arl_ipst = ipst;
235811042SErik.Nordmark@Sun.COM 	arl->arl_zoneid = zoneid;
235911042SErik.Nordmark@Sun.COM 	err = arl_init(q, arl);
236011042SErik.Nordmark@Sun.COM 
236111042SErik.Nordmark@Sun.COM 	if (err != 0) {
236211042SErik.Nordmark@Sun.COM 		mi_free(arl->arl_name);
236311042SErik.Nordmark@Sun.COM 		mi_free(arl);
236411042SErik.Nordmark@Sun.COM 		netstack_rele(ipst->ips_netstack);
236511042SErik.Nordmark@Sun.COM 		q->q_ptr = NULL;
236611042SErik.Nordmark@Sun.COM 		WR(q)->q_ptr = NULL;
236711042SErik.Nordmark@Sun.COM 		return (err);
236811042SErik.Nordmark@Sun.COM 	}
236911042SErik.Nordmark@Sun.COM 
237011042SErik.Nordmark@Sun.COM 	/*
237111042SErik.Nordmark@Sun.COM 	 * Wait for the DL_INFO_ACK if a DL_INFO_REQ was sent.
237211042SErik.Nordmark@Sun.COM 	 */
237311042SErik.Nordmark@Sun.COM 	err = arl_wait_for_info_ack(arl);
237411042SErik.Nordmark@Sun.COM 	if (err == 0)
237511042SErik.Nordmark@Sun.COM 		arl->arl_credp = credp;
237611042SErik.Nordmark@Sun.COM 	else
237711042SErik.Nordmark@Sun.COM 		goto fail;
237811042SErik.Nordmark@Sun.COM 
237911042SErik.Nordmark@Sun.COM 	crhold(credp);
238011042SErik.Nordmark@Sun.COM 
238111042SErik.Nordmark@Sun.COM 	mutex_enter(&ipst->ips_ip_mi_lock);
238211042SErik.Nordmark@Sun.COM 	err = mi_open_link(&ipst->ips_arp_g_head, (IDP)q->q_ptr, devp, flag,
238311042SErik.Nordmark@Sun.COM 	    sflag, credp);
238411042SErik.Nordmark@Sun.COM 	mutex_exit(&ipst->ips_ip_mi_lock);
238511042SErik.Nordmark@Sun.COM fail:
238611042SErik.Nordmark@Sun.COM 	if (err) {
238711042SErik.Nordmark@Sun.COM 		(void) arp_close(q, 0);
238811042SErik.Nordmark@Sun.COM 		return (err);
238911042SErik.Nordmark@Sun.COM 	}
239011042SErik.Nordmark@Sun.COM 	return (0);
239111042SErik.Nordmark@Sun.COM }
239211042SErik.Nordmark@Sun.COM 
239311042SErik.Nordmark@Sun.COM /*
239411042SErik.Nordmark@Sun.COM  * Notify any downstream modules (esp softmac and hitbox) of the name
239511042SErik.Nordmark@Sun.COM  * of this interface using an M_CTL.
239611042SErik.Nordmark@Sun.COM  */
239711042SErik.Nordmark@Sun.COM static void
arp_ifname_notify(arl_t * arl)239811042SErik.Nordmark@Sun.COM arp_ifname_notify(arl_t *arl)
239911042SErik.Nordmark@Sun.COM {
240011042SErik.Nordmark@Sun.COM 	mblk_t *mp1, *mp2;
240111042SErik.Nordmark@Sun.COM 	struct iocblk *iocp;
240211042SErik.Nordmark@Sun.COM 	struct lifreq *lifr;
240311042SErik.Nordmark@Sun.COM 
240411042SErik.Nordmark@Sun.COM 	if ((mp1 = mkiocb(SIOCSLIFNAME)) == NULL)
240511042SErik.Nordmark@Sun.COM 		return;
240611042SErik.Nordmark@Sun.COM 	if ((mp2 = allocb(sizeof (struct lifreq), BPRI_HI)) == NULL) {
240711042SErik.Nordmark@Sun.COM 		freemsg(mp1);
240811042SErik.Nordmark@Sun.COM 		return;
240911042SErik.Nordmark@Sun.COM 	}
241011042SErik.Nordmark@Sun.COM 
241111042SErik.Nordmark@Sun.COM 	lifr = (struct lifreq *)mp2->b_rptr;
241211042SErik.Nordmark@Sun.COM 	mp2->b_wptr += sizeof (struct lifreq);
241311042SErik.Nordmark@Sun.COM 	bzero(lifr, sizeof (struct lifreq));
241411042SErik.Nordmark@Sun.COM 
241511042SErik.Nordmark@Sun.COM 	(void) strncpy(lifr->lifr_name, arl->arl_name, LIFNAMSIZ);
241611042SErik.Nordmark@Sun.COM 	lifr->lifr_ppa = arl->arl_ppa;
241711042SErik.Nordmark@Sun.COM 	lifr->lifr_flags = ILLF_IPV4;
241811042SErik.Nordmark@Sun.COM 
241911042SErik.Nordmark@Sun.COM 	/* Use M_CTL to avoid confusing anyone else who might be listening. */
242011042SErik.Nordmark@Sun.COM 	DB_TYPE(mp1) = M_CTL;
242111042SErik.Nordmark@Sun.COM 	mp1->b_cont = mp2;
242211042SErik.Nordmark@Sun.COM 	iocp = (struct iocblk *)mp1->b_rptr;
242311042SErik.Nordmark@Sun.COM 	iocp->ioc_count = msgsize(mp1->b_cont);
242411042SErik.Nordmark@Sun.COM 	DTRACE_PROBE4(arl__dlpi, char *, "arp_ifname_notify",
242511042SErik.Nordmark@Sun.COM 	    char *, "SIOCSLIFNAME", char *, "-",  arl_t *, arl);
242611042SErik.Nordmark@Sun.COM 	putnext(arl->arl_wq, mp1);
242711042SErik.Nordmark@Sun.COM }
242811042SErik.Nordmark@Sun.COM 
242911042SErik.Nordmark@Sun.COM void
arp_send_replumb_conf(ill_t * ill)243011042SErik.Nordmark@Sun.COM arp_send_replumb_conf(ill_t *ill)
243111042SErik.Nordmark@Sun.COM {
243211042SErik.Nordmark@Sun.COM 	mblk_t *mp;
243311042SErik.Nordmark@Sun.COM 	arl_t *arl = ill_to_arl(ill);
243411042SErik.Nordmark@Sun.COM 
243511042SErik.Nordmark@Sun.COM 	if (arl == NULL)
243611042SErik.Nordmark@Sun.COM 		return;
243711042SErik.Nordmark@Sun.COM 	/*
243811042SErik.Nordmark@Sun.COM 	 * arl_got_replumb and arl_got_unbind to be cleared after we complete
243911042SErik.Nordmark@Sun.COM 	 * arp_cmd_done.
244011042SErik.Nordmark@Sun.COM 	 */
244111042SErik.Nordmark@Sun.COM 	mp = mexchange(NULL, NULL, sizeof (dl_notify_conf_t), M_PROTO,
244211042SErik.Nordmark@Sun.COM 	    DL_NOTIFY_CONF);
244311042SErik.Nordmark@Sun.COM 	((dl_notify_conf_t *)(mp->b_rptr))->dl_notification =
244411042SErik.Nordmark@Sun.COM 	    DL_NOTE_REPLUMB_DONE;
244511042SErik.Nordmark@Sun.COM 	arp_dlpi_send(arl, mp);
244611042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
244711042SErik.Nordmark@Sun.COM 	arl->arl_state_flags &= ~ARL_LL_REPLUMBING;
244811042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
244911042SErik.Nordmark@Sun.COM 	arl_refrele(arl);
245011042SErik.Nordmark@Sun.COM }
245111042SErik.Nordmark@Sun.COM 
245211042SErik.Nordmark@Sun.COM /*
245311042SErik.Nordmark@Sun.COM  * The unplumb code paths call arp_unbind_complete() to make sure that it is
245411042SErik.Nordmark@Sun.COM  * safe to tear down the ill. We wait for DL_UNBIND_ACK to complete, and also
245511042SErik.Nordmark@Sun.COM  * for the arl_refcnt to fall to one so that, when we return from
245611042SErik.Nordmark@Sun.COM  * arp_unbind_complete(), we know for certain that there are no threads in
245711042SErik.Nordmark@Sun.COM  * arp_rput() that might access the arl_ill.
245811042SErik.Nordmark@Sun.COM  */
245911042SErik.Nordmark@Sun.COM void
arp_unbind_complete(ill_t * ill)246011042SErik.Nordmark@Sun.COM arp_unbind_complete(ill_t *ill)
246111042SErik.Nordmark@Sun.COM {
246211042SErik.Nordmark@Sun.COM 	arl_t *arl = ill_to_arl(ill);
246311042SErik.Nordmark@Sun.COM 
246411042SErik.Nordmark@Sun.COM 	if (arl == NULL)
246511042SErik.Nordmark@Sun.COM 		return;
246611042SErik.Nordmark@Sun.COM 	mutex_enter(&arl->arl_lock);
246711042SErik.Nordmark@Sun.COM 	/*
246811042SErik.Nordmark@Sun.COM 	 * wait for unbind ack and arl_refcnt to drop to 1. Note that the
246911042SErik.Nordmark@Sun.COM 	 * quiescent arl_refcnt for this function is 1 (and not 0) because
247011042SErik.Nordmark@Sun.COM 	 * ill_to_arl() will itself return after taking a ref on the arl_t.
247111042SErik.Nordmark@Sun.COM 	 */
247211042SErik.Nordmark@Sun.COM 	while (arl->arl_state_flags & ARL_DL_UNBIND_IN_PROGRESS)
247311042SErik.Nordmark@Sun.COM 		cv_wait(&arl->arl_cv, &arl->arl_lock);
247411042SErik.Nordmark@Sun.COM 	while (arl->arl_refcnt != 1)
247511042SErik.Nordmark@Sun.COM 		cv_wait(&arl->arl_cv, &arl->arl_lock);
247611042SErik.Nordmark@Sun.COM 	mutex_exit(&arl->arl_lock);
247711042SErik.Nordmark@Sun.COM 	arl_refrele(arl);
247811042SErik.Nordmark@Sun.COM }
2479