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