tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

addrs-netlink.c (9376B)


      1 /*
      2 Copyright (c) 2011, The WebRTC project authors. All rights reserved.
      3 
      4 Redistribution and use in source and binary forms, with or without
      5 modification, are permitted provided that the following conditions are
      6 met:
      7 
      8  * Redistributions of source code must retain the above copyright
      9    notice, this list of conditions and the following disclaimer.
     10 
     11  * Redistributions in binary form must reproduce the above copyright
     12    notice, this list of conditions and the following disclaimer in
     13    the documentation and/or other materials provided with the
     14    distribution.
     15 
     16  * Neither the name of Google nor the names of its contributors may
     17    be used to endorse or promote products derived from this software
     18    without specific prior written permission.
     19 
     20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31 */
     32 
     33 #if defined(LINUX)
     34 #include <net/if.h>
     35 #include "addrs-netlink.h"
     36 #include <csi_platform.h>
     37 #include <assert.h>
     38 #include <string.h>
     39 #include "util.h"
     40 #include "stun_util.h"
     41 #include "util.h"
     42 #include <r_macros.h>
     43 
     44 #include <stdlib.h>
     45 #include <sys/types.h>
     46 #include <sys/socket.h>
     47 #include <sys/utsname.h>
     48 #include <sys/ioctl.h>
     49 #include <netinet/in.h>
     50 #include <unistd.h>
     51 #include <errno.h>
     52 #include <linux/netlink.h>
     53 #include <linux/rtnetlink.h>
     54 
     55 #ifdef ANDROID
     56 /* Work around an Android NDK < r8c bug */
     57 #undef __unused
     58 #else
     59 #include <linux/if.h> /* struct ifreq, IFF_POINTTOPOINT */
     60 #include <linux/wireless.h> /* struct iwreq */
     61 #include <linux/ethtool.h> /* struct ethtool_cmd */
     62 #include <linux/sockios.h> /* SIOCETHTOOL */
     63 #endif /* ANDROID */
     64 
     65 
     66 struct netlinkrequest {
     67  struct nlmsghdr header;
     68  struct ifaddrmsg msg;
     69 };
     70 
     71 static const int kMaxReadSize = 4096;
     72 
     73 static void set_ifname(nr_local_addr *addr, struct ifaddrmsg* msg) {
     74  assert(sizeof(addr->addr.ifname) > IF_NAMESIZE);
     75  if_indextoname(msg->ifa_index, addr->addr.ifname);
     76 }
     77 
     78 static int get_siocgifflags(nr_local_addr *addr) {
     79  int fd = socket(AF_INET, SOCK_DGRAM, 0);
     80  if (fd == -1) {
     81    assert(0);
     82    return 0;
     83  }
     84  struct ifreq ifr;
     85  memset(&ifr, 0, sizeof(ifr));
     86  strncpy(ifr.ifr_name, addr->addr.ifname, IFNAMSIZ - 1);
     87  int rc = ioctl(fd, SIOCGIFFLAGS, &ifr);
     88  close(fd);
     89  if (rc == -1) {
     90    assert(0);
     91    return 0;
     92  }
     93  return ifr.ifr_flags;
     94 }
     95 
     96 static int set_sockaddr(nr_local_addr *addr, struct ifaddrmsg* msg, struct rtattr* rta) {
     97  assert(rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL);
     98  void *data = RTA_DATA(rta);
     99  size_t len = RTA_PAYLOAD(rta);
    100  if (msg->ifa_family == AF_INET) {
    101    struct sockaddr_in sa;
    102    memset(&sa, 0, sizeof(struct sockaddr_in));
    103    sa.sin_family = AF_INET;
    104    memcpy(&sa.sin_addr, data, len);
    105    return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr));
    106  } else if (msg->ifa_family == AF_INET6) {
    107    struct sockaddr_in6 sa;
    108    memset(&sa, 0, sizeof(struct sockaddr_in6));
    109    sa.sin6_family = AF_INET6;
    110    /* We do not set sin6_scope_id to ifa_index, because that is only valid for
    111     * link local addresses, and we don't use those anyway */
    112    memcpy(&sa.sin6_addr, data, len);
    113    return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr));
    114  }
    115 
    116  return R_BAD_ARGS;
    117 }
    118 
    119 static int
    120 stun_ifaddr_is_disallowed_v6(int flags) {
    121  return flags & (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | IFA_F_DEPRECATED);
    122 }
    123 
    124 static int
    125 stun_convert_netlink(nr_local_addr *addr, struct ifaddrmsg *address_msg, struct rtattr* rta)
    126 {
    127  int r = set_sockaddr(addr, address_msg, rta);
    128  if (r) {
    129    r_log(NR_LOG_STUN, LOG_ERR, "set_sockaddr error r = %d", r);
    130    return r;
    131  }
    132 
    133  set_ifname(addr, address_msg);
    134 
    135  if (address_msg->ifa_flags & IFA_F_TEMPORARY) {
    136    addr->flags |= NR_ADDR_FLAG_TEMPORARY;
    137  }
    138 
    139  int flags = get_siocgifflags(addr);
    140  if (flags & IFF_POINTOPOINT)
    141  {
    142    addr->iface.type = NR_INTERFACE_TYPE_UNKNOWN | NR_INTERFACE_TYPE_VPN;
    143    /* TODO (Bug 896913): find backend network type of this VPN */
    144  }
    145 
    146 #if defined(LINUX) && !defined(ANDROID)
    147  struct ethtool_cmd ecmd;
    148  struct ifreq ifr;
    149  struct iwreq wrq;
    150  int e;
    151  int s = socket(AF_INET, SOCK_DGRAM, 0);
    152 
    153  strncpy(ifr.ifr_name, addr->addr.ifname, sizeof(ifr.ifr_name));
    154  /* TODO (Bug 896851): interface property for Android */
    155  /* Getting ethtool for ethernet information. */
    156  ecmd.cmd = ETHTOOL_GSET;
    157  /* In/out param */
    158  ifr.ifr_data = (void*)&ecmd;
    159 
    160  e = ioctl(s, SIOCETHTOOL, &ifr);
    161  if (e == 0)
    162  {
    163    /* For wireless network, we won't get ethtool, it's a wired
    164     * connection */
    165    addr->iface.type = NR_INTERFACE_TYPE_WIRED;
    166 #ifdef DONT_HAVE_ETHTOOL_SPEED_HI
    167    addr->iface.estimated_speed = ecmd.speed;
    168 #else
    169    addr->iface.estimated_speed = ((ecmd.speed_hi << 16) | ecmd.speed) * 1000;
    170 #endif
    171  }
    172 
    173  strncpy(wrq.ifr_name, addr->addr.ifname, sizeof(wrq.ifr_name));
    174  e = ioctl(s, SIOCGIWRATE, &wrq);
    175  if (e == 0)
    176  {
    177    addr->iface.type = NR_INTERFACE_TYPE_WIFI;
    178    addr->iface.estimated_speed = wrq.u.bitrate.value / 1000;
    179  }
    180 
    181  close(s);
    182 
    183 #else
    184  addr->iface.type = NR_INTERFACE_TYPE_UNKNOWN;
    185  addr->iface.estimated_speed = 0;
    186 #endif
    187  return 0;
    188 }
    189 
    190 int
    191 stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count)
    192 {
    193  int _status;
    194  int fd = 0;
    195 
    196  /* Scope everything else since we're using ABORT. */
    197  {
    198    *count = 0;
    199 
    200    if (maxaddrs <= 0)
    201      ABORT(R_BAD_ARGS);
    202 
    203    fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    204    if (fd < 0) {
    205      ABORT(R_INTERNAL);
    206    }
    207 
    208    struct netlinkrequest ifaddr_request;
    209    memset(&ifaddr_request, 0, sizeof(ifaddr_request));
    210    ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
    211    ifaddr_request.header.nlmsg_type = RTM_GETADDR;
    212    ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
    213 
    214    ssize_t bytes = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0);
    215    if ((size_t)bytes != ifaddr_request.header.nlmsg_len) {
    216      ABORT(R_INTERNAL);
    217    }
    218 
    219    char buf[kMaxReadSize];
    220    ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0);
    221    while ((amount_read > 0) && (*count != maxaddrs)) {
    222      struct nlmsghdr* header = (struct nlmsghdr*)&buf[0];
    223      size_t header_size = (size_t)amount_read;
    224      for ( ; NLMSG_OK(header, header_size) && (*count != maxaddrs);
    225            header = NLMSG_NEXT(header, header_size)) {
    226        switch (header->nlmsg_type) {
    227          case NLMSG_DONE:
    228            /* Success. Return. */
    229            close(fd);
    230            return 0;
    231          case NLMSG_ERROR:
    232            ABORT(R_INTERNAL);
    233          case RTM_NEWADDR: {
    234            struct ifaddrmsg* address_msg =
    235                (struct ifaddrmsg*)NLMSG_DATA(header);
    236            struct rtattr* rta = IFA_RTA(address_msg);
    237            ssize_t payload_len = IFA_PAYLOAD(header);
    238            bool found = false;
    239            while (RTA_OK(rta, payload_len)) {
    240              // This is a bit convoluted. IFA_ADDRESS and IFA_LOCAL are the
    241              // same thing except when using a POINTTOPOINT interface, in
    242              // which case IFA_LOCAL is the local address, and IFA_ADDRESS is
    243              // the remote address. In a reasonable world, that would mean we
    244              // could just use IFA_LOCAL all the time. Sadly, IFA_LOCAL is not
    245              // always set (IPv6 in particular). So, we have to be on the
    246              // lookout for both, and prefer IFA_LOCAL when present.
    247              if (rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL) {
    248                int family = address_msg->ifa_family;
    249                if ((family == AF_INET || family == AF_INET6) &&
    250                    !stun_ifaddr_is_disallowed_v6(address_msg->ifa_flags) &&
    251                    !stun_convert_netlink(&addrs[*count], address_msg, rta)) {
    252                  found = true;
    253                  if (rta->rta_type == IFA_LOCAL) {
    254                    // IFA_LOCAL is what we really want; if we find it we're
    255                    // done. If this is IFA_ADDRESS instead, we do not proceed
    256                    // yet, and allow a subsequent IFA_LOCAL to overwrite what
    257                    // we just put in |addrs|.
    258                    break;
    259                  }
    260                }
    261              }
    262              /* TODO: Use IFA_LABEL instead of if_indextoname? We would need
    263               * to remember how many nr_local_addr we've converted for this
    264               * ifaddrmsg, and set the label on all of them. */
    265              rta = RTA_NEXT(rta, payload_len);
    266            }
    267 
    268            if (found) {
    269              ++(*count);
    270            }
    271            break;
    272          }
    273        }
    274      }
    275      amount_read = recv(fd, &buf, kMaxReadSize, 0);
    276    }
    277  }
    278 
    279  _status=0;
    280 abort:
    281  close(fd);
    282  return(_status);
    283 }
    284 
    285 #endif  /* defined(LINUX) */