+
+static int
+get_cfm_remote_mpids(const struct ofport *ofport_, const uint64_t **rmps,
+ size_t *n_rmps)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+ if (ofport->cfm) {
+ cfm_get_remote_mpids(ofport->cfm, rmps, n_rmps);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+\f
+/* Spanning Tree. */
+
+static void
+send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_;
+ struct stp_port *sp = stp_get_port(ofproto->stp, port_num);
+ struct ofport_dpif *ofport;
+
+ ofport = stp_port_get_aux(sp);
+ if (!ofport) {
+ VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d",
+ ofproto->up.name, port_num);
+ } else {
+ struct eth_header *eth = pkt->l2;
+
+ 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 {
+ send_packet(ofproto_dpif_cast(ofport->up.ofproto),
+ ofport->odp_port, pkt);
+ }
+ }
+ ofpbuf_delete(pkt);
+}
+
+/* Configures STP on 'ofproto_' using the settings defined in 's'. */
+static int
+set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ /* Only revalidate flows if the configuration changed. */
+ if (!s != !ofproto->stp) {
+ ofproto->need_revalidate = true;
+ }
+
+ if (s) {
+ if (!ofproto->stp) {
+ ofproto->stp = stp_create(ofproto_->name, s->system_id,
+ send_bpdu_cb, ofproto);
+ ofproto->stp_last_tick = time_msec();
+ }
+
+ stp_set_bridge_id(ofproto->stp, s->system_id);
+ stp_set_bridge_priority(ofproto->stp, s->priority);
+ stp_set_hello_time(ofproto->stp, s->hello_time);
+ stp_set_max_age(ofproto->stp, s->max_age);
+ stp_set_forward_delay(ofproto->stp, s->fwd_delay);
+ } else {
+ stp_destroy(ofproto->stp);
+ ofproto->stp = NULL;
+ }
+
+ return 0;
+}
+
+static int
+get_stp_status(struct ofproto *ofproto_, struct ofproto_stp_status *s)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ if (ofproto->stp) {
+ s->enabled = true;
+ s->bridge_id = stp_get_bridge_id(ofproto->stp);
+ s->designated_root = stp_get_designated_root(ofproto->stp);
+ s->root_path_cost = stp_get_root_path_cost(ofproto->stp);
+ } else {
+ s->enabled = false;
+ }
+
+ return 0;
+}
+
+static void
+update_stp_port_state(struct ofport_dpif *ofport)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ enum stp_state state;
+
+ /* Figure out new state. */
+ state = ofport->stp_port ? stp_port_get_state(ofport->stp_port)
+ : STP_DISABLED;
+
+ /* Update state. */
+ if (ofport->stp_state != state) {
+ ovs_be32 of_state;
+ bool fwd_change;
+
+ VLOG_DBG_RL(&rl, "port %s: STP state changed from %s to %s",
+ netdev_get_name(ofport->up.netdev),
+ stp_state_name(ofport->stp_state),
+ stp_state_name(state));
+ if (stp_learn_in_state(ofport->stp_state)
+ != stp_learn_in_state(state)) {
+ /* xxx Learning action flows should also be flushed. */
+ mac_learning_flush(ofproto->ml);
+ }
+ fwd_change = stp_forward_in_state(ofport->stp_state)
+ != stp_forward_in_state(state);
+
+ ofproto->need_revalidate = true;
+ ofport->stp_state = state;
+ ofport->stp_state_entered = time_msec();
+
+ if (fwd_change && ofport->bundle) {
+ bundle_update(ofport->bundle);
+ }
+
+ /* Update the STP state bits in the OpenFlow port description. */
+ of_state = (ofport->up.opp.state & htonl(~OFPPS_STP_MASK))
+ | htonl(state == STP_LISTENING ? OFPPS_STP_LISTEN
+ : state == STP_LEARNING ? OFPPS_STP_LEARN
+ : state == STP_FORWARDING ? OFPPS_STP_FORWARD
+ : state == STP_BLOCKING ? OFPPS_STP_BLOCK
+ : 0);
+ ofproto_port_set_state(&ofport->up, of_state);
+ }
+}
+
+/* Configures STP on 'ofport_' using the settings defined in 's'. The
+ * caller is responsible for assigning STP port numbers and ensuring
+ * there are no duplicates. */
+static int
+set_stp_port(struct ofport *ofport_,
+ const struct ofproto_port_stp_settings *s)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ struct stp_port *sp = ofport->stp_port;
+
+ if (!s || !s->enable) {
+ if (sp) {
+ ofport->stp_port = NULL;
+ stp_port_disable(sp);
+ update_stp_port_state(ofport);
+ }
+ return 0;
+ } else if (sp && stp_port_no(sp) != s->port_num
+ && ofport == stp_port_get_aux(sp)) {
+ /* The port-id changed, so disable the old one if it's not
+ * already in use by another port. */
+ stp_port_disable(sp);
+ }
+
+ sp = ofport->stp_port = stp_get_port(ofproto->stp, s->port_num);
+ stp_port_enable(sp);
+
+ stp_port_set_aux(sp, ofport);
+ stp_port_set_priority(sp, s->priority);
+ stp_port_set_path_cost(sp, s->path_cost);
+
+ update_stp_port_state(ofport);
+
+ return 0;
+}
+
+static int
+get_stp_port_status(struct ofport *ofport_,
+ struct ofproto_port_stp_status *s)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ struct stp_port *sp = ofport->stp_port;
+
+ if (!ofproto->stp || !sp) {
+ s->enabled = false;
+ return 0;
+ }
+
+ s->enabled = true;
+ s->port_id = stp_port_get_id(sp);
+ s->state = stp_port_get_state(sp);
+ s->sec_in_state = (time_msec() - ofport->stp_state_entered) / 1000;
+ s->role = stp_port_get_role(sp);
+ stp_port_get_counts(sp, &s->tx_count, &s->rx_count, &s->error_count);
+
+ return 0;
+}
+
+static void
+stp_run(struct ofproto_dpif *ofproto)
+{
+ if (ofproto->stp) {
+ long long int now = time_msec();
+ long long int elapsed = now - ofproto->stp_last_tick;
+ struct stp_port *sp;
+
+ if (elapsed > 0) {
+ stp_tick(ofproto->stp, MIN(INT_MAX, elapsed));
+ ofproto->stp_last_tick = now;
+ }
+ while (stp_get_changed_port(ofproto->stp, &sp)) {
+ struct ofport_dpif *ofport = stp_port_get_aux(sp);
+
+ if (ofport) {
+ update_stp_port_state(ofport);
+ }
+ }
+ }
+}
+
+static void
+stp_wait(struct ofproto_dpif *ofproto)
+{
+ if (ofproto->stp) {
+ poll_timer_wait(1000);
+ }
+}
+
+/* Returns true if STP should process 'flow'. */
+static bool
+stp_should_process_flow(const struct flow *flow)
+{
+ return eth_addr_equals(flow->dl_dst, eth_addr_stp);
+}
+
+static void
+stp_process_packet(const struct ofport_dpif *ofport,
+ const struct ofpbuf *packet)
+{
+ struct ofpbuf payload = *packet;
+ struct eth_header *eth = payload.data;
+ struct stp_port *sp = ofport->stp_port;
+
+ /* Sink packets on ports that have STP disabled when the bridge has
+ * STP enabled. */
+ if (!sp || stp_port_get_state(sp) == STP_DISABLED) {
+ return;
+ }
+
+ /* Trim off padding on payload. */
+ if (payload.size > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
+ payload.size = ntohs(eth->eth_type) + ETH_HEADER_LEN;
+ }
+
+ if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
+ stp_received_bpdu(sp, payload.data, payload.size);
+ }
+}