OF11: push_vlan support
[openvswitch] / lib / ofp-actions.c
index 4a72e29cd5341e829b2f9115129b3e322c1dc728..170e796fabb4f8b02d187ad4d7a8db63deea7925 100644 (file)
@@ -135,6 +135,23 @@ controller_from_openflow(const struct nx_action_controller *nac,
     oc->reason = nac->reason;
 }
 
+static enum ofperr
+metadata_from_nxast(const struct nx_action_write_metadata *nawm,
+                    struct ofpbuf *out)
+{
+    struct ofpact_metadata *om;
+
+    if (!is_all_zeros(nawm->zeros, sizeof nawm->zeros)) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
+
+    om = ofpact_put_WRITE_METADATA(out);
+    om->metadata = nawm->metadata;
+    om->mask = nawm->mask;
+
+    return 0;
+}
+
 static void
 note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out)
 {
@@ -149,14 +166,14 @@ note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out)
 }
 
 static enum ofperr
-dec_ttl_from_openflow(struct ofpbuf *out)
+dec_ttl_from_openflow(struct ofpbuf *out, enum ofputil_action_code compat)
 {
     uint16_t id = 0;
     struct ofpact_cnt_ids *ids;
     enum ofperr error = 0;
 
     ids = ofpact_put_DEC_TTL(out);
-    ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
+    ids->ofpact.compat = compat;
     ids->n_controllers = 1;
     ofpbuf_put(out, &id, sizeof id);
     ids = out->l2;
@@ -276,6 +293,7 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
     const struct nx_action_set_queue *nasq;
     const struct nx_action_note *nan;
     const struct nx_action_set_tunnel64 *nast64;
+    const struct nx_action_write_metadata *nawm;
     struct ofpact_tunnel *tunnel;
     enum ofperr error = 0;
 
@@ -297,6 +315,11 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
         tunnel->tun_id = ntohl(nast->tun_id);
         break;
 
+    case OFPUTIL_NXAST_WRITE_METADATA:
+        nawm = (const struct nx_action_write_metadata *) a;
+        error = metadata_from_nxast(nawm, out);
+        break;
+
     case OFPUTIL_NXAST_SET_QUEUE:
         nasq = (const struct nx_action_set_queue *) a;
         ofpact_put_SET_QUEUE(out)->queue_id = ntohl(nasq->queue_id);
@@ -362,7 +385,7 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
         break;
 
     case OFPUTIL_NXAST_DEC_TTL:
-        error = dec_ttl_from_openflow(out);
+        error = dec_ttl_from_openflow(out, code);
         break;
 
     case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
@@ -567,6 +590,12 @@ ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len,
     }
 
     error = translate(actions, actions_len / OFP_ACTION_ALIGN, ofpacts);
+    if (error) {
+        ofpbuf_clear(ofpacts);
+        return error;
+    }
+
+    error = ofpacts_verify(ofpacts->data, ofpacts->size);
     if (error) {
         ofpbuf_clear(ofpacts);
     }
@@ -685,6 +714,19 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
         ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
         break;
 
+    case OFPUTIL_OFPAT11_PUSH_VLAN:
+        if (((const struct ofp11_action_push *)a)->ethertype !=
+            htons(ETH_TYPE_VLAN_8021Q)) {
+            /* TODO:XXX 802.1AD(QinQ) isn't supported at the moment */
+            return OFPERR_OFPET_BAD_ACTION;
+        }
+        ofpact_put_PUSH_VLAN(out);
+        break;
+
+    case OFPUTIL_OFPAT11_POP_VLAN:
+        ofpact_put_STRIP_VLAN(out);
+        break;
+
     case OFPUTIL_OFPAT11_SET_DL_SRC:
         memcpy(ofpact_put_SET_ETH_SRC(out)->mac,
                ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
@@ -695,6 +737,10 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
                ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
         break;
 
+    case OFPUTIL_OFPAT11_DEC_NW_TTL:
+        dec_ttl_from_openflow(out, code);
+        break;
+
     case OFPUTIL_OFPAT11_SET_NW_SRC:
         ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr;
         break;
@@ -964,7 +1010,17 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
         ofpact_put_CLEAR_ACTIONS(ofpacts);
     }
     /* TODO:XXX Write-Actions */
-    /* TODO:XXX Write-Metadata */
+    if (insts[OVSINST_OFPIT11_WRITE_METADATA]) {
+        const struct ofp11_instruction_write_metadata *oiwm;
+        struct ofpact_metadata *om;
+
+        oiwm = (const struct ofp11_instruction_write_metadata *)
+            insts[OVSINST_OFPIT11_WRITE_METADATA];
+
+        om = ofpact_put_WRITE_METADATA(ofpacts);
+        om->metadata = oiwm->metadata;
+        om->mask = oiwm->metadata_mask;
+    }
     if (insts[OVSINST_OFPIT11_GOTO_TABLE]) {
         const struct ofp11_instruction_goto_table *oigt;
         struct ofpact_goto_table *ogt;
@@ -975,12 +1031,12 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
         ogt->table_id = oigt->table_id;
     }
 
