/* * DL_UNBIND_REQ */ static void proto_unbind_req(dld_str_t *dsp, mblk_t *mp) { queue_t *q = dsp->ds_wq; t_uscalar_t dl_err; mac_perim_handle_t mph; if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { dl_err = DL_BADPRIM; goto failed; } if (dsp->ds_dlstate != DL_IDLE) { dl_err = DL_OUTSTATE; goto failed; } mutex_enter(&dsp->ds_lock); while (dsp->ds_datathr_cnt != 0) cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock); dsp->ds_dlstate = DL_UNBIND_PENDING; mutex_exit(&dsp->ds_lock); mac_perim_enter_by_mh(dsp->ds_mh, &mph); /* * Unbind the channel to stop packets being received. */ dls_unbind(dsp); /* * Disable polling mode, if it is enabled. */ (void) dld_capab_poll_disable(dsp, NULL); /* * Clear LSO flags. */ dsp->ds_lso = B_FALSE; dsp->ds_lso_max = 0; /* * Clear the receive callback. */ dls_rx_set(dsp, NULL, NULL); dsp->ds_direct = B_FALSE; /* * Set the mode back to the default (unitdata). */ dsp->ds_mode = DLD_UNITDATA; dsp->ds_dlstate = DL_UNBOUND; dls_active_clear(dsp, B_FALSE); mac_perim_exit(mph); dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ); return; failed: dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0); }
/* * DL_ENABMULTI_REQ */ static void proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp) { dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr; int err = 0; t_uscalar_t dl_err; queue_t *q = dsp->ds_wq; mac_perim_handle_t mph; if (dsp->ds_dlstate == DL_UNATTACHED || DL_ACK_PENDING(dsp->ds_dlstate)) { dl_err = DL_OUTSTATE; goto failed; } if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { dl_err = DL_BADPRIM; goto failed; } mac_perim_enter_by_mh(dsp->ds_mh, &mph); if ((dsp->ds_dmap == NULL) && (err = dls_active_set(dsp)) != 0) { dl_err = DL_SYSERR; goto failed2; } err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset); if (err != 0) { switch (err) { case EINVAL: dl_err = DL_BADADDR; err = 0; break; case ENOSPC: dl_err = DL_TOOMANY; err = 0; break; default: dl_err = DL_SYSERR; break; } if (dsp->ds_dmap == NULL) dls_active_clear(dsp, B_FALSE); goto failed2; } mac_perim_exit(mph); dlokack(q, mp, DL_ENABMULTI_REQ); return; failed2: mac_perim_exit(mph); failed: dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); }
void dls_close(dls_channel_t dc) { dls_impl_t *dip = (dls_impl_t *)dc; dls_vlan_t *dvp; dls_link_t *dlp; dls_multicst_addr_t *p; dls_multicst_addr_t *nextp; dls_active_clear(dc); rw_enter(&(dip->di_lock), RW_WRITER); /* * Remove the notify function. */ mac_notify_remove(dip->di_mh, dip->di_mnh); dip->di_mnh = NULL; /* * If the dls_impl_t is bound then unbind it. */ dvp = dip->di_dvp; dlp = dvp->dv_dlp; if (dip->di_bound) { rw_exit(&(dip->di_lock)); dls_link_remove(dlp, dip); rw_enter(&(dip->di_lock), RW_WRITER); dip->di_bound = B_FALSE; } dip->di_rx = NULL; dip->di_rx_arg = NULL; /* * Walk the list of multicast addresses, disabling each at the MAC. */ for (p = dip->di_dmap; p != NULL; p = nextp) { (void) mac_multicst_remove(dip->di_mh, p->dma_addr); nextp = p->dma_nextp; kmem_free(p, sizeof (dls_multicst_addr_t)); } dip->di_dmap = NULL; rw_exit(&(dip->di_lock)); /* * If the MAC has been set in promiscuous mode then disable it. */ (void) dls_promisc(dc, 0); /* * Free the dls_impl_t back to the cache. */ dip->di_dvp = NULL; dip->di_txinfo = NULL; if (dip->di_soft_ring_list != NULL) { soft_ring_set_destroy(dip->di_soft_ring_list, dip->di_soft_ring_size); dip->di_soft_ring_list = NULL; } dip->di_soft_ring_size = 0; kmem_cache_free(i_dls_impl_cachep, dip); /* * Decrement the reference count to allow the cache to be destroyed * if there are no more dls_impl_t. */ atomic_add_32(&i_dls_impl_count, -1); /* * Release our reference to the dls_vlan_t allowing that to be * destroyed if there are no more dls_impl_t. An unreferenced tagged * vlan gets destroyed automatically. */ dls_vlan_rele(dvp); }
/* * DL_SET_PHYS_ADDR_REQ */ static void proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp) { dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr; int err = 0; t_uscalar_t dl_err; queue_t *q = dsp->ds_wq; mac_perim_handle_t mph; if (dsp->ds_dlstate == DL_UNATTACHED || DL_ACK_PENDING(dsp->ds_dlstate)) { dl_err = DL_OUTSTATE; goto failed; } if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { dl_err = DL_BADPRIM; goto failed; } mac_perim_enter_by_mh(dsp->ds_mh, &mph); if ((err = dls_active_set(dsp)) != 0) { dl_err = DL_SYSERR; goto failed2; } /* * If mac-nospoof is enabled and the link is owned by a * non-global zone, changing the mac address is not allowed. */ if (dsp->ds_dlp->dl_zid != GLOBAL_ZONEID && mac_protect_enabled(dsp->ds_mch, MPT_MACNOSPOOF)) { dls_active_clear(dsp, B_FALSE); err = EACCES; goto failed2; } err = mac_unicast_primary_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset); if (err != 0) { switch (err) { case EINVAL: dl_err = DL_BADADDR; err = 0; break; default: dl_err = DL_SYSERR; break; } dls_active_clear(dsp, B_FALSE); goto failed2; } mac_perim_exit(mph); dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); return; failed2: mac_perim_exit(mph); failed: dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); }
/* * DL_PROMISCOFF_REQ */ static void proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp) { dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr; int err = 0; t_uscalar_t dl_err; uint32_t promisc_saved; queue_t *q = dsp->ds_wq; mac_perim_handle_t mph; if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { dl_err = DL_BADPRIM; goto failed; } if (dsp->ds_dlstate == DL_UNATTACHED || DL_ACK_PENDING(dsp->ds_dlstate)) { dl_err = DL_OUTSTATE; goto failed; } promisc_saved = dsp->ds_promisc; switch (dlp->dl_level) { case DL_PROMISC_SAP: if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { dl_err = DL_NOTENAB; goto failed; } dsp->ds_promisc &= ~DLS_PROMISC_SAP; break; case DL_PROMISC_MULTI: if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { dl_err = DL_NOTENAB; goto failed; } dsp->ds_promisc &= ~DLS_PROMISC_MULTI; break; case DL_PROMISC_PHYS: if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { dl_err = DL_NOTENAB; goto failed; } dsp->ds_promisc &= ~DLS_PROMISC_PHYS; break; default: dl_err = DL_NOTSUPPORTED; goto failed; } mac_perim_enter_by_mh(dsp->ds_mh, &mph); /* * Adjust channel promiscuity. */ err = dls_promisc(dsp, promisc_saved); if (err != 0) { mac_perim_exit(mph); dl_err = DL_SYSERR; goto failed; } if (dsp->ds_promisc == 0) dls_active_clear(dsp, B_FALSE); mac_perim_exit(mph); dlokack(q, mp, DL_PROMISCOFF_REQ); return; failed: dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); }
/* * DL_BIND_REQ */ static void proto_bind_req(dld_str_t *dsp, mblk_t *mp) { dl_bind_req_t *dlp = (dl_bind_req_t *)mp->b_rptr; int err = 0; uint8_t dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)]; uint_t dlsap_addr_length; t_uscalar_t dl_err; t_scalar_t sap; queue_t *q = dsp->ds_wq; mac_perim_handle_t mph; void *mdip; int32_t intr_cpu; if (MBLKL(mp) < sizeof (dl_bind_req_t)) { dl_err = DL_BADPRIM; goto failed; } if (dlp->dl_xidtest_flg != 0) { dl_err = DL_NOAUTO; goto failed; } if (dlp->dl_service_mode != DL_CLDLS) { dl_err = DL_UNSUPPORTED; goto failed; } if (dsp->ds_dlstate != DL_UNBOUND) { dl_err = DL_OUTSTATE; goto failed; } mac_perim_enter_by_mh(dsp->ds_mh, &mph); if ((err = dls_active_set(dsp)) != 0) { dl_err = DL_SYSERR; goto failed2; } dsp->ds_dlstate = DL_BIND_PENDING; /* * Set the receive callback. */ dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ? dld_str_rx_raw : dld_str_rx_unitdata, dsp); /* * Bind the channel such that it can receive packets. */ sap = dlp->dl_sap; dsp->ds_nonip = !check_mod_above(dsp->ds_rq, "ip") && !check_mod_above(dsp->ds_rq, "arp"); err = dls_bind(dsp, sap); if (err != 0) { switch (err) { case EINVAL: dl_err = DL_BADADDR; err = 0; break; default: dl_err = DL_SYSERR; break; } dsp->ds_dlstate = DL_UNBOUND; dls_active_clear(dsp, B_FALSE); goto failed2; } intr_cpu = mac_client_intr_cpu(dsp->ds_mch); mdip = mac_get_devinfo(dsp->ds_mh); mac_perim_exit(mph); /* * We do this after we get out of the perim to avoid deadlocks * etc. since part of mac_client_retarget_intr is to walk the * device tree in order to find and retarget the interrupts. */ if (intr_cpu != -1) mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu); /* * Copy in MAC address. */ dlsap_addr_length = dsp->ds_mip->mi_addr_length; mac_unicast_primary_get(dsp->ds_mh, dlsap_addr); /* * Copy in the SAP. */ *(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap; dlsap_addr_length += sizeof (uint16_t); dsp->ds_dlstate = DL_IDLE; dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0); return; failed2: mac_perim_exit(mph); failed: dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); }