diff --git a/src/setup.py b/src/setup.py index 35d1f03d8f..99e7b5cc03 100755 --- a/src/setup.py +++ b/src/setup.py @@ -168,6 +168,7 @@ def is_RH(): minify_ENABLED = html5_ENABLED pam_ENABLED = DEFAULT and (server_ENABLED or proxy_ENABLED) and POSIX and not OSX and (os.path.exists("/usr/include/pam/pam_misc.h") or os.path.exists("/usr/include/security/pam_misc.h")) +netdev_ENABLED = LINUX and DEFAULT vsock_ENABLED = LINUX and os.path.exists("/usr/include/linux/vm_sockets.h") bencode_ENABLED = DEFAULT cython_bencode_ENABLED = DEFAULT @@ -218,7 +219,7 @@ def is_RH(): "v4l2", "dec_avcodec2", "csc_swscale", "csc_libyuv", - "bencode", "cython_bencode", "vsock", "mdns", + "bencode", "cython_bencode", "vsock", "netdev", "mdns", "clipboard", "server", "client", "dbus", "x11", "xinput", "uinput", "sd_listen", "gtk_x11", "service", @@ -2125,6 +2126,12 @@ def which(cmd): ["xpra/net/bencode/cython_bencode.pyx"], **bencode_pkgconfig)) +if netdev_ENABLED: + netdev_pkgconfig = pkgconfig() + cython_add(Extension("xpra.platform.xposix.netdev_query", + ["xpra/platform/xposix/netdev_query.pyx"], + **netdev_pkgconfig)) + if vsock_ENABLED: vsock_pkgconfig = pkgconfig() cython_add(Extension("xpra.net.vsock", diff --git a/src/xpra/client/client_base.py b/src/xpra/client/client_base.py index dd3381a508..f54e2950d9 100644 --- a/src/xpra/client/client_base.py +++ b/src/xpra/client/client_base.py @@ -380,6 +380,12 @@ def up(prefix, d): mid = get_machine_id() if mid: capabilities["machine_id"] = mid + #get socket speed if we have it: + pinfo = self._protocol.get_info() + netlog("protocol info=%s", pinfo) + socket_speed = pinfo.get("socket", {}).get("speed") + if socket_speed: + capabilities["connection-data"] = {"speed" : socket_speed} if self.encryption: assert self.encryption in ENCRYPTION_CIPHERS diff --git a/src/xpra/net/bytestreams.py b/src/xpra/net/bytestreams.py index 3ffa700f24..85de1514af 100644 --- a/src/xpra/net/bytestreams.py +++ b/src/xpra/net/bytestreams.py @@ -320,7 +320,18 @@ def do_get_socket_info(self): except: pass try: - info["fileno"] = s.fileno() + fd = s.fileno() + info["fileno"] = fd + from xpra.platform.netdev_query import get_interface_speed + #ie: self.local = ("192.168.1.7", "14500") + if self.local and len(self.local)==2: + from xpra.net.net_util import get_interface + iface = get_interface(self.local[0]) + #ie: iface = "eth0" + if iface and iface!="lo": + s = get_interface_speed(fd, iface) + if s>0: + info["speed"] = s except: pass return info diff --git a/src/xpra/net/net_util.py b/src/xpra/net/net_util.py index 404e583b91..5e4082e43f 100755 --- a/src/xpra/net/net_util.py +++ b/src/xpra/net/net_util.py @@ -21,7 +21,7 @@ netifaces_version = netifaces.version #@UndefinedVariable except: has_netifaces = False - log.warn("python netifaces package is missing") + log.warn("Warning: the python netifaces package is missing") iface_ipmasks = {} bind_IPs = None @@ -36,8 +36,28 @@ def get_free_tcp_port(): def get_interfaces(): if not has_netifaces: - return [] - return netifaces.interfaces() #@UndefinedVariable + return [] + return netifaces.interfaces() #@UndefinedVariable + +def get_interfaces_addresses(): + d = {} + for iface in get_interfaces(): + d[iface] = netifaces.ifaddresses(iface) #@UndefinedVariable + return d + +def get_interface(address): + for iface, idefs in get_interfaces_addresses().items(): + #ie: { + # 17: [{'broadcast': u'ff:ff:ff:ff:ff:ff', 'addr': u'00:e0:4c:68:46:a6'}], + # 2: [{'broadcast': u'192.168.1.255', 'netmask': u'255.255.255.0', 'addr': u'192.168.1.7'}], + # 10: [{'netmask': u'ffff:ffff:ffff:ffff::/64', 'addr': u'fe80::6c45:655:c59e:92a1%eth0'}] + #} + for _itype, defs in idefs.items(): + #ie: itype=2, defs=[{'broadcast': u'192.168.1.255', 'netmask': u'255.255.255.0', 'addr': u'192.168.1.7'}] + for props in defs: + if props.get("addr")==address: + return iface + return None def get_gateways(): if not has_netifaces: diff --git a/src/xpra/platform/netdev_query.py b/src/xpra/platform/netdev_query.py new file mode 100644 index 0000000000..8a408d9e37 --- /dev/null +++ b/src/xpra/platform/netdev_query.py @@ -0,0 +1,12 @@ +# This file is part of Xpra. +# Copyright (C) 2017 Antoine Martin +# Xpra is released under the terms of the GNU GPL v2, or, at your option, any +# later version. See the file COPYING for details. + +def get_interface_speed(*_args): + return 0 + +from xpra.platform import platform_import +platform_import(globals(), "netdev_query", False, + "get_interface_speed", + ) diff --git a/src/xpra/platform/xposix/netdev_query.pyx b/src/xpra/platform/xposix/netdev_query.pyx new file mode 100644 index 0000000000..dd700db461 --- /dev/null +++ b/src/xpra/platform/xposix/netdev_query.pyx @@ -0,0 +1,72 @@ +# This file is part of Xpra. +# Copyright (C) 2017 Antoine Martin +# Xpra is released under the terms of the GNU GPL v2, or, at your option, any +# later version. See the file COPYING for details. + +from __future__ import absolute_import + +import os + +from libc.stdint cimport uintptr_t, uint32_t, uint16_t, uint8_t + +from xpra.log import Logger +log = Logger("util", "network") + +ctypedef uint32_t __u32 +ctypedef uint16_t __u16 +ctypedef uint8_t __u8 +cdef extern from "linux/ethtool.h": + int ETHTOOL_GSET + cdef struct ethtool_cmd: + __u32 cmd + __u32 supported + __u32 advertising + __u16 speed + __u8 duplex + __u8 port + __u8 phy_address + __u8 transceiver + __u8 autoneg + __u8 mdio_support + __u32 maxtxpkt + __u32 maxrxpkt + __u16 speed_hi + __u8 eth_tp_mdix + __u8 eth_tp_mdix_ctrl + __u32 lp_advertising + __u32 reserved[2] + + +cdef extern from "linux/sockios.h": + int SIOCETHTOOL + +cdef extern from "net/if.h": + DEF IFNAMSIZ=16 + cdef struct ifr_ifrn: + char ifrn_name[IFNAMSIZ] + cdef struct ifr_ifru: + int ifru_flags + int ifru_ivalue + int ifru_mtu + void *ifru_data + cdef struct ifreq: + ifr_ifrn ifr_ifrn + ifr_ifru ifr_ifru + +cdef extern from "sys/ioctl.h": + int ioctl(int fd, unsigned long request, ...) + + +def get_interface_speed(int sockfd, char *ifname): + """ returns the ethtool speed in Mbps, or 0 """ + cdef ifreq ifr + cdef ethtool_cmd edata + ifr.ifr_ifrn.ifrn_name = ifname + ifr.ifr_ifru.ifru_data = &edata + edata.cmd = ETHTOOL_GSET + cdef int r = ioctl(sockfd, SIOCETHTOOL, &ifr) + if r < 0: + log.warn("Warning: failed to query %s device properties with SIOCETHTOOL", ifname) + log.warn(" error %i", r) + return 0 + return edata.speed*1000*1000