From 980904823303ef02af605e62a30c9bebda25f1ef Mon Sep 17 00:00:00 2001 From: Niti Rohilla Date: Thu, 23 Jul 2015 17:05:44 +0530 Subject: [PATCH] ofproto: Implement OF1.4 Set/Get asynchronous configuration messages. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This patch adds support for Openflow1.4 set/get asynchronous configuration messages. OpenVSwitch already supports set/get asynchronous configuration messages for Openflow1.3. In this patch OFPT_SET_ASYNC_CONFIG message allows the controllers to set the configuration for OFPT_ROLE_STATUS, OFPT_TABLE_STATUS and OFPT_REQUESTFORWARD in addition to the Openflow1.3 messages. In a OFPT_SET_ASYNC, only the properties that shall be changed need to be included, properties that are omitted from the message are unchanged. The OFPT_GET_ASYNC_CONFIG is used to query the asynchronous configuration of switch. In a OFPT_GET_ASYNC_REPLY message, all properties must be included. According to Openflow1.4 the initial configuration shall be: - In the “master” or “equal” role, enable all OFPT_PACKET_IN messages, except those with reason OFPR_INVALID_TTL, enable all OFPT_PORT_STATUS and OFPT_FLOW_REMOVED messages, and disable all OFPT_ROLE_STATUS, OFPT_TABLE_STATUS and OFPT_REQUESTFORWARD messages. - In the “slave” role, enable all OFPT_PORT_STATUS messages and disable all OFPT_PACKET_IN, OFPT_FLOW_REMOVED, OFPT_ROLE_STATUS, OFPT_TABLE_STATUS and OFPT_REQUESTFORWARD messages. Signed-off-by: Niti Rohilla Signed-off-by: Ben Pfaff --- AUTHORS | 1 + DESIGN.md | 16 +++ NEWS | 3 + include/openflow/openflow-1.4.h | 12 ++ lib/ofp-msgs.h | 21 ++- lib/ofp-print.c | 238 ++++++++++++++++++++++++++------ lib/ofp-util.c | 207 +++++++++++++++++++++++++++ lib/ofp-util.h | 20 +++ ofproto/connmgr.c | 33 ++++- ofproto/connmgr.h | 8 -- ofproto/ofproto.c | 25 +--- tests/ofp-print.at | 27 ++++ tests/ofproto.at | 122 ++++++++++++++++ 13 files changed, 653 insertions(+), 80 deletions(-) diff --git a/AUTHORS b/AUTHORS index de6e055ee..0ce19f0e8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -130,6 +130,7 @@ Natasha Gude natasha@nicira.com Neil McKee neil.mckee@inmon.com Neil Zhu zhuj@centecnetworks.com Nithin Raju nithin@vmware.com +Niti Rohilla niti.rohilla@tcs.com Padmanabhan Krishnan kprad1@yahoo.com Paraneetharan Chandrasekaran paraneetharanc@gmail.com Paul Fazzone pfazzone@nicira.com diff --git a/DESIGN.md b/DESIGN.md index 38413d7d5..58826d38c 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -61,11 +61,27 @@ sent, an entry labeled "---" means that the message is suppressed. OFPRR_IDLE_TIMEOUT yes --- OFPRR_HARD_TIMEOUT yes --- OFPRR_DELETE yes --- + OFPRR_GROUP_DELETE (OF1.4+) yes --- + OFPRR_METER_DELETE (OF1.4+) yes --- + OFPRR_EVICTION (OF1.4+) yes --- OFPT_PORT_STATUS OFPPR_ADD yes yes OFPPR_DELETE yes yes OFPPR_MODIFY yes yes + + OFPT_ROLE_REQUEST / OFPT_ROLE_REPLY (OF1.4+) + OFPCRR_MASTER_REQUEST --- --- + OFPCRR_CONFIG --- --- + OFPCRR_EXPERIMENTER --- --- + + OFPT_TABLE_STATUS (OF1.4+) + OFPTR_VACANCY_DOWN --- --- + OFPTR_VACANCY_UP --- --- + + OFPT_REQUESTFORWARD (OF1.4+) + OFPRFR_GROUP_MOD --- --- + OFPRFR_METER_MOD --- --- ``` The NXT_SET_ASYNC_CONFIG message directly sets all of the values in diff --git a/NEWS b/NEWS index c2f2a5d69..f16df706d 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,9 @@ Post-v2.4.0 - Support Multicast Listener Discovery (MLDv1 and MLDv2). - Add 'symmetric_l3l4' and 'symmetric_l3l4+udp' hash functions. - sFlow agent now reports tunnel and MPLS structures. + - Added OpenFlow 1.4+ OFPT_SET_ASYNC_CONFIG and OFPT_GET_ASYNC_CONFIG + that allows controllers to have more precise control over OpenFlow1.4 + asynchronous messages. v2.4.0 - xx xxx xxxx diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h index 567aaaea9..37eef4a42 100644 --- a/include/openflow/openflow-1.4.h +++ b/include/openflow/openflow-1.4.h @@ -130,6 +130,12 @@ enum ofp14_table_mod_prop_eviction_flag { OFPTMPEF14_LIFETIME = 1 << 2, /* Using flow entry lifetime. */ }; +/* What changed about the table */ +enum ofp14_table_reason { + OFPTR_VACANCY_DOWN = 3, /* Vacancy down threshold event. */ + OFPTR_VACANCY_UP = 4, /* Vacancy up threshold event. */ +}; + struct ofp14_table_mod_prop_eviction { ovs_be16 type; /* OFPTMPT14_EVICTION. */ ovs_be16 length; /* Length in bytes of this property. */ @@ -249,6 +255,12 @@ struct ofp14_async_config { }; OFP_ASSERT(sizeof(struct ofp14_async_config) == 8); +/* Request forward reason */ +enum ofp14_requestforward_reason { + OFPRFR_GROUP_MOD = 0, /* Forward group mod requests. */ + OFPRFR_METER_MOD = 1, /* Forward meter mod requests. */ +}; + /* Async Config property types. * Low order bit cleared indicates a property for the slave role. * Low order bit set indicates a property for the master/equal role. diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h index 3d9fedfab..1358b21cb 100644 --- a/lib/ofp-msgs.h +++ b/lib/ofp-msgs.h @@ -226,14 +226,20 @@ enum ofpraw { /* NXT 1.0+ (11): struct nx_role_request. */ OFPRAW_NXT_ROLE_REPLY, - /* OFPT 1.3+ (26): void. */ + /* OFPT 1.3 (26): void. */ OFPRAW_OFPT13_GET_ASYNC_REQUEST, - /* OFPT 1.3+ (27): struct ofp13_async_config. */ + /* OFPT 1.4+ (26): void. */ + OFPRAW_OFPT14_GET_ASYNC_REQUEST, + /* OFPT 1.3 (27): struct ofp13_async_config. */ OFPRAW_OFPT13_GET_ASYNC_REPLY, - /* OFPT 1.3+ (28): struct ofp13_async_config. */ + /* OFPT 1.4+ (27): struct ofp14_async_config, uint8_t[8][]. */ + OFPRAW_OFPT14_GET_ASYNC_REPLY, + /* OFPT 1.3 (28): struct ofp13_async_config. */ OFPRAW_OFPT13_SET_ASYNC, /* NXT 1.0+ (19): struct nx_async_config. */ OFPRAW_NXT_SET_ASYNC_CONFIG, + /* OFPT 1.4+ (28): struct ofp14_async_config, uint8_t[8][]. */ + OFPRAW_OFPT14_SET_ASYNC, /* OFPT 1.3+ (29): struct ofp13_meter_mod, uint8_t[8][]. */ OFPRAW_OFPT13_METER_MOD, @@ -539,10 +545,13 @@ enum ofptype { * OFPRAW_NXT_ROLE_REPLY. */ /* Asynchronous message configuration. */ - OFPTYPE_GET_ASYNC_REQUEST, /* OFPRAW_OFPT13_GET_ASYNC_REQUEST. */ - OFPTYPE_GET_ASYNC_REPLY, /* OFPRAW_OFPT13_GET_ASYNC_REPLY. */ + OFPTYPE_GET_ASYNC_REQUEST, /* OFPRAW_OFPT13_GET_ASYNC_REQUEST. + * OFPRAW_OFPT14_GET_ASYNC_REQUEST. */ + OFPTYPE_GET_ASYNC_REPLY, /* OFPRAW_OFPT13_GET_ASYNC_REPLY. + * OFPRAW_OFPT14_GET_ASYNC_REPLY. */ OFPTYPE_SET_ASYNC_CONFIG, /* OFPRAW_NXT_SET_ASYNC_CONFIG. - * OFPRAW_OFPT13_SET_ASYNC. */ + * OFPRAW_OFPT13_SET_ASYNC. + * OFPRAW_OFPT14_SET_ASYNC. */ /* Meters and rate limiters configuration messages. */ OFPTYPE_METER_MOD, /* OFPRAW_OFPT13_METER_MOD. */ diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 4603bb7cc..6db32d14b 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -1903,64 +1903,220 @@ ofp_port_reason_to_string(enum ofp_port_reason reason, } } +/* Returns a string form of 'reason'. The return value is either a statically + * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. + * 'bufsize' should be at least OFP_ASYNC_CONFIG_REASON_BUFSIZE. */ +static const char* +ofp_role_reason_to_string(enum ofp14_controller_role_reason reason, + char *reasonbuf, size_t bufsize) +{ + switch (reason) { + case OFPCRR_MASTER_REQUEST: + return "master_request"; + + case OFPCRR_CONFIG: + return "configuration_changed"; + + case OFPCRR_EXPERIMENTER: + return "experimenter_data_changed"; + + default: + snprintf(reasonbuf, bufsize, "%d", (int) reason); + return reasonbuf; + } +} + +/* Returns a string form of 'reason'. The return value is either a statically + * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. + * 'bufsize' should be at least OFP_ASYNC_CONFIG_REASON_BUFSIZE. */ +static const char* +ofp_table_reason_to_string(enum ofp14_table_reason reason, + char *reasonbuf, size_t bufsize) +{ + switch (reason) { + case OFPTR_VACANCY_DOWN: + return "vacancy_down"; + + case OFPTR_VACANCY_UP: + return "vacancy_up"; + + default: + snprintf(reasonbuf, bufsize, "%d", (int) reason); + return reasonbuf; + } +} + +/* Returns a string form of 'reason'. The return value is either a statically + * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. + * 'bufsize' should be at least OFP_ASYNC_CONFIG_REASON_BUFSIZE. */ +static const char* +ofp_requestforward_reason_to_string(enum ofp14_requestforward_reason reason, + char *reasonbuf, size_t bufsize) +{ + switch (reason) { + case OFPRFR_GROUP_MOD: + return "group_mod_request"; + + case OFPRFR_METER_MOD: + return "meter_mod_request"; + + default: + snprintf(reasonbuf, bufsize, "%d", (int) reason); + return reasonbuf; + } +} + +static const char * +ofp_async_config_reason_to_string(uint32_t reason, + enum ofputil_async_msg_type type, + char *reasonbuf, size_t bufsize) +{ + switch (type) { + case OAM_PACKET_IN: + return ofputil_packet_in_reason_to_string(reason, reasonbuf, bufsize); + + case OAM_PORT_STATUS: + return ofp_port_reason_to_string(reason, reasonbuf, bufsize); + + case OAM_FLOW_REMOVED: + return ofp_flow_removed_reason_to_string(reason, reasonbuf, bufsize); + + case OAM_ROLE_STATUS: + return ofp_role_reason_to_string(reason, reasonbuf, bufsize); + + case OAM_TABLE_STATUS: + return ofp_table_reason_to_string(reason, reasonbuf, bufsize); + + case OAM_REQUESTFORWARD: + return ofp_requestforward_reason_to_string(reason, reasonbuf, bufsize); + + case OAM_N_TYPES: + default: + return "Unknown asynchronous configuration message type"; + } +} + + +#define OFP_ASYNC_CONFIG_REASON_BUFSIZE (INT_STRLEN(int) + 1) static void ofp_print_nxt_set_async_config(struct ds *string, - const struct nx_async_config *nac) + const struct ofp_header *oh) { - int i; + int i, j; + enum ofpraw raw; - for (i = 0; i < 2; i++) { - int j; + ofpraw_decode(&raw, oh); + + if (raw == OFPRAW_OFPT13_SET_ASYNC || + raw == OFPRAW_NXT_SET_ASYNC_CONFIG || + raw == OFPRAW_OFPT13_GET_ASYNC_REPLY) { + const struct nx_async_config *nac = ofpmsg_body(oh); + + for (i = 0; i < 2; i++) { - ds_put_format(string, "\n %s:\n", i == 0 ? "master" : "slave"); + ds_put_format(string, "\n %s:\n", i == 0 ? "master" : "slave"); - ds_put_cstr(string, " PACKET_IN:"); - for (j = 0; j < 32; j++) { - if (nac->packet_in_mask[i] & htonl(1u << j)) { - char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; - const char *reason; + ds_put_cstr(string, " PACKET_IN:"); + for (j = 0; j < 32; j++) { + if (nac->packet_in_mask[i] & htonl(1u << j)) { + char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; + const char *reason; - reason = ofputil_packet_in_reason_to_string(j, reasonbuf, + reason = ofputil_packet_in_reason_to_string(j, reasonbuf, sizeof reasonbuf); - ds_put_format(string, " %s", reason); + ds_put_format(string, " %s", reason); + } } - } - if (!nac->packet_in_mask[i]) { - ds_put_cstr(string, " (off)"); - } - ds_put_char(string, '\n'); + if (!nac->packet_in_mask[i]) { + ds_put_cstr(string, " (off)"); + } + ds_put_char(string, '\n'); - ds_put_cstr(string, " PORT_STATUS:"); - for (j = 0; j < 32; j++) { - if (nac->port_status_mask[i] & htonl(1u << j)) { - char reasonbuf[OFP_PORT_REASON_BUFSIZE]; - const char *reason; + ds_put_cstr(string, " PORT_STATUS:"); + for (j = 0; j < 32; j++) { + if (nac->port_status_mask[i] & htonl(1u << j)) { + char reasonbuf[OFP_PORT_REASON_BUFSIZE]; + const char *reason; - reason = ofp_port_reason_to_string(j, reasonbuf, - sizeof reasonbuf); - ds_put_format(string, " %s", reason); + reason = ofp_port_reason_to_string(j, reasonbuf, + sizeof reasonbuf); + ds_put_format(string, " %s", reason); + } } - } - if (!nac->port_status_mask[i]) { - ds_put_cstr(string, " (off)"); - } - ds_put_char(string, '\n'); + if (!nac->port_status_mask[i]) { + ds_put_cstr(string, " (off)"); + } + ds_put_char(string, '\n'); - ds_put_cstr(string, " FLOW_REMOVED:"); - for (j = 0; j < 32; j++) { - if (nac->flow_removed_mask[i] & htonl(1u << j)) { - char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE]; - const char *reason; + ds_put_cstr(string, " FLOW_REMOVED:"); + for (j = 0; j < 32; j++) { + if (nac->flow_removed_mask[i] & htonl(1u << j)) { + char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE]; + const char *reason; - reason = ofp_flow_removed_reason_to_string(j, reasonbuf, + reason = ofp_flow_removed_reason_to_string(j, reasonbuf, sizeof reasonbuf); - ds_put_format(string, " %s", reason); + ds_put_format(string, " %s", reason); + } } + if (!nac->flow_removed_mask[i]) { + ds_put_cstr(string, " (off)"); + } + ds_put_char(string, '\n'); } - if (!nac->flow_removed_mask[i]) { - ds_put_cstr(string, " (off)"); + } else if (raw == OFPRAW_OFPT14_SET_ASYNC || + raw == OFPRAW_OFPT14_GET_ASYNC_REPLY) { + uint32_t role[2][OAM_N_TYPES] = {{0}}; + uint32_t type; + + ofputil_decode_set_async_config(oh, role[0], role[1], true); + for (i = 0; i < 2; i++) { + + ds_put_format(string, "\n %s:\n", i == 0 ? "master" : "slave"); + for (type = 0; type < OAM_N_TYPES; type++) { + switch (type) { + case OAM_PACKET_IN: + ds_put_cstr(string, " PACKET_IN:"); + break; + + case OAM_PORT_STATUS: + ds_put_cstr(string, " PORT_STATUS:"); + break; + + case OAM_FLOW_REMOVED: + ds_put_cstr(string, " FLOW_REMOVED:"); + break; + + case OAM_ROLE_STATUS: + ds_put_cstr(string, " ROLE_STATUS:"); + break; + + case OAM_TABLE_STATUS: + ds_put_cstr(string, " TABLE_STATUS:"); + break; + + case OAM_REQUESTFORWARD: + ds_put_cstr(string, " REQUESTFORWARD:"); + break; + } + + for (j = 0; j < 32; j++) { + if (role[i][type] & (1u << j)) { + char reasonbuf[OFP_ASYNC_CONFIG_REASON_BUFSIZE]; + const char *reason; + + reason = ofp_async_config_reason_to_string(j, type, + reasonbuf, + sizeof reasonbuf); + ds_put_format(string, " %s", reason); + } + } + if (!role[i][type]) { + ds_put_cstr(string, " (off)"); + } + ds_put_char(string, '\n'); + } } - ds_put_char(string, '\n'); } } @@ -3040,7 +3196,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw, case OFPTYPE_GET_ASYNC_REPLY: case OFPTYPE_SET_ASYNC_CONFIG: - ofp_print_nxt_set_async_config(string, ofpmsg_body(oh)); + ofp_print_nxt_set_async_config(string, oh); break; case OFPTYPE_GET_ASYNC_REQUEST: break; diff --git a/lib/ofp-util.c b/lib/ofp-util.c index cdb85532b..9996e84f2 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -9289,3 +9289,210 @@ ofputil_uninit_geneve_table(struct ovs_list *mappings) free(map); } } + +/* Decodes the OpenFlow "set async config" request and "get async config + * reply" message in '*oh' into an abstract form in 'master' and 'slave'. + * + * If 'loose' is true, this function ignores properties and values that it does + * not understand, as a controller would want to do when interpreting + * capabilities provided by a switch. If 'loose' is false, this function + * treats unknown properties and values as an error, as a switch would want to + * do when interpreting a configuration request made by a controller. + * + * Returns 0 if successful, otherwise an OFPERR_* value. */ +enum ofperr +ofputil_decode_set_async_config(const struct ofp_header *oh, + uint32_t master[OAM_N_TYPES], + uint32_t slave[OAM_N_TYPES], + bool loose) +{ + enum ofpraw raw; + struct ofpbuf b; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + raw = ofpraw_pull_assert(&b); + + if (raw == OFPRAW_OFPT13_SET_ASYNC || + raw == OFPRAW_NXT_SET_ASYNC_CONFIG || + raw == OFPRAW_OFPT13_GET_ASYNC_REPLY) { + const struct nx_async_config *msg = ofpmsg_body(oh); + + master[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[0]); + master[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[0]); + master[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[0]); + + slave[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[1]); + slave[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[1]); + slave[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[1]); + + } else if (raw == OFPRAW_OFPT14_SET_ASYNC || + raw == OFPRAW_OFPT14_GET_ASYNC_REPLY) { + + while (b.size > 0) { + struct ofp14_async_config_prop_reasons *msg; + struct ofpbuf property; + enum ofperr error; + uint16_t type; + + error = ofputil_pull_property(&b, &property, &type); + if (error) { + return error; + } + + msg = property.data; + + if (property.size != sizeof *msg) { + return OFPERR_OFPBRC_BAD_LEN; + } + + switch (type) { + case OFPACPT_PACKET_IN_SLAVE: + slave[OAM_PACKET_IN] = ntohl(msg->mask); + break; + + case OFPACPT_PACKET_IN_MASTER: + master[OAM_PACKET_IN] = ntohl(msg->mask); + break; + + case OFPACPT_PORT_STATUS_SLAVE: + slave[OAM_PORT_STATUS] = ntohl(msg->mask); + break; + + case OFPACPT_PORT_STATUS_MASTER: + master[OAM_PORT_STATUS] = ntohl(msg->mask); + break; + + case OFPACPT_FLOW_REMOVED_SLAVE: + slave[OAM_FLOW_REMOVED] = ntohl(msg->mask); + break; + + case OFPACPT_FLOW_REMOVED_MASTER: + master[OAM_FLOW_REMOVED] = ntohl(msg->mask); + break; + + case OFPACPT_ROLE_STATUS_SLAVE: + slave[OAM_ROLE_STATUS] = ntohl(msg->mask); + break; + + case OFPACPT_ROLE_STATUS_MASTER: + master[OAM_ROLE_STATUS] = ntohl(msg->mask); + break; + + case OFPACPT_TABLE_STATUS_SLAVE: + slave[OAM_TABLE_STATUS] = ntohl(msg->mask); + break; + + case OFPACPT_TABLE_STATUS_MASTER: + master[OAM_TABLE_STATUS] = ntohl(msg->mask); + break; + + case OFPACPT_REQUESTFORWARD_SLAVE: + slave[OAM_REQUESTFORWARD] = ntohl(msg->mask); + break; + + case OFPACPT_REQUESTFORWARD_MASTER: + master[OAM_REQUESTFORWARD] = ntohl(msg->mask); + break; + + default: + error = loose ? 0 : OFPERR_OFPBPC_BAD_TYPE; + break; + } + if (error) { + return error; + } + } + } else { + return OFPERR_OFPBRC_BAD_VERSION; + } + return 0; +} + +/* Append all asynchronous configuration properties in GET_ASYNC_REPLY + * message, describing if various set of asynchronous messages are enabled + * or not. */ +static enum ofperr +ofputil_get_async_reply(struct ofpbuf *buf, const uint32_t master_mask, + const uint32_t slave_mask, const uint32_t type) +{ + int role; + + for (role = 0; role < 2; role++) { + struct ofp14_async_config_prop_reasons *msg; + + msg = ofpbuf_put_zeros(buf, sizeof *msg); + + switch (type) { + case OAM_PACKET_IN: + msg->type = (role ? htons(OFPACPT_PACKET_IN_SLAVE) + : htons(OFPACPT_PACKET_IN_MASTER)); + break; + + case OAM_PORT_STATUS: + msg->type = (role ? htons(OFPACPT_PORT_STATUS_SLAVE) + : htons(OFPACPT_PORT_STATUS_MASTER)); + break; + + case OAM_FLOW_REMOVED: + msg->type = (role ? htons(OFPACPT_FLOW_REMOVED_SLAVE) + : htons(OFPACPT_FLOW_REMOVED_MASTER)); + break; + + case OAM_ROLE_STATUS: + msg->type = (role ? htons(OFPACPT_ROLE_STATUS_SLAVE) + : htons(OFPACPT_ROLE_STATUS_MASTER)); + break; + + case OAM_TABLE_STATUS: + msg->type = (role ? htons(OFPACPT_TABLE_STATUS_SLAVE) + : htons(OFPACPT_TABLE_STATUS_MASTER)); + break; + + case OAM_REQUESTFORWARD: + msg->type = (role ? htons(OFPACPT_REQUESTFORWARD_SLAVE) + : htons(OFPACPT_REQUESTFORWARD_MASTER)); + break; + + default: + return OFPERR_OFPBRC_BAD_TYPE; + } + msg->length = htons(sizeof *msg); + msg->mask = (role ? htonl(slave_mask) : htonl(master_mask)); + } + + return 0; +} + +/* Returns a OpenFlow message that encodes 'asynchronous configuration' properly + * as a reply to get async config request. */ +struct ofpbuf * +ofputil_encode_get_async_config(const struct ofp_header *oh, + uint32_t master[OAM_N_TYPES], + uint32_t slave[OAM_N_TYPES]) +{ + struct ofpbuf *buf; + uint32_t type; + + buf = ofpraw_alloc_reply((oh->version < OFP14_VERSION + ? OFPRAW_OFPT13_GET_ASYNC_REPLY + : OFPRAW_OFPT14_GET_ASYNC_REPLY), oh, 0); + + if (oh->version < OFP14_VERSION) { + struct nx_async_config *msg; + msg = ofpbuf_put_zeros(buf, sizeof *msg); + + msg->packet_in_mask[0] = htonl(master[OAM_PACKET_IN]); + msg->port_status_mask[0] = htonl(master[OAM_PORT_STATUS]); + msg->flow_removed_mask[0] = htonl(master[OAM_FLOW_REMOVED]); + + msg->packet_in_mask[1] = htonl(slave[OAM_PACKET_IN]); + msg->port_status_mask[1] = htonl(slave[OAM_PORT_STATUS]); + msg->flow_removed_mask[1] = htonl(slave[OAM_FLOW_REMOVED]); + } else if (oh->version == OFP14_VERSION) { + for (type = 0; type < OAM_N_TYPES; type++) { + ofputil_get_async_reply(buf, master[type], slave[type], type); + } + } + + return buf; +} diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 431f165a8..2668e752c 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -1219,4 +1219,24 @@ enum ofperr ofputil_decode_geneve_table_reply(const struct ofp_header *, struct ofputil_geneve_table_reply *); void ofputil_uninit_geneve_table(struct ovs_list *mappings); +enum ofputil_async_msg_type { + OAM_PACKET_IN, /* OFPT_PACKET_IN or NXT_PACKET_IN. */ + OAM_PORT_STATUS, /* OFPT_PORT_STATUS. */ + OAM_FLOW_REMOVED, /* OFPT_FLOW_REMOVED or + * NXT_FLOW_REMOVED. */ + OAM_ROLE_STATUS, /* OFPT_ROLE_STATUS. */ + OAM_TABLE_STATUS, /* OFPT_TABLE_STATUS. */ + OAM_REQUESTFORWARD, /* OFPT_REQUESTFORWARD. */ + OAM_N_TYPES +}; + +enum ofperr ofputil_decode_set_async_config(const struct ofp_header *, + uint32_t master[OAM_N_TYPES], + uint32_t slave[OAM_N_TYPES], + bool loose); + +struct ofpbuf *ofputil_encode_get_async_config(const struct ofp_header *, + uint32_t master[OAM_N_TYPES], + uint32_t slave[OAM_N_TYPES]); + #endif /* ofp-util.h */ diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c index 302d9cea6..b1ba0c697 100644 --- a/ofproto/connmgr.c +++ b/ofproto/connmgr.c @@ -1006,6 +1006,22 @@ ofconn_set_protocol(struct ofconn *ofconn, enum ofputil_protocol protocol) /* OFPR_GROUP is not supported before OF1.4 */ master[OAM_PACKET_IN] &= ~(1u << OFPR_GROUP); slave [OAM_PACKET_IN] &= ~(1u << OFPR_GROUP); + + /* OFPR_PACKET_OUT is not supported before OF1.4 */ + master[OAM_PACKET_IN] &= ~(1u << OFPR_PACKET_OUT); + slave [OAM_PACKET_IN] &= ~(1u << OFPR_PACKET_OUT); + + /* OFPRR_GROUP_DELETE is not supported before OF1.4 */ + master[OAM_FLOW_REMOVED] &= ~(1u << OFPRR_GROUP_DELETE); + slave [OAM_FLOW_REMOVED] &= ~(1u << OFPRR_GROUP_DELETE); + + /* OFPRR_METER_DELETE is not supported before OF1.4 */ + master[OAM_FLOW_REMOVED] &= ~(1u << OFPRR_METER_DELETE); + slave [OAM_FLOW_REMOVED] &= ~(1u << OFPRR_METER_DELETE); + + /* OFPRR_EVICTION is not supported before OF1.4 */ + master[OAM_FLOW_REMOVED] &= ~(1u << OFPRR_EVICTION); + slave [OAM_FLOW_REMOVED] &= ~(1u << OFPRR_EVICTION); } } @@ -1298,20 +1314,29 @@ ofconn_flush(struct ofconn *ofconn) master[OAM_PACKET_IN] = ((1u << OFPR_NO_MATCH) | (1u << OFPR_ACTION) | (1u << OFPR_ACTION_SET) - | (1u << OFPR_GROUP)); + | (1u << OFPR_GROUP) + | (1u << OFPR_PACKET_OUT)); master[OAM_PORT_STATUS] = ((1u << OFPPR_ADD) | (1u << OFPPR_DELETE) | (1u << OFPPR_MODIFY)); master[OAM_FLOW_REMOVED] = ((1u << OFPRR_IDLE_TIMEOUT) | (1u << OFPRR_HARD_TIMEOUT) - | (1u << OFPRR_DELETE)); - + | (1u << OFPRR_DELETE) + | (1u << OFPRR_GROUP_DELETE) + | (1u << OFPRR_METER_DELETE) + | (1u << OFPRR_EVICTION)); + master[OAM_ROLE_STATUS] = 0; + master[OAM_TABLE_STATUS] = 0; + master[OAM_REQUESTFORWARD] = 0; /* "slave" role gets port status updates by default. */ slave[OAM_PACKET_IN] = 0; slave[OAM_PORT_STATUS] = ((1u << OFPPR_ADD) | (1u << OFPPR_DELETE) | (1u << OFPPR_MODIFY)); slave[OAM_FLOW_REMOVED] = 0; + slave[OAM_ROLE_STATUS] = 0; + slave[OAM_TABLE_STATUS] = 0; + slave[OAM_REQUESTFORWARD] = 0; } else { memset(ofconn->master_async_config, 0, sizeof ofconn->master_async_config); @@ -1499,7 +1524,7 @@ ofconn_log_flow_mods(struct ofconn *ofconn) * 'ofconn'. */ static bool ofconn_receives_async_msg(const struct ofconn *ofconn, - enum ofconn_async_msg_type type, + enum ofputil_async_msg_type type, unsigned int reason) { const uint32_t *async_config; diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h index 0e1a5b1af..7ef583ad8 100644 --- a/ofproto/connmgr.h +++ b/ofproto/connmgr.h @@ -54,14 +54,6 @@ enum ofconn_type { OFCONN_SERVICE /* A service connection, e.g. "ovs-ofctl". */ }; -/* The type of an OpenFlow asynchronous message. */ -enum ofconn_async_msg_type { - OAM_PACKET_IN, /* OFPT_PACKET_IN or NXT_PACKET_IN. */ - OAM_PORT_STATUS, /* OFPT_PORT_STATUS. */ - OAM_FLOW_REMOVED, /* OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED. */ - OAM_N_TYPES -}; - enum ofproto_packet_in_miss_type { /* Not generated by a flow miss or table-miss flow. */ OFPROTO_PACKET_IN_NO_MISS, diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 616c04c47..a7240716e 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -5384,17 +5384,10 @@ handle_nxt_set_packet_in_format(struct ofconn *ofconn, static enum ofperr handle_nxt_set_async_config(struct ofconn *ofconn, const struct ofp_header *oh) { - const struct nx_async_config *msg = ofpmsg_body(oh); - uint32_t master[OAM_N_TYPES]; - uint32_t slave[OAM_N_TYPES]; - - master[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[0]); - master[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[0]); - master[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[0]); + uint32_t master[OAM_N_TYPES] = {0}; + uint32_t slave[OAM_N_TYPES] = {0}; - slave[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[1]); - slave[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[1]); - slave[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[1]); + ofputil_decode_set_async_config(oh, master, slave, false); ofconn_set_async_config(ofconn, master, slave); if (ofconn_get_type(ofconn) == OFCONN_SERVICE && @@ -5411,20 +5404,10 @@ handle_nxt_get_async_request(struct ofconn *ofconn, const struct ofp_header *oh) struct ofpbuf *buf; uint32_t master[OAM_N_TYPES]; uint32_t slave[OAM_N_TYPES]; - struct nx_async_config *msg; ofconn_get_async_config(ofconn, master, slave); - buf = ofpraw_alloc_reply(OFPRAW_OFPT13_GET_ASYNC_REPLY, oh, 0); - msg = ofpbuf_put_zeros(buf, sizeof *msg); - - msg->packet_in_mask[0] = htonl(master[OAM_PACKET_IN]); - msg->port_status_mask[0] = htonl(master[OAM_PORT_STATUS]); - msg->flow_removed_mask[0] = htonl(master[OAM_FLOW_REMOVED]); - - msg->packet_in_mask[1] = htonl(slave[OAM_PACKET_IN]); - msg->port_status_mask[1] = htonl(slave[OAM_PORT_STATUS]); - msg->flow_removed_mask[1] = htonl(slave[OAM_FLOW_REMOVED]); + buf = ofputil_encode_get_async_config(oh, master, slave); ofconn_send_reply(ofconn, buf); return 0; diff --git a/tests/ofp-print.at b/tests/ofp-print.at index 6e111502a..127bcf1d7 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -2752,6 +2752,33 @@ NXT_SET_ASYNC_CONFIG (xid=0x0): ]) AT_CLEANUP +AT_SETUP([OFPT_SET_ASYNC_CONFIG]) +AT_KEYWORDS([ofp-print]) +AT_CHECK([ovs-ofctl ofp-print "\ +05 1c 00 38 00 00 00 02 00 00 00 08 00 00 00 05 \ +00 01 00 08 00 00 00 02 00 02 00 08 00 00 00 02 \ +00 03 00 08 00 00 00 05 00 04 00 08 00 00 00 1c \ +00 05 00 08 00 00 00 05 \ +"], [0], [dnl +OFPT_SET_ASYNC (OF1.4) (xid=0x2): + master: + PACKET_IN: action + PORT_STATUS: add modify + FLOW_REMOVED: idle delete + ROLE_STATUS: (off) + TABLE_STATUS: (off) + REQUESTFORWARD: (off) + + slave: + PACKET_IN: no_match invalid_ttl + PORT_STATUS: delete + FLOW_REMOVED: delete group_delete meter_delete + ROLE_STATUS: (off) + TABLE_STATUS: (off) + REQUESTFORWARD: (off) +]) +AT_CLEANUP + AT_SETUP([NXT_SET_CONTROLLER_ID]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ diff --git a/tests/ofproto.at b/tests/ofproto.at index 7d78b5770..cf5ab9fb3 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -2519,6 +2519,128 @@ ovs-appctl -t ovs-ofctl exit OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.4)]) +OVS_VSWITCHD_START +AT_CHECK([ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile]) +check_async () { + printf '\n\n--- check_async %d ---\n\n\n' $1 + INDEX=$1 + shift + + ovs-appctl -t ovs-ofctl ofctl/barrier + ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log + : > expout + + # OFPT_PACKET_IN, OFPR_ACTION (controller_id=0) + ovs-ofctl -O OpenFlow14 -v packet-out br0 none controller '0001020304050010203040501234' + if test X"$1" = X"OFPR_ACTION"; then shift; + echo >>expout "OFPT_PACKET_IN (OF1.4): total_len=14 in_port=ANY (via action) data_len=14 (unbuffered) +vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" + fi + + # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123) + ovs-ofctl -O OpenFlow14 -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234' + if test X"$1" = X"OFPR_NO_MATCH"; then shift; + echo >>expout "OFPT_PACKET_IN (OF1.4): total_len=14 in_port=ANY (via no_match) data_len=14 (unbuffered) +vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" + fi + + # OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0) + ovs-ofctl -O OpenFlow14 packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003eb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00' + if test X"$1" = X"OFPR_INVALID_TTL"; then shift; + echo >>expout "OFPT_PACKET_IN (OF1.4): total_len=76 in_port=ANY (via invalid_ttl) data_len=76 (unbuffered) +udp,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=55155,tp_dst=53 udp_csum:8f6d" + fi + +# OFPT_PORT_STATUS, OFPPR_ADD + ovs-vsctl add-port br0 test -- set Interface test type=dummy + if test X"$1" = X"OFPPR_ADD"; then shift; + echo >>expout "OFPT_PORT_STATUS (OF1.4): ADD: ${INDEX}(test): addr:aa:55:aa:55:00:0x + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max" + fi + + # OFPT_PORT_STATUS, OFPPR_MODIFY + ovs-ofctl -O OpenFlow14 -vwarn mod-port br0 test up + if test X"$1" = X"OFPPR_MODIFY"; then shift; + echo >>expout "OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x + config: 0 + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max +OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x + config: 0 + state: 0 + speed: 0 Mbps now, 0 Mbps max" + fi + + # OFPT_PORT_STATUS, OFPPR_DELETE + ovs-vsctl del-port br0 test + if test X"$1" = X"OFPPR_DELETE"; then shift; + echo >>expout "OFPT_PORT_STATUS (OF1.4): DEL: ${INDEX}(test): addr:aa:55:aa:55:00:0x + config: 0 + state: 0 + speed: 0 Mbps now, 0 Mbps max" + fi + + # OFPT_FLOW_REMOVED, OFPRR_DELETE + ovs-ofctl -O OpenFlow14 add-flow br0 send_flow_rem,actions=drop + ovs-ofctl -O OpenFlow14 --strict del-flows br0 '' + if test X"$1" = X"OFPRR_DELETE"; then shift; + echo >>expout "OFPT_FLOW_REMOVED (OF1.4): reason=delete table_id=0" + fi + + # OFPT_FLOW_REMOVED, OFPRR_GROUP_DELETE + ovs-ofctl -O OpenFlow14 add-group br0 group_id=1234,type=all,bucket=output:10 + ovs-ofctl -O OpenFlow14 add-flow br0 send_flow_rem,actions=group:1234 + ovs-ofctl -O OpenFlow14 --strict del-groups br0 group_id=1234 + if test X"$1" = X"OFPRR_GROUP_DELETE"; then shift; + echo >>expout "OFPT_FLOW_REMOVED (OF1.4): reason=group_delete table_id=0" + fi + + AT_FAIL_IF([test X"$1" != X]) + + ovs-appctl -t ovs-ofctl ofctl/barrier + echo >>expout "OFPT_BARRIER_REPLY (OF1.4):" + + AT_CHECK( + [[sed ' +s/ (xid=0x[0-9a-fA-F]*)// +s/ *duration.*// +s/00:0.$/00:0x/' < monitor.log]], + [0], [expout]) +} + +# It's a service connection so initially there should be no async messages. +check_async 1 + +# Set miss_send_len to 128, turning on packet-ins for our service connection. +ovs-appctl -t ovs-ofctl ofctl/send 0509000c0123456700000080 +check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_MODIFY OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE + +# Become slave (OF 1.4), which should disable everything except port status. +ovs-appctl -t ovs-ofctl ofctl/send 051800180000000200000003000000000000000000000001 +check_async 3 OFPPR_ADD OFPPR_MODIFY OFPPR_DELETE + +# Use OF 1.4 OFPT_SET_ASYNC to enable a patchwork of asynchronous messages. +ovs-appctl -t ovs-ofctl ofctl/send 051c0038000000020000000800000005000100080000000200020008000000020003000800000005000400080000001c0005000800000005 +check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE + +# Set controller ID 123. +ovs-appctl -t ovs-ofctl ofctl/send 05040018000000030000232000000014000000000000007b +check_async 5 OFPR_NO_MATCH OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE + +# Restore controller ID 0. +ovs-appctl -t ovs-ofctl ofctl/send 050400180000000300002320000000140000000000000000 + +# Become master (OF 1.4). +ovs-appctl -t ovs-ofctl ofctl/send 051800180000000400000002000000000000000000000002 +check_async 6 OFPR_ACTION OFPPR_ADD OFPPR_MODIFY OFPRR_DELETE + +ovs-appctl -t ovs-ofctl exit +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.5)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow15 monitor br0 --detach --no-chdir --pidfile]) -- 2.20.1