From 918f2b827076f3af4ae6c3f0c20b93233947cf4c Mon Sep 17 00:00:00 2001 From: Andy Zhou Date: Sat, 7 Sep 2013 03:02:32 -0700 Subject: [PATCH] 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 Signed-off-by: Ben Pfaff --- include/openflow/openflow-1.1.h | 4 +-- lib/learning-switch.c | 1 + lib/ofp-msgs.h | 4 +++ lib/ofp-parse.c | 41 ++++++++++++++++++++++ lib/ofp-parse.h | 7 ++++ lib/ofp-print.c | 47 +++++++++++++++++++++++++ lib/ofp-util.c | 61 ++++++++++++++++++++++++++++++++- lib/ofp-util.h | 11 ++++++ lib/rconn.c | 1 + ofproto/ofproto.c | 23 +++++++++++++ tests/ofp-print.at | 27 +++++++++++++++ utilities/ovs-ofctl.8.in | 25 ++++++++++++++ utilities/ovs-ofctl.c | 38 ++++++++++++++++++++ 13 files changed, 287 insertions(+), 3 deletions(-) diff --git a/include/openflow/openflow-1.1.h b/include/openflow/openflow-1.1.h index a2309b1f8..de88ffaae 100644 --- a/include/openflow/openflow-1.1.h +++ b/include/openflow/openflow-1.1.h @@ -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 }; diff --git a/lib/learning-switch.c b/lib/learning-switch.c index c587930ba..76ec3e13e 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -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: diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h index 47256de53..bfc84f3e6 100644 --- a/lib/ofp-msgs.h +++ b/lib/ofp-msgs.h @@ -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. diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index ad3981c57..a61beb99e 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -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'. diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h index 1c3228abd..47ba036ec 100644 --- a/lib/ofp-parse.h +++ b/lib/ofp-parse.h @@ -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) diff --git a/lib/ofp-print.c b/lib/ofp-print.c index b280a3738..c0553afbb 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -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; diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 8ac918681..23c7136bc 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -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(); } diff --git a/lib/ofp-util.h b/lib/ofp-util.h index eae8ed597..0ca483c52 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -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; diff --git a/lib/rconn.c b/lib/rconn.c index 17cbd82d2..64cc6d067 100644 --- a/lib/rconn.c +++ b/lib/rconn.c @@ -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: diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 6900e99fd..4d33de764 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -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); diff --git a/tests/ofp-print.at b/tests/ofp-print.at index 4a627936d..52b84ef8e 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -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 diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 4859ab9b0..3141eff0c 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -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 diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 929475232..95bf1bfbb 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -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 }, -- 2.43.0