Index: sys/dev/hyperv/netvsc/hv_net_vsc.h =================================================================== --- sys/dev/hyperv/netvsc/hv_net_vsc.h +++ sys/dev/hyperv/netvsc/hv_net_vsc.h @@ -232,6 +232,7 @@ struct sysctl_oid *hn_rx_sysctl_tree; struct vmbus_xact_ctx *hn_xact; uint32_t hn_nvs_ver; + uint32_t hn_rx_filter; struct taskqueue *hn_mgmt_taskq; struct taskqueue *hn_mgmt_taskq0; Index: sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c =================================================================== --- sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c +++ sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c @@ -326,6 +326,7 @@ static int hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS); static int hn_caps_sysctl(SYSCTL_HANDLER_ARGS); static int hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS); +static int hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS); static int hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS); static int hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS); static int hn_check_iplen(const struct mbuf *, int); @@ -367,6 +368,7 @@ static void hn_link_status(struct hn_softc *); static int hn_sendpkt_rndis_sglist(struct hn_tx_ring *, struct hn_txdesc *); static int hn_sendpkt_rndis_chim(struct hn_tx_ring *, struct hn_txdesc *); +static int hn_set_rxfilter(struct hn_softc *); static void hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt); @@ -455,6 +457,35 @@ } static int +hn_set_rxfilter(struct hn_softc *sc) +{ + struct ifnet *ifp = sc->hn_ifp; + uint32_t filter; + int error = 0; + + HN_LOCK_ASSERT(sc); + + if (ifp->if_flags & IFF_PROMISC) { + filter = NDIS_PACKET_TYPE_PROMISCUOUS; + } else { + filter = NDIS_PACKET_TYPE_DIRECTED; + if (ifp->if_flags & IFF_BROADCAST) + filter |= NDIS_PACKET_TYPE_BROADCAST; + /* TODO: support multicast list */ + if ((ifp->if_flags & IFF_ALLMULTI) || + !TAILQ_EMPTY(&ifp->if_multiaddrs)) + filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; + } + + if (sc->hn_rx_filter != filter) { + error = hn_rndis_set_rxfilter(sc, filter); + if (!error) + sc->hn_rx_filter = filter; + } + return (error); +} + +static int hn_get_txswq_depth(const struct hn_tx_ring *txr) { @@ -728,6 +759,9 @@ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "hwassist", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, hn_hwassist_sysctl, "A", "hwassist"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, + hn_rxfilter_sysctl, "A", "rxfilter"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key", CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0, hn_rss_key_sysctl, "IU", "RSS key"); @@ -1840,31 +1874,13 @@ } if (ifp->if_flags & IFF_UP) { - /* - * If only the state of the PROMISC flag changed, - * then just use the 'set promisc mode' command - * instead of reinitializing the entire NIC. Doing - * a full re-init means reloading the firmware and - * waiting for it to start up, which may take a - * second or two. - */ -#ifdef notyet - /* Fixme: Promiscuous mode? */ - if (ifp->if_drv_flags & IFF_DRV_RUNNING && - ifp->if_flags & IFF_PROMISC && - !(sc->hn_if_flags & IFF_PROMISC)) { - /* do something here for Hyper-V */ - } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && - !(ifp->if_flags & IFF_PROMISC) && - sc->hn_if_flags & IFF_PROMISC) { - /* do something here for Hyper-V */ - } else -#endif + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + hn_set_rxfilter(sc); + else hn_init_locked(sc); } else { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) hn_stop(sc); - } } sc->hn_if_flags = ifp->if_flags; @@ -1922,12 +1938,16 @@ case SIOCADDMULTI: case SIOCDELMULTI: - /* Always all-multi */ - /* - * TODO: - * Enable/disable all-multi according to the emptiness of - * the mcast address list. - */ + HN_LOCK(sc); + + if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) { + HN_UNLOCK(sc); + break; + } + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + hn_set_rxfilter(sc); + + HN_UNLOCK(sc); break; case SIOCSIFMEDIA: @@ -2035,8 +2055,8 @@ if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; - /* TODO: add hn_rx_filter */ - hn_rndis_set_rxfilter(sc, NDIS_PACKET_TYPE_PROMISCUOUS); + /* Configure RX filter */ + hn_set_rxfilter(sc); /* Clear OACTIVE bit. */ atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE); @@ -2363,6 +2383,21 @@ } static int +hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct hn_softc *sc = arg1; + char filter_str[128]; + uint32_t filter; + + HN_LOCK(sc); + filter = sc->hn_rx_filter; + HN_UNLOCK(sc); + snprintf(filter_str, sizeof(filter_str), "%b", filter, + NDIS_PACKET_TYPES); + return sysctl_handle_string(oidp, filter_str, sizeof(filter_str), req); +} + +static int hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS) { struct hn_softc *sc = arg1; @@ -3783,6 +3818,7 @@ * Disable RX by clearing RX filter. */ hn_rndis_set_rxfilter(sc, 0); + sc->hn_rx_filter = 0; /* * Give RNDIS enough time to flush all pending data packets. @@ -3870,9 +3906,8 @@ /* * Re-enable RX. - * TODO: add hn_rx_filter. */ - hn_rndis_set_rxfilter(sc, NDIS_PACKET_TYPE_PROMISCUOUS); + hn_set_rxfilter(sc); /* * Make sure to clear suspend status on "all" TX rings, Index: sys/net/rndis.h =================================================================== --- sys/net/rndis.h +++ sys/net/rndis.h @@ -351,7 +351,7 @@ uint32_t rm_status; }; -/* packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */ +/* Packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */ #define NDIS_PACKET_TYPE_DIRECTED 0x00000001 #define NDIS_PACKET_TYPE_MULTICAST 0x00000002 #define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 @@ -365,6 +365,14 @@ #define NDIS_PACKET_TYPE_FUNCTIONAL 0x00004000 #define NDIS_PACKET_TYPE_MAC_FRAME 0x00008000 +/* + * Packet filter description for use with printf(9) %b identifier. + */ +#define NDIS_PACKET_TYPES \ + "\20\1DIRECT\2MULTICAST\3ALLMULTI\4BROADCAST" \ + "\5SRCROUTE\6PROMISC\7SMT\10ALLLOCAL" \ + "\11GROUP\12ALLFUNC\13FUNC\14MACFRAME" + /* RNDIS offsets */ #define RNDIS_HEADER_OFFSET ((uint32_t)sizeof(struct rndis_msghdr)) #define RNDIS_DATA_OFFSET \