Merge branch 'net-ndo_fdb_add-del-have-drivers-report-whether-they-notified'

Petr Machata says:

====================
net: ndo_fdb_add/del: Have drivers report whether they notified

Currently when FDB entries are added to or deleted from a VXLAN netdevice,
the VXLAN driver emits one notification, including the VXLAN-specific
attributes. The core however always sends a notification as well, a generic
one. Thus two notifications are unnecessarily sent for these operations. A
similar situation comes up with bridge driver, which also emits
notifications on its own.

 # ip link add name vx type vxlan id 1000 dstport 4789
 # bridge monitor fdb &
 [1] 1981693
 # bridge fdb add de:ad:be:ef:13:37 dev vx self dst 192.0.2.1
 de:ad:be:ef:13:37 dev vx dst 192.0.2.1 self permanent
 de:ad:be:ef:13:37 dev vx self permanent

In order to prevent this duplicity, add a parameter, bool *notified, to
ndo_fdb_add and ndo_fdb_del. The flag is primed to false, and if the callee
sends a notification on its own, it sets the flag to true, thus informing
the core that it should not generate another notification.

Patches #1 to #2 are concerned with the above.

In the remaining patches, #3 to #7, add a selftest. This takes place across
several patches. Many of the helpers we would like to use for the test are
in forwarding/lib.sh, whereas net/ is a more suitable place for the test,
so the libraries need to be massaged a bit first.
====================

Link: https://patch.msgid.link/cover.1731589511.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2024-11-15 16:39:21 -08:00
commit e709d44241
29 changed files with 419 additions and 267 deletions

View File

@ -13095,12 +13095,13 @@ static int i40e_get_phys_port_id(struct net_device *netdev,
* @addr: the MAC address entry being added
* @vid: VLAN ID
* @flags: instructions from stack about fdb operation
* @notified: whether notification was emitted
* @extack: netlink extended ack, unused currently
*/
static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
u16 flags,
u16 flags, bool *notified,
struct netlink_ext_ack *extack)
{
struct i40e_netdev_priv *np = netdev_priv(dev);

View File

@ -6125,12 +6125,14 @@ ice_set_tx_maxrate(struct net_device *netdev, int queue_index, u32 maxrate)
* @addr: the MAC address entry being added
* @vid: VLAN ID
* @flags: instructions from stack about fdb operation
* @notified: whether notification was emitted
* @extack: netlink extended ack
*/
static int
ice_fdb_add(struct ndmsg *ndm, struct nlattr __always_unused *tb[],
struct net_device *dev, const unsigned char *addr, u16 vid,
u16 flags, struct netlink_ext_ack __always_unused *extack)
u16 flags, bool *notified,
struct netlink_ext_ack __always_unused *extack)
{
int err;
@ -6164,12 +6166,14 @@ ice_fdb_add(struct ndmsg *ndm, struct nlattr __always_unused *tb[],
* @dev: the net device pointer
* @addr: the MAC address entry being added
* @vid: VLAN ID
* @notified: whether notification was emitted
* @extack: netlink extended ack
*/
static int
ice_fdb_del(struct ndmsg *ndm, __always_unused struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr,
__always_unused u16 vid, struct netlink_ext_ack *extack)
__always_unused u16 vid, bool *notified,
struct netlink_ext_ack *extack)
{
int err;

View File

@ -2486,7 +2486,7 @@ static int igb_set_features(struct net_device *netdev,
static int igb_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
u16 flags,
u16 flags, bool *notified,
struct netlink_ext_ack *extack)
{
/* guarantee we can provide a unique filter for the unicast address */

View File

@ -9954,7 +9954,7 @@ static int ixgbe_set_features(struct net_device *netdev,
static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
u16 flags,
u16 flags, bool *notified,
struct netlink_ext_ack *extack)
{
/* guarantee we can provide a unique filter for the unicast address */

View File

@ -730,7 +730,7 @@ static void ocelot_get_stats64(struct net_device *dev,
static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr,
u16 vid, u16 flags,
u16 vid, u16 flags, bool *notified,
struct netlink_ext_ack *extack)
{
struct ocelot_port_private *priv = netdev_priv(dev);
@ -744,7 +744,7 @@ static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
struct netlink_ext_ack *extack)
bool *notified, struct netlink_ext_ack *extack)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;

View File

@ -367,7 +367,7 @@ static int qlcnic_set_mac(struct net_device *netdev, void *p)
static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *netdev,
const unsigned char *addr, u16 vid,
const unsigned char *addr, u16 vid, bool *notified,
struct netlink_ext_ack *extack)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
@ -394,7 +394,7 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *netdev,
const unsigned char *addr, u16 vid, u16 flags,
struct netlink_ext_ack *extack)
bool *notified, struct netlink_ext_ack *extack)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
int err = 0;

