Signed-off-by: Isaku Yamahata <yamah...@valinux.co.jp> --- v5 - manual rebase
v4 - squashed goto-table instruction part into single patch - only introduce goto-table. Other instruction will be addressed by other patches. - check zero padding - man page - unit test v3 - introduce OFPACT_{CLEAR_ACTIONS, WRITE_ACTIONS, GOTO_TABLE} v2 - changed for ofp_instruction --- lib/ofp-actions.c | 157 ++++++++++++++++++++++++++++++++++++++++----- lib/ofp-actions.h | 21 ++++++- lib/ofp-parse.c | 125 ++++++++++++++++++++++++++++++++++--- ofproto/ofproto-dpif.c | 9 +++ tests/ofp-actions.at | 14 +++- utilities/ovs-ofctl.8.in | 18 +++++ 6 files changed, 313 insertions(+), 31 deletions(-) diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index baa6fdd..1ff35cc 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -939,9 +939,15 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow, } if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) { + const struct ofp11_instruction_actions *oia; const union ofp_action *actions; size_t n_actions; + oia = instruction_get_OFPIT11_APPLY_ACTIONS( + insts[OVSINST_OFPIT11_APPLY_ACTIONS]); + if (!is_all_zeros(oia->pad, sizeof oia->pad)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS], &actions, &n_actions); error = ofpacts_from_openflow11(actions, n_actions, ofpacts); @@ -949,9 +955,23 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow, goto exit; } } + /* TODO:XXX Clear-Actions */ + /* TODO:XXX Write-Actions */ + /* TODO:XXX Write-Metadata */ + if (insts[OVSINST_OFPIT11_GOTO_TABLE]) { + const struct ofp11_instruction_goto_table *oigt; + struct ofpact_goto_table *ogt; + + oigt = instruction_get_OFPIT11_GOTO_TABLE( + insts[OVSINST_OFPIT11_GOTO_TABLE]); + if (!is_all_zeros(oigt->pad, sizeof oigt->pad)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ogt = ofpact_put_GOTO_TABLE(ofpacts); + ogt->table_id = oigt->table_id; + } - if (insts[OVSINST_OFPIT11_GOTO_TABLE] || - insts[OVSINST_OFPIT11_WRITE_METADATA] || + if (insts[OVSINST_OFPIT11_WRITE_METADATA] || insts[OVSINST_OFPIT11_WRITE_ACTIONS] || insts[OVSINST_OFPIT11_CLEAR_ACTIONS]) { error = OFPERR_OFPBIC_UNSUP_INST; @@ -1031,6 +1051,9 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) case OFPACT_EXIT: return 0; + case OFPACT_GOTO_TABLE: + return 0; + default: NOT_REACHED(); } @@ -1246,6 +1269,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) case OFPACT_SET_IPV4_DSCP: case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: + case OFPACT_GOTO_TABLE: NOT_REACHED(); } } @@ -1335,6 +1359,10 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out) = htons(ofpact_get_SET_L4_DST_PORT(a)->port); break; + case OFPACT_GOTO_TABLE: + /* TODO:XXX */ + break; + case OFPACT_CONTROLLER: case OFPACT_OUTPUT_REG: case OFPACT_BUNDLE: @@ -1443,6 +1471,9 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) = htons(ofpact_get_SET_L4_DST_PORT(a)->port); break; + case OFPACT_GOTO_TABLE: + NOT_REACHED(); + case OFPACT_CONTROLLER: case OFPACT_OUTPUT_REG: case OFPACT_BUNDLE: @@ -1481,18 +1512,10 @@ ofpacts_put_openflow11_actions(const struct ofpact ofpacts[], return openflow->size - start_size; } -void -ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[], - size_t ofpacts_len, - struct ofpbuf *openflow) +static void +ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs) { struct ofp11_instruction_actions *oia; - size_t ofs; - - /* Put an OFPIT11_APPLY_ACTIONS instruction and fill it in. */ - ofs = openflow->size; - instruction_put_OFPIT11_APPLY_ACTIONS(openflow); - ofpacts_put_openflow11_actions(ofpacts, ofpacts_len, openflow); /* Update the instruction's length (or, if it's empty, delete it). */ oia = ofpbuf_at_assert(openflow, ofs, sizeof *oia); @@ -1502,6 +1525,46 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[], openflow->size = ofs; } } + +void +ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[], + size_t ofpacts_len, + struct ofpbuf *openflow) +{ + bool in_instruction; + size_t ofs; + const struct ofpact *a; + + in_instruction = false; + ofs = 0; + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + if (in_instruction && ofpact_is_instruction(a)) { + ofpacts_update_instruction_actions(openflow, ofs); + in_instruction = false; + ofs = 0; + } + + /* TODO:XXX Clear-Actions */ + /* TODO:XXX Write-Actions */ + /* TODO:XXX Write-Metadata */ + if (a->type == OFPACT_GOTO_TABLE) { + struct ofp11_instruction_goto_table *oigt; + oigt = instruction_put_OFPIT11_GOTO_TABLE(openflow); + oigt->table_id = ofpact_get_GOTO_TABLE(a)->table_id; + memset(oigt->pad, 0, sizeof oigt->pad); + } else { + if (!in_instruction) { + in_instruction = true; + ofs = openflow->size; + instruction_put_OFPIT11_APPLY_ACTIONS(openflow); + } + ofpact_to_openflow11(a, openflow); + } + } + if (in_instruction) { + ofpacts_update_instruction_actions(openflow, ofs); + } +} /* Returns true if 'action' outputs to 'port', false otherwise. */ static bool @@ -1540,6 +1603,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port) case OFPACT_AUTOPATH: case OFPACT_NOTE: case OFPACT_EXIT: + case OFPACT_GOTO_TABLE: default: return false; } @@ -1803,28 +1867,85 @@ ofpact_format(const struct ofpact *a, struct ds *s) case OFPACT_EXIT: ds_put_cstr(s, "exit"); break; + + case OFPACT_GOTO_TABLE: + NOT_REACHED(); } } +static void +ofpacts_format_close_paren(struct ds *string, int n_actions) +{ + if (n_actions == 0) { + ds_put_cstr(string, "drop"); + } + ds_put_cstr(string, ")"); +} + /* Appends a string representing the 'ofpacts_len' bytes of ofpacts in * 'ofpacts' to 'string'. */ void ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len, struct ds *string) { - ds_put_cstr(string, "actions="); + const struct ofpact *a; + const char *prefix; + bool use_instruction; + bool in_instruction; + int n_actions; + + prefix = "actions"; + use_instruction = false; + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + if (ofpact_is_instruction(a)) { + prefix = "instructions"; + use_instruction = true; + break; + } + } + ds_put_format(string, "%s=", prefix); if (!ofpacts_len) { ds_put_cstr(string, "drop"); - } else { - const struct ofpact *a; + return; + } - OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { - if (a != ofpacts) { - ds_put_cstr(string, ","); + in_instruction = false; + n_actions = 0; + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + if (a != ofpacts) { + ds_put_cstr(string, ","); + } + + if (in_instruction && ofpact_is_instruction(a)) { + ofpacts_format_close_paren(string, n_actions); + in_instruction = false; + n_actions = 0; + } + + /* TODO:XXX clear-actions */ + /* TODO:XXX write-actions */ + /* TODO:XXX write-metadata */ + if (a->type == OFPACT_GOTO_TABLE) { + ds_put_format(string, "%s:%"PRIu8, + ofpact_instruction_name_from_type( + OVSINST_OFPIT11_GOTO_TABLE), + ofpact_get_GOTO_TABLE(a)->table_id); + } else { + if (!in_instruction && use_instruction) { + ds_put_format(string, "%s(", ofpact_instruction_name_from_type( + OVSINST_OFPIT11_APPLY_ACTIONS)); + in_instruction = true; + n_actions = 0; } + ofpact_format(a, string); + n_actions++; } } + + if (in_instruction) { + ofpacts_format_close_paren(string, n_actions); + } } /* Internal use by helpers. */ diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index 4fc9094..4995c66 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -87,7 +87,11 @@ \ /* Other. */ \ DEFINE_OFPACT(NOTE, ofpact_note, data) \ - DEFINE_OFPACT(EXIT, ofpact_null, ofpact) + DEFINE_OFPACT(EXIT, ofpact_null, ofpact) \ + \ + /* Instructions */ \ + /* TODO:XXX Clear-Actions, Write-Actions, Write-Metadata */ \ + DEFINE_OFPACT(GOTO_TABLE, ofpact_goto_table, ofpact) /* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */ enum OVS_PACKED_ENUM ofpact_type { @@ -389,7 +393,14 @@ struct ofpact_cnt_ids { /* Controller ids. */ unsigned int n_controllers; uint16_t cnt_ids[]; +}; +/* OFPACT_GOTO_TABLE + * + * Used for OFPIT11_GOTO_TABLE */ +struct ofpact_goto_table { + struct ofpact ofpact; + uint8_t table_id; }; /* Converting OpenFlow to ofpacts. */ @@ -542,6 +553,14 @@ enum { #undef DEFINE_INST }; + +static inline bool +ofpact_is_instruction(const struct ofpact *a) +{ + /* TODO:XXX Clear-Actions, Write-Actions, Write-Metadata */ + return a->type == OFPACT_GOTO_TABLE; +} + const char *ofpact_instruction_name_from_type(enum ovs_instruction_type type); int ofpact_instruction_type_from_name(const char *name); diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index e5f5ea0..8248830 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -507,6 +507,90 @@ str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) ofpact_pad(ofpacts); } +static void +parse_named_instruction(enum ovs_instruction_type type, + const struct flow *flow, + char *arg, struct ofpbuf *ofpacts) +{ + switch (type) { + case OVSINST_OFPIT11_APPLY_ACTIONS: + str_to_ofpacts(flow, arg, ofpacts); + break; + + case OVSINST_OFPIT11_WRITE_ACTIONS: + NOT_REACHED(); /* TODO:XXX */ + break; + + case OVSINST_OFPIT11_CLEAR_ACTIONS: + NOT_REACHED(); /* TODO:XXX */ + break; + + case OVSINST_OFPIT11_WRITE_METADATA: + NOT_REACHED(); /* TODO:XXX */ + break; + + case OVSINST_OFPIT11_GOTO_TABLE: { + struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts); + char *table_s = strsep(&arg, ","); + if (!table_s || !table_s[0]) { + ovs_fatal(0, "instruction goto-table needs table id"); + } + ogt->table_id = str_to_table_id(table_s); + break; + } + } +} + +static void +str_to_inst_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) +{ + char *pos, *inst, *arg; + char *inst_args[N_OVS_INSTRUCTIONS]; + int type; + const char *prev_inst = NULL; + int prev_type = -1; + + memset(inst_args, 0, sizeof inst_args); + pos = str; + while (ofputil_parse_key_value(&pos, &inst, &arg)) { + type = ofpact_instruction_type_from_name(inst); + if (type < 0) { + ovs_fatal(0, "Unknown instruction: %s", inst); + } + if (type == prev_type) { + ovs_fatal(0, "instruction can be specified at most once: %s", + inst); + } + if (type <= prev_type) { + ovs_fatal(0, "Instruction %s must be specified before %s", + inst, prev_inst); + } + inst_args[type] = arg; + + prev_inst = inst; + prev_type = type; + } + + /* TODO:XXX */ + if (inst_args[OVSINST_OFPIT11_CLEAR_ACTIONS]) { + ovs_fatal(0, "instruction clear-actions is not supported yet"); + } + if (inst_args[OVSINST_OFPIT11_WRITE_ACTIONS]) { + ovs_fatal(0, "instruction write-actions is not supported yet"); + } + 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); + } + } + + ofpact_pad(ofpacts); +} + struct protocol { const char *name; uint16_t dl_type; @@ -588,6 +672,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, } fields; char *string = xstrdup(str_); char *save_ptr = NULL; + char *inst_str = NULL; char *act_str = NULL; char *name; @@ -638,17 +723,35 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, fm->flags = 0; if (fields & F_ACTIONS) { act_str = strstr(string, "action"); - if (!act_str) { - ofp_fatal(str_, verbose, "must specify an action"); - } - *act_str = '\0'; + if (act_str) { + *act_str = '\0'; + + act_str = strchr(act_str + 1, '='); + if (!act_str) { + ofp_fatal(str_, verbose, "must specify an action"); + } - act_str = strchr(act_str + 1, '='); - if (!act_str) { - ofp_fatal(str_, verbose, "must specify an action"); + act_str++; } - act_str++; + inst_str = strstr(string, "instruction"); + if (inst_str) { + *inst_str = '\0'; + + inst_str = strchr(inst_str + 1, '='); + if (!inst_str) { + ofp_fatal(str_, verbose, "must specify an instruction"); + } + + inst_str++; + } + } + if (fields & F_ACTIONS && act_str == NULL && inst_str == NULL) { + ofp_fatal(str_, verbose, "must specify an action or an instruction"); + } + if (act_str && inst_str) { + ofp_fatal(str_, verbose, + "must not specify both action and instruction."); } for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { @@ -725,7 +828,11 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, struct ofpbuf ofpacts; ofpbuf_init(&ofpacts, 32); - str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts); + if (act_str) { + str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts); + } else { + str_to_inst_ofpacts(&fm->cr.flow, inst_str, &ofpacts); + } fm->ofpacts_len = ofpacts.size; fm->ofpacts = ofpbuf_steal_data(&ofpacts); } else { diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index b83a50f..ed0bc08 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -5581,6 +5581,15 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, ctx->has_fin_timeout = true; xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a)); break; + + case OFPACT_GOTO_TABLE: { + /* TODO:XXX remove recursion */ + /* It is assumed that goto-table is last action */ + struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a); + assert(ctx->table_id < ogt->table_id); + xlate_table_action(ctx, ctx->flow.in_port, ogt->table_id); + break; + } } } diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at index 36c67f1..acc9e13 100644 --- a/tests/ofp-actions.at +++ b/tests/ofp-actions.at @@ -247,6 +247,10 @@ AT_DATA([test-data], [dnl 0004 0018 00000000 dnl 0000 0010 fffffffe 04d2 000000000000 +dnl Apply-Actions non-zero padding +# bad OF1.1 instructions: OFPBAC_BAD_ARGUMENT +0004 0008 00000001 + dnl Check that an empty Apply-Actions instruction gets dropped. # actions=drop # 0: 00 -> (none) @@ -271,8 +275,12 @@ dnl Goto-Table instruction too long. # bad OF1.1 instructions: OFPBIC_BAD_LEN 0001 0010 01 000000 0000000000000000 -dnl Goto-Table not supported yet. -# bad OF1.1 instructions: OFPBIC_UNSUP_INST +dnl Goto-Table instruction non-zero padding +# bad OF1.1 instructions: OFPBAC_BAD_ARGUMENT +0001 0008 01 000001 + +dnl Goto-Table 1 +# instructions=goto_table:1 0001 0008 01 000000 dnl Write-Metadata not supported yet. @@ -292,7 +300,7 @@ dnl Write-Actions not supported yet. 0003 0008 01 000000 dnl Clear-Actions not supported yet. -# bad OF1.1 instructions: OFPBIC_UNSUP_INST +# instructions=clear_actions 0005 0008 01 000000 dnl Experimenter actions not supported yet. diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 386f7c8..8d9bcec 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -1122,6 +1122,24 @@ with no match criteria. (This is why the default \fBtable\fR is 1, to keep the learned flows separate from the primary flow table 0.) .RE . +.IP \fBinstructions=\fR[\fIinstruction\fR][\fB,\fIinstruction\fR...]\fR +Specifies a comma-separated list of instructions to take on a packet +when the flow entry matches. Instructions must be listed in specific order +and can be specified at most once. Otherwise, it results in an error. +If no target is specified, then packtes matching the flow are dropped. +The \fIinstruction\fR is one of the following and must be specified in +listed order. +keywords: +. +.RS +.IP \fBapply_actions(\fR[\fIaction\fR][\fB,\fIaction\fR...]\fB) +Applies the specific action(s) immediately. The syntax of actions are same +to \fBactions=\fR field. +. +.IP \fBgoto_table\fR:\fItable\fR +Indicates the next table in the process pipeline. +.RE +. .IP "\fBfin_timeout(\fIargument\fR[\fB,\fIargument\fR]\fB)" This action changes the idle timeout or hard timeout, or both, of this OpenFlow rule when the rule matches a TCP packet with the FIN or RST -- 1.7.1.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev