In OpenFlow 1.1, we add support for OFPIT_WRITE_METADATA. This allows us to write to the metadata field. Internally it is represented using ofpact_metadata.
We introduce NXAST_WRITE_METADATA to handle writing to the metadata field in OpenFlow 1.0+. This structure reflects OFPIT_WRITE_METADATA. When writing out the structure to OpenFlow 1.1, it uses the OFPIT_WRITE_METADATA instruction only, and not the new NXAST action (which would be redundant). Signed-off-by: Joe Stringer <[email protected]> --- v4: - Fixed v3 feedback - Switched ofpact_metadata to network byte-order - Added tests for duplicated/out-of-order write-metadata messages - Rebased against yamahata's v5 instruction set v3: - New function ofpacts_verify() which makes sure that the internal ofpacts struct is correct after all parsing has been completed. At the moment, this just means making sure that any write_metadata instructions(/actions) are specified last. Looking for comments on this as my solution to the problems from v2. (explanation here: http://openvswitch.org/pipermail/dev/2012-July/018956.html) --- NEWS | 1 + include/openflow/nicira-ext.h | 15 +++++ lib/ofp-actions.c | 115 ++++++++++++++++++++++++++++++++++++++--- lib/ofp-actions.h | 14 ++++- lib/ofp-parse.c | 46 ++++++++++++++-- lib/ofp-util.def | 2 + lib/ofp-util.h | 1 + ofproto/ofproto-dpif.c | 8 +++ tests/ofp-actions.at | 76 +++++++++++++++++++++++++-- utilities/ovs-ofctl.8.in | 9 +++- 10 files changed, 265 insertions(+), 22 deletions(-) diff --git a/NEWS b/NEWS index 872f8d0..b6f82cc 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ post-v1.8.0 - OpenFlow: - Allow bitwise masking for SHA and THA fields in ARP, SLL and TLL fields in IPv6 neighbor discovery messages, and IPv6 flow label. + - Adds support for writing to the metadata field for a flow. - ovs-dpctl: - Support requesting the port number with the "port_no" option in the "add-if" command. diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index 75bf6db..fd032c7 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -304,6 +304,7 @@ enum nx_action_subtype { NXAST_FIN_TIMEOUT, /* struct nx_action_fin_timeout */ NXAST_CONTROLLER, /* struct nx_action_controller */ NXAST_DEC_TTL_CNT_IDS, /* struct nx_action_cnt_ids */ + NXAST_WRITE_METADATA, /* struct nx_action_write_metadata */ }; /* Header for Nicira-defined actions. */ @@ -2192,4 +2193,18 @@ struct nx_flow_monitor_cancel { }; OFP_ASSERT(sizeof(struct nx_flow_monitor_cancel) == 4); +/* Action structure for NXAST_WRITE_METADATA. + * + * Modifies the 'mask' bits of the metadata value. */ +struct nx_action_write_metadata { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 32. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_WRITE_METADATA. */ + uint8_t zeros[6]; /* Must be zero. */ + ovs_be64 metadata; /* Metadata register. */ + ovs_be64 mask; /* Metadata mask. */ +}; +OFP_ASSERT(sizeof(struct nx_action_write_metadata) == 32); + #endif /* openflow/nicira-ext.h */ diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 494faba..c7c42a2 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -135,6 +135,24 @@ controller_from_openflow(const struct nx_action_controller *nac, oc->reason = nac->reason; } +static enum ofperr +metadata_from_openflow(const struct nx_action_write_metadata *nawm, + struct ofpbuf *out) +{ + struct ofpact_metadata *om; + + if (nawm->zeros[0] | nawm->zeros[1] | nawm->zeros[2] | nawm->zeros[3] + | nawm->zeros[4] | nawm->zeros[5]) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + + om = ofpact_put_WRITE_METADATA(out); + om->metadata = nawm->metadata; + om->mask = nawm->mask; + + return 0; +} + static void note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out) { @@ -276,6 +294,7 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code, const struct nx_action_set_queue *nasq; const struct nx_action_note *nan; const struct nx_action_set_tunnel64 *nast64; + const struct nx_action_write_metadata *nawm; struct ofpact_tunnel *tunnel; enum ofperr error = 0; @@ -297,6 +316,11 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code, tunnel->tun_id = ntohl(nast->tun_id); break; + case OFPUTIL_NXAST_WRITE_METADATA: + nawm = (const struct nx_action_write_metadata *) a; + error = metadata_from_openflow(nawm, out); + break; + case OFPUTIL_NXAST_SET_QUEUE: nasq = (const struct nx_action_set_queue *) a; ofpact_put_SET_QUEUE(out)->queue_id = ntohl(nasq->queue_id); @@ -569,6 +593,12 @@ ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len, error = translate(actions, actions_len / OFP_ACTION_ALIGN, ofpacts); if (error) { ofpbuf_clear(ofpacts); + return error; + } + + error = ofpacts_verify(ofpacts->data, ofpacts->size); + if (error) { + ofpbuf_clear(ofpacts); } return error; } @@ -989,7 +1019,17 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow, goto exit; } } - /* TODO:XXX Write-Metadata */ + if (insts[OVSINST_OFPIT11_WRITE_METADATA]) { + const struct ofp11_instruction_write_metadata *oiwm; + struct ofpact_metadata *om; + + oiwm = (const struct ofp11_instruction_write_metadata *) + insts[OVSINST_OFPIT11_WRITE_METADATA]; + + om = ofpact_put_WRITE_METADATA(ofpacts); + om->metadata = oiwm->metadata; + om->mask = oiwm->metadata_mask; + } if (insts[OVSINST_OFPIT11_GOTO_TABLE]) { const struct ofp11_instruction_goto_table *oigt; struct ofpact_goto_table *ogt; @@ -1003,11 +1043,7 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow, ogt->table_id = oigt->table_id; } - if (insts[OVSINST_OFPIT11_WRITE_METADATA]) { - error = OFPERR_OFPBIC_UNSUP_INST; - goto exit; - } - + error = ofpacts_verify(ofpacts->data, ofpacts->size); exit: if (error) { ofpbuf_clear(ofpacts); @@ -1083,6 +1119,7 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) case OFPACT_CLEAR_ACTIONS: case OFPACT_WRITE_ACTIONS: + case OFPACT_WRITE_METADATA: case OFPACT_GOTO_TABLE: return 0; @@ -1109,6 +1146,33 @@ ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len, return 0; } + +/* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are + * in the appropriate order as defined by the OpenFlow spec. */ +enum ofperr +ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len) { + const struct ofpact *a; + const struct ofpact_metadata *om = NULL; + + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + if (om) { + if (a->type == OFPACT_WRITE_METADATA) { + VLOG_WARN("duplicate write_metadata instruction specified"); + return OFPERR_NXBIC_DUP_TYPE; + } else { + VLOG_WARN("write_metadata instruction must be specified after " + "other instructions/actions"); + return OFPERR_OFPBAC_UNSUPPORTED_ORDER; + } + } + + if (a->type == OFPACT_WRITE_METADATA) { + om = (const struct ofpact_metadata *) a; + } + } + + return 0; +} /* Converting ofpacts to Nicira OpenFlow extensions. */ @@ -1155,6 +1219,17 @@ ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel, } static void +ofpact_write_metadata_to_nxast(const struct ofpact_metadata *om, + struct ofpbuf *out) +{ + struct nx_action_write_metadata *nawm; + + nawm = ofputil_put_NXAST_WRITE_METADATA(out); + nawm->metadata = om->metadata; + nawm->mask = om->mask; +} + +static void ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out) { size_t start_ofs = out->size; @@ -1252,6 +1327,10 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out); break; + case OFPACT_WRITE_METADATA: + ofpact_write_metadata_to_nxast(ofpact_get_WRITE_METADATA(a), out); + break; + case OFPACT_SET_QUEUE: ofputil_put_NXAST_SET_QUEUE(out)->queue_id = htonl(ofpact_get_SET_QUEUE(a)->queue_id); @@ -1406,6 +1485,7 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out) case OFPACT_REG_LOAD: case OFPACT_DEC_TTL: case OFPACT_SET_TUNNEL: + case OFPACT_WRITE_METADATA: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: case OFPACT_FIN_TIMEOUT: @@ -1507,6 +1587,10 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) = htons(ofpact_get_SET_L4_DST_PORT(a)->port); break; + case OFPACT_WRITE_METADATA: + /* OpenFlow 1.1 uses OFPIT_WRITE_METADATA to express this action. */ + break; + case OFPACT_CLEAR_ACTIONS: case OFPACT_WRITE_ACTIONS: case OFPACT_GOTO_TABLE: @@ -1582,7 +1666,6 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[], ofs = 0; } - /* TODO:XXX Write-Metadata */ if (a->type == OFPACT_CLEAR_ACTIONS) { struct ofp11_instruction *oi; oi = instruction_put_OFPIT11_CLEAR_ACTIONS(openflow); @@ -1591,6 +1674,13 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[], in_instruction = true; ofs = openflow->size; instruction_put_OFPIT11_WRITE_ACTIONS(openflow); + } else if (a->type == OFPACT_WRITE_METADATA) { + const struct ofpact_metadata *om; + struct ofp11_instruction_write_metadata *oiwm; + om = ofpact_get_WRITE_METADATA(a); + oiwm = instruction_put_OFPIT11_WRITE_METADATA(openflow); + oiwm->metadata = om->metadata; + oiwm->metadata_mask = om->mask; } else if (a->type == OFPACT_GOTO_TABLE) { struct ofp11_instruction_goto_table *oigt; oigt = instruction_put_OFPIT11_GOTO_TABLE(openflow); @@ -1638,6 +1728,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port) case OFPACT_REG_LOAD: case OFPACT_DEC_TTL: case OFPACT_SET_TUNNEL: + case OFPACT_WRITE_METADATA: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: case OFPACT_FIN_TIMEOUT: @@ -1916,6 +2007,7 @@ ofpact_format(const struct ofpact *a, struct ds *s) case OFPACT_CLEAR_ACTIONS: case OFPACT_WRITE_ACTIONS: + case OFPACT_WRITE_METADATA: case OFPACT_GOTO_TABLE: NOT_REACHED(); } @@ -1970,7 +2062,6 @@ ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len, n_actions = 0; } - /* TODO:XXX write-metadata */ if (a->type == OFPACT_CLEAR_ACTIONS) { ds_put_format(string, "%s", ofpact_instruction_name_from_type( OVSINST_OFPIT11_CLEAR_ACTIONS)); @@ -1979,6 +2070,14 @@ ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len, OVSINST_OFPIT11_WRITE_ACTIONS)); in_instruction = true; n_actions = 0; + } else if (a->type == OFPACT_WRITE_METADATA) { + const struct ofpact_metadata *metadata; + metadata = ofpact_get_WRITE_METADATA(a); + ds_put_format(string, "write_metadata:%#"PRIx64, + ntohll(metadata->metadata)); + if (metadata->mask != UINT64_MAX) { + ds_put_format(string, "/%#"PRIx64, ntohll(metadata->mask)); + } } else if (a->type == OFPACT_GOTO_TABLE) { ds_put_format(string, "%s:%"PRIu8, ofpact_instruction_name_from_type( diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index e5cb019..8b11da3 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -90,7 +90,7 @@ DEFINE_OFPACT(EXIT, ofpact_null, ofpact) \ \ /* Instructions */ \ - /* TODO:XXX Write-Metadata */ \ + DEFINE_OFPACT(WRITE_METADATA, ofpact_metadata, ofpact) \ DEFINE_OFPACT(CLEAR_ACTIONS, ofpact_null, ofpact) \ DEFINE_OFPACT(WRITE_ACTIONS, ofpact_null, ofpact) \ DEFINE_OFPACT(GOTO_TABLE, ofpact_goto_table, ofpact) @@ -311,6 +311,15 @@ struct ofpact_fin_timeout { uint16_t fin_hard_timeout; }; +/* OFPACT_WRITE_METADATA. + * + * Used for NXAST_WRITE_METADATA. */ +struct ofpact_metadata { + struct ofpact ofpact; + ovs_be64 metadata; + ovs_be64 mask; +}; + /* OFPACT_RESUBMIT. * * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */ @@ -419,6 +428,7 @@ enum ofperr ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow, struct ofpbuf *ofpacts); enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len, const struct flow *, int max_ports); +enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len); /* Converting ofpacts to OpenFlow. */ void ofpacts_put_openflow10(const struct ofpact[], size_t ofpacts_len, @@ -561,9 +571,9 @@ enum { static inline bool ofpact_is_instruction(const struct ofpact *a) { - /* TODO:XXX Write-Metadata */ return a->type == OFPACT_CLEAR_ACTIONS || a->type == OFPACT_WRITE_ACTIONS + || a->type == OFPACT_WRITE_METADATA || a->type == OFPACT_GOTO_TABLE; } diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index d6eac26..1990687 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -314,6 +314,24 @@ parse_dec_ttl(struct ofpbuf *b, char *arg) } static void +parse_metadata(struct ofpbuf *b, char *arg) +{ + struct ofpact_metadata *om; + char *mask = strchr(arg, '/'); + + om = ofpact_put_WRITE_METADATA(b); + + if (mask) { + *mask = '\0'; + om->mask = htonll(str_to_u64(mask + 1)); + } else { + om->mask = htonll(UINT64_MAX); + } + + om->metadata = htonll(str_to_u64(arg)); +} + +static void parse_named_action(enum ofputil_action_code code, const struct flow *flow, char *arg, struct ofpbuf *ofpacts) { @@ -410,6 +428,10 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, tunnel->tun_id = str_to_u64(arg); break; + case OFPUTIL_NXAST_WRITE_METADATA: + parse_metadata(ofpacts, arg); + break; + case OFPUTIL_NXAST_SET_QUEUE: ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); break; @@ -478,6 +500,7 @@ str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) { char *pos, *act, *arg; int n_actions; + enum ofperr error; pos = str; n_actions = 0; @@ -504,6 +527,12 @@ str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) } n_actions++; } + + error = ofpacts_verify(ofpacts->data, ofpacts->size); + if (error) { + ovs_fatal(0, "Incorrect action ordering: %s", ofperr_to_string(error)); + } + ofpact_pad(ofpacts); } @@ -512,6 +541,8 @@ parse_named_instruction(enum ovs_instruction_type type, const struct flow *flow, char *arg, struct ofpbuf *ofpacts) { + enum ofperr error; + switch (type) { case OVSINST_OFPIT11_APPLY_ACTIONS: str_to_ofpacts(flow, arg, ofpacts); @@ -527,7 +558,7 @@ parse_named_instruction(enum ovs_instruction_type type, break; case OVSINST_OFPIT11_WRITE_METADATA: - NOT_REACHED(); /* TODO:XXX */ + parse_metadata(ofpacts, arg); break; case OVSINST_OFPIT11_GOTO_TABLE: { @@ -540,6 +571,14 @@ parse_named_instruction(enum ovs_instruction_type type, break; } } + + /* If write_metadata is specified as an action AND an instruction, ofpacts + could be invalid. */ + error = ofpacts_verify(ofpacts->data, ofpacts->size); + if (error) { + ovs_fatal(0, "Incorrect instruction ordering: %s", + ofperr_to_string(error)); + } } static void @@ -572,11 +611,6 @@ str_to_inst_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) prev_type = type; } - /* TODO:XXX */ - if (inst_args[OVSINST_OFPIT11_WRITE_METADATA]) { - ovs_fatal(0, "instruction write-metadata is not supported yet"); - } - for (type = 0; type < N_OVS_INSTRUCTIONS; type++) { if (inst_args[type]) { parse_named_instruction(type, flow, inst_args[type], ofpacts); diff --git a/lib/ofp-util.def b/lib/ofp-util.def index 391c14b..5d2d8d8 100644 --- a/lib/ofp-util.def +++ b/lib/ofp-util.def @@ -59,6 +59,8 @@ NXAST_ACTION(NXAST_DEC_TTL, nx_action_header, 0, "dec_ttl") NXAST_ACTION(NXAST_FIN_TIMEOUT, nx_action_fin_timeout, 0, "fin_timeout") NXAST_ACTION(NXAST_CONTROLLER, nx_action_controller, 0, "controller") NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids, 1, NULL) +NXAST_ACTION(NXAST_WRITE_METADATA, nx_action_write_metadata, 0, + "write_metadata") #undef OFPAT10_ACTION #undef OFPAT11_ACTION diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 9cc3028..6b83066 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -529,6 +529,7 @@ bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *); * OFPUTIL_OFPAT10_ENQUEUE * OFPUTIL_NXAST_RESUBMIT * OFPUTIL_NXAST_SET_TUNNEL + * OFPUTIL_NXAST_SET_METADATA * OFPUTIL_NXAST_SET_QUEUE * OFPUTIL_NXAST_POP_QUEUE * OFPUTIL_NXAST_REG_MOVE diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 1964837..d72e368 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -5462,6 +5462,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, } OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { struct ofpact_controller *controller; + const struct ofpact_metadata *metadata; if (ctx->exit) { break; @@ -5610,6 +5611,12 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, NOT_REACHED(); /* TODO:XXX */ break; + case OFPACT_WRITE_METADATA: + metadata = ofpact_get_WRITE_METADATA(a); + ctx->flow.metadata &= ~metadata->mask; + ctx->flow.metadata |= metadata->metadata & metadata->mask; + break; + case OFPACT_GOTO_TABLE: { /* TODO:XXX remove recursion */ /* It is assumed that goto-table is last action */ @@ -5643,6 +5650,7 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx, ctx->flow = *flow; ctx->base_flow = ctx->flow; ctx->base_flow.tun_id = 0; + ctx->base_flow.metadata = 0; ctx->base_flow.vlan_tci = initial_tci; ctx->rule = rule; ctx->packet = packet; diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at index 1c57015..ce677cd 100644 --- a/tests/ofp-actions.at +++ b/tests/ofp-actions.at @@ -1,7 +1,7 @@ AT_BANNER([OpenFlow actions]) AT_SETUP([OpenFlow 1.0 action translation]) -AT_KEYWORDS([OF1.0]) +AT_KEYWORDS([ofp-actions OF1.0]) AT_DATA([test-data], [dnl # actions=LOCAL 0000 0008 fffe 04d2 @@ -69,6 +69,12 @@ ffff 0018 00002320 0009 000000000000 c426384d49c53d60 # actions=set_tunnel64:0x885f3298 ffff 0018 00002320 0009 000000000000 00000000885f3298 +# instructions=write_metadata:0xfedcba9876543210 +ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff + +# instructions=write_metadata:0xfedcba9876543210/0xffff0000ffff0000 +ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffff0000ffff0000 + # actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[]) ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004 @@ -125,7 +131,7 @@ AT_CHECK( AT_CLEANUP AT_SETUP([OpenFlow 1.1 action translation]) -AT_KEYWORDS([OF1.1]) +AT_KEYWORDS([ofp-actions OF1.1]) AT_DATA([test-data], [dnl # actions=LOCAL 0000 0010 fffffffe 04d2 000000000000 @@ -187,6 +193,53 @@ ffff 0018 00002320 0009 000000000000 c426384d49c53d60 # actions=set_tunnel64:0x885f3298 ffff 0018 00002320 0009 000000000000 00000000885f3298 +dnl OpenFlow 1.1 uses OFPIT_WRITE_METADATA to express the NXAST_WRITE_METADATA +dnl action instead, so parse-ofp11-actions will recognise and drop this action. +# instructions=write_metadata:0xfedcba9876543210 +# 0: ff -> (none) +# 1: ff -> (none) +# 2: 00 -> (none) +# 3: 20 -> (none) +# 4: 00 -> (none) +# 5: 00 -> (none) +# 6: 23 -> (none) +# 7: 20 -> (none) +# 8: 00 -> (none) +# 9: 16 -> (none) +# 10: 00 -> (none) +# 11: 00 -> (none) +# 12: 00 -> (none) +# 13: 00 -> (none) +# 14: 00 -> (none) +# 15: 00 -> (none) +# 16: fe -> (none) +# 17: dc -> (none) +# 18: ba -> (none) +# 19: 98 -> (none) +# 20: 76 -> (none) +# 21: 54 -> (none) +# 22: 32 -> (none) +# 23: 10 -> (none) +# 24: ff -> (none) +# 25: ff -> (none) +# 26: ff -> (none) +# 27: ff -> (none) +# 28: ff -> (none) +# 29: ff -> (none) +# 30: ff -> (none) +# 31: ff -> (none) +ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff + +dnl Write-Metadata duplicated. +& ofp_actions|WARN|duplicate write_metadata instruction specified +# bad OF1.1 actions: NXBIC_DUP_TYPE +ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff + +dnl Write-Metadata in wrong position. +& ofp_actions|WARN|write_metadata instruction must be specified after other instructions/actions +# bad OF1.1 actions: OFPBAC_UNSUPPORTED_ORDER +ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff ffff 0010 00002320 0002 0000 12345678 + # actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[]) ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004 @@ -243,7 +296,7 @@ AT_CHECK( AT_CLEANUP AT_SETUP([OpenFlow 1.1 instruction translation]) -AT_KEYWORDS([OF1.1]) +AT_KEYWORDS([ofp-actions OF1.1]) AT_DATA([test-data], [dnl # actions=LOCAL 0004 0018 00000000 dnl @@ -285,10 +338,14 @@ dnl Goto-Table 1 # instructions=goto_table:1 0001 0008 01 000000 -dnl Write-Metadata not supported yet. -# bad OF1.1 instructions: OFPBIC_UNSUP_INST +dnl Write-Metadata. +# instructions=write_metadata:0xfedcba9876543210 0002 0018 00000000 fedcba9876543210 ffffffffffffffff +dnl Write-Metadata with mask. +# instructions=write_metadata:0xfedcba9876543210/0xff00ff00ff00ff00 +0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00 + dnl Write-Metadata too short. # bad OF1.1 instructions: OFPBIC_BAD_LEN 0002 0010 00000000 fedcba9876543210 @@ -297,6 +354,15 @@ dnl Write-Metadata too long. # bad OF1.1 instructions: OFPBIC_BAD_LEN 0002 0020 00000000 fedcba9876543210 ffffffffffffffff 0000000000000000 +dnl Write-Metadata duplicated. +# bad OF1.1 instructions: NXBIC_DUP_TYPE +0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00 0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00 + +dnl Write-Metadata in wrong position. +& ofp_actions|WARN|write_metadata instruction must be specified after other instructions/actions +# bad OF1.1 instructions: OFPBAC_UNSUPPORTED_ORDER +0001 0008 01 000000 0002 0018 00000000 fedcba9876543210 ffffffffffffffff + dnl Write-Actions non-zero padding # bad OF1.1 instructions: OFPBAC_BAD_ARGUMENT 0003 0008 00000001 diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 9573092..6a3750e 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -911,7 +911,6 @@ Sets the TCP or UDP destination port to \fIport\fR. Sets the IPv4 ToS/DSCP field to \fItos\fR. Valid values are between 0 and 255, inclusive. Note that the two lower reserved bits are never modified. -. .RE .IP The following actions are Nicira vendor extensions that, as of this writing, are @@ -1145,6 +1144,14 @@ Clears all the actions in the action set immediately. Merges the specified actions(s) into the current actions set. The syntax of actions are same to \fBactions=\fR field. . +.IP \fBwrite_metadata\fB:\fIvalue\fR[/\fImask\fR] +Updates the metadata field for the flow. If \fImask\fR is omitted, the +metadata field is set exactly to \fIvalue\fR; if \fImask\fR is specified, then +a 1-bit in \fImask\fR indicates that the corresponding bit in the metadata +field will be replaced with the corresponding bit from \fIvalue\fR. Both +\fIvalue\fR and \fImask\fR are 64-bit values that are decimal by default; use +a \fB0x\fR prefix to specify them in hexadecimal. +. .IP \fBgoto_table\fR:\fItable\fR Indicates the next table in the process pipeline. .RE -- 1.7.2.5 _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
