diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 96f7cf49367..5e871e4d923 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -216,6 +216,7 @@ enum { IFLA_BRPORT_PRIORITY, /* " priority */ IFLA_BRPORT_COST, /* " cost */ IFLA_BRPORT_MODE, /* mode (hairpin) */ + IFLA_BRPORT_GUARD, /* bpdu guard */ __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 0188a2f706c..c331e28c788 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -26,6 +26,7 @@ static inline size_t br_port_info_size(void) + nla_total_size(2) /* IFLA_BRPORT_PRIORITY */ + nla_total_size(4) /* IFLA_BRPORT_COST */ + nla_total_size(1) /* IFLA_BRPORT_MODE */ + + nla_total_size(1) /* IFLA_BRPORT_GUARD */ + 0; } @@ -49,7 +50,8 @@ static int br_port_fill_attrs(struct sk_buff *skb, 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)) + nla_put_u8(skb, IFLA_BRPORT_MODE, mode) || + nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD))) return -EMSGSIZE; return 0; @@ -162,6 +164,7 @@ static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_COST] = { .type = NLA_U32 }, [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, + [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, }; /* Change the state of the port and notify spanning tree */ @@ -203,6 +206,7 @@ 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); + br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); if (tb[IFLA_BRPORT_COST]) { err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST])); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 22111ffd68d..c92b0804ff2 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -135,6 +135,7 @@ struct net_bridge_port unsigned long flags; #define BR_HAIRPIN_MODE 0x00000001 +#define BR_BPDU_GUARD 0x00000002 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING u32 multicast_startup_queries_sent; diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index fd30a6022de..7f884e3fb95 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -170,6 +170,13 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, if (!ether_addr_equal(dest, br->group_addr)) goto out; + if (p->flags & BR_BPDU_GUARD) { + br_notice(br, "BPDU received on blocked port %u(%s)\n", + (unsigned int) p->port_no, p->dev->name); + br_stp_disable_port(p); + goto out; + } + buf = skb_pull(skb, 3); if (buf[0] == BPDU_TYPE_CONFIG) { diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index f26173d33d8..d1dfa402618 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -156,6 +156,7 @@ static int store_flush(struct net_bridge_port *p, unsigned long v) static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush); BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE); +BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD); #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) @@ -189,6 +190,7 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_hold_timer, &brport_attr_flush, &brport_attr_hairpin_mode, + &brport_attr_bpdu_guard, #ifdef CONFIG_BRIDGE_IGMP_SNOOPING &brport_attr_multicast_router, #endif