}
static void *
-alloc_openflow_skb(struct datapath *dp, size_t openflow_len,
- uint8_t type, uint32_t xid, struct sk_buff **pskb)
+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;
}
/* Assemble the Generic Netlink wrapper. */
- if (!genlmsg_put(skb, 0, 0, &dp_genl_family, 0, DP_GENL_C_OPENFLOW))
+ 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();
oh->version = OFP_VERSION;
oh->type = type;
oh->length = htons(openflow_len);
- oh->xid = xid;
+ oh->xid = sender ? sender->xid : 0;
return oh;
}
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.
fwd_len = min(fwd_len, max_len);
opi_len = offsetof(struct ofp_packet_in, data) + fwd_len;
- opi = alloc_openflow_skb(dp, opi_len, OFPT_PACKET_IN, 0, &f_skb);
+ 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;
memcpy(opi->data, skb_mac_header(skb), fwd_len);
+ err = send_openflow_skb(f_skb, NULL);
- 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);
+ kfree_skb(skb);
return err;
}
}
int
-dp_send_features_reply(struct datapath *dp, uint32_t xid)
+dp_send_features_reply(struct datapath *dp, const struct sender *sender)
{
struct sk_buff *skb;
struct ofp_switch_features *ofr;
size_t ofr_len, port_max_len;
- int err;
int port_count;
/* Overallocate. */
port_max_len = sizeof(struct ofp_phy_port) * OFPP_MAX;
ofr = alloc_openflow_skb(dp, sizeof(*ofr) + port_max_len,
- OFPT_FEATURES_REPLY, xid, &skb);
+ OFPT_FEATURES_REPLY, sender, &skb);
if (!ofr)
return -ENOMEM;
/* Shrink to fit. */
ofr_len = sizeof(*ofr) + (sizeof(struct ofp_phy_port) * port_count);
resize_openflow_skb(skb, &ofr->header, ofr_len);
-
- /* FIXME: send as reply. */
- err = genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC);
- if (err && net_ratelimit())
- printk(KERN_WARNING "dp_send_hello: genlmsg_multicast failed: %d\n", err);
-
- return err;
+ return send_openflow_skb(skb, sender);
}
int
-dp_send_config_reply(struct datapath *dp, uint32_t xid)
+dp_send_config_reply(struct datapath *dp, const struct sender *sender)
{
struct sk_buff *skb;
struct ofp_switch_config *osc;
- int err;
- osc = alloc_openflow_skb(dp, sizeof *osc, OFPT_PORT_STATUS, 0, &skb);
+ 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);
-
- 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;
+ return send_openflow_skb(skb, sender);
}
int
{
struct sk_buff *skb;
struct ofp_port_status *ops;
- int err;
- ops = alloc_openflow_skb(p->dp, sizeof *ops, OFPT_PORT_STATUS, 0,
+ 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_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;
+ return send_openflow_skb(skb, NULL);
}
int
struct sk_buff *skb;
struct ofp_flow_expired *ofe;
unsigned long duration_j;
- int err;
ofe = alloc_openflow_skb(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &skb);
if (!ofe)
ofe->duration = htonl(duration_j / HZ);
ofe->packet_count = cpu_to_be64(flow->packet_count);
ofe->byte_count = cpu_to_be64(flow->byte_count);
-
- err = genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC);
- if (err && net_ratelimit())
- printk(KERN_WARNING "send_flow_expired: genlmsg_multicast failed: %d\n", err);
-
- return err;
+ return send_openflow_skb(skb, NULL);
}
/* Generic Netlink interface.
{
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)
}
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();
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_features_reply(struct datapath *, uint32_t xid);
-int dp_send_config_reply(struct datapath *, uint32_t xid);
+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_port_stats(struct datapath *dp, uint32_t xid);
+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 */
}
static int
-recv_features_request(struct sw_chain *chain, const void *msg)
+recv_features_request(struct sw_chain *chain, const struct sender *sender,
+ const void *msg)
{
- const struct ofp_header *ofr = msg;
- return dp_send_features_reply(chain->dp, ofr->xid);
+ return dp_send_features_reply(chain->dp, sender);
}
static int
-recv_get_config_request(struct sw_chain *chain, const void *msg)
+recv_get_config_request(struct sw_chain *chain, const struct sender *sender,
+ const void *msg)
{
- const struct ofp_header *ofr = msg;
- return dp_send_config_reply(chain->dp, ofr->xid);
+ return dp_send_config_reply(chain->dp, sender);
}
static int
-recv_set_config(struct sw_chain *chain, const void *msg)
+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;
}
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'. */
+/* '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[] = {
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);
static void parse_options(int argc, char *argv[]);
static void usage(void) NO_RETURN;
-static struct vconn *listen_vconn = NULL;
+static const char *listen_vconn_name;
struct half {
struct rconn *rconn;
int
main(int argc, char *argv[])
{
+ struct vconn *listen_vconn;
const char *nl_name;
int retval;
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);
if (retval) {
fatal(retval, "Could not listen for vlog connections");
relay_run(r);
}
if (listen_vconn) {
- struct vconn *new_remote;
for (;;) {
+ struct vconn *new_remote;
retval = vconn_accept(listen_vconn, &new_remote);
if (retval) {
if (retval != EAGAIN) {
}
break;
}
-
new_management_connection(nl_name, new_remote);
}
}
char *short_options = long_options_to_short_options(long_options);
for (;;) {
- int retval;
int c;
c = getopt_long(argc, argv, short_options, long_options, NULL);
switch (c) {
case 'l':
- if (listen_vconn) {
+ if (listen_vconn_name) {
fatal(0, "-l or --listen may be only specified once");
}
- retval = vconn_open(optarg, &listen_vconn);
- if (retval && retval != EAGAIN) {
- fatal(retval, "opening %s", optarg);
- }
- if (!vconn_is_passive(listen_vconn)) {
- fatal(0, "%s is not a passive vconn", optarg);
- }
+ listen_vconn_name = optarg;
break;
case 'h':
#include "packets.h"
#include "poll-loop.h"
#include "rconn.h"
+#include "vconn.h"
#include "table.h"
#include "xtoxll.h"
struct list node; /* Element in datapath.ports. */
};
-struct datapath {
+/* 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 {
+ /* Remote connections. */
+ struct remote *controller; /* Connection to controller. */
+ struct list remotes; /* All connections (including controller). */
+ struct vconn *listen_vconn;
time_t last_timeout;
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_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
#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);
}
dp->last_timeout = time(0);
- dp->rconn = rconn;
+ 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) {
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) {
}
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 = rconn_recv(dp->rconn);
+ 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);
}
- rconn_run(dp->rconn);
+ 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);
}
- rconn_recv_wait(dp->rconn);
+ 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. */
}
}
+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
opi->in_port = htons(in_port);
opi->reason = reason;
opi->pad = 0;
- rconn_send(dp->rconn, buffer);
+ send_openflow_buffer(dp, buffer, NULL);
}
static void fill_port_desc(struct datapath *dp, struct sw_port *p,
}
static void
-dp_send_features_reply(struct datapath *dp, uint32_t xid)
+dp_send_features_reply(struct datapath *dp, const struct sender *sender)
{
struct buffer *buffer;
struct ofp_switch_features *ofr;
struct sw_port *p;
- buffer = buffer_new(sizeof *ofr);
- ofr = buffer_put_uninit(buffer, sizeof *ofr);
- memset(ofr, 0, sizeof *ofr);
- ofr->header.version = OFP_VERSION;
- ofr->header.type = OFPT_FEATURES_REPLY;
- ofr->header.xid = xid;
+ ofr = 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);
memset(opp, 0, sizeof *opp);
fill_port_desc(dp, p, opp);
}
- ofr = buffer_at_assert(buffer, 0, sizeof *ofr);
- ofr->header.length = htons(buffer->size);
- rconn_send(dp->rconn, 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);
- rconn_send(p->dp->rconn, 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);
- rconn_send(dp->rconn, buffer);
+ send_openflow_buffer(dp, buffer, NULL);
}
\f
/* 'buffer' was received on 'in_port', a physical switch port between 0 and
}
static int
-recv_features_request(struct datapath *dp, const void *msg)
+recv_features_request(struct datapath *dp, const struct sender *sender,
+ const void *msg)
{
- struct ofp_header *ofr = msg;
- dp_send_features_reply(dp, ofr->xid);
+ dp_send_features_reply(dp, sender);
return 0;
}
static int
-recv_get_config_request(struct datapath *dp, const void *msg)
+recv_get_config_request(struct datapath *dp, const struct sender *sender,
+ const void *msg)
{
- struct ofp_header *gcr = msg;
struct buffer *buffer;
struct ofp_switch_config *osc;
- buffer = buffer_new(sizeof dp->config);
- osc = buffer_put(buffer, &dp->config, sizeof dp->config);
- osc->header.version = OFP_VERSION;
- osc->header.type = OFPT_GET_CONFIG_REPLY;
- osc->header.length = htons(sizeof *osc);
- osc->header.xid = gcr->xid;
- rconn_send(dp->rconn, buffer);
- return 0;
+ osc = alloc_openflow_buffer(dp, sizeof *osc, OFPT_GET_CONFIG_REPLY,
+ sender, &buffer);
+
+ 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);
+
+ return send_openflow_buffer(dp, buffer, sender);
}
static int
-recv_set_config(struct datapath *dp, const void *msg)
+recv_set_config(struct datapath *dp, const struct sender *sender UNUSED,
+ const void *msg)
{
const struct ofp_switch_config *osc = msg;
dp->config = *osc;
}
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);
/* '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[] = {
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. */
struct datapath;
struct rconn;
+struct vconn;
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 *);
static void parse_options(int argc, char *argv[]);
static void usage(void) NO_RETURN;
+static const char *listen_vconn_name;
static struct datapath *dp;
static uint64_t dpid = UINT64_MAX;
static char *port_list;
}
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");
}
}
break;
+ case 'l':
+ if (listen_vconn_name) {
+ fatal(0, "-l or --listen may be only specified once");
+ }
+ listen_vconn_name = optarg;
+ break;
+
VCONN_SSL_OPTION_HANDLERS
case '?':
"usage: %s [OPTIONS] CONTROLLER\n"
"where CONTROLLER is an active OpenFlow connection method.\n",
program_name, program_name);
- vconn_usage(true, false);
- printf("\nOptions:\n"
+ 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"
+ " -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");