2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

Implement OpenFlow 1.1+ "groups" protocol.

This doesn't include a dpif implementation of groups functionality.  In its
current form, it is untested.

Signed-off-by: Neil Zhu <zhuj@centecnetworks.com>
Co-authored-by: Ben Pfaff <blp@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
Co-authored-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Simon Horman <horms@verge.net.au>
Co-authored-by: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
This commit is contained in:
Neil Zhu 2013-09-01 18:30:17 -07:00 committed by Ben Pfaff
parent 6f8dbd272a
commit 7395c05254
23 changed files with 2216 additions and 45 deletions

View File

@ -66,6 +66,7 @@ Mehak Mahajan mmahajan@nicira.com
Murphy McCauley murphy.mccauley@gmail.com
Natasha Gude natasha@nicira.com
Neil McKee neil.mckee@inmon.com
Neil Zhu zhuj@centecnetworks.com
Paraneetharan Chandrasekaran paraneetharanc@gmail.com
Paul Fazzone pfazzone@nicira.com
Paul Ingram paul@nicira.com

7
NEWS
View File

@ -7,6 +7,12 @@ v2.0.0 - xx xxx xxxx
- OpenFlow:
* Experimental support for OpenFlow 1.1 (in addition to 1.2 and
1.3, which had experimental support in 1.10).
* Experimental protocol support for OpenFlow 1.1+ groups. This
does not yet include an implementation in the Open vSwitch
software switch.
* Experimental protocol support for OpenFlow 1.2+ meters. This
does not yet include an implementation in the Open vSwitch
software switch.
* New support for matching outer source and destination IP address
of tunneled packets, for tunnel ports configured with the newly
added "remote_ip=flow" and "local_ip=flow" options.
@ -26,6 +32,7 @@ v2.0.0 - xx xxx xxxx
- Support for Linux kernels up to 3.10
- ovs-ofctl:
* New "ofp-parse" for printing OpenFlow messages read from a file.
* New commands for OpenFlow 1.1+ groups.
- Added configurable flow caching support to IPFIX exporter.

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2008, 2011, 2012 The Board of Trustees of The Leland Stanford
/* Copyright (c) 2008, 2011, 2012, 2013 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
@ -150,8 +150,8 @@ OFP_ASSERT(sizeof(struct ofp11_port_mod) == 32);
/* Group setup and teardown (controller -> datapath). */
struct ofp11_group_mod {
ovs_be16 command; /* One of OFPGC_*. */
uint8_t type; /* One of OFPGT_*. */
ovs_be16 command; /* One of OFPGC11_*. */
uint8_t type; /* One of OFPGT11_*. */
uint8_t pad; /* Pad to 64 bits. */
ovs_be32 group_id; /* Group identifier. */
/* struct ofp11_bucket buckets[0]; The bucket length is inferred from the
@ -725,7 +725,7 @@ OFP_ASSERT(sizeof(struct ofp11_bucket_counter) == 16);
/* Body of reply to OFPST11_GROUP_DESC request. */
struct ofp11_group_desc_stats {
ovs_be16 length; /* Length of this entry. */
uint8_t type; /* One of OFPGT_*. */
uint8_t type; /* One of OFPGT11_*. */
uint8_t pad; /* Pad to 64 bits. */
ovs_be32 group_id; /* Group identifier. */
/* struct ofp11_bucket buckets[0]; */

View File

@ -301,7 +301,7 @@ OFP_ASSERT(sizeof(struct ofp12_table_stats) == 128);
/* Body of reply to OFPST12_GROUP_FEATURES request. Group features. */
struct ofp12_group_features_stats {
ovs_be32 types; /* Bitmap of OFPGT_* values supported. */
ovs_be32 types; /* Bitmap of OFPGT11_* values supported. */
ovs_be32 capabilities; /* Bitmap of OFPGFC12_* capability supported. */
ovs_be32 max_groups[4]; /* Maximum number of groups for each type. */
ovs_be32 actions[4]; /* Bitmaps of OFPAT_* that are supported. */

View File

@ -306,7 +306,7 @@ OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8);
* header and any padding used to make the action 64-bit aligned.
* NB: The length of an action *must* always be a multiple of eight. */
struct ofp_action_header {
ovs_be16 type; /* One of OFPAT10_*. */
ovs_be16 type; /* One of OFPAT*. */
ovs_be16 len; /* Length of action, including this
header. This is the length of action,
including any padding to make it

View File

@ -346,6 +346,7 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
case OFPTYPE_PORT_STATUS:
case OFPTYPE_PACKET_OUT:
case OFPTYPE_FLOW_MOD:
case OFPTYPE_GROUP_MOD:
case OFPTYPE_PORT_MOD:
case OFPTYPE_BARRIER_REQUEST:
case OFPTYPE_BARRIER_REPLY:

View File

@ -860,6 +860,12 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
break;
}
case OFPUTIL_OFPAT11_GROUP: {
struct ofp11_action_group *oag = (struct ofp11_action_group *)a;
ofpact_put_GROUP(out)->group_id = ntohl(oag->group_id);
break;
}
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
#include "ofp-util.def"
return ofpact_from_nxast(a, code, out);
@ -945,6 +951,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
case OFPACT_GOTO_TABLE:
return OVSINST_OFPIT11_GOTO_TABLE;
case OFPACT_OUTPUT:
case OFPACT_GROUP:
case OFPACT_CONTROLLER:
case OFPACT_ENQUEUE:
case OFPACT_OUTPUT_REG:
@ -1302,6 +1309,9 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
}
return 0;
case OFPACT_GROUP:
return 0;
default:
NOT_REACHED();
}
@ -1598,6 +1608,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
ofpact_sample_to_nxast(ofpact_get_SAMPLE(a), out);
break;
case OFPACT_GROUP:
case OFPACT_OUTPUT:
case OFPACT_ENQUEUE:
case OFPACT_SET_VLAN_VID:
@ -1710,6 +1721,9 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
/* XXX */
break;
case OFPACT_GROUP:
break;
case OFPACT_CONTROLLER:
case OFPACT_OUTPUT_REG:
case OFPACT_BUNDLE:
@ -1882,6 +1896,11 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
case OFPACT_METER:
NOT_REACHED();
case OFPACT_GROUP:
ofputil_put_OFPAT11_GROUP(out)->group_id =
htonl(ofpact_get_GROUP(a)->group_id);
break;
case OFPACT_CONTROLLER:
case OFPACT_OUTPUT_REG:
case OFPACT_BUNDLE:
@ -2051,6 +2070,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
case OFPACT_METER:
case OFPACT_GROUP:
default:
return false;
}
@ -2073,6 +2093,24 @@ ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len,
return false;
}
/* Returns true if any action in the 'ofpacts_len' bytes of 'ofpacts' outputs
* to 'group', false otherwise. */
bool
ofpacts_output_to_group(const struct ofpact *ofpacts, size_t ofpacts_len,
uint32_t group_id)
{
const struct ofpact *a;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
if (a->type == OFPACT_GROUP
&& ofpact_get_GROUP(a)->group_id == group_id) {
return true;
}
}
return false;
}
bool
ofpacts_equal(const struct ofpact *a, size_t a_len,
const struct ofpact *b, size_t b_len)
@ -2383,6 +2421,11 @@ ofpact_format(const struct ofpact *a, struct ds *s)
ovs_instruction_name_from_type(OVSINST_OFPIT13_METER),
ofpact_get_METER(a)->meter_id);
break;
case OFPACT_GROUP:
ds_put_format(s, "group:%"PRIu32,
ofpact_get_GROUP(a)->group_id);
break;
}
}

View File

