Skip to content

Commit c87c938

Browse files
mpalczew96anguy11
authored andcommitted
i40e: Add VF VLAN pruning
VFs by default are able to see all tagged traffic regardless of trust and VLAN filters configured. Add new private flag vf-vlan-pruning that allows changing of default VF behavior for tagged traffic. When the flag is turned on untrusted VF will only be able to receive untagged traffic or traffic with VLAN tags it has created interfaces for The flag is off by default and can only be changed if there are no VFs spawned on the PF. This flag will only be effective when no PVID is set on VF and VF is not trusted. Add new function that computes the correct VLAN ID for VF VLAN filters based on trust, PVID, vf-vlan-prune-disable flag and current VLAN ID. Testing Hints: Test 1: vf-vlan-pruning == off ============================== 1. Set the private flag > ethtool --set-priv-flag eth0 vf-vlan-pruning off (default setting) 2. Use scapy to send any VLAN tagged traffic and make sure the VF receives all VLAN tagged traffic that matches its destination MAC filters (unicast, multicast, and broadcast). Test 2: vf-vlan-pruning == on ============================== 1. Set the private flag > ethtool --set-priv-flag eth0 vf-vlan-pruning on 2. Use scapy to send any VLAN tagged traffic and make sure the VF does not receive any VLAN tagged traffic that matches its destination MAC filters (unicast, multicast, and broadcast). 3. Add a VLAN filter on the VF netdev > ip link add link eth0v0 name vlan10 type vlan id 10 4. Bring the VLAN netdev up > ip link set vlan10 up 4. Use scapy to send traffic with VLAN 10, VLAN 11 (anything not VLAN 10), and untagged traffic. Make sure the VF only receives VLAN 10 and untagged traffic when the link partner is sending. Test 3: vf-vlan-pruning == off && VF is in a port VLAN ============================== 1. Set the private flag > ethtool --set-priv-flag eth0 vf-vlan-pruning off (default setting) 2. Create a VF > echo 1 > sriov_numvfs 3. Put the VF in a port VLAN > ip link set eth0 vf 0 vlan 10 4. Use scapy to send traffic with VLAN 10 and VLAN 11 (anything not VLAN 10) and make sure the VF only receives untagged traffic when the link partner is sending VLAN 10 tagged traffic as the VLAN tag is expected to be stripped by HW for port VLANs and not visible to the VF. Test 4: Change vf-vlan-pruning while VFs are created ============================== echo 0 > sriov_numvfs ethtool --set-priv-flag eth0 vf-vlan-pruning off echo 1 > sriov_numvfs ethtool --set-priv-flag eth0 vf-vlan-pruning on (expect failure) Signed-off-by: Sylwester Dziedziuch <[email protected]> Signed-off-by: Przemyslaw Patynowski <[email protected]> Signed-off-by: Mateusz Palczewski <[email protected]> Tested-by: Konrad Jankowski <[email protected]> Signed-off-by: Tony Nguyen <[email protected]>
1 parent ba36c5b commit c87c938

File tree

4 files changed

+147
-6
lines changed

4 files changed

+147
-6
lines changed

