/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <config.h>
#include "socket-util.h"
+#include <sys/types.h>
+#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include "openvswitch/dynamic-string.h"
#include "ovs-thread.h"
#include "packets.h"
-#include "poll-loop.h"
+#include "openvswitch/poll-loop.h"
#include "util.h"
#include "openvswitch/vlog.h"
#ifdef __linux__
#include "netlink-protocol.h"
#include "netlink-socket.h"
#endif
+#include "dns-resolve.h"
VLOG_DEFINE_THIS_MODULE(socket_util);
static int getsockopt_int(int fd, int level, int option, const char *optname,
int *valuep);
+static struct sockaddr_in *sin_cast(const struct sockaddr *);
+static struct sockaddr_in6 *sin6_cast(const struct sockaddr *);
+static const struct sockaddr *sa_cast(const struct sockaddr_storage *);
+static bool parse_sockaddr_components(struct sockaddr_storage *ss,
+ char *host_s,
+ const char *port_s,
+ uint16_t default_port,
+ const char *s,
+ bool resolve_host);
/* Sets 'fd' to non-blocking mode. Returns 0 if successful, otherwise a
* positive errno value. */
}
#endif
if (retval == 1) {
- if (pfd.revents & POLLERR) {
+ if (pfd.revents & (POLLERR | POLLHUP)) {
ssize_t n = send(fd, "", 1, 0);
if (n < 0) {
return sock_errno();
: htonl(0)); /* ??? */
}
-/* This is like strsep() except:
- *
- * - The separator string is ":".
- *
- * - Square brackets [] quote ":" separators and are removed from the
- * tokens. */
static char *
-parse_bracketed_token(char **pp)
+unbracket(char *s)
+{
+ if (*s == '[') {
+ s++;
+
+ char *end = strchr(s, '\0');
+ if (end[-1] == ']') {
+ end[-1] = '\0';
+ }
+ }
+ return s;
+}
+
+/* 'host_index' is 0 if the host precedes the port within 's', 1 otherwise. */
+static void
+inet_parse_tokens__(char *s, int host_index, char **hostp, char **portp)
{
- 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;
+ char *colon = NULL;
+ bool in_brackets = false;
+ int n_colons = 0;
+ for (char *p = s; *p; p++) {
+ if (*p == '[') {
+ in_brackets = true;
+ } else if (*p == ']') {
+ in_brackets = false;
+ } else if (*p == ':' && !in_brackets) {
+ n_colons++;
+ colon = p;
+ }
+ }
+
+ *hostp = *portp = NULL;
+ if (n_colons > 1) {
+ *hostp = s;
} else {
- char *start = p;
- char *end = start + strcspn(start, ":");
- *pp = *end == '\0' ? NULL : end + 1;
- *end = '\0';
- return start;
+ char **tokens[2];
+ tokens[host_index] = hostp;
+ tokens[!host_index] = portp;
+
+ if (colon) {
+ *colon = '\0';
+ *tokens[1] = unbracket(colon + 1);
+ }
+ *tokens[0] = unbracket(s);
}
}
+/* Parses 's', a string in the form "<host>[:<port>]", into its (required) host
+ * and (optional) port components, and stores pointers to them in '*hostp' and
+ * '*portp' respectively. Always sets '*hostp' nonnull, although possibly to
+ * an empty string. Can set '*portp' to the null string.
+ *
+ * Supports both IPv4 and IPv6. IPv6 addresses may be quoted with square
+ * brackets. Resolves ambiguous cases that might represent an IPv6 address or
+ * an IPv6 address and a port as representing just a host, e.g. "::1:2:3:4:80"
+ * is a host but "[::1:2:3:4]:80" is a host and a port.
+ *
+ * Modifies 's' and points '*hostp' and '*portp' (if nonnull) into it.
+ */
+void
+inet_parse_host_port_tokens(char *s, char **hostp, char **portp)
+{
+ inet_parse_tokens__(s, 0, hostp, portp);
+}
+
+/* Parses 's', a string in the form "<port>[:<host>]", into its port and host
+ * components, and stores pointers to them in '*portp' and '*hostp'
+ * respectively. Either '*portp' and '*hostp' (but not both) can end up null.
+ *
+ * Supports both IPv4 and IPv6. IPv6 addresses may be quoted with square
+ * brackets. Resolves ambiguous cases that might represent an IPv6 address or
+ * an IPv6 address and a port as representing just a host, e.g. "::1:2:3:4:80"
+ * is a host but "[::1:2:3:4]:80" is a host and a port.
+ *
+ * Modifies 's' and points '*hostp' and '*portp' (if nonnull) into it.
+ */
+void
+inet_parse_port_host_tokens(char *s, char **portp, char **hostp)
+{
+ inet_parse_tokens__(s, 1, hostp, portp);
+}
+
+static bool
+parse_sockaddr_components_dns(struct sockaddr_storage *ss OVS_UNUSED,
+ char *host_s,
+ const char *port_s OVS_UNUSED,
+ uint16_t default_port OVS_UNUSED,
+ const char *s OVS_UNUSED)
+{
+ char *tmp_host_s;
+
+ dns_resolve(host_s, &tmp_host_s);
+ if (tmp_host_s != NULL) {
+ parse_sockaddr_components(ss, tmp_host_s, port_s,
+ default_port, s, false);
+ free(tmp_host_s);
+ return true;
+ }
+ return false;
+}
+
static bool
parse_sockaddr_components(struct sockaddr_storage *ss,
char *host_s,
const char *port_s, uint16_t default_port,
- const char *s)
+ const char *s,
+ bool resolve_host)
{
- struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, ss);
+ struct sockaddr_in *sin = sin_cast(sa_cast(ss));
int port;
if (port_s && port_s[0]) {
memset(ss, 0, sizeof *ss);
if (host_s && strchr(host_s, ':')) {
- struct sockaddr_in6 *sin6
- = ALIGNED_CAST(struct sockaddr_in6 *, ss);
-
+ struct sockaddr_in6 *sin6 = sin6_cast(sa_cast(ss));
char *addr = strsep(&host_s, "%");
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port);
if (!addr || !*addr || !ipv6_parse(addr, &sin6->sin6_addr)) {
- VLOG_ERR("%s: bad IPv6 address \"%s\"", s, addr ? addr : "");
goto exit;
}
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
if (host_s && !ip_parse(host_s, &sin->sin_addr.s_addr)) {
- VLOG_ERR("%s: bad IPv4 address \"%s\"", s, host_s);
- goto exit;
+ goto resolve;
}
}
return true;
+resolve:
+ if (resolve_host && parse_sockaddr_components_dns(ss, host_s, port_s,
+ default_port, s)) {
+ return true;
+ } else if (!resolve_host) {
+ VLOG_ERR("%s: bad IP address \"%s\"", s, host_s);
+ }
exit:
memset(ss, 0, sizeof *ss);
return false;
/* Parses 'target', which should be a string in the format "<host>[:<port>]".
* <host>, which is required, may be an IPv4 address or an IPv6 address
- * enclosed in square brackets. If 'default_port' is nonzero then <port> is
- * optional and defaults to 'default_port'.
+ * enclosed in square brackets. If 'default_port' is nonnegative then <port>
+ * is optional and defaults to 'default_port' (use 0 to make the kernel choose
+ * an available port, although this isn't usually appropriate for active
+ * connections). If 'default_port' is negative, then <port> is required.
+ * It resolves the host if 'resolve_host' is true.
*
* 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)
+inet_parse_active(const char *target_, int default_port,
+ struct sockaddr_storage *ss, bool resolve_host)
{
char *target = xstrdup(target_);
- const char *port;
- char *host;
- char *p;
+ char *port, *host;
bool ok;
- p = target;
- host = parse_bracketed_token(&p);
- port = parse_bracketed_token(&p);
+ inet_parse_host_port_tokens(target, &host, &port);
if (!host) {
VLOG_ERR("%s: host must be specified", target_);
ok = false;
- } else if (!port && !default_port) {
+ } else if (!port && default_port < 0) {
VLOG_ERR("%s: port must be specified", target_);
ok = false;
} else {
- ok = parse_sockaddr_components(ss, host, port, default_port, target_);
+ ok = parse_sockaddr_components(ss, host, port, default_port,
+ target_, resolve_host);
}
if (!ok) {
memset(ss, 0, sizeof *ss);
/* 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>, which is required, may be an IPv4 address or an
- * IPv6 address enclosed in square brackets. If 'default_port' is nonzero then
- * <port> is optional and defaults to 'default_port'.
+ * IPv6 address enclosed in square brackets. If 'default_port' is nonnegative
+ * then <port> is optional and defaults to 'default_port'.
*
* 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP).
*
* 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,
+inet_open_active(int style, const char *target, int default_port,
struct sockaddr_storage *ssp, int *fdp, uint8_t dscp)
{
struct sockaddr_storage ss;
int error;
/* Parse. */
- if (!inet_parse_active(target, default_port, &ss)) {
+ if (!inet_parse_active(target, default_port, &ss, true)) {
error = EAFNOSUPPORT;
goto exit;
}
struct sockaddr_storage *ss)
{
char *target = xstrdup(target_);
- const char *port;
- char *host;
- char *p;
+ char *port, *host;
bool ok;
- p = target;
- port = parse_bracketed_token(&p);
- host = parse_bracketed_token(&p);
+ inet_parse_port_host_tokens(target, &port, &host);
if (!port && default_port < 0) {
VLOG_ERR("%s: port must be specified", target_);
ok = false;
} else {
- ok = parse_sockaddr_components(ss, host, port, default_port, target_);
+ ok = parse_sockaddr_components(ss, host, port, default_port,
+ target_, true);
}
if (!ok) {
memset(ss, 0, sizeof *ss);
/* Bind. */
if (bind(fd, (struct sockaddr *) &ss, ss_length(&ss)) < 0) {
error = sock_errno();
- VLOG_ERR("%s: bind: %s", target, sock_strerror(error));
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_ERR_RL(&rl, "%s: bind: %s", target, sock_strerror(error));
goto error;
}
return -error;
}
+/* Parses 'target', which may be an IPv4 address or an IPv6 address
+ * enclosed in square brackets.
+ *
+ * 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_address(const char *target_, struct sockaddr_storage *ss)
+{
+ char *target = xstrdup(target_);
+ char *host = unbracket(target);
+ bool ok = parse_sockaddr_components(ss, host, NULL, 0, target_, false);
+ if (!ok) {
+ memset(ss, 0, sizeof *ss);
+ }
+ free(target);
+ return ok;
+}
+
int
read_fully(int fd, void *p_, size_t size, size_t *bytes_read)
{
#endif /* _WIN32 */
return ds_steal_cstr(&string);
}
-
\f
-/* sockaddr_storage helpers. */
+/* sockaddr helpers. */
-/* Returns the IPv4 or IPv6 port in 'ss'. */
+static struct sockaddr_in *
+sin_cast(const struct sockaddr *sa)
+{
+ return ALIGNED_CAST(struct sockaddr_in *, sa);
+}
+
+static struct sockaddr_in6 *
+sin6_cast(const struct sockaddr *sa)
+{
+ return ALIGNED_CAST(struct sockaddr_in6 *, sa);
+}
+
+/* Returns true if 'sa' represents an IPv4 or IPv6 address, false otherwise. */
+bool
+sa_is_ip(const struct sockaddr *sa)
+{
+ return sa->sa_family == AF_INET || sa->sa_family == AF_INET6;
+}
+
+/* Returns the IPv4 or IPv6 address in 'sa'. Returns IPv4 addresses as
+ * v6-mapped. */
+struct in6_addr
+sa_get_address(const struct sockaddr *sa)
+{
+ ovs_assert(sa_is_ip(sa));
+ return (sa->sa_family == AF_INET
+ ? in6_addr_mapped_ipv4(sin_cast(sa)->sin_addr.s_addr)
+ : sin6_cast(sa)->sin6_addr);
+}
+
+/* Returns the IPv4 or IPv6 port in 'sa'. */
uint16_t
-ss_get_port(const struct sockaddr_storage *ss)
+sa_get_port(const struct sockaddr *sa)
{
- 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();
- }
+ ovs_assert(sa_is_ip(sa));
+ return ntohs(sa->sa_family == AF_INET
+ ? sin_cast(sa)->sin_port
+ : sin6_cast(sa)->sin6_port);
}
/* Returns true if 'name' is safe to include inside a network address field.
return true;
}
-/* Formats the IPv4 or IPv6 address in 'ss' into 's'. If 'ss' is an IPv6
- * address, puts square brackets around the address. 'bufsize' should be at
- * least SS_NTOP_BUFSIZE. */
-void
-ss_format_address(const struct sockaddr_storage *ss, struct ds *s)
+static void
+sa_format_address__(const struct sockaddr *sa,
+ const char *lbrack, const char *rbrack,
+ struct ds *s)
{
- if (ss->ss_family == AF_INET) {
- const struct sockaddr_in *sin
- = ALIGNED_CAST(const struct sockaddr_in *, ss);
-
- ds_put_format(s, 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);
+ ovs_assert(sa_is_ip(sa));
+ if (sa->sa_family == AF_INET) {
+ ds_put_format(s, IP_FMT, IP_ARGS(sin_cast(sa)->sin_addr.s_addr));
+ } else {
+ const struct sockaddr_in6 *sin6 = sin6_cast(sa);
- ds_put_char(s, '[');
+ ds_put_cstr(s, lbrack);
ds_reserve(s, s->length + INET6_ADDRSTRLEN);
char *tail = &s->string[s->length];
inet_ntop(AF_INET6, sin6->sin6_addr.s6_addr, tail, INET6_ADDRSTRLEN);
}
#endif
- ds_put_char(s, ']');
- } else {
- OVS_NOT_REACHED();
+ ds_put_cstr(s, rbrack);
}
}
+/* Formats the IPv4 or IPv6 address in 'sa' into 's'. If 'sa' is an IPv6
+ * address, puts square brackets around the address. */
+void
+sa_format_address(const struct sockaddr *sa, struct ds *s)
+{
+ sa_format_address__(sa, "[", "]", s);
+}
+
+/* Formats the IPv4 or IPv6 address in 'sa' into 's'. Does not add square
+ * brackets around IPv6 addresses. */
+void
+sa_format_address_nobracks(const struct sockaddr *sa, struct ds *s)
+{
+ sa_format_address__(sa, "", "", s);
+}
+
size_t
-ss_length(const struct sockaddr_storage *ss)
+sa_length(const struct sockaddr *sa)
{
- switch (ss->ss_family) {
+ switch (sa->sa_family) {
case AF_INET:
return sizeof(struct sockaddr_in);
OVS_NOT_REACHED();
}
}
+\f
+/* sockaddr_storage helpers. */
+
+static const struct sockaddr *
+sa_cast(const struct sockaddr_storage *ss)
+{
+ return ALIGNED_CAST(const struct sockaddr *, ss);
+}
+
+bool
+ss_is_ip(const struct sockaddr_storage *ss)
+{
+ return sa_is_ip(sa_cast(ss));
+}
+uint16_t
+ss_get_port(const struct sockaddr_storage *ss)
+{
+ return sa_get_port(sa_cast(ss));
+}
+
+struct in6_addr
+ss_get_address(const struct sockaddr_storage *ss)
+{
+ return sa_get_address(sa_cast(ss));
+}
+
+void
+ss_format_address(const struct sockaddr_storage *ss, struct ds *s)
+{
+ sa_format_address(sa_cast(ss), s);
+}
+
+void
+ss_format_address_nobracks(const struct sockaddr_storage *ss, struct ds *s)
+{
+ sa_format_address_nobracks(sa_cast(ss), s);
+}
+
+size_t
+ss_length(const struct sockaddr_storage *ss)
+{
+ return sa_length(sa_cast(ss));
+}
+\f
/* For Windows socket calls, 'errno' is not set. One has to call
* WSAGetLastError() to get the error number and then pass it to
* this function to get the correct error string.
#endif
}
\f
-#ifndef _WIN32 //Avoid using sendmsg on Windows entirely
+#ifdef __linux__
static int
emulate_sendmmsg(int fd, struct mmsghdr *msgs, unsigned int n,
unsigned int flags)
return emulate_sendmmsg(fd, msgs, n, flags);
}
#endif
+
+static int
+emulate_recvmmsg(int fd, struct mmsghdr *msgs, unsigned int n,
+ int flags, struct timespec *timeout OVS_UNUSED)
+{
+ ovs_assert(!timeout); /* XXX not emulated */
+
+ bool waitforone = flags & MSG_WAITFORONE;
+ flags &= ~MSG_WAITFORONE;
+
+ for (unsigned int i = 0; i < n; i++) {
+ ssize_t retval = recvmsg(fd, &msgs[i].msg_hdr, flags);
+ if (retval < 0) {
+ return i ? i : retval;
+ }
+ msgs[i].msg_len = retval;
+
+ if (waitforone) {
+ flags |= MSG_DONTWAIT;
+ }
+ }
+ return n;
+}
+
+#ifndef HAVE_SENDMMSG
+int
+recvmmsg(int fd, struct mmsghdr *msgs, unsigned int n,
+ int flags, struct timespec *timeout)
+{
+ return emulate_recvmmsg(fd, msgs, n, flags, timeout);
+}
+#else
+/* recvmmsg was redefined in lib/socket-util.c, should undef recvmmsg here
+ * to avoid recursion */
+#undef recvmmsg
+int
+wrap_recvmmsg(int fd, struct mmsghdr *msgs, unsigned int n,
+ int flags, struct timespec *timeout)
+{
+ ovs_assert(!timeout); /* XXX not emulated */
+
+ static bool recvmmsg_broken = false;
+ if (!recvmmsg_broken) {
+ int save_errno = errno;
+ int retval = recvmmsg(fd, msgs, n, flags, timeout);
+ if (retval >= 0 || errno != ENOSYS) {
+ return retval;
+ }
+ recvmmsg_broken = true;
+ errno = save_errno;
+ }
+ return emulate_recvmmsg(fd, msgs, n, flags, timeout);
+}
#endif
+#endif /* __linux__ */