@ -51,6 +51,7 @@
#define OFPACTS \
/* Output. */ \
DEFINE_OFPACT(OUTPUT, ofpact_output, ofpact) \
DEFINE_OFPACT(GROUP, ofpact_group, ofpact) \
DEFINE_OFPACT(CONTROLLER, ofpact_controller, ofpact) \
DEFINE_OFPACT(ENQUEUE, ofpact_enqueue, ofpact) \
DEFINE_OFPACT(OUTPUT_REG, ofpact_output_reg, ofpact) \
@ -490,6 +491,14 @@ struct ofpact_goto_table {
uint8_t table_id;
};
/* OFPACT_GROUP.
*
* Used for OFPAT11_GROUP. */
struct ofpact_group {
struct ofpact ofpact;
uint32_t group_id;
};
/* Converting OpenFlow to ofpacts. */
enum ofperr ofpacts_pull_openflow10(struct ofpbuf *openflow,
unsigned int actions_len,
@ -517,6 +526,8 @@ void ofpacts_put_openflow11_instructions(const struct ofpact[],
/* Working with ofpacts. */
bool ofpacts_output_to_port(const struct ofpact[], size_t ofpacts_len,
ofp_port_t port);
bool ofpacts_output_to_group(const struct ofpact[], size_t ofpacts_len,
uint32_t group_id);
bool ofpacts_equal(const struct ofpact a[], size_t a_len,
const struct ofpact b[], size_t b_len);

View File

@ -177,6 +177,9 @@ enum ofpraw {
/* NXT 1.0+ (13): struct nx_flow_mod, uint8_t[8][]. */
OFPRAW_NXT_FLOW_MOD,
/* OFPT 1.1+ (15): struct ofp11_group_mod, uint8_t[8][]. */
OFPRAW_OFPT11_GROUP_MOD,
/* OFPT 1.0 (15): struct ofp10_port_mod. */
OFPRAW_OFPT10_PORT_MOD,
/* OFPT 1.1+ (16): struct ofp11_port_mod. */
@ -295,15 +298,15 @@ enum ofpraw {
/* OFPST 1.1+ (6): struct ofp11_group_stats_request. */
OFPRAW_OFPST11_GROUP_REQUEST,
/* OFPST 1.1-1.2 (6): struct ofp11_group_stats[]. */
/* OFPST 1.1-1.2 (6): uint8_t[8][]. */
OFPRAW_OFPST11_GROUP_REPLY,
/* OFPST 1.3 (6): struct ofp13_group_stats[]. */
/* OFPST 1.3 (6): uint8_t[8][]. */
OFPRAW_OFPST13_GROUP_REPLY,
/* OFPST 1.1+ (7): void. */
OFPRAW_OFPST11_GROUP_DESC_REQUEST,
/* OFPST 1.1+ (7): struct ofp11_group_desc_stats[]. */
/* OFPST 1.1+ (7): uint8_t[8][]. */
OFPRAW_OFPST11_GROUP_DESC_REPLY,
/* OFPST 1.2+ (8): void. */
@ -460,6 +463,7 @@ enum ofptype {
OFPTYPE_FLOW_MOD, /* OFPRAW_OFPT10_FLOW_MOD.
* OFPRAW_OFPT11_FLOW_MOD.
* OFPRAW_NXT_FLOW_MOD. */
OFPTYPE_GROUP_MOD, /* OFPRAW_OFPT11_GROUP_MOD. */
OFPTYPE_PORT_MOD, /* OFPRAW_OFPT10_PORT_MOD.
* OFPRAW_OFPT11_PORT_MOD. */

View File

@ -819,6 +819,10 @@ parse_named_action(enum ofputil_action_code code,
}
break;
case OFPUTIL_OFPAT11_GROUP:
error = str_to_u32(arg, &ofpact_put_GROUP(ofpacts)->group_id);
break;
case OFPUTIL_NXAST_STACK_PUSH:
error = nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
break;
@ -1157,6 +1161,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
fm->buffer_id = UINT32_MAX;
fm->out_port = OFPP_ANY;
fm->flags = 0;
fm->out_group = OFPG11_ANY;
if (fields & F_ACTIONS) {
act_str = strstr(string, "action");
if (!act_str) {
@ -1794,6 +1799,7 @@ parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
fsr->cookie_mask = fm.cookie_mask;
fsr->match = fm.match;
fsr->out_port = fm.out_port;
fsr->out_group = fm.out_group;
fsr->table_id = fm.table_id;
return NULL;
}
@ -1878,3 +1884,287 @@ exit:
}
return error;
}
static char * WARN_UNUSED_RESULT
parse_bucket_str(struct ofputil_bucket *bucket, char *str_,
enum ofputil_protocol *usable_protocols)
{
struct ofpbuf ofpacts;
char *pos, *act, *arg;
int n_actions;
bucket->weight = 1;
bucket->watch_port = OFPP_ANY;
bucket->watch_group = OFPG11_ANY;
pos = str_;
n_actions = 0;
ofpbuf_init(&ofpacts, 64);
while (ofputil_parse_key_value(&pos, &act, &arg)) {
char *error = NULL;
if (!strcasecmp(act, "weight")) {
error = str_to_u16(arg, "weight", &bucket->weight);
} else if (!strcasecmp(act, "watch_port")) {
if (!ofputil_port_from_string(arg, &bucket->watch_port)
|| (ofp_to_u16(bucket->watch_port) >= ofp_to_u16(OFPP_MAX)
&& bucket->watch_port != OFPP_ANY)) {
error = xasprintf("%s: invalid watch_port", arg);
}
} else if (!strcasecmp(act, "watch_group")) {
error = str_to_u32(arg, &bucket->watch_group);
if (!error && bucket->watch_group > OFPG_MAX) {
error = xasprintf("invalid watch_group id %"PRIu32,
bucket->watch_group);
}
} else {
error = str_to_ofpact__(pos, act, arg, &ofpacts, n_actions,
usable_protocols);
n_actions++;
}
if (error) {
ofpbuf_uninit(&ofpacts);
return error;
}
}
ofpact_pad(&ofpacts);
bucket->ofpacts = ofpacts.data;
bucket->ofpacts_len = ofpacts.size;
return NULL;
}
static char * WARN_UNUSED_RESULT
parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command,
char *string,
enum ofputil_protocol *usable_protocols)
{
enum {
F_GROUP_TYPE = 1 << 0,
F_BUCKETS = 1 << 1,
} fields;
char *save_ptr = NULL;
bool had_type = false;
char *name;
struct ofputil_bucket *bucket;
char *error = NULL;
*usable_protocols = OFPUTIL_P_OF11_UP;
switch (command) {
case OFPGC11_ADD:
fields = F_GROUP_TYPE | F_BUCKETS;
break;
case OFPGC11_DELETE:
fields = 0;
break;
case OFPGC11_MODIFY:
fields = F_GROUP_TYPE | F_BUCKETS;
break;
default:
NOT_REACHED();
}
memset(gm, 0, sizeof *gm);
gm->command = command;
gm->group_id = OFPG_ANY;
list_init(&gm->buckets);
if (command == OFPGC11_DELETE && string[0] == '\0') {
gm->group_id = OFPG_ALL;
return NULL;
}
*usable_protocols = OFPUTIL_P_OF11_UP;
if (fields & F_BUCKETS) {
char *bkt_str = strstr(string, "bucket");
if (bkt_str) {
*bkt_str = '\0';
}
while (bkt_str) {
char *next_bkt_str;
bkt_str = strchr(bkt_str + 1, '=');
if (!bkt_str) {
error = xstrdup("must specify bucket content");
goto out;
}
bkt_str++;
next_bkt_str = strstr(bkt_str, "bucket");
if (next_bkt_str) {
*next_bkt_str = '\0';
}
bucket = xzalloc(sizeof(struct ofputil_bucket));
error = parse_bucket_str(bucket, bkt_str, usable_protocols);
if (error) {
free(bucket);
goto out;
}
list_push_back(&gm->buckets, &bucket->list_node);
bkt_str = next_bkt_str;
}
}
for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
char *value;
value = strtok_r(NULL, ", \t\r\n", &save_ptr);
if (!value) {
error = xasprintf("field %s missing value", name);
goto out;
}
if (!strcmp(name, "group_id")) {
if(!strcmp(value, "all")) {
gm->group_id = OFPG_ALL;
} else {
char *error = str_to_u32(value, &gm->group_id);
if (error) {
goto out;
}
if (gm->group_id != OFPG_ALL && gm->group_id > OFPG_MAX) {
error = xasprintf("invalid group id %"PRIu32,
gm->group_id);
goto out;
}
}
} else if (!strcmp(name, "type")){
if (!(fields & F_GROUP_TYPE)) {
error = xstrdup("type is not needed");
goto out;
}
if (!strcmp(value, "all")) {
gm->type = OFPGT11_ALL;
} else if (!strcmp(value, "select")) {
gm->type = OFPGT11_SELECT;
} else if (!strcmp(value, "indirect")) {
gm->type = OFPGT11_INDIRECT;
} else if (!strcmp(value, "ff") ||
!strcmp(value, "fast_failover")) {
gm->type = OFPGT11_FF;
} else {
error = xasprintf("invalid group type %s", value);
goto out;
}
had_type = true;
} else if (!strcmp(name, "bucket")) {
error = xstrdup("bucket is not needed");
goto out;
} else {
error = xasprintf("unknown keyword %s", name);
goto out;
}
}
if (gm->group_id == OFPG_ANY) {
error = xstrdup("must specify a group_id");
goto out;
}
if (fields & F_GROUP_TYPE && !had_type) {
error = xstrdup("must specify a type");
goto out;
}
/* Validate buckets. */
LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
if (bucket->weight != 1 && gm->type != OFPGT11_SELECT) {
error = xstrdup("Only select groups can have bucket weights.");
goto out;
}
}
if (gm->type == OFPGT11_INDIRECT && !list_is_short(&gm->buckets)) {
error = xstrdup("Indirect groups can have at most one bucket.");
goto out;
}
return NULL;
out:
ofputil_bucket_list_destroy(&gm->buckets);
return error;
}
char * WARN_UNUSED_RESULT
parse_ofp_group_mod_str(struct ofputil_group_mod *gm, uint16_t command,
const char *str_,
enum ofputil_protocol *usable_protocols)
{
char *string = xstrdup(str_);
char *error = parse_ofp_group_mod_str__(gm, command, string,
usable_protocols);
free(string);
if (error) {
ofputil_bucket_list_destroy(&gm->buckets);
}
return error;
}
char * WARN_UNUSED_RESULT
parse_ofp_group_mod_file(const char *file_name, uint16_t command,
struct ofputil_group_mod **gms, size_t *n_gms,
enum ofputil_protocol *usable_protocols)
{
size_t allocated_gms;
int line_number;
FILE *stream;
struct ds s;
*gms = NULL;
*n_gms = 0;
stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
if (stream == NULL) {
return xasprintf("%s: open failed (%s)",
file_name, ovs_strerror(errno));
}
allocated_gms = *n_gms;
ds_init(&s);
line_number = 0;
*usable_protocols = OFPUTIL_P_OF11_UP;
while (!ds_get_preprocessed_line(&s, stream, &line_number)) {
enum ofputil_protocol usable;
char *error;
if (*n_gms >= allocated_gms) {
*gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms);
}
error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s),
&usable);
if (error) {
size_t i;
for (i = 0; i < *n_gms; i++) {
ofputil_bucket_list_destroy(&(*gms)[i].buckets);
}
free(*gms);
*gms = NULL;
*n_gms = 0;
ds_destroy(&s);
if (stream != stdin) {
fclose(stream);
}
return xasprintf("%s:%d: %s", file_name, line_number, error);
}
*usable_protocols &= usable;
*n_gms += 1;
}
ds_destroy(&s);
if (stream != stdin) {
fclose(stream);
}
return NULL;
}

View File

@ -29,6 +29,7 @@ struct ofpbuf;
struct ofputil_flow_mod;
struct ofputil_flow_monitor_request;
struct ofputil_flow_stats_request;
struct ofputil_group_mod;
struct ofputil_meter_mod;
enum ofputil_protocol;
@ -66,4 +67,14 @@ char *parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
enum ofputil_protocol *usable_protocols)
WARN_UNUSED_RESULT;
char *parse_ofp_group_mod_file(const char *file_name, uint16_t command,
struct ofputil_group_mod **gms, size_t *n_gms,
enum ofputil_protocol *usable_protocols)
WARN_UNUSED_RESULT;
char *parse_ofp_group_mod_str(struct ofputil_group_mod *, uint16_t command,
const char *string,
enum ofputil_protocol *usable_protocols)
WARN_UNUSED_RESULT;
#endif /* ofp-parse.h */

View File

