From: Ben Pfaff Date: Thu, 3 Apr 2008 18:40:16 +0000 (-0700) Subject: Break data_hello and control_hello messages into multiple messages. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60a87c9b0032346568485ad40fd72ea1f72b8674;p=openvswitch Break data_hello and control_hello messages into multiple messages. We want to allow multiple OpenFlow connections to a switch, so that dpctl and other management tools can also connect to a switch that is connecte to the controller. These tools will want to fetch the information in the data_hello message, but currently this can only be done by sending a control_hello message, which changes the switch configuration state. So breaking the hello messages up into multiple different messages, only some of which affect configuration state, cures the problem. Also, clean up the kernel datapath a bit, by adding helper functions for allocating and resizing Generic Netlink messages that encapsulate OpenFlow messages. --- diff --git a/controller/controller.c b/controller/controller.c index 7d2b77aa..9355c02b 100644 --- a/controller/controller.c +++ b/controller/controller.c @@ -71,7 +71,7 @@ struct switch_ { struct vconn *vconn; uint64_t datapath_id; - time_t last_control_hello; + time_t last_features_request; struct queue txq; }; @@ -91,7 +91,7 @@ static void close_switch(struct switch_ *); static void queue_tx(struct switch_ *, struct buffer *); -static void send_control_hello(struct switch_ *); +static void send_features_request(struct switch_ *); static int do_switch_recv(struct switch_ *this); static int do_switch_send(struct switch_ *this); @@ -162,6 +162,7 @@ main(int argc, char *argv[]) if (retval) { break; } + printf("accept!\n"); switches[n_switches++] = new_switch("tcp", new_vconn); } } else { @@ -261,9 +262,9 @@ new_switch(const char *name, struct vconn *vconn) this->name = xstrdup(name); this->vconn = vconn; queue_init(&this->txq); - this->last_control_hello = 0; + this->last_features_request = 0; if (!vconn_is_passive(vconn)) { - send_control_hello(this); + send_features_request(this); } return this; } @@ -272,6 +273,7 @@ static void close_switch(struct switch_ *this) { if (this) { + printf("dropped!\n"); free(this->name); vconn_close(this->vconn); queue_destroy(&this->txq); @@ -280,25 +282,35 @@ close_switch(struct switch_ *this) } static void -send_control_hello(struct switch_ *this) +send_features_request(struct switch_ *this) { time_t now = time(0); - if (now >= this->last_control_hello + 1) { + if (now >= this->last_features_request + 1) { struct buffer *b; - struct ofp_control_hello *och; + struct ofp_header *ofr; + struct ofp_switch_config *osc; + /* Send OFPT_SET_CONFIG. */ b = buffer_new(0); - och = buffer_put_uninit(b, sizeof *och); - memset(och, 0, sizeof *och); - och->header.version = OFP_VERSION; - och->header.length = htons(sizeof *och); - - och->version = htonl(OFP_VERSION); - och->flags = htons(OFP_CHELLO_SEND_FLOW_EXP); - och->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN); + osc = buffer_put_uninit(b, sizeof *osc); + memset(osc, 0, sizeof *osc); + osc->header.type = OFPT_SET_CONFIG; + osc->header.version = OFP_VERSION; + osc->header.length = htons(sizeof *osc); + osc->flags = htons(OFPC_SEND_FLOW_EXP); + osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN); queue_tx(this, b); - this->last_control_hello = now; + /* Send OFPT_FEATURES_REQUEST. */ + b = buffer_new(0); + ofr = buffer_put_uninit(b, sizeof *ofr); + memset(ofr, 0, sizeof *ofr); + ofr->type = OFPT_FEATURES_REQUEST; + ofr->version = OFP_VERSION; + ofr->length = htons(sizeof *ofr); + queue_tx(this, b); + + this->last_features_request = now; } } @@ -312,18 +324,9 @@ static void process_packet(struct switch_ *sw, struct buffer *msg) { static const size_t min_size[UINT8_MAX + 1] = { - [0 ... UINT8_MAX] = SIZE_MAX, - [OFPT_CONTROL_HELLO] = sizeof (struct ofp_control_hello), - [OFPT_DATA_HELLO] = sizeof (struct ofp_data_hello), + [0 ... UINT8_MAX] = sizeof (struct ofp_header), + [OFPT_FEATURES_REPLY] = sizeof (struct ofp_switch_features), [OFPT_PACKET_IN] = offsetof (struct ofp_packet_in, data), - [OFPT_PACKET_OUT] = sizeof (struct ofp_packet_out), - [OFPT_FLOW_MOD] = sizeof (struct ofp_flow_mod), - [OFPT_FLOW_EXPIRED] = sizeof (struct ofp_flow_expired), - [OFPT_TABLE] = sizeof (struct ofp_table), - [OFPT_PORT_MOD] = sizeof (struct ofp_port_mod), - [OFPT_PORT_STATUS] = sizeof (struct ofp_port_status), - [OFPT_FLOW_STAT_REQUEST] = sizeof (struct ofp_flow_stat_request), - [OFPT_FLOW_STAT_REPLY] = sizeof (struct ofp_flow_stat_reply), }; struct ofp_header *oh; @@ -334,29 +337,26 @@ process_packet(struct switch_ *sw, struct buffer *msg) return; } - if (oh->type == OFPT_DATA_HELLO) { - struct ofp_data_hello *odh = msg->data; - sw->datapath_id = odh->datapath_id; + if (oh->type == OFPT_FEATURES_REPLY) { + struct ofp_switch_features *osf = msg->data; + sw->datapath_id = osf->datapath_id; } else if (sw->datapath_id == 0) { - send_control_hello(sw); - return; - } - - if (oh->type == OFPT_PACKET_IN) { + send_features_request(sw); + } else if (oh->type == OFPT_PACKET_IN) { + struct ofp_packet_in *opi = msg->data; if (sw->txq.n >= MAX_TXQ) { /* FIXME: ratelimit. */ VLOG_WARN("%s: tx queue overflow", sw->name); } else if (noflow) { - process_noflow(sw, msg->data); + process_noflow(sw, opi); } else if (hub) { - process_hub(sw, msg->data); + process_hub(sw, opi); } else { - process_switch(sw, msg->data); + process_switch(sw, opi); } - return; + } else { + ofp_print(stdout, msg->data, msg->size, 2); } - - ofp_print(stdout, msg->data, msg->size, 2); } static void diff --git a/datapath/datapath.c b/datapath/datapath.c index b80c7b20..b56c5c91 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -95,6 +95,56 @@ void nla_unreserve(struct sk_buff *skb, struct nlattr *nla, int len) nla->nla_len -= len; } +static void * +alloc_openflow_skb(struct datapath *dp, size_t openflow_len, + uint8_t type, uint32_t xid, struct sk_buff **pskb) +{ + size_t genl_len; + struct sk_buff *skb; + struct nlattr *attr; + struct ofp_header *oh; + + genl_len = nla_total_size(sizeof(uint32_t)); /* DP_GENL_A_DP_IDX */ + genl_len += nla_total_size(openflow_len); /* DP_GENL_A_OPENFLOW */ + skb = *pskb = genlmsg_new(genl_len, GFP_ATOMIC); + if (!skb) { + if (net_ratelimit()) + printk("alloc_openflow_skb: genlmsg_new failed\n"); + return NULL; + } + + /* Assemble the Generic Netlink wrapper. */ + if (!genlmsg_put(skb, 0, 0, &dp_genl_family, 0, DP_GENL_C_OPENFLOW)) + BUG(); + if (nla_put_u32(skb, DP_GENL_A_DP_IDX, dp->dp_idx) < 0) + BUG(); + attr = nla_reserve(skb, DP_GENL_A_OPENFLOW, openflow_len); + BUG_ON(!attr); + nlmsg_end(skb, (struct nlmsghdr *) skb->data); + + /* Fill in the header. */ + oh = nla_data(attr); + oh->version = OFP_VERSION; + oh->type = type; + oh->length = htons(openflow_len); + oh->xid = xid; + + return oh; +} + +static void +resize_openflow_skb(struct sk_buff *skb, + struct ofp_header *oh, size_t new_length) +{ + struct nlattr *attr; + + BUG_ON(new_length > ntohs(oh->length)); + attr = ((void *) oh) - NLA_HDRLEN; + nla_unreserve(skb, attr, ntohs(oh->length) - new_length); + oh->length = htons(new_length); + nlmsg_end(skb, (struct nlmsghdr *) skb->data); +} + /* Generates a unique datapath id. It incorporates the datapath index * and a hardware address, if available. If not, it generates a random * one. @@ -174,7 +224,8 @@ static int new_dp(int dp_idx) printk("datapath: problem setting up 'of' device\n"); #endif - dp->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; + dp->config.flags = 0; + dp->config.miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; dp->dp_task = kthread_run(dp_maint_func, dp, "dp%d", dp_idx); if (IS_ERR(dp->dp_task)) @@ -501,58 +552,26 @@ int dp_output_control(struct datapath *dp, struct sk_buff *skb, uint32_t buffer_id, size_t max_len, int reason) { - /* FIXME? packet_rcv_spkt in net/packet/af_packet.c does some stuff - that we should possibly be doing here too. */ /* FIXME? Can we avoid creating a new skbuff in the case where we * forward the whole packet? */ struct sk_buff *f_skb; - struct nlattr *attr; struct ofp_packet_in *opi; - size_t opi_len; - size_t len, fwd_len; - void *data; - int err = -ENOMEM; + size_t fwd_len, opi_len; + int err; fwd_len = skb->len; if ((buffer_id != (uint32_t) -1) && max_len) fwd_len = min(fwd_len, max_len); - len = nla_total_size(offsetof(struct ofp_packet_in, data) + fwd_len) - + nla_total_size(sizeof(uint32_t)); - - f_skb = genlmsg_new(MAX(len, NLMSG_GOODSIZE), GFP_ATOMIC); - if (!f_skb) - goto error_free_skb; - - data = genlmsg_put(f_skb, 0, 0, &dp_genl_family, 0, - DP_GENL_C_OPENFLOW); - if (data == NULL) - goto error_free_f_skb; - - NLA_PUT_U32(f_skb, DP_GENL_A_DP_IDX, dp->dp_idx); - opi_len = offsetof(struct ofp_packet_in, data) + fwd_len; - attr = nla_reserve(f_skb, DP_GENL_A_OPENFLOW, opi_len); - if (!attr) - goto error_free_f_skb; - opi = nla_data(attr); - opi->header.version = OFP_VERSION; - opi->header.type = OFPT_PACKET_IN; - opi->header.length = htons(opi_len); - opi->header.xid = htonl(0); - + opi = alloc_openflow_skb(dp, opi_len, OFPT_PACKET_IN, 0, &f_skb); opi->buffer_id = htonl(buffer_id); opi->total_len = htons(skb->len); opi->in_port = htons(skb->dev->br_port->port_no); opi->reason = reason; opi->pad = 0; - SKB_LINEAR_ASSERT(skb); memcpy(opi->data, skb_mac_header(skb), fwd_len); - err = genlmsg_end(f_skb, data); - if (err < 0) - goto error_free_f_skb; - err = genlmsg_multicast(f_skb, 0, mc_group.id, GFP_ATOMIC); if (err && net_ratelimit()) printk(KERN_WARNING "dp_output_control: genlmsg_multicast failed: %d\n", err); @@ -560,15 +579,6 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, kfree_skb(skb); return err; - -nla_put_failure: -error_free_f_skb: - nlmsg_free(f_skb); -error_free_skb: - kfree_skb(skb); - if (net_ratelimit()) - printk(KERN_ERR "dp_output_control: failed to send: %d\n", err); - return err; } static void fill_port_desc(struct net_bridge_port *p, struct ofp_phy_port *desc) @@ -610,28 +620,24 @@ static void fill_port_desc(struct net_bridge_port *p, struct ofp_phy_port *desc) } static int -fill_data_hello(struct datapath *dp, struct ofp_data_hello *odh) +fill_features_reply(struct datapath *dp, struct ofp_switch_features *ofr) { struct net_bridge_port *p; int port_count = 0; - odh->header.version = OFP_VERSION; - odh->header.type = OFPT_DATA_HELLO; - odh->header.xid = htonl(0); - odh->datapath_id = cpu_to_be64(dp->id); - - odh->n_exact = htonl(2 * TABLE_HASH_MAX_FLOWS); - odh->n_mac_only = htonl(TABLE_MAC_MAX_FLOWS); - odh->n_compression = 0; /* Not supported */ - odh->n_general = htonl(TABLE_LINEAR_MAX_FLOWS); - odh->buffer_mb = htonl(UINT32_MAX); - odh->n_buffers = htonl(N_PKT_BUFFERS); - odh->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES); - odh->actions = htonl(OFP_SUPPORTED_ACTIONS); - odh->miss_send_len = htons(dp->miss_send_len); + ofr->datapath_id = cpu_to_be64(dp->id); + + ofr->n_exact = htonl(2 * TABLE_HASH_MAX_FLOWS); + ofr->n_mac_only = htonl(TABLE_MAC_MAX_FLOWS); + ofr->n_compression = 0; /* Not supported */ + ofr->n_general = htonl(TABLE_LINEAR_MAX_FLOWS); + ofr->buffer_mb = htonl(UINT32_MAX); + ofr->n_buffers = htonl(N_PKT_BUFFERS); + ofr->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES); + ofr->actions = htonl(OFP_SUPPORTED_ACTIONS); list_for_each_entry_rcu (p, &dp->port_list, node) { - fill_port_desc(p, &odh->ports[port_count]); + fill_port_desc(p, &ofr->ports[port_count]); port_count++; } @@ -639,78 +645,55 @@ fill_data_hello(struct datapath *dp, struct ofp_data_hello *odh) } int -dp_send_hello(struct datapath *dp) +dp_send_features_reply(struct datapath *dp, uint32_t xid) { struct sk_buff *skb; - struct nlattr *attr; - struct ofp_data_hello *odh; - size_t odh_max_len, odh_len, port_max_len, len; - void *data; - int err = -ENOMEM; + struct ofp_switch_features *ofr; + size_t ofr_len, port_max_len; + int err; int port_count; - - /* Overallocate, since we can't reliably determine the number of - * ports a priori. */ + /* Overallocate. */ port_max_len = sizeof(struct ofp_phy_port) * OFPP_MAX; + ofr = alloc_openflow_skb(dp, sizeof(*ofr) + port_max_len, + OFPT_FEATURES_REPLY, xid, &skb); + if (!ofr) + return -ENOMEM; - len = nla_total_size(sizeof(*odh) + port_max_len) - + nla_total_size(sizeof(uint32_t)); - - skb = genlmsg_new(MAX(len, NLMSG_GOODSIZE), GFP_ATOMIC); - if (!skb) { - if (net_ratelimit()) - printk("dp_send_hello: genlmsg_new failed\n"); - goto error; - } - - data = genlmsg_put(skb, 0, 0, &dp_genl_family, 0, - DP_GENL_C_OPENFLOW); - if (data == NULL) { - if (net_ratelimit()) - printk("dp_send_hello: genlmsg_put failed\n"); - goto error; - } + /* Fill. */ + port_count = fill_features_reply(dp, ofr); - NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, dp->dp_idx); + /* Shrink to fit. */ + ofr_len = sizeof(*ofr) + (sizeof(struct ofp_phy_port) * port_count); + resize_openflow_skb(skb, &ofr->header, ofr_len); - odh_max_len = sizeof(*odh) + port_max_len; - attr = nla_reserve(skb, DP_GENL_A_OPENFLOW, odh_max_len); - if (!attr) { - if (net_ratelimit()) - printk("dp_send_hello: nla_reserve failed\n"); - goto error; - } - odh = nla_data(attr); - port_count = fill_data_hello(dp, odh); + /* FIXME: send as reply. */ + err = genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC); + if (err && net_ratelimit()) + printk(KERN_WARNING "dp_send_hello: genlmsg_multicast failed: %d\n", err); - /* Only now that we know how many ports we've added can we say - * say something about the length. */ - odh_len = sizeof(*odh) + (sizeof(struct ofp_phy_port) * port_count); - odh->header.length = htons(odh_len); + return err; +} - /* Take back the unused part that was reserved */ - nla_unreserve(skb, attr, (odh_max_len - odh_len)); +int +dp_send_config_reply(struct datapath *dp, uint32_t xid) +{ + struct sk_buff *skb; + struct ofp_switch_config *osc; + int err; - err = genlmsg_end(skb, data); - if (err < 0) { - if (net_ratelimit()) - printk("dp_send_hello: genlmsg_end failed\n"); - goto error; - } + osc = alloc_openflow_skb(dp, sizeof *osc, OFPT_PORT_STATUS, 0, &skb); + if (!osc) + return -ENOMEM; + memcpy(((char *)osc) + sizeof osc->header, + ((char *)&dp->config) + sizeof dp->config.header, + sizeof dp->config - sizeof dp->config.header); err = genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC); if (err && net_ratelimit()) - printk(KERN_WARNING "dp_send_hello: genlmsg_multicast failed: %d\n", err); + printk(KERN_WARNING "send_port_status: genlmsg_multicast failed: %d\n", err); return err; - -nla_put_failure: -error: - kfree_skb(skb); - if (net_ratelimit()) - printk(KERN_ERR "dp_send_hello: failed to send: %d\n", err); - return err; } int @@ -734,106 +717,34 @@ static int send_port_status(struct net_bridge_port *p, uint8_t status) { struct sk_buff *skb; - struct nlattr *attr; struct ofp_port_status *ops; - void *data; - int err = -ENOMEM; - - - skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); - if (!skb) { - if (net_ratelimit()) - printk("send_port_status: genlmsg_new failed\n"); - goto error; - } - - data = genlmsg_put(skb, 0, 0, &dp_genl_family, 0, - DP_GENL_C_OPENFLOW); - if (data == NULL) { - if (net_ratelimit()) - printk("send_port_status: genlmsg_put failed\n"); - goto error; - } - - NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, p->dp->dp_idx); - - attr = nla_reserve(skb, DP_GENL_A_OPENFLOW, sizeof(*ops)); - if (!attr) { - if (net_ratelimit()) - printk("send_port_status: nla_reserve failed\n"); - goto error; - } - - ops = nla_data(attr); - ops->header.version = OFP_VERSION; - ops->header.type = OFPT_PORT_STATUS; - ops->header.length = htons(sizeof(*ops)); - ops->header.xid = htonl(0); + int err; - ops->reason = status; + ops = alloc_openflow_skb(p->dp, sizeof *ops, OFPT_PORT_STATUS, 0, + &skb); + if (!ops) + return -ENOMEM; + ops->reason = status; fill_port_desc(p, &ops->desc); - err = genlmsg_end(skb, data); - if (err < 0) { - if (net_ratelimit()) - printk("send_port_status: genlmsg_end failed\n"); - goto error; - } - err = genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC); if (err && net_ratelimit()) printk(KERN_WARNING "send_port_status: genlmsg_multicast failed: %d\n", err); return err; - -nla_put_failure: -error: - kfree_skb(skb); - if (net_ratelimit()) - printk(KERN_ERR "send_port_status: failed to send: %d\n", err); - return err; } int dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow) { struct sk_buff *skb; - struct nlattr *attr; struct ofp_flow_expired *ofe; - void *data; unsigned long duration_j; - int err = -ENOMEM; - - - skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); - if (!skb) { - if (net_ratelimit()) - printk("dp_send_flow_expired: genlmsg_new failed\n"); - goto error; - } - - data = genlmsg_put(skb, 0, 0, &dp_genl_family, 0, - DP_GENL_C_OPENFLOW); - if (data == NULL) { - if (net_ratelimit()) - printk("dp_send_flow_expired: genlmsg_put failed\n"); - goto error; - } - - NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, dp->dp_idx); - - attr = nla_reserve(skb, DP_GENL_A_OPENFLOW, sizeof(*ofe)); - if (!attr) { - if (net_ratelimit()) - printk("dp_send_flow_expired: nla_reserve failed\n"); - goto error; - } + int err; - ofe = nla_data(attr); - ofe->header.version = OFP_VERSION; - ofe->header.type = OFPT_FLOW_EXPIRED; - ofe->header.length = htons(sizeof(*ofe)); - ofe->header.xid = htonl(0); + ofe = alloc_openflow_skb(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &skb); + if (!ofe) + return -ENOMEM; flow_fill_match(&ofe->match, &flow->key); duration_j = (flow->timeout - HZ * flow->max_idle) - flow->init_time; @@ -841,25 +752,11 @@ dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow) ofe->packet_count = cpu_to_be64(flow->packet_count); ofe->byte_count = cpu_to_be64(flow->byte_count); - err = genlmsg_end(skb, data); - if (err < 0) { - if (net_ratelimit()) - printk("dp_send_flow_expired: genlmsg_end failed\n"); - goto error; - } - err = genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC); if (err && net_ratelimit()) printk(KERN_WARNING "send_flow_expired: genlmsg_multicast failed: %d\n", err); return err; - -nla_put_failure: -error: - kfree_skb(skb); - if (net_ratelimit()) - printk(KERN_ERR "send_flow_expired: failed to send: %d\n", err); - return err; } /* Generic Netlink interface. @@ -1022,79 +919,6 @@ dp_fill_flow(struct ofp_flow_mod* ofm, struct swt_iterator* iter) return 0; } -static int dp_genl_show(struct sk_buff *skb, struct genl_info *info) -{ - struct datapath *dp; - int err = -ENOMEM; - struct sk_buff *ans_skb = NULL; - void *data; - struct nlattr *attr; - struct ofp_data_hello *odh; - size_t odh_max_len, odh_len, port_max_len, len; - int port_count; - - if (!info->attrs[DP_GENL_A_DP_IDX]) - return -EINVAL; - - mutex_lock(&dp_mutex); - dp = dp_get(nla_get_u32((info->attrs[DP_GENL_A_DP_IDX]))); - if (!dp) - goto error; - - /* Overallocate, since we can't reliably determine the number of - * ports a priori. */ - port_max_len = sizeof(struct ofp_phy_port) * OFPP_MAX; - - len = nla_total_size(sizeof(*odh) + port_max_len) - + nla_total_size(sizeof(uint32_t)); - - ans_skb = nlmsg_new(MAX(len, NLMSG_GOODSIZE), GFP_KERNEL); - if (!ans_skb) - goto error; - - data = genlmsg_put_reply(ans_skb, info, &dp_genl_family, - 0, DP_GENL_C_SHOW_DP); - if (data == NULL) - goto error; - - NLA_PUT_U32(ans_skb, DP_GENL_A_DP_IDX, dp->dp_idx); - - odh_max_len = sizeof(*odh) + port_max_len; - attr = nla_reserve(ans_skb, DP_GENL_A_DP_INFO, odh_max_len); - if (!attr) - goto error; - odh = nla_data(attr); - port_count = fill_data_hello(dp, odh); - - /* Only now that we know how many ports we've added can we say - * say something about the length. */ - odh_len = sizeof(*odh) + (sizeof(struct ofp_phy_port) * port_count); - odh->header.length = htons(odh_len); - - /* Take back the unused part that was reserved */ - nla_unreserve(ans_skb, attr, (odh_max_len - odh_len)); - - genlmsg_end(ans_skb, data); - err = genlmsg_reply(ans_skb, info); - if (!err) - ans_skb = NULL; - -error: -nla_put_failure: - if (ans_skb) - kfree_skb(ans_skb); - mutex_unlock(&dp_mutex); - return err; -} - -static struct genl_ops dp_genl_ops_show_dp = { - .cmd = DP_GENL_C_SHOW_DP, - .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ - .policy = dp_genl_policy, - .doit = dp_genl_show, - .dumpit = NULL, -}; - /* Convenience function */ static void* @@ -1542,7 +1366,6 @@ static struct genl_ops *dp_genl_all_ops[] = { &dp_genl_ops_query_flow, &dp_genl_ops_query_table, - &dp_genl_ops_show_dp, &dp_genl_ops_add_dp, &dp_genl_ops_del_dp, &dp_genl_ops_query_dp, diff --git a/datapath/datapath.h b/datapath/datapath.h index b478eaef..9c6a6a80 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -48,11 +48,7 @@ struct datapath { struct net_device dev; struct net_device_stats stats; - /* Flags from the control hello message */ - uint16_t hello_flags; - - /* Maximum number of bytes that should be sent for flow misses */ - uint16_t miss_send_len; + struct ofp_switch_config config; /* Switch ports. */ struct net_bridge_port *ports[OFPP_MAX]; @@ -63,8 +59,10 @@ int dp_output_port(struct datapath *, struct sk_buff *, int out_port); int dp_output_control(struct datapath *, struct sk_buff *, uint32_t buffer_id, size_t max_len, int reason); int dp_set_origin(struct datapath *, uint16_t, struct sk_buff *); -int dp_send_hello(struct datapath *); +int dp_send_features_reply(struct datapath *, uint32_t xid); +int dp_send_config_reply(struct datapath *, uint32_t xid); int dp_send_flow_expired(struct datapath *, struct sw_flow *); +int dp_send_port_stats(struct datapath *dp, uint32_t xid); int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp); /* Should hold at least RCU read lock when calling */ diff --git a/datapath/forward.c b/datapath/forward.c index a154393c..a2b6691f 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -44,7 +44,8 @@ void fwd_port_input(struct sw_chain *chain, struct sk_buff *skb, int in_port) flow->actions, flow->n_actions); } else { dp_output_control(chain->dp, skb, fwd_save_skb(skb), - chain->dp->miss_send_len, OFPR_NO_MATCH); + chain->dp->config.miss_send_len, + OFPR_NO_MATCH); } } @@ -259,20 +260,24 @@ struct sk_buff *execute_setter(struct sk_buff *skb, uint16_t eth_proto, } static int -recv_control_hello(struct sw_chain *chain, const void *msg) +recv_features_request(struct sw_chain *chain, const void *msg) { - const struct ofp_control_hello *och = msg; - - printk("control_hello(version=%d)\n", ntohl(och->version)); - - if (ntohs(och->miss_send_len) != OFP_MISS_SEND_LEN_UNCHANGED) { - chain->dp->miss_send_len = ntohs(och->miss_send_len); - } - - chain->dp->hello_flags = ntohs(och->flags); + const struct ofp_header *ofr = msg; + return dp_send_features_reply(chain->dp, ofr->xid); +} - dp_send_hello(chain->dp); +static int +recv_get_config_request(struct sw_chain *chain, const void *msg) +{ + const struct ofp_header *ofr = msg; + return dp_send_config_reply(chain->dp, ofr->xid); +} +static int +recv_set_config(struct sw_chain *chain, const void *msg) +{ + const struct ofp_switch_config *osc = msg; + chain->dp->config = *osc; return 0; } @@ -427,9 +432,17 @@ fwd_control_input(struct sw_chain *chain, const void *msg, size_t length) }; static const struct openflow_packet packets[] = { - [OFPT_CONTROL_HELLO] = { - sizeof (struct ofp_control_hello), - recv_control_hello, + [OFPT_FEATURES_REQUEST] = { + sizeof (struct ofp_header), + recv_features_request, + }, + [OFPT_GET_CONFIG_REQUEST] = { + sizeof (struct ofp_header), + recv_get_config_request, + }, + [OFPT_SET_CONFIG] = { + sizeof (struct ofp_switch_config), + recv_set_config, }, [OFPT_PACKET_OUT] = { sizeof (struct ofp_packet_out), diff --git a/datapath/table-hash.c b/datapath/table-hash.c index 6f3e4196..7847ef13 100644 --- a/datapath/table-hash.c +++ b/datapath/table-hash.c @@ -123,7 +123,7 @@ static int table_hash_timeout(struct datapath *dp, struct sw_table *swt) struct sw_flow *flow = *bucket; if (flow && flow_timeout(flow)) { count += do_delete(bucket, flow); - if (dp->hello_flags & OFP_CHELLO_SEND_FLOW_EXP) + if (dp->config.flags & OFPC_SEND_FLOW_EXP) dp_send_flow_expired(dp, flow); } } diff --git a/datapath/table-linear.c b/datapath/table-linear.c index fa4f03e5..0b60838a 100644 --- a/datapath/table-linear.c +++ b/datapath/table-linear.c @@ -102,7 +102,7 @@ static int table_linear_timeout(struct datapath *dp, struct sw_table *swt) struct sw_flow *flow = list_entry(pos, struct sw_flow, u.node); if (flow_timeout(flow)) { count += do_delete(swt, flow); - if (dp->hello_flags & OFP_CHELLO_SEND_FLOW_EXP) + if (dp->config.flags & OFPC_SEND_FLOW_EXP) dp_send_flow_expired(dp, flow); } } diff --git a/datapath/table-mac.c b/datapath/table-mac.c index 4e081ad2..3d7e4733 100644 --- a/datapath/table-mac.c +++ b/datapath/table-mac.c @@ -132,7 +132,7 @@ static int table_mac_timeout(struct datapath *dp, struct sw_table *swt) hlist_for_each_entry_rcu (flow, pos, bucket, u.hnode) { if (flow_timeout(flow)) { count += do_delete(swt, flow); - if (dp->hello_flags & OFP_CHELLO_SEND_FLOW_EXP) + if (dp->config.flags & OFPC_SEND_FLOW_EXP) dp_send_flow_expired(dp, flow); } } diff --git a/include/buffer.h b/include/buffer.h index 26608347..0d024ec9 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -68,7 +68,7 @@ void *buffer_tail(const struct buffer *); void *buffer_end(const struct buffer *); void *buffer_put_uninit(struct buffer *, size_t); -void buffer_put(struct buffer *, const void *, size_t); +void *buffer_put(struct buffer *, const void *, size_t); void *buffer_push_uninit(struct buffer *b, size_t); size_t buffer_headroom(struct buffer *); diff --git a/include/openflow.h b/include/openflow.h index 96a60c25..8b8513ed 100644 --- a/include/openflow.h +++ b/include/openflow.h @@ -43,7 +43,7 @@ #endif /* Maximum length of a OpenFlow packet. */ -#define OFP_MAXLEN (sizeof(struct ofp_data_hello) \ +#define OFP_MAXLEN (sizeof(struct ofp_switch_features) \ + (sizeof(struct ofp_phy_port) * OFPP_MAX) + 200) #define OFP_VERSION 1 @@ -71,21 +71,24 @@ enum ofp_port { }; enum ofp_type { - OFPT_CONTROL_HELLO, /* 0 Controller/switch message */ - OFPT_DATA_HELLO, /* 1 Controller/switch message */ - OFPT_PACKET_IN, /* 2 Async message */ - OFPT_PACKET_OUT, /* 3 Controller/switch message */ - OFPT_FLOW_MOD, /* 4 Controller/switch message */ - OFPT_FLOW_EXPIRED, /* 5 Async message */ - OFPT_TABLE, /* 6 Controller/switch message */ - OFPT_PORT_MOD, /* 7 Controller/switch message */ - OFPT_PORT_STATUS, /* 8 Async message */ - OFPT_FLOW_STAT_REQUEST, /* 9 Controller/switch message */ - OFPT_FLOW_STAT_REPLY, /* 10 Controller/switch message */ - OFPT_TABLE_STAT_REQUEST, /* 11 Controller/switch message */ - OFPT_TABLE_STAT_REPLY, /* 12 Controller/switch message */ - OFPT_PORT_STAT_REQUEST, /* 13 Controller/switch message */ - OFPT_PORT_STAT_REPLY /* 14 Controller/switch message */ + OFPT_FEATURES_REQUEST, /* 0 Controller/switch message */ + OFPT_FEATURES_REPLY, /* 1 Controller/switch message */ + OFPT_GET_CONFIG_REQUEST, /* 2 Controller/switch message */ + OFPT_GET_CONFIG_REPLY, /* 3 Controller/switch message */ + OFPT_SET_CONFIG, /* 4 Controller/switch message */ + OFPT_PACKET_IN, /* 5 Async message */ + OFPT_PACKET_OUT, /* 6 Controller/switch message */ + OFPT_FLOW_MOD, /* 7 Controller/switch message */ + OFPT_FLOW_EXPIRED, /* 8 Async message */ + OFPT_TABLE, /* 9 Controller/switch message */ + OFPT_PORT_MOD, /* 10 Controller/switch message */ + OFPT_PORT_STATUS, /* 11 Async message */ + OFPT_FLOW_STAT_REQUEST, /* 12 Controller/switch message */ + OFPT_FLOW_STAT_REPLY, /* 13 Controller/switch message */ + OFPT_TABLE_STAT_REQUEST, /* 14 Controller/switch message */ + OFPT_TABLE_STAT_REPLY, /* 15 Controller/switch message */ + OFPT_PORT_STAT_REQUEST, /* 16 Controller/switch message */ + OFPT_PORT_STAT_REPLY /* 17 Controller/switch message */ }; /* Header on all OpenFlow packets. */ @@ -99,22 +102,18 @@ struct ofp_header { }; #define OFP_DEFAULT_MISS_SEND_LEN 128 -#define OFP_MISS_SEND_LEN_UNCHANGED 0xffff -/* Flag to indicate that datapath should notify the controller of - * expired flow entries. - */ -#define OFP_CHELLO_SEND_FLOW_EXP 0x0001 +enum ofp_config_flags { + /* Tells datapath to notify the controller of expired flow entries. */ + OFPC_SEND_FLOW_EXP = 1 << 0 +}; -/* Controller hello (controller -> datapath). */ -struct ofp_control_hello { +/* Switch configuration. */ +struct ofp_switch_config { struct ofp_header header; - uint32_t version; /* Max supported protocol version (?) */ - uint16_t flags; - uint16_t miss_send_len; /* Max bytes of new flow that datapath should - send to the controller. A value of - OFP_MISS_SEND_LEN_UNCHANGED leaves the - currently configured value unchanged. */ + uint16_t flags; /* OFPC_* flags. */ + uint16_t miss_send_len; /* Max bytes of new flow that datapath should + send to the controller. */ }; /* Capabilities supported by the datapath. */ @@ -154,8 +153,8 @@ struct ofp_phy_port { uint32_t features; /* Bitmap of supported "ofp_port_features"s. */ }; -/* Datapath hello (datapath -> controller). */ -struct ofp_data_hello { +/* Switch features. */ +struct ofp_switch_features { struct ofp_header header; uint64_t datapath_id; /* Datapath unique ID */ @@ -173,15 +172,9 @@ struct ofp_data_hello { uint32_t capabilities; /* Bitmap of support "ofp_capabilities". */ uint32_t actions; /* Bitmap of supported "ofp_action_type"s. */ - /* Miscellany */ - uint16_t miss_send_len; /* Currently configured value for max bytes - of new flow that datapath will send to the - controller. */ - uint8_t pad[2]; /* Align to 32-bits */ - /* Port info.*/ struct ofp_phy_port ports[0]; /* Port definitions. The number of ports - is inferred from the length field in + is inferred from the length field in the header. */ }; diff --git a/lib/buffer.c b/lib/buffer.c index 9f0dbf4e..2b6e3b78 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -171,11 +171,14 @@ buffer_put_uninit(struct buffer *b, size_t size) } /* Appends the 'size' bytes of data in 'p' to the tail end of 'b'. Data in 'b' - * is reallocated and copied if necessary. */ -void + * is reallocated and copied if necessary. Returns a pointer to the first + * byte of the data's location in the buffer. */ +void * buffer_put(struct buffer *b, const void *p, size_t size) { - memcpy(buffer_put_uninit(b, size), p, size); + void *dst = buffer_put_uninit(b, size); + memcpy(dst, p, size); + return dst; } void * diff --git a/lib/dpif.c b/lib/dpif.c index 791c4bec..f9c7a0af 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -258,53 +258,6 @@ dpif_del_port(struct dpif *dp, const char *netdev) return send_mgmt_command(dp, DP_GENL_C_DEL_PORT, netdev); } -/* Prints a description of 'dp' to stdout. Returns 0 if successful, otherwise - * a positive errno value. */ -int -dpif_show(struct dpif *dp) -{ - static const struct nl_policy show_policy[] = { - [DP_GENL_A_DP_INFO] = { .type = NL_A_UNSPEC, - .min_len = sizeof(struct ofp_data_hello), - .max_len = SIZE_MAX }, - }; - - struct buffer request, *reply; - struct nlattr *attrs[ARRAY_SIZE(show_policy)]; - struct ofp_data_hello *odh; - int retval; - size_t len; - - buffer_init(&request, 0); - nl_msg_put_genlmsghdr(&request, dp->sock, 0, openflow_family, - NLM_F_REQUEST, DP_GENL_C_SHOW_DP, 1); - nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, dp->dp_idx); - retval = nl_sock_transact(dp->sock, &request, &reply); - buffer_uninit(&request); - if (retval) { - return retval; - } - if (!nl_policy_parse(reply, show_policy, attrs, - ARRAY_SIZE(show_policy))) { - buffer_delete(reply); - return EPROTO; - } - - odh = (void *) nl_attr_get(attrs[DP_GENL_A_DP_INFO]); - if (odh->header.version != OFP_VERSION - || odh->header.type != OFPT_DATA_HELLO) { - VLOG_ERR("bad show query response (%"PRIu8",%"PRIu8")", - odh->header.version, odh->header.type); - buffer_delete(reply); - return EPROTO; - } - - len = nl_attr_get_size(attrs[DP_GENL_A_DP_INFO]); - ofp_print(stdout, odh, len, 1); - - return retval; -} - static const struct nl_policy table_policy[] = { [DP_GENL_A_NUMTABLES] = { .type = NL_A_U32 }, [DP_GENL_A_TABLE] = { .type = NL_A_UNSPEC }, @@ -364,12 +317,6 @@ static const struct nl_policy flow_policy[] = { [DP_GENL_A_FLOW] = { .type = NL_A_UNSPEC }, }; -struct _dump_ofp_flow_mod -{ - struct ofp_flow_mod ofm; - struct ofp_action oa; -}; - /* Writes a description of flows in the given 'table' in 'dp' to stdout. If * 'match' is null, all flows in the table are written; otherwise, only * matching flows are written. Returns 0 if successful, otherwise a positive @@ -404,7 +351,7 @@ dpif_dump_flows(struct dpif *dp, int table, struct ofp_match *match) for (;;) { struct nlattr *attrs[ARRAY_SIZE(flow_policy)]; - const struct _dump_ofp_flow_mod *flows, *ofm; + const struct ofp_flow_mod *flows, *ofm; int n_flows; if (!nl_policy_parse(reply, flow_policy, attrs, @@ -421,17 +368,17 @@ dpif_dump_flows(struct dpif *dp, int table, struct ofp_match *match) flows = nl_attr_get(attrs[DP_GENL_A_FLOW]); for (ofm = flows; ofm < &flows[n_flows]; ofm++) { - if (ofm->ofm.header.version != 1){ + if (ofm->header.version != 1){ VLOG_DBG("recv_dp_flow incorrect version"); buffer_delete(reply); return EPROTO; - } else if (ofm->ofm.header.type != OFPT_FLOW_MOD) { + } else if (ofm->header.type != OFPT_FLOW_MOD) { VLOG_DBG("recv_fp_flow bad return message type"); buffer_delete(reply); return EPROTO; } - ofp_print(stdout, &ofm->ofm, sizeof(struct ofp_flow_mod), 1); + ofp_print(stdout, ofm, sizeof *ofm, 1); putc('\n', stdout); } diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 98190a4f..bfd90bfb 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -290,39 +290,59 @@ ofp_print_phy_port(struct ds *string, const struct ofp_phy_port *port) ntohl(port->flags), ntohl(port->features)); } -/* Pretty-print the OFPT_DATA_HELLO packet of 'len' bytes at 'oh' to 'stream' - * at the given 'verbosity' level. */ -void ofp_print_data_hello(struct ds *string, const void *oh, size_t len, - int verbosity) +/* Pretty-print the struct ofp_switch_features of 'len' bytes at 'oh' to + * 'string' at the given 'verbosity' level. */ +static void +ofp_print_switch_features(struct ds *string, const void *oh, size_t len, + int verbosity) { - const struct ofp_data_hello *odh = oh; + const struct ofp_switch_features *osf = oh; struct ofp_phy_port port_list[OFPP_MAX]; int n_ports; int i; - - ds_put_format(string, "dp id:%"PRIx64"\n", ntohll(odh->datapath_id)); + ds_put_format(string, "dp id:%"PRIx64"\n", ntohll(osf->datapath_id)); ds_put_format(string, "tables: exact:%d, mac:%d, compressed:%d, general:%d\n", - ntohl(odh->n_exact), ntohl(odh->n_mac_only), - ntohl(odh->n_compression), ntohl(odh->n_general)); - ds_put_format(string, "buffers: size:%d, number:%d, miss_len:%d\n", - ntohl(odh->buffer_mb), ntohl(odh->n_buffers), - ntohs(odh->miss_send_len)); + ntohl(osf->n_exact), ntohl(osf->n_mac_only), + ntohl(osf->n_compression), ntohl(osf->n_general)); + ds_put_format(string, "buffers: size:%d, number:%d\n", + ntohl(osf->buffer_mb), ntohl(osf->n_buffers)); ds_put_format(string, "features: capabilities:%#x, actions:%#x\n", - ntohl(odh->capabilities), ntohl(odh->actions)); + ntohl(osf->capabilities), ntohl(osf->actions)); - if (ntohs(odh->header.length) >= sizeof *odh) { - len = MIN(len, ntohs(odh->header.length)); + if (ntohs(osf->header.length) >= sizeof *osf) { + len = MIN(len, ntohs(osf->header.length)); } - n_ports = (len - sizeof *odh) / sizeof *odh->ports; + n_ports = (len - sizeof *osf) / sizeof *osf->ports; - memcpy(port_list, odh->ports, (len - sizeof *odh)); + memcpy(port_list, osf->ports, (len - sizeof *osf)); qsort(port_list, n_ports, sizeof port_list[0], compare_ports); for (i = 0; i < n_ports; i++) { ofp_print_phy_port(string, &port_list[i]); } } +/* Pretty-print the struct ofp_switch_config of 'len' bytes at 'oh' to 'string' + * at the given 'verbosity' level. */ +static void +ofp_print_switch_config(struct ds *string, const void *oh, size_t len, + int verbosity) +{ + const struct ofp_switch_config *osc = oh; + uint16_t flags; + + flags = ntohs(osc->flags); + if (flags & OFPC_SEND_FLOW_EXP) { + flags &= ~OFPC_SEND_FLOW_EXP; + ds_put_format(string, " (sending flow expirations)"); + } + if (flags) { + ds_put_format(string, " ***unknown flags %04"PRIx16"***", flags); + } + + ds_put_format(string, " miss_send_len=%"PRIu16"\n", ntohs(osc->miss_send_len)); +} + static void print_wild(struct ds *string, const char *leader, int is_wild, const char *format, ...) __attribute__((format(printf, 4, 5))); @@ -414,43 +434,58 @@ struct openflow_packet { }; static const struct openflow_packet packets[] = { - [OFPT_CONTROL_HELLO] = { - "ofp_control_hello", - sizeof (struct ofp_control_hello), + [OFPT_FEATURES_REQUEST] = { + "features_request", + sizeof (struct ofp_header), NULL, }, - [OFPT_DATA_HELLO] = { - "ofp_data_hello", - sizeof (struct ofp_data_hello), - ofp_print_data_hello, + [OFPT_FEATURES_REPLY] = { + "features_reply", + sizeof (struct ofp_switch_features), + ofp_print_switch_features, + }, + [OFPT_GET_CONFIG_REQUEST] = { + "get_config_request", + sizeof (struct ofp_header), + NULL, + }, + [OFPT_GET_CONFIG_REPLY] = { + "get_config_reply", + sizeof (struct ofp_switch_config), + ofp_print_switch_config, + }, + [OFPT_SET_CONFIG] = { + "set_config", + sizeof (struct ofp_switch_config), + ofp_print_switch_config, }, [OFPT_PACKET_IN] = { - "ofp_packet_in", + "packet_in", offsetof(struct ofp_packet_in, data), ofp_packet_in, }, [OFPT_PACKET_OUT] = { - "ofp_packet_out", + "packet_out", sizeof (struct ofp_packet_out), ofp_packet_out, }, [OFPT_FLOW_MOD] = { - "ofp_flow_mod", + "flow_mod", sizeof (struct ofp_flow_mod), ofp_print_flow_mod, }, [OFPT_FLOW_EXPIRED] = { - "ofp_flow_expired", + "flow_expired", sizeof (struct ofp_flow_expired), ofp_print_flow_expired, }, [OFPT_PORT_MOD] = { - "ofp_port_mod", + "port_mod", sizeof (struct ofp_port_mod), NULL, }, [OFPT_PORT_STATUS] = { - "ofp_port_status", + "port_status", sizeof (struct ofp_port_status), ofp_print_port_status }, diff --git a/switch/datapath.c b/switch/datapath.c index b59e789b..3527492e 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -82,11 +82,7 @@ struct datapath { struct sw_chain *chain; /* Forwarding rules. */ - /* Flags from the control hello message */ - uint16_t hello_flags; - - /* Maximum number of bytes that should be sent for flow misses */ - uint16_t miss_send_len; + struct ofp_switch_config config; /* Switch ports. */ struct sw_port ports[OFPP_MAX]; @@ -95,7 +91,6 @@ struct datapath { void dp_output_port(struct datapath *, struct buffer *, int in_port, int out_port); -void dp_send_hello(struct datapath *); void dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp); void dp_output_control(struct datapath *, struct buffer *, int in_port, size_t max_len, int reason); @@ -177,7 +172,8 @@ dp_new(struct datapath **dp_, uint64_t dpid, struct rconn *rconn) } list_init(&dp->port_list); - dp->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; + dp->config.flags = 0; + dp->config.miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; *dp_ = dp; return 0; } @@ -413,36 +409,35 @@ static void fill_port_desc(struct datapath *dp, struct sw_port *p, desc->speed = htonl(netdev_get_speed(p->netdev)); } -void -dp_send_hello(struct datapath *dp) +static void +dp_send_features_reply(struct datapath *dp, uint32_t xid) { struct buffer *buffer; - struct ofp_data_hello *odh; + struct ofp_switch_features *ofr; struct sw_port *p; - buffer = buffer_new(sizeof *odh); - odh = buffer_put_uninit(buffer, sizeof *odh); - memset(odh, 0, sizeof *odh); - odh->header.version = OFP_VERSION; - odh->header.type = OFPT_DATA_HELLO; - odh->header.xid = htonl(0); - odh->datapath_id = htonll(dp->id); - odh->n_exact = htonl(2 * TABLE_HASH_MAX_FLOWS); - odh->n_mac_only = htonl(TABLE_MAC_MAX_FLOWS); - odh->n_compression = 0; /* Not supported */ - odh->n_general = htonl(TABLE_LINEAR_MAX_FLOWS); - odh->buffer_mb = htonl(UINT32_MAX); - odh->n_buffers = htonl(N_PKT_BUFFERS); - odh->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES); - odh->actions = htonl(OFP_SUPPORTED_ACTIONS); - odh->miss_send_len = htons(dp->miss_send_len); + buffer = buffer_new(sizeof *ofr); + ofr = buffer_put_uninit(buffer, sizeof *ofr); + memset(ofr, 0, sizeof *ofr); + ofr->header.version = OFP_VERSION; + ofr->header.type = OFPT_FEATURES_REPLY; + ofr->header.xid = xid; + ofr->datapath_id = htonll(dp->id); + ofr->n_exact = htonl(2 * TABLE_HASH_MAX_FLOWS); + ofr->n_mac_only = htonl(TABLE_MAC_MAX_FLOWS); + ofr->n_compression = 0; /* Not supported */ + ofr->n_general = htonl(TABLE_LINEAR_MAX_FLOWS); + ofr->buffer_mb = htonl(UINT32_MAX); + ofr->n_buffers = htonl(N_PKT_BUFFERS); + ofr->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES); + ofr->actions = htonl(OFP_SUPPORTED_ACTIONS); LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) { struct ofp_phy_port *opp = buffer_put_uninit(buffer, sizeof *opp); memset(opp, 0, sizeof *opp); fill_port_desc(dp, p, opp); } - odh = buffer_at_assert(buffer, 0, sizeof *odh); - odh->header.length = htons(buffer->size); + ofr = buffer_at_assert(buffer, 0, sizeof *ofr); + ofr->header.length = htons(buffer->size); rconn_send(dp->rconn, buffer); } @@ -510,7 +505,7 @@ void fwd_port_input(struct datapath *dp, struct buffer *buffer, int in_port) execute_actions(dp, buffer, in_port, &key, flow->actions, flow->n_actions); } else { - dp_output_control(dp, buffer, in_port, dp->miss_send_len, + dp_output_control(dp, buffer, in_port, dp->config.miss_send_len, OFPR_NO_MATCH); } } @@ -712,20 +707,35 @@ modify_vlan(struct buffer *buffer, } static int -recv_control_hello(struct datapath *dp, const void *msg) +recv_features_request(struct datapath *dp, const void *msg) { - const struct ofp_control_hello *och = msg; - - printf("control_hello(version=%d)\n", ntohl(och->version)); - - if (ntohs(och->miss_send_len) != OFP_MISS_SEND_LEN_UNCHANGED) { - dp->miss_send_len = ntohs(och->miss_send_len); - } - - dp->hello_flags = ntohs(och->flags); + struct ofp_header *ofr = msg; + dp_send_features_reply(dp, ofr->xid); + return 0; +} - dp_send_hello(dp); +static int +recv_get_config_request(struct datapath *dp, const void *msg) +{ + struct ofp_header *gcr = msg; + struct buffer *buffer; + struct ofp_switch_config *osc; + + buffer = buffer_new(sizeof dp->config); + osc = buffer_put(buffer, &dp->config, sizeof dp->config); + osc->header.version = OFP_VERSION; + osc->header.type = OFPT_GET_CONFIG_REPLY; + osc->header.length = htons(sizeof *osc); + osc->header.xid = gcr->xid; + rconn_send(dp->rconn, buffer); + return 0; +} +static int +recv_set_config(struct datapath *dp, const void *msg) +{ + const struct ofp_switch_config *osc = msg; + dp->config = *osc; return 0; } @@ -862,9 +872,17 @@ fwd_control_input(struct datapath *dp, const void *msg, size_t length) }; static const struct openflow_packet packets[] = { - [OFPT_CONTROL_HELLO] = { - sizeof (struct ofp_control_hello), - recv_control_hello, + [OFPT_FEATURES_REQUEST] = { + sizeof (struct ofp_header), + recv_features_request, + }, + [OFPT_GET_CONFIG_REQUEST] = { + sizeof (struct ofp_header), + recv_get_config_request, + }, + [OFPT_SET_CONFIG] = { + sizeof (struct ofp_switch_config), + recv_set_config, }, [OFPT_PACKET_OUT] = { sizeof (struct ofp_packet_out), diff --git a/utilities/Makefile.am b/utilities/Makefile.am index 8367bac9..b07dd49f 100644 --- a/utilities/Makefile.am +++ b/utilities/Makefile.am @@ -1,9 +1,6 @@ include ../Make.vars -bin_PROGRAMS = vlogconf -if HAVE_NETLINK -bin_PROGRAMS += dpctl -endif +bin_PROGRAMS = vlogconf dpctl dpctl_SOURCES = dpctl.c dpctl_LDADD = ../lib/libopenflow.la diff --git a/utilities/dpctl.c b/utilities/dpctl.c index 41dde835..a9c0d8b6 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -53,6 +53,7 @@ #include "openflow.h" #include "ofp-print.h" #include "vconn.h" +#include "vconn-ssl.h" #include "vlog.h" #define THIS_MODULE VLM_DPCTL @@ -111,6 +112,11 @@ parse_options(int argc, char *argv[]) {"verbose", optional_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, +#ifdef HAVE_OPENSSL + {"private-key", required_argument, 0, 'p'}, + {"certificate", required_argument, 0, 'c'}, + {"ca-cert", required_argument, 0, 'C'}, +#endif {0, 0, 0, 0}, }; char *short_options = long_options_to_short_options(long_options); @@ -136,6 +142,20 @@ parse_options(int argc, char *argv[]) vlog_set_verbosity(optarg); break; +#ifdef HAVE_OPENSSL + case 'p': + vconn_ssl_set_private_key_file(optarg); + break; + + case 'c': + vconn_ssl_set_certificate_file(optarg); + break; + + case 'C': + vconn_ssl_set_ca_cert_file(optarg); + break; +#endif + case '?': exit(EXIT_FAILURE); @@ -149,25 +169,41 @@ parse_options(int argc, char *argv[]) static void usage(void) { - printf("%s: Datapath Utility\n" + printf("%s: OpenFlow switch management utility\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n" - "\nAvailable commands:\n" - " adddp DP_ID add a new datapath with ID DP_ID\n" - " deldp DP_ID delete datapath DP_ID\n" - " show DP show information about DP\n" - " addif DP_ID IFACE add IFACE as a port on DP_ID\n" - " delif DP_ID IFACE delete IFACE as a port on DP_ID\n" - " monitor DP_ID print packets received on DP_ID\n" - " dump-tables DP_ID print stats for all tables in DP_ID\n" - " dump-flows DP_ID T_ID print all flow entries in table T_ID of DP_ID\n" - " dump-flows DP_ID T_ID FLOW print matching FLOWs in table T_ID of DP_ID\n" - " add-flows DP FILE add flows from FILE to DP\n" - " benchmark-nl DP_ID N SIZE send N packets of SIZE bytes up netlink\n" - "\nOptions:\n" +#ifdef HAVE_NETLINK + "\nCommands that apply to local datapaths only:\n" + " adddp nl:DP_ID add a new local datapath DP_ID\n" + " deldp nl:DP_ID delete local datapath DP_ID\n" + " addif nl:DP_ID IFACE add IFACE as a port on DP_ID\n" + " delif nl:DP_ID IFACE delete IFACE as a port on DP_ID\n" + " benchmark-nl nl:DP_ID N SIZE send N packets of SIZE bytes\n" +#endif + "\nCommands that also apply to remote switches:\n" + " show VCONN show information about VCONN\n" + " monitor VCONN print packets received on VCONN\n" + " dump-tables VCONN print table stats for VCONN\n" + " dump-flows VCONN T_ID print all flow entries in table T_ID of VCONN\n" + " dump-flows VCONN T_ID FLOW print matching FLOWs in table T_ID of VCONN\n" + " add-flows VCONN FILE add flows from FILE to VCONN\n" + "where each VCONN is one of the following:\n" + " tcp:HOST[:PORT] PORT (default: %d) on remote TCP HOST\n", + program_name, program_name, OFP_TCP_PORT); +#ifdef HAVE_NETLINK + printf(" nl:DP_IDX via netlink to local datapath DP_IDX\n"); +#endif +#ifdef HAVE_OPENSSL + printf(" ssl:HOST[:PORT] SSL PORT (default: %d) on remote HOST\n" + "\nPKI configuration (required to use SSL):\n" + " -p, --private-key=FILE file with private key\n" + " -c, --certificate=FILE file with certificate for private key\n" + " -C, --ca-cert=FILE file with peer CA certificate\n", + OFP_SSL_PORT); +#endif + printf("\nOptions:\n" " -v, --verbose set maximum verbosity level\n" " -h, --help display this help message\n" - " -V, --version display version information\n", - program_name, program_name); + " -V, --version display version information\n"); exit(EXIT_SUCCESS); } @@ -177,6 +213,9 @@ static void run(int retval, const char *name) fatal(retval, "%s", name); } } + +#ifdef HAVE_NETLINK +/* Netlink-only commands. */ static int if_up(const char* intf) { @@ -186,10 +225,20 @@ static int if_up(const char* intf) return system(command); } +static void open_nl_vconn(const char *name, bool subscribe, struct dpif *dpif) +{ + if (strncmp(name, "nl:", 3) + || strlen(name) < 4 + || name[strspn(name + 3, "0123456789") + 3]) { + fatal(0, "%s: argument is not of the form \"nl:DP_ID\"", name); + } + run(dpif_open(atoi(name + 3), subscribe, dpif), "opening datapath"); +} + static void do_add_dp(int argc UNUSED, char *argv[]) { struct dpif dp; - run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open"); + open_nl_vconn(argv[1], false, &dp); run(dpif_add_dp(&dp), "add_dp"); dpif_close(&dp); } @@ -197,24 +246,16 @@ static void do_add_dp(int argc UNUSED, char *argv[]) static void do_del_dp(int argc UNUSED, char *argv[]) { struct dpif dp; - run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open"); + open_nl_vconn(argv[1], false, &dp); run(dpif_del_dp(&dp), "del_dp"); dpif_close(&dp); } -static void do_show(int argc UNUSED, char *argv[]) -{ - struct dpif dp; - run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open"); - run(dpif_show(&dp), "show"); - dpif_close(&dp); -} - static void do_add_port(int argc UNUSED, char *argv[]) { struct dpif dp; if_up(argv[2]); - run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open"); + open_nl_vconn(argv[1], false, &dp); run(dpif_add_port(&dp, argv[2]), "add_port"); dpif_close(&dp); } @@ -222,7 +263,7 @@ static void do_add_port(int argc UNUSED, char *argv[]) static void do_del_port(int argc UNUSED, char *argv[]) { struct dpif dp; - run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open"); + open_nl_vconn(argv[1], false, &dp); run(dpif_del_port(&dp, argv[2]), "del_port"); dpif_close(&dp); } @@ -235,7 +276,7 @@ static void do_benchmark_nl(int argc UNUSED, char *argv[]) uint32_t num_packets, i, milestone; struct timeval start, end; - run(dpif_open(atoi(argv[1]), true, &dp), "dpif_open"); + open_nl_vconn(argv[1], false, &dp); num_packets = atoi(argv[2]); milestone = BENCHMARK_INCR; run(dpif_benchmark_nl(&dp, num_packets, atoi(argv[3])), "benchmark_nl"); @@ -264,6 +305,19 @@ static void do_benchmark_nl(int argc UNUSED, char *argv[]) dpif_close(&dp); } +#endif /* HAVE_NETLINK */ + +/* Generic commands. */ + +static void do_show(int argc UNUSED, char *argv[]) +{ +#if 0 + struct dpif dp; + run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open"); + run(dpif_show(&dp), "show"); + dpif_close(&dp); +#endif +} static void do_monitor(int argc UNUSED, char *argv[]) { @@ -507,25 +561,19 @@ static void do_help(int argc UNUSED, char *argv[] UNUSED) } static struct command all_commands[] = { - { "add-dp", 1, 1, do_add_dp }, +#ifdef HAVE_NETLINK { "adddp", 1, 1, do_add_dp }, - - { "del-dp", 1, 1, do_del_dp }, { "deldp", 1, 1, do_del_dp }, - - { "show", 1, 1, do_show }, - - { "add-port", 2, 2, do_add_port }, { "addif", 2, 2, do_add_port }, - - { "del-port", 2, 2, do_del_port }, { "delif", 2, 2, do_del_port }, + { "benchmark-nl", 3, 3, do_benchmark_nl }, +#endif + + { "show", 1, 1, do_show }, { "help", 0, INT_MAX, do_help }, { "monitor", 1, 1, do_monitor }, { "dump-tables", 1, 1, do_dump_tables }, { "dump-flows", 2, 3, do_dump_flows }, { "add-flows", 2, 2, do_add_flows }, - - { "benchmark-nl", 3, 3, do_benchmark_nl }, };