|
@@ -20,16 +20,39 @@
|
|
|
#include "br_private.h"
|
|
|
#include "br_private_stp.h"
|
|
|
|
|
|
+static inline size_t br_port_info_size(void)
|
|
|
+{
|
|
|
+ return nla_total_size(1) /* IFLA_BRPORT_STATE */
|
|
|
+ + nla_total_size(2) /* IFLA_BRPORT_PRIORITY */
|
|
|
+ + nla_total_size(4) /* IFLA_BRPORT_COST */
|
|
|
+ + nla_total_size(1) /* IFLA_BRPORT_MODE */
|
|
|
+ + 0;
|
|
|
+}
|
|
|
+
|
|
|
static inline size_t br_nlmsg_size(void)
|
|
|
{
|
|
|
return NLMSG_ALIGN(sizeof(struct ifinfomsg))
|
|
|
- + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
|
|
|
- + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
|
|
|
- + nla_total_size(4) /* IFLA_MASTER */
|
|
|
- + nla_total_size(4) /* IFLA_MTU */
|
|
|
- + nla_total_size(4) /* IFLA_LINK */
|
|
|
- + nla_total_size(1) /* IFLA_OPERSTATE */
|
|
|
- + nla_total_size(1); /* IFLA_PROTINFO */
|
|
|
+ + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
|
|
|
+ + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
|
|
|
+ + nla_total_size(4) /* IFLA_MASTER */
|
|
|
+ + nla_total_size(4) /* IFLA_MTU */
|
|
|
+ + nla_total_size(4) /* IFLA_LINK */
|
|
|
+ + nla_total_size(1) /* IFLA_OPERSTATE */
|
|
|
+ + nla_total_size(br_port_info_size()); /* IFLA_PROTINFO */
|
|
|
+}
|
|
|
+
|
|
|
+static int br_port_fill_attrs(struct sk_buff *skb,
|
|
|
+ const struct net_bridge_port *p)
|
|
|
+{
|
|
|
+ u8 mode = !!(p->flags & BR_HAIRPIN_MODE);
|
|
|
+
|
|
|
+ if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) ||
|
|
|
+ nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) ||
|
|
|
+ nla_put_u32(skb, IFLA_BRPORT_COST, p->path_cost) ||
|
|
|
+ nla_put_u8(skb, IFLA_BRPORT_MODE, mode))
|
|
|
+ return -EMSGSIZE;
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -67,10 +90,18 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
|
|
|
(dev->addr_len &&
|
|
|
nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
|
|
|
(dev->ifindex != dev->iflink &&
|
|
|
- nla_put_u32(skb, IFLA_LINK, dev->iflink)) ||
|
|
|
- (event == RTM_NEWLINK &&
|
|
|
- nla_put_u8(skb, IFLA_PROTINFO, port->state)))
|
|
|
+ nla_put_u32(skb, IFLA_LINK, dev->iflink)))
|
|
|
goto nla_put_failure;
|
|
|
+
|
|
|
+ if (event == RTM_NEWLINK) {
|
|
|
+ struct nlattr *nest
|
|
|
+ = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED);
|
|
|
+
|
|
|
+ if (nest == NULL || br_port_fill_attrs(skb, port) < 0)
|
|
|
+ goto nla_put_failure;
|
|
|
+ nla_nest_end(skb, nest);
|
|
|
+ }
|
|
|
+
|
|
|
return nlmsg_end(skb, nlh);
|
|
|
|
|
|
nla_put_failure:
|
|
@@ -126,47 +157,115 @@ out:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Change state of port (ie from forwarding to blocking etc)
|
|
|
- * Used by spanning tree in user space.
|
|
|
- */
|
|
|
+static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = {
|
|
|
+ [IFLA_BRPORT_STATE] = { .type = NLA_U8 },
|
|
|
+ [IFLA_BRPORT_COST] = { .type = NLA_U32 },
|
|
|
+ [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 },
|
|
|
+ [IFLA_BRPORT_MODE] = { .type = NLA_U8 },
|
|
|
+};
|
|
|
+
|
|
|
+/* Change the state of the port and notify spanning tree */
|
|
|
+static int br_set_port_state(struct net_bridge_port *p, u8 state)
|
|
|
+{
|
|
|
+ if (state > BR_STATE_BLOCKING)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* if kernel STP is running, don't allow changes */
|
|
|
+ if (p->br->stp_enabled == BR_KERNEL_STP)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ if (!netif_running(p->dev) ||
|
|
|
+ (!netif_carrier_ok(p->dev) && state != BR_STATE_DISABLED))
|
|
|
+ return -ENETDOWN;
|
|
|
+
|
|
|
+ p->state = state;
|
|
|
+ br_log_state(p);
|
|
|
+ br_port_state_selection(p->br);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Set/clear or port flags based on attribute */
|
|
|
+static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
|
|
|
+ int attrtype, unsigned long mask)
|
|
|
+{
|
|
|
+ if (tb[attrtype]) {
|
|
|
+ u8 flag = nla_get_u8(tb[attrtype]);
|
|
|
+ if (flag)
|
|
|
+ p->flags |= mask;
|
|
|
+ else
|
|
|
+ p->flags &= ~mask;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Process bridge protocol info on port */
|
|
|
+static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
|
|
|
+
|
|
|
+ if (tb[IFLA_BRPORT_COST]) {
|
|
|
+ err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tb[IFLA_BRPORT_PRIORITY]) {
|
|
|
+ err = br_stp_set_port_priority(p, nla_get_u16(tb[IFLA_BRPORT_PRIORITY]));
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tb[IFLA_BRPORT_STATE]) {
|
|
|
+ err = br_set_port_state(p, nla_get_u8(tb[IFLA_BRPORT_STATE]));
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Change state and parameters on port. */
|
|
|
int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
|
|
|
{
|
|
|
struct ifinfomsg *ifm;
|
|
|
struct nlattr *protinfo;
|
|
|
struct net_bridge_port *p;
|
|
|
- u8 new_state;
|
|
|
+ struct nlattr *tb[IFLA_BRPORT_MAX];
|
|
|
+ int err;
|
|
|
|
|
|
ifm = nlmsg_data(nlh);
|
|
|
|
|
|
protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
|
|
|
- if (!protinfo || nla_len(protinfo) < sizeof(u8))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- new_state = nla_get_u8(protinfo);
|
|
|
- if (new_state > BR_STATE_BLOCKING)
|
|
|
- return -EINVAL;
|
|
|
+ if (!protinfo)
|
|
|
+ return 0;
|
|
|
|
|
|
p = br_port_get_rtnl(dev);
|
|
|
if (!p)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- /* if kernel STP is running, don't allow changes */
|
|
|
- if (p->br->stp_enabled == BR_KERNEL_STP)
|
|
|
- return -EBUSY;
|
|
|
+ if (protinfo->nla_type & NLA_F_NESTED) {
|
|
|
+ err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
|
|
|
+ protinfo, ifla_brport_policy);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
|
|
|
- if (!netif_running(dev) ||
|
|
|
- (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED))
|
|
|
- return -ENETDOWN;
|
|
|
+ spin_lock_bh(&p->br->lock);
|
|
|
+ err = br_setport(p, tb);
|
|
|
+ spin_unlock_bh(&p->br->lock);
|
|
|
+ } else {
|
|
|
+ /* Binary compatability with old RSTP */
|
|
|
+ if (nla_len(protinfo) < sizeof(u8))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- p->state = new_state;
|
|
|
- br_log_state(p);
|
|
|
+ spin_lock_bh(&p->br->lock);
|
|
|
+ err = br_set_port_state(p, nla_get_u8(protinfo));
|
|
|
+ spin_unlock_bh(&p->br->lock);
|
|
|
+ }
|
|
|
|
|
|
- spin_lock_bh(&p->br->lock);
|
|
|
- br_port_state_selection(p->br);
|
|
|
- spin_unlock_bh(&p->br->lock);
|
|
|
+ if (err == 0)
|
|
|
+ br_ifinfo_notify(RTM_NEWLINK, p);
|
|
|
|
|
|
- return 0;
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
static int br_validate(struct nlattr *tb[], struct nlattr *data[])
|