View File

@ -1024,7 +1024,7 @@ static int macvlan_vlan_rx_kill_vid(struct net_device *dev,
static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
u16 flags,
u16 flags, bool *notified,
struct netlink_ext_ack *extack)
{
struct macvlan_dev *vlan = netdev_priv(dev);
@ -1049,7 +1049,7 @@ static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
static int macvlan_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
const unsigned char *addr, u16 vid, bool *notified,
struct netlink_ext_ack *extack)
{
struct macvlan_dev *vlan = netdev_priv(dev);

View File

@ -1241,7 +1241,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid, u16 flags,
struct netlink_ext_ack *extack)
bool *notified, struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
/* struct net *net = dev_net(vxlan->dev); */
@ -1277,6 +1277,9 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
nhid, true, extack);
spin_unlock_bh(&vxlan->hash_lock[hash_index]);
if (!err)
*notified = true;
return err;
}
@ -1316,7 +1319,7 @@ out:
/* Delete entry (via netlink) */
static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
const unsigned char *addr, u16 vid, bool *notified,
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
@ -1338,6 +1341,9 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
true);
spin_unlock_bh(&vxlan->hash_lock[hash_index]);
if (!err)
*notified = true;
return err;
}

View File

@ -1248,12 +1248,17 @@ struct netdev_net_notifier {
* int (*ndo_fdb_add)(struct ndmsg *ndm, struct nlattr *tb[],
* struct net_device *dev,
* const unsigned char *addr, u16 vid, u16 flags,
* struct netlink_ext_ack *extack);
* bool *notified, struct netlink_ext_ack *extack);
* Adds an FDB entry to dev for addr.
* Callee shall set *notified to true if it sent any appropriate
* notification(s). Otherwise core will send a generic one.
* int (*ndo_fdb_del)(struct ndmsg *ndm, struct nlattr *tb[],
* struct net_device *dev,
* const unsigned char *addr, u16 vid)
* const unsigned char *addr, u16 vid
* bool *notified, struct netlink_ext_ack *extack);
* Deletes the FDB entry from dev corresponding to addr.
* Callee shall set *notified to true if it sent any appropriate
* notification(s). Otherwise core will send a generic one.
* int (*ndo_fdb_del_bulk)(struct nlmsghdr *nlh, struct net_device *dev,
* struct netlink_ext_ack *extack);
* int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb,
@ -1525,12 +1530,15 @@ struct net_device_ops {
const unsigned char *addr,
u16 vid,
u16 flags,
bool *notified,
struct netlink_ext_ack *extack);
int (*ndo_fdb_del)(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr,
u16 vid, struct netlink_ext_ack *extack);
u16 vid,
bool *notified,
struct netlink_ext_ack *extack);
int (*ndo_fdb_del_bulk)(struct nlmsghdr *nlh,
struct net_device *dev,
struct netlink_ext_ack *extack);

View File

