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
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
- 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
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. */
};
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.
/* 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,
* 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. */
}
}
+/* 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');
}
}
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;
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;
+}
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 */
/* 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);
}
}
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);
* '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;
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,
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 &&
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;
])
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 "\
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])