[ovs-dev] [nxast_controller 2/2] Add ability to direct "packet-in"s to particular controllers.
Ben Pfaff
blp at nicira.com
Tue Feb 7 23:06:44 UTC 2012
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.
Signed-off-by: Ben Pfaff <blp at nicira.com>
---
DESIGN | 10 +++++++
NEWS | 2 +
include/openflow/nicira-ext.h | 42 ++++++++++++++++++++++++++++++
include/openflow/openflow.h | 3 +-
lib/learning-switch.c | 1 +
lib/ofp-errors.h | 8 +++++-
lib/ofp-parse.c | 57 +++++++++++++++++++++++++++++++++--------
lib/ofp-print.c | 27 +++++++++++++++++++
lib/ofp-util.c | 10 +++++++
lib/ofp-util.def | 1 +
lib/ofp-util.h | 2 +
ofproto/connmgr.c | 15 ++++++++++-
ofproto/connmgr.h | 2 +
ofproto/ofproto-dpif.c | 16 +++++++++--
ofproto/ofproto.c | 18 +++++++++++++
tests/ofp-print.at | 10 +++++++
tests/ofproto.at | 20 ++++++++++++--
tests/ovs-ofctl.at | 2 +
utilities/ovs-ofctl.8.in | 32 ++++++++++++++++++++---
19 files changed, 254 insertions(+), 24 deletions(-)
diff --git a/DESIGN b/DESIGN
index f383b65..2112925 100644
--- 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 0f36224..15f4263 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,8 @@ post-v1.5.0
- OpenFlow:
- Added support for bitwise matching on TCP and UDP ports.
See ovs-ofctl(8) for more information.
+ - New Nicira action NXAST_CONTROLLER that offers additional features
+ over output to OFPP_CONTROLLER.
- Logging to console and file will have UTC timestamp as a default for all
the daemons. An example of the default format is 2012-01-27T16:35:17Z.
ovs-appctl can be used to change the default format as before.
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 00411bf..f09854f 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -111,6 +111,7 @@ enum nicira_type {
NXT_PACKET_IN = 17, /* Nicira Packet In. */
NXT_SET_ASYNC_CONFIG = 18, /* struct nx_async_config. */
+ NXT_SET_CONTROLLER_ID = 19, /* struct nx_controller_id. */
};
/* Header for Nicira vendor stats request and reply messages. */
@@ -330,6 +331,7 @@ enum nx_action_subtype {
NXAST_LEARN, /* struct nx_action_learn */
NXAST_EXIT, /* struct nx_action_header */
NXAST_DEC_TTL, /* struct nx_action_header */
+ NXAST_CONTROLLER, /* struct nx_action_controller */
};
/* Header for Nicira-defined actions. */
@@ -1851,5 +1853,45 @@ struct nx_aggregate_stats_reply {
uint8_t pad[4]; /* Align to 64 bits. */
};
OFP_ASSERT(sizeof(struct nx_aggregate_stats_reply) == 48);
+
+/* 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 */
diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h
index a707087..cfdb192 100644
--- a/include/openflow/openflow.h
+++ b/include/openflow/openflow.h
@@ -294,7 +294,8 @@ OFP_ASSERT(sizeof(struct ofp_port_mod) == 32);
enum ofp_packet_in_reason {
OFPR_NO_MATCH, /* No matching flow. */
OFPR_ACTION, /* Action explicitly output to controller. */
- OFPR_INVALID_TTL /* Packet has invalid TTL. */
+ OFPR_INVALID_TTL, /* Packet has invalid TTL. */
+ OFPR_N_REASONS
};
/* Packet received on port (datapath -> controller). */
diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index eec2532..7d775ee 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -265,6 +265,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
case OFPUTIL_NXT_FLOW_MOD:
case OFPUTIL_NXT_FLOW_REMOVED:
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:
diff --git a/lib/ofp-errors.h b/lib/ofp-errors.h
index fbd28e3..28fa112 100644
--- a/lib/ofp-errors.h
+++ b/lib/ofp-errors.h
@@ -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 ## */
/* ## --------------------- ## */
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index f96f817..8271466 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -250,6 +250,48 @@ parse_note(struct ofpbuf *b, const 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)
{
@@ -367,6 +409,10 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
case OFPUTIL_NXAST_DEC_TTL:
ofputil_put_NXAST_DEC_TTL(b);
break;
+
+ case OFPUTIL_NXAST_CONTROLLER:
+ parse_controller(b, arg);
+ break;
}
}
@@ -396,17 +442,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 {
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index ae7ed08..d5d4acf 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -176,6 +176,7 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
const struct nx_action_multipath *nam;
const struct nx_action_autopath *naa;
const struct nx_action_output_reg *naor;
+ const struct nx_action_controller *nac;
struct mf_subfield subfield;
uint16_t port;
@@ -338,6 +339,23 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
ds_put_cstr(s, "exit");
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;
}
@@ -1370,6 +1388,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,
int verbosity)
@@ -1525,6 +1550,8 @@ ofp_to_string__(const struct ofp_header *oh,
ofp_print_nxt_set_packet_in_format(string, msg);
break;
+ case OFPUTIL_NXT_SET_CONTROLLER_ID:
+ ofp_print_nxt_set_controller_id(string, msg);
break;
case OFPUTIL_NXT_SET_ASYNC_CONFIG:
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 704823a..e6ab5a6 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -400,6 +400,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 = {
@@ -2499,6 +2503,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:
diff --git a/lib/ofp-util.def b/lib/ofp-util.def
index d05ec9d..d690389 100644
--- a/lib/ofp-util.def
+++ b/lib/ofp-util.def
@@ -37,4 +37,5 @@ NXAST_ACTION(NXAST_OUTPUT_REG, nx_action_output_reg, 0, NULL)
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_CONTROLLER, nx_action_controller, 0, "controller")
#undef NXAST_ACTION
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 0ca1d99..33b9048 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -80,6 +80,7 @@ enum ofputil_msg_code {
OFPUTIL_NXT_SET_PACKET_IN_FORMAT,
OFPUTIL_NXT_PACKET_IN,
OFPUTIL_NXT_SET_ASYNC_CONFIG,
+ OFPUTIL_NXT_SET_CONTROLLER_ID,
/* NXST_* stat requests. */
OFPUTIL_NXST_FLOW_REQUEST,
@@ -229,6 +230,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;
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 0cd7d53..69534c3 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -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);
}
}
diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
index f9c9f4d..58564b3 100644
--- a/ofproto/connmgr.h
+++ b/ofproto/connmgr.h
@@ -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);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 28f0434..a600f0c 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -2440,6 +2440,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;
@@ -4440,7 +4441,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;
@@ -4486,6 +4488,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;
@@ -4510,7 +4513,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;
@@ -4542,7 +4545,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_LOCAL:
compose_output_action(ctx, OFPP_LOCAL);
@@ -4760,6 +4763,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;
@@ -4915,6 +4919,12 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
case OFPUTIL_NXAST_EXIT:
ctx->exit = true;
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;
}
}
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index c251475..95a81a4 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -3122,6 +3122,21 @@ handle_nxt_set_async_config(struct ofconn *ofconn, const struct ofp_header *oh)
}
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)
{
struct ofp_header *ob;
@@ -3191,6 +3206,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);
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 27f878a..7345b58 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -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 13 \
+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 "\
diff --git a/tests/ofproto.at b/tests/ofproto.at
index fb53d16..1156ee4 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -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 01040028000000020000232000000012000000020000000500000005000000020000000200000005
check_async 5 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE
+# Set controller ID 123.
+ovs-appctl -t ovs-ofctl ofctl/send 01040018000000030000232000000013000000000000007b
+check_async 6 OFPR_NO_MATCH OFPPR_DELETE OFPRR_DELETE
+
+# Restore controller ID 0.
+ovs-appctl -t ovs-ofctl ofctl/send 010400180000000300002320000000130000000000000000
+
# 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
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index e35fc73..edd47ee 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -27,6 +27,7 @@ actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),
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=controller(max_len=123,reason=invalid_ttl,id=555)
]])
AT_CHECK([ovs-ofctl parse-flows flows.txt
@@ -58,6 +59,7 @@ NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle_load(eth_src,0,hrw,ofport,NX
NXT_FLOW_MOD: ADD table:255 actions=resubmit:1,resubmit:2,resubmit(,3),resubmit(2,3)
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=controller(reason=invalid_ttl,max_len=123,id=555)
]])
AT_CLEANUP
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 3531626..3780f49 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -764,11 +764,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
--
1.7.2.5
More information about the dev
mailing list