ofp-util: Work on decoding OF1.1 flow_mods.
[openvswitch] / lib / stp.c
index 94b9a4b5b993d17c5f0d1a8519f4262b011ceaa9..ee5c8bcde84527c4509d46231915e56d272eeb8a 100644 (file)
--- a/lib/stp.c
+++ b/lib/stp.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
 #include "byte-order.h"
 #include "ofpbuf.h"
 #include "packets.h"
+#include "unixctl.h"
 #include "util.h"
 #include "vlog.h"
 
@@ -93,10 +94,16 @@ 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;
 };
 
 struct stp {
+    struct list node;               /* Node in all_stps list. */
+
     /* Static bridge data. */
     char *name;                     /* Human-readable name for log messages. */
     stp_identifier bridge_id;       /* 8.5.3.7: This bridge. */
@@ -127,11 +134,14 @@ struct stp {
     struct stp_port ports[STP_MAX_PORTS];
 
     /* 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 *aux;
 };
 
+static struct list all_stps = LIST_INITIALIZER(&all_stps);
+
 #define FOR_EACH_ENABLED_PORT(PORT, STP)                        \
     for ((PORT) = stp_next_enabled_port((STP), (STP)->ports);   \
          (PORT);                                                \
@@ -188,13 +198,21 @@ static void stp_update_bridge_timers(struct stp *);
 
 static int clamp(int x, int min, int max);
 static int ms_to_timer(int ms);
-static int ms_to_timer_remainder(int ms);
 static int timer_to_ms(int timer);
 static void stp_start_timer(struct stp_timer *, int value);
 static void stp_stop_timer(struct stp_timer *);
 static bool stp_timer_expired(struct stp_timer *, int elapsed, int timeout);
 
 static void stp_send_bpdu(struct stp_port *, const void *, size_t);
+static void stp_unixctl_tcn(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);
+}
 
 /* Creates and returns a new STP instance that initially has no ports enabled.
  *
@@ -252,6 +270,7 @@ stp_create(const char *name, stp_identifier bridge_id,
         p->path_cost = 19;      /* Recommended default for 100 Mb/s link. */
         stp_initialize_port(p, STP_DISABLED);
     }
+    list_push_back(&all_stps, &stp->node);
     return stp;
 }
 
@@ -260,6 +279,7 @@ void
 stp_destroy(struct stp *stp)
 {
     if (stp) {
+        list_remove(&stp->node);
         free(stp->name);
         free(stp);
     }
@@ -277,7 +297,7 @@ stp_tick(struct stp *stp, int ms)
      * are called too frequently. */
     ms = clamp(ms, 0, INT_MAX - 1000) + stp->elapsed_remainder;
     elapsed = ms_to_timer(ms);
-    stp->elapsed_remainder = ms_to_timer_remainder(ms);
+    stp->elapsed_remainder = ms - timer_to_ms(elapsed);
     if (!elapsed) {
         return;
     }
@@ -445,6 +465,17 @@ stp_get_forward_delay(const struct stp *stp)
     return timer_to_ms(stp->bridge_forward_delay);
 }
 
+/* Returns true if something has happened to 'stp' which necessitates flushing
+ * the client's MAC learning table.  Calling this function resets 'stp' so that
+ * future calls will return false until flushing is required again. */
+bool
+stp_check_and_reset_fdb_flush(struct stp *stp)
+{
+    bool needs_flush = stp->fdb_needs_flush;
+    stp->fdb_needs_flush = false;
+    return needs_flush;
+}
+
 /* Returns the port in 'stp' with index 'port_no', which must be between 0 and
  * STP_MAX_PORTS. */
 struct stp_port *
@@ -562,6 +593,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 +601,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 +614,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 +624,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 +633,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 +704,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)
@@ -1022,6 +1068,8 @@ stp_set_port_state(struct stp_port *p, enum stp_state state)
 static void
 stp_topology_change_detection(struct stp *stp)
 {
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
     if (stp_is_root_bridge(stp)) {
         stp->topology_change = true;
         stp_start_timer(&stp->topology_change_timer, 0);
@@ -1029,7 +1077,9 @@ stp_topology_change_detection(struct stp *stp)
         stp_transmit_tcn(stp);
         stp_start_timer(&stp->tcn_timer, 0);
     }
+    stp->fdb_needs_flush = true;
     stp->topology_change_detected = true;
+    VLOG_INFO_RL(&rl, "%s: detected topology change.", stp->name);
 }
 
 static void
@@ -1077,6 +1127,9 @@ stp_received_config_bpdu(struct stp *stp, struct stp_port *p,
                 if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE_ACK) {
                     stp_topology_change_acknowledged(stp);
                 }
+                if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE) {
+                    stp->fdb_needs_flush = true;
+                }
             }
         } else if (stp_is_designated_port(p)) {
             stp_transmit_config(p);
@@ -1185,6 +1238,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
@@ -1233,14 +1287,6 @@ ms_to_timer(int ms)
     return ms * 0x100 / 1000;
 }
 
-/* Returns the number of leftover milliseconds when 'ms' is converted to STP
- * timer ticks. */
-static int
-ms_to_timer_remainder(int ms)
-{
-    return ms * 0x100 % 1000;
-}
-
 /* Returns the number of whole milliseconds in 'timer' STP timer ticks.  There
  * are 256 STP timer ticks per second. */
 static int
@@ -1299,4 +1345,43 @@ 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++;
+}
+\f
+/* Unixctl. */
+
+static struct stp *
+stp_find(const char *name)
+{
+    struct stp *stp;
+
+    LIST_FOR_EACH (stp, node, &all_stps) {
+        if (!strcmp(stp->name, name)) {
+            return stp;
+        }
+    }
+    return NULL;
+}
+
+static void
+stp_unixctl_tcn(struct unixctl_conn *conn, int argc,
+                const char *argv[], void *aux OVS_UNUSED)
+{
+    if (argc > 1) {
+        struct stp *stp = stp_find(argv[1]);
+
+        if (!stp) {
+            unixctl_command_reply_error(conn, "no such stp object");
+            return;
+        }
+        stp_topology_change_detection(stp);
+    } else {
+        struct stp *stp;
+
+        LIST_FOR_EACH (stp, node, &all_stps) {
+            stp_topology_change_detection(stp);
+        }
+    }
+
+    unixctl_command_reply(conn, "OK");
 }