[ovs-dev] [PATCH v4 05/14] Support userdata in NXT_PACKET_IN2.

Ben Pfaff blp at ovn.org
Fri Feb 19 08:34:15 UTC 2016


Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 NEWS                          |   3 +-
 include/openflow/nicira-ext.h |   1 +
 lib/ofp-actions.c             | 168 ++++++++++++++++++++++++++++++++++++++----
 lib/ofp-actions.h             |   9 ++-
 lib/ofp-print.c               |  11 +++
 lib/ofp-util.c                |   9 +++
 lib/ofp-util.h                |   4 +
 ofproto/connmgr.c             |   1 +
 ofproto/ofproto-dpif-xlate.c  |  18 +++--
 ofproto/ofproto-dpif.c        |   1 +
 tests/ofp-actions.at          |   7 ++
 tests/ofp-print.at            |   7 +-
 tests/ofproto.at              |   7 +-
 utilities/ovs-ofctl.8.in      |  20 +++--
 14 files changed, 232 insertions(+), 34 deletions(-)

diff --git a/NEWS b/NEWS
index 696168b..9ab6cae 100644
--- a/NEWS
+++ b/NEWS
@@ -5,7 +5,8 @@ Post-v2.5.0
    - OpenFlow:
      * OpenFlow 1.1+ OFPT_QUEUE_GET_CONFIG_REQUEST now supports OFPP_ANY.
      * OpenFlow 1.4+ OFPMP_QUEUE_DESC is now supported.
-     * New property-based packet-in message format NXT_PACKET_IN2.
+     * New property-based packet-in message format NXT_PACKET_IN2 with support
+       for arbitrary user-provided data.
    - ovs-ofctl:
      * queue-get-config command now allows a queue ID to be specified.
    - DPDK:
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index bcc8758..7e56066 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -279,6 +279,7 @@ enum nx_packet_in2_prop_type {
     /* Other. */
     NXPINT_REASON,              /* uint8_t, one of OFPR_*. */
     NXPINT_METADATA,            /* NXM or OXM for metadata fields. */
+    NXPINT_USERDATA,            /* From NXAST_CONTROLLER2 userdata. */
 };
 
 /* Configures the "role" of the sending controller.  The default role is:
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 7dc852e..38011c3 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -30,6 +30,7 @@
 #include "nx-match.h"
 #include "odp-netlink.h"
 #include "ofp-parse.h"
+#include "ofp-prop.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "unaligned.h"
@@ -273,6 +274,8 @@ enum ofp_raw_action_type {
 
     /* NX1.0+(20): struct nx_action_controller. */
     NXAST_RAW_CONTROLLER,
+    /* NX1.0+(37): struct nx_action_controller2, ... */
+    NXAST_RAW_CONTROLLER2,
 
     /* NX1.0+(22): struct nx_action_write_metadata. */
     NXAST_RAW_WRITE_METADATA,
@@ -625,6 +628,27 @@ struct nx_action_controller {
 };
 OFP_ASSERT(sizeof(struct nx_action_controller) == 16);
 
+/* Properties for NXAST_CONTROLLER2. */
+enum nx_action_controller2_prop_type {
+    NXAC2PT_MAX_LEN,            /* ovs_be16 max length to send controller. */
+    NXAC2PT_CONTROLLER_ID,      /* ovs_be16 controller ID of destination. */
+    NXAC2PT_REASON,             /* uint8_t reason (OFPR_*). */
+    NXAC2PT_USERDATA,           /* Data to copy into NXPINT_USERDATA. */
+};
+
+/* Action structure for NXAST_CONTROLLER2.
+ *
+ * This replacement for NXAST_CONTROLLER makes it extensible via properties. */
+struct nx_action_controller2 {
+    ovs_be16 type;                  /* OFPAT_VENDOR. */
+    ovs_be16 len;                   /* Length is 16 or more. */
+    ovs_be32 vendor;                /* NX_VENDOR_ID. */
+    ovs_be16 subtype;               /* NXAST_CONTROLLER2. */
+    uint8_t zeros[6];               /* Must be zero. */
+    /* Followed by NXAC2PT_* properties. */
+};
+OFP_ASSERT(sizeof(struct nx_action_controller2) == 16);
+
 static enum ofperr
 decode_NXAST_RAW_CONTROLLER(const struct nx_action_controller *nac,
                             enum ofp_version ofp_version OVS_UNUSED,
@@ -633,9 +657,77 @@ decode_NXAST_RAW_CONTROLLER(const struct nx_action_controller *nac,
     struct ofpact_controller *oc;
 
     oc = ofpact_put_CONTROLLER(out);
+    oc->ofpact.raw = NXAST_RAW_CONTROLLER;
     oc->max_len = ntohs(nac->max_len);
     oc->controller_id = ntohs(nac->controller_id);
     oc->reason = nac->reason;
+    ofpact_finish(out, &oc->ofpact);
+
+    return 0;
+}
+
+static enum ofperr
+decode_NXAST_RAW_CONTROLLER2(const struct nx_action_controller2 *nac2,
+                             enum ofp_version ofp_version OVS_UNUSED,
+                             struct ofpbuf *out)
+{
+    if (!is_all_zeros(nac2->zeros, sizeof nac2->zeros)) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
+
+    size_t start_ofs = out->size;
+    struct ofpact_controller *oc = ofpact_put_CONTROLLER(out);
+    oc->ofpact.raw = NXAST_RAW_CONTROLLER2;
+    oc->max_len = UINT16_MAX;
+    oc->reason = OFPR_ACTION;
+
+    struct ofpbuf properties;
+    ofpbuf_use_const(&properties, nac2, ntohs(nac2->len));
+    ofpbuf_pull(&properties, sizeof *nac2);
+
+    while (properties.size > 0) {
+        struct ofpbuf payload;
+        uint64_t type;
+
+        enum ofperr error = ofpprop_pull(&properties, &payload, &type);
+        if (error) {
+            return error;
+        }
+
+        switch (type) {
+        case NXAC2PT_MAX_LEN:
+            error = ofpprop_parse_u16(&payload, &oc->max_len);
+            break;
+
+        case NXAC2PT_CONTROLLER_ID:
+            error = ofpprop_parse_u16(&payload, &oc->controller_id);
+            break;
+
+        case NXAC2PT_REASON: {
+            uint8_t u8;
+            error = ofpprop_parse_u8(&payload, &u8);
+            oc->reason = u8;
+            break;
+        }
+
+        case NXAC2PT_USERDATA:
+            out->size = start_ofs + OFPACT_CONTROLLER_SIZE;
+            ofpbuf_put(out, payload.msg, ofpbuf_msgsize(&payload));
+            oc = ofpbuf_at_assert(out, start_ofs, sizeof *oc);
+            oc->userdata_len = ofpbuf_msgsize(&payload);
+            break;
+
+        default:
+            error = OFPPROP_UNKNOWN(false, "NXAST_RAW_CONTROLLER2", type);
+            break;
+        }
+        if (error) {
+            return error;
+        }
+    }
+
+    ofpact_finish(out, &oc->ofpact);
+
     return 0;
 }
 
@@ -644,12 +736,33 @@ encode_CONTROLLER(const struct ofpact_controller *controller,
                   enum ofp_version ofp_version OVS_UNUSED,
                   struct ofpbuf *out)
 {
-    struct nx_action_controller *nac;
+    if (controller->userdata_len
+        || controller->ofpact.raw == NXAST_RAW_CONTROLLER2) {
+        size_t start_ofs = out->size;
+        put_NXAST_CONTROLLER2(out);
+        if (controller->max_len != UINT16_MAX) {
+            ofpprop_put_u16(out, NXAC2PT_MAX_LEN, controller->max_len);
+        }
+        if (controller->controller_id != 0) {
+            ofpprop_put_u16(out, NXAC2PT_CONTROLLER_ID,
+                            controller->controller_id);
+        }
+        if (controller->reason != OFPR_ACTION) {
+            ofpprop_put_u8(out, NXAC2PT_REASON, controller->reason);
+        }
+        if (controller->userdata_len != 0) {
+            ofpprop_put(out, NXAC2PT_USERDATA, controller->userdata,
+                        controller->userdata_len);
+        }
+        pad_ofpat(out, start_ofs);
+    } else {
+        struct nx_action_controller *nac;
 
-    nac = put_NXAST_CONTROLLER(out);
-    nac->max_len = htons(controller->max_len);
-    nac->controller_id = htons(controller->controller_id);
-    nac->reason = controller->reason;
+        nac = put_NXAST_CONTROLLER(out);
+        nac->max_len = htons(controller->max_len);
+        nac->controller_id = htons(controller->controller_id);
+        nac->reason = controller->reason;
+    }
 }
 
 static char * OVS_WARN_UNUSED_RESULT
@@ -659,6 +772,7 @@ parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts,
     enum ofp_packet_in_reason reason = OFPR_ACTION;
     uint16_t controller_id = 0;
     uint16_t max_len = UINT16_MAX;
+    const char *userdata = NULL;
 
     if (!arg[0]) {
         /* Use defaults. */
@@ -685,6 +799,8 @@ parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts,
                 if (error) {
                     return error;
                 }
+            } else if (!strcmp(name, "userdata")) {
+                userdata = value;
             } else {
                 return xasprintf("unknown key \"%s\" parsing controller "
                                  "action", name);
@@ -692,7 +808,7 @@ parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts,
         }
     }
 
