Implement OFPT_TABLE_STATUS Message.
[cascardo/ovs.git] / lib / rconn.c
index 96b3579..5706c3f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 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.
@@ -29,8 +29,8 @@
 #include "sat-math.h"
 #include "timeval.h"
 #include "util.h"
-#include "vconn.h"
-#include "vlog.h"
+#include "openvswitch/vconn.h"
+#include "openvswitch/vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(rconn);
 
@@ -39,12 +39,31 @@ COVERAGE_DEFINE(rconn_overflow);
 COVERAGE_DEFINE(rconn_queued);
 COVERAGE_DEFINE(rconn_sent);
 
+/* The connection states have the following meanings:
+ *
+ *    - S_VOID: No connection information is configured.
+ *
+ *    - S_BACKOFF: Waiting for a period of time before reconnecting.
+ *
+ *    - S_CONNECTING: A connection attempt is in progress and has not yet
+ *      succeeded or failed.
+ *
+ *    - S_ACTIVE: A connection has been established and appears to be healthy.
+ *
+ *    - S_IDLE: A connection has been established but has been idle for some
+ *      time.  An echo request has been sent, but no reply has yet been
+ *      received.
+ *
+ *    - S_DISCONNECTED: An unreliable connection has disconnected and cannot be
+ *      automatically retried.
+ */
 #define STATES                                  \
     STATE(VOID, 1 << 0)                         \
     STATE(BACKOFF, 1 << 1)                      \
     STATE(CONNECTING, 1 << 2)                   \
     STATE(ACTIVE, 1 << 3)                       \
