ofproto: Implement OpenFlow extension to allow control over async messages.
authorBen Pfaff <blp@nicira.com>
Thu, 9 Feb 2012 22:06:35 +0000 (14:06 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 9 Feb 2012 22:06:35 +0000 (14:06 -0800)
Until now, the rules that cover the asynchronous messages that Open vSwitch
sends to a controller have been ad hoc.  The new NXT_SET_ASYNC_CONFIG
message provides systematic, precise control.

Feature #7086.
Signed-off-by: Ben Pfaff <blp@nicira.com>
12 files changed:
DESIGN
NEWS
include/openflow/nicira-ext.h
lib/learning-switch.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/ofproto.c
tests/ofp-print.at
tests/ofproto.at

diff --git a/DESIGN b/DESIGN
index 164ada2..f383b65 100644 (file)
--- a/DESIGN
+++ b/DESIGN
@@ -9,8 +9,53 @@ successful deployment.  The end of this document contains contact
 information that can be used to let us know how we can make Open vSwitch
 more generally useful.
 
-OpenFlow
-========
+Asynchronous Messages
+=====================
+
+Over time, Open vSwitch has added many knobs that control whether a
+given controller receives OpenFlow asynchronous messages.  This
+section describes how all of these features interact.
+
+First, a service controller never receives any asynchronous messages
+unless it explicitly configures a miss_send_len greater than zero with
+an OFPT_SET_CONFIG message.
+
+Second, OFPT_FLOW_REMOVED and NXT_FLOW_REMOVED messages are generated
+only if the flow that was removed had the OFPFF_SEND_FLOW_REM flag
+set.
+
+Finally, Open vSwitch consults a per-connection table indexed by the
+message type, reason code, and current role.  The following table
+shows how this table is initialized by default when an OpenFlow
+connection is made.  An entry labeled "yes" means that the message is
+sent, an entry labeled "---" means that the message is suppressed.
+
+                                             master/
+  message and reason code                     other     slave
+  ----------------------------------------   -------    -----
+  OFPT_PACKET_IN / NXT_PACKET_IN
+    OFPR_NO_MATCH                              yes       ---
+    OFPR_ACTION                                yes       ---
+    OFPR_INVALID_TTL                           ---       ---
+
+  OFPT_FLOW_REMOVED / NXT_FLOW_REMOVED
+    OFPRR_IDLE_TIMEOUT                         yes       ---
+    OFPRR_HARD_TIMEOUT                         yes       ---
+    OFPRR_DELETE                               yes       ---
+
+  OFPT_PORT_STATUS
+    OFPPR_ADD                                  yes       yes
+    OFPPR_DELETE                               yes       yes
+    OFPPR_MODIFY                               yes       yes
+
+The NXT_SET_ASYNC_CONFIG message directly sets all of the values in
+this table for the current connection.  The
+OFPC_INVALID_TTL_TO_CONTROLLER bit in the OFPT_SET_CONFIG message
+controls the setting for OFPR_INVALID_TTL for the "master" role.
+
+
+OFPAT_ENQUEUE
+=============
 
 The OpenFlow 1.0 specification requires the output port of the OFPAT_ENQUEUE
 action to "refer to a valid physical port (i.e. < OFPP_MAX) or OFPP_IN_PORT".
diff --git a/NEWS b/NEWS
index c153370..fde19f0 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,13 @@ post-v1.5.0
     - OpenFlow:
         - Added support for bitwise matching on TCP and UDP ports.
           See ovs-ofctl(8) for more information.
+        - NXM flow dumps now include times elapsed toward idle and hard
+          timeouts.
+        - Added an OpenFlow extension NXT_SET_ASYNC_CONFIG that allows
+          controllers more precise control over which OpenFlow messages they
+          receive asynchronously.
+    - The default MAC learning timeout has been increased from 60 seconds
+      to 300 seconds.  The MAC learning timeout is now configurable.
     - Logging to console and file will have UTC timestamp as a default for all
       the daemons. An example of the default format is 2012-01-27T16:35:17Z.
       ovs-appctl can be used to change the default format as before.
@@ -18,13 +25,9 @@ post-v1.5.0
       table, with configurable policy for evicting flows upon
       overflow.  See the Flow_Table table in ovs-vswitch.conf.db(5)
       for more information.
-    - OpenFlow:
-      - NXM flow dumps now include times elapsed toward idle and hard timeouts.
     - ofproto-provider interface:
         - "struct rule" has a new member "used" that ofproto implementations
           should maintain by updating with ofproto_rule_update_used().
-    - The default MAC learning timeout has been increased from 60 seconds
-      to 300 seconds.  The MAC learning timeout is now configurable.
     - ovsdb-client:
         - The new option --timestamp causes the "monitor" command to print
           a timestamp with every update.
index b0c8e4e..ff95796 100644 (file)
@@ -114,6 +114,8 @@ enum nicira_type {
      * If so, the switch does not reply to this message (which consists only of
      * a "struct nicira_header").  If not, the switch sends an error reply. */
     NXT_FLOW_AGE = 18,
+
+    NXT_SET_ASYNC_CONFIG = 19,  /* struct nx_async_config. */
 };
 
 /* Header for Nicira vendor stats request and reply messages. */
@@ -288,6 +290,28 @@ enum nx_role {
     NX_ROLE_MASTER,             /* Full access, at most one. */
     NX_ROLE_SLAVE               /* Read-only access. */
 };
+
+/* NXT_SET_ASYNC_CONFIG.
+ *
+ * Sent by a controller, this message configures the asynchronous messages that
+ * the controller wants to receive.  Element 0 in each array specifies messages
+ * of interest when the controller has an "other" or "master" role; element 1,
+ * when the controller has a "slave" role.
+ *
+ * Each array element is a bitmask in which a 0-bit disables receiving a
+ * particular message and a 1-bit enables receiving it.  Each bit controls the
+ * message whose 'reason' corresponds to the bit index.  For example, the bit
+ * with value 1<<2 == 4 in port_status_mask[1] determines whether the
+ * controller will receive OFPT_PORT_STATUS messages with reason OFPPR_MODIFY
+ * (value 2) when the controller has a "slave" role.
+ */
+struct nx_async_config {
+    struct nicira_header nxh;
+    ovs_be32 packet_in_mask[2];    /* Bitmasks of OFPR_* values. */
+    ovs_be32 port_status_mask[2];  /* Bitmasks of OFPRR_* values. */
+    ovs_be32 flow_removed_mask[2]; /* Bitmasks of OFPPR_* values. */
+};
+OFP_ASSERT(sizeof(struct nx_async_config) == 40);
 \f
 /* Nicira vendor flow actions. */
 
index 81aadeb..435acb9 100644 (file)
@@ -265,6 +265,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
     case OFPUTIL_NXT_FLOW_MOD:
     case OFPUTIL_NXT_FLOW_REMOVED:
     case OFPUTIL_NXT_FLOW_AGE:
+    case OFPUTIL_NXT_SET_ASYNC_CONFIG:
     case OFPUTIL_NXST_FLOW_REQUEST:
     case OFPUTIL_NXST_AGGREGATE_REQUEST:
     case OFPUTIL_NXST_FLOW_REPLY:
index f0c134f..30ecdbf 100644 (file)
@@ -80,6 +80,24 @@ ofp_packet_to_string(const void *data, size_t len)
     return ds_cstr(&ds);
 }
 
