Send replies to OpenFlow requests only to the sender.
authorBen Pfaff <blp@nicira.com>
Thu, 3 Apr 2008 22:15:44 +0000 (15:15 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 4 Apr 2008 23:22:23 +0000 (16:22 -0700)
datapath/datapath.c
datapath/datapath.h
datapath/forward.c
datapath/forward.h
secchan/secchan.c
switch/datapath.c
switch/datapath.h
switch/switch.c

index b56c5c9..ab3f866 100644 (file)
@@ -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();
index 9c6a6a8..6dec881 100644 (file)
@@ -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 */
index a2b6691..9c6b697 100644 (file)
@@ -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. */
index 8e92330..3e7686f 100644 (file)
@@ -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);
 
index a0feea0..8ac3d41 100644 (file)
@@ -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':
index 3527492..6dd7c35 100644 (file)
@@ -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);
 }
 \f
 /* '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);
 }
 \f
 /* Packet buffering. */
index b30de51..6914782 100644 (file)
 
 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 *);
 
index abd76b6..f2f107a 100644 (file)
@@ -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");