-    if (reason == OFPR_ACTION && controller_id == 0) {
+    if (reason == OFPR_ACTION && controller_id == 0 && !userdata) {
         struct ofpact_output *output;
 
         output = ofpact_put_OUTPUT(ofpacts);
@@ -705,15 +821,39 @@ parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts,
         controller->max_len = max_len;
         controller->reason = reason;
         controller->controller_id = controller_id;
+
+        if (userdata) {
+            size_t start_ofs = ofpacts->size;
+            const char *end = ofpbuf_put_hex(ofpacts, userdata, NULL);
+            if (*end) {
+                return xstrdup("bad hex digit in `controller' "
+                               "action `userdata'");
+            }
+            size_t userdata_len = ofpacts->size - start_ofs;
+            controller = ofpacts->header;
+            controller->userdata_len = userdata_len;
+        }
+        ofpact_finish(ofpacts, &controller->ofpact);
     }
 
     return NULL;
 }
 
 static void
+format_hex_arg(struct ds *s, const uint8_t *data, size_t len)
+{
+    for (size_t i = 0; i < len; i++) {
+        if (i) {
+            ds_put_char(s, '.');
+        }
+        ds_put_format(s, "%02"PRIx8, data[i]);
+    }
+}
+
+static void
 format_CONTROLLER(const struct ofpact_controller *a, struct ds *s)
 {
-    if (a->reason == OFPR_ACTION && a->controller_id == 0) {
+    if (a->reason == OFPR_ACTION && !a->controller_id && !a->userdata_len) {
         ds_put_format(s, "CONTROLLER:%"PRIu16, a->max_len);
     } else {
         enum ofp_packet_in_reason reason = a->reason;
@@ -732,6 +872,11 @@ format_CONTROLLER(const struct ofpact_controller *a, struct ds *s)
         if (a->controller_id != 0) {
             ds_put_format(s, "id=%"PRIu16",", a->controller_id);
         }
+        if (a->userdata_len) {
+            ds_put_cstr(s, "userdata=");
+            format_hex_arg(s, a->userdata, a->userdata_len);
+            ds_put_char(s, ',');
+        }
         ds_chomp(s, ',');
         ds_put_char(s, ')');
     }
@@ -4400,15 +4545,8 @@ parse_NOTE(const char *arg, struct ofpbuf *ofpacts,
 static void
 format_NOTE(const struct ofpact_note *a, struct ds *s)
 {
-    size_t i;
-
     ds_put_cstr(s, "note:");
-    for (i = 0; i < a->length; i++) {
-        if (i) {
-            ds_put_char(s, '.');
-        }
-        ds_put_format(s, "%02"PRIx8, a->data[i]);
-    }
+    format_hex_arg(s, a->data, a->length);
 }
 
 /* Exit action. */
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index 5dec177..d4125e6 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -56,7 +56,7 @@
     /* Output. */                                                       \
     OFPACT(OUTPUT,          ofpact_output,      ofpact, "output")       \
     OFPACT(GROUP,           ofpact_group,       ofpact, "group")        \
-    OFPACT(CONTROLLER,      ofpact_controller,  ofpact, "controller")   \
+    OFPACT(CONTROLLER,      ofpact_controller,  userdata, "controller") \
     OFPACT(ENQUEUE,         ofpact_enqueue,     ofpact, "enqueue")      \
     OFPACT(OUTPUT_REG,      ofpact_output_reg,  ofpact, "output_reg")   \
     OFPACT(BUNDLE,          ofpact_bundle,      slaves, "bundle")       \
@@ -245,6 +245,11 @@ struct ofpact_controller {
     uint16_t max_len;           /* Maximum length to send to controller. */
     uint16_t controller_id;     /* Controller ID to send packet-in. */
     enum ofp_packet_in_reason reason; /* Reason to put in packet-in. */
+
+    /* Arbitrary data to include in the packet-in message (currently, only in
+     * NXT_PACKET_IN2). */
+    uint16_t userdata_len;
+    uint8_t userdata[];
 };
 
 /* OFPACT_ENQUEUE.
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 74f0de6..39e1c8b 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -141,6 +141,17 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
     }
     ds_put_char(string, '\n');
 
+    if (pin.userdata_len) {
+        ds_put_cstr(string, " userdata=");
+        for (size_t i = 0; i < pin.userdata_len; i++) {
+            if (i) {
+                ds_put_char(string, '.');
+            }
+            ds_put_format(string, "%02x", pin.userdata[i]);
+        }
+        ds_put_char(string, '\n');
+    }
+
     if (verbosity > 0) {
         char *packet = ofp_packet_to_string(pin.packet, pin.packet_len);
         ds_put_cstr(string, packet);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 38ee117..085bc04 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -3359,6 +3359,11 @@ decode_nx_packet_in2(const struct ofp_header *oh,
                                      &pin->flow_metadata);
             break;
 
+        case NXPINT_USERDATA:
+            pin->userdata = payload.msg;
+            pin->userdata_len = ofpbuf_msgsize(&payload);
+            break;
+
         default:
             error = OFPPROP_UNKNOWN(false, "NX_PACKET_IN2", type);
             break;
@@ -3594,6 +3599,10 @@ ofputil_encode_nx_packet_in2(const struct ofputil_packet_in *pin,
     oxm_put_raw(msg, &pin->flow_metadata, version);
     ofpprop_end(msg, start);
 
+    if (pin->userdata_len) {
+        ofpprop_put(msg, NXPINT_USERDATA, pin->userdata, pin->userdata_len);
+    }
+
     ofpmsg_update_length(msg);
     return msg;
 }
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 9d9bb72..05b10ea 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -441,6 +441,10 @@ struct ofputil_packet_in {
      * that case, 'cookie' is UINT64_MAX. */
     uint8_t table_id;                    /* OpenFlow table ID. */
     ovs_be64 cookie;                     /* Flow's cookie. */
+
+    /* Arbitrary user-provided data. */
+    uint8_t *userdata;
+    size_t userdata_len;
 };
 
 struct ofpbuf *ofputil_encode_packet_in(const struct ofputil_packet_in *,
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 03b3520..923975a 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -2248,5 +2248,6 @@ void
 ofproto_async_msg_free(struct ofproto_async_msg *am)
 {
     free(am->pin.up.packet);
+    free(am->pin.up.userdata);
     free(am);
 }
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index b4ea4f0..e5cce9a 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -3587,7 +3587,8 @@ flood_packets(struct xlate_ctx *ctx, bool all)
 static void
 execute_controller_action(struct xlate_ctx *ctx, int len,
                           enum ofp_packet_in_reason reason,
-                          uint16_t controller_id)
+                          uint16_t controller_id,
+                          const uint8_t *userdata, size_t userdata_len)
 {
     struct dp_packet *packet;
 
@@ -3623,6 +3624,10 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
                 .reason = reason,
                 .table_id = ctx->table_id,
                 .cookie = ctx->rule_cookie,
+                .userdata = (userdata_len
+                             ? xmemdup(userdata, userdata_len)
+                             : NULL),
+                .userdata_len = userdata_len,
             },
             .max_len = len,
         },
@@ -3760,7 +3765,7 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
 
         for (i = 0; i < ids->n_controllers; i++) {
             execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
-                                      ids->cnt_ids[i]);
+                                      ids->cnt_ids[i], NULL, 0);
         }
 
         /* Stop processing for current table. */
