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, 20 Feb 2010 10:22:27 +0000 (02:22 -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 a9199c71fa0c0d4217ae1c4cdf072c6c7173a83b..511cdb713f6b2a8707ef1ddff755ed5ff1a75f3c 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 074a056e508fade5f9810b67e394cc0017f3949e..260f5d76abd81055c2a56573a0980b5effd6e8d1 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 ced57c4f833211bc3fc32ae64e2d550af0aa06e0..926531ec4d4fa856120f8f6ca5e55b0b2a617b82 100644 (file)
@@ -582,10 +582,6 @@ ofp_print_switch_config(struct ds *string, const void *oh,
     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,
+ofp_print_flow_removed(struct ds *string, const void *oh, 
                        size_t len OVS_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 6bdbc73f71a96b2a8a7d670da0a20e0033f44b91..f80439c8c279bac7be8d1a1ea9fc81adc35d0958 100644 (file)
@@ -85,6 +85,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. */
@@ -142,7 +143,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 *);
@@ -155,12 +157,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;
@@ -981,7 +984,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);
 }
@@ -1331,7 +1335,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 ();
@@ -1393,12 +1396,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 = xzalloc(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);
@@ -1558,7 +1563,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
@@ -1877,9 +1883,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);
@@ -1903,8 +1906,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:
@@ -2810,7 +2811,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));
 
     error = 0;
@@ -2836,6 +2838,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;
@@ -3224,25 +3228,38 @@ 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;
 
-    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) / 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) / 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;
@@ -3256,11 +3273,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;
         }
@@ -3270,18 +3287,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_)
@@ -3324,9 +3329,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 c890980582dcec4a8c0fa0598f617c42d89c9177..978dd1e0722bba818806c727bd506c15e43e2c16 100644 (file)
@@ -136,7 +136,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
@@ -149,11 +149,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 9a3f8c70aaff3a20d9241b72e070dfe8e60c0415..9dd80b1165a154be75cf4b4f255aff00eaa6019d 100644 (file)
@@ -966,12 +966,10 @@ do_monitor(int argc OVS_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);
     }