ofproto: Match on IP ToS/DSCP bits (OpenFlow 1.0)
[sliver-openvswitch.git] / lib / vconn.c
index b484051..8d25972 100644 (file)
@@ -1,17 +1,17 @@
 /*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
  *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
  *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 #include <config.h>
@@ -128,23 +128,23 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
     printf("\n");
     if (active) {
         printf("Active OpenFlow connection methods:\n");
-        printf("  tcp:HOST[:PORT]         "
-               "PORT (default: %d) on remote TCP HOST\n", OFP_TCP_PORT);
+        printf("  tcp:IP[:PORT]         "
+               "PORT (default: %d) at remote IP\n", OFP_TCP_PORT);
 #ifdef HAVE_OPENSSL
-        printf("  ssl:HOST[:PORT]         "
-               "SSL PORT (default: %d) on remote HOST\n", OFP_SSL_PORT);
+        printf("  ssl:IP[:PORT]         "
+               "SSL PORT (default: %d) at remote IP\n", OFP_SSL_PORT);
 #endif
         printf("  unix:FILE               Unix domain socket named FILE\n");
     }
 
     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              "
@@ -251,9 +251,34 @@ vconn_get_name(const struct vconn *vconn)
 /* Returns the IP address of the peer, or 0 if the peer is not connected over
  * an IP-based protocol or if its IP address is not yet known. */
 uint32_t
-vconn_get_ip(const struct vconn *vconn) 
+vconn_get_remote_ip(const struct vconn *vconn) 
+{
+    return vconn->remote_ip;
+}
+
+/* Returns the transport port of the peer, or 0 if the connection does not 
+ * contain a port or if the port is not yet known. */
+uint16_t
+vconn_get_remote_port(const struct vconn *vconn) 
+{
+    return vconn->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 
+ * yet known. */
+uint32_t
+vconn_get_local_ip(const struct vconn *vconn) 
+{
+    return vconn->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 yet known. */
+uint16_t
+vconn_get_local_port(const struct vconn *vconn) 
 {
-    return vconn->ip;
+    return vconn->local_port;
 }
 
 static void
@@ -834,16 +859,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);
@@ -886,6 +914,28 @@ make_add_simple_flow(const flow_t *flow,
     return buffer;
 }
 
+struct ofpbuf *
+make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason,
+               const struct ofpbuf *payload, int max_send_len)
+{
+    struct ofp_packet_in *opi;
+    struct ofpbuf *buf;
+    int send_len;
+
+    send_len = MIN(max_send_len, payload->size);
+    buf = ofpbuf_new(sizeof *opi + send_len);
+    opi = put_openflow_xid(offsetof(struct ofp_packet_in, data),
+                           OFPT_PACKET_IN, 0, buf);
+    opi->buffer_id = htonl(buffer_id);
+    opi->total_len = htons(payload->size);
+    opi->in_port = htons(in_port);
+    opi->reason = reason;
+    ofpbuf_put(buf, payload->data, send_len);
+    update_openflow_length(buf);
+
+    return buf;
+}
+
 struct ofpbuf *
 make_packet_out(const struct ofpbuf *packet, uint32_t buffer_id,
                 uint16_t in_port,
@@ -995,10 +1045,10 @@ check_ofp_message(const struct ofp_header *msg, uint8_t type, size_t size)
     if (got_size != size) {
         char *type_name = ofp_message_type_to_string(type);
         VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "received %s message of length %"PRIu16" (expected %zu)",
+                     "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;
@@ -1030,22 +1080,22 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type,
     got_size = ntohs(msg->length);
     if (got_size < min_size) {
         char *type_name = ofp_message_type_to_string(type);
-        VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %"PRIu16" "
+        VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %zu "
                      "(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);
         VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "received %s message of bad length %"PRIu16": the "
+                     "received %s message of bad length %zu: the "
                      "excess over %zu (%zu) is not evenly divisible by %zu "
                      "(remainder is %zu)",
                      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;
@@ -1072,16 +1122,16 @@ check_ofp_packet_out(const struct ofp_header *oh, struct ofpbuf *data,
 
     actions_len = ntohs(opo->actions_len);
     if (actions_len > extra) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %zu bytes of actions "
+        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 %zu bytes of actions, "
+        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);
@@ -1205,7 +1255,7 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports)
 {
     int error;
 
-    switch (a->type) {
+    switch (ntohs(a->type)) {
     case OFPAT_OUTPUT:
         error = check_action_port(ntohs(a->output.port), max_ports);
         if (error) {
@@ -1218,6 +1268,7 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports)
     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);
@@ -1235,7 +1286,8 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports)
         break;
 
     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);
     }
 
@@ -1265,7 +1317,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
 
         if (n_slots > slots_left) {
             VLOG_DBG_RL(&bad_ofmsg_rl,
-                        "action requires %u slots but only %td remain",
+                        "action requires %u slots but only %u remain",
                         n_slots, slots_left);
             return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
         }
@@ -1313,7 +1365,7 @@ normalize_match(struct ofp_match *m)
     if (wc & OFPFW_DL_TYPE) {
         m->dl_type = 0;
 
-        /* Can't sensibly m on network or transport headers if the
+        /* Can't sensibly match on network or transport headers if the
          * data link type is unknown. */
         wc |= OFPFW_NW | OFPFW_TP;
         m->nw_src = m->nw_dst = m->nw_proto = 0;
@@ -1322,7 +1374,7 @@ normalize_match(struct ofp_match *m)
         if (wc & OFPFW_NW_PROTO) {
             m->nw_proto = 0;
 
-            /* Can't sensibly m on transport headers if the network
+            /* Can't sensibly match on transport headers if the network
              * protocol is unknown. */
             wc |= OFPFW_TP;
             m->tp_src = m->tp_dst = 0;
@@ -1337,7 +1389,7 @@ normalize_match(struct ofp_match *m)
             }
         } else {
             /* Transport layer fields will always be extracted as zeros, so we
-             * can do an exact-m on those values.  */
+             * can do an exact-match on those values.  */
             wc &= ~OFPFW_TP;
             m->tp_src = m->tp_dst = 0;
         }
@@ -1347,9 +1399,20 @@ 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-m on those values. */
+         * zeros, so we can do an exact-match on those values. */
         wc &= ~(OFPFW_NW | OFPFW_TP);
         m->nw_proto = m->nw_src = m->nw_dst = 0;
         m->tp_src = m->tp_dst = 0;
@@ -1363,9 +1426,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,
-           uint32_t ip, const char *name, bool reconnectable)
+           const char *name)
 {
     vconn->class = class;
     vconn->state = (connect_status == EAGAIN ? VCS_CONNECTING
@@ -1374,9 +1454,35 @@ vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
     vconn->error = connect_status;
     vconn->version = -1;
     vconn->min_version = -1;
-    vconn->ip = ip;
+    vconn->remote_ip = 0;
+    vconn->remote_port = 0;
+    vconn->local_ip = 0;
+    vconn->local_port = 0;
     vconn->name = xstrdup(name);
-    vconn->reconnectable = reconnectable;
+}
+
+void
+vconn_set_remote_ip(struct vconn *vconn, uint32_t ip)
+{
+    vconn->remote_ip = ip;
+}
+
+void
+vconn_set_remote_port(struct vconn *vconn, uint16_t port)
+{
+    vconn->remote_port = port;
+}
+
+void 
+vconn_set_local_ip(struct vconn *vconn, uint32_t ip)
+{
+    vconn->local_ip = ip;
+}
+
+void 
+vconn_set_local_port(struct vconn *vconn, uint16_t port)
+{
+    vconn->local_port = port;
 }
 
 void