+static const char *
+ofp_packet_in_reason_to_string(enum ofp_packet_in_reason reason)
+{
+    static char s[32];
+
+    switch (reason) {
+    case OFPR_NO_MATCH:
+        return "no_match";
+    case OFPR_ACTION:
+        return "action";
+    case OFPR_INVALID_TTL:
+        return "invalid_ttl";
+    default:
+        sprintf(s, "%d", (int) reason);
+        return s;
+    }
+}
+
 static void
 ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
                     int verbosity)
@@ -121,20 +139,8 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
         }
     }
 
-    switch (pin.reason) {
-    case OFPR_NO_MATCH:
-        ds_put_cstr(string, " (via no_match)");
-        break;
-    case OFPR_ACTION:
-        ds_put_cstr(string, " (via action)");
-        break;
-    case OFPR_INVALID_TTL:
-        ds_put_cstr(string, " (via invalid_ttl)");
-        break;
-    default:
-        ds_put_format(string, " (***reason %"PRIu8"***)", pin.reason);
-        break;
-    }
+    ds_put_format(string, " (via %s)",
+                  ofp_packet_in_reason_to_string(pin.reason));
 
     ds_put_format(string, " data_len=%zu", pin.packet_len);
     if (pin.buffer_id == UINT32_MAX) {
@@ -870,6 +876,24 @@ ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec)
     ds_put_char(string, 's');
 }
 
