Skip to content

Commit

Permalink
Merge branch 'mdb-get'
Browse files Browse the repository at this point in the history
Ido Schimmel says:

====================
Add MDB get support

This patchset adds MDB get support, allowing user space to request a
single MDB entry to be retrieved instead of dumping the entire MDB.
Support is added in both the bridge and VXLAN drivers.

Patches #1-gregkh#6 are small preparations in both drivers.

Patches gregkh#7-gregkh#8 add the required uAPI attributes for the new functionality
and the MDB get net device operation (NDO), respectively.

Patches gregkh#9-gregkh#10 implement the MDB get NDO in both drivers.

Patch gregkh#11 registers a handler for RTM_GETMDB messages in rtnetlink core.
The handler derives the net device from the ifindex specified in the
ancillary header and invokes its MDB get NDO.

Patches gregkh#12-gregkh#13 add selftests by converting tests that use MDB dump with
grep to the new MDB get functionality.

iproute2 changes can be found here [1].

v2:
* Patch gregkh#7: Add a comment to describe attributes structure.
* Patch gregkh#9: Add a comment above spin_lock_bh().

[1] https://github.com/idosch/iproute2/tree/submit/mdb_get_v1
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
davem330 committed Oct 27, 2023
2 parents eff8313 + 0514dd0 commit bc4c48e
Show file tree
Hide file tree
Showing 13 changed files with 608 additions and 199 deletions.
1 change: 1 addition & 0 deletions drivers/net/vxlan/vxlan_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -3226,6 +3226,7 @@ static const struct net_device_ops vxlan_netdev_ether_ops = {
.ndo_mdb_add = vxlan_mdb_add,
.ndo_mdb_del = vxlan_mdb_del,
.ndo_mdb_dump = vxlan_mdb_dump,
.ndo_mdb_get = vxlan_mdb_get,
.ndo_fill_metadata_dst = vxlan_fill_metadata_dst,
};

Expand Down
188 changes: 173 additions & 15 deletions drivers/net/vxlan/vxlan_mdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,12 +370,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,
return true;
}

