Index: head/sys/dev/hyperv/netvsc/hv_rndis_filter.c =================================================================== --- head/sys/dev/hyperv/netvsc/hv_rndis_filter.c +++ head/sys/dev/hyperv/netvsc/hv_rndis_filter.c @@ -96,6 +96,8 @@ static void hn_rndis_sent_cb(struct hn_send_ctx *sndc, struct hn_softc *sc, struct vmbus_channel *chan, const void *data, int dlen); +static int hn_rndis_query(struct hn_softc *sc, uint32_t oid, + const void *idata, size_t idlen, void *odata, size_t *odlen0); static __inline uint32_t hn_rndis_rid(struct hn_softc *sc) @@ -695,13 +697,23 @@ /* * RNDIS filter query device MAC address */ -static inline int +static int hv_rf_query_device_mac(rndis_device *device) { - uint32_t size = ETHER_ADDR_LEN; + struct hn_softc *sc = device->sc; + size_t hwaddr_len; + int error; - return (hv_rf_query_device(device, - RNDIS_OID_802_3_PERMANENT_ADDRESS, device->hw_mac_addr, &size)); + hwaddr_len = ETHER_ADDR_LEN; + error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0, + device->hw_mac_addr, &hwaddr_len); + if (error) + return error; + if (hwaddr_len != ETHER_ADDR_LEN) { + if_printf(sc->hn_ifp, "invalid hwaddr len %zu\n", hwaddr_len); + return EINVAL; + } + return 0; } /* @@ -892,12 +904,12 @@ static const void * hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid, - size_t reqlen, size_t min_complen, uint32_t comp_type) + size_t reqlen, size_t *comp_len0, uint32_t comp_type) { struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT]; const struct rndis_comp_hdr *comp; bus_addr_t paddr; - size_t comp_len; + size_t comp_len, min_complen = *comp_len0; int gpa_cnt, error; KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid)); @@ -946,7 +958,14 @@ * Check this RNDIS complete message. */ if (comp_len < min_complen) { - if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n", comp_len); + if (comp_len >= sizeof(*comp)) { + /* rm_status field is valid */ + if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, " + "status 0x%08x\n", comp_len, comp->rm_status); + } else { + if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n", + comp_len); + } return (NULL); } if (comp->rm_len < min_complen) { @@ -965,9 +984,100 @@ return (NULL); } /* All pass! */ + *comp_len0 = comp_len; return (comp); } +static int +hn_rndis_query(struct hn_softc *sc, uint32_t oid, + const void *idata, size_t idlen, void *odata, size_t *odlen0) +{ + struct rndis_query_req *req; + const struct rndis_query_comp *comp; + struct vmbus_xact *xact; + size_t reqlen, odlen = *odlen0, comp_len; + int error, ofs; + uint32_t rid; + + reqlen = sizeof(*req) + idlen; + xact = vmbus_xact_get(sc->hn_xact, reqlen); + if (xact == NULL) { + if_printf(sc->hn_ifp, "no xact for RNDIS query\n"); + return (ENXIO); + } + rid = hn_rndis_rid(sc); + req = vmbus_xact_req_data(xact); + req->rm_type = REMOTE_NDIS_QUERY_MSG; + req->rm_len = reqlen; + req->rm_rid = rid; + req->rm_oid = oid; + /* + * XXX + * This is _not_ RNDIS Spec conforming: + * "This MUST be set to 0 when there is no input data + * associated with the OID." + * + * If this field was set to 0 according to the RNDIS Spec, + * Hyper-V would set non-SUCCESS status in the query + * completion. + */ + req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET; + + if (idlen > 0) { + req->rm_infobuflen = idlen; + /* Input data immediately follows RNDIS query. */ + memcpy(req + 1, idata, idlen); + } + + comp_len = sizeof(*comp) + odlen; + comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len, + REMOTE_NDIS_QUERY_CMPLT); + if (comp == NULL) { + if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid); + error = EIO; + goto done; + } + + if (comp->rm_status != RNDIS_STATUS_SUCCESS) { + if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: " + "status 0x%08x\n", oid, comp->rm_status); + error = EIO; + goto done; + } + if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) { + /* No output data! */ + if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid); + *odlen0 = 0; + error = 0; + goto done; + } + + /* + * Check output data length and offset. + */ + /* ofs is the offset from the beginning of comp. */ + ofs = RNDIS_QUERY_COMP_INFOBUFABS(comp->rm_infobufoffset); + if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) { + if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, " + "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen); + error = EINVAL; + goto done; + } + + /* + * Save output data. + */ + if (comp->rm_infobuflen < odlen) + odlen = comp->rm_infobuflen; + memcpy(odata, ((const uint8_t *)comp) + ofs, odlen); + *odlen0 = odlen; + + error = 0; +done: + vmbus_xact_put(xact); + return (error); +} + /* * RNDIS filter init device */ @@ -978,6 +1088,7 @@ struct rndis_init_req *req; const struct rndis_init_comp *comp; struct vmbus_xact *xact; + size_t comp_len; uint32_t rid; int error; @@ -998,8 +1109,9 @@ req->rm_ver_minor = RNDIS_VERSION_MINOR; req->rm_max_xfersz = HN_RNDIS_XFER_SIZE; - comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), - RNDIS_INIT_COMP_SIZE_MIN, REMOTE_NDIS_INITIALIZE_CMPLT); + comp_len = RNDIS_INIT_COMP_SIZE_MIN; + comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len, + REMOTE_NDIS_INITIALIZE_CMPLT); if (comp == NULL) { if_printf(sc->hn_ifp, "exec RNDIS init failed\n"); error = EIO; Index: head/sys/net/rndis.h =================================================================== --- head/sys/net/rndis.h +++ head/sys/net/rndis.h @@ -172,6 +172,10 @@ uint32_t rm_devicevchdl; }; +#define RNDIS_QUERY_REQ_INFOBUFOFFSET \ + (sizeof(struct rndis_query_req) - \ + __offsetof(struct rndis_query_req, rm_rid)) + struct rndis_query_comp { uint32_t rm_type; uint32_t rm_len; @@ -181,6 +185,9 @@ uint32_t rm_infobufoffset; }; +#define RNDIS_QUERY_COMP_INFOBUFABS(ofs) \ + ((ofs) + __offsetof(struct rndis_query_req, rm_rid)) + /* Send a set object request. */ #define REMOTE_NDIS_SET_MSG 0x00000005 #define REMOTE_NDIS_SET_CMPLT 0x80000005