Add ability to direct "packet-in"s to particular controllers.
authorBen Pfaff <blp@nicira.com>
Thu, 9 Feb 2012 22:17:33 +0000 (14:17 -0800)
committerBen Pfaff <blp@nicira.com>
Mon, 27 Feb 2012 21:21:44 +0000 (13:21 -0800)
Nicira's controllers are somewhat heterogeneous, so that particular
"packet-in" messages should be directed to particular controllers.  This
new Nicira extension action allows designating a controller or controllers
to receive the "packet-in" using a 16-bit integer ID.

The new NXAST_CONTROLLER action also specifies the "reason" code to include
in the "packet-in" message.  This is particularly useful for simulating a
"no-match" "packet-in" using a rule.

Feature #8946.
Signed-off-by: Ben Pfaff <blp@nicira.com>
18 files changed:
DESIGN
NEWS
include/openflow/nicira-ext.h
lib/learning-switch.c
lib/ofp-errors.h
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.def
lib/ofp-util.h
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/ofproto-dpif.c
ofproto/ofproto.c
tests/ofp-print.at
tests/ofproto.at
tests/ovs-ofctl.at
utilities/ovs-ofctl.8.in

diff --git a/DESIGN b/DESIGN
index f383b6526d918a03dd1c003cef14cb887af2199d..211292569ff038dfea63738497417728384bef25 100644 (file)
--- a/DESIGN
+++ b/DESIGN
@@ -24,6 +24,16 @@ 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.
 
+Third, OFPT_PACKET_IN and NXT_PACKET_IN messages are sent only to
+OpenFlow controller connections that have the correct connection ID
+(see "struct nx_controller_id" and "struct nx_action_controller"):
+
+    - For packet-in messages generated by a NXAST_CONTROLLER action,
+      the controller ID specified in the action.
+
+    - For other packet-in messages, controller ID zero.  (This is the
+      default ID when an OpenFlow controller does not configure one.)
+
 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
diff --git a/NEWS b/NEWS
index e92ccc79f00064eadba1700337de543dbff3f3e2..dce30d23ff95bc2f0a2d76b18e938ef7e6fc65dc 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,8 @@ v1.6.0 - xx xxx xxxx
           receive asynchronously.
         - New "fin_timeout" action.
         - Added "fin_timeout" support to "learn" action.
+        - New Nicira action NXAST_CONTROLLER that offers additional features
+          over output to OFPP_CONTROLLER.
     - The default MAC learning timeout has been increased from 60 seconds
       to 300 seconds.  The MAC learning timeout is now configurable.
     - When QoS settings for an interface do not configure a default queue
index 042d304a9e3257e91e8756ebd0d401ebc4aa9040..b66b806ed6f47121461cbbed8639bb249b536255 100644 (file)
@@ -116,6 +116,7 @@ enum nicira_type {
     NXT_FLOW_AGE = 18,
 
     NXT_SET_ASYNC_CONFIG = 19,  /* struct nx_async_config. */
+    NXT_SET_CONTROLLER_ID = 20, /* struct nx_controller_id. */
 };
 
 /* Header for Nicira vendor stats request and reply messages. */
@@ -336,6 +337,7 @@ enum nx_action_subtype {
     NXAST_EXIT,                 /* struct nx_action_header */
     NXAST_DEC_TTL,              /* struct nx_action_header */
     NXAST_FIN_TIMEOUT,          /* struct nx_action_fin_timeout */
+    NXAST_CONTROLLER,           /* struct nx_action_controller */
 };
 
 /* Header for Nicira-defined actions. */
@@ -1921,5 +1923,45 @@ struct nx_aggregate_stats_reply {
     uint8_t pad[4];            /* Align to 64 bits. */
 };
 OFP_ASSERT(sizeof(struct nx_aggregate_stats_reply) == 48);
