[ovs-dev] [PATCH v2] ofproto: Implement OF1.4 Set/Get asynchronous configuration messages.

niti1489 at gmail.com niti1489 at gmail.com
Fri Jul 10 12:12:50 UTC 2015


From: Niti <niti.rohilla at tcs.com>

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 <niti.rohilla at tcs.com>
---
Comments recevied on 9th July,2015 have been incorporated in this version.
Following are the changes between v1 and v2:

1. The message in NEWS has been updated.

2. ofputil_encode_get_async_config() has been implemented to encode get async
reply.

3. typedef has been removed.
 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                  | 195 ++++++++++++++++++++++++++++++++
 lib/ofp-util.h                  |  19 ++++
 ofproto/connmgr.c               |  33 +++++-
 ofproto/connmgr.h               |   8 --
 ofproto/ofproto.c               |  25 +----
 tests/ofp-print.at              |  27 +++++
 tests/ofproto.at                | 122 ++++++++++++++++++++
 12 files changed, 639 insertions(+), 80 deletions(-)

diff --git a/DESIGN.md b/DESIGN.md
index 38413d7..58826d3 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 b08919b..8214d78 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,9 @@ Post-v2.4.0
    - Support for matching and generating options with Geneve tunnels.
    - Support Multicast Listener Discovery (MLDv1 and MLDv2).
    - Add 'symmetric_l3l4' and 'symmetric_l3l4+udp' hash functions.
+   - Added OpenFlow 1.4+ OFPT_SET_ASYNC_CONFIG and OFPT_GET_ASYNC_CONFIG
+     that allows controllers to have more precise control over OpenFlow1.4
+     asynchoronous messages.
 
 
 v2.4.0 - xx xxx xxxx
diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h
index 567aaae..37eef4a 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 3d9fedf..1358b21 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 4603bb7..f7c794b 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]);
+        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 646bae7..8b86d02 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -9281,3 +9281,198 @@ ofputil_uninit_geneve_table(struct ovs_list *mappings)
         free(map);
     }
 }
+
+/* Decodes the OpenFlow "set async config" message in '*oh' into an abstract
+ * form in 'master' and 'slave'. 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])
+{
+    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:
+                return OFPERR_OFPBRC_BAD_TYPE;
+            }
+        }
+    } else {
+        return OFPERR_OFPBRC_BAD_TYPE;
+    }
+    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 431f165..2cec981 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -1219,4 +1219,23 @@ 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]);
+
+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 975ee33..3538c14 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 0e1a5b1..7ef583a 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 7c3948b..9cfb60c 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);
 
     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 6e11150..127bcf1 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 232dd2f..0b704ed 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2518,6 +2518,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.4.4




More information about the dev mailing list