@ -2140,6 +2140,198 @@ ofp_print_not_implemented(struct ds *string)
ds_put_cstr(string, "NOT IMPLEMENTED YET!\n");
}
static void
ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
struct list *p_buckets)
{
static const char *type_str[] = { "all", "select", "indirect",
"ff", "unknown" };
struct ofputil_bucket *bucket;
ds_put_format(s, "group_id=%"PRIu32",type=%s",
group_id, type_str[type > 4 ? 4 : type]);
if (!p_buckets) {
return;
}
LIST_FOR_EACH (bucket, list_node, p_buckets) {
ds_put_cstr(s, ",bucket=");
if (bucket->weight != 1) {
ds_put_format(s, "weight:%"PRIu16",", bucket->weight);
}
if (bucket->watch_port != OFPP_NONE) {
ds_put_format(s, "watch_port:%"PRIu32",", bucket->watch_port);
}
if (bucket->watch_group != OFPG11_ANY) {
ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group);
}
ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, s);
}
}
static void
ofp_print_group_desc(struct ds *s, const struct ofp_header *oh)
{
struct ofpbuf b;
ofpbuf_use_const(&b, oh, ntohs(oh->length));
for (;;) {
struct ofputil_group_desc gd;
int retval;
retval = ofputil_decode_group_desc_reply(&gd, &b);
if (retval) {
if (retval != EOF) {
ds_put_cstr(s, " ***parse error***");
}
break;
}
ds_put_char(s, '\n');
ds_put_char(s, ' ');
ofp_print_group(s, gd.group_id, gd.type, &gd.buckets);
}
}
static void
ofp_print_ofpst_group_request(struct ds *string, const struct ofp_header *oh)
{
enum ofperr error;
uint32_t group_id;
error = ofputil_decode_group_stats_request(oh, &group_id);
if (error) {
ofp_print_error(string, error);
return;
}
ds_put_cstr(string, " group_id=");
ofputil_format_group(group_id, string);
}
static void
ofp_print_group_stats(struct ds *s, const struct ofp_header *oh)
{
struct ofpbuf b;
uint32_t bucket_i;
ofpbuf_use_const(&b, oh, ntohs(oh->length));
for (;;) {
struct ofputil_group_stats gs;
int retval;
retval = ofputil_decode_group_stats_reply(&b, &gs);
if (retval) {
if (retval != EOF) {
ds_put_cstr(s, " ***parse error***");
}
break;
}
ds_put_char(s, '\n');
ds_put_char(s, ' ');
ds_put_format(s, "group_id=%"PRIu32",", gs.group_id);
if (gs.duration_sec != UINT32_MAX) {
ds_put_cstr(s, "duration=");
ofp_print_duration(s, gs.duration_sec, gs.duration_nsec);
ds_put_char(s, ',');
}
ds_put_format(s, "ref_count=%"PRIu32",", gs.ref_count);
ds_put_format(s, "packet_count=%"PRIu64",", gs.packet_count);
ds_put_format(s, "byte_count=%"PRIu64"", gs.byte_count);
for (bucket_i = 0; bucket_i < gs.n_buckets; bucket_i++) {
if (gs.bucket_stats[bucket_i].packet_count != UINT64_MAX) {
ds_put_format(s, ",bucket%"PRIu32":", bucket_i);
ds_put_format(s, "packet_count=%"PRIu64",", gs.bucket_stats[bucket_i].packet_count);
ds_put_format(s, "byte_count=%"PRIu64"", gs.bucket_stats[bucket_i].byte_count);
}
}
}
}
static void
ofp_print_group_features(struct ds *string, const struct ofp_header *oh)
{
struct ofputil_group_features features;
ofputil_decode_group_features_reply(oh, &features);
ds_put_format(string, "\n Group table:\n");
ds_put_format(string, " Types: 0x%"PRIx32"\n", features.types);
ds_put_format(string, " Capabilities: 0x%"PRIx32"\n",
features.capabilities);
if (features.types & (1u << OFPGT11_ALL)) {
ds_put_format(string, " All group :\n");
ds_put_format(string,
" max_groups = %#"PRIx32" actions=0x%08"PRIx32"\n",
features.max_groups[0], features.actions[0]);
}
if (features.types & (1u << OFPGT11_SELECT)) {
ds_put_format(string, " Select group :\n");
ds_put_format(string, " max_groups = %#"PRIx32" "
"actions=0x%08"PRIx32"\n",
features.max_groups[1], features.actions[1]);
}
if (features.types & (1u << OFPGT11_INDIRECT)) {
ds_put_format(string, " Indirect group :\n");
ds_put_format(string, " max_groups = %#"PRIx32" "
"actions=0x%08"PRIx32"\n",
features.max_groups[2], features.actions[2]);
}
if (features.types & (1u << OFPGT11_FF)) {
ds_put_format(string, " Fast Failover group :\n");
ds_put_format(string, " max_groups = %#"PRIx32" "
"actions=0x%08"PRIx32"\n",
features.max_groups[3], features.actions[3]);
}
}
static void
ofp_print_group_mod(struct ds *s, const struct ofp_header *oh)
{
struct ofputil_group_mod gm;
int error;
error = ofputil_decode_group_mod(oh, &gm);
if (error) {
ofp_print_error(s, error);
return;
}
ds_put_char(s, '\n');
ds_put_char(s, ' ');
switch (gm.command) {
case OFPGC11_ADD:
ds_put_cstr(s, "ADD");
break;
case OFPGC11_MODIFY:
ds_put_cstr(s, "MOD");
break;
case OFPGC11_DELETE:
ds_put_cstr(s, "DEL");
break;
default:
ds_put_format(s, "cmd:%"PRIu16"", gm.command);
}
ds_put_char(s, ' ');
ofp_print_group(s, gm.group_id, gm.type, &gm.buckets);
}
static void
ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
struct ds *string, int verbosity)
@ -2149,17 +2341,39 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
ofp_header_to_string__(oh, raw, string);
switch (ofptype_from_ofpraw(raw)) {
/* FIXME: Change the following once they are implemented: */
case OFPTYPE_GROUP_STATS_REQUEST:
ofp_print_stats_request(string, oh);
ofp_print_ofpst_group_request(string, oh);
break;
case OFPTYPE_GROUP_STATS_REPLY:
ofp_print_group_stats(string, oh);
break;
case OFPTYPE_GROUP_DESC_STATS_REQUEST:
ofp_print_stats_request(string, oh);
break;
case OFPTYPE_GROUP_DESC_STATS_REPLY:
ofp_print_group_desc(string, oh);
break;
case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
ofp_print_stats_request(string, oh);
break;
case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
ofp_print_group_features(string, oh);
break;
case OFPTYPE_GROUP_MOD:
ofp_print_group_mod(string, oh);
break;
case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
case OFPTYPE_GET_ASYNC_REQUEST:
case OFPTYPE_GET_ASYNC_REPLY:
case OFPTYPE_GROUP_STATS_REQUEST:
case OFPTYPE_GROUP_STATS_REPLY:
case OFPTYPE_GROUP_DESC_STATS_REQUEST:
case OFPTYPE_GROUP_DESC_STATS_REPLY:
case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
ofp_print_not_implemented(string);

View File

@ -1537,6 +1537,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
if (error) {
return error;
}
fm->out_group = ntohl(ofm->out_group);
if ((ofm->command == OFPFC_DELETE
|| ofm->command == OFPFC_DELETE_STRICT)
&& ofm->out_group != htonl(OFPG_ANY)) {
@ -1578,6 +1580,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
fm->hard_timeout = ntohs(ofm->hard_timeout);
fm->buffer_id = ntohl(ofm->buffer_id);
fm->out_port = u16_to_ofp(ntohs(ofm->out_port));
fm->out_group = OFPG11_ANY;
raw_flags = ofm->flags;
} else if (raw == OFPRAW_NXT_FLOW_MOD) {
/* Nicira extended flow_mod. */
@ -1608,6 +1611,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
fm->hard_timeout = ntohs(nfm->hard_timeout);
fm->buffer_id = ntohl(nfm->buffer_id);
fm->out_port = u16_to_ofp(ntohs(nfm->out_port));
fm->out_group = OFPG11_ANY;
raw_flags = nfm->flags;
} else {
NOT_REACHED();
@ -2058,7 +2062,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
ofm->priority = htons(fm->priority);
ofm->buffer_id = htonl(fm->buffer_id);
ofm->out_port = ofputil_port_to_ofp11(fm->out_port);
ofm->out_group = htonl(OFPG11_ANY);
ofm->out_group = htonl(fm->out_group);
ofm->flags = raw_flags;
ofputil_put_ofp11_match(msg, &fm->match, protocol);
ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg);
@ -2124,6 +2128,7 @@ ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
fsr->aggregate = aggregate;
ofputil_match_from_ofp10_match(&ofsr->match, &fsr->match);
fsr->out_port = u16_to_ofp(ntohs(ofsr->out_port));
fsr->out_group = OFPG11_ANY;
fsr->table_id = ofsr->table_id;
fsr->cookie = fsr->cookie_mask = htonll(0);
@ -2144,9 +2149,7 @@ ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
if (error) {
return error;
}
if (ofsr->out_group != htonl(OFPG11_ANY)) {
return OFPERR_OFPFMFC_UNKNOWN;
}
fsr->out_group = ntohl(ofsr->out_group);
fsr->cookie = ofsr->cookie;
fsr->cookie_mask = ofsr->cookie_mask;
error = ofputil_pull_ofp11_match(b, &fsr->match, NULL);
@ -2176,6 +2179,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
fsr->aggregate = aggregate;
fsr->out_port = u16_to_ofp(ntohs(nfsr->out_port));
fsr->out_group = OFPG11_ANY;
fsr->table_id = nfsr->table_id;
return 0;
@ -2242,7 +2246,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
ofsr->table_id = fsr->table_id;
ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
ofsr->out_group = htonl(OFPG11_ANY);
ofsr->out_group = htonl(fsr->out_group);
ofsr->cookie = fsr->cookie;
ofsr->cookie_mask = fsr->cookie_mask;
ofputil_put_ofp11_match(msg, &fsr->match, protocol);
@ -2486,7 +2490,7 @@ unknown_to_zero(uint64_t count)
/* Appends an OFPST_FLOW or NXST_FLOW reply that contains the data in 'fs' to
* those already present in the list of ofpbufs in 'replies'. 'replies' should
* have been initialized with ofputil_start_stats_reply(). */
* have been initialized with ofpmp_init(). */
void
ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
struct list *replies)
@ -4576,6 +4580,65 @@ ofputil_port_to_string(ofp_port_t port,
}
}
/* Stores the group id represented by 's' into '*group_idp'. 's' may be an
* integer or, for reserved group IDs, the standard OpenFlow name for the group
* (either "ANY" or "ALL").
*
* Returns true if successful, false if 's' is not a valid OpenFlow group ID or
* name. */
bool
ofputil_group_from_string(const char *s, uint32_t *group_idp)
{
if (!strcasecmp(s, "any")) {
*group_idp = OFPG11_ANY;
} else if (!strcasecmp(s, "all")) {
*group_idp = OFPG11_ALL;
} else if (!str_to_uint(s, 10, group_idp)) {
VLOG_WARN("%s is not a valid group ID. (Valid group IDs are "
"32-bit nonnegative integers or the keywords ANY or "
"ALL.)", s);
return false;
}
return true;
}
/* Appends to 's' a string representation of the OpenFlow group ID 'group_id'.
* Most groups' string representation is just the number, but for special
* groups, e.g. OFPG11_ALL, it is the name, e.g. "ALL". */
void
ofputil_format_group(uint32_t group_id, struct ds *s)
{
char name[MAX_GROUP_NAME_LEN];
ofputil_group_to_string(group_id, name, sizeof name);
ds_put_cstr(s, name);
}
/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
* representation of OpenFlow group ID 'group_id'. Most group are represented
* as just their number, but special groups, e.g. OFPG11_ALL, are represented
* by name, e.g. "ALL". */
void
ofputil_group_to_string(uint32_t group_id,
char namebuf[MAX_GROUP_NAME_LEN + 1], size_t bufsize)
{
switch (group_id) {
case OFPG11_ALL:
ovs_strlcpy(namebuf, "ALL", bufsize);
break;
case OFPG11_ANY:
ovs_strlcpy(namebuf, "ANY", bufsize);
break;
default:
snprintf(namebuf, bufsize, "%"PRIu32, group_id);
break;
}
}
/* Given a buffer 'b' that contains an array of OpenFlow ports of type
* 'ofp_version', tries to pull the first element from the array. If
* successful, initializes '*pp' with an abstract representation of the
@ -5193,6 +5256,540 @@ ofputil_decode_port_stats_request(const struct ofp_header *request,
}
}
/* Frees all of the "struct ofputil_bucket"s in the 'buckets' list. */
void
ofputil_bucket_list_destroy(struct list *buckets)
{
struct ofputil_bucket *bucket, *next_bucket;
LIST_FOR_EACH_SAFE (bucket, next_bucket, list_node, buckets) {
list_remove(&bucket->list_node);
free(bucket->ofpacts);
free(bucket);
}
}
/* Returns an OpenFlow group stats request for OpenFlow version 'ofp_version',
* that requests stats for group 'group_id'. (Use OFPG_ALL to request stats
* for all groups.)
*
* Group statistics include packet and byte counts for each group. */
struct ofpbuf *
ofputil_encode_group_stats_request(enum ofp_version ofp_version,
uint32_t group_id)
{
struct ofpbuf *request;
switch (ofp_version) {
case OFP10_VERSION:
ovs_fatal(0, "dump-group-stats needs OpenFlow 1.1 or later "
"(\'-O OpenFlow11\')");
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION: {
struct ofp11_group_stats_request *req;
request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_REQUEST, ofp_version, 0);
req = ofpbuf_put_zeros(request, sizeof *req);
req->group_id = htonl(group_id);
break;
}
default:
NOT_REACHED();
}
return request;
}
/* Returns an OpenFlow group description request for OpenFlow version
* 'ofp_version', that requests stats for group 'group_id'. (Use OFPG_ALL to
* request stats for all groups.)
*
* Group descriptions include the bucket and action configuration for each
* group. */
struct ofpbuf *
ofputil_encode_group_desc_request(enum ofp_version ofp_version)
{
struct ofpbuf *request;
switch (ofp_version) {
case OFP10_VERSION:
ovs_fatal(0, "dump-groups needs OpenFlow 1.1 or later "
"(\'-O OpenFlow11\')");
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION: {
request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST, ofp_version, 0);
break;
}
default:
NOT_REACHED();
}
return request;
}
static void *
ofputil_group_stats_to_ofp11(const struct ofputil_group_stats *ogs,
size_t base_len, struct list *replies)
{
struct ofp11_bucket_counter *bc11;
struct ofp11_group_stats *gs11;
size_t length;
int i;
length = base_len + sizeof(struct ofp11_bucket_counter) * ogs->n_buckets;
gs11 = ofpmp_append(replies, length);
memset(gs11, 0, base_len);
gs11->length = htons(length);
gs11->group_id = htonl(ogs->group_id);
gs11->ref_count = htonl(ogs->ref_count);
gs11->packet_count = htonll(ogs->packet_count);
gs11->byte_count = htonll(ogs->byte_count);
bc11 = (void *) (((uint8_t *) gs11) + base_len);
for (i = 0; i < ogs->n_buckets; i++) {
const struct bucket_counter *obc = &ogs->bucket_stats[i];
bc11[i].packet_count = htonll(obc->packet_count);
bc11[i].byte_count = htonll(obc->byte_count);
}
return gs11;
}
static void
ofputil_append_of13_group_stats(const struct ofputil_group_stats *ogs,
struct list *replies)
{
struct ofp13_group_stats *gs13;
gs13 = ofputil_group_stats_to_ofp11(ogs, sizeof *gs13, replies);
gs13->duration_sec = htonl(ogs->duration_sec);
gs13->duration_nsec = htonl(ogs->duration_nsec);
}
/* Encodes 'ogs' properly for the format of the list of group statistics
* replies already begun in 'replies' and appends it to the list. 'replies'
* must have originally been initialized with ofpmp_init(). */
void
ofputil_append_group_stats(struct list *replies,
const struct ofputil_group_stats *ogs)
{
struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
struct ofp_header *oh = msg->data;
switch ((enum ofp_version)oh->version) {
case OFP11_VERSION:
case OFP12_VERSION:
ofputil_group_stats_to_ofp11(ogs, sizeof(struct ofp11_group_stats),
replies);
break;
case OFP13_VERSION:
ofputil_append_of13_group_stats(ogs, replies);
break;
case OFP10_VERSION:
default:
NOT_REACHED();
}
}
/* Returns an OpenFlow group features request for OpenFlow version
* 'ofp_version'. */
struct ofpbuf *
ofputil_encode_group_features_request(enum ofp_version ofp_version)
{
struct ofpbuf *request = NULL;
switch (ofp_version) {
case OFP10_VERSION:
case OFP11_VERSION:
ovs_fatal(0, "dump-group-features needs OpenFlow 1.2 or later "
"(\'-O OpenFlow12\')");
case OFP12_VERSION:
case OFP13_VERSION: {
request = ofpraw_alloc(OFPRAW_OFPST12_GROUP_FEATURES_REQUEST,
ofp_version, 0);
break;
}
default:
NOT_REACHED();
}
return request;
}
/* Returns a OpenFlow message that encodes 'features' properly as a reply to
* group features request 'request'. */
struct ofpbuf *
ofputil_encode_group_features_reply(
const struct ofputil_group_features *features,
const struct ofp_header *request)
{
struct ofp12_group_features_stats *ogf;
struct ofpbuf *reply;
reply = ofpraw_alloc_xid(OFPRAW_OFPST12_GROUP_FEATURES_REPLY,
request->version, request->xid, 0);
ogf = ofpbuf_put_zeros(reply, sizeof *ogf);
ogf->types = htonl(features->types);
ogf->capabilities = htonl(features->capabilities);
ogf->max_groups[0] = htonl(features->max_groups[0]);
ogf->max_groups[1] = htonl(features->max_groups[1]);
ogf->max_groups[2] = htonl(features->max_groups[2]);
ogf->max_groups[3] = htonl(features->max_groups[3]);
ogf->actions[0] = htonl(features->actions[0]);
ogf->actions[1] = htonl(features->actions[1]);
ogf->actions[2] = htonl(features->actions[2]);
ogf->actions[3] = htonl(features->actions[3]);
return reply;
}
/* Decodes group features reply 'oh' into 'features'. */
void
ofputil_decode_group_features_reply(const struct ofp_header *oh,
struct ofputil_group_features *features)
{
const struct ofp12_group_features_stats *ogf = ofpmsg_body(oh);
features->types = ntohl(ogf->types);
features->capabilities = ntohl(ogf->capabilities);
features->max_groups[0] = ntohl(ogf->max_groups[0]);
features->max_groups[1] = ntohl(ogf->max_groups[1]);
features->max_groups[2] = ntohl(ogf->max_groups[2]);
features->max_groups[3] = ntohl(ogf->max_groups[3]);
features->actions[0] = ntohl(ogf->actions[0]);
features->actions[1] = ntohl(ogf->actions[1]);
features->actions[2] = ntohl(ogf->actions[2]);
features->actions[3] = ntohl(ogf->actions[3]);
}
/* Parse a group status request message into a 32 bit OpenFlow 1.1
* group ID and stores the latter in '*group_id'.
* Returns 0 if successful, otherwise an OFPERR_* number. */
enum ofperr
ofputil_decode_group_stats_request(const struct ofp_header *request,
uint32_t *group_id)
{
const struct ofp11_group_stats_request *gsr11 = ofpmsg_body(request);
*group_id = ntohl(gsr11->group_id);
return 0;
}
/* Converts a group stats reply in 'msg' into an abstract ofputil_group_stats
* in 'gs'.
*
* Multiple group stats replies can be packed into a single OpenFlow message.
* Calling this function multiple times for a single 'msg' iterates through the
* replies. The caller must initially leave 'msg''s layer pointers null and
* not modify them between calls.
*
* Returns 0 if successful, EOF if no replies were left in this 'msg',
* otherwise a positive errno value. */
int
ofputil_decode_group_stats_reply(struct ofpbuf *msg,
struct ofputil_group_stats *gs)
{
struct ofp11_bucket_counter *obc;
struct ofp11_group_stats *ogs11;
enum ofpraw raw;
enum ofperr error;
size_t base_len;
size_t length;
size_t i;
error = (msg->l2
? ofpraw_decode(&raw, msg->l2)
: ofpraw_pull(&raw, msg));
if (error) {
return error;
}
if (!msg->size) {
return EOF;
}
if (raw == OFPRAW_OFPST11_GROUP_REPLY) {
base_len = sizeof *ogs11;
ogs11 = ofpbuf_try_pull(msg, sizeof *ogs11);
gs->duration_sec = gs->duration_nsec = UINT32_MAX;
} else if (raw == OFPRAW_OFPST13_GROUP_REPLY) {
struct ofp13_group_stats *ogs13;
base_len = sizeof *ogs13;
ogs13 = ofpbuf_try_pull(msg, sizeof *ogs13);
if (ogs13) {
ogs11 = &ogs13->gs;
gs->duration_sec = ntohl(ogs13->duration_sec);
gs->duration_nsec = ntohl(ogs13->duration_nsec);
} else {
ogs11 = NULL;
}
} else {
NOT_REACHED();
}
if (!ogs11) {
VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply has %zu leftover bytes at end",
ofpraw_get_name(raw), msg->size);
return OFPERR_OFPBRC_BAD_LEN;
}
length = ntohs(ogs11->length);
if (length < sizeof base_len) {
VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply claims invalid length %zu",
ofpraw_get_name(raw), length);
return OFPERR_OFPBRC_BAD_LEN;
}
gs->group_id = ntohl(ogs11->group_id);
gs->ref_count = ntohl(ogs11->ref_count);
gs->packet_count = ntohll(ogs11->packet_count);
gs->byte_count = ntohll(ogs11->byte_count);
gs->n_buckets = (length - base_len) / sizeof *obc;
obc = ofpbuf_try_pull(msg, gs->n_buckets * sizeof *obc);
if (!obc) {
VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply has %zu leftover bytes at end",
ofpraw_get_name(raw), msg->size);
return OFPERR_OFPBRC_BAD_LEN;
}
for (i = 0; i < gs->n_buckets; i++) {
gs->bucket_stats[i].packet_count = ntohll(obc[i].packet_count);
gs->bucket_stats[i].byte_count = ntohll(obc[i].byte_count);
}
return 0;
}
/* Appends a group stats reply that contains the data in 'gds' to those already
* present in the list of ofpbufs in 'replies'. 'replies' should have been
* initialized with ofpmp_init(). */
void
ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds,
struct list *buckets,
struct list *replies)
{
struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
struct ofp11_group_desc_stats *ogds;
struct ofputil_bucket *bucket;
size_t start_ogds;
start_ogds = reply->size;
ofpbuf_put_zeros(reply, sizeof *ogds);
LIST_FOR_EACH (bucket, list_node, buckets) {
struct ofp11_bucket *ob;
size_t start_ob;
start_ob = reply->size;
ofpbuf_put_zeros(reply, sizeof *ob);
ofpacts_put_openflow11_actions(bucket->ofpacts,
bucket->ofpacts_len, reply);
ob = ofpbuf_at_assert(reply, start_ob, sizeof *ob);
ob->len = htons(reply->size - start_ob);
ob->weight = htons(bucket->weight);
ob->watch_port = ofputil_port_to_ofp11(bucket->watch_port);
ob->watch_group = htonl(bucket->watch_group);
}
ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds);
ogds->length = htons(reply->size - start_ogds);
ogds->type = gds->type;
ogds->group_id = htonl(gds->group_id);
ofpmp_postappend(replies, start_ogds);
}
static enum ofperr
ofputil_pull_buckets(struct ofpbuf *msg, size_t buckets_length,
struct list *buckets)
{
struct ofp11_bucket *ob;
list_init(buckets);
while (buckets_length > 0) {
struct ofputil_bucket *bucket;
struct ofpbuf ofpacts;
enum ofperr error;
size_t ob_len;
ob = (buckets_length >= sizeof *ob
? ofpbuf_try_pull(msg, sizeof *ob)
: NULL);
if (!ob) {
VLOG_WARN_RL(&bad_ofmsg_rl, "buckets end with %zu leftover bytes",
buckets_length);
}
ob_len = ntohs(ob->len);
if (ob_len < sizeof *ob) {
VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length "
"%zu is not valid", ob_len);
return OFPERR_OFPGMFC_BAD_BUCKET;
} else if (ob_len > buckets_length) {
VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length "
"%zu exceeds remaining buckets data size %zu",
ob_len, buckets_length);
return OFPERR_OFPGMFC_BAD_BUCKET;
}
buckets_length -= ob_len;
ofpbuf_init(&ofpacts, 0);
error = ofpacts_pull_openflow11_actions(msg, ob_len - sizeof *ob,
&ofpacts);
if (error) {
ofpbuf_uninit(&ofpacts);
ofputil_bucket_list_destroy(buckets);
return error;
}
bucket = xzalloc(sizeof *bucket);
bucket->weight = ntohs(ob->weight);
error = ofputil_port_from_ofp11(ob->watch_port, &bucket->watch_port);
if (error) {
ofpbuf_uninit(&ofpacts);
ofputil_bucket_list_destroy(buckets);
return OFPERR_OFPGMFC_BAD_WATCH;
}
bucket->watch_group = ntohl(ob->watch_group);
bucket->ofpacts = ofpbuf_steal_data(&ofpacts);
bucket->ofpacts_len = ofpacts.size;
list_push_back(buckets, &bucket->list_node);
}
return 0;
}
/* Converts a group description reply in 'msg' into an abstract
* ofputil_group_desc in 'gd'.
*
* Multiple group description replies can be packed into a single OpenFlow
* message. Calling this function multiple times for a single 'msg' iterates
* through the replies. The caller must initially leave 'msg''s layer pointers
* null and not modify them between calls.
*
* Returns 0 if successful, EOF if no replies were left in this 'msg',
* otherwise a positive errno value. */
int
ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd,
struct ofpbuf *msg)
{
struct ofp11_group_desc_stats *ogds;
size_t length;
if (!msg->l2) {
ofpraw_pull_assert(msg);
}
if (!msg->size) {
return EOF;
}
ogds = ofpbuf_try_pull(msg, sizeof *ogds);
if (!ogds) {
VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply has %zu "
"leftover bytes at end", msg->size);
return OFPERR_OFPBRC_BAD_LEN;
}
gd->type = ogds->type;
gd->group_id = ntohl(ogds->group_id);
length = ntohs(ogds->length);
if (length < sizeof *ogds || length - sizeof *ogds > msg->size) {
VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply claims invalid "
"length %zu", length);
return OFPERR_OFPBRC_BAD_LEN;
}
return ofputil_pull_buckets(msg, length - sizeof *ogds, &gd->buckets);
}
/* Converts abstract group mod 'gm' into a message for OpenFlow version
* 'ofp_version' and returns the message. */
struct ofpbuf *
ofputil_encode_group_mod(enum ofp_version ofp_version,
const struct ofputil_group_mod *gm)
{
struct ofpbuf *b;
struct ofp11_group_mod *ogm;
size_t start_ogm;
size_t start_bucket;
struct ofputil_bucket *bucket;
struct ofp11_bucket *ob;
switch (ofp_version) {
case OFP10_VERSION: {
if (gm->command == OFPGC11_ADD) {
ovs_fatal(0, "add-group needs OpenFlow 1.1 or later "
"(\'-O OpenFlow11\')");
} else if (gm->command == OFPGC11_MODIFY) {
ovs_fatal(0, "mod-group needs OpenFlow 1.1 or later "
"(\'-O OpenFlow11\')");
} else {
ovs_fatal(0, "del-groups needs OpenFlow 1.1 or later "
"(\'-O OpenFlow11\')");
}
}
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION: {
b = ofpraw_alloc(OFPRAW_OFPT11_GROUP_MOD, ofp_version, 0);
start_ogm = b->size;
ofpbuf_put_uninit(b, sizeof *ogm);
LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
start_bucket = b->size;
ofpbuf_put_uninit(b, sizeof *ob);
if (bucket->ofpacts && bucket->ofpacts_len) {
ofpacts_put_openflow11_actions(bucket->ofpacts,
bucket->ofpacts_len, b);
}
ob = ofpbuf_at_assert(b, start_bucket, sizeof *ob);
ob->len = htons(b->size - start_bucket);;
ob->weight = htons(bucket->weight);
ob->watch_port = ofputil_port_to_ofp11(bucket->watch_port);
ob->watch_group = htonl(bucket->watch_group);
}
ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm);
ogm->command = htons(gm->command);
ogm->type = gm->type;
ogm->pad = 0;
ogm->group_id = htonl(gm->group_id);
break;
}
default:
NOT_REACHED();
}
return b;
}
/* Converts OpenFlow group mod message 'oh' into an abstract group mod in
* 'gm'. Returns 0 if successful, otherwise an OpenFlow error code. */
enum ofperr
ofputil_decode_group_mod(const struct ofp_header *oh,
struct ofputil_group_mod *gm)
{
const struct ofp11_group_mod *ogm;
struct ofpbuf msg;
ofpbuf_use_const(&msg, oh, ntohs(oh->length));
ofpraw_pull_assert(&msg);
ogm = ofpbuf_pull(&msg, sizeof *ogm);
gm->command = ntohs(ogm->command);
gm->type = ogm->type;
gm->group_id = ntohl(ogm->group_id);
return ofputil_pull_buckets(&msg, msg.size, &gm->buckets);
}
/* Parse a queue status request message into 'oqsr'.
* Returns 0 if successful, otherwise an OFPERR_* number. */
enum ofperr

