diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index b82906fc999a..85fa4d73bb53 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -468,6 +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_bridge_getlink = br_getlink, .ndo_bridge_setlink = br_setlink, .ndo_bridge_dellink = br_dellink, diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 25c48d81a597..cb8270a5480b 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -458,6 +458,39 @@ out: return skb->len; } +int br_mdb_dump_new(struct net_device *dev, struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct net_bridge *br = netdev_priv(dev); + struct br_port_msg *bpm; + struct nlmsghdr *nlh; + int err; + + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, RTM_GETMDB, sizeof(*bpm), + NLM_F_MULTI); + if (!nlh) + return -EMSGSIZE; + + bpm = nlmsg_data(nlh); + memset(bpm, 0, sizeof(*bpm)); + bpm->ifindex = dev->ifindex; + + rcu_read_lock(); + + err = br_mdb_fill_info(skb, cb, dev); + if (err) + goto out; + err = br_rports_fill_info(skb, &br->multicast_ctx); + if (err) + goto out; + +out: + rcu_read_unlock(); + nlmsg_end(skb, nlh); + return err; +} + static int nlmsg_populate_mdb_fill(struct sk_buff *skb, struct net_device *dev, struct net_bridge_mdb_entry *mp, @@ -1459,6 +1492,65 @@ out: return err; } +int br_mdb_add_new(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags, + struct netlink_ext_ack *extack) +{ + struct net_bridge_vlan_group *vg; + struct br_mdb_config cfg = {}; + struct net_bridge_vlan *v; + int err; + + /* Configuration structure will be initialized here. */ + + err = -EINVAL; + /* host join errors which can happen before creating the group */ + if (!cfg.p && !br_group_is_l2(&cfg.group)) { + /* don't allow any flags for host-joined IP groups */ + if (cfg.entry->state) { + NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups"); + goto out; + } + if (!br_multicast_is_star_g(&cfg.group)) { + NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined"); + goto out; + } + } + + if (br_group_is_l2(&cfg.group) && cfg.entry->state != MDB_PERMANENT) { + NL_SET_ERR_MSG_MOD(extack, "Only permanent L2 entries allowed"); + goto out; + } + + if (cfg.p) { + if (cfg.p->state == BR_STATE_DISABLED && cfg.entry->state != MDB_PERMANENT) { + NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state and entry is not permanent"); + goto out; + } + vg = nbp_vlan_group(cfg.p); + } else { + vg = br_vlan_group(cfg.br); + } + + /* If vlan filtering is enabled and VLAN is not specified + * install mdb entry on all vlans configured on the port. + */ + if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) { + list_for_each_entry(v, &vg->vlan_list, vlist) { + cfg.entry->vid = v->vid; + cfg.group.vid = v->vid; + err = __br_mdb_add(&cfg, extack); + if (err) + break; + } + } else { + err = __br_mdb_add(&cfg, extack); + } + +out: + br_mdb_config_fini(&cfg); + return err; +} + static int __br_mdb_del(const struct br_mdb_config *cfg) { struct br_mdb_entry *entry = cfg->entry; @@ -1535,6 +1627,38 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, return err; } +int br_mdb_del_new(struct net_device *dev, struct nlattr *tb[], + struct netlink_ext_ack *extack) +{ + struct net_bridge_vlan_group *vg; + struct br_mdb_config cfg = {}; + struct net_bridge_vlan *v; + int err = 0; + + /* Configuration structure will be initialized here. */ + + if (cfg.p) + vg = nbp_vlan_group(cfg.p); + else + vg = br_vlan_group(cfg.br); + + /* If vlan filtering is enabled and VLAN is not specified + * delete mdb entry on all vlans configured on the port. + */ + if (br_vlan_enabled(cfg.br->dev) && vg && cfg.entry->vid == 0) { + list_for_each_entry(v, &vg->vlan_list, vlist) { + cfg.entry->vid = v->vid; + cfg.group.vid = v->vid; + err = __br_mdb_del(&cfg); + } + } else { + err = __br_mdb_del(&cfg); + } + + br_mdb_config_fini(&cfg); + return err; +} + void br_mdb_init(void) { rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, 0); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index cef5f6ea850c..a72847c1dc9f 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -981,6 +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); void br_multicast_host_join(const struct net_bridge_mcast *brmctx, @@ -1374,6 +1380,25 @@ 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) +{ + return -EOPNOTSUPP; +} + +static inline int br_mdb_del_new(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) +{ + return 0; +} + static inline void br_mdb_init(void) { }