@ -1152,7 +1152,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
struct net_bridge_port *p, const unsigned char *addr,
u16 nlh_flags, u16 vid, struct nlattr *nfea_tb[],
struct netlink_ext_ack *extack)
bool *notified, struct netlink_ext_ack *extack)
{
int err = 0;
@ -1183,6 +1183,8 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
spin_unlock_bh(&br->hash_lock);
}
if (!err)
*notified = true;
return err;
}
@ -1195,7 +1197,7 @@ static const struct nla_policy br_nda_fdb_pol[NFEA_MAX + 1] = {
int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid, u16 nlh_flags,
struct netlink_ext_ack *extack)
bool *notified, struct netlink_ext_ack *extack)
{
struct nlattr *nfea_tb[NFEA_MAX + 1], *attr;
struct net_bridge_vlan_group *vg;
@ -1258,10 +1260,10 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
/* VID was specified, so use it. */
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid, nfea_tb,
extack);
notified, extack);
} else {
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0, nfea_tb,
extack);
notified, extack);
if (err || !vg || !vg->num_vlans)
goto out;
@ -1273,7 +1275,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
if (!br_vlan_should_use(v))
continue;
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid,
nfea_tb, extack);
nfea_tb, notified, extack);
if (err)
goto out;
}
@ -1285,7 +1287,7 @@ out:
static int fdb_delete_by_addr_and_port(struct net_bridge *br,
const struct net_bridge_port *p,
const u8 *addr, u16 vlan)
const u8 *addr, u16 vlan, bool *notified)
{
struct net_bridge_fdb_entry *fdb;
@ -1294,18 +1296,19 @@ static int fdb_delete_by_addr_and_port(struct net_bridge *br,
return -ENOENT;
fdb_delete(br, fdb, true);
*notified = true;
return 0;
}
static int __br_fdb_delete(struct net_bridge *br,
const struct net_bridge_port *p,
const unsigned char *addr, u16 vid)
const unsigned char *addr, u16 vid, bool *notified)
{
int err;
spin_lock_bh(&br->hash_lock);
err = fdb_delete_by_addr_and_port(br, p, addr, vid);
err = fdb_delete_by_addr_and_port(br, p, addr, vid, notified);
spin_unlock_bh(&br->hash_lock);
return err;
@ -1314,7 +1317,7 @@ static int __br_fdb_delete(struct net_bridge *br,
/* Remove neighbor entry with RTM_DELNEIGH */
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
const unsigned char *addr, u16 vid, bool *notified,
struct netlink_ext_ack *extack)
{
struct net_bridge_vlan_group *vg;
@ -1337,19 +1340,19 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
}
if (vid) {
err = __br_fdb_delete(br, p, addr, vid);
err = __br_fdb_delete(br, p, addr, vid, notified);
} else {
struct net_bridge_vlan *v;
err = -ENOENT;
err &= __br_fdb_delete(br, p, addr, 0);
err &= __br_fdb_delete(br, p, addr, 0, notified);
if (!vg || !vg->num_vlans)
return err;
list_for_each_entry(v, &vg->vlan_list, vlist) {
if (!br_vlan_should_use(v))
continue;
err &= __br_fdb_delete(br, p, addr, v->vid);
err &= __br_fdb_delete(br, p, addr, v->vid, notified);
}
}

View File

@ -853,12 +853,12 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr, u16 vid,
struct netlink_ext_ack *extack);
bool *notified, struct netlink_ext_ack *extack);
int br_fdb_delete_bulk(struct nlmsghdr *nlh, struct net_device *dev,
struct netlink_ext_ack *extack);
int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
const unsigned char *addr, u16 vid, u16 nlh_flags,
struct netlink_ext_ack *extack);
bool *notified, struct netlink_ext_ack *extack);
int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev, struct net_device *fdev, int *idx);
int br_fdb_get(struct sk_buff *skb, struct nlattr *tb[], struct net_device *dev,

View File

@ -4578,9 +4578,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
netif_is_bridge_port(dev)) {
struct net_device *br_dev = netdev_master_upper_dev_get(dev);
const struct net_device_ops *ops = br_dev->netdev_ops;
bool notified = false;
err = ops->ndo_fdb_add(ndm, tb, dev, addr, vid,
nlh->nlmsg_flags, extack);
nlh->nlmsg_flags, &notified, extack);
if (err)
goto out;
else
@ -4589,16 +4590,18 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
/* Embedded bridge, macvlan, and any other device support */
if ((ndm->ndm_flags & NTF_SELF)) {
bool notified = false;
if (dev->netdev_ops->ndo_fdb_add)
err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr,
vid,
nlh->nlmsg_flags,
extack);
&notified, extack);
else
err = ndo_dflt_fdb_add(ndm, tb, dev, addr, vid,
nlh->nlmsg_flags);
if (!err) {
if (!err && !notified) {
rtnl_fdb_notify(dev, addr, vid, RTM_NEWNEIGH,
ndm->ndm_state);
ndm->ndm_flags &= ~NTF_SELF;
@ -4698,11 +4701,13 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
netif_is_bridge_port(dev)) {
struct net_device *br_dev = netdev_master_upper_dev_get(dev);
bool notified = false;
ops = br_dev->netdev_ops;
if (!del_bulk) {
if (ops->ndo_fdb_del)
err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid, extack);
err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid,
&notified, extack);
} else {
if (ops->ndo_fdb_del_bulk)
err = ops->ndo_fdb_del_bulk(nlh, dev, extack);
@ -4716,10 +4721,13 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
/* Embedded bridge, macvlan, and any other device support */
if (ndm->ndm_flags & NTF_SELF) {
bool notified = false;
ops = dev->netdev_ops;
if (!del_bulk) {
if (ops->ndo_fdb_del)
err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid, extack);
err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid,
&notified, extack);
else
err = ndo_dflt_fdb_del(ndm, tb, dev, addr, vid);
} else {
@ -4730,7 +4738,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
}
if (!err) {
if (!del_bulk)
if (!del_bulk && !notified)
rtnl_fdb_notify(dev, addr, vid, RTM_DELNEIGH,
ndm->ndm_state);
ndm->ndm_flags &= ~NTF_SELF;

View File

@ -116,7 +116,7 @@ dev_del_test()
log_test "Device delete"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
}
trap cleanup EXIT

