/* * DL_INFO_REQ */ static void proto_info_req(dld_str_t *dsp, mblk_t *mp) { dl_info_ack_wrapper_t *dlwp; dl_info_ack_t *dlp; dl_qos_cl_sel1_t *selp; dl_qos_cl_range1_t *rangep; uint8_t *addr; uint8_t *brdcst_addr; uint_t addr_length; uint_t sap_length; mac_info_t minfo; mac_info_t *minfop; queue_t *q = dsp->ds_wq; /* * Swap the request message for one large enough to contain the * wrapper structure defined above. */ if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t), M_PCPROTO, 0)) == NULL) return; bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t)); dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr; dlp = &(dlwp->dl_info); ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr); dlp->dl_primitive = DL_INFO_ACK; /* * Set up the sub-structure pointers. */ addr = dlwp->dl_addr; brdcst_addr = dlwp->dl_brdcst_addr; rangep = &(dlwp->dl_qos_range1); selp = &(dlwp->dl_qos_sel1); /* * This driver supports only version 2 connectionless DLPI provider * nodes. */ dlp->dl_service_mode = DL_CLDLS; dlp->dl_version = DL_VERSION_2; /* * Set the style of the provider */ dlp->dl_provider_style = dsp->ds_style; ASSERT(dlp->dl_provider_style == DL_STYLE1 || dlp->dl_provider_style == DL_STYLE2); /* * Set the current DLPI state. */ dlp->dl_current_state = dsp->ds_dlstate; /* * Gratuitously set the media type. This is to deal with modules * that assume the media type is known prior to DL_ATTACH_REQ * being completed. */ dlp->dl_mac_type = DL_ETHER; /* * If the stream is not at least attached we try to retrieve the * mac_info using mac_info_get() */ if (dsp->ds_dlstate == DL_UNATTACHED || dsp->ds_dlstate == DL_ATTACH_PENDING || dsp->ds_dlstate == DL_DETACH_PENDING) { if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) { /* * Cannot find mac_info. giving up. */ goto done; } minfop = &minfo; } else { minfop = (mac_info_t *)dsp->ds_mip; /* We can only get the sdu if we're attached. */ mac_sdu_get(dsp->ds_mh, &dlp->dl_min_sdu, &dlp->dl_max_sdu); } /* * Set the media type (properly this time). */ if (dsp->ds_native) dlp->dl_mac_type = minfop->mi_nativemedia; else dlp->dl_mac_type = minfop->mi_media; /* * Set the DLSAP length. We only support 16 bit values and they * appear after the MAC address portion of DLSAP addresses. */ sap_length = sizeof (uint16_t); dlp->dl_sap_length = NEG(sap_length); addr_length = minfop->mi_addr_length; /* * Copy in the media broadcast address. */ if (minfop->mi_brdcst_addr != NULL) { dlp->dl_brdcst_addr_offset = (uintptr_t)brdcst_addr - (uintptr_t)dlp; bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length); dlp->dl_brdcst_addr_length = addr_length; } /* Only VLAN links and links that have a normal tag mode support QOS. */ if ((dsp->ds_mch != NULL && mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE) || (dsp->ds_dlp != NULL && dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_NORMAL)) { dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); rangep->dl_qos_type = DL_QOS_CL_RANGE1; rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; rangep->dl_protection.dl_min = DL_UNKNOWN; rangep->dl_protection.dl_max = DL_UNKNOWN; rangep->dl_residual_error = DL_UNKNOWN; /* * Specify the supported range of priorities. */ rangep->dl_priority.dl_min = 0; rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); selp->dl_qos_type = DL_QOS_CL_SEL1; selp->dl_trans_delay = DL_UNKNOWN; selp->dl_protection = DL_UNKNOWN; selp->dl_residual_error = DL_UNKNOWN; /* * Specify the current priority (which can be changed by * the DL_UDQOS_REQ primitive). */ selp->dl_priority = dsp->ds_pri; } dlp->dl_addr_length = addr_length + sizeof (uint16_t); if (dsp->ds_dlstate == DL_IDLE) { /* * The stream is bound. Therefore we can formulate a valid * DLSAP address. */ dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; if (addr_length > 0) mac_unicast_primary_get(dsp->ds_mh, addr); *(uint16_t *)(addr + addr_length) = dsp->ds_sap; } done: IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0); IMPLY(dlp->dl_qos_range_offset != 0, dlp->dl_qos_range_length != 0); IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0); IMPLY(dlp->dl_brdcst_addr_offset != 0, dlp->dl_brdcst_addr_length != 0); qreply(q, mp); }
/* * DL_CAPABILITY_ACK/DL_ERROR_ACK */ static void proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) { dl_capability_ack_t *dlap; dl_capability_sub_t *dlsp; size_t subsize; dl_capab_dld_t dld; dl_capab_hcksum_t hcksum; dl_capab_zerocopy_t zcopy; dl_capab_vrrp_t vrrp; mac_capab_vrrp_t vrrp_capab; uint8_t *ptr; queue_t *q = dsp->ds_wq; mblk_t *mp1; boolean_t is_vlan; boolean_t hcksum_capable = B_FALSE; boolean_t zcopy_capable = B_FALSE; boolean_t dld_capable = B_FALSE; boolean_t vrrp_capable = B_FALSE; /* * Initially assume no capabilities. */ subsize = 0; is_vlan = (mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE); /* * Check if checksum offload is supported on this MAC. Don't * advertise DL_CAPAB_HCKSUM if the underlying MAC is VLAN incapable, * since it might not be able to do the hardware checksum offload * with the correct offset. */ bzero(&hcksum, sizeof (dl_capab_hcksum_t)); if ((!is_vlan || (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL))) && mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM, &hcksum.hcksum_txflags)) { if (hcksum.hcksum_txflags != 0) { hcksum_capable = B_TRUE; subsize += sizeof (dl_capability_sub_t) + sizeof (dl_capab_hcksum_t); } } /* * Check if zerocopy is supported on this interface. * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled * then reserve space for that capability. */ if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) && !(dld_opt & DLD_OPT_NO_ZEROCOPY)) { zcopy_capable = B_TRUE; subsize += sizeof (dl_capability_sub_t) + sizeof (dl_capab_zerocopy_t); } /* * Direct capability negotiation interface between IP and DLD */ if (dsp->ds_sap == ETHERTYPE_IP && check_mod_above(dsp->ds_rq, "ip")) { dld_capable = B_TRUE; subsize += sizeof (dl_capability_sub_t) + sizeof (dl_capab_dld_t); } /* * Check if vrrp is supported on this interface. If so, reserve * space for that capability. */ if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_VRRP, &vrrp_capab)) { vrrp_capable = B_TRUE; subsize += sizeof (dl_capability_sub_t) + sizeof (dl_capab_vrrp_t); } /* * If there are no capabilities to advertise or if we * can't allocate a response, send a DL_ERROR_ACK. */ if ((mp1 = reallocb(mp, sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); return; } mp = mp1; DB_TYPE(mp) = M_PROTO; mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; bzero(mp->b_rptr, MBLKL(mp)); dlap = (dl_capability_ack_t *)mp->b_rptr; dlap->dl_primitive = DL_CAPABILITY_ACK; dlap->dl_sub_offset = sizeof (dl_capability_ack_t); dlap->dl_sub_length = subsize; ptr = (uint8_t *)&dlap[1]; /* * TCP/IP checksum offload. */ if (hcksum_capable) { dlsp = (dl_capability_sub_t *)ptr; dlsp->dl_cap = DL_CAPAB_HCKSUM; dlsp->dl_length = sizeof (dl_capab_hcksum_t); ptr += sizeof (dl_capability_sub_t); hcksum.hcksum_version = HCKSUM_VERSION_1; dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); ptr += sizeof (dl_capab_hcksum_t); } /* * Zero copy */ if (zcopy_capable) { dlsp = (dl_capability_sub_t *)ptr; dlsp->dl_cap = DL_CAPAB_ZEROCOPY; dlsp->dl_length = sizeof (dl_capab_zerocopy_t); ptr += sizeof (dl_capability_sub_t); bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); zcopy.zerocopy_version = ZEROCOPY_VERSION_1; zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq); bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); ptr += sizeof (dl_capab_zerocopy_t); } /* * VRRP capability negotiation */ if (vrrp_capable) { dlsp = (dl_capability_sub_t *)ptr; dlsp->dl_cap = DL_CAPAB_VRRP; dlsp->dl_length = sizeof (dl_capab_vrrp_t); ptr += sizeof (dl_capability_sub_t); bzero(&vrrp, sizeof (dl_capab_vrrp_t)); vrrp.vrrp_af = vrrp_capab.mcv_af; bcopy(&vrrp, ptr, sizeof (dl_capab_vrrp_t)); ptr += sizeof (dl_capab_vrrp_t); } /* * Direct capability negotiation interface between IP and DLD. * Refer to dld.h for details. */ if (dld_capable) { dlsp = (dl_capability_sub_t *)ptr; dlsp->dl_cap = DL_CAPAB_DLD; dlsp->dl_length = sizeof (dl_capab_dld_t); ptr += sizeof (dl_capability_sub_t); bzero(&dld, sizeof (dl_capab_dld_t)); dld.dld_version = DLD_CURRENT_VERSION; dld.dld_capab = (uintptr_t)dld_capab; dld.dld_capab_handle = (uintptr_t)dsp; dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq); bcopy(&dld, ptr, sizeof (dl_capab_dld_t)); ptr += sizeof (dl_capab_dld_t); } ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); qreply(q, mp); }