[ovs-dev] [PATCH v8] ofproto: Implement OF1.4 Group & Meter change notification messages

Ben Pfaff blp at nicira.com
Wed Sep 9 20:18:08 UTC 2015


On Wed, Sep 09, 2015 at 05:33:42PM +0530, niti1489 at gmail.com wrote:
> From: Niti Rohilla <niti.rohilla at tcs.com>
> 
> This patch adds support for Openflow1.4 Group & meter change notification
> messages. In a multi controller environment, when a controller modifies the
> state of group and meter table, the request that successfully modifies this
> state is forwarded to other controllers. Other controllers are informed with
> the OFPT_REQUESTFORWARD message. Request forwarding is enabled on a per
> controller channel basis using the Set Asynchronous Configuration Message.
> 
> Signed-off-by: Niti Rohilla <niti.rohilla at tcs.com>
> ---
> Difference between v7->v8:
> - Memory leak issue in ofputil_re_encode_requestforward() has been resolved.
> - Modified the test case so that ofputil_re_encode_requestforward() get
>   excercised in the testsuite to re-encode the request.
> - Rebased with the latest master.

After I looked at this for a while, I realized that there was more
decoding than necessary.  It shouldn't be necessary to decode a message
once to execute it, then decode it again to reencode it inside
ofputil_re_encode_requestforward().  That even introduces a possibility
for failure the second time (though it shouldn't happen).

I decided that it's better, therefore, to make ofputil_requestforward
include the decoded form of the inner message instead of the raw form,
like this:

struct ofputil_requestforward {
    ovs_be32 xid;
    enum ofp14_requestforward_reason reason;
    union {
        /* reason == OFPRFR_METER_MOD. */
        struct ofputil_meter_mod *meter_mod;

        /* reason == OFPRFR_GROUP_MOD. */
        struct {
            struct ofputil_group_mod *group_mod;
            struct ofpbuf bands;
        };
    };
};

This required further changes elsewhere, but they were straightforward.

Anyway, I folded in the following incremental and applied this to
master.  Thanks!

diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index b2d3b4f..d0c94ce 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -1185,21 +1185,9 @@ ofp_print_meter_config(struct ds *s, const struct ofputil_meter_config *mc)
 }
 
 static void