+\f
+/* NXT_SET_CONTROLLER_ID.
+ *
+ * Each OpenFlow controller connection has a 16-bit identifier that is
+ * initially 0.  This message changes the connection's ID to 'id'.
+ *
+ * Controller connection IDs need not be unique.
+ *
+ * The NXAST_CONTROLLER action is the only current user of controller
+ * connection IDs. */
+struct nx_controller_id {
+    struct nicira_header nxh;
+    uint8_t zero[6];            /* Must be zero. */
+    ovs_be16 controller_id;     /* New controller connection ID. */
+};
+OFP_ASSERT(sizeof(struct nx_controller_id) == 24);
+
+/* Action structure for NXAST_CONTROLLER.
+ *
+ * This generalizes using OFPAT_OUTPUT to send a packet to OFPP_CONTROLLER.  In
+ * addition to the 'max_len' that OFPAT_OUTPUT supports, it also allows
+ * specifying:
+ *
+ *    - 'reason': The reason code to use in the ofp_packet_in or nx_packet_in.
+ *
+ *    - 'controller_id': The ID of the controller connection to which the
+ *      ofp_packet_in should be sent.  The ofp_packet_in or nx_packet_in is
+ *      sent only to controllers that have the specified controller connection
+ *      ID.  See "struct nx_controller_id" for more information. */
+struct nx_action_controller {
+    ovs_be16 type;                  /* OFPAT_VENDOR. */
+    ovs_be16 len;                   /* Length is 16. */
+    ovs_be32 vendor;                /* NX_VENDOR_ID. */
+    ovs_be16 subtype;               /* NXAST_CONTROLLER. */
+    ovs_be16 max_len;               /* Maximum length to send to controller. */
+    ovs_be16 controller_id;         /* Controller ID to send packet-in. */
+    uint8_t reason;                 /* enum ofp_packet_in_reason (OFPR_*). */
+    uint8_t zero;                   /* Must be zero. */
+};
+OFP_ASSERT(sizeof(struct nx_action_controller) == 16);
 
 #endif /* openflow/nicira-ext.h */
index 435acb96c24bca013b70b616809d49786db4b025..4ce307e4bddceadfc739571437a06875fdfff2e0 100644 (file)
@@ -266,6 +266,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
     case OFPUTIL_NXT_FLOW_REMOVED:
     case OFPUTIL_NXT_FLOW_AGE:
     case OFPUTIL_NXT_SET_ASYNC_CONFIG:
+    case OFPUTIL_NXT_SET_CONTROLLER_ID:
     case OFPUTIL_NXST_FLOW_REQUEST:
     case OFPUTIL_NXST_AGGREGATE_REQUEST:
     case OFPUTIL_NXST_FLOW_REPLY:
index fbd28e3b72092c39ef4365d65f121df44ba82c40..28fa1122d8a05c64fd2bfbe97e42416f2c7c9b83 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 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.
@@ -123,6 +123,9 @@ enum ofperr {
     /* NX(1,514).  The in_port in an ofp_packet_out request is invalid. */
     OFPERR_NXBRC_BAD_IN_PORT,
 
+    /* NX(1,515).  Must-be-zero field had nonzero value. */
+    OFPERR_NXBRC_MUST_BE_ZERO,
+
 /* ## ---------------- ## */
 /* ## OFPET_BAD_ACTION ## */
 /* ## ---------------- ## */
@@ -170,6 +173,9 @@ enum ofperr {
     /* OF1.1(2,12).  Actions uses an unsupported tag/encap. */
     OFPERR_OFPBAC_BAD_TAG,
 
+    /* NX(2,256).  Must-be-zero action argument had nonzero value. */
+    OFPERR_NXBAC_MUST_BE_ZERO,
+
 /* ## --------------------- ## */
 /* ## OFPET_BAD_INSTRUCTION ## */
 /* ## --------------------- ## */
index 9640a71f83d1a449151eadf58d950f27501bb1d0..58bf49d1010caa886765d9294200865cfad19259 100644 (file)
@@ -267,6 +267,48 @@ parse_fin_timeout(struct ofpbuf *b, char *arg)
     }
 }
 
