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 /* 23*12069SPeter.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 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 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 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 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 * 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 35811042SErik.Nordmark@Sun.COM static uint32_t 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 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 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 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 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 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 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 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 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 || 93612038SSowmini.Varadhan@Sun.COM IN_MULTICAST(src_paddr) || IN_MULTICAST(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 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 * 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 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 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 * 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 * 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 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 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 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 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 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 */ 1707*12069SPeter.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 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)) { 1742*12069SPeter.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 * 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 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 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 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 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 netstack_hold(ipst->ips_netstack); 195511042SErik.Nordmark@Sun.COM 195611042SErik.Nordmark@Sun.COM mutex_enter(&ipst->ips_ip_mi_lock); 195711042SErik.Nordmark@Sun.COM mi_close_unlink(&ipst->ips_arp_g_head, (IDP)arl); 195811042SErik.Nordmark@Sun.COM mutex_exit(&ipst->ips_ip_mi_lock); 195911042SErik.Nordmark@Sun.COM 196011042SErik.Nordmark@Sun.COM /* 196111042SErik.Nordmark@Sun.COM * credp could be null if the open didn't succeed and ip_modopen 196211042SErik.Nordmark@Sun.COM * itself calls ip_close. 196311042SErik.Nordmark@Sun.COM */ 196411042SErik.Nordmark@Sun.COM if (arl->arl_credp != NULL) 196511042SErik.Nordmark@Sun.COM crfree(arl->arl_credp); 196611042SErik.Nordmark@Sun.COM 196711042SErik.Nordmark@Sun.COM /* Free all retained control messages. */ 196811042SErik.Nordmark@Sun.COM mpp = &arl->arl_first_mp_to_free; 196911042SErik.Nordmark@Sun.COM do { 197011042SErik.Nordmark@Sun.COM while (mpp[0]) { 197111042SErik.Nordmark@Sun.COM mblk_t *mp; 197211042SErik.Nordmark@Sun.COM mblk_t *mp1; 197311042SErik.Nordmark@Sun.COM 197411042SErik.Nordmark@Sun.COM mp = mpp[0]; 197511042SErik.Nordmark@Sun.COM mpp[0] = mp->b_next; 197611042SErik.Nordmark@Sun.COM for (mp1 = mp; mp1 != NULL; mp1 = mp1->b_cont) { 197711042SErik.Nordmark@Sun.COM mp1->b_next = NULL; 197811042SErik.Nordmark@Sun.COM mp1->b_prev = NULL; 197911042SErik.Nordmark@Sun.COM } 198011042SErik.Nordmark@Sun.COM freemsg(mp); 198111042SErik.Nordmark@Sun.COM } 198211042SErik.Nordmark@Sun.COM } while (mpp++ != &arl->arl_last_mp_to_free); 198311042SErik.Nordmark@Sun.COM 198411042SErik.Nordmark@Sun.COM netstack_rele(ipst->ips_netstack); 198511042SErik.Nordmark@Sun.COM mi_free(arl->arl_name); 198611042SErik.Nordmark@Sun.COM mi_close_free((IDP)arl); 198711042SErik.Nordmark@Sun.COM } 198811042SErik.Nordmark@Sun.COM 198911042SErik.Nordmark@Sun.COM /* 199011042SErik.Nordmark@Sun.COM * DAD failed. Tear down ipifs with the specified srce address. Note that 199111042SErik.Nordmark@Sun.COM * tearing down the ipif also meas deleting the ncec through ipif_down, 199211042SErik.Nordmark@Sun.COM * so it is not possible to use nce_timer for recovery. Instead we start 199311042SErik.Nordmark@Sun.COM * a timer on the ipif. Caller has to free the mp. 199411042SErik.Nordmark@Sun.COM */ 199511042SErik.Nordmark@Sun.COM void 199611042SErik.Nordmark@Sun.COM arp_failure(mblk_t *mp, ip_recv_attr_t *ira) 199711042SErik.Nordmark@Sun.COM { 199811042SErik.Nordmark@Sun.COM ill_t *ill = ira->ira_ill; 199911042SErik.Nordmark@Sun.COM 200011042SErik.Nordmark@Sun.COM if ((mp = copymsg(mp)) != NULL) { 200111042SErik.Nordmark@Sun.COM ill_refhold(ill); 200211042SErik.Nordmark@Sun.COM qwriter_ip(ill, ill->ill_rq, mp, arp_excl, NEW_OP, B_FALSE); 200311042SErik.Nordmark@Sun.COM } 200411042SErik.Nordmark@Sun.COM } 200511042SErik.Nordmark@Sun.COM 200611042SErik.Nordmark@Sun.COM /* 200711042SErik.Nordmark@Sun.COM * This is for exclusive changes due to ARP. Tear down an interface due 200811042SErik.Nordmark@Sun.COM * to AR_CN_FAILED and AR_CN_BOGON. 200911042SErik.Nordmark@Sun.COM */ 201011042SErik.Nordmark@Sun.COM /* ARGSUSED */ 201111042SErik.Nordmark@Sun.COM static void 201211042SErik.Nordmark@Sun.COM arp_excl(ipsq_t *ipsq, queue_t *rq, mblk_t *mp, void *dummy_arg) 201311042SErik.Nordmark@Sun.COM { 201411042SErik.Nordmark@Sun.COM ill_t *ill = rq->q_ptr; 201511042SErik.Nordmark@Sun.COM arh_t *arh; 201611042SErik.Nordmark@Sun.COM ipaddr_t src; 201711042SErik.Nordmark@Sun.COM ipif_t *ipif; 201811042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ill->ill_ipst; 201911042SErik.Nordmark@Sun.COM uchar_t *haddr; 202011042SErik.Nordmark@Sun.COM uint_t haddrlen; 202111042SErik.Nordmark@Sun.COM 202211042SErik.Nordmark@Sun.COM /* first try src = ar$spa */ 202311042SErik.Nordmark@Sun.COM arh = (arh_t *)mp->b_rptr; 202411042SErik.Nordmark@Sun.COM bcopy((char *)&arh[1] + arh->arh_hlen, &src, IP_ADDR_LEN); 202511042SErik.Nordmark@Sun.COM 202611042SErik.Nordmark@Sun.COM haddrlen = arh->arh_hlen; 202711042SErik.Nordmark@Sun.COM haddr = (uint8_t *)(arh + 1); 202811042SErik.Nordmark@Sun.COM 202911042SErik.Nordmark@Sun.COM if (haddrlen == ill->ill_phys_addr_length) { 203011042SErik.Nordmark@Sun.COM /* 203111042SErik.Nordmark@Sun.COM * Ignore conflicts generated by misbehaving switches that 203211042SErik.Nordmark@Sun.COM * just reflect our own messages back to us. For IPMP, we may 203311042SErik.Nordmark@Sun.COM * see reflections across any ill in the illgrp. 203411042SErik.Nordmark@Sun.COM */ 203511042SErik.Nordmark@Sun.COM /* For an under ill_grp can change under lock */ 203611042SErik.Nordmark@Sun.COM rw_enter(&ipst->ips_ill_g_lock, RW_READER); 203711042SErik.Nordmark@Sun.COM if (bcmp(haddr, ill->ill_phys_addr, haddrlen) == 0 || 203811042SErik.Nordmark@Sun.COM IS_UNDER_IPMP(ill) && ill->ill_grp != NULL && 203911042SErik.Nordmark@Sun.COM ipmp_illgrp_find_ill(ill->ill_grp, haddr, 204011042SErik.Nordmark@Sun.COM haddrlen) != NULL) { 204111042SErik.Nordmark@Sun.COM rw_exit(&ipst->ips_ill_g_lock); 204211042SErik.Nordmark@Sun.COM goto ignore_conflict; 204311042SErik.Nordmark@Sun.COM } 204411042SErik.Nordmark@Sun.COM rw_exit(&ipst->ips_ill_g_lock); 204511042SErik.Nordmark@Sun.COM } 204611042SErik.Nordmark@Sun.COM 204711042SErik.Nordmark@Sun.COM /* 204811042SErik.Nordmark@Sun.COM * Look up the appropriate ipif. 204911042SErik.Nordmark@Sun.COM */ 205011042SErik.Nordmark@Sun.COM ipif = ipif_lookup_addr(src, ill, ALL_ZONES, ipst); 205111042SErik.Nordmark@Sun.COM if (ipif == NULL) 205211042SErik.Nordmark@Sun.COM goto ignore_conflict; 205311042SErik.Nordmark@Sun.COM 205411042SErik.Nordmark@Sun.COM /* Reload the ill to match the ipif */ 205511042SErik.Nordmark@Sun.COM ill = ipif->ipif_ill; 205611042SErik.Nordmark@Sun.COM 205711042SErik.Nordmark@Sun.COM /* If it's already duplicate or ineligible, then don't do anything. */ 205811042SErik.Nordmark@Sun.COM if (ipif->ipif_flags & (IPIF_POINTOPOINT|IPIF_DUPLICATE)) { 205911042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 206011042SErik.Nordmark@Sun.COM goto ignore_conflict; 206111042SErik.Nordmark@Sun.COM } 206211042SErik.Nordmark@Sun.COM 206311042SErik.Nordmark@Sun.COM /* 206411042SErik.Nordmark@Sun.COM * If we failed on a recovery probe, then restart the timer to 206511042SErik.Nordmark@Sun.COM * try again later. 206611042SErik.Nordmark@Sun.COM */ 206711042SErik.Nordmark@Sun.COM if (!ipif->ipif_was_dup) { 206811042SErik.Nordmark@Sun.COM char hbuf[MAC_STR_LEN]; 206911042SErik.Nordmark@Sun.COM char sbuf[INET_ADDRSTRLEN]; 207011042SErik.Nordmark@Sun.COM char ibuf[LIFNAMSIZ]; 207111042SErik.Nordmark@Sun.COM 207211042SErik.Nordmark@Sun.COM (void) mac_colon_addr(haddr, haddrlen, hbuf, sizeof (hbuf)); 207311042SErik.Nordmark@Sun.COM (void) ip_dot_addr(src, sbuf); 207411042SErik.Nordmark@Sun.COM ipif_get_name(ipif, ibuf, sizeof (ibuf)); 207511042SErik.Nordmark@Sun.COM 207611042SErik.Nordmark@Sun.COM cmn_err(CE_WARN, "%s has duplicate address %s (in use by %s);" 207711042SErik.Nordmark@Sun.COM " disabled", ibuf, sbuf, hbuf); 207811042SErik.Nordmark@Sun.COM } 207911042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_lock); 208011042SErik.Nordmark@Sun.COM ASSERT(!(ipif->ipif_flags & IPIF_DUPLICATE)); 208111042SErik.Nordmark@Sun.COM ipif->ipif_flags |= IPIF_DUPLICATE; 208211042SErik.Nordmark@Sun.COM ill->ill_ipif_dup_count++; 208311042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 208411042SErik.Nordmark@Sun.COM (void) ipif_down(ipif, NULL, NULL); 208511042SErik.Nordmark@Sun.COM (void) ipif_down_tail(ipif); 208611042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_lock); 208711042SErik.Nordmark@Sun.COM if (!(ipif->ipif_flags & (IPIF_DHCPRUNNING|IPIF_TEMPORARY)) && 208811042SErik.Nordmark@Sun.COM ill->ill_net_type == IRE_IF_RESOLVER && 208911042SErik.Nordmark@Sun.COM !(ipif->ipif_state_flags & IPIF_CONDEMNED) && 209011042SErik.Nordmark@Sun.COM ipst->ips_ip_dup_recovery > 0) { 209111042SErik.Nordmark@Sun.COM ASSERT(ipif->ipif_recovery_id == 0); 209211042SErik.Nordmark@Sun.COM ipif->ipif_recovery_id = timeout(ipif_dup_recovery, 209311042SErik.Nordmark@Sun.COM ipif, MSEC_TO_TICK(ipst->ips_ip_dup_recovery)); 209411042SErik.Nordmark@Sun.COM } 209511042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 209611042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 209711042SErik.Nordmark@Sun.COM 209811042SErik.Nordmark@Sun.COM ignore_conflict: 209911042SErik.Nordmark@Sun.COM freemsg(mp); 210011042SErik.Nordmark@Sun.COM } 210111042SErik.Nordmark@Sun.COM 210211042SErik.Nordmark@Sun.COM /* 210311042SErik.Nordmark@Sun.COM * This is a place for a dtrace hook. 210411042SErik.Nordmark@Sun.COM * Note that mp can be either the DL_UNITDATA_IND with a b_cont payload, 210511042SErik.Nordmark@Sun.COM * or just the ARP packet payload as an M_DATA. 210611042SErik.Nordmark@Sun.COM */ 210711042SErik.Nordmark@Sun.COM /* ARGSUSED */ 210811042SErik.Nordmark@Sun.COM static void 210911042SErik.Nordmark@Sun.COM arp_drop_packet(const char *str, mblk_t *mp, ill_t *ill) 211011042SErik.Nordmark@Sun.COM { 211111042SErik.Nordmark@Sun.COM freemsg(mp); 211211042SErik.Nordmark@Sun.COM } 211311042SErik.Nordmark@Sun.COM 211411042SErik.Nordmark@Sun.COM static boolean_t 211511042SErik.Nordmark@Sun.COM arp_over_driver(queue_t *q) 211611042SErik.Nordmark@Sun.COM { 211711042SErik.Nordmark@Sun.COM queue_t *qnext = STREAM(q)->sd_wrq->q_next; 211811042SErik.Nordmark@Sun.COM 211911042SErik.Nordmark@Sun.COM /* 212011042SErik.Nordmark@Sun.COM * check if first module below stream head is IP or UDP. 212111042SErik.Nordmark@Sun.COM */ 212211042SErik.Nordmark@Sun.COM ASSERT(qnext != NULL); 212311042SErik.Nordmark@Sun.COM if (strcmp(Q2NAME(qnext), "ip") != 0 && 212411042SErik.Nordmark@Sun.COM strcmp(Q2NAME(qnext), "udp") != 0) { 212511042SErik.Nordmark@Sun.COM /* 212611042SErik.Nordmark@Sun.COM * module below is not ip or udp, so arp has been pushed 212711042SErik.Nordmark@Sun.COM * on the driver. 212811042SErik.Nordmark@Sun.COM */ 212911042SErik.Nordmark@Sun.COM return (B_TRUE); 213011042SErik.Nordmark@Sun.COM } 213111042SErik.Nordmark@Sun.COM return (B_FALSE); 213211042SErik.Nordmark@Sun.COM } 213311042SErik.Nordmark@Sun.COM 213411042SErik.Nordmark@Sun.COM static int 213511042SErik.Nordmark@Sun.COM arp_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 213611042SErik.Nordmark@Sun.COM { 213711042SErik.Nordmark@Sun.COM int err; 213811042SErik.Nordmark@Sun.COM 213911042SErik.Nordmark@Sun.COM ASSERT(sflag & MODOPEN); 214011042SErik.Nordmark@Sun.COM if (!arp_over_driver(q)) { 214111042SErik.Nordmark@Sun.COM q->q_qinfo = dummymodinfo.st_rdinit; 214211042SErik.Nordmark@Sun.COM WR(q)->q_qinfo = dummymodinfo.st_wrinit; 214311042SErik.Nordmark@Sun.COM return ((*dummymodinfo.st_rdinit->qi_qopen)(q, devp, flag, 214411042SErik.Nordmark@Sun.COM sflag, credp)); 214511042SErik.Nordmark@Sun.COM } 214611042SErik.Nordmark@Sun.COM err = arp_modopen(q, devp, flag, sflag, credp); 214711042SErik.Nordmark@Sun.COM return (err); 214811042SErik.Nordmark@Sun.COM } 214911042SErik.Nordmark@Sun.COM 215011042SErik.Nordmark@Sun.COM /* 215111042SErik.Nordmark@Sun.COM * In most cases we must be a writer on the IP stream before coming to 215211042SErik.Nordmark@Sun.COM * arp_dlpi_send(), to serialize DLPI sends to the driver. The exceptions 215311042SErik.Nordmark@Sun.COM * when we are not a writer are very early duing initialization (in 215411042SErik.Nordmark@Sun.COM * arl_init, before the arl has done a SLIFNAME, so that we don't yet know 215511042SErik.Nordmark@Sun.COM * the associated ill) or during arp_mod_close, when we could not enter the 215611042SErik.Nordmark@Sun.COM * ipsq because the ill has already unplumbed. 215711042SErik.Nordmark@Sun.COM */ 215811042SErik.Nordmark@Sun.COM static void 215911042SErik.Nordmark@Sun.COM arp_dlpi_send(arl_t *arl, mblk_t *mp) 216011042SErik.Nordmark@Sun.COM { 216111042SErik.Nordmark@Sun.COM mblk_t **mpp; 216211042SErik.Nordmark@Sun.COM t_uscalar_t prim; 216311042SErik.Nordmark@Sun.COM arl_ill_common_t *ai; 216411042SErik.Nordmark@Sun.COM 216511042SErik.Nordmark@Sun.COM ASSERT(DB_TYPE(mp) == M_PROTO || DB_TYPE(mp) == M_PCPROTO); 216611042SErik.Nordmark@Sun.COM 216711042SErik.Nordmark@Sun.COM #ifdef DEBUG 216811042SErik.Nordmark@Sun.COM ai = arl->arl_common; 216911042SErik.Nordmark@Sun.COM if (ai != NULL) { 217011042SErik.Nordmark@Sun.COM mutex_enter(&ai->ai_lock); 217111042SErik.Nordmark@Sun.COM if (ai->ai_ill != NULL) 217211042SErik.Nordmark@Sun.COM ASSERT(IAM_WRITER_ILL(ai->ai_ill)); 217311042SErik.Nordmark@Sun.COM mutex_exit(&ai->ai_lock); 217411042SErik.Nordmark@Sun.COM } 217511042SErik.Nordmark@Sun.COM #endif /* DEBUG */ 217611042SErik.Nordmark@Sun.COM 217711042SErik.Nordmark@Sun.COM mutex_enter(&arl->arl_lock); 217811042SErik.Nordmark@Sun.COM if (arl->arl_dlpi_pending != DL_PRIM_INVAL) { 217911042SErik.Nordmark@Sun.COM /* Must queue message. Tail insertion */ 218011042SErik.Nordmark@Sun.COM mpp = &arl->arl_dlpi_deferred; 218111042SErik.Nordmark@Sun.COM while (*mpp != NULL) 218211042SErik.Nordmark@Sun.COM mpp = &((*mpp)->b_next); 218311042SErik.Nordmark@Sun.COM 218411042SErik.Nordmark@Sun.COM *mpp = mp; 218511042SErik.Nordmark@Sun.COM mutex_exit(&arl->arl_lock); 218611042SErik.Nordmark@Sun.COM return; 218711042SErik.Nordmark@Sun.COM } 218811042SErik.Nordmark@Sun.COM mutex_exit(&arl->arl_lock); 218911042SErik.Nordmark@Sun.COM if ((prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive) 219011042SErik.Nordmark@Sun.COM == DL_BIND_REQ) { 219111042SErik.Nordmark@Sun.COM ASSERT((arl->arl_state_flags & ARL_DL_UNBIND_IN_PROGRESS) == 0); 219211042SErik.Nordmark@Sun.COM } 219311042SErik.Nordmark@Sun.COM /* 219411042SErik.Nordmark@Sun.COM * No need to take the arl_lock to examine ARL_CONDEMNED at this point 219511042SErik.Nordmark@Sun.COM * because the only thread that can see ARL_CONDEMNED here is the 219611042SErik.Nordmark@Sun.COM * closing arp_modclose() thread which sets the flag after becoming a 219711042SErik.Nordmark@Sun.COM * writer on the ipsq. Threads from IP must have finished and 219811042SErik.Nordmark@Sun.COM * cannot be active now. 219911042SErik.Nordmark@Sun.COM */ 220011042SErik.Nordmark@Sun.COM if (!(arl->arl_state_flags & ARL_CONDEMNED) || 220111042SErik.Nordmark@Sun.COM (prim == DL_UNBIND_REQ)) { 220211042SErik.Nordmark@Sun.COM if (prim != DL_NOTIFY_CONF) { 220311042SErik.Nordmark@Sun.COM ill_t *ill = arl_to_ill(arl); 220411042SErik.Nordmark@Sun.COM 220511042SErik.Nordmark@Sun.COM arl->arl_dlpi_pending = prim; 220611042SErik.Nordmark@Sun.COM if (ill != NULL) { 220711042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_lock); 220811042SErik.Nordmark@Sun.COM ill->ill_arl_dlpi_pending = 1; 220911042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 221011042SErik.Nordmark@Sun.COM ill_refrele(ill); 221111042SErik.Nordmark@Sun.COM } 221211042SErik.Nordmark@Sun.COM } 221311042SErik.Nordmark@Sun.COM } 221411042SErik.Nordmark@Sun.COM DTRACE_PROBE4(arl__dlpi, char *, "arp_dlpi_send", 221511042SErik.Nordmark@Sun.COM char *, dl_primstr(prim), char *, "-", arl_t *, arl); 221611042SErik.Nordmark@Sun.COM putnext(arl->arl_wq, mp); 221711042SErik.Nordmark@Sun.COM } 221811042SErik.Nordmark@Sun.COM 221911042SErik.Nordmark@Sun.COM static void 222011042SErik.Nordmark@Sun.COM arl_defaults_common(arl_t *arl, mblk_t *mp) 222111042SErik.Nordmark@Sun.COM { 222211042SErik.Nordmark@Sun.COM dl_info_ack_t *dlia = (dl_info_ack_t *)mp->b_rptr; 222311042SErik.Nordmark@Sun.COM /* 222411042SErik.Nordmark@Sun.COM * Till the ill is fully up the ill is not globally visible. 222511042SErik.Nordmark@Sun.COM * So no need for a lock. 222611042SErik.Nordmark@Sun.COM */ 222711042SErik.Nordmark@Sun.COM arl->arl_mactype = dlia->dl_mac_type; 222811042SErik.Nordmark@Sun.COM arl->arl_sap_length = dlia->dl_sap_length; 222911042SErik.Nordmark@Sun.COM 223011042SErik.Nordmark@Sun.COM if (!arl->arl_dlpi_style_set) { 223111042SErik.Nordmark@Sun.COM if (dlia->dl_provider_style == DL_STYLE2) 223211042SErik.Nordmark@Sun.COM arl->arl_needs_attach = 1; 223311042SErik.Nordmark@Sun.COM mutex_enter(&arl->arl_lock); 223411042SErik.Nordmark@Sun.COM ASSERT(arl->arl_dlpi_style_set == 0); 223511042SErik.Nordmark@Sun.COM arl->arl_dlpi_style_set = 1; 223611042SErik.Nordmark@Sun.COM arl->arl_state_flags &= ~ARL_LL_SUBNET_PENDING; 223711042SErik.Nordmark@Sun.COM cv_broadcast(&arl->arl_cv); 223811042SErik.Nordmark@Sun.COM mutex_exit(&arl->arl_lock); 223911042SErik.Nordmark@Sun.COM } 224011042SErik.Nordmark@Sun.COM } 224111042SErik.Nordmark@Sun.COM 224211042SErik.Nordmark@Sun.COM int 224311042SErik.Nordmark@Sun.COM arl_init(queue_t *q, arl_t *arl) 224411042SErik.Nordmark@Sun.COM { 224511042SErik.Nordmark@Sun.COM mblk_t *info_mp; 224611042SErik.Nordmark@Sun.COM dl_info_req_t *dlir; 224711042SErik.Nordmark@Sun.COM 224811042SErik.Nordmark@Sun.COM /* subset of ill_init */ 224911042SErik.Nordmark@Sun.COM mutex_init(&arl->arl_lock, NULL, MUTEX_DEFAULT, 0); 225011042SErik.Nordmark@Sun.COM 225111042SErik.Nordmark@Sun.COM arl->arl_rq = q; 225211042SErik.Nordmark@Sun.COM arl->arl_wq = WR(q); 225311042SErik.Nordmark@Sun.COM 225411042SErik.Nordmark@Sun.COM info_mp = allocb(MAX(sizeof (dl_info_req_t), sizeof (dl_info_ack_t)), 225511042SErik.Nordmark@Sun.COM BPRI_HI); 225611042SErik.Nordmark@Sun.COM if (info_mp == NULL) 225711042SErik.Nordmark@Sun.COM return (ENOMEM); 225811042SErik.Nordmark@Sun.COM /* 225911042SErik.Nordmark@Sun.COM * allocate sufficient space to contain device name. 226011042SErik.Nordmark@Sun.COM */ 226111042SErik.Nordmark@Sun.COM arl->arl_name = (char *)(mi_zalloc(2 * LIFNAMSIZ)); 226211042SErik.Nordmark@Sun.COM arl->arl_ppa = UINT_MAX; 226311042SErik.Nordmark@Sun.COM arl->arl_state_flags |= (ARL_LL_SUBNET_PENDING | ARL_LL_UNBOUND); 226411042SErik.Nordmark@Sun.COM 226511042SErik.Nordmark@Sun.COM /* Send down the Info Request to the driver. */ 226611042SErik.Nordmark@Sun.COM info_mp->b_datap->db_type = M_PCPROTO; 226711042SErik.Nordmark@Sun.COM dlir = (dl_info_req_t *)info_mp->b_rptr; 226811042SErik.Nordmark@Sun.COM info_mp->b_wptr = (uchar_t *)&dlir[1]; 226911042SErik.Nordmark@Sun.COM dlir->dl_primitive = DL_INFO_REQ; 227011042SErik.Nordmark@Sun.COM arl->arl_dlpi_pending = DL_PRIM_INVAL; 227111042SErik.Nordmark@Sun.COM qprocson(q); 227211042SErik.Nordmark@Sun.COM 227311042SErik.Nordmark@Sun.COM arp_dlpi_send(arl, info_mp); 227411042SErik.Nordmark@Sun.COM return (0); 227511042SErik.Nordmark@Sun.COM } 227611042SErik.Nordmark@Sun.COM 227711042SErik.Nordmark@Sun.COM int 227811042SErik.Nordmark@Sun.COM arl_wait_for_info_ack(arl_t *arl) 227911042SErik.Nordmark@Sun.COM { 228011042SErik.Nordmark@Sun.COM int err; 228111042SErik.Nordmark@Sun.COM 228211042SErik.Nordmark@Sun.COM mutex_enter(&arl->arl_lock); 228311042SErik.Nordmark@Sun.COM while (arl->arl_state_flags & ARL_LL_SUBNET_PENDING) { 228411042SErik.Nordmark@Sun.COM /* 228511042SErik.Nordmark@Sun.COM * Return value of 0 indicates a pending signal. 228611042SErik.Nordmark@Sun.COM */ 228711042SErik.Nordmark@Sun.COM err = cv_wait_sig(&arl->arl_cv, &arl->arl_lock); 228811042SErik.Nordmark@Sun.COM if (err == 0) { 228911042SErik.Nordmark@Sun.COM mutex_exit(&arl->arl_lock); 229011042SErik.Nordmark@Sun.COM return (EINTR); 229111042SErik.Nordmark@Sun.COM } 229211042SErik.Nordmark@Sun.COM } 229311042SErik.Nordmark@Sun.COM mutex_exit(&arl->arl_lock); 229411042SErik.Nordmark@Sun.COM /* 229511042SErik.Nordmark@Sun.COM * ip_rput_other could have set an error in ill_error on 229611042SErik.Nordmark@Sun.COM * receipt of M_ERROR. 229711042SErik.Nordmark@Sun.COM */ 229811042SErik.Nordmark@Sun.COM return (arl->arl_error); 229911042SErik.Nordmark@Sun.COM } 230011042SErik.Nordmark@Sun.COM 230111042SErik.Nordmark@Sun.COM void 230211042SErik.Nordmark@Sun.COM arl_set_muxid(ill_t *ill, int muxid) 230311042SErik.Nordmark@Sun.COM { 230411042SErik.Nordmark@Sun.COM arl_t *arl; 230511042SErik.Nordmark@Sun.COM 230611042SErik.Nordmark@Sun.COM arl = ill_to_arl(ill); 230711042SErik.Nordmark@Sun.COM if (arl != NULL) { 230811042SErik.Nordmark@Sun.COM arl->arl_muxid = muxid; 230911042SErik.Nordmark@Sun.COM arl_refrele(arl); 231011042SErik.Nordmark@Sun.COM } 231111042SErik.Nordmark@Sun.COM } 231211042SErik.Nordmark@Sun.COM 231311042SErik.Nordmark@Sun.COM int 231411042SErik.Nordmark@Sun.COM arl_get_muxid(ill_t *ill) 231511042SErik.Nordmark@Sun.COM { 231611042SErik.Nordmark@Sun.COM arl_t *arl; 231711042SErik.Nordmark@Sun.COM int muxid = 0; 231811042SErik.Nordmark@Sun.COM 231911042SErik.Nordmark@Sun.COM arl = ill_to_arl(ill); 232011042SErik.Nordmark@Sun.COM if (arl != NULL) { 232111042SErik.Nordmark@Sun.COM muxid = arl->arl_muxid; 232211042SErik.Nordmark@Sun.COM arl_refrele(arl); 232311042SErik.Nordmark@Sun.COM } 232411042SErik.Nordmark@Sun.COM return (muxid); 232511042SErik.Nordmark@Sun.COM } 232611042SErik.Nordmark@Sun.COM 232711042SErik.Nordmark@Sun.COM static int 232811042SErik.Nordmark@Sun.COM arp_modopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 232911042SErik.Nordmark@Sun.COM { 233011042SErik.Nordmark@Sun.COM int err; 233111042SErik.Nordmark@Sun.COM zoneid_t zoneid; 233211042SErik.Nordmark@Sun.COM netstack_t *ns; 233311042SErik.Nordmark@Sun.COM ip_stack_t *ipst; 233411042SErik.Nordmark@Sun.COM arl_t *arl = NULL; 233511042SErik.Nordmark@Sun.COM 233611042SErik.Nordmark@Sun.COM /* 233711042SErik.Nordmark@Sun.COM * Prevent unprivileged processes from pushing IP so that 233811042SErik.Nordmark@Sun.COM * they can't send raw IP. 233911042SErik.Nordmark@Sun.COM */ 234011042SErik.Nordmark@Sun.COM if (secpolicy_net_rawaccess(credp) != 0) 234111042SErik.Nordmark@Sun.COM return (EPERM); 234211042SErik.Nordmark@Sun.COM 234311042SErik.Nordmark@Sun.COM ns = netstack_find_by_cred(credp); 234411042SErik.Nordmark@Sun.COM ASSERT(ns != NULL); 234511042SErik.Nordmark@Sun.COM ipst = ns->netstack_ip; 234611042SErik.Nordmark@Sun.COM ASSERT(ipst != NULL); 234711042SErik.Nordmark@Sun.COM 234811042SErik.Nordmark@Sun.COM /* 234911042SErik.Nordmark@Sun.COM * For exclusive stacks we set the zoneid to zero 235011042SErik.Nordmark@Sun.COM * to make IP operate as if in the global zone. 235111042SErik.Nordmark@Sun.COM */ 235211042SErik.Nordmark@Sun.COM if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID) 235311042SErik.Nordmark@Sun.COM zoneid = GLOBAL_ZONEID; 235411042SErik.Nordmark@Sun.COM else 235511042SErik.Nordmark@Sun.COM zoneid = crgetzoneid(credp); 235611042SErik.Nordmark@Sun.COM 235711042SErik.Nordmark@Sun.COM arl = (arl_t *)mi_open_alloc_sleep(sizeof (arl_t)); 235811042SErik.Nordmark@Sun.COM q->q_ptr = WR(q)->q_ptr = arl; 235911042SErik.Nordmark@Sun.COM arl->arl_ipst = ipst; 236011042SErik.Nordmark@Sun.COM arl->arl_zoneid = zoneid; 236111042SErik.Nordmark@Sun.COM err = arl_init(q, arl); 236211042SErik.Nordmark@Sun.COM 236311042SErik.Nordmark@Sun.COM if (err != 0) { 236411042SErik.Nordmark@Sun.COM mi_free(arl->arl_name); 236511042SErik.Nordmark@Sun.COM mi_free(arl); 236611042SErik.Nordmark@Sun.COM netstack_rele(ipst->ips_netstack); 236711042SErik.Nordmark@Sun.COM q->q_ptr = NULL; 236811042SErik.Nordmark@Sun.COM WR(q)->q_ptr = NULL; 236911042SErik.Nordmark@Sun.COM return (err); 237011042SErik.Nordmark@Sun.COM } 237111042SErik.Nordmark@Sun.COM 237211042SErik.Nordmark@Sun.COM /* 237311042SErik.Nordmark@Sun.COM * Wait for the DL_INFO_ACK if a DL_INFO_REQ was sent. 237411042SErik.Nordmark@Sun.COM */ 237511042SErik.Nordmark@Sun.COM err = arl_wait_for_info_ack(arl); 237611042SErik.Nordmark@Sun.COM if (err == 0) 237711042SErik.Nordmark@Sun.COM arl->arl_credp = credp; 237811042SErik.Nordmark@Sun.COM else 237911042SErik.Nordmark@Sun.COM goto fail; 238011042SErik.Nordmark@Sun.COM 238111042SErik.Nordmark@Sun.COM crhold(credp); 238211042SErik.Nordmark@Sun.COM 238311042SErik.Nordmark@Sun.COM mutex_enter(&ipst->ips_ip_mi_lock); 238411042SErik.Nordmark@Sun.COM err = mi_open_link(&ipst->ips_arp_g_head, (IDP)q->q_ptr, devp, flag, 238511042SErik.Nordmark@Sun.COM sflag, credp); 238611042SErik.Nordmark@Sun.COM mutex_exit(&ipst->ips_ip_mi_lock); 238711042SErik.Nordmark@Sun.COM fail: 238811042SErik.Nordmark@Sun.COM if (err) { 238911042SErik.Nordmark@Sun.COM (void) arp_close(q, 0); 239011042SErik.Nordmark@Sun.COM return (err); 239111042SErik.Nordmark@Sun.COM } 239211042SErik.Nordmark@Sun.COM return (0); 239311042SErik.Nordmark@Sun.COM } 239411042SErik.Nordmark@Sun.COM 239511042SErik.Nordmark@Sun.COM /* 239611042SErik.Nordmark@Sun.COM * Notify any downstream modules (esp softmac and hitbox) of the name 239711042SErik.Nordmark@Sun.COM * of this interface using an M_CTL. 239811042SErik.Nordmark@Sun.COM */ 239911042SErik.Nordmark@Sun.COM static void 240011042SErik.Nordmark@Sun.COM arp_ifname_notify(arl_t *arl) 240111042SErik.Nordmark@Sun.COM { 240211042SErik.Nordmark@Sun.COM mblk_t *mp1, *mp2; 240311042SErik.Nordmark@Sun.COM struct iocblk *iocp; 240411042SErik.Nordmark@Sun.COM struct lifreq *lifr; 240511042SErik.Nordmark@Sun.COM 240611042SErik.Nordmark@Sun.COM if ((mp1 = mkiocb(SIOCSLIFNAME)) == NULL) 240711042SErik.Nordmark@Sun.COM return; 240811042SErik.Nordmark@Sun.COM if ((mp2 = allocb(sizeof (struct lifreq), BPRI_HI)) == NULL) { 240911042SErik.Nordmark@Sun.COM freemsg(mp1); 241011042SErik.Nordmark@Sun.COM return; 241111042SErik.Nordmark@Sun.COM } 241211042SErik.Nordmark@Sun.COM 241311042SErik.Nordmark@Sun.COM lifr = (struct lifreq *)mp2->b_rptr; 241411042SErik.Nordmark@Sun.COM mp2->b_wptr += sizeof (struct lifreq); 241511042SErik.Nordmark@Sun.COM bzero(lifr, sizeof (struct lifreq)); 241611042SErik.Nordmark@Sun.COM 241711042SErik.Nordmark@Sun.COM (void) strncpy(lifr->lifr_name, arl->arl_name, LIFNAMSIZ); 241811042SErik.Nordmark@Sun.COM lifr->lifr_ppa = arl->arl_ppa; 241911042SErik.Nordmark@Sun.COM lifr->lifr_flags = ILLF_IPV4; 242011042SErik.Nordmark@Sun.COM 242111042SErik.Nordmark@Sun.COM /* Use M_CTL to avoid confusing anyone else who might be listening. */ 242211042SErik.Nordmark@Sun.COM DB_TYPE(mp1) = M_CTL; 242311042SErik.Nordmark@Sun.COM mp1->b_cont = mp2; 242411042SErik.Nordmark@Sun.COM iocp = (struct iocblk *)mp1->b_rptr; 242511042SErik.Nordmark@Sun.COM iocp->ioc_count = msgsize(mp1->b_cont); 242611042SErik.Nordmark@Sun.COM DTRACE_PROBE4(arl__dlpi, char *, "arp_ifname_notify", 242711042SErik.Nordmark@Sun.COM char *, "SIOCSLIFNAME", char *, "-", arl_t *, arl); 242811042SErik.Nordmark@Sun.COM putnext(arl->arl_wq, mp1); 242911042SErik.Nordmark@Sun.COM } 243011042SErik.Nordmark@Sun.COM 243111042SErik.Nordmark@Sun.COM void 243211042SErik.Nordmark@Sun.COM arp_send_replumb_conf(ill_t *ill) 243311042SErik.Nordmark@Sun.COM { 243411042SErik.Nordmark@Sun.COM mblk_t *mp; 243511042SErik.Nordmark@Sun.COM arl_t *arl = ill_to_arl(ill); 243611042SErik.Nordmark@Sun.COM 243711042SErik.Nordmark@Sun.COM if (arl == NULL) 243811042SErik.Nordmark@Sun.COM return; 243911042SErik.Nordmark@Sun.COM /* 244011042SErik.Nordmark@Sun.COM * arl_got_replumb and arl_got_unbind to be cleared after we complete 244111042SErik.Nordmark@Sun.COM * arp_cmd_done. 244211042SErik.Nordmark@Sun.COM */ 244311042SErik.Nordmark@Sun.COM mp = mexchange(NULL, NULL, sizeof (dl_notify_conf_t), M_PROTO, 244411042SErik.Nordmark@Sun.COM DL_NOTIFY_CONF); 244511042SErik.Nordmark@Sun.COM ((dl_notify_conf_t *)(mp->b_rptr))->dl_notification = 244611042SErik.Nordmark@Sun.COM DL_NOTE_REPLUMB_DONE; 244711042SErik.Nordmark@Sun.COM arp_dlpi_send(arl, mp); 244811042SErik.Nordmark@Sun.COM mutex_enter(&arl->arl_lock); 244911042SErik.Nordmark@Sun.COM arl->arl_state_flags &= ~ARL_LL_REPLUMBING; 245011042SErik.Nordmark@Sun.COM mutex_exit(&arl->arl_lock); 245111042SErik.Nordmark@Sun.COM arl_refrele(arl); 245211042SErik.Nordmark@Sun.COM } 245311042SErik.Nordmark@Sun.COM 245411042SErik.Nordmark@Sun.COM /* 245511042SErik.Nordmark@Sun.COM * The unplumb code paths call arp_unbind_complete() to make sure that it is 245611042SErik.Nordmark@Sun.COM * safe to tear down the ill. We wait for DL_UNBIND_ACK to complete, and also 245711042SErik.Nordmark@Sun.COM * for the arl_refcnt to fall to one so that, when we return from 245811042SErik.Nordmark@Sun.COM * arp_unbind_complete(), we know for certain that there are no threads in 245911042SErik.Nordmark@Sun.COM * arp_rput() that might access the arl_ill. 246011042SErik.Nordmark@Sun.COM */ 246111042SErik.Nordmark@Sun.COM void 246211042SErik.Nordmark@Sun.COM arp_unbind_complete(ill_t *ill) 246311042SErik.Nordmark@Sun.COM { 246411042SErik.Nordmark@Sun.COM arl_t *arl = ill_to_arl(ill); 246511042SErik.Nordmark@Sun.COM 246611042SErik.Nordmark@Sun.COM if (arl == NULL) 246711042SErik.Nordmark@Sun.COM return; 246811042SErik.Nordmark@Sun.COM mutex_enter(&arl->arl_lock); 246911042SErik.Nordmark@Sun.COM /* 247011042SErik.Nordmark@Sun.COM * wait for unbind ack and arl_refcnt to drop to 1. Note that the 247111042SErik.Nordmark@Sun.COM * quiescent arl_refcnt for this function is 1 (and not 0) because 247211042SErik.Nordmark@Sun.COM * ill_to_arl() will itself return after taking a ref on the arl_t. 247311042SErik.Nordmark@Sun.COM */ 247411042SErik.Nordmark@Sun.COM while (arl->arl_state_flags & ARL_DL_UNBIND_IN_PROGRESS) 247511042SErik.Nordmark@Sun.COM cv_wait(&arl->arl_cv, &arl->arl_lock); 247611042SErik.Nordmark@Sun.COM while (arl->arl_refcnt != 1) 247711042SErik.Nordmark@Sun.COM cv_wait(&arl->arl_cv, &arl->arl_lock); 247811042SErik.Nordmark@Sun.COM mutex_exit(&arl->arl_lock); 247911042SErik.Nordmark@Sun.COM arl_refrele(arl); 248011042SErik.Nordmark@Sun.COM } 2481