2
0
mirror of https://github.com/openvswitch/ovs synced 2025-10-23 14:57:06 +00:00

ovs-ofctl: Add NXM support.

This commit is contained in:
Ben Pfaff
2010-12-07 13:32:01 -08:00
parent 7fa9111326
commit 88ca35eed0
8 changed files with 458 additions and 147 deletions

View File

@@ -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 /* 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 * man page) into 'pf'. If 'actions' is specified, an action must be in
* 'string' and may be expanded or reallocated. */ * 'string' and may be expanded or reallocated. */
void static void
parse_ofp_str(struct parsed_flow *pf, struct ofpbuf *actions, char *string) parse_ofp_str(struct flow_mod *fm, uint8_t *table_idx,
struct ofpbuf *actions, char *string)
{ {
char *save_ptr = NULL; char *save_ptr = NULL;
char *name; char *name;
cls_rule_init_catchall(&pf->rule, OFP_DEFAULT_PRIORITY); if (table_idx) {
pf->table_idx = 0xff; *table_idx = 0xff;
pf->out_port = OFPP_NONE; }
pf->idle_timeout = OFP_FLOW_PERMANENT; cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
pf->hard_timeout = OFP_FLOW_PERMANENT; fm->cookie = htonll(0);
pf->cookie = 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) { if (actions) {
char *act_str = strstr(string, "action"); char *act_str = strstr(string, "action");
if (!act_str) { if (!act_str) {
@@ -564,15 +570,20 @@ parse_ofp_str(struct parsed_flow *pf, struct ofpbuf *actions, char *string)
act_str++; act_str++;
str_to_action(act_str, actions); 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; for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
const struct protocol *p; const struct protocol *p;
if (parse_protocol(name, &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) { 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 { } else {
const struct field *f; 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); ovs_fatal(0, "field %s missing value", name);
} }
if (!strcmp(name, "table")) { if (table_idx && !strcmp(name, "table")) {
pf->table_idx = atoi(value); *table_idx = atoi(value);
} else if (!strcmp(name, "out_port")) { } else if (!strcmp(name, "out_port")) {
pf->out_port = atoi(value); fm->out_port = atoi(value);
} else if (!strcmp(name, "priority")) { } else if (!strcmp(name, "priority")) {
pf->rule.priority = atoi(value); fm->cr.priority = atoi(value);
} else if (!strcmp(name, "idle_timeout")) { } else if (!strcmp(name, "idle_timeout")) {
pf->idle_timeout = atoi(value); fm->idle_timeout = atoi(value);
} else if (!strcmp(name, "hard_timeout")) { } else if (!strcmp(name, "hard_timeout")) {
pf->hard_timeout = atoi(value); fm->hard_timeout = atoi(value);
} else if (!strcmp(name, "cookie")) { } 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)) { } else if (parse_field_name(name, &f)) {
if (!strcmp(value, "*") || !strcmp(value, "ANY")) { if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
if (f->wildcard) { if (f->wildcard) {
pf->rule.wc.wildcards |= f->wildcard; fm->cr.wc.wildcards |= f->wildcard;
cls_rule_zero_wildcarded_fields(&pf->rule); cls_rule_zero_wildcarded_fields(&fm->cr);
} else if (f->index == F_NW_SRC) { } 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) { } 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) { } 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) { } else if (f->index == F_DL_VLAN_PCP) {
cls_rule_set_any_pcp(&pf->rule); cls_rule_set_any_pcp(&fm->cr);
} else { } else {
NOT_REACHED(); NOT_REACHED();
} }
} else { } 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])) { } else if (!strncmp(name, "reg", 3) && isdigit(name[3])) {
unsigned int reg_idx = atoi(name + 3); unsigned int reg_idx = atoi(name + 3);
if (reg_idx >= FLOW_N_REGS) { if (reg_idx >= FLOW_N_REGS) {
ovs_fatal(0, "only %d registers supported", 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 { } else {
ovs_fatal(0, "unknown keyword %s", name); 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_*) /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
* and returns an ofpbuf that contains it. */ * (one of OFPFC_*) and appends the parsed OpenFlow message to 'packets'.
struct ofpbuf * * '*cur_format' should initially contain the flow format currently configured
parse_ofp_flow_mod_str(char *string, uint16_t command) * 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; enum nx_flow_format min_format, next_format;
struct ofpbuf *buffer; struct ofpbuf actions;
struct ofp_flow_mod *ofm; struct ofpbuf *ofm;
struct flow_mod fm;
/* parse_ofp_str() will expand and reallocate the data in 'buffer', so we ofpbuf_init(&actions, 64);
* can't keep pointers to across the parse_ofp_str() call. */ parse_ofp_str(&fm, NULL, &actions, string);
make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer); fm.command = command;
parse_ofp_str(&pf, buffer, string);
ofm = buffer->data; min_format = ofputil_min_flow_format(&fm.cr, true, fm.cookie);
ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &ofm->match); next_format = MAX(*cur_format, min_format);
ofm->command = htons(command); if (next_format != *cur_format) {
ofm->cookie = htonll(pf.cookie); struct ofpbuf *sff = ofputil_make_set_flow_format(next_format);
ofm->idle_timeout = htons(pf.idle_timeout); list_push_back(packets, &sff->list_node);
ofm->hard_timeout = htons(pf.hard_timeout); *cur_format = next_format;
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; 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 /* Similar to parse_ofp_flow_mod_str(), except that the string is read from
* ofpbuf that contains it. Returns a null pointer if end-of-file is reached * 'stream' and the command is always OFPFC_ADD. Returns false if end-of-file
* before reading a flow. */ * is reached before reading a flow, otherwise true. */
struct ofpbuf * bool
parse_ofp_add_flow_file(FILE *stream) 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; struct ds s = DS_EMPTY_INITIALIZER;
bool ok = false;
while (!ds_get_line(&s, stream)) { while (!ds_get_line(&s, stream)) {
char *line = ds_cstr(&s); char *line = ds_cstr(&s);
@@ -679,10 +696,26 @@ parse_ofp_add_flow_file(FILE *stream)
continue; continue;
} }
b = parse_ofp_flow_mod_str(line, OFPFC_ADD); parse_ofp_flow_mod_str(packets, cur, line, OFPFC_ADD);
ok = true;
break; break;
} }
ds_destroy(&s); 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;
}

