12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Spanning tree protocol; generic parts
41da177e4SLinus Torvalds * Linux ethernet bridge
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * Authors:
71da177e4SLinus Torvalds * Lennert Buytenhek <buytenh@gnu.org>
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds #include <linux/kernel.h>
1082524746SFranck Bui-Huu #include <linux/rculist.h>
1138dcf357SScott Feldman #include <net/switchdev.h>
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds #include "br_private.h"
141da177e4SLinus Torvalds #include "br_private_stp.h"
151da177e4SLinus Torvalds
161da177e4SLinus Torvalds /* since time values in bpdu are in jiffies and then scaled (1/256)
17aaca735fSJoakim Tjernlund * before sending, make sure that is at least one STP tick.
181da177e4SLinus Torvalds */
19aaca735fSJoakim Tjernlund #define MESSAGE_AGE_INCR ((HZ / 256) + 1)
201da177e4SLinus Torvalds
2136cbd3dcSJan Engelhardt static const char *const br_port_state_names[] = {
221da177e4SLinus Torvalds [BR_STATE_DISABLED] = "disabled",
231da177e4SLinus Torvalds [BR_STATE_LISTENING] = "listening",
241da177e4SLinus Torvalds [BR_STATE_LEARNING] = "learning",
251da177e4SLinus Torvalds [BR_STATE_FORWARDING] = "forwarding",
261da177e4SLinus Torvalds [BR_STATE_BLOCKING] = "blocking",
271da177e4SLinus Torvalds };
281da177e4SLinus Torvalds
br_set_state(struct net_bridge_port * p,unsigned int state)29775dd692SFlorian Fainelli void br_set_state(struct net_bridge_port *p, unsigned int state)
30775dd692SFlorian Fainelli {
3135636062SScott Feldman struct switchdev_attr attr = {
326ff64f6fSIdo Schimmel .orig_dev = p->dev,
331f868398SJiri Pirko .id = SWITCHDEV_ATTR_ID_PORT_STP_STATE,
340bc05d58SJiri Pirko .flags = SWITCHDEV_F_DEFER,
3542275bd8SScott Feldman .u.stp_state = state,
3635636062SScott Feldman };
3738dcf357SScott Feldman int err;
3838dcf357SScott Feldman
39419dba8aSHoratiu Vultur /* Don't change the state of the ports if they are driven by a different
40419dba8aSHoratiu Vultur * protocol.
41419dba8aSHoratiu Vultur */
42419dba8aSHoratiu Vultur if (p->flags & BR_MRP_AWARE)
43419dba8aSHoratiu Vultur return;
44419dba8aSHoratiu Vultur
45775dd692SFlorian Fainelli p->state = state;
46*ec7328b5STobias Waldekranz if (br_opt_get(p->br, BROPT_MST_ENABLED)) {
47*ec7328b5STobias Waldekranz err = br_mst_set_state(p, 0, state, NULL);
48*ec7328b5STobias Waldekranz if (err)
49*ec7328b5STobias Waldekranz br_warn(p->br, "error setting MST state on port %u(%s)\n",
50*ec7328b5STobias Waldekranz p->port_no, netdev_name(p->dev));
51*ec7328b5STobias Waldekranz }
52dcbdf135SVladimir Oltean err = switchdev_port_attr_set(p->dev, &attr, NULL);
53bbe14f54SIdo Schimmel if (err && err != -EOPNOTSUPP)
5438dcf357SScott Feldman br_warn(p->br, "error setting offload STP state on port %u(%s)\n",
5538dcf357SScott Feldman (unsigned int) p->port_no, p->dev->name);
567c25b16dSVivien Didelot else
577c25b16dSVivien Didelot br_info(p->br, "port %u(%s) entered %s state\n",
587c25b16dSVivien Didelot (unsigned int) p->port_no, p->dev->name,
597c25b16dSVivien Didelot br_port_state_names[p->state]);
60de179966SVivien Didelot
61de179966SVivien Didelot if (p->br->stp_enabled == BR_KERNEL_STP) {
62de179966SVivien Didelot switch (p->state) {
63de179966SVivien Didelot case BR_STATE_BLOCKING:
64de179966SVivien Didelot p->stp_xstats.transition_blk++;
65de179966SVivien Didelot break;
66de179966SVivien Didelot case BR_STATE_FORWARDING:
67de179966SVivien Didelot p->stp_xstats.transition_fwd++;
68de179966SVivien Didelot break;
69de179966SVivien Didelot }
70de179966SVivien Didelot }
71775dd692SFlorian Fainelli }
72775dd692SFlorian Fainelli
br_port_get_stp_state(const struct net_device * dev)73c0e715bbSVladimir Oltean u8 br_port_get_stp_state(const struct net_device *dev)
74c0e715bbSVladimir Oltean {
75c0e715bbSVladimir Oltean struct net_bridge_port *p;
76c0e715bbSVladimir Oltean
77c0e715bbSVladimir Oltean ASSERT_RTNL();
78c0e715bbSVladimir Oltean
79c0e715bbSVladimir Oltean p = br_port_get_rtnl(dev);
80c0e715bbSVladimir Oltean if (!p)
81c0e715bbSVladimir Oltean return BR_STATE_DISABLED;
82c0e715bbSVladimir Oltean
83c0e715bbSVladimir Oltean return p->state;
84c0e715bbSVladimir Oltean }
85c0e715bbSVladimir Oltean EXPORT_SYMBOL_GPL(br_port_get_stp_state);
86c0e715bbSVladimir Oltean
871da177e4SLinus Torvalds /* called under bridge lock */
br_get_port(struct net_bridge * br,u16 port_no)881da177e4SLinus Torvalds struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no)
891da177e4SLinus Torvalds {
901da177e4SLinus Torvalds struct net_bridge_port *p;
911da177e4SLinus Torvalds
9233c4acbeSMadhuparna Bhowmik list_for_each_entry_rcu(p, &br->port_list, list,
9333c4acbeSMadhuparna Bhowmik lockdep_is_held(&br->lock)) {
941da177e4SLinus Torvalds if (p->port_no == port_no)
951da177e4SLinus Torvalds return p;
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds
981da177e4SLinus Torvalds return NULL;
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds /* called under bridge lock */
br_should_become_root_port(const struct net_bridge_port * p,u16 root_port)1021da177e4SLinus Torvalds static int br_should_become_root_port(const struct net_bridge_port *p,
1031da177e4SLinus Torvalds u16 root_port)
1041da177e4SLinus Torvalds {
1051da177e4SLinus Torvalds struct net_bridge *br;
1061da177e4SLinus Torvalds struct net_bridge_port *rp;
1071da177e4SLinus Torvalds int t;
1081da177e4SLinus Torvalds
1091da177e4SLinus Torvalds br = p->br;
1101da177e4SLinus Torvalds if (p->state == BR_STATE_DISABLED ||
1111da177e4SLinus Torvalds br_is_designated_port(p))
1121da177e4SLinus Torvalds return 0;
1131da177e4SLinus Torvalds
1141da177e4SLinus Torvalds if (memcmp(&br->bridge_id, &p->designated_root, 8) <= 0)
1151da177e4SLinus Torvalds return 0;
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds if (!root_port)
1181da177e4SLinus Torvalds return 1;
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds rp = br_get_port(br, root_port);
1211da177e4SLinus Torvalds
1221da177e4SLinus Torvalds t = memcmp(&p->designated_root, &rp->designated_root, 8);
1231da177e4SLinus Torvalds if (t < 0)
1241da177e4SLinus Torvalds return 1;
1251da177e4SLinus Torvalds else if (t > 0)
1261da177e4SLinus Torvalds return 0;
1271da177e4SLinus Torvalds
1281da177e4SLinus Torvalds if (p->designated_cost + p->path_cost <
1291da177e4SLinus Torvalds rp->designated_cost + rp->path_cost)
1301da177e4SLinus Torvalds return 1;
1311da177e4SLinus Torvalds else if (p->designated_cost + p->path_cost >
1321da177e4SLinus Torvalds rp->designated_cost + rp->path_cost)
1331da177e4SLinus Torvalds return 0;
1341da177e4SLinus Torvalds
1351da177e4SLinus Torvalds t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8);
1361da177e4SLinus Torvalds if (t < 0)
1371da177e4SLinus Torvalds return 1;
1381da177e4SLinus Torvalds else if (t > 0)
1391da177e4SLinus Torvalds return 0;
1401da177e4SLinus Torvalds
1411da177e4SLinus Torvalds if (p->designated_port < rp->designated_port)
1421da177e4SLinus Torvalds return 1;
1431da177e4SLinus Torvalds else if (p->designated_port > rp->designated_port)
1441da177e4SLinus Torvalds return 0;
1451da177e4SLinus Torvalds
1461da177e4SLinus Torvalds if (p->port_id < rp->port_id)
1471da177e4SLinus Torvalds return 1;
1481da177e4SLinus Torvalds
1491da177e4SLinus Torvalds return 0;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds
br_root_port_block(const struct net_bridge * br,struct net_bridge_port * p)1521007dd1aSstephen hemminger static void br_root_port_block(const struct net_bridge *br,
1531007dd1aSstephen hemminger struct net_bridge_port *p)
1541007dd1aSstephen hemminger {
1551007dd1aSstephen hemminger
1561007dd1aSstephen hemminger br_notice(br, "port %u(%s) tried to become root port (blocked)",
1571007dd1aSstephen hemminger (unsigned int) p->port_no, p->dev->name);
1581007dd1aSstephen hemminger
159775dd692SFlorian Fainelli br_set_state(p, BR_STATE_LISTENING);
16092899063SNikolay Aleksandrov br_ifinfo_notify(RTM_NEWLINK, NULL, p);
1611007dd1aSstephen hemminger
1621007dd1aSstephen hemminger if (br->forward_delay > 0)
1631007dd1aSstephen hemminger mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay);
1641007dd1aSstephen hemminger }
1651007dd1aSstephen hemminger
1661da177e4SLinus Torvalds /* called under bridge lock */
br_root_selection(struct net_bridge * br)1671da177e4SLinus Torvalds static void br_root_selection(struct net_bridge *br)
1681da177e4SLinus Torvalds {
1691da177e4SLinus Torvalds struct net_bridge_port *p;
1701da177e4SLinus Torvalds u16 root_port = 0;
1711da177e4SLinus Torvalds
1721da177e4SLinus Torvalds list_for_each_entry(p, &br->port_list, list) {
1731007dd1aSstephen hemminger if (!br_should_become_root_port(p, root_port))
1741007dd1aSstephen hemminger continue;
1751007dd1aSstephen hemminger
1761007dd1aSstephen hemminger if (p->flags & BR_ROOT_BLOCK)
1771007dd1aSstephen hemminger br_root_port_block(br, p);
1781007dd1aSstephen hemminger else
1791da177e4SLinus Torvalds root_port = p->port_no;
1801da177e4SLinus Torvalds }
1811da177e4SLinus Torvalds
1821da177e4SLinus Torvalds br->root_port = root_port;
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds if (!root_port) {
1851da177e4SLinus Torvalds br->designated_root = br->bridge_id;
1861da177e4SLinus Torvalds br->root_path_cost = 0;
1871da177e4SLinus Torvalds } else {
1881da177e4SLinus Torvalds p = br_get_port(br, root_port);
1891da177e4SLinus Torvalds br->designated_root = p->designated_root;
1901da177e4SLinus Torvalds br->root_path_cost = p->designated_cost + p->path_cost;
1911da177e4SLinus Torvalds }
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds
1941da177e4SLinus Torvalds /* called under bridge lock */
br_become_root_bridge(struct net_bridge * br)1951da177e4SLinus Torvalds void br_become_root_bridge(struct net_bridge *br)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds br->max_age = br->bridge_max_age;
1981da177e4SLinus Torvalds br->hello_time = br->bridge_hello_time;
1991da177e4SLinus Torvalds br->forward_delay = br->bridge_forward_delay;
2001da177e4SLinus Torvalds br_topology_change_detection(br);
2011da177e4SLinus Torvalds del_timer(&br->tcn_timer);
2021da177e4SLinus Torvalds
2031da177e4SLinus Torvalds if (br->dev->flags & IFF_UP) {
2041da177e4SLinus Torvalds br_config_bpdu_generation(br);
2051da177e4SLinus Torvalds mod_timer(&br->hello_timer, jiffies + br->hello_time);
2061da177e4SLinus Torvalds }
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds
2091da177e4SLinus Torvalds /* called under bridge lock */
br_transmit_config(struct net_bridge_port * p)2101da177e4SLinus Torvalds void br_transmit_config(struct net_bridge_port *p)
2111da177e4SLinus Torvalds {
2121da177e4SLinus Torvalds struct br_config_bpdu bpdu;
2131da177e4SLinus Torvalds struct net_bridge *br;
2141da177e4SLinus Torvalds
2151da177e4SLinus Torvalds if (timer_pending(&p->hold_timer)) {
2161da177e4SLinus Torvalds p->config_pending = 1;
2171da177e4SLinus Torvalds return;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds
2201da177e4SLinus Torvalds br = p->br;
2211da177e4SLinus Torvalds
2221da177e4SLinus Torvalds bpdu.topology_change = br->topology_change;
2231da177e4SLinus Torvalds bpdu.topology_change_ack = p->topology_change_ack;
2241da177e4SLinus Torvalds bpdu.root = br->designated_root;
2251da177e4SLinus Torvalds bpdu.root_path_cost = br->root_path_cost;
2261da177e4SLinus Torvalds bpdu.bridge_id = br->bridge_id;
2271da177e4SLinus Torvalds bpdu.port_id = p->port_id;
2281da177e4SLinus Torvalds if (br_is_root_bridge(br))
2291da177e4SLinus Torvalds bpdu.message_age = 0;
2301da177e4SLinus Torvalds else {
2311da177e4SLinus Torvalds struct net_bridge_port *root
2321da177e4SLinus Torvalds = br_get_port(br, br->root_port);
2330c03150eSstephen hemminger bpdu.message_age = (jiffies - root->designated_age)
2341da177e4SLinus Torvalds + MESSAGE_AGE_INCR;
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds bpdu.max_age = br->max_age;
2371da177e4SLinus Torvalds bpdu.hello_time = br->hello_time;
2381da177e4SLinus Torvalds bpdu.forward_delay = br->forward_delay;
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds if (bpdu.message_age < br->max_age) {
2411da177e4SLinus Torvalds br_send_config_bpdu(p, &bpdu);
2421da177e4SLinus Torvalds p->topology_change_ack = 0;
2431da177e4SLinus Torvalds p->config_pending = 0;
24476b91c32SNikolay Aleksandrov if (p->br->stp_enabled == BR_KERNEL_STP)
2459a834b87SStephen Hemminger mod_timer(&p->hold_timer,
2469a834b87SStephen Hemminger round_jiffies(jiffies + BR_HOLD_TIME));
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds }
2491da177e4SLinus Torvalds
2501da177e4SLinus Torvalds /* called under bridge lock */
br_record_config_information(struct net_bridge_port * p,const struct br_config_bpdu * bpdu)251160d73b8Sstephen hemminger static void br_record_config_information(struct net_bridge_port *p,
2521da177e4SLinus Torvalds const struct br_config_bpdu *bpdu)
2531da177e4SLinus Torvalds {
2541da177e4SLinus Torvalds p->designated_root = bpdu->root;
2551da177e4SLinus Torvalds p->designated_cost = bpdu->root_path_cost;
2561da177e4SLinus Torvalds p->designated_bridge = bpdu->bridge_id;
2571da177e4SLinus Torvalds p->designated_port = bpdu->port_id;
258709e1b5cSJoakim Tjernlund p->designated_age = jiffies - bpdu->message_age;
2591da177e4SLinus Torvalds
2601da177e4SLinus Torvalds mod_timer(&p->message_age_timer, jiffies
2619a062013SChris Healy + (bpdu->max_age - bpdu->message_age));
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds
2641da177e4SLinus Torvalds /* called under bridge lock */
br_record_config_timeout_values(struct net_bridge * br,const struct br_config_bpdu * bpdu)265160d73b8Sstephen hemminger static void br_record_config_timeout_values(struct net_bridge *br,
2661da177e4SLinus Torvalds const struct br_config_bpdu *bpdu)
2671da177e4SLinus Torvalds {
2681da177e4SLinus Torvalds br->max_age = bpdu->max_age;
2691da177e4SLinus Torvalds br->hello_time = bpdu->hello_time;
2701da177e4SLinus Torvalds br->forward_delay = bpdu->forward_delay;
2718384b5f5SVivien Didelot __br_set_topology_change(br, bpdu->topology_change);
2721da177e4SLinus Torvalds }
2731da177e4SLinus Torvalds
2741da177e4SLinus Torvalds /* called under bridge lock */
br_transmit_tcn(struct net_bridge * br)2751da177e4SLinus Torvalds void br_transmit_tcn(struct net_bridge *br)
2761da177e4SLinus Torvalds {
27791bc033cSstephen hemminger struct net_bridge_port *p;
27891bc033cSstephen hemminger
27991bc033cSstephen hemminger p = br_get_port(br, br->root_port);
28091bc033cSstephen hemminger if (p)
28191bc033cSstephen hemminger br_send_tcn_bpdu(p);
28291bc033cSstephen hemminger else
28391bc033cSstephen hemminger br_notice(br, "root port %u not found for topology notice\n",
28491bc033cSstephen hemminger br->root_port);
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds
2871da177e4SLinus Torvalds /* called under bridge lock */
br_should_become_designated_port(const struct net_bridge_port * p)2881da177e4SLinus Torvalds static int br_should_become_designated_port(const struct net_bridge_port *p)
2891da177e4SLinus Torvalds {
2901da177e4SLinus Torvalds struct net_bridge *br;
2911da177e4SLinus Torvalds int t;
2921da177e4SLinus Torvalds
2931da177e4SLinus Torvalds br = p->br;
2941da177e4SLinus Torvalds if (br_is_designated_port(p))
2951da177e4SLinus Torvalds return 1;
2961da177e4SLinus Torvalds
2971da177e4SLinus Torvalds if (memcmp(&p->designated_root, &br->designated_root, 8))
2981da177e4SLinus Torvalds return 1;
2991da177e4SLinus Torvalds
3001da177e4SLinus Torvalds if (br->root_path_cost < p->designated_cost)
3011da177e4SLinus Torvalds return 1;
3021da177e4SLinus Torvalds else if (br->root_path_cost > p->designated_cost)
3031da177e4SLinus Torvalds return 0;
3041da177e4SLinus Torvalds
3051da177e4SLinus Torvalds t = memcmp(&br->bridge_id, &p->designated_bridge, 8);
3061da177e4SLinus Torvalds if (t < 0)
3071da177e4SLinus Torvalds return 1;
3081da177e4SLinus Torvalds else if (t > 0)
3091da177e4SLinus Torvalds return 0;
3101da177e4SLinus Torvalds
3111da177e4SLinus Torvalds if (p->port_id < p->designated_port)
3121da177e4SLinus Torvalds return 1;
3131da177e4SLinus Torvalds
3141da177e4SLinus Torvalds return 0;
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds
3171da177e4SLinus Torvalds /* called under bridge lock */
br_designated_port_selection(struct net_bridge * br)3181da177e4SLinus Torvalds static void br_designated_port_selection(struct net_bridge *br)
3191da177e4SLinus Torvalds {
3201da177e4SLinus Torvalds struct net_bridge_port *p;
3211da177e4SLinus Torvalds
3221da177e4SLinus Torvalds list_for_each_entry(p, &br->port_list, list) {
3231da177e4SLinus Torvalds if (p->state != BR_STATE_DISABLED &&
3241da177e4SLinus Torvalds br_should_become_designated_port(p))
3251da177e4SLinus Torvalds br_become_designated_port(p);
3261da177e4SLinus Torvalds
3271da177e4SLinus Torvalds }
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds
3301da177e4SLinus Torvalds /* called under bridge lock */
br_supersedes_port_info(const struct net_bridge_port * p,const struct br_config_bpdu * bpdu)331160d73b8Sstephen hemminger static int br_supersedes_port_info(const struct net_bridge_port *p,
332160d73b8Sstephen hemminger const struct br_config_bpdu *bpdu)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds int t;
3351da177e4SLinus Torvalds
3361da177e4SLinus Torvalds t = memcmp(&bpdu->root, &p->designated_root, 8);
3371da177e4SLinus Torvalds if (t < 0)
3381da177e4SLinus Torvalds return 1;
3391da177e4SLinus Torvalds else if (t > 0)
3401da177e4SLinus Torvalds return 0;
3411da177e4SLinus Torvalds
3421da177e4SLinus Torvalds if (bpdu->root_path_cost < p->designated_cost)
3431da177e4SLinus Torvalds return 1;
3441da177e4SLinus Torvalds else if (bpdu->root_path_cost > p->designated_cost)
3451da177e4SLinus Torvalds return 0;
3461da177e4SLinus Torvalds
3471da177e4SLinus Torvalds t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8);
3481da177e4SLinus Torvalds if (t < 0)
3491da177e4SLinus Torvalds return 1;
3501da177e4SLinus Torvalds else if (t > 0)
3511da177e4SLinus Torvalds return 0;
3521da177e4SLinus Torvalds
3531da177e4SLinus Torvalds if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8))
3541da177e4SLinus Torvalds return 1;
3551da177e4SLinus Torvalds
3561da177e4SLinus Torvalds if (bpdu->port_id <= p->designated_port)
3571da177e4SLinus Torvalds return 1;
3581da177e4SLinus Torvalds
3591da177e4SLinus Torvalds return 0;
3601da177e4SLinus Torvalds }
3611da177e4SLinus Torvalds
3621da177e4SLinus Torvalds /* called under bridge lock */
br_topology_change_acknowledged(struct net_bridge * br)363160d73b8Sstephen hemminger static void br_topology_change_acknowledged(struct net_bridge *br)
3641da177e4SLinus Torvalds {
3651da177e4SLinus Torvalds br->topology_change_detected = 0;
3661da177e4SLinus Torvalds del_timer(&br->tcn_timer);
3671da177e4SLinus Torvalds }
3681da177e4SLinus Torvalds
3691da177e4SLinus Torvalds /* called under bridge lock */
br_topology_change_detection(struct net_bridge * br)3701da177e4SLinus Torvalds void br_topology_change_detection(struct net_bridge *br)
3711da177e4SLinus Torvalds {
3721da177e4SLinus Torvalds int isroot = br_is_root_bridge(br);
3731da177e4SLinus Torvalds
3744f0611afSStephen Hemminger if (br->stp_enabled != BR_KERNEL_STP)
3754f0611afSStephen Hemminger return;
3764f0611afSStephen Hemminger
37728a16c97Sstephen hemminger br_info(br, "topology change detected, %s\n",
3781da177e4SLinus Torvalds isroot ? "propagating" : "sending tcn bpdu");
3791da177e4SLinus Torvalds
3801da177e4SLinus Torvalds if (isroot) {
3818384b5f5SVivien Didelot __br_set_topology_change(br, 1);
3821da177e4SLinus Torvalds mod_timer(&br->topology_change_timer, jiffies
3831da177e4SLinus Torvalds + br->bridge_forward_delay + br->bridge_max_age);
3841da177e4SLinus Torvalds } else if (!br->topology_change_detected) {
3851da177e4SLinus Torvalds br_transmit_tcn(br);
3861da177e4SLinus Torvalds mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time);
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds
3891da177e4SLinus Torvalds br->topology_change_detected = 1;
3901da177e4SLinus Torvalds }
3911da177e4SLinus Torvalds
3921da177e4SLinus Torvalds /* called under bridge lock */
br_config_bpdu_generation(struct net_bridge * br)3931da177e4SLinus Torvalds void br_config_bpdu_generation(struct net_bridge *br)
3941da177e4SLinus Torvalds {
3951da177e4SLinus Torvalds struct net_bridge_port *p;
3961da177e4SLinus Torvalds
3971da177e4SLinus Torvalds list_for_each_entry(p, &br->port_list, list) {
3981da177e4SLinus Torvalds if (p->state != BR_STATE_DISABLED &&
3991da177e4SLinus Torvalds br_is_designated_port(p))
4001da177e4SLinus Torvalds br_transmit_config(p);
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds }
4031da177e4SLinus Torvalds
4041da177e4SLinus Torvalds /* called under bridge lock */
br_reply(struct net_bridge_port * p)405160d73b8Sstephen hemminger static void br_reply(struct net_bridge_port *p)
4061da177e4SLinus Torvalds {
4071da177e4SLinus Torvalds br_transmit_config(p);
4081da177e4SLinus Torvalds }
4091da177e4SLinus Torvalds
4101da177e4SLinus Torvalds /* called under bridge lock */
br_configuration_update(struct net_bridge * br)4111da177e4SLinus Torvalds void br_configuration_update(struct net_bridge *br)
4121da177e4SLinus Torvalds {
4131da177e4SLinus Torvalds br_root_selection(br);
4141da177e4SLinus Torvalds br_designated_port_selection(br);
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds
4171da177e4SLinus Torvalds /* called under bridge lock */
br_become_designated_port(struct net_bridge_port * p)4181da177e4SLinus Torvalds void br_become_designated_port(struct net_bridge_port *p)
4191da177e4SLinus Torvalds {
4201da177e4SLinus Torvalds struct net_bridge *br;
4211da177e4SLinus Torvalds
4221da177e4SLinus Torvalds br = p->br;
4231da177e4SLinus Torvalds p->designated_root = br->designated_root;
4241da177e4SLinus Torvalds p->designated_cost = br->root_path_cost;
4251da177e4SLinus Torvalds p->designated_bridge = br->bridge_id;
4261da177e4SLinus Torvalds p->designated_port = p->port_id;
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds
4291da177e4SLinus Torvalds
4301da177e4SLinus Torvalds /* called under bridge lock */
br_make_blocking(struct net_bridge_port * p)4311da177e4SLinus Torvalds static void br_make_blocking(struct net_bridge_port *p)
4321da177e4SLinus Torvalds {
4331da177e4SLinus Torvalds if (p->state != BR_STATE_DISABLED &&
4341da177e4SLinus Torvalds p->state != BR_STATE_BLOCKING) {
4351da177e4SLinus Torvalds if (p->state == BR_STATE_FORWARDING ||
4361da177e4SLinus Torvalds p->state == BR_STATE_LEARNING)
4371da177e4SLinus Torvalds br_topology_change_detection(p->br);
4381da177e4SLinus Torvalds
439775dd692SFlorian Fainelli br_set_state(p, BR_STATE_BLOCKING);
44092899063SNikolay Aleksandrov br_ifinfo_notify(RTM_NEWLINK, NULL, p);
4414ecb961cSstephen hemminger
4421da177e4SLinus Torvalds del_timer(&p->forward_delay_timer);
4431da177e4SLinus Torvalds }
4441da177e4SLinus Torvalds }
4451da177e4SLinus Torvalds
4461da177e4SLinus Torvalds /* called under bridge lock */
br_make_forwarding(struct net_bridge_port * p)4471da177e4SLinus Torvalds static void br_make_forwarding(struct net_bridge_port *p)
4481da177e4SLinus Torvalds {
449ef647f13SStephen Hemminger struct net_bridge *br = p->br;
450ef647f13SStephen Hemminger
451ef647f13SStephen Hemminger if (p->state != BR_STATE_BLOCKING)
452ef647f13SStephen Hemminger return;
453ef647f13SStephen Hemminger
454a461c029Sstephen hemminger if (br->stp_enabled == BR_NO_STP || br->forward_delay == 0) {
455775dd692SFlorian Fainelli br_set_state(p, BR_STATE_FORWARDING);
456ef647f13SStephen Hemminger br_topology_change_detection(br);
457ef647f13SStephen Hemminger del_timer(&p->forward_delay_timer);
458160d73b8Sstephen hemminger } else if (br->stp_enabled == BR_KERNEL_STP)
459775dd692SFlorian Fainelli br_set_state(p, BR_STATE_LISTENING);
4609cde0708SStephen Hemminger else
461775dd692SFlorian Fainelli br_set_state(p, BR_STATE_LEARNING);
4629cde0708SStephen Hemminger
46392899063SNikolay Aleksandrov br_ifinfo_notify(RTM_NEWLINK, NULL, p);
464ef647f13SStephen Hemminger
465ef647f13SStephen Hemminger if (br->forward_delay != 0)
466ef647f13SStephen Hemminger mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay);
4671da177e4SLinus Torvalds }
4681da177e4SLinus Torvalds
4691da177e4SLinus Torvalds /* called under bridge lock */
br_port_state_selection(struct net_bridge * br)4701da177e4SLinus Torvalds void br_port_state_selection(struct net_bridge *br)
4711da177e4SLinus Torvalds {
4721da177e4SLinus Torvalds struct net_bridge_port *p;
4731faa4356Sstephen hemminger unsigned int liveports = 0;
4741da177e4SLinus Torvalds
4751da177e4SLinus Torvalds list_for_each_entry(p, &br->port_list, list) {
4761faa4356Sstephen hemminger if (p->state == BR_STATE_DISABLED)
4771faa4356Sstephen hemminger continue;
4781faa4356Sstephen hemminger
479b03b6dd5SVitalii Demianets /* Don't change port states if userspace is handling STP */
480b03b6dd5SVitalii Demianets if (br->stp_enabled != BR_USER_STP) {
4811da177e4SLinus Torvalds if (p->port_no == br->root_port) {
4821da177e4SLinus Torvalds p->config_pending = 0;
4831da177e4SLinus Torvalds p->topology_change_ack = 0;
4841da177e4SLinus Torvalds br_make_forwarding(p);
4851da177e4SLinus Torvalds } else if (br_is_designated_port(p)) {
4861da177e4SLinus Torvalds del_timer(&p->message_age_timer);
4871da177e4SLinus Torvalds br_make_forwarding(p);
4881da177e4SLinus Torvalds } else {
4891da177e4SLinus Torvalds p->config_pending = 0;
4901da177e4SLinus Torvalds p->topology_change_ack = 0;
4911da177e4SLinus Torvalds br_make_blocking(p);
4921da177e4SLinus Torvalds }
493b03b6dd5SVitalii Demianets }
4941faa4356Sstephen hemminger
4957ce42de1SNikolay Aleksandrov if (p->state != BR_STATE_BLOCKING)
4967ce42de1SNikolay Aleksandrov br_multicast_enable_port(p);
4979aa66382SNikolay Aleksandrov /* Multicast is not disabled for the port when it goes in
4989aa66382SNikolay Aleksandrov * blocking state because the timers will expire and stop by
4999aa66382SNikolay Aleksandrov * themselves without sending more queries.
5009aa66382SNikolay Aleksandrov */
5011faa4356Sstephen hemminger if (p->state == BR_STATE_FORWARDING)
5021faa4356Sstephen hemminger ++liveports;
5031da177e4SLinus Torvalds }
5041da177e4SLinus Torvalds
5051faa4356Sstephen hemminger if (liveports == 0)
5061faa4356Sstephen hemminger netif_carrier_off(br->dev);
5071faa4356Sstephen hemminger else
5081faa4356Sstephen hemminger netif_carrier_on(br->dev);
5091da177e4SLinus Torvalds }
5101da177e4SLinus Torvalds
5111da177e4SLinus Torvalds /* called under bridge lock */
br_topology_change_acknowledge(struct net_bridge_port * p)512160d73b8Sstephen hemminger static void br_topology_change_acknowledge(struct net_bridge_port *p)
5131da177e4SLinus Torvalds {
5141da177e4SLinus Torvalds p->topology_change_ack = 1;
5151da177e4SLinus Torvalds br_transmit_config(p);
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds
5181da177e4SLinus Torvalds /* called under bridge lock */
br_received_config_bpdu(struct net_bridge_port * p,const struct br_config_bpdu * bpdu)519160d73b8Sstephen hemminger void br_received_config_bpdu(struct net_bridge_port *p,
520160d73b8Sstephen hemminger const struct br_config_bpdu *bpdu)
5211da177e4SLinus Torvalds {
5221da177e4SLinus Torvalds struct net_bridge *br;
5231da177e4SLinus Torvalds int was_root;
5241da177e4SLinus Torvalds
525de179966SVivien Didelot p->stp_xstats.rx_bpdu++;
526de179966SVivien Didelot
5271da177e4SLinus Torvalds br = p->br;
5281da177e4SLinus Torvalds was_root = br_is_root_bridge(br);
5291da177e4SLinus Torvalds
5301da177e4SLinus Torvalds if (br_supersedes_port_info(p, bpdu)) {
5311da177e4SLinus Torvalds br_record_config_information(p, bpdu);
5321da177e4SLinus Torvalds br_configuration_update(br);
5331da177e4SLinus Torvalds br_port_state_selection(br);
5341da177e4SLinus Torvalds
5351da177e4SLinus Torvalds if (!br_is_root_bridge(br) && was_root) {
5361da177e4SLinus Torvalds del_timer(&br->hello_timer);
5371da177e4SLinus Torvalds if (br->topology_change_detected) {
5381da177e4SLinus Torvalds del_timer(&br->topology_change_timer);
5391da177e4SLinus Torvalds br_transmit_tcn(br);
5401da177e4SLinus Torvalds
5411da177e4SLinus Torvalds mod_timer(&br->tcn_timer,
5421da177e4SLinus Torvalds jiffies + br->bridge_hello_time);
5431da177e4SLinus Torvalds }
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds
5461da177e4SLinus Torvalds if (p->port_no == br->root_port) {
5471da177e4SLinus Torvalds br_record_config_timeout_values(br, bpdu);
5481da177e4SLinus Torvalds br_config_bpdu_generation(br);
5491da177e4SLinus Torvalds if (bpdu->topology_change_ack)
5501da177e4SLinus Torvalds br_topology_change_acknowledged(br);
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds } else if (br_is_designated_port(p)) {
5531da177e4SLinus Torvalds br_reply(p);
5541da177e4SLinus Torvalds }
5551da177e4SLinus Torvalds }
5561da177e4SLinus Torvalds
5571da177e4SLinus Torvalds /* called under bridge lock */
br_received_tcn_bpdu(struct net_bridge_port * p)5581da177e4SLinus Torvalds void br_received_tcn_bpdu(struct net_bridge_port *p)
5591da177e4SLinus Torvalds {
560de179966SVivien Didelot p->stp_xstats.rx_tcn++;
561de179966SVivien Didelot
5621da177e4SLinus Torvalds if (br_is_designated_port(p)) {
56328a16c97Sstephen hemminger br_info(p->br, "port %u(%s) received tcn bpdu\n",
56495c96174SEric Dumazet (unsigned int) p->port_no, p->dev->name);
5651da177e4SLinus Torvalds
5661da177e4SLinus Torvalds br_topology_change_detection(p->br);
5671da177e4SLinus Torvalds br_topology_change_acknowledge(p);
5681da177e4SLinus Torvalds }
5691da177e4SLinus Torvalds }
57014f98f25Sstephen hemminger
57114f98f25Sstephen hemminger /* Change bridge STP parameter */
br_set_hello_time(struct net_bridge * br,unsigned long val)57214f98f25Sstephen hemminger int br_set_hello_time(struct net_bridge *br, unsigned long val)
57314f98f25Sstephen hemminger {
57414f98f25Sstephen hemminger unsigned long t = clock_t_to_jiffies(val);
57514f98f25Sstephen hemminger
57614f98f25Sstephen hemminger if (t < BR_MIN_HELLO_TIME || t > BR_MAX_HELLO_TIME)
57714f98f25Sstephen hemminger return -ERANGE;
57814f98f25Sstephen hemminger
57914f98f25Sstephen hemminger spin_lock_bh(&br->lock);
58014f98f25Sstephen hemminger br->bridge_hello_time = t;
58114f98f25Sstephen hemminger if (br_is_root_bridge(br))
58214f98f25Sstephen hemminger br->hello_time = br->bridge_hello_time;
58314f98f25Sstephen hemminger spin_unlock_bh(&br->lock);
58414f98f25Sstephen hemminger return 0;
58514f98f25Sstephen hemminger }
58614f98f25Sstephen hemminger
br_set_max_age(struct net_bridge * br,unsigned long val)58714f98f25Sstephen hemminger int br_set_max_age(struct net_bridge *br, unsigned long val)
58814f98f25Sstephen hemminger {
58914f98f25Sstephen hemminger unsigned long t = clock_t_to_jiffies(val);
59014f98f25Sstephen hemminger
59114f98f25Sstephen hemminger if (t < BR_MIN_MAX_AGE || t > BR_MAX_MAX_AGE)
59214f98f25Sstephen hemminger return -ERANGE;
59314f98f25Sstephen hemminger
59414f98f25Sstephen hemminger spin_lock_bh(&br->lock);
59514f98f25Sstephen hemminger br->bridge_max_age = t;
59614f98f25Sstephen hemminger if (br_is_root_bridge(br))
59714f98f25Sstephen hemminger br->max_age = br->bridge_max_age;
59814f98f25Sstephen hemminger spin_unlock_bh(&br->lock);
59914f98f25Sstephen hemminger return 0;
60014f98f25Sstephen hemminger
60114f98f25Sstephen hemminger }
60214f98f25Sstephen hemminger
60382dd4332SVivien Didelot /* called under bridge lock */
__set_ageing_time(struct net_device * dev,unsigned long t)60482dd4332SVivien Didelot int __set_ageing_time(struct net_device *dev, unsigned long t)
60582dd4332SVivien Didelot {
60682dd4332SVivien Didelot struct switchdev_attr attr = {
60782dd4332SVivien Didelot .orig_dev = dev,
60882dd4332SVivien Didelot .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
60982dd4332SVivien Didelot .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER,
61082dd4332SVivien Didelot .u.ageing_time = jiffies_to_clock_t(t),
61182dd4332SVivien Didelot };
61282dd4332SVivien Didelot int err;
61382dd4332SVivien Didelot
614dcbdf135SVladimir Oltean err = switchdev_port_attr_set(dev, &attr, NULL);
61582dd4332SVivien Didelot if (err && err != -EOPNOTSUPP)
61682dd4332SVivien Didelot return err;
61782dd4332SVivien Didelot
61882dd4332SVivien Didelot return 0;
61982dd4332SVivien Didelot }
62082dd4332SVivien Didelot
6214c656c13SStephen Hemminger /* Set time interval that dynamic forwarding entries live
6224c656c13SStephen Hemminger * For pure software bridge, allow values outside the 802.1
6234c656c13SStephen Hemminger * standard specification for special cases:
624efb5b338SMenglong Dong * 0 - entry never ages (all permanent)
625efb5b338SMenglong Dong * 1 - entry disappears (no persistence)
6264c656c13SStephen Hemminger *
6274c656c13SStephen Hemminger * Offloaded switch entries maybe more restrictive
6284c656c13SStephen Hemminger */
br_set_ageing_time(struct net_bridge * br,clock_t ageing_time)6299e0b27feSVivien Didelot int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time)
630c62987bbSScott Feldman {
631c62987bbSScott Feldman unsigned long t = clock_t_to_jiffies(ageing_time);
632c62987bbSScott Feldman int err;
633c62987bbSScott Feldman
63482dd4332SVivien Didelot err = __set_ageing_time(br->dev, t);
63582dd4332SVivien Didelot if (err)
636c62987bbSScott Feldman return err;
637c62987bbSScott Feldman
63834d8acd8SVivien Didelot spin_lock_bh(&br->lock);
63934d8acd8SVivien Didelot br->bridge_ageing_time = t;
640c62987bbSScott Feldman br->ageing_time = t;
64134d8acd8SVivien Didelot spin_unlock_bh(&br->lock);
64234d8acd8SVivien Didelot
643f7cdee8aSNikolay Aleksandrov mod_delayed_work(system_long_wq, &br->gc_work, 0);
644c62987bbSScott Feldman
645c62987bbSScott Feldman return 0;
646c62987bbSScott Feldman }
647c62987bbSScott Feldman
br_get_ageing_time(const struct net_device * br_dev)648bdf123b4SVladimir Oltean clock_t br_get_ageing_time(const struct net_device *br_dev)
649f1d42ea1SVladimir Oltean {
650bdf123b4SVladimir Oltean const struct net_bridge *br;
651f1d42ea1SVladimir Oltean
652f1d42ea1SVladimir Oltean if (!netif_is_bridge_master(br_dev))
653f1d42ea1SVladimir Oltean return 0;
654f1d42ea1SVladimir Oltean
655f1d42ea1SVladimir Oltean br = netdev_priv(br_dev);
656f1d42ea1SVladimir Oltean
657f1d42ea1SVladimir Oltean return jiffies_to_clock_t(br->ageing_time);
658f1d42ea1SVladimir Oltean }
659f1d42ea1SVladimir Oltean EXPORT_SYMBOL_GPL(br_get_ageing_time);
660f1d42ea1SVladimir Oltean
6618384b5f5SVivien Didelot /* called under bridge lock */
__br_set_topology_change(struct net_bridge * br,unsigned char val)6628384b5f5SVivien Didelot void __br_set_topology_change(struct net_bridge *br, unsigned char val)
6638384b5f5SVivien Didelot {
66434d8acd8SVivien Didelot unsigned long t;
66534d8acd8SVivien Didelot int err;
66634d8acd8SVivien Didelot
66734d8acd8SVivien Didelot if (br->stp_enabled == BR_KERNEL_STP && br->topology_change != val) {
66834d8acd8SVivien Didelot /* On topology change, set the bridge ageing time to twice the
66934d8acd8SVivien Didelot * forward delay. Otherwise, restore its default ageing time.
67034d8acd8SVivien Didelot */
67134d8acd8SVivien Didelot
67234d8acd8SVivien Didelot if (val) {
67334d8acd8SVivien Didelot t = 2 * br->forward_delay;
67434d8acd8SVivien Didelot br_debug(br, "decreasing ageing time to %lu\n", t);
67534d8acd8SVivien Didelot } else {
67634d8acd8SVivien Didelot t = br->bridge_ageing_time;
67734d8acd8SVivien Didelot br_debug(br, "restoring ageing time to %lu\n", t);
67834d8acd8SVivien Didelot }
67934d8acd8SVivien Didelot
68034d8acd8SVivien Didelot err = __set_ageing_time(br->dev, t);
68134d8acd8SVivien Didelot if (err)
68234d8acd8SVivien Didelot br_warn(br, "error offloading ageing time\n");
68334d8acd8SVivien Didelot else
68434d8acd8SVivien Didelot br->ageing_time = t;
68534d8acd8SVivien Didelot }
68634d8acd8SVivien Didelot
6878384b5f5SVivien Didelot br->topology_change = val;
6888384b5f5SVivien Didelot }
6898384b5f5SVivien Didelot
__br_set_forward_delay(struct net_bridge * br,unsigned long t)690be4f154dSHerbert Xu void __br_set_forward_delay(struct net_bridge *br, unsigned long t)
69114f98f25Sstephen hemminger {
69214f98f25Sstephen hemminger br->bridge_forward_delay = t;
69314f98f25Sstephen hemminger if (br_is_root_bridge(br))
69414f98f25Sstephen hemminger br->forward_delay = br->bridge_forward_delay;
695be4f154dSHerbert Xu }
696be4f154dSHerbert Xu
br_set_forward_delay(struct net_bridge * br,unsigned long val)697be4f154dSHerbert Xu int br_set_forward_delay(struct net_bridge *br, unsigned long val)
698be4f154dSHerbert Xu {
699be4f154dSHerbert Xu unsigned long t = clock_t_to_jiffies(val);
7008a921265SVlad Yasevich int err = -ERANGE;
701be4f154dSHerbert Xu
702be4f154dSHerbert Xu spin_lock_bh(&br->lock);
7038a921265SVlad Yasevich if (br->stp_enabled != BR_NO_STP &&
7048a921265SVlad Yasevich (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY))
7058a921265SVlad Yasevich goto unlock;
7068a921265SVlad Yasevich
707be4f154dSHerbert Xu __br_set_forward_delay(br, t);
7088a921265SVlad Yasevich err = 0;
7098a921265SVlad Yasevich
7108a921265SVlad Yasevich unlock:
71114f98f25Sstephen hemminger spin_unlock_bh(&br->lock);
7128a921265SVlad Yasevich return err;
71314f98f25Sstephen hemminger }
714