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/. */
8 #include <netinet/ether.h>
12 #include <linux/rtnetlink.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
17 #include "nsThreadUtils.h"
18 #include "NetlinkService.h"
19 #include "nsIThread.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>
39 namespace mozilla::net
{
42 static auto eintr_retry(F
&& func
) ->
43 typename FunctionTypeTraits
<decltype(func
)>::ReturnType
{
44 typename FunctionTypeTraits
<decltype(func
)>::ReturnType _rc
;
47 } while (_rc
== -1 && errno
== EINTR
);
51 #define EINTR_RETRY(expr) eintr_retry([&]() { return expr; })
53 // period during which to absorb subsequent network change events, in
55 static const unsigned int kNetworkChangeCoalescingPeriod
= 1000;
57 static LazyLogModule
gNlSvcLog("NetlinkService");
58 #define LOG(args) MOZ_LOG(gNlSvcLog, mozilla::LogLevel::Debug, args)
61 #define LOG_ENABLED() MOZ_LOG_TEST(gNlSvcLog, mozilla::LogLevel::Debug)
63 using in_common_addr
= union {
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
];
73 if (aFamily
== AF_INET
) {
74 inet_ntop(AF_INET
, &(aAddr
->addr4
), addr
, INET_ADDRSTRLEN
);
76 inet_ntop(AF_INET6
, &(aAddr
->addr6
), addr
, INET6_ADDRSTRLEN
);
81 // Assume true by default.
82 static Atomic
<bool, MemoryOrdering::Relaxed
> sHasNonLocalIPv6
{true};
84 bool NetlinkService::HasNonLocalIPv6Address() { return sHasNonLocalIPv6
; }
86 class NetlinkAddress
{
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
) {
104 if (mIfam
.ifa_index
!= aOther
.mIfam
.ifa_index
) {
105 // addresses are different when they are on a different interface
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.
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
,
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
)) {
144 bool Init(struct nlmsghdr
* aNlh
) {
145 struct ifaddrmsg
* ifam
;
149 ifam
= (ifaddrmsg
*)NLMSG_DATA(aNlh
);
150 len
= IFA_PAYLOAD(aNlh
);
152 if (ifam
->ifa_family
!= AF_INET
&& ifam
->ifa_family
!= AF_INET6
) {
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
));
163 if (attr
->rta_type
== IFA_LOCAL
) {
164 // local address is preferred, so don't continue parsing other
175 memcpy(&mIfam
, (ifaddrmsg
*)NLMSG_DATA(aNlh
), sizeof(mIfam
));
180 in_common_addr mAddr
;
181 struct ifaddrmsg mIfam
;
184 class NetlinkNeighbor
{
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=");
202 _retval
.Append(" family=AF_INET6 if=");
204 _retval
.AppendInt(mNeigh
.ndm_ifindex
);
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],
213 bool Init(struct nlmsghdr
* aNlh
) {
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
) {
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
);
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
));
244 memcpy(&mNeigh
, (ndmsg
*)NLMSG_DATA(aNlh
), sizeof(mNeigh
));
250 uint8_t mMAC
[ETH_ALEN
]{};
251 in_common_addr mAddr
{};
252 struct ndmsg mNeigh
{};
257 NetlinkLink() = default;
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
;
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
));
292 memcpy(&mIface
, (ifinfomsg
*)NLMSG_DATA(aNlh
), sizeof(mIface
));
298 struct ifinfomsg mIface
{};
305 mHasPrefSrcAddr(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) {
330 if (mHasOif
!= aOther
.mHasOif
|| mOif
!= aOther
.mOif
) {
333 if (mHasPrio
!= aOther
.mHasPrio
|| mPrio
!= aOther
.mPrio
) {
336 if ((mHasGWAddr
!= aOther
.mHasGWAddr
) ||
337 (mHasGWAddr
&& memcmp(&mGWAddr
, &(aOther
.mGWAddr
), addrSize
) != 0)) {
340 if ((mHasDstAddr
!= aOther
.mHasDstAddr
) ||
341 (mHasDstAddr
&& memcmp(&mDstAddr
, &(aOther
.mDstAddr
), addrSize
) != 0)) {
344 if ((mHasPrefSrcAddr
!= aOther
.mHasPrefSrcAddr
) ||
346 memcmp(&mPrefSrcAddr
, &(aOther
.mPrefSrcAddr
), addrSize
) != 0)) {
352 bool GatewayEquals(const NetlinkNeighbor
& aNeigh
) const {
356 if (aNeigh
.Family() != mRtm
.rtm_family
) {
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
) {
368 if (mRtm
.rtm_family
!= aRoute
->mRtm
.rtm_family
) {
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
) {
380 if (mRtm
.rtm_family
!= aAddress
.Family()) {
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/");
400 _retval
.Append(" family=AF_INET6 dst=");
401 addrStr
.Assign("::/");
404 GetAddrStr(&mDstAddr
, mRtm
.rtm_family
, addrStr
);
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
);
415 _retval
.Append(" via=");
416 GetAddrStr(&mGWAddr
, mRtm
.rtm_family
, addrStr
);
417 _retval
.Append(addrStr
);
420 _retval
.Append(" oif=");
421 _retval
.AppendInt(mOif
);
424 _retval
.Append(" prio=");
425 _retval
.AppendInt(mPrio
);
429 bool Init(struct nlmsghdr
* aNlh
) {
434 rtm
= (rtmsg
*)NLMSG_DATA(aNlh
);
435 len
= RTM_PAYLOAD(aNlh
);
437 if (rtm
->rtm_family
!= AF_INET
&& rtm
->rtm_family
!= AF_INET6
) {
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
));
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
));
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
);
460 } else if (attr
->rta_type
== RTA_PRIORITY
) {
461 mPrio
= *(uint32_t*)RTA_DATA(attr
);
466 memcpy(&mRtm
, (rtmsg
*)NLMSG_DATA(aNlh
), sizeof(mRtm
));
472 bool mHasPrefSrcAddr
: 1;
473 bool mHasDstAddr
: 1;
477 in_common_addr mGWAddr
{};
478 in_common_addr mDstAddr
{};
479 in_common_addr mPrefSrcAddr
{};
483 struct rtmsg mRtm
{};
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;
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;
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
) {
529 bool mIsPending
{false};
532 class NetlinkGenMsg
: public NetlinkMsg
{
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
; }
561 class NetlinkRtMsg
: public NetlinkMsg
{
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;
579 rta
= (struct rtattr
*)(((char*)&mReq
) + NLMSG_ALIGN(mReq
.hdr
.nlmsg_len
));
580 rta
->rta_type
= RTA_DST
;
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
; }
600 unsigned char data
[1024];
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
;
615 if (!mLink
->IsUp()) {
616 // The link is not up or is a loopback
617 LOG(("The link is down or is a loopback"));
619 // Link is up when there is non-local address associated with it.
620 for (uint32_t i
= 0; i
< mAddresses
.Length(); ++i
) {
622 nsAutoCString dbgStr
;
623 GetAddrStr(mAddresses
[i
]->GetAddrPtr(), mAddresses
[i
]->Family(),
625 LOG(("checking address %s", dbgStr
.get()));
627 if (mAddresses
[i
]->ScopeIsUniverse()) {
629 LOG(("global address found"));
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.
658 struct sockaddr_nl kernel
{};
659 memset(&kernel
, 0, sizeof(kernel
));
660 kernel
.nl_family
= AF_NETLINK
;
661 kernel
.nl_groups
= 0;
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
));
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
;
688 if (!mOutgoingMessages
.Length() || !mOutgoingMessages
[0]->IsPending()) {
689 // There is no enqueued message pending?
691 "Ignoring message seq_id %u, because there is no associated message"
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()));
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"));
714 LOG(("received NLMSG_ERROR"));
716 if (mOutgoingMessages
[0]->MsgType() == NetlinkMsg::kRtMsg
) {
717 OnRouteCheckResult(nullptr);
724 MOZ_ASSERT(!isResponse
||
725 (nlh
->nlmsg_flags
& NLM_F_MULTI
) == NLM_F_MULTI
);
730 MOZ_ASSERT(!isResponse
||
731 (nlh
->nlmsg_flags
& NLM_F_MULTI
) == NLM_F_MULTI
);
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
739 MOZ_ASSERT(mOutgoingMessages
[0]->MsgType() == NetlinkMsg::kRtMsg
);
740 OnRouteCheckResult(nlh
);
748 MOZ_ASSERT(!isResponse
||
749 (nlh
->nlmsg_flags
& NLM_F_MULTI
) == NLM_F_MULTI
);
750 OnNeighborMessage(nlh
);
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
)) {
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
) {
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
)));
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
789 linkInfo
->mDefaultRoutes
.Clear();
790 linkInfo
->mNeighbors
.Clear();
793 linkInfo
->mLink
= std::move(link
);
794 linkInfo
->UpdateStatus();
798 // This can happen during startup
799 LOG(("Link info doesn't exist [index=%u, name=%s]", linkIndex
,
802 LOG(("Removing link [index=%u, name=%s]", linkIndex
, linkName
.get()));
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
)) {
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
);
826 // This can happen during startup
827 LOG(("Cannot find link info [ifIdx=%u, addr=%s/%u", ifIdx
, addrStr
.get(),
828 address
->GetPrefixLen()));
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()));
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
);
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
));
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())) {
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())) {
880 nsAutoCString neighDbgStr
;
881 neigh
->GetAsString(neighDbgStr
);
882 LOG(("Removing neighbor %s", neighDbgStr
.get()));
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
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
)) {
910 if (!route
->IsUnicast() || !route
->ScopeIsUniverse()) {
911 // Use only unicast routes
913 nsAutoCString routeDbgStr
;
914 route
->GetAsString(routeDbgStr
);
915 LOG(("Not an unicast global route: %s", routeDbgStr
.get()));
920 // Adding/removing any unicast route might change network ID
921 TriggerNetworkIDCalculation();
923 if (!route
->IsDefault()) {
924 // Store only default routes
926 nsAutoCString routeDbgStr
;
927 route
->GetAsString(routeDbgStr
);
928 LOG(("Not a default route: %s", routeDbgStr
.get()));
933 if (!route
->HasOif()) {
935 nsAutoCString routeDbgStr
;
936 route
->GetAsString(routeDbgStr
);
937 LOG(("There is no output interface in route: %s", routeDbgStr
.get()));
942 if (!route
->GetGWAddrPtr()) {
943 // We won't use the route if there is no gateway, so don't store it
945 nsAutoCString routeDbgStr
;
946 route
->GetAsString(routeDbgStr
);
947 LOG(("There is no gateway in route: %s", routeDbgStr
.get()));
952 if (route
->Family() == AF_INET6
&&
953 net::utils::ipv6_scope((const unsigned char*)route
->GetGWAddrPtr()) !=
956 nsAutoCString routeDbgStr
;
957 route
->GetAsString(routeDbgStr
);
958 LOG(("Scope of GW isn't global: %s", routeDbgStr
.get()));
963 LinkInfo
* linkInfo
= nullptr;
964 mLinks
.Get(route
->Oif(), &linkInfo
);
966 // This can happen during startup
968 nsAutoCString routeDbgStr
;
969 route
->GetAsString(routeDbgStr
);
970 LOG(("Cannot find link info for route: %s", routeDbgStr
.get()));
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.
980 nsAutoCString routeDbgStr
;
981 route
->GetAsString(routeDbgStr
);
982 LOG(("Removing default route: %s", routeDbgStr
.get()));
984 linkInfo
->mDefaultRoutes
.RemoveElementAt(i
);
989 if (aNlh
->nlmsg_type
== RTM_NEWROUTE
) {
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
)) {
1008 LinkInfo
* linkInfo
= nullptr;
1009 mLinks
.Get(neigh
->GetIndex(), &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()));
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()));
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();
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
));
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
;
1076 route
= MakeUnique
<NetlinkRoute
>();
1077 if (!route
->Init(aNlh
)) {
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()));
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()));
1098 if (LOG_ENABLED()) {
1100 nsAutoCString routeDbgStr
;
1101 route
->GetAsString(routeDbgStr
);
1102 LOG(("Storing route: %s", routeDbgStr
.get()));
1104 LOG(("Clearing result for the check"));
1108 if (mOutgoingMessages
[0]->Family() == AF_INET
) {
1109 mIPv4RouteCheckResult
= std::move(route
);
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
;
1151 listener
->OnLinkStatusKnown();
1154 // We've received last response for route check, calculate ID now
1155 MOZ_ASSERT(isRtMessage
);
1156 CalculateNetworkID();
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) {
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
;
1186 fds
[1].fd
= netlinkSocket
;
1187 fds
[1].events
= POLLIN
;
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;
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
1209 int rc
= eintr_retry([&]() {
1210 AUTO_PROFILER_THREAD_SLEEP
;
1211 return poll(fds
, 2, GetPollWait());
1215 if (fds
[0].revents
& POLLIN
) {
1216 // shutdown, abort the loop!
1217 LOG(("thread shutdown received, dying...\n"));
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
;
1229 EINTR_RETRY(close(netlinkSocket
));
1234 nsresult
NetlinkService::Init(NetlinkServiceListener
* aListener
) {
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
);
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
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
1291 void NetlinkService::TriggerNetworkIDCalculation() {
1292 LOG(("NetlinkService::TriggerNetworkIDCalculation"));
1294 if (mRecalculateNetworkId
) {
1298 mRecalculateNetworkId
= true;
1299 mTriggerTime
= TimeStamp::Now();
1302 int NetlinkService::GetPollWait() {
1303 if (!mRecalculateNetworkId
) {
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
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
1328 return static_cast<int>(kNetworkChangeCoalescingPeriod
- period
);
1331 class NeighborComparator
{
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
{
1343 bool LessThan(const nsCString
& aA
, const nsCString
& aB
) const {
1346 bool Equals(const nsCString
& aA
, const nsCString
& aB
) const {
1351 // Get Gateway Neighbours for a particular Address Family, for which we know MAC
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()));
1366 if (!linkInfo
->mLink
->IsTypeEther()) {
1367 LOG((" %s is not ethernet link", linkName
.get()));
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"));
1386 MOZ_ASSERT(linkInfo
->mDefaultRoutes
[i
]->GetGWAddrPtr(),
1387 "Stored routes must have gateway!");
1389 nsAutoCString neighKey
;
1390 GetAddrStr(linkInfo
->mDefaultRoutes
[i
]->GetGWAddrPtr(), aFamily
,
1393 NetlinkNeighbor
* neigh
= nullptr;
1394 if (!linkInfo
->mNeighbors
.Get(neighKey
, &neigh
)) {
1395 LOG(("Neighbor %s not found in hashtable.", neighKey
.get()));
1399 if (!neigh
->HasMAC()) {
1400 // We don't know MAC address
1401 LOG(("We have no MAC for neighbor %s.", neighKey
.get()));
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.",
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
,
1424 LOG(("NetlinkService::CalculateIDForEthernetLink"));
1425 bool retval
= false;
1426 const in_common_addr
* addrPtr
= aRouteCheckResult
->GetGWAddrPtr();
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()));
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(),
1449 NetlinkNeighbor
* neigh
= nullptr;
1450 if (!aRouteCheckLinkInfo
->mNeighbors
.Get(neighKey
, &neigh
)) {
1451 LOG(("Neighbor %s not found in hashtable.", neighKey
.get()));
1455 if (!neigh
->HasMAC()) {
1456 LOG(("We have no MAC for neighbor %s.", neighKey
.get()));
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
);
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
);
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
);
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
);
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();
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
) {
1517 // there is no preferred src, match just the family
1518 if (aRouteCheckLinkInfo
->mAddresses
[i
]->Family() != aFamily
) {
1521 } else if (!aRouteCheckResult
->PrefSrcAddrEquals(
1522 *aRouteCheckLinkInfo
->mAddresses
[i
])) {
1527 linkAddress
->GetPrefixLen() >
1528 aRouteCheckLinkInfo
->mAddresses
[i
]->GetPrefixLen()) {
1529 // We have no address yet or this one has smaller prefix length,
1531 linkAddress
= aRouteCheckLinkInfo
->mAddresses
[i
].get();
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",
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
,
1558 for (int32_t i
= 0; i
< prefixSize
; i
++) {
1559 uint8_t mask
= (bits
>= 8) ? 0xff : maskit
[bits
];
1560 ((unsigned char*)&prefix
)[i
] &= mask
;
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
));
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;
1587 // Skip ID calculation if the link is down, we have no ID...
1588 LOG(("Link is down, skipping ID calculation."));
1592 NetlinkRoute
* routeCheckResult
;
1593 if (aFamily
== AF_INET
) {
1594 routeCheckResult
= mIPv4RouteCheckResult
.get();
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
);
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
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
);
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());
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."));
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
));
1673 if (routeCheckLinkInfo
->mLink
->IsTypeEther()) {
1674 // The traffic is routed through an ethernet device.
1675 retval
|= CalculateIDForEthernetLink(
1676 aFamily
, routeCheckResult
, routeCheckIfIdx
, routeCheckLinkInfo
, aSHA1
);
1678 // The traffic is routed through a non-ethernet device.
1679 retval
|= CalculateIDForNonEthernetLink(aFamily
, routeCheckResult
,
1680 linkNamesToHash
, routeCheckIfIdx
,
1681 routeCheckLinkInfo
, aSHA1
);
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)
1693 struct __res_state res
{};
1694 int ret
= res_ninit(&res
);
1696 LOG(("Call to res_ninit failed: %d", ret
));
1701 for (int i
= 0; i
< MAXDNSRCH
; i
++) {
1702 if (!res
.dnsrch
[i
]) {
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
];
1723 LOG(("Bad ext struct"));
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"));
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
;
1747 MOZ_ASSERT_UNREACHABLE("Unexpected sa_family");
1751 resolvers
.AppendElement(ip
);
1758 RefPtr
<NetlinkServiceListener
> listener
;
1760 MutexAutoLock
lock(mMutex
);
1761 listener
= mListener
;
1762 mDNSSuffixList
= std::move(suffixList
);
1763 mDNSResolvers
= std::move(resolvers
);
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
));
1783 LOG(("Link status has changed [linkUp=%d]", newLinkUp
));
1784 RefPtr
<NetlinkServiceListener
> listener
;
1786 MutexAutoLock
lock(mMutex
);
1787 listener
= mListener
;
1788 mLinkUp
= newLinkUp
;
1792 listener
->OnLinkUp();
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;
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
) {
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
1838 Telemetry::Accumulate(Telemetry::NETWORK_ID2
, 4); // Both!
1840 mNetworkId
= output
;
1844 LOG(("Same network id"));
1845 Telemetry::Accumulate(Telemetry::NETWORK_ID2
, 2);
1849 LOG(("No network id"));
1850 MutexAutoLock
lock(mMutex
);
1851 if (!mNetworkId
.IsEmpty()) {
1852 mNetworkId
.Truncate();
1854 Telemetry::Accumulate(Telemetry::NETWORK_ID2
, 0);
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();
1898 return NS_ERROR_NOT_IMPLEMENTED
;
1902 nsresult
NetlinkService::GetResolvers(nsTArray
<NetAddr
>& aResolvers
) {
1903 #if defined(HAVE_RES_NINIT)
1904 MutexAutoLock
lock(mMutex
);
1905 aResolvers
= mDNSResolvers
.Clone();
1908 return NS_ERROR_NOT_IMPLEMENTED
;
1912 void NetlinkService::GetIsLinkUp(bool* aIsUp
) {
1913 MutexAutoLock
lock(mMutex
);
1917 } // namespace mozilla::net