+static const char *
+ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason)
+{
+    static char s[32];
+
+    switch (reason) {
+    case OFPRR_IDLE_TIMEOUT:
+        return "idle";
+    case OFPRR_HARD_TIMEOUT:
+        return "hard";
+    case OFPRR_DELETE:
+        return "delete";
+    default:
+        sprintf(s, "%d", (int) reason);
+        return s;
+    }
+}
+
 static void
 ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
 {
@@ -885,21 +909,8 @@ ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
     ds_put_char(string, ' ');
     cls_rule_format(&fr.rule, string);
 
-    ds_put_cstr(string, " reason=");
-    switch (fr.reason) {
-    case OFPRR_IDLE_TIMEOUT:
-        ds_put_cstr(string, "idle");
-        break;
-    case OFPRR_HARD_TIMEOUT:
-        ds_put_cstr(string, "hard");
-        break;
-    case OFPRR_DELETE:
-        ds_put_cstr(string, "delete");
-        break;
-    default:
-        ds_put_format(string, "**%"PRIu8"**", fr.reason);
-        break;
-    }
+    ds_put_format(string, " reason=%s",
+                  ofp_flow_removed_reason_to_string(fr.reason));
 
     if (fr.cookie != htonll(0)) {
         ds_put_format(string, " cookie:0x%"PRIx64, ntohll(fr.cookie));
@@ -1313,6 +1324,75 @@ ofp_print_nxt_set_packet_in_format(struct ds *string,
     }
 }
 
+static const char *
+ofp_port_reason_to_string(enum ofp_port_reason reason)
+{
+    static char s[32];
+
+    switch (reason) {
+    case OFPPR_ADD:
+        return "add";
+
+    case OFPPR_DELETE:
+        return "delete";
+
+    case OFPPR_MODIFY:
+        return "modify";
+
+    default:
+        sprintf(s, "%d", (int) reason);
+        return s;
+    }
+}
+
+static void
+ofp_print_nxt_set_async_config(struct ds *string,
+                               const struct nx_async_config *nac)
+{
+    int i;
+
+    for (i = 0; i < 2; i++) {
+        int j;
+
+        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)) {
+                ds_put_format(string, " %s",
+                              ofp_packet_in_reason_to_string(j));
+            }
+        }
+        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)) {
+                ds_put_format(string, " %s", ofp_port_reason_to_string(j));
+            }
+        }
+        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)) {
+                ds_put_format(string, " %s",
+                              ofp_flow_removed_reason_to_string(j));
+            }
+        }
+        if (!nac->flow_removed_mask[i]) {
+            ds_put_cstr(string, " (off)");
+        }
+        ds_put_char(string, '\n');
+    }
+}
+
 static void
 ofp_to_string__(const struct ofp_header *oh,
                 const struct ofputil_msg_type *type, struct ds *string,
@@ -1472,6 +1552,10 @@ ofp_to_string__(const struct ofp_header *oh,
     case OFPUTIL_NXT_FLOW_AGE:
         break;
 
+    case OFPUTIL_NXT_SET_ASYNC_CONFIG:
+        ofp_print_nxt_set_async_config(string, msg);
+        break;
+
     case OFPUTIL_NXST_AGGREGATE_REPLY:
         ofp_print_stats_reply(string, oh);
         ofp_print_nxst_aggregate_reply(string, msg);
index c377e3e..e936693 100644 (file)
@@ -400,6 +400,10 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
         { OFPUTIL_NXT_FLOW_AGE, OFP10_VERSION,
           NXT_FLOW_AGE, "NXT_FLOW_AGE",
           sizeof(struct nicira_header), 0 },
+
+        { OFPUTIL_NXT_SET_ASYNC_CONFIG, OFP10_VERSION,
+          NXT_SET_ASYNC_CONFIG, "NXT_SET_ASYNC_CONFIG",
+          sizeof(struct nx_async_config), 0 },
     };
 
     static const struct ofputil_msg_category nxt_category = {
index 0b6970a..816532e 100644 (file)
@@ -80,6 +80,7 @@ enum ofputil_msg_code {
     OFPUTIL_NXT_SET_PACKET_IN_FORMAT,
     OFPUTIL_NXT_PACKET_IN,
     OFPUTIL_NXT_FLOW_AGE,
+    OFPUTIL_NXT_SET_ASYNC_CONFIG,
 
     /* NXST_* stat requests. */
     OFPUTIL_NXST_FLOW_REQUEST,
index db848fb..a3ffb69 100644 (file)
@@ -60,8 +60,6 @@ struct ofconn {
     enum nx_flow_format flow_format; /* Currently selected flow format. */
     enum nx_packet_in_format packet_in_format; /* OFPT_PACKET_IN format. */
     bool flow_mod_table_id;     /* NXT_FLOW_MOD_TABLE_ID enabled? */
-    bool invalid_ttl_to_controller; /* Send packets with invalid TTL
-                                       to the controller. */
 
     /* Asynchronous flow table operation support. */
     struct list opgroups;       /* Contains pending "ofopgroups", if any. */
@@ -80,6 +78,13 @@ struct ofconn {
      * requests.  */
 #define OFCONN_REPLY_MAX 100
     struct rconn_packet_counter *reply_counter;
+
+    /* Asynchronous message configuration in each possible roles.
+     *
+     * A 1-bit enables sending an asynchronous message for one possible reason
+     * that the message might be generated, a 0-bit disables it. */
+    uint32_t master_async_config[OAM_N_TYPES]; /* master, other */
+    uint32_t slave_async_config[OAM_N_TYPES];  /* slave */
 };
 
 static struct ofconn *ofconn_create(struct connmgr *, struct rconn *,
@@ -100,8 +105,6 @@ static char *ofconn_make_name(const struct connmgr *, const char *target);
 
 static void ofconn_set_rate_limit(struct ofconn *, int rate, int burst);
 
-static bool ofconn_receives_async_msgs(const struct ofconn *);
-
 static void ofconn_send(const struct ofconn *, struct ofpbuf *,
                         struct rconn_packet_counter *);
 
@@ -762,15 +765,21 @@ ofconn_set_role(struct ofconn *ofconn, enum nx_role role)
 }
 
 void
-ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool val)
+ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool enable)
 {
-    ofconn->invalid_ttl_to_controller = val;
+    uint32_t bit = 1u << OFPR_INVALID_TTL;
+    if (enable) {
+        ofconn->master_async_config[OAM_PACKET_IN] |= bit;
+    } else {
+        ofconn->master_async_config[OAM_PACKET_IN] &= ~bit;
+    }
 }
 
 bool
 ofconn_get_invalid_ttl_to_controller(struct ofconn *ofconn)
 {
-    return ofconn->invalid_ttl_to_controller;
+    uint32_t bit = 1u << OFPR_INVALID_TTL;
+    return (ofconn->master_async_config[OAM_PACKET_IN] & bit) != 0;
 }
 
 /* Returns the currently configured flow format for 'ofconn', one of NXFF_*.
@@ -840,6 +849,16 @@ ofconn_set_miss_send_len(struct ofconn *ofconn, int miss_send_len)
     ofconn->miss_send_len = miss_send_len;
 }
 
+void
+ofconn_set_async_config(struct ofconn *ofconn,
+                        const uint32_t master_masks[OAM_N_TYPES],
+                        const uint32_t slave_masks[OAM_N_TYPES])
+{
+    size_t size = sizeof ofconn->master_async_config;
+    memcpy(ofconn->master_async_config, master_masks, size);
+    memcpy(ofconn->slave_async_config, slave_masks, size);
+}
+
 /* Sends 'msg' on 'ofconn', accounting it as a reply.  (If there is a
  * sufficient number of OpenFlow replies in-flight on a single ofconn, then the
  * connmgr will stop accepting new OpenFlow requests on that ofconn until the
@@ -956,6 +975,8 @@ ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type)
 static void
 ofconn_flush(struct ofconn *ofconn)
 {
+    uint32_t *master = ofconn->master_async_config;
+    uint32_t *slave = ofconn->slave_async_config;
     int i;
 
     ofconn->role = NX_ROLE_OTHER;
@@ -996,6 +1017,24 @@ ofconn_flush(struct ofconn *ofconn)
 
     rconn_packet_counter_destroy(ofconn->reply_counter);
     ofconn->reply_counter = rconn_packet_counter_create();
+
+    /* "master" and "other" roles get all asynchronous messages by default,
+     * except that the controller needs to enable nonstandard "packet-in"
+     * reasons itself. */
+    master[OAM_PACKET_IN] = (1u << OFPR_NO_MATCH) | (1u << OFPR_ACTION);
+    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));
+
+    /* "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;
 }
 
 static void
@@ -1100,42 +1139,42 @@ ofconn_wait(struct ofconn *ofconn, bool handling_openflow)
     }
 }
 
-/* Returns true if 'ofconn' should receive asynchronous messages. */
+/* Returns true if 'ofconn' should receive asynchronous messages of the given
+ * OAM_* 'type' and 'reason', which should be a OFPR_* value for OAM_PACKET_IN,
+ * a OFPPR_* value for OAM_PORT_STATUS, or an OFPRR_* value for
+ * OAM_FLOW_REMOVED.  Returns false if the message should not be sent on
+ * 'ofconn'. */
 static bool