+static void
+parse_controller(struct ofpbuf *b, char *arg)
+{
+    enum ofp_packet_in_reason reason = OFPR_ACTION;
+    uint16_t controller_id = 0;
+    uint16_t max_len = UINT16_MAX;
+
+    if (!arg[0]) {
+        /* Use defaults. */
+    } else if (strspn(arg, "0123456789") == strlen(arg)) {
+        max_len = str_to_u16(arg, "max_len");
+    } else {
+        char *name, *value;
+
+        while (ofputil_parse_key_value(&arg, &name, &value)) {
+            if (!strcmp(name, "reason")) {
+                if (!ofputil_packet_in_reason_from_string(value, &reason)) {
+                    ovs_fatal(0, "unknown reason \"%s\"", value);
+                }
+            } else if (!strcmp(name, "max_len")) {
+                max_len = str_to_u16(value, "max_len");
+            } else if (!strcmp(name, "id")) {
+                controller_id = str_to_u16(value, "id");
+            } else {
+                ovs_fatal(0, "unknown key \"%s\" parsing controller action",
+                          name);
+            }
+        }
+    }
+
+    if (reason == OFPR_ACTION && controller_id == 0) {
+        put_output_action(b, OFPP_CONTROLLER)->max_len = htons(max_len);
+    } else {
+        struct nx_action_controller *nac;
+
+        nac = ofputil_put_NXAST_CONTROLLER(b);
+        nac->max_len = htons(max_len);
+        nac->reason = reason;
+        nac->controller_id = htons(controller_id);
+    }
+}
+
 static void
 parse_named_action(enum ofputil_action_code code, const struct flow *flow,
                    struct ofpbuf *b, char *arg)
@@ -389,6 +431,10 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
     case OFPUTIL_NXAST_FIN_TIMEOUT:
         parse_fin_timeout(b, arg);
         break;
+
+    case OFPUTIL_NXAST_CONTROLLER:
+        parse_controller(b, arg);
+        break;
     }
 }
 
@@ -418,17 +464,6 @@ str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
                           "actions");
             }
             break;
-        } else if (!strcasecmp(act, "CONTROLLER")) {
-            struct ofp_action_output *oao;
-            oao = put_output_action(b, OFPP_CONTROLLER);
-
-            /* Unless a numeric argument is specified, we send the whole
-             * packet to the controller. */
-            if (arg[0] && (strspn(arg, "0123456789") == strlen(arg))) {
-               oao->max_len = htons(str_to_u32(arg));
-            } else {
-                oao->max_len = htons(UINT16_MAX);
-            }
         } else if (ofputil_port_from_string(act, &port)) {
             put_output_action(b, port);
         } else {
index 5f55da3b7560d9338925a5c7d00043caab053b0b..f3758ce4d6e5b186e7a1bd278eb251a651d2138f 100644 (file)
@@ -177,6 +177,7 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
     const struct nx_action_autopath *naa;
     const struct nx_action_output_reg *naor;
     const struct nx_action_fin_timeout *naft;
+    const struct nx_action_controller *nac;
     struct mf_subfield subfield;
     uint16_t port;
 
@@ -354,6 +355,23 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
         ds_put_char(s, ')');
         break;
 
+    case OFPUTIL_NXAST_CONTROLLER:
+        nac = (const struct nx_action_controller *) a;
+        ds_put_cstr(s, "controller(");
+        if (nac->reason != OFPR_ACTION) {
+            ds_put_format(s, "reason=%s,",
+                          ofputil_packet_in_reason_to_string(nac->reason));
+        }
+        if (nac->max_len != htons(UINT16_MAX)) {
+            ds_put_format(s, "max_len=%"PRIu16",", ntohs(nac->max_len));
+        }
+        if (nac->controller_id != htons(0)) {
+            ds_put_format(s, "id=%"PRIu16",", ntohs(nac->controller_id));
+        }
+        ds_chomp(s, ',');
+        ds_put_char(s, ')');
+        break;
+
     default:
         break;
     }
