stp: Track BPDU tx and rx counts.
authorJustin Pettit <jpettit@nicira.com>
Mon, 14 Nov 2011 23:04:14 +0000 (15:04 -0800)
committerJustin Pettit <jpettit@nicira.com>
Tue, 15 Nov 2011 17:29:08 +0000 (09:29 -0800)
When debugging spanning tree, it's useful to have counters about how
many BPDUs have been sent and received.  This commit keeps track of
these counters and stores them in a new "statistics" column of the Port
table.

Feature #8103

lib/stp.c
lib/stp.h
ofproto/ofproto-dpif.c
ofproto/ofproto.h
vswitchd/bridge.c
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml

index 94b9a4b5b993d17c5f0d1a8519f4262b011ceaa9..62c2ea8acaf3c5203e8cb664ec2717459c09a1b7 100644 (file)
--- a/lib/stp.c
+++ b/lib/stp.c
@@ -93,6 +93,10 @@ struct stp_port {
     struct stp_timer forward_delay_timer; /* 8.5.6.2: State change timer. */
     struct stp_timer hold_timer;        /* 8.5.6.3: BPDU rate limit timer. */
 
+    int tx_count;                   /* Number of BPDUs transmitted. */
+    int rx_count;                   /* Number of valid BPDUs received. */
+    int error_count;                /* Number of bad BPDUs received. */
+
     bool state_changed;
 };
 
@@ -562,6 +566,7 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
 
     if (bpdu_size < sizeof(struct stp_bpdu_header)) {
         VLOG_WARN("%s: received runt %zu-byte BPDU", stp->name, bpdu_size);
+        p->error_count++;
         return;
     }
 
@@ -569,6 +574,7 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
     if (header->protocol_id != htons(STP_PROTOCOL_ID)) {
         VLOG_WARN("%s: received BPDU with unexpected protocol ID %"PRIu16,
                   stp->name, ntohs(header->protocol_id));
+        p->error_count++;
         return;
     }
     if (header->protocol_version != STP_PROTOCOL_VERSION) {
@@ -581,6 +587,7 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
         if (bpdu_size < sizeof(struct stp_config_bpdu)) {
             VLOG_WARN("%s: received config BPDU with invalid size %zu",
                       stp->name, bpdu_size);
+            p->error_count++;
             return;
         }
         stp_received_config_bpdu(stp, p, bpdu);
@@ -590,6 +597,7 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
         if (bpdu_size != sizeof(struct stp_tcn_bpdu)) {
             VLOG_WARN("%s: received TCN BPDU with invalid size %zu",
                       stp->name, bpdu_size);
+            p->error_count++;
             return;
         }
         stp_received_tcn_bpdu(stp, p);
@@ -598,8 +606,10 @@ stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
     default:
         VLOG_WARN("%s: received BPDU of unexpected type %"PRIu8,
                   stp->name, header->bpdu_type);
+        p->error_count++;
         return;
     }
+    p->rx_count++;
 }
 
 /* Returns the STP entity in which 'p' is nested. */
@@ -667,6 +677,15 @@ stp_port_get_role(const struct stp_port *p)
     }
 }
 
+/* Retrieves BPDU transmit and receive counts for 'p'. */
+void stp_port_get_counts(const struct stp_port *p,
+                         int *tx_count, int *rx_count, int *error_count)
+{
+    *tx_count = p->tx_count;
+    *rx_count = p->rx_count;
+    *error_count = p->error_count;
+}
+
 /* Disables STP on port 'p'. */
 void
 stp_port_disable(struct stp_port *p)
@@ -1185,6 +1204,7 @@ stp_initialize_port(struct stp_port *p, enum stp_state state)
     stp_stop_timer(&p->message_age_timer);
     stp_stop_timer(&p->forward_delay_timer);
     stp_stop_timer(&p->hold_timer);
+    p->tx_count = p->rx_count = p->error_count = 0;
 }
 
 static void
@@ -1299,4 +1319,5 @@ stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
     llc->llc_cntl = STP_LLC_CNTL;
 
     p->stp->send_bpdu(pkt, stp_port_no(p), p->stp->aux);
+    p->tx_count++;
 }
index 54f7f5b4527a75bcab3f4e0ca63804a5c16103d7..ec29d9a77a9954d8dd54b4ee5bf5cc87828a54e9 100644 (file)
--- a/lib/stp.h
+++ b/lib/stp.h
@@ -136,6 +136,8 @@ int stp_port_no(const struct stp_port *);
 int stp_port_get_id(const struct stp_port *);
 enum stp_state stp_port_get_state(const struct stp_port *);
 enum stp_role stp_port_get_role(const struct stp_port *);
+void stp_port_get_counts(const struct stp_port *,
+                         int *tx_count, int *rx_count, int *error_count);
 void stp_port_enable(struct stp_port *);
 void stp_port_disable(struct stp_port *);
 void stp_port_set_priority(struct stp_port *, uint8_t new_priority);
index d525d4e7ddb74ecfc7b42e1d5a6da0a0f35c6e62..4cc720667d143dff167fd4ae42bdcb8c95fbe46b 100644 (file)
@@ -1105,6 +1105,7 @@ get_stp_port_status(struct ofport *ofport_,
     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;
 }
