This action makes it possible for an OpenFlow action to send a packet to the OpenFlow controller with OFPR_NO_MATCH as the reason. There are multiple reasons this is useful:
- It makes it possible to implement flow table misses as OpenFlow flows internally without inventing a special case action that is only understood internally to Open vSwitch. - OpenFlow 1.3 may implement such behavior (see EXT-108 in the Open Networking Foundation bug tracker). - It gives us a place to add a specification for a subset of controllers to which the packet should be sent. (This is not yet implemented.) Signed-off-by: Ben Pfaff <b...@nicira.com> --- NEWS | 3 ++ include/openflow/nicira-ext.h | 26 ++++++++++++++++++- lib/ofp-parse.c | 57 +++++++++++++++++++++++++++++++++-------- lib/ofp-print.c | 14 ++++++++++ lib/ofp-util.c | 1 + lib/ofp-util.def | 1 + ofproto/ofproto-dpif.c | 13 +++++++-- utilities/ovs-ofctl.8.in | 22 +++++++++++++--- 8 files changed, 118 insertions(+), 19 deletions(-) diff --git a/NEWS b/NEWS index eae3a97..1c67186 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ port-v1.4.0 ------------------------ + - OpenFlow: + - New Nicira action NXAST_CONTROLLER that offers additional features over + output to OFPP_CONTROLLER. v1.4.0 - xx xxx xxxx diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index f449329..2a39f0d 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -296,7 +296,8 @@ enum nx_action_subtype { NXAST_RESUBMIT_TABLE, /* struct nx_action_resubmit */ NXAST_OUTPUT_REG, /* struct nx_action_output_reg */ NXAST_LEARN, /* struct nx_action_learn */ - NXAST_EXIT /* struct nx_action_header */ + NXAST_EXIT, /* struct nx_action_header */ + NXAST_CONTROLLER /* struct nx_action_controller */ }; /* Header for Nicira-defined actions. */ @@ -1785,5 +1786,28 @@ struct nx_aggregate_stats_reply { uint8_t pad[4]; /* Align to 64 bits. */ }; OFP_ASSERT(sizeof(struct nx_aggregate_stats_reply) == 48); + +/* 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: + * + * - The reason to use in the ofp_packet_in. + * + * - The controllers to which the ofp_packet_in should be sent. This + * feature is not yet implemented. Controllers should set 'controllers' + * to 0 to ensure forward compatilibility. */ +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 controllers; /* Controllers to send packet-in. */ + uint8_t reason; /* enum ofp_packet_in_reason (OFPR_*). */ + uint8_t pad; /* Pad to 64 bits. */ +}; +OFP_ASSERT(sizeof(struct nx_action_controller) == 16); #endif /* openflow/nicira-ext.h */ diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 4021551..79baa6e 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -251,6 +251,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 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 (!strcmp(value, "action")) { + reason = OFPR_ACTION; + } else if (!strcmp(value, "no_match")) { + reason = OFPR_NO_MATCH; + } else { + ovs_fatal(0, "unknown reason \"%s\"", value); + } + } else if (!strcmp(name, "max_len")) { + max_len = str_to_u16(value, "max_len"); + } else { + ovs_fatal(0, "unknown key \"%s\" parsing controller action", + name); + } + } + } + + if (reason == OFPR_ACTION) { + 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; + } +} + +static void parse_named_action(enum ofputil_action_code code, const struct flow *flow, struct ofpbuf *b, char *arg) { @@ -364,6 +406,10 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, case OFPUTIL_NXAST_EXIT: ofputil_put_NXAST_EXIT(b); break; + + case OFPUTIL_NXAST_CONTROLLER: + parse_controller(b, arg); + break; } } @@ -393,17 +439,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 fe852b4..8de9363 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -183,6 +183,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; uint16_t port; switch (code) { @@ -342,6 +343,19 @@ 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_format(s, "controller(%s", + (nac->reason == OFPR_ACTION ? "" + : nac->reason == OFPR_NO_MATCH ? "reason=no_match," + : "reason=<error>,")); + if (nac->max_len != htons(UINT16_MAX)) { + ds_put_format(s, "max_len=%"PRIu16",", ntohs(nac->max_len)); + } + ds_chomp(s, ','); + ds_put_char(s, ')'); + break; + default: break; } diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 11b0f15..a8b29bf 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -2272,6 +2272,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions, case OFPUTIL_NXAST_NOTE: case OFPUTIL_NXAST_SET_TUNNEL64: case OFPUTIL_NXAST_EXIT: + case OFPUTIL_NXAST_CONTROLLER: break; } diff --git a/lib/ofp-util.def b/lib/ofp-util.def index 2958eb6..08a641a 100644 --- a/lib/ofp-util.def +++ b/lib/ofp-util.def @@ -36,4 +36,5 @@ NXAST_ACTION(NXAST_RESUBMIT_TABLE, nx_action_resubmit, 0, NULL) 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_CONTROLLER, nx_action_controller, 0, "controller") #undef NXAST_ACTION diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index e725f28..47c7e60 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -4231,7 +4231,8 @@ flood_packets(struct action_xlate_ctx *ctx, bool all) } static void -compose_controller_action(struct action_xlate_ctx *ctx, int len) +compose_controller_action(struct action_xlate_ctx *ctx, + enum ofp_packet_in_reason reason, int len) { union user_action_cookie cookie; @@ -4239,7 +4240,7 @@ compose_controller_action(struct action_xlate_ctx *ctx, int len) memset(&cookie, 0, sizeof cookie); cookie.controller.type = USER_ACTION_COOKIE_CONTROLLER; - cookie.controller.reason = OFPR_ACTION; + cookie.controller.reason = reason; cookie.controller.max_len = len; put_userspace_action(ctx->ofproto, ctx->odp_actions, &ctx->flow, &cookie); } @@ -4269,7 +4270,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx, flood_packets(ctx, true); break; case OFPP_CONTROLLER: - compose_controller_action(ctx, max_len); + compose_controller_action(ctx, OFPR_ACTION, max_len); break; case OFPP_LOCAL: compose_output_action(ctx, OFPP_LOCAL); @@ -4480,6 +4481,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; @@ -4626,6 +4628,11 @@ 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; + compose_controller_action(ctx, nac->reason, ntohs(nac->max_len)); + break; } } diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 932c204..40e173a 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -686,11 +686,25 @@ 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) and \fBno_match\fR. Any reason other than \fBaction\fR uses +a Nicira vendor extension that, as of this writing, is only known to +be implemented by Open vSwitch. +.RE +. +.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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev