net: bridge: vlan: add rtm definitions and dump support
This patch adds vlan rtm definitions: - NEWVLAN: to be used for creating vlans, setting options and notifications - DELVLAN: to be used for deleting vlans - GETVLAN: used for dumping vlan information Dumping vlans which can span multiple messages is added now with basic information (vid and flags). We use nlmsg_parse() to validate the header length in order to be able to extend the message with filtering attributes later. Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
8f4cc940a1
commit
8dcea18708
6 changed files with 203 additions and 1 deletions
|
@ -165,6 +165,34 @@ struct bridge_stp_xstats {
|
||||||
__u64 tx_tcn;
|
__u64 tx_tcn;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Bridge vlan RTM header */
|
||||||
|
struct br_vlan_msg {
|
||||||
|
__u8 family;
|
||||||
|
__u8 reserved1;
|
||||||
|
__u16 reserved2;
|
||||||
|
__u32 ifindex;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Bridge vlan RTM attributes
|
||||||
|
* [BRIDGE_VLANDB_ENTRY] = {
|
||||||
|
* [BRIDGE_VLANDB_ENTRY_INFO]
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
BRIDGE_VLANDB_UNSPEC,
|
||||||
|
BRIDGE_VLANDB_ENTRY,
|
||||||
|
__BRIDGE_VLANDB_MAX,
|
||||||
|
};
|
||||||
|
#define BRIDGE_VLANDB_MAX (__BRIDGE_VLANDB_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BRIDGE_VLANDB_ENTRY_UNSPEC,
|
||||||
|
BRIDGE_VLANDB_ENTRY_INFO,
|
||||||
|
__BRIDGE_VLANDB_ENTRY_MAX,
|
||||||
|
};
|
||||||
|
#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1)
|
||||||
|
|
||||||
/* Bridge multicast database attributes
|
/* Bridge multicast database attributes
|
||||||
* [MDBA_MDB] = {
|
* [MDBA_MDB] = {
|
||||||
* [MDBA_MDB_ENTRY] = {
|
* [MDBA_MDB_ENTRY] = {
|
||||||
|
|
|
@ -171,6 +171,13 @@ enum {
|
||||||
RTM_GETLINKPROP,
|
RTM_GETLINKPROP,
|
||||||
#define RTM_GETLINKPROP RTM_GETLINKPROP
|
#define RTM_GETLINKPROP RTM_GETLINKPROP
|
||||||
|
|
||||||
|
RTM_NEWVLAN = 112,
|
||||||
|
#define RTM_NEWNVLAN RTM_NEWVLAN
|
||||||
|
RTM_DELVLAN,
|
||||||
|
#define RTM_DELVLAN RTM_DELVLAN
|
||||||
|
RTM_GETVLAN,
|
||||||
|
#define RTM_GETVLAN RTM_GETVLAN
|
||||||
|
|
||||||
__RTM_MAX,
|
__RTM_MAX,
|
||||||
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
|
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
|
||||||
};
|
};
|
||||||
|
|
|
@ -1657,6 +1657,7 @@ int __init br_netlink_init(void)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
br_mdb_init();
|
br_mdb_init();
|
||||||
|
br_vlan_rtnl_init();
|
||||||
rtnl_af_register(&br_af_ops);
|
rtnl_af_register(&br_af_ops);
|
||||||
|
|
||||||
err = rtnl_link_register(&br_link_ops);
|
err = rtnl_link_register(&br_link_ops);
|
||||||
|
@ -1674,6 +1675,7 @@ out_af:
|
||||||
void br_netlink_fini(void)
|
void br_netlink_fini(void)
|
||||||
{
|
{
|
||||||
br_mdb_uninit();
|
br_mdb_uninit();
|
||||||
|
br_vlan_rtnl_uninit();
|
||||||
rtnl_af_unregister(&br_af_ops);
|
rtnl_af_unregister(&br_af_ops);
|
||||||
rtnl_link_unregister(&br_link_ops);
|
rtnl_link_unregister(&br_link_ops);
|
||||||
}
|
}
|
||||||
|
|
|
@ -958,6 +958,8 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v,
|
||||||
void br_vlan_port_event(struct net_bridge_port *p, unsigned long event);
|
void br_vlan_port_event(struct net_bridge_port *p, unsigned long event);
|
||||||
int br_vlan_bridge_event(struct net_device *dev, unsigned long event,
|
int br_vlan_bridge_event(struct net_device *dev, unsigned long event,
|
||||||
void *ptr);
|
void *ptr);
|
||||||
|
void br_vlan_rtnl_init(void);
|
||||||
|
void br_vlan_rtnl_uninit(void);
|
||||||
|
|
||||||
static inline struct net_bridge_vlan_group *br_vlan_group(
|
static inline struct net_bridge_vlan_group *br_vlan_group(
|
||||||
const struct net_bridge *br)
|
const struct net_bridge *br)
|
||||||
|
@ -1009,6 +1011,10 @@ static inline u16 br_get_pvid(const struct net_bridge_vlan_group *vg)
|
||||||
return vg->pvid;
|
return vg->pvid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u16 br_vlan_flags(const struct net_bridge_vlan *v, u16 pvid)
|
||||||
|
{
|
||||||
|
return v->vid == pvid ? v->flags | BRIDGE_VLAN_INFO_PVID : v->flags;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
static inline bool br_allowed_ingress(const struct net_bridge *br,
|
static inline bool br_allowed_ingress(const struct net_bridge *br,
|
||||||
struct net_bridge_vlan_group *vg,
|
struct net_bridge_vlan_group *vg,
|
||||||
|
@ -1152,6 +1158,14 @@ static inline int br_vlan_bridge_event(struct net_device *dev,
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void br_vlan_rtnl_init(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void br_vlan_rtnl_uninit(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct nf_br_ops {
|
struct nf_br_ops {
|
||||||
|
|
|
@ -1505,3 +1505,151 @@ void br_vlan_port_event(struct net_bridge_port *p, unsigned long event)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool br_vlan_fill_vids(struct sk_buff *skb, u16 vid, u16 flags)
|
||||||
|
{
|
||||||
|
struct bridge_vlan_info info;
|
||||||
|
struct nlattr *nest;
|
||||||
|
|
||||||
|
nest = nla_nest_start(skb, BRIDGE_VLANDB_ENTRY);
|
||||||
|
if (!nest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
info.vid = vid;
|
||||||
|
if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
|
||||||
|
info.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
|
||||||
|
if (flags & BRIDGE_VLAN_INFO_PVID)
|
||||||
|
info.flags |= BRIDGE_VLAN_INFO_PVID;
|
||||||
|
|
||||||
|
if (nla_put(skb, BRIDGE_VLANDB_ENTRY_INFO, sizeof(info), &info))
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
nla_nest_end(skb, nest);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
out_err:
|
||||||
|
nla_nest_cancel(skb, nest);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int br_vlan_dump_dev(const struct net_device *dev,
|
||||||
|
struct sk_buff *skb,
|
||||||
|
struct netlink_callback *cb)
|
||||||
|
{
|
||||||
|
struct net_bridge_vlan_group *vg;
|
||||||
|
int idx = 0, s_idx = cb->args[1];
|
||||||
|
struct nlmsghdr *nlh = NULL;
|
||||||
|
struct net_bridge_vlan *v;
|
||||||
|
struct net_bridge_port *p;
|
||||||
|
struct br_vlan_msg *bvm;
|
||||||
|
struct net_bridge *br;
|
||||||
|
int err = 0;
|
||||||
|
u16 pvid;
|
||||||
|
|
||||||
|
if (!netif_is_bridge_master(dev) && !netif_is_bridge_port(dev))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (netif_is_bridge_master(dev)) {
|
||||||
|
br = netdev_priv(dev);
|
||||||
|
vg = br_vlan_group_rcu(br);
|
||||||
|
p = NULL;
|
||||||
|
} else {
|
||||||
|
p = br_port_get_rcu(dev);
|
||||||
|
if (WARN_ON(!p))
|
||||||
|
return -EINVAL;
|
||||||
|
vg = nbp_vlan_group_rcu(p);
|
||||||
|
br = p->br;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vg)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
|
||||||
|
RTM_NEWVLAN, sizeof(*bvm), NLM_F_MULTI);
|
||||||
|
if (!nlh)
|
||||||
|
return -EMSGSIZE;
|
||||||
|
bvm = nlmsg_data(nlh);
|
||||||
|
memset(bvm, 0, sizeof(*bvm));
|
||||||
|
bvm->family = PF_BRIDGE;
|
||||||
|
bvm->ifindex = dev->ifindex;
|
||||||
|
pvid = br_get_pvid(vg);
|
||||||
|
|
||||||
|
list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
|
||||||
|
if (!br_vlan_should_use(v))
|
||||||
|
continue;
|
||||||
|
if (idx < s_idx)
|
||||||
|
goto skip;
|
||||||
|
if (!br_vlan_fill_vids(skb, v->vid, br_vlan_flags(v, pvid))) {
|
||||||
|
err = -EMSGSIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
skip:
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
if (err)
|
||||||
|
cb->args[1] = idx;
|
||||||
|
else
|
||||||
|
cb->args[1] = 0;
|
||||||
|
nlmsg_end(skb, nlh);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int br_vlan_rtm_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
||||||
|
{
|
||||||
|
int idx = 0, err = 0, s_idx = cb->args[0];
|
||||||
|
struct net *net = sock_net(skb->sk);
|
||||||
|
struct br_vlan_msg *bvm;
|
||||||
|
struct net_device *dev;
|
||||||
|
|
||||||
|
err = nlmsg_parse(cb->nlh, sizeof(*bvm), NULL, 0, NULL, cb->extack);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
bvm = nlmsg_data(cb->nlh);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
if (bvm->ifindex) {
|
||||||
|
dev = dev_get_by_index_rcu(net, bvm->ifindex);
|
||||||
|
if (!dev) {
|
||||||
|
err = -ENODEV;
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
err = br_vlan_dump_dev(dev, skb, cb);
|
||||||
|
if (err && err != -EMSGSIZE)
|
||||||
|
goto out_err;
|
||||||
|
} else {
|
||||||
|
for_each_netdev_rcu(net, dev) {
|
||||||
|
if (idx < s_idx)
|
||||||
|
goto skip;
|
||||||
|
|
||||||
|
err = br_vlan_dump_dev(dev, skb, cb);
|
||||||
|
if (err == -EMSGSIZE)
|
||||||
|
break;
|
||||||
|
skip:
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb->args[0] = idx;
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return skb->len;
|
||||||
|
|
||||||
|
out_err:
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void br_vlan_rtnl_init(void)
|
||||||
|
{
|
||||||
|
rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_GETVLAN, NULL,
|
||||||
|
br_vlan_rtm_dump, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void br_vlan_rtnl_uninit(void)
|
||||||
|
{
|
||||||
|
rtnl_unregister(PF_BRIDGE, RTM_GETVLAN);
|
||||||
|
}
|
||||||
|
|
|
@ -85,6 +85,9 @@ static const struct nlmsg_perm nlmsg_route_perms[] =
|
||||||
{ RTM_GETNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_READ },
|
{ RTM_GETNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_READ },
|
||||||
{ RTM_NEWLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
|
{ RTM_NEWLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
|
||||||
{ RTM_DELLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
|
{ RTM_DELLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
|
||||||
|
{ RTM_NEWVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
|
||||||
|
{ RTM_DELVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
|
||||||
|
{ RTM_GETVLAN, NETLINK_ROUTE_SOCKET__NLMSG_READ },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct nlmsg_perm nlmsg_tcpdiag_perms[] =
|
static const struct nlmsg_perm nlmsg_tcpdiag_perms[] =
|
||||||
|
@ -168,7 +171,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
|
||||||
* structures at the top of this file with the new mappings
|
* structures at the top of this file with the new mappings
|
||||||
* before updating the BUILD_BUG_ON() macro!
|
* before updating the BUILD_BUG_ON() macro!
|
||||||
*/
|
*/
|
||||||
BUILD_BUG_ON(RTM_MAX != (RTM_NEWLINKPROP + 3));
|
BUILD_BUG_ON(RTM_MAX != (RTM_NEWVLAN + 3));
|
||||||
err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
|
err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
|
||||||
sizeof(nlmsg_route_perms));
|
sizeof(nlmsg_route_perms));
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Reference in a new issue