struct vconn *vconn;
uint64_t datapath_id;
- time_t last_control_hello;
+ time_t last_features_request;
struct queue txq;
};
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);
if (retval) {
break;
}
+ printf("accept!\n");
switches[n_switches++] = new_switch("tcp", new_vconn);
}
} else {
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;
}
close_switch(struct switch_ *this)
{
if (this) {
+ printf("dropped!\n");
free(this->name);
vconn_close(this->vconn);
queue_destroy(&this->txq);
}
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;
}
}
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;
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
{"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);
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);
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"
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.
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))
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;
}
}
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++;
}
}
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
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.
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. */
{
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)
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();
* 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,
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 */
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);
}
}
}
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;
}
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;
}
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);
}
}
-/* '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),
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)
if (length < pkt->min_size)
return -EFAULT;
- return pkt->handler(chain, msg);
+ return pkt->handler(chain, sender, msg);
}
/* Packet buffering. */
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
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);
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);
}
}
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);
}
}
packets.h \
poll-loop.h \
queue.h \
+ rconn.h \
socket-util.h \
util.h \
vconn.h \
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 *);
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 */
#define DYNAMIC_STRING_H 1
#include <stdarg.h>
+#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
#include "compiler.h"
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 *);
#include <stdio.h>
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
}
/* 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 */
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 */
#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
};
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. */
};
#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. */
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 */
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. */
};
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 {
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 */
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 */
* derivatives without specific, written prior permission.
*/
-#ifndef CONTROLLER_H
-#define CONTROLLER_H 1
+#ifndef RCONN_H
+#define RCONN_H 1
#include "queue.h"
#include <stdbool.h>
#include <time.h>
-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 *);
+/* 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 /* controller.h */
+#endif /* rconn.h */
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 */
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 *);
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,
#define VLOG_MODULES \
VLOG_MODULE(chain) \
VLOG_MODULE(controller) \
- VLOG_MODULE(controller_connection) \
VLOG_MODULE(ctlpath) \
VLOG_MODULE(datapath) \
VLOG_MODULE(dpif) \
VLOG_MODULE(netlink) \
VLOG_MODULE(poll_loop) \
VLOG_MODULE(secchan) \
+ VLOG_MODULE(rconn) \
VLOG_MODULE(switch) \
VLOG_MODULE(socket_util) \
VLOG_MODULE(vconn_netlink) \
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. */
#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 */
ofp-print.c \
poll-loop.c \
queue.c \
+ rconn.c \
socket-util.c \
util.c \
vconn-tcp.c \
}
/* 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 *
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)
#include "dynamic-string.h"
#include <assert.h>
#include <stdlib.h>
+#include <string.h>
#include "util.h"
void
}
}
+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, ...)
{
{
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;
+ }
+}
#include <ctype.h>
#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 */
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("<error>");
}
/* The pcap reader is responsible for figuring out endianness based on the
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("<error>");
+ }
- 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)) {
} 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. */
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;
}
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);
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);
+}
+\f
+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));
}
--- /dev/null
+/* 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 <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#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;
+}
+\f
+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;
+}
{
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;
return EPROTO;
}
want_bytes = length - rx->size;
+ if (!want_bytes) {
+ *bufferp = rx;
+ sslv->rxbuf = NULL;
+ return 0;
+ }
}
buffer_reserve_tailroom(rx, want_bytes);
return EPROTO;
}
want_bytes = length - rx->size;
+ if (!want_bytes) {
+ *bufferp = rx;
+ tcp->rxbuf = NULL;
+ return 0;
+ }
}
buffer_reserve_tailroom(rx, want_bytes);
}
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;
}
#include <string.h>
#include "buffer.h"
#include "flow.h"
+#include "ofp-print.h"
#include "openflow.h"
#include "poll-loop.h"
#include "util.h"
#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.
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;
{
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;
}
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;
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'.
*
.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.
.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.
include ../Make.vars
+if HAVE_NETLINK
bin_PROGRAMS = secchan
+else
+bin_PROGRAMS =
+endif
secchan_SOURCES = secchan.c
secchan_LDADD = ../lib/libopenflow.la -ldl
#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
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();
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);
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();
}
}
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':
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);
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"
switch_SOURCES = \
chain.c \
chain.h \
- controller.c \
- controller.h \
crc32.c \
crc32.h \
datapath.c \
#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. */
#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 *);
+++ /dev/null
-/* 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 <errno.h>
-#include <string.h>
-#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);
- }
- }
-}
#include <string.h>
#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"
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;
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);
#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);
}
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;
}
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) {
}
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;
}
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 */
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);
}
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) {
}
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) {
}
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. */
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;
}
}
}
}
-/* 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)
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;
}
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,
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
{
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
{
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);
}
\f
/* 'buffer' was received on 'in_port', a physical switch port between 0 and
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);
}
}
}
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;
}
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;
}
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);
}
}
+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),
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)
if (length < pkt->min_size)
return -EFAULT;
- return pkt->handler(dp, msg);
+ return pkt->handler(dp, sender, msg);
}
\f
/* Packet buffering. */
#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 *);
#include <string.h>
#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"
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;
int
main(int argc, char *argv[])
{
- struct controller_connection *cc;
int error;
set_program_name(argv[0]);
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");
}
{
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);
}
switch (c) {
- case 'u':
- reliable = false;
- break;
-
case 'd':
if (strlen(optarg) != 12
|| strspn(optarg, "0123456789abcdefABCDEF") != 12) {
}
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);
{
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");
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
#include <getopt.h>
#include <inttypes.h>
#include <netinet/in.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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";
{"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);
vlog_set_verbosity(optarg);
break;
+ VCONN_SSL_OPTION_HANDLERS
+
case '?':
exit(EXIT_FAILURE);
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);
}
}
+\f
+#ifdef HAVE_NETLINK
+/* Netlink-only commands. */
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);
}
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);
}
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[])
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");
dpif_close(&dp);
}
+#endif /* HAVE_NETLINK */
+\f
+/* 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);
}
}
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;
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");
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;
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[])
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;
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 },
};