View File

@ -595,7 +595,7 @@ irif_disabled_test()
log_test "Ingress RIF disabled"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
ip link set dev $rp1 nomaster
__addr_add_del $rp1 add 192.0.2.2/24 2001:db8:1::2/64
ip link del dev br0 type bridge
@ -645,7 +645,7 @@ erif_disabled_test()
log_test "Egress RIF disabled"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
__addr_add_del $rp1 add 192.0.2.2/24 2001:db8:1::2/64
ip link del dev br0 type bridge
devlink_trap_action_set $trap_name "drop"

View File

@ -202,7 +202,7 @@ mtu_value_is_too_small_test()
mtu_restore $rp2
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $h1 ingress protocol ip pref 1 handle 101 flower
}
@ -235,7 +235,7 @@ __ttl_value_is_too_small_test()
log_test "TTL value is too small: TTL=$ttl_val"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $h1 ingress protocol ip pref 1 handle 101 flower
}
@ -299,7 +299,7 @@ __mc_reverse_path_forwarding_test()
log_test "Multicast reverse path forwarding: $desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $rp2 egress protocol $proto pref 1 handle 101 flower
}
@ -347,7 +347,7 @@ __reject_route_test()
log_test "Reject route: $desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
ip route del unreachable $unreachable
tc filter del dev $h1 ingress protocol $proto pref 1 handle 101 flower
}
@ -542,7 +542,7 @@ ipv4_lpm_miss_test()
log_test "LPM miss: IPv4"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
vrf_without_routes_destroy
}
@ -569,7 +569,7 @@ ipv6_lpm_miss_test()
log_test "LPM miss: IPv6"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
vrf_without_routes_destroy
}

View File

@ -176,7 +176,7 @@ ecn_decap_test()
log_test "$desc: Inner ECN is not ECT and outer is $ecn_desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $swp1 egress protocol ip pref 1 handle 101 flower
}
@ -207,7 +207,7 @@ no_matching_tunnel_test()
log_test "$desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $swp1 egress protocol ip pref 1 handle 101 flower
}

View File

@ -176,7 +176,7 @@ ecn_decap_test()
log_test "$desc: Inner ECN is not ECT and outer is $ecn_desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $swp1 egress protocol ipv6 pref 1 handle 101 flower
}
@ -207,7 +207,7 @@ no_matching_tunnel_test()
log_test "$desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $swp1 egress protocol ipv6 pref 1 handle 101 flower
}

View File

@ -183,7 +183,7 @@ ecn_decap_test()
log_test "$desc: Inner ECN is not ECT and outer is $ecn_desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $swp1 egress protocol ip pref 1 handle 101 flower
}
@ -253,7 +253,7 @@ corrupted_packet_test()
log_test "$desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $swp1 egress protocol ip pref 1 handle 101 flower
}