View File

@ -40,6 +40,7 @@ OFPAT11_ACTION(OFPAT11_SET_QUEUE, ofp11_action_set_queue, 0, "set_queue")
//OFPAT11_ACTION(OFPAT11_SET_NW_TTL, ofp11_action_nw_ttl, 0, "set_nw_ttl")
OFPAT11_ACTION(OFPAT11_DEC_NW_TTL, ofp_action_header, 0, NULL)
OFPAT11_ACTION(OFPAT12_SET_FIELD, ofp12_action_set_field, 1, "set_field")
OFPAT11_ACTION(OFPAT11_GROUP, ofp11_action_group, 0, "group")
#ifndef NXAST_ACTION
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)

View File

@ -43,6 +43,14 @@ void ofputil_format_port(ofp_port_t port, struct ds *);
void ofputil_port_to_string(ofp_port_t, char namebuf[OFP_MAX_PORT_NAME_LEN],
size_t bufsize);
/* Group numbers. */
enum { MAX_GROUP_NAME_LEN = INT_STRLEN(uint32_t) };
bool ofputil_group_from_string(const char *, uint32_t *group_id);
void ofputil_format_group(uint32_t group_id, struct ds *);
void ofputil_group_to_string(uint32_t group_id,
char namebuf[MAX_GROUP_NAME_LEN + 1],
size_t bufsize);
/* Converting OFPFW10_NW_SRC_MASK and OFPFW10_NW_DST_MASK wildcard bit counts
* to and from IP bitmasks. */
ovs_be32 ofputil_wcbits_to_netmask(int wcbits);
@ -268,6 +276,7 @@ struct ofputil_flow_mod {
uint16_t hard_timeout;
uint32_t buffer_id;
ofp_port_t out_port;
uint32_t out_group;
enum ofputil_flow_mod_flags flags;
struct ofpact *ofpacts; /* Series of "struct ofpact"s. */
size_t ofpacts_len; /* Length of ofpacts, in bytes. */
@ -287,6 +296,7 @@ struct ofputil_flow_stats_request {
ovs_be64 cookie;
ovs_be64 cookie_mask;
ofp_port_t out_port;
uint32_t out_group;
uint8_t table_id;
};
@ -867,4 +877,88 @@ int ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *ms
void ofputil_append_queue_stat(struct list *replies,
const struct ofputil_queue_stats *oqs);
/* Bucket for use in groups. */
struct ofputil_bucket {
struct list list_node;
uint16_t weight; /* Relative weight, for "select" groups. */
ofp_port_t watch_port; /* Port whose state affects whether this bucket
* is live. Only required for fast failover
* groups. */
uint32_t watch_group; /* Group whose state affects whether this
* bucket is live. Only required for fast
* failover groups. */
struct ofpact *ofpacts; /* Series of "struct ofpact"s. */
size_t ofpacts_len; /* Length of ofpacts, in bytes. */
};
/* Protocol-independent group_mod. */
struct ofputil_group_mod {
uint16_t command; /* One of OFPGC11_*. */
uint8_t type; /* One of OFPGT11_*. */
uint32_t group_id; /* Group identifier. */
struct list buckets; /* Contains "struct ofputil_bucket"s. */
};
struct bucket_counter {
uint64_t packet_count; /* Number of packets processed by bucket. */
uint64_t byte_count; /* Number of bytes processed by bucket. */
};
/* Group stats reply, independent of protocol. */
struct ofputil_group_stats {
uint32_t group_id; /* Group identifier. */
uint32_t ref_count;
uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */
uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */
uint32_t duration_sec; /* UINT32_MAX if unknown. */
uint32_t duration_nsec;
uint32_t n_buckets;
struct bucket_counter bucket_stats[16];
};
/* Group features reply, independent of protocol. */
struct ofputil_group_features {
uint32_t types; /* Bitmap of OFPGT_* values supported. */
uint32_t capabilities; /* Bitmap of OFPGFC12_* capability supported. */
uint32_t max_groups[4]; /* Maximum number of groups for each type. */
uint32_t actions[4]; /* Bitmaps of OFPAT_* that are supported. */
};
/* Group desc reply, independent of protocol. */
struct ofputil_group_desc {
uint8_t type; /* One of OFPGT_*. */
uint32_t group_id; /* Group identifier. */
struct list buckets; /* Contains "struct ofputil_bucket"s. */
};
void ofputil_bucket_list_destroy(struct list *buckets);
struct ofpbuf *ofputil_encode_group_stats_request(enum ofp_version,
uint32_t group_id);
enum ofperr ofputil_decode_group_stats_request(
const struct ofp_header *request, uint32_t *group_id);
void ofputil_append_group_stats(struct list *replies,
const struct ofputil_group_stats *);
struct ofpbuf *ofputil_encode_group_features_request(enum ofp_version);
struct ofpbuf *ofputil_encode_group_features_reply(
const struct ofputil_group_features *, const struct ofp_header *request);
void ofputil_decode_group_features_reply(const struct ofp_header *,
struct ofputil_group_features *);
struct ofpbuf *ofputil_encode_group_mod(enum ofp_version ofp_version,
const struct ofputil_group_mod *gm);
enum ofperr ofputil_decode_group_mod(const struct ofp_header *,
struct ofputil_group_mod *);
int ofputil_decode_group_stats_reply(struct ofpbuf *,
struct ofputil_group_stats *);
int ofputil_decode_group_desc_reply(struct ofputil_group_desc *,
struct ofpbuf *);
void ofputil_append_group_desc_reply(const struct ofputil_group_desc *,
struct list *buckets,
struct list *replies);
struct ofpbuf *ofputil_encode_group_desc_request(enum ofp_version);
#endif /* ofp-util.h */

View File

@ -1154,6 +1154,7 @@ is_admitted_msg(const struct ofpbuf *b)
case OFPTYPE_PORT_STATUS:
case OFPTYPE_PACKET_OUT:
case OFPTYPE_FLOW_MOD:
case OFPTYPE_GROUP_MOD:
case OFPTYPE_PORT_MOD:
case OFPTYPE_METER_MOD:
case OFPTYPE_BARRIER_REQUEST:

View File

@ -2181,6 +2181,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
ofpact_get_OUTPUT(a)->max_len, true);
break;
case OFPACT_GROUP:
/* XXX not yet implemented */
break;
case OFPACT_CONTROLLER:
controller = ofpact_get_CONTROLLER(a);
execute_controller_action(ctx, controller->max_len,

View File

@ -6384,4 +6384,10 @@ const struct ofproto_class ofproto_dpif_class = {
NULL, /* meter_set */
NULL, /* meter_get */
NULL, /* meter_del */
NULL, /* group_alloc */
NULL, /* group_construct */
NULL, /* group_destruct */
NULL, /* group_dealloc */
NULL, /* group_modify */
NULL, /* group_get_stats */
};

View File

@ -111,6 +111,12 @@ struct ofproto {
unsigned long int *vlan_bitmap; /* 4096-bit bitmap of in-use VLANs. */
bool vlans_changed; /* True if new VLANs are in use. */
int min_mtu; /* Current MTU of non-internal ports. */
/* Groups. */
struct ovs_rwlock groups_rwlock;
struct hmap groups OVS_GUARDED; /* Contains "struct ofgroup"s. */
uint32_t n_groups[4] OVS_GUARDED; /* # of existing groups of each type. */
struct ofputil_group_features ogf;
};
void ofproto_init_tables(struct ofproto *, int n_tables);
@ -289,16 +295,50 @@ bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t out_port);
void ofoperation_complete(struct ofoperation *, enum ofperr);
bool ofoperation_has_out_port(const struct ofoperation *, ofp_port_t out_port);
bool ofproto_rule_has_out_group(const struct rule *, uint32_t group_id);
bool ofproto_rule_is_hidden(const struct rule *);
/* A group within a "struct ofproto".
*
* With few exceptions, ofproto implementations may look at these fields but
* should not modify them. */
struct ofgroup {
/* The rwlock is used to prevent groups from being deleted while child
* threads are using them to xlate flows. A read lock means the
* group is currently being used. A write lock means the group is
* in the process of being deleted or updated. Note that since
* a read lock on the groups container is held while searching, and
* a group is ever write locked only while holding a write lock
* on the container, the user's of groups will never face a group
* in the write locked state. */
struct ovs_rwlock rwlock;
struct hmap_node hmap_node; /* In struct ofproto's "groups" hmap. */
struct ofproto *ofproto; /* The ofproto that contains this group. */
uint32_t group_id;
uint8_t type; /* One of OFPGT_*. */
long long int created; /* Creation time. */
long long int modified; /* Time of last modification. */
struct list buckets; /* Contains "struct ofputil_bucket"s. */
uint32_t n_buckets;
};
bool ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id,
struct ofgroup **group)
OVS_TRY_RDLOCK(true, (*group)->rwlock);
void ofproto_group_release(struct ofgroup *group)
OVS_RELEASES(group->rwlock);
/* ofproto class structure, to be defined by each ofproto implementation.
*
*
* Data Structures
* ===============
*
* These functions work primarily with three different kinds of data
* These functions work primarily with four different kinds of data
* structures:
*
* - "struct ofproto", which represents an OpenFlow switch.
@ -307,6 +347,9 @@ bool ofproto_rule_is_hidden(const struct rule *);
*
* - "struct rule", which represents an OpenFlow flow within an ofproto.
*
* - "struct ofgroup", which represents an OpenFlow 1.1+ group within an
* ofproto.
*
* Each of these data structures contains all of the implementation-independent
* generic state for the respective concept, called the "base" state. None of
* them contains any extra space for ofproto implementations to use. Instead,
@ -328,9 +371,10 @@ bool ofproto_rule_is_hidden(const struct rule *);
* ofproto ->alloc ->construct ->destruct ->dealloc
* ofport ->port_alloc ->port_construct ->port_destruct ->port_dealloc
* rule ->rule_alloc ->rule_construct ->rule_destruct ->rule_dealloc
* group ->group_alloc ->group_construct ->group_destruct ->group_dealloc
*
* "ofproto" and "ofport" have this exact life cycle. The "rule" data
* structure also follow this life cycle with some additional elaborations
* "ofproto", "ofport", and "group" have this exact life cycle. The "rule"
* data structure also follow this life cycle with some additional elaborations
* described under "Rule Life Cycle" below.
*
* Any instance of a given data structure goes through the following life
@ -1451,6 +1495,21 @@ struct ofproto_class {
/* Deletes a meter, making the 'ofproto_meter_id' invalid for any
* further calls. */
void (*meter_del)(struct ofproto *, ofproto_meter_id);
/* ## -------------------- ## */
/* ## OpenFlow 1.1+ groups ## */
/* ## -------------------- ## */
struct ofgroup *(*group_alloc)(void);
enum ofperr (*group_construct)(struct ofgroup *);
void (*group_destruct)(struct ofgroup *);
void (*group_dealloc)(struct ofgroup *);
enum ofperr (*group_modify)(struct ofgroup *, struct ofgroup *victim);
enum ofperr (*group_get_stats)(const struct ofgroup *,
struct ofputil_group_stats *);
};
extern const struct ofproto_class ofproto_dpif_class;