-    if (insts[OVSINST_OFPIT11_WRITE_METADATA] ||
-        insts[OVSINST_OFPIT11_WRITE_ACTIONS]) {
+    if (insts[OVSINST_OFPIT11_WRITE_ACTIONS]) {
         error = OFPERR_OFPBIC_UNSUP_INST;
         goto exit;
     }
 
+    error = ofpacts_verify(ofpacts->data, ofpacts->size);
 exit:
     if (error) {
         ofpbuf_clear(ofpacts);
@@ -1018,6 +1074,7 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports)
     case OFPACT_SET_VLAN_VID:
     case OFPACT_SET_VLAN_PCP:
     case OFPACT_STRIP_VLAN:
+    case OFPACT_PUSH_VLAN:
     case OFPACT_SET_ETH_SRC:
     case OFPACT_SET_ETH_DST:
     case OFPACT_SET_IPV4_SRC:
@@ -1055,6 +1112,7 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports)
         return 0;
 
     case OFPACT_CLEAR_ACTIONS:
+    case OFPACT_WRITE_METADATA:
     case OFPACT_GOTO_TABLE:
         return 0;
 
@@ -1081,6 +1139,35 @@ ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len,
 
     return 0;
 }
+
+/* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are
+ * in the appropriate order as defined by the OpenFlow spec. */
+enum ofperr
+ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len)
+{
+    const struct ofpact *a;
+    const struct ofpact_metadata *om = NULL;
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        if (om) {
+            if (a->type == OFPACT_WRITE_METADATA) {
+                VLOG_WARN("duplicate write_metadata instruction specified");
+                /* should be OFPERR_OFPET_BAD_ACTION? */
+                return OFPERR_OFPBAC_UNSUPPORTED_ORDER;
+            } else {
+                VLOG_WARN("write_metadata instruction must be specified after "
+                          "other instructions/actions");
+                return OFPERR_OFPBAC_UNSUPPORTED_ORDER;
+            }
+        }
+
+        if (a->type == OFPACT_WRITE_METADATA) {
+            om = (const struct ofpact_metadata *) a;
+        }
+    }
+
+    return 0;
+}
 \f
 /* Converting ofpacts to Nicira OpenFlow extensions. */
 
@@ -1126,6 +1213,17 @@ ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel,
     }
 }
 
+static void
+ofpact_write_metadata_to_nxast(const struct ofpact_metadata *om,
+                               struct ofpbuf *out)
+{
+    struct nx_action_write_metadata *nawm;
+
+    nawm = ofputil_put_NXAST_WRITE_METADATA(out);
+    nawm->metadata = om->metadata;
+    nawm->mask = om->mask;
+}
+
 static void
 ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out)
 {
@@ -1224,6 +1322,10 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
         ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out);
         break;
 
+    case OFPACT_WRITE_METADATA:
+        ofpact_write_metadata_to_nxast(ofpact_get_WRITE_METADATA(a), out);
+        break;
+
     case OFPACT_SET_QUEUE:
         ofputil_put_NXAST_SET_QUEUE(out)->queue_id
             = htonl(ofpact_get_SET_QUEUE(a)->queue_id);
@@ -1266,6 +1368,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_SET_VLAN_VID:
     case OFPACT_SET_VLAN_PCP:
     case OFPACT_STRIP_VLAN:
+    case OFPACT_PUSH_VLAN:
     case OFPACT_SET_ETH_SRC:
     case OFPACT_SET_ETH_DST:
     case OFPACT_SET_IPV4_SRC:
@@ -1364,6 +1467,7 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
             = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
         break;
 
+    case OFPACT_PUSH_VLAN:
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
         /* TODO:XXX */
@@ -1376,6 +1480,7 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_REG_LOAD:
     case OFPACT_DEC_TTL:
     case OFPACT_SET_TUNNEL:
+    case OFPACT_WRITE_METADATA:
     case OFPACT_SET_QUEUE:
     case OFPACT_POP_QUEUE:
     case OFPACT_FIN_TIMEOUT:
@@ -1417,6 +1522,19 @@ ofpact_output_to_openflow11(const struct ofpact_output *output,
     oao->max_len = htons(output->max_len);
 }
 
