X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=python%2Fovs%2Fsocket_util.py;h=8d34b71635044739ca7ffc93ac4b0115af083f78;hb=25d6a6a8803761cf321ba45d1eeb6df29c2a6c6e;hp=4f4f603624b094bfd465131613c87c5098c3f706;hpb=991559357f6a03c3a5b70c053c8c2554aa8d5ee4;p=sliver-openvswitch.git diff --git a/python/ovs/socket_util.py b/python/ovs/socket_util.py index 4f4f60362..8d34b7163 100644 --- a/python/ovs/socket_util.py +++ b/python/ovs/socket_util.py @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Nicira Networks +# 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. @@ -13,15 +13,47 @@ # limitations under the License. import errno -import logging import os +import os.path +import random import select import socket import sys import ovs.fatal_signal +import ovs.poller +import ovs.vlog -def make_unix_socket(style, nonblock, bind_path, connect_path): +vlog = ovs.vlog.Vlog("socket_util") + + +def make_short_name(long_name): + if long_name is None: + return None + long_name = os.path.abspath(long_name) + long_dirname = os.path.dirname(long_name) + tmpdir = os.getenv('TMPDIR', '/tmp') + for x in xrange(0, 1000): + link_name = \ + '%s/ovs-un-py-%d-%d' % (tmpdir, random.randint(0, 10000), x) + try: + os.symlink(long_dirname, link_name) + ovs.fatal_signal.add_file_to_unlink(link_name) + return os.path.join(link_name, os.path.basename(long_name)) + except OSError, e: + if e.errno != errno.EEXIST: + break + raise Exception("Failed to create temporary symlink") + + +def free_short_name(short_name): + if short_name is None: + return + link_name = os.path.dirname(short_name) + ovs.fatal_signal.unlink_file_now(link_name) + + +def make_unix_socket(style, nonblock, bind_path, connect_path, short=False): """Creates a Unix domain socket in the given 'style' (either socket.SOCK_DGRAM or socket.SOCK_STREAM) that is bound to 'bind_path' (if 'bind_path' is not None) and connected to 'connect_path' (if 'connect_path' @@ -66,26 +98,139 @@ def make_unix_socket(style, nonblock, bind_path, connect_path): return 0, sock except socket.error, e: sock.close() - try: - os.unlink(bind_path) - except OSError, e: - pass - if bind_path is not None: - ovs.fatal_signal.add_file_to_unlink(bind_path) - return get_exception_errno(e), None + if (bind_path is not None and + os.path.exists(bind_path)): + ovs.fatal_signal.unlink_file_now(bind_path) + eno = ovs.socket_util.get_exception_errno(e) + if (eno == "AF_UNIX path too long" and + os.uname()[0] == "Linux"): + short_connect_path = None + short_bind_path = None + connect_dirfd = None + bind_dirfd = None + # Try workaround using /proc/self/fd + if connect_path is not None: + dirname = os.path.dirname(connect_path) + basename = os.path.basename(connect_path) + try: + connect_dirfd = os.open(dirname, os.O_DIRECTORY | os.O_RDONLY) + except OSError, err: + return get_exception_errno(err), None + short_connect_path = "/proc/self/fd/%d/%s" % (connect_dirfd, basename) + + if bind_path is not None: + dirname = os.path.dirname(bind_path) + basename = os.path.basename(bind_path) + try: + bind_dirfd = os.open(dirname, os.O_DIRECTORY | os.O_RDONLY) + except OSError, err: + return get_exception_errno(err), None + short_bind_path = "/proc/self/fd/%d/%s" % (bind_dirfd, basename) + + try: + return make_unix_socket(style, nonblock, short_bind_path, short_connect_path) + finally: + if connect_dirfd is not None: + os.close(connect_dirfd) + if bind_dirfd is not None: + os.close(bind_dirfd) + elif (eno == "AF_UNIX path too long"): + if short: + return get_exception_errno(e), None + short_bind_path = None + try: + short_bind_path = make_short_name(bind_path) + short_connect_path = make_short_name(connect_path) + except: + free_short_name(short_bind_path) + return errno.ENAMETOOLONG, None + try: + return make_unix_socket(style, nonblock, short_bind_path, + short_connect_path, short=True) + finally: + free_short_name(short_bind_path) + free_short_name(short_connect_path) + else: + return get_exception_errno(e), None + def check_connection_completion(sock): - p = select.poll() - p.register(sock, select.POLLOUT) - if len(p.poll(0)) == 1: - return get_socket_error(sock) + p = ovs.poller.SelectPoll() + p.register(sock, ovs.poller.POLLOUT) + pfds = p.poll(0) + if len(pfds) == 1: + revents = pfds[0][1] + if revents & ovs.poller.POLLERR: + try: + # The following should raise an exception. + socket.send("\0", socket.MSG_DONTWAIT) + + # (Here's where we end up if it didn't.) + # XXX rate-limit + vlog.err("poll return POLLERR but send succeeded") + return errno.EPROTO + except socket.error, e: + return get_exception_errno(e) + else: + return 0 else: return errno.EAGAIN -def get_socket_error(sock): - """Returns the errno value associated with 'socket' (0 if no error) and - resets the socket's error status.""" - return sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + +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(":") + if len(address) >= 2: + host_name = ":".join(address[0:-1]).lstrip('[').rstrip(']') + port = int(address[-1]) + else: + 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: + 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 + + try: + set_nonblocking(sock) + set_dscp(sock, dscp) + try: + sock.connect(address) + except socket.error, e: + if get_exception_errno(e) != errno.EINPROGRESS: + raise + return 0, sock + except socket.error, e: + sock.close() + return get_exception_errno(e), None + def get_exception_errno(e): """A lot of methods on Python socket objects raise socket.error, but that @@ -97,7 +242,10 @@ def get_exception_errno(e): else: return errno.EPROTO + null_fd = -1 + + def get_null_fd(): """Returns a readable and writable fd for /dev/null, if successful, otherwise a negative errno value. The caller must not close the returned @@ -107,11 +255,11 @@ def get_null_fd(): try: null_fd = os.open("/dev/null", os.O_RDWR) except OSError, e: - logging.error("could not open /dev/null: %s" - % os.strerror(e.errno)) + vlog.err("could not open /dev/null: %s" % os.strerror(e.errno)) return -e.errno return null_fd + def write_fully(fd, buf): """Returns an (error, bytes_written) tuple where 'error' is 0 on success, otherwise a positive errno value, and 'bytes_written' is the number of @@ -127,7 +275,7 @@ def write_fully(fd, buf): if retval == len(buf): return 0, bytes_written + len(buf) elif retval == 0: - logging.warning("write returned 0") + vlog.warn("write returned 0") return errno.EPROTO, bytes_written else: bytes_written += retval @@ -135,9 +283,30 @@ def write_fully(fd, buf): except OSError, e: return e.errno, bytes_written + def set_nonblocking(sock): try: sock.setblocking(0) except socket.error, e: - logging.error("could not set nonblocking mode on socket: %s" - % os.strerror(get_socket_error(e))) + vlog.err("could not set nonblocking mode on socket: %s" + % os.strerror(get_exception_errno(e))) + + +def set_dscp(sock, dscp): + if dscp > 63: + raise ValueError("Invalid dscp %d" % dscp) + + # Note: this function is used for both of IPv4 and IPv6 sockets + success = False + val = dscp << 2 + try: + sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, val) + except socket.error, e: + if e.errno != errno.ENOPROTOOPT: + raise + success = True + try: + sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_TCLASS, val) + except socket.error, e: + if e.errno != errno.ENOPROTOOPT or not success: + raise