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