From 88ca35eed0ce0bc65e45122c24e33f50e8339bb8 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 7 Dec 2010 13:32:01 -0800 Subject: [PATCH] ovs-ofctl: Add NXM support. --- lib/ofp-parse.c | 149 +++++++++++++++---------- lib/ofp-parse.h | 24 ++--- lib/ofp-util.c | 94 +++++++++++++++- lib/ofp-util.h | 6 ++ tests/ovs-ofctl.at | 69 ++++++++++-- utilities/ovs-controller.c | 7 +- utilities/ovs-ofctl.8.in | 38 ++++++- utilities/ovs-ofctl.c | 216 +++++++++++++++++++++++++++---------- 8 files changed, 457 insertions(+), 146 deletions(-) diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index e800edbc..72c115ba 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -537,18 +537,24 @@ parse_reg_value(struct cls_rule *rule, int reg_idx, const char *value) /* Convert 'string' (as described in the Flow Syntax section of the ovs-ofctl * man page) into 'pf'. If 'actions' is specified, an action must be in * 'string' and may be expanded or reallocated. */ -void -parse_ofp_str(struct parsed_flow *pf, struct ofpbuf *actions, char *string) +static void +parse_ofp_str(struct flow_mod *fm, uint8_t *table_idx, + struct ofpbuf *actions, char *string) { char *save_ptr = NULL; char *name; - cls_rule_init_catchall(&pf->rule, OFP_DEFAULT_PRIORITY); - pf->table_idx = 0xff; - pf->out_port = OFPP_NONE; - pf->idle_timeout = OFP_FLOW_PERMANENT; - pf->hard_timeout = OFP_FLOW_PERMANENT; - pf->cookie = 0; + if (table_idx) { + *table_idx = 0xff; + } + cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY); + fm->cookie = htonll(0); + fm->command = UINT16_MAX; + fm->idle_timeout = OFP_FLOW_PERMANENT; + fm->hard_timeout = OFP_FLOW_PERMANENT; + fm->buffer_id = UINT32_MAX; + fm->out_port = OFPP_NONE; + fm->flags = 0; if (actions) { char *act_str = strstr(string, "action"); if (!act_str) { @@ -564,15 +570,20 @@ parse_ofp_str(struct parsed_flow *pf, struct ofpbuf *actions, char *string) act_str++; str_to_action(act_str, actions); + fm->actions = actions->data; + fm->n_actions = actions->size / sizeof(union ofp_action); + } else { + fm->actions = NULL; + fm->n_actions = 0; } for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { const struct protocol *p; if (parse_protocol(name, &p)) { - cls_rule_set_dl_type(&pf->rule, htons(p->dl_type)); + cls_rule_set_dl_type(&fm->cr, htons(p->dl_type)); if (p->nw_proto) { - cls_rule_set_nw_proto(&pf->rule, p->nw_proto); + cls_rule_set_nw_proto(&fm->cr, p->nw_proto); } } else { const struct field *f; @@ -583,43 +594,43 @@ parse_ofp_str(struct parsed_flow *pf, struct ofpbuf *actions, char *string) ovs_fatal(0, "field %s missing value", name); } - if (!strcmp(name, "table")) { - pf->table_idx = atoi(value); + if (table_idx && !strcmp(name, "table")) { + *table_idx = atoi(value); } else if (!strcmp(name, "out_port")) { - pf->out_port = atoi(value); + fm->out_port = atoi(value); } else if (!strcmp(name, "priority")) { - pf->rule.priority = atoi(value); + fm->cr.priority = atoi(value); } else if (!strcmp(name, "idle_timeout")) { - pf->idle_timeout = atoi(value); + fm->idle_timeout = atoi(value); } else if (!strcmp(name, "hard_timeout")) { - pf->hard_timeout = atoi(value); + fm->hard_timeout = atoi(value); } else if (!strcmp(name, "cookie")) { - pf->cookie = str_to_u64(value); + fm->cookie = htonll(str_to_u64(value)); } else if (parse_field_name(name, &f)) { if (!strcmp(value, "*") || !strcmp(value, "ANY")) { if (f->wildcard) { - pf->rule.wc.wildcards |= f->wildcard; - cls_rule_zero_wildcarded_fields(&pf->rule); + fm->cr.wc.wildcards |= f->wildcard; + cls_rule_zero_wildcarded_fields(&fm->cr); } else if (f->index == F_NW_SRC) { - cls_rule_set_nw_src_masked(&pf->rule, 0, 0); + cls_rule_set_nw_src_masked(&fm->cr, 0, 0); } else if (f->index == F_NW_DST) { - cls_rule_set_nw_dst_masked(&pf->rule, 0, 0); + cls_rule_set_nw_dst_masked(&fm->cr, 0, 0); } else if (f->index == F_DL_VLAN) { - cls_rule_set_any_vid(&pf->rule); + cls_rule_set_any_vid(&fm->cr); } else if (f->index == F_DL_VLAN_PCP) { - cls_rule_set_any_pcp(&pf->rule); + cls_rule_set_any_pcp(&fm->cr); } else { NOT_REACHED(); } } else { - parse_field_value(&pf->rule, f->index, value); + parse_field_value(&fm->cr, f->index, value); } } else if (!strncmp(name, "reg", 3) && isdigit(name[3])) { unsigned int reg_idx = atoi(name + 3); if (reg_idx >= FLOW_N_REGS) { ovs_fatal(0, "only %d registers supported", FLOW_N_REGS); } - parse_reg_value(&pf->rule, reg_idx, value); + parse_reg_value(&fm->cr, reg_idx, value); } else { ovs_fatal(0, "unknown keyword %s", name); } @@ -627,42 +638,48 @@ parse_ofp_str(struct parsed_flow *pf, struct ofpbuf *actions, char *string) } } -/* Parses 'string' as an OFPT_FLOW_MOD with command 'command' (one of OFPFC_*) - * and returns an ofpbuf that contains it. */ -struct ofpbuf * -parse_ofp_flow_mod_str(char *string, uint16_t command) +/* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' + * (one of OFPFC_*) and appends the parsed OpenFlow message to 'packets'. + * '*cur_format' should initially contain the flow format currently configured + * on the connection; this function will add a message to change the flow + * format and update '*cur_format', if this is necessary to add the parsed + * flow. */ +void +parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format, + char *string, uint16_t command) { - struct parsed_flow pf; - struct ofpbuf *buffer; - struct ofp_flow_mod *ofm; - - /* parse_ofp_str() will expand and reallocate the data in 'buffer', so we - * can't keep pointers to across the parse_ofp_str() call. */ - make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer); - parse_ofp_str(&pf, buffer, string); - - ofm = buffer->data; - ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &ofm->match); - ofm->command = htons(command); - ofm->cookie = htonll(pf.cookie); - ofm->idle_timeout = htons(pf.idle_timeout); - ofm->hard_timeout = htons(pf.hard_timeout); - ofm->buffer_id = htonl(UINT32_MAX); - ofm->out_port = htons(pf.out_port); - ofm->priority = htons(pf.rule.priority); - update_openflow_length(buffer); - - return buffer; + enum nx_flow_format min_format, next_format; + struct ofpbuf actions; + struct ofpbuf *ofm; + struct flow_mod fm; + + ofpbuf_init(&actions, 64); + parse_ofp_str(&fm, NULL, &actions, string); + fm.command = command; + + min_format = ofputil_min_flow_format(&fm.cr, true, fm.cookie); + next_format = MAX(*cur_format, min_format); + if (next_format != *cur_format) { + struct ofpbuf *sff = ofputil_make_set_flow_format(next_format); + list_push_back(packets, &sff->list_node); + *cur_format = next_format; + } + + ofm = ofputil_encode_flow_mod(&fm, *cur_format); + list_push_back(packets, &ofm->list_node); + + ofpbuf_uninit(&actions); } -/* Parses an OFPT_FLOW_MOD with subtype OFPFC_ADD from 'stream' and returns an - * ofpbuf that contains it. Returns a null pointer if end-of-file is reached - * before reading a flow. */ -struct ofpbuf * -parse_ofp_add_flow_file(FILE *stream) +/* Similar to parse_ofp_flow_mod_str(), except that the string is read from + * 'stream' and the command is always OFPFC_ADD. Returns false if end-of-file + * is reached before reading a flow, otherwise true. */ +bool +parse_ofp_add_flow_file(struct list *packets, enum nx_flow_format *cur, + FILE *stream) { - struct ofpbuf *b = NULL; struct ds s = DS_EMPTY_INITIALIZER; + bool ok = false; while (!ds_get_line(&s, stream)) { char *line = ds_cstr(&s); @@ -679,10 +696,26 @@ parse_ofp_add_flow_file(FILE *stream) continue; } - b = parse_ofp_flow_mod_str(line, OFPFC_ADD); + parse_ofp_flow_mod_str(packets, cur, line, OFPFC_ADD); + ok = true; break; } ds_destroy(&s); - return b; + return ok; +} + +void +parse_ofp_flow_stats_request_str(struct flow_stats_request *fsr, + bool aggregate, char *string) +{ + struct flow_mod fm; + uint8_t table_id; + + parse_ofp_str(&fm, &table_id, NULL, string); + fsr->aggregate = aggregate; + fsr->match = fm.cr; + fsr->out_port = fm.out_port; + fsr->table_id = table_id; } + diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h index a68796e5..503148ae 100644 --- a/lib/ofp-parse.h +++ b/lib/ofp-parse.h @@ -19,22 +19,22 @@ #ifndef OFP_PARSE_H #define OFP_PARSE_H 1 +#include #include #include +#include "openflow/nicira-ext.h" -#include "classifier.h" +struct flow_mod; +struct flow_stats_request; +struct list; +struct ofpbuf; -struct parsed_flow { - struct cls_rule rule; - uint8_t table_idx; - uint16_t out_port; - uint16_t idle_timeout; - uint16_t hard_timeout; - uint64_t cookie; -}; +void parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur, + char *string, uint16_t command); +bool parse_ofp_add_flow_file(struct list *packets, enum nx_flow_format *cur, + FILE *); -void parse_ofp_str(struct parsed_flow *, struct ofpbuf *actions, char *string); -struct ofpbuf *parse_ofp_flow_mod_str(char *string, uint16_t command); -struct ofpbuf *parse_ofp_add_flow_file(FILE *); +void parse_ofp_flow_stats_request_str(struct flow_stats_request *, + bool aggregate, char *string); #endif /* ofp-parse.h */ diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 331a8fd5..cf832b7d 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -786,6 +786,91 @@ ofputil_flow_format_to_string(enum nx_flow_format flow_format) } } +int +ofputil_flow_format_from_string(const char *s) +{ + return (!strcmp(s, "openflow10") ? NXFF_OPENFLOW10 + : !strcmp(s, "tun_id_from_cookie") ? NXFF_TUN_ID_FROM_COOKIE + : !strcmp(s, "nxm") ? NXFF_NXM + : -1); +} + +static bool +regs_fully_wildcarded(const struct flow_wildcards *wc) +{ + int i; + + for (i = 0; i < FLOW_N_REGS; i++) { + if (wc->reg_masks[i] != 0) { + return false; + } + } + return true; +} + +/* Returns the minimum nx_flow_format to use for sending 'rule' to a switch + * (e.g. to add or remove a flow). 'cookie_support' should be true if the + * command to be sent includes a flow cookie (as OFPT_FLOW_MOD does, for + * example) or false if the command does not (OFPST_FLOW and OFPST_AGGREGATE do + * not, for example). If 'cookie_support' is true, then 'cookie' should be the + * cookie to be sent; otherwise its value is ignored. + * + * The "best" flow format is chosen on this basis: + * + * - It must be capable of expressing the rule. NXFF_OPENFLOW10 flows can't + * handle tunnel IDs. NXFF_TUN_ID_FROM_COOKIE flows can't handle registers + * or fixing the Ethernet multicast bit, and can't handle tunnel IDs that + * conflict with the high 32 bits of the cookie or commands that don't + * support cookies. + * + * - Otherwise, the chosen format should be as backward compatible as + * possible. (NXFF_OPENFLOW10 is more backward compatible than + * NXFF_TUN_ID_FROM_COOKIE, which is more backward compatible than + * NXFF_NXM.) + */ +enum nx_flow_format +ofputil_min_flow_format(const struct cls_rule *rule, bool cookie_support, + ovs_be64 cookie) +{ + const struct flow_wildcards *wc = &rule->wc; + ovs_be32 cookie_hi = htonl(ntohll(cookie) >> 32); + + if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST) + || !regs_fully_wildcarded(wc) + || (!(wc->wildcards & FWW_TUN_ID) + && (!cookie_support + || (cookie_hi && cookie_hi != rule->flow.tun_id)))) { + return NXFF_NXM; + } else if (!(wc->wildcards & FWW_TUN_ID)) { + return NXFF_TUN_ID_FROM_COOKIE; + } else { + return NXFF_OPENFLOW10; + } +} + +/* Returns an OpenFlow message that can be used to set the flow format to + * 'flow_format'. */ +struct ofpbuf * +ofputil_make_set_flow_format(enum nx_flow_format flow_format) +{ + struct ofpbuf *msg; + + if (flow_format == NXFF_OPENFLOW10 + || flow_format == NXFF_TUN_ID_FROM_COOKIE) { + struct nxt_tun_id_cookie *tic; + + tic = make_nxmsg(sizeof *tic, NXT_TUN_ID_FROM_COOKIE, &msg); + tic->set = flow_format == NXFF_TUN_ID_FROM_COOKIE; + } else { + struct nxt_set_flow_format *sff; + + sff = make_nxmsg(sizeof *sff, NXT_SET_FLOW_FORMAT, &msg); + sff->format = htonl(flow_format); + } + + return msg; +} + /* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh' into an abstract * flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error * code. @@ -901,7 +986,14 @@ ofputil_encode_flow_mod(const struct flow_mod *fm, msg = ofpbuf_new(sizeof *ofm + actions_len); ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg); ofputil_cls_rule_to_match(&fm->cr, flow_format, &ofm->match); - ofm->cookie = fm->cookie; + if (flow_format != NXFF_TUN_ID_FROM_COOKIE + || fm->cr.wc.wildcards & FWW_TUN_ID) { + ofm->cookie = fm->cookie; + } else { + uint32_t cookie_lo = ntohll(fm->cookie); + uint32_t cookie_hi = ntohl(fm->cr.flow.tun_id); + ofm->cookie = htonll(cookie_lo | ((uint64_t) cookie_hi << 32)); + } ofm->command = htons(fm->command); ofm->idle_timeout = htons(fm->idle_timeout); ofm->hard_timeout = htons(fm->hard_timeout); diff --git a/lib/ofp-util.h b/lib/ofp-util.h index f91e7622..abd811fd 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -115,6 +115,12 @@ char *ofp_match_to_literal_string(const struct ofp_match *match); /* Flow formats. */ bool ofputil_flow_format_is_valid(enum nx_flow_format); const char *ofputil_flow_format_to_string(enum nx_flow_format); +int ofputil_flow_format_from_string(const char *); +enum nx_flow_format ofputil_min_flow_format(const struct cls_rule *, + bool cookie_support, + ovs_be64 cookie); + +struct ofpbuf *ofputil_make_set_flow_format(enum nx_flow_format); /* Flow format independent flow_mod. */ struct flow_mod { diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 2fb0e013..3f39538a 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -11,19 +11,68 @@ tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note +tun_id=0x1234,cookie=0x5678,actions=flood actions=drop ]) -AT_CHECK([ovs-ofctl parse-flows flows.txt], [0], [stdout]) +AT_CHECK([ovs-ofctl parse-flows flows.txt], [0], [stdout], [stderr]) AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [dnl -OFPT_FLOW_MOD: tcp,tp_src=123, ADD: actions=FLOOD -OFPT_FLOW_MOD: in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0, ADD: actions=drop -OFPT_FLOW_MOD: arp,nw_src=192.168.0.1, ADD: actions=drop_spoofed_arp,NORMAL -OFPT_FLOW_MOD: udp,dl_vlan_pcp=7, ADD: idle:5 actions=strip_vlan,output:0 -OFPT_FLOW_MOD: tcp,nw_src=192.168.0.3,tp_dst=80, ADD: actions=set_queue:37,output:1 -OFPT_FLOW_MOD: udp,nw_src=192.168.0.3,tp_dst=53, ADD: actions=pop_queue,output:1 -OFPT_FLOW_MOD: ADD: cookie:0x123456789abcdef hard:10 pri:60000 actions=CONTROLLER:65535 -OFPT_FLOW_MOD: ADD: actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 -OFPT_FLOW_MOD: ADD: actions=drop +OFPT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD +OFPT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop +OFPT_FLOW_MOD: ADD arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL +OFPT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0 +OFPT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 +OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 +OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 +OFPT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 +NXT_TUN_ID_FROM_COOKIE: set=1 +OFPT_FLOW_MOD: ADD cookie:0x123400005678 actions=FLOOD +OFPT_FLOW_MOD: ADD actions=drop +]) +AT_CHECK([sed 's/.*|//' stderr], [0], [dnl +normalization changed ofp_match, details: + pre: wildcards= 0x3820f8 in_port=65534 dl_src=00:0a:e4:25:6b:b0 dl_dst=00:00:00:00:00:00 dl_vlan= 9 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +post: wildcards= 0x3ffff8 in_port=65534 dl_src=00:0a:e4:25:6b:b0 dl_dst=00:00:00:00:00:00 dl_vlan= 9 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +normalization changed ofp_match, details: + pre: wildcards= 0x3820ff in_port=65534 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +post: wildcards= 0x3fffff in_port=65534 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +normalization changed ofp_match, details: + pre: wildcards= 0x3820ff in_port=65534 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +post: wildcards= 0x3fffff in_port=65534 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +normalization changed ofp_match, details: + pre: wildcards= 0x3820ff in_port=65534 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +post: wildcards= 0x3fffff in_port=65534 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +normalization changed ofp_match, details: + pre: wildcards= 0x23820ff in_port=65534 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +post: wildcards= 0x23fffff in_port=65534 dl_src=00:00:00:00:00:00 dl_dst=00:00:00:00:00:00 dl_vlan= 0 dl_vlan_pcp= 0 dl_type= 0 nw_tos= 0 nw_proto= 0 nw_src= 0 nw_dst= 0 tp_src= 0 tp_dst= 0 +]) +AT_CLEANUP + +AT_SETUP([ovs-ofctl -F nxm parse-flows]) +AT_DATA([flows.txt], [ +# comment +tcp,tp_src=123,actions=flood +in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop +arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL +udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0 +tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 +udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 +cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller +actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note +tun_id=0x1234,cookie=0x5678,actions=flood +actions=drop +]) +AT_CHECK([ovs-ofctl -F nxm parse-flows flows.txt], [0], [stdout]) +AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [dnl +NXT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD +NXT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop +NXT_FLOW_MOD: ADD arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL +NXT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0 +NXT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 +NXT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 +NXT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 +NXT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 +NXT_FLOW_MOD: ADD tun_id=0x1234 cookie:0x5678 actions=FLOOD +NXT_FLOW_MOD: ADD actions=drop ]) AT_CLEANUP diff --git a/utilities/ovs-controller.c b/utilities/ovs-controller.c index 5675f897..c38c8123 100644 --- a/utilities/ovs-controller.c +++ b/utilities/ovs-controller.c @@ -260,7 +260,7 @@ do_switching(struct switch_ *sw) static void read_flow_file(const char *name) { - struct ofpbuf *b; + enum nx_flow_format flow_format; FILE *stream; stream = fopen(optarg, "r"); @@ -268,8 +268,9 @@ read_flow_file(const char *name) ovs_fatal(errno, "%s: open", name); } - while ((b = parse_ofp_add_flow_file(stream)) != NULL) { - list_push_back(&default_flows, &b->list_node); + flow_format = NXFF_OPENFLOW10; + while (parse_ofp_add_flow_file(&default_flows, &flow_format, stream)) { + continue; } fclose(stream); diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index eb20fa76..ebdfc667 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -354,9 +354,10 @@ unavailable for other use, and specifying \fBtun_id\fR on .RE .IP When \fBtun_id\fR is specified, \fBovs\-ofctl\fR will automatically -attempt to negotiate use of one of these extensions, preferring NXM. -If the switch does not support either extension, then \fBovs\-ofctl\fR -will report a fatal error. +attempt to negotiate use of one of these extensions. It will use the +``tunnel ID from cookie'' extension if neither caveat applies and NXM +otherwise. If the switch does not support the needed extension, then +\fBovs\-ofctl\fR will report a fatal error. .IP "\fBreg\fIidx\fB=\fIvalue\fR[\fB/\fImask\fR]" Matches \fIvalue\fR either exactly or with optional \fImask\fR in register number \fIidx\fR. The valid range of \fIidx\fR depends on @@ -620,6 +621,37 @@ described in \fBFlow Syntax\fR, above. \fB\-\-strict\fR Uses strict matching when running flow modification commands. . +.IP "\fB\-F \fIformat\fR" +.IQ "\fB\-\-flow\-format=\fIformat\fR" +\fBovs\-ofctl\fR supports the following flow formats, in order of +increasing capability: +.RS +.IP "\fBopenflow10\fR" +This is the standard OpenFlow 1.0 flow format. It should be supported +by all OpenFlow switches. +. +.IP "\fBtun_id_from_cookie\fR" +This Nicira extension to OpenFlow adds minimal and limited support for +\fBtun_id\fR, but it does not support any other Nicira flow +extensions. (This flow format is deprecated.) +. +.IP "\fBnxm\fR (Nicira Extended Match)" +This Nicira extension to OpenFlow is flexible and extensible. It +supports all of the Nicira flow extensions, such as \fBtun_id\fR and +registers. +.RE +.IP +Usually, \fBovs\-ofctl\fR picks the correct format automatically. For +commands that modify the flow table, \fBovs\-ofctl\fR by default uses +the most widely supported flow format that supports the flows being +added. For commands that query the flow table, \fBovs\-ofctl\fR by +default queries and uses the most advanced format supported by the +switch. +.IP +This option, where \fIformat\fR is one of the formats listed in the +above table, overrides \fBovs\-ofctl\fR's default choice of flow +format. If a command cannot work as requested using the requested +flow format, \fBovs\-ofctl\fR will report a fatal error. .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/vlog.man diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 3beef3b6..1865c003 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -51,9 +51,13 @@ VLOG_DEFINE_THIS_MODULE(ofctl); -/* Use strict matching for flow mod commands? */ +/* --strict: Use strict matching for flow mod commands? */ static bool strict; +/* -F, --flow-format: Flow format to use. Either one of NXFF_* to force a + * particular flow format or -1 to let ovs-ofctl choose intelligently. */ +static int preferred_flow_format = -1; + static const struct command all_commands[]; static void usage(void) NO_RETURN; @@ -79,6 +83,7 @@ parse_options(int argc, char *argv[]) static struct option long_options[] = { {"timeout", required_argument, 0, 't'}, {"strict", no_argument, 0, OPT_STRICT}, + {"flow-format", required_argument, 0, 'F'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, VLOG_LONG_OPTIONS, @@ -107,6 +112,13 @@ parse_options(int argc, char *argv[]) } break; + case 'F': + preferred_flow_format = ofputil_flow_format_from_string(optarg); + if (preferred_flow_format < 0) { + ovs_fatal(0, "unknown flow format `%s'", optarg); + } + break; + case 'h': usage(); @@ -163,6 +175,7 @@ usage(void) vlog_usage(); printf("\nOther options:\n" " --strict use strict match for flow commands\n" + " -F, --flow-format=FORMAT force particular flow format\n" " -t, --timeout=SECS give up after SECS seconds\n" " -h, --help display this help message\n" " -V, --version display version information\n"); @@ -341,12 +354,15 @@ dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type) * occurs, and waits for it to succeed or fail. If an error does occur, prints * it and exits with an error. */ static void -dump_noreply_transaction(struct vconn *vconn, struct ofpbuf *request) +transact_multiple_noreply(struct vconn *vconn, struct list *requests) { - struct ofpbuf *reply; + struct ofpbuf *request, *reply; - update_openflow_length(request); - run(vconn_transact_noreply(vconn, request, &reply), + LIST_FOR_EACH (request, list_node, requests) { + update_openflow_length(request); + } + + run(vconn_transact_multiple_noreply(vconn, requests, &reply), "talking to %s", vconn_get_name(vconn)); if (reply) { ofp_print(stderr, reply->data, reply->size, 2); @@ -355,6 +371,19 @@ dump_noreply_transaction(struct vconn *vconn, struct ofpbuf *request) ofpbuf_delete(reply); } +/* Sends 'request', which should be a request that only has a reply if an error + * occurs, and waits for it to succeed or fail. If an error does occur, prints + * it and exits with an error. */ +static void +transact_noreply(struct vconn *vconn, struct ofpbuf *request) +{ + struct list requests; + + list_init(&requests); + list_push_back(&requests, &request->list_node); + transact_multiple_noreply(vconn, &requests); +} + static void do_show(int argc OVS_UNUSED, char *argv[]) { @@ -467,38 +496,97 @@ str_to_port_no(const char *vconn_name, const char *port_name) } } +static bool +try_set_flow_format(struct vconn *vconn, enum nx_flow_format flow_format) +{ + struct ofpbuf *sff, *reply; + + sff = ofputil_make_set_flow_format(flow_format); + run(vconn_transact_noreply(vconn, sff, &reply), + "talking to %s", vconn_get_name(vconn)); + if (reply) { + char *s = ofp_to_string(reply->data, reply->size, 2); + VLOG_DBG("%s: failed to set flow format %s, controller replied: %s", + vconn_get_name(vconn), + ofputil_flow_format_to_string(flow_format), + s); + free(s); + ofpbuf_delete(reply); + return false; + } + return true; +} + static void -do_dump_flows(int argc, char *argv[]) +set_flow_format(struct vconn *vconn, enum nx_flow_format flow_format) { - struct ofp_flow_stats_request *req; - struct parsed_flow pf; - struct ofpbuf *request; + struct ofpbuf *sff = ofputil_make_set_flow_format(flow_format); + transact_noreply(vconn, sff); + VLOG_DBG("%s: using user-specified flow format %s", + vconn_get_name(vconn), + ofputil_flow_format_to_string(flow_format)); +} - req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request); - parse_ofp_str(&pf, NULL, argc > 2 ? argv[2] : ""); - ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &req->match); - memset(&req->pad, 0, sizeof req->pad); - req->table_id = pf.table_idx; - req->out_port = htons(pf.out_port); +static enum nx_flow_format +negotiate_highest_flow_format(struct vconn *vconn, const struct cls_rule *rule, + bool cookie_support, ovs_be64 cookie) +{ + int flow_format; - dump_stats_transaction(argv[1], request); + if (preferred_flow_format != -1) { + enum nx_flow_format min_format; + + min_format = ofputil_min_flow_format(rule, cookie_support, cookie); + if (preferred_flow_format >= min_format) { + set_flow_format(vconn, preferred_flow_format); + return preferred_flow_format; + } + + VLOG_WARN("%s: cannot use requested flow format %s for " + "specified flow", vconn_get_name(vconn), + ofputil_flow_format_to_string(min_format)); + } + + if (try_set_flow_format(vconn, NXFF_NXM)) { + flow_format = NXFF_NXM; + } else if (try_set_flow_format(vconn, NXFF_TUN_ID_FROM_COOKIE)) { + flow_format = NXFF_TUN_ID_FROM_COOKIE; + } else { + flow_format = NXFF_OPENFLOW10; + } + + VLOG_DBG("%s: negotiated flow format %s", vconn_get_name(vconn), + ofputil_flow_format_to_string(flow_format)); + return flow_format; } static void -do_dump_aggregate(int argc, char *argv[]) +do_dump_flows__(int argc, char *argv[], bool aggregate) { - struct ofp_aggregate_stats_request *req; + enum nx_flow_format flow_format; + struct flow_stats_request fsr; struct ofpbuf *request; - struct parsed_flow pf; + struct vconn *vconn; - req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request); - parse_ofp_str(&pf, NULL, argc > 2 ? argv[2] : ""); - ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &req->match); - memset(&req->pad, 0, sizeof req->pad); - req->table_id = pf.table_idx; - req->out_port = htons(pf.out_port); + parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : ""); + open_vconn(argv[1], &vconn); + flow_format = negotiate_highest_flow_format(vconn, &fsr.match, false, 0); + request = ofputil_encode_flow_stats_request(&fsr, flow_format); dump_stats_transaction(argv[1], request); + vconn_close(vconn); +} + +static void +do_dump_flows(int argc, char *argv[]) +{ + return do_dump_flows__(argc, argv, false); +} + +static void +do_dump_aggregate(int argc, char *argv[]) +{ + return do_dump_flows__(argc, argv, true); } static void @@ -526,23 +614,33 @@ do_queue_stats(int argc, char *argv[]) } static void -do_add_flow(int argc OVS_UNUSED, char *argv[]) +do_flow_mod__(int argc OVS_UNUSED, char *argv[], uint16_t command) { + enum nx_flow_format flow_format; + struct list requests; struct vconn *vconn; - struct ofpbuf *request; - request = parse_ofp_flow_mod_str(argv[2], OFPFC_ADD); + list_init(&requests); + flow_format = NXFF_OPENFLOW10; + parse_ofp_flow_mod_str(&requests, &flow_format, argv[2], command); open_vconn(argv[1], &vconn); - dump_noreply_transaction(vconn, request); + transact_multiple_noreply(vconn, &requests); vconn_close(vconn); } +static void +do_add_flow(int argc, char *argv[]) +{ + do_flow_mod__(argc, argv, OFPFC_ADD); +} + static void do_add_flows(int argc OVS_UNUSED, char *argv[]) { + enum nx_flow_format flow_format; + struct list requests; struct vconn *vconn; - struct ofpbuf *b; FILE *file; file = fopen(argv[2], "r"); @@ -550,40 +648,28 @@ do_add_flows(int argc OVS_UNUSED, char *argv[]) ovs_fatal(errno, "%s: open", argv[2]); } + list_init(&requests); + flow_format = NXFF_OPENFLOW10; + open_vconn(argv[1], &vconn); - while ((b = parse_ofp_add_flow_file(file)) != NULL) { - dump_noreply_transaction(vconn, b); + while (parse_ofp_add_flow_file(&requests, &flow_format, file)) { + transact_multiple_noreply(vconn, &requests); } vconn_close(vconn); + fclose(file); } static void -do_mod_flows(int argc OVS_UNUSED, char *argv[]) +do_mod_flows(int argc, char *argv[]) { - struct vconn *vconn; - struct ofpbuf *buffer; - uint16_t command; - - command = strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY; - buffer = parse_ofp_flow_mod_str(argv[2], command); - open_vconn(argv[1], &vconn); - dump_noreply_transaction(vconn, buffer); - vconn_close(vconn); + do_flow_mod__(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY); } -static void do_del_flows(int argc, char *argv[]) +static void +do_del_flows(int argc, char *argv[]) { - struct vconn *vconn; - struct ofpbuf *buffer; - uint16_t command; - - command = strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE; - buffer = parse_ofp_flow_mod_str(argc > 2 ? argv[2] : "", command); - - open_vconn(argv[1], &vconn); - dump_noreply_transaction(vconn, buffer); - vconn_close(vconn); + do_flow_mod__(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE); } static void @@ -610,7 +696,7 @@ do_monitor(int argc, char *argv[]) osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf); osc->miss_send_len = htons(miss_send_len); - dump_noreply_transaction(vconn, buf); + transact_noreply(vconn, buf); } monitor_vconn(vconn); } @@ -686,7 +772,7 @@ do_mod_port(int argc OVS_UNUSED, char *argv[]) } open_vconn(argv[1], &vconn); - dump_noreply_transaction(vconn, request); + transact_noreply(vconn, request); vconn_close(vconn); } @@ -791,7 +877,8 @@ do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) static void do_parse_flows(int argc OVS_UNUSED, char *argv[]) { - struct ofpbuf *b; + enum nx_flow_format flow_format; + struct list packets; FILE *file; file = fopen(argv[1], "r"); @@ -799,9 +886,20 @@ do_parse_flows(int argc OVS_UNUSED, char *argv[]) ovs_fatal(errno, "%s: open", argv[2]); } - while ((b = parse_ofp_add_flow_file(file)) != NULL) { - ofp_print(stdout, b->data, b->size, 0); - ofpbuf_delete(b); + list_init(&packets); + flow_format = NXFF_OPENFLOW10; + if (preferred_flow_format > 0) { + flow_format = preferred_flow_format; + } + + while (parse_ofp_add_flow_file(&packets, &flow_format, file)) { + struct ofpbuf *packet, *next; + + LIST_FOR_EACH_SAFE (packet, next, list_node, &packets) { + ofp_print(stdout, packet->data, packet->size, 0); + list_remove(&packet->list_node); + ofpbuf_delete(packet); + } } fclose(file); } -- 2.30.2