Commit cc7f5022 authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller

rtnetlink: bridge: mcast: Move MDB handlers out of bridge driver

Currently, the bridge driver registers handlers for MDB netlink
messages, making it impossible for other drivers to implement MDB
support.

As a preparation for VXLAN MDB support, move the MDB handlers out of the
bridge driver to the core rtnetlink code. The rtnetlink code will call
into individual drivers by invoking their previously added MDB net
device operations.

Note that while the diffstat is large, the change is mechanical. It
moves code out of the bridge driver to rtnetlink code. Also note that a
similar change was made in 2012 with commit 77162022 ("net: add
generic PF_BRIDGE:RTM_ FDB hooks") that moved FDB handlers out of the
bridge driver to the core rtnetlink code.
Signed-off-by: default avatarIdo Schimmel <idosch@nvidia.com>
Reviewed-by: default avatarNikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c009de10
......@@ -468,9 +468,9 @@ static const struct net_device_ops br_netdev_ops = {
.ndo_fdb_del_bulk = br_fdb_delete_bulk,
.ndo_fdb_dump = br_fdb_dump,
.ndo_fdb_get = br_fdb_get,
.ndo_mdb_add = br_mdb_add_new,
.ndo_mdb_del = br_mdb_del_new,
.ndo_mdb_dump = br_mdb_dump_new,
.ndo_mdb_add = br_mdb_add,
.ndo_mdb_del = br_mdb_del,
.ndo_mdb_dump = br_mdb_dump,
.ndo_bridge_getlink = br_getlink,
.ndo_bridge_setlink = br_setlink,
.ndo_bridge_dellink = br_dellink,
......
This diff is collapsed.
......@@ -1886,7 +1886,6 @@ int __init br_netlink_init(void)
{
int err;
br_mdb_init();
br_vlan_rtnl_init();
rtnl_af_register(&br_af_ops);
......@@ -1898,13 +1897,11 @@ int __init br_netlink_init(void)
out_af:
rtnl_af_unregister(&br_af_ops);
br_mdb_uninit();
return err;
}
void br_netlink_fini(void)
{
br_mdb_uninit();
br_vlan_rtnl_uninit();
rtnl_af_unregister(&br_af_ops);
rtnl_link_unregister(&br_link_ops);
......
......@@ -981,14 +981,12 @@ void br_multicast_get_stats(const struct net_bridge *br,
u32 br_multicast_ngroups_get(const struct net_bridge_mcast_port *pmctx);
void br_multicast_ngroups_set_max(struct net_bridge_mcast_port *pmctx, u32 max);
u32 br_multicast_ngroups_get_max(const struct net_bridge_mcast_port *pmctx);
int br_mdb_add_new(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags,
struct netlink_ext_ack *extack);
int br_mdb_del_new(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack);
int br_mdb_dump_new(struct net_device *dev, struct sk_buff *skb,
struct netlink_callback *cb);
void br_mdb_init(void);
void br_mdb_uninit(void);
int br_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags,
struct netlink_ext_ack *extack);
int br_mdb_del(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack);
int br_mdb_dump(struct net_device *dev, struct sk_buff *skb,
struct netlink_callback *cb);
void br_multicast_host_join(const struct net_bridge_mcast *brmctx,
struct net_bridge_mdb_entry *mp, bool notify);
void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify);
......@@ -1380,33 +1378,24 @@ static inline bool br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
return false;
}
static inline int br_mdb_add_new(struct net_device *dev, struct nlattr *tb[],
u16 nlmsg_flags,
struct netlink_ext_ack *extack)
static inline int br_mdb_add(struct net_device *dev, struct nlattr *tb[],
u16 nlmsg_flags, struct netlink_ext_ack *extack)
{
return -EOPNOTSUPP;
}
static inline int br_mdb_del_new(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack)
static inline int br_mdb_del(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack)
{
return -EOPNOTSUPP;
}
static inline int br_mdb_dump_new(struct net_device *dev, struct sk_buff *skb,
struct netlink_callback *cb)
static inline int br_mdb_dump(struct net_device *dev, struct sk_buff *skb,
struct netlink_callback *cb)
{
return 0;
}
static inline void br_mdb_init(void)
{
}
static inline void br_mdb_uninit(void)
{
}
static inline int br_mdb_hash_init(struct net_bridge *br)
{
return 0;
......
......@@ -54,6 +54,9 @@
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/devlink.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/addrconf.h>
#endif
#include "dev.h"
......@@ -6063,6 +6066,216 @@ static int rtnl_stats_set(struct sk_buff *skb, struct nlmsghdr *nlh,
return 0;
}
static int rtnl_mdb_valid_dump_req(const struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct br_port_msg *bpm;
if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*bpm))) {
NL_SET_ERR_MSG(extack, "Invalid header for mdb dump request");
return -EINVAL;
}
bpm = nlmsg_data(nlh);
if (bpm->ifindex) {
NL_SET_ERR_MSG(extack, "Filtering by device index is not supported for mdb dump request");
return -EINVAL;
}
if (nlmsg_attrlen(nlh, sizeof(*bpm))) {
NL_SET_ERR_MSG(extack, "Invalid data after header in mdb dump request");
return -EINVAL;
}
return 0;
}
struct rtnl_mdb_dump_ctx {
long idx;
};
static int rtnl_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
struct rtnl_mdb_dump_ctx *ctx = (void *)cb->ctx;
struct net *net = sock_net(skb->sk);
struct net_device *dev;
int idx, s_idx;
int err;
NL_ASSERT_DUMP_CTX_FITS(struct rtnl_mdb_dump_ctx);
if (cb->strict_check) {
err = rtnl_mdb_valid_dump_req(cb->nlh, cb->extack);
if (err)
return err;
}
s_idx = ctx->idx;
idx = 0;
for_each_netdev(net, dev) {
if (idx < s_idx)
goto skip;
if (!dev->netdev_ops->ndo_mdb_dump)
goto skip;
err = dev->netdev_ops->ndo_mdb_dump(dev, skb, cb);
if (err == -EMSGSIZE)
goto out;
/* Moving on to next device, reset markers and sequence
* counters since they are all maintained per-device.
*/
memset(cb->ctx, 0, sizeof(cb->ctx));
cb->prev_seq = 0;
cb->seq = 0;
skip:
idx++;
}
out:
ctx->idx = idx;
return skb->len;
}
static int rtnl_validate_mdb_entry(const struct nlattr *attr,
struct netlink_ext_ack *extack)
{
struct br_mdb_entry *entry = nla_data(attr);
if (nla_len(attr) != sizeof(struct br_mdb_entry)) {
NL_SET_ERR_MSG_ATTR(extack, attr, "Invalid attribute length");
return -EINVAL;
}
if (entry->ifindex == 0) {
NL_SET_ERR_MSG(extack, "Zero entry ifindex is not allowed");
return -EINVAL;
}
if (entry->addr.proto == htons(ETH_P_IP)) {
if (!ipv4_is_multicast(entry->addr.u.ip4)) {
NL_SET_ERR_MSG(extack, "IPv4 entry group address is not multicast");
return -EINVAL;
}
if (ipv4_is_local_multicast(entry->addr.u.ip4)) {
NL_SET_ERR_MSG(extack, "IPv4 entry group address is local multicast");
return -EINVAL;
}
#if IS_ENABLED(CONFIG_IPV6)
} else if (entry->addr.proto == htons(ETH_P_IPV6)) {
if (ipv6_addr_is_ll_all_nodes(&entry->addr.u.ip6)) {
NL_SET_ERR_MSG(extack, "IPv6 entry group address is link-local all nodes");
return -EINVAL;
}
#endif
} else if (entry->addr.proto == 0) {
/* L2 mdb */
if (!is_multicast_ether_addr(entry->addr.u.mac_addr)) {
NL_SET_ERR_MSG(extack, "L2 entry group is not multicast");
return -EINVAL;
}
} else {
NL_SET_ERR_MSG(extack, "Unknown entry protocol");
return -EINVAL;
}
if (entry->state != MDB_PERMANENT && entry->state != MDB_TEMPORARY) {
NL_SET_ERR_MSG(extack, "Unknown entry state");
return -EINVAL;
}
if (entry->vid >= VLAN_VID_MASK) {
NL_SET_ERR_MSG(extack, "Invalid entry VLAN id");
return -EINVAL;
}
return 0;
}
static const struct nla_policy mdba_policy[MDBA_SET_ENTRY_MAX + 1] = {
[MDBA_SET_ENTRY_UNSPEC] = { .strict_start_type = MDBA_SET_ENTRY_ATTRS + 1 },
[MDBA_SET_ENTRY] = NLA_POLICY_VALIDATE_FN(NLA_BINARY,
rtnl_validate_mdb_entry,
sizeof(struct br_mdb_entry)),
[MDBA_SET_ENTRY_ATTRS] = { .type = NLA_NESTED },
};
static int rtnl_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[MDBA_SET_ENTRY_MAX + 1];
struct net *net = sock_net(skb->sk);
struct br_port_msg *bpm;
struct net_device *dev;
int err;
err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb,
MDBA_SET_ENTRY_MAX, mdba_policy, extack);
if (err)
return err;
bpm = nlmsg_data(nlh);
if (!bpm->ifindex) {
NL_SET_ERR_MSG(extack, "Invalid ifindex");
return -EINVAL;
}
dev = __dev_get_by_index(net, bpm->ifindex);
if (!dev) {
NL_SET_ERR_MSG(extack, "Device doesn't exist");
return -ENODEV;
}
if (NL_REQ_ATTR_CHECK(extack, NULL, tb, MDBA_SET_ENTRY)) {
NL_SET_ERR_MSG(extack, "Missing MDBA_SET_ENTRY attribute");
return -EINVAL;
}
if (!dev->netdev_ops->ndo_mdb_add) {
NL_SET_ERR_MSG(extack, "Device does not support MDB operations");
return -EOPNOTSUPP;
}
return dev->netdev_ops->ndo_mdb_add(dev, tb, nlh->nlmsg_flags, extack);
}
static int rtnl_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[MDBA_SET_ENTRY_MAX + 1];
struct net *net = sock_net(skb->sk);
struct br_port_msg *bpm;
struct net_device *dev;
int err;
err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb,
MDBA_SET_ENTRY_MAX, mdba_policy, extack);
if (err)
return err;
bpm = nlmsg_data(nlh);
if (!bpm->ifindex) {
NL_SET_ERR_MSG(extack, "Invalid ifindex");
return -EINVAL;
}
dev = __dev_get_by_index(net, bpm->ifindex);
if (!dev) {
NL_SET_ERR_MSG(extack, "Device doesn't exist");
return -ENODEV;
}
if (NL_REQ_ATTR_CHECK(extack, NULL, tb, MDBA_SET_ENTRY)) {
NL_SET_ERR_MSG(extack, "Missing MDBA_SET_ENTRY attribute");
return -EINVAL;
}
if (!dev->netdev_ops->ndo_mdb_del) {
NL_SET_ERR_MSG(extack, "Device does not support MDB operations");
return -EOPNOTSUPP;
}
return dev->netdev_ops->ndo_mdb_del(dev, tb, extack);
}
/* Process one rtnetlink message. */
static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
......@@ -6297,4 +6510,8 @@ void __init rtnetlink_init(void)
rtnl_register(PF_UNSPEC, RTM_GETSTATS, rtnl_stats_get, rtnl_stats_dump,
0);
rtnl_register(PF_UNSPEC, RTM_SETSTATS, rtnl_stats_set, NULL, 0);
rtnl_register(PF_BRIDGE, RTM_GETMDB, NULL, rtnl_mdb_dump, 0);
rtnl_register(PF_BRIDGE, RTM_NEWMDB, rtnl_mdb_add, NULL, 0);
rtnl_register(PF_BRIDGE, RTM_DELMDB, rtnl_mdb_del, NULL, 0);
}
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