tor-browser

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

NetlinkService.cpp (59559B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set et sw=2 ts=4: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include <arpa/inet.h>
      8 #include <netinet/ether.h>
      9 #include <net/if.h>
     10 #include <poll.h>
     11 #include <unistd.h>
     12 #include <linux/rtnetlink.h>
     13 #include <ifaddrs.h>
     14 #include <netinet/in.h>
     15 #include <arpa/inet.h>
     16 
     17 #include "nsThreadUtils.h"
     18 #include "NetlinkService.h"
     19 #include "nsIThread.h"
     20 #include "nsString.h"
     21 #include "nsPrintfCString.h"
     22 #include "mozilla/Logging.h"
     23 #include "../../base/IPv6Utils.h"
     24 #include "../LinkServiceCommon.h"
     25 #include "../NetworkLinkServiceDefines.h"
     26 
     27 #include "mozilla/Base64.h"
     28 #include "mozilla/FunctionTypeTraits.h"
     29 #include "mozilla/ProfilerThreadSleep.h"
     30 #include "mozilla/glean/NetwerkMetrics.h"
     31 #include "mozilla/DebugOnly.h"
     32 
     33 #if defined(HAVE_RES_NINIT)
     34 #  include <netinet/in.h>
     35 #  include <resolv.h>
     36 #endif
     37 
     38 namespace mozilla::net {
     39 
     40 template <typename F>
     41 static auto eintr_retry(F&& func) ->
     42    typename FunctionTypeTraits<decltype(func)>::ReturnType {
     43  typename FunctionTypeTraits<decltype(func)>::ReturnType _rc;
     44  do {
     45    _rc = func();
     46  } while (_rc == -1 && errno == EINTR);
     47  return _rc;
     48 }
     49 
     50 #define EINTR_RETRY(expr) eintr_retry([&]() { return expr; })
     51 
     52 // period during which to absorb subsequent network change events, in
     53 // milliseconds
     54 static const unsigned int kNetworkChangeCoalescingPeriod = 1000;
     55 
     56 static LazyLogModule gNlSvcLog("NetlinkService");
     57 #define LOG(args) MOZ_LOG(gNlSvcLog, mozilla::LogLevel::Debug, args)
     58 
     59 #undef LOG_ENABLED
     60 #define LOG_ENABLED() MOZ_LOG_TEST(gNlSvcLog, mozilla::LogLevel::Debug)
     61 
     62 using in_common_addr = union {
     63  struct in_addr addr4;
     64  struct in6_addr addr6;
     65 };
     66 
     67 static void GetAddrStr(const in_common_addr* aAddr, uint8_t aFamily,
     68                       nsACString& _retval) {
     69  char addr[INET6_ADDRSTRLEN];
     70  addr[0] = 0;
     71 
     72  if (aFamily == AF_INET) {
     73    inet_ntop(AF_INET, &(aAddr->addr4), addr, INET_ADDRSTRLEN);
     74  } else {
     75    inet_ntop(AF_INET6, &(aAddr->addr6), addr, INET6_ADDRSTRLEN);
     76  }
     77  _retval.Assign(addr);
     78 }
     79 
     80 // Assume true by default.
     81 static Atomic<bool, MemoryOrdering::Relaxed> sHasNonLocalIPv6{true};
     82 // static
     83 bool NetlinkService::HasNonLocalIPv6Address() { return sHasNonLocalIPv6; }
     84 
     85 class NetlinkAddress {
     86 public:
     87  NetlinkAddress() = default;
     88 
     89  uint8_t Family() const { return mIfam.ifa_family; }
     90  uint32_t GetIndex() const { return mIfam.ifa_index; }
     91  uint8_t GetPrefixLen() const { return mIfam.ifa_prefixlen; }
     92  bool ScopeIsUniverse() const { return mIfam.ifa_scope == RT_SCOPE_UNIVERSE; }
     93  const in_common_addr* GetAddrPtr() const { return &mAddr; }
     94 
     95  bool MsgEquals(const NetlinkAddress& aOther) const {
     96    return !memcmp(&mIfam, &(aOther.mIfam), sizeof(mIfam));
     97  }
     98 
     99  bool Equals(const NetlinkAddress& aOther) const {
    100    if (mIfam.ifa_family != aOther.mIfam.ifa_family) {
    101      return false;
    102    }
    103    if (mIfam.ifa_index != aOther.mIfam.ifa_index) {
    104      // addresses are different when they are on a different interface
    105      return false;
    106    }
    107    if (mIfam.ifa_prefixlen != aOther.mIfam.ifa_prefixlen) {
    108      // It's possible to have two equal addresses with a different netmask on
    109      // the same interface, so we need to check prefixlen too.
    110      return false;
    111    }
    112    size_t addrSize = (mIfam.ifa_family == AF_INET) ? sizeof(mAddr.addr4)
    113                                                    : sizeof(mAddr.addr6);
    114    return memcmp(&mAddr, aOther.GetAddrPtr(), addrSize) == 0;
    115  }
    116 
    117  bool ContainsAddr(const in_common_addr* aAddr) {
    118    int32_t addrSize = (mIfam.ifa_family == AF_INET)
    119                           ? (int32_t)sizeof(mAddr.addr4)
    120                           : (int32_t)sizeof(mAddr.addr6);
    121    uint8_t maskit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
    122    int32_t bits = mIfam.ifa_prefixlen;
    123    if (bits > addrSize * 8) {
    124      MOZ_ASSERT(false, "Unexpected prefix length!");
    125      LOG(("Unexpected prefix length %d, maximum for this family is %d", bits,
    126           addrSize * 8));
    127      return false;
    128    }
    129    for (int32_t i = 0; i < addrSize; i++) {
    130      uint8_t mask = (bits >= 8) ? 0xff : maskit[bits];
    131      if ((((unsigned char*)aAddr)[i] & mask) !=
    132          (((unsigned char*)(&mAddr))[i] & mask)) {
    133        return false;
    134      }
    135      bits -= 8;
    136      if (bits <= 0) {
    137        return true;
    138      }
    139    }
    140    return true;
    141  }
    142 
    143  bool Init(struct nlmsghdr* aNlh) {
    144    struct ifaddrmsg* ifam;
    145    struct rtattr* attr;
    146    int len;
    147 
    148    ifam = (ifaddrmsg*)NLMSG_DATA(aNlh);
    149    len = IFA_PAYLOAD(aNlh);
    150 
    151    if (ifam->ifa_family != AF_INET && ifam->ifa_family != AF_INET6) {
    152      return false;
    153    }
    154 
    155    bool hasAddr = false;
    156    for (attr = IFA_RTA(ifam); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
    157      if (attr->rta_type == IFA_ADDRESS || attr->rta_type == IFA_LOCAL) {
    158        memcpy(&mAddr, RTA_DATA(attr),
    159               ifam->ifa_family == AF_INET ? sizeof(mAddr.addr4)
    160                                           : sizeof(mAddr.addr6));
    161        hasAddr = true;
    162        if (attr->rta_type == IFA_LOCAL) {
    163          // local address is preferred, so don't continue parsing other
    164          // attributes
    165          break;
    166        }
    167      }
    168    }
    169 
    170    if (!hasAddr) {
    171      return false;
    172    }
    173 
    174    memcpy(&mIfam, (ifaddrmsg*)NLMSG_DATA(aNlh), sizeof(mIfam));
    175    return true;
    176  }
    177 
    178 private:
    179  in_common_addr mAddr;
    180  struct ifaddrmsg mIfam;
    181 };
    182 
    183 class NetlinkNeighbor {
    184 public:
    185  NetlinkNeighbor() = default;
    186 
    187  uint8_t Family() const { return mNeigh.ndm_family; }
    188  uint32_t GetIndex() const { return mNeigh.ndm_ifindex; }
    189  const in_common_addr* GetAddrPtr() const { return &mAddr; }
    190  const uint8_t* GetMACPtr() const { return mMAC; }
    191  bool HasMAC() const { return mHasMAC; }
    192 
    193  void GetAsString(nsACString& _retval) const {
    194    nsAutoCString addrStr;
    195    _retval.Assign("addr=");
    196    GetAddrStr(&mAddr, mNeigh.ndm_family, addrStr);
    197    _retval.Append(addrStr);
    198    if (mNeigh.ndm_family == AF_INET) {
    199      _retval.Append(" family=AF_INET if=");
    200    } else {
    201      _retval.Append(" family=AF_INET6 if=");
    202    }
    203    _retval.AppendInt(mNeigh.ndm_ifindex);
    204    if (mHasMAC) {
    205      _retval.Append(" mac=");
    206      _retval.Append(nsPrintfCString("%02x:%02x:%02x:%02x:%02x:%02x", mMAC[0],
    207                                     mMAC[1], mMAC[2], mMAC[3], mMAC[4],
    208                                     mMAC[5]));
    209    }
    210  }
    211 
    212  bool Init(struct nlmsghdr* aNlh) {
    213    struct ndmsg* neigh;
    214    struct rtattr* attr;
    215    int len;
    216 
    217    neigh = (ndmsg*)NLMSG_DATA(aNlh);
    218    len = aNlh->nlmsg_len - NLMSG_LENGTH(sizeof(*neigh));
    219 
    220    if (neigh->ndm_family != AF_INET && neigh->ndm_family != AF_INET6) {
    221      return false;
    222    }
    223 
    224    bool hasDST = false;
    225    for (attr = RTM_RTA(neigh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
    226      if (attr->rta_type == NDA_LLADDR) {
    227        memcpy(mMAC, RTA_DATA(attr), ETH_ALEN);
    228        mHasMAC = true;
    229      }
    230 
    231      if (attr->rta_type == NDA_DST) {
    232        memcpy(&mAddr, RTA_DATA(attr),
    233               neigh->ndm_family == AF_INET ? sizeof(mAddr.addr4)
    234                                            : sizeof(mAddr.addr6));
    235        hasDST = true;
    236      }
    237    }
    238 
    239    if (!hasDST) {
    240      return false;
    241    }
    242 
    243    memcpy(&mNeigh, (ndmsg*)NLMSG_DATA(aNlh), sizeof(mNeigh));
    244    return true;
    245  }
    246 
    247 private:
    248  bool mHasMAC{false};
    249  uint8_t mMAC[ETH_ALEN]{};
    250  in_common_addr mAddr{};
    251  struct ndmsg mNeigh{};
    252 };
    253 
    254 class NetlinkLink {
    255 public:
    256  NetlinkLink() = default;
    257 
    258  bool IsUp() const {
    259    return (mIface.ifi_flags & IFF_RUNNING) &&
    260           !(mIface.ifi_flags & IFF_LOOPBACK);
    261  }
    262 
    263  void GetName(nsACString& _retval) const { _retval = mName; }
    264  bool IsTypeEther() const { return mIface.ifi_type == ARPHRD_ETHER; }
    265  uint32_t GetIndex() const { return mIface.ifi_index; }
    266  uint32_t GetFlags() const { return mIface.ifi_flags; }
    267  uint16_t GetType() const { return mIface.ifi_type; }
    268 
    269  bool Init(struct nlmsghdr* aNlh) {
    270    struct ifinfomsg* iface;
    271    struct rtattr* attr;
    272    int len;
    273 
    274    iface = (ifinfomsg*)NLMSG_DATA(aNlh);
    275    len = aNlh->nlmsg_len - NLMSG_LENGTH(sizeof(*iface));
    276 
    277    bool hasName = false;
    278    for (attr = IFLA_RTA(iface); RTA_OK(attr, len);
    279         attr = RTA_NEXT(attr, len)) {
    280      if (attr->rta_type == IFLA_IFNAME) {
    281        mName.Assign((char*)RTA_DATA(attr));
    282        hasName = true;
    283        break;
    284      }
    285    }
    286 
    287    if (!hasName) {
    288      return false;
    289    }
    290 
    291    memcpy(&mIface, (ifinfomsg*)NLMSG_DATA(aNlh), sizeof(mIface));
    292    return true;
    293  }
    294 
    295 private:
    296  nsCString mName;
    297  struct ifinfomsg mIface{};
    298 };
    299 
    300 class NetlinkRoute {
    301 public:
    302  NetlinkRoute()
    303      : mHasGWAddr(false),
    304        mHasPrefSrcAddr(false),
    305        mHasDstAddr(false),
    306        mHasOif(false),
    307        mHasPrio(false) {}
    308 
    309  bool IsUnicast() const { return mRtm.rtm_type == RTN_UNICAST; }
    310  bool ScopeIsUniverse() const { return mRtm.rtm_scope == RT_SCOPE_UNIVERSE; }
    311  bool IsDefault() const { return mRtm.rtm_dst_len == 0; }
    312  bool HasOif() const { return mHasOif; }
    313  uint8_t Oif() const { return mOif; }
    314  uint8_t Family() const { return mRtm.rtm_family; }
    315  bool HasPrefSrcAddr() const { return mHasPrefSrcAddr; }
    316  const in_common_addr* GetGWAddrPtr() const {
    317    return mHasGWAddr ? &mGWAddr : nullptr;
    318  }
    319  const in_common_addr* GetPrefSrcAddrPtr() const {
    320    return mHasPrefSrcAddr ? &mPrefSrcAddr : nullptr;
    321  }
    322 
    323  bool Equals(const NetlinkRoute& aOther) const {
    324    size_t addrSize = (mRtm.rtm_family == AF_INET) ? sizeof(mDstAddr.addr4)
    325                                                   : sizeof(mDstAddr.addr6);
    326    if (memcmp(&mRtm, &(aOther.mRtm), sizeof(mRtm)) != 0) {
    327      return false;
    328    }
    329    if (mHasOif != aOther.mHasOif || mOif != aOther.mOif) {
    330      return false;
    331    }
    332    if (mHasPrio != aOther.mHasPrio || mPrio != aOther.mPrio) {
    333      return false;
    334    }
    335    if ((mHasGWAddr != aOther.mHasGWAddr) ||
    336        (mHasGWAddr && memcmp(&mGWAddr, &(aOther.mGWAddr), addrSize) != 0)) {
    337      return false;
    338    }
    339    if ((mHasDstAddr != aOther.mHasDstAddr) ||
    340        (mHasDstAddr && memcmp(&mDstAddr, &(aOther.mDstAddr), addrSize) != 0)) {
    341      return false;
    342    }
    343    if ((mHasPrefSrcAddr != aOther.mHasPrefSrcAddr) ||
    344        (mHasPrefSrcAddr &&
    345         memcmp(&mPrefSrcAddr, &(aOther.mPrefSrcAddr), addrSize) != 0)) {
    346      return false;
    347    }
    348    return true;
    349  }
    350 
    351  bool GatewayEquals(const NetlinkNeighbor& aNeigh) const {
    352    if (!mHasGWAddr) {
    353      return false;
    354    }
    355    if (aNeigh.Family() != mRtm.rtm_family) {
    356      return false;
    357    }
    358    size_t addrSize = (mRtm.rtm_family == AF_INET) ? sizeof(mGWAddr.addr4)
    359                                                   : sizeof(mGWAddr.addr6);
    360    return memcmp(&mGWAddr, aNeigh.GetAddrPtr(), addrSize) == 0;
    361  }
    362 
    363  bool GatewayEquals(const NetlinkRoute* aRoute) const {
    364    if (!mHasGWAddr || !aRoute->mHasGWAddr) {
    365      return false;
    366    }
    367    if (mRtm.rtm_family != aRoute->mRtm.rtm_family) {
    368      return false;
    369    }
    370    size_t addrSize = (mRtm.rtm_family == AF_INET) ? sizeof(mGWAddr.addr4)
    371                                                   : sizeof(mGWAddr.addr6);
    372    return memcmp(&mGWAddr, &(aRoute->mGWAddr), addrSize) == 0;
    373  }
    374 
    375  bool PrefSrcAddrEquals(const NetlinkAddress& aAddress) const {
    376    if (!mHasPrefSrcAddr) {
    377      return false;
    378    }
    379    if (mRtm.rtm_family != aAddress.Family()) {
    380      return false;
    381    }
    382    size_t addrSize = (mRtm.rtm_family == AF_INET) ? sizeof(mPrefSrcAddr.addr4)
    383                                                   : sizeof(mPrefSrcAddr.addr6);
    384    return memcmp(&mPrefSrcAddr, aAddress.GetAddrPtr(), addrSize) == 0;
    385  }
    386 
    387  void GetAsString(nsACString& _retval) const {
    388    nsAutoCString addrStr;
    389    _retval.Assign("table=");
    390    _retval.AppendInt(mRtm.rtm_table);
    391    _retval.Append(" type=");
    392    _retval.AppendInt(mRtm.rtm_type);
    393    _retval.Append(" scope=");
    394    _retval.AppendInt(mRtm.rtm_scope);
    395    if (mRtm.rtm_family == AF_INET) {
    396      _retval.Append(" family=AF_INET dst=");
    397      addrStr.Assign("0.0.0.0/");
    398    } else {
    399      _retval.Append(" family=AF_INET6 dst=");
    400      addrStr.Assign("::/");
    401    }
    402    if (mHasDstAddr) {
    403      GetAddrStr(&mDstAddr, mRtm.rtm_family, addrStr);
    404      addrStr.Append("/");
    405    }
    406    _retval.Append(addrStr);
    407    _retval.AppendInt(mRtm.rtm_dst_len);
    408    if (mHasPrefSrcAddr) {
    409      _retval.Append(" src=");
    410      GetAddrStr(&mPrefSrcAddr, mRtm.rtm_family, addrStr);
    411      _retval.Append(addrStr);
    412    }
    413    if (mHasGWAddr) {
    414      _retval.Append(" via=");
    415      GetAddrStr(&mGWAddr, mRtm.rtm_family, addrStr);
    416      _retval.Append(addrStr);
    417    }
    418    if (mHasOif) {
    419      _retval.Append(" oif=");
    420      _retval.AppendInt(mOif);
    421    }
    422    if (mHasPrio) {
    423      _retval.Append(" prio=");
    424      _retval.AppendInt(mPrio);
    425    }
    426  }
    427 
    428  bool Init(struct nlmsghdr* aNlh) {
    429    struct rtmsg* rtm;
    430    struct rtattr* attr;
    431    int len;
    432 
    433    rtm = (rtmsg*)NLMSG_DATA(aNlh);
    434    len = RTM_PAYLOAD(aNlh);
    435 
    436    if (rtm->rtm_family != AF_INET && rtm->rtm_family != AF_INET6) {
    437      return false;
    438    }
    439 
    440    for (attr = RTM_RTA(rtm); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
    441      if (attr->rta_type == RTA_DST) {
    442        memcpy(&mDstAddr, RTA_DATA(attr),
    443               (rtm->rtm_family == AF_INET) ? sizeof(mDstAddr.addr4)
    444                                            : sizeof(mDstAddr.addr6));
    445        mHasDstAddr = true;
    446      } else if (attr->rta_type == RTA_GATEWAY) {
    447        memcpy(&mGWAddr, RTA_DATA(attr),
    448               (rtm->rtm_family == AF_INET) ? sizeof(mGWAddr.addr4)
    449                                            : sizeof(mGWAddr.addr6));
    450        mHasGWAddr = true;
    451      } else if (attr->rta_type == RTA_PREFSRC) {
    452        memcpy(&mPrefSrcAddr, RTA_DATA(attr),
    453               (rtm->rtm_family == AF_INET) ? sizeof(mPrefSrcAddr.addr4)
    454                                            : sizeof(mPrefSrcAddr.addr6));
    455        mHasPrefSrcAddr = true;
    456      } else if (attr->rta_type == RTA_OIF) {
    457        mOif = *(uint32_t*)RTA_DATA(attr);
    458        mHasOif = true;
    459      } else if (attr->rta_type == RTA_PRIORITY) {
    460        mPrio = *(uint32_t*)RTA_DATA(attr);
    461        mHasPrio = true;
    462      }
    463    }
    464 
    465    memcpy(&mRtm, (rtmsg*)NLMSG_DATA(aNlh), sizeof(mRtm));
    466    return true;
    467  }
    468 
    469 private:
    470  bool mHasGWAddr : 1;
    471  bool mHasPrefSrcAddr : 1;
    472  bool mHasDstAddr : 1;
    473  bool mHasOif : 1;
    474  bool mHasPrio : 1;
    475 
    476  in_common_addr mGWAddr{};
    477  in_common_addr mDstAddr{};
    478  in_common_addr mPrefSrcAddr{};
    479 
    480  uint32_t mOif{};
    481  uint32_t mPrio{};
    482  struct rtmsg mRtm{};
    483 };
    484 
    485 class NetlinkMsg {
    486 public:
    487  static uint8_t const kGenMsg = 1;
    488  static uint8_t const kRtMsg = 2;
    489 
    490  NetlinkMsg() = default;
    491  virtual ~NetlinkMsg() = default;
    492 
    493  virtual bool Send(int aFD) = 0;
    494  virtual bool IsPending() { return mIsPending; }
    495  virtual uint32_t SeqId() = 0;
    496  virtual uint8_t Family() = 0;
    497  virtual uint8_t MsgType() = 0;
    498 
    499 protected:
    500  bool SendRequest(int aFD, void* aRequest, uint32_t aRequestLength) {
    501    MOZ_ASSERT(!mIsPending, "Request has been already sent!");
    502 
    503    struct sockaddr_nl kernel{};
    504    memset(&kernel, 0, sizeof(kernel));
    505    kernel.nl_family = AF_NETLINK;
    506    kernel.nl_groups = 0;
    507 
    508    struct iovec io{};
    509    memset(&io, 0, sizeof(io));
    510    io.iov_base = aRequest;
    511    io.iov_len = aRequestLength;
    512 
    513    struct msghdr rtnl_msg{};
    514    memset(&rtnl_msg, 0, sizeof(rtnl_msg));
    515    rtnl_msg.msg_iov = &io;
    516    rtnl_msg.msg_iovlen = 1;
    517    rtnl_msg.msg_name = &kernel;
    518    rtnl_msg.msg_namelen = sizeof(kernel);
    519 
    520    ssize_t rc = EINTR_RETRY(sendmsg(aFD, (struct msghdr*)&rtnl_msg, 0));
    521    if (rc > 0 && (uint32_t)rc == aRequestLength) {
    522      mIsPending = true;
    523    }
    524 
    525    return mIsPending;
    526  }
    527 
    528  bool mIsPending{false};
    529 };
    530 
    531 class NetlinkGenMsg : public NetlinkMsg {
    532 public:
    533  NetlinkGenMsg(uint16_t aMsgType, uint8_t aFamily, uint32_t aSeqId) {
    534    memset(&mReq, 0, sizeof(mReq));
    535 
    536    mReq.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
    537    mReq.hdr.nlmsg_type = aMsgType;
    538    mReq.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    539    mReq.hdr.nlmsg_seq = aSeqId;
    540    mReq.hdr.nlmsg_pid = 0;
    541 
    542    mReq.gen.rtgen_family = aFamily;
    543  }
    544 
    545  virtual bool Send(int aFD) {
    546    return SendRequest(aFD, &mReq, mReq.hdr.nlmsg_len);
    547  }
    548 
    549  virtual uint32_t SeqId() { return mReq.hdr.nlmsg_seq; }
    550  virtual uint8_t Family() { return mReq.gen.rtgen_family; }
    551  virtual uint8_t MsgType() { return kGenMsg; }
    552 
    553 private:
    554  struct {
    555    struct nlmsghdr hdr;
    556    struct rtgenmsg gen;
    557  } mReq{};
    558 };
    559 
    560 class NetlinkRtMsg : public NetlinkMsg {
    561 public:
    562  NetlinkRtMsg(uint8_t aFamily, void* aAddress, uint32_t aSeqId) {
    563    MOZ_ASSERT(aFamily == AF_INET || aFamily == AF_INET6);
    564 
    565    memset(&mReq, 0, sizeof(mReq));
    566 
    567    mReq.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    568    mReq.hdr.nlmsg_type = RTM_GETROUTE;
    569    mReq.hdr.nlmsg_flags = NLM_F_REQUEST;
    570    mReq.hdr.nlmsg_seq = aSeqId;
    571    mReq.hdr.nlmsg_pid = 0;
    572 
    573    mReq.rtm.rtm_family = aFamily;
    574    mReq.rtm.rtm_flags = 0;
    575    mReq.rtm.rtm_dst_len = aFamily == AF_INET ? 32 : 128;
    576 
    577    struct rtattr* rta;
    578    rta = (struct rtattr*)(((char*)&mReq) + NLMSG_ALIGN(mReq.hdr.nlmsg_len));
    579    rta->rta_type = RTA_DST;
    580    size_t addrSize =
    581        aFamily == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr);
    582    rta->rta_len = RTA_LENGTH(addrSize);
    583    memcpy(RTA_DATA(rta), aAddress, addrSize);
    584    mReq.hdr.nlmsg_len = NLMSG_ALIGN(mReq.hdr.nlmsg_len) + RTA_LENGTH(addrSize);
    585  }
    586 
    587  virtual bool Send(int aFD) {
    588    return SendRequest(aFD, &mReq, mReq.hdr.nlmsg_len);
    589  }
    590 
    591  virtual uint32_t SeqId() { return mReq.hdr.nlmsg_seq; }
    592  virtual uint8_t Family() { return mReq.rtm.rtm_family; }
    593  virtual uint8_t MsgType() { return kRtMsg; }
    594 
    595 private:
    596  struct {
    597    struct nlmsghdr hdr;
    598    struct rtmsg rtm;
    599    unsigned char data[1024];
    600  } mReq{};
    601 };
    602 
    603 NetlinkService::LinkInfo::LinkInfo(UniquePtr<NetlinkLink>&& aLink)
    604    : mLink(std::move(aLink)), mIsUp(false) {}
    605 
    606 NetlinkService::LinkInfo::~LinkInfo() = default;
    607 
    608 bool NetlinkService::LinkInfo::UpdateStatus() {
    609  LOG(("NetlinkService::LinkInfo::UpdateStatus"));
    610 
    611  bool oldIsUp = mIsUp;
    612  mIsUp = false;
    613 
    614  if (!mLink->IsUp()) {
    615    // The link is not up or is a loopback
    616    LOG(("The link is down or is a loopback"));
    617  } else {
    618    // Link is up when there is non-local address associated with it.
    619    for (uint32_t i = 0; i < mAddresses.Length(); ++i) {
    620      if (LOG_ENABLED()) {
    621        nsAutoCString dbgStr;
    622        GetAddrStr(mAddresses[i]->GetAddrPtr(), mAddresses[i]->Family(),
    623                   dbgStr);
    624        LOG(("checking address %s", dbgStr.get()));
    625      }
    626      if (mAddresses[i]->ScopeIsUniverse()) {
    627        mIsUp = true;
    628        LOG(("global address found"));
    629        break;
    630      }
    631    }
    632  }
    633 
    634  return mIsUp == oldIsUp;
    635 }
    636 
    637 NS_IMPL_ISUPPORTS(NetlinkService, nsIRunnable)
    638 
    639 NetlinkService::NetlinkService() : mPid(getpid()) {}
    640 
    641 NetlinkService::~NetlinkService() {
    642  MOZ_ASSERT(!mThread, "NetlinkService thread shutdown failed");
    643 
    644  if (mShutdownPipe[0] != -1) {
    645    EINTR_RETRY(close(mShutdownPipe[0]));
    646  }
    647  if (mShutdownPipe[1] != -1) {
    648    EINTR_RETRY(close(mShutdownPipe[1]));
    649  }
    650 }
    651 
    652 void NetlinkService::OnNetlinkMessage(int aNetlinkSocket) {
    653  // The buffer size 4096 is a common page size, which is a recommended limit
    654  // for netlink messages.
    655  char buffer[4096];
    656 
    657  struct sockaddr_nl kernel{};
    658  memset(&kernel, 0, sizeof(kernel));
    659  kernel.nl_family = AF_NETLINK;
    660  kernel.nl_groups = 0;
    661 
    662  struct iovec io{};
    663  memset(&io, 0, sizeof(io));
    664  io.iov_base = buffer;
    665  io.iov_len = sizeof(buffer);
    666 
    667  struct msghdr rtnl_reply{};
    668  memset(&rtnl_reply, 0, sizeof(rtnl_reply));
    669  rtnl_reply.msg_iov = &io;
    670  rtnl_reply.msg_iovlen = 1;
    671  rtnl_reply.msg_name = &kernel;
    672  rtnl_reply.msg_namelen = sizeof(kernel);
    673 
    674  ssize_t rc = EINTR_RETRY(recvmsg(aNetlinkSocket, &rtnl_reply, MSG_DONTWAIT));
    675  if (rc < 0) {
    676    return;
    677  }
    678  size_t netlink_bytes = rc;
    679 
    680  struct nlmsghdr* nlh = reinterpret_cast<struct nlmsghdr*>(buffer);
    681 
    682  for (; NLMSG_OK(nlh, netlink_bytes); nlh = NLMSG_NEXT(nlh, netlink_bytes)) {
    683    // If PID in the message is our PID, then it's a response to our request.
    684    // Otherwise it's a multicast message.
    685    bool isResponse = (pid_t)nlh->nlmsg_pid == mPid;
    686    if (isResponse) {
    687      if (!mOutgoingMessages.Length() || !mOutgoingMessages[0]->IsPending()) {
    688        // There is no enqueued message pending?
    689        LOG((
    690            "Ignoring message seq_id %u, because there is no associated message"
    691            " pending",
    692            nlh->nlmsg_seq));
    693        continue;
    694      }
    695 
    696      if (mOutgoingMessages[0]->SeqId() != nlh->nlmsg_seq) {
    697        LOG(("Received unexpected seq_id [received=%u, expected=%u]",
    698             nlh->nlmsg_seq, mOutgoingMessages[0]->SeqId()));
    699        RemovePendingMsg();
    700        continue;
    701      }
    702    }
    703 
    704    switch (nlh->nlmsg_type) {
    705      case NLMSG_DONE: /* Message signalling end of dump for responses to
    706                          request containing NLM_F_DUMP flag */
    707        LOG(("received NLMSG_DONE"));
    708        if (isResponse) {
    709          RemovePendingMsg();
    710        }
    711        break;
    712      case NLMSG_ERROR:
    713        LOG(("received NLMSG_ERROR"));
    714        if (isResponse) {
    715          if (mOutgoingMessages[0]->MsgType() == NetlinkMsg::kRtMsg) {
    716            OnRouteCheckResult(nullptr);
    717          }
    718          RemovePendingMsg();
    719        }
    720        break;
    721      case RTM_NEWLINK:
    722      case RTM_DELLINK:
    723        MOZ_ASSERT(!isResponse ||
    724                   (nlh->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI);
    725        OnLinkMessage(nlh);
    726        break;
    727      case RTM_NEWADDR:
    728      case RTM_DELADDR:
    729        MOZ_ASSERT(!isResponse ||
    730                   (nlh->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI);
    731        OnAddrMessage(nlh);
    732        break;
    733      case RTM_NEWROUTE:
    734      case RTM_DELROUTE:
    735        if (isResponse && ((nlh->nlmsg_flags & NLM_F_MULTI) != NLM_F_MULTI)) {
    736          // If it's not multipart message, then it must be response to a route
    737          // check.
    738          MOZ_ASSERT(mOutgoingMessages[0]->MsgType() == NetlinkMsg::kRtMsg);
    739          OnRouteCheckResult(nlh);
    740          RemovePendingMsg();
    741        } else {
    742          OnRouteMessage(nlh);
    743        }
    744        break;
    745      case RTM_NEWNEIGH:
    746      case RTM_DELNEIGH:
    747        MOZ_ASSERT(!isResponse ||
    748                   (nlh->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI);
    749        OnNeighborMessage(nlh);
    750        break;
    751      default:
    752        break;
    753    }
    754  }
    755 }
    756 
    757 void NetlinkService::OnLinkMessage(struct nlmsghdr* aNlh) {
    758  LOG(("NetlinkService::OnLinkMessage [type=%s]",
    759       aNlh->nlmsg_type == RTM_NEWLINK ? "new" : "del"));
    760 
    761  UniquePtr<NetlinkLink> link(new NetlinkLink());
    762  if (!link->Init(aNlh)) {
    763    return;
    764  }
    765 
    766  const uint32_t linkIndex = link->GetIndex();
    767  mLinks.WithEntryHandle(linkIndex, [&](auto&& entry) {
    768    nsAutoCString linkName;
    769    link->GetName(linkName);
    770 
    771    if (aNlh->nlmsg_type == RTM_NEWLINK) {
    772      if (!entry) {
    773        LOG(("Creating new link [index=%u, name=%s, flags=%u, type=%u]",
    774             linkIndex, linkName.get(), link->GetFlags(), link->GetType()));
    775        entry.Insert(MakeUnique<LinkInfo>(std::move(link)));
    776      } else {
    777        LOG(("Updating link [index=%u, name=%s, flags=%u, type=%u]", linkIndex,
    778             linkName.get(), link->GetFlags(), link->GetType()));
    779 
    780        auto* linkInfo = entry->get();
    781 
    782        // Check whether administrative state has changed.
    783        if (linkInfo->mLink->GetFlags() & IFF_UP &&
    784            !(link->GetFlags() & IFF_UP)) {
    785          LOG(("  link went down"));
    786          // If the link went down, remove all routes and neighbors, but keep
    787          // addresses.
    788          linkInfo->mDefaultRoutes.Clear();
    789          linkInfo->mNeighbors.Clear();
    790        }
    791 
    792        linkInfo->mLink = std::move(link);
    793        linkInfo->UpdateStatus();
    794      }
    795    } else {
    796      if (!entry) {
    797        // This can happen during startup
    798        LOG(("Link info doesn't exist [index=%u, name=%s]", linkIndex,
    799             linkName.get()));
    800      } else {
    801        LOG(("Removing link [index=%u, name=%s]", linkIndex, linkName.get()));
    802        entry.Remove();
    803      }
    804    }
    805  });
    806 }
    807 
    808 void NetlinkService::OnAddrMessage(struct nlmsghdr* aNlh) {
    809  LOG(("NetlinkService::OnAddrMessage [type=%s]",
    810       aNlh->nlmsg_type == RTM_NEWADDR ? "new" : "del"));
    811 
    812  UniquePtr<NetlinkAddress> address(new NetlinkAddress());
    813  if (!address->Init(aNlh)) {
    814    return;
    815  }
    816 
    817  uint32_t ifIdx = address->GetIndex();
    818 
    819  nsAutoCString addrStr;
    820  GetAddrStr(address->GetAddrPtr(), address->Family(), addrStr);
    821 
    822  LinkInfo* linkInfo = nullptr;
    823  mLinks.Get(ifIdx, &linkInfo);
    824  if (!linkInfo) {
    825    // This can happen during startup
    826    LOG(("Cannot find link info [ifIdx=%u, addr=%s/%u", ifIdx, addrStr.get(),
    827         address->GetPrefixLen()));
    828    return;
    829  }
    830 
    831  // There might be already an equal address in the array even in case of
    832  // RTM_NEWADDR message, e.g. when lifetime of IPv6 address is renewed. Equal
    833  // in this case means that IP and prefix is the same but some attributes
    834  // might be different. Remove existing equal address in case of RTM_DELADDR
    835  // as well as RTM_NEWADDR message and add a new one in the latter case.
    836  for (uint32_t i = 0; i < linkInfo->mAddresses.Length(); ++i) {
    837    if (aNlh->nlmsg_type == RTM_NEWADDR &&
    838        linkInfo->mAddresses[i]->MsgEquals(*address)) {
    839      // If the new address is exactly the same, there is nothing to do.
    840      LOG(("Exactly the same address already exists [ifIdx=%u, addr=%s/%u",
    841           ifIdx, addrStr.get(), address->GetPrefixLen()));
    842      return;
    843    }
    844 
    845    if (linkInfo->mAddresses[i]->Equals(*address)) {
    846      LOG(("Removing address [ifIdx=%u, addr=%s/%u]", ifIdx, addrStr.get(),
    847           address->GetPrefixLen()));
    848      linkInfo->mAddresses.RemoveElementAt(i);
    849      break;
    850    }
    851  }
    852 
    853  if (aNlh->nlmsg_type == RTM_NEWADDR) {
    854    LOG(("Adding address [ifIdx=%u, addr=%s/%u]", ifIdx, addrStr.get(),
    855         address->GetPrefixLen()));
    856    linkInfo->mAddresses.AppendElement(std::move(address));
    857  } else {
    858    // Remove all routes associated with this address
    859    for (uint32_t i = linkInfo->mDefaultRoutes.Length(); i-- > 0;) {
    860      MOZ_ASSERT(linkInfo->mDefaultRoutes[i]->GetGWAddrPtr(),
    861                 "Stored routes must have gateway!");
    862      if (linkInfo->mDefaultRoutes[i]->Family() == address->Family() &&
    863          address->ContainsAddr(linkInfo->mDefaultRoutes[i]->GetGWAddrPtr())) {
    864        if (LOG_ENABLED()) {
    865          nsAutoCString routeDbgStr;
    866          linkInfo->mDefaultRoutes[i]->GetAsString(routeDbgStr);
    867          LOG(("Removing default route: %s", routeDbgStr.get()));
    868        }
    869        linkInfo->mDefaultRoutes.RemoveElementAt(i);
    870      }
    871    }
    872 
    873    // Remove all neighbors associated with this address
    874    for (auto iter = linkInfo->mNeighbors.Iter(); !iter.Done(); iter.Next()) {
    875      NetlinkNeighbor* neigh = iter.UserData();
    876      if (neigh->Family() == address->Family() &&
    877          address->ContainsAddr(neigh->GetAddrPtr())) {
    878        if (LOG_ENABLED()) {
    879          nsAutoCString neighDbgStr;
    880          neigh->GetAsString(neighDbgStr);
    881          LOG(("Removing neighbor %s", neighDbgStr.get()));
    882        }
    883        iter.Remove();
    884      }
    885    }
    886  }
    887 
    888  // Address change on the interface can change its status
    889  linkInfo->UpdateStatus();
    890 
    891  // Don't treat address changes during initial scan as a network change
    892  if (mInitialScanFinished) {
    893    // Send network event change regardless of whether the ID has changed or
    894    // not
    895    mSendNetworkChangeEvent = true;
    896    TriggerNetworkIDCalculation();
    897  }
    898 }
    899 
    900 void NetlinkService::OnRouteMessage(struct nlmsghdr* aNlh) {
    901  LOG(("NetlinkService::OnRouteMessage [type=%s]",
    902       aNlh->nlmsg_type == RTM_NEWROUTE ? "new" : "del"));
    903 
    904  UniquePtr<NetlinkRoute> route(new NetlinkRoute());
    905  if (!route->Init(aNlh)) {
    906    return;
    907  }
    908 
    909  if (!route->IsUnicast() || !route->ScopeIsUniverse()) {
    910    // Use only unicast routes
    911    if (LOG_ENABLED()) {
    912      nsAutoCString routeDbgStr;
    913      route->GetAsString(routeDbgStr);
    914      LOG(("Not an unicast global route: %s", routeDbgStr.get()));
    915    }
    916    return;
    917  }
    918 
    919  // Adding/removing any unicast route might change network ID
    920  TriggerNetworkIDCalculation();
    921 
    922  if (!route->IsDefault()) {
    923    // Store only default routes
    924    if (LOG_ENABLED()) {
    925      nsAutoCString routeDbgStr;
    926      route->GetAsString(routeDbgStr);
    927      LOG(("Not a default route: %s", routeDbgStr.get()));
    928    }
    929    return;
    930  }
    931 
    932  if (!route->HasOif()) {
    933    if (LOG_ENABLED()) {
    934      nsAutoCString routeDbgStr;
    935      route->GetAsString(routeDbgStr);
    936      LOG(("There is no output interface in route: %s", routeDbgStr.get()));
    937    }
    938    return;
    939  }
    940 
    941  if (!route->GetGWAddrPtr()) {
    942    // We won't use the route if there is no gateway, so don't store it
    943    if (LOG_ENABLED()) {
    944      nsAutoCString routeDbgStr;
    945      route->GetAsString(routeDbgStr);
    946      LOG(("There is no gateway in route: %s", routeDbgStr.get()));
    947    }
    948    return;
    949  }
    950 
    951  if (route->Family() == AF_INET6 &&
    952      net::utils::ipv6_scope((const unsigned char*)route->GetGWAddrPtr()) !=
    953          IPV6_SCOPE_GLOBAL) {
    954    if (LOG_ENABLED()) {
    955      nsAutoCString routeDbgStr;
    956      route->GetAsString(routeDbgStr);
    957      LOG(("Scope of GW isn't global: %s", routeDbgStr.get()));
    958    }
    959    return;
    960  }
    961 
    962  LinkInfo* linkInfo = nullptr;
    963  mLinks.Get(route->Oif(), &linkInfo);
    964  if (!linkInfo) {
    965    // This can happen during startup
    966    if (LOG_ENABLED()) {
    967      nsAutoCString routeDbgStr;
    968      route->GetAsString(routeDbgStr);
    969      LOG(("Cannot find link info for route: %s", routeDbgStr.get()));
    970    }
    971    return;
    972  }
    973 
    974  for (uint32_t i = 0; i < linkInfo->mDefaultRoutes.Length(); ++i) {
    975    if (linkInfo->mDefaultRoutes[i]->Equals(*route)) {
    976      // We shouldn't find equal route when adding a new one, but just in case
    977      // it can happen remove the old one to avoid duplicities.
    978      if (LOG_ENABLED()) {
    979        nsAutoCString routeDbgStr;
    980        route->GetAsString(routeDbgStr);
    981        LOG(("Removing default route: %s", routeDbgStr.get()));
    982      }
    983      linkInfo->mDefaultRoutes.RemoveElementAt(i);
    984      break;
    985    }
    986  }
    987 
    988  if (aNlh->nlmsg_type == RTM_NEWROUTE) {
    989    if (LOG_ENABLED()) {
    990      nsAutoCString routeDbgStr;
    991      route->GetAsString(routeDbgStr);
    992      LOG(("Adding default route: %s", routeDbgStr.get()));
    993    }
    994    linkInfo->mDefaultRoutes.AppendElement(std::move(route));
    995  }
    996 }
    997 
    998 void NetlinkService::OnNeighborMessage(struct nlmsghdr* aNlh) {
    999  LOG(("NetlinkService::OnNeighborMessage [type=%s]",
   1000       aNlh->nlmsg_type == RTM_NEWNEIGH ? "new" : "del"));
   1001 
   1002  UniquePtr<NetlinkNeighbor> neigh(new NetlinkNeighbor());
   1003  if (!neigh->Init(aNlh)) {
   1004    return;
   1005  }
   1006 
   1007  LinkInfo* linkInfo = nullptr;
   1008  mLinks.Get(neigh->GetIndex(), &linkInfo);
   1009  if (!linkInfo) {
   1010    // This can happen during startup
   1011    if (LOG_ENABLED()) {
   1012      nsAutoCString neighDbgStr;
   1013      neigh->GetAsString(neighDbgStr);
   1014      LOG(("Cannot find link info for neighbor: %s", neighDbgStr.get()));
   1015    }
   1016    return;
   1017  }
   1018 
   1019  if (!linkInfo->mLink->IsTypeEther()) {
   1020    if (LOG_ENABLED()) {
   1021      nsAutoCString neighDbgStr;
   1022      neigh->GetAsString(neighDbgStr);
   1023      LOG(("Ignoring message on non-ethernet link: %s", neighDbgStr.get()));
   1024    }
   1025    return;
   1026  }
   1027 
   1028  nsAutoCString key;
   1029  GetAddrStr(neigh->GetAddrPtr(), neigh->Family(), key);
   1030 
   1031  if (aNlh->nlmsg_type == RTM_NEWNEIGH) {
   1032    if (!mRecalculateNetworkId && neigh->HasMAC()) {
   1033      NetlinkNeighbor* oldNeigh = nullptr;
   1034      linkInfo->mNeighbors.Get(key, &oldNeigh);
   1035 
   1036      if (!oldNeigh || !oldNeigh->HasMAC()) {
   1037        // The MAC address was added, if it's a host from some of the saved
   1038        // routing tables we should recalculate network ID
   1039        for (uint32_t i = 0; i < linkInfo->mDefaultRoutes.Length(); ++i) {
   1040          if (linkInfo->mDefaultRoutes[i]->GatewayEquals(*neigh)) {
   1041            TriggerNetworkIDCalculation();
   1042            break;
   1043          }
   1044        }
   1045        if ((mIPv4RouteCheckResult &&
   1046             mIPv4RouteCheckResult->GatewayEquals(*neigh)) ||
   1047            (mIPv6RouteCheckResult &&
   1048             mIPv6RouteCheckResult->GatewayEquals(*neigh))) {
   1049          TriggerNetworkIDCalculation();
   1050        }
   1051      }
   1052    }
   1053 
   1054    if (LOG_ENABLED()) {
   1055      nsAutoCString neighDbgStr;
   1056      neigh->GetAsString(neighDbgStr);
   1057      LOG(("Adding neighbor: %s", neighDbgStr.get()));
   1058    }
   1059    linkInfo->mNeighbors.InsertOrUpdate(key, std::move(neigh));
   1060  } else {
   1061    if (LOG_ENABLED()) {
   1062      nsAutoCString neighDbgStr;
   1063      neigh->GetAsString(neighDbgStr);
   1064      LOG(("Removing neighbor %s", neighDbgStr.get()));
   1065    }
   1066    linkInfo->mNeighbors.Remove(key);
   1067  }
   1068 }
   1069 
   1070 void NetlinkService::OnRouteCheckResult(struct nlmsghdr* aNlh) {
   1071  LOG(("NetlinkService::OnRouteCheckResult"));
   1072  UniquePtr<NetlinkRoute> route;
   1073 
   1074  if (aNlh) {
   1075    route = MakeUnique<NetlinkRoute>();
   1076    if (!route->Init(aNlh)) {
   1077      route = nullptr;
   1078    } else {
   1079      if (!route->IsUnicast() || !route->ScopeIsUniverse()) {
   1080        if (LOG_ENABLED()) {
   1081          nsAutoCString routeDbgStr;
   1082          route->GetAsString(routeDbgStr);
   1083          LOG(("Not an unicast global route: %s", routeDbgStr.get()));
   1084        }
   1085        route = nullptr;
   1086      } else if (!route->HasOif()) {
   1087        if (LOG_ENABLED()) {
   1088          nsAutoCString routeDbgStr;
   1089          route->GetAsString(routeDbgStr);
   1090          LOG(("There is no output interface in route: %s", routeDbgStr.get()));
   1091        }
   1092        route = nullptr;
   1093      }
   1094    }
   1095  }
   1096 
   1097  if (LOG_ENABLED()) {
   1098    if (route) {
   1099      nsAutoCString routeDbgStr;
   1100      route->GetAsString(routeDbgStr);
   1101      LOG(("Storing route: %s", routeDbgStr.get()));
   1102    } else {
   1103      LOG(("Clearing result for the check"));
   1104    }
   1105  }
   1106 
   1107  if (mOutgoingMessages[0]->Family() == AF_INET) {
   1108    mIPv4RouteCheckResult = std::move(route);
   1109  } else {
   1110    mIPv6RouteCheckResult = std::move(route);
   1111  }
   1112 }
   1113 
   1114 void NetlinkService::EnqueueGenMsg(uint16_t aMsgType, uint8_t aFamily) {
   1115  NetlinkGenMsg* msg = new NetlinkGenMsg(aMsgType, aFamily, ++mMsgId);
   1116  mOutgoingMessages.AppendElement(msg);
   1117 }
   1118 
   1119 void NetlinkService::EnqueueRtMsg(uint8_t aFamily, void* aAddress) {
   1120  NetlinkRtMsg* msg = new NetlinkRtMsg(aFamily, aAddress, ++mMsgId);
   1121  mOutgoingMessages.AppendElement(msg);
   1122 }
   1123 
   1124 void NetlinkService::RemovePendingMsg() {
   1125  LOG(("NetlinkService::RemovePendingMsg [seqId=%u]",
   1126       mOutgoingMessages[0]->SeqId()));
   1127 
   1128  MOZ_ASSERT(mOutgoingMessages[0]->IsPending());
   1129 
   1130  DebugOnly<bool> isRtMessage =
   1131      (mOutgoingMessages[0]->MsgType() == NetlinkMsg::kRtMsg);
   1132 
   1133  mOutgoingMessages.RemoveElementAt(0);
   1134  if (!mOutgoingMessages.Length()) {
   1135    if (!mInitialScanFinished) {
   1136      // Now we've received all initial data from the kernel. Perform a link
   1137      // check and trigger network ID calculation even if it wasn't triggered
   1138      // by the incoming messages.
   1139      mInitialScanFinished = true;
   1140 
   1141      TriggerNetworkIDCalculation();
   1142 
   1143      // Link status should be known by now.
   1144      RefPtr<NetlinkServiceListener> listener;
   1145      {
   1146        MutexAutoLock lock(mMutex);
   1147        listener = mListener;
   1148      }
   1149      if (listener) {
   1150        listener->OnLinkStatusKnown();
   1151      }
   1152    } else {
   1153      // We've received last response for route check, calculate ID now
   1154      MOZ_ASSERT(isRtMessage);
   1155      CalculateNetworkID();
   1156    }
   1157  }
   1158 }
   1159 
   1160 NS_IMETHODIMP
   1161 NetlinkService::Run() {
   1162  int netlinkSocket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
   1163  if (netlinkSocket < 0) {
   1164    return NS_ERROR_FAILURE;
   1165  }
   1166 
   1167  struct sockaddr_nl addr{};
   1168  memset(&addr, 0, sizeof(addr));
   1169 
   1170  addr.nl_family = AF_NETLINK;
   1171  addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_LINK |
   1172                   RTMGRP_NEIGH | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
   1173 
   1174  if (bind(netlinkSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
   1175    // failure!
   1176    EINTR_RETRY(close(netlinkSocket));
   1177    return NS_ERROR_FAILURE;
   1178  }
   1179 
   1180  struct pollfd fds[2];
   1181  fds[0].fd = mShutdownPipe[0];
   1182  fds[0].events = POLLIN;
   1183  fds[0].revents = 0;
   1184 
   1185  fds[1].fd = netlinkSocket;
   1186  fds[1].events = POLLIN;
   1187  fds[1].revents = 0;
   1188 
   1189  // send all requests to get initial network information
   1190  EnqueueGenMsg(RTM_GETLINK, AF_PACKET);
   1191  EnqueueGenMsg(RTM_GETNEIGH, AF_INET);
   1192  EnqueueGenMsg(RTM_GETNEIGH, AF_INET6);
   1193  EnqueueGenMsg(RTM_GETADDR, AF_PACKET);
   1194  EnqueueGenMsg(RTM_GETROUTE, AF_PACKET);
   1195 
   1196  nsresult rv = NS_OK;
   1197  bool shutdown = false;
   1198  while (!shutdown) {
   1199    if (mOutgoingMessages.Length() && !mOutgoingMessages[0]->IsPending()) {
   1200      if (!mOutgoingMessages[0]->Send(netlinkSocket)) {
   1201        LOG(("Failed to send netlink message"));
   1202        mOutgoingMessages.RemoveElementAt(0);
   1203        // try to send another message if available before polling
   1204        continue;
   1205      }
   1206    }
   1207 
   1208    int rc = eintr_retry([&]() {
   1209      AUTO_PROFILER_THREAD_SLEEP;
   1210      return poll(fds, 2, GetPollWait());
   1211    });
   1212 
   1213    if (rc > 0) {
   1214      if (fds[0].revents & POLLIN) {
   1215        // shutdown, abort the loop!
   1216        LOG(("thread shutdown received, dying...\n"));
   1217        shutdown = true;
   1218      } else if (fds[1].revents & POLLIN) {
   1219        LOG(("netlink message received, handling it...\n"));
   1220        OnNetlinkMessage(netlinkSocket);
   1221      }
   1222    } else if (rc < 0) {
   1223      rv = NS_ERROR_FAILURE;
   1224      break;
   1225    }
   1226  }
   1227 
   1228  EINTR_RETRY(close(netlinkSocket));
   1229 
   1230  return rv;
   1231 }
   1232 
   1233 nsresult NetlinkService::Init(NetlinkServiceListener* aListener) {
   1234  nsresult rv;
   1235 
   1236  mListener = aListener;
   1237 
   1238  if (inet_pton(AF_INET, ROUTE_CHECK_IPV4, &mRouteCheckIPv4) != 1) {
   1239    LOG(("Cannot parse address " ROUTE_CHECK_IPV4));
   1240    MOZ_DIAGNOSTIC_CRASH("Cannot parse address " ROUTE_CHECK_IPV4);
   1241    return NS_ERROR_UNEXPECTED;
   1242  }
   1243 
   1244  if (inet_pton(AF_INET6, ROUTE_CHECK_IPV6, &mRouteCheckIPv6) != 1) {
   1245    LOG(("Cannot parse address " ROUTE_CHECK_IPV6));
   1246    MOZ_DIAGNOSTIC_CRASH("Cannot parse address " ROUTE_CHECK_IPV6);
   1247    return NS_ERROR_UNEXPECTED;
   1248  }
   1249 
   1250  if (pipe(mShutdownPipe) == -1) {
   1251    LOG(("Cannot create pipe"));
   1252    return NS_ERROR_FAILURE;
   1253  }
   1254 
   1255  rv = NS_NewNamedThread("Netlink Monitor", getter_AddRefs(mThread), this);
   1256  NS_ENSURE_SUCCESS(rv, rv);
   1257 
   1258  return NS_OK;
   1259 }
   1260 
   1261 nsresult NetlinkService::Shutdown() {
   1262  LOG(("write() to signal thread shutdown\n"));
   1263 
   1264  {
   1265    MutexAutoLock lock(mMutex);
   1266    mListener = nullptr;
   1267  }
   1268 
   1269  // awake the thread to make it terminate
   1270  ssize_t rc = EINTR_RETRY(write(mShutdownPipe[1], "1", 1));
   1271  LOG(("write() returned %d, errno == %d\n", (int)rc, errno));
   1272 
   1273  nsresult rv = mThread->Shutdown();
   1274 
   1275  // Have to break the cycle here, otherwise NetlinkService holds
   1276  // onto the thread and the thread holds onto the NetlinkService
   1277  // via its mRunnable
   1278  mThread = nullptr;
   1279 
   1280  return rv;
   1281 }
   1282 
   1283 /*
   1284 * A network event that might change network ID has been registered. Delay
   1285 * network ID calculation and sending of the event in case it changed for
   1286 * a while. Absorbing potential subsequent events increases chance of successful
   1287 * network ID calculation (e.g. MAC address of the router might be discovered in
   1288 * the meantime)
   1289 */
   1290 void NetlinkService::TriggerNetworkIDCalculation() {
   1291  LOG(("NetlinkService::TriggerNetworkIDCalculation"));
   1292 
   1293  if (mRecalculateNetworkId) {
   1294    return;
   1295  }
   1296 
   1297  mRecalculateNetworkId = true;
   1298  mTriggerTime = TimeStamp::Now();
   1299 }
   1300 
   1301 int NetlinkService::GetPollWait() {
   1302  if (!mRecalculateNetworkId) {
   1303    return -1;
   1304  }
   1305 
   1306  if (mOutgoingMessages.Length()) {
   1307    MOZ_ASSERT(mOutgoingMessages[0]->IsPending());
   1308    // Message is pending, we don't have to set timeout because we'll receive
   1309    // reply from kernel ASAP
   1310    return -1;
   1311  }
   1312 
   1313  MOZ_ASSERT(mInitialScanFinished);
   1314 
   1315  double period = (TimeStamp::Now() - mTriggerTime).ToMilliseconds();
   1316  if (period >= kNetworkChangeCoalescingPeriod) {
   1317    // Coalescing time has elapsed, send route check messages to find out
   1318    // where IPv4 and IPv6 traffic is routed and calculate network ID after
   1319    // the response is received.
   1320    EnqueueRtMsg(AF_INET, &mRouteCheckIPv4);
   1321    EnqueueRtMsg(AF_INET6, &mRouteCheckIPv6);
   1322 
   1323    // Return 0 to make sure we start sending enqueued messages immediately
   1324    return 0;
   1325  }
   1326 
   1327  return static_cast<int>(kNetworkChangeCoalescingPeriod - period);
   1328 }
   1329 
   1330 class NeighborComparator {
   1331 public:
   1332  bool Equals(const NetlinkNeighbor* a, const NetlinkNeighbor* b) const {
   1333    return (memcmp(a->GetMACPtr(), b->GetMACPtr(), ETH_ALEN) == 0);
   1334  }
   1335  bool LessThan(const NetlinkNeighbor* a, const NetlinkNeighbor* b) const {
   1336    return (memcmp(a->GetMACPtr(), b->GetMACPtr(), ETH_ALEN) < 0);
   1337  }
   1338 };
   1339 
   1340 class LinknameComparator {
   1341 public:
   1342  bool LessThan(const nsCString& aA, const nsCString& aB) const {
   1343    return aA < aB;
   1344  }
   1345  bool Equals(const nsCString& aA, const nsCString& aB) const {
   1346    return aA == aB;
   1347  }
   1348 };
   1349 
   1350 // Get Gateway Neighbours for a particular Address Family, for which we know MAC
   1351 // address
   1352 void NetlinkService::GetGWNeighboursForFamily(
   1353    uint8_t aFamily, nsTArray<NetlinkNeighbor*>& aGwNeighbors) {
   1354  LOG(("NetlinkService::GetGWNeighboursForFamily"));
   1355  // Check only routes on links that are up
   1356  for (const auto& linkInfo : mLinks.Values()) {
   1357    nsAutoCString linkName;
   1358    linkInfo->mLink->GetName(linkName);
   1359 
   1360    if (!linkInfo->mIsUp) {
   1361      LOG((" %s is down", linkName.get()));
   1362      continue;
   1363    }
   1364 
   1365    if (!linkInfo->mLink->IsTypeEther()) {
   1366      LOG((" %s is not ethernet link", linkName.get()));
   1367      continue;
   1368    }
   1369 
   1370    LOG((" checking link %s", linkName.get()));
   1371 
   1372    // Check all default routes and try to get MAC of the gateway
   1373    for (uint32_t i = 0; i < linkInfo->mDefaultRoutes.Length(); ++i) {
   1374      if (LOG_ENABLED()) {
   1375        nsAutoCString routeDbgStr;
   1376        linkInfo->mDefaultRoutes[i]->GetAsString(routeDbgStr);
   1377        LOG(("Checking default route: %s", routeDbgStr.get()));
   1378      }
   1379 
   1380      if (linkInfo->mDefaultRoutes[i]->Family() != aFamily) {
   1381        LOG(("  skipping due to different family"));
   1382        continue;
   1383      }
   1384 
   1385      MOZ_ASSERT(linkInfo->mDefaultRoutes[i]->GetGWAddrPtr(),
   1386                 "Stored routes must have gateway!");
   1387 
   1388      nsAutoCString neighKey;
   1389      GetAddrStr(linkInfo->mDefaultRoutes[i]->GetGWAddrPtr(), aFamily,
   1390                 neighKey);
   1391 
   1392      NetlinkNeighbor* neigh = nullptr;
   1393      if (!linkInfo->mNeighbors.Get(neighKey, &neigh)) {
   1394        LOG(("Neighbor %s not found in hashtable.", neighKey.get()));
   1395        continue;
   1396      }
   1397 
   1398      if (!neigh->HasMAC()) {
   1399        // We don't know MAC address
   1400        LOG(("We have no MAC for neighbor %s.", neighKey.get()));
   1401        continue;
   1402      }
   1403 
   1404      if (aGwNeighbors.IndexOf(neigh, 0, NeighborComparator()) !=
   1405          nsTArray<NetlinkNeighbor*>::NoIndex) {
   1406        // avoid host duplicities
   1407        LOG(("MAC of neighbor %s is already selected for hashing.",
   1408             neighKey.get()));
   1409        continue;
   1410      }
   1411 
   1412      LOG(("MAC of neighbor %s will be used for network ID.", neighKey.get()));
   1413      aGwNeighbors.AppendElement(neigh);
   1414    }
   1415  }
   1416 }
   1417 
   1418 bool NetlinkService::CalculateIDForEthernetLink(uint8_t aFamily,
   1419                                                NetlinkRoute* aRouteCheckResult,
   1420                                                uint32_t aRouteCheckIfIdx,
   1421                                                LinkInfo* aRouteCheckLinkInfo,
   1422                                                SHA1Sum* aSHA1) {
   1423  LOG(("NetlinkService::CalculateIDForEthernetLink"));
   1424  bool retval = false;
   1425  const in_common_addr* addrPtr = aRouteCheckResult->GetGWAddrPtr();
   1426 
   1427  if (!addrPtr) {
   1428    // This shouldn't normally happen, missing next hop in case of ethernet
   1429    // device would mean that the checked host is on the same network.
   1430    if (LOG_ENABLED()) {
   1431      nsAutoCString routeDbgStr;
   1432      aRouteCheckResult->GetAsString(routeDbgStr);
   1433      LOG(("There is no next hop in route: %s", routeDbgStr.get()));
   1434    }
   1435    return retval;
   1436  }
   1437 
   1438  // If we know MAC address of the next hop for mRouteCheckIPv4/6 host, hash
   1439  // it even if it's MAC of some of the default routes we've checked above.
   1440  // This ensures that if we have 2 different default routes and next hop for
   1441  // mRouteCheckIPv4/6 changes from one default route to the other, we'll
   1442  // detect it as a network change.
   1443  nsAutoCString neighKey;
   1444  GetAddrStr(addrPtr, aFamily, neighKey);
   1445  LOG(("Next hop for the checked host is %s on ifIdx %u.", neighKey.get(),
   1446       aRouteCheckIfIdx));
   1447 
   1448  NetlinkNeighbor* neigh = nullptr;
   1449  if (!aRouteCheckLinkInfo->mNeighbors.Get(neighKey, &neigh)) {
   1450    LOG(("Neighbor %s not found in hashtable.", neighKey.get()));
   1451    return retval;
   1452  }
   1453 
   1454  if (!neigh->HasMAC()) {
   1455    LOG(("We have no MAC for neighbor %s.", neighKey.get()));
   1456    return retval;
   1457  }
   1458 
   1459  if (LOG_ENABLED()) {
   1460    nsAutoCString neighDbgStr;
   1461    neigh->GetAsString(neighDbgStr);
   1462    LOG(("Hashing MAC address of neighbor: %s", neighDbgStr.get()));
   1463  }
   1464  aSHA1->update(neigh->GetMACPtr(), ETH_ALEN);
   1465  retval = true;
   1466 
   1467  return retval;
   1468 }
   1469 
   1470 bool NetlinkService::CalculateIDForNonEthernetLink(
   1471    uint8_t aFamily, NetlinkRoute* aRouteCheckResult,
   1472    nsTArray<nsCString>& aLinkNamesToHash, uint32_t aRouteCheckIfIdx,
   1473    LinkInfo* aRouteCheckLinkInfo, SHA1Sum* aSHA1) {
   1474  LOG(("NetlinkService::CalculateIDForNonEthernetLink"));
   1475  bool retval = false;
   1476  const in_common_addr* addrPtr = aRouteCheckResult->GetGWAddrPtr();
   1477  nsAutoCString routeCheckLinkName;
   1478  aRouteCheckLinkInfo->mLink->GetName(routeCheckLinkName);
   1479 
   1480  if (addrPtr) {
   1481    // The route contains next hop. Hash the name of the interface (e.g.
   1482    // "tun1") and the IP address of the next hop.
   1483 
   1484    nsAutoCString addrStr;
   1485    GetAddrStr(addrPtr, aFamily, addrStr);
   1486    size_t addrSize =
   1487        (aFamily == AF_INET) ? sizeof(addrPtr->addr4) : sizeof(addrPtr->addr6);
   1488 
   1489    LOG(("Hashing link name %s", routeCheckLinkName.get()));
   1490    aSHA1->update(routeCheckLinkName.get(), routeCheckLinkName.Length());
   1491 
   1492    // Don't hash GW address if it's rmnet_data device.
   1493    if (!aLinkNamesToHash.Contains(routeCheckLinkName)) {
   1494      LOG(("Hashing GW address %s", addrStr.get()));
   1495      aSHA1->update(addrPtr, addrSize);
   1496    }
   1497 
   1498    retval = true;
   1499  } else {
   1500    // The traffic is routed directly via an interface. Hash the name of the
   1501    // interface and the network address. Using host address would cause that
   1502    // network ID would be different every time we get a different IP address
   1503    // in this network/VPN.
   1504 
   1505    bool hasSrcAddr = aRouteCheckResult->HasPrefSrcAddr();
   1506    if (!hasSrcAddr) {
   1507      LOG(("There is no preferred source address."));
   1508    }
   1509 
   1510    NetlinkAddress* linkAddress = nullptr;
   1511    // Find network address of the interface matching the source address. In
   1512    // theory there could be multiple addresses with different prefix length.
   1513    // Get the one with smallest prefix length.
   1514    for (uint32_t i = 0; i < aRouteCheckLinkInfo->mAddresses.Length(); ++i) {
   1515      if (!hasSrcAddr) {
   1516        // there is no preferred src, match just the family
   1517        if (aRouteCheckLinkInfo->mAddresses[i]->Family() != aFamily) {
   1518          continue;
   1519        }
   1520      } else if (!aRouteCheckResult->PrefSrcAddrEquals(
   1521                     *aRouteCheckLinkInfo->mAddresses[i])) {
   1522        continue;
   1523      }
   1524 
   1525      if (!linkAddress ||
   1526          linkAddress->GetPrefixLen() >
   1527              aRouteCheckLinkInfo->mAddresses[i]->GetPrefixLen()) {
   1528        // We have no address yet or this one has smaller prefix length,
   1529        // use it.
   1530        linkAddress = aRouteCheckLinkInfo->mAddresses[i].get();
   1531      }
   1532    }
   1533 
   1534    if (!linkAddress) {
   1535      // There is no address in our array?
   1536      if (LOG_ENABLED()) {
   1537        nsAutoCString dbgStr;
   1538        aRouteCheckResult->GetAsString(dbgStr);
   1539        LOG(("No address found for preferred source address in route: %s",
   1540             dbgStr.get()));
   1541      }
   1542      return retval;
   1543    }
   1544 
   1545    in_common_addr prefix;
   1546    int32_t prefixSize = (aFamily == AF_INET) ? (int32_t)sizeof(prefix.addr4)
   1547                                              : (int32_t)sizeof(prefix.addr6);
   1548    memcpy(&prefix, linkAddress->GetAddrPtr(), prefixSize);
   1549    uint8_t maskit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
   1550    int32_t bits = linkAddress->GetPrefixLen();
   1551    if (bits > prefixSize * 8) {
   1552      MOZ_ASSERT(false, "Unexpected prefix length!");
   1553      LOG(("Unexpected prefix length %d, maximum for this family is %d", bits,
   1554           prefixSize * 8));
   1555      return retval;
   1556    }
   1557    for (int32_t i = 0; i < prefixSize; i++) {
   1558      uint8_t mask = (bits >= 8) ? 0xff : maskit[bits];
   1559      ((unsigned char*)&prefix)[i] &= mask;
   1560      bits -= 8;
   1561      if (bits <= 0) {
   1562        bits = 0;
   1563      }
   1564    }
   1565 
   1566    nsAutoCString addrStr;
   1567    GetAddrStr(&prefix, aFamily, addrStr);
   1568    LOG(("Hashing link name %s and network address %s/%u",
   1569         routeCheckLinkName.get(), addrStr.get(), linkAddress->GetPrefixLen()));
   1570    aSHA1->update(routeCheckLinkName.get(), routeCheckLinkName.Length());
   1571    aSHA1->update(&prefix, prefixSize);
   1572    bits = linkAddress->GetPrefixLen();
   1573    aSHA1->update(&bits, sizeof(bits));
   1574    retval = true;
   1575  }
   1576  return retval;
   1577 }
   1578 
   1579 bool NetlinkService::CalculateIDForFamily(uint8_t aFamily, SHA1Sum* aSHA1) {
   1580  LOG(("NetlinkService::CalculateIDForFamily [family=%s]",
   1581       aFamily == AF_INET ? "AF_INET" : "AF_INET6"));
   1582 
   1583  bool retval = false;
   1584 
   1585  if (!mLinkUp) {
   1586    // Skip ID calculation if the link is down, we have no ID...
   1587    LOG(("Link is down, skipping ID calculation."));
   1588    return retval;
   1589  }
   1590 
   1591  NetlinkRoute* routeCheckResult;
   1592  if (aFamily == AF_INET) {
   1593    routeCheckResult = mIPv4RouteCheckResult.get();
   1594  } else {
   1595    routeCheckResult = mIPv6RouteCheckResult.get();
   1596  }
   1597 
   1598  // All GW neighbors for which we know MAC address. We'll probably have at
   1599  // most only one, but in case we have more default routes, we hash them all
   1600  // even though the routing rules sends the traffic only via one of them.
   1601  // If the system switches between them, we'll detect the change with
   1602  // mIPv4/6RouteCheckResult.
   1603  nsTArray<NetlinkNeighbor*> gwNeighbors;
   1604 
   1605  GetGWNeighboursForFamily(aFamily, gwNeighbors);
   1606 
   1607  // Sort them so we always have the same network ID on the same network
   1608  gwNeighbors.Sort(NeighborComparator());
   1609 
   1610  for (uint32_t i = 0; i < gwNeighbors.Length(); ++i) {
   1611    if (LOG_ENABLED()) {
   1612      nsAutoCString neighDbgStr;
   1613      gwNeighbors[i]->GetAsString(neighDbgStr);
   1614      LOG(("Hashing MAC address of neighbor: %s", neighDbgStr.get()));
   1615    }
   1616    aSHA1->update(gwNeighbors[i]->GetMACPtr(), ETH_ALEN);
   1617    retval = true;
   1618  }
   1619 
   1620  nsTArray<nsCString> linkNamesToHash;
   1621  if (!gwNeighbors.Length()) {
   1622    // If we don't know MAC of the gateway and link is up, it's probably not
   1623    // an ethernet link. If the name of the link begins with "rmnet" then
   1624    // the mobile data is used. We cannot easily differentiate when user
   1625    // switches sim cards so let's treat mobile data as a single network. We'll
   1626    // simply hash link name. If the traffic is redirected via some VPN, it'll
   1627    // still be detected below.
   1628 
   1629    // TODO: maybe we could get operator name via AndroidBridge
   1630    for (const auto& linkInfo : mLinks.Values()) {
   1631      if (linkInfo->mIsUp) {
   1632        nsAutoCString linkName;
   1633        linkInfo->mLink->GetName(linkName);
   1634        if (StringBeginsWith(linkName, "rmnet"_ns)) {
   1635          // Check whether there is some non-local address associated with this
   1636          // link.
   1637          for (uint32_t i = 0; i < linkInfo->mAddresses.Length(); ++i) {
   1638            if (linkInfo->mAddresses[i]->Family() == aFamily &&
   1639                linkInfo->mAddresses[i]->ScopeIsUniverse()) {
   1640              linkNamesToHash.AppendElement(linkName);
   1641              break;
   1642            }
   1643          }
   1644        }
   1645      }
   1646    }
   1647 
   1648    // Sort link names to ensure consistent results
   1649    linkNamesToHash.Sort(LinknameComparator());
   1650 
   1651    for (uint32_t i = 0; i < linkNamesToHash.Length(); ++i) {
   1652      LOG(("Hashing name of adapter: %s", linkNamesToHash[i].get()));
   1653      aSHA1->update(linkNamesToHash[i].get(), linkNamesToHash[i].Length());
   1654      retval = true;
   1655    }
   1656  }
   1657 
   1658  if (!routeCheckResult) {
   1659    // If we don't have result for route check to mRouteCheckIPv4/6 host, the
   1660    // network is unreachable and there is no more to do.
   1661    LOG(("There is no route check result."));
   1662    return retval;
   1663  }
   1664 
   1665  LinkInfo* routeCheckLinkInfo = nullptr;
   1666  uint32_t routeCheckIfIdx = routeCheckResult->Oif();
   1667  if (!mLinks.Get(routeCheckIfIdx, &routeCheckLinkInfo)) {
   1668    LOG(("Cannot find link with index %u ??", routeCheckIfIdx));
   1669    return retval;
   1670  }
   1671 
   1672  if (routeCheckLinkInfo->mLink->IsTypeEther()) {
   1673    // The traffic is routed through an ethernet device.
   1674    retval |= CalculateIDForEthernetLink(
   1675        aFamily, routeCheckResult, routeCheckIfIdx, routeCheckLinkInfo, aSHA1);
   1676  } else {
   1677    // The traffic is routed through a non-ethernet device.
   1678    retval |= CalculateIDForNonEthernetLink(aFamily, routeCheckResult,
   1679                                            linkNamesToHash, routeCheckIfIdx,
   1680                                            routeCheckLinkInfo, aSHA1);
   1681  }
   1682 
   1683  return retval;
   1684 }
   1685 
   1686 void NetlinkService::ExtractDNSProperties() {
   1687  MOZ_ASSERT(!NS_IsMainThread(), "Must not be called on the main thread");
   1688  nsTArray<nsCString> suffixList;
   1689  nsTArray<NetAddr> resolvers;
   1690 #if defined(HAVE_RES_NINIT)
   1691  [&]() {
   1692    struct __res_state res{};
   1693    int ret = res_ninit(&res);
   1694    if (ret != 0) {
   1695      LOG(("Call to res_ninit failed: %d", ret));
   1696      return;
   1697    }
   1698 
   1699    // Get DNS suffixes
   1700    for (int i = 0; i < MAXDNSRCH; i++) {
   1701      if (!res.dnsrch[i]) {
   1702        break;
   1703      }
   1704      suffixList.AppendElement(nsCString(res.dnsrch[i]));
   1705    }
   1706 
   1707    // Get DNS resolvers
   1708    // Chromium's dns_config_service_posix.cc is the origin of this code
   1709    // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|.
   1710    // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|,
   1711    // but we have to combine the two arrays ourselves.
   1712    for (int i = 0; i < res.nscount; ++i) {
   1713      const struct sockaddr* addr = nullptr;
   1714      size_t addr_len = 0;
   1715      if (res.nsaddr_list[i].sin_family) {  // The indicator used by res_nsend.
   1716        addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]);
   1717        addr_len = sizeof res.nsaddr_list[i];
   1718      } else if (res._u._ext.nsaddrs[i]) {
   1719        addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
   1720        addr_len = sizeof *res._u._ext.nsaddrs[i];
   1721      } else {
   1722        LOG(("Bad ext struct"));
   1723        return;
   1724      }
   1725      const socklen_t kSockaddrInSize = sizeof(struct sockaddr_in);
   1726      const socklen_t kSockaddrIn6Size = sizeof(struct sockaddr_in6);
   1727 
   1728      if ((addr->sa_family == AF_INET && addr_len < kSockaddrInSize) ||
   1729          (addr->sa_family == AF_INET6 && addr_len < kSockaddrIn6Size)) {
   1730        LOG(("Bad address size"));
   1731        return;
   1732      }
   1733 
   1734      NetAddr ip;
   1735      if (addr->sa_family == AF_INET) {
   1736        const struct sockaddr_in* sin = (const struct sockaddr_in*)addr;
   1737        ip.inet.family = AF_INET;
   1738        ip.inet.ip = sin->sin_addr.s_addr;
   1739        ip.inet.port = sin->sin_port;
   1740      } else if (addr->sa_family == AF_INET6) {
   1741        const struct sockaddr_in6* sin6 = (const struct sockaddr_in6*)addr;
   1742        ip.inet6.family = AF_INET6;
   1743        memcpy(&ip.inet6.ip.u8, &sin6->sin6_addr, sizeof(ip.inet6.ip.u8));
   1744        ip.inet6.port = sin6->sin6_port;
   1745      } else {
   1746        MOZ_ASSERT_UNREACHABLE("Unexpected sa_family");
   1747        return;
   1748      }
   1749 
   1750      resolvers.AppendElement(ip);
   1751    }
   1752 
   1753    res_nclose(&res);
   1754  }();
   1755 
   1756 #endif
   1757  RefPtr<NetlinkServiceListener> listener;
   1758  {
   1759    MutexAutoLock lock(mMutex);
   1760    listener = mListener;
   1761    mDNSSuffixList = std::move(suffixList);
   1762    mDNSResolvers = std::move(resolvers);
   1763  }
   1764 
   1765  if (listener) {
   1766    listener->OnDnsSuffixListUpdated();
   1767  }
   1768 }
   1769 
   1770 void NetlinkService::UpdateLinkStatus() {
   1771  LOG(("NetlinkService::UpdateLinkStatus"));
   1772 
   1773  MOZ_ASSERT(!mRecalculateNetworkId);
   1774  MOZ_ASSERT(mInitialScanFinished);
   1775 
   1776  // Link is up when we have a route for ROUTE_CHECK_IPV4 or ROUTE_CHECK_IPV6
   1777  bool newLinkUp = mIPv4RouteCheckResult || mIPv6RouteCheckResult;
   1778 
   1779  if (mLinkUp == newLinkUp) {
   1780    LOG(("Link status hasn't changed [linkUp=%d]", mLinkUp));
   1781  } else {
   1782    LOG(("Link status has changed [linkUp=%d]", newLinkUp));
   1783    RefPtr<NetlinkServiceListener> listener;
   1784    {
   1785      MutexAutoLock lock(mMutex);
   1786      listener = mListener;
   1787      mLinkUp = newLinkUp;
   1788    }
   1789    if (mLinkUp) {
   1790      if (listener) {
   1791        listener->OnLinkUp();
   1792      }
   1793    } else {
   1794      if (listener) {
   1795        listener->OnLinkDown();
   1796      }
   1797    }
   1798  }
   1799 }
   1800 
   1801 // Figure out the "network identification".
   1802 void NetlinkService::CalculateNetworkID() {
   1803  LOG(("NetlinkService::CalculateNetworkID"));
   1804 
   1805  MOZ_ASSERT(!NS_IsMainThread(), "Must not be called on the main thread");
   1806  MOZ_ASSERT(mRecalculateNetworkId);
   1807 
   1808  mRecalculateNetworkId = false;
   1809 
   1810  SHA1Sum sha1;
   1811 
   1812  UpdateLinkStatus();
   1813  ExtractDNSProperties();
   1814 
   1815  bool idChanged = false;
   1816  bool found4 = CalculateIDForFamily(AF_INET, &sha1);
   1817  bool found6 = CalculateIDForFamily(AF_INET6, &sha1);
   1818 
   1819  if (found4 || found6) {
   1820    nsAutoCString output;
   1821    SeedNetworkId(sha1);
   1822    uint8_t digest[SHA1Sum::kHashSize];
   1823    sha1.finish(digest);
   1824    nsAutoCString newString(reinterpret_cast<char*>(digest),
   1825                            SHA1Sum::kHashSize);
   1826    nsresult rv = Base64Encode(newString, output);
   1827    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   1828    LOG(("networkid: id %s\n", output.get()));
   1829    MutexAutoLock lock(mMutex);
   1830    if (mNetworkId != output) {
   1831      // new id
   1832      if (found4 && !found6) {
   1833        glean::network::id.AccumulateSingleSample(1);  // IPv4 only
   1834      } else if (!found4 && found6) {
   1835        glean::network::id.AccumulateSingleSample(3);  // IPv6 only
   1836      } else {
   1837        glean::network::id.AccumulateSingleSample(4);  // Both!
   1838      }
   1839      mNetworkId = output;
   1840      idChanged = true;
   1841    } else {
   1842      // same id
   1843      LOG(("Same network id"));
   1844      glean::network::id.AccumulateSingleSample(2);
   1845    }
   1846  } else {
   1847    // no id
   1848    LOG(("No network id"));
   1849    MutexAutoLock lock(mMutex);
   1850    if (!mNetworkId.IsEmpty()) {
   1851      mNetworkId.Truncate();
   1852      idChanged = true;
   1853      glean::network::id.AccumulateSingleSample(0);
   1854    }
   1855  }
   1856 
   1857  if (idChanged) {
   1858    sHasNonLocalIPv6 = found6;
   1859    LOG(("has IPv6: %d", bool(sHasNonLocalIPv6)));
   1860  }
   1861 
   1862  // If this is first time we calculate network ID, don't report it as a network
   1863  // change. We've started with an empty ID and we've just calculated the
   1864  // correct ID. The network hasn't really changed.
   1865  static bool initialIDCalculation = true;
   1866 
   1867  RefPtr<NetlinkServiceListener> listener;
   1868  {
   1869    MutexAutoLock lock(mMutex);
   1870    listener = mListener;
   1871  }
   1872 
   1873  if (!initialIDCalculation && idChanged && listener) {
   1874    listener->OnNetworkIDChanged();
   1875    mSendNetworkChangeEvent = true;
   1876  }
   1877 
   1878  if (mSendNetworkChangeEvent && listener) {
   1879    listener->OnNetworkChanged();
   1880  }
   1881 
   1882  initialIDCalculation = false;
   1883  mSendNetworkChangeEvent = false;
   1884 }
   1885 
   1886 void NetlinkService::GetNetworkID(nsACString& aNetworkID) {
   1887 #ifdef BASE_BROWSER_VERSION
   1888  aNetworkID.Truncate();
   1889 #else
   1890  MutexAutoLock lock(mMutex);
   1891  aNetworkID = mNetworkId;
   1892 #endif
   1893 }
   1894 
   1895 nsresult NetlinkService::GetDnsSuffixList(nsTArray<nsCString>& aDnsSuffixList) {
   1896 #if defined(HAVE_RES_NINIT)
   1897  MutexAutoLock lock(mMutex);
   1898  aDnsSuffixList = mDNSSuffixList.Clone();
   1899  return NS_OK;
   1900 #else
   1901  return NS_ERROR_NOT_IMPLEMENTED;
   1902 #endif
   1903 }
   1904 
   1905 nsresult NetlinkService::GetResolvers(nsTArray<NetAddr>& aResolvers) {
   1906 #if defined(HAVE_RES_NINIT)
   1907  MutexAutoLock lock(mMutex);
   1908  aResolvers = mDNSResolvers.Clone();
   1909  return NS_OK;
   1910 #else
   1911  return NS_ERROR_NOT_IMPLEMENTED;
   1912 #endif
   1913 }
   1914 
   1915 void NetlinkService::GetIsLinkUp(bool* aIsUp) {
   1916  MutexAutoLock lock(mMutex);
   1917  *aIsUp = mLinkUp;
   1918 }
   1919 
   1920 }  // namespace mozilla::net