@@ -1391,6 +1409,13 @@ ofp_print_nxt_set_async_config(struct ds *string,
     }
 }
 
+static void
+ofp_print_nxt_set_controller_id(struct ds *string,
+                                const struct nx_controller_id *nci)
+{
+    ds_put_format(string, " id=%"PRIu16, ntohs(nci->controller_id));
+}
+
 static void
 ofp_to_string__(const struct ofp_header *oh,
                 const struct ofputil_msg_type *type, struct ds *string,
@@ -1550,6 +1575,10 @@ ofp_to_string__(const struct ofp_header *oh,
     case OFPUTIL_NXT_FLOW_AGE:
         break;
 
+    case OFPUTIL_NXT_SET_CONTROLLER_ID:
+        ofp_print_nxt_set_controller_id(string, msg);
+        break;
+
     case OFPUTIL_NXT_SET_ASYNC_CONFIG:
         ofp_print_nxt_set_async_config(string, msg);
         break;
index f9f4249047413e6181ac247a33b838b4523f51c7..0f3fdb214756d158cfec97d1b625143a29eaddf6 100644 (file)
@@ -404,6 +404,10 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
         { OFPUTIL_NXT_SET_ASYNC_CONFIG, OFP10_VERSION,
           NXT_SET_ASYNC_CONFIG, "NXT_SET_ASYNC_CONFIG",
           sizeof(struct nx_async_config), 0 },
+
+        { OFPUTIL_NXT_SET_CONTROLLER_ID, OFP10_VERSION,
+          NXT_SET_CONTROLLER_ID, "NXT_SET_CONTROLLER_ID",
+          sizeof(struct nx_controller_id), 0 },
     };
 
     static const struct ofputil_msg_category nxt_category = {
@@ -2527,6 +2531,12 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
             error = learn_check((const struct nx_action_learn *) a, flow);
             break;
 
+        case OFPUTIL_NXAST_CONTROLLER:
+            if (((const struct nx_action_controller *) a)->zero) {
+                error = OFPERR_NXBAC_MUST_BE_ZERO;
+            }
+            break;
+
         case OFPUTIL_OFPAT_STRIP_VLAN:
         case OFPUTIL_OFPAT_SET_NW_SRC:
         case OFPUTIL_OFPAT_SET_NW_DST:
index 27e0f26482b69f465618a47a22bd26afc708cba3..c74980ba8e892e12575ca2e77be49d4508e33937 100644 (file)
@@ -38,4 +38,5 @@ NXAST_ACTION(NXAST_LEARN,          nx_action_learn,        1, "learn")
 NXAST_ACTION(NXAST_EXIT,           nx_action_header,       0, "exit")
 NXAST_ACTION(NXAST_DEC_TTL,        nx_action_header,       0, "dec_ttl")
 NXAST_ACTION(NXAST_FIN_TIMEOUT,    nx_action_fin_timeout,  0, "fin_timeout")
+NXAST_ACTION(NXAST_CONTROLLER,     nx_action_controller,   0, "controller")
 #undef NXAST_ACTION
index 6da652770680fee4322bfcdc9ba86a1d5f28144c..5cc63272feb1b3d29752e8e77b46b9d2e15c9972 100644 (file)
@@ -81,6 +81,7 @@ enum ofputil_msg_code {
     OFPUTIL_NXT_PACKET_IN,
     OFPUTIL_NXT_FLOW_AGE,
     OFPUTIL_NXT_SET_ASYNC_CONFIG,
+    OFPUTIL_NXT_SET_CONTROLLER_ID,
 
     /* NXST_* stat requests. */
     OFPUTIL_NXST_FLOW_REQUEST,
@@ -233,6 +234,7 @@ struct ofputil_packet_in {
     size_t packet_len;
 
     enum ofp_packet_in_reason reason;    /* One of OFPRR_*. */
+    uint16_t controller_id;              /* Controller ID to send to. */
     uint8_t table_id;
     ovs_be64 cookie;
 
index 0cd7d53fa791c3410340490341609a9df5726cb2..69534c3d6935c3ba05bbed77d38931b9544bca3d 100644 (file)
@@ -73,6 +73,7 @@ struct ofconn {
     struct pinsched *schedulers[N_SCHEDULERS];
     struct pktbuf *pktbuf;         /* OpenFlow packet buffers. */
     int miss_send_len;             /* Bytes to send of buffered packets. */
+    uint16_t controller_id;     /* Connection controller ID. */
 
     /* Number of OpenFlow messages queued on 'rconn' as replies to OpenFlow
      * requests, and the maximum number before we stop reading OpenFlow
@@ -820,6 +821,16 @@ ofconn_set_packet_in_format(struct ofconn *ofconn,
     ofconn->packet_in_format = packet_in_format;
 }
 
+/* Sets the controller connection ID for 'ofconn' to 'controller_id'.
+ *
+ * The connection controller ID is used for OFPP_CONTROLLER and
+ * NXAST_CONTROLLER actions.  See "struct nx_action_controller" for details. */
+void
+ofconn_set_controller_id(struct ofconn *ofconn, uint16_t controller_id)
+{
+    ofconn->controller_id = controller_id;
+}
+
 /* Returns true if the NXT_FLOW_MOD_TABLE_ID extension is enabled, false
  * otherwise.
  *
@@ -1017,6 +1028,7 @@ ofconn_flush(struct ofconn *ofconn)
     ofconn->miss_send_len = (ofconn->type == OFCONN_PRIMARY
                              ? OFP_DEFAULT_MISS_SEND_LEN
                              : 0);
+    ofconn->controller_id = 0;
 
     rconn_packet_counter_destroy(ofconn->reply_counter);
     ofconn->reply_counter = rconn_packet_counter_create();
@@ -1292,7 +1304,8 @@ connmgr_send_packet_in(struct connmgr *mgr,
     struct ofconn *ofconn;
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        if (ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, pin->reason)) {
+        if (ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, pin->reason)
+            && ofconn->controller_id == pin->controller_id) {
             schedule_packet_in(ofconn, *pin, flow);
         }
     }
index 5f9ac708f1608bc79c75f4034f648527ce8fc654..e326482a1a1b882cb03947a29d584d13ddd0ac76 100644 (file)
@@ -97,6 +97,8 @@ void ofconn_set_flow_format(struct ofconn *, enum nx_flow_format);
 enum nx_packet_in_format ofconn_get_packet_in_format(struct ofconn *);
 void ofconn_set_packet_in_format(struct ofconn *, enum nx_packet_in_format);
 
+void ofconn_set_controller_id(struct ofconn *, uint16_t controller_id);
+
 bool ofconn_get_flow_mod_table_id(const struct ofconn *);
 void ofconn_set_flow_mod_table_id(struct ofconn *, bool enable);
 
index ab1cbe7ee609210b2d7e3dfa50bc5d1bb643c2dc..feb1342eb22240df1f5d56ddf772312728e4926a 100644 (file)
@@ -2456,6 +2456,7 @@ send_packet_in_miss(struct ofproto_dpif *ofproto, const struct ofpbuf *packet,
     pin.packet_len = packet->size;
     pin.total_len = packet->size;
     pin.reason = OFPR_NO_MATCH;
+    pin.controller_id = 0;
 
     pin.table_id = 0;
     pin.cookie = 0;
@@ -4465,7 +4466,8 @@ flood_packets(struct action_xlate_ctx *ctx, bool all)
 
 static void
 execute_controller_action(struct action_xlate_ctx *ctx, int len,
-                          enum ofp_packet_in_reason reason)
+                          enum ofp_packet_in_reason reason,
+                          uint16_t controller_id)
 {
     struct ofputil_packet_in pin;
     struct ofpbuf *packet;
@@ -4511,6 +4513,7 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len,
     pin.packet = packet->data;
     pin.packet_len = packet->size;
     pin.reason = reason;
+    pin.controller_id = controller_id;
     pin.table_id = ctx->table_id;
     pin.cookie = ctx->rule ? ctx->rule->up.flow_cookie : 0;
 
@@ -4535,7 +4538,7 @@ compose_dec_ttl(struct action_xlate_ctx *ctx)
         ctx->flow.nw_ttl--;
         return false;
     } else {
-        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL);
+        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
 
         /* Stop processing for current table. */
         return true;
@@ -4567,7 +4570,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
         flood_packets(ctx, true);
         break;
     case OFPP_CONTROLLER:
-        execute_controller_action(ctx, max_len, OFPR_ACTION);
+        execute_controller_action(ctx, max_len, OFPR_ACTION, 0);
         break;
     case OFPP_NONE:
         break;
@@ -4805,6 +4808,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
         const struct nx_action_autopath *naa;
         const struct nx_action_bundle *nab;
         const struct nx_action_output_reg *naor;
+        const struct nx_action_controller *nac;
         enum ofputil_action_code code;
         ovs_be64 tun_id;
 
@@ -4965,6 +4969,12 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
             ctx->has_fin_timeout = true;
             xlate_fin_timeout(ctx, (const struct nx_action_fin_timeout *) ia);
             break;
+
+        case OFPUTIL_NXAST_CONTROLLER:
+            nac = (const struct nx_action_controller *) ia;
+            execute_controller_action(ctx, ntohs(nac->max_len), nac->reason,
+                                      ntohs(nac->controller_id));
+            break;
         }
     }
 
index 7b9974176061f16bfa421da6612f3f6a1ae781ab..08a18a465b7f82e7b8cc7240f1ea6b2d594fcf97 100644 (file)
@@ -3137,6 +3137,21 @@ handle_nxt_set_async_config(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
+static enum ofperr
+handle_nxt_set_controller_id(struct ofconn *ofconn,
+                             const struct ofp_header *oh)
+{
+    const struct nx_controller_id *nci;
+
+    nci = (const struct nx_controller_id *) oh;
+    if (!is_all_zeros(nci->zero, sizeof nci->zero)) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
+
+    ofconn_set_controller_id(ofconn, ntohs(nci->controller_id));
+    return 0;
+}
+
 static enum ofperr
 handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
@@ -3207,6 +3222,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
         return handle_nxt_set_packet_in_format(ofconn, oh);
 
+    case OFPUTIL_NXT_SET_CONTROLLER_ID:
+        return handle_nxt_set_controller_id(ofconn, oh);
+
     case OFPUTIL_NXT_FLOW_MOD:
         return handle_flow_mod(ofconn, oh);
 
index 471a73cdea4dac1f9e2a14c4b7504648ed183955..6d17d01cc0476dc1402224164fbede6cd4de54a5 100644 (file)
@@ -728,6 +728,16 @@ NXT_SET_ASYNC_CONFIG (xid=0x0):
 ])
 AT_CLEANUP
 
+AT_SETUP([NXT_SET_CONTROLLER_ID])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 18 00 00 00 03 00 00 23 20 00 00 00 14 \
+00 00 00 00 00 00 00 7b \
+"], [0], [dnl
+NXT_SET_CONTROLLER_ID (xid=0x3): id=123
+])
+AT_CLEANUP
+
 AT_SETUP([NXT_SET_FLOW_FORMAT])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