-ofconn_receives_async_msgs__(const struct ofconn *ofconn)
+ofconn_receives_async_msg(const struct ofconn *ofconn,
+                          enum ofconn_async_msg_type type,
+                          unsigned int reason)
 {
-    if (ofconn->type == OFCONN_PRIMARY) {
-        /* Primary controllers always get asynchronous messages unless they
-         * have configured themselves as "slaves".  */
-        return ofconn->role != NX_ROLE_SLAVE;
-    } else {
-        /* Service connections don't get asynchronous messages unless they have
-         * explicitly asked for them by setting a nonzero miss send length. */
-        return ofconn->miss_send_len > 0;
-    }
-}
+    const uint32_t *async_config;
+
+    assert(reason < 32);
+    assert((unsigned int) type < OAM_N_TYPES);
 
-static bool
-ofconn_receives_async_msgs(const struct ofconn *ofconn)
-{
     if (!rconn_is_connected(ofconn->rconn)) {
         return false;
-    } else {
-        return ofconn_receives_async_msgs__(ofconn);
     }
-}
 
-static bool
-ofconn_interested_in_packet(const struct ofconn *ofconn,
-                            const struct ofputil_packet_in *pin)
-{
-    if (!rconn_is_connected(ofconn->rconn)) {
+    /* Keep the following code in sync with the documentation in the
+     * "Asynchronous Messages" section in DESIGN. */
+
+    if (ofconn->type == OFCONN_SERVICE && !ofconn->miss_send_len) {
+        /* Service connections don't get asynchronous messages unless they have
+         * explicitly asked for them by setting a nonzero miss send length. */
         return false;
-    } else if (pin->reason == OFPR_INVALID_TTL) {
-        return ofconn->invalid_ttl_to_controller;
-    } else {
-        return ofconn_receives_async_msgs__(ofconn);
     }
+
+    async_config = (ofconn->role == NX_ROLE_SLAVE
+                    ? ofconn->slave_async_config
+                    : ofconn->master_async_config);
+    if (!(async_config[type] & (1u << reason))) {
+        return false;
+    }
+
+    return true;
 }
 
 /* Returns a human-readable name for an OpenFlow connection between 'mgr' and
@@ -1195,20 +1234,15 @@ connmgr_send_port_status(struct connmgr *mgr, const struct ofp_phy_port *opp,
     struct ofconn *ofconn;
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        struct ofp_port_status *ops;
-        struct ofpbuf *b;
-
-        /* Primary controllers, even slaves, should always get port status
-           updates.  Otherwise obey ofconn_receives_async_msgs(). */
-        if (ofconn->type != OFCONN_PRIMARY
-            && !ofconn_receives_async_msgs(ofconn)) {
-            continue;
+        if (ofconn_receives_async_msg(ofconn, OAM_PORT_STATUS, reason)) {
+            struct ofp_port_status *ops;
+            struct ofpbuf *b;
+
+            ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b);
+            ops->reason = reason;
+            ops->desc = *opp;
+            ofconn_send(ofconn, b, NULL);
         }
-
-        ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b);
-        ops->reason = reason;
-        ops->desc = *opp;
-        ofconn_send(ofconn, b, NULL);
     }
 }
 