View File

@ -213,6 +213,7 @@ static enum ofperr modify_flows__(struct ofproto *, struct ofconn *,
static void delete_flow__(struct rule *rule, struct ofopgroup *,
enum ofp_flow_removed_reason)
OVS_RELEASES(rule->evict);
static enum ofperr add_group(struct ofproto *, struct ofputil_group_mod *);
static bool handle_openflow(struct ofconn *, const struct ofpbuf *);
static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
struct ofputil_flow_mod *,
@ -456,6 +457,8 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
ofproto->vlan_bitmap = NULL;
ofproto->vlans_changed = false;
ofproto->min_mtu = INT_MAX;
ovs_rwlock_init(&ofproto->groups_rwlock);
hmap_init(&ofproto->groups);
error = ofproto->ofproto_class->construct(ofproto);
if (error) {
@ -1124,6 +1127,8 @@ ofproto_flush__(struct ofproto *ofproto)
}
}
static void delete_group(struct ofproto *ofproto, uint32_t group_id);
static void
ofproto_destroy__(struct ofproto *ofproto)
{
@ -1137,6 +1142,10 @@ ofproto_destroy__(struct ofproto *ofproto)
free(ofproto->meters);
}
delete_group(ofproto, OFPG_ALL);
ovs_rwlock_destroy(&ofproto->groups_rwlock);
hmap_destroy(&ofproto->groups);
connmgr_destroy(ofproto->connmgr);
hmap_remove(&all_ofprotos, &ofproto->hmap_node);
@ -2249,6 +2258,14 @@ ofproto_rule_has_out_port(const struct rule *rule, ofp_port_t port)
|| ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port));
}
/* Returns true if 'rule' has group and equals group_id. */
bool
ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id)
{
return (group_id == OFPG11_ANY
|| ofpacts_output_to_group(rule->ofpacts, rule->ofpacts_len, group_id));
}
/* Returns true if a rule related to 'op' has an OpenFlow OFPAT_OUTPUT or
* OFPAT_ENQUEUE action that outputs to 'out_port'. */
bool
@ -2890,7 +2907,8 @@ static enum ofperr
collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
const struct match *match,
ovs_be64 cookie, ovs_be64 cookie_mask,
ofp_port_t out_port, struct list *rules)
ofp_port_t out_port, uint32_t out_group,
struct list *rules)
{
struct oftable *table;
struct cls_rule cr;
@ -2943,6 +2961,7 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
}
if (!ofproto_rule_is_hidden(rule)
&& ofproto_rule_has_out_port(rule, out_port)
&& ofproto_rule_has_out_group(rule, out_group)
&& !((rule->flow_cookie ^ cookie) & cookie_mask)) {
list_push_back(rules, &rule->ofproto_node);
}
@ -2970,7 +2989,8 @@ static enum ofperr
collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
const struct match *match, unsigned int priority,
ovs_be64 cookie, ovs_be64 cookie_mask,
ofp_port_t out_port, struct list *rules)
ofp_port_t out_port, uint32_t out_group,
struct list *rules)
{
struct oftable *table;
struct cls_rule cr;
@ -3023,6 +3043,7 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
}
if (!ofproto_rule_is_hidden(rule)
&& ofproto_rule_has_out_port(rule, out_port)
&& ofproto_rule_has_out_group(rule, out_group)
&& !((rule->flow_cookie ^ cookie) & cookie_mask)) {
list_push_back(rules, &rule->ofproto_node);
}
@ -3062,7 +3083,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
error = collect_rules_loose(ofproto, fsr.table_id, &fsr.match,
fsr.cookie, fsr.cookie_mask,
fsr.out_port, &rules);
fsr.out_port, fsr.out_group, &rules);
if (error) {
return error;
}
@ -3189,7 +3210,7 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
error = collect_rules_loose(ofproto, request.table_id, &request.match,
request.cookie, request.cookie_mask,
request.out_port, &rules);
request.out_port, request.out_group, &rules);
if (error) {
return error;
}
@ -3652,7 +3673,7 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
error = collect_rules_loose(ofproto, fm->table_id, &fm->match,
fm->cookie, fm->cookie_mask,
OFPP_ANY, &rules);
OFPP_ANY, OFPG11_ANY, &rules);
if (error) {
return error;
} else if (list_is_empty(&rules)) {
@ -3677,8 +3698,7 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
error = collect_rules_strict(ofproto, fm->table_id, &fm->match,
fm->priority, fm->cookie, fm->cookie_mask,
OFPP_ANY, &rules);
OFPP_ANY, OFPG11_ANY, &rules);
if (error) {
return error;
} else if (list_is_empty(&rules)) {
@ -3737,7 +3757,7 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
error = collect_rules_loose(ofproto, fm->table_id, &fm->match,
fm->cookie, fm->cookie_mask,
fm->out_port, &rules);
fm->out_port, fm->out_group, &rules);
return (error ? error
: !list_is_empty(&rules) ? delete_flows__(ofproto, ofconn, request,
&rules, OFPRR_DELETE)
@ -3755,7 +3775,7 @@ delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
error = collect_rules_strict(ofproto, fm->table_id, &fm->match,
fm->priority, fm->cookie, fm->cookie_mask,
fm->out_port, &rules);
fm->out_port, fm->out_group, &rules);
return (error ? error
: list_is_singleton(&rules) ? delete_flows__(ofproto, ofconn,
request, &rules,
@ -3818,7 +3838,8 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason)
struct ofproto *ofproto = rule->ofproto;
struct classifier *cls = &ofproto->tables[rule->table_id].cls;
ovs_assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT);
ovs_assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT
|| reason == OFPRR_DELETE || reason == OFPRR_GROUP_DELETE);
ofproto_rule_send_removed(rule, reason);
ovs_rwlock_wrlock(&cls->rwlock);
@ -4675,6 +4696,402 @@ handle_meter_request(struct ofconn *ofconn, const struct ofp_header *request,
return 0;
}
bool
ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id,
struct ofgroup **group)
OVS_TRY_RDLOCK(true, (*group)->rwlock)
{
ovs_rwlock_rdlock(&ofproto->groups_rwlock);
HMAP_FOR_EACH_IN_BUCKET (*group, hmap_node,
hash_int(group_id, 0), &ofproto->groups) {
if ((*group)->group_id == group_id) {
ovs_rwlock_rdlock(&(*group)->rwlock);
ovs_rwlock_unlock(&ofproto->groups_rwlock);
return true;
}
}
ovs_rwlock_unlock(&ofproto->groups_rwlock);
return false;
}
void
ofproto_group_release(struct ofgroup *group)
OVS_RELEASES(group->rwlock)
{
ovs_rwlock_unlock(&group->rwlock);
}
static bool
ofproto_group_write_lookup(const struct ofproto *ofproto, uint32_t group_id,
struct ofgroup **group)
OVS_TRY_WRLOCK(true, ofproto->groups_rwlock)
OVS_TRY_WRLOCK(true, (*group)->rwlock)
{
ovs_rwlock_wrlock(&ofproto->groups_rwlock);
HMAP_FOR_EACH_IN_BUCKET (*group, hmap_node,
hash_int(group_id, 0), &ofproto->groups) {
if ((*group)->group_id == group_id) {
ovs_rwlock_wrlock(&(*group)->rwlock);
return true;
}
}
ovs_rwlock_unlock(&ofproto->groups_rwlock);
return false;
}
static bool
ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id)
OVS_REQ_RDLOCK(ofproto->groups_rwlock)
{
struct ofgroup *grp;
HMAP_FOR_EACH_IN_BUCKET (grp, hmap_node,
hash_int(group_id, 0), &ofproto->groups) {
if (grp->group_id == group_id) {
return true;
}
}
return false;
}
static void
append_group_stats(struct ofgroup *group, struct list *replies)
OVS_REQ_RDLOCK(group->rwlock)
{
struct ofputil_group_stats ogs;
struct ofproto *ofproto = group->ofproto;
long long int now = time_msec();
int error;
error = (ofproto->ofproto_class->group_get_stats
? ofproto->ofproto_class->group_get_stats(group, &ogs)
: EOPNOTSUPP);
if (error) {
ogs.ref_count = UINT32_MAX;
ogs.packet_count = UINT64_MAX;
ogs.byte_count = UINT64_MAX;
ogs.n_buckets = group->n_buckets;
memset(ogs.bucket_stats, 0xff,
ogs.n_buckets * sizeof *ogs.bucket_stats);
}
ogs.group_id = group->group_id;
calc_duration(group->created, now, &ogs.duration_sec, &ogs.duration_nsec);
ofputil_append_group_stats(replies, &ogs);
}
static enum ofperr
handle_group_stats_request(struct ofconn *ofconn,
const struct ofp_header *request)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct list replies;
enum ofperr error;
struct ofgroup *group;
uint32_t group_id;
error = ofputil_decode_group_stats_request(request, &group_id);
if (error) {
return error;
}
ofpmp_init(&replies, request);
if (group_id == OFPG_ALL) {
ovs_rwlock_rdlock(&ofproto->groups_rwlock);
HMAP_FOR_EACH (group, hmap_node, &ofproto->groups) {
ovs_rwlock_rdlock(&group->rwlock);
append_group_stats(group, &replies);
ovs_rwlock_unlock(&group->rwlock);
}
ovs_rwlock_unlock(&ofproto->groups_rwlock);
} else {
if (ofproto_group_lookup(ofproto, group_id, &group)) {
append_group_stats(group, &replies);
ofproto_group_release(group);
}
}
ofconn_send_replies(ofconn, &replies);
return 0;
}
static enum ofperr
handle_group_desc_stats_request(struct ofconn *ofconn,
const struct ofp_header *request)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct list replies;
struct ofputil_group_desc gds;
struct ofgroup *group;
ofpmp_init(&replies, request);
ovs_rwlock_rdlock(&ofproto->groups_rwlock);
HMAP_FOR_EACH (group, hmap_node, &ofproto->groups) {
gds.group_id = group->group_id;
gds.type = group->type;
ofputil_append_group_desc_reply(&gds, &group->buckets, &replies);
}
ovs_rwlock_unlock(&ofproto->groups_rwlock);
ofconn_send_replies(ofconn, &replies);
return 0;
}
static enum ofperr
handle_group_features_stats_request(struct ofconn *ofconn,
const struct ofp_header *request)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
struct ofpbuf *msg;
msg = ofputil_encode_group_features_reply(&p->ogf, request);
if (msg) {
ofconn_send_reply(ofconn, msg);
}
return 0;
}
/* Implements OFPGC11_ADD
* in which no matching flow already exists in the flow table.
*
* Adds the flow specified by 'ofm', which is followed by 'n_actions'
* ofp_actions, to the ofproto's flow table. Returns 0 on success, an OpenFlow
* error code on failure, or OFPROTO_POSTPONE if the operation cannot be
* initiated now but may be retried later.
*
* Upon successful return, takes ownership of 'fm->ofpacts'. On failure,
* ownership remains with the caller.
*
* 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
* if any. */
static enum ofperr
add_group(struct ofproto *ofproto, struct ofputil_group_mod *gm)
{
struct ofgroup *ofgroup;
enum ofperr error;
if (gm->group_id > OFPG_MAX) {
return OFPERR_OFPGMFC_INVALID_GROUP;
}
if (gm->type > OFPGT11_FF) {
return OFPERR_OFPGMFC_BAD_TYPE;
}
/* Allocate new group and initialize it. */
ofgroup = ofproto->ofproto_class->group_alloc();
if (!ofgroup) {
VLOG_WARN_RL(&rl, "%s: failed to create group", ofproto->name);
return OFPERR_OFPGMFC_OUT_OF_GROUPS;
}
ovs_rwlock_init(&ofgroup->rwlock);
ofgroup->ofproto = ofproto;
ofgroup->group_id = gm->group_id;
ofgroup->type = gm->type;
ofgroup->created = ofgroup->modified = time_msec();
list_move(&ofgroup->buckets, &gm->buckets);
ofgroup->n_buckets = list_size(&ofgroup->buckets);
/* Construct called BEFORE any locks are held. */
error = ofproto->ofproto_class->group_construct(ofgroup);
if (error) {
goto free_out;
}
/* We wrlock as late as possible to minimize the time we jam any other
* threads: No visible state changes before acquiring the lock. */
ovs_rwlock_wrlock(&ofproto->groups_rwlock);
if (ofproto->n_groups[gm->type] >= ofproto->ogf.max_groups[gm->type]) {
error = OFPERR_OFPGMFC_OUT_OF_GROUPS;
goto unlock_out;
}
if (ofproto_group_exists(ofproto, gm->group_id)) {
error = OFPERR_OFPGMFC_GROUP_EXISTS;
goto unlock_out;
}
if (!error) {
/* Insert new group. */
hmap_insert(&ofproto->groups, &ofgroup->hmap_node,
hash_int(ofgroup->group_id, 0));
ofproto->n_groups[ofgroup->type]++;
ovs_rwlock_unlock(&ofproto->groups_rwlock);
return error;
}
unlock_out:
ovs_rwlock_unlock(&ofproto->groups_rwlock);
ofproto->ofproto_class->group_destruct(ofgroup);
free_out:
ofputil_bucket_list_destroy(&ofgroup->buckets);
ofproto->ofproto_class->group_dealloc(ofgroup);
return error;
}
/* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code on
* failure.
*
* 'ofconn' is used to retrieve the packet buffer specified in fm->buffer_id,
* if any. */
static enum ofperr
modify_group(struct ofproto *ofproto, struct ofputil_group_mod *gm)
{
struct ofgroup *ofgroup;
struct ofgroup *victim;
enum ofperr error;
if (gm->group_id > OFPG_MAX) {
return OFPERR_OFPGMFC_INVALID_GROUP;
}
if (gm->type > OFPGT11_FF) {
return OFPERR_OFPGMFC_BAD_TYPE;
}
victim = ofproto->ofproto_class->group_alloc();
if (!victim) {
VLOG_WARN_RL(&rl, "%s: failed to allocate group", ofproto->name);
return OFPERR_OFPGMFC_OUT_OF_GROUPS;
}
if (!ofproto_group_write_lookup(ofproto, gm->group_id, &ofgroup)) {
error = OFPERR_OFPGMFC_UNKNOWN_GROUP;
goto free_out;
}
/* Both group's and its container's write locks held now.
* Also, n_groups[] is protected by ofproto->groups_rwlock. */
if (ofgroup->type != gm->type
&& ofproto->n_groups[gm->type] >= ofproto->ogf.max_groups[gm->type]) {
error = OFPERR_OFPGMFC_OUT_OF_GROUPS;
goto unlock_out;
}
*victim = *ofgroup;
list_move(&victim->buckets, &ofgroup->buckets);
ofgroup->type = gm->type;
list_move(&ofgroup->buckets, &gm->buckets);
ofgroup->n_buckets = list_size(&ofgroup->buckets);
error = ofproto->ofproto_class->group_modify(ofgroup, victim);
if (!error) {
ofputil_bucket_list_destroy(&victim->buckets);
ofproto->n_groups[victim->type]--;
ofproto->n_groups[ofgroup->type]++;
ofgroup->modified = time_msec();
} else {
ofputil_bucket_list_destroy(&ofgroup->buckets);
*ofgroup = *victim;
list_move(&ofgroup->buckets, &victim->buckets);
}
unlock_out:
ovs_rwlock_unlock(&ofgroup->rwlock);
ovs_rwlock_unlock(&ofproto->groups_rwlock);
free_out:
ofproto->ofproto_class->group_dealloc(victim);
return error;
}
static void
delete_group__(struct ofproto *ofproto, struct ofgroup *ofgroup)
OVS_RELEASES(ofproto->groups_rwlock)
{
/* Must wait until existing readers are done,
* while holding the container's write lock at the same time. */
ovs_rwlock_wrlock(&ofgroup->rwlock);
hmap_remove(&ofproto->groups, &ofgroup->hmap_node);
/* No-one can find this group any more. */
ofproto->n_groups[ofgroup->type]--;
ovs_rwlock_unlock(&ofproto->groups_rwlock);
ofproto->ofproto_class->group_destruct(ofgroup);
ofputil_bucket_list_destroy(&ofgroup->buckets);
ovs_rwlock_unlock(&ofgroup->rwlock);
ovs_rwlock_destroy(&ofgroup->rwlock);
ofproto->ofproto_class->group_dealloc(ofgroup);
}
/* Implements OFPGC_DELETE. */
static void
delete_group(struct ofproto *ofproto, uint32_t group_id)
{
struct ofgroup *ofgroup;
ovs_rwlock_wrlock(&ofproto->groups_rwlock);
if (group_id == OFPG_ALL) {
for (;;) {
struct hmap_node *node = hmap_first(&ofproto->groups);
if (!node) {
break;
}
ofgroup = CONTAINER_OF(node, struct ofgroup, hmap_node);
delete_group__(ofproto, ofgroup);
/* Lock for each node separately, so that we will not jam the
* other threads for too long time. */
ovs_rwlock_wrlock(&ofproto->groups_rwlock);
}
} else {
HMAP_FOR_EACH_IN_BUCKET (ofgroup, hmap_node,
hash_int(group_id, 0), &ofproto->groups) {
if (ofgroup->group_id == group_id) {
delete_group__(ofproto, ofgroup);
return;
}
}
}
ovs_rwlock_unlock(&ofproto->groups_rwlock);
}
static enum ofperr
handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_group_mod gm;
enum ofperr error;
error = reject_slave_controller(ofconn);
if (error) {
return error;
}
error = ofputil_decode_group_mod(oh, &gm);
if (error) {
return error;
}
switch (gm.command) {
case OFPGC11_ADD:
return add_group(ofproto, &gm);
case OFPGC11_MODIFY:
return modify_group(ofproto, &gm);
case OFPGC11_DELETE:
delete_group(ofproto, gm.group_id);
return 0;
default:
if (gm.command > OFPGC11_DELETE) {
VLOG_WARN_RL(&rl, "%s: Invalid group_mod command type %d",
ofproto->name, gm.command);
}
return OFPERR_OFPGMFC_BAD_COMMAND;
}
}
static enum ofperr
handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
{
@ -4710,6 +5127,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
case OFPTYPE_FLOW_MOD:
return handle_flow_mod(ofconn, oh);
case OFPTYPE_GROUP_MOD:
return handle_group_mod(ofconn, oh);
case OFPTYPE_METER_MOD:
return handle_meter_mod(ofconn, oh);
@ -4778,12 +5198,18 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
case OFPTYPE_METER_FEATURES_STATS_REQUEST:
return handle_meter_features_request(ofconn, oh);
case OFPTYPE_GROUP_STATS_REQUEST:
return handle_group_stats_request(ofconn, oh);
case OFPTYPE_GROUP_DESC_STATS_REQUEST:
return handle_group_desc_stats_request(ofconn, oh);
case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
return handle_group_features_stats_request(ofconn, oh);
/* FIXME: Change the following once they are implemented: */
case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
case OFPTYPE_GET_ASYNC_REQUEST:
case OFPTYPE_GROUP_STATS_REQUEST:
case OFPTYPE_GROUP_DESC_STATS_REQUEST:
case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
return OFPERR_OFPBRC_BAD_TYPE;

View File

@ -1585,13 +1585,6 @@ OFPST_QUEUE reply (xid=0x1): 6 queues
])
AT_CLEANUP
AT_SETUP([OFPST_PORT_DESC request - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "0110000c00000001000d0000"], [0], [dnl
OFPST_PORT_DESC request (xid=0x1):
])
AT_CLEANUP
AT_SETUP([OFPST_QUEUE reply - OF1.1])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
@ -1679,6 +1672,120 @@ OFPST_QUEUE reply (OF1.3) (xid=0x1): 6 queues
])
AT_CLEANUP
AT_SETUP([OFPST_GROUP request])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "\
02 12 00 18 00 00 00 02 00 06 00 00 00 00 00 00 \
ff ff ff ff 00 00 00 00 \
"], [0], [OFPST_GROUP request (OF1.1) (xid=0x2): group_id=ANY
])
AT_CLEANUP
AT_SETUP([OFPST_GROUP reply - OF1.1])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
02 13 00 a0 00 00 00 02 00 06 00 00 00 00 00 00 \
00 50 00 00 87 65 43 21 00 00 00 04 00 00 00 00 \
00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \
00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \
00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \
00 00 00 00 00 00 66 66 00 00 00 00 00 33 33 33 \
00 40 00 00 00 00 00 05 00 00 00 02 00 00 00 00 \
00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \
00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \
00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \
"], [0], [dnl
OFPST_GROUP reply (OF1.1) (xid=0x2):
group_id=2271560481,ref_count=4,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962,bucket2:packet_count=26214,byte_count=3355443
group_id=5,ref_count=2,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962
])
AT_CLEANUP
AT_SETUP([OFPST_GROUP reply - OF1.3])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
04 13 00 b0 00 00 00 02 00 06 00 00 00 00 00 00 \
00 58 00 00 87 65 43 21 00 00 00 04 00 00 00 00 \
00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \
00 00 00 12 1d cd 65 00 \
00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \
00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \
00 00 00 00 00 00 66 66 00 00 00 00 00 33 33 33 \
00 48 00 00 00 00 00 05 00 00 00 02 00 00 00 00 \
00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \
00 00 00 10 1d cd 65 00 \
00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \
00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \
"], [0], [dnl
OFPST_GROUP reply (OF1.3) (xid=0x2):
group_id=2271560481,duration=18.5s,ref_count=4,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962,bucket2:packet_count=26214,byte_count=3355443
group_id=5,duration=16.5s,ref_count=2,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962
])
AT_CLEANUP
AT_SETUP([OFPST_GROUP_DESC request])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "\
02 12 00 10 00 00 00 02 00 07 00 00 00 00 00 00 \
"], [0], [OFPST_GROUP_DESC request (OF1.1) (xid=0x2):
])
AT_CLEANUP
AT_SETUP([OFPST_GROUP_DESC reply])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "\
02 13 00 78 00 00 00 02 00 07 00 00 00 00 00 00 \
00 68 01 00 00 00 20 00 \
00 20 00 64 00 00 00 01 ff ff ff ff 00 00 00 00 \
00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 00 \
00 20 00 c8 00 00 00 02 ff ff ff ff 00 00 00 00 \
00 00 00 10 00 00 00 02 00 00 00 00 00 00 00 00 \
00 20 00 c8 00 00 00 03 ff ff ff ff 00 00 00 00 \
00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
"], [0], [dnl
OFPST_GROUP_DESC reply (OF1.1) (xid=0x2):
group_id=8192,type=select,bucket=weight:100,watch_port:1,actions=output:1,bucket=weight:200,watch_port:2,actions=output:2,bucket=weight:200,watch_port:3,actions=output:3
])
AT_CLEANUP
AT_SETUP([OFPST_GROUP_FEATURES request])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "\
03 12 00 10 00 00 00 02 00 08 00 00 00 00 00 00 \
"], [0], [OFPST_GROUP_FEATURES request (OF1.2) (xid=0x2):
])
AT_CLEANUP
AT_SETUP([OFPST_GROUP_FEATURES reply])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
03 13 00 38 00 00 00 02 00 08 00 00 00 00 00 00 \
00 00 00 0f 00 00 00 0f \
00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 \
00 00 00 01 00 00 00 03 00 00 00 07 00 00 00 0f \
"], [0], [dnl
OFPST_GROUP_FEATURES reply (OF1.2) (xid=0x2):
Group table:
Types: 0xf
Capabilities: 0xf
All group :
max_groups = 0x1 actions=0x00000001
Select group :
max_groups = 0x2 actions=0x00000003
Indirect group :
max_groups = 0x3 actions=0x00000007
Fast Failover group :
max_groups = 0x4 actions=0x0000000f
])
AT_CLEANUP
AT_SETUP([OFPST_PORT_DESC request - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "0110000c00000001000d0000"], [0], [dnl
OFPST_PORT_DESC request (xid=0x1):
])
AT_CLEANUP
AT_SETUP([OFPST_PORT_DESC reply - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
@ -2068,6 +2175,22 @@ NXT_FLOW_MOD (xid=0x2): ADD NXM_NX_TUN_ID(00000000000001c8), NXM_NX_REG0(0000007
])
AT_CLEANUP
AT_SETUP([OFPT_GROUP_MOD])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
02 0f 00 70 11 22 33 44 00 00 01 00 87 65 43 21 \
00 20 00 64 00 00 00 01 ff ff ff ff 00 00 00 00 \
00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 00 \
00 20 00 c8 00 00 00 02 ff ff ff ff 00 00 00 00 \
00 00 00 10 00 00 00 02 00 00 00 00 00 00 00 00 \
00 20 00 c8 00 00 00 03 ff ff ff ff 00 00 00 00 \
00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
"], [0], [dnl
OFPT_GROUP_MOD (OF1.1) (xid=0x11223344):
ADD group_id=2271560481,type=select,bucket=weight:100,watch_port:1,actions=output:1,bucket=weight:200,watch_port:2,actions=output:2,bucket=weight:200,watch_port:3,actions=output:3
])
AT_CLEANUP
AT_SETUP([NXT_FLOW_REMOVED])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\