-    STATE(IDLE, 1 << 4)
+    STATE(IDLE, 1 << 4)                         \
+    STATE(DISCONNECTED, 1 << 5)
 enum state {
 #define STATE(NAME, VALUE) S_##NAME = VALUE,
     STATES
@@ -76,14 +95,13 @@ struct rconn {
     char *target;               /* vconn name, passed to vconn_open(). */
     bool reliable;
 
-    struct list txq;            /* Contains "struct ofpbuf"s. */
+    struct ovs_list txq;        /* Contains "struct ofpbuf"s. */
 
     int backoff;
     int max_backoff;
     time_t backoff_deadline;
     time_t last_connected;
     time_t last_disconnected;
-    unsigned int packets_sent;
     unsigned int seqno;
     int last_error;
 
@@ -100,7 +118,6 @@ struct rconn {
 
     /* These values are simply for statistics reporting, not used directly by
      * anything internal to the rconn (or ofproto for that matter). */
-    unsigned int packets_received;
     unsigned int n_attempted_connections, n_successful_connections;
     time_t creation_time;
     unsigned long int total_time_connected;
@@ -115,22 +132,11 @@ struct rconn {
     int probe_interval;         /* Secs of inactivity before sending probe. */
     time_t last_activity;       /* Last time we saw some activity. */
 
-    /* When we create a vconn we obtain these values, to save them past the end
-     * of the vconn's lifetime.  Otherwise, in-band control will only allow
-     * traffic when a vconn is actually open, but it is nice to allow ARP to
-     * complete even between connection attempts, and it is also polite to
-     * allow traffic from other switches to go through to the controller
-     * whether or not we are connected.
-     *
-     * We don't cache the local port, because that changes from one connection
-     * attempt to the next. */
-    ovs_be32 local_ip, remote_ip;
-    ovs_be16 remote_port;
     uint8_t dscp;
 
     /* Messages sent or received are copied to the monitor connections. */
-#define MAX_MONITORS 8
-    struct vconn *monitors[8];
+#define MAXIMUM_MONITORS 8
+    struct vconn *monitors[MAXIMUM_MONITORS];
     size_t n_monitors;
 
     uint32_t allowed_versions;
@@ -254,12 +260,9 @@ rconn_create(int probe_interval, int max_backoff, uint8_t dscp,
     rc->last_disconnected = TIME_MIN;
     rc->seqno = 0;
 
-    rc->packets_sent = 0;
-
     rc->probably_admitted = false;
     rc->last_admitted = time_now();
 
-    rc->packets_received = 0;
     rc->n_attempted_connections = 0;
     rc->n_successful_connections = 0;
     rc->creation_time = time_now();
@@ -456,9 +459,6 @@ reconnect(struct rconn *rc)
     retval = vconn_open(rc->target, rc->allowed_versions, rc->dscp,
                         &rc->vconn);
     if (!retval) {
-        rc->remote_ip = vconn_get_remote_ip(rc->vconn);
-        rc->local_ip = vconn_get_local_ip(rc->vconn);
-        rc->remote_port = vconn_get_remote_port(rc->vconn);
         rc->backoff_deadline = time_now() + rc->backoff;
         state_transition(rc, S_CONNECTING);
     } else {
@@ -594,6 +594,20 @@ run_IDLE(struct rconn *rc)
     }
 }
 
+static unsigned int
+timeout_DISCONNECTED(const struct rconn *rc OVS_UNUSED)
+    OVS_REQUIRES(rc->mutex)
+{
+    return UINT_MAX;
+}
+
+static void
+run_DISCONNECTED(struct rconn *rc OVS_UNUSED)
+    OVS_REQUIRES(rc->mutex)
+{
+    /* Nothing to do. */
+}
+
 /* Performs whatever activities are necessary to maintain 'rc': if 'rc' is
  * disconnected, attempts to (re)connect, backing off as necessary; if 'rc' is
  * connected, attempts to send packets in the send queue, if any. */
@@ -606,7 +620,15 @@ rconn_run(struct rconn *rc)
 
     ovs_mutex_lock(&rc->mutex);
     if (rc->vconn) {
+        int error;
+
         vconn_run(rc->vconn);
+
+        error = vconn_get_status(rc->vconn);
+        if (error) {
+            report_error(rc, error);
+            disconnect(rc, error);
+        }
     }
     for (i = 0; i < rc->n_monitors; ) {
         struct ofpbuf *msg;
@@ -632,7 +654,7 @@ rconn_run(struct rconn *rc)
             STATES
 #undef STATE
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     } while (rc->state != old_state);
     ovs_mutex_unlock(&rc->mutex);
@@ -687,7 +709,6 @@ rconn_recv(struct rconn *rc)
                 rc->last_admitted = time_now();
             }
             rc->last_activity = time_now();
-            rc->packets_received++;
             if (rc->state == S_IDLE) {
                 state_transition(rc, S_ACTIVE);
             }
@@ -722,10 +743,14 @@ rconn_send__(struct rconn *rc, struct ofpbuf *b,
     if (rconn_is_connected(rc)) {
         COVERAGE_INC(rconn_queued);
         copy_to_monitor(rc, b);
-        b->private_p = counter;
+
         if (counter) {
             rconn_packet_counter_inc(counter, b->size);
         }
+
+        /* Reuse 'frame' as a private pointer while 'b' is in txq. */
+        b->header = counter;
+
         list_push_back(&rc->txq, &b->list_node);
 
         /* If the queue was empty before we added 'b', try to send some
@@ -800,15 +825,6 @@ rconn_send_with_limit(struct rconn *rc, struct ofpbuf *b,
     return error;
 }
 
-/* Returns the total number of packets successfully sent on the underlying
- * vconn.  A packet is not counted as sent while it is still queued in the
- * rconn, only when it has been successfuly passed to the vconn.  */
-unsigned int
-rconn_packets_sent(const struct rconn *rc)
-{
-    return rc->packets_sent;
-}
-
 /* Adds 'vconn' to 'rc' as a monitoring connection, to which all messages sent
  * and received on 'rconn' will be copied.  'rc' takes ownership of 'vconn'. */
 void
@@ -860,7 +876,7 @@ rconn_get_target(const struct rconn *rc)
 bool
 rconn_is_alive(const struct rconn *rconn)
 {
-    return rconn->state != S_VOID;
+    return rconn->state != S_VOID && rconn->state != S_DISCONNECTED;
 }
 
 /* Returns true if 'rconn' is connected, false otherwise. */
@@ -911,46 +927,6 @@ rconn_failure_duration(const struct rconn *rconn)
     return duration;
 }
 
-/* Returns the IP address of the peer, or 0 if the peer's IP address is not
- * known. */
-ovs_be32
-rconn_get_remote_ip(const struct rconn *rconn)
-{
-    return rconn->remote_ip;
-}
-
-/* Returns the transport port of the peer, or 0 if the peer's port is not
- * known. */
-ovs_be16
-rconn_get_remote_port(const struct rconn *rconn)
-{
-    return rconn->remote_port;
-}
-
-/* Returns the IP address used to connect to the peer, or 0 if the
- * connection is not an IP-based protocol or if its IP address is not
- * known. */
-ovs_be32
-rconn_get_local_ip(const struct rconn *rconn)
-{
-    return rconn->local_ip;
-}
-
-/* Returns the transport port used to connect to the peer, or 0 if the
- * connection does not contain a port or if the port is not known. */
-ovs_be16
-rconn_get_local_port(const struct rconn *rconn)
-    OVS_EXCLUDED(rconn->mutex)
-{
-    ovs_be16 port;
-
-    ovs_mutex_lock(&rconn->mutex);
-    port = rconn->vconn ? vconn_get_local_port(rconn->vconn) : 0;
-    ovs_mutex_unlock(&rconn->mutex);
-
-    return port;
-}
-
 static int
 rconn_get_version__(const struct rconn *rconn)
     OVS_REQUIRES(rconn->mutex)
@@ -973,14 +949,6 @@ rconn_get_version(const struct rconn *rconn)
     return version;
 }
 
-/* Returns the total number of packets successfully received by the underlying
- * vconn.  */
-unsigned int
-rconn_packets_received(const struct rconn *rc)
-{
-    return rc->packets_received;
-}
-
 /* Returns a string representing the internal state of 'rc'.  The caller must
  * not modify or free the string. */
 const char *
@@ -1127,10 +1095,7 @@ rconn_packet_counter_n_bytes(const struct rconn_packet_counter *c)
 }
 \f
 /* Set rc->target and rc->name to 'target' and 'name', respectively.  If 'name'
- * is null, 'target' is used.
- *
- * Also, clear out the cached IP address and port information, since changing
- * the target also likely changes these values. */
+ * is null, 'target' is used. */
 static void
 rconn_set_target__(struct rconn *rc, const char *target, const char *name)
     OVS_REQUIRES(rc->mutex)
@@ -1139,9 +1104,6 @@ rconn_set_target__(struct rconn *rc, const char *target, const char *name)
     rc->name = xstrdup(name ? name : target);
     free(rc->target);
     rc->target = xstrdup(target);
-    rc->local_ip = 0;
-    rc->remote_ip = 0;
-    rc->remote_port = 0;
 }
 
 /* Tries to send a packet from 'rc''s send buffer.  Returns 0 if successful,
@@ -1152,16 +1114,18 @@ try_send(struct rconn *rc)
 {
     struct ofpbuf *msg = ofpbuf_from_list(rc->txq.next);
     unsigned int n_bytes = msg->size;
-    struct rconn_packet_counter *counter = msg->private_p;
+    struct rconn_packet_counter *counter = msg->header;
     int retval;
 
     /* Eagerly remove 'msg' from the txq.  We can't remove it from the list
      * after sending, if sending is successful, because it is then owned by the
      * vconn, which might have freed it already. */
     list_remove(&msg->list_node);
+    msg->header = NULL;
 
     retval = vconn_send(rc->vconn, msg);
     if (retval) {
+        msg->header = counter;
         list_push_front(&rc->txq, &msg->list_node);
         if (retval != EAGAIN) {
             report_error(rc, retval);
@@ -1170,7 +1134,6 @@ try_send(struct rconn *rc)
         return retval;
     }
     COVERAGE_INC(rconn_sent);
-    rc->packets_sent++;
     if (counter) {
         rconn_packet_counter_dec(counter, n_bytes);
     }
@@ -1184,7 +1147,14 @@ static void
 report_error(struct rconn *rc, int error)
     OVS_REQUIRES(rc->mutex)
 {
-    if (error == EOF) {
+    /* On Windows, when a peer terminates without calling a closesocket()
+     * on socket fd, we get WSAECONNRESET. Don't print warning messages
+     * for that case. */
+    if (error == EOF
+#ifdef _WIN32
+        || error == WSAECONNRESET
+#endif
+        ) {
         /* If 'rc' isn't reliable, then we don't really expect this connection
          * to last forever anyway (probably it's a connection that we received
          * via accept()), so use DBG level to avoid cluttering the logs. */
@@ -1211,13 +1181,15 @@ disconnect(struct rconn *rc, int error)
     OVS_REQUIRES(rc->mutex)
 {
     rc->last_error = error;
+    if (rc->vconn) {
+        vconn_close(rc->vconn);
+        rc->vconn = NULL;
+    }
     if (rc->reliable) {
         time_t now = time_now();
 
         if (rc->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) {
             rc->last_disconnected = now;
-            vconn_close(rc->vconn);
-            rc->vconn = NULL;
             flush_queue(rc);
         }
 
@@ -1239,7 +1211,7 @@ disconnect(struct rconn *rc, int error)
         state_transition(rc, S_BACKOFF);
     } else {
         rc->last_disconnected = time_now();
-        rconn_disconnect__(rc);
+        state_transition(rc, S_DISCONNECTED);
     }
 }
 
@@ -1254,7 +1226,7 @@ flush_queue(struct rconn *rc)
     }
     while (!list_is_empty(&rc->txq)) {
         struct ofpbuf *b = ofpbuf_from_list(list_pop_front(&rc->txq));
-        struct rconn_packet_counter *counter = b->private_p;
+        struct rconn_packet_counter *counter = b->header;
         if (counter) {
             rconn_packet_counter_dec(counter, b->size);
         }
@@ -1280,7 +1252,7 @@ timeout(const struct rconn *rc)
         STATES
 #undef STATE
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1295,7 +1267,7 @@ static void
 state_transition(struct rconn *rc, enum state state)
     OVS_REQUIRES(rc->mutex)
 {
-    rc->seqno += (rc->state == S_ACTIVE) != (state == S_ACTIVE);
+    rc->seqno += is_connected_state(rc->state) != is_connected_state(state);
     if (is_connected_state(state) && !is_connected_state(rc->state)) {
         rc->probably_admitted = false;
     }
@@ -1349,6 +1321,17 @@ is_connected_state(enum state state)
     return (state & (S_ACTIVE | S_IDLE)) != 0;
 }
 
+/* When a switch initially connects to a controller, the controller may spend a
+ * little time examining the switch, looking at, for example, its datapath ID,
+ * before it decides whether it is willing to control that switch.  At that
+ * point, it either disconnects or starts controlling the switch.
+ *
+ * This function returns a guess to its caller about whether 'b' is OpenFlow
+ * message that indicates that the controller has decided to control the
+ * switch.  It returns false if the message is one that a controller typically
+ * uses to determine whether a switch is admissible, true if the message is one
+ * that would typically be used only after the controller has admitted the
+ * switch. */
 static bool
 is_admitted_msg(const struct ofpbuf *b)
 {
@@ -1370,7 +1353,6 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_GET_CONFIG_REQUEST:
     case OFPTYPE_GET_CONFIG_REPLY:
     case OFPTYPE_SET_CONFIG:
-        /* FIXME: Change the following once they are implemented: */
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
     case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_GET_ASYNC_REQUEST:
@@ -1383,6 +1365,8 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
+    case OFPTYPE_TABLE_DESC_REQUEST:
+    case OFPTYPE_TABLE_DESC_REPLY:
         return false;
 
     case OFPTYPE_PACKET_IN:
@@ -1418,6 +1402,9 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_METER_FEATURES_STATS_REPLY:
     case OFPTYPE_ROLE_REQUEST:
     case OFPTYPE_ROLE_REPLY:
+    case OFPTYPE_ROLE_STATUS:
+    case OFPTYPE_REQUESTFORWARD:
+    case OFPTYPE_TABLE_STATUS:
     case OFPTYPE_SET_FLOW_FORMAT:
     case OFPTYPE_FLOW_MOD_TABLE_ID:
     case OFPTYPE_SET_PACKET_IN_FORMAT:
@@ -1429,6 +1416,12 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_FLOW_MONITOR_CANCEL:
     case OFPTYPE_FLOW_MONITOR_PAUSED:
     case OFPTYPE_FLOW_MONITOR_RESUMED:
+    case OFPTYPE_BUNDLE_CONTROL:
+    case OFPTYPE_BUNDLE_ADD_MESSAGE:
+    case OFPTYPE_NXT_TLV_TABLE_MOD:
+    case OFPTYPE_NXT_TLV_TABLE_REQUEST:
+    case OFPTYPE_NXT_TLV_TABLE_REPLY:
+    case OFPTYPE_NXT_RESUME:
     default:
         return true;
     }