index 9358a6af08515dbf8b6e4396cc2d8f8b2d84b786..6602816ae6c87b424e5210b851362143ba167615 100644 (file)
@@ -433,14 +433,21 @@ check_async () {
     ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
     : > expout
 
-    # OFPT_PACKET_IN, OFPR_ACTION
+    # OFPT_PACKET_IN, OFPR_ACTION (controller_id=0)
     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
+    # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123)
+    ovs-ofctl -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234'
+    if test X"$1" = X"OFPR_NO_MATCH"; then shift;
+        echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via no_match) 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 (controller_id=0)
     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)
@@ -502,9 +509,16 @@ check_async 4 OFPPR_ADD OFPPR_DELETE
 ovs-appctl -t ovs-ofctl ofctl/send 01040028000000020000232000000013000000020000000500000005000000020000000200000005
 check_async 5 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE
 
+# Set controller ID 123.
+ovs-appctl -t ovs-ofctl ofctl/send 01040018000000030000232000000014000000000000007b
+check_async 6 OFPR_NO_MATCH OFPPR_DELETE OFPRR_DELETE
+
+# Restore controller ID 0.
+ovs-appctl -t ovs-ofctl ofctl/send 010400180000000300002320000000140000000000000000
+
 # Become master.
 ovs-appctl -t ovs-ofctl ofctl/send 0104001400000002000023200000000a00000001