View File

@ -188,7 +188,7 @@ ecn_decap_test()
log_test "$desc: Inner ECN is not ECT and outer is $ecn_desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $swp1 egress protocol ipv6 pref 1 handle 101 flower
}
@ -262,7 +262,7 @@ corrupted_packet_test()
log_test "$desc"
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $swp1 egress protocol ipv6 pref 1 handle 101 flower
}

View File

@ -218,7 +218,7 @@ psample_capture_start()
psample_capture_stop()
{
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
}
__tc_sample_rate_test()
@ -499,7 +499,7 @@ tc_sample_md_out_tc_occ_test()
backlog=$(tc -j -p -s qdisc show dev $rp2 | jq '.[0]["backlog"]')
# Kill mausezahn.
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
psample_capture_stop

View File

@ -94,7 +94,7 @@ route_addition_check()
sleep 1
$IP route add $route dev dummy1
sleep 1
kill %% && wait %% &> /dev/null
kill_process %%
route_notify_check $outfile $expected_num_notifications $offload_failed
rm -f $outfile
@ -148,7 +148,7 @@ route_deletion_check()
sleep 1
$IP route del $route dev dummy1
sleep 1
kill %% && wait %% &> /dev/null
kill_process %%
route_notify_check $outfile $expected_num_notifications
rm -f $outfile
@ -191,7 +191,7 @@ route_replacement_check()
sleep 1
$IP route replace $route dev dummy2
sleep 1
kill %% && wait %% &> /dev/null
kill_process %%
route_notify_check $outfile $expected_num_notifications
rm -f $outfile

View File

@ -93,7 +93,7 @@ TEST_PROGS += test_vxlan_mdb.sh
TEST_PROGS += test_bridge_neigh_suppress.sh
TEST_PROGS += test_vxlan_nolocalbypass.sh
TEST_PROGS += test_bridge_backup_port.sh
TEST_PROGS += fdb_flush.sh
TEST_PROGS += fdb_flush.sh fdb_notify.sh
TEST_PROGS += fq_band_pktlimit.sh
TEST_PROGS += vlan_hw_filter.sh
TEST_PROGS += bpf_offload.py

View File

@ -77,7 +77,7 @@ sw_drops_test()
rm ${dir}/packets.pcap
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
timeout 5 dwdump -o sw -w ${dir}/packets.pcap
(( $(tshark -r ${dir}/packets.pcap \
-Y 'ip.dst == 192.0.2.10' 2> /dev/null | wc -l) == 0))

View File

@ -0,0 +1,96 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
source lib.sh
ALL_TESTS="
test_dup_bridge
test_dup_vxlan_self
test_dup_vxlan_master
test_dup_macvlan_self
test_dup_macvlan_master
"
do_test_dup()
{
local op=$1; shift
local what=$1; shift
local tmpf
RET=0
tmpf=$(mktemp)
defer rm "$tmpf"
defer_scope_push
bridge monitor fdb &> "$tmpf" &
defer kill_process $!
sleep 0.5
bridge fdb "$op" 00:11:22:33:44:55 vlan 1 "$@"
sleep 0.5
defer_scope_pop
local count=$(grep -c -e 00:11:22:33:44:55 $tmpf)
((count == 1))
check_err $? "Got $count notifications, expected 1"
log_test "$what $op: Duplicate notifications"
}
test_dup_bridge()
{
ip_link_add br up type bridge vlan_filtering 1
do_test_dup add "bridge" dev br self
do_test_dup del "bridge" dev br self
}
test_dup_vxlan_self()
{
ip_link_add br up type bridge vlan_filtering 1
ip_link_add vx up type vxlan id 2000 dstport 4789
ip_link_master vx br
do_test_dup add "vxlan" dev vx self dst 192.0.2.1
do_test_dup del "vxlan" dev vx self dst 192.0.2.1
}
test_dup_vxlan_master()
{
ip_link_add br up type bridge vlan_filtering 1
ip_link_add vx up type vxlan id 2000 dstport 4789
ip_link_master vx br
do_test_dup add "vxlan master" dev vx master
do_test_dup del "vxlan master" dev vx master
}
test_dup_macvlan_self()
{
ip_link_add dd up type dummy
ip_link_add mv up link dd type macvlan mode passthru
do_test_dup add "macvlan self" dev mv self
do_test_dup del "macvlan self" dev mv self
}
test_dup_macvlan_master()
{
ip_link_add br up type bridge vlan_filtering 1
ip_link_add dd up type dummy
ip_link_add mv up link dd type macvlan mode passthru
ip_link_master mv br
do_test_dup add "macvlan master" dev mv self
do_test_dup del "macvlan master" dev mv self
}
cleanup()
{
defer_scopes_cleanup
}
trap cleanup EXIT
tests_run
exit $EXIT_STATUS

