From: Ben Pfaff Date: Tue, 15 Apr 2008 16:57:38 +0000 (-0700) Subject: Merge remote branch 'repo/master' into stats X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6f8d45bf7dd4007410f42f7233243cff409d06c6;hp=bbbc6bc992fbc018e2d1960a65088ffe70312256;p=openvswitch Merge remote branch 'repo/master' into stats Conflicts: datapath/table-mac.c --- diff --git a/controller/controller.c b/controller/controller.c index 7d2b77aa..9ef72d0f 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 @@ -564,11 +564,7 @@ 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 + VCONN_SSL_LONG_OPTIONS {0, 0, 0, 0}, }; char *short_options = long_options_to_short_options(long_options); @@ -602,19 +598,7 @@ 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 + VCONN_SSL_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); @@ -630,21 +614,10 @@ static void usage(void) { printf("%s: OpenFlow controller\n" - "usage: %s [OPTIONS] VCONN\n" - "where VCONN is one of the following:\n" - " ptcp:[PORT] listen to TCP PORT (default: %d)\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(" pssl:[PORT] listen for SSL on PORT (default: %d)\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 + "usage: %s [OPTIONS] METHOD\n" + "where METHOD is any OpenFlow connection method.\n", + program_name, program_name); + vconn_usage(true, true); printf("\nOther options:\n" " -H, --hub act as hub instead of learning switch\n" " -n, --noflow pass traffic, but don't add flows\n" diff --git a/datapath/datapath.c b/datapath/datapath.c index 0929b704..639a4836 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -96,6 +96,71 @@ 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, + const struct sender *sender, 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, + sender ? sender->pid : 0, + sender ? sender->seq : 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 = sender ? sender->xid : 0; + + 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); +} + +static int +send_openflow_skb(struct sk_buff *skb, const struct sender *sender) +{ + int err = (sender + ? genlmsg_unicast(skb, sender->pid) + : genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC)); + if (err && net_ratelimit()) + printk(KERN_WARNING "send_openflow_skb: send failed: %d\n", + err); + return err; +} + /* Generates a unique datapath id. It incorporates the datapath index * and a hardware address, if available. If not, it generates a random * one. @@ -175,7 +240,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 = htons(OFP_DEFAULT_MISS_SEND_LEN); dp->dp_task = kthread_run(dp_maint_func, dp, "dp%d", dp_idx); if (IS_ERR(dp->dp_task)) @@ -514,73 +580,29 @@ 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, NULL, &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 = send_openflow_skb(f_skb, NULL); - 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); - - 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; } @@ -623,28 +645,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++; } @@ -652,78 +670,43 @@ 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, const struct sender *sender) { 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 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, sender, &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; - } - - NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, dp->dp_idx); - - 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); - - /* 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(skb, attr, (odh_max_len - odh_len)); + /* Fill. */ + port_count = fill_features_reply(dp, ofr); - err = genlmsg_end(skb, data); - if (err < 0) { - if (net_ratelimit()) - printk("dp_send_hello: genlmsg_end failed\n"); - goto error; - } - - 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); + /* Shrink to fit. */ + ofr_len = sizeof(*ofr) + (sizeof(struct ofp_phy_port) * port_count); + resize_openflow_skb(skb, &ofr->header, ofr_len); + return send_openflow_skb(skb, sender); +} - return err; +int +dp_send_config_reply(struct datapath *dp, const struct sender *sender) +{ + struct sk_buff *skb; + struct ofp_switch_config *osc; -nla_put_failure: -error: - kfree_skb(skb); - if (net_ratelimit()) - printk(KERN_ERR "dp_send_hello: failed to send: %d\n", err); - return err; + osc = alloc_openflow_skb(dp, sizeof *osc, OFPT_PORT_STATUS, sender, + &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); + return send_openflow_skb(skb, sender); } int @@ -747,132 +730,181 @@ 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); - - ops->reason = status; + ops = alloc_openflow_skb(p->dp, sizeof *ops, OFPT_PORT_STATUS, NULL, + &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; + return send_openflow_skb(skb, NULL); } 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; + ofe = alloc_openflow_skb(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &skb); + if (!ofe) + return -ENOMEM; - skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); - if (!skb) { - if (net_ratelimit()) - printk("dp_send_flow_expired: genlmsg_new failed\n"); - goto error; - } + flow_fill_match(&ofe->match, &flow->key); + duration_j = (flow->timeout - HZ * flow->max_idle) - flow->init_time; + ofe->duration = htonl(duration_j / HZ); + ofe->packet_count = cpu_to_be64(flow->packet_count); + ofe->byte_count = cpu_to_be64(flow->byte_count); + return send_openflow_skb(skb, NULL); +} - 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; +static void +fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow, + int table_idx) +{ + ofs->match.wildcards = htons(flow->key.wildcards); + ofs->match.in_port = flow->key.in_port; + memcpy(ofs->match.dl_src, flow->key.dl_src, ETH_ALEN); + memcpy(ofs->match.dl_dst, flow->key.dl_dst, ETH_ALEN); + ofs->match.dl_vlan = flow->key.dl_vlan; + ofs->match.dl_type = flow->key.dl_type; + ofs->match.nw_src = flow->key.nw_src; + ofs->match.nw_dst = flow->key.nw_dst; + ofs->match.nw_proto = flow->key.nw_proto; + memset(ofs->match.pad, 0, sizeof ofs->match.pad); + ofs->match.tp_src = flow->key.tp_src; + ofs->match.tp_dst = flow->key.tp_dst; + ofs->duration = htonl((jiffies - flow->init_time) / HZ); + ofs->table_id = table_idx; + ofs->packet_count = cpu_to_be64(flow->packet_count); + ofs->byte_count = cpu_to_be64(flow->byte_count); +} + +int +dp_send_flow_stats(struct datapath *dp, const struct sender *sender, + const struct ofp_match *match) +{ + struct sk_buff *skb; + struct ofp_flow_stat_reply *fsr; + size_t header_size, fudge, flow_size; + struct sw_flow_key match_key; + int table_idx, n_flows, max_flows; + + header_size = offsetof(struct ofp_flow_stat_reply, flows); + fudge = 128; + flow_size = sizeof fsr->flows[0]; + max_flows = (NLMSG_GOODSIZE - header_size - fudge) / flow_size; + fsr = alloc_openflow_skb(dp, header_size + max_flows * flow_size, + OFPT_FLOW_STAT_REPLY, sender, &skb); + if (!fsr) + return -ENOMEM; + + n_flows = 0; + flow_extract_match(&match_key, match); + for (table_idx = 0; table_idx < dp->chain->n_tables; table_idx++) { + struct sw_table *table = dp->chain->tables[table_idx]; + struct swt_iterator iter; + + if (n_flows >= max_flows) { + break; + } + + if (!table->iterator(table, &iter)) { + if (net_ratelimit()) + printk("iterator failed for table %d\n", + table_idx); + continue; + } + + for (; iter.flow; table->iterator_next(&iter)) { + if (flow_matches(&match_key, &iter.flow->key)) { + fill_flow_stats(&fsr->flows[n_flows], + iter.flow, table_idx); + if (++n_flows >= max_flows) { + break; + } + } + } + table->iterator_destroy(&iter); } + resize_openflow_skb(skb, &fsr->header, + header_size + flow_size * n_flows); + return send_openflow_skb(skb, sender); +} - NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, dp->dp_idx); +static int +fill_port_stat_reply(struct datapath *dp, struct ofp_port_stat_reply *psr) +{ + struct net_bridge_port *p; + int port_count = 0; - 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; + list_for_each_entry_rcu (p, &dp->port_list, node) { + struct ofp_port_stats *ps = &psr->ports[port_count++]; + struct net_device_stats *stats = p->dev->get_stats(p->dev); + ps->port_no = htons(p->port_no); + memset(ps->pad, 0, sizeof ps->pad); + ps->rx_count = cpu_to_be64(stats->rx_packets); + ps->tx_count = cpu_to_be64(stats->tx_packets); + ps->drop_count = cpu_to_be64(stats->rx_dropped + + stats->tx_dropped); } - 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); + return port_count; +} - flow_fill_match(&ofe->match, &flow->key); - duration_j = (flow->timeout - HZ * flow->max_idle) - flow->init_time; - ofe->duration = htonl(duration_j / HZ); - ofe->packet_count = cpu_to_be64(flow->packet_count); - ofe->byte_count = cpu_to_be64(flow->byte_count); +int +dp_send_port_stats(struct datapath *dp, const struct sender *sender) +{ + struct sk_buff *skb; + struct ofp_port_stat_reply *psr; + size_t psr_len, port_max_len; + int port_count; - err = genlmsg_end(skb, data); - if (err < 0) { - if (net_ratelimit()) - printk("dp_send_flow_expired: genlmsg_end failed\n"); - goto error; - } + /* Overallocate. */ + port_max_len = sizeof(struct ofp_port_stats) * OFPP_MAX; + psr = alloc_openflow_skb(dp, sizeof *psr + port_max_len, + OFPT_PORT_STAT_REPLY, sender, &skb); + if (!psr) + return -ENOMEM; - 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); + /* Fill. */ + port_count = fill_port_stat_reply(dp, psr); - return err; + /* Shrink to fit. */ + psr_len = sizeof *psr + sizeof(struct ofp_port_stats) * port_count; + resize_openflow_skb(skb, &psr->header, psr_len); + return send_openflow_skb(skb, sender); +} -nla_put_failure: -error: - kfree_skb(skb); - if (net_ratelimit()) - printk(KERN_ERR "send_flow_expired: failed to send: %d\n", err); - return err; +int +dp_send_table_stats(struct datapath *dp, const struct sender *sender) +{ + struct sk_buff *skb; + struct ofp_table_stat_reply *tsr; + int i, n_tables; + + n_tables = dp->chain->n_tables; + tsr = alloc_openflow_skb(dp, (offsetof(struct ofp_table_stat_reply, + tables) + + sizeof tsr->tables[0] * n_tables), + OFPT_TABLE_STAT_REPLY, sender, &skb); + if (!tsr) + return -ENOMEM; + for (i = 0; i < n_tables; i++) { + struct ofp_table_stats *ots = &tsr->tables[i]; + struct sw_table_stats stats; + dp->chain->tables[i]->stats(dp->chain->tables[i], &stats); + strncpy(ots->name, stats.name, sizeof ots->name); + ots->table_id = i; + ots->pad[0] = ots->pad[1] = 0; + ots->max_entries = htonl(stats.max_flows); + ots->active_count = htonl(stats.n_flows); + ots->matched_count = cpu_to_be64(0); /* FIXME */ + } + return send_openflow_skb(skb, sender); } /* Generic Netlink interface. @@ -1002,433 +1034,6 @@ nla_put_failure: return err; } -/* - * Fill flow entry for nl flow query. Called with rcu_lock - * - */ -static -int -dp_fill_flow(struct ofp_flow_mod* ofm, struct swt_iterator* iter) -{ - ofm->header.version = OFP_VERSION; - ofm->header.type = OFPT_FLOW_MOD; - ofm->header.length = htons(sizeof(struct ofp_flow_mod) - + sizeof(ofm->actions[0])); - ofm->header.xid = htonl(0); - - ofm->match.wildcards = htons(iter->flow->key.wildcards); - ofm->match.in_port = iter->flow->key.in_port; - ofm->match.dl_vlan = iter->flow->key.dl_vlan; - memcpy(ofm->match.dl_src, iter->flow->key.dl_src, ETH_ALEN); - memcpy(ofm->match.dl_dst, iter->flow->key.dl_dst, ETH_ALEN); - ofm->match.dl_type = iter->flow->key.dl_type; - ofm->match.nw_src = iter->flow->key.nw_src; - ofm->match.nw_dst = iter->flow->key.nw_dst; - ofm->match.nw_proto = iter->flow->key.nw_proto; - ofm->match.tp_src = iter->flow->key.tp_src; - ofm->match.tp_dst = iter->flow->key.tp_dst; - ofm->group_id = iter->flow->group_id; - ofm->max_idle = iter->flow->max_idle; - /* TODO support multiple actions */ - ofm->actions[0] = iter->flow->actions[0]; - - 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* -dp_init_nl_flow_msg(uint32_t dp_idx, uint16_t table_idx, - struct genl_info *info, struct sk_buff* skb) -{ - void* data; - - data = genlmsg_put_reply(skb, info, &dp_genl_family, 0, - DP_GENL_C_QUERY_FLOW); - if (data == NULL) - return NULL; - NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, dp_idx); - NLA_PUT_U16(skb, DP_GENL_A_TABLEIDX, table_idx); - - return data; - -nla_put_failure: - return NULL; -} - -/* Iterate through the specified table and send all flow entries over - * netlink to userspace. Each flow message has the following format: - * - * 32bit dpix - * 16bit tabletype - * 32bit number of flows - * openflow-flow-entries - * - * The full table may require multiple messages. A message with 0 flows - * signifies end-of message. - */ - -static -int -dp_dump_table(struct datapath *dp, uint16_t table_idx, struct genl_info *info, struct ofp_flow_mod* matchme) -{ - struct sk_buff *skb = 0; - struct sw_table *table = 0; - struct swt_iterator iter; - struct sw_flow_key in_flow; - struct nlattr *attr; - int count = 0, sum_count = 0; - void *data; - uint8_t* ofm_ptr = 0; - struct nlattr *num_attr; - int err = -ENOMEM; - - table = dp->chain->tables[table_idx]; - if ( table == NULL ) { - dprintk("dp::dp_dump_table error, non-existant table at position %d\n", table_idx); - return -EINVAL; - } - - if (!table->iterator(table, &iter)) { - dprintk("dp::dp_dump_table couldn't initialize empty table iterator\n"); - return -ENOMEM; - } - - while (iter.flow) { - - /* verify that we can fit all NL_FLOWS_PER_MESSAGE in a single - * sk_buf */ - if( (sizeof(dp_genl_family) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint32_t) + - (NL_FLOWS_PER_MESSAGE * sizeof(struct ofp_flow_mod))) > (8192 - 64)){ - dprintk("dp::dp_dump_table NL_FLOWS_PER_MESSAGE may cause overrun in skbuf\n"); - return -ENOMEM; - } - - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); - if (skb == NULL) { - return -ENOMEM; - } - - data = dp_init_nl_flow_msg(dp->dp_idx, table_idx, info, skb); - if (data == NULL){ - err= -ENOMEM; - goto error_free_skb; - } - - /* reserve space to put the number of flows for this message, to - * be filled after the loop*/ - num_attr = nla_reserve(skb, DP_GENL_A_NUMFLOWS, sizeof(uint32_t)); - if(!num_attr){ - err = -ENOMEM; - goto error_free_skb; - } - - /* Only load NL_FLOWS_PER_MESSAGE flows at a time */ - attr = nla_reserve(skb, DP_GENL_A_FLOW, - (sizeof(struct ofp_flow_mod) + sizeof(struct ofp_action)) * NL_FLOWS_PER_MESSAGE); - if (!attr){ - err = -ENOMEM; - goto error_free_skb; - } - - /* internal loop to fill NL_FLOWS_PER_MESSAGE flows */ - ofm_ptr = nla_data(attr); - flow_extract_match(&in_flow, &matchme->match); - while (iter.flow && count < NL_FLOWS_PER_MESSAGE) { - if(flow_matches(&in_flow, &iter.flow->key)){ - if((err = dp_fill_flow((struct ofp_flow_mod*)ofm_ptr, &iter))) - goto error_free_skb; - count++; - /* TODO support multiple actions */ - ofm_ptr += sizeof(struct ofp_flow_mod) + sizeof(struct ofp_action); - } - table->iterator_next(&iter); - } - - *((uint32_t*)nla_data(num_attr)) = count; - genlmsg_end(skb, data); - - sum_count += count; - count = 0; - - err = genlmsg_unicast(skb, info->snd_pid); - skb = 0; - } - - /* send a sentinal message saying we're done */ - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); - if (skb == NULL) { - return -ENOMEM; - } - data = dp_init_nl_flow_msg(dp->dp_idx, table_idx, info, skb); - if (data == NULL){ - err= -ENOMEM; - goto error_free_skb; - } - - NLA_PUT_U32(skb, DP_GENL_A_NUMFLOWS, 0); - /* dummy flow so nl doesn't complain */ - attr = nla_reserve(skb, DP_GENL_A_FLOW, sizeof(struct ofp_flow_mod)); - if (!attr){ - err = -ENOMEM; - goto error_free_skb; - } - genlmsg_end(skb, data); - err = genlmsg_reply(skb, info); skb = 0; - -nla_put_failure: -error_free_skb: - if(skb) - kfree_skb(skb); - return err; -} - -/* Helper function to query_table which creates and sends a message packed with - * table stats. Message form is: - * - * u32 DP_IDX - * u32 NUM_TABLES - * OFP_TABLE (list of OFP_TABLES) - * - */ - -static -int -dp_dump_table_stats(struct datapath *dp, int dp_idx, struct genl_info *info) -{ - struct sk_buff *skb = 0; - struct ofp_table *ot = 0; - struct nlattr *attr; - struct sw_table_stats stats; - size_t len; - void *data; - int err = -ENOMEM; - int i = 0; - int nt = dp->chain->n_tables; - - len = 4 + 4 + (sizeof(struct ofp_table) * nt); - - /* u32 IDX, u32 NUMTABLES, list-of-tables */ - skb = nlmsg_new(MAX(len, NLMSG_GOODSIZE), GFP_ATOMIC); - if (skb == NULL) { - return -ENOMEM; - } - - data = genlmsg_put_reply(skb, info, &dp_genl_family, 0, - DP_GENL_C_QUERY_TABLE); - if (data == NULL){ - return -ENOMEM; - } - - NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, dp_idx); - NLA_PUT_U32(skb, DP_GENL_A_NUMTABLES, nt); - - /* ... we assume that all tables can fit in a single message. - * Probably a reasonable assumption seeing that we only have - * 3 atm */ - attr = nla_reserve(skb, DP_GENL_A_TABLE, (sizeof(struct ofp_table) * nt)); - if (!attr){ - err = -ENOMEM; - goto error_free_skb; - } - - ot = nla_data(attr); - - for (i = 0; i < nt; ++i) { - dp->chain->tables[i]->stats(dp->chain->tables[i], &stats); - ot->header.version = OFP_VERSION; - ot->header.type = OFPT_TABLE; - ot->header.length = htons(sizeof(struct ofp_table)); - ot->header.xid = htonl(0); - - strncpy(ot->name, stats.name, OFP_MAX_TABLE_NAME_LEN); - ot->table_id = htons(i); - ot->n_flows = htonl(stats.n_flows); - ot->max_flows = htonl(stats.max_flows); - ot++; - } - - genlmsg_end(skb, data); - err = genlmsg_reply(skb, info); skb = 0; - -nla_put_failure: -error_free_skb: - if(skb) - kfree_skb(skb); - return err; -} - -/* - * Queries a datapath for flow-table statistics - */ - - -static int dp_genl_table_query(struct sk_buff *skb, struct genl_info *info) -{ - struct datapath* dp; - int err = 0; - - if (!info->attrs[DP_GENL_A_DP_IDX]) { - dprintk("dp::dp_genl_table_query received message with missing attributes\n"); - return -EINVAL; - } - - rcu_read_lock(); - dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX])); - if (!dp) { - err = -ENOENT; - goto err_out; - } - - err = dp_dump_table_stats(dp, nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]), info); - -err_out: - rcu_read_unlock(); - return err; -} - -/* - * Queries a datapath for flow-table entries. - */ - -static int dp_genl_flow_query(struct sk_buff *skb, struct genl_info *info) -{ - struct datapath* dp; - struct ofp_flow_mod* ofm; - u16 table_idx; - int err = 0; - - if (!info->attrs[DP_GENL_A_DP_IDX] - || !info->attrs[DP_GENL_A_TABLEIDX] - || !info->attrs[DP_GENL_A_FLOW]) { - dprintk("dp::dp_genl_flow_query received message with missing attributes\n"); - return -EINVAL; - } - - rcu_read_lock(); - dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX])); - if (!dp) { - err = -ENOENT; - goto err_out; - } - - table_idx = nla_get_u16(info->attrs[DP_GENL_A_TABLEIDX]); - - if (dp->chain->n_tables <= table_idx){ - printk("table index %d invalid (dp has %d tables)\n", - table_idx, dp->chain->n_tables); - err = -EINVAL; - goto err_out; - } - - ofm = nla_data(info->attrs[DP_GENL_A_FLOW]); - err = dp_dump_table(dp, table_idx, info, ofm); - -err_out: - rcu_read_unlock(); - return err; -} - -static struct nla_policy dp_genl_flow_policy[DP_GENL_A_MAX + 1] = { - [DP_GENL_A_DP_IDX] = { .type = NLA_U32 }, - [DP_GENL_A_TABLEIDX] = { .type = NLA_U16 }, - [DP_GENL_A_NUMFLOWS] = { .type = NLA_U32 }, -}; - -static struct genl_ops dp_genl_ops_query_flow = { - .cmd = DP_GENL_C_QUERY_FLOW, - .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ - .policy = dp_genl_flow_policy, - .doit = dp_genl_flow_query, - .dumpit = NULL, -}; - -static struct nla_policy dp_genl_table_policy[DP_GENL_A_MAX + 1] = { - [DP_GENL_A_DP_IDX] = { .type = NLA_U32 }, -}; - -static struct genl_ops dp_genl_ops_query_table = { - .cmd = DP_GENL_C_QUERY_TABLE, - .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ - .policy = dp_genl_table_policy, - .doit = dp_genl_table_query, - .dumpit = NULL, -}; - - static struct genl_ops dp_genl_ops_query_dp = { .cmd = DP_GENL_C_QUERY_DP, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ @@ -1500,6 +1105,8 @@ static int dp_genl_openflow(struct sk_buff *skb, struct genl_info *info) { struct nlattr *va = info->attrs[DP_GENL_A_OPENFLOW]; struct datapath *dp; + struct ofp_header *oh; + struct sender sender; int err; if (!info->attrs[DP_GENL_A_DP_IDX] || !va) @@ -1512,9 +1119,16 @@ static int dp_genl_openflow(struct sk_buff *skb, struct genl_info *info) goto out; } - va = info->attrs[DP_GENL_A_OPENFLOW]; + if (nla_len(va) < sizeof(struct ofp_header)) { + err = -EINVAL; + goto out; + } + oh = nla_data(va); - err = fwd_control_input(dp->chain, nla_data(va), nla_len(va)); + sender.xid = oh->xid; + sender.pid = info->snd_pid; + sender.seq = info->snd_seq; + err = fwd_control_input(dp->chain, &sender, nla_data(va), nla_len(va)); out: rcu_read_unlock(); @@ -1553,9 +1167,6 @@ static struct genl_ops *dp_genl_all_ops[] = { * front. */ &dp_genl_ops_openflow, - &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..d81e2f4c 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -48,23 +48,31 @@ 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]; struct list_head port_list; /* List of ports, for flooding. */ }; +/* Information necessary to reply to the sender of an OpenFlow message. */ +struct sender { + uint32_t xid; /* OpenFlow transaction ID of request. */ + uint32_t pid; /* Netlink process ID of sending socket. */ + uint32_t seq; /* Netlink sequence ID of request. */ +}; + 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 *, const struct sender *); +int dp_send_config_reply(struct datapath *, const struct sender *); int dp_send_flow_expired(struct datapath *, struct sw_flow *); +int dp_send_flow_stats(struct datapath *, const struct sender *, + const struct ofp_match *); +int dp_send_table_stats(struct datapath *, const struct sender *); +int dp_send_port_stats(struct datapath *, const struct sender *); 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 65a98aeb..7e987e33 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -41,7 +41,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); + ntohs(chain->dp->config.miss_send_len), + OFPR_NO_MATCH); } } @@ -256,25 +257,31 @@ 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 struct sender *sender, + 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); + return dp_send_features_reply(chain->dp, sender); +} - dp_send_hello(chain->dp); +static int +recv_get_config_request(struct sw_chain *chain, const struct sender *sender, + const void *msg) +{ + return dp_send_config_reply(chain->dp, sender); +} +static int +recv_set_config(struct sw_chain *chain, const struct sender *sender, + const void *msg) +{ + const struct ofp_switch_config *osc = msg; + chain->dp->config = *osc; return 0; } static int -recv_packet_out(struct sw_chain *chain, const void *msg) +recv_packet_out(struct sw_chain *chain, const struct sender *sender, + const void *msg) { const struct ofp_packet_out *opo = msg; struct sk_buff *skb; @@ -322,7 +329,8 @@ recv_packet_out(struct sw_chain *chain, const void *msg) } static int -recv_port_mod(struct sw_chain *chain, const void *msg) +recv_port_mod(struct sw_chain *chain, const struct sender *sender, + const void *msg) { const struct ofp_port_mod *opm = msg; @@ -406,7 +414,7 @@ error: } static int -recv_flow(struct sw_chain *chain, const void *msg) +recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg) { const struct ofp_flow_mod *ofm = msg; uint16_t command = ntohs(ofm->command); @@ -426,21 +434,58 @@ recv_flow(struct sw_chain *chain, const void *msg) } } -/* 'msg', which is 'length' bytes long, was received from the control path. - * Apply it to 'chain'. */ +static int +recv_flow_status_request(struct sw_chain *chain, const struct sender *sender, + const void *msg) +{ + const struct ofp_flow_stat_request *fsr = msg; + if (fsr->type == OFPFS_INDIV) { + return dp_send_flow_stats(chain->dp, sender, &fsr->match); + } else { + /* FIXME */ + return -ENOTSUPP; + } +} + +static int +recv_port_status_request(struct sw_chain *chain, const struct sender *sender, + const void *msg) +{ + return dp_send_port_stats(chain->dp, sender); +} + +static int +recv_table_status_request(struct sw_chain *chain, const struct sender *sender, + const void *msg) +{ + return dp_send_table_stats(chain->dp, sender); +} + +/* 'msg', which is 'length' bytes long, was received across Netlink from + * 'sender'. Apply it to 'chain'. */ int -fwd_control_input(struct sw_chain *chain, const void *msg, size_t length) +fwd_control_input(struct sw_chain *chain, const struct sender *sender, + const void *msg, size_t length) { struct openflow_packet { size_t min_size; - int (*handler)(struct sw_chain *, const void *); + int (*handler)(struct sw_chain *, const struct sender *, + const void *); }; 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), @@ -454,14 +499,23 @@ fwd_control_input(struct sw_chain *chain, const void *msg, size_t length) sizeof (struct ofp_port_mod), recv_port_mod, }, + [OFPT_FLOW_STAT_REQUEST] = { + sizeof (struct ofp_flow_stat_request), + recv_flow_status_request, + }, + [OFPT_PORT_STAT_REQUEST] = { + sizeof (struct ofp_port_stat_request), + recv_port_status_request, + }, + [OFPT_TABLE_STAT_REQUEST] = { + sizeof (struct ofp_table_stat_request), + recv_table_status_request, + }, }; const struct openflow_packet *pkt; struct ofp_header *oh; - if (length < sizeof(struct ofp_header)) - return -EINVAL; - oh = (struct ofp_header *) msg; if (oh->version != 1 || oh->type >= ARRAY_SIZE(packets) || ntohs(oh->length) > length) @@ -473,7 +527,7 @@ fwd_control_input(struct sw_chain *chain, const void *msg, size_t length) if (length < pkt->min_size) return -EFAULT; - return pkt->handler(chain, msg); + return pkt->handler(chain, sender, msg); } /* Packet buffering. */ diff --git a/datapath/forward.h b/datapath/forward.h index f061d2da..ce4d3b14 100644 --- a/datapath/forward.h +++ b/datapath/forward.h @@ -8,6 +8,7 @@ struct sk_buff; struct sw_chain; struct ofp_action; +struct sender; /* Buffers are identified to userspace by a 31-bit opaque ID. We divide the ID * into a buffer number (low bits) and a cookie (high bits). The buffer number @@ -22,7 +23,8 @@ struct ofp_action; void fwd_port_input(struct sw_chain *, struct sk_buff *, int in_port); -int fwd_control_input(struct sw_chain *, const void *, size_t); +int fwd_control_input(struct sw_chain *, const struct sender *, + const void *, size_t); uint32_t fwd_save_skb(struct sk_buff *skb); 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/include/Makefile.am b/include/Makefile.am index 409b2d63..0b7ddbe9 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -16,6 +16,7 @@ noinst_HEADERS = \ packets.h \ poll-loop.h \ queue.h \ + rconn.h \ socket-util.h \ util.h \ vconn.h \ 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/dpif.h b/include/dpif.h index 3fd3f612..4f806d7a 100644 --- a/include/dpif.h +++ b/include/dpif.h @@ -60,9 +60,6 @@ int dpif_add_dp(struct dpif *); int dpif_del_dp(struct dpif *); int dpif_add_port(struct dpif *, const char *netdev); int dpif_del_port(struct dpif *, const char *netdev); -int dpif_show(struct dpif *); -int dpif_dump_tables(struct dpif *); -int dpif_dump_flows(struct dpif *, int table, struct ofp_match *); int dpif_benchmark_nl(struct dpif *, uint32_t, uint32_t); #endif /* dpif.h */ diff --git a/include/dynamic-string.h b/include/dynamic-string.h index c23dfb7e..e43d006a 100644 --- a/include/dynamic-string.h +++ b/include/dynamic-string.h @@ -35,7 +35,9 @@ #define DYNAMIC_STRING_H 1 #include +#include #include +#include #include "compiler.h" struct ds { @@ -48,9 +50,13 @@ struct ds { void ds_init(struct ds *); void ds_reserve(struct ds *, size_t min_length); +void ds_put_char(struct ds *, char); +void ds_put_cstr(struct ds *, const char *); void ds_put_format(struct ds *, const char *, ...) PRINTF_FORMAT(2, 3); void ds_put_format_valist(struct ds *, const char *, va_list) PRINTF_FORMAT(2, 0); +void ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size, + uintptr_t ofs, bool ascii); char *ds_cstr(struct ds *); void ds_destroy(struct ds *); diff --git a/include/ofp-print.h b/include/ofp-print.h index 36981dec..76ff9248 100644 --- a/include/ofp-print.h +++ b/include/ofp-print.h @@ -39,19 +39,16 @@ #include struct ofp_flow_mod; -struct ofp_table; #ifdef __cplusplus extern "C" { #endif void ofp_print(FILE *, const void *, size_t, int verbosity); -void ofp_print_table(FILE *stream, const struct ofp_table* ot); -void ofp_print_flow_mod(FILE *stream, const void *data, size_t len, int verbosity); -void ofp_print_flow_expired(FILE *stream, const void *data, size_t len, int verbosity); -void ofp_print_data_hello(FILE *stream, const void *data, size_t len, int verbosity); void ofp_print_packet(FILE *stream, const void *data, size_t len, size_t total_len); -void ofp_print_port_status(FILE *stream, const void *oh, size_t len, int verbosity); + +char *ofp_to_string(const void *, size_t, int verbosity); +char *ofp_packet_to_string(const void *data, size_t len, size_t total_len); #ifdef __cplusplus } diff --git a/include/openflow-netlink.h b/include/openflow-netlink.h index d9862b0b..e7494777 100644 --- a/include/openflow-netlink.h +++ b/include/openflow-netlink.h @@ -42,21 +42,11 @@ /* Attributes that can be attached to the datapath's netlink messages. */ enum { DP_GENL_A_UNSPEC, - DP_GENL_A_OFPHEADER, /* OFP header information */ DP_GENL_A_DP_IDX, /* Datapath Ethernet device name. */ DP_GENL_A_PORTNAME, /* Device name for datapath port. */ DP_GENL_A_MC_GROUP, /* Generic netlink multicast group. */ DP_GENL_A_OPENFLOW, /* OpenFlow packet. */ - DP_GENL_A_DP_INFO, /* OpenFlow datapath information */ - - DP_GENL_A_FLOW, /* OpenFlow flow entry */ - DP_GENL_A_NUMFLOWS, /* Number of flows */ - DP_GENL_A_TABLEIDX, /* Flow table index */ - - DP_GENL_A_TABLE, /* OpenFlow table entry */ - DP_GENL_A_NUMTABLES, /* Number of tables in a table query */ - DP_GENL_A_NPACKETS, /* Number of packets to send up netlink */ DP_GENL_A_PSIZE, /* Size of packets to send up netlink */ @@ -70,27 +60,14 @@ enum dp_genl_command { DP_GENL_C_ADD_DP, /* Create datapath. */ DP_GENL_C_DEL_DP, /* Destroy datapath. */ DP_GENL_C_QUERY_DP, /* Get multicast group for datapath. */ - DP_GENL_C_SHOW_DP, /* Show information about datapath. */ DP_GENL_C_ADD_PORT, /* Add port to datapath. */ DP_GENL_C_DEL_PORT, /* Remove port from datapath. */ DP_GENL_C_OPENFLOW, /* Encapsulated OpenFlow protocol. */ - DP_GENL_C_QUERY_FLOW, /* Request flow entries. */ - DP_GENL_C_QUERY_TABLE, /* Request table entries. */ - DP_GENL_C_BENCHMARK_NL, /* Benchmark netlink connection */ __DP_GENL_C_MAX, DP_GENL_C_MAX = __DP_GENL_C_MAX - 1 }; -/* Table */ -enum { - TBL_MACONLY, - TBL_HASH, - TBL_LINEAR, - __TBL_MAX, - TBL_MAX = __TBL_MAX - 1 -}; - #endif /* openflow_netlink_h */ diff --git a/include/openflow.h b/include/openflow.h index 9c469066..7462b884 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 @@ -75,21 +75,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. */ @@ -103,22 +106,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. */ @@ -158,8 +157,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 */ @@ -177,15 +176,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. */ }; @@ -361,8 +354,10 @@ struct ofp_flow_stats { struct ofp_match match; /* Description of fields */ uint32_t duration; /* Time flow has been alive in seconds. Only used for non-aggregated results. */ - uint64_t packet_count; - uint64_t byte_count; + uint64_t packet_count; /* Number of packets in flow. */ + uint64_t byte_count; /* Number of bytes in flow. */ + uint8_t table_id; /* ID of table flow came from. */ + uint8_t pad[7]; /* Align to 64-bits. */ }; enum ofp_stat_type { @@ -374,8 +369,10 @@ enum ofp_stat_type { struct ofp_flow_stat_request { struct ofp_header header; struct ofp_match match; /* Fields to match */ + uint8_t table_id; /* ID of table to read (from ofp_table_stats) + or 0xffff for all tables. */ uint8_t type; /* One of OFPFS_ */ - uint8_t pad[3]; /* Align to 32-bits */ + uint16_t pad; /* Align to 32-bits */ }; /* Current flow statistics reply */ @@ -392,13 +389,47 @@ struct ofp_flow_stat_reply { struct ofp_flow_stats flows[0]; }; -/* Table attributes collected at runtime */ -struct ofp_table { +/* Current table statistics request */ +struct ofp_table_stat_request { + struct ofp_header header; +}; + +/* Statistics about a particular table */ +struct ofp_table_stats { + uint8_t table_id; + uint8_t pad[3]; /* Align to 32-bits */ + char name[OFP_MAX_TABLE_NAME_LEN]; + uint32_t max_entries; /* Max number of entries supported */ + uint32_t active_count; /* Number of active entries */ + uint64_t matched_count; /* Number of packets that hit table */ +}; + +/* Current table statistics reply */ +struct ofp_table_stat_reply { + struct ofp_header header; + struct ofp_table_stats tables[]; /* The number of entries is inferred from + the length field in the header. */ +}; + +/* Statistics about a particular port */ +struct ofp_port_stats { + uint16_t port_no; + uint8_t pad[2]; /* Align to 32-bits */ + uint64_t rx_count; /* Number of received packets */ + uint64_t tx_count; /* Number of transmitted packets */ + uint64_t drop_count; /* Number of packets dropped by interface */ +}; + +/* Current port statistics request */ +struct ofp_port_stat_request { + struct ofp_header header; +}; + +/* Current port statistics reply */ +struct ofp_port_stat_reply { struct ofp_header header; - char name[OFP_MAX_TABLE_NAME_LEN]; - uint16_t table_id; - unsigned long int n_flows; - unsigned long int max_flows; + struct ofp_port_stats ports[]; /* The number of entries is inferred from + the length field in the header. */ }; #endif /* openflow.h */ diff --git a/include/rconn.h b/include/rconn.h new file mode 100644 index 00000000..4049e745 --- /dev/null +++ b/include/rconn.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#ifndef RCONN_H +#define RCONN_H 1 + +#include "queue.h" +#include +#include + +/* A wrapper around vconn that provides queuing and optionally reliability. + * + * An rconn maintains a message transmission queue of bounded length specified + * by the caller. The rconn does not guarantee reliable delivery of + * queued messages: all queued messages are dropped when reconnection becomes + * necessary. + * + * An rconn optionally provides reliable communication, in this sense: the + * rconn will re-connect, with exponential backoff, when the underlying vconn + * disconnects. + */ + +struct vconn; + +struct rconn *rconn_new(const char *name, int txq_limit); +struct rconn *rconn_new_from_vconn(const char *name, int txq_limit, + struct vconn *); +void rconn_destroy(struct rconn *); + +void rconn_run(struct rconn *); +void rconn_run_wait(struct rconn *); +struct buffer *rconn_recv(struct rconn *); +void rconn_recv_wait(struct rconn *); +int rconn_send(struct rconn *, struct buffer *); +void rconn_send_wait(struct rconn *); + +const char *rconn_get_name(const struct rconn *); +bool rconn_is_alive(const struct rconn *); + +#endif /* rconn.h */ diff --git a/include/vconn-ssl.h b/include/vconn-ssl.h index dbd30b4a..1d4a923d 100644 --- a/include/vconn-ssl.h +++ b/include/vconn-ssl.h @@ -37,6 +37,27 @@ void vconn_ssl_set_private_key_file(const char *file_name); void vconn_ssl_set_certificate_file(const char *file_name); void vconn_ssl_set_ca_cert_file(const char *file_name); -#endif + +#define VCONN_SSL_LONG_OPTIONS \ + {"private-key", required_argument, 0, 'p'}, \ + {"certificate", required_argument, 0, 'c'}, \ + {"ca-cert", required_argument, 0, 'C'}, + +#define VCONN_SSL_OPTION_HANDLERS \ + 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; +#else /* !HAVE_OPENSSL */ +#define VCONN_SSL_LONG_OPTIONS +#define VCONN_SSL_OPTION_HANDLERS +#endif /* !HAVE_OPENSSL */ #endif /* vconn-ssl.h */ diff --git a/include/vconn.h b/include/vconn.h index b5fddbd3..ecaab0ec 100644 --- a/include/vconn.h +++ b/include/vconn.h @@ -49,6 +49,7 @@ struct vconn { int connect_status; }; +void vconn_usage(bool active, bool passive); int vconn_open(const char *name, struct vconn **); void vconn_close(struct vconn *); bool vconn_is_passive(const struct vconn *); @@ -59,6 +60,7 @@ int vconn_send(struct vconn *, struct buffer *); int vconn_open_block(const char *name, struct vconn **); int vconn_send_block(struct vconn *, struct buffer *); +int vconn_recv_block(struct vconn *, struct buffer **); enum vconn_wait_type { WAIT_CONNECT, diff --git a/include/vlog.h b/include/vlog.h index 63a0c412..01b2a851 100644 --- a/include/vlog.h +++ b/include/vlog.h @@ -63,7 +63,6 @@ enum vlog_facility vlog_get_facility_val(const char *name); #define VLOG_MODULES \ VLOG_MODULE(chain) \ VLOG_MODULE(controller) \ - VLOG_MODULE(controller_connection) \ VLOG_MODULE(ctlpath) \ VLOG_MODULE(datapath) \ VLOG_MODULE(dpif) \ @@ -74,6 +73,7 @@ enum vlog_facility vlog_get_facility_val(const char *name); VLOG_MODULE(netlink) \ VLOG_MODULE(poll_loop) \ VLOG_MODULE(secchan) \ + VLOG_MODULE(rconn) \ VLOG_MODULE(switch) \ VLOG_MODULE(socket_util) \ VLOG_MODULE(vconn_netlink) \ @@ -98,6 +98,7 @@ enum vlog_level vlog_get_level(enum vlog_module, enum vlog_facility); void vlog_set_levels(enum vlog_module, enum vlog_facility, enum vlog_level); char *vlog_set_levels_from_string(const char *); char *vlog_get_levels(void); +bool vlog_is_enabled(enum vlog_module, enum vlog_level); void vlog_set_verbosity(const char *arg); /* Function for actual logging. */ @@ -117,4 +118,12 @@ void vlog(enum vlog_module, enum vlog_level, const char *format, ...) #define VLOG_WARN(...) vlog(THIS_MODULE, VLL_WARN, __VA_ARGS__) #define VLOG_DBG(...) vlog(THIS_MODULE, VLL_DBG, __VA_ARGS__) +/* More convenience macros, for testing whether a given level is enabled in + * THIS_MODULE. When constructing a log message is expensive, this enables it + * to be skipped. */ +#define VLOG_IS_EMER_ENABLED() true +#define VLOG_IS_ERR_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_EMER) +#define VLOG_IS_WARN_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_WARN) +#define VLOG_IS_DBG_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_DBG) + #endif /* vlog.h */ diff --git a/lib/Makefile.am b/lib/Makefile.am index fff0c7fc..c34cba43 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -14,6 +14,7 @@ libopenflow_la_SOURCES = \ ofp-print.c \ poll-loop.c \ queue.c \ + rconn.c \ socket-util.c \ util.c \ vconn-tcp.c \ 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 b36df5f4..63611e93 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -258,193 +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_data_hello(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 }, -}; - -/* Writes a description of 'dp''s tables to stdout. Returns 0 if successful, - * otherwise a positive errno value. */ -int -dpif_dump_tables(struct dpif *dp) -{ - struct buffer request, *reply; - struct nlattr *attrs[ARRAY_SIZE(table_policy)]; - const struct ofp_table *tables; - int n_tables; - int i; - int retval; - - buffer_init(&request, 0); - nl_msg_put_genlmsghdr(&request, dp->sock, 0, openflow_family, - NLM_F_REQUEST, DP_GENL_C_QUERY_TABLE, 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, table_policy, attrs, - ARRAY_SIZE(table_policy))) { - buffer_delete(reply); - return EPROTO; - } - - tables = nl_attr_get(attrs[DP_GENL_A_TABLE]); - n_tables = (nl_attr_get_size(attrs[DP_GENL_A_TABLE]) - / sizeof(struct ofp_table)); - n_tables = MIN(n_tables, nl_attr_get_u32(attrs[DP_GENL_A_NUMTABLES])); - for (i = 0; i < n_tables; i++) { - const struct ofp_table *ot = &tables[i]; - if (ot->header.version != 1 || ot->header.type != OFPT_TABLE) { - VLOG_DBG("bad table query response (%"PRIu8",%"PRIu8")", - ot->header.version, ot->header.type); - retval = EPROTO; - break; - } - - ofp_print_table(stdout, ot); - fprintf(stdout,"\n"); - } - buffer_delete(reply); - - return retval; -} - -static const struct nl_policy flow_policy[] = { - [DP_GENL_A_TABLEIDX] = { .type = NL_A_U16 }, - [DP_GENL_A_NUMFLOWS] = { .type = NL_A_U32 }, - [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 - * errno value. */ -int -dpif_dump_flows(struct dpif *dp, int table, struct ofp_match *match) -{ - struct buffer request, *reply; - struct ofp_flow_mod *ofm; - int retval; - - buffer_init(&request, 0); - nl_msg_put_genlmsghdr(&request, dp->sock, 0, openflow_family, NLM_F_REQUEST, - DP_GENL_C_QUERY_FLOW, 1); - nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, dp->dp_idx); - nl_msg_put_u16(&request, DP_GENL_A_TABLEIDX, table); - ofm = nl_msg_put_unspec_uninit(&request, DP_GENL_A_FLOW, sizeof *ofm); - memset(ofm, 0, sizeof *ofm); - ofm->header.version = 1; - ofm->header.type = OFPT_FLOW_MOD; - ofm->header.length = htons(sizeof ofm); - if (match) { - ofm->match = *match; - } else { - ofm->match.wildcards = htons(OFPFW_ALL); - } - retval = nl_sock_transact(dp->sock, &request, &reply); - buffer_uninit(&request); - if (retval) { - return retval; - } - - for (;;) { - struct nlattr *attrs[ARRAY_SIZE(flow_policy)]; - const struct _dump_ofp_flow_mod *flows, *ofm; - int n_flows; - - if (!nl_policy_parse(reply, flow_policy, attrs, - ARRAY_SIZE(flow_policy))) { - buffer_delete(reply); - return EPROTO; - } - n_flows = (nl_attr_get_size(attrs[DP_GENL_A_FLOW]) - / sizeof(struct ofp_flow_mod)); - n_flows = MIN(n_flows, nl_attr_get_u32(attrs[DP_GENL_A_NUMFLOWS])); - if (n_flows <= 0) { - break; - } - - flows = nl_attr_get(attrs[DP_GENL_A_FLOW]); - for (ofm = flows; ofm < &flows[n_flows]; ofm++) { - if (ofm->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) { - VLOG_DBG("recv_fp_flow bad return message type"); - buffer_delete(reply); - return EPROTO; - } - - ofp_print_flow_mod(stdout, &ofm->ofm, - sizeof(struct ofp_flow_mod), 1); - putc('\n', stdout); - } - - buffer_delete(reply); - retval = nl_sock_recv(dp->sock, &reply, true); - if (retval) { - return retval; - } - } - return 0; -} - /* Tells dp to send num_packets up through netlink for benchmarking*/ int dpif_benchmark_nl(struct dpif *dp, uint32_t num_packets, uint32_t packet_size) diff --git a/lib/dynamic-string.c b/lib/dynamic-string.c index 01d63fe5..68dd3419 100644 --- a/lib/dynamic-string.c +++ b/lib/dynamic-string.c @@ -34,6 +34,7 @@ #include "dynamic-string.h" #include #include +#include #include "util.h" void @@ -54,6 +55,23 @@ ds_reserve(struct ds *ds, size_t min_length) } } +void +ds_put_char(struct ds *ds, char c) +{ + ds_reserve(ds, ds->length + 1); + ds->string[ds->length++] = c; + ds->string[ds->length] = '\0'; +} + +void +ds_put_cstr(struct ds *ds, const char *s) +{ + size_t s_len = strlen(s); + ds_reserve(ds, ds->length + s_len); + memcpy(&ds->string[ds->length], s, s_len + 1); + ds->length += s_len; +} + void ds_put_format(struct ds *ds, const char *format, ...) { @@ -108,3 +126,56 @@ ds_destroy(struct ds *ds) { free(ds->string); } + +/* Writes the 'size' bytes in 'buf' to 'string' as hex bytes arranged 16 per + * line. Numeric offsets are also included, starting at 'ofs' for the first + * byte in 'buf'. If 'ascii' is true then the corresponding ASCII characters + * are also rendered alongside. */ +void +ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size, + uintptr_t ofs, bool ascii) +{ + const uint8_t *buf = buf_; + const size_t per_line = 16; /* Maximum bytes per line. */ + + while (size > 0) + { + size_t start, end, n; + size_t i; + + /* Number of bytes on this line. */ + start = ofs % per_line; + end = per_line; + if (end - start > size) + end = start + size; + n = end - start; + + /* Print line. */ + ds_put_format(ds, "%08jx ", (uintmax_t) ROUND_DOWN(ofs, per_line)); + for (i = 0; i < start; i++) + ds_put_format(ds, " "); + for (; i < end; i++) + ds_put_format(ds, "%02hhx%c", + buf[i - start], i == per_line / 2 - 1? '-' : ' '); + if (ascii) + { + for (; i < per_line; i++) + ds_put_format(ds, " "); + ds_put_format(ds, "|"); + for (i = 0; i < start; i++) + ds_put_format(ds, " "); + for (; i < end; i++) { + int c = buf[i - start]; + ds_put_char(ds, c >= 32 && c < 127 ? c : '.'); + } + for (; i < per_line; i++) + ds_put_format(ds, " "); + ds_put_format(ds, "|"); + } + ds_put_format(ds, "\n"); + + ofs += n; + buf += n; + size -= n; + } +} diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 8ee1af58..8b056fd9 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -43,17 +43,21 @@ #include #include "compiler.h" +#include "dynamic-string.h" #include "util.h" #include "openflow.h" #include "packets.h" -/* Dumps the contents of the Ethernet frame in the 'len' bytes starting at - * 'data' to 'stream' using tcpdump. 'total_len' specifies the full length of - * the Ethernet frame (of which 'len' bytes were captured). +/* Returns a string that represents the contents of the Ethernet frame in the + * 'len' bytes starting at 'data' to 'stream' as output by tcpdump. + * 'total_len' specifies the full length of the Ethernet frame (of which 'len' + * bytes were captured). + * + * The caller must free the returned string. * * This starts and kills a tcpdump subprocess so it's quite expensive. */ -void ofp_print_packet(FILE *stream, const void *data, size_t len, - size_t total_len) +char * +ofp_packet_to_string(const void *data, size_t len, size_t total_len) { struct pcap_hdr { uint32_t magic_number; /* magic number */ @@ -75,17 +79,18 @@ void ofp_print_packet(FILE *stream, const void *data, size_t len, struct pcap_hdr ph; struct pcaprec_hdr prh; + struct ds ds = DS_EMPTY_INITIALIZER; + char command[128]; + FILE *pcap; FILE *tcpdump; int status; + int c; - fflush(stream); - snprintf(command, sizeof command, "tcpdump -n -r - %d>&1 2>/dev/null", - fileno(stream)); - tcpdump = popen(command, "w"); - if (!tcpdump) { - error(errno, "exec(\"%s\")", command); - return; + pcap = tmpfile(); + if (!pcap) { + error(errno, "tmpfile"); + return xstrdup(""); } /* The pcap reader is responsible for figuring out endianness based on the @@ -103,13 +108,28 @@ void ofp_print_packet(FILE *stream, const void *data, size_t len, prh.incl_len = len; prh.orig_len = total_len; - fwrite(&ph, 1, sizeof ph, tcpdump); - fwrite(&prh, 1, sizeof prh, tcpdump); - fwrite(data, 1, len, tcpdump); + fwrite(&ph, 1, sizeof ph, pcap); + fwrite(&prh, 1, sizeof prh, pcap); + fwrite(data, 1, len, pcap); + + fflush(pcap); + if (ferror(pcap)) { + error(errno, "error writing temporary file"); + } + rewind(pcap); + + snprintf(command, sizeof command, "tcpdump -n -r /dev/fd/%d 2>/dev/null", + fileno(pcap)); + tcpdump = popen(command, "r"); + fclose(pcap); + if (!tcpdump) { + error(errno, "exec(\"%s\")", command); + return xstrdup(""); + } - fflush(tcpdump); - if (ferror(tcpdump)) - error(errno, "error writing \"%s\" subprocess", command); + while ((c = getc(tcpdump)) != EOF) { + ds_put_char(&ds, c); + } status = pclose(tcpdump); if (WIFEXITED(status)) { @@ -118,116 +138,124 @@ void ofp_print_packet(FILE *stream, const void *data, size_t len, } else if (WIFSIGNALED(status)) { error(0, "tcpdump exited with signal %d", WTERMSIG(status)); } + return ds_cstr(&ds); } /* Pretty-print the OFPT_PACKET_IN packet of 'len' bytes at 'oh' to 'stream' * at the given 'verbosity' level. */ -static void ofp_packet_in(FILE *stream, const void *oh, size_t len, - int verbosity) +static void +ofp_packet_in(struct ds *string, const void *oh, size_t len, int verbosity) { const struct ofp_packet_in *op = oh; size_t data_len; - fprintf(stream, " total_len=%"PRIu16" in_port=%"PRIu8, + ds_put_format(string, " total_len=%"PRIu16" in_port=%"PRIu8, ntohs(op->total_len), ntohs(op->in_port)); if (op->reason == OFPR_ACTION) - fputs(" (via action)", stream); + ds_put_cstr(string, " (via action)"); else if (op->reason != OFPR_NO_MATCH) - fprintf(stream, " (***reason %"PRIu8"***)", op->reason); + ds_put_format(string, " (***reason %"PRIu8"***)", op->reason); data_len = len - offsetof(struct ofp_packet_in, data); - fprintf(stream, " data_len=%zu", data_len); + ds_put_format(string, " data_len=%zu", data_len); if (htonl(op->buffer_id) == UINT32_MAX) { - fprintf(stream, " (unbuffered)"); + ds_put_format(string, " (unbuffered)"); if (ntohs(op->total_len) != data_len) - fprintf(stream, " (***total_len != data_len***)"); + ds_put_format(string, " (***total_len != data_len***)"); } else { - fprintf(stream, " buffer=%08"PRIx32, ntohl(op->buffer_id)); + ds_put_format(string, " buffer=%08"PRIx32, ntohl(op->buffer_id)); if (ntohs(op->total_len) < data_len) - fprintf(stream, " (***total_len < data_len***)"); + ds_put_format(string, " (***total_len < data_len***)"); } - putc('\n', stream); + ds_put_char(string, '\n'); - if (verbosity > 0) - ofp_print_packet(stream, op->data, data_len, ntohs(op->total_len)); + if (verbosity > 0) { + char *packet = ofp_packet_to_string(op->data, data_len, + ntohs(op->total_len)); + ds_put_cstr(string, packet); + free(packet); + } } -static void ofp_print_port_name(FILE *stream, uint16_t port) +static void ofp_print_port_name(struct ds *string, uint16_t port) { if (port == UINT16_MAX) { - fputs("none", stream); + ds_put_cstr(string, "none"); } else if (port == OFPP_FLOOD) { - fputs("flood", stream); + ds_put_cstr(string, "flood"); } else if (port == OFPP_CONTROLLER) { - fputs("controller", stream); + ds_put_cstr(string, "controller"); } else { - fprintf(stream, "%"PRIu16, port); + ds_put_format(string, "%"PRIu16, port); } } -static void ofp_print_action(FILE *stream, const struct ofp_action *a) +static void +ofp_print_action(struct ds *string, const struct ofp_action *a) { switch (ntohs(a->type)) { case OFPAT_OUTPUT: - fputs("output(", stream); - ofp_print_port_name(stream, ntohs(a->arg.output.port)); + ds_put_cstr(string, "output("); + ofp_print_port_name(string, ntohs(a->arg.output.port)); if (a->arg.output.port == htons(OFPP_CONTROLLER)) { - fprintf(stream, ", max %"PRIu16" bytes", ntohs(a->arg.output.max_len)); + ds_put_format(string, ", max %"PRIu16" bytes", ntohs(a->arg.output.max_len)); } - fputs(")", stream); + ds_put_cstr(string, ")"); break; default: - fprintf(stream, "(decoder %"PRIu16" not implemented)", ntohs(a->type)); + ds_put_format(string, "(decoder %"PRIu16" not implemented)", ntohs(a->type)); break; } } -static void ofp_print_actions(FILE *stream, - const struct ofp_action actions[], - size_t n_bytes) +static void ofp_print_actions(struct ds *string, + const struct ofp_action actions[], + size_t n_bytes) { size_t i; - fputs(" actions[", stream); + ds_put_cstr(string, " actions["); for (i = 0; i < n_bytes / sizeof *actions; i++) { if (i) { - fputs("; ", stream); + ds_put_cstr(string, "; "); } - ofp_print_action(stream, &actions[i]); + ofp_print_action(string, &actions[i]); } if (n_bytes % sizeof *actions) { if (i) { - fputs("; ", stream); + ds_put_cstr(string, "; "); } - fputs("; ***trailing garbage***", stream); + ds_put_cstr(string, "; ***trailing garbage***"); } - fputs("]", stream); + ds_put_cstr(string, "]"); } -/* Pretty-print the OFPT_PACKET_OUT packet of 'len' bytes at 'oh' to 'stream' +/* Pretty-print the OFPT_PACKET_OUT packet of 'len' bytes at 'oh' to 'string' * at the given 'verbosity' level. */ -static void ofp_packet_out(FILE *stream, const void *oh, size_t len, - int verbosity) +static void ofp_packet_out(struct ds *string, const void *oh, size_t len, + int verbosity) { const struct ofp_packet_out *opo = oh; - fputs(" in_port=", stream); - ofp_print_port_name(stream, ntohs(opo->in_port)); + ds_put_cstr(string, " in_port="); + ofp_print_port_name(string, ntohs(opo->in_port)); if (ntohl(opo->buffer_id) == UINT32_MAX) { - fputs(" out_port=", stream); - ofp_print_port_name(stream, ntohs(opo->out_port)); + ds_put_cstr(string, " out_port="); + ofp_print_port_name(string, ntohs(opo->out_port)); if (verbosity > 0 && len > sizeof *opo) { - ofp_print_packet(stream, opo->u.data, len - sizeof *opo, - len - sizeof *opo); + char *packet = ofp_packet_to_string(opo->u.data, len - sizeof *opo, + len - sizeof *opo); + ds_put_cstr(string, packet); + free(packet); } } else { - fprintf(stream, " buffer=%08"PRIx32, ntohl(opo->buffer_id)); - ofp_print_actions(stream, opo->u.actions, len - sizeof *opo); + ds_put_format(string, " buffer=%08"PRIx32, ntohl(opo->buffer_id)); + ofp_print_actions(string, opo->u.actions, len - sizeof *opo); } - putc('\n', stream); + ds_put_char(string, '\n'); } /* qsort comparison function. */ @@ -242,8 +270,8 @@ compare_ports(const void *a_, const void *b_) return ap < bp ? -1 : ap > bp; } -static -void ofp_print_phy_port(FILE *stream, const struct ofp_phy_port *port) +static void +ofp_print_phy_port(struct ds *string, const struct ofp_phy_port *port) { uint8_t name[OFP_MAX_PORT_NAME_LEN]; int j; @@ -256,65 +284,85 @@ void ofp_print_phy_port(FILE *stream, const struct ofp_phy_port *port) } name[j] = '\0'; - fprintf(stream, " %2d(%s): addr:"ETH_ADDR_FMT", speed:%d, flags:%#x, " + ds_put_format(string, " %2d(%s): addr:"ETH_ADDR_FMT", speed:%d, flags:%#x, " "feat:%#x\n", ntohs(port->port_no), name, ETH_ADDR_ARGS(port->hw_addr), ntohl(port->speed), 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(FILE *stream, 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; - - fprintf(stream, "dp id:%"PRIx64"\n", ntohll(odh->datapath_id)); - fprintf(stream, "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)); - fprintf(stream, "buffers: size:%d, number:%d, miss_len:%d\n", - ntohl(odh->buffer_mb), ntohl(odh->n_buffers), - ntohs(odh->miss_send_len)); - fprintf(stream, "features: capabilities:%#x, actions:%#x\n", - ntohl(odh->capabilities), ntohl(odh->actions)); - - if (ntohs(odh->header.length) >= sizeof *odh) { - len = MIN(len, ntohs(odh->header.length)); + 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(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(osf->capabilities), ntohl(osf->actions)); + + 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(stream, &port_list[i]); + ofp_print_phy_port(string, &port_list[i]); } } -static void print_wild(FILE *stream, const char *leader, int is_wild, +/* 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))); -static void print_wild(FILE *stream, const char *leader, int is_wild, +static void print_wild(struct ds *string, const char *leader, int is_wild, const char *format, ...) { - fputs(leader, stream); + ds_put_cstr(string, leader); if (!is_wild) { va_list args; va_start(args, format); - vfprintf(stream, format, args); + ds_put_format_valist(string, format, args); va_end(args); } else { - putc('?', stream); + ds_put_char(string, '?'); } } /* Pretty-print the ofp_match structure */ -static void ofp_print_match(FILE *f, const struct ofp_match *om) +static void ofp_print_match(struct ds *f, const struct ofp_match *om) { uint16_t w = ntohs(om->wildcards); @@ -330,155 +378,323 @@ static void ofp_print_match(FILE *f, const struct ofp_match *om) print_wild(f, "] proto", w & OFPFW_NW_PROTO, "%u", om->nw_proto); print_wild(f, " tport[", w & OFPFW_TP_SRC, "%d", ntohs(om->tp_src)); print_wild(f, "->", w & OFPFW_TP_DST, "%d", ntohs(om->tp_dst)); - fputs("]\n", f); + ds_put_cstr(f, "]\n"); } -/* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'stream' +/* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'string' * at the given 'verbosity' level. */ -void ofp_print_flow_mod(FILE *stream, const void *oh, size_t len, - int verbosity) +static void +ofp_print_flow_mod(struct ds *string, const void *oh, size_t len, + int verbosity) { const struct ofp_flow_mod *ofm = oh; - ofp_print_match(stream, &ofm->match); - fprintf(stream, " cmd:%d idle:%d buf:%#x grp:%d\n", ntohs(ofm->command), + ofp_print_match(string, &ofm->match); + ds_put_format(string, " cmd:%d idle:%d buf:%#x grp:%d\n", ntohs(ofm->command), ntohs(ofm->max_idle), ntohl(ofm->buffer_id), ntohl(ofm->group_id)); } -/* Pretty-print the OFPT_FLOW_EXPIRED packet of 'len' bytes at 'oh' to 'stream' +/* Pretty-print the OFPT_FLOW_EXPIRED packet of 'len' bytes at 'oh' to 'string' * at the given 'verbosity' level. */ -void ofp_print_flow_expired(FILE *stream, const void *oh, size_t len, +void ofp_print_flow_expired(struct ds *string, const void *oh, size_t len, int verbosity) { const struct ofp_flow_expired *ofe = oh; - ofp_print_match(stream, &ofe->match); - fprintf(stream, + ofp_print_match(string, &ofe->match); + ds_put_format(string, " secs%d pkts%lld bytes%lld\n", ntohl(ofe->duration), ntohll(ofe->packet_count), ntohll(ofe->byte_count)); } -/* Pretty-print the OFPT_PORT_STATUS packet of 'len' bytes at 'oh' to 'stream' +/* Pretty-print the OFPT_PORT_STATUS packet of 'len' bytes at 'oh' to 'string' * at the given 'verbosity' level. */ -void ofp_print_port_status(FILE *stream, const void *oh, size_t len, +void ofp_print_port_status(struct ds *string, const void *oh, size_t len, int verbosity) { const struct ofp_port_status *ops = oh; if (ops->reason == OFPPR_ADD) { - fprintf(stream, "add:"); + ds_put_format(string, "add:"); } else if (ops->reason == OFPPR_DELETE) { - fprintf(stream, "del:"); + ds_put_format(string, "del:"); } else if (ops->reason == OFPPR_MOD) { - fprintf(stream, "mod:"); + ds_put_format(string, "mod:"); } else { - fprintf(stream, "err:"); + ds_put_format(string, "err:"); } - ofp_print_phy_port(stream, &ops->desc); + ofp_print_phy_port(string, &ops->desc); +} + +static void +ofp_flow_stat_request(struct ds *string, const void *oh, size_t len, + int verbosity) +{ + const struct ofp_flow_stat_request *fsr = oh; + + if (fsr->table_id == 0xff) { + ds_put_format(string, " table_id=any, "); + } else { + ds_put_format(string, " table_id=%"PRIu8", ", fsr->table_id); + } + + if (fsr->type == OFPFS_INDIV) { + ds_put_cstr(string, " type=indiv, "); + } else if (fsr->type == OFPFS_AGGREGATE) { + ds_put_cstr(string, " type=aggregate, "); + } else { + ds_put_format(string, " ***type=%"PRIu8"***, ", fsr->type); + } + ofp_print_match(string, &fsr->match); +} + +static void +ofp_flow_stat_reply(struct ds *string, const void *oh, size_t len, + int verbosity) +{ + const struct ofp_flow_stat_reply *fsr = oh; + const struct ofp_flow_stats *fs; + size_t n; + + n = (len - offsetof(struct ofp_flow_stat_reply, flows)) / sizeof *fs; + ds_put_format(string, " %zu flows\n", n); + if (verbosity < 1) { + return; + } + + for (fs = &fsr->flows[0]; fs < &fsr->flows[n]; fs++) { + ds_put_format(string, " duration=%"PRIu32" s, ", ntohs(fs->duration)); + ds_put_format(string, "table_id=%"PRIu8", ", fs->table_id); + ds_put_format(string, "n_packets=%"PRIu64", ", + ntohll(fs->packet_count)); + ds_put_format(string, "n_bytes=%"PRIu64", ", ntohll(fs->byte_count)); + ofp_print_match(string, &fs->match); + } +} + +static void +ofp_port_stat_reply(struct ds *string, const void *oh, size_t len, + int verbosity) +{ + const struct ofp_port_stat_reply *psr = oh; + const struct ofp_port_stats *ps; + size_t n; + + n = (len - offsetof(struct ofp_port_stat_reply, ports)) / sizeof *ps; + ds_put_format(string, " %zu ports\n", n); + if (verbosity < 1) { + return; + } + + for (ps = &psr->ports[0]; ps < &psr->ports[n]; ps++) { + ds_put_format(string, " port %"PRIu16": ", ntohs(ps->port_no)); + ds_put_format(string, "rx %"PRIu64", ", ntohll(ps->rx_count)); + ds_put_format(string, "tx %"PRIu64", ", ntohll(ps->tx_count)); + ds_put_format(string, "dropped %"PRIu64"\n", ntohll(ps->drop_count)); + } +} + +static void +ofp_table_stat_reply(struct ds *string, const void *oh, size_t len, + int verbosity) +{ + const struct ofp_table_stat_reply *tsr = oh; + const struct ofp_table_stats *ts; + size_t n; + + n = (len - offsetof(struct ofp_table_stat_reply, tables)) / sizeof *ts; + ds_put_format(string, " %zu tables\n", n); + if (verbosity < 1) { + return; + } + + for (ts = &tsr->tables[0]; ts < &tsr->tables[n]; ts++) { + char name[OFP_MAX_TABLE_NAME_LEN + 1]; + strncpy(name, ts->name, sizeof name); + name[OFP_MAX_TABLE_NAME_LEN] = '\0'; + + ds_put_format(string, " table %"PRIu8": ", ts->table_id); + ds_put_format(string, "name %-8s, ", name); + ds_put_format(string, "max %6"PRIu32", ", ntohl(ts->max_entries)); + ds_put_format(string, "active %6"PRIu32", ", ntohl(ts->active_count)); + ds_put_format(string, "matched %6"PRIu64"\n", + ntohll(ts->matched_count)); + } } struct openflow_packet { const char *name; size_t min_size; - void (*printer)(FILE *, const void *, size_t len, int verbosity); + void (*printer)(struct ds *, const void *, size_t len, int verbosity); }; 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_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_DATA_HELLO] = { - "ofp_data_hello", - sizeof (struct ofp_data_hello), - ofp_print_data_hello, + [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 }, + [OFPT_FLOW_STAT_REQUEST] = { + "flow_stat_request", + sizeof (struct ofp_flow_stat_request), + ofp_flow_stat_request, + }, + [OFPT_FLOW_STAT_REPLY] = { + "flow_stat_reply", + sizeof (struct ofp_flow_stat_reply), + ofp_flow_stat_reply, + }, + [OFPT_PORT_STAT_REQUEST] = { + "port_stat_request", + sizeof (struct ofp_port_stat_request), + NULL, + }, + [OFPT_PORT_STAT_REPLY] = { + "port_stat_reply", + sizeof (struct ofp_port_stat_reply), + ofp_port_stat_reply, + }, + [OFPT_TABLE_STAT_REQUEST] = { + "table_stat_request", + sizeof (struct ofp_table_stat_request), + NULL, + }, + [OFPT_TABLE_STAT_REPLY] = { + "table_stat_reply", + sizeof (struct ofp_table_stat_reply), + ofp_table_stat_reply, + }, }; -/* Pretty-print the OpenFlow packet of 'len' bytes at 'oh' to 'stream' at the - * given 'verbosity' level. 0 is a minimal amount of verbosity and higher - * numbers increase verbosity. */ -void ofp_print(FILE *stream, const void *oh_, size_t len, int verbosity) +/* Composes and returns a string representing the OpenFlow packet of 'len' + * bytes at 'oh' at the given 'verbosity' level. 0 is a minimal amount of + * verbosity and higher numbers increase verbosity. The caller is responsible + * for freeing the string. */ +char * +ofp_to_string(const void *oh_, size_t len, int verbosity) { + struct ds string = DS_EMPTY_INITIALIZER; const struct ofp_header *oh = oh_; const struct openflow_packet *pkt; if (len < sizeof(struct ofp_header)) { - fprintf(stream, "OpenFlow packet too short:\n"); - hex_dump(stream, oh, len, 0, true); - return; + ds_put_cstr(&string, "OpenFlow packet too short:\n"); + ds_put_hex_dump(&string, oh, len, 0, true); + return ds_cstr(&string); } else if (oh->version != 1) { - fprintf(stream, "Bad OpenFlow version %"PRIu8":\n", oh->version); - hex_dump(stream, oh, len, 0, true); - return; + ds_put_format(&string, "Bad OpenFlow version %"PRIu8":\n", oh->version); + ds_put_hex_dump(&string, oh, len, 0, true); + return ds_cstr(&string); } else if (oh->type >= ARRAY_SIZE(packets) || !packets[oh->type].name) { - fprintf(stream, "Unknown OpenFlow packet type %"PRIu8":\n", + ds_put_format(&string, "Unknown OpenFlow packet type %"PRIu8":\n", oh->type); - hex_dump(stream, oh, len, 0, true); - return; + ds_put_hex_dump(&string, oh, len, 0, true); + return ds_cstr(&string); } pkt = &packets[oh->type]; - fprintf(stream, "%s (xid=%"PRIx32"):", pkt->name, oh->xid); + ds_put_format(&string, "%s (xid=%"PRIx32"):", pkt->name, oh->xid); if (ntohs(oh->length) > len) - fprintf(stream, " (***truncated to %zu bytes from %"PRIu16"***)", + ds_put_format(&string, " (***truncated to %zu bytes from %"PRIu16"***)", len, ntohs(oh->length)); else if (ntohs(oh->length) < len) { - fprintf(stream, " (***only uses %"PRIu16" bytes out of %zu***)\n", + ds_put_format(&string, " (***only uses %"PRIu16" bytes out of %zu***)\n", ntohs(oh->length), len); len = ntohs(oh->length); } if (len < pkt->min_size) { - fprintf(stream, " (***length=%zu < min_size=%zu***)\n", + ds_put_format(&string, " (***length=%zu < min_size=%zu***)\n", len, pkt->min_size); } else if (!pkt->printer) { - fprintf(stream, " length=%zu (decoder not implemented)\n", - ntohs(oh->length)); + if (len > sizeof *oh) { + ds_put_format(&string, " length=%zu (decoder not implemented)\n", + ntohs(oh->length)); + } } else { - pkt->printer(stream, oh, len, verbosity); + pkt->printer(&string, oh, len, verbosity); } - if (verbosity >= 3) - hex_dump(stream, oh, len, 0, true); + if (verbosity >= 3) { + ds_put_hex_dump(&string, oh, len, 0, true); + } + return ds_cstr(&string); +} + +static void +print_and_free(FILE *stream, char *string) +{ + fputs(string, stream); + free(string); } -/* Pretty print a openflow table */ -void ofp_print_table(FILE *stream, const struct ofp_table* ot) +/* Pretty-print the OpenFlow packet of 'len' bytes at 'oh' to 'stream' at the + * given 'verbosity' level. 0 is a minimal amount of verbosity and higher + * numbers increase verbosity. */ +void +ofp_print(FILE *stream, const void *oh, size_t len, int verbosity) +{ + print_and_free(stream, ofp_to_string(oh, len, verbosity)); +} + +/* Dumps the contents of the Ethernet frame in the 'len' bytes starting at + * 'data' to 'stream' using tcpdump. 'total_len' specifies the full length of + * the Ethernet frame (of which 'len' bytes were captured). + * + * This starts and kills a tcpdump subprocess so it's quite expensive. */ +void +ofp_print_packet(FILE *stream, const void *data, size_t len, size_t total_len) { - fprintf(stream, "id: %d name: %-8s n_flows: %6d max_flows: %6d", - ntohs(ot->table_id), ot->name, ntohl(ot->n_flows), - ntohl(ot->max_flows)); + print_and_free(stream, ofp_packet_to_string(data, len, total_len)); } diff --git a/lib/rconn.c b/lib/rconn.c new file mode 100644 index 00000000..f62ac61a --- /dev/null +++ b/lib/rconn.c @@ -0,0 +1,287 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#include "rconn.h" +#include +#include +#include +#include +#include "buffer.h" +#include "poll-loop.h" +#include "ofp-print.h" +#include "util.h" +#include "vconn.h" + +#define THIS_MODULE VLM_rconn +#include "vlog.h" + +/* A reliable connection to an OpenFlow switch or controller. + * + * See the large comment in rconn.h for more information. */ +struct rconn { + bool reliable; + char *name; + struct vconn *vconn; + bool connected; + struct queue txq; + int txq_limit; + time_t backoff_deadline; + int backoff; +}; + +static struct rconn *create_rconn(const char *name, int txq_limit, + struct vconn *); +static int try_send(struct rconn *); +static void disconnect(struct rconn *, int error); + +/* Creates and returns a new rconn that connects (and re-connects as necessary) + * to the vconn named 'name'. + * + * 'txq_limit' is the maximum length of the send queue, in packets. */ +struct rconn * +rconn_new(const char *name, int txq_limit) +{ + return create_rconn(name, txq_limit, NULL); +} + +/* Creates and returns a new rconn that is initially connected to 'vconn' and + * has the given 'name'. The rconn will not re-connect after the connection + * drops. + * + * 'txq_limit' is the maximum length of the send queue, in packets. */ +struct rconn * +rconn_new_from_vconn(const char *name, int txq_limit, struct vconn *vconn) +{ + assert(vconn != NULL); + return create_rconn(name, txq_limit, vconn); +} + +/* Disconnects 'rc' and frees the underlying storage. */ +void +rconn_destroy(struct rconn *rc) +{ + if (rc) { + free(rc->name); + vconn_close(rc->vconn); + queue_destroy(&rc->txq); + free(rc); + } +} + +/* Performs whatever activities are necessary to maintain 'rc': if 'rc' is + * disconnected, attempts to (re)connect, backing off as necessary; if 'rc' is + * connected, attempts to send packets in the send queue, if any. */ +void +rconn_run(struct rconn *rc) +{ + if (!rc->vconn) { + if (rc->reliable && time(0) >= rc->backoff_deadline) { + int retval; + + retval = vconn_open(rc->name, &rc->vconn); + if (!retval) { + rc->backoff_deadline = time(0) + rc->backoff; + rc->connected = false; + } else { + VLOG_WARN("%s: connection failed (%s)", + rc->name, strerror(retval)); + disconnect(rc, 0); + } + } + } else if (!rc->connected) { + int error = vconn_connect(rc->vconn); + if (!error) { + VLOG_WARN("%s: connected", rc->name); + if (vconn_is_passive(rc->vconn)) { + fatal(0, "%s: passive vconn not supported in switch", + rc->name); + } + rc->connected = true; + } else if (error != EAGAIN) { + VLOG_WARN("%s: connection failed (%s)", rc->name, strerror(error)); + disconnect(rc, 0); + } + } else { + while (rc->txq.n > 0) { + int error = try_send(rc); + if (error == EAGAIN) { + break; + } else if (error) { + disconnect(rc, error); + return; + } + } + } +} + +/* Causes the next call to poll_block() to wake up when rconn_run() should be + * called on 'rc'. */ +void +rconn_run_wait(struct rconn *rc) +{ + if (rc->vconn) { + if (rc->txq.n) { + vconn_wait(rc->vconn, WAIT_SEND); + } + } else { + poll_timer_wait((rc->backoff_deadline - time(0)) * 1000); + } +} + +/* Attempts to receive a packet from 'rc'. If successful, returns the packet; + * otherwise, returns a null pointer. The caller is responsible for freeing + * the packet (with buffer_delete()). */ +struct buffer * +rconn_recv(struct rconn *rc) +{ + if (rc->vconn && rc->connected) { + struct buffer *buffer; + int error = vconn_recv(rc->vconn, &buffer); + if (!error) { + return buffer; + } else if (error != EAGAIN) { + disconnect(rc, error); + } + } + return NULL; +} + +/* Causes the next call to poll_block() to wake up when a packet may be ready + * to be received by vconn_recv() on 'rc'. */ +void +rconn_recv_wait(struct rconn *rc) +{ + if (rc->vconn) { + vconn_wait(rc->vconn, WAIT_RECV); + } +} + +/* There is no rconn_send_wait() function: an rconn has a send queue that it + * takes care of sending if you call rconn_wait(), which will have the side + * effect of waking up poll_block(). */ +int +rconn_send(struct rconn *rc, struct buffer *b) +{ + if (rc->vconn) { + if (rc->txq.n < rc->txq_limit) { + queue_push_tail(&rc->txq, b); + if (rc->txq.n == 1) { + try_send(rc); + } + return 0; + } else { + return EAGAIN; + } + } else { + return ENOTCONN; + } +} + +/* Returns 'rc''s name (the 'name' argument passed to rconn_new()). */ +const char * +rconn_get_name(const struct rconn *rc) +{ + return rc->name; +} + +/* Returns true if 'rconn' is connected or in the process of reconnecting, + * false if 'rconn' is disconnected and will not be reconnected. */ +bool +rconn_is_alive(const struct rconn *rconn) +{ + return rconn->reliable || rconn->vconn; +} + +static struct rconn * +create_rconn(const char *name, int txq_limit, struct vconn *vconn) +{ + struct rconn *rc = xmalloc(sizeof *rc); + assert(txq_limit > 0); + rc->reliable = vconn == NULL; + rc->name = xstrdup(name); + rc->vconn = vconn; + queue_init(&rc->txq); + rc->txq_limit = txq_limit; + rc->backoff_deadline = 0; + rc->backoff = 0; + return rc; +} + +/* Tries to send a packet from 'rc''s send buffer. Returns 0 if successful, + * otherwise a positive errno value. */ +static int +try_send(struct rconn *rc) +{ + int retval = 0; + struct buffer *next = rc->txq.head->next; + retval = vconn_send(rc->vconn, rc->txq.head); + if (retval) { + return retval; + } + queue_advance_head(&rc->txq, next); + return 0; +} + +/* Disconnects 'rc'. 'error' is used only for logging purposes. If it is + * nonzero, then it should be EOF to indicate the connection was closed by the + * peer in a normal fashion or a positive errno value. */ +static void +disconnect(struct rconn *rc, int error) +{ + time_t now = time(0); + + if (rc->vconn) { + if (error > 0) { + VLOG_WARN("%s: connection dropped (%s)", + rc->name, strerror(error)); + } else if (error == EOF) { + if (rc->reliable) { + VLOG_WARN("%s: connection closed", rc->name); + } + } else { + VLOG_WARN("%s: connection dropped", rc->name); + } + vconn_close(rc->vconn); + rc->vconn = NULL; + queue_clear(&rc->txq); + } + + if (now >= rc->backoff_deadline) { + rc->backoff = 1; + } else { + rc->backoff = MIN(60, MAX(1, 2 * rc->backoff)); + VLOG_WARN("%s: waiting %d seconds before reconnect\n", + rc->name, rc->backoff); + } + rc->backoff_deadline = now + rc->backoff; +} diff --git a/lib/vconn-netlink.c b/lib/vconn-netlink.c index 05c7ab07..ca1f93d5 100644 --- a/lib/vconn-netlink.c +++ b/lib/vconn-netlink.c @@ -72,16 +72,18 @@ netlink_open(const char *name, char *suffix, struct vconn **vconnp) { struct netlink_vconn *netlink; int dp_idx; + int subscribe; int retval; - if (sscanf(suffix, "%d", &dp_idx) != 1) { - fatal(0, "%s: bad peer name format", name); + subscribe = 1; + if (sscanf(suffix, "%d:%d", &dp_idx, &subscribe) < 1) { + fatal(0, "%s: syntax error", name); } netlink = xmalloc(sizeof *netlink); netlink->vconn.class = &netlink_vconn_class; netlink->vconn.connect_status = 0; - retval = dpif_open(dp_idx, true, &netlink->dp); + retval = dpif_open(dp_idx, subscribe, &netlink->dp); if (retval) { free(netlink); *vconnp = NULL; diff --git a/lib/vconn-ssl.c b/lib/vconn-ssl.c index 30d8caf6..2bdcfb1b 100644 --- a/lib/vconn-ssl.c +++ b/lib/vconn-ssl.c @@ -361,6 +361,11 @@ again: return EPROTO; } want_bytes = length - rx->size; + if (!want_bytes) { + *bufferp = rx; + sslv->rxbuf = NULL; + return 0; + } } buffer_reserve_tailroom(rx, want_bytes); diff --git a/lib/vconn-tcp.c b/lib/vconn-tcp.c index 1fe5919f..ea702605 100644 --- a/lib/vconn-tcp.c +++ b/lib/vconn-tcp.c @@ -191,6 +191,11 @@ again: return EPROTO; } want_bytes = length - rx->size; + if (!want_bytes) { + *bufferp = rx; + tcp->rxbuf = NULL; + return 0; + } } buffer_reserve_tailroom(rx, want_bytes); @@ -208,7 +213,12 @@ again: } return EAGAIN; } else if (retval == 0) { - return rx->size ? EPROTO : EOF; + if (rx->size) { + VLOG_ERR("connection dropped mid-packet"); + return EPROTO; + } else { + return EOF; + } } else { return retval ? errno : EAGAIN; } diff --git a/lib/vconn.c b/lib/vconn.c index a3314c57..f9c3ec00 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -40,6 +40,7 @@ #include #include "buffer.h" #include "flow.h" +#include "ofp-print.h" #include "openflow.h" #include "poll-loop.h" #include "util.h" @@ -79,6 +80,50 @@ check_vconn_classes(void) #endif } +/* Prints information on active (if 'active') and passive (if 'passive') + * connection methods supported by the vconn. */ +void +vconn_usage(bool active, bool passive) +{ + /* Really this should be implemented via callbacks into the vconn + * providers, but that seems too heavy-weight to bother with at the + * moment. */ + + printf("\n"); + if (active) { + printf("Active OpenFlow connection methods:\n"); +#ifdef HAVE_NETLINK + printf(" nl:DP_IDX " + "local datapath DP_IDX\n"); +#endif + printf(" tcp:HOST[:PORT] " + "PORT (default: %d) on remote TCP HOST\n", OFP_TCP_PORT); +#ifdef HAVE_OPENSSL + printf(" ssl:HOST[:PORT] " + "SSL PORT (default: %d) on remote HOST\n", OFP_SSL_PORT); +#endif + } + + if (passive) { + printf("Passive OpenFlow connection methods:\n"); + printf(" ptcp:[PORT] " + "listen to TCP PORT (default: %d)\n", + OFP_TCP_PORT); +#ifdef HAVE_OPENSSL + printf(" pssl:[PORT] " + "listen for SSL on PORT (default: %d)\n", + OFP_SSL_PORT); +#endif + } + +#ifdef HAVE_OPENSSL + printf("PKI 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"); +#endif +} + /* Attempts to connect to an OpenFlow device. 'name' is a connection name in * the form "TYPE:ARGS", where TYPE is the vconn class's name and ARGS are * vconn class-specific. @@ -209,6 +254,11 @@ vconn_recv(struct vconn *vconn, struct buffer **msgp) int retval = vconn_connect(vconn); if (!retval) { retval = (vconn->class->recv)(vconn, msgp); + if (VLOG_IS_DBG_ENABLED() && !retval) { + char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1); + VLOG_DBG("received: %s", s); + free(s); + } } if (retval) { *msgp = NULL; @@ -232,7 +282,16 @@ vconn_send(struct vconn *vconn, struct buffer *msg) { int retval = vconn_connect(vconn); if (!retval) { - retval = (vconn->class->send)(vconn, msg); + if (!VLOG_IS_DBG_ENABLED()) { + retval = (vconn->class->send)(vconn, msg); + } else { + char *s = ofp_to_string(msg->data, msg->size, 1); + retval = (vconn->class->send)(vconn, msg); + if (retval != EAGAIN) { + VLOG_DBG("sent (%s): %s", strerror(retval), s); + } + free(s); + } } return retval; } @@ -244,6 +303,20 @@ vconn_send_block(struct vconn *vconn, struct buffer *msg) int retval; while ((retval = vconn_send(vconn, msg)) == EAGAIN) { vconn_send_wait(vconn); + VLOG_DBG("blocking on vconn send"); + poll_block(); + } + return retval; +} + +/* Same as vconn_recv, except that it waits until a message is received. */ +int +vconn_recv_block(struct vconn *vconn, struct buffer **msgp) +{ + int retval; + while ((retval = vconn_recv(vconn, msgp)) == EAGAIN) { + vconn_recv_wait(vconn); + VLOG_DBG("blocking on vconn receive"); poll_block(); } return retval; diff --git a/lib/vlog.c b/lib/vlog.c index 3c977bf6..63162723 100644 --- a/lib/vlog.c +++ b/lib/vlog.c @@ -277,6 +277,16 @@ vlog_get_levels(void) return ds_cstr(&s); } +/* Returns true if a log message emitted for the given 'module' and 'level' + * would cause some log output, false if that module and level are completely + * disabled. */ +bool +vlog_is_enabled(enum vlog_module module, enum vlog_level level) +{ + return (levels[module][VLF_CONSOLE] >= level + || levels[module][VLF_SYSLOG] >= level); +} + /* Writes 'message' to the log at the given 'level' and as coming from the * given 'module'. * diff --git a/man/man8/secchan.8 b/man/man8/secchan.8 index 3f783319..b1eac3a9 100644 --- a/man/man8/secchan.8 +++ b/man/man8/secchan.8 @@ -24,11 +24,6 @@ proceeds to forward packets from one endpoint to the other. .BR \-h ", " \-\^\-help Prints a brief help message to the console. -.TP -.BR \-u ", " \-\^\-unreliable -Do not attempt to reconnect the channel if a connection drops. By -default, \fBsecchan\fR attempts to reconnect. - .TP .BR \-v ", " \-\^\-verbose Prints debug messages to the console. diff --git a/man/man8/switch.8 b/man/man8/switch.8 index 78f9e571..51638a36 100644 --- a/man/man8/switch.8 +++ b/man/man8/switch.8 @@ -76,11 +76,6 @@ the switch is connected to a trustworthy controller. .BR \-h ", " \-\^\-help Prints a brief help message to the console. -.TP -.BR \-u ", " \-\^\-unreliable -Do not attempt to reconnect the channel if a connection drops. By -default, \fBsecchan\fR attempts to reconnect. - .TP .BR \-v ", " \-\^\-verbose Prints debug messages to the console. diff --git a/secchan/Makefile.am b/secchan/Makefile.am index 13ddb044..882c98e7 100644 --- a/secchan/Makefile.am +++ b/secchan/Makefile.am @@ -1,6 +1,10 @@ include ../Make.vars +if HAVE_NETLINK bin_PROGRAMS = secchan +else +bin_PROGRAMS = +endif secchan_SOURCES = secchan.c secchan_LDADD = ../lib/libopenflow.la -ldl diff --git a/secchan/secchan.c b/secchan/secchan.c index 5d693781..8ac3d419 100644 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@ -43,12 +43,14 @@ #include "command-line.h" #include "compiler.h" #include "fault.h" +#include "list.h" #include "util.h" +#include "rconn.h" #include "vconn-ssl.h" -#include "vconn.h" #include "vlog-socket.h" #include "openflow.h" #include "poll-loop.h" +#include "vconn.h" #include "vlog.h" #define THIS_MODULE VLM_secchan @@ -56,24 +58,32 @@ static void parse_options(int argc, char *argv[]); static void usage(void) NO_RETURN; -static bool reliable = true; +static const char *listen_vconn_name; struct half { - const char *name; - struct vconn *vconn; + struct rconn *rconn; struct buffer *rxbuf; - time_t backoff_deadline; - int backoff; }; -static void reconnect(struct half *); +struct relay { + struct list node; + struct half halves[2]; +}; + +static struct list relays = LIST_INITIALIZER(&relays); + +static void new_management_connection(const char *nl_name, struct vconn *new_remote); +static void relay_create(struct rconn *, struct rconn *); +static void relay_run(struct relay *); +static void relay_wait(struct relay *); +static void relay_destroy(struct relay *); int main(int argc, char *argv[]) { - struct half halves[2]; + struct vconn *listen_vconn; + const char *nl_name; int retval; - int i; set_program_name(argv[0]); register_fault_handlers(); @@ -81,7 +91,26 @@ main(int argc, char *argv[]) parse_options(argc, argv); if (argc - optind != 2) { - fatal(0, "exactly two peer arguments required; use --help for usage"); + fatal(0, + "need exactly two non-option arguments; use --help for usage"); + } + nl_name = argv[optind]; + if (strncmp(nl_name, "nl:", 3) + || strlen(nl_name) < 4 + || nl_name[strspn(nl_name + 3, "0123456789") + 3]) { + fatal(0, "%s: argument is not of the form \"nl:DP_ID\"", nl_name); + } + + if (listen_vconn_name) { + retval = vconn_open(listen_vconn_name, &listen_vconn); + if (retval && retval != EAGAIN) { + fatal(retval, "opening %s", listen_vconn_name); + } + if (!vconn_is_passive(listen_vconn)) { + fatal(0, "%s is not a passive vconn", listen_vconn_name); + } + } else { + listen_vconn = NULL; } retval = vlog_server_listen(NULL, NULL); @@ -89,66 +118,34 @@ main(int argc, char *argv[]) fatal(retval, "Could not listen for vlog connections"); } - for (i = 0; i < 2; i++) { - halves[i].name = argv[optind + i]; - halves[i].vconn = NULL; - halves[i].rxbuf = NULL; - halves[i].backoff_deadline = 0; - halves[i].backoff = 1; - reconnect(&halves[i]); - } + relay_create(rconn_new(argv[optind], 1), rconn_new(argv[optind + 1], 1)); for (;;) { - /* Do some work. Limit the number of iterations so that callbacks - * registered with the poll loop don't starve. */ - int iteration; - for (iteration = 0; iteration < 50; iteration++) { - bool progress = false; - for (i = 0; i < 2; i++) { - struct half *this = &halves[i]; - struct half *peer = &halves[!i]; - - if (!this->rxbuf) { - retval = vconn_recv(this->vconn, &this->rxbuf); - if (retval && retval != EAGAIN) { - if (retval == EOF) { - VLOG_DBG("%s: connection closed by remote host", - this->name); - } else { - VLOG_DBG("%s: recv: closing connection: %s", - this->name, strerror(retval)); - } - reconnect(this); - break; - } - } + struct relay *r, *n; - if (this->rxbuf) { - retval = vconn_send(peer->vconn, this->rxbuf); - if (!retval) { - this->rxbuf = NULL; - progress = true; - } else if (retval != EAGAIN) { - VLOG_DBG("%s: send: closing connection: %s", - peer->name, strerror(retval)); - reconnect(peer); - break; + /* Do work. */ + LIST_FOR_EACH_SAFE (r, n, struct relay, node, &relays) { + relay_run(r); + } + if (listen_vconn) { + for (;;) { + struct vconn *new_remote; + retval = vconn_accept(listen_vconn, &new_remote); + if (retval) { + if (retval != EAGAIN) { + VLOG_WARN("accept failed (%s)", strerror(retval)); } + break; } - } - if (!progress) { - break; + new_management_connection(nl_name, new_remote); } } /* Wait for something to happen. */ - for (i = 0; i < 2; i++) { - struct half *this = &halves[i]; - struct half *peer = &halves[!i]; - if (!this->rxbuf) { - vconn_recv_wait(this->vconn); - } else { - vconn_send_wait(peer->vconn); - } + LIST_FOR_EACH (r, struct relay, node, &relays) { + relay_wait(r); + } + if (listen_vconn) { + vconn_accept_wait(listen_vconn); } poll_block(); } @@ -157,85 +154,150 @@ main(int argc, char *argv[]) } static void -reconnect(struct half *this) +new_management_connection(const char *nl_name, struct vconn *new_remote) { - if (this->vconn != NULL) { - if (!reliable) { - fatal(0, "%s: connection dropped", this->name); - } + char *nl_name_without_subscription; + struct vconn *new_local; + struct rconn *r1, *r2; + int retval; - VLOG_WARN("%s: connection dropped, reconnecting", this->name); - vconn_close(this->vconn); - this->vconn = NULL; - buffer_delete(this->rxbuf); - this->rxbuf = NULL; + /* nl:123 or nl:123:1 opens a netlink connection to local datapath 123. We + * only accept the former syntax in main(). + * + * nl:123:0 opens a netlink connection to local datapath 123 without + * obtaining a subscription for ofp_packet_in or ofp_flow_expired + * messages.*/ + nl_name_without_subscription = xasprintf("%s:0", nl_name); + retval = vconn_open(nl_name_without_subscription, &new_local); + if (retval) { + VLOG_ERR("could not connect to %s (%s)", + nl_name_without_subscription, strerror(retval)); + vconn_close(new_remote); + return; } + free(nl_name_without_subscription); - for (;;) { - time_t now = time(0); - int retval; - - if (now >= this->backoff_deadline) { - this->backoff = 1; - } else { - this->backoff *= 2; - if (this->backoff > 60) { - this->backoff = 60; + /* Add it to the relay list. */ + r1 = rconn_new_from_vconn(nl_name_without_subscription, 1, new_local); + r2 = rconn_new_from_vconn("passive", 1, new_remote); + relay_create(r1, r2); +} + +static void +relay_create(struct rconn *a, struct rconn *b) +{ + struct relay *r; + int i; + + r = xmalloc(sizeof *r); + for (i = 0; i < 2; i++) { + r->halves[i].rconn = i ? b : a; + r->halves[i].rxbuf = NULL; + } + list_push_back(&relays, &r->node); +} + +static void +relay_run(struct relay *r) +{ + int iteration; + int i; + + for (i = 0; i < 2; i++) { + rconn_run(r->halves[i].rconn); + } + + /* Limit the number of iterations to prevent other tasks from starving. */ + for (iteration = 0; iteration < 50; iteration++) { + bool progress = false; + for (i = 0; i < 2; i++) { + struct half *this = &r->halves[i]; + struct half *peer = &r->halves[!i]; + + if (!this->rxbuf) { + this->rxbuf = rconn_recv(this->rconn); } - VLOG_WARN("%s: waiting %d seconds before reconnect\n", - this->name, (int) (this->backoff_deadline - now)); - poll_timer_wait((this->backoff_deadline - now) * 1000); - poll_block(); - } - retval = vconn_open_block(this->name, &this->vconn); - if (!retval) { - VLOG_WARN("%s: connected", this->name); - if (vconn_is_passive(this->vconn)) { - fatal(0, "%s: passive vconn not supported in control path", - this->name); + if (this->rxbuf) { + int retval = rconn_send(peer->rconn, this->rxbuf); + if (retval != EAGAIN) { + this->rxbuf = NULL; + if (!retval) { + progress = true; + } + } } - this->backoff_deadline = now + this->backoff; + } + if (!progress) { + break; + } + } + + for (i = 0; i < 2; i++) { + struct half *this = &r->halves[i]; + if (!rconn_is_alive(this->rconn)) { + relay_destroy(r); return; } + } +} - if (!reliable) { - fatal(0, "%s: connection failed", this->name); +static void +relay_wait(struct relay *r) +{ + int i; + + for (i = 0; i < 2; i++) { + struct half *this = &r->halves[i]; + + rconn_run_wait(this->rconn); + if (!this->rxbuf) { + rconn_recv_wait(this->rconn); } - VLOG_WARN("%s: connection failed (%s)", this->name, strerror(retval)); - this->backoff_deadline = time(0) + this->backoff; } } +static void +relay_destroy(struct relay *r) +{ + int i; + + list_remove(&r->node); + for (i = 0; i < 2; i++) { + struct half *this = &r->halves[i]; + rconn_destroy(this->rconn); + buffer_delete(this->rxbuf); + } + free(r); +} + static void parse_options(int argc, char *argv[]) { static struct option long_options[] = { - {"unreliable", no_argument, 0, 'u'}, + {"listen", required_argument, 0, 'l'}, {"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 + VCONN_SSL_LONG_OPTIONS {0, 0, 0, 0}, }; char *short_options = long_options_to_short_options(long_options); for (;;) { - int indexptr; int c; - c = getopt_long(argc, argv, short_options, long_options, &indexptr); + c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { - case 'u': - reliable = false; + case 'l': + if (listen_vconn_name) { + fatal(0, "-l or --listen may be only specified once"); + } + listen_vconn_name = optarg; break; case 'h': @@ -249,19 +311,7 @@ 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 + VCONN_SSL_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); @@ -276,25 +326,14 @@ parse_options(int argc, char *argv[]) static void usage(void) { - printf("%s: Secure Channel\n" + printf("%s: Secure Channel, a relay for OpenFlow messages.\n" "usage: %s [OPTIONS] LOCAL REMOTE\n" - "\nRelays OpenFlow message between LOCAL and REMOTE datapaths.\n" - "LOCAL and REMOTE must each be 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 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 + "where LOCAL and REMOTE are active OpenFlow connection methods.\n", + program_name, program_name); + vconn_usage(true, true); printf("\nNetworking options:\n" - " -u, --unreliable do not reconnect after connections drop\n" + " -l, --listen=METHOD allow management connections on METHOD\n" + " (a passive OpenFlow connection method)\n" "\nOther options:\n" " -v, --verbose set maximum verbosity level\n" " -h, --help display this help message\n" diff --git a/switch/Makefile.am b/switch/Makefile.am index 24c23891..eb9931b0 100644 --- a/switch/Makefile.am +++ b/switch/Makefile.am @@ -5,8 +5,6 @@ bin_PROGRAMS = switch switch_SOURCES = \ chain.c \ chain.h \ - controller.c \ - controller.h \ crc32.c \ crc32.h \ datapath.c \ diff --git a/switch/chain.c b/switch/chain.c index e2ac53c6..2a19df02 100644 --- a/switch/chain.c +++ b/switch/chain.c @@ -41,13 +41,6 @@ #define THIS_MODULE VLM_chain #include "vlog.h" -/* Set of tables chained together in sequence from cheap to expensive. */ -#define CHAIN_MAX_TABLES 4 -struct sw_chain { - int n_tables; - struct sw_table *tables[CHAIN_MAX_TABLES]; -}; - /* Attempts to append 'table' to the set of tables in 'chain'. Returns 0 or * negative error. If 'table' is null it is assumed that table creation failed * due to out-of-memory. */ diff --git a/switch/chain.h b/switch/chain.h index 3c930aa7..2d751d03 100644 --- a/switch/chain.h +++ b/switch/chain.h @@ -43,6 +43,13 @@ struct list; #define TABLE_MAC_MAX_FLOWS 1024 #define TABLE_MAC_NUM_BUCKETS 1024 +/* Set of tables chained together in sequence from cheap to expensive. */ +#define CHAIN_MAX_TABLES 4 +struct sw_chain { + int n_tables; + struct sw_table *tables[CHAIN_MAX_TABLES]; +}; + struct sw_chain *chain_create(void); struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *); int chain_insert(struct sw_chain *, struct sw_flow *); diff --git a/switch/controller.c b/switch/controller.c deleted file mode 100644 index d8152311..00000000 --- a/switch/controller.c +++ /dev/null @@ -1,208 +0,0 @@ -/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford - * Junior University - * - * We are making the OpenFlow specification and associated documentation - * (Software) available for public use and benefit with the expectation - * that others will use, modify and enhance the Software and contribute - * those enhancements back to the community. However, since we would - * like to make the Software available for broadest use, with as few - * restrictions as possible permission is hereby granted, free of - * charge, to any person obtaining a copy of this Software to deal in - * the Software under the copyrights without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The name and trademarks of copyright holder(s) may NOT be used in - * advertising or publicity pertaining to the Software or any - * derivatives without specific, written prior permission. - */ - -#include "controller.h" -#include -#include -#include "buffer.h" -#include "poll-loop.h" -#include "ofp-print.h" -#include "util.h" -#include "vconn.h" - -#define THIS_MODULE VLM_controller_connection -#include "vlog.h" - -struct controller_connection { - bool reliable; - const char *name; - struct vconn *vconn; - bool connected; - struct queue txq; - time_t backoff_deadline; - int backoff; -}; - -struct controller_connection * -controller_new(const char *name, bool reliable) -{ - struct controller_connection *cc = xmalloc(sizeof *cc); - cc->reliable = reliable; - cc->name = name; - cc->vconn = NULL; - queue_init(&cc->txq); - cc->backoff_deadline = 0; - cc->backoff = 0; - return cc; -} - -static int -try_send(struct controller_connection *cc) -{ - int retval = 0; - struct buffer *next = cc->txq.head->next; - retval = vconn_send(cc->vconn, cc->txq.head); - if (retval) { - return retval; - } - queue_advance_head(&cc->txq, next); - return 0; -} - -void -controller_run(struct controller_connection *cc) -{ - if (!cc->vconn) { - if (time(0) >= cc->backoff_deadline) { - int retval; - - retval = vconn_open(cc->name, &cc->vconn); - if (!retval) { - cc->backoff_deadline = time(0) + cc->backoff; - cc->connected = false; - } else { - VLOG_WARN("%s: connection failed (%s)", - cc->name, strerror(retval)); - controller_disconnect(cc, 0); - } - } - } else if (!cc->connected) { - int error = vconn_connect(cc->vconn); - if (!error) { - VLOG_WARN("%s: connected", cc->name); - if (vconn_is_passive(cc->vconn)) { - fatal(0, "%s: passive vconn not supported in switch", - cc->name); - } - cc->connected = true; - } else if (error != EAGAIN) { - VLOG_WARN("%s: connection failed (%s)", - cc->name, strerror(error)); - controller_disconnect(cc, 0); - } - } else { - while (cc->txq.n > 0) { - int error = try_send(cc); - if (error == EAGAIN) { - break; - } else if (error) { - controller_disconnect(cc, error); - return; - } - } - } -} - -void -controller_run_wait(struct controller_connection *cc) -{ - if (cc->vconn) { - if (cc->txq.n) { - vconn_wait(cc->vconn, WAIT_SEND); - } - } else { - poll_timer_wait((cc->backoff_deadline - time(0)) * 1000); - } -} - -void -controller_disconnect(struct controller_connection *cc, int error) -{ - time_t now = time(0); - - if (cc->vconn) { - if (!cc->reliable) { - fatal(0, "%s: connection dropped", cc->name); - } - - if (error > 0) { - VLOG_WARN("%s: connection dropped (%s)", - cc->name, strerror(error)); - } else if (error == EOF) { - VLOG_WARN("%s: connection closed", cc->name); - } else { - VLOG_WARN("%s: connection dropped", cc->name); - } - vconn_close(cc->vconn); - cc->vconn = NULL; - queue_clear(&cc->txq); - } - - if (now >= cc->backoff_deadline) { - cc->backoff = 1; - } else { - cc->backoff = MIN(60, MAX(1, 2 * cc->backoff)); - VLOG_WARN("%s: waiting %d seconds before reconnect\n", - cc->name, cc->backoff); - } - cc->backoff_deadline = now + cc->backoff; -} - -struct buffer * -controller_recv(struct controller_connection *cc) -{ - if (cc->vconn && cc->connected) { - struct buffer *buffer; - int error = vconn_recv(cc->vconn, &buffer); - if (!error) { - return buffer; - } else if (error != EAGAIN) { - controller_disconnect(cc, error); - } - } - return NULL; -} - -void -controller_recv_wait(struct controller_connection *cc) -{ - if (cc->vconn) { - vconn_wait(cc->vconn, WAIT_RECV); - } -} - -void -controller_send(struct controller_connection *cc, struct buffer *b) -{ - if (cc->vconn) { - if (cc->txq.n < 128) { - queue_push_tail(&cc->txq, b); - if (cc->txq.n == 1) { - try_send(cc); - } - } else { - VLOG_WARN("%s: controller queue overflow", cc->name); - buffer_delete(b); - } - } -} diff --git a/switch/controller.h b/switch/controller.h deleted file mode 100644 index 6c38dd11..00000000 --- a/switch/controller.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford - * Junior University - * - * We are making the OpenFlow specification and associated documentation - * (Software) available for public use and benefit with the expectation - * that others will use, modify and enhance the Software and contribute - * those enhancements back to the community. However, since we would - * like to make the Software available for broadest use, with as few - * restrictions as possible permission is hereby granted, free of - * charge, to any person obtaining a copy of this Software to deal in - * the Software under the copyrights without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The name and trademarks of copyright holder(s) may NOT be used in - * advertising or publicity pertaining to the Software or any - * derivatives without specific, written prior permission. - */ - -#ifndef CONTROLLER_H -#define CONTROLLER_H 1 - -#include "queue.h" -#include -#include - -struct controller_connection *controller_new(const char *name, bool reliable); -void controller_run(struct controller_connection *); -void controller_run_wait(struct controller_connection *); -void controller_connect(struct controller_connection *); -void controller_disconnect(struct controller_connection *, int error); -struct buffer *controller_recv(struct controller_connection *); -void controller_recv_wait(struct controller_connection *); -void controller_send(struct controller_connection *, struct buffer *); - -#endif /* controller.h */ diff --git a/switch/datapath.c b/switch/datapath.c index ffe578af..0d4ffa77 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -39,11 +39,12 @@ #include #include "buffer.h" #include "chain.h" -#include "controller.h" #include "flow.h" #include "netdev.h" #include "packets.h" #include "poll-loop.h" +#include "rconn.h" +#include "vconn.h" #include "table.h" #include "xtoxll.h" @@ -70,10 +71,26 @@ struct sw_port { struct datapath *dp; struct netdev *netdev; struct list node; /* Element in datapath.ports. */ + unsigned long long int rx_count, tx_count, drop_count; +}; + +/* A connection to a controller or a management device. */ +struct remote { + struct list node; + struct rconn *rconn; +}; + +/* The origin of a received OpenFlow message, to enable sending a reply. */ +struct sender { + struct remote *remote; /* The device that sent the message. */ + uint32_t xid; /* The OpenFlow transaction ID. */ }; struct datapath { - struct controller_connection *cc; + /* Remote connections. */ + struct remote *controller; /* Connection to controller. */ + struct list remotes; /* All connections (including controller). */ + struct vconn *listen_vconn; time_t last_timeout; @@ -82,20 +99,20 @@ 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]; struct list port_list; /* List of ports, for flooding. */ }; +static struct remote *remote_create(struct datapath *, struct rconn *); +static void remote_run(struct datapath *, struct remote *); +static void remote_wait(struct remote *); +static void remote_destroy(struct remote *); + 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); @@ -124,7 +141,8 @@ static void modify_th(struct buffer *buffer, uint16_t eth_proto, #define PKT_COOKIE_BITS (32 - PKT_BUFFER_BITS) void fwd_port_input(struct datapath *, struct buffer *, int in_port); -int fwd_control_input(struct datapath *, const void *, size_t); +int fwd_control_input(struct datapath *, const struct sender *, + const void *, size_t); uint32_t save_buffer(struct buffer *); static struct buffer *retrieve_buffer(uint32_t id); @@ -157,7 +175,7 @@ gen_datapath_id(void) } int -dp_new(struct datapath **dp_, uint64_t dpid, struct controller_connection *cc) +dp_new(struct datapath **dp_, uint64_t dpid, struct rconn *rconn) { struct datapath *dp; @@ -167,7 +185,9 @@ dp_new(struct datapath **dp_, uint64_t dpid, struct controller_connection *cc) } dp->last_timeout = time(0); - dp->cc = cc; + list_init(&dp->remotes); + dp->controller = remote_create(dp, rconn); + dp->listen_vconn = NULL; dp->id = dpid <= UINT64_C(0xffffffffffff) ? dpid : gen_datapath_id(); dp->chain = chain_create(); if (!dp->chain) { @@ -177,7 +197,8 @@ dp_new(struct datapath **dp_, uint64_t dpid, struct controller_connection *cc) } list_init(&dp->port_list); - dp->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; + dp->config.flags = 0; + dp->config.miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN); *dp_ = dp; return 0; } @@ -204,6 +225,9 @@ dp_add_port(struct datapath *dp, const char *name) p->dp = dp; p->netdev = netdev; + p->tx_count = 0; + p->rx_count = 0; + p->drop_count = 0; list_push_back(&dp->port_list, &p->node); /* Notify the ctlpath that this port has been added */ @@ -212,13 +236,20 @@ dp_add_port(struct datapath *dp, const char *name) return 0; } +void +dp_add_listen_vconn(struct datapath *dp, struct vconn *listen_vconn) +{ + assert(!dp->listen_vconn); + dp->listen_vconn = listen_vconn; +} + void dp_run(struct datapath *dp) { time_t now = time(0); - struct sw_port *p, *n; + struct sw_port *p, *pn; + struct remote *r, *rn; struct buffer *buffer = NULL; - int i; if (now != dp->last_timeout) { struct list deleted = LIST_INITIALIZER(&deleted); @@ -234,7 +265,7 @@ dp_run(struct datapath *dp) } poll_timer_wait(1000); - LIST_FOR_EACH_SAFE (p, n, struct sw_port, node, &dp->port_list) { + LIST_FOR_EACH_SAFE (p, pn, struct sw_port, node, &dp->port_list) { int error; if (!buffer) { @@ -249,6 +280,7 @@ dp_run(struct datapath *dp) } error = netdev_recv(p->netdev, buffer); if (!error) { + p->rx_count++; fwd_port_input(dp, buffer, port_no(dp, p)); buffer = NULL; } else if (error != EAGAIN) { @@ -259,29 +291,104 @@ dp_run(struct datapath *dp) } buffer_delete(buffer); - /* Process a number of commands from the controller, but cap it at a + /* Talk to remotes. */ + LIST_FOR_EACH_SAFE (r, rn, struct remote, node, &dp->remotes) { + remote_run(dp, r); + } + if (dp->listen_vconn) { + for (;;) { + struct vconn *new_vconn; + int retval; + + retval = vconn_accept(dp->listen_vconn, &new_vconn); + if (retval) { + if (retval != EAGAIN) { + VLOG_WARN("accept failed (%s)", strerror(retval)); + } + break; + } + remote_create(dp, rconn_new_from_vconn("passive", 128, new_vconn)); + } + } +} + +static void +remote_run(struct datapath *dp, struct remote *r) +{ + int i; + + rconn_run(r->rconn); + + /* Process a number of commands from the remote, but cap them at a * reasonable number so that other processing doesn't starve. */ for (i = 0; i < 50; i++) { - struct buffer *buffer = controller_recv(dp->cc); + struct buffer *buffer; + struct ofp_header *oh; + + buffer = rconn_recv(r->rconn); if (!buffer) { break; } - fwd_control_input(dp, buffer->data, buffer->size); + + if (buffer->size >= sizeof *oh) { + struct sender sender; + + oh = buffer->data; + sender.remote = r; + sender.xid = oh->xid; + fwd_control_input(dp, &sender, buffer->data, buffer->size); + } else { + VLOG_WARN("received too-short OpenFlow message"); + } buffer_delete(buffer); } - controller_run(dp->cc); + if (!rconn_is_alive(r->rconn)) { + remote_destroy(r); + } +} + +static void +remote_wait(struct remote *r) +{ + rconn_run_wait(r->rconn); + rconn_recv_wait(r->rconn); +} + +static void +remote_destroy(struct remote *r) +{ + if (r) { + list_remove(&r->node); + rconn_destroy(r->rconn); + free(r); + } +} + +static struct remote * +remote_create(struct datapath *dp, struct rconn *rconn) +{ + struct remote *remote = xmalloc(sizeof *remote); + list_push_back(&dp->remotes, &remote->node); + remote->rconn = rconn; + return remote; } void dp_wait(struct datapath *dp) { struct sw_port *p; + struct remote *r; LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) { netdev_recv_wait(p->netdev); } - controller_recv_wait(dp->cc); + LIST_FOR_EACH (r, struct remote, node, &dp->remotes) { + remote_wait(r); + } + if (dp->listen_vconn) { + vconn_accept_wait(dp->listen_vconn); + } } /* Delete 'p' from switch. */ @@ -340,7 +447,11 @@ output_packet(struct datapath *dp, struct buffer *buffer, int out_port) if (out_port >= 0 && out_port < OFPP_MAX) { struct sw_port *p = &dp->ports[out_port]; if (p->netdev != NULL) { - netdev_send(p->netdev, buffer); + if (!netdev_send(p->netdev, buffer)) { + p->tx_count++; + } else { + p->drop_count++; + } return; } } @@ -367,11 +478,49 @@ dp_output_port(struct datapath *dp, struct buffer *buffer, } } -/* Takes ownership of 'buffer' and transmits it to 'dp''s controller. If - * 'buffer_id' != -1, then only the first 64 bytes of 'buffer' are sent; - * otherwise, all of 'buffer' is sent. 'reason' indicates why 'buffer' is - * being sent. 'max_len' sets the maximum number of bytes that the caller wants - * to be sent; a value of 0 indicates the entire packet should be sent. */ +static void * +alloc_openflow_buffer(struct datapath *dp, size_t openflow_len, uint8_t type, + const struct sender *sender, struct buffer **bufferp) +{ + struct buffer *buffer; + struct ofp_header *oh; + + buffer = *bufferp = buffer_new(openflow_len); + oh = buffer_put_uninit(buffer, openflow_len); + oh->version = OFP_VERSION; + oh->type = type; + oh->length = 0; /* Filled in by send_openflow_buffer(). */ + oh->xid = sender ? sender->xid : 0; + return oh; +} + +static int +send_openflow_buffer(struct datapath *dp, struct buffer *buffer, + const struct sender *sender) +{ + struct remote *remote = sender ? sender->remote : dp->controller; + struct rconn *rconn = remote->rconn; + struct ofp_header *oh; + int retval; + + oh = buffer_at_assert(buffer, 0, sizeof *oh); + oh->length = htons(buffer->size); + + retval = rconn_send(rconn, buffer); + if (retval) { + VLOG_WARN("send to %s failed: %s", + rconn_get_name(rconn), strerror(retval)); + buffer_delete(buffer); + } + return retval; +} + +/* Takes ownership of 'buffer' and transmits it to 'dp''s controller. If the + * packet can be saved in a buffer, then only the first max_len bytes of + * 'buffer' are sent; otherwise, all of 'buffer' is sent. 'reason' indicates + * why 'buffer' is being sent. 'max_len' sets the maximum number of bytes that + * the caller wants to be sent; a value of 0 indicates the entire packet should + * be sent. */ void dp_output_control(struct datapath *dp, struct buffer *buffer, int in_port, size_t max_len, int reason) @@ -382,7 +531,7 @@ dp_output_control(struct datapath *dp, struct buffer *buffer, int in_port, buffer_id = save_buffer(buffer); total_len = buffer->size; - if (buffer_id != UINT32_MAX && max_len > buffer->size) { + if (buffer_id != UINT32_MAX && buffer->size > max_len) { buffer->size = max_len; } @@ -396,7 +545,7 @@ dp_output_control(struct datapath *dp, struct buffer *buffer, int in_port, opi->in_port = htons(in_port); opi->reason = reason; opi->pad = 0; - controller_send(dp->cc, buffer); + send_openflow_buffer(dp, buffer, NULL); } static void fill_port_desc(struct datapath *dp, struct sw_port *p, @@ -412,37 +561,30 @@ 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, const struct sender *sender) { 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); + ofr = alloc_openflow_buffer(dp, sizeof *ofr, OFPT_FEATURES_REPLY, + sender, &buffer); + 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); - controller_send(dp->cc, buffer); + send_openflow_buffer(dp, buffer, sender); } void @@ -465,15 +607,11 @@ send_port_status(struct sw_port *p, uint8_t status) { struct buffer *buffer; struct ofp_port_status *ops; - buffer = buffer_new(sizeof *ops); - ops = buffer_put_uninit(buffer, sizeof *ops); - ops->header.version = OFP_VERSION; - ops->header.type = OFPT_PORT_STATUS; - ops->header.length = htons(sizeof(*ops)); - ops->header.xid = htonl(0); + ops = alloc_openflow_buffer(p->dp, sizeof *ops, OFPT_PORT_STATUS, NULL, + &buffer); ops->reason = status; fill_port_desc(p->dp, p, &ops->desc); - controller_send(p->dp->cc, buffer); + send_openflow_buffer(p->dp, buffer, NULL); } void @@ -481,17 +619,129 @@ send_flow_expired(struct datapath *dp, struct sw_flow *flow) { struct buffer *buffer; struct ofp_flow_expired *ofe; - buffer = buffer_new(sizeof *ofe); - ofe = buffer_put_uninit(buffer, sizeof *ofe); - 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_buffer(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, NULL, + &buffer); flow_fill_match(&ofe->match, &flow->key); ofe->duration = htonl(flow->timeout - flow->max_idle - flow->created); ofe->packet_count = htonll(flow->packet_count); ofe->byte_count = htonll(flow->byte_count); - controller_send(dp->cc, buffer); + send_openflow_buffer(dp, buffer, NULL); +} + +static void +fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow, + int table_idx, time_t now) +{ + ofs->match.wildcards = htons(flow->key.wildcards); + ofs->match.in_port = flow->key.flow.in_port; + memcpy(ofs->match.dl_src, flow->key.flow.dl_src, ETH_ADDR_LEN); + memcpy(ofs->match.dl_dst, flow->key.flow.dl_dst, ETH_ADDR_LEN); + ofs->match.dl_vlan = flow->key.flow.dl_vlan; + ofs->match.dl_type = flow->key.flow.dl_type; + ofs->match.nw_src = flow->key.flow.nw_src; + ofs->match.nw_dst = flow->key.flow.nw_dst; + ofs->match.nw_proto = flow->key.flow.nw_proto; + memset(ofs->match.pad, 0, sizeof ofs->match.pad); + ofs->match.tp_src = flow->key.flow.tp_src; + ofs->match.tp_dst = flow->key.flow.tp_dst; + ofs->duration = htonl(now - flow->created); + ofs->table_id = table_idx; + ofs->packet_count = htonll(flow->packet_count); + ofs->byte_count = htonll(flow->byte_count); +} + +int +dp_send_flow_stats(struct datapath *dp, const struct sender *sender, + const struct ofp_match *match) +{ + struct buffer *buffer; + struct ofp_flow_stat_reply *fsr; + size_t header_size, fudge, flow_size; + struct sw_flow_key match_key; + int table_idx, n_flows, max_flows; + time_t now; + + header_size = offsetof(struct ofp_flow_stat_reply, flows); + fudge = 128; + flow_size = sizeof fsr->flows[0]; + max_flows = (65536 - header_size - fudge) / flow_size; + fsr = alloc_openflow_buffer(dp, header_size, + OFPT_FLOW_STAT_REPLY, sender, &buffer); + + n_flows = 0; + flow_extract_match(&match_key, match); + now = time(0); + for (table_idx = 0; table_idx < dp->chain->n_tables; table_idx++) { + struct sw_table *table = dp->chain->tables[table_idx]; + struct swt_iterator iter; + + if (n_flows >= max_flows) { + break; + } + + if (!table->iterator(table, &iter)) { + printf("iterator failed for table %d\n", table_idx); + continue; + } + + for (; iter.flow; table->iterator_next(&iter)) { + if (flow_matches(&match_key, &iter.flow->key)) { + struct ofp_flow_stats *ofs = buffer_put_uninit(buffer, + sizeof *ofs); + fill_flow_stats(ofs, iter.flow, table_idx, now); + if (++n_flows >= max_flows) { + break; + } + } + } + table->iterator_destroy(&iter); + } + return send_openflow_buffer(dp, buffer, sender); +} + +int +dp_send_port_stats(struct datapath *dp, const struct sender *sender) +{ + struct buffer *buffer; + struct ofp_port_stat_reply *psr; + struct sw_port *p; + + psr = alloc_openflow_buffer(dp, offsetof(struct ofp_port_stat_reply, + ports), + OFPT_PORT_STAT_REPLY, sender, &buffer); + LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) { + struct ofp_port_stats *ps = buffer_put_uninit(buffer, sizeof *ps); + ps->port_no = htons(port_no(dp, p)); + memset(ps->pad, 0, sizeof ps->pad); + ps->rx_count = htonll(p->rx_count); + ps->tx_count = htonll(p->tx_count); + ps->drop_count = htonll(p->drop_count); + } + return send_openflow_buffer(dp, buffer, sender); +} + +int +dp_send_table_stats(struct datapath *dp, const struct sender *sender) +{ + struct buffer *buffer; + struct ofp_table_stat_reply *tsr; + int i; + + tsr = alloc_openflow_buffer(dp, offsetof(struct ofp_table_stat_reply, + tables), + OFPT_TABLE_STAT_REPLY, sender, &buffer); + for (i = 0; i < dp->chain->n_tables; i++) { + struct ofp_table_stats *ots = buffer_put_uninit(buffer, sizeof *ots); + struct sw_table_stats stats; + dp->chain->tables[i]->stats(dp->chain->tables[i], &stats); + strncpy(ots->name, stats.name, sizeof ots->name); + ots->table_id = i; + ots->pad[0] = ots->pad[1] = 0; + ots->max_entries = htonl(stats.max_flows); + ots->active_count = htonl(stats.n_flows); + ots->matched_count = htonll(0); /* FIXME */ + } + return send_openflow_buffer(dp, buffer, sender); } /* 'buffer' was received on 'in_port', a physical switch port between 0 and @@ -509,7 +759,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, ntohs(dp->config.miss_send_len), OFPR_NO_MATCH); } } @@ -711,25 +961,43 @@ modify_vlan(struct buffer *buffer, } static int -recv_control_hello(struct datapath *dp, const void *msg) +recv_features_request(struct datapath *dp, const struct sender *sender, + const void *msg) { - const struct ofp_control_hello *och = msg; + dp_send_features_reply(dp, sender); + return 0; +} - printf("control_hello(version=%d)\n", ntohl(och->version)); +static int +recv_get_config_request(struct datapath *dp, const struct sender *sender, + const void *msg) +{ + struct buffer *buffer; + struct ofp_switch_config *osc; - if (ntohs(och->miss_send_len) != OFP_MISS_SEND_LEN_UNCHANGED) { - dp->miss_send_len = ntohs(och->miss_send_len); - } + osc = alloc_openflow_buffer(dp, sizeof *osc, OFPT_GET_CONFIG_REPLY, + sender, &buffer); - dp->hello_flags = ntohs(och->flags); + assert(sizeof *osc == sizeof dp->config); + memcpy(((char *)osc) + sizeof osc->header, + ((char *)&dp->config) + sizeof dp->config.header, + sizeof dp->config - sizeof dp->config.header); - dp_send_hello(dp); + return send_openflow_buffer(dp, buffer, sender); +} +static int +recv_set_config(struct datapath *dp, const struct sender *sender UNUSED, + const void *msg) +{ + const struct ofp_switch_config *osc = msg; + dp->config = *osc; return 0; } static int -recv_packet_out(struct datapath *dp, const void *msg) +recv_packet_out(struct datapath *dp, const struct sender *sender UNUSED, + const void *msg) { const struct ofp_packet_out *opo = msg; @@ -760,7 +1028,8 @@ recv_packet_out(struct datapath *dp, const void *msg) } static int -recv_port_mod(struct datapath *dp, const void *msg) +recv_port_mod(struct datapath *dp, const struct sender *sender UNUSED, + const void *msg) { const struct ofp_port_mod *opm = msg; @@ -829,7 +1098,8 @@ error: } static int -recv_flow(struct datapath *dp, const void *msg) +recv_flow(struct datapath *dp, const struct sender *sender UNUSED, + const void *msg) { const struct ofp_flow_mod *ofm = msg; uint16_t command = ntohs(ofm->command); @@ -849,21 +1119,56 @@ recv_flow(struct datapath *dp, const void *msg) } } +static int +recv_flow_status_request(struct datapath *dp, const struct sender *sender, + const void *msg) +{ + const struct ofp_flow_stat_request *fsr = msg; + if (fsr->type == OFPFS_INDIV) { + return dp_send_flow_stats(dp, sender, &fsr->match); + } else { + /* FIXME */ + return -ENOSYS; + } +} + +static int +recv_port_status_request(struct datapath *dp, const struct sender *sender, + const void *msg) +{ + return dp_send_port_stats(dp, sender); +} + +static int +recv_table_status_request(struct datapath *dp, const struct sender *sender, + const void *msg) +{ + return dp_send_table_stats(dp, sender); +} + /* 'msg', which is 'length' bytes long, was received from the control path. * Apply it to 'chain'. */ int -fwd_control_input(struct datapath *dp, const void *msg, size_t length) +fwd_control_input(struct datapath *dp, const struct sender *sender, + const void *msg, size_t length) { - struct openflow_packet { size_t min_size; - int (*handler)(struct datapath *, const void *); + int (*handler)(struct datapath *, const struct sender *, const void *); }; 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), @@ -877,14 +1182,23 @@ fwd_control_input(struct datapath *dp, const void *msg, size_t length) sizeof (struct ofp_port_mod), recv_port_mod, }, + [OFPT_FLOW_STAT_REQUEST] = { + sizeof (struct ofp_flow_stat_request), + recv_flow_status_request, + }, + [OFPT_PORT_STAT_REQUEST] = { + sizeof (struct ofp_port_stat_request), + recv_port_status_request, + }, + [OFPT_TABLE_STAT_REQUEST] = { + sizeof (struct ofp_table_stat_request), + recv_table_status_request, + }, }; const struct openflow_packet *pkt; struct ofp_header *oh; - if (length < sizeof(struct ofp_header)) - return -EINVAL; - oh = (struct ofp_header *) msg; if (oh->version != 1 || oh->type >= ARRAY_SIZE(packets) || ntohs(oh->length) > length) @@ -896,7 +1210,7 @@ fwd_control_input(struct datapath *dp, const void *msg, size_t length) if (length < pkt->min_size) return -EFAULT; - return pkt->handler(dp, msg); + return pkt->handler(dp, sender, msg); } /* Packet buffering. */ diff --git a/switch/datapath.h b/switch/datapath.h index 6d52360d..6914782f 100644 --- a/switch/datapath.h +++ b/switch/datapath.h @@ -43,10 +43,12 @@ #include "list.h" struct datapath; -struct controller_connection; +struct rconn; +struct vconn; -int dp_new(struct datapath **, uint64_t dpid, struct controller_connection *); +int dp_new(struct datapath **, uint64_t dpid, struct rconn *); int dp_add_port(struct datapath *, const char *netdev); +void dp_add_listen_vconn(struct datapath *, struct vconn *); void dp_run(struct datapath *); void dp_wait(struct datapath *); diff --git a/switch/switch.c b/switch/switch.c index e489b9fa..f8f45bc3 100644 --- a/switch/switch.c +++ b/switch/switch.c @@ -38,13 +38,13 @@ #include #include "command-line.h" -#include "controller.h" #include "datapath.h" #include "fault.h" #include "openflow.h" #include "poll-loop.h" #include "queue.h" #include "util.h" +#include "rconn.h" #include "vconn.h" #include "vconn-ssl.h" #include "vlog-socket.h" @@ -55,7 +55,7 @@ static void parse_options(int argc, char *argv[]); static void usage(void) NO_RETURN; -static bool reliable = true; +static const char *listen_vconn_name; static struct datapath *dp; static uint64_t dpid = UINT64_MAX; static char *port_list; @@ -65,7 +65,6 @@ static void add_ports(struct datapath *dp, char *port_list); int main(int argc, char *argv[]) { - struct controller_connection *cc; int error; set_program_name(argv[0]); @@ -77,8 +76,20 @@ main(int argc, char *argv[]) fatal(0, "missing controller argument; use --help for usage"); } - cc = controller_new(argv[optind], reliable); - error = dp_new(&dp, dpid, cc); + error = dp_new(&dp, dpid, rconn_new(argv[optind], 128)); + if (listen_vconn_name) { + struct vconn *listen_vconn; + int retval; + + retval = vconn_open(listen_vconn_name, &listen_vconn); + if (retval && retval != EAGAIN) { + fatal(retval, "opening %s", listen_vconn_name); + } + if (!vconn_is_passive(listen_vconn)) { + fatal(0, "%s is not a passive vconn", listen_vconn_name); + } + dp_add_listen_vconn(dp, listen_vconn); + } if (error) { fatal(error, "could not create datapath"); } @@ -123,16 +134,12 @@ parse_options(int argc, char *argv[]) { static struct option long_options[] = { {"interfaces", required_argument, 0, 'i'}, - {"unreliable", no_argument, 0, 'u'}, {"datapath-id", required_argument, 0, 'd'}, + {"listen", required_argument, 0, 'l'}, {"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 + VCONN_SSL_LONG_OPTIONS {0, 0, 0, 0}, }; char *short_options = long_options_to_short_options(long_options); @@ -147,10 +154,6 @@ parse_options(int argc, char *argv[]) } switch (c) { - case 'u': - reliable = false; - break; - case 'd': if (strlen(optarg) != 12 || strspn(optarg, "0123456789abcdefABCDEF") != 12) { @@ -182,19 +185,14 @@ parse_options(int argc, char *argv[]) } break; -#ifdef HAVE_OPENSSL - case 'p': - vconn_ssl_set_private_key_file(optarg); - break; - - case 'c': - vconn_ssl_set_certificate_file(optarg); + case 'l': + if (listen_vconn_name) { + fatal(0, "-l or --listen may be only specified once"); + } + listen_vconn_name = optarg; break; - case 'C': - vconn_ssl_set_ca_cert_file(optarg); - break; -#endif + VCONN_SSL_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); @@ -211,23 +209,17 @@ usage(void) { printf("%s: userspace OpenFlow switch\n" "usage: %s [OPTIONS] CONTROLLER\n" - "CONTROLLER must be 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_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("Options:\n" + "where CONTROLLER is an active OpenFlow connection method.\n", + program_name, program_name); + vconn_usage(true, true); + printf("\nConfiguration options:\n" " -i, --interfaces=NETDEV[,NETDEV]...\n" " add specified initial switch ports\n" " -d, --datapath-id=ID Use ID as the OpenFlow switch ID\n" " (ID must consist of 12 hex digits)\n" - " -u, --unreliable do not reconnect to controller\n" + " -l, --listen=METHOD allow management connections on METHOD\n" + " (a passive OpenFlow connection method)\n" + "\nOther options:\n" " -v, --verbose set maximum verbosity level\n" " -h, --help display this help message\n" " -V, --version display version information\n"); 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..80808acf 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -53,9 +54,10 @@ #include "openflow.h" #include "ofp-print.h" #include "vconn.h" +#include "vconn-ssl.h" #include "vlog.h" -#define THIS_MODULE VLM_DPCTL +#define THIS_MODULE VLM_dpctl static const char* ifconfigbin = "/sbin/ifconfig"; @@ -111,6 +113,7 @@ parse_options(int argc, char *argv[]) {"verbose", optional_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, + VCONN_SSL_LONG_OPTIONS {0, 0, 0, 0}, }; char *short_options = long_options_to_short_options(long_options); @@ -136,6 +139,8 @@ parse_options(int argc, char *argv[]) vlog_set_verbosity(optarg); break; + VCONN_SSL_OPTION_HANDLERS + case '?': exit(EXIT_FAILURE); @@ -149,34 +154,58 @@ 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" + " monitor nl:DP_ID print packets received\n" + " benchmark-nl nl:DP_ID N SIZE send N packets of SIZE bytes\n" +#endif + "\nCommands that apply to local datapaths and remote switches:\n" + " show SWITCH show information\n" + " dump-tables SWITCH print table stats\n" + " dump-ports SWITCH print port statistics\n" + " dump-flows SWITCH print all flow entries\n" + " dump-flows SWITCH FLOW print matching FLOWs\n" + " add-flows SWITCH FILE add flows from FILE\n" + "where each SWITCH is an active OpenFlow connection method.\n", + program_name, program_name); + vconn_usage(true, false); + 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); } -static void run(int retval, const char *name) +static void run(int retval, const char *message, ...) + PRINTF_FORMAT(2, 3); + +static void run(int retval, const char *message, ...) { if (retval) { - fatal(retval, "%s", name); + va_list args; + + fprintf(stderr, "%s: ", program_name); + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + if (retval == EOF) { + fputs(": unexpected end of file\n", stderr); + } else { + vfprintf(stderr, ": %s\n", strerror(retval)); + } + + exit(EXIT_FAILURE); } } + +#ifdef HAVE_NETLINK +/* Netlink-only commands. */ static int if_up(const char* intf) { @@ -186,10 +215,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 +236,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,11 +253,23 @@ 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); } +static void do_monitor(int argc UNUSED, char *argv[]) +{ + struct dpif dp; + open_nl_vconn(argv[1], false, &dp); + for (;;) { + struct buffer *b; + run(dpif_recv_openflow(&dp, &b, true), "dpif_recv_openflow"); + ofp_print(stderr, b->data, b->size, 2); + buffer_delete(b); + } +} + #define BENCHMARK_INCR 100 static void do_benchmark_nl(int argc UNUSED, char *argv[]) @@ -235,7 +278,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,25 +307,99 @@ static void do_benchmark_nl(int argc UNUSED, char *argv[]) dpif_close(&dp); } +#endif /* HAVE_NETLINK */ + +/* Generic commands. */ -static void do_monitor(int argc UNUSED, char *argv[]) +static uint32_t +random_xid(void) { - struct dpif dp; - run(dpif_open(atoi(argv[1]), true, &dp), "dpif_open"); + static bool inited = false; + if (!inited) { + struct timeval tv; + inited = true; + if (gettimeofday(&tv, NULL) < 0) { + fatal(errno, "gettimeofday"); + } + srand(tv.tv_sec ^ tv.tv_usec); + } + return rand(); +} + +static void * +alloc_openflow_buffer(size_t openflow_len, uint8_t type, + struct buffer **bufferp) +{ + struct buffer *buffer; + struct ofp_header *oh; + + buffer = *bufferp = buffer_new(openflow_len); + oh = buffer_put_uninit(buffer, openflow_len); + memset(oh, 0, openflow_len); + oh->version = OFP_VERSION; + oh->type = type; + oh->length = 0; + oh->xid = random_xid(); + return oh; +} + +static void +send_openflow_buffer(struct vconn *vconn, struct buffer *buffer) +{ + struct ofp_header *oh; + + oh = buffer_at_assert(buffer, 0, sizeof *oh); + oh->length = htons(buffer->size); + + run(vconn_send_block(vconn, buffer), "failed to send packet to switch"); +} + +static struct buffer * +transact_openflow(struct vconn *vconn, struct buffer *request) +{ + uint32_t send_xid = ((struct ofp_header *) request->data)->xid; + + send_openflow_buffer(vconn, request); for (;;) { - struct buffer *b; - run(dpif_recv_openflow(&dp, &b, true), "dpif_recv_openflow"); - ofp_print(stderr, b->data, b->size, 2); - buffer_delete(b); + uint32_t recv_xid; + struct buffer *reply; + + run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); + recv_xid = ((struct ofp_header *) reply->data)->xid; + if (send_xid == recv_xid) { + return reply; + } + + VLOG_DBG("received reply with xid %08"PRIx32" != expected %08"PRIx32, + recv_xid, send_xid); + buffer_delete(reply); } } -static void do_dump_tables(int argc, char *argv[]) +static void +dump_transaction(const char *vconn_name, uint8_t request_type) { - struct dpif dp; - run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open"); - run(dpif_dump_tables(&dp), "dump_tables"); - dpif_close(&dp); + struct vconn *vconn; + struct buffer *request, *reply; + + run(vconn_open_block(vconn_name, &vconn), "connecting to %s", vconn_name); + alloc_openflow_buffer(sizeof(struct ofp_header), request_type, &request); + reply = transact_openflow(vconn, request); + ofp_print(stdout, reply->data, reply->size, 1); + vconn_close(vconn); +} + +static void +do_show(int argc UNUSED, char *argv[]) +{ + dump_transaction(argv[1], OFPT_FEATURES_REQUEST); +} + + +static void +do_dump_tables(int argc, char *argv[]) +{ + dump_transaction(argv[1], OFPT_TABLE_STAT_REQUEST); } @@ -337,7 +454,8 @@ str_to_action(const char *str, struct ofp_action *action) } static void -str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action) +str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action, + uint8_t *table_idx) { struct field { const char *name; @@ -364,6 +482,9 @@ str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action) uint32_t wildcards; bool got_action = false; + if (table_idx) { + *table_idx = 0xff; + } memset(match, 0, sizeof *match); wildcards = OFPFW_ALL; for (name = strtok(string, "="), value = strtok(NULL, " \t\n"); @@ -379,6 +500,11 @@ str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action) continue; } + if (table_idx && !strcmp(name, "table")) { + *table_idx = atoi(value); + continue; + } + for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) { if (!strcmp(f->name, name)) { goto found; @@ -425,17 +551,18 @@ str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action) static void do_dump_flows(int argc, char *argv[]) { - struct dpif dp; - struct ofp_match match, *matchp; - run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open"); - if (argc == 4) { - str_to_flow(argv[3], &match, NULL); - matchp = &match; - } else { - matchp = NULL; - } - run(dpif_dump_flows(&dp, atoi(argv[2]), matchp), "dump_flows"); - dpif_close(&dp); + struct vconn *vconn; + struct buffer *request, *reply; + struct ofp_flow_stat_request *fsr; + + run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]); + fsr = alloc_openflow_buffer(sizeof *fsr, OFPT_FLOW_STAT_REQUEST, &request); + str_to_flow(argc > 2 ? argv[2] : "", &fsr->match, NULL, &fsr->table_id); + fsr->type = OFPFS_INDIV; + fsr->pad = 0; + reply = transact_openflow(vconn, request); + ofp_print(stdout, reply->data, reply->size, 1); + vconn_close(vconn); } static void do_add_flows(int argc, char *argv[]) @@ -446,19 +573,12 @@ static void do_add_flows(int argc, char *argv[]) FILE *file; char line[1024]; - int retval; - file = fopen(argv[2], "r"); if (file == NULL) { fatal(errno, "%s: open", argv[2]); } - sprintf(vconn_name, "nl:%d", atoi(argv[1])); - retval = vconn_open(vconn_name, &vconn); - if (retval) { - fatal(retval, "opening datapath"); - } - + run(vconn_open_block(vconn_name, &vconn), "connecting to %s", argv[1]); while (fgets(line, sizeof line, file)) { struct buffer *buffer; struct ofp_flow_mod *ofm; @@ -477,55 +597,46 @@ static void do_add_flows(int argc, char *argv[]) continue; } + /* Parse and send. */ size = sizeof *ofm + sizeof ofm->actions[0]; - buffer = buffer_new(size); - ofm = buffer_put_uninit(buffer, size); - - /* Parse. */ - memset(ofm, 0, size); - ofm->header.type = OFPT_FLOW_MOD; - ofm->header.version = OFP_VERSION; - ofm->header.length = htons(size); + ofm = alloc_openflow_buffer(size, OFPT_FLOW_MOD, &buffer); ofm->command = htons(OFPFC_ADD); ofm->max_idle = htons(50); ofm->buffer_id = htonl(UINT32_MAX); ofm->group_id = htonl(0); - str_to_flow(line, &ofm->match, &ofm->actions[0]); - - retval = vconn_send_block(vconn, buffer); - if (retval) { - fatal(retval, "sending to datapath"); - } + str_to_flow(line, &ofm->match, &ofm->actions[0], NULL); + run(vconn_send_block(vconn, buffer), "send OpenFlow packet"); } vconn_close(vconn); fclose(file); } +static void +do_dump_ports(int argc, char *argv[]) +{ + dump_transaction(argv[1], OFPT_PORT_STAT_REQUEST); +} + static void do_help(int argc UNUSED, char *argv[] UNUSED) { usage(); } 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 }, + { "dump-flows", 1, 2, do_dump_flows }, { "add-flows", 2, 2, do_add_flows }, - - { "benchmark-nl", 3, 3, do_benchmark_nl }, + { "dump-ports", 1, 1, do_dump_ports }, };