diff options
Diffstat (limited to 'ofproto/ofproto-dpif.c')
-rw-r--r-- | ofproto/ofproto-dpif.c | 286 |
1 files changed, 283 insertions, 3 deletions
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index a635ca6ed..bff0ff4ba 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -145,6 +145,10 @@ static void stp_wait(struct ofproto_dpif *ofproto); static int set_stp_port(struct ofport *, const struct ofproto_port_stp_settings *); +static void rstp_run(struct ofproto_dpif *ofproto); +static void set_rstp_port(struct ofport *, + const struct ofproto_port_rstp_settings *); + struct ofport_dpif { struct hmap_node odp_port_node; /* In dpif_backer's "odp_to_ofport_map". */ struct ofport up; @@ -165,6 +169,10 @@ struct ofport_dpif { enum stp_state stp_state; /* Always STP_DISABLED if STP not in use. */ long long int stp_state_entered; + /* Rapid Spanning Tree. */ + struct rstp_port *rstp_port; /* Rapid Spanning Tree Protocol, if any. */ + enum rstp_state rstp_state; /* Always RSTP_DISABLED if RSTP not in use. */ + /* Queue to DSCP mapping. */ struct ofproto_port_queue *qdscp; size_t n_qdscp; @@ -224,6 +232,7 @@ static void ofport_update_peer(struct ofport_dpif *); enum revalidate_reason { REV_RECONFIGURE = 1, /* Switch configuration changed. */ REV_STP, /* Spanning tree protocol port status change. */ + REV_RSTP, /* RSTP port status change. */ REV_BOND, /* Bonding changed. */ REV_PORT_TOGGLED, /* Port enabled or disabled by CFM, LACP, ...*/ REV_FLOW_TABLE, /* Flow table changed. */ @@ -232,6 +241,7 @@ enum revalidate_reason { }; COVERAGE_DEFINE(rev_reconfigure); COVERAGE_DEFINE(rev_stp); +COVERAGE_DEFINE(rev_rstp); COVERAGE_DEFINE(rev_bond); COVERAGE_DEFINE(rev_port_toggled); COVERAGE_DEFINE(rev_flow_table); @@ -302,6 +312,10 @@ struct ofproto_dpif { struct stp *stp; long long int stp_last_tick; + /* Rapid Spanning Tree. */ + struct rstp *rstp; + long long int rstp_last_tick; + /* VLAN splinters. */ struct ovs_mutex vsp_mutex; struct hmap realdev_vid_map OVS_GUARDED; /* (realdev,vid) -> vlandev. */ @@ -575,6 +589,7 @@ type_run(const char *type) switch (backer->need_revalidate) { case REV_RECONFIGURE: COVERAGE_INC(rev_reconfigure); break; case REV_STP: COVERAGE_INC(rev_stp); break; + case REV_RSTP: COVERAGE_INC(rev_rstp); break; case REV_BOND: COVERAGE_INC(rev_bond); break; case REV_PORT_TOGGLED: COVERAGE_INC(rev_port_toggled); break; case REV_FLOW_TABLE: COVERAGE_INC(rev_flow_table); break; @@ -595,8 +610,8 @@ type_run(const char *type) xlate_ofproto_set(ofproto, ofproto->up.name, ofproto->backer->dpif, ofproto->miss_rule, ofproto->no_packet_in_rule, ofproto->ml, - ofproto->stp, ofproto->ms, ofproto->mbridge, - ofproto->sflow, ofproto->ipfix, + ofproto->stp, ofproto->rstp, ofproto->ms, + ofproto->mbridge, ofproto->sflow, ofproto->ipfix, ofproto->netflow, ofproto->up.frag_handling, ofproto->up.forward_bpdu, connmgr_has_in_band(ofproto->up.connmgr), @@ -616,11 +631,14 @@ type_run(const char *type) int stp_port = ofport->stp_port ? stp_port_no(ofport->stp_port) : -1; + int rstp_port = ofport->rstp_port + ? rstp_port_number(ofport->rstp_port) + : -1; xlate_ofport_set(ofproto, ofport->bundle, ofport, ofport->up.ofp_port, ofport->odp_port, ofport->up.netdev, ofport->cfm, ofport->bfd, ofport->peer, stp_port, - ofport->qdscp, ofport->n_qdscp, + rstp_port, ofport->qdscp, ofport->n_qdscp, ofport->up.pp.config, ofport->up.pp.state, ofport->is_tunnel, ofport->may_enable); } @@ -1129,6 +1147,7 @@ construct(struct ofproto *ofproto_) ofproto->sflow = NULL; ofproto->ipfix = NULL; ofproto->stp = NULL; + ofproto->rstp = NULL; ofproto->dump_seq = 0; hmap_init(&ofproto->bundles); ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME); @@ -1393,6 +1412,7 @@ run(struct ofproto *ofproto_) } stp_run(ofproto); + rstp_run(ofproto); ovs_rwlock_wrlock(&ofproto->ml->rwlock); if (mac_learning_run(ofproto->ml)) { ofproto->backer->need_revalidate = REV_MAC_LEARNING; @@ -1551,6 +1571,8 @@ port_construct(struct ofport *port_) port->may_enable = true; port->stp_port = NULL; port->stp_state = STP_DISABLED; + port->rstp_port = NULL; + port->rstp_state = RSTP_DISABLED; port->is_tunnel = false; port->peer = NULL; port->qdscp = NULL; @@ -1661,6 +1683,9 @@ port_destruct(struct ofport *port_) if (port->stp_port) { stp_port_disable(port->stp_port); } + if (port->rstp_port) { + rstp_delete_port(port->rstp_port); + } if (ofproto->sflow) { dpif_sflow_del_port(ofproto->sflow, port->odp_port); } @@ -1884,6 +1909,31 @@ get_bfd_status(struct ofport *ofport_, struct smap *smap) /* Spanning Tree. */ static void +rstp_send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_) +{ + struct ofproto_dpif *ofproto = ofproto_; + struct rstp_port *rp = rstp_get_port(ofproto->rstp, port_num); + struct ofport_dpif *ofport; + + ofport = rstp_port_get_aux(rp); + if (!ofport) { + VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d", + ofproto->up.name, port_num); + } else { + struct eth_header *eth = ofpbuf_l2(pkt); + + netdev_get_etheraddr(ofport->up.netdev, eth->eth_src); + if (eth_addr_is_zero(eth->eth_src)) { + VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d " + "with unknown MAC", ofproto->up.name, port_num); + } else { + ofproto_dpif_send_packet(ofport, pkt); + } + } + ofpbuf_delete(pkt); +} + +static void send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_) { struct ofproto_dpif *ofproto = ofproto_; @@ -1908,6 +1958,138 @@ send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_) ofpbuf_delete(pkt); } +/* Configure RSTP on 'ofproto_' using the settings defined in 's'. */ +static void +set_rstp(struct ofproto *ofproto_, const struct ofproto_rstp_settings *s) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + /* Only revalidate flows if the configuration changed. */ + if (!s != !ofproto->rstp) { + ofproto->backer->need_revalidate = REV_RECONFIGURE; + } + + if (s) { + if (!ofproto->rstp) { + ofproto->rstp = rstp_create(ofproto_->name, s->address, + rstp_send_bpdu_cb, ofproto); + ofproto->rstp_last_tick = time_msec(); + } + rstp_set_bridge_address(ofproto->rstp, s->address); + rstp_set_bridge_priority(ofproto->rstp, s->priority); + rstp_set_bridge_ageing_time(ofproto->rstp, s->ageing_time); + rstp_set_bridge_force_protocol_version(ofproto->rstp, + s->force_protocol_version); + rstp_set_bridge_max_age(ofproto->rstp, s->bridge_max_age); + rstp_set_bridge_forward_delay(ofproto->rstp, s->bridge_forward_delay); + rstp_set_bridge_transmit_hold_count(ofproto->rstp, + s->transmit_hold_count); + } else { + struct ofport *ofport; + HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) { + set_rstp_port(ofport, NULL); + } + rstp_unref(ofproto->rstp); + ofproto->rstp = NULL; + } +} + +static void +get_rstp_status(struct ofproto *ofproto_, struct ofproto_rstp_status *s) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + if (ofproto->rstp) { + s->enabled = true; + s->root_id = rstp_get_root_id(ofproto->rstp); + s->bridge_id = rstp_get_bridge_id(ofproto->rstp); + s->designated_id = rstp_get_designated_id(ofproto->rstp); + s->root_path_cost = rstp_get_root_path_cost(ofproto->rstp); + s->designated_port_id = rstp_get_designated_port_id(ofproto->rstp); + s->bridge_port_id = rstp_get_bridge_port_id(ofproto->rstp); + } else { + s->enabled = false; + } +} + +static void +update_rstp_port_state(struct ofport_dpif *ofport) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + enum rstp_state state; + + /* Figure out new state. */ + state = ofport->rstp_port ? rstp_port_get_state(ofport->rstp_port) + : RSTP_DISABLED; + + /* Update state. */ + if (ofport->rstp_state != state) { + enum ofputil_port_state of_state; + bool fwd_change; + + VLOG_DBG_RL(&rl, "port %s: RSTP state changed from %s to %s", + netdev_get_name(ofport->up.netdev), + rstp_state_name(ofport->rstp_state), + rstp_state_name(state)); + if (rstp_learn_in_state(ofport->rstp_state) + != rstp_learn_in_state(state)) { + /* xxx Learning action flows should also be flushed. */ + ovs_rwlock_wrlock(&ofproto->ml->rwlock); + mac_learning_flush(ofproto->ml); + ovs_rwlock_unlock(&ofproto->ml->rwlock); + } + fwd_change = rstp_forward_in_state(ofport->rstp_state) + != rstp_forward_in_state(state); + + ofproto->backer->need_revalidate = REV_RSTP; + ofport->rstp_state = state; + + if (fwd_change && ofport->bundle) { + bundle_update(ofport->bundle); + } + + /* Update the RSTP state bits in the OpenFlow port description. */ + of_state = ofport->up.pp.state & ~OFPUTIL_PS_STP_MASK; + of_state |= (state == RSTP_LEARNING ? OFPUTIL_PS_STP_LEARN + : state == RSTP_FORWARDING ? OFPUTIL_PS_STP_FORWARD + : state == RSTP_DISCARDING ? OFPUTIL_PS_STP_LISTEN + : 0); + ofproto_port_set_state(&ofport->up, of_state); + } +} + +static void +rstp_run(struct ofproto_dpif *ofproto) +{ + if (ofproto->rstp) { + long long int now = time_msec(); + long long int elapsed = now - ofproto->rstp_last_tick; + struct rstp_port *rp; + /* Every second, decrease the values of the timers. */ + if (elapsed >= 1000) { + rstp_tick_timers(ofproto->rstp); + ofproto->rstp_last_tick = now; + } + while (rstp_get_changed_port(ofproto->rstp, &rp)) { + struct ofport_dpif *ofport = rstp_port_get_aux(rp); + if (ofport) { + update_rstp_port_state(ofport); + } + } + /* FIXME: This check should be done on-event (i.e., when setting + * p->fdb_flush) and not periodically. + */ + if (rstp_check_and_reset_fdb_flush(ofproto->rstp)) { + ovs_rwlock_wrlock(&ofproto->ml->rwlock); + /* FIXME: RSTP should be able to flush the entries pertaining to a + * single port, not the whole table. + */ + mac_learning_flush(ofproto->ml); + ovs_rwlock_unlock(&ofproto->ml->rwlock); + } + } +} + /* Configures STP on 'ofproto_' using the settings defined in 's'. */ static int set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s) @@ -2126,6 +2308,94 @@ stp_wait(struct ofproto_dpif *ofproto) poll_timer_wait(1000); } } + +/* Configures RSTP on 'ofport_' using the settings defined in 's'. The + * caller is responsible for assigning RSTP port numbers and ensuring + * there are no duplicates. */ +static void +set_rstp_port(struct ofport *ofport_, + const struct ofproto_port_rstp_settings *s) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + struct rstp_port *rp = ofport->rstp_port; + int stp_port; + + if (!s || !s->enable) { + if (rp) { + ofport->rstp_port = NULL; + rstp_delete_port(rp); + update_rstp_port_state(ofport); + } + return; + } else if (rp && rstp_port_number(rp) != s->port_num + && ofport == rstp_port_get_aux(rp)) { + /* The port-id changed, so disable the old one if it's not + * already in use by another port. */ + if (s->port_num != 0) { + xlate_txn_start(); + stp_port = ofport->stp_port ? stp_port_no(ofport->stp_port) : -1; + xlate_ofport_set(ofproto, ofport->bundle, ofport, + ofport->up.ofp_port, ofport->odp_port, + ofport->up.netdev, ofport->cfm, + ofport->bfd, ofport->peer, stp_port, + s->port_num, + ofport->qdscp, ofport->n_qdscp, + ofport->up.pp.config, ofport->up.pp.state, + ofport->is_tunnel, ofport->may_enable); + xlate_txn_commit(); + } + + rstp_port_set_aux(rp, ofport); + rstp_port_set_priority(rp, s->priority); + rstp_port_set_port_number(rp, s->port_num); + rstp_port_set_path_cost(rp, s->path_cost); + rstp_port_set_admin_edge(rp, s->admin_edge_port); + rstp_port_set_auto_edge(rp, s->auto_edge); + rstp_port_set_mcheck(rp, s->mcheck); + + update_rstp_port_state(ofport); + + return; + } + rp = ofport->rstp_port = rstp_get_port(ofproto->rstp, s->port_num); + /* Enable RSTP on port */ + if (!rp) { + rp = ofport->rstp_port = rstp_add_port(ofproto->rstp); + } + /* Setters */ + rstp_port_set_aux(rp, ofport); + rstp_port_set_priority(rp, s->priority); + rstp_port_set_port_number(rp, s->port_num); + rstp_port_set_path_cost(rp, s->path_cost); + rstp_port_set_admin_edge(rp, s->admin_edge_port); + rstp_port_set_auto_edge(rp, s->auto_edge); + rstp_port_set_mcheck(rp, s->mcheck); + + update_rstp_port_state(ofport); +} + +static void +get_rstp_port_status(struct ofport *ofport_, + struct ofproto_port_rstp_status *s) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + struct rstp_port *rp = ofport->rstp_port; + + if (!ofproto->rstp || !rp) { + s->enabled = false; + return; + } + + s->enabled = true; + s->port_id = rstp_port_get_id(rp); + s->state = rstp_port_get_state(rp); + s->role = rstp_port_get_role(rp); + rstp_port_get_counts(rp, &s->tx_count, &s->rx_count, + &s->error_count, &s->uptime); +} + static int set_queues(struct ofport *ofport_, const struct ofproto_port_queue *qdscp, @@ -2863,6 +3133,12 @@ port_run(struct ofport_dpif *ofport) } ofport->may_enable = enable; + + if (ofport->rstp_port) { + if (rstp_port_get_mac_operational(ofport->rstp_port) != enable) { + rstp_port_set_mac_operational(ofport->rstp_port, enable); + } + } } static int @@ -5128,6 +5404,10 @@ const struct ofproto_class ofproto_dpif_class = { set_stp_port, get_stp_port_status, get_stp_port_stats, + set_rstp, + get_rstp_status, + set_rstp_port, + get_rstp_port_status, set_queues, bundle_set, bundle_remove, |