@@ -1221,19 +1255,17 @@ connmgr_send_flow_removed(struct connmgr *mgr,
     struct ofconn *ofconn;
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        struct ofpbuf *msg;
-
-        if (!ofconn_receives_async_msgs(ofconn)) {
-            continue;
+        if (ofconn_receives_async_msg(ofconn, OAM_FLOW_REMOVED, fr->reason)) {
+            struct ofpbuf *msg;
+
+            /* Account flow expirations as replies to OpenFlow requests.  That
+             * works because preventing OpenFlow requests from being processed
+             * also prevents new flows from being added (and expiring).  (It
+             * also prevents processing OpenFlow requests that would not add
+             * new flows, so it is imperfect.) */
+            msg = ofputil_encode_flow_removed(fr, ofconn->flow_format);
+            ofconn_send_reply(ofconn, msg);
         }
-
-        /* Account flow expirations as replies to OpenFlow requests.  That
-         * works because preventing OpenFlow requests from being processed also
-         * prevents new flows from being added (and expiring).  (It also
-         * prevents processing OpenFlow requests that would not add new flows,
-         * so it is imperfect.) */
-        msg = ofputil_encode_flow_removed(fr, ofconn->flow_format);
-        ofconn_send_reply(ofconn, msg);
     }
 }
 
