ofproto: Add selective Flow Removed messages and flow deletes (OpenFlow 0.9)
authorJustin Pettit <jpettit@nicira.com>
Fri, 13 Nov 2009 19:08:32 +0000 (11:08 -0800)
committerJustin Pettit <jpettit@nicira.com>
Sat, 23 Jan 2010 02:08:04 +0000 (18:08 -0800)
In OpenFlow 0.9, flow "expiration" messages are sent when flows are
explicitly removed by a delete action.  As such, the message is renamed
from Flow Expired to Flow Removed.  This commit adds that support as well
as supporting the ability to choose sending these messages on a per flow
basis.

NOTE: OVS at this point is not wire-compatible with OpenFlow 0.9 until the
final commit in this OpenFlow 0.9 set.

include/openflow/openflow.h
lib/learning-switch.c
lib/ofp-print.c
ofproto/ofproto.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c

index a9199c7..511cdb7 100644 (file)
@@ -95,7 +95,7 @@ enum ofp_type {
 
     /* Asynchronous messages. */
     OFPT_PACKET_IN,           /* Async message */
-    OFPT_FLOW_EXPIRED,        /* Async message */
+    OFPT_FLOW_REMOVED,        /* Async message */
     OFPT_PORT_STATUS,         /* Async message */
 
     /* Controller command messages. */
@@ -128,9 +128,6 @@ struct ofp_hello {
 #define OFP_DEFAULT_MISS_SEND_LEN   128
 
 enum ofp_config_flags {
-    /* Tells datapath to notify the controller of expired flow entries. */
-    OFPC_SEND_FLOW_EXP = 1 << 0,
-
     /* Handling of IP fragments. */
     OFPC_FRAG_NORMAL   = 0 << 1,  /* No special handling for fragments. */
     OFPC_FRAG_DROP     = 1 << 1,  /* Drop fragments. */
@@ -560,27 +557,29 @@ struct ofp_flow_mod {
 };
 OFP_ASSERT(sizeof(struct ofp_flow_mod) == 68);
 
-/* Why did this flow expire? */
-enum ofp_flow_expired_reason {
-    OFPER_IDLE_TIMEOUT,         /* Flow idle time exceeded idle_timeout. */
-    OFPER_HARD_TIMEOUT          /* Time exceeded hard_timeout. */
+/* Why was this flow removed? */
+enum ofp_flow_removed_reason {
+    OFPRR_IDLE_TIMEOUT,         /* Flow idle time exceeded idle_timeout. */
+    OFPRR_HARD_TIMEOUT,         /* Time exceeded hard_timeout. */
+    OFPRR_DELETE                /* Evicted by a DELETE flow mod. */
 };
 
-/* Flow expiration (datapath -> controller). */
-struct ofp_flow_expired {
+/* Flow removed (datapath -> controller). */
+struct ofp_flow_removed {
     struct ofp_header header;
     struct ofp_match match;   /* Description of fields. */
 
     uint16_t priority;        /* Priority level of flow entry. */
-    uint8_t reason;           /* One of OFPER_*. */
+    uint8_t reason;           /* One of OFPRR_*. */
     uint8_t pad[1];           /* Align to 32-bits. */
 
     uint32_t duration;        /* Time flow was alive in seconds. */
-    uint8_t pad2[4];          /* Align to 64-bits. */
+    uint16_t idle_timeout;    /* Idle timeout from original flow mod. */
+    uint8_t pad2[6];          /* Align to 64-bits. */
     uint64_t packet_count;    
     uint64_t byte_count;
 };
-OFP_ASSERT(sizeof(struct ofp_flow_expired) == 76);
+OFP_ASSERT(sizeof(struct ofp_flow_removed) == 80);
 
 /* Values for 'type' in ofp_error_message.  These values are immutable: they
  * will not change in future versions of the protocol (although new values may
index ecfa87f..c4ab48f 100644 (file)
@@ -283,8 +283,8 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
             process_stats_reply
         },
         {
-            OFPT_FLOW_EXPIRED,
-            sizeof(struct ofp_flow_expired),
+            OFPT_FLOW_REMOVED,
+            sizeof(struct ofp_flow_removed),
             NULL
         },
     };
@@ -337,7 +337,6 @@ send_features_request(struct lswitch *sw, struct rconn *rconn)
 
         /* Send OFPT_SET_CONFIG. */
         osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &b);
-        osc->flags = htons(OFPC_SEND_FLOW_EXP);
         osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
         queue_tx(sw, rconn, b);
 
index 1dd269d..6236a96 100644 (file)
@@ -582,10 +582,6 @@ ofp_print_switch_config(struct ds *string, const void *oh, size_t len UNUSED,
     uint16_t flags;
 
     flags = ntohs(osc->flags);
-    if (flags & OFPC_SEND_FLOW_EXP) {
-        flags &= ~OFPC_SEND_FLOW_EXP;
-        ds_put_format(string, " (sending flow expirations)");
-    }
     if (flags) {
         ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags);
     }
@@ -739,41 +735,45 @@ ofp_print_flow_mod(struct ds *string, const void *oh, size_t len,
     default:
         ds_put_format(string, " cmd:%d ", ntohs(ofm->command));
     }
-    ds_put_format(string, "idle:%d hard:%d pri:%d buf:%#x", 
+    ds_put_format(string, "idle:%d hard:%d pri:%d buf:%#x flags:%"PRIx16" ", 
             ntohs(ofm->idle_timeout), ntohs(ofm->hard_timeout),
             ofm->match.wildcards ? ntohs(ofm->priority) : (uint16_t)-1,
-            ntohl(ofm->buffer_id));
+            ntohl(ofm->buffer_id), ntohs(ofm->flags));
     ofp_print_actions(string, ofm->actions,
                       len - offsetof(struct ofp_flow_mod, actions));
     ds_put_char(string, '\n');
 }
 
-/* Pretty-print the OFPT_FLOW_EXPIRED packet of 'len' bytes at 'oh' to 'string'
+/* Pretty-print the OFPT_FLOW_REMOVED packet of 'len' bytes at 'oh' to 'string'
  * at the given 'verbosity' level. */
 static void
-ofp_print_flow_expired(struct ds *string, const void *oh, size_t len UNUSED, 
+ofp_print_flow_removed(struct ds *string, const void *oh, size_t len UNUSED, 
                        int verbosity)
 {
-    const struct ofp_flow_expired *ofe = oh;
+    const struct ofp_flow_removed *ofr = oh;
 
-    ofp_print_match(string, &ofe->match, verbosity);
+    ofp_print_match(string, &ofr->match, verbosity);
     ds_put_cstr(string, " reason=");
-    switch (ofe->reason) {
-    case OFPER_IDLE_TIMEOUT:
+    switch (ofr->reason) {
+    case OFPRR_IDLE_TIMEOUT:
         ds_put_cstr(string, "idle");
         break;
-    case OFPER_HARD_TIMEOUT:
+    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"**", ofe->reason);
+        ds_put_format(string, "**%"PRIu8"**", ofr->reason);
         break;
     }
     ds_put_format(string, 
-         " pri%"PRIu16" secs%"PRIu32" pkts%"PRIu64" bytes%"PRIu64"\n", 
-         ofe->match.wildcards ? ntohs(ofe->priority) : (uint16_t)-1,
-         ntohl(ofe->duration), ntohll(ofe->packet_count), 
-         ntohll(ofe->byte_count));
+         " pri%"PRIu16" secs%"PRIu32" idle%"PRIu16" pkts%"PRIu64
+         " bytes%"PRIu64"\n", 
+         ofr->match.wildcards ? ntohs(ofr->priority) : (uint16_t)-1,
+         ntohl(ofr->duration), ntohs(ofr->idle_timeout),
+         ntohll(ofr->packet_count), ntohll(ofr->byte_count));
 }
 
 static void
@@ -1327,10 +1327,10 @@ static const struct openflow_packet packets[] = {
         ofp_print_flow_mod,
     },
     {
-        OFPT_FLOW_EXPIRED,
-        "flow_expired",
-        sizeof (struct ofp_flow_expired),
-        ofp_print_flow_expired,
+        OFPT_FLOW_REMOVED,
+        "flow_removed",
+        sizeof (struct ofp_flow_removed),
+        ofp_print_flow_removed,
     },
     {
         OFPT_PORT_MOD,
index f25813f..d3fd45a 100644 (file)
@@ -89,6 +89,7 @@ struct rule {
 
     uint16_t idle_timeout;      /* In seconds from time of last use. */
     uint16_t hard_timeout;      /* In seconds from time of creation. */
+    bool send_flow_removed;     /* Send a flow removed message? */
     long long int used;         /* Last-used time (0 if never used). */
     long long int created;      /* Creation time. */
     uint64_t packet_count;      /* Number of packets received. */
@@ -146,7 +147,8 @@ rule_is_hidden(const struct rule *rule)
 
 static struct rule *rule_create(struct ofproto *, struct rule *super,
                                 const union ofp_action *, size_t n_actions,
-                                uint16_t idle_timeout, uint16_t hard_timeout);
+                                uint16_t idle_timeout, uint16_t hard_timeout,
+                                bool send_flow_removed);
 static void rule_free(struct rule *);
 static void rule_destroy(struct ofproto *, struct rule *);
 static struct rule *rule_from_cls_rule(const struct cls_rule *);
@@ -159,12 +161,13 @@ static void rule_install(struct ofproto *, struct rule *,
                          struct rule *displaced_rule);
 static void rule_uninstall(struct ofproto *, struct rule *);
 static void rule_post_uninstall(struct ofproto *, struct rule *);
+static void send_flow_removed(struct ofproto *p, struct rule *rule,
+                              long long int now, uint8_t reason);
 
 struct ofconn {
     struct list node;
     struct rconn *rconn;
     struct pktbuf *pktbuf;
-    bool send_flow_exp;
     int miss_send_len;
 
     struct rconn_packet_counter *packet_in_counter;
@@ -991,7 +994,8 @@ ofproto_add_flow(struct ofproto *p,
 {
     struct rule *rule;
     rule = rule_create(p, NULL, actions, n_actions,
-                       idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 0);
+                       idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 
+                       0, false);
     cls_rule_from_flow(&rule->cr, flow, wildcards, priority);
     rule_insert(p, rule, NULL, 0);
 }
@@ -1320,7 +1324,6 @@ ofconn_create(struct ofproto *p, struct rconn *rconn)
     list_push_back(&p->all_conns, &ofconn->node);
     ofconn->rconn = rconn;
     ofconn->pktbuf = NULL;
-    ofconn->send_flow_exp = false;
     ofconn->miss_send_len = 0;
     ofconn->packet_in_counter = rconn_packet_counter_create ();
     ofconn->reply_counter = rconn_packet_counter_create ();
@@ -1386,12 +1389,14 @@ ofconn_wait(struct ofconn *ofconn)
 static struct rule *
 rule_create(struct ofproto *ofproto, struct rule *super,
             const union ofp_action *actions, size_t n_actions,
-            uint16_t idle_timeout, uint16_t hard_timeout)
+            uint16_t idle_timeout, uint16_t hard_timeout,
+            bool send_flow_removed)
 {
     struct rule *rule = xcalloc(1, sizeof *rule);
     rule->idle_timeout = idle_timeout;
     rule->hard_timeout = hard_timeout;
     rule->used = rule->created = time_msec();
+    rule->send_flow_removed = send_flow_removed;
     rule->super = super;
     if (super) {
         list_push_back(&super->list, &rule->list);
@@ -1551,7 +1556,8 @@ rule_create_subrule(struct ofproto *ofproto, struct rule *rule,
                     const flow_t *flow)
 {
     struct rule *subrule = rule_create(ofproto, rule, NULL, 0,
-                                       rule->idle_timeout, rule->hard_timeout);
+                                       rule->idle_timeout, rule->hard_timeout,
+                                       false);
     COVERAGE_INC(ofproto_subrule_create);
     cls_rule_from_flow(&subrule->cr, flow, 0,
                        (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
@@ -1870,9 +1876,6 @@ handle_get_config_request(struct ofproto *p, struct ofconn *ofconn,
     /* Figure out flags. */
     dpif_get_drop_frags(p->dpif, &drop_frags);
     flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
-    if (ofconn->send_flow_exp) {
-        flags |= OFPC_SEND_FLOW_EXP;
-    }
 
     /* Send reply. */
     osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
@@ -1896,8 +1899,6 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
     }
     flags = ntohs(osc->flags);
 
-    ofconn->send_flow_exp = (flags & OFPC_SEND_FLOW_EXP) != 0;
-
     if (ofconn == p->controller) {
         switch (flags & OFPC_FRAG_MASK) {
         case OFPC_FRAG_NORMAL:
@@ -2803,7 +2804,8 @@ add_flow(struct ofproto *p, struct ofconn *ofconn,
 
     rule = rule_create(p, NULL, (const union ofp_action *) ofm->actions,
                        n_actions, ntohs(ofm->idle_timeout),
-                       ntohs(ofm->hard_timeout));
+                       ntohs(ofm->hard_timeout),
+                       ofm->flags & htons(OFPFF_SEND_FLOW_REM));
     cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
 
     packet = NULL;
@@ -2827,6 +2829,8 @@ modify_flow(struct ofproto *p, const struct ofp_flow_mod *ofm,
     }
 
     if (command == OFPFC_DELETE) {
+        long long int now = time_msec();
+        send_flow_removed(p, rule, now, OFPRR_DELETE);
         rule_remove(p, rule);
     } else {
         size_t actions_len = n_actions * sizeof *rule->actions;
@@ -3260,26 +3264,39 @@ revalidate_rule(struct ofproto *p, struct rule *rule)
 }
 
 static struct ofpbuf *
-compose_flow_exp(const struct rule *rule, long long int now, uint8_t reason)
+compose_flow_removed(const struct rule *rule, long long int now, uint8_t reason)
 {
-    struct ofp_flow_expired *ofe;
+    struct ofp_flow_removed *ofr;
     struct ofpbuf *buf;
     long long int last_used = rule->used ? now - rule->used : 0;
 
-    ofe = make_openflow(sizeof *ofe, OFPT_FLOW_EXPIRED, &buf);
-    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofe->match);
-    ofe->priority = htons(rule->cr.priority);
-    ofe->reason = reason;
-    ofe->duration = htonl((now - rule->created - last_used) / 1000);
-    ofe->packet_count = htonll(rule->packet_count);
-    ofe->byte_count = htonll(rule->byte_count);
+    ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf);
+    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofr->match);
+    ofr->priority = htons(rule->cr.priority);
+    ofr->reason = reason;
+    ofr->duration = htonl((now - rule->created - last_used) / 1000);
+    ofr->idle_timeout = htons(rule->idle_timeout);
+    ofr->packet_count = htonll(rule->packet_count);
+    ofr->byte_count = htonll(rule->byte_count);
 
     return buf;
 }
 
 static void
-send_flow_exp(struct ofproto *p, struct rule *rule,
-              long long int now, uint8_t reason)
+uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule)
+{
+    assert(rule->installed);
+    assert(!rule->cr.wc.wildcards);
+
+    if (rule->super) {
+        rule_remove(ofproto, rule);
+    } else {
+        rule_uninstall(ofproto, rule);
+    }
+}
+static void
+send_flow_removed(struct ofproto *p, struct rule *rule,
+                  long long int now, uint8_t reason)
 {
     struct ofconn *ofconn;
     struct ofconn *prev;
@@ -3293,11 +3310,11 @@ send_flow_exp(struct ofproto *p, struct rule *rule,
 
     prev = NULL;
     LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
-        if (ofconn->send_flow_exp && rconn_is_connected(ofconn->rconn)) {
+        if (rule->send_flow_removed && rconn_is_connected(ofconn->rconn)) {
             if (prev) {
                 queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter);
             } else {
-                buf = compose_flow_exp(rule, now, reason);
+                buf = compose_flow_removed(rule, now, reason);
             }
             prev = ofconn;
         }
@@ -3307,18 +3324,6 @@ send_flow_exp(struct ofproto *p, struct rule *rule,
     }
 }
 
-static void
-uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule)
-{
-    assert(rule->installed);
-    assert(!rule->cr.wc.wildcards);
-
-    if (rule->super) {
-        rule_remove(ofproto, rule);
-    } else {
-        rule_uninstall(ofproto, rule);
-    }
-}
 
 static void
 expire_rule(struct cls_rule *cls_rule, void *p_)
@@ -3361,9 +3366,9 @@ expire_rule(struct cls_rule *cls_rule, void *p_)
     }
 
     if (!rule_is_hidden(rule)) {
-        send_flow_exp(p, rule, now,
-                      (now >= hard_expire
-                       ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
+        send_flow_removed(p, rule, now,
+                          (now >= hard_expire
+                           ? OFPRR_HARD_TIMEOUT : OFPRR_IDLE_TIMEOUT));
     }
     rule_remove(p, rule);
 }
index 109041b..d7f3ad2 100644 (file)
@@ -143,7 +143,7 @@ switch's tables are removed.  See \fBFlow Syntax\fR, below, for the
 syntax of \fIflows\fR.
 
 .TP
-\fBmonitor \fIswitch\fR [\fImiss-len\fR [\fIsend-exp]]
+\fBmonitor \fIswitch\fR [\fImiss-len\fR]
 Connects to \fIswitch\fR and prints to the console all OpenFlow
 messages received.  Usually, \fIswitch\fR should specify a connection
 named on \fBovs\-openflowd\fR(8)'s \fB-l\fR or \fB--listen\fR command line
@@ -156,11 +156,6 @@ OpenFlow reference implementation does not send these messages to the
 \fBovs\-ofctl monitor\fR client connection unless a nonzero value is
 specified on this argument.
 
-If \fIsend-exp\fR is specified as \fB1\fR, \fBovs\-ofctl\fR will also
-request to be sent flow expiration messages.  If this argument is
-omitted, or \fB0\fR is specified, then \fRovs\-ofctl\fR will not request
-flow expirations.
-
 This command may be useful for debugging switch or controller
 implementations.
 
index fa2b5d9..7d6a600 100644 (file)
@@ -999,12 +999,10 @@ do_monitor(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
     open_vconn(argv[1], &vconn);
     if (argc > 2) {
         int miss_send_len = atoi(argv[2]);
-        int send_flow_exp = argc > 3 ? atoi(argv[3]) : 0;
         struct ofp_switch_config *osc;
         struct ofpbuf *buf;
 
         osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf);
-        osc->flags = htons(send_flow_exp ? OFPC_SEND_FLOW_EXP : 0);
         osc->miss_send_len = htons(miss_send_len);
         send_openflow_buffer(vconn, buf);
     }