-ofp_print_meter_mod(struct ds *s, const struct ofp_header *oh)
+ofp_print_meter_mod__(struct ds *s, const struct ofputil_meter_mod *mm)
 {
-    struct ofputil_meter_mod mm;
-    struct ofpbuf bands;
-    enum ofperr error;
-
-    ofpbuf_init(&bands, 64);
-    error = ofputil_decode_meter_mod(oh, &mm, &bands);
-    if (error) {
-        ofpbuf_uninit(&bands);
-        ofp_print_error(s, error);
-        return;
-    }
-
-    switch (mm.command) {
+    switch (mm->command) {
     case OFPMC13_ADD:
         ds_put_cstr(s, " ADD ");
         break;
@@ -1210,10 +1198,26 @@ ofp_print_meter_mod(struct ds *s, const struct ofp_header *oh)
         ds_put_cstr(s, " DEL ");
         break;
     default:
-        ds_put_format(s, " cmd:%d ", mm.command);
+        ds_put_format(s, " cmd:%d ", mm->command);
     }
 
-    ofp_print_meter_config(s, &mm.meter);
+    ofp_print_meter_config(s, &mm->meter);
+}
+
+static void
+ofp_print_meter_mod(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofputil_meter_mod mm;
+    struct ofpbuf bands;
+    enum ofperr error;
+
+    ofpbuf_init(&bands, 64);
+    error = ofputil_decode_meter_mod(oh, &mm, &bands);
+    if (error) {
+        ofp_print_error(s, error);
+    } else {
+        ofp_print_meter_mod__(s, &mm);
+    }
     ofpbuf_uninit(&bands);
 }
 
@@ -2333,7 +2337,8 @@ ofp_print_bucket_id(struct ds *s, const char *label, uint32_t bucket_id,
 
 static void
 ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
-                struct ovs_list *p_buckets, struct ofputil_group_props *props,
+                const struct ovs_list *p_buckets,
+                const struct ofputil_group_props *props,
                 enum ofp_version ofp_version, bool suppress_type)
 {
     struct ofputil_bucket *bucket;
@@ -2529,22 +2534,15 @@ ofp_print_group_features(struct ds *string, const struct ofp_header *oh)
 }
 
 static void
-ofp_print_group_mod(struct ds *s, const struct ofp_header *oh)
+ofp_print_group_mod__(struct ds *s, enum ofp_version ofp_version,
+                      const struct ofputil_group_mod *gm)
 {
-    struct ofputil_group_mod gm;
-    int error;
     bool bucket_command = false;
 
-    error = ofputil_decode_group_mod(oh, &gm);
-    if (error) {
-        ofp_print_error(s, error);
-        return;
-    }
-
     ds_put_char(s, '\n');
 
     ds_put_char(s, ' ');
-    switch (gm.command) {
+    switch (gm->command) {
     case OFPGC11_ADD:
         ds_put_cstr(s, "ADD");
         break;
@@ -2568,17 +2566,31 @@ ofp_print_group_mod(struct ds *s, const struct ofp_header *oh)
         break;
 
     default:
-        ds_put_format(s, "cmd:%"PRIu16"", gm.command);
+        ds_put_format(s, "cmd:%"PRIu16"", gm->command);
     }
     ds_put_char(s, ' ');
 
     if (bucket_command) {
         ofp_print_bucket_id(s, "command_bucket_id:",
-                            gm.command_bucket_id, oh->version);
+                            gm->command_bucket_id, ofp_version);
     }
 
-    ofp_print_group(s, gm.group_id, gm.type, &gm.buckets, &gm.props,
-                    oh->version, bucket_command);
+    ofp_print_group(s, gm->group_id, gm->type, &gm->buckets, &gm->props,
+                    ofp_version, bucket_command);
+}
+
+static void
+ofp_print_group_mod(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofputil_group_mod gm;
+    int error;
+
+    error = ofputil_decode_group_mod(oh, &gm);
+    if (error) {
+        ofp_print_error(s, error);
+        return;
+    }
+    ofp_print_group_mod__(s, oh->version, &gm);
     ofputil_bucket_list_destroy(&gm.buckets);
 }
 
@@ -3057,7 +3069,6 @@ ofp_print_requestforward(struct ds *string, const struct ofp_header *oh)
 {
     struct ofputil_requestforward rf;
     enum ofperr error;
-    enum ofptype type;
 
     error = ofputil_decode_requestforward(oh, &rf);
     if (error) {
@@ -3065,21 +3076,20 @@ ofp_print_requestforward(struct ds *string, const struct ofp_header *oh)
         return;
     }
 
-    error = ofptype_decode(&type, rf.request);
-    if (error) {
-        ofp_print_error(string, error);
-        return;
-    }
-
     ds_put_cstr(string, " reason=");
 
-    if (type == OFPTYPE_GROUP_MOD) {
+    switch (rf.reason) {
+    case OFPRFR_GROUP_MOD:
         ds_put_cstr(string, "group_mod");
-        ofp_print_group_mod(string, rf.request);
-    } else if (type == OFPTYPE_METER_MOD) {
+        ofp_print_group_mod__(string, oh->version, rf.group_mod);
+        break;
+
+    case OFPRFR_METER_MOD:
         ds_put_cstr(string, "meter_mod");
-        ofp_print_meter_mod(string, rf.request);
+        ofp_print_meter_mod__(string, rf.meter_mod);
+        break;
     }
+    ofputil_destroy_requestforward(&rf);
 }
 
 static void
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 05ee6bb..d90cca8 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -5376,106 +5376,125 @@ ofputil_decode_role_status(const struct ofp_header *oh,
     return 0;
 }
 
-/* Re-encodes the "GROUP_MOD" or "METER_MOD" request header for the
- * 'protocol' in the ofconn. */
-enum ofperr
-ofputil_re_encode_requestforward(uint8_t reason,
-                                 const struct ofp_header *request,
-                                 struct ofp_header **requestp,
-                                 enum ofputil_protocol protocol)
+/* Encodes 'rf' according to 'protocol', and returns the encoded message.
+ * 'protocol' must be for OpenFlow 1.4 or later. */
+struct ofpbuf *
+ofputil_encode_requestforward(const struct ofputil_requestforward *rf,
+                              enum ofputil_protocol protocol)
 {
-    enum ofp_version version;
-    struct ofp_header *rf;
-    struct ofpbuf *buf;
-    enum ofperr error;
-
-    version = ofputil_protocol_to_ofp_version(protocol);
-    switch (reason) {
-    case OFPRFR_GROUP_MOD: {
-        struct ofputil_group_mod gm;
+    enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
+    struct ofpbuf *inner;
 
-        error = ofputil_decode_group_mod(request, &gm);
-        if (error) {
-            return error;
-        }
-        buf = ofputil_encode_group_mod(version, &gm);
-        ofputil_bucket_list_destroy(&gm.buckets);
+    switch (rf->reason) {
+    case OFPRFR_GROUP_MOD:
+        inner = ofputil_encode_group_mod(ofp_version, rf->group_mod);
         break;
-    }
-    case OFPRFR_METER_MOD: {
-        struct ofputil_meter_mod mm;
-        struct ofpbuf bands;
 
-        ofpbuf_init(&bands, 64);
-        error = ofputil_decode_meter_mod(request, &mm, &bands);
-        if (error) {
-            ofpbuf_uninit(&bands);
-            return error;
-        }
-        buf = ofputil_encode_meter_mod(version, &mm);
-        ofpbuf_uninit(&bands);
+    case OFPRFR_METER_MOD:
+        inner = ofputil_encode_meter_mod(ofp_version, rf->meter_mod);
         break;
-    }
+
     default:
         OVS_NOT_REACHED();
     }
-    rf = buf->data;
-    rf->length = htons(buf->size);
-    rf->xid = request->xid;
-    *requestp = rf;
-    return 0;
-}
 
-/* Encodes "request forward" message encapsulating "GROUP_MOD" or "METER_MOD"
- * request in 'rf'. The OpenFlow version of OFPT_REQUESTFORWARD message and
- * 'rf->request' should be same.
- * 'rf->request' is not converted from host to network byte order because
- * "GROUP_MOD" or "METER_MOD" request header in 'rf' is directly encapsulated
- * in request forward message. */
-struct ofpbuf *
-ofputil_encode_requestforward(const struct ofputil_requestforward *rf)
-{
-    struct ofpbuf *buf;
+    struct ofp_header *inner_oh = inner->data;
+    inner_oh->xid = rf->xid;
+    inner_oh->length = htons(inner->size);
 
-    buf = ofpraw_alloc_xid(OFPRAW_OFPT14_REQUESTFORWARD, rf->request->version,
-                           htonl(0), ntohs(rf->request->length));
-    ofpbuf_put(buf, rf->request, ntohs(rf->request->length));
-    return buf;
+    struct ofpbuf *outer = ofpraw_alloc_xid(OFPRAW_OFPT14_REQUESTFORWARD,
+                                            ofp_version, htonl(0),
+                                            inner->size);
+    ofpbuf_put(outer, inner->data, inner->size);
+    ofpbuf_delete(inner);
+
+    return outer;
 }
 
-/* Converts OpenFlow request forward  message 'oh' into an abstract request
- * forward in 'rf'. Returns 0 if successful, otherwise an OpenFlow error
- * code. */
+/* Decodes OFPT_REQUESTFORWARD message 'outer'.  On success, puts the decoded
+ * form into '*rf' and returns 0, and the caller is later responsible for
+ * freeing the content of 'rf', with ofputil_destroy_requestforward(rf).  On
+ * failure, returns an ofperr and '*rf' is indeterminate. */
 enum ofperr
-ofputil_decode_requestforward(const struct ofp_header *oh,
+ofputil_decode_requestforward(const struct ofp_header *outer,
                               struct ofputil_requestforward *rf)
 {
     struct ofpbuf b;
-    enum ofpraw raw;
     enum ofperr error;
-    size_t inner_len;
 
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    error = ofpraw_pull(&raw, &b);
-    if (error) {
-        return error;
-    }
-
-    ovs_assert(raw == OFPRAW_OFPT14_REQUESTFORWARD);
+    ofpbuf_use_const(&b, outer, ntohs(outer->length));
 
-    rf->request = b.data;
+    /* Skip past outer message. */
+    enum ofpraw outer_raw = ofpraw_pull_assert(&b);
+    ovs_assert(outer_raw == OFPRAW_OFPT14_REQUESTFORWARD);
 
-    if (rf->request->version != oh->version) {
-        return OFPERR_OFPBRC_BAD_VERSION;
+    /* Validate inner message. */
+    if (b.size < sizeof(struct ofp_header)) {
+        return OFPERR_OFPBFC_MSG_BAD_LEN;
     }
-    inner_len = ntohs(rf->request->length);
+    const struct ofp_header *inner = b.data;
+    unsigned int inner_len = ntohs(inner->length);
     if (inner_len < sizeof(struct ofp_header) || inner_len > b.size) {
         return OFPERR_OFPBFC_MSG_BAD_LEN;
     }
+    if (inner->version != outer->version) {
+        return OFPERR_OFPBRC_BAD_VERSION;
+    }
+
+    /* Parse inner message. */
+    enum ofptype type;
+    error = ofptype_decode(&type, inner);
+    if (error) {
+        return error;
+    }
+
+    rf->xid = inner->xid;
+    if (type == OFPTYPE_GROUP_MOD) {
+        rf->reason = OFPRFR_GROUP_MOD;
+        rf->group_mod = xmalloc(sizeof *rf->group_mod);
+        error = ofputil_decode_group_mod(inner, rf->group_mod);
+        if (error) {
+            free(rf->group_mod);
+            return error;
+        }
+    } else if (type == OFPTYPE_METER_MOD) {
+        rf->reason = OFPRFR_METER_MOD;
+        rf->meter_mod = xmalloc(sizeof *rf->meter_mod);
+        ofpbuf_init(&rf->bands, 64);
+        error = ofputil_decode_meter_mod(inner, rf->meter_mod, &rf->bands);
+        if (error) {
+            free(rf->meter_mod);
+            ofpbuf_uninit(&rf->bands);
+            return error;
+        }
+    } else {
+        return OFPERR_OFPBFC_MSG_UNSUP;
+    }
 
     return 0;
 }
 
+/* Frees the content of 'rf', which should have been initialized through a
+ * successful call to ofputil_decode_requestforward(). */
+void
+ofputil_destroy_requestforward(struct ofputil_requestforward *rf)
+{
+    if (!rf) {
+        return;
+    }
+
+    switch (rf->reason) {
+    case OFPRFR_GROUP_MOD:
+        ofputil_uninit_group_mod(rf->group_mod);
+        free(rf->group_mod);
+        break;
+
+    case OFPRFR_METER_MOD:
+        ofpbuf_uninit(&rf->bands);
+        free(rf->meter_mod);
+    }
+}
+
 /* Table stats. */
 
 /* OpenFlow 1.0 and 1.1 don't distinguish between a field that cannot be
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 7589f51..cbd6175 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -861,22 +861,6 @@ struct ofpbuf *ofputil_encode_role_status(
 enum ofperr ofputil_decode_role_status(const struct ofp_header *oh,
                                        struct ofputil_role_status *rs);
 
-struct ofputil_requestforward {
-    const struct ofp_header *request;
-};
-
-enum ofperr
-ofputil_re_encode_requestforward(uint8_t reason,
-                                 const struct ofp_header *request,
-                                 struct ofp_header **requestp,
-                                 enum ofputil_protocol protocol);
-struct ofpbuf *
-ofputil_encode_requestforward(const struct ofputil_requestforward *rf);
-
-enum ofperr
-ofputil_decode_requestforward(const struct ofp_header *oh,
-                              struct ofputil_requestforward *rf);
-
 /* Abstract table stats.
  *
  * This corresponds to the OpenFlow 1.3 table statistics structure, which only
@@ -1255,4 +1239,25 @@ struct ofpbuf *ofputil_encode_get_async_config(const struct ofp_header *,
                                                uint32_t master[OAM_N_TYPES],
                                                uint32_t slave[OAM_N_TYPES]);
 
+struct ofputil_requestforward {
+    ovs_be32 xid;
+    enum ofp14_requestforward_reason reason;
+    union {
+        /* reason == OFPRFR_METER_MOD. */
+        struct ofputil_meter_mod *meter_mod;
+
+        /* reason == OFPRFR_GROUP_MOD. */
+        struct {
+            struct ofputil_group_mod *group_mod;
+            struct ofpbuf bands;
+        };
+    };
+};
+
+struct ofpbuf *ofputil_encode_requestforward(
+    const struct ofputil_requestforward *, enum ofputil_protocol);
+enum ofperr ofputil_decode_requestforward(const struct ofp_header *,
+                                          struct ofputil_requestforward *);
+void ofputil_destroy_requestforward(struct ofputil_requestforward *);
+
 #endif /* ofp-util.h */
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 60f3d9a..ef2c06f 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -1714,46 +1714,21 @@ connmgr_send_port_status(struct connmgr *mgr, struct ofconn *source,
  * controller OFPT_GROUP_MOD and OFPT_METER_MOD, specify 'source' as the
  * controller connection that sent the request; otherwise, specify 'source'
  * as NULL. */
-enum ofperr
-connmgr_send_requestforward(struct connmgr *mgr, struct ofconn *source,
-                            uint8_t reason, const struct ofp_header *oh)
+void
+connmgr_send_requestforward(struct connmgr *mgr, const struct ofconn *source,
+                            const struct ofputil_requestforward *rf)
 {
-    struct ofputil_requestforward rf;
     struct ofconn *ofconn;
-    struct ofp_header *request = NULL;
-    enum ofperr error;
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        if (ofconn_receives_async_msg(ofconn, OAM_REQUESTFORWARD, reason)) {
-            struct ofpbuf *msg;
-
-            rf.request = oh;
-            /* For OpenFlow 1.4 and later, it generates OFPT_REQUESTFORWARD
-             * for OFPT_GROUP_MOD and OFPT_METER_MOD, but not back to the
-             * originating controller.  In a single-controller environment, in
-             * particular, this means that it will never generate
-             * OFPT_REQUESTFORWARD for OFPT_GROUP_MOD and OFPT_METER_MOD at
-             * all. */
-            if (rconn_get_version(ofconn->rconn) < OFP14_VERSION
-                || ofconn == source) {
-                continue;
-            }
-            /* When the version of GROUP_MOD or METER_MOD request is not same
-             * as that of ofconn, then re-encode the GROUP_MOD or METER_MOD
-             * request for the protocol in use. */
-            if (oh->version != rconn_get_version(ofconn->rconn)) {
-                error = ofputil_re_encode_requestforward(reason, oh, &request,
-                                                  ofconn_get_protocol(ofconn));
-                if (error) {
-                    return error;
-                }
-                rf.request = request;
-            }
-            msg = ofputil_encode_requestforward(&rf);
-            ofconn_send(ofconn, msg, NULL);
+        if (ofconn_receives_async_msg(ofconn, OAM_REQUESTFORWARD, rf->reason)
+            && rconn_get_version(ofconn->rconn) >= OFP14_VERSION
+            && ofconn != source) {
+            enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
+            ofconn_send(ofconn, ofputil_encode_requestforward(rf, protocol),
+                        NULL);
         }
     }
-    return 0;
 }
 
 /* Sends an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message based on 'fr' to
diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
index 2de7f6f..8048424 100644
--- a/ofproto/connmgr.h
+++ b/ofproto/connmgr.h
@@ -168,9 +168,8 @@ void connmgr_send_packet_in(struct connmgr *,
 void ofconn_send_role_status(struct ofconn *ofconn, uint32_t role,
                              uint8_t reason);
 
-enum ofperr
-connmgr_send_requestforward(struct connmgr *, struct ofconn *source,
-                            uint8_t reason, const struct ofp_header *);
+void connmgr_send_requestforward(struct connmgr *, const struct ofconn *source,
+                                 const struct ofputil_requestforward *);
 
 /* Fail-open settings. */
 enum ofproto_fail_mode connmgr_get_fail_mode(const struct connmgr *);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 99b743c..c1301b5 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -5909,8 +5909,11 @@ handle_meter_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     }
 
     if (!error) {
-        error = connmgr_send_requestforward(ofproto->connmgr, ofconn,
-                                           OFPRFR_METER_MOD, oh);
+        struct ofputil_requestforward rf;
+        rf.xid = oh->xid;
+        rf.reason = OFPRFR_METER_MOD;
+        rf.meter_mod = &mm;
+        connmgr_send_requestforward(ofproto->connmgr, ofconn, &rf);
     }
 
 exit_free_bands:
@@ -6609,8 +6612,11 @@ handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     }
 
     if (!error) {
-        error = connmgr_send_requestforward(ofproto->connmgr, ofconn,
-                                            OFPRFR_GROUP_MOD, oh);
+        struct ofputil_requestforward rf;
+        rf.xid = oh->xid;
+        rf.reason = OFPRFR_GROUP_MOD;
+        rf.group_mod = &gm;
+        connmgr_send_requestforward(ofproto->connmgr, ofconn, &rf);
     }
     return error;
 }
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 38d8ba6..7e80293 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2904,7 +2904,7 @@ dnl command is sent from one controller and the reply is received by
 dnl other controllers.
 AT_SETUP([ofproto - requestforward (OpenFlow 1.4)])
 OVS_VSWITCHD_START
-ON_EXIT([kill `cat c1.pid c2.pid c3.pid`])
+on_exit 'kill `cat c1.pid c2.pid c3.pid`'
 
 # Start two ovs-ofctl controller processes.
 AT_CAPTURE_FILE([monitor1.log])



More information about the dev mailing list