@@ -3809,7 +3814,8 @@ compose_dec_mpls_ttl_action(struct xlate_ctx *ctx)
             set_mpls_lse_ttl(&flow->mpls_lse[0], ttl);
             return false;
         } else {
-            execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
+            execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0,
+                                      NULL, 0);
         }
     }
 
@@ -3847,7 +3853,7 @@ xlate_output_action(struct xlate_ctx *ctx,
                                   (ctx->in_group ? OFPR_GROUP
                                    : ctx->in_action_set ? OFPR_ACTION_SET
                                    : OFPR_ACTION),
-                                  0);
+                                  0, NULL, 0);
         break;
     case OFPP_NONE:
         break;
@@ -4458,7 +4464,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             controller = ofpact_get_CONTROLLER(a);
             execute_controller_action(ctx, controller->max_len,
                                       controller->reason,
-                                      controller->controller_id);
+                                      controller->controller_id,
+                                      controller->userdata,
+                                      controller->userdata_len);
             break;
 
         case OFPACT_ENQUEUE:
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index adfaeb6..826e6e6 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1411,6 +1411,7 @@ add_internal_flows(struct ofproto_dpif *ofproto)
     controller->max_len = UINT16_MAX;
     controller->controller_id = 0;
     controller->reason = OFPR_IMPLICIT_MISS;
