patch: Remove veth driver.
[sliver-openvswitch.git] / lib / vconn.c
index 922198a..80ba471 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
 #include <string.h>
 #include "coverage.h"
 #include "dynamic-string.h"
+#include "fatal-signal.h"
 #include "flow.h"
 #include "ofp-print.h"
 #include "ofpbuf.h"
@@ -90,7 +91,8 @@ check_vconn_classes(void)
         struct vconn_class *class = vconn_classes[i];
         assert(class->name != NULL);
         assert(class->open != NULL);
-        if (class->close || class->recv || class->send || class->wait) {
+        if (class->close || class->recv || class->send
+            || class->run || class->run_wait || class->wait) {
             assert(class->close != NULL);
             assert(class->recv != NULL);
             assert(class->send != NULL);
@@ -119,7 +121,7 @@ check_vconn_classes(void)
  * connection methods supported by the vconn.  If 'bootstrap' is true, also
  * advertises options to bootstrap the CA certificate. */
 void
-vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
+vconn_usage(bool active, bool passive, bool bootstrap OVS_UNUSED)
 {
     /* Really this should be implemented via callbacks into the vconn
      * providers, but that seems too heavy-weight to bother with at the
@@ -139,12 +141,12 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
 
     if (passive) {
         printf("Passive OpenFlow connection methods:\n");
-        printf("  ptcp:[PORT]             "
-               "listen to TCP PORT (default: %d)\n",
+        printf("  ptcp:[PORT][:IP]        "
+               "listen to TCP PORT (default: %d) on IP\n",
                OFP_TCP_PORT);
 #ifdef HAVE_OPENSSL
-        printf("  pssl:[PORT]             "
-               "listen for SSL on PORT (default: %d)\n",
+        printf("  pssl:[PORT][:IP]        "
+               "listen for SSL on PORT (default: %d) on IP\n",
                OFP_SSL_PORT);
 #endif
         printf("  punix:FILE              "
@@ -163,6 +165,42 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
 #endif
 }
 
+/* Given 'name', a connection name in the form "TYPE:ARGS", stores the class
+ * named "TYPE" into '*classp' and returns 0.  Returns EAFNOSUPPORT and stores
+ * a null pointer into '*classp' if 'name' is in the wrong form or if no such
+ * class exists. */
+static int
+vconn_lookup_class(const char *name, struct vconn_class **classp)
+{
+    size_t prefix_len;
+
+    prefix_len = strcspn(name, ":");
+    if (name[prefix_len] != '\0') {
+        size_t i;
+
+        for (i = 0; i < ARRAY_SIZE(vconn_classes); i++) {
+            struct vconn_class *class = vconn_classes[i];
+            if (strlen(class->name) == prefix_len
+                && !memcmp(class->name, name, prefix_len)) {
+                *classp = class;
+                return 0;
+            }
+        }
+    }
+
+    *classp = NULL;
+    return EAFNOSUPPORT;
+}
+
+/* Returns 0 if 'name' is a connection name in the form "TYPE:ARGS" and TYPE is
+ * a supported connection type, otherwise EAFNOSUPPORT.  */
+int
+vconn_verify_name(const char *name)
+{
+    struct vconn_class *class;
+    return vconn_lookup_class(name, &class);
+}
+
 /* Attempts to connect to an OpenFlow device.  'name' is a connection name in
  * the form "TYPE:ARGS", where TYPE is an active vconn class's name and ARGS
  * are vconn class-specific.
@@ -177,35 +215,57 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
 int
 vconn_open(const char *name, int min_version, struct vconn **vconnp)
 {
-    size_t prefix_len;
-    size_t i;
+    struct vconn_class *class;
+    struct vconn *vconn;
+    char *suffix_copy;
+    int error;
 
     COVERAGE_INC(vconn_open);
     check_vconn_classes();
 
+    /* Look up the class. */
+    error = vconn_lookup_class(name, &class);
+    if (!class) {
+        goto error;
+    }
+
+    /* Call class's "open" function. */
+    suffix_copy = xstrdup(strchr(name, ':') + 1);
+    error = class->open(name, suffix_copy, &vconn);
+    free(suffix_copy);
+    if (error) {
+        goto error;
+    }
+
+    /* Success. */
+    assert(vconn->state != VCS_CONNECTING || vconn->class->connect);
+    vconn->min_version = min_version;
+    *vconnp = vconn;
+    return 0;
+
+error:
     *vconnp = NULL;
-    prefix_len = strcspn(name, ":");
-    if (prefix_len == strlen(name)) {
-        return EAFNOSUPPORT;
+    return error;
+}
+
+/* Allows 'vconn' to perform maintenance activities, such as flushing output
+ * buffers. */
+void
+vconn_run(struct vconn *vconn)
+{
+    if (vconn->class->run) {
+        (vconn->class->run)(vconn);
     }
-    for (i = 0; i < ARRAY_SIZE(vconn_classes); i++) {
-        struct vconn_class *class = vconn_classes[i];
-        if (strlen(class->name) == prefix_len
-            && !memcmp(class->name, name, prefix_len)) {
-            struct vconn *vconn;
-            char *suffix_copy = xstrdup(name + prefix_len + 1);
-            int retval = class->open(name, suffix_copy, &vconn);
-            free(suffix_copy);
-            if (!retval) {
-                assert(vconn->state != VCS_CONNECTING
-                       || vconn->class->connect);
-                vconn->min_version = min_version;
-                *vconnp = vconn;
-            }
-            return retval;
-        }
+}
+
+/* Arranges for the poll loop to wake up when 'vconn' needs to perform
+ * maintenance activities. */
+void
+vconn_run_wait(struct vconn *vconn)
+{
+    if (vconn->class->run_wait) {
+        (vconn->class->run_wait)(vconn);
     }
-    return EAFNOSUPPORT;
 }
 
 int
@@ -214,8 +274,12 @@ vconn_open_block(const char *name, int min_version, struct vconn **vconnp)
     struct vconn *vconn;
     int error;
 
+    fatal_signal_run();
+
     error = vconn_open(name, min_version, &vconn);
     while (error == EAGAIN) {
+        vconn_run(vconn);
+        vconn_run_wait(vconn);
         vconn_connect_wait(vconn);
         poll_block();
         error = vconn_connect(vconn);
@@ -364,7 +428,7 @@ vcs_recv_hello(struct vconn *vconn)
 
     if (retval != EAGAIN) {
         vconn->state = VCS_DISCONNECTED;
-        vconn->error = retval;
+        vconn->error = retval == EOF ? ECONNRESET : retval;
     }
 }
 
@@ -458,10 +522,7 @@ vconn_recv(struct vconn *vconn, struct ofpbuf **msgp)
 static int
 do_recv(struct vconn *vconn, struct ofpbuf **msgp)
 {
-    int retval;
-
-again:
-    retval = (vconn->class->recv)(vconn, msgp);
+    int retval = (vconn->class->recv)(vconn, msgp);
     if (!retval) {
         struct ofp_header *oh;
 
@@ -481,20 +542,6 @@ again:
             && oh->type != OFPT_VENDOR)
         {
             if (vconn->version < 0) {
-                if (oh->type == OFPT_PACKET_IN
-                    || oh->type == OFPT_FLOW_EXPIRED
-                    || oh->type == OFPT_PORT_STATUS) {
-                    /* The kernel datapath is stateless and doesn't really
-                     * support version negotiation, so it can end up sending
-                     * these asynchronous message before version negotiation
-                     * is complete.  Just ignore them.
-                     *
-                     * (After we move OFPT_PORT_STATUS messages from the kernel
-                     * into secchan, we won't get those here, since secchan
-                     * does proper version negotiation.) */
-                    ofpbuf_delete(*msgp);
-                    goto again;
-                }
                 VLOG_ERR_RL(&bad_ofmsg_rl,
                             "%s: received OpenFlow message type %"PRIu8" "
                             "before version negotiation complete",
@@ -563,7 +610,12 @@ int
 vconn_send_block(struct vconn *vconn, struct ofpbuf *msg)
 {
     int retval;
+
+    fatal_signal_run();
+
     while ((retval = vconn_send(vconn, msg)) == EAGAIN) {
+        vconn_run(vconn);
+        vconn_run_wait(vconn);
         vconn_send_wait(vconn);
         poll_block();
     }
@@ -575,7 +627,12 @@ int
 vconn_recv_block(struct vconn *vconn, struct ofpbuf **msgp)
 {
     int retval;
+
+    fatal_signal_run();
+
     while ((retval = vconn_recv(vconn, msgp)) == EAGAIN) {
+        vconn_run(vconn);
+        vconn_run_wait(vconn);
         vconn_recv_wait(vconn);
         poll_block();
     }
@@ -681,6 +738,42 @@ vconn_send_wait(struct vconn *vconn)
     vconn_wait(vconn, WAIT_SEND);
 }
 
+/* Given 'name', a connection name in the form "TYPE:ARGS", stores the class
+ * named "TYPE" into '*classp' and returns 0.  Returns EAFNOSUPPORT and stores
+ * a null pointer into '*classp' if 'name' is in the wrong form or if no such
+ * class exists. */
+static int
+pvconn_lookup_class(const char *name, struct pvconn_class **classp)
+{
+    size_t prefix_len;
+
+    prefix_len = strcspn(name, ":");
+    if (name[prefix_len] != '\0') {
+        size_t i;
+
+        for (i = 0; i < ARRAY_SIZE(pvconn_classes); i++) {
+            struct pvconn_class *class = pvconn_classes[i];
+            if (strlen(class->name) == prefix_len
+                && !memcmp(class->name, name, prefix_len)) {
+                *classp = class;
+                return 0;
+            }
+        }
+    }
+
+    *classp = NULL;
+    return EAFNOSUPPORT;
+}
+
+/* Returns 0 if 'name' is a connection name in the form "TYPE:ARGS" and TYPE is
+ * a supported connection type, otherwise EAFNOSUPPORT.  */
+int
+pvconn_verify_name(const char *name)
+{
+    struct pvconn_class *class;
+    return pvconn_lookup_class(name, &class);
+}
+
 /* Attempts to start listening for OpenFlow connections.  'name' is a
  * connection name in the form "TYPE:ARGS", where TYPE is an passive vconn
  * class's name and ARGS are vconn class-specific.
@@ -691,30 +784,34 @@ vconn_send_wait(struct vconn *vconn)
 int
 pvconn_open(const char *name, struct pvconn **pvconnp)
 {
-    size_t prefix_len;
-    size_t i;
+    struct pvconn_class *class;
+    struct pvconn *pvconn;
+    char *suffix_copy;
+    int error;
 
     check_vconn_classes();
 
-    *pvconnp = NULL;
-    prefix_len = strcspn(name, ":");
-    if (prefix_len == strlen(name)) {
-        return EAFNOSUPPORT;
+    /* Look up the class. */
+    error = pvconn_lookup_class(name, &class);
+    if (!class) {
+        goto error;
     }
-    for (i = 0; i < ARRAY_SIZE(pvconn_classes); i++) {
-        struct pvconn_class *class = pvconn_classes[i];
-        if (strlen(class->name) == prefix_len
-            && !memcmp(class->name, name, prefix_len)) {
-            char *suffix_copy = xstrdup(name + prefix_len + 1);
-            int retval = class->listen(name, suffix_copy, pvconnp);
-            free(suffix_copy);
-            if (retval) {
-                *pvconnp = NULL;
-            }
-            return retval;
-        }
+
+    /* Call class's "open" function. */
+    suffix_copy = xstrdup(strchr(name, ':') + 1);
+    error = class->listen(name, suffix_copy, &pvconn);
+    free(suffix_copy);
+    if (error) {
+        goto error;
     }
-    return EAFNOSUPPORT;
+
+    /* Success. */
+    *pvconnp = pvconn;
+    return 0;
+
+error:
+    *pvconnp = NULL;
+    return error;
 }
 
 /* Returns the name that was used to open 'pvconn'.  The caller must not
@@ -876,16 +973,19 @@ make_flow_mod(uint16_t command, const flow_t *flow, size_t actions_len)
     ofm->header.version = OFP_VERSION;
     ofm->header.type = OFPT_FLOW_MOD;
     ofm->header.length = htons(size);
+    ofm->cookie = 0;
     ofm->match.wildcards = htonl(0);
     ofm->match.in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL
                                : flow->in_port);
     memcpy(ofm->match.dl_src, flow->dl_src, sizeof ofm->match.dl_src);
     memcpy(ofm->match.dl_dst, flow->dl_dst, sizeof ofm->match.dl_dst);
     ofm->match.dl_vlan = flow->dl_vlan;
+    ofm->match.dl_vlan_pcp = flow->dl_vlan_pcp;
     ofm->match.dl_type = flow->dl_type;
     ofm->match.nw_src = flow->nw_src;
     ofm->match.nw_dst = flow->nw_dst;
     ofm->match.nw_proto = flow->nw_proto;
+    ofm->match.nw_tos = flow->nw_tos;
     ofm->match.tp_src = flow->tp_src;
     ofm->match.tp_dst = flow->tp_dst;
     ofm->command = htons(command);
@@ -1062,7 +1162,7 @@ check_ofp_message(const struct ofp_header *msg, uint8_t type, size_t size)
                      "received %s message of length %zu (expected %zu)",
                      type_name, got_size, size);
         free(type_name);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
     }
 
     return 0;
@@ -1098,7 +1198,7 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type,
                      "(expected at least %zu)",
                      type_name, got_size, min_size);
         free(type_name);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
     }
     if ((got_size - min_size) % array_elt_size) {
         char *type_name = ofp_message_type_to_string(type);
@@ -1109,7 +1209,7 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type,
                      type_name, got_size, min_size, got_size - min_size,
                      array_elt_size, (got_size - min_size) % array_elt_size);
         free(type_name);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
     }
     if (n_array_elts) {
         *n_array_elts = (got_size - min_size) / array_elt_size;
@@ -1139,13 +1239,13 @@ check_ofp_packet_out(const struct ofp_header *oh, struct ofpbuf *data,
         VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions "
                      "but message has room for only %zu bytes",
                      actions_len, extra);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
     }
     if (actions_len % sizeof(union ofp_action)) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions, "
                      "which is not a multiple of %zu",
                      actions_len, sizeof(union ofp_action));
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
     }
 
     n_actions = actions_len / sizeof(union ofp_action);
@@ -1258,6 +1358,7 @@ check_nicira_action(const union ofp_action *a, unsigned int len)
 
     switch (ntohs(nah->subtype)) {
     case NXAST_RESUBMIT:
+    case NXAST_SET_TUNNEL:
         return check_action_exact_len(a, len, 16);
     default:
         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
@@ -1269,30 +1370,17 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports)
 {
     int error;
 
-    if (!len) {
-        VLOG_DBG_RL(&bad_ofmsg_rl, "action has invalid length 0");
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
-    }
-
-    if (len % ACTION_ALIGNMENT) {
-        VLOG_DBG_RL(&bad_ofmsg_rl, "action length %u is not a multiple of %d",
-                    len, ACTION_ALIGNMENT);
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
-    }
-
     switch (ntohs(a->type)) {
     case OFPAT_OUTPUT:
         error = check_action_port(ntohs(a->output.port), max_ports);
-        if (error) {
-            return error;
-        }
-        return check_action_exact_len(a, len, 8);
+        return error ? error : check_action_exact_len(a, len, 8);
 
     case OFPAT_SET_VLAN_VID:
     case OFPAT_SET_VLAN_PCP:
     case OFPAT_STRIP_VLAN:
     case OFPAT_SET_NW_SRC:
     case OFPAT_SET_NW_DST:
+    case OFPAT_SET_NW_TOS:
     case OFPAT_SET_TP_SRC:
     case OFPAT_SET_TP_DST:
         return check_action_exact_len(a, len, 8);
@@ -1302,15 +1390,13 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports)
         return check_action_exact_len(a, len, 16);
 
     case OFPAT_VENDOR:
-        if (a->vendor.vendor == htonl(NX_VENDOR_ID)) {
-            return check_nicira_action(a, len);
-        } else {
-            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR);
-        }
-        break;
+        return (a->vendor.vendor == htonl(NX_VENDOR_ID)
+                ? check_nicira_action(a, len)
+                : ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR));
 
     default:
-        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16, a->type);
+        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16,
+                ntohs(a->type));
         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE);
     }
 }
@@ -1332,7 +1418,15 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
                         "action requires %u slots but only %u remain",
                         n_slots, slots_left);
             return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+        } else if (!len) {
+            VLOG_DBG_RL(&bad_ofmsg_rl, "action has invalid length 0");
+            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+        } else if (len % ACTION_ALIGNMENT) {
+            VLOG_DBG_RL(&bad_ofmsg_rl, "action length %u is not a multiple "
+                        "of %d", len, ACTION_ALIGNMENT);
+            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
         }
+
         error = check_action(a, len, max_ports);
         if (error) {
             return error;
@@ -1373,7 +1467,7 @@ normalize_match(struct ofp_match *m)
     enum { OFPFW_TP = OFPFW_TP_SRC | OFPFW_TP_DST };
     uint32_t wc;
 
-    wc = ntohl(m->wildcards) & OFPFW_ALL;
+    wc = ntohl(m->wildcards) & OVSFW_ALL;
     if (wc & OFPFW_DL_TYPE) {
         m->dl_type = 0;
 
@@ -1411,6 +1505,17 @@ normalize_match(struct ofp_match *m)
         if (wc & OFPFW_NW_DST_MASK) {
             m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT);
         }
+    } else if (m->dl_type == htons(ETH_TYPE_ARP)) {
+        if (wc & OFPFW_NW_PROTO) {
+            m->nw_proto = 0;
+        }
+        if (wc & OFPFW_NW_SRC_MASK) {
+            m->nw_src &= flow_nw_bits_to_mask(wc, OFPFW_NW_SRC_SHIFT);
+        }
+        if (wc & OFPFW_NW_DST_MASK) {
+            m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT);
+        }
+        m->tp_src = m->tp_dst = 0;
     } else {
         /* Network and transport layer fields will always be extracted as
          * zeros, so we can do an exact-match on those values. */
@@ -1427,9 +1532,26 @@ normalize_match(struct ofp_match *m)
     m->wildcards = htonl(wc);
 }
 
+/* Initializes 'vconn' as a new vconn named 'name', implemented via 'class'.
+ * The initial connection status, supplied as 'connect_status', is interpreted
+ * as follows:
+ *
+ *      - 0: 'vconn' is connected.  Its 'send' and 'recv' functions may be
+ *        called in the normal fashion.
+ *
+ *      - EAGAIN: 'vconn' is trying to complete a connection.  Its 'connect'
+ *        function should be called to complete the connection.
+ *
+ *      - Other positive errno values indicate that the connection failed with
+ *        the specified error.
+ *
+ * After calling this function, vconn_close() must be used to destroy 'vconn',
+ * otherwise resources will be leaked.
+ *
+ * The caller retains ownership of 'name'. */
 void
 vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
-           const char *name, bool reconnectable)
+           const char *name)
 {
     vconn->class = class;
     vconn->state = (connect_status == EAGAIN ? VCS_CONNECTING
@@ -1443,7 +1565,7 @@ vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
     vconn->local_ip = 0;
     vconn->local_port = 0;
     vconn->name = xstrdup(name);
-    vconn->reconnectable = reconnectable;
+    assert(vconn->state != VCS_CONNECTING || class->connect);
 }
 
 void