+#ifndef _WIN32
+void
+xpipe(int fds[2])
+{
+ if (pipe(fds)) {
+ VLOG_FATAL("failed to create pipe (%s)", ovs_strerror(errno));
+ }
+}
+
+void
+xpipe_nonblocking(int fds[2])
+{
+ xpipe(fds);
+ xset_nonblocking(fds[0]);
+ xset_nonblocking(fds[1]);
+}
+#endif
+
+static int
+getsockopt_int(int fd, int level, int option, const char *optname, int *valuep)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 10);
+ socklen_t len;
+ int value;
+ int error;
+
+ len = sizeof value;
+ if (getsockopt(fd, level, option, &value, &len)) {
+ error = sock_errno();
+ VLOG_ERR_RL(&rl, "getsockopt(%s): %s", optname, sock_strerror(error));
+ } else if (len != sizeof value) {
+ error = EINVAL;
+ VLOG_ERR_RL(&rl, "getsockopt(%s): value is %u bytes (expected %"PRIuSIZE")",
+ optname, (unsigned int) len, sizeof value);
+ } else {
+ error = 0;
+ }
+
+ *valuep = error ? 0 : value;
+ return error;
+}
+
+static void
+describe_sockaddr(struct ds *string, int fd,
+ int (*getaddr)(int, struct sockaddr *, socklen_t *))
+{
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof ss;
+
+ if (!getaddr(fd, (struct sockaddr *) &ss, &len)) {
+ if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) {
+ char addrbuf[SS_NTOP_BUFSIZE];
+
+ ds_put_format(string, "%s:%"PRIu16,
+ ss_format_address(&ss, addrbuf, sizeof addrbuf),
+ ss_get_port(&ss));
+#ifndef _WIN32
+ } else if (ss.ss_family == AF_UNIX) {
+ struct sockaddr_un sun;
+ const char *null;
+ size_t maxlen;
+
+ memcpy(&sun, &ss, sizeof sun);
+ maxlen = len - offsetof(struct sockaddr_un, sun_path);
+ null = memchr(sun.sun_path, '\0', maxlen);
+ ds_put_buffer(string, sun.sun_path,
+ null ? null - sun.sun_path : maxlen);
+#endif
+ }
+#ifdef HAVE_NETLINK
+ else if (ss.ss_family == AF_NETLINK) {
+ int protocol;
+
+/* SO_PROTOCOL was introduced in 2.6.32. Support it regardless of the version
+ * of the Linux kernel headers in use at build time. */
+#ifndef SO_PROTOCOL
+#define SO_PROTOCOL 38
+#endif
+
+ if (!getsockopt_int(fd, SOL_SOCKET, SO_PROTOCOL, "SO_PROTOCOL",
+ &protocol)) {
+ switch (protocol) {
+ case NETLINK_ROUTE:
+ ds_put_cstr(string, "NETLINK_ROUTE");
+ break;
+
+ case NETLINK_GENERIC:
+ ds_put_cstr(string, "NETLINK_GENERIC");
+ break;
+
+ default:
+ ds_put_format(string, "AF_NETLINK family %d", protocol);
+ break;
+ }
+ } else {
+ ds_put_cstr(string, "AF_NETLINK");
+ }
+ }
+#endif
+#if __linux__
+ else if (ss.ss_family == AF_PACKET) {
+ struct sockaddr_ll sll;
+
+ memcpy(&sll, &ss, sizeof sll);
+ ds_put_cstr(string, "AF_PACKET");
+ if (sll.sll_ifindex) {
+ char name[IFNAMSIZ];
+
+ if (if_indextoname(sll.sll_ifindex, name)) {
+ ds_put_format(string, "(%s)", name);
+ } else {
+ ds_put_format(string, "(ifindex=%d)", sll.sll_ifindex);
+ }
+ }
+ if (sll.sll_protocol) {
+ ds_put_format(string, "(protocol=0x%"PRIu16")",
+ ntohs(sll.sll_protocol));
+ }
+ }
+#endif
+ else if (ss.ss_family == AF_UNSPEC) {
+ ds_put_cstr(string, "AF_UNSPEC");
+ } else {
+ ds_put_format(string, "AF_%d", (int) ss.ss_family);
+ }
+ }
+}
+
+
+#ifdef __linux__
+static void
+put_fd_filename(struct ds *string, int fd)
+{
+ char buf[1024];
+ char *linkname;
+ int n;
+
+ linkname = xasprintf("/proc/self/fd/%d", fd);
+ n = readlink(linkname, buf, sizeof buf);
+ if (n > 0) {
+ ds_put_char(string, ' ');
+ ds_put_buffer(string, buf, n);
+ if (n > sizeof buf) {
+ ds_put_cstr(string, "...");
+ }
+ }
+ free(linkname);
+}
+#endif
+
+/* Returns a malloc()'d string describing 'fd', for use in logging. */
+char *
+describe_fd(int fd)
+{
+ struct ds string;
+ struct stat s;
+
+ ds_init(&string);
+#ifndef _WIN32
+ if (fstat(fd, &s)) {
+ ds_put_format(&string, "fstat failed (%s)", ovs_strerror(errno));
+ } else if (S_ISSOCK(s.st_mode)) {
+ describe_sockaddr(&string, fd, getsockname);
+ ds_put_cstr(&string, "<->");
+ describe_sockaddr(&string, fd, getpeername);
+ } else {
+ ds_put_cstr(&string, (isatty(fd) ? "tty"
+ : S_ISDIR(s.st_mode) ? "directory"
+ : S_ISCHR(s.st_mode) ? "character device"
+ : S_ISBLK(s.st_mode) ? "block device"
+ : S_ISREG(s.st_mode) ? "file"
+ : S_ISFIFO(s.st_mode) ? "FIFO"
+ : S_ISLNK(s.st_mode) ? "symbolic link"
+ : "unknown"));
+#ifdef __linux__
+ put_fd_filename(&string, fd);
+#endif
+ }
+#else
+ ds_put_format(&string,"file descriptor");
+#endif /* _WIN32 */
+ return ds_steal_cstr(&string);
+}
+
+#ifndef _WIN32
+/* Calls ioctl() on an AF_INET sock, passing the specified 'command' and
+ * 'arg'. Returns 0 if successful, otherwise a positive errno value. */
+int
+af_inet_ioctl(unsigned long int command, const void *arg)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ static int sock;
+
+ if (ovsthread_once_start(&once)) {
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ int error = sock_errno();
+ VLOG_ERR("failed to create inet socket: %s", sock_strerror(error));
+ sock = -error;
+ }
+ ovsthread_once_done(&once);
+ }
+
+ return (sock < 0 ? -sock
+ : ioctl(sock, command, arg) == -1 ? errno
+ : 0);
+}
+
+int
+af_inet_ifreq_ioctl(const char *name, struct ifreq *ifr, unsigned long int cmd,
+ const char *cmd_name)
+{
+ int error;
+
+ ovs_strzcpy(ifr->ifr_name, name, sizeof ifr->ifr_name);
+ error = af_inet_ioctl(cmd, ifr);
+ if (error) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+ VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name,
+ ovs_strerror(error));
+ }
+ return error;
+}
+#endif
+\f
+/* 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();
+ }
+}
+
+/* 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.
+ *
+ * ovs_strerror() calls strerror_r() and would not get the correct error
+ * string for Windows sockets, but is good for POSIX. */
+const char *
+sock_strerror(int error)
+{
+#ifdef _WIN32
+ return ovs_format_message(error);
+#else
+ return ovs_strerror(error);
+#endif
+}