+    ofpact_finish(&ofpacts, &controller->ofpact);
 
     error = add_internal_miss_flow(ofproto, id++, &ofpacts,
                                    &ofproto->miss_rule);
diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at
index f3e5277..e16de32 100644
--- a/tests/ofp-actions.at
+++ b/tests/ofp-actions.at
@@ -113,6 +113,13 @@ ffff 0010 00002320 0013 000a 0014 0000
 # actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
 ffff 0010 00002320 0014 04d2 162e 02 00
 
+# actions=controller(reason=invalid_ttl,max_len=1234,id=5678,userdata=01.02.03.04.05)
+ffff 0038 00002320 0025   000000000000 dnl
+0000 0008 04d2   0000 dnl
+0001 0008 162e   0000 dnl
+0002 0005 02   000000 dnl
+0003 0009 0102030405   00000000000000
+
 # actions=dec_ttl(32768,12345,90,765,1024)
 ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
 
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 54980fc..10ec04b 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -2849,7 +2849,7 @@ AT_CLEANUP
 AT_SETUP([NX_PACKET_IN2])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "
-01 04 0088 00000000 00002320 0000001e
+01 04 0098 00000000 00002320 0000001e
 0000 0034
   82 82 82 82 82 82 80 81 81 81 81 81 81 00 00 50
   08 00 45 00 00 28 00 00 00 00 00 06 32 05 53 53
