[ovs-dev] [PATCH v3.1] lib: Add ipv6 support for active and passive socket connections

Arun Sharma arun.sharma at calsoftinc.com
Mon Dec 30 12:15:56 UTC 2013


Allows active/passive socket operations to use IPv6 network addresses,
except in-band control which is not supported for IPv6.

tests: Added tests for socket connections over IPv6.
Also added tests for NetFlow and sFlow using IPv6 sockets.
manpages: Updated connection information for IPv6 socket communication.

Signed-off-by: Nandan Nivgune <nandan.nivgune at calsoftinc.com>
Signed-off-by: Abhijit Bhopatkar <abhijit.bhopatkar at calsoftinc.com>
Signed-off-by: Arun Sharma <arun.sharma at calsoftinc.com>

---
v3-v3.1
- Rebased with current master.

v2-v3:
- Rebased with current master.

v1-v2:
- Fixed clang reported errors.
- Fixed sparse reported warnings.
- IPv6 addresses to be wrapped into square brackets, e.g.
  tcp:[1::2]:1234. Also updated man pages/tests code.
- Using inet_ntop() for IPv6 address for string representation.
- Modified parse_active() to work properly if input has no ":".
- Removed unwanted vconn, stream and rconn functions for get
  or set operations.
- Updated NEWS
- Updated ./vswitchd/vswitch.xml
- rebased with master.
---
---
 NEWS                         |    1 +
 include/sparse/netinet/in.h  |   11 +
 lib/packets.h                |   12 +
 lib/rconn.c                  |   15 +
 lib/socket-util.c            |  263 +++++++++----
 lib/socket-util.h            |    9 +-
 lib/stream-ssl.c             |   54 ++-
 lib/stream-tcp.c             |   37 +-
 lib/stream.c                 |    8 +-
 lib/stream.h                 |    2 +-
 lib/vconn-active.man         |    8 +-
 lib/vconn-passive.man        |   12 +-
 ofproto/connmgr.c            |    9 +-
 ofproto/ofproto-dpif-sflow.c |   26 +-
 ovsdb/remote-active.man      |   10 +-
 ovsdb/remote-passive.man     |   17 +-
 python/ovs/socket_util.py    |   36 +-
 tests/ofproto-dpif.at        |  856 ++++++++++++++++++++++--------------------
 tests/ofproto-macros.at      |   17 +
 tests/ovs-vsctl.at           |   28 +-
 tests/ovsdb-idl.at           |   24 +-
 tests/ovsdb-server.at        |  225 +++++++----
 tests/test-sflow.c           |    8 +-
 vswitchd/bridge.c            |    8 +-
 vswitchd/vswitch.xml         |   32 +-
 25 files changed, 1107 insertions(+), 621 deletions(-)

diff --git a/NEWS b/NEWS
index 515a236..f02257a 100644
--- a/NEWS
+++ b/NEWS
@@ -63,6 +63,7 @@ v2.1.0 - xx xxx xxxx
      hard limit on the number of flows in the datapath.  It defaults to 200,000
      flows.  OVS automatically adjusts this number depending on network
      conditions.
+   - Added IPv6 support for active and passive socket communications.
 
 
 v2.0.0 - 15 Oct 2013
diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h
index 781358b..f7f8105 100644
--- a/include/sparse/netinet/in.h
+++ b/include/sparse/netinet/in.h
@@ -41,13 +41,24 @@ struct sockaddr_in {
 struct in6_addr {
     union {
         uint8_t u_s6_addr[16];
+        uint16_t u_s6_addr16[8];
     } u;
 };
 
 #define s6_addr u.u_s6_addr
+#define s6_addr16 u.u_s6_addr16
 
 extern const struct in6_addr in6addr_any;
 
+/* Ditto, for IPv6.  */
+struct sockaddr_in6 {
+    __SOCKADDR_COMMON (sin6_);
+    in_port_t sin6_port;        /* Transport layer port # */
+    uint32_t sin6_flowinfo;     /* IPv6 flow information */
+    struct in6_addr sin6_addr;  /* IPv6 address */
+    uint32_t sin6_scope_id;     /* IPv6 scope-id */
+};
+
 #define IPPROTO_IP 0
 #define IPPROTO_HOPOPTS 0
 #define IPPROTO_ICMP 1
diff --git a/lib/packets.h b/lib/packets.h
index d291c14..4823806 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -367,6 +367,18 @@ mpls_lse_to_bos(ovs_be32 mpls_lse)
     return (mpls_lse & htonl(MPLS_BOS_MASK)) != 0;
 }
 
+#define IP6_FMT "%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16   \
+    ":%"PRIx16":%"PRIx16":%"PRIx16
+#define IP6_ARGS(ip6)                                  \
+    ntohs(ip6[0]),                                     \
+    ntohs(ip6[1]),                                     \
+    ntohs(ip6[2]),                                     \
+    ntohs(ip6[3]),                                     \
+    ntohs(ip6[4]),                                     \
+    ntohs(ip6[5]),                                     \
+    ntohs(ip6[6]),                                     \
+    ntohs(ip6[7])
+
 #define IP_FMT "%"PRIu32".%"PRIu32".%"PRIu32".%"PRIu32
 #define IP_ARGS(ip)                             \
     ntohl(ip) >> 24,                            \
diff --git a/lib/rconn.c b/lib/rconn.c
index d339365..20f6295 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -21,12 +21,14 @@
 #include <stdlib.h>
 #include <string.h>
 #include "coverage.h"
+#include "jsonrpc.h"
 #include "ofp-msgs.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "poll-loop.h"
 #include "sat-math.h"
+#include "socket-util.h"
 #include "timeval.h"
 #include "util.h"
 #include "vconn.h"
@@ -437,11 +439,20 @@ reconnect(struct rconn *rc)
     OVS_REQUIRES(rc->mutex)
 {
     int retval;
+    struct sockaddr_storage ss;
 
     if (rconn_logging_connection_attempts__(rc)) {
         VLOG_INFO("%s: connecting...", rc->name);
     }
     rc->n_attempted_connections++;
+    if (!strncmp(rc->target, "tcp:", 4) || !strncmp(rc->target, "ssl:", 4)) {
+        if (!inet_parse_active(rc->target + 4, OVSDB_PORT, &ss)) {
+            VLOG_WARN("%s:failed to get address family", rc->name);
+            retval = -1;
+            goto exit;
+         }
+    }
+
     retval = vconn_open(rc->target, rc->allowed_versions, rc->dscp,
                         &rc->vconn);
     if (!retval) {
@@ -450,6 +461,10 @@ reconnect(struct rconn *rc)
     } else {
         VLOG_WARN("%s: connection failed (%s)",
                   rc->name, ovs_strerror(retval));
+    }
+
+exit:
+    if (retval) {
         rc->backoff_deadline = TIME_MAX; /* Prevent resetting backoff. */
         disconnect(rc, retval);
     }
diff --git a/lib/socket-util.c b/lib/socket-util.c
index bb48ade..e6e77ba 100644
--- a/lib/socket-util.c
+++ b/lib/socket-util.c
@@ -67,6 +67,11 @@ VLOG_DEFINE_THIS_MODULE(socket_util);
 static int getsockopt_int(int fd, int level, int option, const char *optname,
                           int *valuep);
 
+static int do_getaddrinfo(const char *host, const char *port,
+                          struct sockaddr_storage *ssp);
+
+static void parse_active(char *target, const char **host, const char **port);
+
 /* Sets 'fd' to non-blocking mode.  Returns 0 if successful, otherwise a
  * positive errno value. */
 int
@@ -616,38 +621,34 @@ guess_netmask(ovs_be32 ip_)
  * <host> is required.  If 'default_port' is nonzero then <port> is optional
  * and defaults to 'default_port'.
  *
- * On success, returns true and stores the parsed remote address into '*sinp'.
- * On failure, logs an error, stores zeros into '*sinp', and returns false. */
+ * On success, returns true and stores the parsed remote address into '*ssp'.
+ * On failure, logs an error, stores zeros into '*ssp', and returns false. */
 bool
 inet_parse_active(const char *target_, uint16_t default_port,
-                  struct sockaddr_in *sinp)
+                  struct sockaddr_storage *ssp)
 {
     char *target = xstrdup(target_);
-    char *save_ptr = NULL;
+    char *save_ptr = target;
     const char *host_name;
     const char *port_string;
+    char default_port_str[6] = {'\0'};
     bool ok = false;
 
-    /* Defaults. */
-    sinp->sin_family = AF_INET;
-    sinp->sin_port = htons(default_port);
-
-    /* Tokenize. */
-    host_name = strtok_r(target, ":", &save_ptr);
-    port_string = strtok_r(NULL, ":", &save_ptr);
+    /* Parse IP address and port. */
+    parse_active(target, &host_name, &port_string);
     if (!host_name) {
         VLOG_ERR("%s: bad peer name format", target_);
         goto exit;
     }
-
-    /* Look up IP, port. */
-    if (lookup_ip(host_name, &sinp->sin_addr)) {
-        goto exit;
+    if (port_string == NULL) {
+        if (!default_port) {
+            VLOG_ERR("%s: port number must be specified", target_);
+            goto exit;
+        }
+        sprintf(default_port_str, "%d", default_port);
+        port_string = default_port_str;
     }
-    if (port_string && atoi(port_string)) {
-        sinp->sin_port = htons(atoi(port_string));
-    } else if (!default_port) {
-        VLOG_ERR("%s: port number must be specified", target_);
+    if (do_getaddrinfo(host_name, port_string, ssp) == -1) {
         goto exit;
     }
 
@@ -655,16 +656,16 @@ inet_parse_active(const char *target_, uint16_t default_port,
 
 exit:
     if (!ok) {
-        memset(sinp, 0, sizeof *sinp);
+        memset(ssp, 0, sizeof *ssp);
     }
-    free(target);
+    free(save_ptr);
     return ok;
 }
 
-/* Opens a non-blocking IPv4 socket of the specified 'style' and connects to
- * 'target', which should be a string in the format "<host>[:<port>]".  <host>
- * is required.  If 'default_port' is nonzero then <port> is optional and
- * defaults to 'default_port'.
+/* Opens a non-blocking IPv4 or IPv6 socket of the specified 'style' and
+ * connects to 'target', which should be a string in the format
+ * "<host>[:<port>]".  <host> is required.  If 'default_port' is nonzero then
+ * <port> is optional and defaults to 'default_port'.
  *
  * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP).
  *
@@ -673,28 +674,29 @@ exit:
  * into '*fdp'.  On failure, returns a positive errno value other than EAGAIN
  * and stores -1 into '*fdp'.
  *
- * If 'sinp' is non-null, then on success the target address is stored into
- * '*sinp'.
+ * If 'ssp' is non-null, then on success the target address is stored into
+ * '*ssp'.
  *
  * 'dscp' becomes the DSCP bits in the IP headers for the new connection.  It
  * should be in the range [0, 63] and will automatically be shifted to the
  * appropriately place in the IP tos field. */
 int
 inet_open_active(int style, const char *target, uint16_t default_port,
-                 struct sockaddr_in *sinp, int *fdp, uint8_t dscp)
+                 struct sockaddr_storage *ssp, int *fdp, uint8_t dscp)
 {
-    struct sockaddr_in sin;
+    struct sockaddr_storage sstorage;
     int fd = -1;
     int error;
+    socklen_t addrlen;
 
     /* Parse. */
-    if (!inet_parse_active(target, default_port, &sin)) {
+    if (!inet_parse_active(target, default_port, &sstorage)) {
         error = EAFNOSUPPORT;
         goto exit;
     }
 
     /* Create non-blocking socket. */
-    fd = socket(AF_INET, style, 0);
+    fd = socket(sstorage.ss_family, style, 0);
     if (fd < 0) {
         VLOG_ERR("%s: socket: %s", target, ovs_strerror(errno));
         error = errno;
@@ -705,8 +707,8 @@ inet_open_active(int style, const char *target, uint16_t default_port,
         goto exit;
     }
 
-    /* The dscp bits must be configured before connect() to ensure that the TOS
-     * field is set during the connection establishment.  If set after
+    /* The dscp bits must be configured before connect() to ensure that the
+     * TOS field is set during the connection establishment.  If set after
      * connect(), the handshake SYN frames will be sent with a TOS of 0. */
     error = set_dscp(fd, dscp);
     if (error) {
@@ -714,16 +716,24 @@ inet_open_active(int style, const char *target, uint16_t default_port,
         goto exit;
     }
 
+    if (sstorage.ss_family == AF_INET) {
+        addrlen = sizeof (struct sockaddr_in);
+    } else {
+        addrlen = sizeof (struct sockaddr_in6);
+    }
+
     /* Connect. */
-    error = connect(fd, (struct sockaddr *) &sin, sizeof sin) == 0 ? 0 : errno;
+    error = connect(fd, (struct sockaddr *) &sstorage, addrlen) == 0
+                    ? 0
+                    : errno;
     if (error == EINPROGRESS) {
         error = EAGAIN;
     }
 
 exit:
     if (!error || error == EAGAIN) {
-        if (sinp) {
-            *sinp = sin;
+        if (ssp) {
+            *ssp = sstorage;
         }
     } else if (fd >= 0) {
         close(fd);
@@ -743,37 +753,33 @@ exit:
  *
  *      - If <ip> is omitted then the IP address is wildcarded.
  *
- * If successful, stores the address into '*sinp' and returns true; otherwise
- * zeros '*sinp' and returns false. */
+ * If successful, stores the address into '*ssp' and returns true; otherwise
+ * zeros '*ssp' and returns false. */
 bool
 inet_parse_passive(const char *target_, int default_port,
-                   struct sockaddr_in *sinp)
+                   struct sockaddr_storage *ssp)
 {
     char *target = xstrdup(target_);
     char *string_ptr = target;
     const char *host_name;
     const char *port_string;
+    char default_port_str[6] = {'\0'};
     bool ok = false;
-    int port;
 
-    /* Address defaults. */
-    memset(sinp, 0, sizeof *sinp);
-    sinp->sin_family = AF_INET;
-    sinp->sin_addr.s_addr = htonl(INADDR_ANY);
-    sinp->sin_port = htons(default_port);
+    memset(ssp, 0, sizeof *ssp);
 
-    /* Parse optional port number. */
     port_string = strsep(&string_ptr, ":");
-    if (port_string && str_to_int(port_string, 10, &port)) {
-        sinp->sin_port = htons(port);
-    } else if (default_port < 0) {
-        VLOG_ERR("%s: port number must be specified", target_);
-        goto exit;
+    host_name = string_ptr;
+    if (port_string == NULL) {
+        if (default_port == -1) {
+            VLOG_ERR("%s: port number must be specified", target_);
+            goto exit;
+        }
+        sprintf(default_port_str, "%d", default_port);
+        port_string = default_port_str;
     }
 
-    /* Parse optional bind IP. */
-    host_name = strsep(&string_ptr, ":");
-    if (host_name && host_name[0] && lookup_ip(host_name, &sinp->sin_addr)) {
+    if (do_getaddrinfo(host_name, port_string, ssp) == -1) {
         goto exit;
     }
 
@@ -781,14 +787,14 @@ inet_parse_passive(const char *target_, int default_port,
 
 exit:
     if (!ok) {
-        memset(sinp, 0, sizeof *sinp);
+        memset(ssp, 0, sizeof *ssp);
     }
     free(target);
     return ok;
 }
 
 
-/* Opens a non-blocking IPv4 socket of the specified 'style', binds to
+/* Opens a non-blocking IPv4 or IPv6 socket of the specified 'style', binds to
  * 'target', and listens for incoming connections.  Parses 'target' in the same
  * way was inet_parse_passive().
  *
@@ -799,27 +805,31 @@ exit:
  * On success, returns a non-negative file descriptor.  On failure, returns a
  * negative errno value.
  *
- * If 'sinp' is non-null, then on success the bound address is stored into
- * '*sinp'.
+ * If 'ssp' is non-null, then on success the bound address is stored into
+ * '*ssp'.
  *
  * 'dscp' becomes the DSCP bits in the IP headers for the new connection.  It
  * should be in the range [0, 63] and will automatically be shifted to the
  * appropriately place in the IP tos field. */
 int
 inet_open_passive(int style, const char *target, int default_port,
-                  struct sockaddr_in *sinp, uint8_t dscp)
+                  struct sockaddr_storage *ssp, uint8_t dscp)
 {
     bool kernel_chooses_port;
-    struct sockaddr_in sin;
+    struct sockaddr_storage ss;
+    struct sockaddr_in *sin_p;
+    struct sockaddr_in6 *sin6_p;
+    in_port_t port;
     int fd = 0, error;
     unsigned int yes = 1;
+    socklen_t addrlen;
 
-    if (!inet_parse_passive(target, default_port, &sin)) {
+    if (!inet_parse_passive(target, default_port, &ss)) {
         return -EAFNOSUPPORT;
     }
 
     /* Create non-blocking socket, set SO_REUSEADDR. */
-    fd = socket(AF_INET, style, 0);
+    fd = socket(ss.ss_family, style, 0);
     if (fd < 0) {
         error = errno;
         VLOG_ERR("%s: socket: %s", target, ovs_strerror(error));
@@ -837,8 +847,19 @@ inet_open_passive(int style, const char *target, int default_port,
         goto error;
     }
 
+    if (ss.ss_family == AF_INET) {
+        sin_p = (struct sockaddr_in *) &ss;
+        addrlen = sizeof (struct sockaddr_in);
+        port = sin_p->sin_port;
+    } else {
+        sin6_p = (struct sockaddr_in6 *) &ss;
+        addrlen = sizeof (struct sockaddr_in6);
+        port = sin6_p->sin6_port;
+    }
+
+    kernel_chooses_port = port == htons(0);
     /* Bind. */
-    if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
+    if (bind(fd, (struct sockaddr *) &ss, addrlen) < 0) {
         error = errno;
         VLOG_ERR("%s: bind: %s", target, ovs_strerror(error));
         goto error;
@@ -860,25 +881,20 @@ inet_open_passive(int style, const char *target, int default_port,
         goto error;
     }
 
-    kernel_chooses_port = sin.sin_port == htons(0);
-    if (sinp || kernel_chooses_port) {
-        socklen_t sin_len = sizeof sin;
-        if (getsockname(fd, (struct sockaddr *) &sin, &sin_len) < 0) {
+    if (ssp || kernel_chooses_port) {
+        socklen_t sin_len = addrlen;
+        if (getsockname(fd, (struct sockaddr *) &ss, &sin_len) < 0) {
             error = errno;
             VLOG_ERR("%s: getsockname: %s", target, ovs_strerror(error));
             goto error;
         }
-        if (sin.sin_family != AF_INET || sin_len != sizeof sin) {
-            error = EAFNOSUPPORT;
-            VLOG_ERR("%s: getsockname: invalid socket name", target);
-            goto error;
-        }
-        if (sinp) {
-            *sinp = sin;
+        if (ssp) {
+            *ssp = ss;
         }
+        sin_p = (struct sockaddr_in *) &ss;
         if (kernel_chooses_port) {
             VLOG_INFO("%s: listening on port %"PRIu16,
-                      target, ntohs(sin.sin_port));
+                      target, ntohs(sin_p->sin_port));
         }
     }
 
@@ -1060,6 +1076,93 @@ getsockopt_int(int fd, int level, int option, const char *optname, int *valuep)
     return error;
 }
 
+/* Wrapper for getaddrinfo().  On success, 0 is returned else -1 is returned
+ * for in error.  If 'res' is not NULL, output address list is passed to
+ * caller. */
+static int
+do_getaddrinfo(const char *host_, const char *port,
+               struct sockaddr_storage *ssp)
+{
+    char *host = NULL;
+    char *save_host_ptr = NULL;
+    struct addrinfo hint;
+    struct addrinfo *info;
+    struct addrinfo *addr;
+    int ret;
+    int len;
+
+    memset(&hint, 0, sizeof hint);
+    hint.ai_family = AF_UNSPEC;
+    hint.ai_socktype = SOCK_STREAM;
+    hint.ai_flags = AI_PASSIVE;
+    hint.ai_canonname = NULL;
+    hint.ai_addr = NULL;
+    hint.ai_next = NULL;
+
+    if (host_) {
+        host = xstrdup(host_);
+        save_host_ptr = host;
+        len = strlen(host);
+        if (len > 2 && (host[0] == '[') && host[len - 1] == ']') {
+            host++;
+            host[len - 2] = '\0';
+        }
+    }
+
+    ret = getaddrinfo(host, port, &hint, &info);
+    if (ret) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_ERR_RL(&rl, "getaddrinfo: %s\n", gai_strerror(ret));
+        ret = -1;
+        goto exit;
+    }
+
+    for (addr = info; addr != NULL; addr = addr->ai_next) {
+        memcpy(ssp, addr->ai_addr, addr->ai_addrlen);
+        break;
+    }
+    if (addr == NULL) {
+        ret = -1;
+    }
+    if (info) {
+        freeaddrinfo(info);
+    }
+
+exit:
+    if (save_host_ptr) {
+        free(save_host_ptr);
+    }
+    return ret;
+}
+
+/* Parse IP address and port from string 'itarget'.
+ * If 'host' and 'port' are not NULL, parsed string values are stored in
+ * them. */
+static void
+parse_active(char *itarget, const char **host, const char **port)
+{
+    char *ptr = NULL;
+    char *start_ptr = itarget;
+
+    if (strstr(itarget, ":") == NULL) {
+        *host = NULL;
+        *port = NULL;
+        return;
+    }
+
+    while (*itarget != '\0') {
+        if (*itarget == ':') {
+            ptr = itarget;
+        }
+        itarget++;
+    }
+    if (host && port) {
+        *port = ++ptr;
+        *(--ptr) = '\0';
+        *host = start_ptr;
+    }
+}
+
 static void
 describe_sockaddr(struct ds *string, int fd,
                   int (*getaddr)(int, struct sockaddr *, socklen_t *))
@@ -1074,6 +1177,16 @@ describe_sockaddr(struct ds *string, int fd,
             memcpy(&sin, &ss, sizeof sin);
             ds_put_format(string, IP_FMT":%"PRIu16,
                           IP_ARGS(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+        } else if (ss.ss_family == AF_INET6) {
+            struct sockaddr_in6 sin6;
+            struct ds ipv6_ds = DS_EMPTY_INITIALIZER;
+
+            memcpy(&sin6, &ss, sizeof sin6);
+            print_ipv6_addr(&ipv6_ds, &sin6.sin6_addr);
+            ds_put_format(string, "%s:%"PRIu16,
+                          ds_cstr(&ipv6_ds),
+                          ntohs(sin6.sin6_port));
+            ds_destroy(&ipv6_ds);
         } else if (ss.ss_family == AF_UNIX) {
             struct sockaddr_un sun;
             const char *null;
diff --git a/lib/socket-util.h b/lib/socket-util.h
index 670eeb3..13e66fd 100644
--- a/lib/socket-util.h
+++ b/lib/socket-util.h
@@ -25,6 +25,7 @@
 #include "openvswitch/types.h"
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
+#include <netdb.h>
 
 int set_nonblocking(int fd);
 void xset_nonblocking(int fd);
@@ -48,14 +49,14 @@ ovs_be32 guess_netmask(ovs_be32 ip);
 int get_null_fd(void);
 
 bool inet_parse_active(const char *target, uint16_t default_port,
-                       struct sockaddr_in *sinp);
+                       struct sockaddr_storage *ssp);
 int inet_open_active(int style, const char *target, uint16_t default_port,
-		     struct sockaddr_in *sinp, int *fdp, uint8_t dscp);
+                     struct sockaddr_storage *ssp, int *fdp, uint8_t dscp);
 
 bool inet_parse_passive(const char *target, int default_port,
-                        struct sockaddr_in *sinp);
+                        struct sockaddr_storage *ssp);
 int inet_open_passive(int style, const char *target, int default_port,
-                      struct sockaddr_in *sinp, uint8_t dscp);
+                      struct sockaddr_storage *ssp, uint8_t dscp);
 
 int read_fully(int fd, void *, size_t, size_t *bytes_read);
 int write_fully(int fd, const void *, size_t, size_t *bytes_written);
diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c
index 2ed5282..ee82f9f 100644
--- a/lib/stream-ssl.c
+++ b/lib/stream-ssl.c
@@ -206,7 +206,7 @@ static int
 new_ssl_stream(const char *name, int fd, enum session_type type,
                enum ssl_state state, struct stream **streamp)
 {
-    struct sockaddr_in local;
+    struct sockaddr_storage local;
     socklen_t local_len = sizeof local;
     struct ssl_stream *sslv;
     SSL *ssl = NULL;
@@ -781,7 +781,8 @@ pssl_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp,
           uint8_t dscp)
 {
     struct pssl_pstream *pssl;
-    struct sockaddr_in sin;
+    struct sockaddr_storage ss;
+    in_port_t port;
     char bound_name[128];
     int retval;
     int fd;
@@ -791,16 +792,31 @@ pssl_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp,
         return retval;
     }
 
-    fd = inet_open_passive(SOCK_STREAM, suffix, OFP_OLD_PORT, &sin, dscp);
+    fd = inet_open_passive(SOCK_STREAM, suffix, OFP_OLD_PORT, &ss, dscp);
     if (fd < 0) {
         return -fd;
     }
-    sprintf(bound_name, "pssl:%"PRIu16":"IP_FMT,
-            ntohs(sin.sin_port), IP_ARGS(sin.sin_addr.s_addr));
+
+    if (ss.ss_family == AF_INET) {
+        struct sockaddr_in *sin_addr = (struct sockaddr_in *) &ss;
+
+        port = sin_addr->sin_port;
+        sprintf(bound_name, "pssl:%"PRIu16":"IP_FMT,
+                ntohs(port), IP_ARGS(sin_addr->sin_addr.s_addr));
+    } else {
+        struct sockaddr_in6 *sin_addr6 = (struct sockaddr_in6 *) &ss;
+        struct ds ipv6_ds = DS_EMPTY_INITIALIZER;
+
+        port = sin_addr6->sin6_port;
+        print_ipv6_addr(&ipv6_ds, &sin_addr6->sin6_addr);
+        sprintf(bound_name, "pssl:%"PRIu16":%s",
+                ntohs(port), ds_cstr(&ipv6_ds));
+        ds_destroy(&ipv6_ds);
+    }
 
     pssl = xmalloc(sizeof *pssl);
     pstream_init(&pssl->pstream, &pssl_pstream_class, bound_name);
-    pstream_set_bound_port(&pssl->pstream, sin.sin_port);
+    pstream_set_bound_port(&pssl->pstream, port);
     pssl->fd = fd;
     *pstreamp = &pssl->pstream;
     return 0;
@@ -818,13 +834,13 @@ static int
 pssl_accept(struct pstream *pstream, struct stream **new_streamp)
 {
     struct pssl_pstream *pssl = pssl_pstream_cast(pstream);
-    struct sockaddr_in sin;
-    socklen_t sin_len = sizeof sin;
+    struct sockaddr_storage ss;
+    socklen_t sin_len = sizeof ss;
     char name[128];
     int new_fd;
     int error;
 
-    new_fd = accept(pssl->fd, (struct sockaddr *) &sin, &sin_len);
+    new_fd = accept(pssl->fd, (struct sockaddr *) &ss, &sin_len);
     if (new_fd < 0) {
         error = errno;
         if (error != EAGAIN) {
@@ -839,9 +855,23 @@ pssl_accept(struct pstream *pstream, struct stream **new_streamp)
         return error;
     }
 
-    sprintf(name, "ssl:"IP_FMT, IP_ARGS(sin.sin_addr.s_addr));
-    if (sin.sin_port != htons(OFP_OLD_PORT)) {
-        sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin.sin_port));
+    if (ss.ss_family == AF_INET) {
+        struct sockaddr_in *sin_addr = (struct sockaddr_in *) &ss;
+
+        sprintf(name, "ssl:"IP_FMT, IP_ARGS(sin_addr->sin_addr.s_addr));
+        if (sin_addr->sin_port != htons(OFP_OLD_PORT)) {
+            sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin_addr->sin_port));
+        }
+    } else {
+        struct sockaddr_in6 *sin_addr6 = (struct sockaddr_in6 *) &ss;
+        struct ds ipv6_ds = DS_EMPTY_INITIALIZER;
+        print_ipv6_addr(&ipv6_ds, &sin_addr6->sin6_addr);
+        sprintf(name, "ssl:%s", ds_cstr(&ipv6_ds));
+        if (sin_addr6->sin6_port != htons(OFP_OLD_PORT)) {
+            sprintf(strchr(name, '\0'), ":%"PRIu16,
+            ntohs(sin_addr6->sin6_port));
+        }
+        ds_destroy(&ipv6_ds);
     }
     return new_ssl_stream(name, new_fd, SERVER, STATE_SSL_CONNECTING,
                           new_streamp);
diff --git a/lib/stream-tcp.c b/lib/stream-tcp.c
index b3237d6..bdab667 100644
--- a/lib/stream-tcp.c
+++ b/lib/stream-tcp.c
@@ -21,10 +21,12 @@
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#include <netdb.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <unistd.h>
+#include "dynamic-string.h"
 #include "packets.h"
 #include "socket-util.h"
 #include "util.h"
@@ -40,13 +42,13 @@ static int
 new_tcp_stream(const char *name, int fd, int connect_status,
                struct stream **streamp)
 {
-    struct sockaddr_in local;
+    struct sockaddr_storage local;
     socklen_t local_len = sizeof local;
     int on = 1;
     int retval;
 
     /* Get the local IP and port information */
-    retval = getsockname(fd, (struct sockaddr *)&local, &local_len);
+    retval = getsockname(fd, (struct sockaddr *) &local, &local_len);
     if (retval) {
         memset(&local, 0, sizeof local);
     }
@@ -97,22 +99,35 @@ static int
 ptcp_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp,
           uint8_t dscp)
 {
-    struct sockaddr_in sin;
+    struct sockaddr_storage ss;
+    in_port_t port;
     char bound_name[128];
     int error;
     int fd;
 
-    fd = inet_open_passive(SOCK_STREAM, suffix, -1, &sin, dscp);
+    fd = inet_open_passive(SOCK_STREAM, suffix, -1, &ss, dscp);
     if (fd < 0) {
         return -fd;
     }
 
-    sprintf(bound_name, "ptcp:%"PRIu16":"IP_FMT,
-            ntohs(sin.sin_port), IP_ARGS(sin.sin_addr.s_addr));
+    if (ss.ss_family == AF_INET) {
+        struct sockaddr_in *sin_addr = (struct sockaddr_in *) &ss;
+        port = sin_addr->sin_port;
+        sprintf(bound_name, "ptcp:%"PRIu16":"IP_FMT,
+                ntohs(sin_addr->sin_port), IP_ARGS(sin_addr->sin_addr.s_addr));
+    } else {
+        struct sockaddr_in6 *sin_addr6 = (struct sockaddr_in6 *) &ss;
+        struct ds ipv6_ds = DS_EMPTY_INITIALIZER;
+        port = sin_addr6->sin6_port;
+        print_ipv6_addr(&ipv6_ds, &sin_addr6->sin6_addr);
+        sprintf(bound_name, "ptcp:%"PRIu16":%s",
+                ntohs(sin_addr6->sin6_port), ds_cstr(&ipv6_ds));
+        ds_destroy(&ipv6_ds);
+    }
     error = new_fd_pstream(bound_name, fd, ptcp_accept, set_dscp, NULL,
                            pstreamp);
     if (!error) {
-        pstream_set_bound_port(*pstreamp, sin.sin_port);
+        pstream_set_bound_port(*pstreamp, port);
     }
     return error;
 }
@@ -128,6 +143,14 @@ ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
     if (sa_len == sizeof(struct sockaddr_in) && sin->sin_family == AF_INET) {
         sprintf(name, "tcp:"IP_FMT, IP_ARGS(sin->sin_addr.s_addr));
         sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin->sin_port));
+    } else if (sa_len == sizeof(struct sockaddr_in6)) {
+        const struct sockaddr_in6 *sin6 = ALIGNED_CAST(
+            const struct sockaddr_in6 *, sa);
+        struct ds ipv6_ds = DS_EMPTY_INITIALIZER;
+        print_ipv6_addr(&ipv6_ds, &sin6->sin6_addr);
+        sprintf(name, "tcp:%s", ds_cstr(&ipv6_ds));
+        sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin->sin_port));
+        ds_destroy(&ipv6_ds);
     } else {
         strcpy(name, "tcp");
     }
diff --git a/lib/stream.c b/lib/stream.c
index 3542455..b69f03c 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -720,18 +720,18 @@ pstream_open_with_default_port(const char *name_,
 /*
  * This function extracts IP address and port from the target string.
  *
- *     - On success, function returns true and fills *sin structure with port
+ *     - On success, function returns true and fills *ss structure with port
  *       and IP address. If port was absent in target string then it will use
  *       corresponding default port value.
- *     - On error, function returns false and *sin contains garbage.
+ *     - On error, function returns false and *ss contains garbage.
  */
 bool
 stream_parse_target_with_default_port(const char *target,
                                       uint16_t default_port,
-                                      struct sockaddr_in *sin)
+                                      struct sockaddr_storage *ss)
 {
     return ((!strncmp(target, "tcp:", 4) || !strncmp(target, "ssl:", 4))
-             && inet_parse_active(target + 4, default_port, sin));
+            && inet_parse_active(target + 4, default_port, ss));
 }
 
 /* Attempts to guess the content type of a stream whose first few bytes were
diff --git a/lib/stream.h b/lib/stream.h
index d966cde..3780ff9 100644
--- a/lib/stream.h
+++ b/lib/stream.h
@@ -81,7 +81,7 @@ int pstream_open_with_default_port(const char *name,
                                    uint8_t dscp);
 bool stream_parse_target_with_default_port(const char *target,
                                            uint16_t default_port,
-                                           struct sockaddr_in *sin);
+                                           struct sockaddr_storage *ss);
 int stream_or_pstream_needs_probes(const char *name);
 
 /* Error reporting. */
diff --git a/lib/vconn-active.man b/lib/vconn-active.man
index bf7aaf7..5b4c9eb 100644
--- a/lib/vconn-active.man
+++ b/lib/vconn-active.man
@@ -1,9 +1,11 @@
 .IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
 .IQ "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
 The specified \fIport\fR on the host at the given \fIip\fR, which must
-be expressed as an IP address (not a DNS name).  For \fBssl\fR, the
-\fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR
-options are mandatory.
+be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address format.
+If \fIip\fR is an IPv6 address, then wrap \fIip\fR with square
+brackets, e.g.: \fBtcp\fR:\fB[\fRfc01::2\fB]\fR:6634.
+For \fBssl\fR, the \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and
+\fB\-\-ca\-cert\fR options are mandatory.
 .IP
 If \fIport\fR is not specified, it currently defaults to 6633.  In the
 future, the default will change to 6653, which is the IANA-defined
diff --git a/lib/vconn-passive.man b/lib/vconn-passive.man
index a9efdb3..5eaa979 100644
--- a/lib/vconn-passive.man
+++ b/lib/vconn-passive.man
@@ -1,10 +1,14 @@
 .IP "\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]"
 .IQ "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]"
 Listens for OpenFlow connections on \fIport\fR.  By
-default, connections are not bound to a particular local IP address, but
-\fIip\fR may be specified to listen only for connections to the given
-\fIip\fR.  For \fBpssl\fR, the \fB\-\-private\-key\fR,
-\fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory.
+default, connections are not bound to a particular local IP address
+and it listens only on IPv4 (but not IPv6) addresses,
+but specifying \fIip\fR limits  connections to those from the
+given ip, either IPv4 or IPv6 address.
+If \fIip\fR is an IPv6 address, then wrap \fIip\fR with square brackets, e.g.:
+\fBptcp\fR:6633:\fB[\fRfc01::2\fB]\fR.
+For \fBpssl\fR, the \fB\-\-private\-key\fR,\fB\-\-certificate\fR, and
+\fB\-\-ca\-cert\fR options are mandatory.
 .IP
 If \fIport\fR is not specified, it currently defaults to 6633.  In the
 future, the default will change to 6653, which is the IANA-defined
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 2350c33..19f6191 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -735,6 +735,7 @@ update_in_band_remotes(struct connmgr *mgr)
     /* Add all the remotes. */
     HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) {
         struct sockaddr_in *sin = &addrs[n_addrs];
+        struct sockaddr_storage sstorage;
         const char *target = rconn_get_target(ofconn->rconn);
 
         if (ofconn->band == OFPROTO_OUT_OF_BAND) {
@@ -743,8 +744,12 @@ update_in_band_remotes(struct connmgr *mgr)
 
         if (stream_parse_target_with_default_port(target,
                                                   OFP_OLD_PORT,
-                                                  sin)) {
-            n_addrs++;
+                                                  &sstorage)) {
+            if (sstorage.ss_family == AF_INET) {
+                /* Allow only IPv4 Address for in-band. */
+                n_addrs++;
+                memcpy(sin, &sstorage, sizeof *sin);
+            }
         }
     }
     for (i = 0; i < mgr->n_extra_remotes; i++) {
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 158887f..1028ca4 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -242,6 +242,7 @@ sflow_choose_agent_address(const char *agent_device,
 {
     const char *target;
     struct in_addr in4;
+    struct sockaddr_storage ss;
 
     memset(agent_addr, 0, sizeof *agent_addr);
     agent_addr->type = SFLADDRESSTYPE_IP_V4;
@@ -253,13 +254,18 @@ sflow_choose_agent_address(const char *agent_device,
     }
 
     SSET_FOR_EACH (target, targets) {
-        struct sockaddr_in sin;
         char name[IFNAMSIZ];
 
-        if (inet_parse_active(target, SFL_DEFAULT_COLLECTOR_PORT, &sin)
-            && route_table_get_name(sin.sin_addr.s_addr, name)
-            && !netdev_get_in4_by_name(name, &in4)) {
-            goto success;
+        if (inet_parse_active(target, SFL_DEFAULT_COLLECTOR_PORT, &ss)) {
+            if (ss.ss_family == AF_INET) {
+                struct sockaddr_in *tsin = (struct sockaddr_in *) &ss;
+                if (route_table_get_name(tsin->sin_addr.s_addr, name)
+                    && !netdev_get_in4_by_name(name, &in4)) {
+                    goto success;
+                }
+            } else if (ss.ss_family == AF_INET6) {
+                goto success;
+            }
         }
     }
 
@@ -271,7 +277,15 @@ sflow_choose_agent_address(const char *agent_device,
     return false;
 
 success:
-    agent_addr->address.ip_v4.addr = (OVS_FORCE uint32_t) in4.s_addr;
+    if (ss.ss_family == AF_INET) {
+        agent_addr->address.ip_v4.addr = (OVS_FORCE uint32_t) in4.s_addr;
+    } else if (ss.ss_family == AF_INET6) {
+        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
+
+        memcpy(agent_addr->address.ip_v6.addr, sin6->sin6_addr.s6_addr16,
+               sizeof sin6->sin6_addr);
+        agent_addr->type = SFLADDRESSTYPE_IP_V6;
+    }
     return true;
 }
 
diff --git a/ovsdb/remote-active.man b/ovsdb/remote-active.man
index a934cf0..758a3a2 100644
--- a/ovsdb/remote-active.man
+++ b/ovsdb/remote-active.man
@@ -1,11 +1,15 @@
 .IP "\fBssl:\fIip\fB:\fIport\fR"
 The specified SSL \fIport\fR on the host at the given \fIip\fR, which
-must be expressed as an IP address (not a DNS name).  The
-\fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR
+must be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address
+format.  If \fIip\fR is an IPv6 address, then wrap \fIip\fR with square
+brackets, e.g.: \fBssl\fR:\fB[\fRfc01::2\fB]\fR:6632.
+The \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR
 options are mandatory when this form is used.
 .
 .IP "\fBtcp:\fIip\fB:\fIport\fR"
-Connect to the given TCP \fIport\fR on \fIip\fR.
+Connect to the given TCP \fIport\fR on \fIip\fR, where \fIip\fR can be IPv4
+or IPv6 address. If \fIip\fR is an IPv6 address, then wrap \fIip\fR with
+square brackets, e.g.: \fBtcp\fR:\fB[\fRfc01::2\fB]\fR:6632.
 .
 .IP "\fBunix:\fIfile\fR"
 Connect to the Unix domain server socket named \fIfile\fR.
diff --git a/ovsdb/remote-passive.man b/ovsdb/remote-passive.man
index 4cbc007..393965c 100644
--- a/ovsdb/remote-passive.man
+++ b/ovsdb/remote-passive.man
@@ -1,15 +1,22 @@
 .IP "\fBpssl:\fIport\fR[\fB:\fIip\fR]"
 Listen on the given SSL \fIport\fR for a connection.  By default,
-connections are not bound to a particular local IP address, but
+connections are not bound to a particular local IP address and
+it listens only on IPv4 (but not IPv6) addresses, but
 specifying \fIip\fR limits connections to those from the given
-\fIip\fR.  The \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and
-\fB\-\-ca\-cert\fR options are mandatory when this form is used.
+\fIip\fR, either IPv4 or IPv6 address.  If \fIip\fR is
+an IPv6 address, then wrap \fIip\fR with square brackets, e.g.:
+\fBpssl\fR:6632:\fB[\fRfc01::2\fB]\fR.  The \fB\-\-private\-key\fR,
+\fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory
+when this form is used.
 .
 .IP "\fBptcp:\fIport\fR[\fB:\fIip\fR]"
 Listen on the given TCP \fIport\fR for a connection.  By default,
-connections are not bound to a particular local IP address, but
+connections are not bound to a particular local IP address and
+it listens only on IPv4 (but not IPv6) addresses, but
 \fIip\fR may be specified to listen only for connections to the given
-\fIip\fR.
+\fIip\fR, either IPv4 or IPv6 address.  If \fIip\fR is
+an IPv6 address, then wrap \fIip\fR with square brackets, e.g.:
+\fBptcp\fR:6632:\fB[\fRfc01::2\fB]\fR.
 .
 .IP "\fBpunix:\fIfile\fR"
 Listen on the Unix domain server socket named \fIfile\fR for a
diff --git a/python/ovs/socket_util.py b/python/ovs/socket_util.py
index c657047..ff2a79c 100644
--- a/python/ovs/socket_util.py
+++ b/python/ovs/socket_util.py
@@ -177,24 +177,44 @@ def check_connection_completion(sock):
         return errno.EAGAIN
 
 
+def is_valid_ipv4_address(address):
+    try:
+        socket.inet_pton(socket.AF_INET, address)
+    except AttributeError:
+        try:
+            socket.inet_aton(address)
+        except socket.error:
+            return False
+    except socket.error:
+        return False
+
+    return True
+
+
 def inet_parse_active(target, default_port):
     address = target.split(":")
-    host_name = address[0]
-    if not host_name:
-        raise ValueError("%s: bad peer name format" % target)
     if len(address) >= 2:
-        port = int(address[1])
-    elif default_port:
-        port = default_port
+        host_name = ":".join(address[0:-1])
+        port = int(address[-1])
     else:
-        raise ValueError("%s: port number must be specified" % target)
+        if default_port:
+            port = default_port
+        else:
+            raise ValueError("%s: port number must be specified" % target)
+        host_name = address[0]
+    if not host_name:
+        raise ValueError("%s: bad peer name format" % target)
     return (host_name, port)
 
 
 def inet_open_active(style, target, default_port, dscp):
     address = inet_parse_active(target, default_port)
     try:
-        sock = socket.socket(socket.AF_INET, style, 0)
+        is_addr_inet = is_valid_ipv4_address(address[0])
+        if is_addr_inet:
+            sock = socket.socket(socket.AF_INET, style, 0)
+        else:
+            sock = socket.socket(socket.AF_INET6, style, 0)
     except socket.error, e:
         return get_exception_errno(e), None
 
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 3e74f80..a6946c1 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -1781,415 +1781,444 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/' | sort],
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-dnl Test that sFlow samples packets correctly.
-AT_SETUP([ofproto-dpif - sFlow packet sampling])
-OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
-
-ON_EXIT([kill `cat test-sflow.pid`])
-AT_CHECK([test-sflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > sflow.log], [0], [], [ignore])
-AT_CAPTURE_FILE([sflow.log])
-SFLOW_PORT=`parse_listening_port < test-sflow.log`
-
-ovs-appctl time/stop
-
-ADD_OF_PORTS([br0], 1, 2)
-ovs-vsctl \
-   set Interface br0 options:ifindex=1002 -- \
-   set Interface p1 options:ifindex=1004 -- \
-   set Interface p2 options:ifindex=1003 -- \
-   set Bridge br0 sflow=@sf -- \
-   --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \
-     header=128 sampling=1 polling=1
-
-dnl open with ARP packets to seed the bridge-learning.  The output
-dnl ifIndex numbers should be reported predictably after that.
-dnl Since we set sampling=1 we should see all of these packets
-dnl reported. Sorting the output by data-source and seqNo makes
-dnl it deterministic. Ensuring that we send at least two packets
-dnl into each port means we get to check the seq nos are
-dnl incrementing correctly.
-
-ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
-ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:07,tha=00:00:00:00:00:00)'
-ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
-ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'
-ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x86dd),ipv6(src=fe80::1,dst=fe80::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'
-
-dnl sleep long enough to get more than one counter sample
-dnl from each datasource so we can check sequence numbers
-for i in `seq 1 30`; do
-    ovs-appctl time/warp 100
-done
-OVS_VSWITCHD_STOP
-ovs-appctl -t test-sflow exit
+# CHECK_SFLOW_SAMPLING_PACKET(LOOPBACK_ADDR, IP_VERSION_TYPE)
+#
+# Test that sFlow samples packets correctly using IPv4/IPv6 sFlow collector
+#
+# IP_VERSION_TYPE is used in AT_SETUP
+m4_define([CHECK_SFLOW_SAMPLING_PACKET],
+  [AT_SETUP([ofproto-dpif - sFlow packet sampling - $2 collector])
+  OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
+
+  ON_EXIT([kill `cat test-sflow.pid`])
+  AT_CHECK([test-sflow --log-file --detach --no-chdir --pidfile 0:$1 > sflow.log], [0], [], [ignore])
+  AT_CAPTURE_FILE([sflow.log])
+  m4_if([$1], [127.0.0.1],
+    [SFLOW_PORT=`parse_listening_port < test-sflow.log`
+     DS_IN_SFLOW_OUTPUT=$1],
+    [SFLOW_PORT=`parse_ipv6_listening_port < test-sflow.log`
+     DS_IN_SFLOW_OUTPUT=0:0:0:0:0:0:0:1])
+
+  ovs-appctl time/stop
+
+  ADD_OF_PORTS([br0], 1, 2)
+  ovs-vsctl \
+     set Interface br0 options:ifindex=1002 -- \
+     set Interface p1 options:ifindex=1004 -- \
+     set Interface p2 options:ifindex=1003 -- \
+     set Bridge br0 sflow=@sf -- \
+     --id=@sf create sflow targets=\"$1:$SFLOW_PORT\" \
+       header=128 sampling=1 polling=1
+
+  dnl open with ARP packets to seed the bridge-learning.  The output
+  dnl ifIndex numbers should be reported predictably after that.
+  dnl Since we set sampling=1 we should see all of these packets
+  dnl reported. Sorting the output by data-source and seqNo makes
+  dnl it deterministic. Ensuring that we send at least two packets
+  dnl into each port means we get to check the seq nos are
+  dnl incrementing correctly.
+
+  ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
+  ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:07,tha=00:00:00:00:00:00)'
+  ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+  ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'
+  ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x86dd),ipv6(src=fe80::1,dst=fe80::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'
+
+  dnl sleep long enough to get more than one counter sample
+  dnl from each datasource so we can check sequence numbers
+  for i in `seq 1 30`; do
+      ovs-appctl time/warp 100
+  done
+  OVS_VSWITCHD_STOP
+  ovs-appctl -t test-sflow exit
 
-AT_CHECK([[sort sflow.log | $EGREP 'HEADER|ERROR' | sed 's/ /\
-	/g']], [0], [dnl
+  AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'HEADER|ERROR' | sed 's/ /\
+      /g']], [0], [dnl
 HEADER
-	dgramSeqNo=1
-	ds=127.0.0.1>2:1000
-	fsSeqNo=1
-	in_vlan=0
-	in_priority=0
-	out_vlan=0
-	out_priority=0
-	meanSkip=1
-	samplePool=1
-	dropEvents=0
-	in_ifindex=1004
-	in_format=0
-	out_ifindex=2
-	out_format=2
-	hdr_prot=1
-	pkt_len=64
-	stripped=4
-	hdr_len=60
-	hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-05-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-05-C0-A8-00-02-00-00-00-00-00-00-C0-A8-00-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
+      dgramSeqNo=1
+      ds=$DS_IN_SFLOW_OUTPUT>2:1000
+      fsSeqNo=1
+      in_vlan=0
+      in_priority=0
+      out_vlan=0
+      out_priority=0
+      meanSkip=1
+      samplePool=1
+      dropEvents=0
+      in_ifindex=1004
+      in_format=0
+      out_ifindex=2
+      out_format=2
+      hdr_prot=1
+      pkt_len=64
+      stripped=4
+      hdr_len=60
+      hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-05-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-05-C0-A8-00-02-00-00-00-00-00-00-C0-A8-00-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
 HEADER
-	dgramSeqNo=1
-	ds=127.0.0.1>2:1000
-	fsSeqNo=2
-	in_vlan=0
-	in_priority=0
-	out_vlan=0
-	out_priority=0
-	meanSkip=1
-	samplePool=2
-	dropEvents=0
-	in_ifindex=1003
-	in_format=0
-	out_ifindex=2
-	out_format=2
-	hdr_prot=1
-	pkt_len=64
-	stripped=4
-	hdr_len=60
-	hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-07-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-07-C0-A8-00-01-00-00-00-00-00-00-C0-A8-00-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
+      dgramSeqNo=1
+      ds=$DS_IN_SFLOW_OUTPUT>2:1000
+      fsSeqNo=2
+      in_vlan=0
+      in_priority=0
+      out_vlan=0
+      out_priority=0
+      meanSkip=1
+      samplePool=2
+      dropEvents=0
+      in_ifindex=1003
+      in_format=0
+      out_ifindex=2
+      out_format=2
+      hdr_prot=1
+      pkt_len=64
+      stripped=4
+      hdr_len=60
+      hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-07-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-07-C0-A8-00-01-00-00-00-00-00-00-C0-A8-00-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
 HEADER
-	dgramSeqNo=1
-	ds=127.0.0.1>2:1000
-	fsSeqNo=3
-	in_vlan=0
-	in_priority=0
-	out_vlan=0
-	out_priority=0
-	meanSkip=1
-	samplePool=3
-	dropEvents=0
-	in_ifindex=1004
-	in_format=0
-	out_ifindex=1003
-	out_format=0
-	hdr_prot=1
-	pkt_len=64
-	stripped=4
-	hdr_len=60
-	hdr=50-54-00-00-00-07-50-54-00-00-00-05-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-01-C0-A8-00-02-08-00-F7-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
+      dgramSeqNo=1
+      ds=$DS_IN_SFLOW_OUTPUT>2:1000
+      fsSeqNo=3
+      in_vlan=0
+      in_priority=0
+      out_vlan=0
+      out_priority=0
+      meanSkip=1
+      samplePool=3
+      dropEvents=0
+      in_ifindex=1004
+      in_format=0
+      out_ifindex=1003
+      out_format=0
+      hdr_prot=1
+      pkt_len=64
+      stripped=4
+      hdr_len=60
+      hdr=50-54-00-00-00-07-50-54-00-00-00-05-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-01-C0-A8-00-02-08-00-F7-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
 HEADER
-	dgramSeqNo=1
-	ds=127.0.0.1>2:1000
-	fsSeqNo=4
-	in_vlan=0
-	in_priority=0
-	out_vlan=0
-	out_priority=0
-	meanSkip=1
-	samplePool=4
-	dropEvents=0
-	in_ifindex=1003
-	in_format=0
-	out_ifindex=1004
-	out_format=0
-	hdr_prot=1
-	pkt_len=64
-	stripped=4
-	hdr_len=60
-	hdr=50-54-00-00-00-05-50-54-00-00-00-07-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-02-C0-A8-00-01-00-00-FF-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
+      dgramSeqNo=1
+      ds=$DS_IN_SFLOW_OUTPUT>2:1000
+      fsSeqNo=4
+      in_vlan=0
+      in_priority=0
+      out_vlan=0
+      out_priority=0
+      meanSkip=1
+      samplePool=4
+      dropEvents=0
+      in_ifindex=1003
+      in_format=0
+      out_ifindex=1004
+      out_format=0
+      hdr_prot=1
+      pkt_len=64
+      stripped=4
+      hdr_len=60
+      hdr=50-54-00-00-00-05-50-54-00-00-00-07-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-02-C0-A8-00-01-00-00-FF-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
 HEADER
-	dgramSeqNo=1
-	ds=127.0.0.1>2:1000
-	fsSeqNo=5
-	in_vlan=0
-	in_priority=0
-	out_vlan=0
-	out_priority=0
-	meanSkip=1
-	samplePool=5
-	dropEvents=0
-	in_ifindex=1003
-	in_format=0
-	out_ifindex=1004
-	out_format=0
-	hdr_prot=1
-	pkt_len=64
-	stripped=4
-	hdr_len=60
-	hdr=50-54-00-00-00-05-50-54-00-00-00-07-86-DD-67-00-00-00-00-00-0A-80-FE-80-00-00-00-00-00-00-00-00-00-00-00-00-00-01-FE-80-00-00-00-00-00-00-00-00-00-00-00-00-00-02-00-00-00-00-00-00
-])
-
-AT_CHECK([[sort sflow.log | $EGREP 'IFCOUNTERS|ERROR' | head -6 | sed 's/ /\
-	/g']], [0], [dnl
+      dgramSeqNo=1
+      ds=$DS_IN_SFLOW_OUTPUT>2:1000
+      fsSeqNo=5
+      in_vlan=0
+      in_priority=0
+      out_vlan=0
+      out_priority=0
+      meanSkip=1
+      samplePool=5
+      dropEvents=0
+      in_ifindex=1003
+      in_format=0
+      out_ifindex=1004
+      out_format=0
+      hdr_prot=1
+      pkt_len=64
+      stripped=4
+      hdr_len=60
+      hdr=50-54-00-00-00-05-50-54-00-00-00-07-86-DD-67-00-00-00-00-00-0A-80-FE-80-00-00-00-00-00-00-00-00-00-00-00-00-00-01-FE-80-00-00-00-00-00-00-00-00-00-00-00-00-00-02-00-00-00-00-00-00
+])
+
+  AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'IFCOUNTERS|ERROR' | head -6 | sed 's/ /\
+      /g']], [0], [dnl
 IFCOUNTERS
-	dgramSeqNo=2
-	ds=127.0.0.1>0:1002
-	csSeqNo=1
-	ifindex=1002
-	type=6
-	ifspeed=100000000
-	direction=0
-	status=3
-	in_octets=0
-	in_unicasts=0
-	in_multicasts=0
-	in_broadcasts=4294967295
-	in_discards=0
-	in_errors=0
-	in_unknownprotos=4294967295
-	out_octets=120
-	out_unicasts=2
-	out_multicasts=4294967295
-	out_broadcasts=4294967295
-	out_discards=0
-	out_errors=0
-	promiscuous=0
+      dgramSeqNo=2
+      ds=$DS_IN_SFLOW_OUTPUT>0:1002
+      csSeqNo=1
+      ifindex=1002
+      type=6
+      ifspeed=100000000
+      direction=0
+      status=3
+      in_octets=0
+      in_unicasts=0
+      in_multicasts=0
+      in_broadcasts=4294967295
+      in_discards=0
+      in_errors=0
+      in_unknownprotos=4294967295
+      out_octets=120
+      out_unicasts=2
+      out_multicasts=4294967295
+      out_broadcasts=4294967295
+      out_discards=0
+      out_errors=0
+      promiscuous=0
 IFCOUNTERS
-	dgramSeqNo=2
-	ds=127.0.0.1>0:1003
-	csSeqNo=1
-	ifindex=1003
-	type=6
-	ifspeed=100000000
-	direction=0
-	status=0
-	in_octets=138
-	in_unicasts=3
-	in_multicasts=0
-	in_broadcasts=4294967295
-	in_discards=0
-	in_errors=0
-	in_unknownprotos=4294967295
-	out_octets=120
-	out_unicasts=2
-	out_multicasts=4294967295
-	out_broadcasts=4294967295
-	out_discards=0
-	out_errors=0
-	promiscuous=0
+      dgramSeqNo=2
+      ds=$DS_IN_SFLOW_OUTPUT>0:1003
+      csSeqNo=1
+      ifindex=1003
+      type=6
+      ifspeed=100000000
+      direction=0
+      status=0
+      in_octets=138
+      in_unicasts=3
+      in_multicasts=0
+      in_broadcasts=4294967295
+      in_discards=0
+      in_errors=0
+      in_unknownprotos=4294967295
+      out_octets=120
+      out_unicasts=2
+      out_multicasts=4294967295
+      out_broadcasts=4294967295
+      out_discards=0
+      out_errors=0
+      promiscuous=0
 IFCOUNTERS
-	dgramSeqNo=2
-	ds=127.0.0.1>0:1004
-	csSeqNo=1
-	ifindex=1004
-	type=6
-	ifspeed=100000000
-	direction=0
-	status=0
-	in_octets=84
-	in_unicasts=2
-	in_multicasts=0
-	in_broadcasts=4294967295
-	in_discards=0
-	in_errors=0
-	in_unknownprotos=4294967295
-	out_octets=180
-	out_unicasts=3
-	out_multicasts=4294967295
-	out_broadcasts=4294967295
-	out_discards=0
-	out_errors=0
-	promiscuous=0
+      dgramSeqNo=2
+      ds=$DS_IN_SFLOW_OUTPUT>0:1004
+      csSeqNo=1
+      ifindex=1004
+      type=6
+      ifspeed=100000000
+      direction=0
+      status=0
+      in_octets=84
+      in_unicasts=2
+      in_multicasts=0
+      in_broadcasts=4294967295
+      in_discards=0
+      in_errors=0
+      in_unknownprotos=4294967295
+      out_octets=180
+      out_unicasts=3
+      out_multicasts=4294967295
+      out_broadcasts=4294967295
+      out_discards=0
+      out_errors=0
+      promiscuous=0
 IFCOUNTERS
-	dgramSeqNo=3
-	ds=127.0.0.1>0:1002
-	csSeqNo=2
-	ifindex=1002
-	type=6
-	ifspeed=100000000
-	direction=0
-	status=3
-	in_octets=0
-	in_unicasts=0
-	in_multicasts=0
-	in_broadcasts=4294967295
-	in_discards=0
-	in_errors=0
-	in_unknownprotos=4294967295
-	out_octets=120
-	out_unicasts=2
-	out_multicasts=4294967295
-	out_broadcasts=4294967295
-	out_discards=0
-	out_errors=0
-	promiscuous=0
+      dgramSeqNo=3
+      ds=$DS_IN_SFLOW_OUTPUT>0:1002
+      csSeqNo=2
+      ifindex=1002
+      type=6
+      ifspeed=100000000
+      direction=0
+      status=3
+      in_octets=0
+      in_unicasts=0
+      in_multicasts=0
+      in_broadcasts=4294967295
+      in_discards=0
+      in_errors=0
+      in_unknownprotos=4294967295
+      out_octets=120
+      out_unicasts=2
+      out_multicasts=4294967295
+      out_broadcasts=4294967295
+      out_discards=0
+      out_errors=0
+      promiscuous=0
 IFCOUNTERS
-	dgramSeqNo=3
-	ds=127.0.0.1>0:1003
-	csSeqNo=2
-	ifindex=1003
-	type=6
-	ifspeed=100000000
-	direction=0
-	status=0
-	in_octets=138
-	in_unicasts=3
-	in_multicasts=0
-	in_broadcasts=4294967295
-	in_discards=0
-	in_errors=0
-	in_unknownprotos=4294967295
-	out_octets=120
-	out_unicasts=2
-	out_multicasts=4294967295
-	out_broadcasts=4294967295
-	out_discards=0
-	out_errors=0
-	promiscuous=0
+      dgramSeqNo=3
+      ds=$DS_IN_SFLOW_OUTPUT>0:1003
+      csSeqNo=2
+      ifindex=1003
+      type=6
+      ifspeed=100000000
+      direction=0
+      status=0
+      in_octets=138
+      in_unicasts=3
+      in_multicasts=0
+      in_broadcasts=4294967295
+      in_discards=0
+      in_errors=0
+      in_unknownprotos=4294967295
+      out_octets=120
+      out_unicasts=2
+      out_multicasts=4294967295
+      out_broadcasts=4294967295
+      out_discards=0
+      out_errors=0
+      promiscuous=0
 IFCOUNTERS
-	dgramSeqNo=3
-	ds=127.0.0.1>0:1004
-	csSeqNo=2
-	ifindex=1004
-	type=6
-	ifspeed=100000000
-	direction=0
-	status=0
-	in_octets=84
-	in_unicasts=2
-	in_multicasts=0
-	in_broadcasts=4294967295
-	in_discards=0
-	in_errors=0
-	in_unknownprotos=4294967295
-	out_octets=180
-	out_unicasts=3
-	out_multicasts=4294967295
-	out_broadcasts=4294967295
-	out_discards=0
-	out_errors=0
-	promiscuous=0
-])
-AT_CLEANUP
-
-
-
-dnl Test that basic NetFlow reports flow statistics correctly:
-dnl - The initial packet of a flow are correctly accounted.
-dnl - Later packets within a flow are correctly accounted.
-dnl - Flow actions changing (in this case, due to MAC learning)
-dnl   cause a record to be sent.
-AT_SETUP([ofproto-dpif - NetFlow flow expiration])
-
-OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
-ADD_OF_PORTS([br0], 1, 2)
-
-ovs-appctl time/stop
-ON_EXIT([kill `cat test-netflow.pid`])
-AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > netflow.log], [0], [], [ignore])
-AT_CAPTURE_FILE([netflow.log])
-NETFLOW_PORT=`parse_listening_port < test-netflow.log`
-
-ovs-vsctl \
-   set Bridge br0 netflow=@nf -- \
-   --id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \
-     engine_id=1 engine_type=2 active_timeout=30 add-id-to-interface=false
-
-for delay in 1000 30000; do
-    ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
-    ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'
-
-    ovs-appctl time/warp $delay
-done
-
-ovs-appctl time/warp 6000
-sleep 1
-OVS_VSWITCHD_STOP
-ovs-appctl -t test-netflow exit
-
-AT_CHECK([test `grep "192.168.0.1 > 192.168.0.2, if 1 > 65535, 1 pkts, 60 bytes, ICMP 8:0" netflow.log | wc -l` -eq 1])
-
-AT_CHECK([test `grep "192.168.0.1 > 192.168.0.2, if 1 > 2, 1 pkts, 60 bytes, ICMP 8:0" netflow.log | wc -l` -eq 1])
-
-combined=`grep "192.168.0.2 > 192.168.0.1, if 2 > 1, 2 pkts, 120 bytes, ICMP 0:0" netflow.log | wc -l`
-separate=`grep "192.168.0.2 > 192.168.0.1, if 2 > 1, 1 pkts, 60 bytes, ICMP 0:0" netflow.log | wc -l`
-AT_CHECK([test $separate = 2 || test $combined = 1], [0])
-
-AT_CLEANUP
+      dgramSeqNo=3
+      ds=$DS_IN_SFLOW_OUTPUT>0:1004
+      csSeqNo=2
+      ifindex=1004
+      type=6
+      ifspeed=100000000
+      direction=0
+      status=0
+      in_octets=84
+      in_unicasts=2
+      in_multicasts=0
+      in_broadcasts=4294967295
+      in_discards=0
+      in_errors=0
+      in_unknownprotos=4294967295
+      out_octets=180
+      out_unicasts=3
+      out_multicasts=4294967295
+      out_broadcasts=4294967295
+      out_discards=0
+      out_errors=0
+      promiscuous=0
+])
+  AT_CLEANUP])
+
+CHECK_SFLOW_SAMPLING_PACKET([127.0.0.1], [IPv4])
+CHECK_SFLOW_SAMPLING_PACKET([[::1]], [IPv6])
+
+# CHECK_NETFLOW_EXPIRATION(LOOPBACK_ADDR, IP_VERSION_TYPE)
+#
+# Test that basic NetFlow reports flow statistics correctly:
+# The initial packet of a flow are correctly accounted.
+# Later packets within a flow are correctly accounted.
+# Flow actions changing (in this case, due to MAC learning)
+# cause a record to be sent.
+#
+# IP_VERSION_TYPE is used in AT_SETUP
+m4_define([CHECK_NETFLOW_EXPIRATION],
+  [AT_SETUP([ofproto-dpif - NetFlow flow expiration - $2 collector])
+  OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
+  ADD_OF_PORTS([br0], 1, 2)
+
+  ovs-appctl time/stop
+  ON_EXIT([kill `cat test-netflow.pid`])
+  AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:$1 > netflow.log], [0], [], [ignore])
+  AT_CAPTURE_FILE([netflow.log])
+  m4_if([$1], [127.0.0.1],
+    [NETFLOW_PORT=`parse_listening_port < test-netflow.log`],
+    [NETFLOW_PORT=`parse_ipv6_listening_port < test-netflow.log`])
+
+  ovs-vsctl \
+     set Bridge br0 netflow=@nf -- \
+     --id=@nf create NetFlow targets=\"$1:$NETFLOW_PORT\" \
+       engine_id=1 engine_type=2 active_timeout=30 add-id-to-interface=false
+
+  for delay in 1000 30000; do
+      ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+      ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'
+
+      ovs-appctl time/warp $delay
+  done
 
-dnl Test that basic NetFlow reports active expirations correctly.
-AT_SETUP([ofproto-dpif - NetFlow active expiration])
+  ovs-appctl time/warp 6000
+  sleep 1
+  OVS_VSWITCHD_STOP
+  ovs-appctl -t test-netflow exit
 
-OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
-ADD_OF_PORTS([br0], 1, 2)
+  AT_CHECK([test `grep "192.168.0.1 > 192.168.0.2, if 1 > 65535, 1 pkts, 60 bytes, ICMP 8:0" netflow.log | wc -l` -eq 1])
 
-ON_EXIT([kill `cat test-netflow.pid`])
-AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > netflow.log], [0], [], [ignore])
-AT_CAPTURE_FILE([netflow.log])
-NETFLOW_PORT=`parse_listening_port < test-netflow.log`
+  AT_CHECK([test `grep "192.168.0.1 > 192.168.0.2, if 1 > 2, 1 pkts, 60 bytes, ICMP 8:0" netflow.log | wc -l` -eq 1])
 
-ovs-vsctl \
-   set Bridge br0 netflow=@nf -- \
-   --id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \
-     engine_id=1 engine_type=2 active_timeout=10 add-id-to-interface=false
+  combined=`grep "192.168.0.2 > 192.168.0.1, if 2 > 1, 2 pkts, 120 bytes, ICMP 0:0" netflow.log | wc -l`
+  separate=`grep "192.168.0.2 > 192.168.0.1, if 2 > 1, 1 pkts, 60 bytes, ICMP 0:0" netflow.log | wc -l`
+  AT_CHECK([test $separate = 2 || test $combined = 1], [0])
 
-AT_CHECK([ovs-appctl time/stop])
-n=1
-while test $n -le 60; do
-    n=`expr $n + 1`
+  AT_CLEANUP])
 
-    ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=1234,dst=80)'
-    ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=1234)'
+CHECK_NETFLOW_EXPIRATION([127.0.0.1], [IPv4])
+CHECK_NETFLOW_EXPIRATION([[::1]], [IPv6])
 
-    ovs-appctl time/warp 1000
-done
-
-ovs-appctl time/warp 10000
-
-sleep 1
-OVS_VSWITCHD_STOP
-ovs-appctl -t test-netflow exit
-
-# Count the number of reported packets:
-# - From source to destination before MAC learning kicks in (just one).
-# - From source to destination after that.
-# - From destination to source.
-n_learn=0
-n_in=0
-n_out=0
-n_other=0
-n_recs=0
-none=0
-while read line; do
-    pkts=`echo "$line" | sed 's/.*, \([[0-9]]*\) pkts,.*/\1/'`
-    case $pkts in
-         [[0-9]]*) ;;
-	 *) continue ;;
-    esac
-
-    case $line in
-        "seq "*": 192.168.0.1 > 192.168.0.2, if 1 > 65535, "*" pkts, "*" bytes, TCP 1234 > 80, time "*)
-            counter=n_learn
-	    ;;
-	"seq "*": 192.168.0.1 > 192.168.0.2, if 1 > 2, "*" pkts, "*" bytes, TCP 1234 > 80, time "*)
-	    counter=n_in
-	    ;;
-	"seq "*": 192.168.0.2 > 192.168.0.1, if 2 > 1, "*" pkts, "*" bytes, TCP 80 > 1234, time "*)
-	    counter=n_out
-	    ;;
-	*)
-	    counter=n_other
-	    ;;
-    esac
-    eval $counter=\`expr \$$counter + \$pkts\`
-    n_recs=`expr $n_recs + 1`
-done < netflow.log
-
-# There should be exactly 1 MAC learning packet,
-# exactly 59 other packets in that direction,
-# and exactly 60 packets in the other direction.
-AT_CHECK([echo $n_learn $n_in $n_out $n_other], [0], [1 59 60 0
-])
+# CHECK_NETFLOW_ACTIVE_EXPIRATION(LOOPBACK_ADDR, IP_VERSION_TYPE)
+#
+# Test that basic NetFlow reports active expirations correctly.
+#
+# IP_VERSION_TYPE is used in AT_SETUP
+m4_define([CHECK_NETFLOW_ACTIVE_EXPIRATION],
+  [AT_SETUP([ofproto-dpif - NetFlow active expiration - $2 collector])
+
+  OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
+  ADD_OF_PORTS([br0], 1, 2)
+
+  ON_EXIT([kill `cat test-netflow.pid`])
+  AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:$1 > netflow.log], [0], [], [ignore])
+  AT_CAPTURE_FILE([netflow.log])
+  m4_if([$1], [127.0.0.1],
+    [NETFLOW_PORT=`parse_listening_port < test-netflow.log`],
+    [NETFLOW_PORT=`parse_ipv6_listening_port < test-netflow.log`])
+
+  ovs-vsctl \
+     set Bridge br0 netflow=@nf -- \
+     --id=@nf create NetFlow targets=\"$1:$NETFLOW_PORT\" \
+       engine_id=1 engine_type=2 active_timeout=10 add-id-to-interface=false
+
+  AT_CHECK([ovs-appctl time/stop])
+  n=1
+  while test $n -le 60; do
+      n=`expr $n + 1`
+
+      ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=1234,dst=80)'
+      ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=1234)'
+
+      ovs-appctl time/warp 1000
+  done
 
-AT_CLEANUP
+  ovs-appctl time/warp 10000
+
+  sleep 1
+  OVS_VSWITCHD_STOP
+  ovs-appctl -t test-netflow exit
+
+  # Count the number of reported packets:
+  # - From source to destination before MAC learning kicks in (just one).
+  # - From source to destination after that.
+  # - From destination to source.
+  n_learn=0
+  n_in=0
+  n_out=0
+  n_other=0
+  n_recs=0
+  none=0
+  while read line; do
+      pkts=`echo "$line" | sed 's/.*, \([[0-9]]*\) pkts,.*/\1/'`
+      case $pkts in
+           [[0-9]]*) ;;
+       *) continue ;;
+      esac
+
+      case $line in
+          "seq "*": 192.168.0.1 > 192.168.0.2, if 1 > 65535, "*" pkts, "*" bytes, TCP 1234 > 80, time "*)
+              counter=n_learn
+          ;;
+      "seq "*": 192.168.0.1 > 192.168.0.2, if 1 > 2, "*" pkts, "*" bytes, TCP 1234 > 80, time "*)
+          counter=n_in
+          ;;
+      "seq "*": 192.168.0.2 > 192.168.0.1, if 2 > 1, "*" pkts, "*" bytes, TCP 80 > 1234, time "*)
+          counter=n_out
+          ;;
+      *)
+          counter=n_other
+          ;;
+      esac
+      eval $counter=\`expr \$$counter + \$pkts\`
+      n_recs=`expr $n_recs + 1`
+  done < netflow.log
+
+  # There should be exactly 1 MAC learning packet,
+  # exactly 59 other packets in that direction,
+  # and exactly 60 packets in the other direction.
+  AT_CHECK([echo $n_learn $n_in $n_out $n_other], [0], [1 59 60 0
+])
+
+  AT_CLEANUP])
+
+CHECK_NETFLOW_ACTIVE_EXPIRATION([127.0.0.1], [IPv4])
+CHECK_NETFLOW_ACTIVE_EXPIRATION([[::1]], [IPv6])
 
 AT_SETUP([idle_age and hard_age increase over time])
 OVS_VSWITCHD_START
@@ -2595,29 +2624,38 @@ skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto-dpif megaflow - netflow])
-OVS_VSWITCHD_START
-ADD_OF_PORTS([br0], [1], [2])
-
-dnl NetFlow configuration disables wildcarding relevant fields
-ON_EXIT([kill `cat test-netflow.pid`])
-AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > netflow.log], [0], [], [ignore])
-AT_CAPTURE_FILE([netflow.log])
-NETFLOW_PORT=`parse_listening_port < test-netflow.log`
-ovs-vsctl \
-   set Bridge br0 netflow=@nf -- \
-   --id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \
-     engine_id=1 engine_type=2 active_timeout=30 add-id-to-interface=false
-
-AT_CHECK([ovs-ofctl add-flow br0 action=normal])
-AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+# CHECK_MEGAFLOW_NETFLOW(LOOPBACK_ADDR, IP_VERSION_TYPE)
+#
+# IP_VERSION_TYPE is used in AT_SETUP
+m4_define([CHECK_MEGAFLOW_NETFLOW],
+  [AT_SETUP([ofproto-dpif megaflow - netflow - $2 collector])
+  OVS_VSWITCHD_START
+  ADD_OF_PORTS([br0], [1], [2])
+
+  dnl NetFlow configuration disables wildcarding relevant fields
+  ON_EXIT([kill `cat test-netflow.pid`])
+  AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:$1 > netflow.log], [0], [], [ignore])
+  AT_CAPTURE_FILE([netflow.log])
+  m4_if([$1], [127.0.0.1],
+    [NETFLOW_PORT=`parse_listening_port < test-netflow.log`],
+    [NETFLOW_PORT=`parse_ipv6_listening_port < test-netflow.log`])
+  ovs-vsctl \
+     set Bridge br0 netflow=@nf -- \
+     --id=@nf create NetFlow targets=\"$1:$NETFLOW_PORT\" \
+       engine_id=1 engine_type=2 active_timeout=30 add-id-to-interface=false
+
+  AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+  AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+  AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+  AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
 ])
-OVS_VSWITCHD_STOP
-AT_CLEANUP
+  OVS_VSWITCHD_STOP
+  AT_CLEANUP])
+
+CHECK_MEGAFLOW_NETFLOW([127.0.0.1], [IPv4])
+CHECK_MEGAFLOW_NETFLOW([[::1]], [IPv6])
 
 AT_SETUP([ofproto-dpif megaflow - normal, active-backup bonding])
 OVS_VSWITCHD_START(
diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
index 3bcffc2..aed5c42 100644
--- a/tests/ofproto-macros.at
+++ b/tests/ofproto-macros.at
@@ -30,6 +30,23 @@ s/ hard_age=[0-9]*,//
 #    TCP_PORT=`parse_listening_port < ovsdb-server.log`
 parse_listening_port () {
     sed -n 's/.*0:127\.0\.0\.1: listening on port \([0-9]*\)$/\1/p'
+}
+
+# parse_ipv6_listening_port [SERVER]
+#
+# Parses the TCP or SSL port on which a server is listening from the
+# log, given that the server was told to listen on a kernel-chosen
+# port, file provided on stdin, and prints the port number on stdout.
+# You should specify the listening remote as ptcp:0:[::1] or
+# pssl:0:[::1].
+#
+# Here's an example of how to use this with ovsdb-server:
+#
+#    OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+#    ovsdb-server --log-file --remote=ptcp:0:[::1] ...
+#    TCP_PORT=`parse_ipv6_listening_port < ovsdb-server.log`
+parse_ipv6_listening_port () {
+    sed -n 's/.*0:::1: listening on port \([0-9]*\)$/\1/p'
 }]
 m4_divert_pop([PREPARE_TESTS])
 
diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
index 851e4d8..ecebe20 100644
--- a/tests/ovs-vsctl.at
+++ b/tests/ovs-vsctl.at
@@ -414,7 +414,7 @@ CHECK_IFACES([a], [a1], [a2], [a3])
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
 
-AT_SETUP([controllers])
+AT_SETUP([controllers IPv4])
 AT_KEYWORDS([controller ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
@@ -440,6 +440,32 @@ tcp:5.4.3.2\ntcp:8.9.10.11
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
 
+AT_SETUP([controllers IPv6])
+AT_KEYWORDS([controller ovs-vsctl])
+OVS_VSCTL_SETUP
+AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
+  [add-br br0],
+
+  [get-controller br0],
+  [set-controller br0 tcp:2607:f0d0:2001:a::10],
+  [get-controller br0],
+
+  [del-controller br0],
+  [get-controller br0],
+
+  [set-controller br0 tcp:2607:f0d0:2001:a::11 tcp:2607:f0d0:2001:a::12],
+  [get-controller br0])], [0], [
+
+
+tcp:2607:f0d0:2001:a::10
+
+
+
+tcp:2607:f0d0:2001:a::11\ntcp:2607:f0d0:2001:a::12
+], [], [OVS_VSCTL_CLEANUP])
+OVS_VSCTL_CLEANUP
+AT_CLEANUP
+
 dnl ----------------------------------------------------------------------
 dnl OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF([VLAN])
 m4_define([OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF],
diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at
index 9ff7b1b..18db1e5 100644
--- a/tests/ovsdb-idl.at
+++ b/tests/ovsdb-idl.at
@@ -72,10 +72,32 @@ m4_define([OVSDB_CHECK_IDL_TCP_PY],
    OVSDB_SERVER_SHUTDOWN
    AT_CLEANUP])
 
+# same as OVSDB_CHECK_IDL but uses the Python IDL implementation with tcp6
+m4_define([OVSDB_CHECK_IDL_TCP6_PY],
+  [AT_SETUP([$1 - Python tcp6])
+   AT_SKIP_IF([test $HAVE_PYTHON = no])
+   AT_KEYWORDS([ovsdb server idl positive Python with tcp6 socket $5])
+   OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+   OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+   AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
+                  [0], [stdout], [ignore])
+   AT_CHECK([ovsdb-server --log-file '-vPATTERN:console:ovsdb-server|%c|%m' --detach --no-chdir --pidfile="`pwd`"/pid --remote=ptcp:0:[::1] --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   TCP_PORT=`parse_ipv6_listening_port < ovsdb-server.log`
+
+   m4_if([$2], [], [],
+     [AT_CHECK([ovsdb-client transact tcp:[::1]:$TCP_PORT $2], [0], [ignore], [ignore], [kill `cat pid`])])
+   AT_CHECK([$PYTHON $srcdir/test-ovsdb.py  -t10 idl $srcdir/idltest.ovsschema tcp:[::1]:$TCP_PORT $3],
+            [0], [stdout], [ignore], [kill `cat pid`])
+   AT_CHECK([sort stdout | ${PERL} $srcdir/uuidfilt.pl]m4_if([$6],,, [[| $6]]),
+            [0], [$4], [], [kill `cat pid`])
+   OVSDB_SERVER_SHUTDOWN
+   AT_CLEANUP])
+
 m4_define([OVSDB_CHECK_IDL],
   [OVSDB_CHECK_IDL_C($@)
    OVSDB_CHECK_IDL_PY($@)
-   OVSDB_CHECK_IDL_TCP_PY($@)])
+   OVSDB_CHECK_IDL_TCP_PY($@)
+   OVSDB_CHECK_IDL_TCP6_PY($@)])
 
 OVSDB_CHECK_IDL([simple idl, initially empty, no ops],
   [],
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
index b05401f..27ac33c 100644
--- a/tests/ovsdb-server.at
+++ b/tests/ovsdb-server.at
@@ -1,6 +1,6 @@
 AT_BANNER([OVSDB -- ovsdb-server transactions (Unix sockets)])
 
-m4_define([OVSDB_SERVER_SHUTDOWN], 
+m4_define([OVSDB_SERVER_SHUTDOWN],
   [cp pid savepid
    AT_CHECK([ovs-appctl -t "`pwd`"/unixctl -e exit], [0], [ignore], [ignore])
    OVS_WAIT_WHILE([kill -0 `cat savepid`], [kill `cat savepid`])])
@@ -19,14 +19,14 @@ m4_define([OVSDB_SERVER_SHUTDOWN],
 # same marker.
 #
 # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
-m4_define([OVSDB_CHECK_EXECUTION], 
+m4_define([OVSDB_CHECK_EXECUTION],
   [AT_SETUP([$1])
   OVS_RUNDIR=`pwd`; export OVS_RUNDIR
    AT_KEYWORDS([ovsdb server positive unix $5])
    $2 > schema
    AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
    AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
-   m4_foreach([txn], [$3], 
+   m4_foreach([txn], [$3],
      [AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0], [stdout], [ignore],
      [test ! -e pid || kill `cat pid`])
 cat stdout >> output
@@ -155,7 +155,7 @@ AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore])
 AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore])
 AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db1 db2], [0], [ignore], [ignore])
 AT_CHECK(
-  [[ovsdb-client list-dbs unix:socket]], 
+  [[ovsdb-client list-dbs unix:socket]],
   [0], [constraints
 ordinals
 ], [ignore], [test ! -e pid || kill `cat pid`])
@@ -365,10 +365,10 @@ AT_CHECK(
         "columns": ["target", "is_connected"]}]']],
   [0], [stdout], [ignore])
 AT_CHECK(
-  [${PERL} $srcdir/uuidfilt.pl stdout], 
-  [0], 
+  [${PERL} $srcdir/uuidfilt.pl stdout],
+  [0],
   [[[{"rows":[{"managers":"punix:socket1"}]},{"rows":[{"is_connected":false,"target":"punix:socket2"}]}]
-]], 
+]],
   [ignore])
 AT_CLEANUP
 
@@ -478,59 +478,69 @@ OVS_WAIT_UNTIL(
 AT_CHECK([test ! -e socket1])
 AT_CLEANUP
 
-AT_SETUP([SSL db: implementation])
-AT_KEYWORDS([ovsdb server positive ssl $5])
-AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
-PKIDIR=$abs_top_builddir/tests
-AT_SKIP_IF([expr "$PKIDIR" : ".*[ 	'\"
+# CHECK_SSL_DB_IMPLEMENTATION(LOOPBACK_ADDR, IP_VERSION_TYPE)
+#
+# IP_VERSION_TYPE is used in AT_SETUP
+m4_define([CHECK_SSL_DB_IMPLEMENTATION],
+    [AT_SETUP([SSL db: implementation using $2])
+    AT_KEYWORDS([ovsdb server positive ssl $5])
+    AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
+    PKIDIR=$abs_top_builddir/tests
+    AT_SKIP_IF([expr "$PKIDIR" : ".*[   '\"
 \\]"])
-AT_DATA([schema],
-  [[{"name": "mydb",
-     "tables": {
-       "SSL": {
-         "columns": {
-           "private_key": {"type": "string"},
-           "certificate": {"type": "string"},
-           "ca_cert": {"type": "string"}}}}}
+    AT_DATA([schema],
+      [[{"name": "mydb",
+         "tables": {
+           "SSL": {
+             "columns": {
+               "private_key": {"type": "string"},
+               "certificate": {"type": "string"},
+               "ca_cert": {"type": "string"}}}}}
 ]])
-AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
-AT_CHECK(
-  [[ovsdb-tool transact db \
-     '["mydb",
-       {"op": "insert",
-        "table": "SSL",
-        "row": {"private_key": "'"$PKIDIR/testpki-privkey2.pem"'",
-                "certificate": "'"$PKIDIR/testpki-cert2.pem"'",
-                "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'"}}]']],
-  [0], [ignore], [ignore])
-OVS_LOGDIR=`pwd`; export OVS_LOGDIR
-AT_CHECK(
-  [ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid \
-        --private-key=db:mydb,SSL,private_key \
-        --certificate=db:mydb,SSL,certificate \
-        --ca-cert=db:mydb,SSL,ca_cert \
-        --remote=pssl:0:127.0.0.1 --unixctl="`pwd`"/unixctl db],
-  [0], [ignore], [ignore])
-SSL_PORT=`parse_listening_port < ovsdb-server.log`
-AT_CHECK(
-  [[ovsdb-client \
-        --private-key=$PKIDIR/testpki-privkey.pem \
-        --certificate=$PKIDIR/testpki-cert.pem \
-        --ca-cert=$PKIDIR/testpki-cacert.pem \
-        transact ssl:127.0.0.1:$SSL_PORT \
-        '["mydb",
-          {"op": "select",
-           "table": "SSL",
-           "where": [],
-           "columns": ["private_key"]}]']], 
-  [0], [stdout], [ignore], [test ! -e pid || kill `cat pid`])
-cat stdout >> output
-AT_CHECK_UNQUOTED(
-  [cat output], [0],
-  [[[{"rows":[{"private_key":"$PKIDIR/testpki-privkey2.pem"}]}]
+    AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
+    AT_CHECK(
+      [[ovsdb-tool transact db \
+         '["mydb",
+           {"op": "insert",
+            "table": "SSL",
+            "row": {"private_key": "'"$PKIDIR/testpki-privkey2.pem"'",
+                    "certificate": "'"$PKIDIR/testpki-cert2.pem"'",
+                    "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'"}}]']],
+      [0], [ignore], [ignore])
+    OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+    AT_CHECK(
+      [ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid \
+            --private-key=db:mydb,SSL,private_key \
+            --certificate=db:mydb,SSL,certificate \
+            --ca-cert=db:mydb,SSL,ca_cert \
+            --remote=pssl:0:$1 --unixctl="`pwd`"/unixctl db],
+      [0], [ignore], [ignore])
+    m4_if([$1], [127.0.0.1],
+      [SSL_PORT=`parse_listening_port < ovsdb-server.log`],
+      [SSL_PORT=`parse_ipv6_listening_port < ovsdb-server.log`])
+
+    AT_CHECK(
+      [[ovsdb-client \
+            --private-key=$PKIDIR/testpki-privkey.pem \
+            --certificate=$PKIDIR/testpki-cert.pem \
+            --ca-cert=$PKIDIR/testpki-cacert.pem \
+            transact ssl:$1:$SSL_PORT \
+            '["mydb",
+              {"op": "select",
+               "table": "SSL",
+               "where": [],
+               "columns": ["private_key"]}]']],
+      [0], [stdout], [ignore], [test ! -e pid || kill `cat pid`])
+    cat stdout >> output
+    AT_CHECK_UNQUOTED(
+      [cat output], [0],
+      [[[{"rows":[{"private_key":"$PKIDIR/testpki-privkey2.pem"}]}]
 ]], [ignore], [test ! -e pid || kill `cat pid`])
-OVSDB_SERVER_SHUTDOWN
-AT_CLEANUP
+    OVSDB_SERVER_SHUTDOWN
+    AT_CLEANUP])
+
+CHECK_SSL_DB_IMPLEMENTATION([127.0.0.1], [IPv4])
+CHECK_SSL_DB_IMPLEMENTATION([[::1]], [IPv6])
 
 AT_SETUP([compacting online])
 AT_KEYWORDS([ovsdb server compact])
@@ -748,7 +758,7 @@ for i in `seq 1 100`; do
 done
 AT_CLEANUP
 
-AT_BANNER([OVSDB -- ovsdb-server transactions (SSL sockets)])
+AT_BANNER([OVSDB -- ovsdb-server transactions (SSL IPv4 sockets)])
 
 # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
 #
@@ -764,7 +774,7 @@ AT_BANNER([OVSDB -- ovsdb-server transactions (SSL sockets)])
 # same marker.
 #
 # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
-m4_define([OVSDB_CHECK_EXECUTION], 
+m4_define([OVSDB_CHECK_EXECUTION],
   [AT_SETUP([$1])
    AT_KEYWORDS([ovsdb server positive ssl $5])
    AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
@@ -775,7 +785,7 @@ m4_define([OVSDB_CHECK_EXECUTION],
    AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
    AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
    SSL_PORT=`parse_listening_port < ovsdb-server.log`
-   m4_foreach([txn], [$3], 
+   m4_foreach([txn], [$3],
      [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:127.0.0.1:$SSL_PORT 'txn'], [0], [stdout], [ignore],
      [test ! -e pid || kill `cat pid`])
 cat stdout >> output
@@ -787,7 +797,46 @@ cat stdout >> output
 
 EXECUTION_EXAMPLES
 
-AT_BANNER([OVSDB -- ovsdb-server transactions (TCP sockets)])
+AT_BANNER([OVSDB -- ovsdb-server transactions (SSL IPv6 sockets)])
+
+# OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
+#
+# Creates a database with the given SCHEMA, starts an ovsdb-server on
+# that database, and runs each of the TRANSACTIONS (which should be a
+# quoted list of quoted strings) against it with ovsdb-client one at a
+# time.
+#
+# Checks that the overall output is OUTPUT, but UUIDs in the output
+# are replaced by markers of the form <N> where N is a number.  The
+# first unique UUID is replaced by <0>, the next by <1>, and so on.
+# If a given UUID appears more than once it is always replaced by the
+# same marker.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_EXECUTION],
+  [AT_SETUP([$1])
+   AT_KEYWORDS([ovsdb server positive ssl6 $5])
+   AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
+   OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+   OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+   $2 > schema
+   PKIDIR=$abs_top_builddir/tests
+   AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
+   AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:[::1] --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   SSL_PORT=`parse_ipv6_listening_port < ovsdb-server.log`
+   m4_foreach([txn], [$3],
+     [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:[::1]:$SSL_PORT 'txn'], [0], [stdout], [ignore],
+     [test ! -e pid || kill `cat pid`])
+cat stdout >> output
+])
+   AT_CHECK([${PERL} $srcdir/uuidfilt.pl output], [0], [$4], [ignore],
+            [test ! -e pid || kill `cat pid`])
+   OVSDB_SERVER_SHUTDOWN
+   AT_CLEANUP])
+
+EXECUTION_EXAMPLES
+
+AT_BANNER([OVSDB -- ovsdb-server transactions (TCP IPv4 sockets)])
 
 AT_SETUP([ovsdb-client get-schema-version - tcp socket])
 AT_KEYWORDS([ovsdb server positive tcp])
@@ -836,6 +885,56 @@ cat stdout >> output
    AT_CLEANUP])
 
 EXECUTION_EXAMPLES
+
+AT_BANNER([OVSDB -- ovsdb-server transactions (TCP IPv6 sockets)])
+
+AT_SETUP([ovsdb-client get-schema-version - tcp6 socket])
+AT_KEYWORDS([ovsdb server positive tcp6])
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=ptcp:0:[::1] db], [0], [ignore], [ignore])
+TCP_PORT=`parse_ipv6_listening_port < ovsdb-server.log`
+AT_CHECK([ovsdb-client get-schema-version tcp:[::1]:$TCP_PORT ordinals], [0], [5.1.3
+])
+OVSDB_SERVER_SHUTDOWN
+AT_CLEANUP])
+
+# OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
+#
+# Creates a database with the given SCHEMA, starts an ovsdb-server on
+# that database, and runs each of the TRANSACTIONS (which should be a
+# quoted list of quoted strings) against it with ovsdb-client one at a
+# time.
+#
+# Checks that the overall output is OUTPUT, but UUIDs in the output
+# are replaced by markers of the form <N> where N is a number.  The
+# first unique UUID is replaced by <0>, the next by <1>, and so on.
+# If a given UUID appears more than once it is always replaced by the
+# same marker.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_EXECUTION],
+  [AT_SETUP([$1])
+   AT_KEYWORDS([ovsdb server positive tcp6 $5])
+   OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+   OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+   $2 > schema
+   PKIDIR=$abs_top_builddir/tests
+   AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
+   AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid --remote=ptcp:0:[::1] --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   TCP_PORT=`parse_ipv6_listening_port < ovsdb-server.log`
+   m4_foreach([txn], [$3],
+     [AT_CHECK([ovsdb-client transact tcp:[::1]:$TCP_PORT 'txn'], [0], [stdout], [ignore],
+     [test ! -e pid || kill `cat pid`])
+cat stdout >> output
+])
+   AT_CHECK([${PERL} $srcdir/uuidfilt.pl output], [0], [$4], [ignore],
+            [test ! -e pid || kill `cat pid`])
+   OVSDB_SERVER_SHUTDOWN
+   AT_CLEANUP])
+
+EXECUTION_EXAMPLES
 
 AT_BANNER([OVSDB -- transactions on transient ovsdb-server])
 
@@ -856,13 +955,13 @@ AT_BANNER([OVSDB -- transactions on transient ovsdb-server])
 # same marker.
 #
 # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
-m4_define([OVSDB_CHECK_EXECUTION], 
+m4_define([OVSDB_CHECK_EXECUTION],
   [AT_SETUP([$1])
    AT_KEYWORDS([ovsdb server positive transient $5])
    OVS_RUNDIR=`pwd`; export OVS_RUNDIR
    $2 > schema
    AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
-   m4_foreach([txn], [$3], 
+   m4_foreach([txn], [$3],
      [AT_DATA([txnfile], [ovsdb-client transact unix:socket 'txn'
 ])
       AT_CHECK([ovsdb-server --remote=punix:socket --unixctl="`pwd`"/unixctl db --run="sh txnfile"], [0], [stdout], [ignore])
diff --git a/tests/test-sflow.c b/tests/test-sflow.c
index cba01b9..1ca44c3 100644
--- a/tests/test-sflow.c
+++ b/tests/test-sflow.c
@@ -325,12 +325,10 @@ process_datagram(struct sflow_xdr *x)
 
     /* Store the agent address as a string. */
     if (x->agentAddr.type == SFLOW_ADDRTYPE_IP6) {
+        ovs_be16 *addr = (ovs_be16 *) &x->agentAddr.a.ip6;
+
         snprintf(x->agentIPStr, SFLOW_MAX_AGENTIP_STRLEN,
-                 "%04x:%04x:%04x:%04x",
-                 x->agentAddr.a.ip6[0],
-                 x->agentAddr.a.ip6[1],
-                 x->agentAddr.a.ip6[2],
-                 x->agentAddr.a.ip6[3]);
+                 IP6_FMT, IP6_ARGS(addr));
     } else {
         snprintf(x->agentIPStr, SFLOW_MAX_AGENTIP_STRLEN,
                  IP_FMT, IP_ARGS(x->agentAddr.a.ip4));
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 6311ff3..0cf8c3a 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -468,11 +468,15 @@ collect_in_band_managers(const struct ovsrec_open_vswitch *ovs_cfg,
         managers = xmalloc(sset_count(&targets) * sizeof *managers);
         SSET_FOR_EACH (target, &targets) {
             struct sockaddr_in *sin = &managers[n_managers];
+            struct sockaddr_storage sstorage;
 
             if (stream_parse_target_with_default_port(target,
                                                       OVSDB_OLD_PORT,
-                                                      sin)) {
-                n_managers++;
+                                                      &sstorage)) {
+                if (sstorage.ss_family == AF_INET) {
+                    n_managers++;
+                    memcpy(sin, &sstorage, sizeof *sin);
+                }
             }
         }
     }
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 5fd82fc..6f6533d 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -2992,7 +2992,10 @@
           <dd>
             <p>The specified TCP <var>port</var> on the host at the
             given <var>ip</var>, which must be expressed as an IP
-            address (not a DNS name).</p>
+            address (not a DNS name), where <var>ip</var> can be
+            IPv4 or IPv6 address.  If <var>ip</var> is an IPv6 address,
+            then wrap <var>ip</var> with square brackets,
+            e.g.: tcp:[fc01::2]:6632.</p>
             <p>If <var>port</var> is not specified, it currently
             defaults to 6633.  In the future, the default will change to
             6653, which is the IANA-defined value.</p>
@@ -3009,7 +3012,11 @@
             <var>port</var>.  If <var>ip</var>, which must be expressed
             as an IP address (not a DNS name), is specified, then
             connections are restricted to the specified local IP
-            address.  The <ref table="Open_vSwitch" column="ssl"/>
+            address (either IPv4 or IPv6). If <var>ip</var> is an
+            IPv6 address, then wrap <var>ip</var> with square brackets,
+            e.g.: pssl:6632:[fc01::2].  If <var>ip</var> is not specified
+            then it listens only on IPv4 (but not IPv6) addresses.  The
+            <ref table="Open_vSwitch" column="ssl"/>
             column in the <ref table="Open_vSwitch"/> table must point
             to a valid SSL configuration when this form is used.</p>
             <p>If <var>port</var> is not specified, it currently
@@ -3024,7 +3031,10 @@
             <var>port</var>.  If <var>ip</var>, which must be expressed
             as an IP address (not a DNS name), is specified, then
             connections are restricted to the specified local IP
-            address.</p>
+            address (either IPv4 or IPv6).  If <var>ip</var> is an
+            IPv6 address, then wrap <var>ip</var> with square brackets,
+            e.g.: ptcp:6632:[fc01::2].  If <var>ip</var> is not specified
+            then it listens only on IPv4 (but not IPv6) addresses. </p>
             <p>If <var>port</var> is not specified, it currently
             defaults to 6633.  In the future, the default will change to
             6653, which is the IANA-defined value.</p>
@@ -3322,7 +3332,9 @@
             <p>
               The specified TCP <var>port</var> on the host at the given
               <var>ip</var>, which must be expressed as an IP address
-              (not a DNS name).
+              (not a DNS name), where <var>ip</var> can be IPv4 or IPv6 address.
+              If <var>ip</var> is an IPv6 address, then wrap <var>ip</var>
+              with square brackets, e.g.: tcp:[fc01::2]:6632.
             </p>
             <p>
               If <var>port</var> is not specified, it currently defaults
@@ -3338,7 +3350,11 @@
               the kernel automatically choose an available port.  If
               <var>ip</var>, which must be expressed as an IP address
               (not a DNS name), is specified, then connections are
-              restricted to the specified local IP address.  The <ref
+              restricted to the specified local IP address (either IPv4
+              or IPv6 address).  If <var>ip</var> is an IPv6 address,
+              then wrap <var>ip</var> with square brackets,
+              e.g.: pssl:6632:[fc01::2].  If <var>ip</var> is not specified
+              then it listens only on IPv4 (but not IPv6) addresses.  The <ref
               table="Open_vSwitch" column="ssl"/> column in the <ref
               table="Open_vSwitch"/> table must point to a valid SSL
               configuration when this form is used.
@@ -3361,7 +3377,11 @@
               the kernel automatically choose an available port.  If
               <var>ip</var>, which must be expressed as an IP address
               (not a DNS name), is specified, then connections are
-              restricted to the specified local IP address.
+              restricted to the specified local IP address (either IPv4
+              or IPv6 address).  If <var>ip</var> is an IPv6 address,
+              then wrap <var>ip</var> with square brackets,
+              e.g.: ptcp:6632:[fc01::2].  If <var>ip</var> is not specified
+              then it listens only on IPv4 (but not IPv6) addresses.
             </p>
             <p>
               If <var>port</var> is not specified, it currently defaults
-- 
1.7.10.4




More information about the dev mailing list