-check_async 6 OFPR_ACTION OFPPR_ADD
+check_async 7 OFPR_ACTION OFPPR_ADD
 
 ovs-appctl -t ovs-ofctl exit
 AT_CLEANUP
index 8eba261f7a0d6710c5d45213fa75067894a412c9..39625d118783e0333f8bbbc330a39f5f6a68e78f 100644 (file)
@@ -28,6 +28,7 @@ actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3)
 send_flow_rem,actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
 check_overlap,actions=output:1,exit,output:2
 actions=fin_timeout(idle_timeout=5,hard_timeout=15)
+actions=controller(max_len=123,reason=invalid_ttl,id=555)
 ]])
 
 AT_CHECK([ovs-ofctl parse-flows flows.txt
@@ -60,6 +61,7 @@ NXT_FLOW_MOD: ADD table:255 actions=resubmit:1,resubmit:2,resubmit(,3),resubmit(
 NXT_FLOW_MOD: ADD table:255 send_flow_rem actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
 NXT_FLOW_MOD: ADD table:255 check_overlap actions=output:1,exit,output:2
 NXT_FLOW_MOD: ADD table:255 actions=fin_timeout(idle_timeout=5,hard_timeout=15)
+NXT_FLOW_MOD: ADD table:255 actions=controller(reason=invalid_ttl,max_len=123,id=555)
 ]])
 AT_CLEANUP
 
