X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fsocket-util.c;h=935e7477771b494edcc09170c490492b1d62e913;hb=1ff83721f49bb89f2931ce464b4745e47c6a657a;hp=e97e7c90ebd037cb4d488e14038f06946ce69e13;hpb=db5ce51427906ed54aa3e8e1dffc0ba6d908aa8c;p=openvswitch diff --git a/lib/socket-util.c b/lib/socket-util.c index e97e7c90..935e7477 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -30,9 +31,18 @@ #include #include #include +#include "dynamic-string.h" #include "fatal-signal.h" +#include "packets.h" #include "util.h" #include "vlog.h" +#if AF_PACKET && __linux__ +#include +#endif +#ifdef HAVE_NETLINK +#include "netlink-protocol.h" +#include "netlink-socket.h" +#endif VLOG_DEFINE_THIS_MODULE(socket_util); @@ -121,6 +131,20 @@ lookup_ip(const char *host_name, struct in_addr *addr) return 0; } +/* Translates 'host_name', which must be a string representation of an IPv6 + * address, into a numeric IPv6 address in '*addr'. Returns 0 if successful, + * otherwise a positive errno value. */ +int +lookup_ipv6(const char *host_name, struct in6_addr *addr) +{ + if (inet_pton(AF_INET6, host_name, addr) != 1) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_ERR_RL(&rl, "\"%s\" is not a valid IPv6 address", host_name); + return ENOENT; + } + return 0; +} + /* Returns the error condition associated with socket 'fd' and resets the * socket's error status. */ int @@ -219,8 +243,7 @@ static void make_sockaddr_un__(const char *name, struct sockaddr_un *un, socklen_t *un_len) { un->sun_family = AF_UNIX; - strncpy(un->sun_path, name, sizeof un->sun_path); - un->sun_path[sizeof un->sun_path - 1] = '\0'; + ovs_strzcpy(un->sun_path, name, sizeof un->sun_path); *un_len = (offsetof(struct sockaddr_un, sun_path) + strlen (un->sun_path) + 1); } @@ -256,6 +279,8 @@ make_sockaddr_un(const char *name, struct sockaddr_un *un, socklen_t *un_len, dirfd = open(dir, O_DIRECTORY | O_RDONLY); if (dirfd < 0) { + free(base); + free(dir); return errno; } @@ -287,6 +312,17 @@ make_sockaddr_un(const char *name, struct sockaddr_un *un, socklen_t *un_len, } } +/* Binds Unix domain socket 'fd' to a file with permissions 0700. */ +static int +bind_unix_socket(int fd, struct sockaddr *sun, socklen_t sun_len) +{ + /* According to _Unix Network Programming_, umask should affect bind(). */ + mode_t old_umask = umask(0077); + int error = bind(fd, sun, sun_len) ? errno : 0; + umask(old_umask); + return error; +} + /* Creates a Unix domain socket in the given 'style' (either SOCK_DGRAM or * SOCK_STREAM) that is bound to '*bind_path' (if 'bind_path' is non-null) and * connected to '*connect_path' (if 'connect_path' is non-null). If 'nonblock' @@ -333,9 +369,8 @@ make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED, fatal_signal_add_file_to_unlink(bind_path); error = make_sockaddr_un(bind_path, &un, &un_len, &dirfd); - if (!error && (bind(fd, (struct sockaddr*) &un, un_len) - || fchmod(fd, S_IRWXU))) { - error = errno; + if (!error) { + error = bind_unix_socket(fd, (struct sockaddr *) &un, un_len); } if (dirfd >= 0) { close(dirfd); @@ -395,10 +430,10 @@ get_unix_name_len(socklen_t sun_len) : 0); } -uint32_t -guess_netmask(uint32_t ip) +ovs_be32 +guess_netmask(ovs_be32 ip_) { - ip = ntohl(ip); + uint32_t ip = ntohl(ip_); return ((ip >> 31) == 0 ? htonl(0xff000000) /* Class A */ : (ip >> 30) == 2 ? htonl(0xffff0000) /* Class B */ : (ip >> 29) == 6 ? htonl(0xffffff00) /* Class C */ @@ -752,3 +787,155 @@ get_mtime(const char *file_name, struct timespec *mtime) } } +void +xpipe(int fds[2]) +{ + if (pipe(fds)) { + VLOG_FATAL("failed to create pipe (%s)", strerror(errno)); + } +} + +static int +getsockopt_int(int fd, int level, int optname, int *valuep) +{ + socklen_t len = sizeof *valuep; + + return (getsockopt(fd, level, optname, valuep, &len) ? errno + : len == sizeof *valuep ? 0 + : EINVAL); +} + +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) { + struct sockaddr_in sin; + + memcpy(&sin, &ss, sizeof sin); + ds_put_format(string, IP_FMT":%"PRIu16, + IP_ARGS(&sin.sin_addr.s_addr), ntohs(sin.sin_port)); + } else if (ss.ss_family == AF_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); + } +#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, &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 AF_PACKET && __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); + if (fstat(fd, &s)) { + ds_put_format(&string, "fstat failed (%s)", 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 + } + return ds_steal_cstr(&string); +}