@@ -2859,9 +2859,12 @@ AT_CHECK([ovs-ofctl ofp-print "
 0003 0005 07   000000
 0004 0010 00000000   fedcba9876543210
 0005 0005 01   000000
-0006 0010 80000408 5a5a5a5a5a5a5a5a"
+0006 0010 80000408 5a5a5a5a5a5a5a5a
+0007 0009 0102030405  00000000000000
+"
 ], [0], [dnl
 NXT_PACKET_IN2 (xid=0x0): table_id=7 cookie=0xfedcba9876543210 total_len=64 metadata=0x5a5a5a5a5a5a5a5a (via action) data_len=48 buffer=0x00000114
+ userdata=01.02.03.04.05
 ip,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=0.0.0.0,nw_dst=0.0.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
 ])
 AT_CLEANUP
diff --git a/tests/ofproto.at b/tests/ofproto.at
index d036a20..bede254 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -3353,8 +3353,8 @@ OFPT_BARRIER_REPLY (OF1.1):
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-dnl This test checks that metadata is encoded in NXT_PACKET_IN2.
-AT_SETUP([ofproto - packet-out with metadata (NXT_PACKET_IN2)])
+dnl This test checks that metadata and userdata are encoded in NXT_PACKET_IN2.
+AT_SETUP([ofproto - packet-out with metadata and userdata (NXT_PACKET_IN2)])
 OVS_VSWITCHD_START
 
 # Start a monitor listening for packet-ins.
@@ -3365,7 +3365,7 @@ ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
 AT_CAPTURE_FILE([monitor.log])
 
 # Send a packet-out with a load action to set some metadata, and forward to controller
-AT_CHECK([ovs-ofctl packet-out br0 controller 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), load(0xaa->NXM_NX_PKT_MARK[[]]), controller' '0001020304050010203040501234'])
+AT_CHECK([ovs-ofctl packet-out br0 controller 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), load(0xaa->NXM_NX_PKT_MARK[[]]), controller(userdata=01.02.03.04.05)' '0001020304050010203040501234'])
 
 # Stop the monitor and check its output.
 ovs-appctl -t ovs-ofctl ofctl/barrier
@@ -3373,6 +3373,7 @@ ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
 NXT_PACKET_IN2: total_len=14 pkt_mark=0xaa,metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=14 (unbuffered)
+ userdata=01.02.03.04.05
 vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
 OFPT_BARRIER_REPLY:
 ])
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 6cbb65b..49e95a7 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -1523,12 +1523,18 @@ default connection ID for each controller connection, and a given
 controller connection will only have a nonzero connection ID if its
 controller uses the \fBNXT_SET_CONTROLLER_ID\fR Nicira extension to
 OpenFlow.
+.IP "\fBuserdata=\fIhh\fR...\fR"
+Supplies the bytes represented as hex digits \fIhh\fR as additional
+data to the controller in the packet-in message.  Pairs of hex digits
+may be separated by periods for readability.
+.
 .RE
 .IP
-Any \fIreason\fR other than \fBaction\fR and any nonzero
-\fIcontroller-id\fR uses a Nicira vendor extension that, as of this
-writing, is only known to be implemented by Open vSwitch (version 1.6
-or later).
+If any \fIreason\fR other than \fBaction\fR or any nonzero
+\fIcontroller-id\fR is supplied, Open vSwitch extension
+\fBNXAST_CONTROLLER\fR, supported by Open vSwitch 1.6 and later, is
+used.  If \fBuserdata\fR is supplied, then \fBNXAST_CONTROLLER2\fR,
+supported by Open vSwitch 2.6 and later, is used.
 .
 .IP \fBcontroller\fR
 .IQ \fBcontroller\fR[\fB:\fInbytes\fR]
@@ -2868,8 +2874,10 @@ little reason to use it with those versions of OpenFlow).
 .
 .IP "\fBnxt_packet_in2\fR"
 This uses the \fBNXT_PACKET_IN2\fR message, which is extensible and
-should avoid the need to define new formats later.  Open vSwitch 2.6
-and later support this format.
+should avoid the need to define new formats later.  In particular,
+this format supports passing arbitrary user-provided data to a
+controller using the \fBuserdata\fB option on the \fBcontroller\fR
+action.  Open vSwitch 2.6 and later support this format.
 .
 .RE
 .IP
-- 
2.1.3




More information about the dev mailing list