]> git.proxmox.com Git - mirror_ovs.git/blobdiff - lib/stp.c
userspace: Add packet_type in dp_packet and flow
[mirror_ovs.git] / lib / stp.c
index afe77d0824c0757e06ac183b8f2f8fe05b384521..952d3ce434c3e3db54e4ece14f17d0e76fe564fa 100644 (file)
--- a/lib/stp.c
+++ b/lib/stp.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <stdlib.h>
 #include "byte-order.h"
 #include "connectivity.h"
-#include "ofpbuf.h"
+#include "openvswitch/ofpbuf.h"
+#include "ovs-atomic.h"
+#include "dp-packet.h"
 #include "packets.h"
 #include "seq.h"
 #include "unixctl.h"
 #include "util.h"
-#include "vlog.h"
+#include "openvswitch/vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(stp);
 
+static struct vlog_rate_limit stp_rl = VLOG_RATE_LIMIT_INIT(60, 60);
+
 #define STP_PROTOCOL_ID 0x0000
 #define STP_PROTOCOL_VERSION 0x00
 #define STP_TYPE_CONFIG 0x00
@@ -82,6 +86,7 @@ struct stp_timer {
 
 struct stp_port {
     struct stp *stp;
+    char *port_name;                /* Human-readable name for log messages. */
     void *aux;                      /* Auxiliary data the user may retrieve. */
     int port_id;                    /* 8.5.5.1: Unique port identifier. */
     enum stp_state state;           /* 8.5.5.2: Current state. */
@@ -106,7 +111,7 @@ struct stp_port {
 };
 
 struct stp {
-    struct list node;               /* Node in all_stps list. */
+    struct ovs_list node;           /* Node in all_stps list. */
 
     /* Static bridge data. */
     char *name;                     /* Human-readable name for log messages. */
@@ -140,15 +145,15 @@ struct stp {
     /* Interface to client. */
     bool fdb_needs_flush;          /* MAC learning tables needs flushing. */
     struct stp_port *first_changed_port;
-    void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux);
+    void (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux);
     void *aux;
 
     struct ovs_refcount ref_cnt;
 };
 
 static struct ovs_mutex mutex;
-static struct list all_stps__ = LIST_INITIALIZER(&all_stps__);
-static struct list *const all_stps OVS_GUARDED_BY(mutex) = &all_stps__;
+static struct ovs_list all_stps__ = OVS_LIST_INITIALIZER(&all_stps__);
+static struct ovs_list *const all_stps OVS_GUARDED_BY(mutex) = &all_stps__;
 
 #define FOR_EACH_ENABLED_PORT(PORT, STP)                        \
     for ((PORT) = stp_next_enabled_port((STP), (STP)->ports);   \
@@ -231,12 +236,27 @@ static void stp_send_bpdu(struct stp_port *, const void *, size_t)
     OVS_REQUIRES(mutex);
 static void stp_unixctl_tcn(struct unixctl_conn *, int argc,
                             const char *argv[], void *aux);
+static void stp_unixctl_show(struct unixctl_conn *, int argc,
+                             const char *argv[], void *aux);
 
 void
 stp_init(void)
 {
-    unixctl_command_register("stp/tcn", "[bridge]", 0, 1, stp_unixctl_tcn,
-                             NULL);
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+
+    if (ovsthread_once_start(&once)) {
+        /* We need a recursive mutex because stp_send_bpdu() could loop back
+         * into the stp module through a patch port.  This happens
+         * intentionally as part of the unit tests.  Ideally we'd ditch
+         * the call back function, but for now this is what we have. */
+        ovs_mutex_init_recursive(&mutex);
+
+        unixctl_command_register("stp/tcn", "[bridge]", 0, 1, stp_unixctl_tcn,
+                                 NULL);
+        unixctl_command_register("stp/show", "[bridge]", 0, 1,
+                                 stp_unixctl_show, NULL);
+        ovsthread_once_done(&once);
+    }
 }
 
 /* Creates and returns a new STP instance that initially has no ports enabled.
@@ -254,21 +274,13 @@ stp_init(void)
  */
 struct stp *
 stp_create(const char *name, stp_identifier bridge_id,
-           void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux),
+           void (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux),
            void *aux)
 {
-    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
     struct stp *stp;
     struct stp_port *p;
 
-    if (ovsthread_once_start(&once)) {
-        /* We need a recursive mutex because stp_send_bpdu() could loop back
-         * into the stp module through a patch port.  This happens
-         * intentionally as part of the unit tests.  Ideally we'd ditch
-         * the call back function, but for now this is what we have. */
-        ovs_mutex_init_recursive(&mutex);
-        ovsthread_once_done(&once);
-    }
+    stp_init();
 
     ovs_mutex_lock(&mutex);
     stp = xzalloc(sizeof *stp);
@@ -294,7 +306,7 @@ stp_create(const char *name, stp_identifier bridge_id,
 
     stp_stop_timer(&stp->tcn_timer);
     stp_stop_timer(&stp->topology_change_timer);
-    stp_start_timer(&stp->hello_timer, 0);
+    stp_start_timer(&stp->hello_timer, stp->hello_time);
 
     stp->send_bpdu = send_bpdu;
     stp->aux = aux;
@@ -308,7 +320,7 @@ stp_create(const char *name, stp_identifier bridge_id,
     }
     ovs_refcount_init(&stp->ref_cnt);
 
-    list_push_back(all_stps, &stp->node);
+    ovs_list_push_back(all_stps, &stp->node);
     ovs_mutex_unlock(&mutex);
     return stp;
 }
@@ -327,11 +339,17 @@ stp_ref(const struct stp *stp_)
 void
 stp_unref(struct stp *stp)
 {
-    if (stp && ovs_refcount_unref(&stp->ref_cnt) == 1) {
+    if (stp && ovs_refcount_unref_relaxed(&stp->ref_cnt) == 1) {
+        size_t i;
+
         ovs_mutex_lock(&mutex);
-        list_remove(&stp->node);
+        ovs_list_remove(&stp->node);
         ovs_mutex_unlock(&mutex);
         free(stp->name);
+
+        for (i = 0; i < STP_MAX_PORTS; i++) {
+            free(stp->ports[i].port_name);
+        }
         free(stp);
     }
 }
@@ -664,24 +682,34 @@ stp_state_name(enum stp_state state)
 
 /* Returns true if 'state' is one in which packets received on a port should
  * be forwarded, false otherwise.
- *
- * Returns true if 'state' is STP_DISABLED, since presumably in that case the
- * port should still work, just not have STP applied to it. */
+ */
 bool
 stp_forward_in_state(enum stp_state state)
 {
-    return (state & (STP_DISABLED | STP_FORWARDING)) != 0;
+    return (state & STP_FORWARDING) != 0;
 }
 
 /* Returns true if 'state' is one in which MAC learning should be done on
  * packets received on a port, false otherwise.
- *
- * Returns true if 'state' is STP_DISABLED, since presumably in that case the
- * port should still work, just not have STP applied to it. */
+ */
 bool
 stp_learn_in_state(enum stp_state state)
 {
-    return (state & (STP_DISABLED | STP_LEARNING | STP_FORWARDING)) != 0;
+    return (state & (STP_LEARNING | STP_FORWARDING)) != 0;
+}
+
+/* Returns true if 'state' is one in which bpdus should be forwarded on a
+ * port, false otherwise.
+ *
+ * Returns true if 'state' is STP_DISABLED, since in that case the port does
+ * not generate the bpdu and should just forward it (e.g. patch port on pif
+ * bridge). */
+bool
+stp_should_forward_bpdu(enum stp_state state)
+{
+    return (state &
+            ( STP_DISABLED | STP_LISTENING | STP_LEARNING
+              | STP_FORWARDING)) != 0;
 }
 
 /* Returns the name for the given 'role' (for use in debugging and log
@@ -781,6 +809,18 @@ stp_port_get_stp(struct stp_port *p)
     return stp;
 }
 
+void
+stp_port_set_name(struct stp_port *p, const char *name)
+{
+    char *old;
+
+    ovs_mutex_lock(&mutex);
+    old = p->port_name;
+    p->port_name = xstrdup(name);
+    free(old);
+    ovs_mutex_unlock(&mutex);
+}
+
 /* Sets the 'aux' member of 'p'.
  *
  * The 'aux' member will be reset to NULL when stp_port_disable() is
@@ -821,18 +861,6 @@ stp_port_no(const struct stp_port *p)
     return index;
 }
 
-/* Returns the port ID for 'p'. */
-int
-stp_port_get_id(const struct stp_port *p)
-{
-    int port_id;
-
-    ovs_mutex_lock(&mutex);
-    port_id = p->port_id;
-    ovs_mutex_unlock(&mutex);
-    return port_id;
-}
-
 /* Returns the state of port 'p'. */
 enum stp_state
 stp_port_get_state(const struct stp_port *p)
@@ -846,13 +874,12 @@ stp_port_get_state(const struct stp_port *p)
 }
 
 /* Returns the role of port 'p'. */
-enum stp_role
-stp_port_get_role(const struct stp_port *p)
+static enum stp_role
+stp_port_get_role(const struct stp_port *p) OVS_REQUIRES(mutex)
 {
     struct stp_port *root_port;
     enum stp_role role;
 
-    ovs_mutex_lock(&mutex);
     root_port = p->stp->root_port;
     if (root_port && root_port->port_id == p->port_id) {
         role = STP_ROLE_ROOT;
@@ -863,7 +890,6 @@ stp_port_get_role(const struct stp_port *p)
     } else {
         role = STP_ROLE_ALTERNATE;
     }
-    ovs_mutex_unlock(&mutex);
     return role;
 }
 
@@ -879,6 +905,17 @@ stp_port_get_counts(const struct stp_port *p,
     ovs_mutex_unlock(&mutex);
 }
 
+void
+stp_port_get_status(const struct stp_port *p,
+                    int *port_id, enum stp_state *state, enum stp_role *role)
+{
+    ovs_mutex_lock(&mutex);
+    *port_id = p->port_id;
+    *state = p->state;
+    *role = stp_port_get_role(p);
+    ovs_mutex_unlock(&mutex);
+}
+
 /* Disables STP on port 'p'. */
 void
 stp_port_disable(struct stp_port *p)
@@ -1005,6 +1042,8 @@ stp_transmit_config(struct stp_port *p) OVS_REQUIRES(mutex)
         return;
     }
     if (p->hold_timer.active) {
+        VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu pending",
+                    stp->name, p->port_name);
         p->config_pending = true;
     } else {
         struct stp_config_bpdu config;
@@ -1035,6 +1074,8 @@ stp_transmit_config(struct stp_port *p) OVS_REQUIRES(mutex)
         if (ntohs(config.message_age) < stp->max_age) {
             p->topology_change_ack = false;
             p->config_pending = false;
+            VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu",
+                        stp->name, p->port_name);
             stp_send_bpdu(p, &config, sizeof config);
             stp_start_timer(&p->hold_timer, 0);
         }
@@ -1105,9 +1146,12 @@ stp_transmit_tcn(struct stp *stp) OVS_REQUIRES(mutex)
 {
     struct stp_port *p = stp->root_port;
     struct stp_tcn_bpdu tcn_bpdu;
+
     if (!p) {
         return;
     }
+    VLOG_DBG_RL(&stp_rl, "bridge: %s, root port: %s, transmit tcn", stp->name,
+                p->port_name);
     tcn_bpdu.header.protocol_id = htons(STP_PROTOCOL_ID);
     tcn_bpdu.header.protocol_version = STP_PROTOCOL_VERSION;
     tcn_bpdu.header.bpdu_type = STP_TYPE_TCN;
@@ -1353,6 +1397,9 @@ stp_message_age_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
 {
     struct stp *stp = p->stp;
     bool root = stp_is_root_bridge(stp);
+
+    VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, message age timer expired",
+                stp->name, p->port_name);
     stp_become_designated_port(p);
     stp_configuration_update(stp);
     stp_port_state_selection(stp);
@@ -1424,7 +1471,12 @@ stp_initialize_port(struct stp_port *p, enum stp_state state)
 {
     ovs_assert(state & (STP_DISABLED | STP_BLOCKING));
     stp_become_designated_port(p);
-    stp_set_port_state(p, state);
+
+    if (!p->state && state == STP_DISABLED) {
+        p->state = state; /* Do not trigger state change when initializing. */
+    } else {
+        stp_set_port_state(p, state);
+    }
     p->topology_change_ack = false;
     p->config_pending = false;
     p->change_detection_enabled = true;
@@ -1522,18 +1574,19 @@ stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
 {
     struct eth_header *eth;
     struct llc_header *llc;
-    struct ofpbuf *pkt;
+    struct dp_packet *pkt;
 
     /* Skeleton. */
-    pkt = ofpbuf_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
-    pkt->l2 = eth = ofpbuf_put_zeros(pkt, sizeof *eth);
-    llc = ofpbuf_put_zeros(pkt, sizeof *llc);
-    pkt->l3 = ofpbuf_put(pkt, bpdu, bpdu_size);
+    pkt = dp_packet_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
+    eth = dp_packet_put_zeros(pkt, sizeof *eth);
+    llc = dp_packet_put_zeros(pkt, sizeof *llc);
+    dp_packet_reset_offsets(pkt);
+    dp_packet_set_l3(pkt, dp_packet_put(pkt, bpdu, bpdu_size));
 
     /* 802.2 header. */
-    memcpy(eth->eth_dst, eth_addr_stp, ETH_ADDR_LEN);
+    eth->eth_dst = eth_addr_stp;
     /* p->stp->send_bpdu() must fill in source address. */
-    eth->eth_type = htons(pkt->size - ETH_HEADER_LEN);
+    eth->eth_type = htons(dp_packet_size(pkt) - ETH_HEADER_LEN);
 
     /* LLC header. */
     llc->llc_dsap = STP_LLC_DSAP;
@@ -1585,3 +1638,97 @@ stp_unixctl_tcn(struct unixctl_conn *conn, int argc,
 out:
     ovs_mutex_unlock(&mutex);
 }
+
+static void
+stp_bridge_id_details(struct ds *ds, const stp_identifier bridge_id,
+                      const int hello_time, const int max_age,
+                      const int forward_delay)
+    OVS_REQUIRES(mutex)
+{
+    uint16_t priority = bridge_id >> 48;
+    ds_put_format(ds, "\tstp-priority\t%"PRIu16"\n", priority);
+
+    struct eth_addr mac;
+    const uint64_t mac_bits = (UINT64_C(1) << 48) - 1;
+    eth_addr_from_uint64(bridge_id & mac_bits, &mac);
+    ds_put_format(ds, "\tstp-system-id\t"ETH_ADDR_FMT"\n", ETH_ADDR_ARGS(mac));
+    ds_put_format(ds, "\tstp-hello-time\t%ds\n",
+                  timer_to_ms(hello_time) / 1000);
+    ds_put_format(ds, "\tstp-max-age\t%ds\n", timer_to_ms(max_age) / 1000);
+    ds_put_format(ds, "\tstp-fwd-delay\t%ds\n",
+                  timer_to_ms(forward_delay) / 1000);
+}
+
+static void
+stp_print_details(struct ds *ds, const struct stp *stp)
+    OVS_REQUIRES(mutex)
+{
+    const uint16_t port_no_bits = (UINT16_C(1) << 8) - 1;
+
+    ds_put_format(ds, "---- %s ----\n", stp->name);
+    ds_put_cstr(ds, "Root ID:\n");
+
+    stp_bridge_id_details(ds, stp->designated_root, stp->bridge_hello_time,
+                          stp->bridge_max_age, stp->bridge_forward_delay);
+
+    if (stp_is_root_bridge(stp)) {
+        ds_put_cstr(ds, "\tThis bridge is the root\n");
+    } else {
+        ds_put_format(ds, "\troot-port\t%s\n", stp->root_port->port_name);
+        ds_put_format(ds, "\troot-path-cost\t%u\n", stp->root_path_cost);
+    }
+
+    ds_put_cstr(ds, "\n");
+
+    ds_put_cstr(ds, "Bridge ID:\n");
+    stp_bridge_id_details(ds, stp->bridge_id, stp->hello_time,
+                          stp->max_age, stp->forward_delay);
+
+    ds_put_cstr(ds, "\n");
+
+    const struct stp_port *p;
+    ds_put_format(ds, "\t%-11.10s%-11.10s%-11.10s%-6.5s%-8.7s\n",
+                  "Interface", "Role", "State", "Cost", "Pri.Nbr");
+    ds_put_cstr(ds, "\t---------- ---------- ---------- ----- -------\n");
+    FOR_EACH_ENABLED_PORT (p, stp) {
+        ds_put_format(ds, "\t%-11.10s", p->port_name);
+        ds_put_format(ds, "%-11.10s", stp_role_name(stp_port_get_role(p)));
+        ds_put_format(ds, "%-11.10s", stp_state_name(p->state));
+        ds_put_format(ds, "%-6d", p->path_cost);
+        ds_put_format(ds, "%d.%d\n", p->port_id >> 8,
+                      p->port_id & port_no_bits);
+    }
+
+    ds_put_cstr(ds, "\n");
+}
+
+static void
+stp_unixctl_show(struct unixctl_conn *conn, int argc,
+                 const char *argv[], void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+
+    ovs_mutex_lock(&mutex);
+    if (argc > 1) {
+        struct stp *stp = stp_find(argv[1]);
+
+        if (!stp) {
+            unixctl_command_reply_error(conn, "no such stp object");
+            goto out;
+        }
+
+        stp_print_details(&ds, stp);
+    } else {
+        struct stp *stp;
+
+        LIST_FOR_EACH (stp, node, all_stps) {
+            stp_print_details(&ds, stp);
+        }
+    }
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+
+out:
+    ovs_mutex_unlock(&mutex);
+}