+static void
+ofpact_dec_ttl_to_openflow11(const struct ofpact_cnt_ids *dec_ttl,
+                             struct ofpbuf *out)
+{
+    if (dec_ttl->n_controllers == 1 && dec_ttl->cnt_ids[0] == 0
+        && (!dec_ttl->ofpact.compat ||
+            dec_ttl->ofpact.compat == OFPUTIL_OFPAT11_DEC_NW_TTL)) {
+        ofputil_put_OFPAT11_DEC_NW_TTL(out);
+    } else {
+        ofpact_dec_ttl_to_nxast(dec_ttl, out);
+    }
+}
+
 static void
 ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
 {
@@ -1439,7 +1557,13 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
         break;
 
     case OFPACT_STRIP_VLAN:
-        /* XXX */
+        ofputil_put_OFPAT11_POP_VLAN(out);
+        break;
+
+    case OFPACT_PUSH_VLAN:
+        /* TODO:XXX ETH_TYPE_VLAN_8021AD case */
+        ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype =
+            htons(ETH_TYPE_VLAN_8021Q);
         break;
 
     case OFPACT_SET_ETH_SRC:
@@ -1477,6 +1601,14 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
             = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
         break;
 
+    case OFPACT_DEC_TTL:
+        ofpact_dec_ttl_to_openflow11(ofpact_get_DEC_TTL(a), out);
+        break;
+
+    case OFPACT_WRITE_METADATA:
+        /* OpenFlow 1.1 uses OFPIT_WRITE_METADATA to express this action. */
+        break;
+
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
         NOT_REACHED();
@@ -1486,7 +1618,6 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_BUNDLE:
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
-    case OFPACT_DEC_TTL:
     case OFPACT_SET_TUNNEL:
     case OFPACT_SET_QUEUE:
     case OFPACT_POP_QUEUE:
@@ -1542,16 +1673,23 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
 
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
         /* TODO:XXX Write-Actions */
-        /* TODO:XXX Write-Metadata */
+
         if (a->type == OFPACT_CLEAR_ACTIONS) {
-            struct ofp11_instruction *oi;
-            oi = instruction_put_OFPIT11_CLEAR_ACTIONS(openflow);
+            instruction_put_OFPIT11_CLEAR_ACTIONS(openflow);
         } else if (a->type == OFPACT_GOTO_TABLE) {
             struct ofp11_instruction_goto_table *oigt;
 
             oigt = instruction_put_OFPIT11_GOTO_TABLE(openflow);
             oigt->table_id = ofpact_get_GOTO_TABLE(a)->table_id;
             memset(oigt->pad, 0, sizeof oigt->pad);
+        } else if (a->type == OFPACT_WRITE_METADATA) {
+            const struct ofpact_metadata *om;
+            struct ofp11_instruction_write_metadata *oiwm;
+
+            om = ofpact_get_WRITE_METADATA(a);
+            oiwm = instruction_put_OFPIT11_WRITE_METADATA(openflow);
+            oiwm->metadata = om->metadata;
+            oiwm->metadata_mask = om->mask;
         } else if (!ofpact_is_instruction(a)) {
             /* Apply-actions */
             const size_t ofs = openflow->size;
@@ -1591,6 +1729,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
     case OFPACT_SET_VLAN_VID:
     case OFPACT_SET_VLAN_PCP:
     case OFPACT_STRIP_VLAN:
+    case OFPACT_PUSH_VLAN:
     case OFPACT_SET_ETH_SRC:
     case OFPACT_SET_ETH_DST:
     case OFPACT_SET_IPV4_SRC:
@@ -1602,6 +1741,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
     case OFPACT_REG_LOAD:
     case OFPACT_DEC_TTL:
     case OFPACT_SET_TUNNEL:
+    case OFPACT_WRITE_METADATA:
     case OFPACT_SET_QUEUE:
     case OFPACT_POP_QUEUE:
     case OFPACT_FIN_TIMEOUT:
@@ -1701,6 +1841,7 @@ ofpact_format(const struct ofpact *a, struct ds *s)
     const struct ofpact_resubmit *resubmit;
     const struct ofpact_autopath *autopath;
     const struct ofpact_controller *controller;
+    const struct ofpact_metadata *metadata;
     const struct ofpact_tunnel *tunnel;
     uint16_t port;
 
@@ -1772,6 +1913,11 @@ ofpact_format(const struct ofpact *a, struct ds *s)
         ds_put_cstr(s, "strip_vlan");
         break;
 
+    case OFPACT_PUSH_VLAN:
+        /* TODO:XXX 802.1AD case*/
+        ds_put_format(s, "push_vlan:%#"PRIx16, ETH_TYPE_VLAN_8021Q);
+        break;
+
     case OFPACT_SET_ETH_SRC:
         ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
                       ETH_ADDR_ARGS(ofpact_get_SET_ETH_SRC(a)->mac));
@@ -1886,6 +2032,17 @@ ofpact_format(const struct ofpact *a, struct ds *s)
                           OVSINST_OFPIT11_CLEAR_ACTIONS));
         break;
 
+    case OFPACT_WRITE_METADATA:
+        metadata = ofpact_get_WRITE_METADATA(a);
+        ds_put_format(s, "%s:%#"PRIx64,
+                      ofpact_instruction_name_from_type(
+                          OVSINST_OFPIT11_WRITE_METADATA),
+                      ntohll(metadata->metadata));
+        if (metadata->mask != htonll(UINT64_MAX)) {
+            ds_put_format(s, "/%#"PRIx64, ntohll(metadata->mask));
+        }
+        break;
+
     case OFPACT_GOTO_TABLE:
         ds_put_format(s, "%s:%"PRIu8,
                       ofpact_instruction_name_from_type(
@@ -1913,7 +2070,6 @@ ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
             }
 
             /* TODO:XXX write-actions */
-            /* TODO:XXX write-metadata */
             ofpact_format(a, string);
         }
     }