@@ -1247,7 +1279,7 @@ connmgr_send_packet_in(struct connmgr *mgr,
     struct ofconn *ofconn;
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        if (ofconn_interested_in_packet(ofconn, pin)) {
+        if (ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, pin->reason)) {
             schedule_packet_in(ofconn, *pin, flow);
         }
     }
index 8ff89f3..5f9ac70 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -51,6 +51,14 @@ 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
+};
+
 /* Basics. */
 struct connmgr *connmgr_create(struct ofproto *ofproto,
                                const char *dpif_name, const char *local_name);
@@ -98,6 +106,10 @@ bool ofconn_get_invalid_ttl_to_controller(struct ofconn *);
 int ofconn_get_miss_send_len(const struct ofconn *);
 void ofconn_set_miss_send_len(struct ofconn *, int miss_send_len);
 
+void ofconn_set_async_config(struct ofconn *,
+                             const uint32_t master_masks[OAM_N_TYPES],
+                             const uint32_t slave_masks[OAM_N_TYPES]);
+
 void ofconn_send_reply(const struct ofconn *, struct ofpbuf *);
 void ofconn_send_replies(const struct ofconn *, struct list *);
 void ofconn_send_error(const struct ofconn *, const struct ofp_header *request,
index 92744a2..7b99741 100644 (file)
@@ -3117,6 +3117,26 @@ handle_nxt_set_packet_in_format(struct ofconn *ofconn,
     return 0;
 }
 
+static enum ofperr
+handle_nxt_set_async_config(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    const struct nx_async_config *msg = (const struct nx_async_config *) 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]);
+
+    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]);
+
+    ofconn_set_async_config(ofconn, master, slave);
+
+    return 0;
+}
+
 static enum ofperr
 handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
@@ -3194,6 +3214,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
         /* Nothing to do. */
         return 0;
 
+    case OFPUTIL_NXT_SET_ASYNC_CONFIG:
+        return handle_nxt_set_async_config(ofconn, oh);
+
         /* Statistics requests. */
     case OFPUTIL_OFPST_DESC_REQUEST:
         return handle_desc_stats_request(ofconn, msg->data);
index 09c9e83..471a73c 100644 (file)
@@ -708,6 +708,26 @@ priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82
 ])
 AT_CLEANUP
 
