162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Spanning tree protocol; generic parts 462306a36Sopenharmony_ci * Linux ethernet bridge 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Lennert Buytenhek <buytenh@gnu.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/rculist.h> 1162306a36Sopenharmony_ci#include <net/switchdev.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "br_private.h" 1462306a36Sopenharmony_ci#include "br_private_stp.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* since time values in bpdu are in jiffies and then scaled (1/256) 1762306a36Sopenharmony_ci * before sending, make sure that is at least one STP tick. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci#define MESSAGE_AGE_INCR ((HZ / 256) + 1) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic const char *const br_port_state_names[] = { 2262306a36Sopenharmony_ci [BR_STATE_DISABLED] = "disabled", 2362306a36Sopenharmony_ci [BR_STATE_LISTENING] = "listening", 2462306a36Sopenharmony_ci [BR_STATE_LEARNING] = "learning", 2562306a36Sopenharmony_ci [BR_STATE_FORWARDING] = "forwarding", 2662306a36Sopenharmony_ci [BR_STATE_BLOCKING] = "blocking", 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_civoid br_set_state(struct net_bridge_port *p, unsigned int state) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct switchdev_attr attr = { 3262306a36Sopenharmony_ci .orig_dev = p->dev, 3362306a36Sopenharmony_ci .id = SWITCHDEV_ATTR_ID_PORT_STP_STATE, 3462306a36Sopenharmony_ci .flags = SWITCHDEV_F_DEFER, 3562306a36Sopenharmony_ci .u.stp_state = state, 3662306a36Sopenharmony_ci }; 3762306a36Sopenharmony_ci int err; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* Don't change the state of the ports if they are driven by a different 4062306a36Sopenharmony_ci * protocol. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci if (p->flags & BR_MRP_AWARE) 4362306a36Sopenharmony_ci return; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci p->state = state; 4662306a36Sopenharmony_ci if (br_opt_get(p->br, BROPT_MST_ENABLED)) { 4762306a36Sopenharmony_ci err = br_mst_set_state(p, 0, state, NULL); 4862306a36Sopenharmony_ci if (err) 4962306a36Sopenharmony_ci br_warn(p->br, "error setting MST state on port %u(%s)\n", 5062306a36Sopenharmony_ci p->port_no, netdev_name(p->dev)); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci err = switchdev_port_attr_set(p->dev, &attr, NULL); 5362306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 5462306a36Sopenharmony_ci br_warn(p->br, "error setting offload STP state on port %u(%s)\n", 5562306a36Sopenharmony_ci (unsigned int) p->port_no, p->dev->name); 5662306a36Sopenharmony_ci else 5762306a36Sopenharmony_ci br_info(p->br, "port %u(%s) entered %s state\n", 5862306a36Sopenharmony_ci (unsigned int) p->port_no, p->dev->name, 5962306a36Sopenharmony_ci br_port_state_names[p->state]); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (p->br->stp_enabled == BR_KERNEL_STP) { 6262306a36Sopenharmony_ci switch (p->state) { 6362306a36Sopenharmony_ci case BR_STATE_BLOCKING: 6462306a36Sopenharmony_ci p->stp_xstats.transition_blk++; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci case BR_STATE_FORWARDING: 6762306a36Sopenharmony_ci p->stp_xstats.transition_fwd++; 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciu8 br_port_get_stp_state(const struct net_device *dev) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct net_bridge_port *p; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci ASSERT_RTNL(); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci p = br_port_get_rtnl(dev); 8062306a36Sopenharmony_ci if (!p) 8162306a36Sopenharmony_ci return BR_STATE_DISABLED; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return p->state; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_port_get_stp_state); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* called under bridge lock */ 8862306a36Sopenharmony_cistruct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct net_bridge_port *p; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci list_for_each_entry_rcu(p, &br->port_list, list, 9362306a36Sopenharmony_ci lockdep_is_held(&br->lock)) { 9462306a36Sopenharmony_ci if (p->port_no == port_no) 9562306a36Sopenharmony_ci return p; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return NULL; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* called under bridge lock */ 10262306a36Sopenharmony_cistatic int br_should_become_root_port(const struct net_bridge_port *p, 10362306a36Sopenharmony_ci u16 root_port) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct net_bridge *br; 10662306a36Sopenharmony_ci struct net_bridge_port *rp; 10762306a36Sopenharmony_ci int t; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci br = p->br; 11062306a36Sopenharmony_ci if (p->state == BR_STATE_DISABLED || 11162306a36Sopenharmony_ci br_is_designated_port(p)) 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (memcmp(&br->bridge_id, &p->designated_root, 8) <= 0) 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (!root_port) 11862306a36Sopenharmony_ci return 1; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci rp = br_get_port(br, root_port); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci t = memcmp(&p->designated_root, &rp->designated_root, 8); 12362306a36Sopenharmony_ci if (t < 0) 12462306a36Sopenharmony_ci return 1; 12562306a36Sopenharmony_ci else if (t > 0) 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (p->designated_cost + p->path_cost < 12962306a36Sopenharmony_ci rp->designated_cost + rp->path_cost) 13062306a36Sopenharmony_ci return 1; 13162306a36Sopenharmony_ci else if (p->designated_cost + p->path_cost > 13262306a36Sopenharmony_ci rp->designated_cost + rp->path_cost) 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8); 13662306a36Sopenharmony_ci if (t < 0) 13762306a36Sopenharmony_ci return 1; 13862306a36Sopenharmony_ci else if (t > 0) 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (p->designated_port < rp->designated_port) 14262306a36Sopenharmony_ci return 1; 14362306a36Sopenharmony_ci else if (p->designated_port > rp->designated_port) 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (p->port_id < rp->port_id) 14762306a36Sopenharmony_ci return 1; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void br_root_port_block(const struct net_bridge *br, 15362306a36Sopenharmony_ci struct net_bridge_port *p) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci br_notice(br, "port %u(%s) tried to become root port (blocked)", 15762306a36Sopenharmony_ci (unsigned int) p->port_no, p->dev->name); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci br_set_state(p, BR_STATE_LISTENING); 16062306a36Sopenharmony_ci br_ifinfo_notify(RTM_NEWLINK, NULL, p); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (br->forward_delay > 0) 16362306a36Sopenharmony_ci mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* called under bridge lock */ 16762306a36Sopenharmony_cistatic void br_root_selection(struct net_bridge *br) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct net_bridge_port *p; 17062306a36Sopenharmony_ci u16 root_port = 0; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci list_for_each_entry(p, &br->port_list, list) { 17362306a36Sopenharmony_ci if (!br_should_become_root_port(p, root_port)) 17462306a36Sopenharmony_ci continue; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (p->flags & BR_ROOT_BLOCK) 17762306a36Sopenharmony_ci br_root_port_block(br, p); 17862306a36Sopenharmony_ci else 17962306a36Sopenharmony_ci root_port = p->port_no; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci br->root_port = root_port; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!root_port) { 18562306a36Sopenharmony_ci br->designated_root = br->bridge_id; 18662306a36Sopenharmony_ci br->root_path_cost = 0; 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci p = br_get_port(br, root_port); 18962306a36Sopenharmony_ci br->designated_root = p->designated_root; 19062306a36Sopenharmony_ci br->root_path_cost = p->designated_cost + p->path_cost; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* called under bridge lock */ 19562306a36Sopenharmony_civoid br_become_root_bridge(struct net_bridge *br) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci br->max_age = br->bridge_max_age; 19862306a36Sopenharmony_ci br->hello_time = br->bridge_hello_time; 19962306a36Sopenharmony_ci br->forward_delay = br->bridge_forward_delay; 20062306a36Sopenharmony_ci br_topology_change_detection(br); 20162306a36Sopenharmony_ci del_timer(&br->tcn_timer); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (br->dev->flags & IFF_UP) { 20462306a36Sopenharmony_ci br_config_bpdu_generation(br); 20562306a36Sopenharmony_ci mod_timer(&br->hello_timer, jiffies + br->hello_time); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* called under bridge lock */ 21062306a36Sopenharmony_civoid br_transmit_config(struct net_bridge_port *p) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct br_config_bpdu bpdu; 21362306a36Sopenharmony_ci struct net_bridge *br; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (timer_pending(&p->hold_timer)) { 21662306a36Sopenharmony_ci p->config_pending = 1; 21762306a36Sopenharmony_ci return; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci br = p->br; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci bpdu.topology_change = br->topology_change; 22362306a36Sopenharmony_ci bpdu.topology_change_ack = p->topology_change_ack; 22462306a36Sopenharmony_ci bpdu.root = br->designated_root; 22562306a36Sopenharmony_ci bpdu.root_path_cost = br->root_path_cost; 22662306a36Sopenharmony_ci bpdu.bridge_id = br->bridge_id; 22762306a36Sopenharmony_ci bpdu.port_id = p->port_id; 22862306a36Sopenharmony_ci if (br_is_root_bridge(br)) 22962306a36Sopenharmony_ci bpdu.message_age = 0; 23062306a36Sopenharmony_ci else { 23162306a36Sopenharmony_ci struct net_bridge_port *root 23262306a36Sopenharmony_ci = br_get_port(br, br->root_port); 23362306a36Sopenharmony_ci bpdu.message_age = (jiffies - root->designated_age) 23462306a36Sopenharmony_ci + MESSAGE_AGE_INCR; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci bpdu.max_age = br->max_age; 23762306a36Sopenharmony_ci bpdu.hello_time = br->hello_time; 23862306a36Sopenharmony_ci bpdu.forward_delay = br->forward_delay; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (bpdu.message_age < br->max_age) { 24162306a36Sopenharmony_ci br_send_config_bpdu(p, &bpdu); 24262306a36Sopenharmony_ci p->topology_change_ack = 0; 24362306a36Sopenharmony_ci p->config_pending = 0; 24462306a36Sopenharmony_ci if (p->br->stp_enabled == BR_KERNEL_STP) 24562306a36Sopenharmony_ci mod_timer(&p->hold_timer, 24662306a36Sopenharmony_ci round_jiffies(jiffies + BR_HOLD_TIME)); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* called under bridge lock */ 25162306a36Sopenharmony_cistatic void br_record_config_information(struct net_bridge_port *p, 25262306a36Sopenharmony_ci const struct br_config_bpdu *bpdu) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci p->designated_root = bpdu->root; 25562306a36Sopenharmony_ci p->designated_cost = bpdu->root_path_cost; 25662306a36Sopenharmony_ci p->designated_bridge = bpdu->bridge_id; 25762306a36Sopenharmony_ci p->designated_port = bpdu->port_id; 25862306a36Sopenharmony_ci p->designated_age = jiffies - bpdu->message_age; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci mod_timer(&p->message_age_timer, jiffies 26162306a36Sopenharmony_ci + (bpdu->max_age - bpdu->message_age)); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* called under bridge lock */ 26562306a36Sopenharmony_cistatic void br_record_config_timeout_values(struct net_bridge *br, 26662306a36Sopenharmony_ci const struct br_config_bpdu *bpdu) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci br->max_age = bpdu->max_age; 26962306a36Sopenharmony_ci br->hello_time = bpdu->hello_time; 27062306a36Sopenharmony_ci br->forward_delay = bpdu->forward_delay; 27162306a36Sopenharmony_ci __br_set_topology_change(br, bpdu->topology_change); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* called under bridge lock */ 27562306a36Sopenharmony_civoid br_transmit_tcn(struct net_bridge *br) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct net_bridge_port *p; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci p = br_get_port(br, br->root_port); 28062306a36Sopenharmony_ci if (p) 28162306a36Sopenharmony_ci br_send_tcn_bpdu(p); 28262306a36Sopenharmony_ci else 28362306a36Sopenharmony_ci br_notice(br, "root port %u not found for topology notice\n", 28462306a36Sopenharmony_ci br->root_port); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/* called under bridge lock */ 28862306a36Sopenharmony_cistatic int br_should_become_designated_port(const struct net_bridge_port *p) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct net_bridge *br; 29162306a36Sopenharmony_ci int t; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci br = p->br; 29462306a36Sopenharmony_ci if (br_is_designated_port(p)) 29562306a36Sopenharmony_ci return 1; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (memcmp(&p->designated_root, &br->designated_root, 8)) 29862306a36Sopenharmony_ci return 1; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (br->root_path_cost < p->designated_cost) 30162306a36Sopenharmony_ci return 1; 30262306a36Sopenharmony_ci else if (br->root_path_cost > p->designated_cost) 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci t = memcmp(&br->bridge_id, &p->designated_bridge, 8); 30662306a36Sopenharmony_ci if (t < 0) 30762306a36Sopenharmony_ci return 1; 30862306a36Sopenharmony_ci else if (t > 0) 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (p->port_id < p->designated_port) 31262306a36Sopenharmony_ci return 1; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* called under bridge lock */ 31862306a36Sopenharmony_cistatic void br_designated_port_selection(struct net_bridge *br) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct net_bridge_port *p; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci list_for_each_entry(p, &br->port_list, list) { 32362306a36Sopenharmony_ci if (p->state != BR_STATE_DISABLED && 32462306a36Sopenharmony_ci br_should_become_designated_port(p)) 32562306a36Sopenharmony_ci br_become_designated_port(p); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* called under bridge lock */ 33162306a36Sopenharmony_cistatic int br_supersedes_port_info(const struct net_bridge_port *p, 33262306a36Sopenharmony_ci const struct br_config_bpdu *bpdu) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int t; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci t = memcmp(&bpdu->root, &p->designated_root, 8); 33762306a36Sopenharmony_ci if (t < 0) 33862306a36Sopenharmony_ci return 1; 33962306a36Sopenharmony_ci else if (t > 0) 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (bpdu->root_path_cost < p->designated_cost) 34362306a36Sopenharmony_ci return 1; 34462306a36Sopenharmony_ci else if (bpdu->root_path_cost > p->designated_cost) 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8); 34862306a36Sopenharmony_ci if (t < 0) 34962306a36Sopenharmony_ci return 1; 35062306a36Sopenharmony_ci else if (t > 0) 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8)) 35462306a36Sopenharmony_ci return 1; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (bpdu->port_id <= p->designated_port) 35762306a36Sopenharmony_ci return 1; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* called under bridge lock */ 36362306a36Sopenharmony_cistatic void br_topology_change_acknowledged(struct net_bridge *br) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci br->topology_change_detected = 0; 36662306a36Sopenharmony_ci del_timer(&br->tcn_timer); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/* called under bridge lock */ 37062306a36Sopenharmony_civoid br_topology_change_detection(struct net_bridge *br) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci int isroot = br_is_root_bridge(br); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (br->stp_enabled != BR_KERNEL_STP) 37562306a36Sopenharmony_ci return; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci br_info(br, "topology change detected, %s\n", 37862306a36Sopenharmony_ci isroot ? "propagating" : "sending tcn bpdu"); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (isroot) { 38162306a36Sopenharmony_ci __br_set_topology_change(br, 1); 38262306a36Sopenharmony_ci mod_timer(&br->topology_change_timer, jiffies 38362306a36Sopenharmony_ci + br->bridge_forward_delay + br->bridge_max_age); 38462306a36Sopenharmony_ci } else if (!br->topology_change_detected) { 38562306a36Sopenharmony_ci br_transmit_tcn(br); 38662306a36Sopenharmony_ci mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci br->topology_change_detected = 1; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* called under bridge lock */ 39362306a36Sopenharmony_civoid br_config_bpdu_generation(struct net_bridge *br) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct net_bridge_port *p; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci list_for_each_entry(p, &br->port_list, list) { 39862306a36Sopenharmony_ci if (p->state != BR_STATE_DISABLED && 39962306a36Sopenharmony_ci br_is_designated_port(p)) 40062306a36Sopenharmony_ci br_transmit_config(p); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/* called under bridge lock */ 40562306a36Sopenharmony_cistatic void br_reply(struct net_bridge_port *p) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci br_transmit_config(p); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* called under bridge lock */ 41162306a36Sopenharmony_civoid br_configuration_update(struct net_bridge *br) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci br_root_selection(br); 41462306a36Sopenharmony_ci br_designated_port_selection(br); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* called under bridge lock */ 41862306a36Sopenharmony_civoid br_become_designated_port(struct net_bridge_port *p) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct net_bridge *br; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci br = p->br; 42362306a36Sopenharmony_ci p->designated_root = br->designated_root; 42462306a36Sopenharmony_ci p->designated_cost = br->root_path_cost; 42562306a36Sopenharmony_ci p->designated_bridge = br->bridge_id; 42662306a36Sopenharmony_ci p->designated_port = p->port_id; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci/* called under bridge lock */ 43162306a36Sopenharmony_cistatic void br_make_blocking(struct net_bridge_port *p) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci if (p->state != BR_STATE_DISABLED && 43462306a36Sopenharmony_ci p->state != BR_STATE_BLOCKING) { 43562306a36Sopenharmony_ci if (p->state == BR_STATE_FORWARDING || 43662306a36Sopenharmony_ci p->state == BR_STATE_LEARNING) 43762306a36Sopenharmony_ci br_topology_change_detection(p->br); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci br_set_state(p, BR_STATE_BLOCKING); 44062306a36Sopenharmony_ci br_ifinfo_notify(RTM_NEWLINK, NULL, p); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci del_timer(&p->forward_delay_timer); 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* called under bridge lock */ 44762306a36Sopenharmony_cistatic void br_make_forwarding(struct net_bridge_port *p) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct net_bridge *br = p->br; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (p->state != BR_STATE_BLOCKING) 45262306a36Sopenharmony_ci return; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (br->stp_enabled == BR_NO_STP || br->forward_delay == 0) { 45562306a36Sopenharmony_ci br_set_state(p, BR_STATE_FORWARDING); 45662306a36Sopenharmony_ci br_topology_change_detection(br); 45762306a36Sopenharmony_ci del_timer(&p->forward_delay_timer); 45862306a36Sopenharmony_ci } else if (br->stp_enabled == BR_KERNEL_STP) 45962306a36Sopenharmony_ci br_set_state(p, BR_STATE_LISTENING); 46062306a36Sopenharmony_ci else 46162306a36Sopenharmony_ci br_set_state(p, BR_STATE_LEARNING); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci br_ifinfo_notify(RTM_NEWLINK, NULL, p); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (br->forward_delay != 0) 46662306a36Sopenharmony_ci mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci/* called under bridge lock */ 47062306a36Sopenharmony_civoid br_port_state_selection(struct net_bridge *br) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct net_bridge_port *p; 47362306a36Sopenharmony_ci unsigned int liveports = 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci list_for_each_entry(p, &br->port_list, list) { 47662306a36Sopenharmony_ci if (p->state == BR_STATE_DISABLED) 47762306a36Sopenharmony_ci continue; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Don't change port states if userspace is handling STP */ 48062306a36Sopenharmony_ci if (br->stp_enabled != BR_USER_STP) { 48162306a36Sopenharmony_ci if (p->port_no == br->root_port) { 48262306a36Sopenharmony_ci p->config_pending = 0; 48362306a36Sopenharmony_ci p->topology_change_ack = 0; 48462306a36Sopenharmony_ci br_make_forwarding(p); 48562306a36Sopenharmony_ci } else if (br_is_designated_port(p)) { 48662306a36Sopenharmony_ci del_timer(&p->message_age_timer); 48762306a36Sopenharmony_ci br_make_forwarding(p); 48862306a36Sopenharmony_ci } else { 48962306a36Sopenharmony_ci p->config_pending = 0; 49062306a36Sopenharmony_ci p->topology_change_ack = 0; 49162306a36Sopenharmony_ci br_make_blocking(p); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (p->state != BR_STATE_BLOCKING) 49662306a36Sopenharmony_ci br_multicast_enable_port(p); 49762306a36Sopenharmony_ci /* Multicast is not disabled for the port when it goes in 49862306a36Sopenharmony_ci * blocking state because the timers will expire and stop by 49962306a36Sopenharmony_ci * themselves without sending more queries. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ci if (p->state == BR_STATE_FORWARDING) 50262306a36Sopenharmony_ci ++liveports; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (liveports == 0) 50662306a36Sopenharmony_ci netif_carrier_off(br->dev); 50762306a36Sopenharmony_ci else 50862306a36Sopenharmony_ci netif_carrier_on(br->dev); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/* called under bridge lock */ 51262306a36Sopenharmony_cistatic void br_topology_change_acknowledge(struct net_bridge_port *p) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci p->topology_change_ack = 1; 51562306a36Sopenharmony_ci br_transmit_config(p); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/* called under bridge lock */ 51962306a36Sopenharmony_civoid br_received_config_bpdu(struct net_bridge_port *p, 52062306a36Sopenharmony_ci const struct br_config_bpdu *bpdu) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct net_bridge *br; 52362306a36Sopenharmony_ci int was_root; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci p->stp_xstats.rx_bpdu++; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci br = p->br; 52862306a36Sopenharmony_ci was_root = br_is_root_bridge(br); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (br_supersedes_port_info(p, bpdu)) { 53162306a36Sopenharmony_ci br_record_config_information(p, bpdu); 53262306a36Sopenharmony_ci br_configuration_update(br); 53362306a36Sopenharmony_ci br_port_state_selection(br); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (!br_is_root_bridge(br) && was_root) { 53662306a36Sopenharmony_ci del_timer(&br->hello_timer); 53762306a36Sopenharmony_ci if (br->topology_change_detected) { 53862306a36Sopenharmony_ci del_timer(&br->topology_change_timer); 53962306a36Sopenharmony_ci br_transmit_tcn(br); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci mod_timer(&br->tcn_timer, 54262306a36Sopenharmony_ci jiffies + br->bridge_hello_time); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (p->port_no == br->root_port) { 54762306a36Sopenharmony_ci br_record_config_timeout_values(br, bpdu); 54862306a36Sopenharmony_ci br_config_bpdu_generation(br); 54962306a36Sopenharmony_ci if (bpdu->topology_change_ack) 55062306a36Sopenharmony_ci br_topology_change_acknowledged(br); 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci } else if (br_is_designated_port(p)) { 55362306a36Sopenharmony_ci br_reply(p); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci/* called under bridge lock */ 55862306a36Sopenharmony_civoid br_received_tcn_bpdu(struct net_bridge_port *p) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci p->stp_xstats.rx_tcn++; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (br_is_designated_port(p)) { 56362306a36Sopenharmony_ci br_info(p->br, "port %u(%s) received tcn bpdu\n", 56462306a36Sopenharmony_ci (unsigned int) p->port_no, p->dev->name); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci br_topology_change_detection(p->br); 56762306a36Sopenharmony_ci br_topology_change_acknowledge(p); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/* Change bridge STP parameter */ 57262306a36Sopenharmony_ciint br_set_hello_time(struct net_bridge *br, unsigned long val) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci unsigned long t = clock_t_to_jiffies(val); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (t < BR_MIN_HELLO_TIME || t > BR_MAX_HELLO_TIME) 57762306a36Sopenharmony_ci return -ERANGE; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci spin_lock_bh(&br->lock); 58062306a36Sopenharmony_ci br->bridge_hello_time = t; 58162306a36Sopenharmony_ci if (br_is_root_bridge(br)) 58262306a36Sopenharmony_ci br->hello_time = br->bridge_hello_time; 58362306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 58462306a36Sopenharmony_ci return 0; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ciint br_set_max_age(struct net_bridge *br, unsigned long val) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci unsigned long t = clock_t_to_jiffies(val); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (t < BR_MIN_MAX_AGE || t > BR_MAX_MAX_AGE) 59262306a36Sopenharmony_ci return -ERANGE; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci spin_lock_bh(&br->lock); 59562306a36Sopenharmony_ci br->bridge_max_age = t; 59662306a36Sopenharmony_ci if (br_is_root_bridge(br)) 59762306a36Sopenharmony_ci br->max_age = br->bridge_max_age; 59862306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci/* called under bridge lock */ 60462306a36Sopenharmony_ciint __set_ageing_time(struct net_device *dev, unsigned long t) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct switchdev_attr attr = { 60762306a36Sopenharmony_ci .orig_dev = dev, 60862306a36Sopenharmony_ci .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, 60962306a36Sopenharmony_ci .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER, 61062306a36Sopenharmony_ci .u.ageing_time = jiffies_to_clock_t(t), 61162306a36Sopenharmony_ci }; 61262306a36Sopenharmony_ci int err; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci err = switchdev_port_attr_set(dev, &attr, NULL); 61562306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 61662306a36Sopenharmony_ci return err; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/* Set time interval that dynamic forwarding entries live 62262306a36Sopenharmony_ci * For pure software bridge, allow values outside the 802.1 62362306a36Sopenharmony_ci * standard specification for special cases: 62462306a36Sopenharmony_ci * 0 - entry never ages (all permanent) 62562306a36Sopenharmony_ci * 1 - entry disappears (no persistence) 62662306a36Sopenharmony_ci * 62762306a36Sopenharmony_ci * Offloaded switch entries maybe more restrictive 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ciint br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci unsigned long t = clock_t_to_jiffies(ageing_time); 63262306a36Sopenharmony_ci int err; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci err = __set_ageing_time(br->dev, t); 63562306a36Sopenharmony_ci if (err) 63662306a36Sopenharmony_ci return err; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci spin_lock_bh(&br->lock); 63962306a36Sopenharmony_ci br->bridge_ageing_time = t; 64062306a36Sopenharmony_ci br->ageing_time = t; 64162306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci mod_delayed_work(system_long_wq, &br->gc_work, 0); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ciclock_t br_get_ageing_time(const struct net_device *br_dev) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci const struct net_bridge *br; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (!netif_is_bridge_master(br_dev)) 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci br = netdev_priv(br_dev); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return jiffies_to_clock_t(br->ageing_time); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_get_ageing_time); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/* called under bridge lock */ 66262306a36Sopenharmony_civoid __br_set_topology_change(struct net_bridge *br, unsigned char val) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci unsigned long t; 66562306a36Sopenharmony_ci int err; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (br->stp_enabled == BR_KERNEL_STP && br->topology_change != val) { 66862306a36Sopenharmony_ci /* On topology change, set the bridge ageing time to twice the 66962306a36Sopenharmony_ci * forward delay. Otherwise, restore its default ageing time. 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (val) { 67362306a36Sopenharmony_ci t = 2 * br->forward_delay; 67462306a36Sopenharmony_ci br_debug(br, "decreasing ageing time to %lu\n", t); 67562306a36Sopenharmony_ci } else { 67662306a36Sopenharmony_ci t = br->bridge_ageing_time; 67762306a36Sopenharmony_ci br_debug(br, "restoring ageing time to %lu\n", t); 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci err = __set_ageing_time(br->dev, t); 68162306a36Sopenharmony_ci if (err) 68262306a36Sopenharmony_ci br_warn(br, "error offloading ageing time\n"); 68362306a36Sopenharmony_ci else 68462306a36Sopenharmony_ci br->ageing_time = t; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci br->topology_change = val; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_civoid __br_set_forward_delay(struct net_bridge *br, unsigned long t) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci br->bridge_forward_delay = t; 69362306a36Sopenharmony_ci if (br_is_root_bridge(br)) 69462306a36Sopenharmony_ci br->forward_delay = br->bridge_forward_delay; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ciint br_set_forward_delay(struct net_bridge *br, unsigned long val) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci unsigned long t = clock_t_to_jiffies(val); 70062306a36Sopenharmony_ci int err = -ERANGE; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci spin_lock_bh(&br->lock); 70362306a36Sopenharmony_ci if (br->stp_enabled != BR_NO_STP && 70462306a36Sopenharmony_ci (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY)) 70562306a36Sopenharmony_ci goto unlock; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci __br_set_forward_delay(br, t); 70862306a36Sopenharmony_ci err = 0; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ciunlock: 71162306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 71262306a36Sopenharmony_ci return err; 71362306a36Sopenharmony_ci} 714