linux/net/bridge/br_stp.c
<<
>>
Prefs
   1/*
   2 *      Spanning tree protocol; generic parts
   3 *      Linux ethernet bridge
   4 *
   5 *      Authors:
   6 *      Lennert Buytenhek               <buytenh@gnu.org>
   7 *
   8 *      $Id: br_stp.c,v 1.4 2000/06/19 10:13:35 davem Exp $
   9 *
  10 *      This program is free software; you can redistribute it and/or
  11 *      modify it under the terms of the GNU General Public License
  12 *      as published by the Free Software Foundation; either version
  13 *      2 of the License, or (at your option) any later version.
  14 */
  15#include <linux/kernel.h>
  16
  17#include "br_private.h"
  18#include "br_private_stp.h"
  19
  20/* since time values in bpdu are in jiffies and then scaled (1/256)
  21 * before sending, make sure that is at least one.
  22 */
  23#define MESSAGE_AGE_INCR        ((HZ < 256) ? 1 : (HZ/256))
  24
  25static const char *br_port_state_names[] = {
  26        [BR_STATE_DISABLED] = "disabled",
  27        [BR_STATE_LISTENING] = "listening",
  28        [BR_STATE_LEARNING] = "learning",
  29        [BR_STATE_FORWARDING] = "forwarding",
  30        [BR_STATE_BLOCKING] = "blocking",
  31};
  32
  33void br_log_state(const struct net_bridge_port *p)
  34{
  35        pr_info("%s: port %d(%s) entering %s state\n",
  36                p->br->dev->name, p->port_no, p->dev->name,
  37                br_port_state_names[p->state]);
  38
  39}
  40
  41/* called under bridge lock */
  42struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no)
  43{
  44        struct net_bridge_port *p;
  45
  46        list_for_each_entry_rcu(p, &br->port_list, list) {
  47                if (p->port_no == port_no)
  48                        return p;
  49        }
  50
  51        return NULL;
  52}
  53
  54/* called under bridge lock */
  55static int br_should_become_root_port(const struct net_bridge_port *p,
  56                                      u16 root_port)
  57{
  58        struct net_bridge *br;
  59        struct net_bridge_port *rp;
  60        int t;
  61
  62        br = p->br;
  63        if (p->state == BR_STATE_DISABLED ||
  64            br_is_designated_port(p))
  65                return 0;
  66
  67        if (memcmp(&br->bridge_id, &p->designated_root, 8) <= 0)
  68                return 0;
  69
  70        if (!root_port)
  71                return 1;
  72
  73        rp = br_get_port(br, root_port);
  74
  75        t = memcmp(&p->designated_root, &rp->designated_root, 8);
  76        if (t < 0)
  77                return 1;
  78        else if (t > 0)
  79                return 0;
  80
  81        if (p->designated_cost + p->path_cost <
  82            rp->designated_cost + rp->path_cost)
  83                return 1;
  84        else if (p->designated_cost + p->path_cost >
  85                 rp->designated_cost + rp->path_cost)
  86                return 0;
  87
  88        t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8);
  89        if (t < 0)
  90                return 1;
  91        else if (t > 0)
  92                return 0;
  93
  94        if (p->designated_port < rp->designated_port)
  95                return 1;
  96        else if (p->designated_port > rp->designated_port)
  97                return 0;
  98
  99        if (p->port_id < rp->port_id)
 100                return 1;
 101
 102        return 0;
 103}
 104
 105/* called under bridge lock */
 106static void br_root_selection(struct net_bridge *br)
 107{
 108        struct net_bridge_port *p;
 109        u16 root_port = 0;
 110
 111        list_for_each_entry(p, &br->port_list, list) {
 112                if (br_should_become_root_port(p, root_port))
 113                        root_port = p->port_no;
 114
 115        }
 116
 117        br->root_port = root_port;
 118
 119        if (!root_port) {
 120                br->designated_root = br->bridge_id;
 121                br->root_path_cost = 0;
 122        } else {
 123                p = br_get_port(br, root_port);
 124                br->designated_root = p->designated_root;
 125                br->root_path_cost = p->designated_cost + p->path_cost;
 126        }
 127}
 128
 129/* called under bridge lock */
 130void br_become_root_bridge(struct net_bridge *br)
 131{
 132        br->max_age = br->bridge_max_age;
 133        br->hello_time = br->bridge_hello_time;
 134        br->forward_delay = br->bridge_forward_delay;
 135        br_topology_change_detection(br);
 136        del_timer(&br->tcn_timer);
 137
 138        if (br->dev->flags & IFF_UP) {
 139                br_config_bpdu_generation(br);
 140                mod_timer(&br->hello_timer, jiffies + br->hello_time);
 141        }
 142}
 143
 144/* called under bridge lock */
 145void br_transmit_config(struct net_bridge_port *p)
 146{
 147        struct br_config_bpdu bpdu;
 148        struct net_bridge *br;
 149
 150
 151        if (timer_pending(&p->hold_timer)) {
 152                p->config_pending = 1;
 153                return;
 154        }
 155
 156        br = p->br;
 157
 158        bpdu.topology_change = br->topology_change;
 159        bpdu.topology_change_ack = p->topology_change_ack;
 160        bpdu.root = br->designated_root;
 161        bpdu.root_path_cost = br->root_path_cost;
 162        bpdu.bridge_id = br->bridge_id;
 163        bpdu.port_id = p->port_id;
 164        if (br_is_root_bridge(br))
 165                bpdu.message_age = 0;
 166        else {
 167                struct net_bridge_port *root
 168                        = br_get_port(br, br->root_port);
 169                bpdu.message_age = br->max_age
 170                        - (root->message_age_timer.expires - jiffies)
 171                        + MESSAGE_AGE_INCR;
 172        }
 173        bpdu.max_age = br->max_age;
 174        bpdu.hello_time = br->hello_time;
 175        bpdu.forward_delay = br->forward_delay;
 176
 177        if (bpdu.message_age < br->max_age) {
 178                br_send_config_bpdu(p, &bpdu);
 179                p->topology_change_ack = 0;
 180                p->config_pending = 0;
 181                mod_timer(&p->hold_timer,
 182                          round_jiffies(jiffies + BR_HOLD_TIME));
 183        }
 184}
 185
 186/* called under bridge lock */
 187static inline void br_record_config_information(struct net_bridge_port *p,
 188                                                const struct br_config_bpdu *bpdu)
 189{
 190        p->designated_root = bpdu->root;
 191        p->designated_cost = bpdu->root_path_cost;
 192        p->designated_bridge = bpdu->bridge_id;
 193        p->designated_port = bpdu->port_id;
 194
 195        mod_timer(&p->message_age_timer, jiffies
 196                  + (p->br->max_age - bpdu->message_age));
 197}
 198
 199/* called under bridge lock */
 200static inline void br_record_config_timeout_values(struct net_bridge *br,
 201                                            const struct br_config_bpdu *bpdu)
 202{
 203        br->max_age = bpdu->max_age;
 204        br->hello_time = bpdu->hello_time;
 205        br->forward_delay = bpdu->forward_delay;
 206        br->topology_change = bpdu->topology_change;
 207}
 208
 209/* called under bridge lock */
 210void br_transmit_tcn(struct net_bridge *br)
 211{
 212        br_send_tcn_bpdu(br_get_port(br, br->root_port));
 213}
 214
 215/* called under bridge lock */
 216static int br_should_become_designated_port(const struct net_bridge_port *p)
 217{
 218        struct net_bridge *br;
 219        int t;
 220
 221        br = p->br;
 222        if (br_is_designated_port(p))
 223                return 1;
 224
 225        if (memcmp(&p->designated_root, &br->designated_root, 8))
 226                return 1;
 227
 228        if (br->root_path_cost < p->designated_cost)
 229                return 1;
 230        else if (br->root_path_cost > p->designated_cost)
 231                return 0;
 232
 233        t = memcmp(&br->bridge_id, &p->designated_bridge, 8);
 234        if (t < 0)
 235                return 1;
 236        else if (t > 0)
 237                return 0;
 238
 239        if (p->port_id < p->designated_port)
 240                return 1;
 241
 242        return 0;
 243}
 244
 245/* called under bridge lock */
 246static void br_designated_port_selection(struct net_bridge *br)
 247{
 248        struct net_bridge_port *p;
 249
 250        list_for_each_entry(p, &br->port_list, list) {
 251                if (p->state != BR_STATE_DISABLED &&
 252                    br_should_become_designated_port(p))
 253                        br_become_designated_port(p);
 254
 255        }
 256}
 257
 258/* called under bridge lock */
 259static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
 260{
 261        int t;
 262
 263        t = memcmp(&bpdu->root, &p->designated_root, 8);
 264        if (t < 0)
 265                return 1;
 266        else if (t > 0)
 267                return 0;
 268
 269        if (bpdu->root_path_cost < p->designated_cost)
 270                return 1;
 271        else if (bpdu->root_path_cost > p->designated_cost)
 272                return 0;
 273
 274        t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8);
 275        if (t < 0)
 276                return 1;
 277        else if (t > 0)
 278                return 0;
 279
 280        if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8))
 281                return 1;
 282
 283        if (bpdu->port_id <= p->designated_port)
 284                return 1;
 285
 286        return 0;
 287}
 288
 289/* called under bridge lock */
 290static inline void br_topology_change_acknowledged(struct net_bridge *br)
 291{
 292        br->topology_change_detected = 0;
 293        del_timer(&br->tcn_timer);
 294}
 295
 296/* called under bridge lock */
 297void br_topology_change_detection(struct net_bridge *br)
 298{
 299        int isroot = br_is_root_bridge(br);
 300
 301        pr_info("%s: topology change detected, %s\n", br->dev->name,
 302                isroot ? "propagating" : "sending tcn bpdu");
 303
 304        if (isroot) {
 305                br->topology_change = 1;
 306                mod_timer(&br->topology_change_timer, jiffies
 307                          + br->bridge_forward_delay + br->bridge_max_age);
 308        } else if (!br->topology_change_detected) {
 309                br_transmit_tcn(br);
 310                mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time);
 311        }
 312
 313        br->topology_change_detected = 1;
 314}
 315
 316/* called under bridge lock */
 317void br_config_bpdu_generation(struct net_bridge *br)
 318{
 319        struct net_bridge_port *p;
 320
 321        list_for_each_entry(p, &br->port_list, list) {
 322                if (p->state != BR_STATE_DISABLED &&
 323                    br_is_designated_port(p))
 324                        br_transmit_config(p);
 325        }
 326}
 327
 328/* called under bridge lock */
 329static inline void br_reply(struct net_bridge_port *p)
 330{
 331        br_transmit_config(p);
 332}
 333
 334/* called under bridge lock */
 335void br_configuration_update(struct net_bridge *br)
 336{
 337        br_root_selection(br);
 338        br_designated_port_selection(br);
 339}
 340
 341/* called under bridge lock */
 342void br_become_designated_port(struct net_bridge_port *p)
 343{
 344        struct net_bridge *br;
 345
 346        br = p->br;
 347        p->designated_root = br->designated_root;
 348        p->designated_cost = br->root_path_cost;
 349        p->designated_bridge = br->bridge_id;
 350        p->designated_port = p->port_id;
 351}
 352
 353
 354/* called under bridge lock */
 355static void br_make_blocking(struct net_bridge_port *p)
 356{
 357        if (p->state != BR_STATE_DISABLED &&
 358            p->state != BR_STATE_BLOCKING) {
 359                if (p->state == BR_STATE_FORWARDING ||
 360                    p->state == BR_STATE_LEARNING)
 361                        br_topology_change_detection(p->br);
 362
 363                p->state = BR_STATE_BLOCKING;
 364                br_log_state(p);
 365                del_timer(&p->forward_delay_timer);
 366        }
 367}
 368
 369/* called under bridge lock */
 370static void br_make_forwarding(struct net_bridge_port *p)
 371{
 372        if (p->state == BR_STATE_BLOCKING) {
 373                if (p->br->stp_enabled == BR_KERNEL_STP)
 374                        p->state = BR_STATE_LISTENING;
 375                else
 376                        p->state = BR_STATE_LEARNING;
 377
 378                br_log_state(p);
 379                mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay);     }
 380}
 381
 382/* called under bridge lock */
 383void br_port_state_selection(struct net_bridge *br)
 384{
 385        struct net_bridge_port *p;
 386
 387        /* Don't change port states if userspace is handling STP */
 388        if (br->stp_enabled == BR_USER_STP)
 389                return;
 390
 391        list_for_each_entry(p, &br->port_list, list) {
 392                if (p->state != BR_STATE_DISABLED) {
 393                        if (p->port_no == br->root_port) {
 394                                p->config_pending = 0;
 395                                p->topology_change_ack = 0;
 396                                br_make_forwarding(p);
 397                        } else if (br_is_designated_port(p)) {
 398                                del_timer(&p->message_age_timer);
 399                                br_make_forwarding(p);
 400                        } else {
 401                                p->config_pending = 0;
 402                                p->topology_change_ack = 0;
 403                                br_make_blocking(p);
 404                        }
 405                }
 406
 407        }
 408}
 409
 410/* called under bridge lock */
 411static inline void br_topology_change_acknowledge(struct net_bridge_port *p)
 412{
 413        p->topology_change_ack = 1;
 414        br_transmit_config(p);
 415}
 416
 417/* called under bridge lock */
 418void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
 419{
 420        struct net_bridge *br;
 421        int was_root;
 422
 423        br = p->br;
 424        was_root = br_is_root_bridge(br);
 425
 426        if (br_supersedes_port_info(p, bpdu)) {
 427                br_record_config_information(p, bpdu);
 428                br_configuration_update(br);
 429                br_port_state_selection(br);
 430
 431                if (!br_is_root_bridge(br) && was_root) {
 432                        del_timer(&br->hello_timer);
 433                        if (br->topology_change_detected) {
 434                                del_timer(&br->topology_change_timer);
 435                                br_transmit_tcn(br);
 436
 437                                mod_timer(&br->tcn_timer,
 438                                          jiffies + br->bridge_hello_time);
 439                        }
 440                }
 441
 442                if (p->port_no == br->root_port) {
 443                        br_record_config_timeout_values(br, bpdu);
 444                        br_config_bpdu_generation(br);
 445                        if (bpdu->topology_change_ack)
 446                                br_topology_change_acknowledged(br);
 447                }
 448        } else if (br_is_designated_port(p)) {
 449                br_reply(p);
 450        }
 451}
 452
 453/* called under bridge lock */
 454void br_received_tcn_bpdu(struct net_bridge_port *p)
 455{
 456        if (br_is_designated_port(p)) {
 457                pr_info("%s: received tcn bpdu on port %i(%s)\n",
 458                       p->br->dev->name, p->port_no, p->dev->name);
 459
 460                br_topology_change_detection(p->br);
 461                br_topology_change_acknowledge(p);
 462        }
 463}
 464
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.