drivers/net/ethernet/intel/i40e/i40e.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ struct i40e_pf {
565565
#define I40E_FLAG_DISABLE_FW_LLDP BIT(24)
566566
#define I40E_FLAG_RS_FEC BIT(25)
567567
#define I40E_FLAG_BASE_R_FEC BIT(26)
568+
#define I40E_FLAG_VF_VLAN_PRUNING BIT(27)
568569
/* TOTAL_PORT_SHUTDOWN
569570
* Allows to physically disable the link on the NIC's port.
570571
* If enabled, (after link down request from the OS)

drivers/net/ethernet/intel/i40e/i40e_ethtool.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ static const struct i40e_priv_flags i40e_gstrings_priv_flags[] = {
457457
I40E_PRIV_FLAG("disable-fw-lldp", I40E_FLAG_DISABLE_FW_LLDP, 0),
458458
I40E_PRIV_FLAG("rs-fec", I40E_FLAG_RS_FEC, 0),
459459
I40E_PRIV_FLAG("base-r-fec", I40E_FLAG_BASE_R_FEC, 0),
460+
I40E_PRIV_FLAG("vf-vlan-pruning",
461+
I40E_FLAG_VF_VLAN_PRUNING, 0),
460462
};
461463

462464
#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gstrings_priv_flags)
@@ -5285,6 +5287,13 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
52855287
return -EOPNOTSUPP;
52865288
}
52875289

5290+
if ((changed_flags & I40E_FLAG_VF_VLAN_PRUNING) &&
5291+
pf->num_alloc_vfs) {
5292+
dev_warn(&pf->pdev->dev,
5293+
"Changing vf-vlan-pruning flag while VF(s) are active is not supported\n");
5294+
return -EOPNOTSUPP;
5295+
}
5296+
52885297
if ((changed_flags & new_flags &
52895298
I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED) &&
52905299
(new_flags & I40E_FLAG_MFP_ENABLED))

drivers/net/ethernet/intel/i40e/i40e_main.c

Lines changed: 130 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,114 @@ static int i40e_correct_mac_vlan_filters(struct i40e_vsi *vsi,
13681368
return 0;
13691369
}
13701370

1371+
/**
1372+
* i40e_get_vf_new_vlan - Get new vlan id on a vf
1373+
* @vsi: the vsi to configure
1374+
* @new_mac: new mac filter to be added
1375+
* @f: existing mac filter, replaced with new_mac->f if new_mac is not NULL
1376+
* @vlan_filters: the number of active VLAN filters
1377+
* @trusted: flag if the VF is trusted
1378+
*
1379+
* Get new VLAN id based on current VLAN filters, trust, PVID
1380+
* and vf-vlan-prune-disable flag.
1381+
*
1382+
* Returns the value of the new vlan filter or
1383+
* the old value if no new filter is needed.
1384+
*/
1385+
static s16 i40e_get_vf_new_vlan(struct i40e_vsi *vsi,
1386+
struct i40e_new_mac_filter *new_mac,
1387+
struct i40e_mac_filter *f,
1388+
int vlan_filters,
1389+
bool trusted)
1390+
{
1391+
s16 pvid = le16_to_cpu(vsi->info.pvid);
1392+
struct i40e_pf *pf = vsi->back;
1393+
bool is_any;
1394+
1395+
if (new_mac)
1396+
f = new_mac->f;
1397+
1398+
if (pvid && f->vlan != pvid)
1399+
return pvid;
1400+
1401+
is_any = (trusted ||
1402+
!(pf->flags & I40E_FLAG_VF_VLAN_PRUNING));
1403+
1404+
if ((vlan_filters && f->vlan == I40E_VLAN_ANY) ||
1405+
(!is_any && !vlan_filters && f->vlan == I40E_VLAN_ANY) ||
1406+
(is_any && !vlan_filters && f->vlan == 0)) {
1407+
if (is_any)
1408+
return I40E_VLAN_ANY;
1409+
else
1410+
return 0;
1411+
}
1412+
1413+
return f->vlan;
1414+
}
1415+
1416+
/**
1417+
* i40e_correct_vf_mac_vlan_filters - Correct non-VLAN VF filters if necessary
1418+
* @vsi: the vsi to configure
1419+
* @tmp_add_list: list of filters ready to be added
1420+
* @tmp_del_list: list of filters ready to be deleted
1421+
* @vlan_filters: the number of active VLAN filters
1422+
* @trusted: flag if the VF is trusted
1423+
*
1424+
* Correct VF VLAN filters based on current VLAN filters, trust, PVID
1425+
* and vf-vlan-prune-disable flag.
1426+
*
1427+
* In case of memory allocation failure return -ENOMEM. Otherwise, return 0.
1428+
*
1429+
* This function is only expected to be called from within
1430+
* i40e_sync_vsi_filters.
1431+
*
1432+
* NOTE: This function expects to be called while under the
1433+
* mac_filter_hash_lock
1434+
*/
1435+
static int i40e_correct_vf_mac_vlan_filters(struct i40e_vsi *vsi,
1436+
struct hlist_head *tmp_add_list,
1437+
struct hlist_head *tmp_del_list,
1438+
int vlan_filters,
1439+
bool trusted)
1440+
{
1441+
struct i40e_mac_filter *f, *add_head;
1442+
struct i40e_new_mac_filter *new_mac;
1443+
struct hlist_node *h;
1444+
int bkt, new_vlan;
1445+
1446+
hlist_for_each_entry(new_mac, tmp_add_list, hlist) {
1447+
new_mac->f->vlan = i40e_get_vf_new_vlan(vsi, new_mac, NULL,
1448+
vlan_filters, trusted);
1449+
}
1450+
1451+
hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
1452+
new_vlan = i40e_get_vf_new_vlan(vsi, NULL, f, vlan_filters,
1453+
trusted);
1454+
if (new_vlan != f->vlan) {
1455+
add_head = i40e_add_filter(vsi, f->macaddr, new_vlan);
1456+
if (!add_head)
1457+
return -ENOMEM;
1458+
/* Create a temporary i40e_new_mac_filter */
1459+
new_mac = kzalloc(sizeof(*new_mac), GFP_ATOMIC);
1460+
if (!new_mac)
1461+
return -ENOMEM;
1462+
new_mac->f = add_head;
1463+
new_mac->state = add_head->state;
1464+
1465+
/* Add the new filter to the tmp list */
1466+
hlist_add_head(&new_mac->hlist, tmp_add_list);
1467+
1468+
/* Put the original filter into the delete list */
1469+
f->state = I40E_FILTER_REMOVE;
1470+
hash_del(&f->hlist);
1471+
hlist_add_head(&f->hlist, tmp_del_list);
1472+
}
1473+
}
1474+
1475+
vsi->has_vlan_filter = !!vlan_filters;
1476+
return 0;
1477+
}
1478+
13711479
/**
13721480
* i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM
13731481
* @vsi: the PF Main VSI - inappropriate for any other VSI
@@ -2423,10 +2531,14 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
24232531
vlan_filters++;
24242532
}
24252533

2426-
retval = i40e_correct_mac_vlan_filters(vsi,
2427-
&tmp_add_list,
2428-
&tmp_del_list,
2429-
vlan_filters);
2534+
if (vsi->type != I40E_VSI_SRIOV)
2535+
retval = i40e_correct_mac_vlan_filters
2536+
(vsi, &tmp_add_list, &tmp_del_list,
2537+
vlan_filters);
2538+
else
2539+
retval = i40e_correct_vf_mac_vlan_filters
2540+
(vsi, &tmp_add_list, &tmp_del_list,
2541+
vlan_filters, pf->vf[vsi->vf_id].trusted);
24302542

24312543
hlist_for_each_entry(new, &tmp_add_list, hlist)
24322544
netdev_hw_addr_refcnt(new->f, vsi->netdev, 1);
@@ -2855,8 +2967,21 @@ int i40e_add_vlan_all_mac(struct i40e_vsi *vsi, s16 vid)
28552967
int bkt;
28562968

28572969
hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
2858-
if (f->state == I40E_FILTER_REMOVE)
2970+
/* If we're asked to add a filter that has been marked for
2971+
* removal, it is safe to simply restore it to active state.
2972+
* __i40e_del_filter will have simply deleted any filters which
2973+
* were previously marked NEW or FAILED, so if it is currently
2974+
* marked REMOVE it must have previously been ACTIVE. Since we
2975+
* haven't yet run the sync filters task, just restore this
2976+
* filter to the ACTIVE state so that the sync task leaves it
2977+
* in place.
2978+
*/
2979+
if (f->state == I40E_FILTER_REMOVE && f->vlan == vid) {
2980+
f->state = I40E_FILTER_ACTIVE;
2981+
continue;
2982+
} else if (f->state == I40E_FILTER_REMOVE) {
28592983
continue;
2984+
}
28602985
add_f = i40e_add_filter(vsi, f->macaddr, vid);
28612986
if (!add_f) {
28622987
dev_info(&vsi->back->pdev->dev,

drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4349,6 +4349,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
43494349
/* duplicate request, so just return success */
43504350
goto error_pvid;
43514351

4352+
i40e_vlan_stripping_enable(vsi);
43524353
i40e_vc_reset_vf(vf, true);
43534354
/* During reset the VF got a new VSI, so refresh a pointer. */
43544355
vsi = pf->vsi[vf->lan_vsi_idx];
@@ -4364,7 +4365,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
43644365
* MAC addresses deleted.
43654366
*/
43664367
if ((!(vlan_id || qos) ||
4367-
vlanprio != le16_to_cpu(vsi->info.pvid)) &&
4368+
vlanprio != le16_to_cpu(vsi->info.pvid)) &&
43684369
vsi->info.pvid) {
43694370
ret = i40e_add_vlan_all_mac(vsi, I40E_VLAN_ANY);
43704371
if (ret) {
@@ -4727,6 +4728,11 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
47274728
goto out;
47284729

47294730
vf->trusted = setting;
4731+
4732+
/* request PF to sync mac/vlan filters for the VF */
4733+
set_bit(__I40E_MACVLAN_SYNC_PENDING, pf->state);
4734+
pf->vsi[vf->lan_vsi_idx]->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
4735+
47304736
i40e_vc_reset_vf(vf, true);
47314737
dev_info(&pf->pdev->dev, "VF %u is now %strusted\n",
47324738
vf_id, setting ? "" : "un");

0 commit comments

Comments
 (0)