static void vxlan_mdb_config_group_set(struct vxlan_mdb_config *cfg,
const struct br_mdb_entry *entry,
const struct nlattr *source_attr)
static void vxlan_mdb_group_set(struct vxlan_mdb_entry_key *group,
const struct br_mdb_entry *entry,
const struct nlattr *source_attr)
{
struct vxlan_mdb_entry_key *group = &cfg->group;

switch (entry->addr.proto) {
case htons(ETH_P_IP):
group->dst.sa.sa_family = AF_INET;
Expand Down Expand Up @@ -503,7 +501,7 @@ static int vxlan_mdb_config_attrs_init(struct vxlan_mdb_config *cfg,
entry->addr.proto, extack))
return -EINVAL;

vxlan_mdb_config_group_set(cfg, entry, mdbe_attrs[MDBE_ATTR_SOURCE]);
vxlan_mdb_group_set(&cfg->group, entry, mdbe_attrs[MDBE_ATTR_SOURCE]);

/* rtnetlink code only validates that IPv4 group address is
* multicast.
Expand Down Expand Up @@ -927,23 +925,20 @@ vxlan_mdb_nlmsg_src_list_size(const struct vxlan_mdb_entry_key *group,
return nlmsg_size;
}

static size_t vxlan_mdb_nlmsg_size(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
static size_t
vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
const struct vxlan_mdb_entry_key *group = &mdb_entry->key;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
size_t nlmsg_size;

nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) +
/* MDBA_MDB */
nla_total_size(0) +
/* MDBA_MDB_ENTRY */
nla_total_size(0) +
/* MDBA_MDB_ENTRY_INFO */
nla_total_size(sizeof(struct br_mdb_entry)) +
nlmsg_size = nla_total_size(sizeof(struct br_mdb_entry)) +
/* MDBA_MDB_EATTR_TIMER */
nla_total_size(sizeof(u32));

/* MDBA_MDB_EATTR_SOURCE */
if (vxlan_mdb_is_sg(group))
nlmsg_size += nla_total_size(vxlan_addr_size(&group->dst));
Expand Down Expand Up @@ -971,6 +966,19 @@ static size_t vxlan_mdb_nlmsg_size(const struct vxlan_dev *vxlan,
return nlmsg_size;
}

static size_t vxlan_mdb_nlmsg_size(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
return NLMSG_ALIGN(sizeof(struct br_port_msg)) +
/* MDBA_MDB */
nla_total_size(0) +
/* MDBA_MDB_ENTRY */
nla_total_size(0) +
/* Remote entry */
vxlan_mdb_nlmsg_remote_size(vxlan, mdb_entry, remote);
}

static int vxlan_mdb_nlmsg_fill(const struct vxlan_dev *vxlan,
struct sk_buff *skb,
const struct vxlan_mdb_entry *mdb_entry,
Expand Down Expand Up @@ -1298,6 +1306,156 @@ int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[],
return err;
}

static const struct nla_policy vxlan_mdbe_attrs_get_pol[MDBE_ATTR_MAX + 1] = {
[MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY,
sizeof(struct in_addr),
sizeof(struct in6_addr)),
[MDBE_ATTR_SRC_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range),
};

static int vxlan_mdb_get_parse(struct net_device *dev, struct nlattr *tb[],
struct vxlan_mdb_entry_key *group,
struct netlink_ext_ack *extack)
{
struct br_mdb_entry *entry = nla_data(tb[MDBA_GET_ENTRY]);
struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1];
struct vxlan_dev *vxlan = netdev_priv(dev);
int err;

memset(group, 0, sizeof(*group));
group->vni = vxlan->default_dst.remote_vni;

if (!tb[MDBA_GET_ENTRY_ATTRS]) {
vxlan_mdb_group_set(group, entry, NULL);
return 0;
}

err = nla_parse_nested(mdbe_attrs, MDBE_ATTR_MAX,
tb[MDBA_GET_ENTRY_ATTRS],
vxlan_mdbe_attrs_get_pol, extack);
if (err)
return err;

if (mdbe_attrs[MDBE_ATTR_SOURCE] &&
!vxlan_mdb_is_valid_source(mdbe_attrs[MDBE_ATTR_SOURCE],
entry->addr.proto, extack))
return -EINVAL;

vxlan_mdb_group_set(group, entry, mdbe_attrs[MDBE_ATTR_SOURCE]);

if (mdbe_attrs[MDBE_ATTR_SRC_VNI])
group->vni =
cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_SRC_VNI]));

return 0;
}

static struct sk_buff *
vxlan_mdb_get_reply_alloc(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry)
{
struct vxlan_mdb_remote *remote;
size_t nlmsg_size;

nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) +
/* MDBA_MDB */
nla_total_size(0) +
/* MDBA_MDB_ENTRY */
nla_total_size(0);

list_for_each_entry(remote, &mdb_entry->remotes, list)
nlmsg_size += vxlan_mdb_nlmsg_remote_size(vxlan, mdb_entry,
remote);

return nlmsg_new(nlmsg_size, GFP_KERNEL);
}

static int
vxlan_mdb_get_reply_fill(const struct vxlan_dev *vxlan,
struct sk_buff *skb,
const struct vxlan_mdb_entry *mdb_entry,
u32 portid, u32 seq)
{
struct nlattr *mdb_nest, *mdb_entry_nest;
struct vxlan_mdb_remote *remote;
struct br_port_msg *bpm;
struct nlmsghdr *nlh;
int err;

nlh = nlmsg_put(skb, portid, seq, RTM_NEWMDB, sizeof(*bpm), 0);
if (!nlh)
return -EMSGSIZE;

bpm = nlmsg_data(nlh);
memset(bpm, 0, sizeof(*bpm));
bpm->family = AF_BRIDGE;
bpm->ifindex = vxlan->dev->ifindex;
mdb_nest = nla_nest_start_noflag(skb, MDBA_MDB);
if (!mdb_nest) {
err = -EMSGSIZE;
goto cancel;
}
mdb_entry_nest = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY);
if (!mdb_entry_nest) {
err = -EMSGSIZE;
goto cancel;
}