+AT_SETUP([NXT_SET_ASYNC_CONFIG])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 28 00 00 00 00 00 00 23 20 00 00 00 13 \
+00 00 10 05 00 00 10 07 00 00 00 03 00 00 00 07 \
+00 00 00 00 00 00 00 03 \
+"], [0], [dnl
+NXT_SET_ASYNC_CONFIG (xid=0x0):
+ master:
+       PACKET_IN: no_match invalid_ttl 12
+     PORT_STATUS: add delete
+    FLOW_REMOVED: (off)
+
+ slave:
+       PACKET_IN: no_match action invalid_ttl 12
+     PORT_STATUS: add delete modify
+    FLOW_REMOVED: idle hard
+])
+AT_CLEANUP
+
 AT_SETUP([NXT_SET_FLOW_FORMAT])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
index d7fef5d..9358a6a 100644 (file)
@@ -174,6 +174,7 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  cookie=0x3, in_port=3 actions=output:0
 NXST_FLOW reply:
 ])
+
 AT_CHECK([ovs-ofctl del-flows br0 cookie=0x3])
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  cookie=0x1, in_port=1 actions=output:0
@@ -421,3 +422,89 @@ NXST_FLOW reply:
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ofproto - asynchronous message control])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -P openflow10 monitor br0 --detach --no-chdir --pidfile])
+check_async () {
+    printf '\n\n--- check_async %d ---\n\n\n' $1
+    shift
+
+    ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+    : > expout
+
+    # OFPT_PACKET_IN, OFPR_ACTION
+    ovs-ofctl -v packet-out br0 none controller '0001020304050010203040501234'
+    if test X"$1" = X"OFPR_ACTION"; then shift;
+        echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via action) data_len=14 (unbuffered)
+priority:0,tunnel:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)"
+    fi
+
+    # OFPT_PACKET_IN, OFPR_INVALID_TTL
+    ovs-ofctl packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003fb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00'
+    if test X"$1" = X"OFPR_INVALID_TTL"; then shift;
+        echo >>expout "OFPT_PACKET_IN: total_len=76 in_port=NONE (via invalid_ttl) data_len=76 (unbuffered)
+priority:0,tunnel:0,in_port:0000,tci(0) mac(00:26:b9:8c:b0:f9->00:25:83:df:b4:00) type:0800 proto:17 tos:0 ttl:0 ip(172.17.55.13->172.16.0.2) port(55155->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: ADD: 1(test): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN"
+    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: DEL: 1(test): addr:aa:55:aa:55:00:0x
+     config:     PORT_DOWN
+     state:      LINK_DOWN"
+    fi
+
+    # OFPT_FLOW_REMOVED, OFPRR_DELETE
+    ovs-ofctl add-flow br0 send_flow_rem,actions=drop
+    ovs-ofctl --strict del-flows br0 ''
+    if test X"$1" = X"OFPRR_DELETE"; then shift;
+        echo >>expout "OFPT_FLOW_REMOVED:  reason=delete"
+    fi
+    AT_FAIL_IF([test X"$1" != X])
+
+    ovs-appctl -t ovs-ofctl ofctl/barrier
+    echo >>expout "send: OFPT_BARRIER_REQUEST:
+OFPT_BARRIER_REPLY:"
+
+    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-outs for our service connection.
+ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
+check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_DELETE OFPRR_DELETE
+
+# Set miss_send_len to 128 and enable invalid_ttl.
+ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700040080
+check_async 3 OFPR_ACTION OFPR_INVALID_TTL OFPPR_ADD OFPPR_DELETE OFPRR_DELETE
+
+# Become slave, which should disable everything except port status.
+ovs-appctl -t ovs-ofctl ofctl/send 0104001400000002000023200000000a00000002
+check_async 4 OFPPR_ADD OFPPR_DELETE
+
+# Use NXT_SET_ASYNC_CONFIG to enable a patchwork of asynchronous messages.
+ovs-appctl -t ovs-ofctl ofctl/send 01040028000000020000232000000013000000020000000500000005000000020000000200000005
+check_async 5 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE
+
+# Become master.
+ovs-appctl -t ovs-ofctl ofctl/send 0104001400000002000023200000000a00000001
+check_async 6 OFPR_ACTION OFPPR_ADD
+
+ovs-appctl -t ovs-ofctl exit
+AT_CLEANUP