From: Ben Pfaff Date: Thu, 3 Apr 2008 22:15:44 +0000 (-0700) Subject: Send replies to OpenFlow requests only to the sender. X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=4fc7e7d11f08336ad99b3a3dbd34ba8d195893f7;p=sliver-openvswitch.git Send replies to OpenFlow requests only to the sender. --- diff --git a/datapath/datapath.c b/datapath/datapath.c index b56c5c911..ab3f86665 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -96,8 +96,8 @@ void nla_unreserve(struct sk_buff *skb, struct nlattr *nla, int len) } 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; @@ -114,7 +114,10 @@ alloc_openflow_skb(struct datapath *dp, size_t openflow_len, } /* 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(); @@ -127,7 +130,7 @@ alloc_openflow_skb(struct datapath *dp, size_t openflow_len, oh->version = OFP_VERSION; oh->type = type; oh->length = htons(openflow_len); - oh->xid = xid; + oh->xid = sender ? sender->xid : 0; return oh; } @@ -145,6 +148,18 @@ resize_openflow_skb(struct sk_buff *skb, 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. @@ -564,19 +579,16 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, 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; } @@ -645,18 +657,17 @@ fill_features_reply(struct datapath *dp, struct ofp_switch_features *ofr) } 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; @@ -666,34 +677,23 @@ dp_send_features_reply(struct datapath *dp, uint32_t xid) /* 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 @@ -718,20 +718,15 @@ send_port_status(struct net_bridge_port *p, uint8_t status) { 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 @@ -740,7 +735,6 @@ dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow) 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) @@ -751,12 +745,7 @@ dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow) 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. @@ -1311,6 +1300,8 @@ static int dp_genl_openflow(struct sk_buff *skb, struct genl_info *info) { struct nlattr *va = info->attrs[DP_GENL_A_OPENFLOW]; struct datapath *dp; + struct ofp_header *oh; + struct sender sender; int err; if (!info->attrs[DP_GENL_A_DP_IDX] || !va) @@ -1324,8 +1315,16 @@ static int dp_genl_openflow(struct sk_buff *skb, struct genl_info *info) } 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(); diff --git a/datapath/datapath.h b/datapath/datapath.h index 9c6a6a806..6dec88121 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -55,14 +55,21 @@ struct datapath { 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 */ diff --git a/datapath/forward.c b/datapath/forward.c index a2b6691f1..9c6b697f0 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -260,21 +260,22 @@ struct sk_buff *execute_setter(struct sk_buff *skb, uint16_t eth_proto, } 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; @@ -282,7 +283,8 @@ recv_set_config(struct sw_chain *chain, const void *msg) } 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; @@ -330,7 +332,8 @@ recv_packet_out(struct sw_chain *chain, const void *msg) } static int -recv_port_mod(struct sw_chain *chain, const void *msg) +recv_port_mod(struct sw_chain *chain, const struct sender *sender, + const void *msg) { const struct ofp_port_mod *opm = msg; @@ -400,7 +403,7 @@ error: } static int -recv_flow(struct sw_chain *chain, const void *msg) +recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg) { const struct ofp_flow_mod *ofm = msg; uint16_t command = ntohs(ofm->command); @@ -420,15 +423,17 @@ recv_flow(struct sw_chain *chain, const void *msg) } } -/* 'msg', which is 'length' bytes long, was received from the control path. - * Apply it to 'chain'. */ +/* '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[] = { @@ -461,9 +466,6 @@ fwd_control_input(struct sw_chain *chain, const void *msg, size_t length) 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) @@ -475,7 +477,7 @@ fwd_control_input(struct sw_chain *chain, const void *msg, size_t length) if (length < pkt->min_size) return -EFAULT; - return pkt->handler(chain, msg); + return pkt->handler(chain, sender, msg); } /* Packet buffering. */ diff --git a/datapath/forward.h b/datapath/forward.h index 8e92330ff..3e7686fa5 100644 --- a/datapath/forward.h +++ b/datapath/forward.h @@ -7,6 +7,7 @@ struct sk_buff; struct sw_chain; struct ofp_action; +struct sender; /* Buffers are identified to userspace by a 31-bit opaque ID. We divide the ID * into a buffer number (low bits) and a cookie (high bits). The buffer number @@ -21,7 +22,8 @@ struct ofp_action; void fwd_port_input(struct sw_chain *, struct sk_buff *, int in_port); -int fwd_control_input(struct sw_chain *, const void *, size_t); +int fwd_control_input(struct sw_chain *, const struct sender *, + const void *, size_t); uint32_t fwd_save_skb(struct sk_buff *skb); diff --git a/secchan/secchan.c b/secchan/secchan.c index a0feea04f..8ac3d4190 100644 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@ -58,7 +58,7 @@ 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; @@ -81,6 +81,7 @@ static void relay_destroy(struct relay *); int main(int argc, char *argv[]) { + struct vconn *listen_vconn; const char *nl_name; int retval; @@ -100,6 +101,18 @@ main(int argc, char *argv[]) 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"); @@ -114,8 +127,8 @@ main(int argc, char *argv[]) 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) { @@ -123,7 +136,6 @@ main(int argc, char *argv[]) } break; } - new_management_connection(nl_name, new_remote); } } @@ -273,7 +285,6 @@ parse_options(int argc, char *argv[]) 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); @@ -283,16 +294,10 @@ parse_options(int argc, char *argv[]) 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': diff --git a/switch/datapath.c b/switch/datapath.c index 3527492e7..6dd7c3576 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -44,6 +44,7 @@ #include "packets.h" #include "poll-loop.h" #include "rconn.h" +#include "vconn.h" #include "table.h" #include "xtoxll.h" @@ -72,8 +73,23 @@ struct sw_port { 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; @@ -89,6 +105,11 @@ struct datapath { 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); @@ -119,7 +140,8 @@ static void modify_th(struct buffer *buffer, uint16_t eth_proto, #define PKT_COOKIE_BITS (32 - PKT_BUFFER_BITS) void fwd_port_input(struct datapath *, struct buffer *, int in_port); -int fwd_control_input(struct datapath *, const void *, size_t); +int fwd_control_input(struct datapath *, const struct sender *, + const void *, size_t); uint32_t save_buffer(struct buffer *); static struct buffer *retrieve_buffer(uint32_t id); @@ -162,7 +184,9 @@ dp_new(struct datapath **dp_, uint64_t dpid, struct rconn *rconn) } 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) { @@ -208,13 +232,20 @@ dp_add_port(struct datapath *dp, const char *name) return 0; } +void +dp_add_listen_vconn(struct datapath *dp, struct vconn *listen_vconn) +{ + assert(!dp->listen_vconn); + dp->listen_vconn = listen_vconn; +} + void dp_run(struct datapath *dp) { time_t now = time(0); - struct sw_port *p, *n; + struct sw_port *p, *pn; + struct remote *r, *rn; struct buffer *buffer = NULL; - int i; if (now != dp->last_timeout) { struct list deleted = LIST_INITIALIZER(&deleted); @@ -230,7 +261,7 @@ dp_run(struct datapath *dp) } poll_timer_wait(1000); - LIST_FOR_EACH_SAFE (p, n, struct sw_port, node, &dp->port_list) { + LIST_FOR_EACH_SAFE (p, pn, struct sw_port, node, &dp->port_list) { int error; if (!buffer) { @@ -255,29 +286,104 @@ dp_run(struct datapath *dp) } buffer_delete(buffer); - /* Process a number of commands from the controller, but cap it at a + /* Talk to remotes. */ + LIST_FOR_EACH_SAFE (r, rn, struct remote, node, &dp->remotes) { + remote_run(dp, r); + } + if (dp->listen_vconn) { + for (;;) { + struct vconn *new_vconn; + int retval; + + retval = vconn_accept(dp->listen_vconn, &new_vconn); + if (retval) { + if (retval != EAGAIN) { + VLOG_WARN("accept failed (%s)", strerror(retval)); + } + break; + } + remote_create(dp, rconn_new_from_vconn("passive", 128, new_vconn)); + } + } +} + +static void +remote_run(struct datapath *dp, struct remote *r) +{ + int i; + + rconn_run(r->rconn); + + /* Process a number of commands from the remote, but cap them at a * reasonable number so that other processing doesn't starve. */ for (i = 0; i < 50; i++) { - struct buffer *buffer = 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. */ @@ -363,6 +469,43 @@ dp_output_port(struct datapath *dp, struct buffer *buffer, } } +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 @@ -393,7 +536,7 @@ dp_output_control(struct datapath *dp, struct buffer *buffer, int in_port, opi->in_port = htons(in_port); opi->reason = reason; opi->pad = 0; - rconn_send(dp->rconn, buffer); + send_openflow_buffer(dp, buffer, NULL); } static void fill_port_desc(struct datapath *dp, struct sw_port *p, @@ -410,18 +553,14 @@ 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); @@ -436,9 +575,7 @@ dp_send_features_reply(struct datapath *dp, uint32_t xid) 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 @@ -461,15 +598,11 @@ send_port_status(struct sw_port *p, uint8_t status) { struct buffer *buffer; struct ofp_port_status *ops; - buffer = buffer_new(sizeof *ops); - ops = buffer_put_uninit(buffer, sizeof *ops); - ops->header.version = OFP_VERSION; - ops->header.type = OFPT_PORT_STATUS; - ops->header.length = htons(sizeof(*ops)); - ops->header.xid = htonl(0); + ops = alloc_openflow_buffer(p->dp, sizeof *ops, OFPT_PORT_STATUS, NULL, + &buffer); ops->reason = status; fill_port_desc(p->dp, p, &ops->desc); - rconn_send(p->dp->rconn, buffer); + send_openflow_buffer(p->dp, buffer, NULL); } void @@ -477,17 +610,13 @@ send_flow_expired(struct datapath *dp, struct sw_flow *flow) { struct buffer *buffer; struct ofp_flow_expired *ofe; - buffer = buffer_new(sizeof *ofe); - ofe = buffer_put_uninit(buffer, sizeof *ofe); - ofe->header.version = OFP_VERSION; - ofe->header.type = OFPT_FLOW_EXPIRED; - ofe->header.length = htons(sizeof(*ofe)); - ofe->header.xid = htonl(0); + ofe = alloc_openflow_buffer(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, NULL, + &buffer); flow_fill_match(&ofe->match, &flow->key); ofe->duration = htonl(flow->timeout - flow->max_idle - flow->created); ofe->packet_count = htonll(flow->packet_count); ofe->byte_count = htonll(flow->byte_count); - rconn_send(dp->rconn, buffer); + send_openflow_buffer(dp, buffer, NULL); } /* 'buffer' was received on 'in_port', a physical switch port between 0 and @@ -707,32 +836,34 @@ modify_vlan(struct buffer *buffer, } 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; @@ -740,7 +871,8 @@ recv_set_config(struct datapath *dp, const void *msg) } 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; @@ -771,7 +903,8 @@ recv_packet_out(struct datapath *dp, const void *msg) } static int -recv_port_mod(struct datapath *dp, const void *msg) +recv_port_mod(struct datapath *dp, const struct sender *sender UNUSED, + const void *msg) { const struct ofp_port_mod *opm = msg; @@ -840,7 +973,8 @@ error: } static int -recv_flow(struct datapath *dp, const void *msg) +recv_flow(struct datapath *dp, const struct sender *sender UNUSED, + const void *msg) { const struct ofp_flow_mod *ofm = msg; uint16_t command = ntohs(ofm->command); @@ -863,12 +997,12 @@ recv_flow(struct datapath *dp, const void *msg) /* '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[] = { @@ -901,9 +1035,6 @@ fwd_control_input(struct datapath *dp, const void *msg, size_t length) 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) @@ -915,7 +1046,7 @@ fwd_control_input(struct datapath *dp, const void *msg, size_t length) if (length < pkt->min_size) return -EFAULT; - return pkt->handler(dp, msg); + return pkt->handler(dp, sender, msg); } /* Packet buffering. */ diff --git a/switch/datapath.h b/switch/datapath.h index b30de513e..6914782f6 100644 --- a/switch/datapath.h +++ b/switch/datapath.h @@ -44,9 +44,11 @@ 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 *); diff --git a/switch/switch.c b/switch/switch.c index abd76b6e1..f2f107aaa 100644 --- a/switch/switch.c +++ b/switch/switch.c @@ -55,6 +55,7 @@ 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; @@ -76,6 +77,19 @@ main(int argc, char *argv[]) } 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"); } @@ -170,6 +184,13 @@ parse_options(int argc, char *argv[]) } 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 '?': @@ -189,12 +210,15 @@ usage(void) "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");