X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=lib%2Fofp-actions.c;h=ae961f6cc5bbe42dcb8bd29c1d55cee52090cac7;hb=8f79bb4d3999d993424e9578342b4130d10a556c;hp=5df36a200c8f75c3bc287f23f1e021aad3eaa099;hpb=355ead69010c123475a9d2ade23b1cc667d868cb;p=cascardo%2Fovs.git diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 5df36a200..ae961f6cc 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. + * Copyright (c) 2008-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. @@ -15,22 +15,27 @@ */ #include +#include + #include "ofp-actions.h" #include "bundle.h" #include "byte-order.h" #include "compiler.h" +#include "dummy.h" #include "dynamic-string.h" #include "hmap.h" #include "learn.h" #include "meta-flow.h" #include "multipath.h" #include "nx-match.h" +#include "odp-netlink.h" #include "ofp-parse.h" +#include "ofp-prop.h" #include "ofp-util.h" #include "ofpbuf.h" #include "unaligned.h" #include "util.h" -#include "vlog.h" +#include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ofp_actions); @@ -201,14 +206,26 @@ enum ofp_raw_action_type { /* NX1.0+(21): struct nx_action_cnt_ids, ... */ NXAST_RAW_DEC_TTL_CNT_IDS, - /* OF1.2+(25): struct ofp12_action_set_field, ... */ + /* OF1.2-1.4(25): struct ofp12_action_set_field, ... */ OFPAT_RAW12_SET_FIELD, - /* NX1.0+(7): struct nx_action_reg_load. */ + /* OF1.5+(25): struct ofp12_action_set_field, ... */ + OFPAT_RAW15_SET_FIELD, + /* NX1.0-1.4(7): struct nx_action_reg_load. + * + * [In OpenFlow 1.5, set_field is a superset of reg_load functionality, so + * we drop reg_load.] */ NXAST_RAW_REG_LOAD, + /* NX1.0-1.4(33): struct nx_action_reg_load2, ... + * + * [In OpenFlow 1.5, set_field is a superset of reg_load2 functionality, so + * we drop reg_load2.] */ + NXAST_RAW_REG_LOAD2, - /* OF1.5+(28): struct ofp15_action_copy_field. */ + /* OF1.5+(28): struct ofp15_action_copy_field, ... */ OFPAT_RAW15_COPY_FIELD, - /* NX1.0-1.4(6): struct nx_action_reg_move. */ + /* ONF1.3-1.4(3200): struct onf_action_copy_field, ... */ + ONFACT_RAW13_COPY_FIELD, + /* NX1.0-1.4(6): struct nx_action_reg_move, ... */ NXAST_RAW_REG_MOVE, /* ## ------------------------- ## */ @@ -243,6 +260,8 @@ enum ofp_raw_action_type { /* NX1.0+(15): struct nx_action_output_reg. */ NXAST_RAW_OUTPUT_REG, + /* NX1.0+(32): struct nx_action_output_reg2. */ + NXAST_RAW_OUTPUT_REG2, /* NX1.0+(16): struct nx_action_learn, ... */ NXAST_RAW_LEARN, @@ -255,6 +274,8 @@ enum ofp_raw_action_type { /* NX1.0+(20): struct nx_action_controller. */ NXAST_RAW_CONTROLLER, + /* NX1.0+(37): struct nx_action_controller2, ... */ + NXAST_RAW_CONTROLLER2, /* NX1.0+(22): struct nx_action_write_metadata. */ NXAST_RAW_WRITE_METADATA, @@ -267,6 +288,25 @@ enum ofp_raw_action_type { /* NX1.0+(29): struct nx_action_sample. */ NXAST_RAW_SAMPLE, + + /* NX1.0+(34): struct nx_action_conjunction. */ + NXAST_RAW_CONJUNCTION, + + /* NX1.0+(35): struct nx_action_conntrack, ... */ + NXAST_RAW_CT, + + /* NX1.0+(36): struct nx_action_nat, ... */ + NXAST_RAW_NAT, + +/* ## ------------------ ## */ +/* ## Debugging actions. ## */ +/* ## ------------------ ## */ + +/* These are intentionally undocumented, subject to change, and ovs-vswitchd */ +/* accepts them only if started with --enable-dummy. */ + + /* NX1.0+(255): void. */ + NXAST_RAW_DEBUG_RECIRC, }; /* OpenFlow actions are always a multiple of 8 bytes in length. */ @@ -301,9 +341,11 @@ OVS_INSTRUCTIONS static void ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs); +static void pad_ofpat(struct ofpbuf *openflow, size_t start_ofs); static enum ofperr ofpacts_verify(const struct ofpact[], size_t ofpacts_len, - uint32_t allowed_ovsinsts); + uint32_t allowed_ovsinsts, + enum ofpact_type outer_action); static void ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version, enum mf_field_id, uint64_t value); @@ -313,9 +355,103 @@ static enum ofperr ofpact_pull_raw(struct ofpbuf *, enum ofp_version, static void *ofpact_put_raw(struct ofpbuf *, enum ofp_version, enum ofp_raw_action_type, uint64_t arg); -static char *WARN_UNUSED_RESULT ofpacts_parse( +static char *OVS_WARN_UNUSED_RESULT ofpacts_parse( char *str, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols, - bool allow_instructions); + bool allow_instructions, enum ofpact_type outer_action); +static enum ofperr ofpacts_pull_openflow_actions__( + struct ofpbuf *openflow, unsigned int actions_len, + enum ofp_version version, uint32_t allowed_ovsinsts, + struct ofpbuf *ofpacts, enum ofpact_type outer_action); +static char * OVS_WARN_UNUSED_RESULT ofpacts_parse_copy( + const char *s_, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols, + bool allow_instructions, enum ofpact_type outer_action); + +/* Returns the ofpact following 'ofpact', except that if 'ofpact' contains + * nested ofpacts it returns the first one. */ +struct ofpact * +ofpact_next_flattened(const struct ofpact *ofpact) +{ + switch (ofpact->type) { + case OFPACT_OUTPUT: + case OFPACT_GROUP: + case OFPACT_CONTROLLER: + case OFPACT_ENQUEUE: + case OFPACT_OUTPUT_REG: + case OFPACT_BUNDLE: + case OFPACT_SET_FIELD: + 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: + case OFPACT_SET_IPV4_DST: + case OFPACT_SET_IP_DSCP: + case OFPACT_SET_IP_ECN: + case OFPACT_SET_IP_TTL: + case OFPACT_SET_L4_SRC_PORT: + case OFPACT_SET_L4_DST_PORT: + case OFPACT_REG_MOVE: + case OFPACT_STACK_PUSH: + case OFPACT_STACK_POP: + case OFPACT_DEC_TTL: + case OFPACT_SET_MPLS_LABEL: + case OFPACT_SET_MPLS_TC: + case OFPACT_SET_MPLS_TTL: + case OFPACT_DEC_MPLS_TTL: + case OFPACT_PUSH_MPLS: + case OFPACT_POP_MPLS: + case OFPACT_SET_TUNNEL: + case OFPACT_SET_QUEUE: + case OFPACT_POP_QUEUE: + case OFPACT_FIN_TIMEOUT: + case OFPACT_RESUBMIT: + case OFPACT_LEARN: + case OFPACT_CONJUNCTION: + case OFPACT_MULTIPATH: + case OFPACT_NOTE: + case OFPACT_EXIT: + case OFPACT_SAMPLE: + case OFPACT_UNROLL_XLATE: + case OFPACT_DEBUG_RECIRC: + case OFPACT_METER: + case OFPACT_CLEAR_ACTIONS: + case OFPACT_WRITE_METADATA: + case OFPACT_GOTO_TABLE: + case OFPACT_NAT: + return ofpact_next(ofpact); + + case OFPACT_CT: + return ofpact_get_CT(ofpact)->actions; + + case OFPACT_WRITE_ACTIONS: + return ofpact_get_WRITE_ACTIONS(ofpact)->actions; + } + + OVS_NOT_REACHED(); +} + +/* Pull off existing actions or instructions. Used by nesting actions to keep + * ofpacts_parse() oblivious of actions nesting. + * + * Push the actions back on after nested parsing, e.g.: + * + * size_t ofs = ofpacts_pull(ofpacts); + * ...nested parsing... + * ofpbuf_push_uninit(ofpacts, ofs); + */ +static size_t +ofpacts_pull(struct ofpbuf *ofpacts) +{ + size_t ofs; + + ofs = ofpacts->size; + ofpbuf_pull(ofpacts, ofs); + + return ofs; +} #include "ofp-actions.inc1" @@ -348,6 +484,7 @@ OFP_ASSERT(sizeof(struct ofp11_action_output) == 16); static enum ofperr decode_OFPAT_RAW10_OUTPUT(const struct ofp10_action_output *oao, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_output *output; @@ -361,7 +498,8 @@ decode_OFPAT_RAW10_OUTPUT(const struct ofp10_action_output *oao, static enum ofperr decode_OFPAT_RAW11_OUTPUT(const struct ofp11_action_output *oao, - struct ofpbuf *out) + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { struct ofpact_output *output; enum ofperr error; @@ -396,7 +534,7 @@ encode_OUTPUT(const struct ofpact_output *output, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -434,7 +572,9 @@ format_OUTPUT(const struct ofpact_output *a, struct ds *s) /* Group actions. */ static enum ofperr -decode_OFPAT_RAW11_GROUP(uint32_t group_id, struct ofpbuf *out) +decode_OFPAT_RAW11_GROUP(uint32_t group_id, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_GROUP(out)->group_id = group_id; return 0; @@ -451,7 +591,7 @@ encode_GROUP(const struct ofpact_group *group, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_GROUP(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -488,16 +628,114 @@ struct nx_action_controller { }; OFP_ASSERT(sizeof(struct nx_action_controller) == 16); +/* Properties for NXAST_CONTROLLER2. + * + * For more information on the effect of NXAC2PT_PAUSE, see the large comment + * on NXT_PACKET_IN2 in nicira-ext.h */ +enum nx_action_controller2_prop_type { + NXAC2PT_MAX_LEN, /* ovs_be16 max bytes to send (default all). */ + NXAC2PT_CONTROLLER_ID, /* ovs_be16 dest controller ID (default 0). */ + NXAC2PT_REASON, /* uint8_t reason (OFPR_*), default 0. */ + NXAC2PT_USERDATA, /* Data to copy into NXPINT_USERDATA. */ + NXAC2PT_PAUSE, /* Flag to pause pipeline to resume later. */ +}; + +/* Action structure for NXAST_CONTROLLER2. + * + * This replacement for NXAST_CONTROLLER makes it extensible via properties. */ +struct nx_action_controller2 { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 16 or more. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_CONTROLLER2. */ + uint8_t zeros[6]; /* Must be zero. */ + /* Followed by NXAC2PT_* properties. */ +}; +OFP_ASSERT(sizeof(struct nx_action_controller2) == 16); + static enum ofperr decode_NXAST_RAW_CONTROLLER(const struct nx_action_controller *nac, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_controller *oc; oc = ofpact_put_CONTROLLER(out); + oc->ofpact.raw = NXAST_RAW_CONTROLLER; oc->max_len = ntohs(nac->max_len); oc->controller_id = ntohs(nac->controller_id); oc->reason = nac->reason; + ofpact_finish(out, &oc->ofpact); + + return 0; +} + +static enum ofperr +decode_NXAST_RAW_CONTROLLER2(const struct nx_action_controller2 *nac2, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + if (!is_all_zeros(nac2->zeros, sizeof nac2->zeros)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + + size_t start_ofs = out->size; + struct ofpact_controller *oc = ofpact_put_CONTROLLER(out); + oc->ofpact.raw = NXAST_RAW_CONTROLLER2; + oc->max_len = UINT16_MAX; + oc->reason = OFPR_ACTION; + + struct ofpbuf properties; + ofpbuf_use_const(&properties, nac2, ntohs(nac2->len)); + ofpbuf_pull(&properties, sizeof *nac2); + + while (properties.size > 0) { + struct ofpbuf payload; + uint64_t type; + + enum ofperr error = ofpprop_pull(&properties, &payload, &type); + if (error) { + return error; + } + + switch (type) { + case NXAC2PT_MAX_LEN: + error = ofpprop_parse_u16(&payload, &oc->max_len); + break; + + case NXAC2PT_CONTROLLER_ID: + error = ofpprop_parse_u16(&payload, &oc->controller_id); + break; + + case NXAC2PT_REASON: { + uint8_t u8; + error = ofpprop_parse_u8(&payload, &u8); + oc->reason = u8; + break; + } + + case NXAC2PT_USERDATA: + out->size = start_ofs + OFPACT_CONTROLLER_SIZE; + ofpbuf_put(out, payload.msg, ofpbuf_msgsize(&payload)); + oc = ofpbuf_at_assert(out, start_ofs, sizeof *oc); + oc->userdata_len = ofpbuf_msgsize(&payload); + break; + + case NXAC2PT_PAUSE: + oc->pause = true; + break; + + default: + error = OFPPROP_UNKNOWN(false, "NXAST_RAW_CONTROLLER2", type); + break; + } + if (error) { + return error; + } + } + + ofpact_finish(out, &oc->ofpact); + return 0; } @@ -506,21 +744,48 @@ encode_CONTROLLER(const struct ofpact_controller *controller, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { - struct nx_action_controller *nac; + if (controller->userdata_len + || controller->pause + || controller->ofpact.raw == NXAST_RAW_CONTROLLER2) { + size_t start_ofs = out->size; + put_NXAST_CONTROLLER2(out); + if (controller->max_len != UINT16_MAX) { + ofpprop_put_u16(out, NXAC2PT_MAX_LEN, controller->max_len); + } + if (controller->controller_id != 0) { + ofpprop_put_u16(out, NXAC2PT_CONTROLLER_ID, + controller->controller_id); + } + if (controller->reason != OFPR_ACTION) { + ofpprop_put_u8(out, NXAC2PT_REASON, controller->reason); + } + if (controller->userdata_len != 0) { + ofpprop_put(out, NXAC2PT_USERDATA, controller->userdata, + controller->userdata_len); + } + if (controller->pause) { + ofpprop_put_flag(out, NXAC2PT_PAUSE); + } + pad_ofpat(out, start_ofs); + } else { + struct nx_action_controller *nac; - nac = put_NXAST_CONTROLLER(out); - nac->max_len = htons(controller->max_len); - nac->controller_id = htons(controller->controller_id); - nac->reason = controller->reason; + nac = put_NXAST_CONTROLLER(out); + nac->max_len = htons(controller->max_len); + nac->controller_id = htons(controller->controller_id); + nac->reason = controller->reason; + } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { enum ofp_packet_in_reason reason = OFPR_ACTION; uint16_t controller_id = 0; uint16_t max_len = UINT16_MAX; + const char *userdata = NULL; + bool pause = false; if (!arg[0]) { /* Use defaults. */ @@ -547,6 +812,10 @@ parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts, if (error) { return error; } + } else if (!strcmp(name, "userdata")) { + userdata = value; + } else if (!strcmp(name, "pause")) { + pause = true; } else { return xasprintf("unknown key \"%s\" parsing controller " "action", name); @@ -554,7 +823,7 @@ parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts, } } - if (reason == OFPR_ACTION && controller_id == 0) { + if (reason == OFPR_ACTION && controller_id == 0 && !userdata && !pause) { struct ofpact_output *output; output = ofpact_put_OUTPUT(ofpacts); @@ -567,15 +836,41 @@ parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts, controller->max_len = max_len; controller->reason = reason; controller->controller_id = controller_id; + controller->pause = pause; + + if (userdata) { + size_t start_ofs = ofpacts->size; + const char *end = ofpbuf_put_hex(ofpacts, userdata, NULL); + if (*end) { + return xstrdup("bad hex digit in `controller' " + "action `userdata'"); + } + size_t userdata_len = ofpacts->size - start_ofs; + controller = ofpacts->header; + controller->userdata_len = userdata_len; + } + ofpact_finish(ofpacts, &controller->ofpact); } return NULL; } +static void +format_hex_arg(struct ds *s, const uint8_t *data, size_t len) +{ + for (size_t i = 0; i < len; i++) { + if (i) { + ds_put_char(s, '.'); + } + ds_put_format(s, "%02"PRIx8, data[i]); + } +} + static void format_CONTROLLER(const struct ofpact_controller *a, struct ds *s) { - if (a->reason == OFPR_ACTION && a->controller_id == 0) { + if (a->reason == OFPR_ACTION && !a->controller_id && !a->userdata_len + && !a->pause) { ds_put_format(s, "CONTROLLER:%"PRIu16, a->max_len); } else { enum ofp_packet_in_reason reason = a->reason; @@ -594,6 +889,14 @@ format_CONTROLLER(const struct ofpact_controller *a, struct ds *s) if (a->controller_id != 0) { ds_put_format(s, "id=%"PRIu16",", a->controller_id); } + if (a->userdata_len) { + ds_put_cstr(s, "userdata="); + format_hex_arg(s, a->userdata, a->userdata_len); + ds_put_char(s, ','); + } + if (a->pause) { + ds_put_cstr(s, "pause,"); + } ds_chomp(s, ','); ds_put_char(s, ')'); } @@ -613,6 +916,7 @@ OFP_ASSERT(sizeof(struct ofp10_action_enqueue) == 16); static enum ofperr decode_OFPAT_RAW10_ENQUEUE(const struct ofp10_action_enqueue *oae, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_enqueue *enqueue; @@ -643,7 +947,7 @@ encode_ENQUEUE(const struct ofpact_enqueue *enqueue, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_ENQUEUE(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -702,8 +1006,30 @@ struct nx_action_output_reg { }; OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24); +/* Action structure for NXAST_OUTPUT_REG2. + * + * Like the NXAST_OUTPUT_REG but organized so that there is room for a 64-bit + * experimenter OXM as 'src'. + */ +struct nx_action_output_reg2 { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* 24. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_OUTPUT_REG2. */ + + ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ + ovs_be16 max_len; /* Max length to send to controller. */ + + /* Followed by: + * - 'src', as an OXM/NXM header (either 4 or 8 bytes). + * - Enough 0-bytes to pad the action out to 24 bytes. */ + uint8_t pad[10]; +}; +OFP_ASSERT(sizeof(struct nx_action_output_reg2) == 24); + static enum ofperr decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_output_reg *output_reg; @@ -713,6 +1039,7 @@ decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor, } output_reg = ofpact_put_OUTPUT_REG(out); + output_reg->ofpact.raw = NXAST_RAW_OUTPUT_REG; output_reg->src.field = mf_from_nxm_header(ntohl(naor->src)); output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits); output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits); @@ -721,20 +1048,63 @@ decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor, return mf_check_src(&output_reg->src, NULL); } +static enum ofperr +decode_NXAST_RAW_OUTPUT_REG2(const struct nx_action_output_reg2 *naor, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + struct ofpact_output_reg *output_reg; + output_reg = ofpact_put_OUTPUT_REG(out); + output_reg->ofpact.raw = NXAST_RAW_OUTPUT_REG2; + output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits); + output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits); + output_reg->max_len = ntohs(naor->max_len); + + struct ofpbuf b = ofpbuf_const_initializer(naor, ntohs(naor->len)); + ofpbuf_pull(&b, OBJECT_OFFSETOF(naor, pad)); + + enum ofperr error = nx_pull_header(&b, &output_reg->src.field, NULL); + if (error) { + return error; + } + if (!is_all_zeros(b.data, b.size)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + + return mf_check_src(&output_reg->src, NULL); +} + static void encode_OUTPUT_REG(const struct ofpact_output_reg *output_reg, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { - struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out); + /* If 'output_reg' came in as an NXAST_RAW_OUTPUT_REG2 action, or if it + * cannot be encoded in the older form, encode it as + * NXAST_RAW_OUTPUT_REG2. */ + if (output_reg->ofpact.raw == NXAST_RAW_OUTPUT_REG2 + || !mf_nxm_header(output_reg->src.field->id)) { + struct nx_action_output_reg2 *naor = put_NXAST_OUTPUT_REG2(out); + size_t size = out->size; + + naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, + output_reg->src.n_bits); + naor->max_len = htons(output_reg->max_len); + + out->size = size - sizeof naor->pad; + nx_put_header(out, output_reg->src.field->id, 0, false); + out->size = size; + } else { + struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out); - naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, - output_reg->src.n_bits); - naor->src = htonl(output_reg->src.field->nxm_header); - naor->max_len = htons(output_reg->max_len); + naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, + output_reg->src.n_bits); + naor->src = htonl(mf_nxm_header(output_reg->src.field->id)); + naor->max_len = htons(output_reg->max_len); + } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_OUTPUT_REG(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -843,7 +1213,7 @@ decode_bundle(bool load, const struct nx_action_bundle *nab, } else if (bundle->algorithm != NX_BD_ALG_HRW && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) { VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm); - } else if (slave_type != NXM_OF_IN_PORT) { + } else if (slave_type != mf_nxm_header(MFF_IN_PORT)) { VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type); } else { error = 0; @@ -885,8 +1255,8 @@ decode_bundle(bool load, const struct nx_action_bundle *nab, ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port); } - bundle = ofpacts->frame; - ofpact_update_len(ofpacts, &bundle->ofpact); + bundle = ofpacts->header; + ofpact_finish(ofpacts, &bundle->ofpact); if (!error) { error = bundle_check(bundle, OFPP_MAX, NULL); @@ -895,13 +1265,16 @@ decode_bundle(bool load, const struct nx_action_bundle *nab, } static enum ofperr -decode_NXAST_RAW_BUNDLE(const struct nx_action_bundle *nab, struct ofpbuf *out) +decode_NXAST_RAW_BUNDLE(const struct nx_action_bundle *nab, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { return decode_bundle(false, nab, out); } static enum ofperr decode_NXAST_RAW_BUNDLE_LOAD(const struct nx_action_bundle *nab, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { return decode_bundle(true, nab, out); @@ -924,12 +1297,12 @@ encode_BUNDLE(const struct ofpact_bundle *bundle, nab->algorithm = htons(bundle->algorithm); nab->fields = htons(bundle->fields); nab->basis = htons(bundle->basis); - nab->slave_type = htonl(NXM_OF_IN_PORT); + nab->slave_type = htonl(mf_nxm_header(MFF_IN_PORT)); nab->n_slaves = htons(bundle->n_slaves); if (bundle->dst.field) { nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs, bundle->dst.n_bits); - nab->dst = htonl(bundle->dst.field->nxm_header); + nab->dst = htonl(mf_nxm_header(bundle->dst.field->id)); } slaves = ofpbuf_put_zeros(out, slaves_len); @@ -938,14 +1311,14 @@ encode_BUNDLE(const struct ofpact_bundle *bundle, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_BUNDLE(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return bundle_parse(arg, ofpacts); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_bundle_load(const char *arg, struct ofpbuf *ofpacts) { return bundle_parse_load(arg, ofpacts); @@ -973,13 +1346,17 @@ decode_set_vlan_vid(uint16_t vid, bool push_vlan_if_needed, struct ofpbuf *out) } static enum ofperr -decode_OFPAT_RAW10_SET_VLAN_VID(uint16_t vid, struct ofpbuf *out) +decode_OFPAT_RAW10_SET_VLAN_VID(uint16_t vid, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { return decode_set_vlan_vid(vid, true, out); } static enum ofperr -decode_OFPAT_RAW11_SET_VLAN_VID(uint16_t vid, struct ofpbuf *out) +decode_OFPAT_RAW11_SET_VLAN_VID(uint16_t vid, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { return decode_set_vlan_vid(vid, false, out); } @@ -1008,7 +1385,7 @@ encode_SET_VLAN_VID(const struct ofpact_vlan_vid *vlan_vid, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_set_vlan_vid(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed) { struct ofpact_vlan_vid *vlan_vid; @@ -1029,7 +1406,7 @@ parse_set_vlan_vid(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed) return NULL; } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_VLAN_VID(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1060,13 +1437,17 @@ decode_set_vlan_pcp(uint8_t pcp, bool push_vlan_if_needed, struct ofpbuf *out) } static enum ofperr -decode_OFPAT_RAW10_SET_VLAN_PCP(uint8_t pcp, struct ofpbuf *out) +decode_OFPAT_RAW10_SET_VLAN_PCP(uint8_t pcp, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { return decode_set_vlan_pcp(pcp, true, out); } static enum ofperr -decode_OFPAT_RAW11_SET_VLAN_PCP(uint8_t pcp, struct ofpbuf *out) +decode_OFPAT_RAW11_SET_VLAN_PCP(uint8_t pcp, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { return decode_set_vlan_pcp(pcp, false, out); } @@ -1094,7 +1475,7 @@ encode_SET_VLAN_PCP(const struct ofpact_vlan_pcp *vlan_pcp, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_set_vlan_pcp(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed) { struct ofpact_vlan_pcp *vlan_pcp; @@ -1115,7 +1496,7 @@ parse_set_vlan_pcp(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed) return NULL; } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_VLAN_PCP(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1157,7 +1538,7 @@ encode_STRIP_VLAN(const struct ofpact_null *null OVS_UNUSED, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_STRIP_VLAN(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1165,7 +1546,7 @@ parse_STRIP_VLAN(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, return NULL; } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_pop_vlan(struct ofpbuf *ofpacts) { ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW11_POP_VLAN; @@ -1183,7 +1564,9 @@ format_STRIP_VLAN(const struct ofpact_null *a, struct ds *s) /* Push VLAN action. */ static enum ofperr -decode_OFPAT_RAW11_PUSH_VLAN(ovs_be16 eth_type, struct ofpbuf *out) +decode_OFPAT_RAW11_PUSH_VLAN(ovs_be16 eth_type, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { if (eth_type != htons(ETH_TYPE_VLAN_8021Q)) { /* XXX 802.1AD(QinQ) isn't supported at the moment */ @@ -1206,7 +1589,7 @@ encode_PUSH_VLAN(const struct ofpact_null *null OVS_UNUSED, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_PUSH_VLAN(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1239,24 +1622,26 @@ format_PUSH_VLAN(const struct ofpact_null *a OVS_UNUSED, struct ds *s) struct ofp_action_dl_addr { ovs_be16 type; /* Type. */ ovs_be16 len; /* Length is 16. */ - uint8_t dl_addr[OFP_ETH_ALEN]; /* Ethernet address. */ + struct eth_addr dl_addr; /* Ethernet address. */ uint8_t pad[6]; }; OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16); static enum ofperr decode_OFPAT_RAW_SET_DL_SRC(const struct ofp_action_dl_addr *a, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { - memcpy(ofpact_put_SET_ETH_SRC(out)->mac, a->dl_addr, ETH_ADDR_LEN); + ofpact_put_SET_ETH_SRC(out)->mac = a->dl_addr; return 0; } static enum ofperr decode_OFPAT_RAW_SET_DL_DST(const struct ofp_action_dl_addr *a, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { - memcpy(ofpact_put_SET_ETH_DST(out)->mac, a->dl_addr, ETH_ADDR_LEN); + ofpact_put_SET_ETH_DST(out)->mac = a->dl_addr; return 0; } @@ -1265,16 +1650,14 @@ encode_SET_ETH_addr(const struct ofpact_mac *mac, enum ofp_version ofp_version, enum ofp_raw_action_type raw, enum mf_field_id field, struct ofpbuf *out) { - const uint8_t *addr = mac->mac; - if (ofp_version < OFP12_VERSION) { struct ofp_action_dl_addr *oada; oada = ofpact_put_raw(out, ofp_version, raw, 0); - memcpy(oada->dl_addr, addr, ETH_ADDR_LEN); + oada->dl_addr = mac->mac; } else { ofpact_put_set_field(out, ofp_version, field, - eth_addr_to_uint64(addr)); + eth_addr_to_uint64(mac->mac)); } } @@ -1297,18 +1680,18 @@ encode_SET_ETH_DST(const struct ofpact_mac *mac, } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_ETH_SRC(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { - return str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); + return str_to_mac(arg, &ofpact_put_SET_ETH_SRC(ofpacts)->mac); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_ETH_DST(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { - return str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); + return str_to_mac(arg, &ofpact_put_SET_ETH_DST(ofpacts)->mac); } static void @@ -1326,14 +1709,18 @@ format_SET_ETH_DST(const struct ofpact_mac *a, struct ds *s) /* Set IPv4 address actions. */ static enum ofperr -decode_OFPAT_RAW_SET_NW_SRC(ovs_be32 ipv4, struct ofpbuf *out) +decode_OFPAT_RAW_SET_NW_SRC(ovs_be32 ipv4, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_SET_IPV4_SRC(out)->ipv4 = ipv4; return 0; } static enum ofperr -decode_OFPAT_RAW_SET_NW_DST(ovs_be32 ipv4, struct ofpbuf *out) +decode_OFPAT_RAW_SET_NW_DST(ovs_be32 ipv4, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_SET_IPV4_DST(out)->ipv4 = ipv4; return 0; @@ -1369,14 +1756,14 @@ encode_SET_IPV4_DST(const struct ofpact_ipv4 *ipv4, out); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_IPV4_SRC(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_IPV4_DST(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1398,7 +1785,9 @@ format_SET_IPV4_DST(const struct ofpact_ipv4 *a, struct ds *s) /* Set IPv4/v6 TOS actions. */ static enum ofperr -decode_OFPAT_RAW_SET_NW_TOS(uint8_t dscp, struct ofpbuf *out) +decode_OFPAT_RAW_SET_NW_TOS(uint8_t dscp, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { if (dscp & ~IP_DSCP_MASK) { return OFPERR_OFPBAC_BAD_ARGUMENT; @@ -1420,7 +1809,7 @@ encode_SET_IP_DSCP(const struct ofpact_dscp *dscp, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_IP_DSCP(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1448,7 +1837,9 @@ format_SET_IP_DSCP(const struct ofpact_dscp *a, struct ds *s) /* Set IPv4/v6 ECN actions. */ static enum ofperr -decode_OFPAT_RAW11_SET_NW_ECN(uint8_t ecn, struct ofpbuf *out) +decode_OFPAT_RAW11_SET_NW_ECN(uint8_t ecn, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { if (ecn & ~IP_ECN_MASK) { return OFPERR_OFPBAC_BAD_ARGUMENT; @@ -1472,7 +1863,7 @@ encode_SET_IP_ECN(const struct ofpact_ecn *ip_ecn, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_IP_ECN(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1500,7 +1891,9 @@ format_SET_IP_ECN(const struct ofpact_ecn *a, struct ds *s) /* Set IPv4/v6 TTL actions. */ static enum ofperr -decode_OFPAT_RAW11_SET_NW_TTL(uint8_t ttl, struct ofpbuf *out) +decode_OFPAT_RAW11_SET_NW_TTL(uint8_t ttl, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_SET_IP_TTL(out)->ttl = ttl; return 0; @@ -1517,7 +1910,7 @@ encode_SET_IP_TTL(const struct ofpact_ip_ttl *ttl, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_IP_TTL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1542,14 +1935,18 @@ format_SET_IP_TTL(const struct ofpact_ip_ttl *a, struct ds *s) /* Set TCP/UDP/SCTP port actions. */ static enum ofperr -decode_OFPAT_RAW_SET_TP_SRC(ovs_be16 port, struct ofpbuf *out) +decode_OFPAT_RAW_SET_TP_SRC(ovs_be16 port, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(port); return 0; } static enum ofperr -decode_OFPAT_RAW_SET_TP_DST(ovs_be16 port, struct ofpbuf *out) +decode_OFPAT_RAW_SET_TP_DST(ovs_be16 port, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(port); return 0; @@ -1596,7 +1993,7 @@ encode_SET_L4_DST_PORT(const struct ofpact_l4_port *l4_port, encode_SET_L4_port(l4_port, ofp_version, OFPAT_RAW_SET_TP_DST, field, out); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_L4_SRC_PORT(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1604,7 +2001,7 @@ parse_SET_L4_SRC_PORT(char *arg, struct ofpbuf *ofpacts, &ofpact_put_SET_L4_SRC_PORT(ofpacts)->port); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_L4_DST_PORT(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1631,18 +2028,35 @@ struct ofp15_action_copy_field { ovs_be16 n_bits; /* Number of bits to copy. */ ovs_be16 src_offset; /* Starting bit offset in source. */ ovs_be16 dst_offset; /* Starting bit offset in destination. */ - ovs_be16 oxm_id_len; /* Length of oxm_ids. */ - - /* OpenFlow allows for experimenter OXM fields whose expression is longer - * than a standard 32-bit OXM. Thus, in the OpenFlow specification, the - * following is variable-length. Open vSwitch does not yet support - * experimenter OXM fields, so until it does we leave these as fixed - * size. */ - ovs_be32 src; /* OXM for source field. */ - ovs_be32 dst; /* OXM for destination field. */ - uint8_t pad[4]; /* Must be zero. */ + uint8_t pad[2]; + /* Followed by: + * - OXM header for source field. + * - OXM header for destination field. + * - Padding with 0-bytes to a multiple of 8 bytes. + * The "pad2" member is the beginning of the above. */ + uint8_t pad2[4]; +}; +OFP_ASSERT(sizeof(struct ofp15_action_copy_field) == 16); + +/* Action structure for OpenFlow 1.3 extension copy-field action.. */ +struct onf_action_copy_field { + ovs_be16 type; /* OFPAT_EXPERIMENTER. */ + ovs_be16 len; /* Length is padded to 64 bits. */ + ovs_be32 experimenter; /* ONF_VENDOR_ID. */ + ovs_be16 exp_type; /* 3200. */ + uint8_t pad[2]; /* Not used. */ + ovs_be16 n_bits; /* Number of bits to copy. */ + ovs_be16 src_offset; /* Starting bit offset in source. */ + ovs_be16 dst_offset; /* Starting bit offset in destination. */ + uint8_t pad2[2]; /* Not used. */ + /* Followed by: + * - OXM header for source field. + * - OXM header for destination field. + * - Padding with 0-bytes (either 0 or 4 of them) to a multiple of 8 bytes. + * The "pad3" member is the beginning of the above. */ + uint8_t pad3[4]; /* Not used. */ }; -OFP_ASSERT(sizeof(struct ofp15_action_copy_field) == 24); +OFP_ASSERT(sizeof(struct onf_action_copy_field) == 24); /* Action structure for NXAST_REG_MOVE. * @@ -1698,6 +2112,10 @@ OFP_ASSERT(sizeof(struct ofp15_action_copy_field) == 24); * - NXM_OF_TCP_DST * - NXM_OF_UDP_SRC * - NXM_OF_UDP_DST + * - NXM_OF_ICMP_TYPE + * - NXM_OF_ICMP_CODE + * - NXM_NX_ICMPV6_TYPE + * - NXM_NX_ICMPV6_CODE * - NXM_NX_ARP_SHA * - NXM_NX_ARP_THA * - NXM_OF_ARP_OP @@ -1744,47 +2162,91 @@ struct nx_action_reg_move { ovs_be16 n_bits; /* Number of bits. */ ovs_be16 src_ofs; /* Starting bit offset in source. */ ovs_be16 dst_ofs; /* Starting bit offset in destination. */ - ovs_be32 src; /* Source register. */ - ovs_be32 dst; /* Destination register. */ + /* Followed by: + * - OXM/NXM header for source field (4 or 8 bytes). + * - OXM/NXM header for destination field (4 or 8 bytes). + * - Padding with 0-bytes to a multiple of 8 bytes, if necessary. */ }; -OFP_ASSERT(sizeof(struct nx_action_reg_move) == 24); +OFP_ASSERT(sizeof(struct nx_action_reg_move) == 16); static enum ofperr -decode_OFPAT_RAW15_COPY_FIELD(const struct ofp15_action_copy_field *oacf, - struct ofpbuf *ofpacts) +decode_copy_field__(ovs_be16 src_offset, ovs_be16 dst_offset, ovs_be16 n_bits, + const void *action, ovs_be16 action_len, size_t oxm_offset, + struct ofpbuf *ofpacts) { - struct ofpact_reg_move *move; + struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts); + move->ofpact.raw = ONFACT_RAW13_COPY_FIELD; + move->src.ofs = ntohs(src_offset); + move->src.n_bits = ntohs(n_bits); + move->dst.ofs = ntohs(dst_offset); + move->dst.n_bits = ntohs(n_bits); - if (oacf->oxm_id_len != htons(8)) { - /* We only support 4-byte OXM IDs so far. */ - return OFPERR_OFPBAC_BAD_LEN; + struct ofpbuf b = ofpbuf_const_initializer(action, ntohs(action_len)); + ofpbuf_pull(&b, oxm_offset); + + enum ofperr error = nx_pull_header(&b, &move->src.field, NULL); + if (error) { + return error; + } + error = nx_pull_header(&b, &move->dst.field, NULL); + if (error) { + return error; } - move = ofpact_put_REG_MOVE(ofpacts); - move->src.field = mf_from_nxm_header(ntohl(oacf->src)); - move->src.ofs = ntohs(oacf->src_offset); - move->src.n_bits = ntohs(oacf->n_bits); - move->dst.field = mf_from_nxm_header(ntohl(oacf->dst)); - move->dst.ofs = ntohs(oacf->dst_offset); - move->dst.n_bits = ntohs(oacf->n_bits); + if (!is_all_zeros(b.data, b.size)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } return nxm_reg_move_check(move, NULL); } +static enum ofperr +decode_OFPAT_RAW15_COPY_FIELD(const struct ofp15_action_copy_field *oacf, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + return decode_copy_field__(oacf->src_offset, oacf->dst_offset, + oacf->n_bits, oacf, oacf->len, + OBJECT_OFFSETOF(oacf, pad2), ofpacts); +} + +static enum ofperr +decode_ONFACT_RAW13_COPY_FIELD(const struct onf_action_copy_field *oacf, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + return decode_copy_field__(oacf->src_offset, oacf->dst_offset, + oacf->n_bits, oacf, oacf->len, + OBJECT_OFFSETOF(oacf, pad3), ofpacts); +} + static enum ofperr decode_NXAST_RAW_REG_MOVE(const struct nx_action_reg_move *narm, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { - struct ofpact_reg_move *move; - - move = ofpact_put_REG_MOVE(ofpacts); - move->src.field = mf_from_nxm_header(ntohl(narm->src)); + struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts); + move->ofpact.raw = NXAST_RAW_REG_MOVE; move->src.ofs = ntohs(narm->src_ofs); move->src.n_bits = ntohs(narm->n_bits); - move->dst.field = mf_from_nxm_header(ntohl(narm->dst)); move->dst.ofs = ntohs(narm->dst_ofs); move->dst.n_bits = ntohs(narm->n_bits); + struct ofpbuf b = ofpbuf_const_initializer(narm, ntohs(narm->len)); + ofpbuf_pull(&b, sizeof *narm); + + enum ofperr error = nx_pull_header(&b, &move->src.field, NULL); + if (error) { + return error; + } + error = nx_pull_header(&b, &move->dst.field, NULL); + if (error) { + return error; + } + if (!is_all_zeros(b.data, b.size)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + return nxm_reg_move_check(move, NULL); } @@ -1792,29 +2254,42 @@ static void encode_REG_MOVE(const struct ofpact_reg_move *move, enum ofp_version ofp_version, struct ofpbuf *out) { + /* For OpenFlow 1.3, the choice of ONFACT_RAW13_COPY_FIELD versus + * NXAST_RAW_REG_MOVE is somewhat difficult. Neither one is guaranteed to + * be supported by every OpenFlow 1.3 implementation. It would be ideal to + * probe for support. Until we have that ability, we currently prefer + * NXAST_RAW_REG_MOVE for backward compatibility with older Open vSwitch + * versions. */ + size_t start_ofs = out->size; if (ofp_version >= OFP15_VERSION) { - struct ofp15_action_copy_field *copy; - - copy = put_OFPAT15_COPY_FIELD(out); + struct ofp15_action_copy_field *copy = put_OFPAT15_COPY_FIELD(out); + copy->n_bits = htons(move->dst.n_bits); + copy->src_offset = htons(move->src.ofs); + copy->dst_offset = htons(move->dst.ofs); + out->size = out->size - sizeof copy->pad2; + nx_put_header(out, move->src.field->id, ofp_version, false); + nx_put_header(out, move->dst.field->id, ofp_version, false); + } else if (ofp_version == OFP13_VERSION + && move->ofpact.raw == ONFACT_RAW13_COPY_FIELD) { + struct onf_action_copy_field *copy = put_ONFACT13_COPY_FIELD(out); copy->n_bits = htons(move->dst.n_bits); copy->src_offset = htons(move->src.ofs); copy->dst_offset = htons(move->dst.ofs); - copy->oxm_id_len = htons(8); - copy->src = htonl(mf_oxm_header(move->src.field->id, ofp_version)); - copy->dst = htonl(mf_oxm_header(move->dst.field->id, ofp_version)); + out->size = out->size - sizeof copy->pad3; + nx_put_header(out, move->src.field->id, ofp_version, false); + nx_put_header(out, move->dst.field->id, ofp_version, false); } else { - struct nx_action_reg_move *narm; - - narm = put_NXAST_REG_MOVE(out); + struct nx_action_reg_move *narm = put_NXAST_REG_MOVE(out); narm->n_bits = htons(move->dst.n_bits); narm->src_ofs = htons(move->src.ofs); narm->dst_ofs = htons(move->dst.ofs); - narm->src = htonl(move->src.field->nxm_header); - narm->dst = htonl(move->dst.field->nxm_header); + nx_put_header(out, move->src.field->id, 0, false); + nx_put_header(out, move->dst.field->id, 0, false); } + pad_ofpat(out, start_ofs); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_REG_MOVE(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -1849,6 +2324,20 @@ format_REG_MOVE(const struct ofpact_reg_move *a, struct ds *s) nxm_format_reg_move(a, s); } +/* Action structure for OFPAT12_SET_FIELD. */ +struct ofp12_action_set_field { + ovs_be16 type; /* OFPAT12_SET_FIELD. */ + ovs_be16 len; /* Length is padded to 64 bits. */ + + /* Followed by: + * - An OXM header, value, and (in OpenFlow 1.5+) optionally a mask. + * - Enough 0-bytes to pad out to a multiple of 64 bits. + * + * The "pad" member is the beginning of the above. */ + uint8_t pad[4]; +}; +OFP_ASSERT(sizeof(struct ofp12_action_set_field) == 8); + /* Action structure for NXAST_REG_LOAD. * * Copies value[0:n_bits] to dst[ofs:ofs+n_bits], where a[b:c] denotes the bits @@ -1886,112 +2375,154 @@ struct nx_action_reg_load { }; OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24); -static enum ofperr -decode_NXAST_RAW_REG_LOAD(const struct nx_action_reg_load *narl, - struct ofpbuf *out) -{ - struct ofpact_reg_load *load; - - load = ofpact_put_REG_LOAD(out); - load->dst.field = mf_from_nxm_header(ntohl(narl->dst)); - load->dst.ofs = nxm_decode_ofs(narl->ofs_nbits); - load->dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits); - load->subvalue.be64[1] = narl->value; +/* Action structure for NXAST_REG_LOAD2. + * + * Compared to OFPAT_SET_FIELD, we can use this to set whole or partial fields + * in any OpenFlow version. Compared to NXAST_REG_LOAD, we can use this to set + * OXM experimenter fields. */ +struct nx_action_reg_load2 { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* At least 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_SET_FIELD. */ - /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in - * narl->value. */ - if (load->dst.n_bits < 64 && - ntohll(narl->value) >> load->dst.n_bits) { - return OFPERR_OFPBAC_BAD_ARGUMENT; + /* Followed by: + * - An NXM/OXM header, value, and optionally a mask. + * - Enough 0-bytes to pad out to a multiple of 64 bits. + * + * The "pad" member is the beginning of the above. */ + uint8_t pad[6]; +}; +OFP_ASSERT(sizeof(struct nx_action_reg_load2) == 16); + +static enum ofperr +decode_ofpat_set_field(const struct ofp12_action_set_field *oasf, + bool may_mask, struct ofpbuf *ofpacts) +{ + struct ofpbuf b = ofpbuf_const_initializer(oasf, ntohs(oasf->len)); + ofpbuf_pull(&b, OBJECT_OFFSETOF(oasf, pad)); + + struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); + enum ofperr error = nx_pull_entry(&b, &sf->field, &sf->value, + may_mask ? &sf->mask : NULL); + if (error) { + return (error == OFPERR_OFPBMC_BAD_MASK + ? OFPERR_OFPBAC_BAD_SET_MASK + : error); + } + if (!may_mask) { + memset(&sf->mask, 0xff, sf->field->n_bytes); } - return nxm_reg_load_check(load, NULL); -} + if (!is_all_zeros(b.data, b.size)) { + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; + } -static void -encode_REG_LOAD(const struct ofpact_reg_load *load, - enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) -{ - struct nx_action_reg_load *narl; + /* OpenFlow says specifically that one may not set OXM_OF_IN_PORT via + * Set-Field. */ + if (sf->field->id == MFF_IN_PORT_OXM) { + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; + } + + /* oxm_length is now validated to be compatible with mf_value. */ + if (!sf->field->writable) { + VLOG_WARN_RL(&rl, "destination field %s is not writable", + sf->field->name); + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; + } + + /* The value must be valid for match. OpenFlow 1.5 also says, + * "In an OXM_OF_VLAN_VID set-field action, the OFPVID_PRESENT bit must be + * a 1-bit in oxm_value and in oxm_mask." */ + if (!mf_is_value_valid(sf->field, &sf->value) + || (sf->field->id == MFF_VLAN_VID + && (!(sf->mask.be16 & htons(OFPVID12_PRESENT)) + || !(sf->value.be16 & htons(OFPVID12_PRESENT))))) { + struct ds ds = DS_EMPTY_INITIALIZER; + mf_format(sf->field, &sf->value, NULL, &ds); + VLOG_WARN_RL(&rl, "Invalid value for set field %s: %s", + sf->field->name, ds_cstr(&ds)); + ds_destroy(&ds); - narl = put_NXAST_REG_LOAD(out); - narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits); - narl->dst = htonl(load->dst.field->nxm_header); - narl->value = load->subvalue.be64[1]; + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; + } + return 0; } -static char * WARN_UNUSED_RESULT -parse_REG_LOAD(char *arg, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols OVS_UNUSED) +static enum ofperr +decode_OFPAT_RAW12_SET_FIELD(const struct ofp12_action_set_field *oasf, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *ofpacts) { - return nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); + return decode_ofpat_set_field(oasf, false, ofpacts); } -static void -format_REG_LOAD(const struct ofpact_reg_load *a, struct ds *s) +static enum ofperr +decode_OFPAT_RAW15_SET_FIELD(const struct ofp12_action_set_field *oasf, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *ofpacts) { - nxm_format_reg_load(a, s); + return decode_ofpat_set_field(oasf, true, ofpacts); } - -/* Action structure for OFPAT12_SET_FIELD. */ -struct ofp12_action_set_field { - ovs_be16 type; /* OFPAT12_SET_FIELD. */ - ovs_be16 len; /* Length is padded to 64 bits. */ - ovs_be32 dst; /* OXM TLV header */ - /* Followed by: - * - Exactly ((oxm_len + 4) + 7)/8*8 - (oxm_len + 4) (between 0 and 7) - * bytes of all-zero bytes - */ -}; -OFP_ASSERT(sizeof(struct ofp12_action_set_field) == 8); static enum ofperr -decode_OFPAT_RAW12_SET_FIELD(const struct ofp12_action_set_field *oasf, - struct ofpbuf *ofpacts) +decode_NXAST_RAW_REG_LOAD(const struct nx_action_reg_load *narl, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { - uint16_t oasf_len = ntohs(oasf->len); - uint32_t oxm_header = ntohl(oasf->dst); - uint8_t oxm_length = NXM_LENGTH(oxm_header); - struct ofpact_set_field *sf; - const struct mf_field *mf; + struct ofpact_set_field *sf = ofpact_put_reg_load(out); + struct mf_subfield dst; + enum ofperr error; - /* ofp12_action_set_field is padded to 64 bits by zero */ - if (oasf_len != ROUND_UP(sizeof *oasf + oxm_length, 8)) { - return OFPERR_OFPBAC_BAD_SET_LEN; - } - if (!is_all_zeros((const uint8_t *)oasf + sizeof *oasf + oxm_length, - oasf_len - oxm_length - sizeof *oasf)) { - return OFPERR_OFPBAC_BAD_SET_ARGUMENT; + sf->ofpact.raw = NXAST_RAW_REG_LOAD; + + dst.field = mf_from_nxm_header(ntohl(narl->dst)); + dst.ofs = nxm_decode_ofs(narl->ofs_nbits); + dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits); + error = mf_check_dst(&dst, NULL); + if (error) { + return error; } - if (NXM_HASMASK(oxm_header)) { - return OFPERR_OFPBAC_BAD_SET_MASK; + /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in + * narl->value. */ + if (dst.n_bits < 64 && ntohll(narl->value) >> dst.n_bits) { + return OFPERR_OFPBAC_BAD_ARGUMENT; } - mf = mf_from_nxm_header(oxm_header); - if (!mf) { - return OFPERR_OFPBAC_BAD_SET_TYPE; + + sf->field = dst.field; + bitwise_put(ntohll(narl->value), + &sf->value, dst.field->n_bytes, dst.ofs, + dst.n_bits); + bitwise_put(UINT64_MAX, + &sf->mask, dst.field->n_bytes, dst.ofs, + dst.n_bits); + + return 0; +} + +static enum ofperr +decode_NXAST_RAW_REG_LOAD2(const struct nx_action_reg_load2 *narl, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + struct ofpact_set_field *sf = ofpact_put_SET_FIELD(out); + sf->ofpact.raw = NXAST_RAW_REG_LOAD2; + + struct ofpbuf b = ofpbuf_const_initializer(narl, ntohs(narl->len)); + ofpbuf_pull(&b, OBJECT_OFFSETOF(narl, pad)); + + enum ofperr error = nx_pull_entry(&b, &sf->field, &sf->value, &sf->mask); + if (error) { + return error; } - ovs_assert(mf->n_bytes == oxm_length); - /* oxm_length is now validated to be compatible with mf_value. */ - if (!mf->writable) { - VLOG_WARN_RL(&rl, "destination field %s is not writable", mf->name); + if (!is_all_zeros(b.data, b.size)) { return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } - sf = ofpact_put_SET_FIELD(ofpacts); - sf->field = mf; - memcpy(&sf->value, oasf + 1, mf->n_bytes); - - /* The value must be valid for match and must have the OFPVID_PRESENT bit - * on for OXM_OF_VLAN_VID. */ - if (!mf_is_value_valid(mf, &sf->value) - || (mf->id == MFF_VLAN_VID - && !(sf->value.be16 & htons(OFPVID12_PRESENT)))) { - struct ds ds = DS_EMPTY_INITIALIZER; - mf_format(mf, &sf->value, NULL, &ds); - VLOG_WARN_RL(&rl, "Invalid value for set field %s: %s", - mf->name, ds_cstr(&ds)); - ds_destroy(&ds); + if (!sf->field->writable) { + VLOG_WARN_RL(&rl, "destination field %s is not writable", + sf->field->name); return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } return 0; @@ -1999,56 +2530,75 @@ decode_OFPAT_RAW12_SET_FIELD(const struct ofp12_action_set_field *oasf, static void ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version ofp_version, - enum mf_field_id field, uint64_t value) + enum mf_field_id field, uint64_t value_) { - const struct mf_field *mf = mf_from_id(field); - struct ofp12_action_set_field *oasf; - ovs_be64 n_value; + struct ofp12_action_set_field *oasf OVS_UNUSED; + int n_bytes = mf_from_id(field)->n_bytes; + size_t start_ofs = openflow->size; + union mf_value value; + + value.be64 = htonll(value_ << (8 * (8 - n_bytes))); oasf = put_OFPAT12_SET_FIELD(openflow); - oasf->dst = htonl(mf_oxm_header(mf->id, ofp_version)); - oasf->len = htons(sizeof *oasf + 8); + openflow->size = openflow->size - sizeof oasf->pad; + nx_put_entry(openflow, field, ofp_version, &value, NULL); + pad_ofpat(openflow, start_ofs); +} - ovs_assert(mf->n_bytes <= 8); - if (mf->n_bytes < 8) { - value <<= 8 * (8 - mf->n_bytes); +static bool +next_load_segment(const struct ofpact_set_field *sf, + struct mf_subfield *dst, uint64_t *value) +{ + int n_bits = sf->field->n_bits; + int n_bytes = sf->field->n_bytes; + int start = dst->ofs + dst->n_bits; + + if (start < n_bits) { + dst->field = sf->field; + dst->ofs = bitwise_scan(&sf->mask, n_bytes, 1, start, n_bits); + if (dst->ofs < n_bits) { + dst->n_bits = bitwise_scan(&sf->mask, n_bytes, 0, dst->ofs + 1, + MIN(dst->ofs + 64, n_bits)) - dst->ofs; + *value = bitwise_get(&sf->value, n_bytes, dst->ofs, dst->n_bits); + return true; + } } - n_value = htonll(value); - ofpbuf_put(openflow, &n_value, 8); + return false; } -/* Convert 'sf' to one or two REG_LOADs. */ +/* Convert 'sf' to a series of REG_LOADs. */ static void set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow) { - const struct mf_field *mf = sf->field; - struct nx_action_reg_load *narl; - - if (mf->n_bits > 64) { - ovs_assert(mf->n_bytes == 16); /* IPv6 addr. */ - /* Split into 64bit chunks */ - /* Lower bits first. */ - narl = put_NXAST_REG_LOAD(openflow); - narl->ofs_nbits = nxm_encode_ofs_nbits(0, 64); - narl->dst = htonl(mf->nxm_header); - memcpy(&narl->value, &sf->value.ipv6.s6_addr[8], sizeof narl->value); - /* Higher bits next. */ - narl = put_NXAST_REG_LOAD(openflow); - narl->ofs_nbits = nxm_encode_ofs_nbits(64, mf->n_bits - 64); - narl->dst = htonl(mf->nxm_header); - memcpy(&narl->value, &sf->value.ipv6.s6_addr[0], sizeof narl->value); + /* If 'sf' cannot be encoded as NXAST_REG_LOAD because it requires an + * experimenter OXM or is variable length (or if it came in as + * NXAST_REG_LOAD2), encode as NXAST_REG_LOAD2. Otherwise use + * NXAST_REG_LOAD, which is backward compatible. */ + if (sf->ofpact.raw == NXAST_RAW_REG_LOAD2 + || !mf_nxm_header(sf->field->id) || sf->field->variable_len) { + struct nx_action_reg_load2 *narl OVS_UNUSED; + size_t start_ofs = openflow->size; + + narl = put_NXAST_REG_LOAD2(openflow); + openflow->size = openflow->size - sizeof narl->pad; + nx_put_entry(openflow, sf->field->id, 0, &sf->value, &sf->mask); + pad_ofpat(openflow, start_ofs); } else { - narl = put_NXAST_REG_LOAD(openflow); - narl->ofs_nbits = nxm_encode_ofs_nbits(0, mf->n_bits); - narl->dst = htonl(mf->nxm_header); - memset(&narl->value, 0, 8 - mf->n_bytes); - memcpy((char*)&narl->value + (8 - mf->n_bytes), - &sf->value, mf->n_bytes); + struct mf_subfield dst; + uint64_t value; + + dst.ofs = dst.n_bits = 0; + while (next_load_segment(sf, &dst, &value)) { + struct nx_action_reg_load *narl = put_NXAST_REG_LOAD(openflow); + narl->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits); + narl->dst = htonl(mf_nxm_header(dst.field->id)); + narl->value = htonll(value); + } } } -/* Convert 'sf' to standard OpenFlow 1.0/1.1 actions, if we can, falling back - * to Nicira extensions if we must. +/* Convert 'sf', which must set an entire field, to standard OpenFlow 1.0/1.1 + * actions, if we can, falling back to Nicira extensions if we must. * * We check only meta-flow types that can appear within set field actions and * that have a mapping to compatible action types. These struct mf_field @@ -2127,13 +2677,11 @@ set_field_to_legacy_openflow(const struct ofpact_set_field *sf, break; case MFF_ETH_SRC: - memcpy(put_OFPAT_SET_DL_SRC(out, ofp_version)->dl_addr, - sf->value.mac, ETH_ADDR_LEN); + put_OFPAT_SET_DL_SRC(out, ofp_version)->dl_addr = sf->value.mac; break; case MFF_ETH_DST: - memcpy(put_OFPAT_SET_DL_DST(out, ofp_version)->dl_addr, - sf->value.mac, ETH_ADDR_LEN); + put_OFPAT_SET_DL_DST(out, ofp_version)->dl_addr = sf->value.mac; break; case MFF_IPV4_SRC: @@ -2168,24 +2716,77 @@ set_field_to_legacy_openflow(const struct ofpact_set_field *sf, } } +static void +set_field_to_set_field(const struct ofpact_set_field *sf, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + struct ofp12_action_set_field *oasf OVS_UNUSED; + size_t start_ofs = out->size; + + oasf = put_OFPAT12_SET_FIELD(out); + out->size = out->size - sizeof oasf->pad; + nx_put_entry(out, sf->field->id, ofp_version, &sf->value, &sf->mask); + pad_ofpat(out, start_ofs); +} + static void encode_SET_FIELD(const struct ofpact_set_field *sf, enum ofp_version ofp_version, struct ofpbuf *out) { - if (ofp_version < OFP12_VERSION) { + if (ofp_version >= OFP15_VERSION) { + /* OF1.5+ only has Set-Field (reg_load is redundant so we drop it + * entirely). */ + set_field_to_set_field(sf, ofp_version, out); + } else if (sf->ofpact.raw == NXAST_RAW_REG_LOAD || + sf->ofpact.raw == NXAST_RAW_REG_LOAD2) { + /* It came in as reg_load, send it out the same way. */ + set_field_to_nxast(sf, out); + } else if (ofp_version < OFP12_VERSION) { + /* OpenFlow 1.0 and 1.1 don't have Set-Field. */ set_field_to_legacy_openflow(sf, ofp_version, out); + } else if (is_all_ones((const uint8_t *) &sf->mask, sf->field->n_bytes)) { + /* We're encoding to OpenFlow 1.2, 1.3, or 1.4. The action sets an + * entire field, so encode it as OFPAT_SET_FIELD. */ + set_field_to_set_field(sf, ofp_version, out); } else { - uint16_t padded_value_len = ROUND_UP(sf->field->n_bytes, 8); - struct ofp12_action_set_field *oasf; - char *value; + /* We're encoding to OpenFlow 1.2, 1.3, or 1.4. The action cannot be + * encoded as OFPAT_SET_FIELD because it does not set an entire field, + * so encode it as reg_load. */ + set_field_to_nxast(sf, out); + } +} + +/* Parses the input argument 'arg' into the key, value, and delimiter + * components that are common across the reg_load and set_field action format. + * + * With an argument like "1->metadata", sets the following pointers to + * point within 'arg': + * key: "metadata" + * value: "1" + * delim: "->" + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * OVS_WARN_UNUSED_RESULT +set_field_split_str(char *arg, char **key, char **value, char **delim) +{ + char *value_end; - oasf = ofpact_put_raw(out, ofp_version, OFPAT_RAW12_SET_FIELD, 0); - oasf->dst = htonl(mf_oxm_header(sf->field->id, ofp_version)); - oasf->len = htons(sizeof *oasf + padded_value_len); + *value = arg; + value_end = strstr(arg, "->"); + *key = value_end + strlen("->"); + if (delim) { + *delim = value_end; + } - value = ofpbuf_put_zeros(out, padded_value_len); - memcpy(value, &sf->value, sf->field->n_bytes); + if (!value_end) { + return xasprintf("%s: missing `->'", arg); + } + if (strlen(value_end) <= strlen("->")) { + return xasprintf("%s: missing field name following `->'", arg); } + + return NULL; } /* Parses a "set_field" action with argument 'arg', appending the parsed @@ -2193,7 +2794,7 @@ encode_SET_FIELD(const struct ofpact_set_field *sf, * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT set_field_parse__(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { @@ -2204,16 +2805,11 @@ set_field_parse__(char *arg, struct ofpbuf *ofpacts, const struct mf_field *mf; char *error; - value = arg; - delim = strstr(arg, "->"); - if (!delim) { - return xasprintf("%s: missing `->'", arg); - } - if (strlen(delim) <= strlen("->")) { - return xasprintf("%s: missing field name following `->'", arg); + error = set_field_split_str(arg, &key, &value, &delim); + if (error) { + return error; } - key = delim + strlen("->"); mf = mf_from_name(key); if (!mf) { return xasprintf("%s is not a valid OXM field name", key); @@ -2223,7 +2819,7 @@ set_field_parse__(char *arg, struct ofpbuf *ofpacts, } sf->field = mf; delim[0] = '\0'; - error = mf_parse_value(mf, value, &sf->value); + error = mf_parse(mf, value, &sf->value, &sf->mask); if (error) { return error; } @@ -2232,7 +2828,7 @@ set_field_parse__(char *arg, struct ofpbuf *ofpacts, return xasprintf("%s is not a valid value for field %s", value, key); } - *usable_protocols &= mf->usable_protocols; + *usable_protocols &= mf->usable_protocols_exact; return NULL; } @@ -2241,7 +2837,7 @@ set_field_parse__(char *arg, struct ofpbuf *ofpacts, * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_FIELD(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { @@ -2251,12 +2847,83 @@ parse_SET_FIELD(const char *arg, struct ofpbuf *ofpacts, return error; } +static char * OVS_WARN_UNUSED_RESULT +parse_reg_load(char *arg, struct ofpbuf *ofpacts) +{ + struct ofpact_set_field *sf = ofpact_put_reg_load(ofpacts); + struct mf_subfield dst; + char *key, *value_str; + union mf_value value; + char *error; + + error = set_field_split_str(arg, &key, &value_str, NULL); + if (error) { + return error; + } + + error = mf_parse_subfield(&dst, key); + if (error) { + return error; + } + + if (parse_int_string(value_str, (uint8_t *)&value, dst.field->n_bytes, + &key)) { + return xasprintf("%s: cannot parse integer value", arg); + } + + if (!bitwise_is_all_zeros(&value, dst.field->n_bytes, dst.n_bits, + dst.field->n_bytes * 8 - dst.n_bits)) { + struct ds ds; + + ds_init(&ds); + mf_format(dst.field, &value, NULL, &ds); + error = xasprintf("%s: value %s does not fit into %d bits", + arg, ds_cstr(&ds), dst.n_bits); + ds_destroy(&ds); + return error; + } + + sf->field = dst.field; + memset(&sf->value, 0, sizeof sf->value); + bitwise_copy(&value, dst.field->n_bytes, 0, &sf->value, + dst.field->n_bytes, dst.ofs, dst.n_bits); + bitwise_one(&sf->mask, dst.field->n_bytes, dst.ofs, dst.n_bits); + + return NULL; +} + static void format_SET_FIELD(const struct ofpact_set_field *a, struct ds *s) { - ds_put_format(s, "set_field:"); - mf_format(a->field, &a->value, NULL, s); - ds_put_format(s, "->%s", a->field->name); + if (a->ofpact.raw == NXAST_RAW_REG_LOAD) { + struct mf_subfield dst; + uint64_t value; + + dst.ofs = dst.n_bits = 0; + while (next_load_segment(a, &dst, &value)) { + ds_put_format(s, "load:%#"PRIx64"->", value); + mf_format_subfield(&dst, s); + ds_put_char(s, ','); + } + ds_chomp(s, ','); + } else { + ds_put_cstr(s, "set_field:"); + mf_format(a->field, &a->value, &a->mask, s); + ds_put_format(s, "->%s", a->field->name); + } +} + +/* Appends an OFPACT_SET_FIELD ofpact to 'ofpacts' and returns it. The ofpact + * is marked such that, if possible, it will be translated to OpenFlow as + * NXAST_REG_LOAD extension actions rather than OFPAT_SET_FIELD, either because + * that was the way that the action was expressed when it came into OVS or for + * backward compatibility. */ +struct ofpact_set_field * +ofpact_put_reg_load(struct ofpbuf *ofpacts) +{ + struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); + sf->ofpact.raw = NXAST_RAW_REG_LOAD; + return sf; } /* Action structure for NXAST_STACK_PUSH and NXAST_STACK_POP. @@ -2270,46 +2937,70 @@ struct nx_action_stack { ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_STACK_PUSH or NXAST_STACK_POP. */ ovs_be16 offset; /* Bit offset into the field. */ - ovs_be32 field; /* The field used for push or pop. */ - ovs_be16 n_bits; /* (n_bits + 1) bits of the field. */ - uint8_t zero[6]; /* Reserved, must be zero. */ + /* Followed by: + * - OXM/NXM header for field to push or pop (4 or 8 bytes). + * - ovs_be16 'n_bits', the number of bits to extract from the field. + * - Enough 0-bytes to pad out the action to 24 bytes. */ + uint8_t pad[12]; /* See above. */ }; OFP_ASSERT(sizeof(struct nx_action_stack) == 24); -static void +static enum ofperr decode_stack_action(const struct nx_action_stack *nasp, struct ofpact_stack *stack_action) { - stack_action->subfield.field = mf_from_nxm_header(ntohl(nasp->field)); stack_action->subfield.ofs = ntohs(nasp->offset); - stack_action->subfield.n_bits = ntohs(nasp->n_bits); + + struct ofpbuf b = ofpbuf_const_initializer(nasp, sizeof *nasp); + ofpbuf_pull(&b, OBJECT_OFFSETOF(nasp, pad)); + enum ofperr error = nx_pull_header(&b, &stack_action->subfield.field, + NULL); + if (error) { + return error; + } + stack_action->subfield.n_bits = ntohs(*(const ovs_be16 *) b.data); + ofpbuf_pull(&b, 2); + if (!is_all_zeros(b.data, b.size)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + + return 0; } static enum ofperr decode_NXAST_RAW_STACK_PUSH(const struct nx_action_stack *nasp, - struct ofpbuf *ofpacts) + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *ofpacts) { struct ofpact_stack *push = ofpact_put_STACK_PUSH(ofpacts); - decode_stack_action(nasp, push); - return nxm_stack_push_check(push, NULL); + enum ofperr error = decode_stack_action(nasp, push); + return error ? error : nxm_stack_push_check(push, NULL); } static enum ofperr decode_NXAST_RAW_STACK_POP(const struct nx_action_stack *nasp, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { struct ofpact_stack *pop = ofpact_put_STACK_POP(ofpacts); - decode_stack_action(nasp, pop); - return nxm_stack_pop_check(pop, NULL); + enum ofperr error = decode_stack_action(nasp, pop); + return error ? error : nxm_stack_pop_check(pop, NULL); } static void encode_STACK_op(const struct ofpact_stack *stack_action, struct nx_action_stack *nasp) { + struct ofpbuf b; + ovs_be16 n_bits; + nasp->offset = htons(stack_action->subfield.ofs); - nasp->n_bits = htons(stack_action->subfield.n_bits); - nasp->field = htonl(stack_action->subfield.field->nxm_header); + + ofpbuf_use_stack(&b, nasp, ntohs(nasp->len)); + ofpbuf_put_uninit(&b, OBJECT_OFFSETOF(nasp, pad)); + nx_put_header(&b, stack_action->subfield.field->id, 0, false); + n_bits = htons(stack_action->subfield.n_bits); + ofpbuf_put(&b, &n_bits, sizeof n_bits); } static void @@ -2326,14 +3017,14 @@ encode_STACK_POP(const struct ofpact_stack *stack, encode_STACK_op(stack, put_NXAST_STACK_POP(out)); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_STACK_PUSH(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_STACK_POP(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2389,13 +3080,14 @@ decode_OFPAT_RAW_DEC_NW_TTL(struct ofpbuf *out) ids = ofpact_put_DEC_TTL(out); ids->n_controllers = 1; ofpbuf_put(out, &id, sizeof id); - ids = out->frame; - ofpact_update_len(out, &ids->ofpact); + ids = out->header; + ofpact_finish(out, &ids->ofpact); return error; } static enum ofperr decode_NXAST_RAW_DEC_TTL_CNT_IDS(const struct nx_action_cnt_ids *nac_ids, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_cnt_ids *ids; @@ -2423,10 +3115,10 @@ decode_NXAST_RAW_DEC_TTL_CNT_IDS(const struct nx_action_cnt_ids *nac_ids, for (i = 0; i < ids->n_controllers; i++) { uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]); ofpbuf_put(out, &id, sizeof id); - ids = out->frame; + ids = out->header; } - ofpact_update_len(out, &ids->ofpact); + ofpact_finish(out, &ids->ofpact); return 0; } @@ -2463,12 +3155,12 @@ parse_noargs_dec_ttl(struct ofpbuf *ofpacts) ofpact_put_DEC_TTL(ofpacts); ofpbuf_put(ofpacts, &id, sizeof id); - ids = ofpacts->frame; + ids = ofpacts->header; ids->n_controllers++; - ofpact_update_len(ofpacts, &ids->ofpact); + ofpact_finish(ofpacts, &ids->ofpact); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_DEC_TTL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2485,14 +3177,14 @@ parse_DEC_TTL(char *arg, struct ofpbuf *ofpacts, uint16_t id = atoi(cntr); ofpbuf_put(ofpacts, &id, sizeof id); - ids = ofpacts->frame; + ids = ofpacts->header; ids->n_controllers++; } if (!ids->n_controllers) { return xstrdup("dec_ttl_cnt_ids: expected at least one controller " "id."); } - ofpact_update_len(ofpacts, &ids->ofpact); + ofpact_finish(ofpacts, &ids->ofpact); } return NULL; } @@ -2518,7 +3210,9 @@ format_DEC_TTL(const struct ofpact_cnt_ids *a, struct ds *s) /* Set MPLS label actions. */ static enum ofperr -decode_OFPAT_RAW_SET_MPLS_LABEL(ovs_be32 label, struct ofpbuf *out) +decode_OFPAT_RAW_SET_MPLS_LABEL(ovs_be32 label, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_SET_MPLS_LABEL(out)->label = label; return 0; @@ -2537,7 +3231,7 @@ encode_SET_MPLS_LABEL(const struct ofpact_mpls_label *label, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_MPLS_LABEL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2559,7 +3253,9 @@ format_SET_MPLS_LABEL(const struct ofpact_mpls_label *a, struct ds *s) /* Set MPLS TC actions. */ static enum ofperr -decode_OFPAT_RAW_SET_MPLS_TC(uint8_t tc, struct ofpbuf *out) +decode_OFPAT_RAW_SET_MPLS_TC(uint8_t tc, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_SET_MPLS_TC(out)->tc = tc; return 0; @@ -2576,7 +3272,7 @@ encode_SET_MPLS_TC(const struct ofpact_mpls_tc *tc, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_MPLS_TC(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2599,7 +3295,9 @@ format_SET_MPLS_TC(const struct ofpact_mpls_tc *a, struct ds *s) /* Set MPLS TTL actions. */ static enum ofperr -decode_OFPAT_RAW_SET_MPLS_TTL(uint8_t ttl, struct ofpbuf *out) +decode_OFPAT_RAW_SET_MPLS_TTL(uint8_t ttl, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_SET_MPLS_TTL(out)->ttl = ttl; return 0; @@ -2617,7 +3315,7 @@ encode_SET_MPLS_TTL(const struct ofpact_mpls_ttl *ttl, * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_MPLS_TTL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2653,7 +3351,7 @@ encode_DEC_MPLS_TTL(const struct ofpact_null *null OVS_UNUSED, put_OFPAT_DEC_MPLS_TTL(out, ofp_version); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_DEC_MPLS_TTL(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2670,7 +3368,9 @@ format_DEC_MPLS_TTL(const struct ofpact_null *a OVS_UNUSED, struct ds *s) /* Push MPLS label action. */ static enum ofperr -decode_OFPAT_RAW_PUSH_MPLS(ovs_be16 ethertype, struct ofpbuf *out) +decode_OFPAT_RAW_PUSH_MPLS(ovs_be16 ethertype, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { struct ofpact_push_mpls *oam; @@ -2690,7 +3390,7 @@ encode_PUSH_MPLS(const struct ofpact_push_mpls *push_mpls, put_OFPAT_PUSH_MPLS(out, ofp_version, push_mpls->ethertype); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_PUSH_MPLS(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2713,7 +3413,9 @@ format_PUSH_MPLS(const struct ofpact_push_mpls *a, struct ds *s) /* Pop MPLS label action. */ static enum ofperr -decode_OFPAT_RAW_POP_MPLS(ovs_be16 ethertype, struct ofpbuf *out) +decode_OFPAT_RAW_POP_MPLS(ovs_be16 ethertype, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_POP_MPLS(out)->ethertype = ethertype; return 0; @@ -2726,7 +3428,7 @@ encode_POP_MPLS(const struct ofpact_pop_mpls *pop_mpls, put_OFPAT_POP_MPLS(out, ofp_version, pop_mpls->ethertype); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_POP_MPLS(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2749,7 +3451,9 @@ format_POP_MPLS(const struct ofpact_pop_mpls *a, struct ds *s) /* Set tunnel ID actions. */ static enum ofperr -decode_NXAST_RAW_SET_TUNNEL(uint32_t tun_id, struct ofpbuf *out) +decode_NXAST_RAW_SET_TUNNEL(uint32_t tun_id, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { struct ofpact_tunnel *tunnel = ofpact_put_SET_TUNNEL(out); tunnel->ofpact.raw = NXAST_RAW_SET_TUNNEL; @@ -2758,7 +3462,9 @@ decode_NXAST_RAW_SET_TUNNEL(uint32_t tun_id, struct ofpbuf *out) } static enum ofperr -decode_NXAST_RAW_SET_TUNNEL64(uint64_t tun_id, struct ofpbuf *out) +decode_NXAST_RAW_SET_TUNNEL64(uint64_t tun_id, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { struct ofpact_tunnel *tunnel = ofpact_put_SET_TUNNEL(out); tunnel->ofpact.raw = NXAST_RAW_SET_TUNNEL64; @@ -2784,7 +3490,7 @@ encode_SET_TUNNEL(const struct ofpact_tunnel *tunnel, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_set_tunnel(char *arg, struct ofpbuf *ofpacts, enum ofp_raw_action_type raw) { @@ -2795,7 +3501,7 @@ parse_set_tunnel(char *arg, struct ofpbuf *ofpacts, return str_to_u64(arg, &tunnel->tun_id); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_TUNNEL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2814,7 +3520,9 @@ format_SET_TUNNEL(const struct ofpact_tunnel *a, struct ds *s) /* Set queue action. */ static enum ofperr -decode_OFPAT_RAW_SET_QUEUE(uint32_t queue_id, struct ofpbuf *out) +decode_OFPAT_RAW_SET_QUEUE(uint32_t queue_id, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { ofpact_put_SET_QUEUE(out)->queue_id = queue_id; return 0; @@ -2827,7 +3535,7 @@ encode_SET_QUEUE(const struct ofpact_queue *queue, put_OFPAT_SET_QUEUE(out, ofp_version, queue->queue_id); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_SET_QUEUE(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2856,7 +3564,7 @@ encode_POP_QUEUE(const struct ofpact_null *null OVS_UNUSED, put_NXAST_POP_QUEUE(out); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_POP_QUEUE(const char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -2906,6 +3614,7 @@ OFP_ASSERT(sizeof(struct nx_action_fin_timeout) == 16); static enum ofperr decode_NXAST_RAW_FIN_TIMEOUT(const struct nx_action_fin_timeout *naft, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_fin_timeout *oft; @@ -2926,7 +3635,7 @@ encode_FIN_TIMEOUT(const struct ofpact_fin_timeout *fin_timeout, naft->fin_hard_timeout = htons(fin_timeout->fin_hard_timeout); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_FIN_TIMEOUT(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -3024,7 +3733,9 @@ struct nx_action_resubmit { OFP_ASSERT(sizeof(struct nx_action_resubmit) == 16); static enum ofperr -decode_NXAST_RAW_RESUBMIT(uint16_t port, struct ofpbuf *out) +decode_NXAST_RAW_RESUBMIT(uint16_t port, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { struct ofpact_resubmit *resubmit; @@ -3037,6 +3748,7 @@ decode_NXAST_RAW_RESUBMIT(uint16_t port, struct ofpbuf *out) static enum ofperr decode_NXAST_RAW_RESUBMIT_TABLE(const struct nx_action_resubmit *nar, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_resubmit *resubmit; @@ -3068,7 +3780,7 @@ encode_RESUBMIT(const struct ofpact_resubmit *resubmit, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_RESUBMIT(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -3320,7 +4032,24 @@ format_RESUBMIT(const struct ofpact_resubmit *a, struct ds *s) * address. This is not usually the intent in MAC learning; instead, we want * the MAC learn entry to expire when no traffic has been sent *from* the * learned address. Use a hard timeout for that. - */ + * + * + * Visibility of Changes + * --------------------- + * + * Prior to Open vSwitch 2.4, any changes made by a "learn" action in a given + * flow translation are visible to flow table lookups made later in the flow + * translation. This means that, in the example above, a MAC learned by the + * learn action in table 0 would be found in table 1 (if the packet being + * processed had the same source and destination MAC address). + * + * In Open vSwitch 2.4 and later, changes to a flow table (whether to add or + * modify a flow) by a "learn" action are visible only for later flow + * translations, not for later lookups within the same flow translation. In + * the MAC learning example, a MAC learned by the learn action in table 0 would + * not be found in table 1 if the flow translation would resubmit to table 1 + * after the processing of the learn action, meaning that if this MAC had not + * been learned before then the packet would be flooded. */ struct nx_action_learn { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* At least 24. */ @@ -3393,6 +4122,7 @@ learn_min_len(uint16_t header) * 'ofpacts'. Returns 0 if successful, otherwise an OFPERR_*. */ static enum ofperr decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; @@ -3432,7 +4162,7 @@ decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal, } spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); - learn = ofpacts->frame; + learn = ofpacts->header; learn->n_specs++; spec->src_type = header & NX_LEARN_SRC_MASK; @@ -3472,7 +4202,7 @@ decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal, get_subfield(spec->n_bits, &p, &spec->dst); } } - ofpact_update_len(ofpacts, &learn->ofpact); + ofpact_finish(ofpacts, &learn->ofpact); if (!is_all_zeros(p, (char *) end - (char *) p)) { return OFPERR_OFPBAC_BAD_ARGUMENT; @@ -3513,7 +4243,7 @@ encode_LEARN(const struct ofpact_learn *learn, struct nx_action_learn *nal; size_t start_ofs; - start_ofs = ofpbuf_size(out); + start_ofs = out->size; nal = put_NXAST_LEARN(out); nal->idle_timeout = htons(learn->idle_timeout); nal->hard_timeout = htons(learn->hard_timeout); @@ -3528,7 +4258,7 @@ encode_LEARN(const struct ofpact_learn *learn, put_u16(out, spec->n_bits | spec->dst_type | spec->src_type); if (spec->src_type == NX_LEARN_SRC_FIELD) { - put_u32(out, spec->src.field->nxm_header); + put_u32(out, mf_nxm_header(spec->src.field->id)); put_u16(out, spec->src.ofs); } else { size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); @@ -3540,20 +4270,15 @@ encode_LEARN(const struct ofpact_learn *learn, if (spec->dst_type == NX_LEARN_DST_MATCH || spec->dst_type == NX_LEARN_DST_LOAD) { - put_u32(out, spec->dst.field->nxm_header); + put_u32(out, mf_nxm_header(spec->dst.field->id)); put_u16(out, spec->dst.ofs); } } - if ((ofpbuf_size(out) - start_ofs) % 8) { - ofpbuf_put_zeros(out, 8 - (ofpbuf_size(out) - start_ofs) % 8); - } - - nal = ofpbuf_at_assert(out, start_ofs, sizeof *nal); - nal->len = htons(ofpbuf_size(out) - start_ofs); + pad_ofpat(out, start_ofs); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_LEARN(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -3566,6 +4291,90 @@ format_LEARN(const struct ofpact_learn *a, struct ds *s) learn_format(a, s); } +/* Action structure for NXAST_CONJUNCTION. */ +struct nx_action_conjunction { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* At least 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* See enum ofp_raw_action_type. */ + uint8_t clause; + uint8_t n_clauses; + ovs_be32 id; +}; +OFP_ASSERT(sizeof(struct nx_action_conjunction) == 16); + +static void +add_conjunction(struct ofpbuf *out, + uint32_t id, uint8_t clause, uint8_t n_clauses) +{ + struct ofpact_conjunction *oc; + + oc = ofpact_put_CONJUNCTION(out); + oc->id = id; + oc->clause = clause; + oc->n_clauses = n_clauses; +} + +static enum ofperr +decode_NXAST_RAW_CONJUNCTION(const struct nx_action_conjunction *nac, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + if (nac->n_clauses < 2 || nac->n_clauses > 64 + || nac->clause >= nac->n_clauses) { + return OFPERR_NXBAC_BAD_CONJUNCTION; + } else { + add_conjunction(out, ntohl(nac->id), nac->clause, nac->n_clauses); + return 0; + } +} + +static void +encode_CONJUNCTION(const struct ofpact_conjunction *oc, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + struct nx_action_conjunction *nac = put_NXAST_CONJUNCTION(out); + nac->clause = oc->clause; + nac->n_clauses = oc->n_clauses; + nac->id = htonl(oc->id); +} + +static void +format_CONJUNCTION(const struct ofpact_conjunction *oc, struct ds *s) +{ + ds_put_format(s, "conjunction(%"PRIu32",%"PRIu8"/%"PRIu8")", + oc->id, oc->clause + 1, oc->n_clauses); +} + +static char * OVS_WARN_UNUSED_RESULT +parse_CONJUNCTION(const char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + uint8_t n_clauses; + uint8_t clause; + uint32_t id; + int n; + + if (!ovs_scan(arg, "%"SCNi32" , %"SCNu8" / %"SCNu8" %n", + &id, &clause, &n_clauses, &n) || n != strlen(arg)) { + return xstrdup("\"conjunction\" syntax is \"conjunction(id,i/n)\""); + } + + if (n_clauses < 2) { + return xstrdup("conjunction must have at least 2 clauses"); + } else if (n_clauses > 64) { + return xstrdup("conjunction must have at most 64 clauses"); + } else if (clause < 1) { + return xstrdup("clause index must be positive"); + } else if (clause > n_clauses) { + return xstrdup("clause index must be less than or equal to " + "number of clauses"); + } + + add_conjunction(ofpacts, id, clause - 1, n_clauses); + return NULL; +} + /* Action structure for NXAST_MULTIPATH. * * This action performs the following steps in sequence: @@ -3624,6 +4433,7 @@ OFP_ASSERT(sizeof(struct nx_action_multipath) == 32); static enum ofperr decode_NXAST_RAW_MULTIPATH(const struct nx_action_multipath *nam, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { uint32_t n_links = ntohs(nam->max_link) + 1; @@ -3670,10 +4480,10 @@ encode_MULTIPATH(const struct ofpact_multipath *mp, nam->max_link = htons(mp->max_link); nam->arg = htonl(mp->arg); nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits); - nam->dst = htonl(mp->dst.field->nxm_header); + nam->dst = htonl(mf_nxm_header(mp->dst.field->id)); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_MULTIPATH(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -3705,16 +4515,18 @@ struct nx_action_note { OFP_ASSERT(sizeof(struct nx_action_note) == 16); static enum ofperr -decode_NXAST_RAW_NOTE(const struct nx_action_note *nan, struct ofpbuf *out) +decode_NXAST_RAW_NOTE(const struct nx_action_note *nan, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) { struct ofpact_note *note; unsigned int length; length = ntohs(nan->len) - offsetof(struct nx_action_note, note); - note = ofpact_put(out, OFPACT_NOTE, - offsetof(struct ofpact_note, data) + length); + note = ofpact_put_NOTE(out); note->length = length; - memcpy(note->data, nan->note, length); + ofpbuf_put(out, nan->note, length); + ofpact_finish(out, out->header); return 0; } @@ -3723,206 +4535,941 @@ static void encode_NOTE(const struct ofpact_note *note, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { - size_t start_ofs = ofpbuf_size(out); + size_t start_ofs = out->size; struct nx_action_note *nan; - unsigned int remainder; - unsigned int len; put_NXAST_NOTE(out); - ofpbuf_set_size(out, ofpbuf_size(out) - sizeof nan->note); + out->size = out->size - sizeof nan->note; ofpbuf_put(out, note->data, note->length); - - len = ofpbuf_size(out) - start_ofs; - remainder = len % OFP_ACTION_ALIGN; - if (remainder) { - ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder); - } - nan = ofpbuf_at(out, start_ofs, sizeof *nan); - nan->len = htons(ofpbuf_size(out) - start_ofs); + pad_ofpat(out, start_ofs); } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_NOTE(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { - struct ofpact_note *note; - - note = ofpact_put_NOTE(ofpacts); - while (*arg != '\0') { - uint8_t byte; - bool ok; + size_t start_ofs = ofpacts->size; + ofpact_put_NOTE(ofpacts); + arg = ofpbuf_put_hex(ofpacts, arg, NULL); + if (arg[0]) { + return xstrdup("bad hex digit in `note' argument"); + } + struct ofpact_note *note = ofpbuf_at_assert(ofpacts, start_ofs, + sizeof *note); + note->length = ofpacts->size - (start_ofs + sizeof *note); + ofpact_finish(ofpacts, ¬e->ofpact); + return NULL; +} - if (*arg == '.') { - arg++; - } - if (*arg == '\0') { - break; - } +static void +format_NOTE(const struct ofpact_note *a, struct ds *s) +{ + ds_put_cstr(s, "note:"); + format_hex_arg(s, a->data, a->length); +} + +/* Exit action. */ - byte = hexits_value(arg, 2, &ok); - if (!ok) { - return xstrdup("bad hex digit in `note' argument"); - } - ofpbuf_put(ofpacts, &byte, 1); +static enum ofperr +decode_NXAST_RAW_EXIT(struct ofpbuf *out) +{ + ofpact_put_EXIT(out); + return 0; +} - note = ofpacts->frame; - note->length++; +static void +encode_EXIT(const struct ofpact_null *null OVS_UNUSED, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + put_NXAST_EXIT(out); +} - arg += 2; - } - ofpact_update_len(ofpacts, ¬e->ofpact); +static char * OVS_WARN_UNUSED_RESULT +parse_EXIT(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + ofpact_put_EXIT(ofpacts); return NULL; } -static void -format_NOTE(const struct ofpact_note *a, struct ds *s) -{ - size_t i; +static void +format_EXIT(const struct ofpact_null *a OVS_UNUSED, struct ds *s) +{ + ds_put_cstr(s, "exit"); +} + +/* Unroll xlate action. */ + +static void +encode_UNROLL_XLATE(const struct ofpact_unroll_xlate *unroll OVS_UNUSED, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out OVS_UNUSED) +{ + OVS_NOT_REACHED(); +} + +static char * OVS_WARN_UNUSED_RESULT +parse_UNROLL_XLATE(char *arg OVS_UNUSED, struct ofpbuf *ofpacts OVS_UNUSED, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + OVS_NOT_REACHED(); + return NULL; +} + +static void +format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a, struct ds *s) +{ + ds_put_format(s, "unroll_xlate(table=%"PRIu8", cookie=%"PRIu64")", + a->rule_table_id, ntohll(a->rule_cookie)); +} + +/* Action structure for NXAST_SAMPLE. + * + * Samples matching packets with the given probability and sends them + * each to the set of collectors identified with the given ID. The + * probability is expressed as a number of packets to be sampled out + * of USHRT_MAX packets, and must be >0. + * + * When sending packet samples to IPFIX collectors, the IPFIX flow + * record sent for each sampled packet is associated with the given + * observation domain ID and observation point ID. Each IPFIX flow + * record contain the sampled packet's headers when executing this + * rule. If a sampled packet's headers are modified by previous + * actions in the flow, those modified headers are sent. */ +struct nx_action_sample { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 24. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_SAMPLE. */ + ovs_be16 probability; /* Fraction of packets to sample. */ + ovs_be32 collector_set_id; /* ID of collector set in OVSDB. */ + ovs_be32 obs_domain_id; /* ID of sampling observation domain. */ + ovs_be32 obs_point_id; /* ID of sampling observation point. */ +}; +OFP_ASSERT(sizeof(struct nx_action_sample) == 24); + +static enum ofperr +decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + struct ofpact_sample *sample; + + sample = ofpact_put_SAMPLE(out); + sample->probability = ntohs(nas->probability); + sample->collector_set_id = ntohl(nas->collector_set_id); + sample->obs_domain_id = ntohl(nas->obs_domain_id); + sample->obs_point_id = ntohl(nas->obs_point_id); + + if (sample->probability == 0) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + return 0; +} + +static void +encode_SAMPLE(const struct ofpact_sample *sample, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + struct nx_action_sample *nas; + + nas = put_NXAST_SAMPLE(out); + nas->probability = htons(sample->probability); + nas->collector_set_id = htonl(sample->collector_set_id); + nas->obs_domain_id = htonl(sample->obs_domain_id); + nas->obs_point_id = htonl(sample->obs_point_id); +} + +/* Parses 'arg' as the argument to a "sample" action, and appends such an + * action to 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * OVS_WARN_UNUSED_RESULT +parse_SAMPLE(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts); + char *key, *value; + + while (ofputil_parse_key_value(&arg, &key, &value)) { + char *error = NULL; + + if (!strcmp(key, "probability")) { + error = str_to_u16(value, "probability", &os->probability); + if (!error && os->probability == 0) { + error = xasprintf("invalid probability value \"%s\"", value); + } + } else if (!strcmp(key, "collector_set_id")) { + error = str_to_u32(value, &os->collector_set_id); + } else if (!strcmp(key, "obs_domain_id")) { + error = str_to_u32(value, &os->obs_domain_id); + } else if (!strcmp(key, "obs_point_id")) { + error = str_to_u32(value, &os->obs_point_id); + } else { + error = xasprintf("invalid key \"%s\" in \"sample\" argument", + key); + } + if (error) { + return error; + } + } + if (os->probability == 0) { + return xstrdup("non-zero \"probability\" must be specified on sample"); + } + return NULL; +} + +static void +format_SAMPLE(const struct ofpact_sample *a, struct ds *s) +{ + ds_put_format(s, "sample(probability=%"PRIu16",collector_set_id=%"PRIu32 + ",obs_domain_id=%"PRIu32",obs_point_id=%"PRIu32")", + a->probability, a->collector_set_id, + a->obs_domain_id, a->obs_point_id); +} + +/* debug_recirc instruction. */ + +static bool enable_debug; + +void +ofpact_dummy_enable(void) +{ + enable_debug = true; +} + +static enum ofperr +decode_NXAST_RAW_DEBUG_RECIRC(struct ofpbuf *out) +{ + if (!enable_debug) { + return OFPERR_OFPBAC_BAD_VENDOR_TYPE; + } + + ofpact_put_DEBUG_RECIRC(out); + return 0; +} + +static void +encode_DEBUG_RECIRC(const struct ofpact_null *n OVS_UNUSED, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + put_NXAST_DEBUG_RECIRC(out); +} + +static char * OVS_WARN_UNUSED_RESULT +parse_DEBUG_RECIRC(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + ofpact_put_DEBUG_RECIRC(ofpacts); + return NULL; +} + +static void +format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED, struct ds *s) +{ + ds_put_cstr(s, "debug_recirc"); +} + +/* Action structure for NXAST_CT. + * + * Pass traffic to the connection tracker. + * + * There are two important concepts to understanding the connection tracking + * interface: Packet state and Connection state. Packets may be "Untracked" or + * "Tracked". Connections may be "Uncommitted" or "Committed". + * + * - Packet State: + * + * Untracked packets have not yet passed through the connection tracker, + * and the connection state for such packets is unknown. In most cases, + * packets entering the OpenFlow pipeline will initially be in the + * untracked state. Untracked packets may become tracked by executing + * NXAST_CT with a "recirc_table" specified. This makes various aspects + * about the connection available, in particular the connection state. + * + * Tracked packets have previously passed through the connection tracker. + * These packets will remain tracked through until the end of the OpenFlow + * pipeline. Tracked packets which have NXAST_CT executed with a + * "recirc_table" specified will return to the tracked state. + * + * The packet state is only significant for the duration of packet + * processing within the OpenFlow pipeline. + * + * - Connection State: + * + * Multiple packets may be associated with a single connection. Initially, + * all connections are uncommitted. The connection state corresponding to + * a packet is available in the NXM_NX_CT_STATE field for tracked packets. + * + * Uncommitted connections have no state stored about them. Uncommitted + * connections may transition into the committed state by executing + * NXAST_CT with the NX_CT_F_COMMIT flag. + * + * Once a connection becomes committed, information may be gathered about + * the connection by passing subsequent packets through the connection + * tracker, and the state of the connection will be stored beyond the + * lifetime of packet processing. + * + * Connections may transition back into the uncommitted state due to + * external timers, or due to the contents of packets that are sent to the + * connection tracker. This behaviour is outside of the scope of the + * OpenFlow interface. + * + * The "zone" specifies a context within which the tracking is done: + * + * The connection tracking zone is a 16-bit number. Each zone is an + * independent connection tracking context. The connection state for each + * connection is completely separate for each zone, so if a connection + * is committed to zone A, then it will remain uncommitted in zone B. + * If NXAST_CT is executed with the same zone multiple times, later + * executions have no effect. + * + * If 'zone_src' is nonzero, this specifies that the zone should be + * sourced from a field zone_src[ofs:ofs+nbits]. The format and semantics + * of 'zone_src' and 'zone_ofs_nbits' are similar to those for the + * NXAST_REG_LOAD action. The acceptable nxm_header values for 'zone_src' + * are the same as the acceptable nxm_header values for the 'src' field of + * NXAST_REG_MOVE. + * + * If 'zone_src' is zero, then the value of 'zone_imm' will be used as the + * connection tracking zone. + * + * The "recirc_table" allows NXM_NX_CT_* fields to become available: + * + * If "recirc_table" has a value other than NX_CT_RECIRC_NONE, then the + * packet will be logically cloned prior to executing this action. One + * copy will be sent to the connection tracker, then will be re-injected + * into the OpenFlow pipeline beginning at the OpenFlow table specified in + * this field. When the packet re-enters the pipeline, the NXM_NX_CT_* + * fields will be populated. The original instance of the packet will + * continue the current actions list. This can be thought of as similar to + * the effect of the "output" action: One copy is sent out (in this case, + * to the connection tracker), but the current copy continues processing. + * + * It is strongly recommended that this table is later than the current + * table, to prevent loops. + * + * The "alg" attaches protocol-specific behaviour to this action: + * + * The ALG is a 16-bit number which specifies that additional + * processing should be applied to this traffic. + * + * Protocol | Value | Meaning + * -------------------------------------------------------------------- + * None | 0 | No protocol-specific behaviour. + * FTP | 21 | Parse FTP control connections and observe the + * | | negotiation of related data connections. + * Other | Other | Unsupported protocols. + * + * By way of example, if FTP control connections have this action applied + * with the ALG set to FTP (21), then the connection tracker will observe + * the negotiation of data connections. This allows the connection + * tracker to identify subsequent data connections as "related" to this + * existing connection. The "related" flag will be populated in the + * NXM_NX_CT_STATE field for such connections if the 'recirc_table' is + * specified. + * + * Zero or more actions may immediately follow this action. These actions will + * be executed within the context of the connection tracker, and they require + * the NX_CT_F_COMMIT flag to be set. + */ +struct nx_action_conntrack { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* At least 24. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_CT. */ + ovs_be16 flags; /* Zero or more NX_CT_F_* flags. + * Unspecified flag bits must be zero. */ + ovs_be32 zone_src; /* Connection tracking context. */ + union { + ovs_be16 zone_ofs_nbits;/* Range to use from source field. */ + ovs_be16 zone_imm; /* Immediate value for zone. */ + }; + uint8_t recirc_table; /* Recirculate to a specific table, or + NX_CT_RECIRC_NONE for no recirculation. */ + uint8_t pad[3]; /* Zeroes */ + ovs_be16 alg; /* Well-known port number for the protocol. + * 0 indicates no ALG is required. */ + /* Followed by a sequence of zero or more OpenFlow actions. The length of + * these is included in 'len'. */ +}; +OFP_ASSERT(sizeof(struct nx_action_conntrack) == 24); + +static enum ofperr +decode_ct_zone(const struct nx_action_conntrack *nac, + struct ofpact_conntrack *out) +{ + if (nac->zone_src) { + enum ofperr error; + + out->zone_src.field = mf_from_nxm_header(ntohl(nac->zone_src)); + out->zone_src.ofs = nxm_decode_ofs(nac->zone_ofs_nbits); + out->zone_src.n_bits = nxm_decode_n_bits(nac->zone_ofs_nbits); + error = mf_check_src(&out->zone_src, NULL); + if (error) { + return error; + } + + if (out->zone_src.n_bits != 16) { + VLOG_WARN_RL(&rl, "zone n_bits %d not within valid range [16..16]", + out->zone_src.n_bits); + return OFPERR_OFPBAC_BAD_SET_LEN; + } + } else { + out->zone_src.field = NULL; + out->zone_imm = ntohs(nac->zone_imm); + } + + return 0; +} + +static enum ofperr +decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + const size_t ct_offset = ofpacts_pull(out); + struct ofpact_conntrack *conntrack = ofpact_put_CT(out); + conntrack->flags = ntohs(nac->flags); + + int error = decode_ct_zone(nac, conntrack); + if (error) { + goto out; + } + conntrack->recirc_table = nac->recirc_table; + conntrack->alg = ntohs(nac->alg); + + ofpbuf_pull(out, sizeof(*conntrack)); + + struct ofpbuf openflow = ofpbuf_const_initializer( + nac + 1, ntohs(nac->len) - sizeof(*nac)); + error = ofpacts_pull_openflow_actions__(&openflow, openflow.size, + ofp_version, + 1u << OVSINST_OFPIT11_APPLY_ACTIONS, + out, OFPACT_CT); + if (error) { + goto out; + } + + conntrack = ofpbuf_push_uninit(out, sizeof(*conntrack)); + out->header = &conntrack->ofpact; + ofpact_finish(out, &conntrack->ofpact); + + if (conntrack->ofpact.len > sizeof(*conntrack) + && !(conntrack->flags & NX_CT_F_COMMIT)) { + const struct ofpact *a; + size_t ofpacts_len = conntrack->ofpact.len - sizeof(*conntrack); + + OFPACT_FOR_EACH (a, conntrack->actions, ofpacts_len) { + if (a->type != OFPACT_NAT || ofpact_get_NAT(a)->flags + || ofpact_get_NAT(a)->range_af != AF_UNSPEC) { + VLOG_WARN_RL(&rl, "CT action requires commit flag if actions " + "other than NAT without arguments are specified."); + error = OFPERR_OFPBAC_BAD_ARGUMENT; + goto out; + } + } + } + +out: + ofpbuf_push_uninit(out, ct_offset); + return error; +} + +static void +encode_CT(const struct ofpact_conntrack *conntrack, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + struct nx_action_conntrack *nac; + const size_t ofs = out->size; + size_t len; + + nac = put_NXAST_CT(out); + nac->flags = htons(conntrack->flags); + if (conntrack->zone_src.field) { + nac->zone_src = htonl(mf_nxm_header(conntrack->zone_src.field->id)); + nac->zone_ofs_nbits = nxm_encode_ofs_nbits(conntrack->zone_src.ofs, + conntrack->zone_src.n_bits); + } else { + nac->zone_src = htonl(0); + nac->zone_imm = htons(conntrack->zone_imm); + } + nac->recirc_table = conntrack->recirc_table; + nac->alg = htons(conntrack->alg); + + len = ofpacts_put_openflow_actions(conntrack->actions, + ofpact_ct_get_action_len(conntrack), + out, ofp_version); + len += sizeof(*nac); + nac = ofpbuf_at(out, ofs, sizeof(*nac)); + nac->len = htons(len); +} + +static char * OVS_WARN_UNUSED_RESULT parse_NAT(char *arg, struct ofpbuf *, + enum ofputil_protocol * OVS_UNUSED); + +/* Parses 'arg' as the argument to a "ct" action, and appends such an + * action to 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * OVS_WARN_UNUSED_RESULT +parse_CT(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + const size_t ct_offset = ofpacts_pull(ofpacts); + struct ofpact_conntrack *oc; + char *error = NULL; + char *key, *value; + + oc = ofpact_put_CT(ofpacts); + oc->flags = 0; + oc->recirc_table = NX_CT_RECIRC_NONE; + while (ofputil_parse_key_value(&arg, &key, &value)) { + if (!strcmp(key, "commit")) { + oc->flags |= NX_CT_F_COMMIT; + } else if (!strcmp(key, "table")) { + error = str_to_u8(value, "recirc_table", &oc->recirc_table); + if (!error && oc->recirc_table == NX_CT_RECIRC_NONE) { + error = xasprintf("invalid table %#"PRIx16, oc->recirc_table); + } + } else if (!strcmp(key, "zone")) { + error = str_to_u16(value, "zone", &oc->zone_imm); + + if (error) { + free(error); + error = mf_parse_subfield(&oc->zone_src, value); + if (error) { + return error; + } + } + } else if (!strcmp(key, "alg")) { + error = str_to_connhelper(value, &oc->alg); + } else if (!strcmp(key, "nat")) { + const size_t nat_offset = ofpacts_pull(ofpacts); + + error = parse_NAT(value, ofpacts, usable_protocols); + /* Update CT action pointer and length. */ + ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset); + oc = ofpacts->header; + } else if (!strcmp(key, "exec")) { + /* Hide existing actions from ofpacts_parse_copy(), so the + * nesting can be handled transparently. */ + enum ofputil_protocol usable_protocols2; + const size_t exec_offset = ofpacts_pull(ofpacts); + + /* Initializes 'usable_protocol2', fold it back to + * '*usable_protocols' afterwards, so that we do not lose + * restrictions already in there. */ + error = ofpacts_parse_copy(value, ofpacts, &usable_protocols2, + false, OFPACT_CT); + *usable_protocols &= usable_protocols2; + ofpacts->header = ofpbuf_push_uninit(ofpacts, exec_offset); + oc = ofpacts->header; + } else { + error = xasprintf("invalid argument to \"ct\" action: `%s'", key); + } + if (error) { + break; + } + } + + ofpact_finish(ofpacts, &oc->ofpact); + ofpbuf_push_uninit(ofpacts, ct_offset); + return error; +} + +static void +format_alg(int port, struct ds *s) +{ + if (port == IPPORT_FTP) { + ds_put_format(s, "alg=ftp,"); + } else if (port) { + ds_put_format(s, "alg=%d,", port); + } +} + +static void format_NAT(const struct ofpact_nat *a, struct ds *ds); + +static void +format_CT(const struct ofpact_conntrack *a, struct ds *s) +{ + ds_put_cstr(s, "ct("); + if (a->flags & NX_CT_F_COMMIT) { + ds_put_cstr(s, "commit,"); + } + if (a->recirc_table != NX_CT_RECIRC_NONE) { + ds_put_format(s, "table=%"PRIu8",", a->recirc_table); + } + if (a->zone_src.field) { + ds_put_format(s, "zone="); + mf_format_subfield(&a->zone_src, s); + ds_put_char(s, ','); + } else if (a->zone_imm) { + ds_put_format(s, "zone=%"PRIu16",", a->zone_imm); + } + /* If the first action is a NAT action, format it outside of the 'exec' + * envelope. */ + const struct ofpact *action = a->actions; + size_t actions_len = ofpact_ct_get_action_len(a); + if (actions_len && action->type == OFPACT_NAT) { + format_NAT(ofpact_get_NAT(action), s); + ds_put_char(s, ','); + actions_len -= OFPACT_ALIGN(action->len); + action = ofpact_next(action); + } + if (actions_len) { + ds_put_cstr(s, "exec("); + ofpacts_format(action, actions_len, s); + ds_put_cstr(s, "),"); + } + format_alg(a->alg, s); + ds_chomp(s, ','); + ds_put_char(s, ')'); +} + +/* NAT action. */ + +/* Which optional fields are present? */ +enum nx_nat_range { + NX_NAT_RANGE_IPV4_MIN = 1 << 0, /* ovs_be32 */ + NX_NAT_RANGE_IPV4_MAX = 1 << 1, /* ovs_be32 */ + NX_NAT_RANGE_IPV6_MIN = 1 << 2, /* struct in6_addr */ + NX_NAT_RANGE_IPV6_MAX = 1 << 3, /* struct in6_addr */ + NX_NAT_RANGE_PROTO_MIN = 1 << 4, /* ovs_be16 */ + NX_NAT_RANGE_PROTO_MAX = 1 << 5, /* ovs_be16 */ +}; + +/* Action structure for NXAST_NAT. */ +struct nx_action_nat { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* At least 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_NAT. */ + uint8_t pad[2]; /* Must be zero. */ + ovs_be16 flags; /* Zero or more NX_NAT_F_* flags. + * Unspecified flag bits must be zero. */ + ovs_be16 range_present; /* NX_NAT_RANGE_* */ + /* Followed by optional parameters as specified by 'range_present' */ +}; +OFP_ASSERT(sizeof(struct nx_action_nat) == 16); + +static void +encode_NAT(const struct ofpact_nat *nat, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + struct nx_action_nat *nan; + const size_t ofs = out->size; + uint16_t range_present = 0; + + nan = put_NXAST_NAT(out); + nan->flags = htons(nat->flags); + if (nat->range_af == AF_INET) { + if (nat->range.addr.ipv4.min) { + ovs_be32 *min = ofpbuf_put_uninit(out, sizeof *min); + *min = nat->range.addr.ipv4.min; + range_present |= NX_NAT_RANGE_IPV4_MIN; + } + if (nat->range.addr.ipv4.max) { + ovs_be32 *max = ofpbuf_put_uninit(out, sizeof *max); + *max = nat->range.addr.ipv4.max; + range_present |= NX_NAT_RANGE_IPV4_MAX; + } + } else if (nat->range_af == AF_INET6) { + if (!ipv6_mask_is_any(&nat->range.addr.ipv6.min)) { + struct in6_addr *min = ofpbuf_put_uninit(out, sizeof *min); + *min = nat->range.addr.ipv6.min; + range_present |= NX_NAT_RANGE_IPV6_MIN; + } + if (!ipv6_mask_is_any(&nat->range.addr.ipv6.max)) { + struct in6_addr *max = ofpbuf_put_uninit(out, sizeof *max); + *max = nat->range.addr.ipv6.max; + range_present |= NX_NAT_RANGE_IPV6_MAX; + } + } + if (nat->range_af != AF_UNSPEC) { + if (nat->range.proto.min) { + ovs_be16 *min = ofpbuf_put_uninit(out, sizeof *min); + *min = htons(nat->range.proto.min); + range_present |= NX_NAT_RANGE_PROTO_MIN; + } + if (nat->range.proto.max) { + ovs_be16 *max = ofpbuf_put_uninit(out, sizeof *max); + *max = htons(nat->range.proto.max); + range_present |= NX_NAT_RANGE_PROTO_MAX; + } + } + pad_ofpat(out, ofs); + nan = ofpbuf_at(out, ofs, sizeof *nan); + nan->range_present = htons(range_present); +} + +static enum ofperr +decode_NXAST_RAW_NAT(const struct nx_action_nat *nan, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + struct ofpact_nat *nat; + uint16_t range_present = ntohs(nan->range_present); + const char *opts = (char *)(nan + 1); + uint16_t len = ntohs(nan->len) - sizeof *nan; + + nat = ofpact_put_NAT(out); + nat->flags = ntohs(nan->flags); + +#define NX_NAT_GET_OPT(DST, SRC, LEN, TYPE) \ + (LEN >= sizeof(TYPE) \ + ? (memcpy(DST, SRC, sizeof(TYPE)), LEN -= sizeof(TYPE), \ + SRC += sizeof(TYPE)) \ + : NULL) + + nat->range_af = AF_UNSPEC; + if (range_present & NX_NAT_RANGE_IPV4_MIN) { + if (range_present & (NX_NAT_RANGE_IPV6_MIN | NX_NAT_RANGE_IPV6_MAX)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + if (!NX_NAT_GET_OPT(&nat->range.addr.ipv4.min, opts, len, ovs_be32) + || !nat->range.addr.ipv4.min) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + nat->range_af = AF_INET; + + if (range_present & NX_NAT_RANGE_IPV4_MAX) { + if (!NX_NAT_GET_OPT(&nat->range.addr.ipv4.max, opts, len, + ovs_be32)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + if (ntohl(nat->range.addr.ipv4.max) + < ntohl(nat->range.addr.ipv4.min)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + } + } else if (range_present & NX_NAT_RANGE_IPV4_MAX) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else if (range_present & NX_NAT_RANGE_IPV6_MIN) { + if (!NX_NAT_GET_OPT(&nat->range.addr.ipv6.min, opts, len, + struct in6_addr) + || ipv6_mask_is_any(&nat->range.addr.ipv6.min)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + nat->range_af = AF_INET6; + + if (range_present & NX_NAT_RANGE_IPV6_MAX) { + if (!NX_NAT_GET_OPT(&nat->range.addr.ipv6.max, opts, len, + struct in6_addr)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + if (memcmp(&nat->range.addr.ipv6.max, &nat->range.addr.ipv6.min, + sizeof(struct in6_addr)) < 0) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + } + } else if (range_present & NX_NAT_RANGE_IPV6_MAX) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + if (range_present & NX_NAT_RANGE_PROTO_MIN) { + ovs_be16 proto; - ds_put_cstr(s, "note:"); - for (i = 0; i < a->length; i++) { - if (i) { - ds_put_char(s, '.'); + if (nat->range_af == AF_UNSPEC) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + if (!NX_NAT_GET_OPT(&proto, opts, len, ovs_be16) || proto == 0) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + nat->range.proto.min = ntohs(proto); + if (range_present & NX_NAT_RANGE_PROTO_MAX) { + if (!NX_NAT_GET_OPT(&proto, opts, len, ovs_be16)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + nat->range.proto.max = ntohs(proto); + if (nat->range.proto.max < nat->range.proto.min) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } } - ds_put_format(s, "%02"PRIx8, a->data[i]); + } else if (range_present & NX_NAT_RANGE_PROTO_MAX) { + return OFPERR_OFPBAC_BAD_ARGUMENT; } -} - -/* Exit action. */ -static enum ofperr -decode_NXAST_RAW_EXIT(struct ofpbuf *out) -{ - ofpact_put_EXIT(out); return 0; } static void -encode_EXIT(const struct ofpact_null *null OVS_UNUSED, - enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +format_NAT(const struct ofpact_nat *a, struct ds *ds) { - put_NXAST_EXIT(out); -} + ds_put_cstr(ds, "nat"); -static char * WARN_UNUSED_RESULT -parse_EXIT(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols OVS_UNUSED) -{ - ofpact_put_EXIT(ofpacts); - return NULL; -} + if (a->flags & (NX_NAT_F_SRC | NX_NAT_F_DST)) { + ds_put_char(ds, '('); + ds_put_cstr(ds, a->flags & NX_NAT_F_SRC ? "src" : "dst"); -static void -format_EXIT(const struct ofpact_null *a OVS_UNUSED, struct ds *s) -{ - ds_put_cstr(s, "exit"); -} - -/* Action structure for NXAST_SAMPLE. - * - * Samples matching packets with the given probability and sends them - * each to the set of collectors identified with the given ID. The - * probability is expressed as a number of packets to be sampled out - * of USHRT_MAX packets, and must be >0. - * - * When sending packet samples to IPFIX collectors, the IPFIX flow - * record sent for each sampled packet is associated with the given - * observation domain ID and observation point ID. Each IPFIX flow - * record contain the sampled packet's headers when executing this - * rule. If a sampled packet's headers are modified by previous - * actions in the flow, those modified headers are sent. */ -struct nx_action_sample { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 24. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_SAMPLE. */ - ovs_be16 probability; /* Fraction of packets to sample. */ - ovs_be32 collector_set_id; /* ID of collector set in OVSDB. */ - ovs_be32 obs_domain_id; /* ID of sampling observation domain. */ - ovs_be32 obs_point_id; /* ID of sampling observation point. */ -}; -OFP_ASSERT(sizeof(struct nx_action_sample) == 24); + if (a->range_af != AF_UNSPEC) { + ds_put_cstr(ds, "="); -static enum ofperr -decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas, struct ofpbuf *out) -{ - struct ofpact_sample *sample; + if (a->range_af == AF_INET) { + ds_put_format(ds, IP_FMT, IP_ARGS(a->range.addr.ipv4.min)); - sample = ofpact_put_SAMPLE(out); - sample->probability = ntohs(nas->probability); - sample->collector_set_id = ntohl(nas->collector_set_id); - sample->obs_domain_id = ntohl(nas->obs_domain_id); - sample->obs_point_id = ntohl(nas->obs_point_id); + if (a->range.addr.ipv4.max + && a->range.addr.ipv4.max != a->range.addr.ipv4.min) { + ds_put_format(ds, "-"IP_FMT, + IP_ARGS(a->range.addr.ipv4.max)); + } + } else if (a->range_af == AF_INET6) { + ipv6_format_addr_bracket(&a->range.addr.ipv6.min, ds, + a->range.proto.min); + + if (!ipv6_mask_is_any(&a->range.addr.ipv6.max) + && memcmp(&a->range.addr.ipv6.max, &a->range.addr.ipv6.min, + sizeof(struct in6_addr)) != 0) { + ds_put_char(ds, '-'); + ipv6_format_addr_bracket(&a->range.addr.ipv6.max, ds, + a->range.proto.min); + } + } + if (a->range.proto.min) { + ds_put_char(ds, ':'); + ds_put_format(ds, "%"PRIu16, a->range.proto.min); - if (sample->probability == 0) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } + if (a->range.proto.max + && a->range.proto.max != a->range.proto.min) { + ds_put_format(ds, "-%"PRIu16, a->range.proto.max); + } + } + ds_put_char(ds, ','); - return 0; + if (a->flags & NX_NAT_F_PERSISTENT) { + ds_put_cstr(ds, "persistent,"); + } + if (a->flags & NX_NAT_F_PROTO_HASH) { + ds_put_cstr(ds, "hash,"); + } + if (a->flags & NX_NAT_F_PROTO_RANDOM) { + ds_put_cstr(ds, "random,"); + } + } + ds_chomp(ds, ','); + ds_put_char(ds, ')'); + } } -static void -encode_SAMPLE(const struct ofpact_sample *sample, - enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +static char * OVS_WARN_UNUSED_RESULT +str_to_nat_range(const char *s, struct ofpact_nat *on) { - struct nx_action_sample *nas; + char ipv6_s[IPV6_SCAN_LEN + 1]; + int n = 0; - nas = put_NXAST_SAMPLE(out); - nas->probability = htons(sample->probability); - nas->collector_set_id = htonl(sample->collector_set_id); - nas->obs_domain_id = htonl(sample->obs_domain_id); - nas->obs_point_id = htonl(sample->obs_point_id); + on->range_af = AF_UNSPEC; + if (ovs_scan_len(s, &n, IP_SCAN_FMT, + IP_SCAN_ARGS(&on->range.addr.ipv4.min))) { + on->range_af = AF_INET; + + if (s[n] == '-') { + n++; + if (!ovs_scan_len(s, &n, IP_SCAN_FMT, + IP_SCAN_ARGS(&on->range.addr.ipv4.max)) + || (ntohl(on->range.addr.ipv4.max) + < ntohl(on->range.addr.ipv4.min))) { + goto error; + } + } + } else if ((ovs_scan_len(s, &n, IPV6_SCAN_FMT, ipv6_s) + || ovs_scan_len(s, &n, "["IPV6_SCAN_FMT"]", ipv6_s)) + && inet_pton(AF_INET6, ipv6_s, &on->range.addr.ipv6.min) == 1) { + on->range_af = AF_INET6; + + if (s[n] == '-') { + n++; + if (!(ovs_scan_len(s, &n, IPV6_SCAN_FMT, ipv6_s) + || ovs_scan_len(s, &n, "["IPV6_SCAN_FMT"]", ipv6_s)) + || inet_pton(AF_INET6, ipv6_s, &on->range.addr.ipv6.max) != 1 + || memcmp(&on->range.addr.ipv6.max, &on->range.addr.ipv6.min, + sizeof on->range.addr.ipv6.max) < 0) { + goto error; + } + } + } + if (on->range_af != AF_UNSPEC && s[n] == ':') { + n++; + if (!ovs_scan_len(s, &n, "%"SCNu16, &on->range.proto.min)) { + goto error; + } + if (s[n] == '-') { + n++; + if (!ovs_scan_len(s, &n, "%"SCNu16, &on->range.proto.max) + || on->range.proto.max < on->range.proto.min) { + goto error; + } + } + } + if (strlen(s) != n) { + return xasprintf("garbage (%s) after nat range \"%s\" (pos: %d)", + &s[n], s, n); + } + return NULL; +error: + return xasprintf("invalid nat range \"%s\"", s); } -/* Parses 'arg' as the argument to a "sample" action, and appends such an + +/* Parses 'arg' as the argument to a "nat" action, and appends such an * action to 'ofpacts'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_SAMPLE(char *arg, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols OVS_UNUSED) +static char * OVS_WARN_UNUSED_RESULT +parse_NAT(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) { - struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts); + struct ofpact_nat *on = ofpact_put_NAT(ofpacts); char *key, *value; + on->flags = 0; + on->range_af = AF_UNSPEC; + while (ofputil_parse_key_value(&arg, &key, &value)) { char *error = NULL; - if (!strcmp(key, "probability")) { - error = str_to_u16(value, "probability", &os->probability); - if (!error && os->probability == 0) { - error = xasprintf("invalid probability value \"%s\"", value); - } - } else if (!strcmp(key, "collector_set_id")) { - error = str_to_u32(value, &os->collector_set_id); - } else if (!strcmp(key, "obs_domain_id")) { - error = str_to_u32(value, &os->obs_domain_id); - } else if (!strcmp(key, "obs_point_id")) { - error = str_to_u32(value, &os->obs_point_id); + if (!strcmp(key, "src")) { + on->flags |= NX_NAT_F_SRC; + error = str_to_nat_range(value, on); + } else if (!strcmp(key, "dst")) { + on->flags |= NX_NAT_F_DST; + error = str_to_nat_range(value, on); + } else if (!strcmp(key, "persistent")) { + on->flags |= NX_NAT_F_PERSISTENT; + } else if (!strcmp(key, "hash")) { + on->flags |= NX_NAT_F_PROTO_HASH; + } else if (!strcmp(key, "random")) { + on->flags |= NX_NAT_F_PROTO_RANDOM; } else { - error = xasprintf("invalid key \"%s\" in \"sample\" argument", + error = xasprintf("invalid key \"%s\" in \"nat\" argument", key); } if (error) { return error; } } - if (os->probability == 0) { - return xstrdup("non-zero \"probability\" must be specified on sample"); + if (on->flags & NX_NAT_F_SRC && on->flags & NX_NAT_F_DST) { + return xasprintf("May only specify one of \"snat\" or \"dnat\"."); + } + if (!(on->flags & NX_NAT_F_SRC || on->flags & NX_NAT_F_DST)) { + if (on->flags) { + return xasprintf("Flags allowed only with \"snat\" or \"dnat\"."); + } + if (on->range_af != AF_UNSPEC) { + return xasprintf("Range allowed only with \"snat\" or \"dnat\"."); + } } return NULL; } -static void -format_SAMPLE(const struct ofpact_sample *a, struct ds *s) -{ - ds_put_format(s, "sample(probability=%"PRIu16",collector_set_id=%"PRIu32 - ",obs_domain_id=%"PRIu32",obs_point_id=%"PRIu32")", - a->probability, a->collector_set_id, - a->obs_domain_id, a->obs_point_id); -} /* Meter instruction. */ @@ -3935,7 +5482,7 @@ encode_METER(const struct ofpact_meter *meter, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_METER(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { @@ -3961,7 +5508,7 @@ encode_CLEAR_ACTIONS(const struct ofpact_null *null OVS_UNUSED, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_CLEAR_ACTIONS(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -3982,7 +5529,7 @@ encode_WRITE_ACTIONS(const struct ofpact_nest *actions, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version > OFP10_VERSION) { - const size_t ofs = ofpbuf_size(out); + const size_t ofs = out->size; instruction_put_OFPIT11_WRITE_ACTIONS(out); ofpacts_put_openflow_actions(actions->actions, @@ -3992,18 +5539,13 @@ encode_WRITE_ACTIONS(const struct ofpact_nest *actions, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_WRITE_ACTIONS(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { + size_t ofs = ofpacts_pull(ofpacts); struct ofpact_nest *on; char *error; - size_t ofs; - - /* Pull off existing actions or instructions. */ - ofpact_pad(ofpacts); - ofs = ofpbuf_size(ofpacts); - ofpbuf_pull(ofpacts, ofs); /* Add a Write-Actions instruction and then pull it off. */ ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, sizeof *on); @@ -4016,11 +5558,12 @@ parse_WRITE_ACTIONS(char *arg, struct ofpbuf *ofpacts, * that it doesn't actually include the nested actions. That means that * ofpacts_parse() would reject them as being part of an Apply-Actions that * follows a Write-Actions, which is an invalid order. */ - error = ofpacts_parse(arg, ofpacts, usable_protocols, false); + error = ofpacts_parse(arg, ofpacts, usable_protocols, false, + OFPACT_WRITE_ACTIONS); /* Put the Write-Actions back on and update its length. */ on = ofpbuf_push_uninit(ofpacts, sizeof *on); - on->ofpact.len = ofpbuf_size(ofpacts); + on->ofpact.len = ofpacts->size; /* Put any previous actions or instructions back on. */ ofpbuf_push_uninit(ofpacts, ofs); @@ -4052,6 +5595,7 @@ OFP_ASSERT(sizeof(struct nx_action_write_metadata) == 32); static enum ofperr decode_NXAST_RAW_WRITE_METADATA(const struct nx_action_write_metadata *nawm, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_metadata *om; @@ -4086,7 +5630,7 @@ encode_WRITE_METADATA(const struct ofpact_metadata *metadata, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_WRITE_METADATA(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { @@ -4141,7 +5685,7 @@ encode_GOTO_TABLE(const struct ofpact_goto_table *goto_table, } } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT parse_GOTO_TABLE(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -4179,18 +5723,16 @@ static enum ofperr ofpacts_decode(const void *actions, size_t actions_len, enum ofp_version ofp_version, struct ofpbuf *ofpacts) { - struct ofpbuf openflow; - - ofpbuf_use_const(&openflow, actions, actions_len); - while (ofpbuf_size(&openflow)) { - const struct ofp_action_header *action = ofpbuf_data(&openflow); + struct ofpbuf openflow = ofpbuf_const_initializer(actions, actions_len); + while (openflow.size) { + const struct ofp_action_header *action = openflow.data; enum ofp_raw_action_type raw; enum ofperr error; uint64_t arg; error = ofpact_pull_raw(&openflow, ofp_version, &raw, &arg); if (!error) { - error = ofpact_decode(action, raw, arg, ofpacts); + error = ofpact_decode(action, raw, ofp_version, arg, ofpacts); } if (error) { @@ -4198,8 +5740,6 @@ ofpacts_decode(const void *actions, size_t actions_len, return error; } } - - ofpact_pad(ofpacts); return 0; } @@ -4208,13 +5748,13 @@ ofpacts_pull_openflow_actions__(struct ofpbuf *openflow, unsigned int actions_len, enum ofp_version version, uint32_t allowed_ovsinsts, - struct ofpbuf *ofpacts) + struct ofpbuf *ofpacts, + enum ofpact_type outer_action) { const struct ofp_action_header *actions; + size_t orig_size = ofpacts->size; enum ofperr error; - ofpbuf_clear(ofpacts); - if (actions_len % OFP_ACTION_ALIGN != 0) { VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a " "multiple of %d", actions_len, OFP_ACTION_ALIGN); @@ -4225,27 +5765,27 @@ ofpacts_pull_openflow_actions__(struct ofpbuf *openflow, if (actions == NULL) { VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds " "remaining message length (%"PRIu32")", - actions_len, ofpbuf_size(openflow)); + actions_len, openflow->size); return OFPERR_OFPBRC_BAD_LEN; } error = ofpacts_decode(actions, actions_len, version, ofpacts); if (error) { - ofpbuf_clear(ofpacts); + ofpacts->size = orig_size; return error; } - error = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts), - allowed_ovsinsts); + error = ofpacts_verify(ofpacts->data, ofpacts->size, allowed_ovsinsts, + outer_action); if (error) { - ofpbuf_clear(ofpacts); + ofpacts->size = orig_size; } return error; } -/* Attempts to convert 'actions_len' bytes of OpenFlow actions from the - * front of 'openflow' into ofpacts. On success, replaces any existing content - * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'. +/* Attempts to convert 'actions_len' bytes of OpenFlow actions from the front + * of 'openflow' into ofpacts. On success, appends the converted actions to + * 'ofpacts'; on failure, 'ofpacts' is unchanged (but might be reallocated) . * Returns 0 if successful, otherwise an OpenFlow error. * * Actions are processed according to their OpenFlow version which @@ -4268,7 +5808,7 @@ ofpacts_pull_openflow_actions(struct ofpbuf *openflow, { return ofpacts_pull_openflow_actions__(openflow, actions_len, version, 1u << OVSINST_OFPIT11_APPLY_ACTIONS, - ofpacts); + ofpacts, 0); } /* OpenFlow 1.1 actions. */ @@ -4283,7 +5823,6 @@ ofpact_is_set_or_move_action(const struct ofpact *a) { switch (a->type) { case OFPACT_SET_FIELD: - case OFPACT_REG_LOAD: case OFPACT_REG_MOVE: case OFPACT_SET_ETH_DST: case OFPACT_SET_ETH_SRC: @@ -4304,15 +5843,19 @@ ofpact_is_set_or_move_action(const struct ofpact *a) return true; case OFPACT_BUNDLE: case OFPACT_CLEAR_ACTIONS: + case OFPACT_CT: + case OFPACT_NAT: case OFPACT_CONTROLLER: case OFPACT_DEC_MPLS_TTL: case OFPACT_DEC_TTL: case OFPACT_ENQUEUE: case OFPACT_EXIT: + case OFPACT_UNROLL_XLATE: case OFPACT_FIN_TIMEOUT: case OFPACT_GOTO_TABLE: case OFPACT_GROUP: case OFPACT_LEARN: + case OFPACT_CONJUNCTION: case OFPACT_METER: case OFPACT_MULTIPATH: case OFPACT_NOTE: @@ -4329,6 +5872,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a) case OFPACT_STRIP_VLAN: case OFPACT_WRITE_ACTIONS: case OFPACT_WRITE_METADATA: + case OFPACT_DEBUG_RECIRC: return false; default: OVS_NOT_REACHED(); @@ -4348,7 +5892,6 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a) case OFPACT_POP_MPLS: case OFPACT_PUSH_MPLS: case OFPACT_PUSH_VLAN: - case OFPACT_REG_LOAD: case OFPACT_REG_MOVE: case OFPACT_SET_FIELD: case OFPACT_SET_ETH_DST: @@ -4376,10 +5919,14 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a) * in the action set is undefined. */ case OFPACT_BUNDLE: case OFPACT_CONTROLLER: + case OFPACT_CT: + case OFPACT_NAT: case OFPACT_ENQUEUE: case OFPACT_EXIT: + case OFPACT_UNROLL_XLATE: case OFPACT_FIN_TIMEOUT: case OFPACT_LEARN: + case OFPACT_CONJUNCTION: case OFPACT_MULTIPATH: case OFPACT_NOTE: case OFPACT_OUTPUT_REG: @@ -4388,6 +5935,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a) case OFPACT_SAMPLE: case OFPACT_STACK_POP: case OFPACT_STACK_PUSH: + case OFPACT_DEBUG_RECIRC: /* The action set may only include actions and thus * may not include any instructions */ @@ -4418,7 +5966,7 @@ ofpacts_copy_last(struct ofpbuf *out, const struct ofpbuf *in, const struct ofpact *a; target = NULL; - OFPACT_FOR_EACH (a, ofpbuf_data(in), ofpbuf_size(in)) { + OFPACT_FOR_EACH (a, in->data, in->size) { if (a->type == filter) { target = a; } @@ -4437,7 +5985,7 @@ ofpacts_copy_all(struct ofpbuf *out, const struct ofpbuf *in, { const struct ofpact *a; - OFPACT_FOR_EACH (a, ofpbuf_data(in), ofpbuf_size(in)) { + OFPACT_FOR_EACH (a, in->data, in->size) { if (filter(a)) { ofpact_copy(out, a); } @@ -4450,7 +5998,7 @@ ofpacts_copy_all(struct ofpbuf *out, const struct ofpbuf *in, * "Action Set" and "Action List" terms used in OpenFlow 1.1+.) * * In general this involves appending the last instance of each action that is - * adimissible in the action set in the order described in the OpenFlow + * admissible in the action set in the order described in the OpenFlow * specification. * * Exceptions: @@ -4496,7 +6044,7 @@ ofpacts_decode_for_action_set(const struct ofp_action_header *in, { enum ofperr error; struct ofpact *a; - size_t start = ofpbuf_size(out); + size_t start = out->size; error = ofpacts_decode(in, n_in, version, out); @@ -4504,7 +6052,7 @@ ofpacts_decode_for_action_set(const struct ofp_action_header *in, return error; } - OFPACT_FOR_EACH (a, ofpact_end(ofpbuf_data(out), start), ofpbuf_size(out) - start) { + OFPACT_FOR_EACH (a, ofpact_end(out->data, start), out->size - start) { if (!ofpact_is_allowed_in_actions_set(a)) { VLOG_WARN_RL(&rl, "disallowed action in action set"); return OFPERR_OFPBAC_BAD_TYPE; @@ -4579,7 +6127,6 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type) case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: case OFPACT_REG_MOVE: - case OFPACT_REG_LOAD: case OFPACT_SET_FIELD: case OFPACT_STACK_PUSH: case OFPACT_STACK_POP: @@ -4596,10 +6143,15 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type) case OFPACT_FIN_TIMEOUT: case OFPACT_RESUBMIT: case OFPACT_LEARN: + case OFPACT_CONJUNCTION: case OFPACT_MULTIPATH: case OFPACT_NOTE: case OFPACT_EXIT: + case OFPACT_UNROLL_XLATE: case OFPACT_SAMPLE: + case OFPACT_DEBUG_RECIRC: + case OFPACT_CT: + case OFPACT_NAT: default: return OVSINST_OFPIT11_APPLY_ACTIONS; } @@ -4795,15 +6347,14 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, const struct ofp11_instruction *insts[N_OVS_INSTRUCTIONS]; enum ofperr error; + ofpbuf_clear(ofpacts); if (version == OFP10_VERSION) { return ofpacts_pull_openflow_actions__(openflow, instructions_len, version, (1u << N_OVS_INSTRUCTIONS) - 1, - ofpacts); + ofpacts, 0); } - ofpbuf_clear(ofpacts); - if (instructions_len % OFP11_INSTRUCTION_ALIGN != 0) { VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u is not a " "multiple of %d", @@ -4816,7 +6367,7 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, if (instructions == NULL) { VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u exceeds " "remaining message length (%"PRIu32")", - instructions_len, ofpbuf_size(openflow)); + instructions_len, openflow->size); error = OFPERR_OFPBIC_BAD_LEN; goto exit; } @@ -4858,12 +6409,9 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, struct ofpact_nest *on; const struct ofp_action_header *actions; size_t actions_len; - size_t start; - - ofpact_pad(ofpacts); - start = ofpbuf_size(ofpacts); - on = ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, - offsetof(struct ofpact_nest, actions)); + size_t start = ofpacts->size; + ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, + offsetof(struct ofpact_nest, actions)); get_actions_from_instruction(insts[OVSINST_OFPIT11_WRITE_ACTIONS], &actions, &actions_len); error = ofpacts_decode_for_action_set(actions, actions_len, @@ -4872,7 +6420,7 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, goto exit; } on = ofpbuf_at_assert(ofpacts, start, sizeof *on); - on->ofpact.len = ofpbuf_size(ofpacts) - start; + on->ofpact.len = ofpacts->size - start; } if (insts[OVSINST_OFPIT11_WRITE_METADATA]) { const struct ofp11_instruction_write_metadata *oiwm; @@ -4895,8 +6443,8 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, ogt->table_id = oigt->table_id; } - error = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts), - (1u << N_OVS_INSTRUCTIONS) - 1); + error = ofpacts_verify(ofpacts->data, ofpacts->size, + (1u << N_OVS_INSTRUCTIONS) - 1, 0); exit: if (error) { ofpbuf_clear(ofpacts); @@ -4913,10 +6461,10 @@ ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs) struct ofp11_instruction_actions *oia; oia = ofpbuf_at_assert(openflow, ofs, sizeof *oia); - if (ofpbuf_size(openflow) > ofs + sizeof *oia) { - oia->len = htons(ofpbuf_size(openflow) - ofs); + if (openflow->size > ofs + sizeof *oia) { + oia->len = htons(openflow->size - ofs); } else { - ofpbuf_set_size(openflow, ofs); + openflow->size = ofs; } } @@ -4933,10 +6481,12 @@ ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports) case OFPP_FLOOD: case OFPP_ALL: case OFPP_CONTROLLER: - case OFPP_NONE: case OFPP_LOCAL: return 0; + case OFPP_NONE: + return OFPERR_OFPBAC_BAD_OUT_PORT; + default: if (ofp_to_u16(port) < ofp_to_u16(max_ports)) { return 0; @@ -5057,19 +6607,8 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, return 0; case OFPACT_SET_L4_SRC_PORT: - if (!is_ip_any(flow) || - (flow->nw_proto != IPPROTO_TCP && flow->nw_proto != IPPROTO_UDP - && flow->nw_proto != IPPROTO_SCTP)) { - inconsistent_match(usable_protocols); - } - /* Note on which transport protocol the port numbers are set. - * This allows this set action to be converted to an OF1.2 set field - * action. */ - ofpact_get_SET_L4_SRC_PORT(a)->flow_ip_proto = flow->nw_proto; - return 0; - case OFPACT_SET_L4_DST_PORT: - if (!is_ip_any(flow) || + if (!is_ip_any(flow) || (flow->nw_frag & FLOW_NW_FRAG_LATER) || (flow->nw_proto != IPPROTO_TCP && flow->nw_proto != IPPROTO_UDP && flow->nw_proto != IPPROTO_SCTP)) { inconsistent_match(usable_protocols); @@ -5077,15 +6616,16 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, /* Note on which transport protocol the port numbers are set. * This allows this set action to be converted to an OF1.2 set field * action. */ - ofpact_get_SET_L4_DST_PORT(a)->flow_ip_proto = flow->nw_proto; + if (a->type == OFPACT_SET_L4_SRC_PORT) { + ofpact_get_SET_L4_SRC_PORT(a)->flow_ip_proto = flow->nw_proto; + } else { + ofpact_get_SET_L4_DST_PORT(a)->flow_ip_proto = flow->nw_proto; + } return 0; case OFPACT_REG_MOVE: return nxm_reg_move_check(ofpact_get_REG_MOVE(a), flow); - case OFPACT_REG_LOAD: - return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow); - case OFPACT_SET_FIELD: mf = ofpact_get_SET_FIELD(a)->field; /* Require OXM_OF_VLAN_VID to have an existing VLAN header. */ @@ -5136,6 +6676,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, case OFPACT_LEARN: return learn_check(ofpact_get_LEARN(a), flow); + case OFPACT_CONJUNCTION: + return 0; + case OFPACT_MULTIPATH: return multipath_check(ofpact_get_MULTIPATH(a), flow); @@ -5161,6 +6704,37 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, case OFPACT_SAMPLE: return 0; + case OFPACT_CT: { + struct ofpact_conntrack *oc = ofpact_get_CT(a); + enum ofperr err; + + if (!dl_type_is_ip_any(flow->dl_type) + || (flow->ct_state & CS_INVALID && oc->flags & NX_CT_F_COMMIT)) { + inconsistent_match(usable_protocols); + } + + if (oc->zone_src.field) { + return mf_check_src(&oc->zone_src, flow); + } + + err = ofpacts_check(oc->actions, ofpact_ct_get_action_len(oc), + flow, max_ports, table_id, n_tables, + usable_protocols); + return err; + } + + case OFPACT_NAT: { + struct ofpact_nat *on = ofpact_get_NAT(a); + + if (!dl_type_is_ip_any(flow->dl_type) || + (on->range_af == AF_INET && flow->dl_type != htons(ETH_TYPE_IP)) || + (on->range_af == AF_INET6 + && flow->dl_type != htons(ETH_TYPE_IPV6))) { + inconsistent_match(usable_protocols); + } + return 0; + } + case OFPACT_CLEAR_ACTIONS: return 0; @@ -5188,7 +6762,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, uint8_t goto_table = ofpact_get_GOTO_TABLE(a)->table_id; if ((table_id != 255 && goto_table <= table_id) || (n_tables != 255 && goto_table >= n_tables)) { - return OFPERR_OFPBRC_BAD_TABLE_ID; + return OFPERR_OFPBIC_BAD_TABLE_ID; } return 0; } @@ -5196,6 +6770,14 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, case OFPACT_GROUP: return 0; + case OFPACT_UNROLL_XLATE: + /* UNROLL is an internal action that should never be seen via + * OpenFlow. */ + return OFPERR_OFPBAC_BAD_TYPE; + + case OFPACT_DEBUG_RECIRC: + return 0; + default: OVS_NOT_REACHED(); } @@ -5257,11 +6839,88 @@ ofpacts_check_consistency(struct ofpact ofpacts[], size_t ofpacts_len, : 0); } -/* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are - * in the appropriate order as defined by the OpenFlow spec. */ +/* Returns the destination field that 'ofpact' would write to, or NULL + * if the action would not write to an mf_field. */ +const struct mf_field * +ofpact_get_mf_dst(const struct ofpact *ofpact) +{ + if (ofpact->type == OFPACT_SET_FIELD) { + const struct ofpact_set_field *orl; + + orl = CONTAINER_OF(ofpact, struct ofpact_set_field, ofpact); + return orl->field; + } else if (ofpact->type == OFPACT_REG_MOVE) { + const struct ofpact_reg_move *orm; + + orm = CONTAINER_OF(ofpact, struct ofpact_reg_move, ofpact); + return orm->dst.field; + } + + return NULL; +} + +static enum ofperr +unsupported_nesting(enum ofpact_type action, enum ofpact_type outer_action) +{ + VLOG_WARN("%s action doesn't support nested action %s", + ofpact_name(outer_action), ofpact_name(action)); + return OFPERR_OFPBAC_BAD_ARGUMENT; +} + +static bool +field_requires_ct(enum mf_field_id field) +{ + return field == MFF_CT_MARK || field == MFF_CT_LABEL; +} + +/* Apply nesting constraints for actions */ +static enum ofperr +ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action) +{ + const struct mf_field *field = ofpact_get_mf_dst(a); + + if (field && field_requires_ct(field->id) && outer_action != OFPACT_CT) { + VLOG_WARN("cannot set CT fields outside of ct action"); + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; + } + if (a->type == OFPACT_NAT) { + if (outer_action != OFPACT_CT) { + VLOG_WARN("Cannot have NAT action outside of \"ct\" action"); + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; + } + return 0; + } + + if (outer_action) { + ovs_assert(outer_action == OFPACT_WRITE_ACTIONS + || outer_action == OFPACT_CT); + + if (outer_action == OFPACT_CT) { + if (!field) { + return unsupported_nesting(a->type, outer_action); + } else if (!field_requires_ct(field->id)) { + VLOG_WARN("%s action doesn't support nested modification " + "of %s", ofpact_name(outer_action), field->name); + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + } + } + + return 0; +} + +/* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are in the + * appropriate order as defined by the OpenFlow spec and as required by Open + * vSwitch. + * + * 'allowed_ovsinsts' is a bitmap of OVSINST_* values, in which 1-bits indicate + * instructions that are allowed within 'ofpacts[]'. + * + * If 'outer_action' is not zero, it specifies that the actions are nested + * within another action of type 'outer_action'. */ static enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len, - uint32_t allowed_ovsinsts) + uint32_t allowed_ovsinsts, enum ofpact_type outer_action) { const struct ofpact *a; enum ovs_instruction_type inst; @@ -5269,6 +6928,25 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len, inst = OVSINST_OFPIT13_METER; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { enum ovs_instruction_type next; + enum ofperr error; + + if (a->type == OFPACT_CONJUNCTION) { + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + if (a->type != OFPACT_CONJUNCTION && a->type != OFPACT_NOTE) { + VLOG_WARN("\"conjunction\" actions may be used along with " + "\"note\" but not any other kind of action " + "(such as the \"%s\" action used here)", + ofpact_name(a->type)); + return OFPERR_NXBAC_BAD_CONJUNCTION; + } + } + return 0; + } + + error = ofpacts_verify_nested(a, outer_action); + if (error) { + return error; + } next = ovs_instruction_type_from_ofpact_type(a->type); if (a > ofpacts @@ -5328,12 +7006,12 @@ ofpacts_put_openflow_actions(const struct ofpact ofpacts[], size_t ofpacts_len, enum ofp_version ofp_version) { const struct ofpact *a; - size_t start_size = ofpbuf_size(openflow); + size_t start_size = openflow->size; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { encode_ofpact(a, ofp_version, openflow); } - return ofpbuf_size(openflow) - start_size; + return openflow->size - start_size; } static enum ovs_instruction_type @@ -5361,7 +7039,7 @@ ofpacts_put_openflow_instructions(const struct ofpact ofpacts[], a = ofpacts; while (a < end) { if (ofpact_is_apply_actions(a)) { - size_t ofs = ofpbuf_size(openflow); + size_t ofs = openflow->size; instruction_put_OFPIT11_APPLY_ACTIONS(openflow); do { @@ -5553,7 +7231,6 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port) case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: case OFPACT_REG_MOVE: - case OFPACT_REG_LOAD: case OFPACT_SET_FIELD: case OFPACT_STACK_PUSH: case OFPACT_STACK_POP: @@ -5569,9 +7246,11 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port) case OFPACT_FIN_TIMEOUT: case OFPACT_RESUBMIT: case OFPACT_LEARN: + case OFPACT_CONJUNCTION: case OFPACT_MULTIPATH: case OFPACT_NOTE: case OFPACT_EXIT: + case OFPACT_UNROLL_XLATE: case OFPACT_PUSH_MPLS: case OFPACT_POP_MPLS: case OFPACT_SAMPLE: @@ -5580,6 +7259,9 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port) case OFPACT_GOTO_TABLE: case OFPACT_METER: case OFPACT_GROUP: + case OFPACT_DEBUG_RECIRC: + case OFPACT_CT: + case OFPACT_NAT: default: return false; } @@ -5593,7 +7275,7 @@ ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len, { const struct ofpact *a; - OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + OFPACT_FOR_EACH_FLATTENED (a, ofpacts, ofpacts_len) { if (ofpact_outputs_to_port(a, port)) { return true; } @@ -5610,7 +7292,7 @@ ofpacts_output_to_group(const struct ofpact *ofpacts, size_t ofpacts_len, { const struct ofpact *a; - OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + OFPACT_FOR_EACH_FLATTENED (a, ofpacts, ofpacts_len) { if (a->type == OFPACT_GROUP && ofpact_get_GROUP(a)->group_id == group_id) { return true; @@ -5697,8 +7379,8 @@ ofpact_put(struct ofpbuf *ofpacts, enum ofpact_type type, size_t len) { struct ofpact *ofpact; - ofpact_pad(ofpacts); - ofpact = ofpacts->frame = ofpbuf_put_uninit(ofpacts, len); + ofpacts->header = ofpbuf_put_uninit(ofpacts, len); + ofpact = ofpacts->header; ofpact_init(ofpact, type, len); return ofpact; } @@ -5712,42 +7394,19 @@ ofpact_init(struct ofpact *ofpact, enum ofpact_type type, size_t len) ofpact->len = len; } -/* Updates 'ofpact->len' to the number of bytes in the tail of 'ofpacts' - * starting at 'ofpact'. - * - * This is the correct way to update a variable-length ofpact's length after - * adding the variable-length part of the payload. (See the large comment - * near the end of ofp-actions.h for more information.) */ +/* Finishes composing a variable-length action (begun using + * ofpact_put_()), by padding the action to a multiple of OFPACT_ALIGNTO + * bytes and updating its embedded length field. See the large comment near + * the end of ofp-actions.h for more information. */ void -ofpact_update_len(struct ofpbuf *ofpacts, struct ofpact *ofpact) +ofpact_finish(struct ofpbuf *ofpacts, struct ofpact *ofpact) { - ovs_assert(ofpact == ofpacts->frame); + ovs_assert(ofpact == ofpacts->header); ofpact->len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact; -} - -/* Pads out 'ofpacts' to a multiple of OFPACT_ALIGNTO bytes in length. Each - * ofpact_put_() calls this function automatically beforehand, but the - * client must call this itself after adding the final ofpact to an array of - * them. - * - * (The consequences of failing to call this function are probably not dire. - * OFPACT_FOR_EACH will calculate a pointer beyond the end of the ofpacts, but - * not dereference it. That's undefined behavior, technically, but it will not - * cause a real problem on common systems. Still, it seems better to call - * it.) */ -void -ofpact_pad(struct ofpbuf *ofpacts) -{ - unsigned int pad = PAD_SIZE(ofpbuf_size(ofpacts), OFPACT_ALIGNTO); - if (pad) { - ofpbuf_put_zeros(ofpacts, pad); - } + ofpbuf_padto(ofpacts, OFPACT_ALIGN(ofpacts->size)); } - - - -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT ofpact_parse(enum ofpact_type type, char *value, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { @@ -5779,11 +7438,14 @@ ofpact_type_from_name(const char *name, enum ofpact_type *type) /* Parses 'str' as a series of instructions, and appends them to 'ofpacts'. * * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT + * error. The caller is responsible for freeing the returned string. + * + * If 'outer_action' is specified, indicates that the actions being parsed + * are nested within another action of the type specified in 'outer_action'. */ +static char * OVS_WARN_UNUSED_RESULT ofpacts_parse__(char *str, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols, - bool allow_instructions) + bool allow_instructions, enum ofpact_type outer_action) { int prev_inst = -1; enum ofperr retval; @@ -5812,6 +7474,8 @@ ofpacts_parse__(char *str, struct ofpbuf *ofpacts, } else if (!strcasecmp(key, "set_tunnel64")) { error = parse_set_tunnel(value, ofpacts, NXAST_RAW_SET_TUNNEL64); + } else if (!strcasecmp(key, "load")) { + error = parse_reg_load(value, ofpacts); } else if (!strcasecmp(key, "bundle_load")) { error = parse_bundle_load(value, ofpacts); } else if (!strcasecmp(key, "drop")) { @@ -5845,17 +7509,17 @@ ofpacts_parse__(char *str, struct ofpbuf *ofpacts, } prev_inst = inst; } - ofpact_pad(ofpacts); - if (drop && ofpbuf_size(ofpacts)) { + if (drop && ofpacts->size) { return xstrdup("\"drop\" must not be accompanied by any other action " "or instruction"); } - retval = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts), + retval = ofpacts_verify(ofpacts->data, ofpacts->size, (allow_instructions ? (1u << N_OVS_INSTRUCTIONS) - 1 - : 1u << OVSINST_OFPIT11_APPLY_ACTIONS)); + : 1u << OVSINST_OFPIT11_APPLY_ACTIONS), + outer_action); if (retval) { return xstrdup("Incorrect instruction ordering"); } @@ -5863,45 +7527,48 @@ ofpacts_parse__(char *str, struct ofpbuf *ofpacts, return NULL; } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT ofpacts_parse(char *str, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols, bool allow_instructions) + enum ofputil_protocol *usable_protocols, bool allow_instructions, + enum ofpact_type outer_action) { - uint32_t orig_size = ofpbuf_size(ofpacts); + uint32_t orig_size = ofpacts->size; char *error = ofpacts_parse__(str, ofpacts, usable_protocols, - allow_instructions); + allow_instructions, outer_action); if (error) { - ofpbuf_set_size(ofpacts, orig_size); + ofpacts->size = orig_size; } return error; } -static char * WARN_UNUSED_RESULT +static char * OVS_WARN_UNUSED_RESULT ofpacts_parse_copy(const char *s_, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols, - bool allow_instructions) + bool allow_instructions, enum ofpact_type outer_action) { char *error, *s; *usable_protocols = OFPUTIL_P_ANY; s = xstrdup(s_); - error = ofpacts_parse(s, ofpacts, usable_protocols, allow_instructions); + error = ofpacts_parse(s, ofpacts, usable_protocols, allow_instructions, + outer_action); free(s); return error; } /* Parses 's' as a set of OpenFlow actions and appends the actions to - * 'ofpacts'. + * 'ofpacts'. 'outer_action', if nonzero, specifies that 's' contains actions + * that are nested within the action of type 'outer_action'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ -char * WARN_UNUSED_RESULT +char * OVS_WARN_UNUSED_RESULT ofpacts_parse_actions(const char *s, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { - return ofpacts_parse_copy(s, ofpacts, usable_protocols, false); + return ofpacts_parse_copy(s, ofpacts, usable_protocols, false, 0); } /* Parses 's' as a set of OpenFlow instructions and appends the instructions to @@ -5909,11 +7576,11 @@ ofpacts_parse_actions(const char *s, struct ofpbuf *ofpacts, * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ -char * WARN_UNUSED_RESULT +char * OVS_WARN_UNUSED_RESULT ofpacts_parse_instructions(const char *s, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { - return ofpacts_parse_copy(s, ofpacts, usable_protocols, true); + return ofpacts_parse_copy(s, ofpacts, usable_protocols, true, 0); } const char * @@ -5991,15 +7658,20 @@ struct ofp_action_header { }; OFP_ASSERT(sizeof(struct ofp_action_header) == 8); -/* Header for Nicira-defined actions. */ -struct nx_action_header { +/* Header for Nicira-defined actions and for ONF vendor extensions. + * + * This cannot be used as an entirely generic vendor extension action header, + * because OpenFlow does not specify the location or size of the action + * subtype; it just happens that ONF extensions and Nicira extensions share + * this format. */ +struct ext_action_header { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* At least 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be32 vendor; /* NX_VENDOR_ID or ONF_VENDOR_ID. */ ovs_be16 subtype; /* See enum ofp_raw_action_type. */ uint8_t pad[6]; }; -OFP_ASSERT(sizeof(struct nx_action_header) == 16); +OFP_ASSERT(sizeof(struct ext_action_header) == 16); static bool ofpact_hdrs_equal(const struct ofpact_hdrs *a, @@ -6077,11 +7749,11 @@ ofpact_decode_raw(enum ofp_version ofp_version, if (oah->type == htons(OFPAT_VENDOR)) { /* Get vendor. */ hdrs.vendor = ntohl(oah->vendor); - if (hdrs.vendor == NX_VENDOR_ID) { - /* Get Nicira action type. */ - const struct nx_action_header *nah; + if (hdrs.vendor == NX_VENDOR_ID || hdrs.vendor == ONF_VENDOR_ID) { + /* Get extension subtype. */ + const struct ext_action_header *nah; - nah = ALIGNED_CAST(const struct nx_action_header *, oah); + nah = ALIGNED_CAST(const struct ext_action_header *, oah); if (length < sizeof *nah) { return OFPERR_OFPBAC_BAD_LEN; } @@ -6114,13 +7786,13 @@ static enum ofperr ofpact_pull_raw(struct ofpbuf *buf, enum ofp_version ofp_version, enum ofp_raw_action_type *raw, uint64_t *arg) { - const struct ofp_action_header *oah = ofpbuf_data(buf); + const struct ofp_action_header *oah = buf->data; const struct ofpact_raw_instance *action; unsigned int length; enum ofperr error; *raw = *arg = 0; - error = ofpact_decode_raw(ofp_version, oah, ofpbuf_size(buf), &action); + error = ofpact_decode_raw(ofp_version, oah, buf->size, &action); if (error) { return error; } @@ -6132,6 +7804,11 @@ ofpact_pull_raw(struct ofpbuf *buf, enum ofp_version ofp_version, } length = ntohs(oah->len); + if (length > buf->size) { + VLOG_WARN_RL(&rl, "OpenFlow action %s length %u exceeds action buffer " + "length %"PRIu32, action->name, length, buf->size); + return OFPERR_OFPBAC_BAD_LEN; + } if (length < action->min_length || length > action->max_length) { VLOG_WARN_RL(&rl, "OpenFlow action %s length %u not in valid range " "[%hu,%hu]", action->name, length, @@ -6195,8 +7872,9 @@ ofpact_put_raw(struct ofpbuf *buf, enum ofp_version ofp_version, case 0: break; - case NX_VENDOR_ID: { - struct nx_action_header *nah = (struct nx_action_header *) oah; + case NX_VENDOR_ID: + case ONF_VENDOR_ID: { + struct ext_action_header *nah = (struct ext_action_header *) oah; nah->subtype = htons(hdrs->type); break; } @@ -6219,3 +7897,16 @@ ofpact_put_raw(struct ofpbuf *buf, enum ofp_version ofp_version, return oah; } + +static void +pad_ofpat(struct ofpbuf *openflow, size_t start_ofs) +{ + struct ofp_action_header *oah; + + ofpbuf_put_zeros(openflow, PAD_SIZE(openflow->size - start_ofs, + OFP_ACTION_ALIGN)); + + oah = ofpbuf_at_assert(openflow, start_ofs, sizeof *oah); + oah->len = htons(openflow->size - start_ofs); +} +