View File

@ -190,6 +190,29 @@ statistics are printed for all queues on \fIport\fR; if only
\fIport\fR is omitted, then statistics are printed for \fIqueue\fR on
every port where it exists.
.
.SS "OpenFlow 1.1+ Switch Management Commands"
.
The following commands work only with switches that support OpenFlow
1.1 or later. Because support for OpenFlow 1.1 and later is still
experimental in Open vSwitch, it is necessary to explicitly enable
these protocol versions in \fBovs\-ofctl\fR (using \fB\-O\fR) and in
the switch itself (with the \fBprotocols\fR column in the \fBBridge\fR
table). For more information, see ``Q: What versions of OpenFlow does
Open vSwitch support?'' in the Open vSwitch FAQ.
.
.IP "\fBdump\-groups \fIswitch"
Prints to the console all group entries in \fIswitch\fR's tables. Each line
of output is a group entry as described in \fBGroup Syntax\fR below.
.
.IP "\fBdump\-group\-features \fIswitch"
Prints to the console the group features of the \fIswitch\fR.
.
.IP "\fBdump\-group-stats \fIswitch \fR[\fIgroups\fR]"
Prints to the console statistics for the specified \fIgroups in the
\fIswitch\fR's tables. If \fIgroups\fR is omitted then statistics for all
groups are printed. See \fBGroup Syntax\fR, below, for the syntax of
\fIgroups\fR.
.
.SS "OpenFlow Switch Flow Table Commands"
.
These commands manage the flow table in an OpenFlow switch. In each
@ -258,6 +281,30 @@ keyword \fBLOCAL\fR (the preferred way to refer to the OpenFlow
``local'' port), or the keyword \fBNONE\fR to indicate that the packet
was generated by the switch itself.
.
.SS "OpenFlow Switch Group Table Commands"
.
These commands manage the group table in an OpenFlow switch. In each
case, \fIgroup\fR specifies a group entry in the format described in
\fBGroup Syntax\fR, below, and \fIfile\fR is a text file that contains
zero or more groups in the same syntax, one per line.
.IP "\fBadd\-group \fIswitch group\fR"
.IQ "\fBadd\-group \fIswitch \fB\- < \fIfile\fR"
.IQ "\fBadd\-groups \fIswitch file\fR"
Add each group entry to \fIswitch\fR's tables.
.
.IP "\fBmod\-group \fIswitch group\fR"
.IQ "\fBmod\-group \fIswitch \fB\- < \fIfile\fR"
Modify the action buckets in entries from \fIswitch\fR's tables for
each group entry.
.
.IP "\fBdel\-groups \fIswitch\fR"
.IQ "\fBdel\-groups \fIswitch \fR[\fIgroup\fR]"
.IQ "\fBdel\-groups \fIswitch \fB\- < \fIfile\fR"
Deletes entries from \fIswitch\fR's group table. With only a
\fIswitch\fR argument, deletes all groups. Otherwise, deletes the group
for each group entry.
.
.SS "OpenFlow Switch Monitoring Commands"
.
.IP "\fBsnoop \fIswitch\fR"
@ -1439,6 +1486,75 @@ of \fBduration\fR. (This is separate from \fBduration\fR because
The integer number of seconds that have passed without any packets
passing through the flow.
.
.SS "Group Syntax"
.PP
Some \fBovs\-ofctl\fR commands accept an argument that describes a group or
groups. Such flow descriptions comprise a series
\fIfield\fB=\fIvalue\fR assignments, separated by commas or white
space. (Embedding spaces into a group description normally requires
quoting to prevent the shell from breaking the description into
multiple arguments.). Unless noted otherwise only the last instance
of each field is honoured.
.PP
.IP \fBgroup_id=\fIid\fR
The integer group id of group.
When this field is specified in \fBdel-groups\fR or \fBdump-groups\fR,
the keyword "all" may be used to designate all groups.
.
This field is required.
.IP \fBtype=\fItype\fR
The type of the group. This \fBadd-group\fR, \fBadd-groups\fR and
\fBdel-groups\fR command require this field. The following keywords
designated the allowed types:
.RS
.IP \fBall\fR
Execute all buckets in the group.
.IP \fBselect\fR
Execute one bucket in the group.
The switch should select the bucket in such a way that should implement
equal load sharing is achieved. The switch may optionally select the
bucket based on bucket weights.
.IP \fBindirect\fR
Executes the one bucket in the group.
.IP \fBff\fR
.IQ \fBfast_failover\fR
Executes the first live bucket in the group which is associated with
a live port or group.
.RE
.IP \fBbucket\fR=\fIbucket_parameters\fR
The \fBadd-group\fR, \fBadd-groups\fR and \fBmod-group\fR commands
require at least one bucket field. Bucket fields must appear after
all other fields.
.
Multiple bucket fields to specify multiple buckets.
The order in which buckets are specified corresponds to their order in
the group. If the type of the group is "indirect" then only one group may
be specified.
.
\fIbucket_parameters\fR consists of a list of \fIfield\fB=\fIvalue\fR
assignments, separated by commas or white space followed by a
comma-separated list of actions.
The syntax of actions are same
to \fBactions=\fR field described in \fBFlow Syntax\fR above.
The fields for \fIbucket_parameters\fR are:
.
.RS
.IP \fBweight=\fIvalue\fR
The relative weight of the bucket as an integer. This may be used by the switch
during bucket select for groups whose \fBtype\fR is \fBselect\fR.
.IP \fBwatch_port=\fIport\fR
Port used to determine liveness of group.
This or the \fBwatch_group\fR field is required
for groups whose \fBtype\fR is \fBff\fR or \fBfast_failover\fR.
.IP \fBwatch_group=\fIgroup_id\fR
Group identifier of group used to determine liveness of group.
This or the \fBwatch_port\fR field is required
for groups whose \fBtype\fR is \fBff\fR or \fBfast_failover\fR.
.RE
.
.SH OPTIONS
.TP
\fB\-\-strict\fR

View File

@ -301,6 +301,13 @@ usage(void)
" monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n"
" print packets received from SWITCH\n"
" snoop SWITCH snoop on SWITCH and its controller\n"
" add-group SWITCH GROUP add group described by GROUP\n"
" add-group SWITCH FILE add group from FILE\n"
" mod-group SWITCH GROUP modify specific group\n"
" del-groups SWITCH [GROUP] delete matching GROUPs\n"
" dump-group-features SWITCH print group features\n"
" dump-groups SWITCH print group description\n"
" dump-group-stats SWITCH [GROUP] print group statistics\n"
"\nFor OpenFlow switches and controllers:\n"
" probe TARGET probe whether TARGET is up\n"
" ping TARGET [N] latency of N-byte echos\n"
@ -1842,6 +1849,153 @@ ofctl_benchmark(int argc OVS_UNUSED, char *argv[])
count * message_size / (duration / 1000.0));
}
static void
ofctl_group_mod__(const char *remote, struct ofputil_group_mod *gms,
size_t n_gms)
{
struct ofputil_group_mod *gm;
struct ofpbuf *request;
struct vconn *vconn;
size_t i;
open_vconn(remote, &vconn);
for (i = 0; i < n_gms; i++) {
gm = &gms[i];
request = ofputil_encode_group_mod(vconn_get_version(vconn), gm);
if (request) {
transact_noreply(vconn, request);
}
}
vconn_close(vconn);
}
static void
ofctl_group_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
{
struct ofputil_group_mod *gms = NULL;
enum ofputil_protocol usable_protocols;
size_t n_gms = 0;
char *error;
error = parse_ofp_group_mod_file(argv[2], command, &gms, &n_gms,
&usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
ofctl_group_mod__(argv[1], gms, n_gms);
free(gms);
}
static void
ofctl_group_mod(int argc, char *argv[], uint16_t command)
{
if (argc > 2 && !strcmp(argv[2], "-")) {
ofctl_group_mod_file(argc, argv, command);
} else {
enum ofputil_protocol usable_protocols;
struct ofputil_group_mod gm;
char *error;
error = parse_ofp_group_mod_str(&gm, command, argc > 2 ? argv[2] : "",
&usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
ofctl_group_mod__(argv[1], &gm, 1);
}
}
static void
ofctl_add_group(int argc, char *argv[])
{
ofctl_group_mod(argc, argv, OFPGC11_ADD);
}
static void
ofctl_add_groups(int argc, char *argv[])
{
ofctl_group_mod_file(argc, argv, OFPGC11_ADD);
}
static void
ofctl_mod_group(int argc, char *argv[])
{
ofctl_group_mod(argc, argv, OFPGC11_MODIFY);
}
static void
ofctl_del_groups(int argc, char *argv[])
{
ofctl_group_mod(argc, argv, OFPGC11_DELETE);
}
static void
ofctl_dump_group_stats(int argc, char *argv[])
{
enum ofputil_protocol usable_protocols;
struct ofputil_group_mod gm;
struct ofpbuf *request;
struct vconn *vconn;
uint32_t group_id;
char *error;
memset(&gm, 0, sizeof gm);
error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
argc > 2 ? argv[2] : "",
&usable_protocols);
if (error) {
ovs_fatal(0, "%s", error);
}
group_id = gm.group_id;
open_vconn(argv[1], &vconn);
request = ofputil_encode_group_stats_request(vconn_get_version(vconn),
group_id);
if (request) {
dump_stats_transaction(vconn, request);
}
vconn_close(vconn);
}
static void
ofctl_dump_group_desc(int argc OVS_UNUSED, char *argv[])
{
struct ofpbuf *request;
struct vconn *vconn;
open_vconn(argv[1], &vconn);
request = ofputil_encode_group_desc_request(vconn_get_version(vconn));
if (request) {
dump_stats_transaction(vconn, request);
}
vconn_close(vconn);
}
static void
ofctl_dump_group_features(int argc OVS_UNUSED, char *argv[])
{
struct ofpbuf *request;
struct vconn *vconn;
open_vconn(argv[1], &vconn);
request = ofputil_encode_group_features_request(vconn_get_version(vconn));
if (request) {
dump_stats_transaction(vconn, request);
}
vconn_close(vconn);
}
static void
ofctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
@ -3004,6 +3158,14 @@ static const struct command all_commands[] = {
{ "probe", 1, 1, ofctl_probe },
{ "ping", 1, 2, ofctl_ping },
{ "benchmark", 3, 3, ofctl_benchmark },
{ "add-group", 1, 2, ofctl_add_group },
{ "add-groups", 1, 2, ofctl_add_groups },
{ "mod-group", 1, 2, ofctl_mod_group },
{ "del-groups", 1, 2, ofctl_del_groups },
{ "dump-groups", 1, 1, ofctl_dump_group_desc },
{ "dump-group-stats", 1, 2, ofctl_dump_group_stats },
{ "dump-group-features", 1, 1, ofctl_dump_group_features },
{ "help", 0, INT_MAX, ofctl_help },
/* Undocumented commands for testing. */