/* * DL_UDQOS_REQ */ static void proto_udqos_req(dld_str_t *dsp, mblk_t *mp) { dl_udqos_req_t *dlp = (dl_udqos_req_t *)mp->b_rptr; dl_qos_cl_sel1_t *selp; int off, len; t_uscalar_t dl_err; queue_t *q = dsp->ds_wq; off = dlp->dl_qos_offset; len = dlp->dl_qos_length; if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) { dl_err = DL_BADPRIM; goto failed; } selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off); if (selp->dl_qos_type != DL_QOS_CL_SEL1) { dl_err = DL_BADQOSTYPE; goto failed; } if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 || selp->dl_priority < 0) { dl_err = DL_BADQOSPARAM; goto failed; } dsp->ds_pri = selp->dl_priority; dlokack(q, mp, DL_UDQOS_REQ); return; failed: dlerrorack(q, mp, DL_UDQOS_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); }
/* * Bounds check payload area(s). */ static boolean_t pbuf_ref_valid(multidata_t *mmd, pdescinfo_t *pdi) { int i = 0, idx; boolean_t valid = B_TRUE; struct pld_ary_s *pa; mutex_enter(&mmd->mmd_pd_slab_lock); if (pdi->pld_cnt == 0 || pdi->pld_cnt > mmd->mmd_pbuf_cnt) { mutex_exit(&mmd->mmd_pd_slab_lock); return (B_FALSE); } pa = &pdi->pld_ary[0]; while (valid && i < pdi->pld_cnt) { valid = (((idx = pa->pld_pbuf_idx) < mmd->mmd_pbuf_cnt) && pa->pld_rptr != NULL && pa->pld_wptr != NULL && pa->pld_wptr >= pa->pld_rptr && pa->pld_rptr >= mmd->mmd_pbuf[idx]->b_rptr && MBLKIN(mmd->mmd_pbuf[idx], (pa->pld_rptr - mmd->mmd_pbuf[idx]->b_rptr), PDESC_PLD_SPAN_SIZE(pdi, i))); if (!valid) { MMD_DEBUG((CE_WARN, "pbuf_ref_valid: pdi 0x%p pld out of bound; " "index %d has pld_cnt %d pbuf_idx %d " "(mmd_pbuf_cnt %d), " "pld_rptr 0x%p pld_wptr 0x%p len %d " "(valid 0x%p-0x%p len %d)\n", (void *)pdi, i, pdi->pld_cnt, idx, mmd->mmd_pbuf_cnt, (void *)pa->pld_rptr, (void *)pa->pld_wptr, (int)PDESC_PLD_SPAN_SIZE(pdi, i), (void *)mmd->mmd_pbuf[idx]->b_rptr, (void *)mmd->mmd_pbuf[idx]->b_wptr, (int)MBLKL(mmd->mmd_pbuf[idx]))); } /* advance to next entry */ i++; pa++; } mutex_exit(&mmd->mmd_pd_slab_lock); return (valid); }
/* * 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_UINTDATA_REQ */ void proto_unitdata_req(dld_str_t *dsp, mblk_t *mp) { queue_t *q = dsp->ds_wq; dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr; off_t off; size_t len, size; const uint8_t *addr; uint16_t sap; uint_t addr_length; mblk_t *bp, *payload; uint32_t start, stuff, end, value, flags; t_uscalar_t dl_err; uint_t max_sdu; if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0); return; } mutex_enter(&dsp->ds_lock); if (dsp->ds_dlstate != DL_IDLE) { mutex_exit(&dsp->ds_lock); dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0); return; } DLD_DATATHR_INC(dsp); mutex_exit(&dsp->ds_lock); addr_length = dsp->ds_mip->mi_addr_length; off = dlp->dl_dest_addr_offset; len = dlp->dl_dest_addr_length; if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { dl_err = DL_BADPRIM; goto failed; } if (len != addr_length + sizeof (uint16_t)) { dl_err = DL_BADADDR; goto failed; } addr = mp->b_rptr + off; sap = *(uint16_t *)(mp->b_rptr + off + addr_length); /* * Check the length of the packet and the block types. */ size = 0; payload = mp->b_cont; for (bp = payload; bp != NULL; bp = bp->b_cont) { if (DB_TYPE(bp) != M_DATA) goto baddata; size += MBLKL(bp); } mac_sdu_get(dsp->ds_mh, NULL, &max_sdu); if (size > max_sdu) goto baddata; /* * Build a packet header. */ if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max, &payload)) == NULL) { dl_err = DL_BADADDR; goto failed; } /* * We no longer need the M_PROTO header, so free it. */ freeb(mp); /* * Transfer the checksum offload information if it is present. */ hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value, &flags); (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0); /* * Link the payload onto the new header. */ ASSERT(bp->b_cont == NULL); bp->b_cont = payload; /* * No lock can be held across modules and putnext()'s, * which can happen here with the call from DLD_TX(). */ if (DLD_TX(dsp, bp, 0, 0) != NULL) { /* flow-controlled */ DLD_SETQFULL(dsp); } DLD_DATATHR_DCR(dsp); return; failed: dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); DLD_DATATHR_DCR(dsp); return; baddata: dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); DLD_DATATHR_DCR(dsp); }
/* * DL_CAPABILITY_REQ */ static void proto_capability_req(dld_str_t *dsp, mblk_t *mp) { dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr; dl_capability_sub_t *sp; size_t size, len; offset_t off, end; t_uscalar_t dl_err; queue_t *q = dsp->ds_wq; if (MBLKL(mp) < sizeof (dl_capability_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; } /* * This request is overloaded. If there are no requested capabilities * then we just want to acknowledge with all the capabilities we * support. Otherwise we enable the set of capabilities requested. */ if (dlp->dl_sub_length == 0) { proto_capability_advertise(dsp, mp); return; } if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { dl_err = DL_BADPRIM; goto failed; } dlp->dl_primitive = DL_CAPABILITY_ACK; off = dlp->dl_sub_offset; len = dlp->dl_sub_length; /* * Walk the list of capabilities to be enabled. */ for (end = off + len; off < end; ) { sp = (dl_capability_sub_t *)(mp->b_rptr + off); size = sizeof (dl_capability_sub_t) + sp->dl_length; if (off + size > end || !IS_P2ALIGNED(off, sizeof (uint32_t))) { dl_err = DL_BADPRIM; goto failed; } switch (sp->dl_cap) { /* * TCP/IP checksum offload to hardware. */ case DL_CAPAB_HCKSUM: { dl_capab_hcksum_t *hcksump; dl_capab_hcksum_t hcksum; hcksump = (dl_capab_hcksum_t *)&sp[1]; /* * Copy for alignment. */ bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); break; } case DL_CAPAB_DLD: { dl_capab_dld_t *dldp; dl_capab_dld_t dld; dldp = (dl_capab_dld_t *)&sp[1]; /* * Copy for alignment. */ bcopy(dldp, &dld, sizeof (dl_capab_dld_t)); dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq); bcopy(&dld, dldp, sizeof (dl_capab_dld_t)); break; } default: break; } off += size; } qreply(q, mp); return; failed: dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); }
/* ARGSUSED */ int pdesc_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { multidata_t mmd; pdesc_t pd; pdescinfo_t *pdi = &pd.pd_pdi; pdesc_slab_t slab; mblk_t hbuf, pbuf[MULTIDATA_MAX_PBUFS]; uint_t i, idx; boolean_t valid = B_TRUE; struct pld_ary_s *pa; if (!(flags & DCMD_ADDRSPEC) || argc != 0) return (DCMD_USAGE); if (mdb_vread(&pd, sizeof (pd), addr) == -1) { mdb_warn("couldn't read pdesc_t at %p", addr); return (DCMD_ERR); } if (pd.pd_magic != PDESC_MAGIC) { mdb_warn("Incorrect pdesc magic number at %p\n", VA_OFF(addr, offsetof(pdesc_t, pd_magic))); return (DCMD_ERR); } if (mdb_vread(&slab, sizeof (slab), (uintptr_t)pd.pd_slab) == -1) { mdb_warn("couldn't read pdesc_slab_t at %p", pd.pd_slab); return (DCMD_ERR); } if (mdb_vread(&mmd, sizeof (mmd), (uintptr_t)slab.pds_mmd) == -1) { mdb_warn("couldn't read multidata_t at %p", slab.pds_mmd); return (DCMD_ERR); } if (mmd.mmd_magic != MULTIDATA_MAGIC) mdb_printf("Incorrect Multidata magic number at %p\n", VA_OFF(slab.pds_mmd, offsetof(multidata_t, mmd_magic))); if (mmd.mmd_hbuf != 0 && mdb_vread(&hbuf, sizeof (hbuf), (uintptr_t)mmd.mmd_hbuf) == -1) { mdb_warn("couldn't read mblk_t at %p", mmd.mmd_hbuf); return (DCMD_ERR); } if (mmd.mmd_pbuf_cnt > MULTIDATA_MAX_PBUFS) { mdb_warn("Multidata pbuf count exceeds %d\n", MULTIDATA_MAX_PBUFS); return (DCMD_ERR); } else if (pdi->pld_cnt > mmd.mmd_pbuf_cnt) { mdb_warn("descriptor pbuf count exceeds Multidata " "pbuf count %d\n", mmd.mmd_pbuf_cnt); return (DCMD_ERR); } if (mmd.mmd_pbuf_cnt > 0) { for (i = 0; i < mmd.mmd_pbuf_cnt; i++) { if (mdb_vread(&pbuf[i], sizeof (mblk_t), (uintptr_t)mmd.mmd_pbuf[i]) == -1) { mdb_warn("couldn't read mblk_t at %p", mmd.mmd_pbuf[i]); return (DCMD_ERR); } } } /* It should have at least one buffer reference */ if (!(pdi->flags & PDESC_HAS_REF)) { mdb_warn("descriptor has no buffer reference indicator " "in flags (0x%x)\n", pdi->flags); return (DCMD_ERR); } else if (!(pdi->flags & PDESC_PBUF_REF) && pdi->pld_cnt > 0) { mdb_warn("descriptor has no pbuf reference indicator in " "flags (0x%x); but pld_cnt is %d\n", pdi->flags, pdi->pld_cnt); return (DCMD_ERR); } /* Bounds check the header fragment, if any */ if (!((pdi->flags & PDESC_HBUF_REF) && pdi->hdr_rptr != 0 && pdi->hdr_wptr != 0 && pdi->hdr_base != 0 && pdi->hdr_lim != 0 && pdi->hdr_lim >= pdi->hdr_base && pdi->hdr_wptr >= pdi->hdr_rptr && pdi->hdr_base <= pdi->hdr_rptr && pdi->hdr_lim >= pdi->hdr_wptr && pdi->hdr_base >= hbuf.b_rptr && MBLKIN(&hbuf, (pdi->hdr_base - hbuf.b_rptr), PDESC_HDRSIZE(pdi)))) { mdb_warn("descriptor has invalid header fragment\n"); return (DCMD_ERR); } i = 0; pa = &pdi->pld_ary[0]; /* Bounds check the payload fragment, if any */ while (valid && i < pdi->pld_cnt) { valid = (((idx = pa->pld_pbuf_idx) < mmd.mmd_pbuf_cnt) && pa->pld_rptr != NULL && pa->pld_wptr != NULL && pa->pld_wptr >= pa->pld_rptr && pa->pld_rptr >= pbuf[idx].b_rptr && MBLKIN(&pbuf[idx], (pa->pld_rptr - pbuf[idx].b_rptr), PDESC_PLD_SPAN_SIZE(pdi, i))); if (!valid) { mdb_warn("descriptor has invalid payload fragment\n"); return (DCMD_ERR); } /* advance to next entry */ i++; pa++; } return (DCMD_OK); }