vconn: New functions for validating and iterating over OpenFlow actions.
authorBen Pfaff <blp@nicira.com>
Mon, 2 Mar 2009 19:12:06 +0000 (11:12 -0800)
committerBen Pfaff <blp@nicira.com>
Mon, 2 Mar 2009 21:42:06 +0000 (13:42 -0800)
lib/vconn.c
lib/vconn.h
udatapath/datapath.c
udatapath/dp_act.c
udatapath/dp_act.h

index c7342d249596e6025c46eabcb9daa551b91ac94a..cf527ddf9e1377f8d3ce2481aa5e2917fafb9f66 100644 (file)
@@ -44,7 +44,9 @@
 #include "flow.h"
 #include "ofp-print.h"
 #include "ofpbuf.h"
+#include "openflow/nicira-ext.h"
 #include "openflow/openflow.h"
+#include "packets.h"
 #include "poll-loop.h"
 #include "random.h"
 #include "util.h"
@@ -1177,6 +1179,173 @@ flow_stats_next(struct flow_stats_iterator *iter)
     return fs;
 }
 
+/* Alignment of ofp_actions. */
+#define ACTION_ALIGNMENT 8
+
+static int
+check_action_exact_len(const union ofp_action *a, unsigned int len,
+                       unsigned int required_len)
+{
+    if (len != required_len) {
+        VLOG_DBG_RL(&bad_ofmsg_rl,
+                    "action %u has invalid length %"PRIu16" (must be %u)\n",
+                    a->type, ntohs(a->header.len), required_len);
+        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+    }
+    return 0;
+}
+
+static int
+check_action_port(int port)
+{
+    switch (port) {
+    case OFPP_IN_PORT:
+    case OFPP_FLOOD:
+    case OFPP_ALL:
+    case OFPP_CONTROLLER:
+    case OFPP_LOCAL:
+        return 0;
+
+    case OFPP_TABLE:
+    case OFPP_NORMAL:
+        VLOG_WARN_RL(&bad_ofmsg_rl, "port %x not yet implemented", port);
+        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
+
+    default:
+        if (port >= 0 && port < OFPP_MAX) {
+            return 0;
+        }
+        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown output port %x", port);
+        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
+    }
+}
+
+static int
+check_nicira_action(const union ofp_action *a, unsigned int len)
+{
+    const struct nx_action_header *nah;
+    int error;
+
+    if (len < 16) {
+        VLOG_DBG_RL(&bad_ofmsg_rl,
+                    "Nicira vendor action only %u bytes", len);
+        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+    }
+    nah = (const struct nx_action_header *) a;
+
+    switch (ntohs(nah->subtype)) {
+    case NXAST_SNAT:
+        error = check_action_exact_len(a, len, 16);
+        if (error) {
+            return error;
+        }
+        return check_action_port(ntohs(((struct nx_action_snat *) nah)->port));
+    default:
+        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
+    }
+}
+
+static int
+check_action(const union ofp_action *a, unsigned int len)
+{
+    int error;
+
+    switch (a->type) {
+    case OFPAT_OUTPUT:
+        error = check_action_port(ntohs(a->output.port));
+        if (error) {
+            return error;
+        }
+        return 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_TP_SRC:
+    case OFPAT_SET_TP_DST:
+        return check_action_exact_len(a, len, 8);
+
+    case OFPAT_SET_DL_SRC:
+    case OFPAT_SET_DL_DST:
+        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;
+
+    default:
+        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16, a->type);
+        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE);
+    }
+
+    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);
+    }
+    return 0;
+}
+
+int
+validate_actions(const union ofp_action *actions, size_t n_actions)
+{
+    const union ofp_action *a;
+
+    for (a = actions; a < &actions[n_actions]; ) {
+        unsigned int len = ntohs(a->header.len);
+        unsigned int n_slots = len / ACTION_ALIGNMENT;
+        unsigned int slots_left = &actions[n_actions] - a;
+        int error;
+
+        if (n_slots > slots_left) {
+            VLOG_DBG_RL(&bad_ofmsg_rl,
+                        "action requires %u slots but only %td remain",
+                        n_slots, slots_left);
+            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+        }
+        error = check_action(a, len);
+        if (error) {
+            return error;
+        }
+        a += n_slots;
+    }
+    return 0;
+}
+
+/* The set of actions must either come from a trusted source or have been
+ * previously validated with validate_actions(). */
+const union ofp_action *
+actions_first(struct actions_iterator *iter,
+              const union ofp_action *oa, size_t n_actions)
+{
+    iter->pos = oa;
+    iter->end = oa + n_actions;
+    return actions_next(iter);
+}
+
+const union ofp_action *
+actions_next(struct actions_iterator *iter)
+{
+    if (iter->pos < iter->end) {
+        const union ofp_action *a = iter->pos;
+        unsigned int len = ntohs(a->header.len);
+        iter->pos += len / ACTION_ALIGNMENT;
+        return a;
+    } else {
+        return NULL;
+    }
+}
+
 void
 vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
            uint32_t ip, const char *name, bool reconnectable)