View File

@ -689,7 +689,7 @@ fib6_notify_test()
log_test $ret 0 "ipv6 route add notify"
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
#rm errors.txt
@ -736,7 +736,7 @@ fib_notify_test()
log_test $ret 0 "ipv4 route add notify"
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
rm errors.txt
@ -2328,7 +2328,7 @@ ipv4_mangle_test()
$IP route del table 123 172.16.101.0/24 dev veth1
$IP rule del pref 100
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
rm $tmp_file
route_cleanup
@ -2386,7 +2386,7 @@ ipv6_mangle_test()
$IP -6 route del table 123 2001:db8:101::/64 dev veth1
$IP -6 rule del pref 100
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
rm $tmp_file
route_cleanup

View File

@ -501,7 +501,7 @@ devlink_trap_drop_cleanup()
local pref=$1; shift
local handle=$1; shift
kill $mz_pid && wait $mz_pid &> /dev/null
kill_process $mz_pid
tc filter del dev $dev egress protocol $proto pref $pref handle $handle flower
}

View File

@ -48,7 +48,6 @@ declare -A NETIFS=(
: "${WAIT_TIME:=5}"
# Whether to pause on, respectively, after a failure and before cleanup.
: "${PAUSE_ON_FAIL:=no}"
: "${PAUSE_ON_CLEANUP:=no}"
# Whether to create virtual interfaces, and what netdevice type they should be.
@ -446,191 +445,6 @@ done
##############################################################################
# Helpers
# Exit status to return at the end. Set in case one of the tests fails.
EXIT_STATUS=0
# Per-test return value. Clear at the beginning of each test.
RET=0
ret_set_ksft_status()
{
local ksft_status=$1; shift
local msg=$1; shift
RET=$(ksft_status_merge $RET $ksft_status)
if (( $? )); then
retmsg=$msg
fi
}
# Whether FAILs should be interpreted as XFAILs. Internal.
FAIL_TO_XFAIL=
check_err()
{
local err=$1
local msg=$2
if ((err)); then
if [[ $FAIL_TO_XFAIL = yes ]]; then
ret_set_ksft_status $ksft_xfail "$msg"
else
ret_set_ksft_status $ksft_fail "$msg"
fi
fi
}
check_fail()
{
local err=$1
local msg=$2
check_err $((!err)) "$msg"
}
check_err_fail()
{
local should_fail=$1; shift
local err=$1; shift
local what=$1; shift
if ((should_fail)); then
check_fail $err "$what succeeded, but should have failed"
else
check_err $err "$what failed"
fi
}
xfail()
{
FAIL_TO_XFAIL=yes "$@"
}
xfail_on_slow()
{
if [[ $KSFT_MACHINE_SLOW = yes ]]; then
FAIL_TO_XFAIL=yes "$@"
else
"$@"
fi
}
omit_on_slow()
{
if [[ $KSFT_MACHINE_SLOW != yes ]]; then
"$@"
fi
}
xfail_on_veth()
{
local dev=$1; shift
local kind
kind=$(ip -j -d link show dev $dev |
jq -r '.[].linkinfo.info_kind')
if [[ $kind = veth ]]; then
FAIL_TO_XFAIL=yes "$@"
else
"$@"
fi
}
log_test_result()
{
local test_name=$1; shift
local opt_str=$1; shift
local result=$1; shift
local retmsg=$1; shift
printf "TEST: %-60s [%s]\n" "$test_name $opt_str" "$result"
if [[ $retmsg ]]; then
printf "\t%s\n" "$retmsg"
fi
}
pause_on_fail()
{
if [[ $PAUSE_ON_FAIL == yes ]]; then
echo "Hit enter to continue, 'q' to quit"
read a
[[ $a == q ]] && exit 1
fi
}
handle_test_result_pass()
{
local test_name=$1; shift
local opt_str=$1; shift
log_test_result "$test_name" "$opt_str" " OK "
}
handle_test_result_fail()
{
local test_name=$1; shift
local opt_str=$1; shift
log_test_result "$test_name" "$opt_str" FAIL "$retmsg"
pause_on_fail
}
handle_test_result_xfail()
{
local test_name=$1; shift
local opt_str=$1; shift
log_test_result "$test_name" "$opt_str" XFAIL "$retmsg"
pause_on_fail
}
handle_test_result_skip()
{
local test_name=$1; shift
local opt_str=$1; shift
log_test_result "$test_name" "$opt_str" SKIP "$retmsg"
}
log_test()
{
local test_name=$1
local opt_str=$2
if [[ $# -eq 2 ]]; then
opt_str="($opt_str)"
fi
if ((RET == ksft_pass)); then
handle_test_result_pass "$test_name" "$opt_str"
elif ((RET == ksft_xfail)); then
handle_test_result_xfail "$test_name" "$opt_str"
elif ((RET == ksft_skip)); then
handle_test_result_skip "$test_name" "$opt_str"
else
handle_test_result_fail "$test_name" "$opt_str"
fi
EXIT_STATUS=$(ksft_exit_status_merge $EXIT_STATUS $RET)
return $RET
}
log_test_skip()
{
RET=$ksft_skip retmsg= log_test "$@"
}
log_test_xfail()
{
RET=$ksft_xfail retmsg= log_test "$@"
}
log_info()
{
local msg=$1
echo "INFO: $msg"
}
not()
{
"$@"
@ -1398,16 +1212,6 @@ matchall_sink_create()
action drop
}
tests_run()
{
local current_test
for current_test in ${TESTS:-$ALL_TESTS}; do
in_defer_scope \
$current_test
done
}
cleanup()
{
pre_cleanup
@ -1770,8 +1574,7 @@ stop_traffic()
{
local pid=${1-%%}; shift
# Suppress noise from killing mausezahn.
{ kill $pid && wait $pid; } 2>/dev/null
kill_process "$pid"
}
declare -A cappid

View File

@ -148,7 +148,7 @@ police_common_test()
log_test "$test_name"
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
}
@ -198,7 +198,7 @@ police_shared_common_test()
log_test "$test_name"
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
}
police_shared_test()
@ -278,7 +278,7 @@ police_mirror_common_test()
log_test "$test_name"
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
tc filter del dev $pol_if $dir protocol ip pref 1 handle 101 flower
tc filter del dev $h3 ingress protocol ip pref 1 handle 101 flower
tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
@ -320,7 +320,7 @@ police_pps_common_test()
log_test "$test_name"
{ kill %% && wait %%; } 2>/dev/null
kill_process %%
tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
}

View File

@ -9,6 +9,9 @@ source "$net_dir/lib/sh/defer.sh"
: "${WAIT_TIMEOUT:=20}"
# Whether to pause on after a failure.
: "${PAUSE_ON_FAIL:=no}"
BUSYWAIT_TIMEOUT=$((WAIT_TIMEOUT * 1000)) # ms
# Kselftest framework constants.
@ -20,6 +23,11 @@ ksft_skip=4
# namespace list created by setup_ns
NS_LIST=()
# Exit status to return at the end. Set in case one of the tests fails.
EXIT_STATUS=0
# Per-test return value. Clear at the beginning of each test.
RET=0
##############################################################################
# Helpers
@ -236,3 +244,218 @@ tc_rule_handle_stats_get()
| jq ".[] | select(.options.handle == $handle) | \
.options.actions[0].stats$selector"
}
ret_set_ksft_status()
{
local ksft_status=$1; shift
local msg=$1; shift
RET=$(ksft_status_merge $RET $ksft_status)
if (( $? )); then
retmsg=$msg
fi
}
log_test_result()
{
local test_name=$1; shift
local opt_str=$1; shift
local result=$1; shift
local retmsg=$1; shift
printf "TEST: %-60s [%s]\n" "$test_name $opt_str" "$result"
if [[ $retmsg ]]; then
printf "\t%s\n" "$retmsg"
fi
}
pause_on_fail()
{
if [[ $PAUSE_ON_FAIL == yes ]]; then
echo "Hit enter to continue, 'q' to quit"
read a
[[ $a == q ]] && exit 1
fi
}
handle_test_result_pass()
{
local test_name=$1; shift
local opt_str=$1; shift
log_test_result "$test_name" "$opt_str" " OK "
}
handle_test_result_fail()
{
local test_name=$1; shift
local opt_str=$1; shift
log_test_result "$test_name" "$opt_str" FAIL "$retmsg"
pause_on_fail
}
handle_test_result_xfail()
{
local test_name=$1; shift
local opt_str=$1; shift
log_test_result "$test_name" "$opt_str" XFAIL "$retmsg"
pause_on_fail
}
handle_test_result_skip()
{
local test_name=$1; shift
local opt_str=$1; shift
log_test_result "$test_name" "$opt_str" SKIP "$retmsg"
}
log_test()
{
local test_name=$1
local opt_str=$2
if [[ $# -eq 2 ]]; then
opt_str="($opt_str)"
fi
if ((RET == ksft_pass)); then
handle_test_result_pass "$test_name" "$opt_str"
elif ((RET == ksft_xfail)); then
handle_test_result_xfail "$test_name" "$opt_str"
elif ((RET == ksft_skip)); then
handle_test_result_skip "$test_name" "$opt_str"
else
handle_test_result_fail "$test_name" "$opt_str"
fi
EXIT_STATUS=$(ksft_exit_status_merge $EXIT_STATUS $RET)
return $RET
}
log_test_skip()
{
RET=$ksft_skip retmsg= log_test "$@"
}
log_test_xfail()
{
RET=$ksft_xfail retmsg= log_test "$@"
}
log_info()
{
local msg=$1
echo "INFO: $msg"
}
tests_run()
{
local current_test
for current_test in ${TESTS:-$ALL_TESTS}; do
in_defer_scope \
$current_test
done
}
# Whether FAILs should be interpreted as XFAILs. Internal.
FAIL_TO_XFAIL=
check_err()
{
local err=$1
local msg=$2
if ((err)); then
if [[ $FAIL_TO_XFAIL = yes ]]; then
ret_set_ksft_status $ksft_xfail "$msg"
else
ret_set_ksft_status $ksft_fail "$msg"
fi
fi
}
check_fail()
{
local err=$1
local msg=$2
check_err $((!err)) "$msg"
}
check_err_fail()
{
local should_fail=$1; shift
local err=$1; shift
local what=$1; shift
if ((should_fail)); then
check_fail $err "$what succeeded, but should have failed"
else
check_err $err "$what failed"
fi
}
xfail()
{
FAIL_TO_XFAIL=yes "$@"
}
xfail_on_slow()
{
if [[ $KSFT_MACHINE_SLOW = yes ]]; then
FAIL_TO_XFAIL=yes "$@"
else
"$@"
fi
}
omit_on_slow()
{
if [[ $KSFT_MACHINE_SLOW != yes ]]; then
"$@"
fi
}
xfail_on_veth()
{
local dev=$1; shift
local kind
kind=$(ip -j -d link show dev $dev |
jq -r '.[].linkinfo.info_kind')
if [[ $kind = veth ]]; then
FAIL_TO_XFAIL=yes "$@"
else
"$@"
fi
}
kill_process()
{
local pid=$1; shift
# Suppress noise from killing the process.
{ kill $pid && wait $pid; } 2>/dev/null
}
ip_link_add()
{
local name=$1; shift
ip link add name "$name" "$@"
defer ip link del dev "$name"
}
ip_link_master()
{
local member=$1; shift
local master=$1; shift
ip link set dev "$member" master "$master"
defer ip link set dev "$member" nomaster
}