OpenFlow 1.1+ specs encourage switches to verify action consistency at flow setup time. Implement this for OpenFlow 1.1+ only to not break any current OF 1.0 based use.
Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com> --- lib/ofp-actions.c | 61 ++++++++++++++++++++++++++++++++++++++++---- lib/ofp-actions.h | 2 +- lib/ofp-parse.c | 43 ++++++++++++++++++++++--------- lib/ofp-parse.h | 12 ++++++--- lib/ofp-util.h | 1 + ofproto/ofproto.c | 17 +++++++----- tests/learn.at | 2 ++ tests/ovs-ofctl.at | 10 ++++++-- utilities/ovs-controller.c | 2 +- utilities/ovs-ofctl.c | 27 +++++++++++++------- 10 files changed, 137 insertions(+), 40 deletions(-) diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 777677a..f02ba4d 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -1521,7 +1521,7 @@ exit: /* May modify flow->dl_type, caller must restore it. */ static enum ofperr ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports, - uint8_t table_id) + uint8_t table_id, bool enforce_consistency) { const struct ofpact_enqueue *enqueue; @@ -1550,15 +1550,40 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports, case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: + return 0; + case OFPACT_STRIP_VLAN: + if (!(flow->vlan_tci & htons(VLAN_CFI))) { + goto inconsistent; + } + return 0; + case OFPACT_PUSH_VLAN: + if (flow->vlan_tci & htons(VLAN_CFI)) { + /* Multiple VLAN headers not supported. */ + return OFPERR_OFPBAC_BAD_TAG; + } + return 0; + case OFPACT_SET_ETH_SRC: case OFPACT_SET_ETH_DST: + return 0; + case OFPACT_SET_IPV4_SRC: case OFPACT_SET_IPV4_DST: case OFPACT_SET_IPV4_DSCP: + if (flow->dl_type != htons(ETH_TYPE_IP)) { + goto inconsistent; + } + return 0; + case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: + if (!is_ip_any(flow) || + (flow->nw_proto != IPPROTO_TCP && flow->nw_proto != IPPROTO_UDP + && flow->nw_proto != IPPROTO_SCTP)) { + goto inconsistent; + } return 0; case OFPACT_REG_MOVE: @@ -1574,15 +1599,30 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports, return nxm_stack_pop_check(ofpact_get_STACK_POP(a), flow); case OFPACT_DEC_TTL: + if (!is_ip_any(flow)) { + goto inconsistent; + } + return 0; + case OFPACT_SET_MPLS_TTL: case OFPACT_DEC_MPLS_TTL: + if (!eth_type_mpls(flow->dl_type)) { + goto inconsistent; + } + return 0; + case OFPACT_SET_TUNNEL: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: - case OFPACT_FIN_TIMEOUT: case OFPACT_RESUBMIT: return 0; + case OFPACT_FIN_TIMEOUT: + if (flow->nw_proto != IPPROTO_TCP) { + goto inconsistent; + } + return 0; + case OFPACT_LEARN: return learn_check(ofpact_get_LEARN(a), flow); @@ -1599,6 +1639,9 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports, case OFPACT_POP_MPLS: flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype; + if (!eth_type_mpls(flow->dl_type)) { + goto inconsistent; + } return 0; case OFPACT_SAMPLE: @@ -1610,7 +1653,7 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports, case OFPACT_WRITE_ACTIONS: { struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a); return ofpacts_check(on->actions, ofpact_nest_get_action_len(on), - flow, max_ports, table_id); + flow, max_ports, table_id, enforce_consistency); } case OFPACT_WRITE_METADATA: @@ -1636,6 +1679,12 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports, default: NOT_REACHED(); } + + inconsistent: + if (enforce_consistency) { + return OFPERR_OFPBAC_MATCH_INCONSISTENT; + } + return 0; } /* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are @@ -1645,14 +1694,16 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports, * May temporarily modify 'flow', but restores the changes before returning. */ enum ofperr ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len, - struct flow *flow, ofp_port_t max_ports, uint8_t table_id) + struct flow *flow, ofp_port_t max_ports, uint8_t table_id, + bool enforce_consistency) { const struct ofpact *a; ovs_be16 dl_type = flow->dl_type; enum ofperr error = 0; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { - error = ofpact_check__(a, flow, max_ports, table_id); + error = ofpact_check__(a, flow, max_ports, table_id, + enforce_consistency); if (error) { break; } diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index d6aabf9..199acdf 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -547,7 +547,7 @@ enum ofperr ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow, struct ofpbuf *ofpacts); enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len, struct flow *, ofp_port_t max_ports, - uint8_t table_id); + uint8_t table_id, bool enforce_consistency); enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len); /* Converting ofpacts to OpenFlow. */ diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 17bd7e2..f8fa9eb 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -1131,7 +1131,8 @@ parse_field(const struct mf_field *mf, const char *s, struct match *match, static char * WARN_UNUSED_RESULT parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, - enum ofputil_protocol *usable_protocols) + enum ofputil_protocol *usable_protocols, + bool enforce_consistency) { enum { F_OUT_PORT = 1 << 0, @@ -1339,10 +1340,21 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, enum ofperr err; err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow, - OFPP_MAX, 0); + OFPP_MAX, 0, true); if (err) { - error = xasprintf("actions are invalid with specified match " - "(%s)", ofperr_to_string(err)); + if (!enforce_consistency && + err == OFPERR_OFPBAC_MATCH_INCONSISTENT) { + /* Allow inconsistent actions to be used with OF 1.0. */ + *usable_protocols &= OFPUTIL_P_OF10_ANY; + /* Try again, allowing for inconsistency. + * XXX: As a side effect, logging may be duplicated. */ + err = ofpacts_check(ofpacts.data, ofpacts.size, + &fm->match.flow, OFPP_MAX, 0, false); + } + if (err) { + error = xasprintf("actions are invalid with specified match " + "(%s)", ofperr_to_string(err)); + } } } if (error) { @@ -1372,12 +1384,14 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, * error. The caller is responsible for freeing the returned string. */ char * WARN_UNUSED_RESULT parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, - enum ofputil_protocol *usable_protocols) + enum ofputil_protocol *usable_protocols, + bool enforce_consistency) { char *string = xstrdup(str_); char *error; - error = parse_ofp_str__(fm, command, string, usable_protocols); + error = parse_ofp_str__(fm, command, string, usable_protocols, + enforce_consistency); if (error) { fm->ofpacts = NULL; fm->ofpacts_len = 0; @@ -1724,9 +1738,11 @@ parse_ofpacts(const char *s_, struct ofpbuf *ofpacts, char * WARN_UNUSED_RESULT parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, uint16_t command, - enum ofputil_protocol *usable_protocols) + enum ofputil_protocol *usable_protocols, + bool enforce_consistency) { - char *error = parse_ofp_str(fm, command, string, usable_protocols); + char *error = parse_ofp_str(fm, command, string, usable_protocols, + enforce_consistency); if (!error) { /* Normalize a copy of the match. This ensures that non-normalized * flows get logged but doesn't affect what gets sent to the switch, so @@ -1788,7 +1804,8 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, char * WARN_UNUSED_RESULT parse_ofp_flow_mod_file(const char *file_name, uint16_t command, struct ofputil_flow_mod **fms, size_t *n_fms, - enum ofputil_protocol *usable_protocols) + enum ofputil_protocol *usable_protocols, + bool enforce_consistency) { size_t allocated_fms; int line_number; @@ -1817,7 +1834,7 @@ parse_ofp_flow_mod_file(const char *file_name, uint16_t command, *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); } error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, - &usable); + &usable, enforce_consistency); if (error) { size_t i; @@ -1849,12 +1866,14 @@ parse_ofp_flow_mod_file(const char *file_name, uint16_t command, char * WARN_UNUSED_RESULT parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, bool aggregate, const char *string, - enum ofputil_protocol *usable_protocols) + enum ofputil_protocol *usable_protocols, + bool enforce_consistency) { struct ofputil_flow_mod fm; char *error; - error = parse_ofp_str(&fm, -1, string, usable_protocols); + error = parse_ofp_str(&fm, -1, string, usable_protocols, + enforce_consistency); if (error) { return error; } diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h index 515ccd7..58a3e87 100644 --- a/lib/ofp-parse.h +++ b/lib/ofp-parse.h @@ -36,12 +36,14 @@ struct simap; enum ofputil_protocol; char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_, - enum ofputil_protocol *usable_protocols) + enum ofputil_protocol *usable_protocols, + bool enforce_consistency) WARN_UNUSED_RESULT; char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string, uint16_t command, - enum ofputil_protocol *usable_protocols) + enum ofputil_protocol *usable_protocols, + bool enforce_consistency) WARN_UNUSED_RESULT; char *parse_ofp_table_mod(struct ofputil_table_mod *, @@ -51,12 +53,14 @@ char *parse_ofp_table_mod(struct ofputil_table_mod *, char *parse_ofp_flow_mod_file(const char *file_name, uint16_t command, struct ofputil_flow_mod **fms, size_t *n_fms, - enum ofputil_protocol *usable_protocols) + enum ofputil_protocol *usable_protocols, + bool enforce_consistency) WARN_UNUSED_RESULT; char *parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *, bool aggregate, const char *string, - enum ofputil_protocol *usable_protocols) + enum ofputil_protocol *usable_protocols, + bool enforce_consistency) WARN_UNUSED_RESULT; char *parse_ofpacts(const char *, struct ofpbuf *ofpacts, diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 7355559..44e60d8 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -89,6 +89,7 @@ enum ofputil_protocol { OFPUTIL_P_OF10_NXM_TID = 1 << 3, #define OFPUTIL_P_OF10_STD_ANY (OFPUTIL_P_OF10_STD | OFPUTIL_P_OF10_STD_TID) #define OFPUTIL_P_OF10_NXM_ANY (OFPUTIL_P_OF10_NXM | OFPUTIL_P_OF10_NXM_TID) +#define OFPUTIL_P_OF10_ANY (OFPUTIL_P_OF10_STD_ANY | OFPUTIL_P_OF10_NXM_ANY) /* OpenFlow 1.1 protocol. * diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 6d625c2..402b38d 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -2823,13 +2823,15 @@ reject_slave_controller(struct ofconn *ofconn) static enum ofperr ofproto_check_ofpacts(struct ofproto *ofproto, const struct ofpact ofpacts[], size_t ofpacts_len, - struct flow *flow, uint8_t table_id) + struct flow *flow, uint8_t table_id, + bool enforce_consistency) { enum ofperr error; uint32_t mid; error = ofpacts_check(ofpacts, ofpacts_len, flow, - u16_to_ofp(ofproto->max_ports), table_id); + u16_to_ofp(ofproto->max_ports), table_id, + enforce_consistency); if (error) { return error; } @@ -2887,7 +2889,8 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh) /* Verify actions against packet, then send packet if successful. */ in_port_.ofp_port = po.in_port; flow_extract(payload, 0, 0, NULL, &in_port_, &flow); - error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len, &flow, 0); + error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len, &flow, 0, + oh->version > OFP10_VERSION); if (!error) { error = p->ofproto_class->packet_out(p, payload, &flow, po.ofpacts, po.ofpacts_len); @@ -3937,7 +3940,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, /* Verify actions. */ error = ofproto_check_ofpacts(ofproto, fm->ofpacts, fm->ofpacts_len, - &fm->match.flow, table_id); + &fm->match.flow, table_id, + request && request->version > OFP10_VERSION); if (error) { cls_rule_destroy(&cr); return error; @@ -4059,9 +4063,10 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, continue; } - /* Verify actions. */ + /* Verify actions, enforce consistency check on OF1.1+. */ error = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow, - u16_to_ofp(ofproto->max_ports), rule->table_id); + u16_to_ofp(ofproto->max_ports), rule->table_id, + request && request->version > OFP10_VERSION); if (error) { return error; } diff --git a/tests/learn.at b/tests/learn.at index f99cc3a..7a4bda5 100644 --- a/tests/learn.at +++ b/tests/learn.at @@ -75,12 +75,14 @@ AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:5->NXM_OF_IP_DST[])']], [1], [], [stderr]) AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0], [[destination field ip_dst lacks correct prerequisites +destination field ip_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]], [[]]) AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[])']], [1], [], [stderr]) AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0], [[source field ip_dst lacks correct prerequisites +source field ip_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]]) AT_CLEANUP diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index cbd6aec..a49213e 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -139,6 +139,12 @@ OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_d ]]) AT_CLEANUP +AT_SETUP([ovs-ofctl action inconsistency (OpenFlow 1.1)]) +AT_CHECK([ovs-ofctl --protocols OpenFlow11 add-flow br0 'ip actions=mod_tp_dst:1234' +], [1], [stdout], [ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) +]) +AT_CLEANUP + AT_SETUP([ovs-ofctl parse-flows (OpenFlow 1.2)]) AT_DATA([flows.txt], [[ # comment @@ -229,7 +235,7 @@ actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1), actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3) send_flow_rem,actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3 check_overlap,actions=output:1,exit,output:2 -actions=fin_timeout(idle_timeout=5,hard_timeout=15) +tcp,actions=fin_timeout(idle_timeout=5,hard_timeout=15) actions=controller(max_len=123,reason=invalid_ttl,id=555) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) ]]) @@ -265,7 +271,7 @@ NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle_load(eth_src,0,hrw,ofport,NX NXT_FLOW_MOD: ADD table:255 actions=resubmit:1,resubmit:2,resubmit(,3),resubmit(2,3) NXT_FLOW_MOD: ADD table:255 send_flow_rem actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3 NXT_FLOW_MOD: ADD table:255 check_overlap actions=output:1,exit,output:2 -NXT_FLOW_MOD: ADD table:255 actions=fin_timeout(idle_timeout=5,hard_timeout=15) +NXT_FLOW_MOD: ADD table:255 tcp actions=fin_timeout(idle_timeout=5,hard_timeout=15) NXT_FLOW_MOD: ADD table:255 actions=controller(reason=invalid_ttl,max_len=123,id=555) NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) ]]) diff --git a/utilities/ovs-controller.c b/utilities/ovs-controller.c index f487d8c..9596ad4 100644 --- a/utilities/ovs-controller.c +++ b/utilities/ovs-controller.c @@ -332,7 +332,7 @@ parse_options(int argc, char *argv[]) case OPT_WITH_FLOWS: error = parse_ofp_flow_mod_file(optarg, OFPFC_ADD, &default_flows, &n_default_flows, - &usable_protocols); + &usable_protocols, false); if (error) { ovs_fatal(0, "%s", error); } diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 630591c..1a1d423 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -889,7 +889,9 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate, error = parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "", - &usable_protocols); + &usable_protocols, + !(allowed_protocols + & OFPUTIL_P_OF10_ANY)); if (error) { ovs_fatal(0, "%s", error); } @@ -1111,7 +1113,8 @@ ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command) char *error; error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms, - &usable_protocols); + &usable_protocols, + !(allowed_protocols & OFPUTIL_P_OF10_ANY)); if (error) { ovs_fatal(0, "%s", error); } @@ -1130,7 +1133,9 @@ ofctl_flow_mod(int argc, char *argv[], uint16_t command) enum ofputil_protocol usable_protocols; error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, - &usable_protocols); + &usable_protocols, + !(allowed_protocols + & OFPUTIL_P_OF10_ANY)); if (error) { ovs_fatal(0, "%s", error); } @@ -2205,7 +2210,8 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index) char *error; enum ofputil_protocol usable; - error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), &usable); + error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), &usable, + !(allowed_protocols & OFPUTIL_P_OF10_ANY)); if (error) { ovs_fatal(0, "%s:%d: %s", filename, line_number, error); } @@ -2624,7 +2630,8 @@ ofctl_parse_flow(int argc OVS_UNUSED, char *argv[]) struct ofputil_flow_mod fm; char *error; - error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, &usable_protocols); + error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, &usable_protocols, + !(allowed_protocols & OFPUTIL_P_OF10_ANY)); if (error) { ovs_fatal(0, "%s", error); } @@ -2642,7 +2649,8 @@ ofctl_parse_flows(int argc OVS_UNUSED, char *argv[]) char *error; error = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms, - &usable_protocols); + &usable_protocols, + !(allowed_protocols & OFPUTIL_P_OF10_ANY)); if (error) { ovs_fatal(0, "%s", error); } @@ -3042,11 +3050,11 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) error = ofpacts_pull_openflow11_instructions(&of11_in, OFP11_VERSION, of11_in.size, &ofpacts); if (!error) { - /* Verify actions. */ + /* Verify actions, enforce consistency. */ struct flow flow; memset(&flow, 0, sizeof flow); error = ofpacts_check(ofpacts.data, ofpacts.size, &flow, OFPP_MAX, - table_id ? atoi(table_id) : 0); + table_id ? atoi(table_id) : 0, true); } if (error) { printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error)); @@ -3144,7 +3152,8 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[]) string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY); printf("%s -> ", string_s); fflush(stdout); - error_s = parse_ofp_str(&fm, -1, string_s, &usable_protocols); + error_s = parse_ofp_str(&fm, -1, string_s, &usable_protocols, + !(allowed_protocols & OFPUTIL_P_OF10_ANY)); if (error_s) { ovs_fatal(0, "%s", error_s); } -- 1.7.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev