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