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