index 48ff7afe990f401ef32aabc4b4e10fcc895823a7..0755e9cb10b193a798e917fa5274dc539a5c5fc9 100644 (file)
@@ -756,11 +756,35 @@ tree protocol).
 Outputs the packet on all switch physical ports other than the port on
 which it was received.
 .
-.IP \fBcontroller\fR:\fImax_len\fR
+.IP \fBcontroller(\fIkey\fB=\fIvalue\fR...\fB)
 Sends the packet to the OpenFlow controller as a ``packet in''
-message.  If \fImax_len\fR is a number, then it specifies the maximum
-number of bytes that should be sent.  If \fImax_len\fR is \fBALL\fR or
-omitted, then the entire packet is sent.
+message.  The supported key-value pairs are:
+.RS
+.IP "\fBmax_len=\fInbytes\fR"
+Limit to \fInbytes\fR the number of bytes of the packet to send to
+the controller.  By default the entire packet is sent.
+.IP "\fBreason=\fIreason\fR"
+Specify \fIreason\fR as the reason for sending the message in the
+``packet in'' message.  The supported reasons are \fBaction\fR (the
+default), \fBno_match\fR, and \fBinvalid_ttl\fR.
+.IP "\fBid=\fIcontroller-id\fR"
+Specify \fIcontroller-id\fR, a 16-bit integer, as the connection ID of
+the OpenFlow controller or controllers to which the ``packet in''
+message should be sent.  The default is zero.  Zero is also the
+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.
+.RE
+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).
+.
+.IP \fBcontroller\fR
+.IQ \fBcontroller\fR[\fB:\fInbytes\fR]
+Shorthand for \fBcontroller()\fR or
+\fBcontroller(max_len=\fInbytes\fB)\fR, respectively.
 .
 .IP \fBlocal\fR
 Outputs the packet on the ``local port,'' which corresponds to the