From: Ben Pfaff Date: Thu, 9 Feb 2012 22:06:35 +0000 (-0800) Subject: ofproto: Implement OpenFlow extension to allow control over async messages. X-Git-Tag: sliver-openvswitch-0.1-1~342 X-Git-Url: http://git.onelab.eu/?p=sliver-openvswitch.git;a=commitdiff_plain;h=80d5aefd65b9dd953d873f9995b949bc8b8d19d5 ofproto: Implement OpenFlow extension to allow control over async messages. Until now, the rules that cover the asynchronous messages that Open vSwitch sends to a controller have been ad hoc. The new NXT_SET_ASYNC_CONFIG message provides systematic, precise control. Feature #7086. Signed-off-by: Ben Pfaff --- diff --git a/DESIGN b/DESIGN index 164ada20e..f383b6526 100644 --- a/DESIGN +++ b/DESIGN @@ -9,8 +9,53 @@ successful deployment. The end of this document contains contact information that can be used to let us know how we can make Open vSwitch more generally useful. -OpenFlow -======== +Asynchronous Messages +===================== + +Over time, Open vSwitch has added many knobs that control whether a +given controller receives OpenFlow asynchronous messages. This +section describes how all of these features interact. + +First, a service controller never receives any asynchronous messages +unless it explicitly configures a miss_send_len greater than zero with +an OFPT_SET_CONFIG message. + +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. + +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 +connection is made. An entry labeled "yes" means that the message is +sent, an entry labeled "---" means that the message is suppressed. + + master/ + message and reason code other slave + ---------------------------------------- ------- ----- + OFPT_PACKET_IN / NXT_PACKET_IN + OFPR_NO_MATCH yes --- + OFPR_ACTION yes --- + OFPR_INVALID_TTL --- --- + + OFPT_FLOW_REMOVED / NXT_FLOW_REMOVED + OFPRR_IDLE_TIMEOUT yes --- + OFPRR_HARD_TIMEOUT yes --- + OFPRR_DELETE yes --- + + OFPT_PORT_STATUS + OFPPR_ADD yes yes + OFPPR_DELETE yes yes + OFPPR_MODIFY yes yes + +The NXT_SET_ASYNC_CONFIG message directly sets all of the values in +this table for the current connection. The +OFPC_INVALID_TTL_TO_CONTROLLER bit in the OFPT_SET_CONFIG message +controls the setting for OFPR_INVALID_TTL for the "master" role. + + +OFPAT_ENQUEUE +============= The OpenFlow 1.0 specification requires the output port of the OFPAT_ENQUEUE action to "refer to a valid physical port (i.e. < OFPP_MAX) or OFPP_IN_PORT". diff --git a/NEWS b/NEWS index c153370e2..fde19f00d 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,13 @@ post-v1.5.0 - OpenFlow: - Added support for bitwise matching on TCP and UDP ports. See ovs-ofctl(8) for more information. + - NXM flow dumps now include times elapsed toward idle and hard + timeouts. + - Added an OpenFlow extension NXT_SET_ASYNC_CONFIG that allows + controllers more precise control over which OpenFlow messages they + receive asynchronously. + - The default MAC learning timeout has been increased from 60 seconds + to 300 seconds. The MAC learning timeout is now configurable. - 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. @@ -18,13 +25,9 @@ post-v1.5.0 table, with configurable policy for evicting flows upon overflow. See the Flow_Table table in ovs-vswitch.conf.db(5) for more information. - - OpenFlow: - - NXM flow dumps now include times elapsed toward idle and hard timeouts. - ofproto-provider interface: - "struct rule" has a new member "used" that ofproto implementations should maintain by updating with ofproto_rule_update_used(). - - The default MAC learning timeout has been increased from 60 seconds - to 300 seconds. The MAC learning timeout is now configurable. - ovsdb-client: - The new option --timestamp causes the "monitor" command to print a timestamp with every update. diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index b0c8e4eb3..ff957969f 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -114,6 +114,8 @@ enum nicira_type { * If so, the switch does not reply to this message (which consists only of * a "struct nicira_header"). If not, the switch sends an error reply. */ NXT_FLOW_AGE = 18, + + NXT_SET_ASYNC_CONFIG = 19, /* struct nx_async_config. */ }; /* Header for Nicira vendor stats request and reply messages. */ @@ -288,6 +290,28 @@ enum nx_role { NX_ROLE_MASTER, /* Full access, at most one. */ NX_ROLE_SLAVE /* Read-only access. */ }; + +/* NXT_SET_ASYNC_CONFIG. + * + * Sent by a controller, this message configures the asynchronous messages that + * the controller wants to receive. Element 0 in each array specifies messages + * of interest when the controller has an "other" or "master" role; element 1, + * when the controller has a "slave" role. + * + * Each array element is a bitmask in which a 0-bit disables receiving a + * particular message and a 1-bit enables receiving it. Each bit controls the + * message whose 'reason' corresponds to the bit index. For example, the bit + * with value 1<<2 == 4 in port_status_mask[1] determines whether the + * controller will receive OFPT_PORT_STATUS messages with reason OFPPR_MODIFY + * (value 2) when the controller has a "slave" role. + */ +struct nx_async_config { + struct nicira_header nxh; + ovs_be32 packet_in_mask[2]; /* Bitmasks of OFPR_* values. */ + ovs_be32 port_status_mask[2]; /* Bitmasks of OFPRR_* values. */ + ovs_be32 flow_removed_mask[2]; /* Bitmasks of OFPPR_* values. */ +}; +OFP_ASSERT(sizeof(struct nx_async_config) == 40); /* Nicira vendor flow actions. */ diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 81aadeb40..435acb96c 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_FLOW_AGE: + case OFPUTIL_NXT_SET_ASYNC_CONFIG: case OFPUTIL_NXST_FLOW_REQUEST: case OFPUTIL_NXST_AGGREGATE_REQUEST: case OFPUTIL_NXST_FLOW_REPLY: diff --git a/lib/ofp-print.c b/lib/ofp-print.c index f0c134fa8..30ecdbf87 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -80,6 +80,24 @@ ofp_packet_to_string(const void *data, size_t len) return ds_cstr(&ds); } +static const char * +ofp_packet_in_reason_to_string(enum ofp_packet_in_reason reason) +{ + static char s[32]; + + switch (reason) { + case OFPR_NO_MATCH: + return "no_match"; + case OFPR_ACTION: + return "action"; + case OFPR_INVALID_TTL: + return "invalid_ttl"; + default: + sprintf(s, "%d", (int) reason); + return s; + } +} + static void ofp_print_packet_in(struct ds *string, const struct ofp_header *oh, int verbosity) @@ -121,20 +139,8 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh, } } - switch (pin.reason) { - case OFPR_NO_MATCH: - ds_put_cstr(string, " (via no_match)"); - break; - case OFPR_ACTION: - ds_put_cstr(string, " (via action)"); - break; - case OFPR_INVALID_TTL: - ds_put_cstr(string, " (via invalid_ttl)"); - break; - default: - ds_put_format(string, " (***reason %"PRIu8"***)", pin.reason); - break; - } + ds_put_format(string, " (via %s)", + ofp_packet_in_reason_to_string(pin.reason)); ds_put_format(string, " data_len=%zu", pin.packet_len); if (pin.buffer_id == UINT32_MAX) { @@ -870,6 +876,24 @@ ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec) ds_put_char(string, 's'); } +static const char * +ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason) +{ + static char s[32]; + + switch (reason) { + case OFPRR_IDLE_TIMEOUT: + return "idle"; + case OFPRR_HARD_TIMEOUT: + return "hard"; + case OFPRR_DELETE: + return "delete"; + default: + sprintf(s, "%d", (int) reason); + return s; + } +} + static void ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh) { @@ -885,21 +909,8 @@ ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh) ds_put_char(string, ' '); cls_rule_format(&fr.rule, string); - ds_put_cstr(string, " reason="); - switch (fr.reason) { - case OFPRR_IDLE_TIMEOUT: - ds_put_cstr(string, "idle"); - break; - case OFPRR_HARD_TIMEOUT: - ds_put_cstr(string, "hard"); - break; - case OFPRR_DELETE: - ds_put_cstr(string, "delete"); - break; - default: - ds_put_format(string, "**%"PRIu8"**", fr.reason); - break; - } + ds_put_format(string, " reason=%s", + ofp_flow_removed_reason_to_string(fr.reason)); if (fr.cookie != htonll(0)) { ds_put_format(string, " cookie:0x%"PRIx64, ntohll(fr.cookie)); @@ -1313,6 +1324,75 @@ ofp_print_nxt_set_packet_in_format(struct ds *string, } } +static const char * +ofp_port_reason_to_string(enum ofp_port_reason reason) +{ + static char s[32]; + + switch (reason) { + case OFPPR_ADD: + return "add"; + + case OFPPR_DELETE: + return "delete"; + + case OFPPR_MODIFY: + return "modify"; + + default: + sprintf(s, "%d", (int) reason); + return s; + } +} + +static void +ofp_print_nxt_set_async_config(struct ds *string, + const struct nx_async_config *nac) +{ + int i; + + for (i = 0; i < 2; i++) { + int j; + + ds_put_format(string, "\n %s:\n", i == 0 ? "master" : "slave"); + + ds_put_cstr(string, " PACKET_IN:"); + for (j = 0; j < 32; j++) { + if (nac->packet_in_mask[i] & htonl(1u << j)) { + ds_put_format(string, " %s", + ofp_packet_in_reason_to_string(j)); + } + } + if (!nac->packet_in_mask[i]) { + ds_put_cstr(string, " (off)"); + } + ds_put_char(string, '\n'); + + ds_put_cstr(string, " PORT_STATUS:"); + for (j = 0; j < 32; j++) { + if (nac->port_status_mask[i] & htonl(1u << j)) { + ds_put_format(string, " %s", ofp_port_reason_to_string(j)); + } + } + if (!nac->port_status_mask[i]) { + ds_put_cstr(string, " (off)"); + } + ds_put_char(string, '\n'); + + ds_put_cstr(string, " FLOW_REMOVED:"); + for (j = 0; j < 32; j++) { + if (nac->flow_removed_mask[i] & htonl(1u << j)) { + ds_put_format(string, " %s", + ofp_flow_removed_reason_to_string(j)); + } + } + if (!nac->flow_removed_mask[i]) { + ds_put_cstr(string, " (off)"); + } + ds_put_char(string, '\n'); + } +} + static void ofp_to_string__(const struct ofp_header *oh, const struct ofputil_msg_type *type, struct ds *string, @@ -1472,6 +1552,10 @@ ofp_to_string__(const struct ofp_header *oh, case OFPUTIL_NXT_FLOW_AGE: break; + case OFPUTIL_NXT_SET_ASYNC_CONFIG: + ofp_print_nxt_set_async_config(string, msg); + break; + case OFPUTIL_NXST_AGGREGATE_REPLY: ofp_print_stats_reply(string, oh); ofp_print_nxst_aggregate_reply(string, msg); diff --git a/lib/ofp-util.c b/lib/ofp-util.c index c377e3e4c..e9366937b 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_FLOW_AGE, OFP10_VERSION, NXT_FLOW_AGE, "NXT_FLOW_AGE", sizeof(struct nicira_header), 0 }, + + { OFPUTIL_NXT_SET_ASYNC_CONFIG, OFP10_VERSION, + NXT_SET_ASYNC_CONFIG, "NXT_SET_ASYNC_CONFIG", + sizeof(struct nx_async_config), 0 }, }; static const struct ofputil_msg_category nxt_category = { diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 0b6970a86..816532e07 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_FLOW_AGE, + OFPUTIL_NXT_SET_ASYNC_CONFIG, /* NXST_* stat requests. */ OFPUTIL_NXST_FLOW_REQUEST, diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c index db848fbb2..a3ffb691a 100644 --- a/ofproto/connmgr.c +++ b/ofproto/connmgr.c @@ -60,8 +60,6 @@ struct ofconn { enum nx_flow_format flow_format; /* Currently selected flow format. */ enum nx_packet_in_format packet_in_format; /* OFPT_PACKET_IN format. */ bool flow_mod_table_id; /* NXT_FLOW_MOD_TABLE_ID enabled? */ - bool invalid_ttl_to_controller; /* Send packets with invalid TTL - to the controller. */ /* Asynchronous flow table operation support. */ struct list opgroups; /* Contains pending "ofopgroups", if any. */ @@ -80,6 +78,13 @@ struct ofconn { * requests. */ #define OFCONN_REPLY_MAX 100 struct rconn_packet_counter *reply_counter; + + /* Asynchronous message configuration in each possible roles. + * + * A 1-bit enables sending an asynchronous message for one possible reason + * that the message might be generated, a 0-bit disables it. */ + uint32_t master_async_config[OAM_N_TYPES]; /* master, other */ + uint32_t slave_async_config[OAM_N_TYPES]; /* slave */ }; static struct ofconn *ofconn_create(struct connmgr *, struct rconn *, @@ -100,8 +105,6 @@ static char *ofconn_make_name(const struct connmgr *, const char *target); static void ofconn_set_rate_limit(struct ofconn *, int rate, int burst); -static bool ofconn_receives_async_msgs(const struct ofconn *); - static void ofconn_send(const struct ofconn *, struct ofpbuf *, struct rconn_packet_counter *); @@ -762,15 +765,21 @@ ofconn_set_role(struct ofconn *ofconn, enum nx_role role) } void -ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool val) +ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool enable) { - ofconn->invalid_ttl_to_controller = val; + uint32_t bit = 1u << OFPR_INVALID_TTL; + if (enable) { + ofconn->master_async_config[OAM_PACKET_IN] |= bit; + } else { + ofconn->master_async_config[OAM_PACKET_IN] &= ~bit; + } } bool ofconn_get_invalid_ttl_to_controller(struct ofconn *ofconn) { - return ofconn->invalid_ttl_to_controller; + uint32_t bit = 1u << OFPR_INVALID_TTL; + return (ofconn->master_async_config[OAM_PACKET_IN] & bit) != 0; } /* Returns the currently configured flow format for 'ofconn', one of NXFF_*. @@ -840,6 +849,16 @@ ofconn_set_miss_send_len(struct ofconn *ofconn, int miss_send_len) ofconn->miss_send_len = miss_send_len; } +void +ofconn_set_async_config(struct ofconn *ofconn, + const uint32_t master_masks[OAM_N_TYPES], + const uint32_t slave_masks[OAM_N_TYPES]) +{ + size_t size = sizeof ofconn->master_async_config; + memcpy(ofconn->master_async_config, master_masks, size); + memcpy(ofconn->slave_async_config, slave_masks, size); +} + /* Sends 'msg' on 'ofconn', accounting it as a reply. (If there is a * sufficient number of OpenFlow replies in-flight on a single ofconn, then the * connmgr will stop accepting new OpenFlow requests on that ofconn until the @@ -956,6 +975,8 @@ ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type) static void ofconn_flush(struct ofconn *ofconn) { + uint32_t *master = ofconn->master_async_config; + uint32_t *slave = ofconn->slave_async_config; int i; ofconn->role = NX_ROLE_OTHER; @@ -996,6 +1017,24 @@ ofconn_flush(struct ofconn *ofconn) rconn_packet_counter_destroy(ofconn->reply_counter); ofconn->reply_counter = rconn_packet_counter_create(); + + /* "master" and "other" roles get all asynchronous messages by default, + * except that the controller needs to enable nonstandard "packet-in" + * reasons itself. */ + master[OAM_PACKET_IN] = (1u << OFPR_NO_MATCH) | (1u << OFPR_ACTION); + master[OAM_PORT_STATUS] = ((1u << OFPPR_ADD) + | (1u << OFPPR_DELETE) + | (1u << OFPPR_MODIFY)); + master[OAM_FLOW_REMOVED] = ((1u << OFPRR_IDLE_TIMEOUT) + | (1u << OFPRR_HARD_TIMEOUT) + | (1u << OFPRR_DELETE)); + + /* "slave" role gets port status updates by default. */ + slave[OAM_PACKET_IN] = 0; + slave[OAM_PORT_STATUS] = ((1u << OFPPR_ADD) + | (1u << OFPPR_DELETE) + | (1u << OFPPR_MODIFY)); + slave[OAM_FLOW_REMOVED] = 0; } static void @@ -1100,42 +1139,42 @@ ofconn_wait(struct ofconn *ofconn, bool handling_openflow) } } -/* Returns true if 'ofconn' should receive asynchronous messages. */ +/* Returns true if 'ofconn' should receive asynchronous messages of the given + * OAM_* 'type' and 'reason', which should be a OFPR_* value for OAM_PACKET_IN, + * a OFPPR_* value for OAM_PORT_STATUS, or an OFPRR_* value for + * OAM_FLOW_REMOVED. Returns false if the message should not be sent on + * 'ofconn'. */ static bool -ofconn_receives_async_msgs__(const struct ofconn *ofconn) +ofconn_receives_async_msg(const struct ofconn *ofconn, + enum ofconn_async_msg_type type, + unsigned int reason) { - if (ofconn->type == OFCONN_PRIMARY) { - /* Primary controllers always get asynchronous messages unless they - * have configured themselves as "slaves". */ - return ofconn->role != NX_ROLE_SLAVE; - } else { - /* Service connections don't get asynchronous messages unless they have - * explicitly asked for them by setting a nonzero miss send length. */ - return ofconn->miss_send_len > 0; - } -} + const uint32_t *async_config; + + assert(reason < 32); + assert((unsigned int) type < OAM_N_TYPES); -static bool -ofconn_receives_async_msgs(const struct ofconn *ofconn) -{ if (!rconn_is_connected(ofconn->rconn)) { return false; - } else { - return ofconn_receives_async_msgs__(ofconn); } -} -static bool -ofconn_interested_in_packet(const struct ofconn *ofconn, - const struct ofputil_packet_in *pin) -{ - if (!rconn_is_connected(ofconn->rconn)) { + /* Keep the following code in sync with the documentation in the + * "Asynchronous Messages" section in DESIGN. */ + + if (ofconn->type == OFCONN_SERVICE && !ofconn->miss_send_len) { + /* Service connections don't get asynchronous messages unless they have + * explicitly asked for them by setting a nonzero miss send length. */ return false; - } else if (pin->reason == OFPR_INVALID_TTL) { - return ofconn->invalid_ttl_to_controller; - } else { - return ofconn_receives_async_msgs__(ofconn); } + + async_config = (ofconn->role == NX_ROLE_SLAVE + ? ofconn->slave_async_config + : ofconn->master_async_config); + if (!(async_config[type] & (1u << reason))) { + return false; + } + + return true; } /* Returns a human-readable name for an OpenFlow connection between 'mgr' and @@ -1195,20 +1234,15 @@ connmgr_send_port_status(struct connmgr *mgr, const struct ofp_phy_port *opp, struct ofconn *ofconn; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { - struct ofp_port_status *ops; - struct ofpbuf *b; - - /* Primary controllers, even slaves, should always get port status - updates. Otherwise obey ofconn_receives_async_msgs(). */ - if (ofconn->type != OFCONN_PRIMARY - && !ofconn_receives_async_msgs(ofconn)) { - continue; + if (ofconn_receives_async_msg(ofconn, OAM_PORT_STATUS, reason)) { + struct ofp_port_status *ops; + struct ofpbuf *b; + + ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b); + ops->reason = reason; + ops->desc = *opp; + ofconn_send(ofconn, b, NULL); } - - ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b); - ops->reason = reason; - ops->desc = *opp; - ofconn_send(ofconn, b, NULL); } } @@ -1221,19 +1255,17 @@ connmgr_send_flow_removed(struct connmgr *mgr, struct ofconn *ofconn; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { - struct ofpbuf *msg; - - if (!ofconn_receives_async_msgs(ofconn)) { - continue; + if (ofconn_receives_async_msg(ofconn, OAM_FLOW_REMOVED, fr->reason)) { + struct ofpbuf *msg; + + /* Account flow expirations as replies to OpenFlow requests. That + * works because preventing OpenFlow requests from being processed + * also prevents new flows from being added (and expiring). (It + * also prevents processing OpenFlow requests that would not add + * new flows, so it is imperfect.) */ + msg = ofputil_encode_flow_removed(fr, ofconn->flow_format); + ofconn_send_reply(ofconn, msg); } - - /* Account flow expirations as replies to OpenFlow requests. That - * works because preventing OpenFlow requests from being processed also - * prevents new flows from being added (and expiring). (It also - * prevents processing OpenFlow requests that would not add new flows, - * so it is imperfect.) */ - msg = ofputil_encode_flow_removed(fr, ofconn->flow_format); - ofconn_send_reply(ofconn, msg); } } @@ -1247,7 +1279,7 @@ connmgr_send_packet_in(struct connmgr *mgr, struct ofconn *ofconn; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { - if (ofconn_interested_in_packet(ofconn, pin)) { + if (ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, pin->reason)) { schedule_packet_in(ofconn, *pin, flow); } } diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h index 8ff89f360..5f9ac708f 100644 --- a/ofproto/connmgr.h +++ b/ofproto/connmgr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011 Nicira Networks. + * Copyright (c) 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. @@ -51,6 +51,14 @@ enum ofconn_type { OFCONN_SERVICE /* A service connection, e.g. "ovs-ofctl". */ }; +/* The type of an OpenFlow asynchronous message. */ +enum ofconn_async_msg_type { + OAM_PACKET_IN, /* OFPT_PACKET_IN or NXT_PACKET_IN. */ + OAM_PORT_STATUS, /* OFPT_PORT_STATUS. */ + OAM_FLOW_REMOVED, /* OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED. */ + OAM_N_TYPES +}; + /* Basics. */ struct connmgr *connmgr_create(struct ofproto *ofproto, const char *dpif_name, const char *local_name); @@ -98,6 +106,10 @@ bool ofconn_get_invalid_ttl_to_controller(struct ofconn *); int ofconn_get_miss_send_len(const struct ofconn *); void ofconn_set_miss_send_len(struct ofconn *, int miss_send_len); +void ofconn_set_async_config(struct ofconn *, + const uint32_t master_masks[OAM_N_TYPES], + const uint32_t slave_masks[OAM_N_TYPES]); + void ofconn_send_reply(const struct ofconn *, struct ofpbuf *); void ofconn_send_replies(const struct ofconn *, struct list *); void ofconn_send_error(const struct ofconn *, const struct ofp_header *request, diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 92744a216..7b9974176 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -3117,6 +3117,26 @@ handle_nxt_set_packet_in_format(struct ofconn *ofconn, return 0; } +static enum ofperr +handle_nxt_set_async_config(struct ofconn *ofconn, const struct ofp_header *oh) +{ + const struct nx_async_config *msg = (const struct nx_async_config *) oh; + uint32_t master[OAM_N_TYPES]; + uint32_t slave[OAM_N_TYPES]; + + master[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[0]); + master[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[0]); + master[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[0]); + + slave[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[1]); + slave[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[1]); + slave[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[1]); + + ofconn_set_async_config(ofconn, master, slave); + + return 0; +} + static enum ofperr handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh) { @@ -3194,6 +3214,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) /* Nothing to do. */ return 0; + case OFPUTIL_NXT_SET_ASYNC_CONFIG: + return handle_nxt_set_async_config(ofconn, oh); + /* Statistics requests. */ case OFPUTIL_OFPST_DESC_REQUEST: return handle_desc_stats_request(ofconn, msg->data); diff --git a/tests/ofp-print.at b/tests/ofp-print.at index 09c9e835f..471a73cde 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -708,6 +708,26 @@ priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82 ]) AT_CLEANUP +AT_SETUP([NXT_SET_ASYNC_CONFIG]) +AT_KEYWORDS([ofp-print]) +AT_CHECK([ovs-ofctl ofp-print "\ +01 04 00 28 00 00 00 00 00 00 23 20 00 00 00 13 \ +00 00 10 05 00 00 10 07 00 00 00 03 00 00 00 07 \ +00 00 00 00 00 00 00 03 \ +"], [0], [dnl +NXT_SET_ASYNC_CONFIG (xid=0x0): + master: + PACKET_IN: no_match invalid_ttl 12 + PORT_STATUS: add delete + FLOW_REMOVED: (off) + + slave: + PACKET_IN: no_match action invalid_ttl 12 + PORT_STATUS: add delete modify + FLOW_REMOVED: idle hard +]) +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 d7fef5d3e..9358a6af0 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -174,6 +174,7 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x3, in_port=3 actions=output:0 NXST_FLOW reply: ]) + AT_CHECK([ovs-ofctl del-flows br0 cookie=0x3]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:0 @@ -421,3 +422,89 @@ NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([ofproto - asynchronous message control]) +OVS_VSWITCHD_START +AT_CHECK([ovs-ofctl -P openflow10 monitor br0 --detach --no-chdir --pidfile]) +check_async () { + printf '\n\n--- check_async %d ---\n\n\n' $1 + shift + + ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log + : > expout + + # OFPT_PACKET_IN, OFPR_ACTION + 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 + 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) +priority:0,tunnel:0,in_port:0000,tci(0) mac(00:26:b9:8c:b0:f9->00:25:83:df:b4:00) type:0800 proto:17 tos:0 ttl:0 ip(172.17.55.13->172.16.0.2) port(55155->53) udp_csum:8f6d" + fi + + # OFPT_PORT_STATUS, OFPPR_ADD + ovs-vsctl add-port br0 test -- set Interface test type=dummy + if test X"$1" = X"OFPPR_ADD"; then shift; + echo >>expout "OFPT_PORT_STATUS: ADD: 1(test): addr:aa:55:aa:55:00:0x + config: PORT_DOWN + state: LINK_DOWN" + fi + + # OFPT_PORT_STATUS, OFPPR_DELETE + ovs-vsctl del-port br0 test + if test X"$1" = X"OFPPR_DELETE"; then shift; + echo >>expout "OFPT_PORT_STATUS: DEL: 1(test): addr:aa:55:aa:55:00:0x + config: PORT_DOWN + state: LINK_DOWN" + fi + + # OFPT_FLOW_REMOVED, OFPRR_DELETE + ovs-ofctl add-flow br0 send_flow_rem,actions=drop + ovs-ofctl --strict del-flows br0 '' + if test X"$1" = X"OFPRR_DELETE"; then shift; + echo >>expout "OFPT_FLOW_REMOVED: reason=delete" + fi + AT_FAIL_IF([test X"$1" != X]) + + ovs-appctl -t ovs-ofctl ofctl/barrier + echo >>expout "send: OFPT_BARRIER_REQUEST: +OFPT_BARRIER_REPLY:" + + AT_CHECK( + [[sed ' +s/ (xid=0x[0-9a-fA-F]*)// +s/ *duration.*// +s/00:0.$/00:0x/' < monitor.log]], + [0], [expout]) +} + +# It's a service connection so initially there should be no async messages. +check_async 1 + +# Set miss_send_len to 128, turning on packet-outs for our service connection. +ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080 +check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_DELETE OFPRR_DELETE + +# Set miss_send_len to 128 and enable invalid_ttl. +ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700040080 +check_async 3 OFPR_ACTION OFPR_INVALID_TTL OFPPR_ADD OFPPR_DELETE OFPRR_DELETE + +# Become slave, which should disable everything except port status. +ovs-appctl -t ovs-ofctl ofctl/send 0104001400000002000023200000000a00000002 +check_async 4 OFPPR_ADD OFPPR_DELETE + +# Use NXT_SET_ASYNC_CONFIG to enable a patchwork of asynchronous messages. +ovs-appctl -t ovs-ofctl ofctl/send 01040028000000020000232000000013000000020000000500000005000000020000000200000005 +check_async 5 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE + +# Become master. +ovs-appctl -t ovs-ofctl ofctl/send 0104001400000002000023200000000a00000001 +check_async 6 OFPR_ACTION OFPPR_ADD + +ovs-appctl -t ovs-ofctl exit +AT_CLEANUP