View File

@@ -19,22 +19,22 @@
#ifndef OFP_PARSE_H #ifndef OFP_PARSE_H
#define OFP_PARSE_H 1 #define OFP_PARSE_H 1
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include "openflow/nicira-ext.h"
#include "classifier.h" struct flow_mod;
struct flow_stats_request;
struct list;
struct ofpbuf;
struct parsed_flow { void parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur,
struct cls_rule rule; char *string, uint16_t command);
uint8_t table_idx; bool parse_ofp_add_flow_file(struct list *packets, enum nx_flow_format *cur,
uint16_t out_port; FILE *);
uint16_t idle_timeout;
uint16_t hard_timeout;
uint64_t cookie;
};
void parse_ofp_str(struct parsed_flow *, struct ofpbuf *actions, char *string); void parse_ofp_flow_stats_request_str(struct flow_stats_request *,
struct ofpbuf *parse_ofp_flow_mod_str(char *string, uint16_t command); bool aggregate, char *string);
struct ofpbuf *parse_ofp_add_flow_file(FILE *);
#endif /* ofp-parse.h */ #endif /* ofp-parse.h */

View File

@@ -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 /* 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 * flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error
* code. * code.
@@ -901,7 +986,14 @@ ofputil_encode_flow_mod(const struct flow_mod *fm,
msg = ofpbuf_new(sizeof *ofm + actions_len); msg = ofpbuf_new(sizeof *ofm + actions_len);
ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg); ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg);
ofputil_cls_rule_to_match(&fm->cr, flow_format, &ofm->match); 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->command = htons(fm->command);
ofm->idle_timeout = htons(fm->idle_timeout); ofm->idle_timeout = htons(fm->idle_timeout);
ofm->hard_timeout = htons(fm->hard_timeout); ofm->hard_timeout = htons(fm->hard_timeout);

View File

@@ -115,6 +115,12 @@ char *ofp_match_to_literal_string(const struct ofp_match *match);
/* Flow formats. */ /* Flow formats. */
bool ofputil_flow_format_is_valid(enum nx_flow_format); bool ofputil_flow_format_is_valid(enum nx_flow_format);
const char *ofputil_flow_format_to_string(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. */ /* Flow format independent flow_mod. */
struct flow_mod { struct flow_mod {

View File

@@ -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 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller
actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
tun_id=0x1234,cookie=0x5678,actions=flood
actions=drop 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 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: ADD tcp,tp_src=123 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: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
OFPT_FLOW_MOD: arp,nw_src=192.168.0.1, ADD: actions=drop_spoofed_arp,NORMAL OFPT_FLOW_MOD: ADD arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL
OFPT_FLOW_MOD: udp,dl_vlan_pcp=7, ADD: idle:5 actions=strip_vlan,output:0 OFPT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 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: ADD tcp,nw_src=192.168.0.3,tp_dst=80 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 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
OFPT_FLOW_MOD: ADD: cookie:0x123456789abcdef hard:10 pri:60000 actions=CONTROLLER:65535 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 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 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 AT_CLEANUP

View File

@@ -260,7 +260,7 @@ do_switching(struct switch_ *sw)
static void static void
read_flow_file(const char *name) read_flow_file(const char *name)
{ {
struct ofpbuf *b; enum nx_flow_format flow_format;
FILE *stream; FILE *stream;
stream = fopen(optarg, "r"); stream = fopen(optarg, "r");
@@ -268,8 +268,9 @@ read_flow_file(const char *name)
ovs_fatal(errno, "%s: open", name); ovs_fatal(errno, "%s: open", name);
} }
while ((b = parse_ofp_add_flow_file(stream)) != NULL) { flow_format = NXFF_OPENFLOW10;
list_push_back(&default_flows, &b->list_node); while (parse_ofp_add_flow_file(&default_flows, &flow_format, stream)) {
continue;
} }
fclose(stream); fclose(stream);

View File

@@ -354,9 +354,10 @@ unavailable for other use, and specifying \fBtun_id\fR on
.RE .RE
.IP .IP
When \fBtun_id\fR is specified, \fBovs\-ofctl\fR will automatically When \fBtun_id\fR is specified, \fBovs\-ofctl\fR will automatically
attempt to negotiate use of one of these extensions, preferring NXM. attempt to negotiate use of one of these extensions. It will use the
If the switch does not support either extension, then \fBovs\-ofctl\fR ``tunnel ID from cookie'' extension if neither caveat applies and NXM
will report a fatal error. 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]" .IP "\fBreg\fIidx\fB=\fIvalue\fR[\fB/\fImask\fR]"
Matches \fIvalue\fR either exactly or with optional \fImask\fR in Matches \fIvalue\fR either exactly or with optional \fImask\fR in
register number \fIidx\fR. The valid range of \fIidx\fR depends on 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 \fB\-\-strict\fR
Uses strict matching when running flow modification commands. 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" .SS "Public Key Infrastructure Options"
.so lib/ssl.man .so lib/ssl.man
.so lib/vlog.man .so lib/vlog.man

View File

@@ -51,9 +51,13 @@
VLOG_DEFINE_THIS_MODULE(ofctl); VLOG_DEFINE_THIS_MODULE(ofctl);
/* Use strict matching for flow mod commands? */ /* --strict: Use strict matching for flow mod commands? */
static bool strict; 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 const struct command all_commands[];
static void usage(void) NO_RETURN; static void usage(void) NO_RETURN;
@@ -79,6 +83,7 @@ parse_options(int argc, char *argv[])
static struct option long_options[] = { static struct option long_options[] = {
{"timeout", required_argument, 0, 't'}, {"timeout", required_argument, 0, 't'},
{"strict", no_argument, 0, OPT_STRICT}, {"strict", no_argument, 0, OPT_STRICT},
{"flow-format", required_argument, 0, 'F'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'}, {"version", no_argument, 0, 'V'},
VLOG_LONG_OPTIONS, VLOG_LONG_OPTIONS,
@@ -107,6 +112,13 @@ parse_options(int argc, char *argv[])
} }
break; 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': case 'h':
usage(); usage();
@@ -163,6 +175,7 @@ usage(void)
vlog_usage(); vlog_usage();
printf("\nOther options:\n" printf("\nOther options:\n"
" --strict use strict match for flow commands\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" " -t, --timeout=SECS give up after SECS seconds\n"
" -h, --help display this help message\n" " -h, --help display this help message\n"
" -V, --version display version information\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 * occurs, and waits for it to succeed or fail. If an error does occur, prints
* it and exits with an error. */ * it and exits with an error. */
static void 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); LIST_FOR_EACH (request, list_node, requests) {
run(vconn_transact_noreply(vconn, request, &reply), update_openflow_length(request);
}
run(vconn_transact_multiple_noreply(vconn, requests, &reply),
"talking to %s", vconn_get_name(vconn)); "talking to %s", vconn_get_name(vconn));
if (reply) { if (reply) {
ofp_print(stderr, reply->data, reply->size, 2); ofp_print(stderr, reply->data, reply->size, 2);
@@ -355,6 +371,19 @@ dump_noreply_transaction(struct vconn *vconn, struct ofpbuf *request)
ofpbuf_delete(reply); 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 static void
do_show(int argc OVS_UNUSED, char *argv[]) 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
set_flow_format(struct vconn *vconn, enum nx_flow_format flow_format)
{
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));
}
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;
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_flows__(int argc, char *argv[], bool aggregate)
{
enum nx_flow_format flow_format;
struct flow_stats_request fsr;
struct ofpbuf *request;
struct vconn *vconn;
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 static void
do_dump_flows(int argc, char *argv[]) do_dump_flows(int argc, char *argv[])
{ {
struct ofp_flow_stats_request *req; return do_dump_flows__(argc, argv, false);
struct parsed_flow pf;
struct ofpbuf *request;
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);
dump_stats_transaction(argv[1], request);
} }
static void static void
do_dump_aggregate(int argc, char *argv[]) do_dump_aggregate(int argc, char *argv[])
{ {
struct ofp_aggregate_stats_request *req; return do_dump_flows__(argc, argv, true);
struct ofpbuf *request;
struct parsed_flow pf;
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);
dump_stats_transaction(argv[1], request);
} }
static void static void
@@ -526,23 +614,33 @@ do_queue_stats(int argc, char *argv[])
} }
static void 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 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); open_vconn(argv[1], &vconn);
dump_noreply_transaction(vconn, request); transact_multiple_noreply(vconn, &requests);
vconn_close(vconn); vconn_close(vconn);
} }
static void
do_add_flow(int argc, char *argv[])
{
do_flow_mod__(argc, argv, OFPFC_ADD);
}
static void static void
do_add_flows(int argc OVS_UNUSED, char *argv[]) do_add_flows(int argc OVS_UNUSED, char *argv[])
{ {
enum nx_flow_format flow_format;
struct list requests;
struct vconn *vconn; struct vconn *vconn;
struct ofpbuf *b;
FILE *file; FILE *file;
file = fopen(argv[2], "r"); 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]); ovs_fatal(errno, "%s: open", argv[2]);
} }
list_init(&requests);
flow_format = NXFF_OPENFLOW10;
open_vconn(argv[1], &vconn); open_vconn(argv[1], &vconn);
while ((b = parse_ofp_add_flow_file(file)) != NULL) { while (parse_ofp_add_flow_file(&requests, &flow_format, file)) {
dump_noreply_transaction(vconn, b); transact_multiple_noreply(vconn, &requests);
} }
vconn_close(vconn); vconn_close(vconn);
fclose(file); fclose(file);
} }
static void static void
do_mod_flows(int argc OVS_UNUSED, char *argv[]) do_mod_flows(int argc, char *argv[])
{ {
struct vconn *vconn; do_flow_mod__(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
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);
} }
static void do_del_flows(int argc, char *argv[]) static void
do_del_flows(int argc, char *argv[])
{ {
struct vconn *vconn; do_flow_mod__(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
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);
} }
static void static void
@@ -610,7 +696,7 @@ do_monitor(int argc, char *argv[])
osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf); osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf);
osc->miss_send_len = htons(miss_send_len); osc->miss_send_len = htons(miss_send_len);
dump_noreply_transaction(vconn, buf); transact_noreply(vconn, buf);
} }
monitor_vconn(vconn); monitor_vconn(vconn);
} }
@@ -686,7 +772,7 @@ do_mod_port(int argc OVS_UNUSED, char *argv[])
} }
open_vconn(argv[1], &vconn); open_vconn(argv[1], &vconn);
dump_noreply_transaction(vconn, request); transact_noreply(vconn, request);
vconn_close(vconn); vconn_close(vconn);
} }
@@ -791,7 +877,8 @@ do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
static void static void
do_parse_flows(int argc OVS_UNUSED, char *argv[]) do_parse_flows(int argc OVS_UNUSED, char *argv[])
{ {
struct ofpbuf *b; enum nx_flow_format flow_format;
struct list packets;
FILE *file; FILE *file;
file = fopen(argv[1], "r"); 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]); ovs_fatal(errno, "%s: open", argv[2]);
} }
while ((b = parse_ofp_add_flow_file(file)) != NULL) { list_init(&packets);
ofp_print(stdout, b->data, b->size, 0); flow_format = NXFF_OPENFLOW10;
ofpbuf_delete(b); 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); fclose(file);
} }