Bug 1931913 - [wpt-sync] Update web-platform-tests to fb6c27f65c829cea708e497db128f58...
[gecko.git] / netwerk / system / netlink / NetlinkService.cpp
blob5a3154bc0522dcaa4c88f490955b0be69528caad
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/. */
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>
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"
27 #include "mozilla/Base64.h"
28 #include "mozilla/FunctionTypeTraits.h"
29 #include "mozilla/ProfilerThreadSleep.h"
30 #include "mozilla/Telemetry.h"
31 #include "mozilla/DebugOnly.h"
32 #include "mozilla/ScopeExit.h"
34 #if defined(HAVE_RES_NINIT)
35 # include <netinet/in.h>
36 # include <resolv.h>
37 #endif
39 namespace mozilla::net {
41 template <typename F>
42 static auto eintr_retry(F&& func) ->
43 typename FunctionTypeTraits<decltype(func)>::ReturnType {
44 typename FunctionTypeTraits<decltype(func)>::ReturnType _rc;
45 do {
46 _rc = func();
47 } while (_rc == -1 && errno == EINTR);
48 return _rc;
51 #define EINTR_RETRY(expr) eintr_retry([&]() { return expr; })
53 // period during which to absorb subsequent network change events, in
54 // milliseconds
55 static const unsigned int kNetworkChangeCoalescingPeriod = 1000;
57 static LazyLogModule gNlSvcLog("NetlinkService");
58 #define LOG(args) MOZ_LOG(gNlSvcLog, mozilla::LogLevel::Debug, args)
60 #undef LOG_ENABLED
61 #define LOG_ENABLED() MOZ_LOG_TEST(gNlSvcLog, mozilla::LogLevel::Debug)
63 using in_common_addr = union {
64 struct in_addr addr4;
65 struct in6_addr addr6;
68 static void GetAddrStr(const in_common_addr* aAddr, uint8_t aFamily,
69 nsACString& _retval) {
70 char addr[INET6_ADDRSTRLEN];
71 addr[0] = 0;
73 if (aFamily == AF_INET) {
74 inet_ntop(AF_INET, &(aAddr->addr4), addr, INET_ADDRSTRLEN);
75 } else {
76 inet_ntop(AF_INET6, &(aAddr->addr6), addr, INET6_ADDRSTRLEN);
78 _retval.Assign(addr);
81 // Assume true by default.
82 static Atomic<bool, MemoryOrdering::Relaxed> sHasNonLocalIPv6{true};
83 // static
84 bool NetlinkService::HasNonLocalIPv6Address() { return sHasNonLocalIPv6; }
86 class NetlinkAddress {
87 public:
88 NetlinkAddress() = default;
90 uint8_t Family() const { return mIfam.ifa_family; }
91 uint32_t GetIndex() const { return mIfam.ifa_index; }
92 uint8_t GetPrefixLen() const { return mIfam.ifa_prefixlen; }
93 bool ScopeIsUniverse() const { return mIfam.ifa_scope == RT_SCOPE_UNIVERSE; }
94 const in_common_addr* GetAddrPtr() const { return &mAddr; }
96 bool MsgEquals(const NetlinkAddress& aOther) const {
97 return !memcmp(&mIfam, &(aOther.mIfam), sizeof(mIfam));
100 bool Equals(const NetlinkAddress& aOther) const {
101 if (mIfam.ifa_family != aOther.mIfam.ifa_family) {
102 return false;
104 if (mIfam.ifa_index != aOther.mIfam.ifa_index) {
105 // addresses are different when they are on a different interface
106 return false;
108 if (mIfam.ifa_prefixlen != aOther.mIfam.ifa_prefixlen) {
109 // It's possible to have two equal addresses with a different netmask on
110 // the same interface, so we need to check prefixlen too.
111 return false;
113 size_t addrSize = (mIfam.ifa_family == AF_INET) ? sizeof(mAddr.addr4)
114 : sizeof(mAddr.addr6);
115 return memcmp(&mAddr, aOther.GetAddrPtr(), addrSize) == 0;
118 bool ContainsAddr(const in_common_addr* aAddr) {
119 int32_t addrSize = (mIfam.ifa_family == AF_INET)
120 ? (int32_t)sizeof(mAddr.addr4)
121 : (int32_t)sizeof(mAddr.addr6);
122 uint8_t maskit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
123 int32_t bits = mIfam.ifa_prefixlen;
124 if (bits > addrSize * 8) {
125 MOZ_ASSERT(false, "Unexpected prefix length!");
126 LOG(("Unexpected prefix length %d, maximum for this family is %d", bits,
127 addrSize * 8));
128 return false;
130 for (int32_t i = 0; i < addrSize; i++) {
131 uint8_t mask = (bits >= 8) ? 0xff : maskit[bits];
132 if ((((unsigned char*)aAddr)[i] & mask) !=
133 (((unsigned char*)(&mAddr))[i] & mask)) {
134 return false;
136 bits -= 8;
137 if (bits <= 0) {
138 return true;
141 return true;
144 bool Init(struct nlmsghdr* aNlh) {
145 struct ifaddrmsg* ifam;
146 struct rtattr* attr;
147 int len;
149 ifam = (ifaddrmsg*)NLMSG_DATA(aNlh);
150 len = IFA_PAYLOAD(aNlh);
152 if (ifam->ifa_family != AF_INET && ifam->ifa_family != AF_INET6) {
153 return false;
156 bool hasAddr = false;
157 for (attr = IFA_RTA(ifam); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
158 if (attr->rta_type == IFA_ADDRESS || attr->rta_type == IFA_LOCAL) {
159 memcpy(&mAddr, RTA_DATA(attr),
160 ifam->ifa_family == AF_INET ? sizeof(mAddr.addr4)
161 : sizeof(mAddr.addr6));
162 hasAddr = true;
163 if (attr->rta_type == IFA_LOCAL) {
164 // local address is preferred, so don't continue parsing other
165 // attributes
166 break;
171 if (!hasAddr) {
172 return false;
175 memcpy(&mIfam, (ifaddrmsg*)NLMSG_DATA(aNlh), sizeof(mIfam));
176 return true;
179 private:
180 in_common_addr mAddr;
181 struct ifaddrmsg mIfam;
184 class NetlinkNeighbor {
185 public:
186 NetlinkNeighbor() = default;
188 uint8_t Family() const { return mNeigh.ndm_family; }
189 uint32_t GetIndex() const { return mNeigh.ndm_ifindex; }
190 const in_common_addr* GetAddrPtr() const { return &mAddr; }
191 const uint8_t* GetMACPtr() const { return mMAC; }
192 bool HasMAC() const { return mHasMAC; }
194 void GetAsString(nsACString& _retval) const {
195 nsAutoCString addrStr;
196 _retval.Assign("addr=");
197 GetAddrStr(&mAddr, mNeigh.ndm_family, addrStr);
198 _retval.Append(addrStr);
199 if (mNeigh.ndm_family == AF_INET) {
200 _retval.Append(" family=AF_INET if=");
201 } else {
202 _retval.Append(" family=AF_INET6 if=");
204 _retval.AppendInt(mNeigh.ndm_ifindex);
205 if (mHasMAC) {
206 _retval.Append(" mac=");
207 _retval.Append(nsPrintfCString("%02x:%02x:%02x:%02x:%02x:%02x", mMAC[0],
208 mMAC[1], mMAC[2], mMAC[3], mMAC[4],
209 mMAC[5]));
213 bool Init(struct nlmsghdr* aNlh) {
214 struct ndmsg* neigh;
215 struct rtattr* attr;
216 int len;
218 neigh = (ndmsg*)NLMSG_DATA(aNlh);
219 len = aNlh->nlmsg_len - NLMSG_LENGTH(sizeof(*neigh));
221 if (neigh->ndm_family != AF_INET && neigh->ndm_family != AF_INET6) {
222 return false;
225 bool hasDST = false;
226 for (attr = RTM_RTA(neigh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
227 if (attr->rta_type == NDA_LLADDR) {
228 memcpy(mMAC, RTA_DATA(attr), ETH_ALEN);
229 mHasMAC = true;
232 if (attr->rta_type == NDA_DST) {
233 memcpy(&mAddr, RTA_DATA(attr),
234 neigh->ndm_family == AF_INET ? sizeof(mAddr.addr4)
235 : sizeof(mAddr.addr6));
236 hasDST = true;
240 if (!hasDST) {
241 return false;
244 memcpy(&mNeigh, (ndmsg*)NLMSG_DATA(aNlh), sizeof(mNeigh));
245 return true;
248 private:
249 bool mHasMAC{false};
250 uint8_t mMAC[ETH_ALEN]{};
251 in_common_addr mAddr{};
252 struct ndmsg mNeigh {};
255 class NetlinkLink {
256 public:
257 NetlinkLink() = default;
259 bool IsUp() const {
260 return (mIface.ifi_flags & IFF_RUNNING) &&
261 !(mIface.ifi_flags & IFF_LOOPBACK);
264 void GetName(nsACString& _retval) const { _retval = mName; }
265 bool IsTypeEther() const { return mIface.ifi_type == ARPHRD_ETHER; }
266 uint32_t GetIndex() const { return mIface.ifi_index; }
267 uint32_t GetFlags() const { return mIface.ifi_flags; }
268 uint16_t GetType() const { return mIface.ifi_type; }
270 bool Init(struct nlmsghdr* aNlh) {
271 struct ifinfomsg* iface;
272 struct rtattr* attr;
273 int len;
275 iface = (ifinfomsg*)NLMSG_DATA(aNlh);
276 len = aNlh->nlmsg_len - NLMSG_LENGTH(sizeof(*iface));
278 bool hasName = false;
279 for (attr = IFLA_RTA(iface); RTA_OK(attr, len);
280 attr = RTA_NEXT(attr, len)) {
281 if (attr->rta_type == IFLA_IFNAME) {
282 mName.Assign((char*)RTA_DATA(attr));
283 hasName = true;
284 break;
288 if (!hasName) {
289 return false;
292 memcpy(&mIface, (ifinfomsg*)NLMSG_DATA(aNlh), sizeof(mIface));
293 return true;
296 private:
297 nsCString mName;
298 struct ifinfomsg mIface {};
301 class NetlinkRoute {
302 public:
303 NetlinkRoute()
304 : mHasGWAddr(false),
305 mHasPrefSrcAddr(false),
306 mHasDstAddr(false),
307 mHasOif(false),
308 mHasPrio(false) {}
310 bool IsUnicast() const { return mRtm.rtm_type == RTN_UNICAST; }
311 bool ScopeIsUniverse() const { return mRtm.rtm_scope == RT_SCOPE_UNIVERSE; }
312 bool IsDefault() const { return mRtm.rtm_dst_len == 0; }
313 bool HasOif() const { return mHasOif; }
314 uint8_t Oif() const { return mOif; }
315 uint8_t Family() const { return mRtm.rtm_family; }
316 bool HasPrefSrcAddr() const { return mHasPrefSrcAddr; }
317 const in_common_addr* GetGWAddrPtr() const {
318 return mHasGWAddr ? &mGWAddr : nullptr;
320 const in_common_addr* GetPrefSrcAddrPtr() const {
321 return mHasPrefSrcAddr ? &mPrefSrcAddr : nullptr;
324 bool Equals(const NetlinkRoute& aOther) const {
325 size_t addrSize = (mRtm.rtm_family == AF_INET) ? sizeof(mDstAddr.addr4)
326 : sizeof(mDstAddr.addr6);
327 if (memcmp(&mRtm, &(aOther.mRtm), sizeof(mRtm)) != 0) {
328 return false;
330 if (mHasOif != aOther.mHasOif || mOif != aOther.mOif) {
331 return false;
333 if (mHasPrio != aOther.mHasPrio || mPrio != aOther.mPrio) {
334 return false;
336 if ((mHasGWAddr != aOther.mHasGWAddr) ||
337 (mHasGWAddr && memcmp(&mGWAddr, &(aOther.mGWAddr), addrSize) != 0)) {
338 return false;
340 if ((mHasDstAddr != aOther.mHasDstAddr) ||
341 (mHasDstAddr && memcmp(&mDstAddr, &(aOther.mDstAddr), addrSize) != 0)) {
342 return false;
344 if ((mHasPrefSrcAddr != aOther.mHasPrefSrcAddr) ||
345 (mHasPrefSrcAddr &&
346 memcmp(&mPrefSrcAddr, &(aOther.mPrefSrcAddr), addrSize) != 0)) {
347 return false;
349 return true;
352 bool GatewayEquals(const NetlinkNeighbor& aNeigh) const {
353 if (!mHasGWAddr) {
354 return false;
356 if (aNeigh.Family() != mRtm.rtm_family) {
357 return false;
359 size_t addrSize = (mRtm.rtm_family == AF_INET) ? sizeof(mGWAddr.addr4)
360 : sizeof(mGWAddr.addr6);
361 return memcmp(&mGWAddr, aNeigh.GetAddrPtr(), addrSize) == 0;
364 bool GatewayEquals(const NetlinkRoute* aRoute) const {
365 if (!mHasGWAddr || !aRoute->mHasGWAddr) {
366 return false;
368 if (mRtm.rtm_family != aRoute->mRtm.rtm_family) {
369 return false;
371 size_t addrSize = (mRtm.rtm_family == AF_INET) ? sizeof(mGWAddr.addr4)
372 : sizeof(mGWAddr.addr6);
373 return memcmp(&mGWAddr, &(aRoute->mGWAddr), addrSize) == 0;
376 bool PrefSrcAddrEquals(const NetlinkAddress& aAddress) const {
377 if (!mHasPrefSrcAddr) {
378 return false;
380 if (mRtm.rtm_family != aAddress.Family()) {
381 return false;
383 size_t addrSize = (mRtm.rtm_family == AF_INET) ? sizeof(mPrefSrcAddr.addr4)
384 : sizeof(mPrefSrcAddr.addr6);
385 return memcmp(&mPrefSrcAddr, aAddress.GetAddrPtr(), addrSize) == 0;
388 void GetAsString(nsACString& _retval) const {
389 nsAutoCString addrStr;
390 _retval.Assign("table=");
391 _retval.AppendInt(mRtm.rtm_table);
392 _retval.Append(" type=");
393 _retval.AppendInt(mRtm.rtm_type);
394 _retval.Append(" scope=");
395 _retval.AppendInt(mRtm.rtm_scope);
396 if (mRtm.rtm_family == AF_INET) {
397 _retval.Append(" family=AF_INET dst=");
398 addrStr.Assign("0.0.0.0/");
399 } else {
400 _retval.Append(" family=AF_INET6 dst=");
401 addrStr.Assign("::/");
403 if (mHasDstAddr) {
404 GetAddrStr(&mDstAddr, mRtm.rtm_family, addrStr);
405 addrStr.Append("/");
407 _retval.Append(addrStr);
408 _retval.AppendInt(mRtm.rtm_dst_len);
409 if (mHasPrefSrcAddr) {
410 _retval.Append(" src=");
411 GetAddrStr(&mPrefSrcAddr, mRtm.rtm_family, addrStr);
412 _retval.Append(addrStr);
414 if (mHasGWAddr) {
415 _retval.Append(" via=");
416 GetAddrStr(&mGWAddr, mRtm.rtm_family, addrStr);
417 _retval.Append(addrStr);
419 if (mHasOif) {
420 _retval.Append(" oif=");
421 _retval.AppendInt(mOif);
423 if (mHasPrio) {
424 _retval.Append(" prio=");
425 _retval.AppendInt(mPrio);
429 bool Init(struct nlmsghdr* aNlh) {
430 struct rtmsg* rtm;
431 struct rtattr* attr;
432 int len;
434 rtm = (rtmsg*)NLMSG_DATA(aNlh);
435 len = RTM_PAYLOAD(aNlh);
437 if (rtm->rtm_family != AF_INET && rtm->rtm_family != AF_INET6) {
438 return false;
441 for (attr = RTM_RTA(rtm); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
442 if (attr->rta_type == RTA_DST) {
443 memcpy(&mDstAddr, RTA_DATA(attr),
444 (rtm->rtm_family == AF_INET) ? sizeof(mDstAddr.addr4)
445 : sizeof(mDstAddr.addr6));
446 mHasDstAddr = true;
447 } else if (attr->rta_type == RTA_GATEWAY) {
448 memcpy(&mGWAddr, RTA_DATA(attr),
449 (rtm->rtm_family == AF_INET) ? sizeof(mGWAddr.addr4)
450 : sizeof(mGWAddr.addr6));
451 mHasGWAddr = true;
452 } else if (attr->rta_type == RTA_PREFSRC) {
453 memcpy(&mPrefSrcAddr, RTA_DATA(attr),
454 (rtm->rtm_family == AF_INET) ? sizeof(mPrefSrcAddr.addr4)
455 : sizeof(mPrefSrcAddr.addr6));
456 mHasPrefSrcAddr = true;
457 } else if (attr->rta_type == RTA_OIF) {
458 mOif = *(uint32_t*)RTA_DATA(attr);
459 mHasOif = true;
460 } else if (attr->rta_type == RTA_PRIORITY) {
461 mPrio = *(uint32_t*)RTA_DATA(attr);
462 mHasPrio = true;
466 memcpy(&mRtm, (rtmsg*)NLMSG_DATA(aNlh), sizeof(mRtm));
467 return true;
470 private:
471 bool mHasGWAddr : 1;
472 bool mHasPrefSrcAddr : 1;
473 bool mHasDstAddr : 1;
474 bool mHasOif : 1;
475 bool mHasPrio : 1;
477 in_common_addr mGWAddr{};
478 in_common_addr mDstAddr{};
479 in_common_addr mPrefSrcAddr{};
481 uint32_t mOif{};
482 uint32_t mPrio{};
483 struct rtmsg mRtm {};
486 class NetlinkMsg {
487 public:
488 static uint8_t const kGenMsg = 1;
489 static uint8_t const kRtMsg = 2;
491 NetlinkMsg() = default;
492 virtual ~NetlinkMsg() = default;
494 virtual bool Send(int aFD) = 0;
495 virtual bool IsPending() { return mIsPending; }
496 virtual uint32_t SeqId() = 0;
497 virtual uint8_t Family() = 0;
498 virtual uint8_t MsgType() = 0;
500 protected:
501 bool SendRequest(int aFD, void* aRequest, uint32_t aRequestLength) {
502 MOZ_ASSERT(!mIsPending, "Request has been already sent!");
504 struct sockaddr_nl kernel {};
505 memset(&kernel, 0, sizeof(kernel));
506 kernel.nl_family = AF_NETLINK;
507 kernel.nl_groups = 0;
509 struct iovec io {};
510 memset(&io, 0, sizeof(io));
511 io.iov_base = aRequest;
512 io.iov_len = aRequestLength;
514 struct msghdr rtnl_msg {};
515 memset(&rtnl_msg, 0, sizeof(rtnl_msg));
516 rtnl_msg.msg_iov = &io;
517 rtnl_msg.msg_iovlen = 1;
518 rtnl_msg.msg_name = &kernel;
519 rtnl_msg.msg_namelen = sizeof(kernel);
521 ssize_t rc = EINTR_RETRY(sendmsg(aFD, (struct msghdr*)&rtnl_msg, 0));
522 if (rc > 0 && (uint32_t)rc == aRequestLength) {
523 mIsPending = true;
526 return mIsPending;
529 bool mIsPending{false};
532 class NetlinkGenMsg : public NetlinkMsg {
533 public:
534 NetlinkGenMsg(uint16_t aMsgType, uint8_t aFamily, uint32_t aSeqId) {
535 memset(&mReq, 0, sizeof(mReq));
537 mReq.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
538 mReq.hdr.nlmsg_type = aMsgType;
539 mReq.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
540 mReq.hdr.nlmsg_seq = aSeqId;
541 mReq.hdr.nlmsg_pid = 0;
543 mReq.gen.rtgen_family = aFamily;
546 virtual bool Send(int aFD) {
547 return SendRequest(aFD, &mReq, mReq.hdr.nlmsg_len);
550 virtual uint32_t SeqId() { return mReq.hdr.nlmsg_seq; }
551 virtual uint8_t Family() { return mReq.gen.rtgen_family; }
552 virtual uint8_t MsgType() { return kGenMsg; }
554 private:
555 struct {
556 struct nlmsghdr hdr;
557 struct rtgenmsg gen;
558 } mReq{};
561 class NetlinkRtMsg : public NetlinkMsg {
562 public:
563 NetlinkRtMsg(uint8_t aFamily, void* aAddress, uint32_t aSeqId) {
564 MOZ_ASSERT(aFamily == AF_INET || aFamily == AF_INET6);
566 memset(&mReq, 0, sizeof(mReq));
568 mReq.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
569 mReq.hdr.nlmsg_type = RTM_GETROUTE;
570 mReq.hdr.nlmsg_flags = NLM_F_REQUEST;
571 mReq.hdr.nlmsg_seq = aSeqId;
572 mReq.hdr.nlmsg_pid = 0;
574 mReq.rtm.rtm_family = aFamily;
575 mReq.rtm.rtm_flags = 0;
576 mReq.rtm.rtm_dst_len = aFamily == AF_INET ? 32 : 128;
578 struct rtattr* rta;
579 rta = (struct rtattr*)(((char*)&mReq) + NLMSG_ALIGN(mReq.hdr.nlmsg_len));
580 rta->rta_type = RTA_DST;
581 size_t addrSize =
582 aFamily == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr);
583 rta->rta_len = RTA_LENGTH(addrSize);
584 memcpy(RTA_DATA(rta), aAddress, addrSize);
585 mReq.hdr.nlmsg_len = NLMSG_ALIGN(mReq.hdr.nlmsg_len) + RTA_LENGTH(addrSize);
588 virtual bool Send(int aFD) {
589 return SendRequest(aFD, &mReq, mReq.hdr.nlmsg_len);
592 virtual uint32_t SeqId() { return mReq.hdr.nlmsg_seq; }
593 virtual uint8_t Family() { return mReq.rtm.rtm_family; }
594 virtual uint8_t MsgType() { return kRtMsg; }
596 private:
597 struct {
598 struct nlmsghdr hdr;
599 struct rtmsg rtm;
600 unsigned char data[1024];
601 } mReq{};
604 NetlinkService::LinkInfo::LinkInfo(UniquePtr<NetlinkLink>&& aLink)
605 : mLink(std::move(aLink)), mIsUp(false) {}
607 NetlinkService::LinkInfo::~LinkInfo() = default;
609 bool NetlinkService::LinkInfo::UpdateStatus() {
610 LOG(("NetlinkService::LinkInfo::UpdateStatus"));
612 bool oldIsUp = mIsUp;
613 mIsUp = false;
615 if (!mLink->IsUp()) {
616 // The link is not up or is a loopback
617 LOG(("The link is down or is a loopback"));
618 } else {
619 // Link is up when there is non-local address associated with it.
620 for (uint32_t i = 0; i < mAddresses.Length(); ++i) {
621 if (LOG_ENABLED()) {
622 nsAutoCString dbgStr;
623 GetAddrStr(mAddresses[i]->GetAddrPtr(), mAddresses[i]->Family(),
624 dbgStr);
625 LOG(("checking address %s", dbgStr.get()));
627 if (mAddresses[i]->ScopeIsUniverse()) {
628 mIsUp = true;
629 LOG(("global address found"));
630 break;
635 return mIsUp == oldIsUp;
638 NS_IMPL_ISUPPORTS(NetlinkService, nsIRunnable)
640 NetlinkService::NetlinkService() : mPid(getpid()) {}
642 NetlinkService::~NetlinkService() {
643 MOZ_ASSERT(!mThread, "NetlinkService thread shutdown failed");
645 if (mShutdownPipe[0] != -1) {
646 EINTR_RETRY(close(mShutdownPipe[0]));
648 if (mShutdownPipe[1] != -1) {
649 EINTR_RETRY(close(mShutdownPipe[1]));
653 void NetlinkService::OnNetlinkMessage(int aNetlinkSocket) {
654 // The buffer size 4096 is a common page size, which is a recommended limit
655 // for netlink messages.
656 char buffer[4096];
658 struct sockaddr_nl kernel {};
659 memset(&kernel, 0, sizeof(kernel));
660 kernel.nl_family = AF_NETLINK;
661 kernel.nl_groups = 0;
663 struct iovec io {};
664 memset(&io, 0, sizeof(io));
665 io.iov_base = buffer;
666 io.iov_len = sizeof(buffer);
668 struct msghdr rtnl_reply {};
669 memset(&rtnl_reply, 0, sizeof(rtnl_reply));
670 rtnl_reply.msg_iov = &io;
671 rtnl_reply.msg_iovlen = 1;
672 rtnl_reply.msg_name = &kernel;
673 rtnl_reply.msg_namelen = sizeof(kernel);
675 ssize_t rc = EINTR_RETRY(recvmsg(aNetlinkSocket, &rtnl_reply, MSG_DONTWAIT));
676 if (rc < 0) {
677 return;
679 size_t netlink_bytes = rc;
681 struct nlmsghdr* nlh = reinterpret_cast<struct nlmsghdr*>(buffer);
683 for (; NLMSG_OK(nlh, netlink_bytes); nlh = NLMSG_NEXT(nlh, netlink_bytes)) {
684 // If PID in the message is our PID, then it's a response to our request.
685 // Otherwise it's a multicast message.
686 bool isResponse = (pid_t)nlh->nlmsg_pid == mPid;
687 if (isResponse) {
688 if (!mOutgoingMessages.Length() || !mOutgoingMessages[0]->IsPending()) {
689 // There is no enqueued message pending?
690 LOG((
691 "Ignoring message seq_id %u, because there is no associated message"
692 " pending",
693 nlh->nlmsg_seq));
694 continue;
697 if (mOutgoingMessages[0]->SeqId() != nlh->nlmsg_seq) {
698 LOG(("Received unexpected seq_id [received=%u, expected=%u]",
699 nlh->nlmsg_seq, mOutgoingMessages[0]->SeqId()));
700 RemovePendingMsg();
701 continue;
705 switch (nlh->nlmsg_type) {
706 case NLMSG_DONE: /* Message signalling end of dump for responses to
707 request containing NLM_F_DUMP flag */
708 LOG(("received NLMSG_DONE"));
709 if (isResponse) {
710 RemovePendingMsg();
712 break;
713 case NLMSG_ERROR:
714 LOG(("received NLMSG_ERROR"));
715 if (isResponse) {
716 if (mOutgoingMessages[0]->MsgType() == NetlinkMsg::kRtMsg) {
717 OnRouteCheckResult(nullptr);
719 RemovePendingMsg();
721 break;
722 case RTM_NEWLINK:
723 case RTM_DELLINK:
724 MOZ_ASSERT(!isResponse ||
725 (nlh->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI);
726 OnLinkMessage(nlh);
727 break;
728 case RTM_NEWADDR:
729 case RTM_DELADDR:
730 MOZ_ASSERT(!isResponse ||
731 (nlh->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI);
732 OnAddrMessage(nlh);
733 break;
734 case RTM_NEWROUTE:
735 case RTM_DELROUTE:
736 if (isResponse && ((nlh->nlmsg_flags & NLM_F_MULTI) != NLM_F_MULTI)) {
737 // If it's not multipart message, then it must be response to a route
738 // check.
739 MOZ_ASSERT(mOutgoingMessages[0]->MsgType() == NetlinkMsg::kRtMsg);
740 OnRouteCheckResult(nlh);
741 RemovePendingMsg();
742 } else {
743 OnRouteMessage(nlh);
745 break;
746 case RTM_NEWNEIGH:
747 case RTM_DELNEIGH:
748 MOZ_ASSERT(!isResponse ||
749 (nlh->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI);
750 OnNeighborMessage(nlh);
751 break;
752 default:
753 break;
758 void NetlinkService::OnLinkMessage(struct nlmsghdr* aNlh) {
759 LOG(("NetlinkService::OnLinkMessage [type=%s]",
760 aNlh->nlmsg_type == RTM_NEWLINK ? "new" : "del"));
762 UniquePtr<NetlinkLink> link(new NetlinkLink());
763 if (!link->Init(aNlh)) {
764 return;
767 const uint32_t linkIndex = link->GetIndex();
768 mLinks.WithEntryHandle(linkIndex, [&](auto&& entry) {
769 nsAutoCString linkName;
770 link->GetName(linkName);
772 if (aNlh->nlmsg_type == RTM_NEWLINK) {
773 if (!entry) {
774 LOG(("Creating new link [index=%u, name=%s, flags=%u, type=%u]",
775 linkIndex, linkName.get(), link->GetFlags(), link->GetType()));
776 entry.Insert(MakeUnique<LinkInfo>(std::move(link)));
777 } else {
778 LOG(("Updating link [index=%u, name=%s, flags=%u, type=%u]", linkIndex,
779 linkName.get(), link->GetFlags(), link->GetType()));
781 auto* linkInfo = entry->get();
783 // Check whether administrative state has changed.
784 if (linkInfo->mLink->GetFlags() & IFF_UP &&
785 !(link->GetFlags() & IFF_UP)) {
786 LOG((" link went down"));
787 // If the link went down, remove all routes and neighbors, but keep
788 // addresses.
789 linkInfo->mDefaultRoutes.Clear();
790 linkInfo->mNeighbors.Clear();
793 linkInfo->mLink = std::move(link);
794 linkInfo->UpdateStatus();
796 } else {
797 if (!entry) {
798 // This can happen during startup
799 LOG(("Link info doesn't exist [index=%u, name=%s]", linkIndex,
800 linkName.get()));
801 } else {
802 LOG(("Removing link [index=%u, name=%s]", linkIndex, linkName.get()));
803 entry.Remove();
809 void NetlinkService::OnAddrMessage(struct nlmsghdr* aNlh) {
810 LOG(("NetlinkService::OnAddrMessage [type=%s]",
811 aNlh->nlmsg_type == RTM_NEWADDR ? "new" : "del"));
813 UniquePtr<NetlinkAddress> address(new NetlinkAddress());
814 if (!address->Init(aNlh)) {
815 return;
818 uint32_t ifIdx = address->GetIndex();
820 nsAutoCString addrStr;
821 GetAddrStr(address->GetAddrPtr(), address->Family(), addrStr);
823 LinkInfo* linkInfo = nullptr;
824 mLinks.Get(ifIdx, &linkInfo);
825 if (!linkInfo) {
826 // This can happen during startup
827 LOG(("Cannot find link info [ifIdx=%u, addr=%s/%u", ifIdx, addrStr.get(),
828 address->GetPrefixLen()));
829 return;
832 // There might be already an equal address in the array even in case of
833 // RTM_NEWADDR message, e.g. when lifetime of IPv6 address is renewed. Equal
834 // in this case means that IP and prefix is the same but some attributes
835 // might be different. Remove existing equal address in case of RTM_DELADDR
836 // as well as RTM_NEWADDR message and add a new one in the latter case.
837 for (uint32_t i = 0; i < linkInfo->mAddresses.Length(); ++i) {
838 if (aNlh->nlmsg_type == RTM_NEWADDR &&
839 linkInfo->mAddresses[i]->MsgEquals(*address)) {
840 // If the new address is exactly the same, there is nothing to do.
841 LOG(("Exactly the same address already exists [ifIdx=%u, addr=%s/%u",
842 ifIdx, addrStr.get(), address->GetPrefixLen()));
843 return;
846 if (linkInfo->mAddresses[i]->Equals(*address)) {
847 LOG(("Removing address [ifIdx=%u, addr=%s/%u]", ifIdx, addrStr.get(),
848 address->GetPrefixLen()));
849 linkInfo->mAddresses.RemoveElementAt(i);
850 break;
854 if (aNlh->nlmsg_type == RTM_NEWADDR) {
855 LOG(("Adding address [ifIdx=%u, addr=%s/%u]", ifIdx, addrStr.get(),
856 address->GetPrefixLen()));
857 linkInfo->mAddresses.AppendElement(std::move(address));
858 } else {
859 // Remove all routes associated with this address
860 for (uint32_t i = linkInfo->mDefaultRoutes.Length(); i-- > 0;) {
861 MOZ_ASSERT(linkInfo->mDefaultRoutes[i]->GetGWAddrPtr(),
862 "Stored routes must have gateway!");
863 if (linkInfo->mDefaultRoutes[i]->Family() == address->Family() &&
864 address->ContainsAddr(linkInfo->mDefaultRoutes[i]->GetGWAddrPtr())) {
865 if (LOG_ENABLED()) {
866 nsAutoCString routeDbgStr;
867 linkInfo->mDefaultRoutes[i]->GetAsString(routeDbgStr);
868 LOG(("Removing default route: %s", routeDbgStr.get()));
870 linkInfo->mDefaultRoutes.RemoveElementAt(i);
874 // Remove all neighbors associated with this address
875 for (auto iter = linkInfo->mNeighbors.Iter(); !iter.Done(); iter.Next()) {
876 NetlinkNeighbor* neigh = iter.UserData();
877 if (neigh->Family() == address->Family() &&
878 address->ContainsAddr(neigh->GetAddrPtr())) {
879 if (LOG_ENABLED()) {
880 nsAutoCString neighDbgStr;
881 neigh->GetAsString(neighDbgStr);
882 LOG(("Removing neighbor %s", neighDbgStr.get()));
884 iter.Remove();
889 // Address change on the interface can change its status
890 linkInfo->UpdateStatus();
892 // Don't treat address changes during initial scan as a network change
893 if (mInitialScanFinished) {
894 // Send network event change regardless of whether the ID has changed or
895 // not
896 mSendNetworkChangeEvent = true;
897 TriggerNetworkIDCalculation();
901 void NetlinkService::OnRouteMessage(struct nlmsghdr* aNlh) {
902 LOG(("NetlinkService::OnRouteMessage [type=%s]",
903 aNlh->nlmsg_type == RTM_NEWROUTE ? "new" : "del"));
905 UniquePtr<NetlinkRoute> route(new NetlinkRoute());
906 if (!route->Init(aNlh)) {
907 return;
910 if (!route->IsUnicast() || !route->ScopeIsUniverse()) {
911 // Use only unicast routes
912 if (LOG_ENABLED()) {
913 nsAutoCString routeDbgStr;
914 route->GetAsString(routeDbgStr);
915 LOG(("Not an unicast global route: %s", routeDbgStr.get()));
917 return;
920 // Adding/removing any unicast route might change network ID
921 TriggerNetworkIDCalculation();
923 if (!route->IsDefault()) {
924 // Store only default routes
925 if (LOG_ENABLED()) {
926 nsAutoCString routeDbgStr;
927 route->GetAsString(routeDbgStr);
928 LOG(("Not a default route: %s", routeDbgStr.get()));
930 return;
933 if (!route->HasOif()) {
934 if (LOG_ENABLED()) {
935 nsAutoCString routeDbgStr;
936 route->GetAsString(routeDbgStr);
937 LOG(("There is no output interface in route: %s", routeDbgStr.get()));
939 return;
942 if (!route->GetGWAddrPtr()) {
943 // We won't use the route if there is no gateway, so don't store it
944 if (LOG_ENABLED()) {
945 nsAutoCString routeDbgStr;
946 route->GetAsString(routeDbgStr);
947 LOG(("There is no gateway in route: %s", routeDbgStr.get()));
949 return;
952 if (route->Family() == AF_INET6 &&
953 net::utils::ipv6_scope((const unsigned char*)route->GetGWAddrPtr()) !=
954 IPV6_SCOPE_GLOBAL) {
955 if (LOG_ENABLED()) {
956 nsAutoCString routeDbgStr;
957 route->GetAsString(routeDbgStr);
958 LOG(("Scope of GW isn't global: %s", routeDbgStr.get()));
960 return;
963 LinkInfo* linkInfo = nullptr;
964 mLinks.Get(route->Oif(), &linkInfo);
965 if (!linkInfo) {
966 // This can happen during startup
967 if (LOG_ENABLED()) {
968 nsAutoCString routeDbgStr;
969 route->GetAsString(routeDbgStr);
970 LOG(("Cannot find link info for route: %s", routeDbgStr.get()));
972 return;
975 for (uint32_t i = 0; i < linkInfo->mDefaultRoutes.Length(); ++i) {
976 if (linkInfo->mDefaultRoutes[i]->Equals(*route)) {
977 // We shouldn't find equal route when adding a new one, but just in case
978 // it can happen remove the old one to avoid duplicities.
979 if (LOG_ENABLED()) {
980 nsAutoCString routeDbgStr;
981 route->GetAsString(routeDbgStr);
982 LOG(("Removing default route: %s", routeDbgStr.get()));
984 linkInfo->mDefaultRoutes.RemoveElementAt(i);
985 break;
989 if (aNlh->nlmsg_type == RTM_NEWROUTE) {
990 if (LOG_ENABLED()) {
991 nsAutoCString routeDbgStr;
992 route->GetAsString(routeDbgStr);
993 LOG(("Adding default route: %s", routeDbgStr.get()));
995 linkInfo->mDefaultRoutes.AppendElement(std::move(route));
999 void NetlinkService::OnNeighborMessage(struct nlmsghdr* aNlh) {
1000 LOG(("NetlinkService::OnNeighborMessage [type=%s]",
1001 aNlh->nlmsg_type == RTM_NEWNEIGH ? "new" : "del"));
1003 UniquePtr<NetlinkNeighbor> neigh(new NetlinkNeighbor());
1004 if (!neigh->Init(aNlh)) {
1005 return;
1008 LinkInfo* linkInfo = nullptr;
1009 mLinks.Get(neigh->GetIndex(), &linkInfo);
1010 if (!linkInfo) {
1011 // This can happen during startup
1012 if (LOG_ENABLED()) {
1013 nsAutoCString neighDbgStr;
1014 neigh->GetAsString(neighDbgStr);
1015 LOG(("Cannot find link info for neighbor: %s", neighDbgStr.get()));
1017 return;
1020 if (!linkInfo->mLink->IsTypeEther()) {
1021 if (LOG_ENABLED()) {
1022 nsAutoCString neighDbgStr;
1023 neigh->GetAsString(neighDbgStr);
1024 LOG(("Ignoring message on non-ethernet link: %s", neighDbgStr.get()));
1026 return;
1029 nsAutoCString key;
1030 GetAddrStr(neigh->GetAddrPtr(), neigh->Family(), key);
1032 if (aNlh->nlmsg_type == RTM_NEWNEIGH) {
1033 if (!mRecalculateNetworkId && neigh->HasMAC()) {
1034 NetlinkNeighbor* oldNeigh = nullptr;
1035 linkInfo->mNeighbors.Get(key, &oldNeigh);
1037 if (!oldNeigh || !oldNeigh->HasMAC()) {
1038 // The MAC address was added, if it's a host from some of the saved
1039 // routing tables we should recalculate network ID
1040 for (uint32_t i = 0; i < linkInfo->mDefaultRoutes.Length(); ++i) {
1041 if (linkInfo->mDefaultRoutes[i]->GatewayEquals(*neigh)) {
1042 TriggerNetworkIDCalculation();
1043 break;
1046 if ((mIPv4RouteCheckResult &&
1047 mIPv4RouteCheckResult->GatewayEquals(*neigh)) ||
1048 (mIPv6RouteCheckResult &&
1049 mIPv6RouteCheckResult->GatewayEquals(*neigh))) {
1050 TriggerNetworkIDCalculation();
1055 if (LOG_ENABLED()) {
1056 nsAutoCString neighDbgStr;
1057 neigh->GetAsString(neighDbgStr);
1058 LOG(("Adding neighbor: %s", neighDbgStr.get()));
1060 linkInfo->mNeighbors.InsertOrUpdate(key, std::move(neigh));
1061 } else {
1062 if (LOG_ENABLED()) {
1063 nsAutoCString neighDbgStr;
1064 neigh->GetAsString(neighDbgStr);
1065 LOG(("Removing neighbor %s", neighDbgStr.get()));
1067 linkInfo->mNeighbors.Remove(key);
1071 void NetlinkService::OnRouteCheckResult(struct nlmsghdr* aNlh) {
1072 LOG(("NetlinkService::OnRouteCheckResult"));
1073 UniquePtr<NetlinkRoute> route;
1075 if (aNlh) {
1076 route = MakeUnique<NetlinkRoute>();
1077 if (!route->Init(aNlh)) {
1078 route = nullptr;
1079 } else {
1080 if (!route->IsUnicast() || !route->ScopeIsUniverse()) {
1081 if (LOG_ENABLED()) {
1082 nsAutoCString routeDbgStr;
1083 route->GetAsString(routeDbgStr);
1084 LOG(("Not an unicast global route: %s", routeDbgStr.get()));
1086 route = nullptr;
1087 } else if (!route->HasOif()) {
1088 if (LOG_ENABLED()) {
1089 nsAutoCString routeDbgStr;
1090 route->GetAsString(routeDbgStr);
1091 LOG(("There is no output interface in route: %s", routeDbgStr.get()));
1093 route = nullptr;
1098 if (LOG_ENABLED()) {
1099 if (route) {
1100 nsAutoCString routeDbgStr;
1101 route->GetAsString(routeDbgStr);
1102 LOG(("Storing route: %s", routeDbgStr.get()));
1103 } else {
1104 LOG(("Clearing result for the check"));
1108 if (mOutgoingMessages[0]->Family() == AF_INET) {
1109 mIPv4RouteCheckResult = std::move(route);
1110 } else {
1111 mIPv6RouteCheckResult = std::move(route);
1115 void NetlinkService::EnqueueGenMsg(uint16_t aMsgType, uint8_t aFamily) {
1116 NetlinkGenMsg* msg = new NetlinkGenMsg(aMsgType, aFamily, ++mMsgId);
1117 mOutgoingMessages.AppendElement(msg);
1120 void NetlinkService::EnqueueRtMsg(uint8_t aFamily, void* aAddress) {
1121 NetlinkRtMsg* msg = new NetlinkRtMsg(aFamily, aAddress, ++mMsgId);
1122 mOutgoingMessages.AppendElement(msg);
1125 void NetlinkService::RemovePendingMsg() {
1126 LOG(("NetlinkService::RemovePendingMsg [seqId=%u]",
1127 mOutgoingMessages[0]->SeqId()));
1129 MOZ_ASSERT(mOutgoingMessages[0]->IsPending());
1131 DebugOnly<bool> isRtMessage =
1132 (mOutgoingMessages[0]->MsgType() == NetlinkMsg::kRtMsg);
1134 mOutgoingMessages.RemoveElementAt(0);
1135 if (!mOutgoingMessages.Length()) {
1136 if (!mInitialScanFinished) {
1137 // Now we've received all initial data from the kernel. Perform a link
1138 // check and trigger network ID calculation even if it wasn't triggered
1139 // by the incoming messages.
1140 mInitialScanFinished = true;
1142 TriggerNetworkIDCalculation();
1144 // Link status should be known by now.
1145 RefPtr<NetlinkServiceListener> listener;
1147 MutexAutoLock lock(mMutex);
1148 listener = mListener;
1150 if (listener) {
1151 listener->OnLinkStatusKnown();
1153 } else {
1154 // We've received last response for route check, calculate ID now
1155 MOZ_ASSERT(isRtMessage);
1156 CalculateNetworkID();
1161 NS_IMETHODIMP
1162 NetlinkService::Run() {
1163 int netlinkSocket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
1164 if (netlinkSocket < 0) {
1165 return NS_ERROR_FAILURE;
1168 struct sockaddr_nl addr {};
1169 memset(&addr, 0, sizeof(addr));
1171 addr.nl_family = AF_NETLINK;
1172 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_LINK |
1173 RTMGRP_NEIGH | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
1175 if (bind(netlinkSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
1176 // failure!
1177 EINTR_RETRY(close(netlinkSocket));
1178 return NS_ERROR_FAILURE;
1181 struct pollfd fds[2];
1182 fds[0].fd = mShutdownPipe[0];
1183 fds[0].events = POLLIN;
1184 fds[0].revents = 0;
1186 fds[1].fd = netlinkSocket;
1187 fds[1].events = POLLIN;
1188 fds[1].revents = 0;
1190 // send all requests to get initial network information
1191 EnqueueGenMsg(RTM_GETLINK, AF_PACKET);
1192 EnqueueGenMsg(RTM_GETNEIGH, AF_INET);
1193 EnqueueGenMsg(RTM_GETNEIGH, AF_INET6);
1194 EnqueueGenMsg(RTM_GETADDR, AF_PACKET);
1195 EnqueueGenMsg(RTM_GETROUTE, AF_PACKET);
1197 nsresult rv = NS_OK;
1198 bool shutdown = false;
1199 while (!shutdown) {
1200 if (mOutgoingMessages.Length() && !mOutgoingMessages[0]->IsPending()) {
1201 if (!mOutgoingMessages[0]->Send(netlinkSocket)) {
1202 LOG(("Failed to send netlink message"));
1203 mOutgoingMessages.RemoveElementAt(0);
1204 // try to send another message if available before polling
1205 continue;
1209 int rc = eintr_retry([&]() {
1210 AUTO_PROFILER_THREAD_SLEEP;
1211 return poll(fds, 2, GetPollWait());
1214 if (rc > 0) {
1215 if (fds[0].revents & POLLIN) {
1216 // shutdown, abort the loop!
1217 LOG(("thread shutdown received, dying...\n"));
1218 shutdown = true;
1219 } else if (fds[1].revents & POLLIN) {
1220 LOG(("netlink message received, handling it...\n"));
1221 OnNetlinkMessage(netlinkSocket);
1223 } else if (rc < 0) {
1224 rv = NS_ERROR_FAILURE;
1225 break;
1229 EINTR_RETRY(close(netlinkSocket));
1231 return rv;
1234 nsresult NetlinkService::Init(NetlinkServiceListener* aListener) {
1235 nsresult rv;
1237 mListener = aListener;
1239 if (inet_pton(AF_INET, ROUTE_CHECK_IPV4, &mRouteCheckIPv4) != 1) {
1240 LOG(("Cannot parse address " ROUTE_CHECK_IPV4));
1241 MOZ_DIAGNOSTIC_ASSERT(false, "Cannot parse address " ROUTE_CHECK_IPV4);
1242 return NS_ERROR_UNEXPECTED;
1245 if (inet_pton(AF_INET6, ROUTE_CHECK_IPV6, &mRouteCheckIPv6) != 1) {
1246 LOG(("Cannot parse address " ROUTE_CHECK_IPV6));
1247 MOZ_DIAGNOSTIC_ASSERT(false, "Cannot parse address " ROUTE_CHECK_IPV6);
1248 return NS_ERROR_UNEXPECTED;
1251 if (pipe(mShutdownPipe) == -1) {
1252 LOG(("Cannot create pipe"));
1253 return NS_ERROR_FAILURE;
1256 rv = NS_NewNamedThread("Netlink Monitor", getter_AddRefs(mThread), this);
1257 NS_ENSURE_SUCCESS(rv, rv);
1259 return NS_OK;
1262 nsresult NetlinkService::Shutdown() {
1263 LOG(("write() to signal thread shutdown\n"));
1266 MutexAutoLock lock(mMutex);
1267 mListener = nullptr;
1270 // awake the thread to make it terminate
1271 ssize_t rc = EINTR_RETRY(write(mShutdownPipe[1], "1", 1));
1272 LOG(("write() returned %d, errno == %d\n", (int)rc, errno));
1274 nsresult rv = mThread->Shutdown();
1276 // Have to break the cycle here, otherwise NetlinkService holds
1277 // onto the thread and the thread holds onto the NetlinkService
1278 // via its mRunnable
1279 mThread = nullptr;
1281 return rv;
1285 * A network event that might change network ID has been registered. Delay
1286 * network ID calculation and sending of the event in case it changed for
1287 * a while. Absorbing potential subsequent events increases chance of successful
1288 * network ID calculation (e.g. MAC address of the router might be discovered in
1289 * the meantime)
1291 void NetlinkService::TriggerNetworkIDCalculation() {
1292 LOG(("NetlinkService::TriggerNetworkIDCalculation"));
1294 if (mRecalculateNetworkId) {
1295 return;
1298 mRecalculateNetworkId = true;
1299 mTriggerTime = TimeStamp::Now();
1302 int NetlinkService::GetPollWait() {
1303 if (!mRecalculateNetworkId) {
1304 return -1;
1307 if (mOutgoingMessages.Length()) {
1308 MOZ_ASSERT(mOutgoingMessages[0]->IsPending());
1309 // Message is pending, we don't have to set timeout because we'll receive
1310 // reply from kernel ASAP
1311 return -1;
1314 MOZ_ASSERT(mInitialScanFinished);
1316 double period = (TimeStamp::Now() - mTriggerTime).ToMilliseconds();
1317 if (period >= kNetworkChangeCoalescingPeriod) {
1318 // Coalescing time has elapsed, send route check messages to find out
1319 // where IPv4 and IPv6 traffic is routed and calculate network ID after
1320 // the response is received.
1321 EnqueueRtMsg(AF_INET, &mRouteCheckIPv4);
1322 EnqueueRtMsg(AF_INET6, &mRouteCheckIPv6);
1324 // Return 0 to make sure we start sending enqueued messages immediately
1325 return 0;
1328 return static_cast<int>(kNetworkChangeCoalescingPeriod - period);
1331 class NeighborComparator {
1332 public:
1333 bool Equals(const NetlinkNeighbor* a, const NetlinkNeighbor* b) const {
1334 return (memcmp(a->GetMACPtr(), b->GetMACPtr(), ETH_ALEN) == 0);
1336 bool LessThan(const NetlinkNeighbor* a, const NetlinkNeighbor* b) const {
1337 return (memcmp(a->GetMACPtr(), b->GetMACPtr(), ETH_ALEN) < 0);
1341 class LinknameComparator {
1342 public:
1343 bool LessThan(const nsCString& aA, const nsCString& aB) const {
1344 return aA < aB;
1346 bool Equals(const nsCString& aA, const nsCString& aB) const {
1347 return aA == aB;
1351 // Get Gateway Neighbours for a particular Address Family, for which we know MAC
1352 // address
1353 void NetlinkService::GetGWNeighboursForFamily(
1354 uint8_t aFamily, nsTArray<NetlinkNeighbor*>& aGwNeighbors) {
1355 LOG(("NetlinkService::GetGWNeighboursForFamily"));
1356 // Check only routes on links that are up
1357 for (const auto& linkInfo : mLinks.Values()) {
1358 nsAutoCString linkName;
1359 linkInfo->mLink->GetName(linkName);
1361 if (!linkInfo->mIsUp) {
1362 LOG((" %s is down", linkName.get()));
1363 continue;
1366 if (!linkInfo->mLink->IsTypeEther()) {
1367 LOG((" %s is not ethernet link", linkName.get()));
1368 continue;
1371 LOG((" checking link %s", linkName.get()));
1373 // Check all default routes and try to get MAC of the gateway
1374 for (uint32_t i = 0; i < linkInfo->mDefaultRoutes.Length(); ++i) {
1375 if (LOG_ENABLED()) {
1376 nsAutoCString routeDbgStr;
1377 linkInfo->mDefaultRoutes[i]->GetAsString(routeDbgStr);
1378 LOG(("Checking default route: %s", routeDbgStr.get()));
1381 if (linkInfo->mDefaultRoutes[i]->Family() != aFamily) {
1382 LOG((" skipping due to different family"));
1383 continue;
1386 MOZ_ASSERT(linkInfo->mDefaultRoutes[i]->GetGWAddrPtr(),
1387 "Stored routes must have gateway!");
1389 nsAutoCString neighKey;
1390 GetAddrStr(linkInfo->mDefaultRoutes[i]->GetGWAddrPtr(), aFamily,
1391 neighKey);
1393 NetlinkNeighbor* neigh = nullptr;
1394 if (!linkInfo->mNeighbors.Get(neighKey, &neigh)) {
1395 LOG(("Neighbor %s not found in hashtable.", neighKey.get()));
1396 continue;
1399 if (!neigh->HasMAC()) {
1400 // We don't know MAC address
1401 LOG(("We have no MAC for neighbor %s.", neighKey.get()));
1402 continue;
1405 if (aGwNeighbors.IndexOf(neigh, 0, NeighborComparator()) !=
1406 nsTArray<NetlinkNeighbor*>::NoIndex) {
1407 // avoid host duplicities
1408 LOG(("MAC of neighbor %s is already selected for hashing.",
1409 neighKey.get()));
1410 continue;
1413 LOG(("MAC of neighbor %s will be used for network ID.", neighKey.get()));
1414 aGwNeighbors.AppendElement(neigh);
1419 bool NetlinkService::CalculateIDForEthernetLink(uint8_t aFamily,
1420 NetlinkRoute* aRouteCheckResult,
1421 uint32_t aRouteCheckIfIdx,
1422 LinkInfo* aRouteCheckLinkInfo,
1423 SHA1Sum* aSHA1) {
1424 LOG(("NetlinkService::CalculateIDForEthernetLink"));
1425 bool retval = false;
1426 const in_common_addr* addrPtr = aRouteCheckResult->GetGWAddrPtr();
1428 if (!addrPtr) {
1429 // This shouldn't normally happen, missing next hop in case of ethernet
1430 // device would mean that the checked host is on the same network.
1431 if (LOG_ENABLED()) {
1432 nsAutoCString routeDbgStr;
1433 aRouteCheckResult->GetAsString(routeDbgStr);
1434 LOG(("There is no next hop in route: %s", routeDbgStr.get()));
1436 return retval;
1439 // If we know MAC address of the next hop for mRouteCheckIPv4/6 host, hash
1440 // it even if it's MAC of some of the default routes we've checked above.
1441 // This ensures that if we have 2 different default routes and next hop for
1442 // mRouteCheckIPv4/6 changes from one default route to the other, we'll
1443 // detect it as a network change.
1444 nsAutoCString neighKey;
1445 GetAddrStr(addrPtr, aFamily, neighKey);
1446 LOG(("Next hop for the checked host is %s on ifIdx %u.", neighKey.get(),
1447 aRouteCheckIfIdx));
1449 NetlinkNeighbor* neigh = nullptr;
1450 if (!aRouteCheckLinkInfo->mNeighbors.Get(neighKey, &neigh)) {
1451 LOG(("Neighbor %s not found in hashtable.", neighKey.get()));
1452 return retval;
1455 if (!neigh->HasMAC()) {
1456 LOG(("We have no MAC for neighbor %s.", neighKey.get()));
1457 return retval;
1460 if (LOG_ENABLED()) {
1461 nsAutoCString neighDbgStr;
1462 neigh->GetAsString(neighDbgStr);
1463 LOG(("Hashing MAC address of neighbor: %s", neighDbgStr.get()));
1465 aSHA1->update(neigh->GetMACPtr(), ETH_ALEN);
1466 retval = true;
1468 return retval;
1471 bool NetlinkService::CalculateIDForNonEthernetLink(
1472 uint8_t aFamily, NetlinkRoute* aRouteCheckResult,
1473 nsTArray<nsCString>& aLinkNamesToHash, uint32_t aRouteCheckIfIdx,
1474 LinkInfo* aRouteCheckLinkInfo, SHA1Sum* aSHA1) {
1475 LOG(("NetlinkService::CalculateIDForNonEthernetLink"));
1476 bool retval = false;
1477 const in_common_addr* addrPtr = aRouteCheckResult->GetGWAddrPtr();
1478 nsAutoCString routeCheckLinkName;
1479 aRouteCheckLinkInfo->mLink->GetName(routeCheckLinkName);
1481 if (addrPtr) {
1482 // The route contains next hop. Hash the name of the interface (e.g.
1483 // "tun1") and the IP address of the next hop.
1485 nsAutoCString addrStr;
1486 GetAddrStr(addrPtr, aFamily, addrStr);
1487 size_t addrSize =
1488 (aFamily == AF_INET) ? sizeof(addrPtr->addr4) : sizeof(addrPtr->addr6);
1490 LOG(("Hashing link name %s", routeCheckLinkName.get()));
1491 aSHA1->update(routeCheckLinkName.get(), routeCheckLinkName.Length());
1493 // Don't hash GW address if it's rmnet_data device.
1494 if (!aLinkNamesToHash.Contains(routeCheckLinkName)) {
1495 LOG(("Hashing GW address %s", addrStr.get()));
1496 aSHA1->update(addrPtr, addrSize);
1499 retval = true;
1500 } else {
1501 // The traffic is routed directly via an interface. Hash the name of the
1502 // interface and the network address. Using host address would cause that
1503 // network ID would be different every time we get a different IP address
1504 // in this network/VPN.
1506 bool hasSrcAddr = aRouteCheckResult->HasPrefSrcAddr();
1507 if (!hasSrcAddr) {
1508 LOG(("There is no preferred source address."));
1511 NetlinkAddress* linkAddress = nullptr;
1512 // Find network address of the interface matching the source address. In
1513 // theory there could be multiple addresses with different prefix length.
1514 // Get the one with smallest prefix length.
1515 for (uint32_t i = 0; i < aRouteCheckLinkInfo->mAddresses.Length(); ++i) {
1516 if (!hasSrcAddr) {
1517 // there is no preferred src, match just the family
1518 if (aRouteCheckLinkInfo->mAddresses[i]->Family() != aFamily) {
1519 continue;
1521 } else if (!aRouteCheckResult->PrefSrcAddrEquals(
1522 *aRouteCheckLinkInfo->mAddresses[i])) {
1523 continue;
1526 if (!linkAddress ||
1527 linkAddress->GetPrefixLen() >
1528 aRouteCheckLinkInfo->mAddresses[i]->GetPrefixLen()) {
1529 // We have no address yet or this one has smaller prefix length,
1530 // use it.
1531 linkAddress = aRouteCheckLinkInfo->mAddresses[i].get();
1535 if (!linkAddress) {
1536 // There is no address in our array?
1537 if (LOG_ENABLED()) {
1538 nsAutoCString dbgStr;
1539 aRouteCheckResult->GetAsString(dbgStr);
1540 LOG(("No address found for preferred source address in route: %s",
1541 dbgStr.get()));
1543 return retval;
1546 in_common_addr prefix;
1547 int32_t prefixSize = (aFamily == AF_INET) ? (int32_t)sizeof(prefix.addr4)
1548 : (int32_t)sizeof(prefix.addr6);
1549 memcpy(&prefix, linkAddress->GetAddrPtr(), prefixSize);
1550 uint8_t maskit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
1551 int32_t bits = linkAddress->GetPrefixLen();
1552 if (bits > prefixSize * 8) {
1553 MOZ_ASSERT(false, "Unexpected prefix length!");
1554 LOG(("Unexpected prefix length %d, maximum for this family is %d", bits,
1555 prefixSize * 8));
1556 return retval;
1558 for (int32_t i = 0; i < prefixSize; i++) {
1559 uint8_t mask = (bits >= 8) ? 0xff : maskit[bits];
1560 ((unsigned char*)&prefix)[i] &= mask;
1561 bits -= 8;
1562 if (bits <= 0) {
1563 bits = 0;
1567 nsAutoCString addrStr;
1568 GetAddrStr(&prefix, aFamily, addrStr);
1569 LOG(("Hashing link name %s and network address %s/%u",
1570 routeCheckLinkName.get(), addrStr.get(), linkAddress->GetPrefixLen()));
1571 aSHA1->update(routeCheckLinkName.get(), routeCheckLinkName.Length());
1572 aSHA1->update(&prefix, prefixSize);
1573 bits = linkAddress->GetPrefixLen();
1574 aSHA1->update(&bits, sizeof(bits));
1575 retval = true;
1577 return retval;
1580 bool NetlinkService::CalculateIDForFamily(uint8_t aFamily, SHA1Sum* aSHA1) {
1581 LOG(("NetlinkService::CalculateIDForFamily [family=%s]",
1582 aFamily == AF_INET ? "AF_INET" : "AF_INET6"));
1584 bool retval = false;
1586 if (!mLinkUp) {
1587 // Skip ID calculation if the link is down, we have no ID...
1588 LOG(("Link is down, skipping ID calculation."));
1589 return retval;
1592 NetlinkRoute* routeCheckResult;
1593 if (aFamily == AF_INET) {
1594 routeCheckResult = mIPv4RouteCheckResult.get();
1595 } else {
1596 routeCheckResult = mIPv6RouteCheckResult.get();
1599 // All GW neighbors for which we know MAC address. We'll probably have at
1600 // most only one, but in case we have more default routes, we hash them all
1601 // even though the routing rules sends the traffic only via one of them.
1602 // If the system switches between them, we'll detect the change with
1603 // mIPv4/6RouteCheckResult.
1604 nsTArray<NetlinkNeighbor*> gwNeighbors;
1606 GetGWNeighboursForFamily(aFamily, gwNeighbors);
1608 // Sort them so we always have the same network ID on the same network
1609 gwNeighbors.Sort(NeighborComparator());
1611 for (uint32_t i = 0; i < gwNeighbors.Length(); ++i) {
1612 if (LOG_ENABLED()) {
1613 nsAutoCString neighDbgStr;
1614 gwNeighbors[i]->GetAsString(neighDbgStr);
1615 LOG(("Hashing MAC address of neighbor: %s", neighDbgStr.get()));
1617 aSHA1->update(gwNeighbors[i]->GetMACPtr(), ETH_ALEN);
1618 retval = true;
1621 nsTArray<nsCString> linkNamesToHash;
1622 if (!gwNeighbors.Length()) {
1623 // If we don't know MAC of the gateway and link is up, it's probably not
1624 // an ethernet link. If the name of the link begins with "rmnet" then
1625 // the mobile data is used. We cannot easily differentiate when user
1626 // switches sim cards so let's treat mobile data as a single network. We'll
1627 // simply hash link name. If the traffic is redirected via some VPN, it'll
1628 // still be detected below.
1630 // TODO: maybe we could get operator name via AndroidBridge
1631 for (const auto& linkInfo : mLinks.Values()) {
1632 if (linkInfo->mIsUp) {
1633 nsAutoCString linkName;
1634 linkInfo->mLink->GetName(linkName);
1635 if (StringBeginsWith(linkName, "rmnet"_ns)) {
1636 // Check whether there is some non-local address associated with this
1637 // link.
1638 for (uint32_t i = 0; i < linkInfo->mAddresses.Length(); ++i) {
1639 if (linkInfo->mAddresses[i]->Family() == aFamily &&
1640 linkInfo->mAddresses[i]->ScopeIsUniverse()) {
1641 linkNamesToHash.AppendElement(linkName);
1642 break;
1649 // Sort link names to ensure consistent results
1650 linkNamesToHash.Sort(LinknameComparator());
1652 for (uint32_t i = 0; i < linkNamesToHash.Length(); ++i) {
1653 LOG(("Hashing name of adapter: %s", linkNamesToHash[i].get()));
1654 aSHA1->update(linkNamesToHash[i].get(), linkNamesToHash[i].Length());
1655 retval = true;
1659 if (!routeCheckResult) {
1660 // If we don't have result for route check to mRouteCheckIPv4/6 host, the
1661 // network is unreachable and there is no more to do.
1662 LOG(("There is no route check result."));
1663 return retval;
1666 LinkInfo* routeCheckLinkInfo = nullptr;
1667 uint32_t routeCheckIfIdx = routeCheckResult->Oif();
1668 if (!mLinks.Get(routeCheckIfIdx, &routeCheckLinkInfo)) {
1669 LOG(("Cannot find link with index %u ??", routeCheckIfIdx));
1670 return retval;
1673 if (routeCheckLinkInfo->mLink->IsTypeEther()) {
1674 // The traffic is routed through an ethernet device.
1675 retval |= CalculateIDForEthernetLink(
1676 aFamily, routeCheckResult, routeCheckIfIdx, routeCheckLinkInfo, aSHA1);
1677 } else {
1678 // The traffic is routed through a non-ethernet device.
1679 retval |= CalculateIDForNonEthernetLink(aFamily, routeCheckResult,
1680 linkNamesToHash, routeCheckIfIdx,
1681 routeCheckLinkInfo, aSHA1);
1684 return retval;
1687 void NetlinkService::ExtractDNSProperties() {
1688 MOZ_ASSERT(!NS_IsMainThread(), "Must not be called on the main thread");
1689 nsTArray<nsCString> suffixList;
1690 nsTArray<NetAddr> resolvers;
1691 #if defined(HAVE_RES_NINIT)
1692 [&]() {
1693 struct __res_state res {};
1694 int ret = res_ninit(&res);
1695 if (ret != 0) {
1696 LOG(("Call to res_ninit failed: %d", ret));
1697 return;
1700 // Get DNS suffixes
1701 for (int i = 0; i < MAXDNSRCH; i++) {
1702 if (!res.dnsrch[i]) {
1703 break;
1705 suffixList.AppendElement(nsCString(res.dnsrch[i]));
1708 // Get DNS resolvers
1709 // Chromium's dns_config_service_posix.cc is the origin of this code
1710 // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|.
1711 // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|,
1712 // but we have to combine the two arrays ourselves.
1713 for (int i = 0; i < res.nscount; ++i) {
1714 const struct sockaddr* addr = nullptr;
1715 size_t addr_len = 0;
1716 if (res.nsaddr_list[i].sin_family) { // The indicator used by res_nsend.
1717 addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]);
1718 addr_len = sizeof res.nsaddr_list[i];
1719 } else if (res._u._ext.nsaddrs[i]) {
1720 addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
1721 addr_len = sizeof *res._u._ext.nsaddrs[i];
1722 } else {
1723 LOG(("Bad ext struct"));
1724 return;
1726 const socklen_t kSockaddrInSize = sizeof(struct sockaddr_in);
1727 const socklen_t kSockaddrIn6Size = sizeof(struct sockaddr_in6);
1729 if ((addr->sa_family == AF_INET && addr_len < kSockaddrInSize) ||
1730 (addr->sa_family == AF_INET6 && addr_len < kSockaddrIn6Size)) {
1731 LOG(("Bad address size"));
1732 return;
1735 NetAddr ip;
1736 if (addr->sa_family == AF_INET) {
1737 const struct sockaddr_in* sin = (const struct sockaddr_in*)addr;
1738 ip.inet.family = AF_INET;
1739 ip.inet.ip = sin->sin_addr.s_addr;
1740 ip.inet.port = sin->sin_port;
1741 } else if (addr->sa_family == AF_INET6) {
1742 const struct sockaddr_in6* sin6 = (const struct sockaddr_in6*)addr;
1743 ip.inet6.family = AF_INET6;
1744 memcpy(&ip.inet6.ip.u8, &sin6->sin6_addr, sizeof(ip.inet6.ip.u8));
1745 ip.inet6.port = sin6->sin6_port;
1746 } else {
1747 MOZ_ASSERT_UNREACHABLE("Unexpected sa_family");
1748 return;
1751 resolvers.AppendElement(ip);
1754 res_nclose(&res);
1755 }();
1757 #endif
1758 RefPtr<NetlinkServiceListener> listener;
1760 MutexAutoLock lock(mMutex);
1761 listener = mListener;
1762 mDNSSuffixList = std::move(suffixList);
1763 mDNSResolvers = std::move(resolvers);
1766 if (listener) {
1767 listener->OnDnsSuffixListUpdated();
1771 void NetlinkService::UpdateLinkStatus() {
1772 LOG(("NetlinkService::UpdateLinkStatus"));
1774 MOZ_ASSERT(!mRecalculateNetworkId);
1775 MOZ_ASSERT(mInitialScanFinished);
1777 // Link is up when we have a route for ROUTE_CHECK_IPV4 or ROUTE_CHECK_IPV6
1778 bool newLinkUp = mIPv4RouteCheckResult || mIPv6RouteCheckResult;
1780 if (mLinkUp == newLinkUp) {
1781 LOG(("Link status hasn't changed [linkUp=%d]", mLinkUp));
1782 } else {
1783 LOG(("Link status has changed [linkUp=%d]", newLinkUp));
1784 RefPtr<NetlinkServiceListener> listener;
1786 MutexAutoLock lock(mMutex);
1787 listener = mListener;
1788 mLinkUp = newLinkUp;
1790 if (mLinkUp) {
1791 if (listener) {
1792 listener->OnLinkUp();
1794 } else {
1795 if (listener) {
1796 listener->OnLinkDown();
1802 // Figure out the "network identification".
1803 void NetlinkService::CalculateNetworkID() {
1804 LOG(("NetlinkService::CalculateNetworkID"));
1806 MOZ_ASSERT(!NS_IsMainThread(), "Must not be called on the main thread");
1807 MOZ_ASSERT(mRecalculateNetworkId);
1809 mRecalculateNetworkId = false;
1811 SHA1Sum sha1;
1813 UpdateLinkStatus();
1814 ExtractDNSProperties();
1816 bool idChanged = false;
1817 bool found4 = CalculateIDForFamily(AF_INET, &sha1);
1818 bool found6 = CalculateIDForFamily(AF_INET6, &sha1);
1820 if (found4 || found6) {
1821 nsAutoCString output;
1822 SeedNetworkId(sha1);
1823 uint8_t digest[SHA1Sum::kHashSize];
1824 sha1.finish(digest);
1825 nsAutoCString newString(reinterpret_cast<char*>(digest),
1826 SHA1Sum::kHashSize);
1827 nsresult rv = Base64Encode(newString, output);
1828 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
1829 LOG(("networkid: id %s\n", output.get()));
1830 MutexAutoLock lock(mMutex);
1831 if (mNetworkId != output) {
1832 // new id
1833 if (found4 && !found6) {
1834 Telemetry::Accumulate(Telemetry::NETWORK_ID2, 1); // IPv4 only
1835 } else if (!found4 && found6) {
1836 Telemetry::Accumulate(Telemetry::NETWORK_ID2, 3); // IPv6 only
1837 } else {
1838 Telemetry::Accumulate(Telemetry::NETWORK_ID2, 4); // Both!
1840 mNetworkId = output;
1841 idChanged = true;
1842 } else {
1843 // same id
1844 LOG(("Same network id"));
1845 Telemetry::Accumulate(Telemetry::NETWORK_ID2, 2);
1847 } else {
1848 // no id
1849 LOG(("No network id"));
1850 MutexAutoLock lock(mMutex);
1851 if (!mNetworkId.IsEmpty()) {
1852 mNetworkId.Truncate();
1853 idChanged = true;
1854 Telemetry::Accumulate(Telemetry::NETWORK_ID2, 0);
1858 if (idChanged) {
1859 sHasNonLocalIPv6 = found6;
1860 LOG(("has IPv6: %d", bool(sHasNonLocalIPv6)));
1863 // If this is first time we calculate network ID, don't report it as a network
1864 // change. We've started with an empty ID and we've just calculated the
1865 // correct ID. The network hasn't really changed.
1866 static bool initialIDCalculation = true;
1868 RefPtr<NetlinkServiceListener> listener;
1870 MutexAutoLock lock(mMutex);
1871 listener = mListener;
1874 if (!initialIDCalculation && idChanged && listener) {
1875 listener->OnNetworkIDChanged();
1876 mSendNetworkChangeEvent = true;
1879 if (mSendNetworkChangeEvent && listener) {
1880 listener->OnNetworkChanged();
1883 initialIDCalculation = false;
1884 mSendNetworkChangeEvent = false;
1887 void NetlinkService::GetNetworkID(nsACString& aNetworkID) {
1888 MutexAutoLock lock(mMutex);
1889 aNetworkID = mNetworkId;
1892 nsresult NetlinkService::GetDnsSuffixList(nsTArray<nsCString>& aDnsSuffixList) {
1893 #if defined(HAVE_RES_NINIT)
1894 MutexAutoLock lock(mMutex);
1895 aDnsSuffixList = mDNSSuffixList.Clone();
1896 return NS_OK;
1897 #else
1898 return NS_ERROR_NOT_IMPLEMENTED;
1899 #endif
1902 nsresult NetlinkService::GetResolvers(nsTArray<NetAddr>& aResolvers) {
1903 #if defined(HAVE_RES_NINIT)
1904 MutexAutoLock lock(mMutex);
1905 aResolvers = mDNSResolvers.Clone();
1906 return NS_OK;
1907 #else
1908 return NS_ERROR_NOT_IMPLEMENTED;
1909 #endif
1912 void NetlinkService::GetIsLinkUp(bool* aIsUp) {
1913 MutexAutoLock lock(mMutex);
1914 *aIsUp = mLinkUp;
1917 } // namespace mozilla::net