list_for_each_entry(remote, &mdb_entry->remotes, list) {
err = vxlan_mdb_entry_info_fill(vxlan, skb, mdb_entry, remote);
if (err)
goto cancel;
}

nla_nest_end(skb, mdb_entry_nest);
nla_nest_end(skb, mdb_nest);
nlmsg_end(skb, nlh);

return 0;

cancel:
nlmsg_cancel(skb, nlh);
return err;
}

int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid,
u32 seq, struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_mdb_entry *mdb_entry;
struct vxlan_mdb_entry_key group;
struct sk_buff *skb;
int err;

ASSERT_RTNL();

err = vxlan_mdb_get_parse(dev, tb, &group, extack);
if (err)
return err;

mdb_entry = vxlan_mdb_entry_lookup(vxlan, &group);
if (!mdb_entry) {
NL_SET_ERR_MSG_MOD(extack, "MDB entry not found");
return -ENOENT;
}

skb = vxlan_mdb_get_reply_alloc(vxlan, mdb_entry);
if (!skb)
return -ENOMEM;

err = vxlan_mdb_get_reply_fill(vxlan, skb, mdb_entry, portid, seq);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to fill MDB get reply");
goto free;
}

return rtnl_unicast(skb, dev_net(dev), portid);

free:
kfree_skb(skb);
return err;
}

struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
struct sk_buff *skb,
__be32 src_vni)
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/vxlan/vxlan_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ int vxlan_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags,
struct netlink_ext_ack *extack);
int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack);
int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid,
u32 seq, struct netlink_ext_ack *extack);
struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
struct sk_buff *skb,
__be32 src_vni);
Expand Down
4 changes: 4 additions & 0 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,10 @@ struct net_device_ops {
int (*ndo_mdb_dump)(struct net_device *dev,
struct sk_buff *skb,
struct netlink_callback *cb);
int (*ndo_mdb_get)(struct net_device *dev,
struct nlattr *tb[], u32 portid,
u32 seq,
struct netlink_ext_ack *extack);
int (*ndo_bridge_setlink)(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags,
Expand Down
18 changes: 18 additions & 0 deletions include/uapi/linux/if_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,24 @@ enum {
};
#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1)

/* [MDBA_GET_ENTRY] = {
* struct br_mdb_entry
* [MDBA_GET_ENTRY_ATTRS] = {
* [MDBE_ATTR_SOURCE]
* struct in_addr / struct in6_addr
* [MDBE_ATTR_SRC_VNI]
* u32
* }
* }
*/
enum {
MDBA_GET_ENTRY_UNSPEC,
MDBA_GET_ENTRY,
MDBA_GET_ENTRY_ATTRS,
__MDBA_GET_ENTRY_MAX,
};
#define MDBA_GET_ENTRY_MAX (__MDBA_GET_ENTRY_MAX - 1)

/* [MDBA_SET_ENTRY_ATTRS] = {
* [MDBE_ATTR_xxx]
* ...
Expand Down
3 changes: 2 additions & 1 deletion net/bridge/br_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
goto out;
}

mdst = br_mdb_get(brmctx, skb, vid);
mdst = br_mdb_entry_skb_get(brmctx, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst))
br_multicast_flood(mdst, skb, brmctx, false, true);
Expand Down Expand Up @@ -472,6 +472,7 @@ static const struct net_device_ops br_netdev_ops = {
.ndo_mdb_add = br_mdb_add,
.ndo_mdb_del = br_mdb_del,
.ndo_mdb_dump = br_mdb_dump,
.ndo_mdb_get = br_mdb_get,
.ndo_bridge_getlink = br_getlink,
.ndo_bridge_setlink = br_setlink,
.ndo_bridge_dellink = br_dellink,
Expand Down
2 changes: 1 addition & 1 deletion net/bridge/br_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb

switch (pkt_type) {
case BR_PKT_MULTICAST:
mdst = br_mdb_get(brmctx, skb, vid);
mdst = br_mdb_entry_skb_get(brmctx, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) {
if ((mdst && mdst->host_joined) ||
Expand Down

0 comments on commit bc4c48e

Please sign in to comment.