mirror of
https://github.com/openvswitch/ovs
synced 2025-09-01 06:45:17 +00:00
openflow-1.1+: OFPT_TABLE_MOD (part 1)
Added infrastructure to support Openflow OFPT_TABLE_MOD message. This patch does not include the flexible table miss handling code that is necessary to support the semantics specified in OFPT_TABLE_MOD messages. Current flow miss behavior continues to conform to Openflow 1.0. Future commits to add more flexible table miss support are needed to fully support OPFT_TABLE_MOD for Openflow-1.1+. Signed-off-by: Andy Zhou <azhou@nicira.com> Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
@@ -447,11 +447,11 @@ OFP_ASSERT(sizeof(struct ofp11_table_mod) == 8);
|
||||
These flags are used in ofp_table_stats messages to describe the current
|
||||
configuration and in ofp_table_mod messages to configure table behavior. */
|
||||
enum ofp11_table_config {
|
||||
OFPTC11_TABLE_MISS_CONTROLLER = 0, /* Send to controller. */
|
||||
OFPTC11_TABLE_MISS_CONTROLLER = 0 << 0, /* Send to controller. */
|
||||
OFPTC11_TABLE_MISS_CONTINUE = 1 << 0, /* Continue to the next table in the
|
||||
pipeline (OpenFlow 1.0
|
||||
behavior). */
|
||||
OFPTC11_TABLE_MISS_DROP = 1 << 1, /* Drop the packet. */
|
||||
OFPTC11_TABLE_MISS_DROP = 2 << 0, /* Drop the packet. */
|
||||
OFPTC11_TABLE_MISS_MASK = 3
|
||||
};
|
||||
|
||||
|
@@ -348,6 +348,7 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
|
||||
case OFPTYPE_FLOW_MOD:
|
||||
case OFPTYPE_GROUP_MOD:
|
||||
case OFPTYPE_PORT_MOD:
|
||||
case OFPTYPE_TABLE_MOD:
|
||||
case OFPTYPE_BARRIER_REQUEST:
|
||||
case OFPTYPE_BARRIER_REPLY:
|
||||
case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
|
||||
|
@@ -185,6 +185,9 @@ enum ofpraw {
|
||||
/* OFPT 1.1+ (16): struct ofp11_port_mod. */
|
||||
OFPRAW_OFPT11_PORT_MOD,
|
||||
|
||||
/* OFPT 1.1+ (17): struct ofp11_table_mod. */
|
||||
OFPRAW_OFPT11_TABLE_MOD,
|
||||
|
||||
/* OFPT 1.0 (18): void. */
|
||||
OFPRAW_OFPT10_BARRIER_REQUEST,
|
||||
/* OFPT 1.1+ (20): void. */
|
||||
@@ -466,6 +469,7 @@ enum ofptype {
|
||||
OFPTYPE_GROUP_MOD, /* OFPRAW_OFPT11_GROUP_MOD. */
|
||||
OFPTYPE_PORT_MOD, /* OFPRAW_OFPT10_PORT_MOD.
|
||||
* OFPRAW_OFPT11_PORT_MOD. */
|
||||
OFPTYPE_TABLE_MOD, /* OFPRAW_OFPT11_TABLE_MOD. */
|
||||
|
||||
/* Barrier messages. */
|
||||
OFPTYPE_BARRIER_REQUEST, /* OFPRAW_OFPT10_BARRIER_REQUEST.
|
||||
|
@@ -1706,6 +1706,47 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Convert 'table_id' and 'flow_miss_handling' (as described for the
|
||||
* "mod-table" command in the ovs-ofctl man page) into 'tm' for sending the
|
||||
* specified table_mod 'command' to a switch.
|
||||
*
|
||||
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
||||
* error. The caller is responsible for freeing the returned string. */
|
||||
char * WARN_UNUSED_RESULT
|
||||
parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
|
||||
const char *flow_miss_handling,
|
||||
enum ofputil_protocol *usable_protocols)
|
||||
{
|
||||
/* Table mod requires at least OF 1.1. */
|
||||
*usable_protocols = OFPUTIL_P_OF11_UP;
|
||||
|
||||
if (!strcasecmp(table_id, "all")) {
|
||||
tm->table_id = 255;
|
||||
} else {
|
||||
char *error = str_to_u8(table_id, "table_id", &tm->table_id);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(flow_miss_handling, "controller") == 0) {
|
||||
tm->config = OFPTC11_TABLE_MISS_CONTROLLER;
|
||||
} else if (strcmp(flow_miss_handling, "continue") == 0) {
|
||||
tm->config = OFPTC11_TABLE_MISS_CONTINUE;
|
||||
} else if (strcmp(flow_miss_handling, "drop") == 0) {
|
||||
tm->config = OFPTC11_TABLE_MISS_DROP;
|
||||
} else {
|
||||
return xasprintf("invalid flow_miss_handling %s", flow_miss_handling);
|
||||
}
|
||||
|
||||
if (tm->table_id == 0xfe && tm->config == OFPTC11_TABLE_MISS_CONTINUE) {
|
||||
return xstrdup("last table's flow miss handling can not be continue");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Opens file 'file_name' and reads each line as a flow_mod of the specified
|
||||
* type (one of OFPFC_*). Stores each flow_mod in '*fm', an array allocated
|
||||
* on the caller's behalf, and the number of flow_mods in '*n_fms'.
|
||||
|
@@ -31,6 +31,7 @@ struct ofputil_flow_monitor_request;
|
||||
struct ofputil_flow_stats_request;
|
||||
struct ofputil_group_mod;
|
||||
struct ofputil_meter_mod;
|
||||
struct ofputil_table_mod;
|
||||
enum ofputil_protocol;
|
||||
|
||||
char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
|
||||
@@ -41,6 +42,12 @@ char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
|
||||
uint16_t command,
|
||||
enum ofputil_protocol *usable_protocols)
|
||||
WARN_UNUSED_RESULT;
|
||||
|
||||
char *parse_ofp_table_mod(struct ofputil_table_mod *,
|
||||
const char *table_id, const char *flow_miss_handling,
|
||||
enum ofputil_protocol *usable_protocols)
|
||||
WARN_UNUSED_RESULT;
|
||||
|
||||
char *parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
|
||||
struct ofputil_flow_mod **fms, size_t *n_fms,
|
||||
enum ofputil_protocol *usable_protocols)
|
||||
|
@@ -964,6 +964,49 @@ ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ofp_print_table_miss_config(struct ds *string, const uint32_t config)
|
||||
{
|
||||
uint32_t table_miss_config = config & OFPTC11_TABLE_MISS_MASK;
|
||||
|
||||
switch (table_miss_config) {
|
||||
case OFPTC11_TABLE_MISS_CONTROLLER:
|
||||
ds_put_cstr(string, "controller\n");
|
||||
break;
|
||||
case OFPTC11_TABLE_MISS_CONTINUE:
|
||||
ds_put_cstr(string, "continue\n");
|
||||
break;
|
||||
case OFPTC11_TABLE_MISS_DROP:
|
||||
ds_put_cstr(string, "drop\n");
|
||||
break;
|
||||
default:
|
||||
ds_put_cstr(string, "Unknown\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ofp_print_table_mod(struct ds *string, const struct ofp_header *oh)
|
||||
{
|
||||
struct ofputil_table_mod pm;
|
||||
enum ofperr error;
|
||||
|
||||
error = ofputil_decode_table_mod(oh, &pm);
|
||||
if (error) {
|
||||
ofp_print_error(string, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pm.table_id == 0xff) {
|
||||
ds_put_cstr(string, " table_id: ALL_TABLES");
|
||||
} else {
|
||||
ds_put_format(string, " table_id=%"PRIu8, pm.table_id);
|
||||
}
|
||||
|
||||
ds_put_cstr(string, ", flow_miss_config=");
|
||||
ofp_print_table_miss_config(string, pm.config);
|
||||
}
|
||||
|
||||
static void
|
||||
ofp_print_meter_flags(struct ds *s, uint16_t flags)
|
||||
{
|
||||
@@ -2431,6 +2474,10 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
|
||||
ofp_print_port_mod(string, oh);
|
||||
break;
|
||||
|
||||
case OFPTYPE_TABLE_MOD:
|
||||
ofp_print_table_mod(string, oh);
|
||||
break;
|
||||
|
||||
case OFPTYPE_METER_MOD:
|
||||
ofp_print_meter_mod(string, oh);
|
||||
break;
|
||||
|
@@ -3660,7 +3660,7 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps,
|
||||
ofpmsg_update_length(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/* ofputil_port_mod */
|
||||
|
||||
/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
|
||||
@@ -3742,7 +3742,66 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
|
||||
opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/* ofputil_table_mod */
|
||||
|
||||
/* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in
|
||||
* '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */
|
||||
enum ofperr
|
||||
ofputil_decode_table_mod(const struct ofp_header *oh,
|
||||
struct ofputil_table_mod *pm)
|
||||
{
|
||||
enum ofpraw raw;
|
||||
struct ofpbuf b;
|
||||
|
||||
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
||||
raw = ofpraw_pull_assert(&b);
|
||||
|
||||
if (raw == OFPRAW_OFPT11_TABLE_MOD) {
|
||||
const struct ofp11_table_mod *otm = b.data;
|
||||
|
||||
pm->table_id = otm->table_id;
|
||||
pm->config = ntohl(otm->config);
|
||||
} else {
|
||||
return OFPERR_OFPBRC_BAD_TYPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Converts the abstract form of a "table mod" message in '*pm' into an OpenFlow
|
||||
* message suitable for 'protocol', and returns that encoded form in a buffer
|
||||
* owned by the caller. */
|
||||
struct ofpbuf *
|
||||
ofputil_encode_table_mod(const struct ofputil_table_mod *pm,
|
||||
enum ofputil_protocol protocol)
|
||||
{
|
||||
enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
|
||||
struct ofpbuf *b;
|
||||
|
||||
switch (ofp_version) {
|
||||
case OFP10_VERSION: {
|
||||
ovs_fatal(0, "table mod needs OpenFlow 1.1 or later "
|
||||
"(\'-O OpenFlow11\')");
|
||||
break;
|
||||
}
|
||||
case OFP11_VERSION:
|
||||
case OFP12_VERSION:
|
||||
case OFP13_VERSION: {
|
||||
struct ofp11_table_mod *otm;
|
||||
|
||||
b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0);
|
||||
otm = ofpbuf_put_zeros(b, sizeof *otm);
|
||||
otm->table_id = pm->table_id;
|
||||
otm->config = htonl(pm->config);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
@@ -565,6 +565,17 @@ enum ofperr ofputil_decode_port_mod(const struct ofp_header *,
|
||||
struct ofpbuf *ofputil_encode_port_mod(const struct ofputil_port_mod *,
|
||||
enum ofputil_protocol);
|
||||
|
||||
/* Abstract ofp_table_mod. */
|
||||
struct ofputil_table_mod {
|
||||
uint8_t table_id; /* ID of the table, 0xff indicates all tables. */
|
||||
uint32_t config;
|
||||
};
|
||||
|
||||
enum ofperr ofputil_decode_table_mod(const struct ofp_header *,
|
||||
struct ofputil_table_mod *);
|
||||
struct ofpbuf *ofputil_encode_table_mod(const struct ofputil_table_mod *,
|
||||
enum ofputil_protocol);
|
||||
|
||||
/* Meter band configuration for all supported band types. */
|
||||
struct ofputil_meter_band {
|
||||
uint16_t type;
|
||||
|
@@ -1156,6 +1156,7 @@ is_admitted_msg(const struct ofpbuf *b)
|
||||
case OFPTYPE_FLOW_MOD:
|
||||
case OFPTYPE_GROUP_MOD:
|
||||
case OFPTYPE_PORT_MOD:
|
||||
case OFPTYPE_TABLE_MOD:
|
||||
case OFPTYPE_METER_MOD:
|
||||
case OFPTYPE_BARRIER_REQUEST:
|
||||
case OFPTYPE_BARRIER_REPLY:
|
||||
|
@@ -5144,6 +5144,26 @@ handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh)
|
||||
}
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
handle_table_mod(struct ofconn *ofconn, const struct ofp_header *oh)
|
||||
{
|
||||
struct ofputil_table_mod tm;
|
||||
enum ofperr error;
|
||||
|
||||
error = reject_slave_controller(ofconn);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
error = ofputil_decode_table_mod(oh, &tm);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
/* XXX Actual table mod support is not implemented yet. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
|
||||
{
|
||||
@@ -5182,6 +5202,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
|
||||
case OFPTYPE_GROUP_MOD:
|
||||
return handle_group_mod(ofconn, oh);
|
||||
|
||||
case OFPTYPE_TABLE_MOD:
|
||||
return handle_table_mod(ofconn, oh);
|
||||
|
||||
case OFPTYPE_METER_MOD:
|
||||
return handle_meter_mod(ofconn, oh);
|
||||
|
||||
|
@@ -978,6 +978,33 @@ OFPT_PORT_MOD (OF1.3) (xid=0x3):port: 3: addr:50:54:00:00:00:01
|
||||
])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([OFPT_TABLE_MOD - OF1.1])
|
||||
AT_KEYWORDS([ofp-print])
|
||||
AT_CHECK([ovs-ofctl ofp-print "\
|
||||
02 11 00 10 00 00 00 02 02 00 00 00 00 00 00 02 \
|
||||
" 3], [0], [dnl
|
||||
OFPT_TABLE_MOD (OF1.1) (xid=0x2): table_id=2, flow_miss_config=drop
|
||||
])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([OFPT_TABLE_MOD - OF1.2])
|
||||
AT_KEYWORDS([ofp-print])
|
||||
AT_CHECK([ovs-ofctl ofp-print "\
|
||||
03 11 00 10 00 00 00 02 02 00 00 00 00 00 00 01 \
|
||||
" 3], [0], [dnl
|
||||
OFPT_TABLE_MOD (OF1.2) (xid=0x2): table_id=2, flow_miss_config=continue
|
||||
])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([OFPT_TABLE_MOD - OF1.3])
|
||||
AT_KEYWORDS([ofp-print])
|
||||
AT_CHECK([ovs-ofctl ofp-print "\
|
||||
04 11 00 10 00 00 00 02 02 00 00 00 00 00 00 00 \
|
||||
" 3], [0], [dnl
|
||||
OFPT_TABLE_MOD (OF1.3) (xid=0x2): table_id=2, flow_miss_config=controller
|
||||
])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([OFPST_DESC request])
|
||||
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
|
||||
AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100000000"], [0], [dnl
|
||||
|
@@ -213,6 +213,31 @@ Prints to the console statistics for the specified \fIgroups in the
|
||||
groups are printed. See \fBGroup Syntax\fR, below, for the syntax of
|
||||
\fIgroups\fR.
|
||||
.
|
||||
.IP "\fBmod\-table \fIswitch\fR \fItable_id\fR \fIflow_miss_handling\fR"
|
||||
An OpenFlow 1.0 switch looks up each packet that arrives at the switch
|
||||
in table 0, then in table 1 if there is no match in table 0, then in
|
||||
table 2, and so on until the packet finds a match in some table.
|
||||
Finally, if no match was found, the switch sends the packet to the
|
||||
controller
|
||||
.IP
|
||||
OpenFlow 1.1 and later offer more flexibility. This command
|
||||
configures the flow table miss handling configuration for table
|
||||
\fItable_id\fR in \fIswitch\fR. \fItable_id\fR may be an OpenFlow
|
||||
table number between 0 and 254, inclusive, or the keyword \fBALL\fR to
|
||||
modify all tables. \fIflow_miss_handling\fR may be any one of the
|
||||
following:
|
||||
.RS
|
||||
.IP \fBdrop\fR
|
||||
Drop the packet.
|
||||
.IP \fBcontinue\fR
|
||||
Continue to the next table in the pipeline. (This is how an OpenFlow
|
||||
1.0 switch always handles packets that do not match any flow, in
|
||||
tables other than the last one.)
|
||||
.IP \fBcontroller\fR
|
||||
Send to controller. (This is how an OpenFlow 1.0 switch always
|
||||
handles packets that do not match any flow in the last table.)
|
||||
.RE
|
||||
.
|
||||
.SS "OpenFlow Switch Flow Table Commands"
|
||||
.
|
||||
These commands manage the flow table in an OpenFlow switch. In each
|
||||
|
@@ -281,6 +281,7 @@ usage(void)
|
||||
" dump-desc SWITCH print switch description\n"
|
||||
" dump-tables SWITCH print table stats\n"
|
||||
" mod-port SWITCH IFACE ACT modify port behavior\n"
|
||||
" mod-table SWITCH MOD modify flow table behavior\n"
|
||||
" get-frags SWITCH print fragment handling behavior\n"
|
||||
" set-frags SWITCH FRAG_MODE set fragment handling behavior\n"
|
||||
" dump-ports SWITCH [PORT] print port statistics\n"
|
||||
@@ -1663,6 +1664,42 @@ found:
|
||||
vconn_close(vconn);
|
||||
}
|
||||
|
||||
static void
|
||||
ofctl_mod_table(int argc OVS_UNUSED, char *argv[])
|
||||
{
|
||||
enum ofputil_protocol protocol, usable_protocols;
|
||||
struct ofputil_table_mod tm;
|
||||
struct vconn *vconn;
|
||||
char *error;
|
||||
int i;
|
||||
|
||||
error = parse_ofp_table_mod(&tm, argv[2], argv[3], &usable_protocols);
|
||||
if (error) {
|
||||
ovs_fatal(0, "%s", error);
|
||||
}
|
||||
|
||||
protocol = open_vconn(argv[1], &vconn);
|
||||
if (!(protocol & usable_protocols)) {
|
||||
for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
|
||||
enum ofputil_protocol f = 1 << i;
|
||||
if (f != protocol
|
||||
&& f & usable_protocols
|
||||
&& try_set_protocol(vconn, f, &protocol)) {
|
||||
protocol = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(protocol & usable_protocols)) {
|
||||
char *usable_s = ofputil_protocols_to_string(usable_protocols);
|
||||
ovs_fatal(0, "Switch does not support table mod message(%s)", usable_s);
|
||||
}
|
||||
|
||||
transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol));
|
||||
vconn_close(vconn);
|
||||
}
|
||||
|
||||
static void
|
||||
ofctl_get_frags(int argc OVS_UNUSED, char *argv[])
|
||||
{
|
||||
@@ -3152,6 +3189,7 @@ static const struct command all_commands[] = {
|
||||
{ "dump-ports", 1, 2, ofctl_dump_ports },
|
||||
{ "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
|
||||
{ "mod-port", 3, 3, ofctl_mod_port },
|
||||
{ "mod-table", 3, 3, ofctl_mod_table },
|
||||
{ "get-frags", 1, 1, ofctl_get_frags },
|
||||
{ "set-frags", 2, 2, ofctl_set_frags },
|
||||
{ "ofp-parse", 1, 1, ofctl_ofp_parse },
|
||||
|
Reference in New Issue
Block a user