18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Spanning tree protocol; generic parts 48c2ecf20Sopenharmony_ci * Linux ethernet bridge 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Lennert Buytenhek <buytenh@gnu.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/rculist.h> 118c2ecf20Sopenharmony_ci#include <net/switchdev.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "br_private.h" 148c2ecf20Sopenharmony_ci#include "br_private_stp.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* since time values in bpdu are in jiffies and then scaled (1/256) 178c2ecf20Sopenharmony_ci * before sending, make sure that is at least one STP tick. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci#define MESSAGE_AGE_INCR ((HZ / 256) + 1) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic const char *const br_port_state_names[] = { 228c2ecf20Sopenharmony_ci [BR_STATE_DISABLED] = "disabled", 238c2ecf20Sopenharmony_ci [BR_STATE_LISTENING] = "listening", 248c2ecf20Sopenharmony_ci [BR_STATE_LEARNING] = "learning", 258c2ecf20Sopenharmony_ci [BR_STATE_FORWARDING] = "forwarding", 268c2ecf20Sopenharmony_ci [BR_STATE_BLOCKING] = "blocking", 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_civoid br_set_state(struct net_bridge_port *p, unsigned int state) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct switchdev_attr attr = { 328c2ecf20Sopenharmony_ci .orig_dev = p->dev, 338c2ecf20Sopenharmony_ci .id = SWITCHDEV_ATTR_ID_PORT_STP_STATE, 348c2ecf20Sopenharmony_ci .flags = SWITCHDEV_F_DEFER, 358c2ecf20Sopenharmony_ci .u.stp_state = state, 368c2ecf20Sopenharmony_ci }; 378c2ecf20Sopenharmony_ci int err; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci /* Don't change the state of the ports if they are driven by a different 408c2ecf20Sopenharmony_ci * protocol. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci if (p->flags & BR_MRP_AWARE) 438c2ecf20Sopenharmony_ci return; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci p->state = state; 468c2ecf20Sopenharmony_ci err = switchdev_port_attr_set(p->dev, &attr); 478c2ecf20Sopenharmony_ci if (err && err != -EOPNOTSUPP) 488c2ecf20Sopenharmony_ci br_warn(p->br, "error setting offload STP state on port %u(%s)\n", 498c2ecf20Sopenharmony_ci (unsigned int) p->port_no, p->dev->name); 508c2ecf20Sopenharmony_ci else 518c2ecf20Sopenharmony_ci br_info(p->br, "port %u(%s) entered %s state\n", 528c2ecf20Sopenharmony_ci (unsigned int) p->port_no, p->dev->name, 538c2ecf20Sopenharmony_ci br_port_state_names[p->state]); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (p->br->stp_enabled == BR_KERNEL_STP) { 568c2ecf20Sopenharmony_ci switch (p->state) { 578c2ecf20Sopenharmony_ci case BR_STATE_BLOCKING: 588c2ecf20Sopenharmony_ci p->stp_xstats.transition_blk++; 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci case BR_STATE_FORWARDING: 618c2ecf20Sopenharmony_ci p->stp_xstats.transition_fwd++; 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* called under bridge lock */ 688c2ecf20Sopenharmony_cistruct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct net_bridge_port *p; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci list_for_each_entry_rcu(p, &br->port_list, list, 738c2ecf20Sopenharmony_ci lockdep_is_held(&br->lock)) { 748c2ecf20Sopenharmony_ci if (p->port_no == port_no) 758c2ecf20Sopenharmony_ci return p; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return NULL; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* called under bridge lock */ 828c2ecf20Sopenharmony_cistatic int br_should_become_root_port(const struct net_bridge_port *p, 838c2ecf20Sopenharmony_ci u16 root_port) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct net_bridge *br; 868c2ecf20Sopenharmony_ci struct net_bridge_port *rp; 878c2ecf20Sopenharmony_ci int t; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci br = p->br; 908c2ecf20Sopenharmony_ci if (p->state == BR_STATE_DISABLED || 918c2ecf20Sopenharmony_ci br_is_designated_port(p)) 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (memcmp(&br->bridge_id, &p->designated_root, 8) <= 0) 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (!root_port) 988c2ecf20Sopenharmony_ci return 1; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci rp = br_get_port(br, root_port); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci t = memcmp(&p->designated_root, &rp->designated_root, 8); 1038c2ecf20Sopenharmony_ci if (t < 0) 1048c2ecf20Sopenharmony_ci return 1; 1058c2ecf20Sopenharmony_ci else if (t > 0) 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (p->designated_cost + p->path_cost < 1098c2ecf20Sopenharmony_ci rp->designated_cost + rp->path_cost) 1108c2ecf20Sopenharmony_ci return 1; 1118c2ecf20Sopenharmony_ci else if (p->designated_cost + p->path_cost > 1128c2ecf20Sopenharmony_ci rp->designated_cost + rp->path_cost) 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8); 1168c2ecf20Sopenharmony_ci if (t < 0) 1178c2ecf20Sopenharmony_ci return 1; 1188c2ecf20Sopenharmony_ci else if (t > 0) 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (p->designated_port < rp->designated_port) 1228c2ecf20Sopenharmony_ci return 1; 1238c2ecf20Sopenharmony_ci else if (p->designated_port > rp->designated_port) 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (p->port_id < rp->port_id) 1278c2ecf20Sopenharmony_ci return 1; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void br_root_port_block(const struct net_bridge *br, 1338c2ecf20Sopenharmony_ci struct net_bridge_port *p) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci br_notice(br, "port %u(%s) tried to become root port (blocked)", 1378c2ecf20Sopenharmony_ci (unsigned int) p->port_no, p->dev->name); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci br_set_state(p, BR_STATE_LISTENING); 1408c2ecf20Sopenharmony_ci br_ifinfo_notify(RTM_NEWLINK, NULL, p); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (br->forward_delay > 0) 1438c2ecf20Sopenharmony_ci mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* called under bridge lock */ 1478c2ecf20Sopenharmony_cistatic void br_root_selection(struct net_bridge *br) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct net_bridge_port *p; 1508c2ecf20Sopenharmony_ci u16 root_port = 0; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci list_for_each_entry(p, &br->port_list, list) { 1538c2ecf20Sopenharmony_ci if (!br_should_become_root_port(p, root_port)) 1548c2ecf20Sopenharmony_ci continue; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (p->flags & BR_ROOT_BLOCK) 1578c2ecf20Sopenharmony_ci br_root_port_block(br, p); 1588c2ecf20Sopenharmony_ci else 1598c2ecf20Sopenharmony_ci root_port = p->port_no; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci br->root_port = root_port; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!root_port) { 1658c2ecf20Sopenharmony_ci br->designated_root = br->bridge_id; 1668c2ecf20Sopenharmony_ci br->root_path_cost = 0; 1678c2ecf20Sopenharmony_ci } else { 1688c2ecf20Sopenharmony_ci p = br_get_port(br, root_port); 1698c2ecf20Sopenharmony_ci br->designated_root = p->designated_root; 1708c2ecf20Sopenharmony_ci br->root_path_cost = p->designated_cost + p->path_cost; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* called under bridge lock */ 1758c2ecf20Sopenharmony_civoid br_become_root_bridge(struct net_bridge *br) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci br->max_age = br->bridge_max_age; 1788c2ecf20Sopenharmony_ci br->hello_time = br->bridge_hello_time; 1798c2ecf20Sopenharmony_ci br->forward_delay = br->bridge_forward_delay; 1808c2ecf20Sopenharmony_ci br_topology_change_detection(br); 1818c2ecf20Sopenharmony_ci del_timer(&br->tcn_timer); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (br->dev->flags & IFF_UP) { 1848c2ecf20Sopenharmony_ci br_config_bpdu_generation(br); 1858c2ecf20Sopenharmony_ci mod_timer(&br->hello_timer, jiffies + br->hello_time); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* called under bridge lock */ 1908c2ecf20Sopenharmony_civoid br_transmit_config(struct net_bridge_port *p) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct br_config_bpdu bpdu; 1938c2ecf20Sopenharmony_ci struct net_bridge *br; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (timer_pending(&p->hold_timer)) { 1968c2ecf20Sopenharmony_ci p->config_pending = 1; 1978c2ecf20Sopenharmony_ci return; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci br = p->br; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci bpdu.topology_change = br->topology_change; 2038c2ecf20Sopenharmony_ci bpdu.topology_change_ack = p->topology_change_ack; 2048c2ecf20Sopenharmony_ci bpdu.root = br->designated_root; 2058c2ecf20Sopenharmony_ci bpdu.root_path_cost = br->root_path_cost; 2068c2ecf20Sopenharmony_ci bpdu.bridge_id = br->bridge_id; 2078c2ecf20Sopenharmony_ci bpdu.port_id = p->port_id; 2088c2ecf20Sopenharmony_ci if (br_is_root_bridge(br)) 2098c2ecf20Sopenharmony_ci bpdu.message_age = 0; 2108c2ecf20Sopenharmony_ci else { 2118c2ecf20Sopenharmony_ci struct net_bridge_port *root 2128c2ecf20Sopenharmony_ci = br_get_port(br, br->root_port); 2138c2ecf20Sopenharmony_ci bpdu.message_age = (jiffies - root->designated_age) 2148c2ecf20Sopenharmony_ci + MESSAGE_AGE_INCR; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci bpdu.max_age = br->max_age; 2178c2ecf20Sopenharmony_ci bpdu.hello_time = br->hello_time; 2188c2ecf20Sopenharmony_ci bpdu.forward_delay = br->forward_delay; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (bpdu.message_age < br->max_age) { 2218c2ecf20Sopenharmony_ci br_send_config_bpdu(p, &bpdu); 2228c2ecf20Sopenharmony_ci p->topology_change_ack = 0; 2238c2ecf20Sopenharmony_ci p->config_pending = 0; 2248c2ecf20Sopenharmony_ci if (p->br->stp_enabled == BR_KERNEL_STP) 2258c2ecf20Sopenharmony_ci mod_timer(&p->hold_timer, 2268c2ecf20Sopenharmony_ci round_jiffies(jiffies + BR_HOLD_TIME)); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* called under bridge lock */ 2318c2ecf20Sopenharmony_cistatic void br_record_config_information(struct net_bridge_port *p, 2328c2ecf20Sopenharmony_ci const struct br_config_bpdu *bpdu) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci p->designated_root = bpdu->root; 2358c2ecf20Sopenharmony_ci p->designated_cost = bpdu->root_path_cost; 2368c2ecf20Sopenharmony_ci p->designated_bridge = bpdu->bridge_id; 2378c2ecf20Sopenharmony_ci p->designated_port = bpdu->port_id; 2388c2ecf20Sopenharmony_ci p->designated_age = jiffies - bpdu->message_age; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci mod_timer(&p->message_age_timer, jiffies 2418c2ecf20Sopenharmony_ci + (bpdu->max_age - bpdu->message_age)); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* called under bridge lock */ 2458c2ecf20Sopenharmony_cistatic void br_record_config_timeout_values(struct net_bridge *br, 2468c2ecf20Sopenharmony_ci const struct br_config_bpdu *bpdu) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci br->max_age = bpdu->max_age; 2498c2ecf20Sopenharmony_ci br->hello_time = bpdu->hello_time; 2508c2ecf20Sopenharmony_ci br->forward_delay = bpdu->forward_delay; 2518c2ecf20Sopenharmony_ci __br_set_topology_change(br, bpdu->topology_change); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* called under bridge lock */ 2558c2ecf20Sopenharmony_civoid br_transmit_tcn(struct net_bridge *br) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct net_bridge_port *p; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci p = br_get_port(br, br->root_port); 2608c2ecf20Sopenharmony_ci if (p) 2618c2ecf20Sopenharmony_ci br_send_tcn_bpdu(p); 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci br_notice(br, "root port %u not found for topology notice\n", 2648c2ecf20Sopenharmony_ci br->root_port); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* called under bridge lock */ 2688c2ecf20Sopenharmony_cistatic int br_should_become_designated_port(const struct net_bridge_port *p) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct net_bridge *br; 2718c2ecf20Sopenharmony_ci int t; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci br = p->br; 2748c2ecf20Sopenharmony_ci if (br_is_designated_port(p)) 2758c2ecf20Sopenharmony_ci return 1; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (memcmp(&p->designated_root, &br->designated_root, 8)) 2788c2ecf20Sopenharmony_ci return 1; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (br->root_path_cost < p->designated_cost) 2818c2ecf20Sopenharmony_ci return 1; 2828c2ecf20Sopenharmony_ci else if (br->root_path_cost > p->designated_cost) 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci t = memcmp(&br->bridge_id, &p->designated_bridge, 8); 2868c2ecf20Sopenharmony_ci if (t < 0) 2878c2ecf20Sopenharmony_ci return 1; 2888c2ecf20Sopenharmony_ci else if (t > 0) 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (p->port_id < p->designated_port) 2928c2ecf20Sopenharmony_ci return 1; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* called under bridge lock */ 2988c2ecf20Sopenharmony_cistatic void br_designated_port_selection(struct net_bridge *br) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct net_bridge_port *p; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci list_for_each_entry(p, &br->port_list, list) { 3038c2ecf20Sopenharmony_ci if (p->state != BR_STATE_DISABLED && 3048c2ecf20Sopenharmony_ci br_should_become_designated_port(p)) 3058c2ecf20Sopenharmony_ci br_become_designated_port(p); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* called under bridge lock */ 3118c2ecf20Sopenharmony_cistatic int br_supersedes_port_info(const struct net_bridge_port *p, 3128c2ecf20Sopenharmony_ci const struct br_config_bpdu *bpdu) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int t; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci t = memcmp(&bpdu->root, &p->designated_root, 8); 3178c2ecf20Sopenharmony_ci if (t < 0) 3188c2ecf20Sopenharmony_ci return 1; 3198c2ecf20Sopenharmony_ci else if (t > 0) 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (bpdu->root_path_cost < p->designated_cost) 3238c2ecf20Sopenharmony_ci return 1; 3248c2ecf20Sopenharmony_ci else if (bpdu->root_path_cost > p->designated_cost) 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8); 3288c2ecf20Sopenharmony_ci if (t < 0) 3298c2ecf20Sopenharmony_ci return 1; 3308c2ecf20Sopenharmony_ci else if (t > 0) 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8)) 3348c2ecf20Sopenharmony_ci return 1; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (bpdu->port_id <= p->designated_port) 3378c2ecf20Sopenharmony_ci return 1; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/* called under bridge lock */ 3438c2ecf20Sopenharmony_cistatic void br_topology_change_acknowledged(struct net_bridge *br) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci br->topology_change_detected = 0; 3468c2ecf20Sopenharmony_ci del_timer(&br->tcn_timer); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci/* called under bridge lock */ 3508c2ecf20Sopenharmony_civoid br_topology_change_detection(struct net_bridge *br) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci int isroot = br_is_root_bridge(br); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (br->stp_enabled != BR_KERNEL_STP) 3558c2ecf20Sopenharmony_ci return; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci br_info(br, "topology change detected, %s\n", 3588c2ecf20Sopenharmony_ci isroot ? "propagating" : "sending tcn bpdu"); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (isroot) { 3618c2ecf20Sopenharmony_ci __br_set_topology_change(br, 1); 3628c2ecf20Sopenharmony_ci mod_timer(&br->topology_change_timer, jiffies 3638c2ecf20Sopenharmony_ci + br->bridge_forward_delay + br->bridge_max_age); 3648c2ecf20Sopenharmony_ci } else if (!br->topology_change_detected) { 3658c2ecf20Sopenharmony_ci br_transmit_tcn(br); 3668c2ecf20Sopenharmony_ci mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time); 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci br->topology_change_detected = 1; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/* called under bridge lock */ 3738c2ecf20Sopenharmony_civoid br_config_bpdu_generation(struct net_bridge *br) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct net_bridge_port *p; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci list_for_each_entry(p, &br->port_list, list) { 3788c2ecf20Sopenharmony_ci if (p->state != BR_STATE_DISABLED && 3798c2ecf20Sopenharmony_ci br_is_designated_port(p)) 3808c2ecf20Sopenharmony_ci br_transmit_config(p); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/* called under bridge lock */ 3858c2ecf20Sopenharmony_cistatic void br_reply(struct net_bridge_port *p) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci br_transmit_config(p); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/* called under bridge lock */ 3918c2ecf20Sopenharmony_civoid br_configuration_update(struct net_bridge *br) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci br_root_selection(br); 3948c2ecf20Sopenharmony_ci br_designated_port_selection(br); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* called under bridge lock */ 3988c2ecf20Sopenharmony_civoid br_become_designated_port(struct net_bridge_port *p) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct net_bridge *br; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci br = p->br; 4038c2ecf20Sopenharmony_ci p->designated_root = br->designated_root; 4048c2ecf20Sopenharmony_ci p->designated_cost = br->root_path_cost; 4058c2ecf20Sopenharmony_ci p->designated_bridge = br->bridge_id; 4068c2ecf20Sopenharmony_ci p->designated_port = p->port_id; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* called under bridge lock */ 4118c2ecf20Sopenharmony_cistatic void br_make_blocking(struct net_bridge_port *p) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci if (p->state != BR_STATE_DISABLED && 4148c2ecf20Sopenharmony_ci p->state != BR_STATE_BLOCKING) { 4158c2ecf20Sopenharmony_ci if (p->state == BR_STATE_FORWARDING || 4168c2ecf20Sopenharmony_ci p->state == BR_STATE_LEARNING) 4178c2ecf20Sopenharmony_ci br_topology_change_detection(p->br); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci br_set_state(p, BR_STATE_BLOCKING); 4208c2ecf20Sopenharmony_ci br_ifinfo_notify(RTM_NEWLINK, NULL, p); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci del_timer(&p->forward_delay_timer); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/* called under bridge lock */ 4278c2ecf20Sopenharmony_cistatic void br_make_forwarding(struct net_bridge_port *p) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct net_bridge *br = p->br; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (p->state != BR_STATE_BLOCKING) 4328c2ecf20Sopenharmony_ci return; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (br->stp_enabled == BR_NO_STP || br->forward_delay == 0) { 4358c2ecf20Sopenharmony_ci br_set_state(p, BR_STATE_FORWARDING); 4368c2ecf20Sopenharmony_ci br_topology_change_detection(br); 4378c2ecf20Sopenharmony_ci del_timer(&p->forward_delay_timer); 4388c2ecf20Sopenharmony_ci } else if (br->stp_enabled == BR_KERNEL_STP) 4398c2ecf20Sopenharmony_ci br_set_state(p, BR_STATE_LISTENING); 4408c2ecf20Sopenharmony_ci else 4418c2ecf20Sopenharmony_ci br_set_state(p, BR_STATE_LEARNING); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci br_ifinfo_notify(RTM_NEWLINK, NULL, p); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (br->forward_delay != 0) 4468c2ecf20Sopenharmony_ci mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/* called under bridge lock */ 4508c2ecf20Sopenharmony_civoid br_port_state_selection(struct net_bridge *br) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct net_bridge_port *p; 4538c2ecf20Sopenharmony_ci unsigned int liveports = 0; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci list_for_each_entry(p, &br->port_list, list) { 4568c2ecf20Sopenharmony_ci if (p->state == BR_STATE_DISABLED) 4578c2ecf20Sopenharmony_ci continue; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* Don't change port states if userspace is handling STP */ 4608c2ecf20Sopenharmony_ci if (br->stp_enabled != BR_USER_STP) { 4618c2ecf20Sopenharmony_ci if (p->port_no == br->root_port) { 4628c2ecf20Sopenharmony_ci p->config_pending = 0; 4638c2ecf20Sopenharmony_ci p->topology_change_ack = 0; 4648c2ecf20Sopenharmony_ci br_make_forwarding(p); 4658c2ecf20Sopenharmony_ci } else if (br_is_designated_port(p)) { 4668c2ecf20Sopenharmony_ci del_timer(&p->message_age_timer); 4678c2ecf20Sopenharmony_ci br_make_forwarding(p); 4688c2ecf20Sopenharmony_ci } else { 4698c2ecf20Sopenharmony_ci p->config_pending = 0; 4708c2ecf20Sopenharmony_ci p->topology_change_ack = 0; 4718c2ecf20Sopenharmony_ci br_make_blocking(p); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (p->state != BR_STATE_BLOCKING) 4768c2ecf20Sopenharmony_ci br_multicast_enable_port(p); 4778c2ecf20Sopenharmony_ci /* Multicast is not disabled for the port when it goes in 4788c2ecf20Sopenharmony_ci * blocking state because the timers will expire and stop by 4798c2ecf20Sopenharmony_ci * themselves without sending more queries. 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci if (p->state == BR_STATE_FORWARDING) 4828c2ecf20Sopenharmony_ci ++liveports; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (liveports == 0) 4868c2ecf20Sopenharmony_ci netif_carrier_off(br->dev); 4878c2ecf20Sopenharmony_ci else 4888c2ecf20Sopenharmony_ci netif_carrier_on(br->dev); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/* called under bridge lock */ 4928c2ecf20Sopenharmony_cistatic void br_topology_change_acknowledge(struct net_bridge_port *p) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci p->topology_change_ack = 1; 4958c2ecf20Sopenharmony_ci br_transmit_config(p); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* called under bridge lock */ 4998c2ecf20Sopenharmony_civoid br_received_config_bpdu(struct net_bridge_port *p, 5008c2ecf20Sopenharmony_ci const struct br_config_bpdu *bpdu) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct net_bridge *br; 5038c2ecf20Sopenharmony_ci int was_root; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci p->stp_xstats.rx_bpdu++; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci br = p->br; 5088c2ecf20Sopenharmony_ci was_root = br_is_root_bridge(br); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (br_supersedes_port_info(p, bpdu)) { 5118c2ecf20Sopenharmony_ci br_record_config_information(p, bpdu); 5128c2ecf20Sopenharmony_ci br_configuration_update(br); 5138c2ecf20Sopenharmony_ci br_port_state_selection(br); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (!br_is_root_bridge(br) && was_root) { 5168c2ecf20Sopenharmony_ci del_timer(&br->hello_timer); 5178c2ecf20Sopenharmony_ci if (br->topology_change_detected) { 5188c2ecf20Sopenharmony_ci del_timer(&br->topology_change_timer); 5198c2ecf20Sopenharmony_ci br_transmit_tcn(br); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci mod_timer(&br->tcn_timer, 5228c2ecf20Sopenharmony_ci jiffies + br->bridge_hello_time); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (p->port_no == br->root_port) { 5278c2ecf20Sopenharmony_ci br_record_config_timeout_values(br, bpdu); 5288c2ecf20Sopenharmony_ci br_config_bpdu_generation(br); 5298c2ecf20Sopenharmony_ci if (bpdu->topology_change_ack) 5308c2ecf20Sopenharmony_ci br_topology_change_acknowledged(br); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci } else if (br_is_designated_port(p)) { 5338c2ecf20Sopenharmony_ci br_reply(p); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci/* called under bridge lock */ 5388c2ecf20Sopenharmony_civoid br_received_tcn_bpdu(struct net_bridge_port *p) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci p->stp_xstats.rx_tcn++; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (br_is_designated_port(p)) { 5438c2ecf20Sopenharmony_ci br_info(p->br, "port %u(%s) received tcn bpdu\n", 5448c2ecf20Sopenharmony_ci (unsigned int) p->port_no, p->dev->name); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci br_topology_change_detection(p->br); 5478c2ecf20Sopenharmony_ci br_topology_change_acknowledge(p); 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci/* Change bridge STP parameter */ 5528c2ecf20Sopenharmony_ciint br_set_hello_time(struct net_bridge *br, unsigned long val) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci unsigned long t = clock_t_to_jiffies(val); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (t < BR_MIN_HELLO_TIME || t > BR_MAX_HELLO_TIME) 5578c2ecf20Sopenharmony_ci return -ERANGE; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci spin_lock_bh(&br->lock); 5608c2ecf20Sopenharmony_ci br->bridge_hello_time = t; 5618c2ecf20Sopenharmony_ci if (br_is_root_bridge(br)) 5628c2ecf20Sopenharmony_ci br->hello_time = br->bridge_hello_time; 5638c2ecf20Sopenharmony_ci spin_unlock_bh(&br->lock); 5648c2ecf20Sopenharmony_ci return 0; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ciint br_set_max_age(struct net_bridge *br, unsigned long val) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci unsigned long t = clock_t_to_jiffies(val); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (t < BR_MIN_MAX_AGE || t > BR_MAX_MAX_AGE) 5728c2ecf20Sopenharmony_ci return -ERANGE; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci spin_lock_bh(&br->lock); 5758c2ecf20Sopenharmony_ci br->bridge_max_age = t; 5768c2ecf20Sopenharmony_ci if (br_is_root_bridge(br)) 5778c2ecf20Sopenharmony_ci br->max_age = br->bridge_max_age; 5788c2ecf20Sopenharmony_ci spin_unlock_bh(&br->lock); 5798c2ecf20Sopenharmony_ci return 0; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci/* called under bridge lock */ 5848c2ecf20Sopenharmony_ciint __set_ageing_time(struct net_device *dev, unsigned long t) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci struct switchdev_attr attr = { 5878c2ecf20Sopenharmony_ci .orig_dev = dev, 5888c2ecf20Sopenharmony_ci .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, 5898c2ecf20Sopenharmony_ci .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER, 5908c2ecf20Sopenharmony_ci .u.ageing_time = jiffies_to_clock_t(t), 5918c2ecf20Sopenharmony_ci }; 5928c2ecf20Sopenharmony_ci int err; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci err = switchdev_port_attr_set(dev, &attr); 5958c2ecf20Sopenharmony_ci if (err && err != -EOPNOTSUPP) 5968c2ecf20Sopenharmony_ci return err; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return 0; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci/* Set time interval that dynamic forwarding entries live 6028c2ecf20Sopenharmony_ci * For pure software bridge, allow values outside the 802.1 6038c2ecf20Sopenharmony_ci * standard specification for special cases: 6048c2ecf20Sopenharmony_ci * 0 - entry never ages (all permanant) 6058c2ecf20Sopenharmony_ci * 1 - entry disappears (no persistance) 6068c2ecf20Sopenharmony_ci * 6078c2ecf20Sopenharmony_ci * Offloaded switch entries maybe more restrictive 6088c2ecf20Sopenharmony_ci */ 6098c2ecf20Sopenharmony_ciint br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci unsigned long t = clock_t_to_jiffies(ageing_time); 6128c2ecf20Sopenharmony_ci int err; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci err = __set_ageing_time(br->dev, t); 6158c2ecf20Sopenharmony_ci if (err) 6168c2ecf20Sopenharmony_ci return err; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci spin_lock_bh(&br->lock); 6198c2ecf20Sopenharmony_ci br->bridge_ageing_time = t; 6208c2ecf20Sopenharmony_ci br->ageing_time = t; 6218c2ecf20Sopenharmony_ci spin_unlock_bh(&br->lock); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci mod_delayed_work(system_long_wq, &br->gc_work, 0); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return 0; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/* called under bridge lock */ 6298c2ecf20Sopenharmony_civoid __br_set_topology_change(struct net_bridge *br, unsigned char val) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci unsigned long t; 6328c2ecf20Sopenharmony_ci int err; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (br->stp_enabled == BR_KERNEL_STP && br->topology_change != val) { 6358c2ecf20Sopenharmony_ci /* On topology change, set the bridge ageing time to twice the 6368c2ecf20Sopenharmony_ci * forward delay. Otherwise, restore its default ageing time. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (val) { 6408c2ecf20Sopenharmony_ci t = 2 * br->forward_delay; 6418c2ecf20Sopenharmony_ci br_debug(br, "decreasing ageing time to %lu\n", t); 6428c2ecf20Sopenharmony_ci } else { 6438c2ecf20Sopenharmony_ci t = br->bridge_ageing_time; 6448c2ecf20Sopenharmony_ci br_debug(br, "restoring ageing time to %lu\n", t); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci err = __set_ageing_time(br->dev, t); 6488c2ecf20Sopenharmony_ci if (err) 6498c2ecf20Sopenharmony_ci br_warn(br, "error offloading ageing time\n"); 6508c2ecf20Sopenharmony_ci else 6518c2ecf20Sopenharmony_ci br->ageing_time = t; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci br->topology_change = val; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_civoid __br_set_forward_delay(struct net_bridge *br, unsigned long t) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci br->bridge_forward_delay = t; 6608c2ecf20Sopenharmony_ci if (br_is_root_bridge(br)) 6618c2ecf20Sopenharmony_ci br->forward_delay = br->bridge_forward_delay; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ciint br_set_forward_delay(struct net_bridge *br, unsigned long val) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci unsigned long t = clock_t_to_jiffies(val); 6678c2ecf20Sopenharmony_ci int err = -ERANGE; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci spin_lock_bh(&br->lock); 6708c2ecf20Sopenharmony_ci if (br->stp_enabled != BR_NO_STP && 6718c2ecf20Sopenharmony_ci (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY)) 6728c2ecf20Sopenharmony_ci goto unlock; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci __br_set_forward_delay(br, t); 6758c2ecf20Sopenharmony_ci err = 0; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ciunlock: 6788c2ecf20Sopenharmony_ci spin_unlock_bh(&br->lock); 6798c2ecf20Sopenharmony_ci return err; 6808c2ecf20Sopenharmony_ci} 681