/* called under bridge lock */ static void br_check_timers(struct net_bridge *br) { struct net_bridge_port *p; if (br_timer_has_expired(&br->gc_timer, br->gc_interval)) { br_timer_set(&br->gc_timer, jiffies); br_fdb_cleanup(br); } if (br_timer_has_expired(&br->hello_timer, br->hello_time)) { br_timer_clear(&br->hello_timer); br_hello_timer_expired(br); } if (br_timer_has_expired(&br->tcn_timer, br->bridge_hello_time)) { br_timer_clear(&br->tcn_timer); br_tcn_timer_expired(br); } if (br_timer_has_expired(&br->topology_change_timer, br->bridge_forward_delay + br->bridge_max_age)) { br_timer_clear(&br->topology_change_timer); br_topology_change_timer_expired(br); } p = br->port_list; while (p != NULL) { if (p->state != BR_STATE_DISABLED) br_check_port_timers(p); p = p->next; } }
/* called under bridge lock */ void br_topology_change_detection(struct net_bridge *br) { // printk(KERN_INFO "%s: topology change detected", br->dev.name); if (br_is_root_bridge(br)) { // printk(", propagating"); br->topology_change = 1; br_timer_set(&br->topology_change_timer, jiffies); } else if (!br->topology_change_detected) { // printk(", sending tcn bpdu"); br_transmit_tcn(br); br_timer_set(&br->tcn_timer, jiffies); } // printk("\n"); br->topology_change_detected = 1; }
/* called under bridge lock */ static void br_record_config_information(struct net_bridge_port *p, struct br_config_bpdu *bpdu) { p->designated_root = bpdu->root; p->designated_cost = bpdu->root_path_cost; p->designated_bridge = bpdu->bridge_id; p->designated_port = bpdu->port_id; br_timer_set(&p->message_age_timer, jiffies - bpdu->message_age); }
/* called under bridge lock */ void br_become_root_bridge(struct net_bridge *br) { br->max_age = br->bridge_max_age; br->hello_time = br->bridge_hello_time; br->forward_delay = br->bridge_forward_delay; br_topology_change_detection(br); br_timer_clear(&br->tcn_timer); br_timer_set(&br->hello_timer, jiffies - br->hello_time); }
/* called under bridge lock */ void br_stp_enable_bridge(struct net_bridge *br) { struct net_bridge_port *p; struct timer_list *timer = &br->tick; init_timer(timer); timer->data = (unsigned long) br; timer->function = br_tick; timer->expires = jiffies + 1; add_timer(timer); br_timer_set(&br->hello_timer, jiffies); br_config_bpdu_generation(br); p = br->port_list; while (p != NULL) { if (p->dev->flags & IFF_UP) br_stp_enable_port(p); p = p->next; } br_timer_set(&br->gc_timer, jiffies); }
/* called under bridge lock */ static void br_make_forwarding(struct net_bridge_port *p) { if (p->state == BR_STATE_BLOCKING) { if (p->br->stp_enabled) { printk(KERN_INFO "%s: port %i(%s) entering %s state\n", p->br->dev.name, p->port_no, p->dev->name, "listening"); p->state = BR_STATE_LISTENING; #if defined( CONFIG_RTL865X)&&defined(CONFIG_RE865X) { int retval=0, vlanPort; char name[IFNAMSIZ]; strcpy(name, p->dev->name); vlanPort=name[strlen(name)-1]-'0'; retval=rtl8651_setSpanningTreeInstancePortState(0, vlanPort,2); if(0==retval) printk("RTL865x Port %d state=LISTENING\n", vlanPort); else printk("RTL865x port %d set state error=%d", vlanPort, retval); } #endif } else { printk(KERN_INFO "%s: port %i(%s) entering %s state\n", p->br->dev.name, p->port_no, p->dev->name, "learning"); p->state = BR_STATE_LEARNING; #if defined( CONFIG_RTL865X)&&defined(CONFIG_RE865X) { int retval=0, vlanPort; char name[IFNAMSIZ]; strcpy(name, p->dev->name); vlanPort=name[strlen(name)-1]-'0'; retval=rtl8651_setSpanningTreeInstancePortState(0, vlanPort,3); if(0==retval) printk("RTL865x Port %d state=LEARNING\n", vlanPort); else printk("RTL865x port %d set state error=%d", vlanPort, retval); } #endif } br_timer_set(&p->forward_delay_timer, jiffies); } }
/* called under bridge lock */ static void br_forward_delay_timer_expired(struct net_bridge_port *p) { if (p->state == BR_STATE_LISTENING) { printk(KERN_INFO "%s: port %i(%s) entering %s state\n", p->br->dev.name, p->port_no, p->dev->name, "learning"); p->state = BR_STATE_LEARNING; br_timer_set(&p->forward_delay_timer, jiffies); } else if (p->state == BR_STATE_LEARNING) { printk(KERN_INFO "%s: port %i(%s) entering %s state\n", p->br->dev.name, p->port_no, p->dev->name, "forwarding"); p->state = BR_STATE_FORWARDING; if (br_is_designated_for_some_port(p->br)) br_topology_change_detection(p->br); } }
/* called under bridge lock */ static void br_make_forwarding(struct net_bridge_port *p) { if (p->state == BR_STATE_BLOCKING) { if (p->br->stp_enabled) { // printk(KERN_INFO "%s: port %i(%s) entering %s state\n", // p->br->dev.name, p->port_no, p->dev->name, // "listening"); p->state = BR_STATE_LISTENING; } else { // printk(KERN_INFO "%s: port %i(%s) entering %s state\n", // p->br->dev.name, p->port_no, p->dev->name, // "learning"); p->state = BR_STATE_LEARNING; } br_timer_set(&p->forward_delay_timer, jiffies); } }
/* called under bridge lock */ void br_transmit_config(struct net_bridge_port *p) { struct br_config_bpdu bpdu; struct net_bridge *br; if (br_timer_is_running(&p->hold_timer)) { p->config_pending = 1; return; } br = p->br; bpdu.topology_change = br->topology_change; bpdu.topology_change_ack = p->topology_change_ack; bpdu.root = br->designated_root; bpdu.root_path_cost = br->root_path_cost; bpdu.bridge_id = br->bridge_id; bpdu.port_id = p->port_id; if (br_is_root_bridge(br)) bpdu.message_age = 0; else { struct net_bridge_port *root; root = br_get_port(br, br->root_port); bpdu.message_age = br_timer_get_residue(&root->message_age_timer) + MESSAGE_AGE_INCR; } bpdu.max_age = br->max_age; bpdu.hello_time = br->hello_time; bpdu.forward_delay = br->forward_delay; if (bpdu.message_age < br->max_age) { br_send_config_bpdu(p, &bpdu); p->topology_change_ack = 0; p->config_pending = 0; br_timer_set(&p->hold_timer, jiffies); } }
/* lock-safe */ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) { struct net_bridge *br; int was_root; if (p->state == BR_STATE_DISABLED) return; br = p->br; read_lock(&br->lock); was_root = br_is_root_bridge(br); if (br_supersedes_port_info(p, bpdu)) { br_record_config_information(p, bpdu); br_configuration_update(br); br_port_state_selection(br); if (!br_is_root_bridge(br) && was_root) { br_timer_clear(&br->hello_timer); if (br->topology_change_detected) { br_timer_clear(&br->topology_change_timer); br_transmit_tcn(br); br_timer_set(&br->tcn_timer, jiffies); } } if (p->port_no == br->root_port) { br_record_config_timeout_values(br, bpdu); br_config_bpdu_generation(br); if (bpdu->topology_change_ack) br_topology_change_acknowledged(br); } } else if (br_is_designated_port(p)) { br_reply(p); } read_unlock(&br->lock); }
/* called under bridge lock */ static void br_tcn_timer_expired(struct net_bridge *br) { printk(KERN_INFO "%s: retransmitting tcn bpdu\n", br->dev.name); br_transmit_tcn(br); br_timer_set(&br->tcn_timer, jiffies); }
/* called under bridge lock */ static void br_hello_timer_expired(struct net_bridge *br) { br_config_bpdu_generation(br); br_timer_set(&br->hello_timer, jiffies); }