Merge remote branch 'repo/master' into stats
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 15 Apr 2008 16:57:38 +0000 (09:57 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 15 Apr 2008 16:57:38 +0000 (09:57 -0700)
Conflicts:

datapath/table-mac.c

43 files changed:
controller/controller.c
datapath/datapath.c
datapath/datapath.h
datapath/forward.c
datapath/forward.h
datapath/table-hash.c
datapath/table-linear.c
include/Makefile.am
include/buffer.h
include/dpif.h
include/dynamic-string.h
include/ofp-print.h
include/openflow-netlink.h
include/openflow.h
include/rconn.h [new file with mode: 0644]
include/vconn-ssl.h
include/vconn.h
include/vlog.h
lib/Makefile.am
lib/buffer.c
lib/dpif.c
lib/dynamic-string.c
lib/ofp-print.c
lib/rconn.c [new file with mode: 0644]
lib/vconn-netlink.c
lib/vconn-ssl.c
lib/vconn-tcp.c
lib/vconn.c
lib/vlog.c
man/man8/secchan.8
man/man8/switch.8
secchan/Makefile.am
secchan/secchan.c
switch/Makefile.am
switch/chain.c
switch/chain.h
switch/controller.c [deleted file]
switch/controller.h [deleted file]
switch/datapath.c
switch/datapath.h
switch/switch.c
utilities/Makefile.am
utilities/dpctl.c

index 7d2b77aa321445afc71ecf66e62e7d74e4a910ee..9ef72d0f0d92879334889686d8928c41919e0712 100644 (file)
@@ -71,7 +71,7 @@ struct switch_ {
     struct vconn *vconn;
 
     uint64_t datapath_id;
-    time_t last_control_hello;
+    time_t last_features_request;
 
     struct queue txq;
 };
@@ -91,7 +91,7 @@ static void close_switch(struct switch_ *);
 
 static void queue_tx(struct switch_ *, struct buffer *);
 
-static void send_control_hello(struct switch_ *);
+static void send_features_request(struct switch_ *);
 
 static int do_switch_recv(struct switch_ *this);
 static int do_switch_send(struct switch_ *this);
@@ -162,6 +162,7 @@ main(int argc, char *argv[])
                         if (retval) {
                             break;
                         }
+                        printf("accept!\n");
                         switches[n_switches++] = new_switch("tcp", new_vconn);
                     }
                 } else {
@@ -261,9 +262,9 @@ new_switch(const char *name, struct vconn *vconn)
     this->name = xstrdup(name);
     this->vconn = vconn;
     queue_init(&this->txq);
-    this->last_control_hello = 0;
+    this->last_features_request = 0;
     if (!vconn_is_passive(vconn)) {
-        send_control_hello(this);
+        send_features_request(this);
     }
     return this;
 }
@@ -272,6 +273,7 @@ static void
 close_switch(struct switch_ *this) 
 {
     if (this) {
+        printf("dropped!\n");
         free(this->name);
         vconn_close(this->vconn);
         queue_destroy(&this->txq);
@@ -280,25 +282,35 @@ close_switch(struct switch_ *this)
 }
 
 static void
-send_control_hello(struct switch_ *this)
+send_features_request(struct switch_ *this)
 {
     time_t now = time(0);
-    if (now >= this->last_control_hello + 1) {
+    if (now >= this->last_features_request + 1) {
         struct buffer *b;
-        struct ofp_control_hello *och;
+        struct ofp_header *ofr;
+        struct ofp_switch_config *osc;
 
+        /* Send OFPT_SET_CONFIG. */
         b = buffer_new(0);
-        och = buffer_put_uninit(b, sizeof *och);
-        memset(och, 0, sizeof *och);
-        och->header.version = OFP_VERSION;
-        och->header.length = htons(sizeof *och);
-
-        och->version = htonl(OFP_VERSION);
-        och->flags = htons(OFP_CHELLO_SEND_FLOW_EXP);
-        och->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
+        osc = buffer_put_uninit(b, sizeof *osc);
+        memset(osc, 0, sizeof *osc);
+        osc->header.type = OFPT_SET_CONFIG;
+        osc->header.version = OFP_VERSION;
+        osc->header.length = htons(sizeof *osc);
+        osc->flags = htons(OFPC_SEND_FLOW_EXP);
+        osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
         queue_tx(this, b);
 
-        this->last_control_hello = now;
+        /* Send OFPT_FEATURES_REQUEST. */
+        b = buffer_new(0);
+        ofr = buffer_put_uninit(b, sizeof *ofr);
+        memset(ofr, 0, sizeof *ofr);
+        ofr->type = OFPT_FEATURES_REQUEST;
+        ofr->version = OFP_VERSION;
+        ofr->length = htons(sizeof *ofr);
+        queue_tx(this, b);
+
+        this->last_features_request = now;
     }
 }
 
@@ -312,18 +324,9 @@ static void
 process_packet(struct switch_ *sw, struct buffer *msg) 
 {
     static const size_t min_size[UINT8_MAX + 1] = {
-        [0 ... UINT8_MAX] = SIZE_MAX,
-        [OFPT_CONTROL_HELLO] = sizeof (struct ofp_control_hello),
-        [OFPT_DATA_HELLO] = sizeof (struct ofp_data_hello),
+        [0 ... UINT8_MAX] = sizeof (struct ofp_header),
+        [OFPT_FEATURES_REPLY] = sizeof (struct ofp_switch_features),
         [OFPT_PACKET_IN] = offsetof (struct ofp_packet_in, data),
-        [OFPT_PACKET_OUT] = sizeof (struct ofp_packet_out),
-        [OFPT_FLOW_MOD] = sizeof (struct ofp_flow_mod),
-        [OFPT_FLOW_EXPIRED] = sizeof (struct ofp_flow_expired),
-        [OFPT_TABLE] = sizeof (struct ofp_table),
-        [OFPT_PORT_MOD] = sizeof (struct ofp_port_mod),
-        [OFPT_PORT_STATUS] = sizeof (struct ofp_port_status),
-        [OFPT_FLOW_STAT_REQUEST] = sizeof (struct ofp_flow_stat_request),
-        [OFPT_FLOW_STAT_REPLY] = sizeof (struct ofp_flow_stat_reply),
     };
     struct ofp_header *oh;
 
@@ -334,29 +337,26 @@ process_packet(struct switch_ *sw, struct buffer *msg)
         return;
     }
 
-    if (oh->type == OFPT_DATA_HELLO) {
-        struct ofp_data_hello *odh = msg->data;
-        sw->datapath_id = odh->datapath_id;
+    if (oh->type == OFPT_FEATURES_REPLY) {
+        struct ofp_switch_features *osf = msg->data;
+        sw->datapath_id = osf->datapath_id;
     } else if (sw->datapath_id == 0) {
-        send_control_hello(sw);
-        return;
-    }
-
-    if (oh->type == OFPT_PACKET_IN) {
+        send_features_request(sw);
+    } else if (oh->type == OFPT_PACKET_IN) {
+        struct ofp_packet_in *opi = msg->data;
         if (sw->txq.n >= MAX_TXQ) {
             /* FIXME: ratelimit. */
             VLOG_WARN("%s: tx queue overflow", sw->name);
         } else if (noflow) {
-            process_noflow(sw, msg->data);
+            process_noflow(sw, opi);
         } else if (hub) {
-            process_hub(sw, msg->data);
+            process_hub(sw, opi);
         } else {
-            process_switch(sw, msg->data);
+            process_switch(sw, opi);
         }
-        return;
+    } else {
+        ofp_print(stdout, msg->data, msg->size, 2); 
     }
-
-    ofp_print(stdout, msg->data, msg->size, 2);
 }
 
 static void
@@ -564,11 +564,7 @@ parse_options(int argc, char *argv[])
         {"verbose",     optional_argument, 0, 'v'},
         {"help",        no_argument, 0, 'h'},
         {"version",     no_argument, 0, 'V'},
-#ifdef HAVE_OPENSSL
-        {"private-key", required_argument, 0, 'p'},
-        {"certificate", required_argument, 0, 'c'},
-        {"ca-cert",     required_argument, 0, 'C'},
-#endif
+        VCONN_SSL_LONG_OPTIONS
         {0, 0, 0, 0},
     };
     char *short_options = long_options_to_short_options(long_options);
@@ -602,19 +598,7 @@ parse_options(int argc, char *argv[])
             vlog_set_verbosity(optarg);
             break;
 
-#ifdef HAVE_OPENSSL
-        case 'p':
-            vconn_ssl_set_private_key_file(optarg);
-            break;
-
-        case 'c':
-            vconn_ssl_set_certificate_file(optarg);
-            break;
-
-        case 'C':
-            vconn_ssl_set_ca_cert_file(optarg);
-            break;
-#endif
+        VCONN_SSL_OPTION_HANDLERS
 
         case '?':
             exit(EXIT_FAILURE);
@@ -630,21 +614,10 @@ static void
 usage(void)
 {
     printf("%s: OpenFlow controller\n"
-           "usage: %s [OPTIONS] VCONN\n"
-           "where VCONN is one of the following:\n"
-           "  ptcp:[PORT]             listen to TCP PORT (default: %d)\n",
-           program_name, program_name, OFP_TCP_PORT);
-#ifdef HAVE_NETLINK
-    printf("  nl:DP_IDX               via netlink to local datapath DP_IDX\n");
-#endif
-#ifdef HAVE_OPENSSL
-    printf("  pssl:[PORT]             listen for SSL on PORT (default: %d)\n"
-           "\nPKI configuration (required to use SSL):\n"
-           "  -p, --private-key=FILE  file with private key\n"
-           "  -c, --certificate=FILE  file with certificate for private key\n"
-           "  -C, --ca-cert=FILE      file with peer CA certificate\n",
-           OFP_SSL_PORT);
-#endif
+           "usage: %s [OPTIONS] METHOD\n"
+           "where METHOD is any OpenFlow connection method.\n",
+           program_name, program_name);
+    vconn_usage(true, true);
     printf("\nOther options:\n"
            "  -H, --hub               act as hub instead of learning switch\n"
            "  -n, --noflow            pass traffic, but don't add flows\n"
index 0929b7047aea9dacc6e5ba0b05d9d36327c6b181..639a4836e2c0b81d90f3f956f5947d5f82894ce6 100644 (file)
@@ -96,6 +96,71 @@ void nla_unreserve(struct sk_buff *skb, struct nlattr *nla, int len)
        nla->nla_len -= len;
 }
 
+static void *
+alloc_openflow_skb(struct datapath *dp, size_t openflow_len, uint8_t type,
+                  const struct sender *sender, struct sk_buff **pskb) 
+{
+       size_t genl_len;
+       struct sk_buff *skb;
+       struct nlattr *attr;
+       struct ofp_header *oh;
+
+       genl_len = nla_total_size(sizeof(uint32_t)); /* DP_GENL_A_DP_IDX */
+       genl_len += nla_total_size(openflow_len);    /* DP_GENL_A_OPENFLOW */
+       skb = *pskb = genlmsg_new(genl_len, GFP_ATOMIC);
+       if (!skb) {
+               if (net_ratelimit())
+                       printk("alloc_openflow_skb: genlmsg_new failed\n");
+               return NULL;
+       }
+
+       /* Assemble the Generic Netlink wrapper. */
+       if (!genlmsg_put(skb,
+                        sender ? sender->pid : 0,
+                        sender ? sender->seq : 0,
+                        &dp_genl_family, 0, DP_GENL_C_OPENFLOW))
+               BUG();
+       if (nla_put_u32(skb, DP_GENL_A_DP_IDX, dp->dp_idx) < 0)
+               BUG();
+       attr = nla_reserve(skb, DP_GENL_A_OPENFLOW, openflow_len);
+       BUG_ON(!attr);
+       nlmsg_end(skb, (struct nlmsghdr *) skb->data);
+
+       /* Fill in the header. */
+       oh = nla_data(attr);
+       oh->version = OFP_VERSION;
+       oh->type = type;
+       oh->length = htons(openflow_len);
+       oh->xid = sender ? sender->xid : 0;
+
+       return oh;
+}
+
+static void
+resize_openflow_skb(struct sk_buff *skb,
+                   struct ofp_header *oh, size_t new_length)
+{
+       struct nlattr *attr;
+
+       BUG_ON(new_length > ntohs(oh->length));
+       attr = ((void *) oh) - NLA_HDRLEN;
+       nla_unreserve(skb, attr, ntohs(oh->length) - new_length);
+       oh->length = htons(new_length);
+       nlmsg_end(skb, (struct nlmsghdr *) skb->data);
+}
+
+static int
+send_openflow_skb(struct sk_buff *skb, const struct sender *sender) 
+{
+       int err = (sender
+                  ? genlmsg_unicast(skb, sender->pid)
+                  : genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC));
+       if (err && net_ratelimit())
+               printk(KERN_WARNING "send_openflow_skb: send failed: %d\n",
+                      err);
+       return err;
+}
+
 /* Generates a unique datapath id.  It incorporates the datapath index
  * and a hardware address, if available.  If not, it generates a random
  * one.
@@ -175,7 +240,8 @@ static int new_dp(int dp_idx)
                printk("datapath: problem setting up 'of' device\n");
 #endif
 
-       dp->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
+       dp->config.flags = 0;
+       dp->config.miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
 
        dp->dp_task = kthread_run(dp_maint_func, dp, "dp%d", dp_idx);
        if (IS_ERR(dp->dp_task))
@@ -514,73 +580,29 @@ int
 dp_output_control(struct datapath *dp, struct sk_buff *skb,
                           uint32_t buffer_id, size_t max_len, int reason)
 {
-       /* FIXME? packet_rcv_spkt in net/packet/af_packet.c does some stuff
-          that we should possibly be doing here too. */
        /* FIXME?  Can we avoid creating a new skbuff in the case where we
         * forward the whole packet? */
        struct sk_buff *f_skb;
-       struct nlattr *attr;
        struct ofp_packet_in *opi;
-       size_t opi_len;
-       size_t len, fwd_len;
-       void *data;
-       int err = -ENOMEM;
+       size_t fwd_len, opi_len;
+       int err;
 
        fwd_len = skb->len;
        if ((buffer_id != (uint32_t) -1) && max_len)
                fwd_len = min(fwd_len, max_len);
 
-       len = nla_total_size(offsetof(struct ofp_packet_in, data) + fwd_len) 
-                               + nla_total_size(sizeof(uint32_t));
-
-       f_skb = genlmsg_new(MAX(len, NLMSG_GOODSIZE), GFP_ATOMIC); 
-       if (!f_skb)
-               goto error_free_skb;
-
-       data = genlmsg_put(f_skb, 0, 0, &dp_genl_family, 0,
-                               DP_GENL_C_OPENFLOW);
-       if (data == NULL)
-               goto error_free_f_skb;
-
-       NLA_PUT_U32(f_skb, DP_GENL_A_DP_IDX, dp->dp_idx);
-
        opi_len = offsetof(struct ofp_packet_in, data) + fwd_len;
-       attr = nla_reserve(f_skb, DP_GENL_A_OPENFLOW, opi_len);
-       if (!attr)
-               goto error_free_f_skb;
-       opi = nla_data(attr);
-       opi->header.version = OFP_VERSION;
-       opi->header.type    = OFPT_PACKET_IN;
-       opi->header.length  = htons(opi_len);
-       opi->header.xid     = htonl(0);
-
+       opi = alloc_openflow_skb(dp, opi_len, OFPT_PACKET_IN, NULL, &f_skb);
        opi->buffer_id      = htonl(buffer_id);
        opi->total_len      = htons(skb->len);
        opi->in_port        = htons(skb->dev->br_port->port_no);
        opi->reason         = reason;
        opi->pad            = 0;
-       SKB_LINEAR_ASSERT(skb);
        memcpy(opi->data, skb_mac_header(skb), fwd_len);
+       err = send_openflow_skb(f_skb, NULL);
 
-       err = genlmsg_end(f_skb, data);
-       if (err < 0)
-               goto error_free_f_skb;
-
-       err = genlmsg_multicast(f_skb, 0, mc_group.id, GFP_ATOMIC);
-       if (err && net_ratelimit())
-               printk(KERN_WARNING "dp_output_control: genlmsg_multicast failed: %d\n", err);
-
-       kfree_skb(skb);  
-
-       return err;
-
-nla_put_failure:
-error_free_f_skb:
-       nlmsg_free(f_skb);
-error_free_skb:
        kfree_skb(skb);
-       if (net_ratelimit())
-               printk(KERN_ERR "dp_output_control: failed to send: %d\n", err);
+
        return err;
 }
 
@@ -623,28 +645,24 @@ static void fill_port_desc(struct net_bridge_port *p, struct ofp_phy_port *desc)
 }
 
 static int 
-fill_data_hello(struct datapath *dp, struct ofp_data_hello *odh)
+fill_features_reply(struct datapath *dp, struct ofp_switch_features *ofr)
 {
        struct net_bridge_port *p;
        int port_count = 0;
 
-       odh->header.version = OFP_VERSION;
-       odh->header.type    = OFPT_DATA_HELLO;
-       odh->header.xid     = htonl(0);
-       odh->datapath_id    = cpu_to_be64(dp->id); 
-
-       odh->n_exact        = htonl(2 * TABLE_HASH_MAX_FLOWS);
-       odh->n_mac_only     = htonl(TABLE_MAC_MAX_FLOWS);
-       odh->n_compression  = 0;                                           /* Not supported */
-       odh->n_general      = htonl(TABLE_LINEAR_MAX_FLOWS);
-       odh->buffer_mb      = htonl(UINT32_MAX);
-       odh->n_buffers      = htonl(N_PKT_BUFFERS);
-       odh->capabilities   = htonl(OFP_SUPPORTED_CAPABILITIES);
-       odh->actions        = htonl(OFP_SUPPORTED_ACTIONS);
-       odh->miss_send_len  = htons(dp->miss_send_len); 
+       ofr->datapath_id    = cpu_to_be64(dp->id); 
+
+       ofr->n_exact        = htonl(2 * TABLE_HASH_MAX_FLOWS);
+       ofr->n_mac_only     = htonl(TABLE_MAC_MAX_FLOWS);
+       ofr->n_compression  = 0;                                           /* Not supported */
+       ofr->n_general      = htonl(TABLE_LINEAR_MAX_FLOWS);
+       ofr->buffer_mb      = htonl(UINT32_MAX);
+       ofr->n_buffers      = htonl(N_PKT_BUFFERS);
+       ofr->capabilities   = htonl(OFP_SUPPORTED_CAPABILITIES);
+       ofr->actions        = htonl(OFP_SUPPORTED_ACTIONS);
 
        list_for_each_entry_rcu (p, &dp->port_list, node) {
-               fill_port_desc(p, &odh->ports[port_count]);
+               fill_port_desc(p, &ofr->ports[port_count]);
                port_count++;
        }
 
@@ -652,78 +670,43 @@ fill_data_hello(struct datapath *dp, struct ofp_data_hello *odh)
 }
 
 int
-dp_send_hello(struct datapath *dp)
+dp_send_features_reply(struct datapath *dp, const struct sender *sender)
 {
        struct sk_buff *skb;
-       struct nlattr *attr;
-       struct ofp_data_hello *odh;
-       size_t odh_max_len, odh_len, port_max_len, len;
-       void *data;
-       int err = -ENOMEM;
+       struct ofp_switch_features *ofr;
+       size_t ofr_len, port_max_len;
        int port_count;
 
-
-       /* Overallocate, since we can't reliably determine the number of
-        * ports a priori. */
+       /* Overallocate. */
        port_max_len = sizeof(struct ofp_phy_port) * OFPP_MAX;
+       ofr = alloc_openflow_skb(dp, sizeof(*ofr) + port_max_len,
+                                OFPT_FEATURES_REPLY, sender, &skb);
+       if (!ofr)
+               return -ENOMEM;
 
-       len = nla_total_size(sizeof(*odh) + port_max_len) 
-                               + nla_total_size(sizeof(uint32_t));
-
-       skb = genlmsg_new(MAX(len, NLMSG_GOODSIZE), GFP_ATOMIC);
-       if (!skb) {
-               if (net_ratelimit())
-                       printk("dp_send_hello: genlmsg_new failed\n");
-               goto error;
-       }
-
-       data = genlmsg_put(skb, 0, 0, &dp_genl_family, 0,
-                          DP_GENL_C_OPENFLOW);
-       if (data == NULL) {
-               if (net_ratelimit())
-                       printk("dp_send_hello: genlmsg_put failed\n");
-               goto error;
-       }
-
-       NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, dp->dp_idx);
-
-       odh_max_len = sizeof(*odh) + port_max_len;
-       attr = nla_reserve(skb, DP_GENL_A_OPENFLOW, odh_max_len);
-       if (!attr) {
-               if (net_ratelimit())
-                       printk("dp_send_hello: nla_reserve failed\n");
-               goto error;
-       }
-       odh = nla_data(attr);
-       port_count = fill_data_hello(dp, odh);
-
-       /* Only now that we know how many ports we've added can we say
-        * say something about the length. */
-       odh_len = sizeof(*odh) + (sizeof(struct ofp_phy_port) * port_count);
-       odh->header.length = htons(odh_len);
-
-       /* Take back the unused part that was reserved */
-       nla_unreserve(skb, attr, (odh_max_len - odh_len));
+       /* Fill. */
+       port_count = fill_features_reply(dp, ofr);
 
-       err = genlmsg_end(skb, data);
-       if (err < 0) {
-               if (net_ratelimit())
-                       printk("dp_send_hello: genlmsg_end failed\n");
-               goto error;
-       }
-
-       err = genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC);
-       if (err && net_ratelimit())
-               printk(KERN_WARNING "dp_send_hello: genlmsg_multicast failed: %d\n", err);
+       /* Shrink to fit. */
+       ofr_len = sizeof(*ofr) + (sizeof(struct ofp_phy_port) * port_count);
+       resize_openflow_skb(skb, &ofr->header, ofr_len);
+       return send_openflow_skb(skb, sender);
+}
 
-       return err;
+int
+dp_send_config_reply(struct datapath *dp, const struct sender *sender)
+{
+       struct sk_buff *skb;
+       struct ofp_switch_config *osc;
 
-nla_put_failure:
-error:
-       kfree_skb(skb);
-       if (net_ratelimit())
-               printk(KERN_ERR "dp_send_hello: failed to send: %d\n", err);
-       return err;
+       osc = alloc_openflow_skb(dp, sizeof *osc, OFPT_PORT_STATUS, sender,
+                                &skb);
+       if (!osc)
+               return -ENOMEM;
+       memcpy(((char *)osc) + sizeof osc->header,
+              ((char *)&dp->config) + sizeof dp->config.header,
+              sizeof dp->config - sizeof dp->config.header);
+       return send_openflow_skb(skb, sender);
 }
 
 int
@@ -747,132 +730,181 @@ static int
 send_port_status(struct net_bridge_port *p, uint8_t status)
 {
        struct sk_buff *skb;
-       struct nlattr *attr;
        struct ofp_port_status *ops;
-       void *data;
-       int err = -ENOMEM;
-
-
-       skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
-       if (!skb) {
-               if (net_ratelimit())
-                       printk("send_port_status: genlmsg_new failed\n");
-               goto error;
-       }
-
-       data = genlmsg_put(skb, 0, 0, &dp_genl_family, 0,
-                          DP_GENL_C_OPENFLOW);
-       if (data == NULL) {
-               if (net_ratelimit())
-                       printk("send_port_status: genlmsg_put failed\n");
-               goto error;
-       }
-
-       NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, p->dp->dp_idx);
 
-       attr = nla_reserve(skb, DP_GENL_A_OPENFLOW, sizeof(*ops));
-       if (!attr) {
-               if (net_ratelimit())
-                       printk("send_port_status: nla_reserve failed\n");
-               goto error;
-       }
-
-       ops = nla_data(attr);
-       ops->header.version = OFP_VERSION;
-       ops->header.type    = OFPT_PORT_STATUS;
-       ops->header.length  = htons(sizeof(*ops));
-       ops->header.xid     = htonl(0);
-
-       ops->reason         = status;
+       ops = alloc_openflow_skb(p->dp, sizeof *ops, OFPT_PORT_STATUS, NULL,
+                                &skb);
+       if (!ops)
+               return -ENOMEM;
+       ops->reason = status;
        fill_port_desc(p, &ops->desc);
 
-       err = genlmsg_end(skb, data);
-       if (err < 0) {
-               if (net_ratelimit())
-                       printk("send_port_status: genlmsg_end failed\n");
-               goto error;
-       }
-
-       err = genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC);
-       if (err && net_ratelimit())
-               printk(KERN_WARNING "send_port_status: genlmsg_multicast failed: %d\n", err);
-
-       return err;
-
-nla_put_failure:
-error:
-       kfree_skb(skb);
-       if (net_ratelimit())
-               printk(KERN_ERR "send_port_status: failed to send: %d\n", err);
-       return err;
+       return send_openflow_skb(skb, NULL);
 }
 
 int 
 dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow)
 {
        struct sk_buff *skb;
-       struct nlattr *attr;
        struct ofp_flow_expired *ofe;
-       void *data;
        unsigned long duration_j;
-       int err = -ENOMEM;
 
+       ofe = alloc_openflow_skb(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &skb);
+       if (!ofe)
+               return -ENOMEM;
 
-       skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
-       if (!skb) {
-               if (net_ratelimit())
-                       printk("dp_send_flow_expired: genlmsg_new failed\n");
-               goto error;
-       }
+       flow_fill_match(&ofe->match, &flow->key);
+       duration_j = (flow->timeout - HZ * flow->max_idle) - flow->init_time;
+       ofe->duration   = htonl(duration_j / HZ);
+       ofe->packet_count   = cpu_to_be64(flow->packet_count);
+       ofe->byte_count     = cpu_to_be64(flow->byte_count);
+       return send_openflow_skb(skb, NULL);
+}
 
-       data = genlmsg_put(skb, 0, 0, &dp_genl_family, 0,
-                          DP_GENL_C_OPENFLOW);
-       if (data == NULL) {
-               if (net_ratelimit())
-                       printk("dp_send_flow_expired: genlmsg_put failed\n");
-               goto error;
+static void
+fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow,
+               int table_idx)
+{
+       ofs->match.wildcards = htons(flow->key.wildcards);
+       ofs->match.in_port   = flow->key.in_port;
+       memcpy(ofs->match.dl_src, flow->key.dl_src, ETH_ALEN);
+       memcpy(ofs->match.dl_dst, flow->key.dl_dst, ETH_ALEN);
+       ofs->match.dl_vlan   = flow->key.dl_vlan;
+       ofs->match.dl_type   = flow->key.dl_type;
+       ofs->match.nw_src    = flow->key.nw_src;
+       ofs->match.nw_dst    = flow->key.nw_dst;
+       ofs->match.nw_proto  = flow->key.nw_proto;
+       memset(ofs->match.pad, 0, sizeof ofs->match.pad);
+       ofs->match.tp_src    = flow->key.tp_src;
+       ofs->match.tp_dst    = flow->key.tp_dst;
+       ofs->duration        = htonl((jiffies - flow->init_time) / HZ);
+       ofs->table_id        = table_idx;
+       ofs->packet_count    = cpu_to_be64(flow->packet_count);
+       ofs->byte_count      = cpu_to_be64(flow->byte_count);
+}
+
+int
+dp_send_flow_stats(struct datapath *dp, const struct sender *sender,
+                  const struct ofp_match *match)
+{
+       struct sk_buff *skb;
+       struct ofp_flow_stat_reply *fsr;
+       size_t header_size, fudge, flow_size;
+       struct sw_flow_key match_key;
+       int table_idx, n_flows, max_flows;
+
+       header_size = offsetof(struct ofp_flow_stat_reply, flows);
+       fudge = 128;
+       flow_size = sizeof fsr->flows[0];
+       max_flows = (NLMSG_GOODSIZE - header_size - fudge) / flow_size;
+       fsr = alloc_openflow_skb(dp, header_size + max_flows * flow_size,
+                                OFPT_FLOW_STAT_REPLY, sender, &skb);
+       if (!fsr)
+               return -ENOMEM;
+
+       n_flows = 0;
+       flow_extract_match(&match_key, match);
+       for (table_idx = 0; table_idx < dp->chain->n_tables; table_idx++) {
+               struct sw_table *table = dp->chain->tables[table_idx];
+               struct swt_iterator iter;
+
+               if (n_flows >= max_flows) {
+                       break;
+               }
+
+               if (!table->iterator(table, &iter)) {
+                       if (net_ratelimit())
+                               printk("iterator failed for table %d\n",
+                                      table_idx);
+                       continue;
+               }
+
+               for (; iter.flow; table->iterator_next(&iter)) {
+                       if (flow_matches(&match_key, &iter.flow->key)) {
+                               fill_flow_stats(&fsr->flows[n_flows],
+                                               iter.flow, table_idx);
+                               if (++n_flows >= max_flows) {
+                                       break;
+                               }
+                       }
+               }
+               table->iterator_destroy(&iter);
        }
+       resize_openflow_skb(skb, &fsr->header,
+                           header_size + flow_size * n_flows);
+       return send_openflow_skb(skb, sender);
+}
 
-       NLA_PUT_U32(skb, DP_GENL_A_DP_IDX, dp->dp_idx);
+static int 
+fill_port_stat_reply(struct datapath *dp, struct ofp_port_stat_reply *psr)
+{
+       struct net_bridge_port *p;
+       int port_count = 0;
 
-       attr = nla_reserve(skb, DP_GENL_A_OPENFLOW, sizeof(*ofe));
-       if (!attr) {
-               if (net_ratelimit())
-                       printk("dp_send_flow_expired: nla_reserve failed\n");
-               goto error;
+       list_for_each_entry_rcu (p, &dp->port_list, node) {
+               struct ofp_port_stats *ps = &psr->ports[port_count++];
+               struct net_device_stats *stats = p->dev->get_stats(p->dev);
+               ps->port_no = htons(p->port_no);
+               memset(ps->pad, 0, sizeof ps->pad);
+               ps->rx_count = cpu_to_be64(stats->rx_packets);
+               ps->tx_count = cpu_to_be64(stats->tx_packets);
+               ps->drop_count = cpu_to_be64(stats->rx_dropped
+                                            + stats->tx_dropped);
        }
 
-       ofe = nla_data(attr);
-       ofe->header.version = OFP_VERSION;
-       ofe->header.type    = OFPT_FLOW_EXPIRED;
-       ofe->header.length  = htons(sizeof(*ofe));
-       ofe->header.xid     = htonl(0);
+       return port_count;
+}
 
-       flow_fill_match(&ofe->match, &flow->key);
-       duration_j = (flow->timeout - HZ * flow->max_idle) - flow->init_time;
-       ofe->duration   = htonl(duration_j / HZ);
-       ofe->packet_count   = cpu_to_be64(flow->packet_count);
-       ofe->byte_count     = cpu_to_be64(flow->byte_count);
+int
+dp_send_port_stats(struct datapath *dp, const struct sender *sender)
+{
+       struct sk_buff *skb;
+       struct ofp_port_stat_reply *psr;
+       size_t psr_len, port_max_len;
+       int port_count;
 
-       err = genlmsg_end(skb, data);
-       if (err < 0) {
-               if (net_ratelimit())
-                       printk("dp_send_flow_expired: genlmsg_end failed\n");
-               goto error;
-       }
+       /* Overallocate. */
+       port_max_len = sizeof(struct ofp_port_stats) * OFPP_MAX;
+       psr = alloc_openflow_skb(dp, sizeof *psr + port_max_len,
+                                OFPT_PORT_STAT_REPLY, sender, &skb);
+       if (!psr)
+               return -ENOMEM;
 
-       err = genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC);
-       if (err && net_ratelimit())
-               printk(KERN_WARNING "send_flow_expired: genlmsg_multicast failed: %d\n", err);
+       /* Fill. */
+       port_count = fill_port_stat_reply(dp, psr);
 
-       return err;
+       /* Shrink to fit. */
+       psr_len = sizeof *psr + sizeof(struct ofp_port_stats) * port_count;
+       resize_openflow_skb(skb, &psr->header, psr_len);
+       return send_openflow_skb(skb, sender);
+}
 
-nla_put_failure:
-error:
-       kfree_skb(skb);
-       if (net_ratelimit())
-               printk(KERN_ERR "send_flow_expired: failed to send: %d\n", err);
-       return err;
+int
+dp_send_table_stats(struct datapath *dp, const struct sender *sender)
+{
+       struct sk_buff *skb;
+       struct ofp_table_stat_reply *tsr;
+       int i, n_tables;
+
+       n_tables = dp->chain->n_tables;
+       tsr = alloc_openflow_skb(dp, (offsetof(struct ofp_table_stat_reply,
+                                              tables)
+                                     + sizeof tsr->tables[0] * n_tables),
+                                OFPT_TABLE_STAT_REPLY, sender, &skb);
+       if (!tsr)
+               return -ENOMEM;
+       for (i = 0; i < n_tables; i++) {
+               struct ofp_table_stats *ots = &tsr->tables[i];
+               struct sw_table_stats stats;
+               dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
+               strncpy(ots->name, stats.name, sizeof ots->name);
+               ots->table_id = i;
+               ots->pad[0] = ots->pad[1] = 0;
+               ots->max_entries = htonl(stats.max_flows);
+               ots->active_count = htonl(stats.n_flows);
+               ots->matched_count = cpu_to_be64(0); /* FIXME */
+       }
+       return send_openflow_skb(skb, sender);
 }
 
 /* Generic Netlink interface.
@@ -1002,433 +1034,6 @@ nla_put_failure:
        return err;
 }
 
-/*
- * Fill flow entry for nl flow query.  Called with rcu_lock  
- *
- */
-static
-int
-dp_fill_flow(struct ofp_flow_mod* ofm, struct swt_iterator* iter)
-{
-       ofm->header.version  = OFP_VERSION;
-       ofm->header.type     = OFPT_FLOW_MOD;
-       ofm->header.length   = htons(sizeof(struct ofp_flow_mod) 
-                               + sizeof(ofm->actions[0]));
-       ofm->header.xid      = htonl(0);
-
-       ofm->match.wildcards = htons(iter->flow->key.wildcards);
-       ofm->match.in_port   = iter->flow->key.in_port;
-       ofm->match.dl_vlan   = iter->flow->key.dl_vlan;
-       memcpy(ofm->match.dl_src, iter->flow->key.dl_src, ETH_ALEN);
-       memcpy(ofm->match.dl_dst, iter->flow->key.dl_dst, ETH_ALEN);
-       ofm->match.dl_type   = iter->flow->key.dl_type;
-       ofm->match.nw_src    = iter->flow->key.nw_src;
-       ofm->match.nw_dst    = iter->flow->key.nw_dst;
-       ofm->match.nw_proto  = iter->flow->key.nw_proto;
-       ofm->match.tp_src    = iter->flow->key.tp_src;
-       ofm->match.tp_dst    = iter->flow->key.tp_dst;
-       ofm->group_id        = iter->flow->group_id;
-       ofm->max_idle        = iter->flow->max_idle;
-       /* TODO support multiple actions  */
-       ofm->actions[0]      = iter->flow->actions[0];
-
-       return 0;
-}
-
-static int dp_genl_show(struct sk_buff *skb, struct genl_info *info)
-{
-       struct datapath *dp;
-       int err = -ENOMEM;
-       struct sk_buff *ans_skb = NULL;
-       void *data;
-       struct nlattr *attr;
-       struct ofp_data_hello *odh;
-       size_t odh_max_len, odh_len, port_max_len, len;
-       int port_count;
-
-       if (!info->attrs[DP_GENL_A_DP_IDX])
-               return -EINVAL;
-
-       mutex_lock(&dp_mutex);
-       dp = dp_get(nla_get_u32((info->attrs[DP_GENL_A_DP_IDX])));
-       if (!dp)
-               goto error;
-
-       /* Overallocate, since we can't reliably determine the number of
-        * ports a priori. */
-       port_max_len = sizeof(struct ofp_phy_port) * OFPP_MAX;
-
-       len = nla_total_size(sizeof(*odh) + port_max_len)
-                       + nla_total_size(sizeof(uint32_t));
-
-       ans_skb = nlmsg_new(MAX(len, NLMSG_GOODSIZE), GFP_KERNEL);
-       if (!ans_skb)
-               goto error;
-
-       data = genlmsg_put_reply(ans_skb, info, &dp_genl_family,
-                                0, DP_GENL_C_SHOW_DP);
-       if (data == NULL) 
-               goto error;
-
-       NLA_PUT_U32(ans_skb, DP_GENL_A_DP_IDX, dp->dp_idx);
-
-       odh_max_len = sizeof(*odh) + port_max_len;
-       attr = nla_reserve(ans_skb, DP_GENL_A_DP_INFO, odh_max_len);
-       if (!attr)
-               goto error;
-       odh = nla_data(attr);
-       port_count = fill_data_hello(dp, odh);
-
-       /* Only now that we know how many ports we've added can we say
-        * say something about the length. */
-       odh_len = sizeof(*odh) + (sizeof(struct ofp_phy_port) * port_count);
-       odh->header.length = htons(odh_len);
-
-       /* Take back the unused part that was reserved */
-       nla_unreserve(ans_skb, attr, (odh_max_len - odh_len));
-
-       genlmsg_end(ans_skb, data);
-       err = genlmsg_reply(ans_skb, info);
-       if (!err)
-               ans_skb = NULL;
-
-error:
-nla_put_failure:
-       if (ans_skb)
-               kfree_skb(ans_skb);
-       mutex_unlock(&dp_mutex);
-       return err;
-}
-
-static struct genl_ops dp_genl_ops_show_dp = {
-       .cmd = DP_GENL_C_SHOW_DP,
-       .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
-       .policy = dp_genl_policy,
-       .doit = dp_genl_show,
-       .dumpit = NULL,
-};
-
-/* Convenience function */
-static
-void* 
-dp_init_nl_flow_msg(uint32_t dp_idx, uint16_t table_idx, 
-               struct genl_info *info, struct sk_buff* skb)
-{
-       void* data;
-
-       data = genlmsg_put_reply(skb, info, &dp_genl_family, 0, 
-                               DP_GENL_C_QUERY_FLOW);
-       if (data == NULL)
-               return NULL;
-       NLA_PUT_U32(skb, DP_GENL_A_DP_IDX,   dp_idx);
-       NLA_PUT_U16(skb, DP_GENL_A_TABLEIDX, table_idx);
-
-       return data;
-
-nla_put_failure:
-       return NULL;
-}
-
-/*  Iterate through the specified table and send all flow entries over
- *  netlink to userspace.  Each flow message has the following format:
- *
- *  32bit dpix
- *  16bit tabletype
- *  32bit number of flows
- *  openflow-flow-entries
- *
- *  The full table may require multiple messages.  A message with 0 flows
- *  signifies end-of message.
- */
-
-static 
-int 
-dp_dump_table(struct datapath *dp, uint16_t table_idx, struct genl_info *info, struct ofp_flow_mod* matchme) 
-{ 
-       struct sk_buff  *skb = 0; 
-       struct sw_table *table = 0;
-       struct swt_iterator iter;
-       struct sw_flow_key in_flow; 
-       struct nlattr   *attr;
-       int count = 0, sum_count = 0;
-       void *data; 
-       uint8_t* ofm_ptr = 0;
-       struct nlattr   *num_attr; 
-       int err = -ENOMEM;
-
-       table = dp->chain->tables[table_idx]; 
-       if ( table == NULL ) {
-               dprintk("dp::dp_dump_table error, non-existant table at position %d\n", table_idx);
-               return -EINVAL;
-       }
-
-       if (!table->iterator(table, &iter)) {
-               dprintk("dp::dp_dump_table couldn't initialize empty table iterator\n");
-               return -ENOMEM;
-       }
-
-       while (iter.flow) {
-
-               /* verify that we can fit all NL_FLOWS_PER_MESSAGE in a single
-                * sk_buf */
-               if( (sizeof(dp_genl_family) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint32_t) + 
-                                       (NL_FLOWS_PER_MESSAGE * sizeof(struct ofp_flow_mod))) > (8192 - 64)){
-                       dprintk("dp::dp_dump_table NL_FLOWS_PER_MESSAGE may cause overrun in skbuf\n");
-                       return -ENOMEM;
-               }
-
-               skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
-               if (skb == NULL) {
-                       return -ENOMEM;
-               }
-
-               data = dp_init_nl_flow_msg(dp->dp_idx, table_idx, info, skb);
-               if (data == NULL){
-                       err= -ENOMEM;   
-                       goto error_free_skb;
-               } 
-
-               /* reserve space to put the number of flows for this message, to
-                * be filled after the loop*/
-               num_attr = nla_reserve(skb, DP_GENL_A_NUMFLOWS, sizeof(uint32_t));
-               if(!num_attr){
-                       err = -ENOMEM;
-                       goto error_free_skb;
-               }
-
-               /* Only load NL_FLOWS_PER_MESSAGE flows at a time */
-               attr = nla_reserve(skb, DP_GENL_A_FLOW, 
-                               (sizeof(struct ofp_flow_mod) + sizeof(struct ofp_action)) * NL_FLOWS_PER_MESSAGE);
-               if (!attr){
-                       err = -ENOMEM;
-                       goto error_free_skb;
-               }
-
-               /* internal loop to fill NL_FLOWS_PER_MESSAGE flows */
-               ofm_ptr = nla_data(attr);
-               flow_extract_match(&in_flow, &matchme->match);
-               while (iter.flow && count < NL_FLOWS_PER_MESSAGE) {
-                       if(flow_matches(&in_flow, &iter.flow->key)){
-                               if((err = dp_fill_flow((struct ofp_flow_mod*)ofm_ptr, &iter))) 
-                                       goto error_free_skb;
-                               count++; 
-                               /* TODO support multiple actions  */
-                               ofm_ptr += sizeof(struct ofp_flow_mod) + sizeof(struct ofp_action);
-                       }
-                       table->iterator_next(&iter);
-               }
-
-               *((uint32_t*)nla_data(num_attr)) = count;
-               genlmsg_end(skb, data); 
-
-               sum_count += count; 
-               count = 0;
-
-               err = genlmsg_unicast(skb, info->snd_pid); 
-               skb = 0;
-       }
-
-       /* send a sentinal message saying we're done */
-       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
-       if (skb == NULL) {
-               return -ENOMEM;
-       }
-       data = dp_init_nl_flow_msg(dp->dp_idx, table_idx, info, skb);
-       if (data == NULL){
-               err= -ENOMEM;   
-               goto error_free_skb;
-       } 
-
-       NLA_PUT_U32(skb, DP_GENL_A_NUMFLOWS,   0);
-       /* dummy flow so nl doesn't complain */
-       attr = nla_reserve(skb, DP_GENL_A_FLOW, sizeof(struct ofp_flow_mod));
-       if (!attr){
-               err = -ENOMEM;
-               goto error_free_skb;
-       }
-       genlmsg_end(skb, data); 
-       err = genlmsg_reply(skb, info); skb = 0;
-
-nla_put_failure:
-error_free_skb:
-       if(skb)
-               kfree_skb(skb);
-       return err;
-}
-
-/* Helper function to query_table which creates and sends a message packed with
- * table stats.  Message form is:
- *
- * u32 DP_IDX
- * u32 NUM_TABLES
- * OFP_TABLE (list of OFP_TABLES)
- *
- */
-
-static 
-int 
-dp_dump_table_stats(struct datapath *dp, int dp_idx, struct genl_info *info) 
-{ 
-       struct sk_buff   *skb = 0; 
-       struct ofp_table *ot = 0;
-       struct nlattr   *attr;
-       struct sw_table_stats stats; 
-       size_t len;
-       void *data; 
-       int err = -ENOMEM;
-       int i = 0;
-       int nt = dp->chain->n_tables;
-
-       len = 4 + 4 + (sizeof(struct ofp_table) * nt);
-
-       /* u32 IDX, u32 NUMTABLES, list-of-tables */
-       skb = nlmsg_new(MAX(len, NLMSG_GOODSIZE), GFP_ATOMIC);
-       if (skb == NULL) {
-               return -ENOMEM;
-       }
-       
-       data = genlmsg_put_reply(skb, info, &dp_genl_family, 0, 
-                               DP_GENL_C_QUERY_TABLE);
-       if (data == NULL){
-               return -ENOMEM;
-       } 
-
-       NLA_PUT_U32(skb, DP_GENL_A_DP_IDX,      dp_idx);
-       NLA_PUT_U32(skb, DP_GENL_A_NUMTABLES, nt);
-
-       /* ... we assume that all tables can fit in a single message.
-        * Probably a reasonable assumption seeing that we only have
-        * 3 atm */
-       attr = nla_reserve(skb, DP_GENL_A_TABLE, (sizeof(struct ofp_table) * nt));
-       if (!attr){
-               err = -ENOMEM;
-               goto error_free_skb;
-       }
-
-       ot = nla_data(attr);
-
-       for (i = 0; i < nt; ++i) {
-               dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
-               ot->header.version = OFP_VERSION;
-               ot->header.type    = OFPT_TABLE;
-               ot->header.length  = htons(sizeof(struct ofp_table));
-               ot->header.xid     = htonl(0);
-
-               strncpy(ot->name, stats.name, OFP_MAX_TABLE_NAME_LEN); 
-               ot->table_id  = htons(i);
-               ot->n_flows   = htonl(stats.n_flows);
-               ot->max_flows = htonl(stats.max_flows);
-               ot++;
-       }
-
-       genlmsg_end(skb, data); 
-       err = genlmsg_reply(skb, info); skb = 0;
-
-nla_put_failure:
-error_free_skb:
-       if(skb)
-               kfree_skb(skb);
-       return err;
-}
-
-/* 
- * Queries a datapath for flow-table statistics 
- */
-
-
-static int dp_genl_table_query(struct sk_buff *skb, struct genl_info *info)
-{
-       struct   datapath* dp;
-       int       err = 0;
-
-       if (!info->attrs[DP_GENL_A_DP_IDX]) {
-               dprintk("dp::dp_genl_table_query received message with missing attributes\n");
-               return -EINVAL;
-       }
-
-       rcu_read_lock();
-       dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
-       if (!dp) {
-               err = -ENOENT;
-               goto err_out;
-       }
-
-       err = dp_dump_table_stats(dp, nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]), info); 
-
-err_out:
-       rcu_read_unlock();
-       return err;
-}
-
-/* 
- * Queries a datapath for flow-table entries.
- */
-
-static int dp_genl_flow_query(struct sk_buff *skb, struct genl_info *info)
-{
-       struct datapath* dp;
-       struct ofp_flow_mod*  ofm;
-       u16     table_idx;
-       int     err = 0;
-
-       if (!info->attrs[DP_GENL_A_DP_IDX]
-                               || !info->attrs[DP_GENL_A_TABLEIDX]
-                               || !info->attrs[DP_GENL_A_FLOW]) {
-               dprintk("dp::dp_genl_flow_query received message with missing attributes\n");
-               return -EINVAL;
-       }
-
-       rcu_read_lock();
-       dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
-       if (!dp) {
-               err = -ENOENT;
-               goto err_out;
-       }
-
-       table_idx = nla_get_u16(info->attrs[DP_GENL_A_TABLEIDX]);
-
-       if (dp->chain->n_tables <= table_idx){
-               printk("table index %d invalid (dp has %d tables)\n",
-                               table_idx, dp->chain->n_tables);
-       err = -EINVAL;
-               goto err_out;
-       }
-
-       ofm = nla_data(info->attrs[DP_GENL_A_FLOW]);
-       err = dp_dump_table(dp, table_idx, info, ofm); 
-
-err_out:
-       rcu_read_unlock();
-       return err;
-}
-
-static struct nla_policy dp_genl_flow_policy[DP_GENL_A_MAX + 1] = {
-       [DP_GENL_A_DP_IDX]      = { .type = NLA_U32 },
-       [DP_GENL_A_TABLEIDX] = { .type = NLA_U16 },
-       [DP_GENL_A_NUMFLOWS]  = { .type = NLA_U32 },
-};
-
-static struct genl_ops dp_genl_ops_query_flow = {
-       .cmd    = DP_GENL_C_QUERY_FLOW,
-       .flags  = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
-       .policy = dp_genl_flow_policy,
-       .doit   = dp_genl_flow_query,
-       .dumpit = NULL,
-};
-
-static struct nla_policy dp_genl_table_policy[DP_GENL_A_MAX + 1] = {
-       [DP_GENL_A_DP_IDX]      = { .type = NLA_U32 },
-};
-
-static struct genl_ops dp_genl_ops_query_table = {
-       .cmd    = DP_GENL_C_QUERY_TABLE,
-       .flags  = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
-       .policy = dp_genl_table_policy,
-       .doit   = dp_genl_table_query,
-       .dumpit = NULL,
-};
-
-
 static struct genl_ops dp_genl_ops_query_dp = {
        .cmd = DP_GENL_C_QUERY_DP,
        .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
@@ -1500,6 +1105,8 @@ static int dp_genl_openflow(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr *va = info->attrs[DP_GENL_A_OPENFLOW];
        struct datapath *dp;
+       struct ofp_header *oh;
+       struct sender sender;
        int err;
 
        if (!info->attrs[DP_GENL_A_DP_IDX] || !va)
@@ -1512,9 +1119,16 @@ static int dp_genl_openflow(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       va = info->attrs[DP_GENL_A_OPENFLOW];
+       if (nla_len(va) < sizeof(struct ofp_header)) {
+               err = -EINVAL;
+               goto out;
+       }
+       oh = nla_data(va);
 
-       err = fwd_control_input(dp->chain, nla_data(va), nla_len(va));
+       sender.xid = oh->xid;
+       sender.pid = info->snd_pid;
+       sender.seq = info->snd_seq;
+       err = fwd_control_input(dp->chain, &sender, nla_data(va), nla_len(va));
 
 out:
        rcu_read_unlock();
@@ -1553,9 +1167,6 @@ static struct genl_ops *dp_genl_all_ops[] = {
         * front. */
        &dp_genl_ops_openflow,
 
-       &dp_genl_ops_query_flow,
-       &dp_genl_ops_query_table,
-       &dp_genl_ops_show_dp,
        &dp_genl_ops_add_dp,
        &dp_genl_ops_del_dp,
        &dp_genl_ops_query_dp,
index b478eaef6747dbb41015288a6cdd5b2f03648225..d81e2f4cffe9dbfd3e0e6b99ca1f5a75a2cabfee 100644 (file)
@@ -48,23 +48,31 @@ struct datapath {
        struct net_device dev;
        struct net_device_stats stats;
 
-       /* Flags from the control hello message */
-       uint16_t hello_flags;
-
-       /* Maximum number of bytes that should be sent for flow misses */
-       uint16_t miss_send_len;
+       struct ofp_switch_config config;
 
        /* Switch ports. */
        struct net_bridge_port *ports[OFPP_MAX];
        struct list_head port_list; /* List of ports, for flooding. */
 };
 
+/* Information necessary to reply to the sender of an OpenFlow message. */
+struct sender {
+       uint32_t xid;           /* OpenFlow transaction ID of request. */
+       uint32_t pid;           /* Netlink process ID of sending socket. */
+       uint32_t seq;           /* Netlink sequence ID of request. */
+};
+
 int dp_output_port(struct datapath *, struct sk_buff *, int out_port);
 int dp_output_control(struct datapath *, struct sk_buff *,
                           uint32_t buffer_id, size_t max_len, int reason);
 int dp_set_origin(struct datapath *, uint16_t, struct sk_buff *);
-int dp_send_hello(struct datapath *);
+int dp_send_features_reply(struct datapath *, const struct sender *);
+int dp_send_config_reply(struct datapath *, const struct sender *);
 int dp_send_flow_expired(struct datapath *, struct sw_flow *);
+int dp_send_flow_stats(struct datapath *, const struct sender *,
+                      const struct ofp_match *);
+int dp_send_table_stats(struct datapath *, const struct sender *);
+int dp_send_port_stats(struct datapath *, const struct sender *);
 int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
 
 /* Should hold at least RCU read lock when calling */
index 65a98aebc4eb3b10a3022892084bbfcad283f027..7e987e338cf79c46fe29ee1e3ab0cd05d2e7b297 100644 (file)
@@ -41,7 +41,8 @@ void fwd_port_input(struct sw_chain *chain, struct sk_buff *skb, int in_port)
                                flow->actions, flow->n_actions);
        } else {
                dp_output_control(chain->dp, skb, fwd_save_skb(skb), 
-                               chain->dp->miss_send_len, OFPR_NO_MATCH);
+                                 ntohs(chain->dp->config.miss_send_len),
+                                 OFPR_NO_MATCH);
        }
 }
 
@@ -256,25 +257,31 @@ struct sk_buff *execute_setter(struct sk_buff *skb, uint16_t eth_proto,
 }
 
 static int
-recv_control_hello(struct sw_chain *chain, const void *msg)
+recv_features_request(struct sw_chain *chain, const struct sender *sender,
+                     const void *msg) 
 {
-       const struct ofp_control_hello *och = msg;
-
-       printk("control_hello(version=%d)\n", ntohl(och->version));
-
-       if (ntohs(och->miss_send_len) != OFP_MISS_SEND_LEN_UNCHANGED) {
-               chain->dp->miss_send_len = ntohs(och->miss_send_len);
-       }
-
-       chain->dp->hello_flags = ntohs(och->flags);
+       return dp_send_features_reply(chain->dp, sender);
+}
 
-       dp_send_hello(chain->dp);
+static int
+recv_get_config_request(struct sw_chain *chain, const struct sender *sender,
+                       const void *msg)
+{
+       return dp_send_config_reply(chain->dp, sender);
+}
 
+static int
+recv_set_config(struct sw_chain *chain, const struct sender *sender,
+               const void *msg)
+{
+       const struct ofp_switch_config *osc = msg;
+       chain->dp->config = *osc;
        return 0;
 }
 
 static int
-recv_packet_out(struct sw_chain *chain, const void *msg)
+recv_packet_out(struct sw_chain *chain, const struct sender *sender,
+               const void *msg)
 {
        const struct ofp_packet_out *opo = msg;
        struct sk_buff *skb;
@@ -322,7 +329,8 @@ recv_packet_out(struct sw_chain *chain, const void *msg)
 }
 
 static int
-recv_port_mod(struct sw_chain *chain, const void *msg)
+recv_port_mod(struct sw_chain *chain, const struct sender *sender,
+             const void *msg)
 {
        const struct ofp_port_mod *opm = msg;
 
@@ -406,7 +414,7 @@ error:
 }
 
 static int
-recv_flow(struct sw_chain *chain, const void *msg)
+recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg)
 {
        const struct ofp_flow_mod *ofm = msg;
        uint16_t command = ntohs(ofm->command);
@@ -426,21 +434,58 @@ recv_flow(struct sw_chain *chain, const void *msg)
        }
 }
 
-/* 'msg', which is 'length' bytes long, was received from the control path.
- * Apply it to 'chain'. */
+static int
+recv_flow_status_request(struct sw_chain *chain, const struct sender *sender,
+                        const void *msg)
+{
+       const struct ofp_flow_stat_request *fsr = msg;
+       if (fsr->type == OFPFS_INDIV) {
+               return dp_send_flow_stats(chain->dp, sender, &fsr->match); 
+       } else {
+               /* FIXME */
+               return -ENOTSUPP;
+       }
+}
+
+static int
+recv_port_status_request(struct sw_chain *chain, const struct sender *sender,
+                        const void *msg)
+{
+       return dp_send_port_stats(chain->dp, sender);
+}
+
+static int
+recv_table_status_request(struct sw_chain *chain, const struct sender *sender,
+                         const void *msg)
+{
+       return dp_send_table_stats(chain->dp, sender);
+}
+
+/* 'msg', which is 'length' bytes long, was received across Netlink from
+ * 'sender'.  Apply it to 'chain'. */
 int
-fwd_control_input(struct sw_chain *chain, const void *msg, size_t length)
+fwd_control_input(struct sw_chain *chain, const struct sender *sender,
+                 const void *msg, size_t length)
 {
 
        struct openflow_packet {
                size_t min_size;
-               int (*handler)(struct sw_chain *, const void *);
+               int (*handler)(struct sw_chain *, const struct sender *,
+                              const void *);
        };
 
        static const struct openflow_packet packets[] = {
-               [OFPT_CONTROL_HELLO] = {
-                       sizeof (struct ofp_control_hello),
-                       recv_control_hello,
+               [OFPT_FEATURES_REQUEST] = {
+                       sizeof (struct ofp_header),
+                       recv_features_request,
+               },
+               [OFPT_GET_CONFIG_REQUEST] = {
+                       sizeof (struct ofp_header),
+                       recv_get_config_request,
+               },
+               [OFPT_SET_CONFIG] = {
+                       sizeof (struct ofp_switch_config),
+                       recv_set_config,
                },
                [OFPT_PACKET_OUT] = {
                        sizeof (struct ofp_packet_out),
@@ -454,14 +499,23 @@ fwd_control_input(struct sw_chain *chain, const void *msg, size_t length)
                        sizeof (struct ofp_port_mod),
                        recv_port_mod,
                },
+               [OFPT_FLOW_STAT_REQUEST] = {
+                       sizeof (struct ofp_flow_stat_request),
+                       recv_flow_status_request,
+               },
+               [OFPT_PORT_STAT_REQUEST] = {
+                       sizeof (struct ofp_port_stat_request),
+                       recv_port_status_request,
+               },
+               [OFPT_TABLE_STAT_REQUEST] = {
+                       sizeof (struct ofp_table_stat_request),
+                       recv_table_status_request,
+               },
        };
 
        const struct openflow_packet *pkt;
        struct ofp_header *oh;
 
-       if (length < sizeof(struct ofp_header))
-               return -EINVAL;
-
        oh = (struct ofp_header *) msg;
        if (oh->version != 1 || oh->type >= ARRAY_SIZE(packets)
                || ntohs(oh->length) > length)
@@ -473,7 +527,7 @@ fwd_control_input(struct sw_chain *chain, const void *msg, size_t length)
        if (length < pkt->min_size)
                return -EFAULT;
 
-       return pkt->handler(chain, msg);
+       return pkt->handler(chain, sender, msg);
 }
 
 /* Packet buffering. */
index f061d2da19569026be397c3db19a07273e1b9c28..ce4d3b145f5e6a041fcb6f6822157609e4d83a9f 100644 (file)
@@ -8,6 +8,7 @@
 struct sk_buff;
 struct sw_chain;
 struct ofp_action;
+struct sender;
 
 /* Buffers are identified to userspace by a 31-bit opaque ID.  We divide the ID
  * into a buffer number (low bits) and a cookie (high bits).  The buffer number
@@ -22,7 +23,8 @@ struct ofp_action;
 
 
 void fwd_port_input(struct sw_chain *, struct sk_buff *, int in_port);
-int fwd_control_input(struct sw_chain *, const void *, size_t);
+int fwd_control_input(struct sw_chain *, const struct sender *,
+                     const void *, size_t);
 
 uint32_t fwd_save_skb(struct sk_buff *skb);
 
index 6f3e419628984e0edb85ccb5b3e3ef14fb88fd91..7847ef139842ae99dcb0f1019245bfb8e9691904 100644 (file)
@@ -123,7 +123,7 @@ static int table_hash_timeout(struct datapath *dp, struct sw_table *swt)
                struct sw_flow *flow = *bucket;
                if (flow && flow_timeout(flow)) {
                        count += do_delete(bucket, flow); 
-                       if (dp->hello_flags & OFP_CHELLO_SEND_FLOW_EXP)
+                       if (dp->config.flags & OFPC_SEND_FLOW_EXP)
                                dp_send_flow_expired(dp, flow);
                }
        }
index fa4f03e5be0b3e3ed8033b97b64b30d4b23cd11b..0b60838ac9c23ccd6096251f979ffb9949b9ef57 100644 (file)
@@ -102,7 +102,7 @@ static int table_linear_timeout(struct datapath *dp, struct sw_table *swt)
                struct sw_flow *flow = list_entry(pos, struct sw_flow, u.node);
                if (flow_timeout(flow)) {
                        count += do_delete(swt, flow);
-                       if (dp->hello_flags & OFP_CHELLO_SEND_FLOW_EXP)
+                       if (dp->config.flags & OFPC_SEND_FLOW_EXP)
                                dp_send_flow_expired(dp, flow);
                }
        }
index 409b2d636b8e0f3d536600a7b7c70e4740c4bff8..0b7ddbe96fef9babb8b5c4525afee83b16b97aea 100644 (file)
@@ -16,6 +16,7 @@ noinst_HEADERS = \
        packets.h \
        poll-loop.h \
        queue.h \
+       rconn.h \
        socket-util.h \
        util.h \
        vconn.h \
index 2660834786af92d3a56eb1ad8904591906f03881..0d024ec9e803e3f930ef9ebf09152ca80d0b6c1d 100644 (file)
@@ -68,7 +68,7 @@ void *buffer_tail(const struct buffer *);
 void *buffer_end(const struct buffer *);
 
 void *buffer_put_uninit(struct buffer *, size_t);
-void buffer_put(struct buffer *, const void *, size_t);
+void *buffer_put(struct buffer *, const void *, size_t);
 void *buffer_push_uninit(struct buffer *b, size_t);
 
 size_t buffer_headroom(struct buffer *);
index 3fd3f6126803df53d374db490f2c7e750b5a4e0d..4f806d7aa872e89151ca06b6263ca981f1ec9763 100644 (file)
@@ -60,9 +60,6 @@ int dpif_add_dp(struct dpif *);
 int dpif_del_dp(struct dpif *);
 int dpif_add_port(struct dpif *, const char *netdev);
 int dpif_del_port(struct dpif *, const char *netdev);
-int dpif_show(struct dpif *);
-int dpif_dump_tables(struct dpif *);
-int dpif_dump_flows(struct dpif *, int table, struct ofp_match *);
 int dpif_benchmark_nl(struct dpif *, uint32_t, uint32_t);
 
 #endif /* dpif.h */
index c23dfb7e732829db5fbedcdd8ff3d2702874e30b..e43d006aa6f8013686f04bb091f7712df2fd9741 100644 (file)
@@ -35,7 +35,9 @@
 #define DYNAMIC_STRING_H 1
 
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stddef.h>
+#include <stdint.h>
 #include "compiler.h"
 
 struct ds {
@@ -48,9 +50,13 @@ struct ds {
 
 void ds_init(struct ds *);
 void ds_reserve(struct ds *, size_t min_length);
+void ds_put_char(struct ds *, char);
+void ds_put_cstr(struct ds *, const char *);
 void ds_put_format(struct ds *, const char *, ...) PRINTF_FORMAT(2, 3);
 void ds_put_format_valist(struct ds *, const char *, va_list)
     PRINTF_FORMAT(2, 0);
+void ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size,
+                     uintptr_t ofs, bool ascii);
 char *ds_cstr(struct ds *);
 void ds_destroy(struct ds *);
 
index 36981decdd7b4ba82315ff0ba01fe993a7284842..76ff9248d1fc8fbcaa6c8946da43db4a2acf7a53 100644 (file)
 #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
 }
index d9862b0bab420f19dbc3c1d9851ab291045b1b20..e7494777881a6b4916d8df46c7616ce98a0efbd2 100644 (file)
 /* Attributes that can be attached to the datapath's netlink messages. */
 enum {
        DP_GENL_A_UNSPEC,
-    DP_GENL_A_OFPHEADER, /* OFP header information */
        DP_GENL_A_DP_IDX,        /* Datapath Ethernet device name. */
        DP_GENL_A_PORTNAME,      /* Device name for datapath port. */
        DP_GENL_A_MC_GROUP,      /* Generic netlink multicast group. */
        DP_GENL_A_OPENFLOW,  /* OpenFlow packet. */
 
-    DP_GENL_A_DP_INFO,   /* OpenFlow datapath information */
-
-    DP_GENL_A_FLOW,      /* OpenFlow flow entry */
-    DP_GENL_A_NUMFLOWS,  /* Number of flows  */
-    DP_GENL_A_TABLEIDX,  /* Flow table index */
-
-    DP_GENL_A_TABLE,     /* OpenFlow table entry */
-    DP_GENL_A_NUMTABLES, /* Number of tables in a table query */
-
     DP_GENL_A_NPACKETS,  /* Number of packets to send up netlink */
     DP_GENL_A_PSIZE,     /* Size of packets to send up netlink */
 
@@ -70,27 +60,14 @@ enum dp_genl_command {
        DP_GENL_C_ADD_DP,        /* Create datapath. */
        DP_GENL_C_DEL_DP,        /* Destroy datapath. */
        DP_GENL_C_QUERY_DP,      /* Get multicast group for datapath. */
-       DP_GENL_C_SHOW_DP,       /* Show information about datapath. */
        DP_GENL_C_ADD_PORT,      /* Add port to datapath. */
        DP_GENL_C_DEL_PORT,      /* Remove port from datapath. */
        DP_GENL_C_OPENFLOW,  /* Encapsulated OpenFlow protocol. */
 
-    DP_GENL_C_QUERY_FLOW,  /* Request flow entries. */
-    DP_GENL_C_QUERY_TABLE, /* Request table entries. */
-
     DP_GENL_C_BENCHMARK_NL, /* Benchmark netlink connection */
 
        __DP_GENL_C_MAX,
        DP_GENL_C_MAX = __DP_GENL_C_MAX - 1
 };
 
-/* Table */
-enum {
-    TBL_MACONLY,
-    TBL_HASH,
-    TBL_LINEAR,
-     __TBL_MAX,
-     TBL_MAX = __TBL_MAX - 1
-};
-
 #endif /* openflow_netlink_h */
index 9c4690668386d31fc30684225b46a29aec85cd43..7462b884fd3ca59e3561ff43de4349d3df164dbc 100644 (file)
@@ -43,7 +43,7 @@
 #endif
 
 /* Maximum length of a OpenFlow packet. */
-#define OFP_MAXLEN (sizeof(struct ofp_data_hello) \
+#define OFP_MAXLEN (sizeof(struct ofp_switch_features) \
         + (sizeof(struct ofp_phy_port) * OFPP_MAX) + 200)
 
 #define OFP_VERSION   1
@@ -75,21 +75,24 @@ enum ofp_port {
 };
 
 enum ofp_type {
-    OFPT_CONTROL_HELLO,      /* 0  Controller/switch message */
-    OFPT_DATA_HELLO,         /* 1  Controller/switch message */
-    OFPT_PACKET_IN,          /* 2  Async message */
-    OFPT_PACKET_OUT,         /* 3  Controller/switch message */
-    OFPT_FLOW_MOD,           /* 4  Controller/switch message */
-    OFPT_FLOW_EXPIRED,       /* 5  Async message */
-    OFPT_TABLE,              /* 6  Controller/switch message */
-    OFPT_PORT_MOD,           /* 7  Controller/switch message */
-    OFPT_PORT_STATUS,        /* 8  Async message */
-    OFPT_FLOW_STAT_REQUEST,  /* 9  Controller/switch message */
-    OFPT_FLOW_STAT_REPLY,    /* 10 Controller/switch message */
-    OFPT_TABLE_STAT_REQUEST, /* 11 Controller/switch message */
-    OFPT_TABLE_STAT_REPLY,   /* 12 Controller/switch message */
-    OFPT_PORT_STAT_REQUEST,  /* 13 Controller/switch message */
-    OFPT_PORT_STAT_REPLY     /* 14 Controller/switch message */
+    OFPT_FEATURES_REQUEST,   /*  0 Controller/switch message */
+    OFPT_FEATURES_REPLY,     /*  1 Controller/switch message */
+    OFPT_GET_CONFIG_REQUEST, /*  2 Controller/switch message */
+    OFPT_GET_CONFIG_REPLY,   /*  3 Controller/switch message */
+    OFPT_SET_CONFIG,         /*  4 Controller/switch message */
+    OFPT_PACKET_IN,          /*  5 Async message */
+    OFPT_PACKET_OUT,         /*  6 Controller/switch message */
+    OFPT_FLOW_MOD,           /*  7 Controller/switch message */
+    OFPT_FLOW_EXPIRED,       /*  8 Async message */
+    OFPT_TABLE,              /*  9 Controller/switch message */
+    OFPT_PORT_MOD,           /* 10 Controller/switch message */
+    OFPT_PORT_STATUS,        /* 11 Async message */
+    OFPT_FLOW_STAT_REQUEST,  /* 12 Controller/switch message */
+    OFPT_FLOW_STAT_REPLY,    /* 13 Controller/switch message */
+    OFPT_TABLE_STAT_REQUEST, /* 14 Controller/switch message */
+    OFPT_TABLE_STAT_REPLY,   /* 15 Controller/switch message */
+    OFPT_PORT_STAT_REQUEST,  /* 16 Controller/switch message */
+    OFPT_PORT_STAT_REPLY     /* 17 Controller/switch message */
 };
 
 /* Header on all OpenFlow packets. */
@@ -103,22 +106,18 @@ struct ofp_header {
 };
 
 #define OFP_DEFAULT_MISS_SEND_LEN   128
-#define OFP_MISS_SEND_LEN_UNCHANGED 0xffff
 
-/* Flag to indicate that datapath should notify the controller of
- * expired flow entries.
- */
-#define OFP_CHELLO_SEND_FLOW_EXP 0x0001
+enum ofp_config_flags {
+    /* Tells datapath to notify the controller of expired flow entries. */
+    OFPC_SEND_FLOW_EXP = 1 << 0
+};
 
-/* Controller hello (controller -> datapath). */
-struct ofp_control_hello {
+/* Switch configuration. */
+struct ofp_switch_config {
     struct ofp_header header;
-    uint32_t version;         /* Max supported protocol version (?) */
-    uint16_t flags;           
-    uint16_t miss_send_len;   /* Max bytes of new flow that datapath should 
-                                 send to the controller.  A value of 
-                                 OFP_MISS_SEND_LEN_UNCHANGED leaves the 
-                                 currently configured value unchanged. */
+    uint16_t flags;             /* OFPC_* flags. */
+    uint16_t miss_send_len;     /* Max bytes of new flow that datapath should
+                                   send to the controller. */
 };
 
 /* Capabilities supported by the datapath. */
@@ -158,8 +157,8 @@ struct ofp_phy_port {
     uint32_t features;      /* Bitmap of supported "ofp_port_features"s. */
 };
 
-/* Datapath hello (datapath -> controller). */
-struct ofp_data_hello {
+/* Switch features. */
+struct ofp_switch_features {
     struct ofp_header header;
     uint64_t datapath_id;   /* Datapath unique ID */
 
@@ -177,15 +176,9 @@ struct ofp_data_hello {
     uint32_t capabilities;  /* Bitmap of support "ofp_capabilities". */
     uint32_t actions;       /* Bitmap of supported "ofp_action_type"s. */
 
-    /* Miscellany */
-    uint16_t miss_send_len; /* Currently configured value for max bytes 
-                               of new flow that datapath will send to the 
-                               controller. */
-    uint8_t pad[2];         /* Align to 32-bits */
-
     /* Port info.*/
     struct ofp_phy_port ports[0];   /* Port definitions.  The number of ports
-                                      is inferred from the length field in 
+                                      is inferred from the length field in
                                       the header. */
 };
 
@@ -361,8 +354,10 @@ struct ofp_flow_stats {
     struct ofp_match match;   /* Description of fields */
     uint32_t duration;        /* Time flow has been alive in seconds.  Only 
                                  used for non-aggregated results. */
-    uint64_t packet_count;    
-    uint64_t byte_count;
+    uint64_t packet_count;    /* Number of packets in flow. */
+    uint64_t byte_count;      /* Number of bytes in flow. */
+    uint8_t table_id;         /* ID of table flow came from. */
+    uint8_t pad[7];           /* Align to 64-bits. */
 };
 
 enum ofp_stat_type {
@@ -374,8 +369,10 @@ enum ofp_stat_type {
 struct ofp_flow_stat_request {
     struct ofp_header header;
     struct ofp_match match;   /* Fields to match */
+    uint8_t table_id;         /* ID of table to read (from ofp_table_stats)
+                                 or 0xffff for all tables. */
     uint8_t type;             /* One of OFPFS_ */
-    uint8_t pad[3];           /* Align to 32-bits */
+    uint16_t pad;               /* Align to 32-bits */
 };
 
 /* Current flow statistics reply */
@@ -392,13 +389,47 @@ struct ofp_flow_stat_reply {
     struct ofp_flow_stats flows[0];  
 };
 
-/* Table attributes collected at runtime */
-struct ofp_table {
+/* Current table statistics request */
+struct ofp_table_stat_request {
+    struct ofp_header header;
+};
+
+/* Statistics about a particular table */
+struct ofp_table_stats {
+    uint8_t table_id;
+    uint8_t pad[3];          /* Align to 32-bits */
+    char name[OFP_MAX_TABLE_NAME_LEN];
+    uint32_t max_entries;    /* Max number of entries supported */
+    uint32_t active_count;   /* Number of active entries */
+    uint64_t matched_count;  /* Number of packets that hit table */
+};
+
+/* Current table statistics reply */
+struct ofp_table_stat_reply {
+    struct ofp_header header;
+    struct ofp_table_stats tables[]; /* The number of entries is inferred from
+                                        the length field in the header. */
+};
+
+/* Statistics about a particular port */
+struct ofp_port_stats {
+    uint16_t port_no;
+    uint8_t pad[2];          /* Align to 32-bits */
+    uint64_t rx_count;     /* Number of received packets */
+    uint64_t tx_count;     /* Number of transmitted packets */
+    uint64_t drop_count; /* Number of packets dropped by interface */
+};
+
+/* Current port statistics request */
+struct ofp_port_stat_request {
+    struct ofp_header header;
+};
+
+/* Current port statistics reply */
+struct ofp_port_stat_reply {
     struct ofp_header header;
-    char              name[OFP_MAX_TABLE_NAME_LEN];
-    uint16_t          table_id;
-    unsigned long int n_flows;
-    unsigned long int max_flows;
+    struct ofp_port_stats ports[]; /* The number of entries is inferred from
+                                      the length field in the header. */
 };
 
 #endif /* openflow.h */
diff --git a/include/rconn.h b/include/rconn.h
new file mode 100644 (file)
index 0000000..4049e74
--- /dev/null
@@ -0,0 +1,70 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * 
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef RCONN_H
+#define RCONN_H 1
+
+#include "queue.h"
+#include <stdbool.h>
+#include <time.h>
+
+/* A wrapper around vconn that provides queuing and optionally reliability.
+ *
+ * An rconn maintains a message transmission queue of bounded length specified
+ * by the caller.  The rconn does not guarantee reliable delivery of
+ * queued messages: all queued messages are dropped when reconnection becomes
+ * necessary.
+ *
+ * An rconn optionally provides reliable communication, in this sense: the
+ * rconn will re-connect, with exponential backoff, when the underlying vconn
+ * disconnects.
+ */
+
+struct vconn;
+
+struct rconn *rconn_new(const char *name, int txq_limit);
+struct rconn *rconn_new_from_vconn(const char *name, int txq_limit,
+                                   struct vconn *);
+void rconn_destroy(struct rconn *);
+
+void rconn_run(struct rconn *);
+void rconn_run_wait(struct rconn *);
+struct buffer *rconn_recv(struct rconn *);
+void rconn_recv_wait(struct rconn *);
+int rconn_send(struct rconn *, struct buffer *);
+void rconn_send_wait(struct rconn *);
+
+const char *rconn_get_name(const struct rconn *);
+bool rconn_is_alive(const struct rconn *);
+
+#endif /* rconn.h */
index dbd30b4a2853edc02711053b6cc13cacac41ad61..1d4a923d46bbe4416da05b12cc999dbf0b9812c1 100644 (file)
 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 */
index b5fddbd3942c06ef061b97b2899ed042120898e5..ecaab0ec7f5d8f3b6755b4ecce2702c54784d231 100644 (file)
@@ -49,6 +49,7 @@ struct vconn {
     int connect_status;
 };
 
+void vconn_usage(bool active, bool passive);
 int vconn_open(const char *name, struct vconn **);
 void vconn_close(struct vconn *);
 bool vconn_is_passive(const struct vconn *);
@@ -59,6 +60,7 @@ int vconn_send(struct vconn *, struct buffer *);
 
 int vconn_open_block(const char *name, struct vconn **);
 int vconn_send_block(struct vconn *, struct buffer *);
+int vconn_recv_block(struct vconn *, struct buffer **);
 
 enum vconn_wait_type {
     WAIT_CONNECT,
index 63a0c412e1fe5f6db6522b47a6a9d5caa0e5526e..01b2a851bd4f59d416a8d26443fec1758e737fc5 100644 (file)
@@ -63,7 +63,6 @@ enum vlog_facility vlog_get_facility_val(const char *name);
 #define VLOG_MODULES                            \
         VLOG_MODULE(chain)                      \
         VLOG_MODULE(controller)                 \
-        VLOG_MODULE(controller_connection)      \
         VLOG_MODULE(ctlpath)                    \
         VLOG_MODULE(datapath)                   \
         VLOG_MODULE(dpif)                       \
@@ -74,6 +73,7 @@ enum vlog_facility vlog_get_facility_val(const char *name);
         VLOG_MODULE(netlink)                    \
         VLOG_MODULE(poll_loop)                  \
         VLOG_MODULE(secchan)                    \
+        VLOG_MODULE(rconn)                      \
         VLOG_MODULE(switch)                     \
         VLOG_MODULE(socket_util)                \
         VLOG_MODULE(vconn_netlink)              \
@@ -98,6 +98,7 @@ enum vlog_level vlog_get_level(enum vlog_module, enum vlog_facility);
 void vlog_set_levels(enum vlog_module, enum vlog_facility, enum vlog_level);
 char *vlog_set_levels_from_string(const char *);
 char *vlog_get_levels(void);
+bool vlog_is_enabled(enum vlog_module, enum vlog_level);
 void vlog_set_verbosity(const char *arg);
 
 /* Function for actual logging. */
@@ -117,4 +118,12 @@ void vlog(enum vlog_module, enum vlog_level, const char *format, ...)
 #define VLOG_WARN(...) vlog(THIS_MODULE, VLL_WARN, __VA_ARGS__)
 #define VLOG_DBG(...) vlog(THIS_MODULE, VLL_DBG, __VA_ARGS__)
 
+/* More convenience macros, for testing whether a given level is enabled in
+ * THIS_MODULE.  When constructing a log message is expensive, this enables it
+ * to be skipped. */
+#define VLOG_IS_EMER_ENABLED() true
+#define VLOG_IS_ERR_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_EMER)
+#define VLOG_IS_WARN_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_WARN)
+#define VLOG_IS_DBG_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_DBG)
+
 #endif /* vlog.h */
index fff0c7fc2bc0e1341125df4e05a52262a2ef038e..c34cba433a0d4bde76fcfa22dac57a3537e24367 100644 (file)
@@ -14,6 +14,7 @@ libopenflow_la_SOURCES = \
        ofp-print.c \
        poll-loop.c \
        queue.c \
+       rconn.c \
        socket-util.c \
        util.c \
        vconn-tcp.c \
index 9f0dbf4efe959a617b9b3ceab29e01cbcb7d1cc1..2b6e3b783c133b34be055a63be025a64a26dfd2e 100644 (file)
@@ -171,11 +171,14 @@ buffer_put_uninit(struct buffer *b, size_t size)
 }
 
 /* Appends the 'size' bytes of data in 'p' to the tail end of 'b'.  Data in 'b'
- * is reallocated and copied if necessary. */
-void
+ * is reallocated and copied if necessary.  Returns a pointer to the first
+ * byte of the data's location in the buffer. */
+void *
 buffer_put(struct buffer *b, const void *p, size_t size) 
 {
-    memcpy(buffer_put_uninit(b, size), p, size);
+    void *dst = buffer_put_uninit(b, size);
+    memcpy(dst, p, size);
+    return dst;
 }
 
 void *
index b36df5f48e615d526237732e5e5ce1449dadb4dc..63611e9367bb77037abde93166b30c81244a6135 100644 (file)
@@ -258,193 +258,6 @@ dpif_del_port(struct dpif *dp, const char *netdev)
     return send_mgmt_command(dp, DP_GENL_C_DEL_PORT, netdev);
 }
 
-/* Prints a description of 'dp' to stdout.  Returns 0 if successful, otherwise
- * a positive errno value. */
-int
-dpif_show(struct dpif *dp) 
-{
-    static const struct nl_policy show_policy[] = {
-        [DP_GENL_A_DP_INFO] = { .type = NL_A_UNSPEC,
-                                .min_len = sizeof(struct ofp_data_hello),
-                                .max_len = SIZE_MAX },
-    };
-
-    struct buffer request, *reply;
-    struct nlattr *attrs[ARRAY_SIZE(show_policy)];
-    struct ofp_data_hello *odh;
-    int retval;
-    size_t len;
-
-    buffer_init(&request, 0);
-    nl_msg_put_genlmsghdr(&request, dp->sock, 0, openflow_family,
-                          NLM_F_REQUEST, DP_GENL_C_SHOW_DP, 1);
-    nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, dp->dp_idx);
-    retval = nl_sock_transact(dp->sock, &request, &reply);
-    buffer_uninit(&request);
-    if (retval) {
-        return retval;
-    }
-    if (!nl_policy_parse(reply, show_policy, attrs,
-                         ARRAY_SIZE(show_policy))) {
-        buffer_delete(reply);
-        return EPROTO;
-    }
-
-    odh = (void *) nl_attr_get(attrs[DP_GENL_A_DP_INFO]);
-    if (odh->header.version != OFP_VERSION
-        || odh->header.type != OFPT_DATA_HELLO) {
-        VLOG_ERR("bad show query response (%"PRIu8",%"PRIu8")",
-                 odh->header.version, odh->header.type);
-        buffer_delete(reply);
-        return EPROTO;
-    }
-
-    len = nl_attr_get_size(attrs[DP_GENL_A_DP_INFO]);
-    ofp_print_data_hello(stdout, odh, len, 1);
-
-    return retval;
-}
-
-static const struct nl_policy table_policy[] = {
-    [DP_GENL_A_NUMTABLES] = { .type = NL_A_U32 },
-    [DP_GENL_A_TABLE] = { .type = NL_A_UNSPEC },
-};
-
-/* Writes a description of 'dp''s tables to stdout.  Returns 0 if successful,
- * otherwise a positive errno value. */
-int
-dpif_dump_tables(struct dpif *dp) 
-{
-    struct buffer request, *reply;
-    struct nlattr *attrs[ARRAY_SIZE(table_policy)];
-    const struct ofp_table *tables;
-    int n_tables;
-    int i;
-    int retval;
-
-    buffer_init(&request, 0);
-    nl_msg_put_genlmsghdr(&request, dp->sock, 0, openflow_family,
-                          NLM_F_REQUEST, DP_GENL_C_QUERY_TABLE, 1);
-    nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, dp->dp_idx);
-    retval = nl_sock_transact(dp->sock, &request, &reply);
-    buffer_uninit(&request);
-    if (retval) {
-        return retval;
-    }
-    if (!nl_policy_parse(reply, table_policy, attrs,
-                         ARRAY_SIZE(table_policy))) {
-        buffer_delete(reply);
-        return EPROTO;
-    }
-
-    tables = nl_attr_get(attrs[DP_GENL_A_TABLE]);
-    n_tables = (nl_attr_get_size(attrs[DP_GENL_A_TABLE])
-                / sizeof(struct ofp_table));
-    n_tables = MIN(n_tables, nl_attr_get_u32(attrs[DP_GENL_A_NUMTABLES]));
-    for (i = 0; i < n_tables; i++) {
-        const struct ofp_table *ot = &tables[i];
-        if (ot->header.version != 1 || ot->header.type != OFPT_TABLE) {
-            VLOG_DBG("bad table query response (%"PRIu8",%"PRIu8")",
-                     ot->header.version, ot->header.type);
-            retval = EPROTO;
-            break;
-        }
-
-        ofp_print_table(stdout, ot);
-        fprintf(stdout,"\n");
-    }
-    buffer_delete(reply);
-
-    return retval;
-}
-
-static const struct nl_policy flow_policy[] = {
-    [DP_GENL_A_TABLEIDX] = { .type = NL_A_U16 },
-    [DP_GENL_A_NUMFLOWS] = { .type = NL_A_U32 },
-    [DP_GENL_A_FLOW] = { .type = NL_A_UNSPEC },
-};
-
-struct _dump_ofp_flow_mod
-{
-    struct ofp_flow_mod ofm;
-    struct ofp_action   oa;
-};
-
-/* Writes a description of flows in the given 'table' in 'dp' to stdout.  If
- * 'match' is null, all flows in the table are written; otherwise, only
- * matching flows are written.  Returns 0 if successful, otherwise a positive
- * errno value. */
-int
-dpif_dump_flows(struct dpif *dp, int table, struct ofp_match *match)
-{
-    struct buffer request, *reply;
-    struct ofp_flow_mod *ofm;
-    int retval;
-
-    buffer_init(&request, 0);
-    nl_msg_put_genlmsghdr(&request, dp->sock, 0, openflow_family, NLM_F_REQUEST,
-                          DP_GENL_C_QUERY_FLOW, 1);
-    nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, dp->dp_idx);
-    nl_msg_put_u16(&request, DP_GENL_A_TABLEIDX, table);
-    ofm = nl_msg_put_unspec_uninit(&request, DP_GENL_A_FLOW, sizeof *ofm);
-    memset(ofm, 0, sizeof *ofm);
-    ofm->header.version = 1;
-    ofm->header.type = OFPT_FLOW_MOD;
-    ofm->header.length = htons(sizeof ofm);
-    if (match) {
-        ofm->match = *match;
-    } else {
-        ofm->match.wildcards = htons(OFPFW_ALL);
-    }
-    retval = nl_sock_transact(dp->sock, &request, &reply);
-    buffer_uninit(&request);
-    if (retval) {
-        return retval;
-    }
-
-    for (;;) {
-        struct nlattr *attrs[ARRAY_SIZE(flow_policy)];
-        const struct _dump_ofp_flow_mod *flows, *ofm;
-        int n_flows;
-
-        if (!nl_policy_parse(reply, flow_policy, attrs,
-                             ARRAY_SIZE(flow_policy))) {
-            buffer_delete(reply);
-            return EPROTO;
-        }
-        n_flows = (nl_attr_get_size(attrs[DP_GENL_A_FLOW])
-                   / sizeof(struct ofp_flow_mod));
-        n_flows = MIN(n_flows, nl_attr_get_u32(attrs[DP_GENL_A_NUMFLOWS]));
-        if (n_flows <= 0) {
-            break;
-        }
-
-        flows = nl_attr_get(attrs[DP_GENL_A_FLOW]);
-        for (ofm = flows; ofm < &flows[n_flows]; ofm++) {
-            if (ofm->ofm.header.version != 1){
-                VLOG_DBG("recv_dp_flow incorrect version");
-                buffer_delete(reply);
-                return EPROTO;
-            } else if (ofm->ofm.header.type != OFPT_FLOW_MOD) {
-                VLOG_DBG("recv_fp_flow bad return message type");
-                buffer_delete(reply);
-                return EPROTO;
-            }
-
-            ofp_print_flow_mod(stdout, &ofm->ofm, 
-                    sizeof(struct ofp_flow_mod), 1);
-            putc('\n', stdout);
-        }
-
-        buffer_delete(reply);
-        retval = nl_sock_recv(dp->sock, &reply, true);
-        if (retval) {
-            return retval;
-        }
-    }
-    return 0;
-}
-
 /* Tells dp to send num_packets up through netlink for benchmarking*/
 int
 dpif_benchmark_nl(struct dpif *dp, uint32_t num_packets, uint32_t packet_size)
index 01d63fe5a3f36f9972475783dca6bd01ea330c89..68dd341912a4e1eba6ac550ee5e1052d5dd41f59 100644 (file)
@@ -34,6 +34,7 @@
 #include "dynamic-string.h"
 #include <assert.h>
 #include <stdlib.h>
+#include <string.h>
 #include "util.h"
 
 void
@@ -54,6 +55,23 @@ ds_reserve(struct ds *ds, size_t min_length)
     }
 }
 
+void
+ds_put_char(struct ds *ds, char c)
+{
+    ds_reserve(ds, ds->length + 1);
+    ds->string[ds->length++] = c;
+    ds->string[ds->length] = '\0';
+}
+
+void
+ds_put_cstr(struct ds *ds, const char *s)
+{
+    size_t s_len = strlen(s);
+    ds_reserve(ds, ds->length + s_len);
+    memcpy(&ds->string[ds->length], s, s_len + 1);
+    ds->length += s_len;
+}
+
 void
 ds_put_format(struct ds *ds, const char *format, ...)
 {
@@ -108,3 +126,56 @@ ds_destroy(struct ds *ds)
 {
     free(ds->string);
 }
+
+/* Writes the 'size' bytes in 'buf' to 'string' as hex bytes arranged 16 per
+ * line.  Numeric offsets are also included, starting at 'ofs' for the first
+ * byte in 'buf'.  If 'ascii' is true then the corresponding ASCII characters
+ * are also rendered alongside. */
+void
+ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size,
+                uintptr_t ofs, bool ascii)
+{
+  const uint8_t *buf = buf_;
+  const size_t per_line = 16; /* Maximum bytes per line. */
+
+  while (size > 0)
+    {
+      size_t start, end, n;
+      size_t i;
+
+      /* Number of bytes on this line. */
+      start = ofs % per_line;
+      end = per_line;
+      if (end - start > size)
+        end = start + size;
+      n = end - start;
+
+      /* Print line. */
+      ds_put_format(ds, "%08jx  ", (uintmax_t) ROUND_DOWN(ofs, per_line));
+      for (i = 0; i < start; i++)
+        ds_put_format(ds, "   ");
+      for (; i < end; i++)
+        ds_put_format(ds, "%02hhx%c",
+                buf[i - start], i == per_line / 2 - 1? '-' : ' ');
+      if (ascii)
+        {
+          for (; i < per_line; i++)
+            ds_put_format(ds, "   ");
+          ds_put_format(ds, "|");
+          for (i = 0; i < start; i++)
+            ds_put_format(ds, " ");
+          for (; i < end; i++) {
+              int c = buf[i - start];
+              ds_put_char(ds, c >= 32 && c < 127 ? c : '.');
+          }
+          for (; i < per_line; i++)
+            ds_put_format(ds, " ");
+          ds_put_format(ds, "|");
+        }
+      ds_put_format(ds, "\n");
+
+      ofs += n;
+      buf += n;
+      size -= n;
+    }
+}
index 8ee1af58704253df51d04f259c50c139458f4bd2..8b056fd9ea3420343700dacc8f5afd7dde0c323f 100644 (file)
 #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 */
@@ -75,17 +79,18 @@ void ofp_print_packet(FILE *stream, const void *data, size_t len,
     struct pcap_hdr ph;
     struct pcaprec_hdr prh;
 
+    struct ds ds = DS_EMPTY_INITIALIZER;
+
     char command[128];
+    FILE *pcap;
     FILE *tcpdump;
     int status;
+    int c;
 
-    fflush(stream);
-    snprintf(command, sizeof command, "tcpdump -n -r - %d>&1 2>/dev/null",
-             fileno(stream));
-    tcpdump = popen(command, "w");
-    if (!tcpdump) {
-        error(errno, "exec(\"%s\")", command);
-        return;
+    pcap = tmpfile();
+    if (!pcap) {
+        error(errno, "tmpfile");
+        return xstrdup("<error>");
     }
 
     /* The pcap reader is responsible for figuring out endianness based on the
@@ -103,13 +108,28 @@ void ofp_print_packet(FILE *stream, const void *data, size_t len,
     prh.incl_len = len;
     prh.orig_len = total_len;
 
-    fwrite(&ph, 1, sizeof ph, tcpdump);
-    fwrite(&prh, 1, sizeof prh, tcpdump);
-    fwrite(data, 1, len, tcpdump);
+    fwrite(&ph, 1, sizeof ph, pcap);
+    fwrite(&prh, 1, sizeof prh, pcap);
+    fwrite(data, 1, len, pcap);
+
+    fflush(pcap);
+    if (ferror(pcap)) {
+        error(errno, "error writing temporary file");
+    }
+    rewind(pcap);
+
+    snprintf(command, sizeof command, "tcpdump -n -r /dev/fd/%d 2>/dev/null",
+             fileno(pcap));
+    tcpdump = popen(command, "r");
+    fclose(pcap);
+    if (!tcpdump) {
+        error(errno, "exec(\"%s\")", command);
+        return xstrdup("<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)) {
@@ -118,116 +138,124 @@ void ofp_print_packet(FILE *stream, const void *data, size_t len,
     } else if (WIFSIGNALED(status)) {
         error(0, "tcpdump exited with signal %d", WTERMSIG(status)); 
     }
+    return ds_cstr(&ds);
 }
 
 /* Pretty-print the OFPT_PACKET_IN packet of 'len' bytes at 'oh' to 'stream'
  * at the given 'verbosity' level. */
-static void ofp_packet_in(FILE *stream, const void *oh, size_t len,
-                            int verbosity)
+static void
+ofp_packet_in(struct ds *string, const void *oh, size_t len, int verbosity)
 {
     const struct ofp_packet_in *op = oh;
     size_t data_len;
 
-    fprintf(stream, " total_len=%"PRIu16" in_port=%"PRIu8,
+    ds_put_format(string, " total_len=%"PRIu16" in_port=%"PRIu8,
             ntohs(op->total_len), ntohs(op->in_port));
 
     if (op->reason == OFPR_ACTION)
-        fputs(" (via action)", stream);
+        ds_put_cstr(string, " (via action)");
     else if (op->reason != OFPR_NO_MATCH)
-        fprintf(stream, " (***reason %"PRIu8"***)", op->reason);
+        ds_put_format(string, " (***reason %"PRIu8"***)", op->reason);
 
     data_len = len - offsetof(struct ofp_packet_in, data);
-    fprintf(stream, " data_len=%zu", data_len);
+    ds_put_format(string, " data_len=%zu", data_len);
     if (htonl(op->buffer_id) == UINT32_MAX) {
-        fprintf(stream, " (unbuffered)");
+        ds_put_format(string, " (unbuffered)");
         if (ntohs(op->total_len) != data_len)
-            fprintf(stream, " (***total_len != data_len***)");
+            ds_put_format(string, " (***total_len != data_len***)");
     } else {
-        fprintf(stream, " buffer=%08"PRIx32, ntohl(op->buffer_id));
+        ds_put_format(string, " buffer=%08"PRIx32, ntohl(op->buffer_id));
         if (ntohs(op->total_len) < data_len)
-            fprintf(stream, " (***total_len < data_len***)");
+            ds_put_format(string, " (***total_len < data_len***)");
     }
-    putc('\n', stream);
+    ds_put_char(string, '\n');
 
-    if (verbosity > 0)
-        ofp_print_packet(stream, op->data, data_len, ntohs(op->total_len));
+    if (verbosity > 0) {
+        char *packet = ofp_packet_to_string(op->data, data_len,
+                                            ntohs(op->total_len)); 
+        ds_put_cstr(string, packet);
+        free(packet);
+    }
 }
 
-static void ofp_print_port_name(FILE *stream, uint16_t port) 
+static void ofp_print_port_name(struct ds *string, uint16_t port) 
 {
     if (port == UINT16_MAX) {
-        fputs("none", stream);
+        ds_put_cstr(string, "none");
     } else if (port == OFPP_FLOOD) {
-        fputs("flood", stream);
+        ds_put_cstr(string, "flood");
     } else if (port == OFPP_CONTROLLER) {
-        fputs("controller", stream);
+        ds_put_cstr(string, "controller");
     } else {
-        fprintf(stream, "%"PRIu16, port);
+        ds_put_format(string, "%"PRIu16, port);
     }
 }
 
-static void ofp_print_action(FILE *stream, const struct ofp_action *a) 
+static void
+ofp_print_action(struct ds *string, const struct ofp_action *a) 
 {
     switch (ntohs(a->type)) {
     case OFPAT_OUTPUT:
-        fputs("output(", stream);
-        ofp_print_port_name(stream, ntohs(a->arg.output.port));
+        ds_put_cstr(string, "output(");
+        ofp_print_port_name(string, ntohs(a->arg.output.port));
         if (a->arg.output.port == htons(OFPP_CONTROLLER)) {
-            fprintf(stream, ", max %"PRIu16" bytes", ntohs(a->arg.output.max_len));
+            ds_put_format(string, ", max %"PRIu16" bytes", ntohs(a->arg.output.max_len));
         }
-        fputs(")", stream);
+        ds_put_cstr(string, ")");
         break;
 
     default:
-        fprintf(stream, "(decoder %"PRIu16" not implemented)", ntohs(a->type));
+        ds_put_format(string, "(decoder %"PRIu16" not implemented)", ntohs(a->type));
         break;
     }
 }
 
-static void ofp_print_actions(FILE *stream,
-                                const struct ofp_action actions[],
-                                size_t n_bytes) 
+static void ofp_print_actions(struct ds *string,
+                              const struct ofp_action actions[],
+                              size_t n_bytes) 
 {
     size_t i;
 
-    fputs(" actions[", stream);
+    ds_put_cstr(string, " actions[");
     for (i = 0; i < n_bytes / sizeof *actions; i++) {
         if (i) {
-            fputs("; ", stream);
+            ds_put_cstr(string, "; ");
         }
-        ofp_print_action(stream, &actions[i]);
+        ofp_print_action(string, &actions[i]);
     }
     if (n_bytes % sizeof *actions) {
         if (i) {
-            fputs("; ", stream);
+            ds_put_cstr(string, "; ");
         }
-        fputs("; ***trailing garbage***", stream);
+        ds_put_cstr(string, "; ***trailing garbage***");
     }
-    fputs("]", stream);
+    ds_put_cstr(string, "]");
 }
 
-/* Pretty-print the OFPT_PACKET_OUT packet of 'len' bytes at 'oh' to 'stream'
+/* Pretty-print the OFPT_PACKET_OUT packet of 'len' bytes at 'oh' to 'string'
  * at the given 'verbosity' level. */
-static void ofp_packet_out(FILE *stream, const void *oh, size_t len,
-                            int verbosity) 
+static void ofp_packet_out(struct ds *string, const void *oh, size_t len,
+                           int verbosity) 
 {
     const struct ofp_packet_out *opo = oh;
 
-    fputs(" in_port=", stream);
-    ofp_print_port_name(stream, ntohs(opo->in_port));
+    ds_put_cstr(string, " in_port=");
+    ofp_print_port_name(string, ntohs(opo->in_port));
 
     if (ntohl(opo->buffer_id) == UINT32_MAX) {
-        fputs(" out_port=", stream);
-        ofp_print_port_name(stream, ntohs(opo->out_port));
+        ds_put_cstr(string, " out_port=");
+        ofp_print_port_name(string, ntohs(opo->out_port));
         if (verbosity > 0 && len > sizeof *opo) {
-            ofp_print_packet(stream, opo->u.data, len - sizeof *opo,
-                               len - sizeof *opo);
+            char *packet = ofp_packet_to_string(opo->u.data, len - sizeof *opo,
+                                                len - sizeof *opo);
+            ds_put_cstr(string, packet);
+            free(packet);
         }
     } else {
-        fprintf(stream, " buffer=%08"PRIx32, ntohl(opo->buffer_id));
-        ofp_print_actions(stream, opo->u.actions, len - sizeof *opo);
+        ds_put_format(string, " buffer=%08"PRIx32, ntohl(opo->buffer_id));
+        ofp_print_actions(string, opo->u.actions, len - sizeof *opo);
     }
-    putc('\n', stream);
+    ds_put_char(string, '\n');
 }
 
 /* qsort comparison function. */
@@ -242,8 +270,8 @@ compare_ports(const void *a_, const void *b_)
     return ap < bp ? -1 : ap > bp;
 }
 
-static
-void ofp_print_phy_port(FILE *stream, const struct ofp_phy_port *port)
+static void
+ofp_print_phy_port(struct ds *string, const struct ofp_phy_port *port)
 {
     uint8_t name[OFP_MAX_PORT_NAME_LEN];
     int j;
@@ -256,65 +284,85 @@ void ofp_print_phy_port(FILE *stream, const struct ofp_phy_port *port)
     }
     name[j] = '\0';
 
-    fprintf(stream, " %2d(%s): addr:"ETH_ADDR_FMT", speed:%d, flags:%#x, "
+    ds_put_format(string, " %2d(%s): addr:"ETH_ADDR_FMT", speed:%d, flags:%#x, "
             "feat:%#x\n", ntohs(port->port_no), name, 
             ETH_ADDR_ARGS(port->hw_addr), ntohl(port->speed),
             ntohl(port->flags), ntohl(port->features));
 }
 
-/* Pretty-print the OFPT_DATA_HELLO packet of 'len' bytes at 'oh' to 'stream'
- * at the given 'verbosity' level. */
-void ofp_print_data_hello(FILE *stream, const void *oh, size_t len, 
-        int verbosity)
+/* Pretty-print the struct ofp_switch_features of 'len' bytes at 'oh' to
+ * 'string' at the given 'verbosity' level. */
+static void
+ofp_print_switch_features(struct ds *string, const void *oh, size_t len,
+                          int verbosity)
 {
-    const struct ofp_data_hello *odh = oh;
+    const struct ofp_switch_features *osf = oh;
     struct ofp_phy_port port_list[OFPP_MAX];
     int n_ports;
     int i;
 
-
-    fprintf(stream, "dp id:%"PRIx64"\n", ntohll(odh->datapath_id));
-    fprintf(stream, "tables: exact:%d, mac:%d, compressed:%d, general:%d\n",
-           ntohl(odh->n_exact), ntohl(odh->n_mac_only),
-           ntohl(odh->n_compression), ntohl(odh->n_general));
-    fprintf(stream, "buffers: size:%d, number:%d, miss_len:%d\n",
-           ntohl(odh->buffer_mb), ntohl(odh->n_buffers),
-           ntohs(odh->miss_send_len));
-    fprintf(stream, "features: capabilities:%#x, actions:%#x\n",
-           ntohl(odh->capabilities), ntohl(odh->actions));
-
-    if (ntohs(odh->header.length) >= sizeof *odh) {
-        len = MIN(len, ntohs(odh->header.length));
+    ds_put_format(string, "dp id:%"PRIx64"\n", ntohll(osf->datapath_id));
+    ds_put_format(string, "tables: exact:%d, mac:%d, compressed:%d, general:%d\n",
+           ntohl(osf->n_exact), ntohl(osf->n_mac_only),
+           ntohl(osf->n_compression), ntohl(osf->n_general));
+    ds_put_format(string, "buffers: size:%d, number:%d\n",
+           ntohl(osf->buffer_mb), ntohl(osf->n_buffers));
+    ds_put_format(string, "features: capabilities:%#x, actions:%#x\n",
+           ntohl(osf->capabilities), ntohl(osf->actions));
+
+    if (ntohs(osf->header.length) >= sizeof *osf) {
+        len = MIN(len, ntohs(osf->header.length));
     }
-    n_ports = (len - sizeof *odh) / sizeof *odh->ports;
+    n_ports = (len - sizeof *osf) / sizeof *osf->ports;
 
-    memcpy(port_list, odh->ports, (len - sizeof *odh));
+    memcpy(port_list, osf->ports, (len - sizeof *osf));
     qsort(port_list, n_ports, sizeof port_list[0], compare_ports);
     for (i = 0; i < n_ports; i++) {
-        ofp_print_phy_port(stream, &port_list[i]);
+        ofp_print_phy_port(string, &port_list[i]);
     }
 }
 
-static void print_wild(FILE *stream, const char *leader, int is_wild,
+/* Pretty-print the struct ofp_switch_config of 'len' bytes at 'oh' to 'string'
+ * at the given 'verbosity' level. */
+static void
+ofp_print_switch_config(struct ds *string, const void *oh, size_t len,
+                        int verbosity)
+{
+    const struct ofp_switch_config *osc = oh;
+    uint16_t flags;
+
+    flags = ntohs(osc->flags);
+    if (flags & OFPC_SEND_FLOW_EXP) {
+        flags &= ~OFPC_SEND_FLOW_EXP;
+        ds_put_format(string, " (sending flow expirations)");
+    }
+    if (flags) {
+        ds_put_format(string, " ***unknown flags %04"PRIx16"***", flags);
+    }
+
+    ds_put_format(string, " miss_send_len=%"PRIu16"\n", ntohs(osc->miss_send_len));
+}
+
+static void print_wild(struct ds *string, const char *leader, int is_wild,
             const char *format, ...) __attribute__((format(printf, 4, 5)));
 
-static void print_wild(FILE *stream, const char *leader, int is_wild,
+static void print_wild(struct ds *string, const char *leader, int is_wild,
                        const char *format, ...) 
 {
-    fputs(leader, stream);
+    ds_put_cstr(string, leader);
     if (!is_wild) {
         va_list args;
 
         va_start(args, format);
-        vfprintf(stream, format, args);
+        ds_put_format_valist(string, format, args);
         va_end(args);
     } else {
-        putc('?', stream);
+        ds_put_char(string, '?');
     }
 }
 
 /* Pretty-print the ofp_match structure */
-static void ofp_print_match(FILE *f, const struct ofp_match *om)
+static void ofp_print_match(struct ds *f, const struct ofp_match *om)
 {
     uint16_t w = ntohs(om->wildcards);
 
@@ -330,155 +378,323 @@ static void ofp_print_match(FILE *f, const struct ofp_match *om)
     print_wild(f, "] proto", w & OFPFW_NW_PROTO, "%u", om->nw_proto);
     print_wild(f, " tport[", w & OFPFW_TP_SRC, "%d", ntohs(om->tp_src));
     print_wild(f, "->", w & OFPFW_TP_DST, "%d", ntohs(om->tp_dst));
-    fputs("]\n", f);
+    ds_put_cstr(f, "]\n");
 }
 
-/* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'stream'
+/* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'string'
  * at the given 'verbosity' level. */
-void ofp_print_flow_mod(FILE *stream, const void *oh, size_t len, 
-        int verbosity)
+static void
+ofp_print_flow_mod(struct ds *string, const void *oh, size_t len, 
+                   int verbosity)
 {
     const struct ofp_flow_mod *ofm = oh;
 
-    ofp_print_match(stream, &ofm->match);
-    fprintf(stream, " cmd:%d idle:%d buf:%#x grp:%d\n", ntohs(ofm->command),
+    ofp_print_match(string, &ofm->match);
+    ds_put_format(string, " cmd:%d idle:%d buf:%#x grp:%d\n", ntohs(ofm->command),
          ntohs(ofm->max_idle), ntohl(ofm->buffer_id), ntohl(ofm->group_id));
 }
 
-/* Pretty-print the OFPT_FLOW_EXPIRED packet of 'len' bytes at 'oh' to 'stream'
+/* Pretty-print the OFPT_FLOW_EXPIRED packet of 'len' bytes at 'oh' to 'string'
  * at the given 'verbosity' level. */
-void ofp_print_flow_expired(FILE *stream, const void *oh, size_t len, 
+void ofp_print_flow_expired(struct ds *string, const void *oh, size_t len, 
         int verbosity)
 {
     const struct ofp_flow_expired *ofe = oh;
 
-    ofp_print_match(stream, &ofe->match);
-    fprintf(stream
+    ofp_print_match(string, &ofe->match);
+    ds_put_format(string
          " secs%d pkts%lld bytes%lld\n", ntohl(ofe->duration),
          ntohll(ofe->packet_count), ntohll(ofe->byte_count));
 }
 
-/* Pretty-print the OFPT_PORT_STATUS packet of 'len' bytes at 'oh' to 'stream'
+/* Pretty-print the OFPT_PORT_STATUS packet of 'len' bytes at 'oh' to 'string'
  * at the given 'verbosity' level. */
-void ofp_print_port_status(FILE *stream, const void *oh, size_t len, 
+void ofp_print_port_status(struct ds *string, const void *oh, size_t len, 
         int verbosity)
 {
     const struct ofp_port_status *ops = oh;
 
     if (ops->reason == OFPPR_ADD) {
-        fprintf(stream, "add:");
+        ds_put_format(string, "add:");
     } else if (ops->reason == OFPPR_DELETE) {
-        fprintf(stream, "del:");
+        ds_put_format(string, "del:");
     } else if (ops->reason == OFPPR_MOD) {
-        fprintf(stream, "mod:");
+        ds_put_format(string, "mod:");
     } else {
-        fprintf(stream, "err:");
+        ds_put_format(string, "err:");
     }
 
-    ofp_print_phy_port(stream, &ops->desc);
+    ofp_print_phy_port(string, &ops->desc);
+}
+
+static void
+ofp_flow_stat_request(struct ds *string, const void *oh, size_t len,
+                      int verbosity) 
+{
+    const struct ofp_flow_stat_request *fsr = oh;
+
+    if (fsr->table_id == 0xff) {
+        ds_put_format(string, " table_id=any, ");
+    } else {
+        ds_put_format(string, " table_id=%"PRIu8", ", fsr->table_id);
+    }
+
+    if (fsr->type == OFPFS_INDIV) {
+        ds_put_cstr(string, " type=indiv, ");
+    } else if (fsr->type == OFPFS_AGGREGATE) {
+        ds_put_cstr(string, " type=aggregate, ");
+    } else {
+        ds_put_format(string, " ***type=%"PRIu8"***, ", fsr->type);
+    }
+    ofp_print_match(string, &fsr->match);
+}
+
+static void
+ofp_flow_stat_reply(struct ds *string, const void *oh, size_t len,
+                     int verbosity)
+{
+    const struct ofp_flow_stat_reply *fsr = oh;
+    const struct ofp_flow_stats *fs;
+    size_t n;
+
+    n = (len - offsetof(struct ofp_flow_stat_reply, flows)) / sizeof *fs;
+    ds_put_format(string, " %zu flows\n", n);
+    if (verbosity < 1) {
+        return;
+    }
+
+    for (fs = &fsr->flows[0]; fs < &fsr->flows[n]; fs++) {
+        ds_put_format(string, "  duration=%"PRIu32" s, ", ntohs(fs->duration));
+        ds_put_format(string, "table_id=%"PRIu8", ", fs->table_id);
+        ds_put_format(string, "n_packets=%"PRIu64", ",
+                      ntohll(fs->packet_count));
+        ds_put_format(string, "n_bytes=%"PRIu64", ", ntohll(fs->byte_count));
+        ofp_print_match(string, &fs->match);
+     }
+}
+
+static void
+ofp_port_stat_reply(struct ds *string, const void *oh, size_t len,
+                    int verbosity)
+{
+    const struct ofp_port_stat_reply *psr = oh;
+    const struct ofp_port_stats *ps;
+    size_t n;
+
+    n = (len - offsetof(struct ofp_port_stat_reply, ports)) / sizeof *ps;
+    ds_put_format(string, " %zu ports\n", n);
+    if (verbosity < 1) {
+        return;
+    }
+
+    for (ps = &psr->ports[0]; ps < &psr->ports[n]; ps++) {
+        ds_put_format(string, "  port %"PRIu16": ", ntohs(ps->port_no));
+        ds_put_format(string, "rx %"PRIu64", ", ntohll(ps->rx_count));
+        ds_put_format(string, "tx %"PRIu64", ", ntohll(ps->tx_count));
+        ds_put_format(string, "dropped %"PRIu64"\n", ntohll(ps->drop_count));
+    }
+}
+
+static void
+ofp_table_stat_reply(struct ds *string, const void *oh, size_t len,
+                     int verbosity)
+{
+    const struct ofp_table_stat_reply *tsr = oh;
+    const struct ofp_table_stats *ts;
+    size_t n;
+
+    n = (len - offsetof(struct ofp_table_stat_reply, tables)) / sizeof *ts;
+    ds_put_format(string, " %zu tables\n", n);
+    if (verbosity < 1) {
+        return;
+    }
+
+    for (ts = &tsr->tables[0]; ts < &tsr->tables[n]; ts++) {
+        char name[OFP_MAX_TABLE_NAME_LEN + 1];
+        strncpy(name, ts->name, sizeof name);
+        name[OFP_MAX_TABLE_NAME_LEN] = '\0';
+
+        ds_put_format(string, "  table %"PRIu8": ", ts->table_id);
+        ds_put_format(string, "name %-8s, ", name);
+        ds_put_format(string, "max %6"PRIu32", ", ntohl(ts->max_entries));
+        ds_put_format(string, "active %6"PRIu32", ", ntohl(ts->active_count));
+        ds_put_format(string, "matched %6"PRIu64"\n",
+                      ntohll(ts->matched_count));
+     }
 }
 
 struct openflow_packet {
     const char *name;
     size_t min_size;
-    void (*printer)(FILE *, const void *, size_t len, int verbosity);
+    void (*printer)(struct ds *, const void *, size_t len, int verbosity);
 };
 
 static const struct openflow_packet packets[] = {
-    [OFPT_CONTROL_HELLO] = {
-        "ofp_control_hello",
-        sizeof (struct ofp_control_hello),
+    [OFPT_FEATURES_REQUEST] = {
+        "features_request",
+        sizeof (struct ofp_header),
+        NULL,
+    },
+    [OFPT_FEATURES_REPLY] = {
+        "features_reply",
+        sizeof (struct ofp_switch_features),
+        ofp_print_switch_features,
+    },
+    [OFPT_GET_CONFIG_REQUEST] = {
+        "get_config_request",
+        sizeof (struct ofp_header),
         NULL,
     },
-    [OFPT_DATA_HELLO] = {
-        "ofp_data_hello",
-        sizeof (struct ofp_data_hello),
-        ofp_print_data_hello,
+    [OFPT_GET_CONFIG_REPLY] = {
+        "get_config_reply",
+        sizeof (struct ofp_switch_config),
+        ofp_print_switch_config,
+    },
+    [OFPT_SET_CONFIG] = {
+        "set_config",
+        sizeof (struct ofp_switch_config),
+        ofp_print_switch_config,
     },
     [OFPT_PACKET_IN] = {
-        "ofp_packet_in",
+        "packet_in",
         offsetof(struct ofp_packet_in, data),
         ofp_packet_in,
     },
     [OFPT_PACKET_OUT] = {
-        "ofp_packet_out",
+        "packet_out",
         sizeof (struct ofp_packet_out),
         ofp_packet_out,
     },
     [OFPT_FLOW_MOD] = {
-        "ofp_flow_mod",
+        "flow_mod",
         sizeof (struct ofp_flow_mod),
         ofp_print_flow_mod,
     },
     [OFPT_FLOW_EXPIRED] = {
-        "ofp_flow_expired",
+        "flow_expired",
         sizeof (struct ofp_flow_expired),
         ofp_print_flow_expired,
     },
     [OFPT_PORT_MOD] = {
-        "ofp_port_mod",
+        "port_mod",
         sizeof (struct ofp_port_mod),
         NULL,
     },
     [OFPT_PORT_STATUS] = {
-        "ofp_port_status",
+        "port_status",
         sizeof (struct ofp_port_status),
         ofp_print_port_status
     },
+    [OFPT_FLOW_STAT_REQUEST] = {
+        "flow_stat_request",
+        sizeof (struct ofp_flow_stat_request),
+        ofp_flow_stat_request,
+    },
+    [OFPT_FLOW_STAT_REPLY] = {
+        "flow_stat_reply",
+        sizeof (struct ofp_flow_stat_reply),
+        ofp_flow_stat_reply,
+    },
+    [OFPT_PORT_STAT_REQUEST] = {
+        "port_stat_request",
+        sizeof (struct ofp_port_stat_request),
+        NULL,
+    },
+    [OFPT_PORT_STAT_REPLY] = {
+        "port_stat_reply",
+        sizeof (struct ofp_port_stat_reply),
+        ofp_port_stat_reply,
+    },
+    [OFPT_TABLE_STAT_REQUEST] = {
+        "table_stat_request",
+        sizeof (struct ofp_table_stat_request),
+        NULL,
+    },
+    [OFPT_TABLE_STAT_REPLY] = {
+        "table_stat_reply",
+        sizeof (struct ofp_table_stat_reply),
+        ofp_table_stat_reply,
+    },
 };
 
-/* Pretty-print the OpenFlow packet of 'len' bytes at 'oh' to 'stream' at the
- * given 'verbosity' level.  0 is a minimal amount of verbosity and higher
- * numbers increase verbosity. */
-void ofp_print(FILE *stream, const void *oh_, size_t len, int verbosity)
+/* Composes and returns a string representing the OpenFlow packet of 'len'
+ * bytes at 'oh' at the given 'verbosity' level.  0 is a minimal amount of
+ * verbosity and higher numbers increase verbosity.  The caller is responsible
+ * for freeing the string. */
+char *
+ofp_to_string(const void *oh_, size_t len, int verbosity)
 {
+    struct ds string = DS_EMPTY_INITIALIZER;
     const struct ofp_header *oh = oh_;
     const struct openflow_packet *pkt;
 
     if (len < sizeof(struct ofp_header)) {
-        fprintf(stream, "OpenFlow packet too short:\n");
-        hex_dump(stream, oh, len, 0, true);
-        return;
+        ds_put_cstr(&string, "OpenFlow packet too short:\n");
+        ds_put_hex_dump(&string, oh, len, 0, true);
+        return ds_cstr(&string);
     } else if (oh->version != 1) {
-        fprintf(stream, "Bad OpenFlow version %"PRIu8":\n", oh->version);
-        hex_dump(stream, oh, len, 0, true);
-        return;
+        ds_put_format(&string, "Bad OpenFlow version %"PRIu8":\n", oh->version);
+        ds_put_hex_dump(&string, oh, len, 0, true);
+        return ds_cstr(&string);
     } else if (oh->type >= ARRAY_SIZE(packets) || !packets[oh->type].name) {
-        fprintf(stream, "Unknown OpenFlow packet type %"PRIu8":\n",
+        ds_put_format(&string, "Unknown OpenFlow packet type %"PRIu8":\n",
                 oh->type);
-        hex_dump(stream, oh, len, 0, true);
-        return;
+        ds_put_hex_dump(&string, oh, len, 0, true);
+        return ds_cstr(&string);
     }
 
     pkt = &packets[oh->type];
-    fprintf(stream, "%s (xid=%"PRIx32"):", pkt->name, oh->xid);
+    ds_put_format(&string, "%s (xid=%"PRIx32"):", pkt->name, oh->xid);
 
     if (ntohs(oh->length) > len)
-        fprintf(stream, " (***truncated to %zu bytes from %"PRIu16"***)",
+        ds_put_format(&string, " (***truncated to %zu bytes from %"PRIu16"***)",
                 len, ntohs(oh->length));
     else if (ntohs(oh->length) < len) {
-        fprintf(stream, " (***only uses %"PRIu16" bytes out of %zu***)\n",
+        ds_put_format(&string, " (***only uses %"PRIu16" bytes out of %zu***)\n",
                 ntohs(oh->length), len);
         len = ntohs(oh->length);
     }
 
     if (len < pkt->min_size) {
-        fprintf(stream, " (***length=%zu < min_size=%zu***)\n",
+        ds_put_format(&string, " (***length=%zu < min_size=%zu***)\n",
                 len, pkt->min_size);
     } else if (!pkt->printer) {
-        fprintf(stream, " length=%zu (decoder not implemented)\n",
-                ntohs(oh->length));
+        if (len > sizeof *oh) {
+            ds_put_format(&string, " length=%zu (decoder not implemented)\n",
+                          ntohs(oh->length)); 
+        }
     } else {
-        pkt->printer(stream, oh, len, verbosity);
+        pkt->printer(&string, oh, len, verbosity);
     }
-    if (verbosity >= 3)
-        hex_dump(stream, oh, len, 0, true);
+    if (verbosity >= 3) {
+        ds_put_hex_dump(&string, oh, len, 0, true);
+    }
+    return ds_cstr(&string);
+}
+\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));
 }
diff --git a/lib/rconn.c b/lib/rconn.c
new file mode 100644 (file)
index 0000000..f62ac61
--- /dev/null
@@ -0,0 +1,287 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * 
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include "rconn.h"
+#include <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;
+}
index 05c7ab07c328fed1c0045b6e0811c47487c1945a..ca1f93d5ae07a7dc176c5646b8acbbb378c4f1ff 100644 (file)
@@ -72,16 +72,18 @@ netlink_open(const char *name, char *suffix, struct vconn **vconnp)
 {
     struct netlink_vconn *netlink;
     int dp_idx;
+    int subscribe;
     int retval;
 
-    if (sscanf(suffix, "%d", &dp_idx) != 1) {
-        fatal(0, "%s: bad peer name format", name);
+    subscribe = 1;
+    if (sscanf(suffix, "%d:%d", &dp_idx, &subscribe) < 1) {
+        fatal(0, "%s: syntax error", name);
     }
 
     netlink = xmalloc(sizeof *netlink);
     netlink->vconn.class = &netlink_vconn_class;
     netlink->vconn.connect_status = 0;
-    retval = dpif_open(dp_idx, true, &netlink->dp);
+    retval = dpif_open(dp_idx, subscribe, &netlink->dp);
     if (retval) {
         free(netlink);
         *vconnp = NULL;
index 30d8caf63194b850845eb07fe11cc86d663a8f8b..2bdcfb1b5ffe28ec207045f5b7fab27162ce5dde 100644 (file)
@@ -361,6 +361,11 @@ again:
             return EPROTO;
         }
         want_bytes = length - rx->size;
+        if (!want_bytes) {
+            *bufferp = rx;
+            sslv->rxbuf = NULL;
+            return 0;
+        }
     }
     buffer_reserve_tailroom(rx, want_bytes);
 
index 1fe5919f68730e8a5646fee5383203a3500295ea..ea702605da955f44f49ec339c4e2d23d2dcaf419 100644 (file)
@@ -191,6 +191,11 @@ again:
             return EPROTO;
         }
         want_bytes = length - rx->size;
+        if (!want_bytes) {
+            *bufferp = rx;
+            tcp->rxbuf = NULL;
+            return 0;
+        }
     }
     buffer_reserve_tailroom(rx, want_bytes);
 
@@ -208,7 +213,12 @@ again:
         }
         return EAGAIN;
     } else if (retval == 0) {
-        return rx->size ? EPROTO : EOF;
+        if (rx->size) {
+            VLOG_ERR("connection dropped mid-packet");
+            return EPROTO;
+        } else {
+            return EOF; 
+        }
     } else {
         return retval ? errno : EAGAIN;
     }
index a3314c5757a4f18722554e4f4586be9d7562288b..f9c3ec00a81effbb85c151b9bcc248b1a6dd8af7 100644 (file)
@@ -40,6 +40,7 @@
 #include <string.h>
 #include "buffer.h"
 #include "flow.h"
+#include "ofp-print.h"
 #include "openflow.h"
 #include "poll-loop.h"
 #include "util.h"
@@ -79,6 +80,50 @@ check_vconn_classes(void)
 #endif
 }
 
+/* Prints information on active (if 'active') and passive (if 'passive')
+ * connection methods supported by the vconn. */
+void
+vconn_usage(bool active, bool passive)
+{
+    /* Really this should be implemented via callbacks into the vconn
+     * providers, but that seems too heavy-weight to bother with at the
+     * moment. */
+    
+    printf("\n");
+    if (active) {
+        printf("Active OpenFlow connection methods:\n");
+#ifdef HAVE_NETLINK
+        printf("  nl:DP_IDX               "
+               "local datapath DP_IDX\n");
+#endif
+        printf("  tcp:HOST[:PORT]         "
+               "PORT (default: %d) on remote TCP HOST\n", OFP_TCP_PORT);
+#ifdef HAVE_OPENSSL
+        printf("  ssl:HOST[:PORT]         "
+               "SSL PORT (default: %d) on remote HOST\n", OFP_SSL_PORT);
+#endif
+    }
+
+    if (passive) {
+        printf("Passive OpenFlow connection methods:\n");
+        printf("  ptcp:[PORT]             "
+               "listen to TCP PORT (default: %d)\n",
+               OFP_TCP_PORT);
+#ifdef HAVE_OPENSSL
+        printf("  pssl:[PORT]             "
+               "listen for SSL on PORT (default: %d)\n",
+               OFP_SSL_PORT);
+#endif
+    }
+
+#ifdef HAVE_OPENSSL
+    printf("PKI configuration (required to use SSL):\n"
+           "  -p, --private-key=FILE  file with private key\n"
+           "  -c, --certificate=FILE  file with certificate for private key\n"
+           "  -C, --ca-cert=FILE      file with peer CA certificate\n");
+#endif
+}
+
 /* Attempts to connect to an OpenFlow device.  'name' is a connection name in
  * the form "TYPE:ARGS", where TYPE is the vconn class's name and ARGS are
  * vconn class-specific.
@@ -209,6 +254,11 @@ vconn_recv(struct vconn *vconn, struct buffer **msgp)
     int retval = vconn_connect(vconn);
     if (!retval) {
         retval = (vconn->class->recv)(vconn, msgp);
+        if (VLOG_IS_DBG_ENABLED() && !retval) {
+            char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
+            VLOG_DBG("received: %s", s);
+            free(s);
+        }
     }
     if (retval) {
         *msgp = NULL;
@@ -232,7 +282,16 @@ vconn_send(struct vconn *vconn, struct buffer *msg)
 {
     int retval = vconn_connect(vconn);
     if (!retval) {
-        retval = (vconn->class->send)(vconn, msg);
+        if (!VLOG_IS_DBG_ENABLED()) { 
+            retval = (vconn->class->send)(vconn, msg);
+        } else {
+            char *s = ofp_to_string(msg->data, msg->size, 1);
+            retval = (vconn->class->send)(vconn, msg);
+            if (retval != EAGAIN) {
+                VLOG_DBG("sent (%s): %s", strerror(retval), s);
+            }
+            free(s);
+        }
     }
     return retval;
 }
@@ -244,6 +303,20 @@ vconn_send_block(struct vconn *vconn, struct buffer *msg)
     int retval;
     while ((retval = vconn_send(vconn, msg)) == EAGAIN) {
         vconn_send_wait(vconn);
+        VLOG_DBG("blocking on vconn send");
+        poll_block();
+    }
+    return retval;
+}
+
+/* Same as vconn_recv, except that it waits until a message is received. */
+int
+vconn_recv_block(struct vconn *vconn, struct buffer **msgp)
+{
+    int retval;
+    while ((retval = vconn_recv(vconn, msgp)) == EAGAIN) {
+        vconn_recv_wait(vconn);
+        VLOG_DBG("blocking on vconn receive");
         poll_block();
     }
     return retval;
index 3c977bf6504fd7d72b8cdebff9acf96896d88c1d..631627234fcf3b5752aa68feb20382570abf8053 100644 (file)
@@ -277,6 +277,16 @@ vlog_get_levels(void)
     return ds_cstr(&s);
 }
 
+/* Returns true if a log message emitted for the given 'module' and 'level'
+ * would cause some log output, false if that module and level are completely
+ * disabled. */
+bool
+vlog_is_enabled(enum vlog_module module, enum vlog_level level)
+{
+    return (levels[module][VLF_CONSOLE] >= level
+            || levels[module][VLF_SYSLOG] >= level);
+}
+
 /* Writes 'message' to the log at the given 'level' and as coming from the
  * given 'module'.
  *
index 3f7833196ec083ec403cc690c73813ddcfd62702..b1eac3a9f294ebd32c8c7e6edd5a0292882d91d1 100644 (file)
@@ -24,11 +24,6 @@ proceeds to forward packets from one endpoint to the other.
 .BR \-h ", " \-\^\-help
 Prints a brief help message to the console.
 
-.TP
-.BR \-u ", " \-\^\-unreliable
-Do not attempt to reconnect the channel if a connection drops.  By
-default, \fBsecchan\fR attempts to reconnect.
-
 .TP
 .BR \-v ", " \-\^\-verbose
 Prints debug messages to the console.
index 78f9e57199a75a7664bd6f9533730b66c042cc30..51638a3673a8b84d44bbcabb5cfcc8eed644d98d 100644 (file)
@@ -76,11 +76,6 @@ the switch is connected to a trustworthy controller.
 .BR \-h ", " \-\^\-help
 Prints a brief help message to the console.
 
-.TP
-.BR \-u ", " \-\^\-unreliable
-Do not attempt to reconnect the channel if a connection drops.  By
-default, \fBsecchan\fR attempts to reconnect.
-
 .TP
 .BR \-v ", " \-\^\-verbose
 Prints debug messages to the console.
index 13ddb0446cb573967af1b786ecb4474d1a3ebc9c..882c98e7ecb43273e2cdfef9a51b5861bfdcd615 100644 (file)
@@ -1,6 +1,10 @@
 include ../Make.vars
 
+if HAVE_NETLINK
 bin_PROGRAMS = secchan
+else
+bin_PROGRAMS = 
+endif
 
 secchan_SOURCES = secchan.c
 secchan_LDADD = ../lib/libopenflow.la -ldl
index 5d693781ab4cd2c487e6d46bc783ed8cc1ed810e..8ac3d4190dad7b2869ffdecd7dd06aa0016f78ea 100644 (file)
 #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();
@@ -81,7 +91,26 @@ main(int argc, char *argv[])
     parse_options(argc, argv);
 
     if (argc - optind != 2) {
-        fatal(0, "exactly two peer arguments required; use --help for usage");
+        fatal(0,
+              "need exactly two non-option arguments; use --help for usage");
+    }
+    nl_name = argv[optind];
+    if (strncmp(nl_name, "nl:", 3)
+        || strlen(nl_name) < 4
+        || nl_name[strspn(nl_name + 3, "0123456789") + 3]) {
+        fatal(0, "%s: argument is not of the form \"nl:DP_ID\"", nl_name);
+    }
+
+    if (listen_vconn_name) {
+        retval = vconn_open(listen_vconn_name, &listen_vconn);
+        if (retval && retval != EAGAIN) {
+            fatal(retval, "opening %s", listen_vconn_name);
+        }
+        if (!vconn_is_passive(listen_vconn)) {
+            fatal(0, "%s is not a passive vconn", listen_vconn_name);
+        }
+    } else {
+        listen_vconn = NULL;
     }
 
     retval = vlog_server_listen(NULL, NULL);
@@ -89,66 +118,34 @@ main(int argc, char *argv[])
         fatal(retval, "Could not listen for vlog connections");
     }
 
-    for (i = 0; i < 2; i++) {
-        halves[i].name = argv[optind + i];
-        halves[i].vconn = NULL;
-        halves[i].rxbuf = NULL;
-        halves[i].backoff_deadline = 0;
-        halves[i].backoff = 1;
-        reconnect(&halves[i]);
-    }
+    relay_create(rconn_new(argv[optind], 1), rconn_new(argv[optind + 1], 1));
     for (;;) {
-        /* Do some work.  Limit the number of iterations so that callbacks
-         * registered with the poll loop don't starve. */
-        int iteration;
-        for (iteration = 0; iteration < 50; iteration++) {
-            bool progress = false;
-            for (i = 0; i < 2; i++) {
-                struct half *this = &halves[i];
-                struct half *peer = &halves[!i];
-
-                if (!this->rxbuf) {
-                    retval = vconn_recv(this->vconn, &this->rxbuf);
-                    if (retval && retval != EAGAIN) {
-                        if (retval == EOF) {
-                            VLOG_DBG("%s: connection closed by remote host",
-                                     this->name); 
-                        } else {
-                            VLOG_DBG("%s: recv: closing connection: %s",
-                                     this->name, strerror(retval));
-                        }
-                        reconnect(this);
-                        break;
-                    }
-                }
+        struct relay *r, *n;
 
-                if (this->rxbuf) {
-                    retval = vconn_send(peer->vconn, this->rxbuf);
-                    if (!retval) {
-                        this->rxbuf = NULL;
-                        progress = true;
-                    } else if (retval != EAGAIN) {
-                        VLOG_DBG("%s: send: closing connection: %s",
-                                 peer->name, strerror(retval));
-                        reconnect(peer);
-                        break;
+        /* Do work. */
+        LIST_FOR_EACH_SAFE (r, n, struct relay, node, &relays) {
+            relay_run(r);
+        }
+        if (listen_vconn) {
+            for (;;) {
+                struct vconn *new_remote;
+                retval = vconn_accept(listen_vconn, &new_remote);
+                if (retval) {
+                    if (retval != EAGAIN) {
+                        VLOG_WARN("accept failed (%s)", strerror(retval));
                     }
+                    break;
                 }
-            }
-            if (!progress) {
-                break;
+                new_management_connection(nl_name, new_remote);
             }
         }
 
         /* Wait for something to happen. */
-        for (i = 0; i < 2; i++) {
-            struct half *this = &halves[i];
-            struct half *peer = &halves[!i];
-            if (!this->rxbuf) {
-                vconn_recv_wait(this->vconn);
-            } else {
-                vconn_send_wait(peer->vconn);
-            }
+        LIST_FOR_EACH (r, struct relay, node, &relays) {
+            relay_wait(r);
+        }
+        if (listen_vconn) {
+            vconn_accept_wait(listen_vconn);
         }
         poll_block();
     }
@@ -157,85 +154,150 @@ main(int argc, char *argv[])
 }
 
 static void
-reconnect(struct half *this) 
+new_management_connection(const char *nl_name, struct vconn *new_remote)
 {
-    if (this->vconn != NULL) {
-        if (!reliable) {
-            fatal(0, "%s: connection dropped", this->name);
-        }
+    char *nl_name_without_subscription;
+    struct vconn *new_local;
+    struct rconn *r1, *r2;
+    int retval;
 
-        VLOG_WARN("%s: connection dropped, reconnecting", this->name);
-        vconn_close(this->vconn);
-        this->vconn = NULL;
-        buffer_delete(this->rxbuf);
-        this->rxbuf = NULL;
+    /* nl:123 or nl:123:1 opens a netlink connection to local datapath 123.  We
+     * only accept the former syntax in main().
+     *
+     * nl:123:0 opens a netlink connection to local datapath 123 without
+     * obtaining a subscription for ofp_packet_in or ofp_flow_expired
+     * messages.*/
+    nl_name_without_subscription = xasprintf("%s:0", nl_name);
+    retval = vconn_open(nl_name_without_subscription, &new_local);
+    if (retval) {
+        VLOG_ERR("could not connect to %s (%s)",
+                 nl_name_without_subscription, strerror(retval));
+        vconn_close(new_remote);
+        return;
     }
+    free(nl_name_without_subscription);
 
-    for (;;) {
-        time_t now = time(0);
-        int retval;
-
-        if (now >= this->backoff_deadline) {
-            this->backoff = 1;
-        } else {
-            this->backoff *= 2;
-            if (this->backoff > 60) {
-                this->backoff = 60;
+    /* Add it to the relay list. */
+    r1 = rconn_new_from_vconn(nl_name_without_subscription, 1, new_local);
+    r2 = rconn_new_from_vconn("passive", 1, new_remote);
+    relay_create(r1, r2);
+}
+
+static void
+relay_create(struct rconn *a, struct rconn *b)
+{
+    struct relay *r;
+    int i;
+
+    r = xmalloc(sizeof *r);
+    for (i = 0; i < 2; i++) {
+        r->halves[i].rconn = i ? b : a;
+        r->halves[i].rxbuf = NULL;
+    }
+    list_push_back(&relays, &r->node);
+}
+
+static void
+relay_run(struct relay *r)
+{
+    int iteration;
+    int i;
+
+    for (i = 0; i < 2; i++) {
+        rconn_run(r->halves[i].rconn);
+    }
+
+    /* Limit the number of iterations to prevent other tasks from starving. */
+    for (iteration = 0; iteration < 50; iteration++) {
+        bool progress = false;
+        for (i = 0; i < 2; i++) {
+            struct half *this = &r->halves[i];
+            struct half *peer = &r->halves[!i];
+
+            if (!this->rxbuf) {
+                this->rxbuf = rconn_recv(this->rconn);
             }
-            VLOG_WARN("%s: waiting %d seconds before reconnect\n",
-                      this->name, (int) (this->backoff_deadline - now));
-            poll_timer_wait((this->backoff_deadline - now) * 1000);
-            poll_block();
-        }
 
-        retval = vconn_open_block(this->name, &this->vconn);
-        if (!retval) {
-            VLOG_WARN("%s: connected", this->name);
-            if (vconn_is_passive(this->vconn)) {
-                fatal(0, "%s: passive vconn not supported in control path",
-                      this->name);
+            if (this->rxbuf) {
+                int retval = rconn_send(peer->rconn, this->rxbuf);
+                if (retval != EAGAIN) {
+                    this->rxbuf = NULL;
+                    if (!retval) {
+                        progress = true;
+                    }
+                }
             }
-            this->backoff_deadline = now + this->backoff;
+        }
+        if (!progress) {
+            break;
+        }
+    }
+
+    for (i = 0; i < 2; i++) {
+        struct half *this = &r->halves[i];
+        if (!rconn_is_alive(this->rconn)) {
+            relay_destroy(r);
             return;
         }
+    }
+}
 
-        if (!reliable) {
-            fatal(0, "%s: connection failed", this->name);
+static void
+relay_wait(struct relay *r)
+{
+    int i;
+
+    for (i = 0; i < 2; i++) {
+        struct half *this = &r->halves[i];
+
+        rconn_run_wait(this->rconn);
+        if (!this->rxbuf) {
+            rconn_recv_wait(this->rconn);
         }
-        VLOG_WARN("%s: connection failed (%s)", this->name, strerror(retval));
-        this->backoff_deadline = time(0) + this->backoff;
     }
 }
 
+static void
+relay_destroy(struct relay *r)
+{
+    int i;
+
+    list_remove(&r->node);
+    for (i = 0; i < 2; i++) {
+        struct half *this = &r->halves[i];
+        rconn_destroy(this->rconn);
+        buffer_delete(this->rxbuf);
+    }
+    free(r);
+}
+
 static void
 parse_options(int argc, char *argv[]) 
 {
     static struct option long_options[] = {
-        {"unreliable",  no_argument, 0, 'u'},
+        {"listen",      required_argument, 0, 'l'},
         {"verbose",     optional_argument, 0, 'v'},
         {"help",        no_argument, 0, 'h'},
         {"version",     no_argument, 0, 'V'},
-#ifdef HAVE_OPENSSL
-        {"private-key", required_argument, 0, 'p'},
-        {"certificate", required_argument, 0, 'c'},
-        {"ca-cert",     required_argument, 0, 'C'},
-#endif
+        VCONN_SSL_LONG_OPTIONS
         {0, 0, 0, 0},
     };
     char *short_options = long_options_to_short_options(long_options);
     
     for (;;) {
-        int indexptr;
         int c;
 
-        c = getopt_long(argc, argv, short_options, long_options, &indexptr);
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
         if (c == -1) {
             break;
         }
 
         switch (c) {
-        case 'u':
-            reliable = false;
+        case 'l':
+            if (listen_vconn_name) {
+                fatal(0, "-l or --listen may be only specified once");
+            }
+            listen_vconn_name = optarg;
             break;
 
         case 'h':
@@ -249,19 +311,7 @@ parse_options(int argc, char *argv[])
             vlog_set_verbosity(optarg);
             break;
 
-#ifdef HAVE_OPENSSL
-        case 'p':
-            vconn_ssl_set_private_key_file(optarg);
-            break;
-
-        case 'c':
-            vconn_ssl_set_certificate_file(optarg);
-            break;
-
-        case 'C':
-            vconn_ssl_set_ca_cert_file(optarg);
-            break;
-#endif
+        VCONN_SSL_OPTION_HANDLERS
 
         case '?':
             exit(EXIT_FAILURE);
@@ -276,25 +326,14 @@ parse_options(int argc, char *argv[])
 static void
 usage(void)
 {
-    printf("%s: Secure Channel\n"
+    printf("%s: Secure Channel, a relay for OpenFlow messages.\n"
            "usage: %s [OPTIONS] LOCAL REMOTE\n"
-           "\nRelays OpenFlow message between LOCAL and REMOTE datapaths.\n"
-           "LOCAL and REMOTE must each be one of the following:\n"
-           "  tcp:HOST[:PORT]         PORT (default: %d) on remote TCP HOST\n",
-           program_name, program_name, OFP_TCP_PORT);
-#ifdef HAVE_NETLINK
-    printf("  nl:DP_IDX               local datapath DP_IDX\n");
-#endif
-#ifdef HAVE_OPENSSL
-    printf("  ssl:HOST[:PORT]         SSL PORT (default: %d) on remote HOST\n"
-           "\nPKI configuration (required to use SSL):\n"
-           "  -p, --private-key=FILE  file with private key\n"
-           "  -c, --certificate=FILE  file with certificate for private key\n"
-           "  -C, --ca-cert=FILE      file with peer CA certificate\n",
-           OFP_SSL_PORT);
-#endif
+           "where LOCAL and REMOTE are active OpenFlow connection methods.\n",
+           program_name, program_name);
+    vconn_usage(true, true);
     printf("\nNetworking options:\n"
-           "  -u, --unreliable        do not reconnect after connections drop\n"
+           "  -l, --listen=METHOD     allow management connections on METHOD\n"
+           "                          (a passive OpenFlow connection method)\n"
            "\nOther options:\n"
            "  -v, --verbose           set maximum verbosity level\n"
            "  -h, --help              display this help message\n"
index 24c23891cfca74f9304130c4f173a9fbc662f7cb..eb9931b003f279561508f1c5137a9303decde095 100644 (file)
@@ -5,8 +5,6 @@ bin_PROGRAMS = switch
 switch_SOURCES = \
        chain.c \
        chain.h \
-       controller.c \
-       controller.h \
        crc32.c \
        crc32.h \
        datapath.c \
index e2ac53c6b4ea2ff3b6144649c74c92091ce90aaa..2a19df0271b4d827a32bd24516c90888573fbf85 100644 (file)
 #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. */
index 3c930aa7196f4605300729a373f661e30df6488e..2d751d03a5510838cbcaae33a5522b9acdb53e8d 100644 (file)
@@ -43,6 +43,13 @@ struct list;
 #define TABLE_MAC_MAX_FLOWS      1024
 #define TABLE_MAC_NUM_BUCKETS   1024
 
+/* Set of tables chained together in sequence from cheap to expensive. */
+#define CHAIN_MAX_TABLES 4
+struct sw_chain {
+    int n_tables;
+    struct sw_table *tables[CHAIN_MAX_TABLES];
+};
+
 struct sw_chain *chain_create(void);
 struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *);
 int chain_insert(struct sw_chain *, struct sw_flow *);
diff --git a/switch/controller.c b/switch/controller.c
deleted file mode 100644 (file)
index d815231..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
- * Junior University
- * 
- * We are making the OpenFlow specification and associated documentation
- * (Software) available for public use and benefit with the expectation
- * that others will use, modify and enhance the Software and contribute
- * those enhancements back to the community. However, since we would
- * like to make the Software available for broadest use, with as few
- * restrictions as possible permission is hereby granted, free of
- * charge, to any person obtaining a copy of this Software to deal in
- * the Software under the copyrights without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- * 
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- * 
- * The name and trademarks of copyright holder(s) may NOT be used in
- * advertising or publicity pertaining to the Software or any
- * derivatives without specific, written prior permission.
- */
-
-#include "controller.h"
-#include <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);
-        } 
-    }
-}
diff --git a/switch/controller.h b/switch/controller.h
deleted file mode 100644 (file)
index 6c38dd1..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
- * Junior University
- * 
- * We are making the OpenFlow specification and associated documentation
- * (Software) available for public use and benefit with the expectation
- * that others will use, modify and enhance the Software and contribute
- * those enhancements back to the community. However, since we would
- * like to make the Software available for broadest use, with as few
- * restrictions as possible permission is hereby granted, free of
- * charge, to any person obtaining a copy of this Software to deal in
- * the Software under the copyrights without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- * 
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- * 
- * The name and trademarks of copyright holder(s) may NOT be used in
- * advertising or publicity pertaining to the Software or any
- * derivatives without specific, written prior permission.
- */
-
-#ifndef CONTROLLER_H
-#define CONTROLLER_H 1
-
-#include "queue.h"
-#include <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 *);
-
-#endif /* controller.h */
index ffe578af5f61b9d9254a7e89f63a41b103bcd63b..0d4ffa77d647cb9c19ae371daf45c88f1bde5ca6 100644 (file)
 #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"
 
@@ -70,10 +71,26 @@ struct sw_port {
     struct datapath *dp;
     struct netdev *netdev;
     struct list node; /* Element in datapath.ports. */
+    unsigned long long int rx_count, tx_count, drop_count;
+};
+
+/* A connection to a controller or a management device. */
+struct remote {
+    struct list node;
+    struct rconn *rconn;
+};
+
+/* The origin of a received OpenFlow message, to enable sending a reply. */
+struct sender {
+    struct remote *remote;      /* The device that sent the message. */
+    uint32_t xid;               /* The OpenFlow transaction ID. */
 };
 
 struct datapath {
-    struct controller_connection *cc;
+    /* Remote connections. */
+    struct remote *controller;  /* Connection to controller. */
+    struct list remotes;        /* All connections (including controller). */
+    struct vconn *listen_vconn;
 
     time_t last_timeout;
 
@@ -82,20 +99,20 @@ struct datapath {
 
     struct sw_chain *chain;  /* Forwarding rules. */
 
-    /* Flags from the control hello message */
-    uint16_t hello_flags;
-
-    /* Maximum number of bytes that should be sent for flow misses */
-    uint16_t miss_send_len;
+    struct ofp_switch_config config;
 
     /* Switch ports. */
     struct sw_port ports[OFPP_MAX];
     struct list port_list; /* List of ports, for flooding. */
 };
 
+static struct remote *remote_create(struct datapath *, struct rconn *);
+static void remote_run(struct datapath *, struct remote *);
+static void remote_wait(struct remote *);
+static void remote_destroy(struct remote *);
+
 void dp_output_port(struct datapath *, struct buffer *,
                     int in_port, int out_port);
-void dp_send_hello(struct datapath *);
 void dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
 void dp_output_control(struct datapath *, struct buffer *, int in_port,
                        size_t max_len, int reason);
@@ -124,7 +141,8 @@ static void modify_th(struct buffer *buffer, uint16_t eth_proto,
 #define PKT_COOKIE_BITS (32 - PKT_BUFFER_BITS)
 
 void fwd_port_input(struct datapath *, struct buffer *, int in_port);
-int fwd_control_input(struct datapath *, const void *, size_t);
+int fwd_control_input(struct datapath *, const struct sender *,
+                      const void *, size_t);
 
 uint32_t save_buffer(struct buffer *);
 static struct buffer *retrieve_buffer(uint32_t id);
@@ -157,7 +175,7 @@ gen_datapath_id(void)
 }
 
 int
-dp_new(struct datapath **dp_, uint64_t dpid, struct controller_connection *cc)
+dp_new(struct datapath **dp_, uint64_t dpid, struct rconn *rconn)
 {
     struct datapath *dp;
 
@@ -167,7 +185,9 @@ dp_new(struct datapath **dp_, uint64_t dpid, struct controller_connection *cc)
     }
 
     dp->last_timeout = time(0);
-    dp->cc = cc;
+    list_init(&dp->remotes);
+    dp->controller = remote_create(dp, rconn);
+    dp->listen_vconn = NULL;
     dp->id = dpid <= UINT64_C(0xffffffffffff) ? dpid : gen_datapath_id();
     dp->chain = chain_create();
     if (!dp->chain) {
@@ -177,7 +197,8 @@ dp_new(struct datapath **dp_, uint64_t dpid, struct controller_connection *cc)
     }
 
     list_init(&dp->port_list);
-    dp->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
+    dp->config.flags = 0;
+    dp->config.miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
     *dp_ = dp;
     return 0;
 }
@@ -204,6 +225,9 @@ dp_add_port(struct datapath *dp, const char *name)
 
     p->dp = dp;
     p->netdev = netdev;
+    p->tx_count = 0;
+    p->rx_count = 0;
+    p->drop_count = 0;
     list_push_back(&dp->port_list, &p->node);
 
     /* Notify the ctlpath that this port has been added */
@@ -212,13 +236,20 @@ dp_add_port(struct datapath *dp, const char *name)
     return 0;
 }
 
+void
+dp_add_listen_vconn(struct datapath *dp, struct vconn *listen_vconn)
+{
+    assert(!dp->listen_vconn);
+    dp->listen_vconn = listen_vconn;
+}
+
 void
 dp_run(struct datapath *dp) 
 {
     time_t now = time(0);
-    struct sw_port *p, *n;
+    struct sw_port *p, *pn;
+    struct remote *r, *rn;
     struct buffer *buffer = NULL;
-    int i;
 
     if (now != dp->last_timeout) {
         struct list deleted = LIST_INITIALIZER(&deleted);
@@ -234,7 +265,7 @@ dp_run(struct datapath *dp)
     }
     poll_timer_wait(1000);
     
-    LIST_FOR_EACH_SAFE (p, n, struct sw_port, node, &dp->port_list) {
+    LIST_FOR_EACH_SAFE (p, pn, struct sw_port, node, &dp->port_list) {
         int error;
 
         if (!buffer) {
@@ -249,6 +280,7 @@ dp_run(struct datapath *dp)
         }
         error = netdev_recv(p->netdev, buffer);
         if (!error) {
+            p->rx_count++;
             fwd_port_input(dp, buffer, port_no(dp, p));
             buffer = NULL;
         } else if (error != EAGAIN) {
@@ -259,29 +291,104 @@ dp_run(struct datapath *dp)
     }
     buffer_delete(buffer);
 
-    /* Process a number of commands from the controller, but cap it at a
+    /* Talk to remotes. */
+    LIST_FOR_EACH_SAFE (r, rn, struct remote, node, &dp->remotes) {
+        remote_run(dp, r);
+    }
+    if (dp->listen_vconn) {
+        for (;;) {
+            struct vconn *new_vconn;
+            int retval;
+
+            retval = vconn_accept(dp->listen_vconn, &new_vconn);
+            if (retval) {
+                if (retval != EAGAIN) {
+                    VLOG_WARN("accept failed (%s)", strerror(retval));
+                }
+                break;
+            }
+            remote_create(dp, rconn_new_from_vconn("passive", 128, new_vconn));
+        }
+    }
+}
+
+static void
+remote_run(struct datapath *dp, struct remote *r)
+{
+    int i;
+
+    rconn_run(r->rconn);
+
+    /* Process a number of commands from the remote, but cap them at a
      * reasonable number so that other processing doesn't starve. */
     for (i = 0; i < 50; i++) {
-        struct buffer *buffer = controller_recv(dp->cc);
+        struct buffer *buffer;
+        struct ofp_header *oh;
+
+        buffer = rconn_recv(r->rconn);
         if (!buffer) {
             break;
         }
-        fwd_control_input(dp, buffer->data, buffer->size);
+
+        if (buffer->size >= sizeof *oh) {
+            struct sender sender;
+
+            oh = buffer->data;
+            sender.remote = r;
+            sender.xid = oh->xid;
+            fwd_control_input(dp, &sender, buffer->data, buffer->size);
+        } else {
+            VLOG_WARN("received too-short OpenFlow message"); 
+        }
         buffer_delete(buffer);
     }
 
-    controller_run(dp->cc);
+    if (!rconn_is_alive(r->rconn)) {
+        remote_destroy(r);
+    }
+}
+
+static void
+remote_wait(struct remote *r) 
+{
+    rconn_run_wait(r->rconn);
+    rconn_recv_wait(r->rconn);
+}
+
+static void
+remote_destroy(struct remote *r)
+{
+    if (r) {
+        list_remove(&r->node);
+        rconn_destroy(r->rconn);
+        free(r);
+    }
+}
+
+static struct remote *
+remote_create(struct datapath *dp, struct rconn *rconn) 
+{
+    struct remote *remote = xmalloc(sizeof *remote);
+    list_push_back(&dp->remotes, &remote->node);
+    remote->rconn = rconn;
+    return remote;
 }
 
 void
 dp_wait(struct datapath *dp) 
 {
     struct sw_port *p;
+    struct remote *r;
 
     LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
         netdev_recv_wait(p->netdev);
     }
-    controller_recv_wait(dp->cc);
+    LIST_FOR_EACH (r, struct remote, node, &dp->remotes) {
+        remote_wait(r);
+    }
+    if (dp->listen_vconn) {
+        vconn_accept_wait(dp->listen_vconn);
+    }
 }
 
 /* Delete 'p' from switch. */
@@ -340,7 +447,11 @@ output_packet(struct datapath *dp, struct buffer *buffer, int out_port)
     if (out_port >= 0 && out_port < OFPP_MAX) { 
         struct sw_port *p = &dp->ports[out_port];
         if (p->netdev != NULL) {
-            netdev_send(p->netdev, buffer);
+            if (!netdev_send(p->netdev, buffer)) {
+                p->tx_count++;
+            } else {
+                p->drop_count++;
+            }
             return;
         }
     }
@@ -367,11 +478,49 @@ dp_output_port(struct datapath *dp, struct buffer *buffer,
     }
 }
 
-/* Takes ownership of 'buffer' and transmits it to 'dp''s controller.  If
- * 'buffer_id' != -1, then only the first 64 bytes of 'buffer' are sent;
- * otherwise, all of 'buffer' is sent.  'reason' indicates why 'buffer' is
- * being sent. 'max_len' sets the maximum number of bytes that the caller wants
- * to be sent; a value of 0 indicates the entire packet should be sent. */
+static void *
+alloc_openflow_buffer(struct datapath *dp, size_t openflow_len, uint8_t type,
+                      const struct sender *sender, struct buffer **bufferp)
+{
+       struct buffer *buffer;
+       struct ofp_header *oh;
+
+       buffer = *bufferp = buffer_new(openflow_len);
+       oh = buffer_put_uninit(buffer, openflow_len);
+       oh->version = OFP_VERSION;
+       oh->type = type;
+       oh->length = 0;             /* Filled in by send_openflow_buffer(). */
+       oh->xid = sender ? sender->xid : 0;
+       return oh;
+}
+
+static int
+send_openflow_buffer(struct datapath *dp, struct buffer *buffer,
+                     const struct sender *sender)
+{
+    struct remote *remote = sender ? sender->remote : dp->controller;
+    struct rconn *rconn = remote->rconn;
+    struct ofp_header *oh;
+    int retval;
+
+    oh = buffer_at_assert(buffer, 0, sizeof *oh);
+    oh->length = htons(buffer->size);
+
+    retval = rconn_send(rconn, buffer);
+    if (retval) {
+        VLOG_WARN("send to %s failed: %s",
+                  rconn_get_name(rconn), strerror(retval));
+        buffer_delete(buffer);
+    }
+    return retval;
+}
+
+/* Takes ownership of 'buffer' and transmits it to 'dp''s controller.  If the
+ * packet can be saved in a buffer, then only the first max_len bytes of
+ * 'buffer' are sent; otherwise, all of 'buffer' is sent.  'reason' indicates
+ * why 'buffer' is being sent. 'max_len' sets the maximum number of bytes that
+ * the caller wants to be sent; a value of 0 indicates the entire packet should
+ * be sent. */
 void
 dp_output_control(struct datapath *dp, struct buffer *buffer, int in_port,
                   size_t max_len, int reason)
@@ -382,7 +531,7 @@ dp_output_control(struct datapath *dp, struct buffer *buffer, int in_port,
 
     buffer_id = save_buffer(buffer);
     total_len = buffer->size;
-    if (buffer_id != UINT32_MAX && max_len > buffer->size) {
+    if (buffer_id != UINT32_MAX && buffer->size > max_len) {
         buffer->size = max_len;
     }
 
@@ -396,7 +545,7 @@ dp_output_control(struct datapath *dp, struct buffer *buffer, int in_port,
     opi->in_port        = htons(in_port);
     opi->reason         = reason;
     opi->pad            = 0;
-    controller_send(dp->cc, buffer);
+    send_openflow_buffer(dp, buffer, NULL);
 }
 
 static void fill_port_desc(struct datapath *dp, struct sw_port *p,
@@ -412,37 +561,30 @@ static void fill_port_desc(struct datapath *dp, struct sw_port *p,
     desc->speed = htonl(netdev_get_speed(p->netdev));
 }
 
-void
-dp_send_hello(struct datapath *dp)
+static void
+dp_send_features_reply(struct datapath *dp, const struct sender *sender)
 {
     struct buffer *buffer;
-    struct ofp_data_hello *odh;
+    struct ofp_switch_features *ofr;
     struct sw_port *p;
 
-    buffer = buffer_new(sizeof *odh);
-    odh = buffer_put_uninit(buffer, sizeof *odh);
-    memset(odh, 0, sizeof *odh);
-    odh->header.version = OFP_VERSION;
-    odh->header.type    = OFPT_DATA_HELLO;
-    odh->header.xid     = htonl(0);
-    odh->datapath_id    = htonll(dp->id); 
-    odh->n_exact        = htonl(2 * TABLE_HASH_MAX_FLOWS);
-    odh->n_mac_only     = htonl(TABLE_MAC_MAX_FLOWS);
-    odh->n_compression  = 0;                                           /* Not supported */
-    odh->n_general      = htonl(TABLE_LINEAR_MAX_FLOWS);
-    odh->buffer_mb      = htonl(UINT32_MAX);
-    odh->n_buffers      = htonl(N_PKT_BUFFERS);
-    odh->capabilities   = htonl(OFP_SUPPORTED_CAPABILITIES);
-    odh->actions        = htonl(OFP_SUPPORTED_ACTIONS);
-    odh->miss_send_len  = htons(dp->miss_send_len); 
+    ofr = alloc_openflow_buffer(dp, sizeof *ofr, OFPT_FEATURES_REPLY,
+                                sender, &buffer);
+    ofr->datapath_id    = htonll(dp->id); 
+    ofr->n_exact        = htonl(2 * TABLE_HASH_MAX_FLOWS);
+    ofr->n_mac_only     = htonl(TABLE_MAC_MAX_FLOWS);
+    ofr->n_compression  = 0;                                           /* Not supported */
+    ofr->n_general      = htonl(TABLE_LINEAR_MAX_FLOWS);
+    ofr->buffer_mb      = htonl(UINT32_MAX);
+    ofr->n_buffers      = htonl(N_PKT_BUFFERS);
+    ofr->capabilities   = htonl(OFP_SUPPORTED_CAPABILITIES);
+    ofr->actions        = htonl(OFP_SUPPORTED_ACTIONS);
     LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
         struct ofp_phy_port *opp = buffer_put_uninit(buffer, sizeof *opp);
         memset(opp, 0, sizeof *opp);
         fill_port_desc(dp, p, opp);
     }
-    odh = buffer_at_assert(buffer, 0, sizeof *odh);
-    odh->header.length = htons(buffer->size);
-    controller_send(dp->cc, buffer);
+    send_openflow_buffer(dp, buffer, sender);
 }
 
 void
@@ -465,15 +607,11 @@ send_port_status(struct sw_port *p, uint8_t status)
 {
     struct buffer *buffer;
     struct ofp_port_status *ops;
-    buffer = buffer_new(sizeof *ops);
-    ops = buffer_put_uninit(buffer, sizeof *ops);
-    ops->header.version = OFP_VERSION;
-    ops->header.type    = OFPT_PORT_STATUS;
-    ops->header.length  = htons(sizeof(*ops));
-    ops->header.xid     = htonl(0);
+    ops = alloc_openflow_buffer(p->dp, sizeof *ops, OFPT_PORT_STATUS, NULL,
+                                &buffer);
     ops->reason         = status;
     fill_port_desc(p->dp, p, &ops->desc);
-    controller_send(p->dp->cc, buffer);
+    send_openflow_buffer(p->dp, buffer, NULL);
 }
 
 void
@@ -481,17 +619,129 @@ send_flow_expired(struct datapath *dp, struct sw_flow *flow)
 {
     struct buffer *buffer;
     struct ofp_flow_expired *ofe;
-    buffer = buffer_new(sizeof *ofe);
-    ofe = buffer_put_uninit(buffer, sizeof *ofe);
-    ofe->header.version = OFP_VERSION;
-    ofe->header.type    = OFPT_FLOW_EXPIRED;
-    ofe->header.length  = htons(sizeof(*ofe));
-    ofe->header.xid     = htonl(0);
+    ofe = alloc_openflow_buffer(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, NULL,
+                                &buffer);
     flow_fill_match(&ofe->match, &flow->key);
     ofe->duration   = htonl(flow->timeout - flow->max_idle - flow->created);
     ofe->packet_count   = htonll(flow->packet_count);
     ofe->byte_count     = htonll(flow->byte_count);
-    controller_send(dp->cc, buffer);
+    send_openflow_buffer(dp, buffer, NULL);
+}
+
+static void
+fill_flow_stats(struct ofp_flow_stats *ofs, struct sw_flow *flow,
+                int table_idx, time_t now)
+{
+       ofs->match.wildcards = htons(flow->key.wildcards);
+       ofs->match.in_port   = flow->key.flow.in_port;
+       memcpy(ofs->match.dl_src, flow->key.flow.dl_src, ETH_ADDR_LEN);
+       memcpy(ofs->match.dl_dst, flow->key.flow.dl_dst, ETH_ADDR_LEN);
+       ofs->match.dl_vlan   = flow->key.flow.dl_vlan;
+       ofs->match.dl_type   = flow->key.flow.dl_type;
+       ofs->match.nw_src    = flow->key.flow.nw_src;
+       ofs->match.nw_dst    = flow->key.flow.nw_dst;
+       ofs->match.nw_proto  = flow->key.flow.nw_proto;
+       memset(ofs->match.pad, 0, sizeof ofs->match.pad);
+       ofs->match.tp_src    = flow->key.flow.tp_src;
+       ofs->match.tp_dst    = flow->key.flow.tp_dst;
+       ofs->duration        = htonl(now - flow->created);
+       ofs->table_id        = table_idx;
+       ofs->packet_count    = htonll(flow->packet_count);
+       ofs->byte_count      = htonll(flow->byte_count);
+}
+
+int
+dp_send_flow_stats(struct datapath *dp, const struct sender *sender,
+                   const struct ofp_match *match)
+{
+    struct buffer *buffer;
+    struct ofp_flow_stat_reply *fsr;
+    size_t header_size, fudge, flow_size;
+    struct sw_flow_key match_key;
+    int table_idx, n_flows, max_flows;
+    time_t now;
+
+    header_size = offsetof(struct ofp_flow_stat_reply, flows);
+    fudge = 128;
+    flow_size = sizeof fsr->flows[0];
+    max_flows = (65536 - header_size - fudge) / flow_size;
+    fsr = alloc_openflow_buffer(dp, header_size,
+                                OFPT_FLOW_STAT_REPLY, sender, &buffer);
+
+    n_flows = 0;
+    flow_extract_match(&match_key, match);
+    now = time(0);
+    for (table_idx = 0; table_idx < dp->chain->n_tables; table_idx++) {
+        struct sw_table *table = dp->chain->tables[table_idx];
+        struct swt_iterator iter;
+
+        if (n_flows >= max_flows) {
+            break;
+        }
+
+        if (!table->iterator(table, &iter)) {
+            printf("iterator failed for table %d\n", table_idx);
+            continue;
+        }
+
+        for (; iter.flow; table->iterator_next(&iter)) {
+            if (flow_matches(&match_key, &iter.flow->key)) {
+                struct ofp_flow_stats *ofs = buffer_put_uninit(buffer,
+                                                               sizeof *ofs);
+                fill_flow_stats(ofs, iter.flow, table_idx, now);
+                if (++n_flows >= max_flows) {
+                    break;
+                }
+            }
+        }
+        table->iterator_destroy(&iter);
+    }
+    return send_openflow_buffer(dp, buffer, sender);
+}
+
+int
+dp_send_port_stats(struct datapath *dp, const struct sender *sender)
+{
+       struct buffer *buffer;
+       struct ofp_port_stat_reply *psr;
+    struct sw_port *p;
+
+       psr = alloc_openflow_buffer(dp, offsetof(struct ofp_port_stat_reply,
+                                             ports),
+                                OFPT_PORT_STAT_REPLY, sender, &buffer);
+    LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
+               struct ofp_port_stats *ps = buffer_put_uninit(buffer, sizeof *ps);
+               ps->port_no = htons(port_no(dp, p));
+               memset(ps->pad, 0, sizeof ps->pad);
+               ps->rx_count = htonll(p->rx_count);
+               ps->tx_count = htonll(p->tx_count);
+               ps->drop_count = htonll(p->drop_count);
+       }
+       return send_openflow_buffer(dp, buffer, sender);
+}
+
+int
+dp_send_table_stats(struct datapath *dp, const struct sender *sender)
+{
+       struct buffer *buffer;
+       struct ofp_table_stat_reply *tsr;
+       int i;
+
+       tsr = alloc_openflow_buffer(dp, offsetof(struct ofp_table_stat_reply,
+                                             tables),
+                                OFPT_TABLE_STAT_REPLY, sender, &buffer);
+       for (i = 0; i < dp->chain->n_tables; i++) {
+               struct ofp_table_stats *ots = buffer_put_uninit(buffer, sizeof *ots);
+               struct sw_table_stats stats;
+               dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
+               strncpy(ots->name, stats.name, sizeof ots->name);
+               ots->table_id = i;
+               ots->pad[0] = ots->pad[1] = 0;
+               ots->max_entries = htonl(stats.max_flows);
+               ots->active_count = htonl(stats.n_flows);
+               ots->matched_count = htonll(0); /* FIXME */
+       }
+       return send_openflow_buffer(dp, buffer, sender);
 }
 \f
 /* 'buffer' was received on 'in_port', a physical switch port between 0 and
@@ -509,7 +759,7 @@ void fwd_port_input(struct datapath *dp, struct buffer *buffer, int in_port)
         execute_actions(dp, buffer, in_port, &key,
                         flow->actions, flow->n_actions);
     } else {
-        dp_output_control(dp, buffer, in_port, dp->miss_send_len,
+        dp_output_control(dp, buffer, in_port, ntohs(dp->config.miss_send_len),
                           OFPR_NO_MATCH);
     }
 }
@@ -711,25 +961,43 @@ modify_vlan(struct buffer *buffer,
 }
 
 static int
-recv_control_hello(struct datapath *dp, const void *msg)
+recv_features_request(struct datapath *dp, const struct sender *sender,
+                      const void *msg) 
 {
-    const struct ofp_control_hello *och = msg;
+    dp_send_features_reply(dp, sender);
+    return 0;
+}
 
-    printf("control_hello(version=%d)\n", ntohl(och->version));
+static int
+recv_get_config_request(struct datapath *dp, const struct sender *sender,
+                        const void *msg) 
+{
+    struct buffer *buffer;
+    struct ofp_switch_config *osc;
 
-    if (ntohs(och->miss_send_len) != OFP_MISS_SEND_LEN_UNCHANGED) {
-        dp->miss_send_len = ntohs(och->miss_send_len);
-    }
+    osc = alloc_openflow_buffer(dp, sizeof *osc, OFPT_GET_CONFIG_REPLY,
+                                sender, &buffer);
 
-    dp->hello_flags = ntohs(och->flags);
+    assert(sizeof *osc == sizeof dp->config);
+       memcpy(((char *)osc) + sizeof osc->header,
+              ((char *)&dp->config) + sizeof dp->config.header,
+              sizeof dp->config - sizeof dp->config.header);
 
-    dp_send_hello(dp);
+    return send_openflow_buffer(dp, buffer, sender);
+}
 
+static int
+recv_set_config(struct datapath *dp, const struct sender *sender UNUSED,
+                const void *msg)
+{
+    const struct ofp_switch_config *osc = msg;
+    dp->config = *osc;
     return 0;
 }
 
 static int
-recv_packet_out(struct datapath *dp, const void *msg)
+recv_packet_out(struct datapath *dp, const struct sender *sender UNUSED,
+                const void *msg)
 {
     const struct ofp_packet_out *opo = msg;
 
@@ -760,7 +1028,8 @@ recv_packet_out(struct datapath *dp, const void *msg)
 }
 
 static int
-recv_port_mod(struct datapath *dp, const void *msg)
+recv_port_mod(struct datapath *dp, const struct sender *sender UNUSED,
+              const void *msg)
 {
     const struct ofp_port_mod *opm = msg;
 
@@ -829,7 +1098,8 @@ error:
 }
 
 static int
-recv_flow(struct datapath *dp, const void *msg)
+recv_flow(struct datapath *dp, const struct sender *sender UNUSED,
+          const void *msg)
 {
     const struct ofp_flow_mod *ofm = msg;
     uint16_t command = ntohs(ofm->command);
@@ -849,21 +1119,56 @@ recv_flow(struct datapath *dp, const void *msg)
     }
 }
 
+static int
+recv_flow_status_request(struct datapath *dp, const struct sender *sender,
+                         const void *msg)
+{
+       const struct ofp_flow_stat_request *fsr = msg;
+       if (fsr->type == OFPFS_INDIV) {
+               return dp_send_flow_stats(dp, sender, &fsr->match); 
+       } else {
+               /* FIXME */
+               return -ENOSYS;
+       }
+}
+
+static int
+recv_port_status_request(struct datapath *dp, const struct sender *sender,
+                         const void *msg)
+{
+       return dp_send_port_stats(dp, sender);
+}
+
+static int
+recv_table_status_request(struct datapath *dp, const struct sender *sender,
+                          const void *msg)
+{
+       return dp_send_table_stats(dp, sender);
+}
+
 /* 'msg', which is 'length' bytes long, was received from the control path.
  * Apply it to 'chain'. */
 int
-fwd_control_input(struct datapath *dp, const void *msg, size_t length)
+fwd_control_input(struct datapath *dp, const struct sender *sender,
+                  const void *msg, size_t length)
 {
-
     struct openflow_packet {
         size_t min_size;
-        int (*handler)(struct datapath *, const void *);
+        int (*handler)(struct datapath *, const struct sender *, const void *);
     };
 
     static const struct openflow_packet packets[] = {
-        [OFPT_CONTROL_HELLO] = {
-            sizeof (struct ofp_control_hello),
-            recv_control_hello,
+        [OFPT_FEATURES_REQUEST] = {
+            sizeof (struct ofp_header),
+            recv_features_request,
+        },
+        [OFPT_GET_CONFIG_REQUEST] = {
+            sizeof (struct ofp_header),
+            recv_get_config_request,
+        },
+        [OFPT_SET_CONFIG] = {
+            sizeof (struct ofp_switch_config),
+            recv_set_config,
         },
         [OFPT_PACKET_OUT] = {
             sizeof (struct ofp_packet_out),
@@ -877,14 +1182,23 @@ fwd_control_input(struct datapath *dp, const void *msg, size_t length)
             sizeof (struct ofp_port_mod),
             recv_port_mod,
         },
+               [OFPT_FLOW_STAT_REQUEST] = {
+                       sizeof (struct ofp_flow_stat_request),
+                       recv_flow_status_request,
+               },
+               [OFPT_PORT_STAT_REQUEST] = {
+                       sizeof (struct ofp_port_stat_request),
+                       recv_port_status_request,
+               },
+               [OFPT_TABLE_STAT_REQUEST] = {
+                       sizeof (struct ofp_table_stat_request),
+                       recv_table_status_request,
+               },
     };
 
     const struct openflow_packet *pkt;
     struct ofp_header *oh;
 
-    if (length < sizeof(struct ofp_header))
-        return -EINVAL;
-
     oh = (struct ofp_header *) msg;
     if (oh->version != 1 || oh->type >= ARRAY_SIZE(packets)
         || ntohs(oh->length) > length)
@@ -896,7 +1210,7 @@ fwd_control_input(struct datapath *dp, const void *msg, size_t length)
     if (length < pkt->min_size)
         return -EFAULT;
 
-    return pkt->handler(dp, msg);
+    return pkt->handler(dp, sender, msg);
 }
 \f
 /* Packet buffering. */
index 6d52360d65ad1fd2f0c795008eb2e6701073506c..6914782f6edfe0d16feb5f9e8d8a9f774f4bc055 100644 (file)
 #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 *);
 
index e489b9fabfe7eebe70c85d34dbbf9f4dff7038f8..f8f45bc3c1430c624cd96dace97a66c941826ce1 100644 (file)
 #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"
@@ -55,7 +55,7 @@
 static void parse_options(int argc, char *argv[]);
 static void usage(void) NO_RETURN;
 
-static bool reliable = true;
+static const char *listen_vconn_name;
 static struct datapath *dp;
 static uint64_t dpid = UINT64_MAX;
 static char *port_list;
@@ -65,7 +65,6 @@ static void add_ports(struct datapath *dp, char *port_list);
 int
 main(int argc, char *argv[])
 {
-    struct controller_connection *cc;
     int error;
 
     set_program_name(argv[0]);
@@ -77,8 +76,20 @@ main(int argc, char *argv[])
         fatal(0, "missing controller argument; use --help for usage");
     }
 
-    cc = controller_new(argv[optind], reliable);
-    error = dp_new(&dp, dpid, cc);
+    error = dp_new(&dp, dpid, rconn_new(argv[optind], 128));
+    if (listen_vconn_name) {
+        struct vconn *listen_vconn;
+        int retval;
+        
+        retval = vconn_open(listen_vconn_name, &listen_vconn);
+        if (retval && retval != EAGAIN) {
+            fatal(retval, "opening %s", listen_vconn_name);
+        }
+        if (!vconn_is_passive(listen_vconn)) {
+            fatal(0, "%s is not a passive vconn", listen_vconn_name);
+        }
+        dp_add_listen_vconn(dp, listen_vconn);
+    }
     if (error) {
         fatal(error, "could not create datapath");
     }
@@ -123,16 +134,12 @@ parse_options(int argc, char *argv[])
 {
     static struct option long_options[] = {
         {"interfaces",  required_argument, 0, 'i'},
-        {"unreliable",  no_argument, 0, 'u'},
         {"datapath-id", required_argument, 0, 'd'},
+        {"listen",      required_argument, 0, 'l'},
         {"verbose",     optional_argument, 0, 'v'},
         {"help",        no_argument, 0, 'h'},
         {"version",     no_argument, 0, 'V'},
-#ifdef HAVE_OPENSSL
-        {"private-key", required_argument, 0, 'p'},
-        {"certificate", required_argument, 0, 'c'},
-        {"ca-cert",     required_argument, 0, 'C'},
-#endif
+        VCONN_SSL_LONG_OPTIONS
         {0, 0, 0, 0},
     };
     char *short_options = long_options_to_short_options(long_options);
@@ -147,10 +154,6 @@ parse_options(int argc, char *argv[])
         }
 
         switch (c) {
-        case 'u':
-            reliable = false;
-            break;
-
         case 'd':
             if (strlen(optarg) != 12
                 || strspn(optarg, "0123456789abcdefABCDEF") != 12) {
@@ -182,19 +185,14 @@ parse_options(int argc, char *argv[])
             }
             break;
 
-#ifdef HAVE_OPENSSL
-        case 'p':
-            vconn_ssl_set_private_key_file(optarg);
-            break;
-
-        case 'c':
-            vconn_ssl_set_certificate_file(optarg);
+        case 'l':
+            if (listen_vconn_name) {
+                fatal(0, "-l or --listen may be only specified once");
+            }
+            listen_vconn_name = optarg;
             break;
 
-        case 'C':
-            vconn_ssl_set_ca_cert_file(optarg);
-            break;
-#endif
+        VCONN_SSL_OPTION_HANDLERS
 
         case '?':
             exit(EXIT_FAILURE);
@@ -211,23 +209,17 @@ usage(void)
 {
     printf("%s: userspace OpenFlow switch\n"
            "usage: %s [OPTIONS] CONTROLLER\n"
-           "CONTROLLER must be one of the following:\n"
-           "  tcp:HOST[:PORT]         PORT (default: %d) on remote TCP HOST\n",
-           program_name, program_name, OFP_TCP_PORT);
-#ifdef HAVE_OPENSSL
-    printf("  ssl:HOST[:PORT]         SSL PORT (default: %d) on remote HOST\n"
-           "\nPKI configuration (required to use SSL):\n"
-           "  -p, --private-key=FILE  file with private key\n"
-           "  -c, --certificate=FILE  file with certificate for private key\n"
-           "  -C, --ca-cert=FILE      file with peer CA certificate\n",
-           OFP_SSL_PORT);
-#endif
-    printf("Options:\n"
+           "where CONTROLLER is an active OpenFlow connection method.\n",
+           program_name, program_name);
+    vconn_usage(true, true);
+    printf("\nConfiguration options:\n"
            "  -i, --interfaces=NETDEV[,NETDEV]...\n"
            "                          add specified initial switch ports\n"
            "  -d, --datapath-id=ID    Use ID as the OpenFlow switch ID\n"
            "                          (ID must consist of 12 hex digits)\n"
-           "  -u, --unreliable        do not reconnect to controller\n"
+           "  -l, --listen=METHOD     allow management connections on METHOD\n"
+           "                          (a passive OpenFlow connection method)\n"
+           "\nOther options:\n"
            "  -v, --verbose           set maximum verbosity level\n"
            "  -h, --help              display this help message\n"
            "  -V, --version           display version information\n");
index 8367bac973793b0ea628294d459754da744b41e4..b07dd49f19513c911da588357a3cbf299805d60c 100644 (file)
@@ -1,9 +1,6 @@
 include ../Make.vars
 
-bin_PROGRAMS = vlogconf
-if HAVE_NETLINK
-bin_PROGRAMS += dpctl
-endif
+bin_PROGRAMS = vlogconf dpctl
 
 dpctl_SOURCES = dpctl.c
 dpctl_LDADD = ../lib/libopenflow.la
index 41dde83533ee0343f72192a219810aa5b1bde056..80808acfd57f6b8cada23544a27a2d0e09e37619 100644 (file)
@@ -35,6 +35,7 @@
 #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";
 
@@ -111,6 +113,7 @@ parse_options(int argc, char *argv[])
         {"verbose", optional_argument, 0, 'v'},
         {"help", no_argument, 0, 'h'},
         {"version", no_argument, 0, 'V'},
+        VCONN_SSL_LONG_OPTIONS
         {0, 0, 0, 0},
     };
     char *short_options = long_options_to_short_options(long_options);
@@ -136,6 +139,8 @@ parse_options(int argc, char *argv[])
             vlog_set_verbosity(optarg);
             break;
 
+        VCONN_SSL_OPTION_HANDLERS
+
         case '?':
             exit(EXIT_FAILURE);
 
@@ -149,34 +154,58 @@ parse_options(int argc, char *argv[])
 static void
 usage(void)
 {
-    printf("%s: Datapath Utility\n"
+    printf("%s: OpenFlow switch management utility\n"
            "usage: %s [OPTIONS] COMMAND [ARG...]\n"
-           "\nAvailable commands:\n"
-           "  adddp DP_ID                 add a new datapath with ID DP_ID\n"
-           "  deldp DP_ID                 delete datapath DP_ID\n"
-           "  show DP                     show information about DP\n"
-           "  addif DP_ID IFACE           add IFACE as a port on DP_ID\n"
-           "  delif DP_ID IFACE           delete IFACE as a port on DP_ID\n"
-           "  monitor DP_ID               print packets received on DP_ID\n"
-           "  dump-tables DP_ID           print stats for all tables in DP_ID\n"
-           "  dump-flows DP_ID T_ID       print all flow entries in table T_ID of DP_ID\n"
-           "  dump-flows DP_ID T_ID FLOW  print matching FLOWs in table T_ID of DP_ID\n"
-           "  add-flows DP FILE           add flows from FILE to DP\n"
-           "  benchmark-nl DP_ID N SIZE   send N packets of SIZE bytes up netlink\n"
-           "\nOptions:\n"
+#ifdef HAVE_NETLINK
+           "\nCommands that apply to local datapaths only:\n"
+           "  adddp nl:DP_ID              add a new local datapath DP_ID\n"
+           "  deldp nl:DP_ID              delete local datapath DP_ID\n"
+           "  addif nl:DP_ID IFACE        add IFACE as a port on DP_ID\n"
+           "  delif nl:DP_ID IFACE        delete IFACE as a port on DP_ID\n"
+           "  monitor nl:DP_ID            print packets received\n"
+           "  benchmark-nl nl:DP_ID N SIZE   send N packets of SIZE bytes\n"
+#endif
+           "\nCommands that apply to local datapaths and remote switches:\n"
+           "  show SWITCH                 show information\n"
+           "  dump-tables SWITCH          print table stats\n"
+           "  dump-ports SWITCH           print port statistics\n"
+           "  dump-flows SWITCH           print all flow entries\n"
+           "  dump-flows SWITCH FLOW      print matching FLOWs\n"
+           "  add-flows SWITCH FILE       add flows from FILE\n"
+           "where each SWITCH is an active OpenFlow connection method.\n",
+           program_name, program_name);
+    vconn_usage(true, false);
+    printf("\nOptions:\n"
            "  -v, --verbose               set maximum verbosity level\n"
            "  -h, --help                  display this help message\n"
-           "  -V, --version               display version information\n",
-           program_name, program_name);
+           "  -V, --version               display version information\n");
     exit(EXIT_SUCCESS);
 }
 
-static void run(int retval, const char *name) 
+static void run(int retval, const char *message, ...)
+    PRINTF_FORMAT(2, 3);
+
+static void run(int retval, const char *message, ...)
 {
     if (retval) {
-        fatal(retval, "%s", name);
+        va_list args;
+
+        fprintf(stderr, "%s: ", program_name);
+        va_start(args, message);
+        vfprintf(stderr, message, args);
+        va_end(args);
+        if (retval == EOF) {
+            fputs(": unexpected end of file\n", stderr);
+        } else {
+            vfprintf(stderr, ": %s\n", strerror(retval));
+        }
+
+        exit(EXIT_FAILURE);
     }
 }
+\f
+#ifdef HAVE_NETLINK
+/* Netlink-only commands. */
 
 static int  if_up(const char* intf)
 {
@@ -186,10 +215,20 @@ static int  if_up(const char* intf)
     return system(command);
 }
 
+static void open_nl_vconn(const char *name, bool subscribe, struct dpif *dpif)
+{
+    if (strncmp(name, "nl:", 3)
+        || strlen(name) < 4
+        || name[strspn(name + 3, "0123456789") + 3]) {
+        fatal(0, "%s: argument is not of the form \"nl:DP_ID\"", name);
+    }
+    run(dpif_open(atoi(name + 3), subscribe, dpif), "opening datapath");
+}
+
 static void do_add_dp(int argc UNUSED, char *argv[])
 {
     struct dpif dp;
-    run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
+    open_nl_vconn(argv[1], false, &dp);
     run(dpif_add_dp(&dp), "add_dp");
     dpif_close(&dp);
 }
@@ -197,24 +236,16 @@ static void do_add_dp(int argc UNUSED, char *argv[])
 static void do_del_dp(int argc UNUSED, char *argv[])
 {
     struct dpif dp;
-    run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
+    open_nl_vconn(argv[1], false, &dp);
     run(dpif_del_dp(&dp), "del_dp");
     dpif_close(&dp);
 }
 
-static void do_show(int argc UNUSED, char *argv[])
-{
-    struct dpif dp;
-    run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
-    run(dpif_show(&dp), "show");
-    dpif_close(&dp);
-}
-
 static void do_add_port(int argc UNUSED, char *argv[])
 {
     struct dpif dp;
     if_up(argv[2]);
-    run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
+    open_nl_vconn(argv[1], false, &dp);
     run(dpif_add_port(&dp, argv[2]), "add_port");
     dpif_close(&dp);
 }
@@ -222,11 +253,23 @@ static void do_add_port(int argc UNUSED, char *argv[])
 static void do_del_port(int argc UNUSED, char *argv[])
 {
     struct dpif dp;
-    run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
+    open_nl_vconn(argv[1], false, &dp);
     run(dpif_del_port(&dp, argv[2]), "del_port");
     dpif_close(&dp);
 }
 
+static void do_monitor(int argc UNUSED, char *argv[])
+{
+    struct dpif dp;
+    open_nl_vconn(argv[1], false, &dp);
+    for (;;) {
+        struct buffer *b;
+        run(dpif_recv_openflow(&dp, &b, true), "dpif_recv_openflow");
+        ofp_print(stderr, b->data, b->size, 2);
+        buffer_delete(b);
+    }
+}
+
 #define BENCHMARK_INCR   100
 
 static void do_benchmark_nl(int argc UNUSED, char *argv[])
@@ -235,7 +278,7 @@ static void do_benchmark_nl(int argc UNUSED, char *argv[])
     uint32_t num_packets, i, milestone;
     struct timeval start, end;
 
-    run(dpif_open(atoi(argv[1]), true, &dp), "dpif_open");
+    open_nl_vconn(argv[1], false, &dp);
     num_packets = atoi(argv[2]);
     milestone = BENCHMARK_INCR;
     run(dpif_benchmark_nl(&dp, num_packets, atoi(argv[3])), "benchmark_nl");
@@ -264,25 +307,99 @@ static void do_benchmark_nl(int argc UNUSED, char *argv[])
 
     dpif_close(&dp);
 }
+#endif /* HAVE_NETLINK */
+\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);
 }
 
 
@@ -337,7 +454,8 @@ str_to_action(const char *str, struct ofp_action *action)
 }
 
 static void
-str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action)
+str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action,
+            uint8_t *table_idx)
 {
     struct field {
         const char *name;
@@ -364,6 +482,9 @@ str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action)
     uint32_t wildcards;
     bool got_action = false;
 
+    if (table_idx) {
+        *table_idx = 0xff;
+    }
     memset(match, 0, sizeof *match);
     wildcards = OFPFW_ALL;
     for (name = strtok(string, "="), value = strtok(NULL, " \t\n");
@@ -379,6 +500,11 @@ str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action)
             continue;
         }
 
+        if (table_idx && !strcmp(name, "table")) {
+            *table_idx = atoi(value);
+            continue;
+        }
+
         for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
             if (!strcmp(f->name, name)) {
                 goto found;
@@ -425,17 +551,18 @@ str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action)
 
 static void do_dump_flows(int argc, char *argv[])
 {
-    struct dpif dp;
-    struct ofp_match match, *matchp;
-    run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
-    if (argc == 4) {
-        str_to_flow(argv[3], &match, NULL);
-        matchp = &match;
-    } else {
-        matchp = NULL;
-    }
-    run(dpif_dump_flows(&dp, atoi(argv[2]), matchp), "dump_flows");
-    dpif_close(&dp);
+    struct vconn *vconn;
+    struct buffer *request, *reply;
+    struct ofp_flow_stat_request *fsr;
+
+    run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
+    fsr = alloc_openflow_buffer(sizeof *fsr, OFPT_FLOW_STAT_REQUEST, &request);
+    str_to_flow(argc > 2 ? argv[2] : "", &fsr->match, NULL, &fsr->table_id);
+    fsr->type = OFPFS_INDIV;
+    fsr->pad = 0;
+    reply = transact_openflow(vconn, request);
+    ofp_print(stdout, reply->data, reply->size, 1);
+    vconn_close(vconn);
 }
 
 static void do_add_flows(int argc, char *argv[])
@@ -446,19 +573,12 @@ static void do_add_flows(int argc, char *argv[])
     FILE *file;
     char line[1024];
 
-    int retval;
-
     file = fopen(argv[2], "r");
     if (file == NULL) {
         fatal(errno, "%s: open", argv[2]);
     }
 
-    sprintf(vconn_name, "nl:%d", atoi(argv[1]));
-    retval = vconn_open(vconn_name, &vconn);
-    if (retval) {
-        fatal(retval, "opening datapath");
-    }
-
+    run(vconn_open_block(vconn_name, &vconn), "connecting to %s", argv[1]);
     while (fgets(line, sizeof line, file)) {
         struct buffer *buffer;
         struct ofp_flow_mod *ofm;
@@ -477,55 +597,46 @@ static void do_add_flows(int argc, char *argv[])
             continue;
         }
 
+        /* Parse and send. */
         size = sizeof *ofm + sizeof ofm->actions[0];
-        buffer = buffer_new(size);
-        ofm = buffer_put_uninit(buffer, size);
-
-        /* Parse. */
-        memset(ofm, 0, size);
-        ofm->header.type = OFPT_FLOW_MOD;
-        ofm->header.version = OFP_VERSION;
-        ofm->header.length = htons(size);
+        ofm = alloc_openflow_buffer(size, OFPT_FLOW_MOD, &buffer);
         ofm->command = htons(OFPFC_ADD);
         ofm->max_idle = htons(50);
         ofm->buffer_id = htonl(UINT32_MAX);
         ofm->group_id = htonl(0);
-        str_to_flow(line, &ofm->match, &ofm->actions[0]);
-
-        retval = vconn_send_block(vconn, buffer);
-        if (retval) {
-            fatal(retval, "sending to datapath");
-        }
+        str_to_flow(line, &ofm->match, &ofm->actions[0], NULL);
+        run(vconn_send_block(vconn, buffer), "send OpenFlow packet");
     }
     vconn_close(vconn);
     fclose(file);
 }
 
+static void
+do_dump_ports(int argc, char *argv[])
+{
+    dump_transaction(argv[1], OFPT_PORT_STAT_REQUEST);
+}
+
 static void do_help(int argc UNUSED, char *argv[] UNUSED)
 {
     usage();
 }
 
 static struct command all_commands[] = {
-    { "add-dp", 1, 1, do_add_dp },
+#ifdef HAVE_NETLINK
     { "adddp", 1, 1, do_add_dp },
-
-    { "del-dp", 1, 1, do_del_dp },
     { "deldp", 1, 1, do_del_dp },
-
-    { "show", 1, 1, do_show },
-
-    { "add-port", 2, 2, do_add_port },
     { "addif", 2, 2, do_add_port },
-
-    { "del-port", 2, 2, do_del_port },
     { "delif", 2, 2, do_del_port },
+    { "benchmark-nl", 3, 3, do_benchmark_nl },
+#endif
+
+    { "show", 1, 1, do_show },
 
     { "help", 0, INT_MAX, do_help },
     { "monitor", 1, 1, do_monitor },
     { "dump-tables", 1, 1, do_dump_tables },
-    { "dump-flows", 2, 3, do_dump_flows },
+    { "dump-flows", 1, 2, do_dump_flows },
     { "add-flows", 2, 2, do_add_flows },
-
-    { "benchmark-nl", 3, 3, do_benchmark_nl },
+    { "dump-ports", 1, 1, do_dump_ports },
 };