/* called under bridge lock */ static void br_stp_change_bridge_id(struct net_bridge *br, unsigned char *addr) { unsigned char oldaddr[6]; struct net_bridge_port *p; int wasroot; wasroot = br_is_root_bridge(br); memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN); memcpy(br->bridge_id.addr, addr, ETH_ALEN); memcpy(br->dev.dev_addr, addr, ETH_ALEN); p = br->port_list; while (p != NULL) { if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN)) memcpy(p->designated_bridge.addr, addr, ETH_ALEN); if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN)) memcpy(p->designated_root.addr, addr, ETH_ALEN); p = p->next; } br_configuration_update(br); br_port_state_selection(br); if (br_is_root_bridge(br) && !wasroot) br_become_root_bridge(br); }
/* * Change state of port (ie from forwarding to blocking etc) * Used by spanning tree in user space. */ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); struct ifinfomsg *ifm; struct nlattr *protinfo; struct net_device *dev; struct net_bridge_port *p; u8 new_state; if (nlmsg_len(nlh) < sizeof(*ifm)) return -EINVAL; ifm = nlmsg_data(nlh); if (ifm->ifi_family != AF_BRIDGE) return -EPFNOSUPPORT; protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); if (!protinfo || nla_len(protinfo) < sizeof(u8)) return -EINVAL; new_state = nla_get_u8(protinfo); if (new_state > BR_STATE_BLOCKING) return -EINVAL; dev = __dev_get_by_index(net, ifm->ifi_index); if (!dev) return -ENODEV; p = br_port_get_rtnl(dev); if (!p) return -EINVAL; /* if kernel STP is running, don't allow changes */ if (p->br->stp_enabled == BR_KERNEL_STP) return -EBUSY; if (!netif_running(dev) || (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED)) return -ENETDOWN; p->state = new_state; br_log_state(p); spin_lock_bh(&p->br->lock); br_port_state_selection(p->br); spin_unlock_bh(&p->br->lock); br_ifinfo_notify(RTM_NEWLINK, p); return 0; }
/* Change the state of the port and notify spanning tree */ static int br_set_port_state(struct net_bridge_port *p, u8 state) { if (state > BR_STATE_BLOCKING) return -EINVAL; /* if kernel STP is running, don't allow changes */ if (p->br->stp_enabled == BR_KERNEL_STP) return -EBUSY; if (!netif_running(p->dev) || (!netif_carrier_ok(p->dev) && state != BR_STATE_DISABLED)) return -ENETDOWN; p->state = state; br_log_state(p); br_port_state_selection(p->br); return 0; }
/* Change the state of the port and notify spanning tree */ static int br_set_port_state(struct net_bridge_port *p, u8 state) { if (state > BR_STATE_BLOCKING) return -EINVAL; /* if kernel STP is running, don't allow changes */ if (p->br->stp_enabled == BR_KERNEL_STP) return -EBUSY; /* if device is not up, change is not allowed * if link is not present, only allowable state is disabled */ if (!netif_running(p->dev) || (!netif_oper_up(p->dev) && state != BR_STATE_DISABLED)) return -ENETDOWN; p->state = state; br_log_state(p); br_port_state_selection(p->br); return 0; }
/* 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 */ void br_stp_disable_port(struct net_bridge_port *p) { struct net_bridge *br; int wasroot; br = p->br; printk(KERN_INFO "%s: port %i(%s) entering %s state\n", br->dev.name, p->port_no, p->dev->name, "disabled"); wasroot = br_is_root_bridge(br); br_become_designated_port(p); p->state = BR_STATE_DISABLED; p->topology_change_ack = 0; p->config_pending = 0; br_timer_clear(&p->message_age_timer); br_timer_clear(&p->forward_delay_timer); br_timer_clear(&p->hold_timer); br_configuration_update(br); br_port_state_selection(br); if (br_is_root_bridge(br) && !wasroot) br_become_root_bridge(br); }
/* called under bridge lock */ static void br_message_age_timer_expired(struct net_bridge_port *p) { struct net_bridge *br; int was_root; br = p->br; printk(KERN_INFO "%s: ", br->dev.name); printk("neighbour "); dump_bridge_id(&p->designated_bridge); printk(" lost on port %i(%s)\n", p->port_no, p->dev->name); /* * According to the spec, the message age timer cannot be * running when we are the root bridge. So.. this was_root * check is redundant. I'm leaving it in for now, though. */ was_root = br_is_root_bridge(br); br_become_designated_port(p); br_configuration_update(br); br_port_state_selection(br); if (br_is_root_bridge(br) && !was_root) br_become_root_bridge(br); }
/* called under bridge lock */ void br_stp_enable_port(struct net_bridge_port *p) { br_init_port(p); br_port_state_selection(p->br); }