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:
143
lib/ofp-parse.c
143
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
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user