Commit b6f568e2 authored by Jon Cooper's avatar Jon Cooper Committed by David S. Miller

sfc: Insert multicast filters as well as mismatch filters in promiscuous mode

If a function is in promiscuous mode and another function has a broadcast or
 multicast filter inserted, the function in promiscuous mode won't see that
 broadcast or multicast traffic.
Most notably this breaks broadcast, which means ARP doesn't work. Less
 show-stoppingly, a function listening on a multicast address that's also in
 promiscuous mode will not see that multicast traffic if another function is
 also listening on that multicast address.
Signed-off-by: default avatarEdward Cree <ecree@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5a55a72a
...@@ -3758,7 +3758,8 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) ...@@ -3758,7 +3758,8 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
struct netdev_hw_addr *uc; struct netdev_hw_addr *uc;
struct netdev_hw_addr *mc; struct netdev_hw_addr *mc;
unsigned int filter_idx; unsigned int filter_idx;
int i, n, rc; int i, rc;
bool uc_promisc = false, mc_promisc = false;
if (!efx_dev_registered(efx)) if (!efx_dev_registered(efx))
return; return;
...@@ -3768,13 +3769,11 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) ...@@ -3768,13 +3769,11 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
/* Mark old filters that may need to be removed */ /* Mark old filters that may need to be removed */
spin_lock_bh(&efx->filter_lock); spin_lock_bh(&efx->filter_lock);
n = table->dev_uc_count < 0 ? 1 : table->dev_uc_count; for (i = 0; i < table->dev_uc_count; i++) {
for (i = 0; i < n; i++) {
filter_idx = table->dev_uc_list[i].id % HUNT_FILTER_TBL_ROWS; filter_idx = table->dev_uc_list[i].id % HUNT_FILTER_TBL_ROWS;
table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;
} }
n = table->dev_mc_count < 0 ? 1 : table->dev_mc_count; for (i = 0; i < table->dev_mc_count; i++) {
for (i = 0; i < n; i++) {
filter_idx = table->dev_mc_list[i].id % HUNT_FILTER_TBL_ROWS; filter_idx = table->dev_mc_list[i].id % HUNT_FILTER_TBL_ROWS;
table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;
} }
...@@ -3786,7 +3785,8 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) ...@@ -3786,7 +3785,8 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
netif_addr_lock_bh(net_dev); netif_addr_lock_bh(net_dev);
if (net_dev->flags & IFF_PROMISC || if (net_dev->flags & IFF_PROMISC ||
netdev_uc_count(net_dev) >= EFX_EF10_FILTER_DEV_UC_MAX) { netdev_uc_count(net_dev) >= EFX_EF10_FILTER_DEV_UC_MAX) {
table->dev_uc_count = -1; table->dev_uc_count = 0;
uc_promisc = true;
} else { } else {
table->dev_uc_count = 1 + netdev_uc_count(net_dev); table->dev_uc_count = 1 + netdev_uc_count(net_dev);
ether_addr_copy(table->dev_uc_list[0].addr, net_dev->dev_addr); ether_addr_copy(table->dev_uc_list[0].addr, net_dev->dev_addr);
...@@ -3796,9 +3796,11 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) ...@@ -3796,9 +3796,11 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
i++; i++;
} }
} }
if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI) || if (netdev_mc_count(net_dev) + 2 /* room for broadcast and promisc */
netdev_mc_count(net_dev) >= EFX_EF10_FILTER_DEV_MC_MAX) { >= EFX_EF10_FILTER_DEV_MC_MAX) {
table->dev_mc_count = -1; table->dev_mc_count = 1;
eth_broadcast_addr(table->dev_mc_list[0].addr);
mc_promisc = true;
} else { } else {
table->dev_mc_count = 1 + netdev_mc_count(net_dev); table->dev_mc_count = 1 + netdev_mc_count(net_dev);
eth_broadcast_addr(table->dev_mc_list[0].addr); eth_broadcast_addr(table->dev_mc_list[0].addr);
...@@ -3807,31 +3809,32 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) ...@@ -3807,31 +3809,32 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
ether_addr_copy(table->dev_mc_list[i].addr, mc->addr); ether_addr_copy(table->dev_mc_list[i].addr, mc->addr);
i++; i++;
} }
if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI))
mc_promisc = true;
} }
netif_addr_unlock_bh(net_dev); netif_addr_unlock_bh(net_dev);
/* Insert/renew unicast filters */ /* Insert/renew unicast filters */
if (table->dev_uc_count >= 0) { for (i = 0; i < table->dev_uc_count; i++) {
for (i = 0; i < table->dev_uc_count; i++) { efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, EFX_FILTER_FLAG_RX_RSS,
EFX_FILTER_FLAG_RX_RSS, 0);
0); efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC,
efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, table->dev_uc_list[i].addr);
table->dev_uc_list[i].addr); rc = efx_ef10_filter_insert(efx, &spec, true);
rc = efx_ef10_filter_insert(efx, &spec, true); if (rc < 0) {
if (rc < 0) { /* Fall back to unicast-promisc */
/* Fall back to unicast-promisc */ while (i--)
while (i--) efx_ef10_filter_remove_safe(
efx_ef10_filter_remove_safe( efx, EFX_FILTER_PRI_AUTO,
efx, EFX_FILTER_PRI_AUTO, table->dev_uc_list[i].id);
table->dev_uc_list[i].id); table->dev_uc_count = 0;
table->dev_uc_count = -1; uc_promisc = true;
break; break;
}
table->dev_uc_list[i].id = rc;
} }
table->dev_uc_list[i].id = rc;
} }
if (table->dev_uc_count < 0) { if (uc_promisc) {
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
EFX_FILTER_FLAG_RX_RSS, EFX_FILTER_FLAG_RX_RSS,
0); 0);
...@@ -3839,34 +3842,34 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) ...@@ -3839,34 +3842,34 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
rc = efx_ef10_filter_insert(efx, &spec, true); rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) { if (rc < 0) {
WARN_ON(1); WARN_ON(1);
table->dev_uc_count = 0;
} else { } else {
table->dev_uc_list[0].id = rc; table->dev_uc_list[table->dev_uc_count++].id = rc;
} }
} }
/* Insert/renew multicast filters */ /* Insert/renew multicast filters */
if (table->dev_mc_count >= 0) { for (i = 0; i < table->dev_mc_count; i++) {
for (i = 0; i < table->dev_mc_count; i++) { efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, EFX_FILTER_FLAG_RX_RSS,
EFX_FILTER_FLAG_RX_RSS, 0);
0); efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC,
efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, table->dev_mc_list[i].addr);
table->dev_mc_list[i].addr); rc = efx_ef10_filter_insert(efx, &spec, true);
rc = efx_ef10_filter_insert(efx, &spec, true); if (rc < 0) {
if (rc < 0) { /* Fall back to multicast-promisc.
/* Fall back to multicast-promisc */ * Leave the broadcast filter.
while (i--) */
efx_ef10_filter_remove_safe( while (i > 1)
efx, EFX_FILTER_PRI_AUTO, efx_ef10_filter_remove_safe(
table->dev_mc_list[i].id); efx, EFX_FILTER_PRI_AUTO,
table->dev_mc_count = -1; table->dev_mc_list[--i].id);
break; table->dev_mc_count = i;
} mc_promisc = true;
table->dev_mc_list[i].id = rc; break;
} }
table->dev_mc_list[i].id = rc;
} }
if (table->dev_mc_count < 0) { if (mc_promisc) {
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
EFX_FILTER_FLAG_RX_RSS, EFX_FILTER_FLAG_RX_RSS,
0); 0);
...@@ -3874,9 +3877,8 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) ...@@ -3874,9 +3877,8 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
rc = efx_ef10_filter_insert(efx, &spec, true); rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) { if (rc < 0) {
WARN_ON(1); WARN_ON(1);
table->dev_mc_count = 0;
} else { } else {
table->dev_mc_list[0].id = rc; table->dev_mc_list[table->dev_mc_count++].id = rc;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment