From e731d71bf47b8370e4bfa87827113eedd20b7398 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Thu, 6 Feb 2014 16:04:05 -0800 Subject: [PATCH] Add IPv6 support for OpenFlow, OVSDB, NetFlow, and sFlow. Does not add IPv6 support for in-band control. Co-authored-by: Ben Pfaff Signed-off-by: Nandan Nivgune Signed-off-by: Abhijit Bhopatkar Signed-off-by: Arun Sharma Signed-off-by: Ben Pfaff --- NEWS | 1 + include/sparse/netinet/in.h | 12 +- lib/socket-util.c | 348 +++++++++++++++++++++----------- lib/socket-util.h | 18 +- lib/stream-fd.c | 11 +- lib/stream-fd.h | 8 +- lib/stream-ssl.c | 36 ++-- lib/stream-tcp.c | 46 +++-- lib/stream-unix.c | 12 +- lib/stream.c | 8 +- lib/stream.h | 2 +- lib/vconn-active.man | 7 +- lib/vconn-passive.man | 16 +- ofproto/connmgr.c | 16 +- ofproto/ofproto-dpif-sflow.c | 15 +- ovsdb/remote-active.man | 10 +- ovsdb/remote-passive.man | 17 +- python/ovs/socket_util.py | 38 +++- tests/ofproto-dpif.at | 378 +++++++++++++++++++---------------- tests/ofproto-macros.at | 4 +- tests/ovsdb-execution.at | 5 +- tests/ovsdb-idl.at | 25 ++- tests/ovsdb-server.at | 79 +++++++- tests/test-sflow.c | 17 +- vswitchd/bridge.c | 10 +- vswitchd/vswitch.xml | 115 +++++++---- 26 files changed, 792 insertions(+), 462 deletions(-) diff --git a/NEWS b/NEWS index 8689f5c14..fe4b8cf2e 100644 --- a/NEWS +++ b/NEWS @@ -64,6 +64,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 781358bd3..a2204282d 100644 --- a/include/sparse/netinet/in.h +++ b/include/sparse/netinet/in.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013 Nicira, Inc. + * Copyright (c) 2011, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,15 @@ struct in6_addr { extern const struct in6_addr in6addr_any; +/* Ditto, for IPv6. */ +struct sockaddr_in6 { + sa_family_t sin6_family; + 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 @@ -84,6 +93,7 @@ extern const struct in6_addr in6addr_any; #define INADDR_ANY 0x00000000 #define INADDR_BROADCAST 0xffffffff +#define INADDR_LOOPBACK 0x7f000001 #define INADDR_NONE 0xffffffff #define INET6_ADDRSTRLEN 46 diff --git a/lib/socket-util.c b/lib/socket-util.c index 24fc6fe60..f5d313748 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -612,59 +612,125 @@ guess_netmask(ovs_be32 ip_) : htonl(0)); /* ??? */ } -/* Parses 'target', which should be a string in the format "[:]". - * is required. If 'default_port' is nonzero then is optional - * and defaults to 'default_port'. +/* This is like strsep() except: * - * On success, returns true and stores the parsed remote address into '*sinp'. - * On failure, logs an error, stores zeros into '*sinp', and returns false. */ -bool -inet_parse_active(const char *target_, uint16_t default_port, - struct sockaddr_in *sinp) + * - The separator string is ":". + * + * - Square brackets [] quote ":" separators and are removed from the + * tokens. */ +static char * +parse_bracketed_token(char **pp) { - char *target = xstrdup(target_); - char *save_ptr = NULL; - const char *host_name; - const char *port_string; - 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); - if (!host_name) { - VLOG_ERR("%s: bad peer name format", target_); - goto exit; + char *p = *pp; + + if (p == NULL) { + return NULL; + } else if (*p == '\0') { + *pp = NULL; + return p; + } else if (*p == '[') { + char *start = p + 1; + char *end = start + strcspn(start, "]"); + *pp = (*end == '\0' ? NULL + : end[1] == ':' ? end + 2 + : end + 1); + *end = '\0'; + return start; + } else { + char *start = p; + char *end = start + strcspn(start, ":"); + *pp = *end == '\0' ? NULL : end + 1; + *end = '\0'; + return start; } +} - /* Look up IP, port. */ - if (lookup_ip(host_name, &sinp->sin_addr)) { - goto exit; +static bool +parse_sockaddr_components(struct sockaddr_storage *ss, + const char *host_s, + const char *port_s, uint16_t default_port, + const char *s) +{ + struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, ss); + int port; + + if (port_s && port_s[0]) { + if (!str_to_int(port_s, 10, &port) || port < 0 || port > 65535) { + VLOG_ERR("%s: bad port number \"%s\"", s, port_s); + } + } else { + port = default_port; } - 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_); - goto exit; + + memset(ss, 0, sizeof *ss); + if (strchr(host_s, ':')) { + struct sockaddr_in6 *sin6 + = ALIGNED_CAST(struct sockaddr_in6 *, ss); + + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(port); + if (!inet_pton(AF_INET6, host_s, sin6->sin6_addr.s6_addr)) { + VLOG_ERR("%s: bad IPv6 address \"%s\"", s, host_s); + goto exit; + } + } else { + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + if (!inet_pton(AF_INET, host_s, &sin->sin_addr.s_addr)) { + VLOG_ERR("%s: bad IPv4 address \"%s\"", s, host_s); + goto exit; + } } - ok = true; + return true; exit: + memset(ss, 0, sizeof *ss); + return false; +} + +/* Parses 'target', which should be a string in the format "[:]". + * , which is required, may be an IPv4 address or an IPv6 address + * enclosed in square brackets. If 'default_port' is nonzero then is + * optional and defaults to 'default_port'. + * + * On success, returns true and stores the parsed remote address into '*ss'. + * On failure, logs an error, stores zeros into '*ss', and returns false. */ +bool +inet_parse_active(const char *target_, uint16_t default_port, + struct sockaddr_storage *ss) +{ + char *target = xstrdup(target_); + const char *port; + const char *host; + char *p; + bool ok; + + p = target; + host = parse_bracketed_token(&p); + port = parse_bracketed_token(&p); + if (!host) { + VLOG_ERR("%s: host must be specified", target_); + ok = false; + } else if (!port && !default_port) { + VLOG_ERR("%s: port must be specified", target_); + ok = false; + } else { + ok = parse_sockaddr_components(ss, host, port, default_port, target_); + } if (!ok) { - memset(sinp, 0, sizeof *sinp); + memset(ss, 0, sizeof *ss); } free(target); return ok; } -/* Opens a non-blocking IPv4 socket of the specified 'style' and connects to - * 'target', which should be a string in the format "[:]". - * is required. If 'default_port' is nonzero then 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 + * "[:]". , which is required, may be an IPv4 address or an + * IPv6 address enclosed in square brackets. If 'default_port' is nonzero then + * is optional and defaults to 'default_port'. * * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP). * @@ -673,28 +739,27 @@ 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 'ss' is non-null, then on success stores the target address into '*ss'. * * '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 ss; int fd = -1; int error; /* Parse. */ - if (!inet_parse_active(target, default_port, &sin)) { + if (!inet_parse_active(target, default_port, &ss)) { error = EAFNOSUPPORT; goto exit; } /* Create non-blocking socket. */ - fd = socket(AF_INET, style, 0); + fd = socket(ss.ss_family, style, 0); if (fd < 0) { VLOG_ERR("%s: socket: %s", target, ovs_strerror(errno)); error = errno; @@ -705,8 +770,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) { @@ -715,25 +780,32 @@ inet_open_active(int style, const char *target, uint16_t default_port, } /* Connect. */ - error = connect(fd, (struct sockaddr *) &sin, sizeof sin) == 0 ? 0 : errno; + error = connect(fd, (struct sockaddr *) &ss, ss_length(&ss)) == 0 + ? 0 + : errno; if (error == EINPROGRESS) { error = EAGAIN; } exit: - if (!error || error == EAGAIN) { - if (sinp) { - *sinp = sin; + if (error && error != EAGAIN) { + if (ssp) { + memset(ssp, 0, sizeof *ssp); + } + if (fd >= 0) { + close(fd); + fd = -1; + } + } else { + if (ssp) { + *ssp = ss; } - } else if (fd >= 0) { - close(fd); - fd = -1; } *fdp = fd; return error; } -/* Parses 'target', which should be a string in the format "[][:]": +/* Parses 'target', which should be a string in the format "[][:]": * * - If 'default_port' is -1, then is required. Otherwise, if * is omitted, then 'default_port' is used instead. @@ -741,54 +813,41 @@ exit: * - If (or 'default_port', if used) is 0, then no port is bound * and the TCP/IP stack will select a port. * - * - If is omitted then the IP address is wildcarded. + * - is optional. If supplied, it may be an IPv4 address or an + * IPv6 address enclosed in square brackets. If omitted, 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 '*ss' and returns true; otherwise + * zeros '*ss' and returns false. */ bool inet_parse_passive(const char *target_, int default_port, - struct sockaddr_in *sinp) + struct sockaddr_storage *ss) { char *target = xstrdup(target_); - char *string_ptr = target; - const char *host_name; - const char *port_string; - 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); - - /* 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; - } - - /* Parse optional bind IP. */ - host_name = strsep(&string_ptr, ":"); - if (host_name && host_name[0] && lookup_ip(host_name, &sinp->sin_addr)) { - goto exit; + const char *port; + const char *host; + char *p; + bool ok; + + p = target; + port = parse_bracketed_token(&p); + host = parse_bracketed_token(&p); + if (!port && default_port < 0) { + VLOG_ERR("%s: port must be specified", target_); + ok = false; + } else { + ok = parse_sockaddr_components(ss, host ? host : "0.0.0.0", + port, default_port, target_); } - - ok = true; - -exit: if (!ok) { - memset(sinp, 0, sizeof *sinp); + memset(ss, 0, sizeof *ss); } 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 +858,27 @@ 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 'ss' is non-null, then on success stores the bound address into '*ss'. * * '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; int fd = 0, error; unsigned int yes = 1; - if (!inet_parse_passive(target, default_port, &sin)) { + if (!inet_parse_passive(target, default_port, &ss)) { return -EAFNOSUPPORT; } + kernel_chooses_port = ss_get_port(&ss) == 0; /* 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)); @@ -838,7 +897,7 @@ inet_open_passive(int style, const char *target, int default_port, } /* Bind. */ - if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) { + if (bind(fd, (struct sockaddr *) &ss, ss_length(&ss)) < 0) { error = errno; VLOG_ERR("%s: bind: %s", target, ovs_strerror(error)); goto error; @@ -860,31 +919,28 @@ 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 ss_len = sizeof ss; + if (getsockname(fd, (struct sockaddr *) &ss, &ss_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 (kernel_chooses_port) { VLOG_INFO("%s: listening on port %"PRIu16, - target, ntohs(sin.sin_port)); + target, ss_get_port(&ss)); + } + if (ssp) { + *ssp = ss; } } return fd; error: + if (ssp) { + memset(ssp, 0, sizeof *ssp); + } close(fd); return -error; } @@ -1060,12 +1116,12 @@ describe_sockaddr(struct ds *string, int fd, socklen_t len = sizeof ss; if (!getaddr(fd, (struct sockaddr *) &ss, &len)) { - if (ss.ss_family == AF_INET) { - struct sockaddr_in sin; + if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) { + char addrbuf[SS_NTOP_BUFSIZE]; - memcpy(&sin, &ss, sizeof sin); - ds_put_format(string, IP_FMT":%"PRIu16, - IP_ARGS(sin.sin_addr.s_addr), ntohs(sin.sin_port)); + ds_put_format(string, "%s:%"PRIu16, + ss_format_address(&ss, addrbuf, sizeof addrbuf), + ss_get_port(&ss)); } else if (ss.ss_family == AF_UNIX) { struct sockaddr_un sun; const char *null; @@ -1225,4 +1281,66 @@ af_inet_ifreq_ioctl(const char *name, struct ifreq *ifr, unsigned long int cmd, } return error; } + +/* sockaddr_storage helpers. */ +/* Returns the IPv4 or IPv6 port in 'ss'. */ +uint16_t +ss_get_port(const struct sockaddr_storage *ss) +{ + if (ss->ss_family == AF_INET) { + const struct sockaddr_in *sin + = ALIGNED_CAST(const struct sockaddr_in *, ss); + return ntohs(sin->sin_port); + } else if (ss->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6 + = ALIGNED_CAST(const struct sockaddr_in6 *, ss); + return ntohs(sin6->sin6_port); + } else { + OVS_NOT_REACHED(); + } +} + +/* Formats the IPv4 or IPv6 address in 'ss' into the 'bufsize' bytes in 'buf'. + * If 'ss' is an IPv6 address, puts square brackets around the address. + * 'bufsize' should be at least SS_NTOP_BUFSIZE. + * + * Returns 'buf'. */ +char * +ss_format_address(const struct sockaddr_storage *ss, + char *buf, size_t bufsize) +{ + ovs_assert(bufsize >= SS_NTOP_BUFSIZE); + if (ss->ss_family == AF_INET) { + const struct sockaddr_in *sin + = ALIGNED_CAST(const struct sockaddr_in *, ss); + + snprintf(buf, bufsize, IP_FMT, IP_ARGS(sin->sin_addr.s_addr)); + } else if (ss->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6 + = ALIGNED_CAST(const struct sockaddr_in6 *, ss); + + buf[0] = '['; + inet_ntop(AF_INET6, sin6->sin6_addr.s6_addr, buf + 1, bufsize - 1); + strcpy(strchr(buf, '\0'), "]"); + } else { + OVS_NOT_REACHED(); + } + + return buf; +} + +size_t +ss_length(const struct sockaddr_storage *ss) +{ + switch (ss->ss_family) { + case AF_INET: + return sizeof(struct sockaddr_in); + + case AF_INET6: + return sizeof(struct sockaddr_in6); + + default: + OVS_NOT_REACHED(); + } +} diff --git a/lib/socket-util.h b/lib/socket-util.h index d5b44b01c..b54d1a1f7 100644 --- a/lib/socket-util.h +++ b/lib/socket-util.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,14 +48,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); @@ -79,4 +79,12 @@ int af_inet_ioctl(unsigned long int command, const void *arg); int af_inet_ifreq_ioctl(const char *name, struct ifreq *, unsigned long int cmd, const char *cmd_name); +/* Functions for working with sockaddr_storage that might contain an IPv4 or + * IPv6 address. */ +uint16_t ss_get_port(const struct sockaddr_storage *); +#define SS_NTOP_BUFSIZE (1 + INET6_ADDRSTRLEN + 1) +char *ss_format_address(const struct sockaddr_storage *, + char *buf, size_t bufsize); +size_t ss_length(const struct sockaddr_storage *); + #endif /* socket-util.h */ diff --git a/lib/stream-fd.c b/lib/stream-fd.c index 81c5a43ae..8062d2dea 100644 --- a/lib/stream-fd.c +++ b/lib/stream-fd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2012, 2013 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -148,7 +148,7 @@ struct fd_pstream { struct pstream pstream; int fd; - int (*accept_cb)(int fd, const struct sockaddr *, size_t sa_len, + int (*accept_cb)(int fd, const struct sockaddr_storage *, size_t ss_len, struct stream **); int (*set_dscp_cb)(int fd, uint8_t dscp); char *unlink_path; @@ -179,8 +179,8 @@ fd_pstream_cast(struct pstream *pstream) * implementation never fails.) */ int new_fd_pstream(const char *name, int fd, - int (*accept_cb)(int fd, const struct sockaddr *sa, - size_t sa_len, struct stream **streamp), + int (*accept_cb)(int fd, const struct sockaddr_storage *ss, + size_t ss_len, struct stream **streamp), int (*set_dscp_cb)(int fd, uint8_t dscp), char *unlink_path, struct pstream **pstreamp) { @@ -227,8 +227,7 @@ pfd_accept(struct pstream *pstream, struct stream **new_streamp) return retval; } - return ps->accept_cb(new_fd, (const struct sockaddr *) &ss, ss_len, - new_streamp); + return ps->accept_cb(new_fd, &ss, ss_len, new_streamp); } static void diff --git a/lib/stream-fd.h b/lib/stream-fd.h index 8b8b48f34..8f595a908 100644 --- a/lib/stream-fd.h +++ b/lib/stream-fd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2012 Nicira, Inc. + * Copyright (c) 2008, 2009, 2012, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,13 +23,13 @@ struct stream; struct pstream; -struct sockaddr; +struct sockaddr_storage; int new_fd_stream(const char *name, int fd, int connect_status, struct stream **streamp); int new_fd_pstream(const char *name, int fd, - int (*accept_cb)(int fd, const struct sockaddr *, - size_t sa_len, struct stream **), + int (*accept_cb)(int fd, const struct sockaddr_storage *ss, + size_t ss_len, struct stream **), int (*set_dscp_cb)(int fd, uint8_t dscp), char *unlink_path, struct pstream **pstreamp); diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c index 2ed52827f..a6f1362e3 100644 --- a/lib/stream-ssl.c +++ b/lib/stream-ssl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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; @@ -780,9 +780,11 @@ static int pssl_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp, uint8_t dscp) { + char bound_name[SS_NTOP_BUFSIZE + 16]; + char addrbuf[SS_NTOP_BUFSIZE]; + struct sockaddr_storage ss; struct pssl_pstream *pssl; - struct sockaddr_in sin; - char bound_name[128]; + uint16_t port; int retval; int fd; @@ -791,16 +793,18 @@ 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)); + + port = ss_get_port(&ss); + snprintf(bound_name, sizeof bound_name, "ptcp:%"PRIu16":%s", + port, ss_format_address(&ss, addrbuf, sizeof addrbuf)); 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, htons(port)); pssl->fd = fd; *pstreamp = &pssl->pstream; return 0; @@ -818,13 +822,14 @@ 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; - char name[128]; + char name[SS_NTOP_BUFSIZE + 16]; + char addrbuf[SS_NTOP_BUFSIZE]; + struct sockaddr_storage ss; + socklen_t ss_len = sizeof ss; int new_fd; int error; - new_fd = accept(pssl->fd, (struct sockaddr *) &sin, &sin_len); + new_fd = accept(pssl->fd, (struct sockaddr *) &ss, &ss_len); if (new_fd < 0) { error = errno; if (error != EAGAIN) { @@ -839,10 +844,9 @@ 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)); - } + snprintf(name, sizeof name, "tcp:%s:%"PRIu16, + ss_format_address(&ss, addrbuf, sizeof addrbuf), + ss_get_port(&ss)); 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 b3237d618..55e581878 100644 --- a/lib/stream-tcp.c +++ b/lib/stream-tcp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2012, 2013 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +21,12 @@ #include #include #include +#include #include #include #include #include +#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); } @@ -90,47 +92,47 @@ const struct stream_class tcp_stream_class = { /* Passive TCP. */ -static int ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len, - struct stream **streamp); +static int ptcp_accept(int fd, const struct sockaddr_storage *, + size_t, struct stream **streamp); static int ptcp_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp, uint8_t dscp) { - struct sockaddr_in sin; - char bound_name[128]; + char bound_name[SS_NTOP_BUFSIZE + 16]; + char addrbuf[SS_NTOP_BUFSIZE]; + struct sockaddr_storage ss; + uint16_t port; 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)); + port = ss_get_port(&ss); + snprintf(bound_name, sizeof bound_name, "ptcp:%"PRIu16":%s", + port, ss_format_address(&ss, addrbuf, sizeof addrbuf)); + 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, htons(port)); } return error; } static int -ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len, - struct stream **streamp) +ptcp_accept(int fd, const struct sockaddr_storage *ss, + size_t ss_len OVS_UNUSED, struct stream **streamp) { - const struct sockaddr_in *sin = ALIGNED_CAST(const struct sockaddr_in *, - sa); - char name[128]; + char name[SS_NTOP_BUFSIZE + 16]; + char addrbuf[SS_NTOP_BUFSIZE]; - 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 { - strcpy(name, "tcp"); - } + snprintf(name, sizeof name, "tcp:%s:%"PRIu16, + ss_format_address(ss, addrbuf, sizeof addrbuf), + ss_get_port(ss)); return new_tcp_stream(name, fd, 0, streamp); } diff --git a/lib/stream-unix.c b/lib/stream-unix.c index e4b7e779e..b3d70b659 100644 --- a/lib/stream-unix.c +++ b/lib/stream-unix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,8 +75,8 @@ const struct stream_class unix_stream_class = { /* Passive UNIX socket. */ -static int punix_accept(int fd, const struct sockaddr *sa, size_t sa_len, - struct stream **streamp); +static int punix_accept(int fd, const struct sockaddr_storage *ss, + size_t ss_len, struct stream **streamp); static int punix_open(const char *name OVS_UNUSED, char *suffix, @@ -105,11 +105,11 @@ punix_open(const char *name OVS_UNUSED, char *suffix, } static int -punix_accept(int fd, const struct sockaddr *sa, size_t sa_len, +punix_accept(int fd, const struct sockaddr_storage *ss, size_t ss_len, struct stream **streamp) { - const struct sockaddr_un *sun = (const struct sockaddr_un *) sa; - int name_len = get_unix_name_len(sa_len); + const struct sockaddr_un *sun = (const struct sockaddr_un *) ss; + int name_len = get_unix_name_len(ss_len); char name[128]; if (name_len > 0) { diff --git a/lib/stream.c b/lib/stream.c index 354245574..b69f03cba 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 d966cde81..3780ff98f 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 bf7aaf7b5..c1e9281e0 100644 --- a/lib/vconn-active.man +++ b/lib/vconn-active.man @@ -1,9 +1,10 @@ .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. Wrap IPv6 addresses in square brackets, +e.g. \fBtcp:[::1]:6633\fR. 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 a9efdb3bd..0cf8fc1d6 100644 --- a/lib/vconn-passive.man +++ b/lib/vconn-passive.man @@ -1,14 +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. +Listens for OpenFlow connections on \fIport\fR. The default +\fIport\fR is 6633, but a future version of Open vSwitch will change +the default to the IANA-defined port 6653. By default, connections +are allowed from any IPv4 address. Specify \fIip\fR as an IPv4 +address or a bracketed IPv6 address (e.g. \fBptcp:6633:[::1]\fR). DNS +names may not be used. 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 -value. . .IP "\fBpunix:\fIfile\fR" Listens for OpenFlow connections on the Unix domain server socket diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c index b7c16d2b5..a58e785a2 100644 --- a/ofproto/connmgr.c +++ b/ofproto/connmgr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -734,17 +734,13 @@ 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]; const char *target = rconn_get_target(ofconn->rconn); + struct sockaddr_storage ss; - if (ofconn->band == OFPROTO_OUT_OF_BAND) { - continue; - } - - if (stream_parse_target_with_default_port(target, - OFP_OLD_PORT, - sin)) { - n_addrs++; + if (ofconn->band == OFPROTO_IN_BAND + && stream_parse_target_with_default_port(target, OFP_OLD_PORT, &ss) + && ss.ss_family == AF_INET) { + addrs[n_addrs++] = *(struct sockaddr_in *) &ss; } } for (i = 0; i < mgr->n_extra_remotes; i++) { diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index e9ef434dd..ff07e70cf 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * Copyright (c) 2009 InMon Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -253,13 +253,16 @@ sflow_choose_agent_address(const char *agent_device, } SSET_FOR_EACH (target, targets) { - struct sockaddr_in sin; + struct sockaddr_storage ss; 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) + && ss.ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) &ss; + if (route_table_get_name(sin->sin_addr.s_addr, name) + && !netdev_get_in4_by_name(name, &in4)) { + goto success; + } } } diff --git a/ovsdb/remote-active.man b/ovsdb/remote-active.man index a934cf0ef..419b1c835 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:[::1]:6632\fR. +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:[::1]:6632\fR. . .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 4cbc00712..200651ba6 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:6632:[::1]\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:6632:[::1]\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 c65704762..be9fc95a0 100644 --- a/python/ovs/socket_util.py +++ b/python/ovs/socket_util.py @@ -1,4 +1,4 @@ -# Copyright (c) 2010, 2012 Nicira, Inc. +# Copyright (c) 2010, 2012, 2014 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -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]).lstrip('[').rstrip(']') + 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 1c1f02935..7a936a218 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -1829,55 +1829,58 @@ 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. - -dnl because packets from different ports can be handled by separate -dnl threads, put some sleeps - -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)' -sleep 1 -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)' -sleep 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=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' -sleep 1 -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, ADDR_WITHOUT_BRACKETS) +# +# 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]) + 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=\"$1:$SFLOW_PORT\" \ + header=128 sampling=1 polling=1 agent=lo + + 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. + dnl because packets from different ports can be handled by separate + dnl threads, put some sleeps + + 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)' + sleep 1 + 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)' + sleep 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=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' + sleep 1 + 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/ /\ + AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'HEADER|ERROR' | sed 's/ /\ /g']], [0], [dnl HEADER dgramSeqNo=1 @@ -1981,7 +1984,7 @@ HEADER 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/ /\ + AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'IFCOUNTERS|ERROR' | head -6 | sed 's/ /\ /g']], [0], [dnl IFCOUNTERS dgramSeqNo=2 @@ -2122,128 +2125,144 @@ IFCOUNTERS 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) + AT_CLEANUP]) -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)' +CHECK_SFLOW_SAMPLING_PACKET([127.0.0.1], [IPv4]) +CHECK_SFLOW_SAMPLING_PACKET([[[::1]]], [IPv6]) - ovs-appctl time/warp $delay -done - -ovs-appctl time/warp 6000 -sleep 1 -OVS_VSWITCHD_STOP -ovs-appctl -t test-netflow exit +# 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]) + NETFLOW_PORT=`parse_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 -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]) + 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 > 2, 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 > 65535, 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_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]) -AT_CLEANUP + 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]) -dnl Test that basic NetFlow reports active expirations correctly. -AT_SETUP([ofproto-dpif - NetFlow active expiration]) + AT_CLEANUP]) -OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone]) -ADD_OF_PORTS([br0], 1, 2) +CHECK_NETFLOW_EXPIRATION([127.0.0.1], [IPv4]) +CHECK_NETFLOW_EXPIRATION([[[::1]]], [IPv6]) -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` +# 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-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 + OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone]) + ADD_OF_PORTS([br0], 1, 2) -AT_CHECK([ovs-appctl time/stop]) -n=1 -while test $n -le 60; do - n=`expr $n + 1` + 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]) + NETFLOW_PORT=`parse_listening_port < test-netflow.log` - 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-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 - ovs-appctl time/warp 1000 -done + AT_CHECK([ovs-appctl time/stop]) + n=1 + while test $n -le 60; do + n=`expr $n + 1` -ovs-appctl time/warp 10000 + 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)' -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 -]) + 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 @@ -2700,30 +2719,37 @@ 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)']) -sleep 1 -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]) + NETFLOW_PORT=`parse_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)']) + sleep 1 + 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: 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: ]) -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 3bcffc252..a82a9b121 100644 --- a/tests/ofproto-macros.at +++ b/tests/ofproto-macros.at @@ -21,7 +21,7 @@ s/ hard_age=[0-9]*,// # 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:127.0.0.1 or -# pssl:0:127.0.0.1. +# pssl:0:127.0.0.1, or the equivalent with [::1] instead of 127.0.0.1. # # Here's an example of how to use this with ovsdb-server: # @@ -29,7 +29,7 @@ s/ hard_age=[0-9]*,// # ovsdb-server --log-file --remote=ptcp:0:127.0.0.1 ... # 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' + sed -n 's/.*0:.*: listening on port \([0-9]*\)$/\1/p' }] m4_divert_pop([PREPARE_TESTS]) diff --git a/tests/ovsdb-execution.at b/tests/ovsdb-execution.at index d014804a6..949ed5982 100644 --- a/tests/ovsdb-execution.at +++ b/tests/ovsdb-execution.at @@ -177,7 +177,7 @@ OVSDB_CHECK_EXECUTION([duplicate uuid-name not allowed], [[[{"uuid":["uuid","<0>"]},{"details":"This \"uuid-name\" appeared on an earlier \"insert\" operation.","error":"duplicate uuid-name","syntax":"\"x\""}] ]]) -m4_define([EXECUTION_EXAMPLES], [ +m4_define([ONE_EXECUTION_EXAMPLE], [dnl dnl At one point the "commit" code ignored new rows with all-default values, dnl so this checks for that problem. OVSDB_CHECK_EXECUTION([insert default row, query table], @@ -193,7 +193,10 @@ OVSDB_CHECK_EXECUTION([insert default row, query table], [[[{"uuid":["uuid","<0>"]}] [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"","number":0}]}] ]]) +]) +m4_define([EXECUTION_EXAMPLES], [ +ONE_EXECUTION_EXAMPLE OVSDB_CHECK_EXECUTION([insert row, query table], [ordinal_schema], [[[["ordinals", diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at index 9ff7b1bd8..0c6d7df16 100644 --- a/tests/ovsdb-idl.at +++ b/tests/ovsdb-idl.at @@ -72,10 +72,33 @@ 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_listening_port < ovsdb-server.log` + echo "TCP_PORT=$TCP_PORT" + + m4_if([$2], [], [], + [AT_CHECK([ovsdb-client transact "tcp:[[::1]]:$TCP_PORT" $2], [0], [ignore], [ignore], [kill `cat pid`])]) + AT_CHECK([strace $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 a2c53f3a6..b01743df8 100644 --- a/tests/ovsdb-server.at +++ b/tests/ovsdb-server.at @@ -748,7 +748,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]) # @@ -787,7 +787,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 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_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]) + +ONE_EXECUTION_EXAMPLE + +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 +875,42 @@ cat stdout >> output AT_CLEANUP]) EXECUTION_EXAMPLES + +# 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 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_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]) + +ONE_EXECUTION_EXAMPLE AT_BANNER([OVSDB -- transactions on transient ovsdb-server]) diff --git a/tests/test-sflow.c b/tests/test-sflow.c index cba01b9e6..deebd8268 100644 --- a/tests/test-sflow.c +++ b/tests/test-sflow.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc. * Copyright (c) 2013 InMon Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,7 +44,6 @@ static unixctl_cb_func test_sflow_exit; /* Datagram. */ #define SFLOW_VERSION_5 5 #define SFLOW_MIN_LEN 36 -#define SFLOW_MAX_AGENTIP_STRLEN 64 /* Sample tag numbers. */ #define SFLOW_FLOW_SAMPLE 1 @@ -82,7 +81,7 @@ struct sflow_xdr { /* Agent. */ struct sflow_addr agentAddr; - char agentIPStr[SFLOW_MAX_AGENTIP_STRLEN]; + char agentIPStr[INET6_ADDRSTRLEN + 2]; uint32_t subAgentId; uint32_t uptime_mS; @@ -325,14 +324,12 @@ process_datagram(struct sflow_xdr *x) /* Store the agent address as a string. */ if (x->agentAddr.type == SFLOW_ADDRTYPE_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]); + char ipstr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, (const void *) &x->agentAddr.a.ip6, + ipstr, INET6_ADDRSTRLEN); + snprintf(x->agentIPStr, sizeof x->agentIPStr, "[%s]", ipstr); } else { - snprintf(x->agentIPStr, SFLOW_MAX_AGENTIP_STRLEN, + snprintf(x->agentIPStr, sizeof x->agentIPStr, IP_FMT, IP_ARGS(x->agentAddr.a.ip4)); } diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 5b1aec373..cde4bd059 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -466,12 +466,12 @@ 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 ss; - if (stream_parse_target_with_default_port(target, - OVSDB_OLD_PORT, - sin)) { - n_managers++; + if (stream_parse_target_with_default_port(target, OVSDB_OLD_PORT, + &ss) + && ss.ss_family == AF_INET) { + managers[n_managers++] = *(struct sockaddr_in *) &ss; } } } diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 797f330f5..e915cafdf 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -3000,12 +3000,18 @@
tcp:ip[:port]
-

The specified TCP port on the host at the - given ip, which must be expressed as an IP - address (not a DNS name).

-

If port is not specified, it currently - defaults to 6633. In the future, the default will change to - 6653, which is the IANA-defined value.

+

+ The specified TCP port on the host at the given + ip, which must be expressed as an IP address (not a + DNS name), where ip can be IPv4 or IPv6 address. If + ip is an IPv6 address, wrap it in square brackets, + e.g. tcp:[::1]:6632. +

+

+ If port is not specified, it currently defaults to + 6633. In the future, the default will change to 6653, which is + the IANA-defined value. +

@@ -3015,29 +3021,48 @@

pssl:[port][:ip]
-

Listens for SSL connections on the specified TCP - port. If ip, 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 - column in the table must point - to a valid SSL configuration when this form is used.

-

If port is not specified, it currently - defaults to 6633. In the future, the default will change to - 6653, which is the IANA-defined value.

-

SSL support is an optional feature that is not always built as - part of Open vSwitch.

+

+ Listens for SSL connections on the specified TCP port. + If ip, which must be expressed as an IP address (not a + DNS name), is specified, then connections are restricted to the + specified local IP address (either IPv4 or IPv6). If + ip is an IPv6 address, wrap it in square brackets, + e.g. pssl:6632:[::1]. +

+

+ If port is not specified, it currently defaults to + 6633. If ip is not specified then it listens only on + IPv4 (but not IPv6) addresses. The + + column in the table must point to a + valid SSL configuration when this form is used. +

+

+ If port is not specified, it currently defaults to + 6633. In the future, the default will change to 6653, which is + the IANA-defined value. +

+

+ SSL support is an optional feature that is not always built as + part of Open vSwitch. +

ptcp:[port][:ip]
-

Listens for connections on the specified TCP - port. If ip, which must be expressed - as an IP address (not a DNS name), is specified, then - connections are restricted to the specified local IP - address.

-

If port is not specified, it currently - defaults to 6633. In the future, the default will change to - 6653, which is the IANA-defined value.

+

+ Listens for connections on the specified TCP port. If + ip, which must be expressed as an IP address (not a + DNS name), is specified, then connections are restricted to the + specified local IP address (either IPv4 or IPv6). If + ip is an IPv6 address, wrap it in square brackets, + e.g. ptcp:6632:[::1]. If ip is not + specified then it listens only on IPv4 addresses. +

+

+ If port is not specified, it currently defaults to + 6633. In the future, the default will change to 6653, which is + the IANA-defined value. +

When multiple controllers are configured for a single bridge, the @@ -3331,8 +3356,10 @@

The specified TCP port on the host at the given - ip, which must be expressed as an IP address - (not a DNS name). + ip, which must be expressed as an IP address (not a + DNS name), where ip can be IPv4 or IPv6 address. If + ip is an IPv6 address, wrap it in square brackets, + e.g. tcp:[::1]:6632.

If port is not specified, it currently defaults @@ -3343,13 +3370,16 @@

pssl:[port][:ip]

- Listens for SSL connections on the specified TCP - port. Specify 0 for port to have - the kernel automatically choose an available port. If - ip, 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 column in the port. + Specify 0 for port to have the kernel automatically + choose an available port. If ip, which must be + expressed as an IP address (not a DNS name), is specified, then + connections are restricted to the specified local IP address + (either IPv4 or IPv6 address). If ip is an IPv6 + address, wrap in square brackets, + e.g. pssl:6632:[::1]. If ip is not + specified then it listens only on IPv4 (but not IPv6) addresses. + The column in the table must point to a valid SSL configuration when this form is used.

@@ -3366,12 +3396,15 @@
ptcp:[port][:ip]

- Listens for connections on the specified TCP - port. Specify 0 for port to have - the kernel automatically choose an available port. If - ip, which must be expressed as an IP address - (not a DNS name), is specified, then connections are - restricted to the specified local IP address. + Listens for connections on the specified TCP port. + Specify 0 for port to have the kernel automatically + choose an available port. If ip, which must be + expressed as an IP address (not a DNS name), is specified, then + connections are restricted to the specified local IP address + (either IPv4 or IPv6 address). If ip is an IPv6 + address, wrap it in square brackets, + e.g. ptcp:6632:[::1]. If ip is not + specified then it listens only on IPv4 addresses.

If port is not specified, it currently defaults -- 2.43.0