index d521a8be320b2bc2a488ea683956709fbaa52bfc..3c7a9a4ac968a5bc1eed48ebf10c5c9795007ad4 100644 (file)
@@ -34,6 +34,7 @@
 #ifndef VCONN_H
 #define VCONN_H 1
 
+#include <assert.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -120,4 +121,20 @@ const struct ofp_flow_stats *flow_stats_first(struct flow_stats_iterator *,
                                               const struct ofp_stats_reply *);
 const struct ofp_flow_stats *flow_stats_next(struct flow_stats_iterator *);
 
+struct actions_iterator {
+    const union ofp_action *pos, *end;
+};
+const union ofp_action *actions_first(struct actions_iterator *,
+                                      const union ofp_action *,
+                                      size_t n_actions);
+const union ofp_action *actions_next(struct actions_iterator *);
+int validate_actions(const union ofp_action *, size_t n_actions);
+
+static inline int
+ofp_mkerr(uint16_t type, uint16_t code)
+{
+    assert(type > 0 && type <= 0x7fff);
+    return (type << 16) | code;
+}
+
 #endif /* vconn.h */
index d46fd128e46bd9baf27d598845f16e13bfe290c6..e5e11d54b7e83994378256dbda172d4c12b59a97 100644 (file)
@@ -928,7 +928,7 @@ recv_packet_out(struct datapath *dp, const struct sender *sender,
  
     flow_extract(buffer, ntohs(opo->in_port), &key.flow);
 
-    v_code = validate_actions(dp, &key, opo->actions, actions_len);
+    v_code = dp_validate_actions(dp, &key, opo->actions, actions_len);
     if (v_code != ACT_VALIDATION_OK) {
         dp_send_error_msg(dp, sender, OFPET_BAD_ACTION, v_code,
                   msg, ntohs(opo->header.length));
@@ -971,7 +971,7 @@ add_flow(struct datapath *dp, const struct sender *sender,
 
     flow_extract_match(&flow->key, &ofm->match);
 
-    v_code = validate_actions(dp, &flow->key, ofm->actions, actions_len);
+    v_code = dp_validate_actions(dp, &flow->key, ofm->actions, actions_len);
     if (v_code != ACT_VALIDATION_OK) {
         dp_send_error_msg(dp, sender, OFPET_BAD_ACTION, v_code,
                   ofm, ntohs(ofm->header.length));
@@ -1038,7 +1038,7 @@ mod_flow(struct datapath *dp, const struct sender *sender,
  
     actions_len = ntohs(ofm->header.length) - sizeof *ofm;
  
-    v_code = validate_actions(dp, &key, ofm->actions, actions_len);
+    v_code = dp_validate_actions(dp, &key, ofm->actions, actions_len);
     if (v_code != ACT_VALIDATION_OK) {
         dp_send_error_msg(dp, sender, OFPET_BAD_ACTION, v_code,
                   ofm, ntohs(ofm->header.length));
index 8f7f9b63f37e7a5f97e965a28f575afc245bbd08..2b2115eb8778345ae43909cb19a2bd806868528c 100644 (file)
@@ -347,7 +347,7 @@ validate_vendor(struct datapath *dp, const struct sw_flow_key *key,
  * OFPET_BAD_ACTION error type is returned.  If the action list validates, 
  * ACT_VALIDATION_OK is returned. */
 uint16_t 
-validate_actions(struct datapath *dp, const struct sw_flow_key *key,
+dp_validate_actions(struct datapath *dp, const struct sw_flow_key *key,
         const struct ofp_action_header *actions, size_t actions_len)
 {
     uint8_t *p = (uint8_t *)actions;
index e0181fadcc3721036e77510b41ee06b1d1d84ae8..daa8b270201dedd8b24ff20e3b808d48ed2c8380 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
  * Junior University
  * 
  * We are making the OpenFlow specification and associated documentation
@@ -40,7 +40,7 @@
 
 #define ACT_VALIDATION_OK ((uint16_t)-1)
 
-uint16_t validate_actions(struct datapath *, const struct sw_flow_key *,
+uint16_t dp_validate_actions(struct datapath *, const struct sw_flow_key *,
                const struct ofp_action_header *, size_t);
 void execute_actions(struct datapath *, struct ofpbuf *,
                struct sw_flow_key *, const struct ofp_action_header *,