From: Ben Pfaff Date: Thu, 9 Feb 2012 22:17:33 +0000 (-0800) Subject: Add ability to direct "packet-in"s to particular controllers. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a7349929fb86d41f2e2ef77395e33b21f9457d48;p=openvswitch Add ability to direct "packet-in"s to particular controllers. 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 --- diff --git a/DESIGN b/DESIGN index f383b652..21129256 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 e92ccc79..dce30d23 100644 --- 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 diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index 042d304a..b66b806e 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -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); + +/* 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/lib/learning-switch.c b/lib/learning-switch.c index 435acb96..4ce307e4 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -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: diff --git a/lib/ofp-errors.h b/lib/ofp-errors.h index fbd28e3b..28fa1122 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 9640a71f..58bf49d1 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -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 { diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 5f55da3b..f3758ce4 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -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; diff --git a/lib/ofp-util.c b/lib/ofp-util.c index f9f42490..0f3fdb21 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -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: diff --git a/lib/ofp-util.def b/lib/ofp-util.def index 27e0f264..c74980ba 100644 --- a/lib/ofp-util.def +++ b/lib/ofp-util.def @@ -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 diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 6da65277..5cc63272 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -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; diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c index 0cd7d53f..69534c3d 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 5f9ac708..e326482a 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 ab1cbe7e..feb1342e 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -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; } } diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 7b997417..08a18a46 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -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); diff --git a/tests/ofp-print.at b/tests/ofp-print.at index 471a73cd..6d17d01c 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 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 "\ diff --git a/tests/ofproto.at b/tests/ofproto.at index 9358a6af..6602816a 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 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 diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 8eba261f..39625d11 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -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 diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 48ff7afe..0755e9cb 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -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