X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fsocket-util.c;h=e6a6c70ef836b9a370bd9fffb64fc7cf3844b409;hb=84f7e9b6d0f8730f4b470ffdc339b0e66dcd958e;hp=d13255ff1cb4efe618f2061e5fe62d0355582190;hpb=2b35e1475ef0c274495aa04d60b054288971d503;p=sliver-openvswitch.git diff --git a/lib/socket-util.c b/lib/socket-util.c index d13255ff1..e6a6c70ef 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -290,6 +291,186 @@ guess_netmask(uint32_t ip) : htonl(0)); /* ??? */ } +/* 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'. + * + * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP). + * + * On success, returns 0 (indicating connection complete) or EAGAIN (indicating + * connection in progress), in which case the new file descriptor is stored + * 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'. */ +int +inet_open_active(int style, const char *target_, uint16_t default_port, + struct sockaddr_in *sinp, int *fdp) +{ + char *target = xstrdup(target_); + char *save_ptr = NULL; + const char *host_name; + const char *port_string; + struct sockaddr_in sin; + int fd = -1; + int error; + + /* Defaults. */ + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(default_port); + + /* Tokenize. */ + host_name = strtok_r(target, ":", &save_ptr); + port_string = strtok_r(NULL, ":", &save_ptr); + if (!host_name) { + ovs_error(0, "%s: bad peer name format", target_); + error = EAFNOSUPPORT; + goto exit; + } + + /* Look up IP, port. */ + error = lookup_ip(host_name, &sin.sin_addr); + if (error) { + goto exit; + } + if (port_string && atoi(port_string)) { + sin.sin_port = htons(atoi(port_string)); + } else if (!default_port) { + VLOG_ERR("%s: port number must be specified", target_); + error = EAFNOSUPPORT; + goto exit; + } + + /* Create non-blocking socket. */ + fd = socket(AF_INET, style, 0); + if (fd < 0) { + VLOG_ERR("%s: socket: %s", target_, strerror(errno)); + error = errno; + goto exit; + } + error = set_nonblocking(fd); + if (error) { + goto exit_close; + } + + /* Connect. */ + error = connect(fd, (struct sockaddr *) &sin, sizeof sin) == 0 ? 0 : errno; + if (error == EINPROGRESS) { + error = EAGAIN; + } else if (error && error != EAGAIN) { + goto exit_close; + } + + /* Success: error is 0 or EAGAIN. */ + goto exit; + +exit_close: + close(fd); +exit: + if (!error || error == EAGAIN) { + if (sinp) { + *sinp = sin; + } + *fdp = fd; + } else { + *fdp = -1; + } + free(target); + return error; +} + +/* Opens a non-blocking IPv4 socket of the specified 'style', binds to + * 'target', and listens for incoming connections. 'target' should be a string + * in the format "[][:]". may be omitted if 'default_port' is + * nonzero, in which case it defaults to 'default_port'. If is omitted it + * defaults to the wildcard IP address. + * + * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP). + * + * For TCP, the socket will have SO_REUSEADDR turned on. + * + * On success, returns a non-negative file descriptor. On failure, returns a + * negative errno value. */ +int +inet_open_passive(int style, const char *target_, uint16_t default_port) +{ + char *target = xstrdup(target_); + char *string_ptr = target; + struct sockaddr_in sin; + const char *host_name; + const char *port_string; + int fd, error; + unsigned int yes = 1; + + /* Address defaults. */ + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(default_port); + + /* Parse optional port number. */ + port_string = strsep(&string_ptr, ":"); + if (port_string && atoi(port_string)) { + sin.sin_port = htons(atoi(port_string)); + } else if (!default_port) { + VLOG_ERR("%s: port number must be specified", target_); + error = EAFNOSUPPORT; + goto exit; + } + + /* Parse optional bind IP. */ + host_name = strsep(&string_ptr, ":"); + if (host_name && host_name[0]) { + error = lookup_ip(host_name, &sin.sin_addr); + if (error) { + goto exit; + } + } + + /* Create non-blocking socket, set SO_REUSEADDR. */ + fd = socket(AF_INET, style, 0); + if (fd < 0) { + error = errno; + VLOG_ERR("%s: socket: %s", target_, strerror(error)); + goto exit; + } + error = set_nonblocking(fd); + if (error) { + goto exit_close; + } + if (style == SOCK_STREAM + && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) { + error = errno; + VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", target_, strerror(error)); + goto exit_close; + } + + /* Bind. */ + if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) { + error = errno; + VLOG_ERR("%s: bind: %s", target_, strerror(error)); + goto exit_close; + } + + /* Listen. */ + if (listen(fd, 10) < 0) { + error = errno; + VLOG_ERR("%s: listen: %s", target_, strerror(error)); + goto exit_close; + } + error = 0; + goto exit; + +exit_close: + close(fd); +exit: + free(target); + return error ? -error : fd; +} + /* Returns a readable and writable fd for /dev/null, if successful, otherwise * a negative errno value. The caller must not close the returned fd (because * the same fd will be handed out to subsequent callers). */