aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Pettit <jpettit@nicira.com>2009-06-11 01:58:52 -0700
committerJustin Pettit <jpettit@nicira.com>2009-06-11 01:58:52 -0700
commita8d211487e1bb9b8d81e65b89e3ae389da4b884c (patch)
tree24e746720d3e19e7a99ba2b7847f95f54e0f31fd
parent08359020c222a73e3225b9be18dd9daf88305fcb (diff)
vswitch: Add support for large OFMP messages
OpenFlow uses a 16-bit field to describe the message length, which limits messages to a maximum 65535 bytes. Some of the messages passed by the management protocol may be larger than this, so a general Extended Data message has been added to management protocol. It encapsulates a single giant OpenFlow-like message, and breaks it into however many vaild smaller ones are required.
-rw-r--r--include/openflow/openflow-mgmt.h23
-rw-r--r--vswitchd/mgmt.c197
2 files changed, 171 insertions, 49 deletions
diff --git a/include/openflow/openflow-mgmt.h b/include/openflow/openflow-mgmt.h
index c900c182..1283eaea 100644
--- a/include/openflow/openflow-mgmt.h
+++ b/include/openflow/openflow-mgmt.h
@@ -27,7 +27,8 @@ enum ofmp_type {
OFMPT_CONFIG_REQUEST,
OFMPT_CONFIG_UPDATE,
OFMPT_CONFIG_UPDATE_ACK,
- OFMPT_ERROR
+ OFMPT_ERROR,
+ OFMPT_EXTENDED_DATA
};
/* Header on all OpenFlow management packets. */
@@ -235,4 +236,24 @@ struct ofmp_error_msg {
};
OFP_ASSERT(sizeof(struct ofmp_error_msg) == 24);
+/* Bitmask of extended data message flags. */
+enum ofmp_extended_data_flags {
+ OFMPEDF_MORE_DATA = 1 << 0, /* More data follows. */
+};
+
+/* Body of extended data message. May be sent by either the switch or the
+ * controller to send messages that are greater than 65535 bytes in
+ * length.
+ *
+ * OFMPT_EXTENDED_DATA (switch <-> controller) */
+struct ofmp_extended_data {
+ struct ofmp_header header;
+
+ uint16_t type; /* Type code of the encapsulated message. */
+ uint8_t flags; /* One of OFMPEDF_*. */
+ uint8_t pad;
+ uint8_t data[0]; /* Variable-length data. */
+};
+OFP_ASSERT(sizeof(struct ofmp_extended_data) == 24);
+
#endif /* openflow/openflow-mgmt.h */
diff --git a/vswitchd/mgmt.c b/vswitchd/mgmt.c
index f42f679c..4f79263c 100644
--- a/vswitchd/mgmt.c
+++ b/vswitchd/mgmt.c
@@ -61,6 +61,7 @@ static uint8_t cfg_cookie[CFG_COOKIE_LEN];
static struct rconn *mgmt_rconn;
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
static struct svec capabilities;
+static struct ofpbuf ext_data_buffer;
uint64_t mgmt_id;
@@ -70,6 +71,7 @@ struct rconn_packet_counter *txqlen; /* # pkts queued for tx on mgmt_rconn. */
static uint64_t pick_fallback_mgmt_id(void);
static void send_config_update(uint32_t xid, bool use_xid);
static void send_resources_update(uint32_t xid, bool use_xid);
+static int recv_ofmp(uint32_t xid, struct ofmp_header *ofmph, size_t len);
void
mgmt_init(void)
@@ -86,6 +88,8 @@ mgmt_init(void)
/* Randomly generate a mgmt id */
mgmt_id = pick_fallback_mgmt_id();
}
+
+ ofpbuf_init(&ext_data_buffer, 0);
}
#ifdef HAVE_OPENSSL
@@ -220,40 +224,6 @@ mgmt_reconfigure(void)
}
}
-static int
-send_openflow_buffer(struct ofpbuf *buffer)
-{
- int retval;
-
- if (!mgmt_rconn) {
- VLOG_ERR("attempt to send openflow packet with no rconn\n");
- return EINVAL;
- }
-
- update_openflow_length(buffer);
- retval = rconn_send_with_limit(mgmt_rconn, buffer, txqlen, TXQ_LIMIT);
- if (retval) {
- VLOG_WARN_RL(&rl, "send to %s failed: %s",
- rconn_get_name(mgmt_rconn), strerror(retval));
- }
- return retval;
-}
-
-static void
-send_features_reply(uint32_t xid)
-{
- struct ofpbuf *buffer;
- struct ofp_switch_features *ofr;
-
- ofr = make_openflow_xid(sizeof *ofr, OFPT_FEATURES_REPLY, xid, &buffer);
- ofr->datapath_id = 0;
- ofr->n_tables = 0;
- ofr->n_buffers = 0;
- ofr->capabilities = 0;
- ofr->actions = 0;
- send_openflow_buffer(buffer);
-}
-
static void *
make_ofmp_xid(size_t ofmp_len, uint16_t type, uint32_t xid,
struct ofpbuf **bufferp)
@@ -281,6 +251,93 @@ make_ofmp(size_t ofmp_len, uint16_t type, struct ofpbuf **bufferp)
return oh;
}
+static int
+send_openflow_buffer(struct ofpbuf *buffer)
+{
+ int retval;
+
+ if (!mgmt_rconn) {
+ VLOG_ERR("attempt to send openflow packet with no rconn\n");
+ return EINVAL;
+ }
+
+ /* OpenFlow messages use a 16-bit length field, so messages over 64K
+ * must be broken into multiple pieces.
+ */
+ if (buffer->size <= 65535) {
+ update_openflow_length(buffer);
+ retval = rconn_send_with_limit(mgmt_rconn, buffer, txqlen, TXQ_LIMIT);
+ if (retval) {
+ VLOG_WARN_RL(&rl, "send to %s failed: %s",
+ rconn_get_name(mgmt_rconn), strerror(retval));
+ }
+ return retval;
+ } else {
+ struct ofmp_header *header = (struct ofmp_header *)buffer->data;
+ uint32_t xid = header->header.header.xid;
+ size_t remain = buffer->size;
+ uint8_t *ptr = buffer->data;
+
+ /* Mark the OpenFlow header with a zero length to indicate some
+ * funkiness.
+ */
+ header->header.header.length = 0;
+
+ while (remain > 0) {
+ struct ofpbuf *new_buffer;
+ struct ofmp_extended_data *oed;
+ size_t new_len = MIN(65535 - sizeof *oed, remain);
+
+ oed = make_ofmp_xid(sizeof *oed, OFMPT_EXTENDED_DATA, xid,
+ &new_buffer);
+ oed->type = header->type;
+
+ if (remain > 65535) {
+ oed->flags |= OFMPEDF_MORE_DATA;
+ }
+
+ printf("xxx SENDING LEN: %d\n", new_len);
+
+ /* Copy the entire original message, including the OpenFlow
+ * header, since management protocol structure definitions
+ * include these headers.
+ */
+ ofpbuf_put(new_buffer, ptr, new_len);
+
+ update_openflow_length(new_buffer);
+ retval = rconn_send_with_limit(mgmt_rconn, new_buffer, txqlen,
+ TXQ_LIMIT);
+ if (retval) {
+ VLOG_WARN_RL(&rl, "send to %s failed: %s",
+ rconn_get_name(mgmt_rconn), strerror(retval));
+ ofpbuf_delete(buffer);
+ return retval;
+ }
+
+ remain -= new_len;
+ ptr += new_len;
+ }
+
+ ofpbuf_delete(buffer);
+ return 0;
+ }
+}
+
+static void
+send_features_reply(uint32_t xid)
+{
+ struct ofpbuf *buffer;
+ struct ofp_switch_features *ofr;
+
+ ofr = make_openflow_xid(sizeof *ofr, OFPT_FEATURES_REPLY, xid, &buffer);
+ ofr->datapath_id = 0;
+ ofr->n_tables = 0;
+ ofr->n_buffers = 0;
+ ofr->capabilities = 0;
+ ofr->actions = 0;
+ send_openflow_buffer(buffer);
+}
+
static void
send_capability_reply(uint32_t xid)
{
@@ -520,11 +577,12 @@ recv_set_config(uint32_t xid UNUSED, const void *msg UNUSED)
}
static int
-recv_ofmp_capability_request(uint32_t xid, const struct ofmp_header *ofmph)
+recv_ofmp_capability_request(uint32_t xid, const struct ofmp_header *ofmph,
+ size_t len)
{
struct ofmp_capability_request *ofmpcr;
- if (htons(ofmph->header.header.length) != sizeof(*ofmpcr)) {
+ if (len != sizeof(*ofmpcr)) {
/* xxx Send error */
return -EINVAL;
}
@@ -541,18 +599,20 @@ recv_ofmp_capability_request(uint32_t xid, const struct ofmp_header *ofmph)
}
static int
-recv_ofmp_resources_request(uint32_t xid, const void *msg UNUSED)
+recv_ofmp_resources_request(uint32_t xid, const void *msg UNUSED,
+ size_t len UNUSED)
{
send_resources_update(xid, true);
return 0;
}
static int
-recv_ofmp_config_request(uint32_t xid, const struct ofmp_header *ofmph)
+recv_ofmp_config_request(uint32_t xid, const struct ofmp_header *ofmph,
+ size_t len)
{
struct ofmp_config_request *ofmpcr;
- if (htons(ofmph->header.header.length) != sizeof(*ofmpcr)) {
+ if (len != sizeof(*ofmpcr)) {
/* xxx Send error */
return -EINVAL;
}
@@ -569,12 +629,13 @@ recv_ofmp_config_request(uint32_t xid, const struct ofmp_header *ofmph)
}
static int
-recv_ofmp_config_update(uint32_t xid, const struct ofmp_header *ofmph)
+recv_ofmp_config_update(uint32_t xid, const struct ofmp_header *ofmph,
+ size_t len)
{
struct ofmp_config_update *ofmpcu;
int data_len;
- data_len = htons(ofmph->header.header.length) - sizeof(*ofmpcu);
+ data_len = len - sizeof(*ofmpcu);
if (data_len <= sizeof(*ofmpcu)) {
/* xxx Send error. */
return -EINVAL;
@@ -612,20 +673,60 @@ recv_ofmp_config_update(uint32_t xid, const struct ofmp_header *ofmph)
return 0;
}
+static int
+recv_ofmp_extended_data(uint32_t xid, const struct ofmp_header *ofmph,
+ size_t len)
+{
+ size_t data_len;
+ struct ofmp_extended_data *ofmped;
+ uint8_t *ptr;
+
+ data_len = len - sizeof(*ofmped);
+ if (data_len <= sizeof(*ofmped)) {
+ /* xxx Send error. */
+ return -EINVAL;
+ }
+
+ ofmped = (struct ofmp_extended_data *)ofmph;
+
+ ptr = ofpbuf_put(&ext_data_buffer, ofmped->data, data_len);
+
+ if (!ofmped->flags & OFMPEDF_MORE_DATA) {
+ recv_ofmp(xid, ext_data_buffer.data, ext_data_buffer.size);
+ ofpbuf_clear(&ext_data_buffer);
+ }
+
+ return 0;
+}
+
+/* Handles receiving a management message. Generally, this function
+ * will be called 'len' set to zero, and the length will be derived by
+ * the OpenFlow header. With the extended data message, management
+ * messages are not constrained by OpenFlow's 64K message length limit.
+ * The extended data handler calls this function with the 'len' set to
+ * the total message length and the OpenFlow header's length field is
+ * ignored.
+ */
static
-int recv_ofmp(uint32_t xid, struct ofmp_header *ofmph)
+int recv_ofmp(uint32_t xid, struct ofmp_header *ofmph, size_t len)
{
+ if (!len) {
+ len = ntohs(ofmph->header.header.length);
+ }
+
/* xxx Should sanity-check for min/max length */
switch (ntohs(ofmph->type))
{
case OFMPT_CAPABILITY_REQUEST:
- return recv_ofmp_capability_request(xid, ofmph);
+ return recv_ofmp_capability_request(xid, ofmph, len);
case OFMPT_RESOURCES_REQUEST:
- return recv_ofmp_resources_request(xid, ofmph);
+ return recv_ofmp_resources_request(xid, ofmph, len);
case OFMPT_CONFIG_REQUEST:
- return recv_ofmp_config_request(xid, ofmph);
+ return recv_ofmp_config_request(xid, ofmph, len);
case OFMPT_CONFIG_UPDATE:
- return recv_ofmp_config_update(xid, ofmph);
+ return recv_ofmp_config_update(xid, ofmph, len);
+ case OFMPT_EXTENDED_DATA:
+ return recv_ofmp_extended_data(xid, ofmph, len);
default:
VLOG_WARN_RL(&rl, "unknown mgmt message: %d",
ntohs(ofmph->type));
@@ -641,11 +742,11 @@ recv_nx_msg(uint32_t xid, const void *oh)
switch (ntohl(nh->subtype)) {
case NXT_MGMT:
- return recv_ofmp(xid, (struct ofmp_header *)oh);
+ return recv_ofmp(xid, (struct ofmp_header *)oh, 0);
default:
send_error_msg(xid, OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE,
- oh, htons(nh->header.length));
+ oh, ntohs(nh->header.length));
return -EINVAL;
}
}