index 4b37bd78ffc7a732e0eec6e74d991e57fac4714f..eed4e5083e6deeeca3e946339a4a78c79dba0b69 100644 (file)
@@ -94,6 +94,9 @@ struct ofproto_port_stp_status {
     enum stp_state state;
     unsigned int sec_in_state;
     enum stp_role role;
+    int tx_count;               /* Number of BPDUs transmitted. */
+    int rx_count;               /* Number of valid BPDUs received. */
+    int error_count;            /* Number of bad BPDUs received. */
 };
 
 /* How the switch should act if the controller cannot be contacted. */
index 5100b7870bf8961ca6477ceaf7168bf309048df8..6a25b9590bac104cb1bc695d691713f7e024aea6 100644 (file)
@@ -240,6 +240,7 @@ bridge_init(const char *remote)
     ovsdb_idl_omit(idl, &ovsrec_bridge_col_external_ids);
 
     ovsdb_idl_omit_alert(idl, &ovsrec_port_col_status);
+    ovsdb_idl_omit_alert(idl, &ovsrec_port_col_statistics);
     ovsdb_idl_omit(idl, &ovsrec_port_col_external_ids);
     ovsdb_idl_omit(idl, &ovsrec_port_col_fake_bridge);
 
@@ -1598,7 +1599,9 @@ port_refresh_stp_status(struct port *port)
     struct ofproto *ofproto = port->bridge->ofproto;
     struct iface *iface;
     struct ofproto_port_stp_status status;
-    char *keys[4], *values[4];
+    char *keys[4];
+    char *str_values[4];
+    int64_t int_values[3];
     size_t i;
 
     if (port_is_synthetic(port)) {
@@ -1619,23 +1622,37 @@ port_refresh_stp_status(struct port *port)
 
     if (!status.enabled) {
         ovsrec_port_set_status(port->cfg, NULL, NULL, 0);
+        ovsrec_port_set_statistics(port->cfg, NULL, NULL, 0);
         return;
     }
 
-    keys[0]  = "stp_port_id";
-    values[0] = xasprintf(STP_PORT_ID_FMT, status.port_id);
+    /* Set Status column. */
+    keys[0] = "stp_port_id";
+    str_values[0] = xasprintf(STP_PORT_ID_FMT, status.port_id);
     keys[1] = "stp_state";
-    values[1] = xstrdup(stp_state_name(status.state));
+    str_values[1] = xstrdup(stp_state_name(status.state));
     keys[2] = "stp_sec_in_state";
-    values[2] = xasprintf("%u", status.sec_in_state);
+    str_values[2] = xasprintf("%u", status.sec_in_state);
     keys[3] = "stp_role";
-    values[3] = xstrdup(stp_role_name(status.role));
+    str_values[3] = xstrdup(stp_role_name(status.role));
 
-    ovsrec_port_set_status(port->cfg, keys, values, ARRAY_SIZE(values));
+    ovsrec_port_set_status(port->cfg, keys, str_values,
+                           ARRAY_SIZE(str_values));
 
-    for (i = 0; i < ARRAY_SIZE(values); i++) {
-        free(values[i]);
+    for (i = 0; i < ARRAY_SIZE(str_values); i++) {
+        free(str_values[i]);
     }
+
+    /* Set Statistics column. */
+    keys[0] = "stp_tx_count";
+    int_values[0] = status.tx_count;
+    keys[1] = "stp_rx_count";
+    int_values[1] = status.rx_count;
+    keys[2] = "stp_error_count";
+    int_values[2] = status.error_count;
+
+    ovsrec_port_set_statistics(port->cfg, keys, int_values,
+                               ARRAY_SIZE(int_values));
 }
 
 static bool
index 3a9c51fb09acbc1d96b22c0c3dada25727f8f7a4..19c59227cc3463c86353fc68898b9ca20c76c957 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "6.2.0",
- "cksum": "145151998 15203",
+ "version": "6.3.0",
+ "cksum": "1659474737 15341",
  "tables": {
    "Open_vSwitch": {
      "columns": {
        "status": {
          "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"},
          "ephemeral": true},
+       "statistics": {
+         "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"},
+         "ephemeral": true},
        "other_config": {
          "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
        "external_ids": {
index 80d9cdccf9c3334f97c5d1c59e5ddf111278ac6f..7d2a72a732813cb78b358befe4b57a5c81274510 100644 (file)
       </column>
     </group>
 
+    <group title="Port Statistics">
+      <p>
+        Key-value pairs that report port statistics.
+      </p>
+      <group title="Statistics: STP transmit and receive counters">
+        <column name="statistics" key="stp_tx_count">
+          Number of STP BPDUs sent on this port by the spanning
+          tree library.
+        </column>
+        <column name="statistics" key="stp_rx_count">
+          Number of STP BPDUs received on this port and accepted by the
+          spanning tree library.
+        </column>
+        <column name="statistics" key="stp_error_count">
+          Number of bad STP BPDUs received on this port.  Bad BPDUs
+          include runt packets and those with an unexpected protocol ID.
+        </column>
+      </group>
+    </group>
+
     <group title="Common Columns">
       The overall purpose of these columns is described under <code>Common
       Columns</code> at the beginning of this document.