1From bfa1d68bed863e22c40a6d9a19ffbcc8694bbff6 Mon Sep 17 00:00:00 2001
2From: Nate Karstens <nate.karstens@garmin.com>
3Date: Mon, 24 Jul 2017 09:38:55 -0500
4Subject: [PATCH 6/8] Handle noisy netlink sockets
5
6The POSIX implementation currently clears all network interfaces
7when netlink indicates that there has been a change. This causes
8the following problems:
9
10  1) Applications are informed that all of the services they are
11     tracking have been removed.
12  2) Increases network load because the client must re-query for
13     all records it is interested in.
14
15This changes netlink notification handling by:
16
17  1) Always comparing with the latest interface list returned
18     by the OS.
19  2) Confirming that the interface has been changed in a way
20     that we care about.
21
22Upstream-Status: Submitted [dts@apple.com]
23
24Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
25Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
26---
27 mDNSPosix/mDNSPosix.c | 182 +++++++++++++++++++++++++++++++++++++++---
28 1 file changed, 172 insertions(+), 10 deletions(-)
29
30Index: mDNSResponder/mDNSPosix/mDNSPosix.c
31===================================================================
32--- mDNSResponder.orig/mDNSPosix/mDNSPosix.c
33+++ mDNSResponder/mDNSPosix/mDNSPosix.c
34@@ -1788,14 +1788,43 @@ mDNSlocal void          ProcessRoutingNo
35
36 #endif // USES_NETLINK
37
38+// Test whether the given PosixNetworkInterface matches the given struct ifaddrs
39+mDNSlocal mDNSBool InterfacesMatch(PosixNetworkInterface *intf, struct ifaddrs *ifi)
40+{
41+    mDNSBool match = mDNSfalse;
42+    mDNSAddr ip, mask;
43+    unsigned int if_index;
44+
45+    if_index = if_nametoindex(ifi->ifa_name);
46+    if (if_index == 0)
47+        return mDNSfalse;
48+
49+    if((intf->index == if_index) &&
50+       (intf->sa_family == ifi->ifa_addr->sa_family) &&
51+       (strcmp(intf->coreIntf.ifname, ifi->ifa_name) == 0))
52+        {
53+        SockAddrTomDNSAddr(ifi->ifa_addr,    &ip,   NULL);
54+        SockAddrTomDNSAddr(ifi->ifa_netmask, &mask, NULL);
55+
56+        match = mDNSSameAddress(&intf->coreIntf.ip, &ip) &&
57+                mDNSSameAddress(&intf->coreIntf.mask, &mask);
58+        }
59+
60+    return match;
61+}
62+
63 // Called when data appears on interface change notification socket
64 mDNSlocal void InterfaceChangeCallback(int fd, void *context)
65 {
66     IfChangeRec     *pChgRec = (IfChangeRec*) context;
67+    mDNS            *m = pChgRec->mDNS;
68     fd_set readFDs;
69     GenLinkedList changedInterfaces;
70     NetworkInterfaceIndex *changedInterface;
71     struct timeval zeroTimeout = { 0, 0 };
72+    struct ifaddrs *ifa_list, **ifi, *ifa_loop4 = NULL;
73+    PosixNetworkInterface *intf, *intfNext;
74+    mDNSBool found, foundav4;
75
76     (void)fd; // Unused
77
78@@ -1810,12 +1839,149 @@ mDNSlocal void InterfaceChangeCallback(i
79     }
80     while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout));
81
82-    // Currently we rebuild the entire interface list whenever any interface change is
83-    // detected. If this ever proves to be a performance issue in a multi-homed
84-    // configuration, more care should be paid to changedInterfaces.
85-    if (changedInterfaces.Head != NULL)
86-        mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS);
87+    CleanRecentInterfaces();
88+
89+    if (changedInterfaces.Head == NULL) goto cleanup;
90+
91+    if (getifaddrs(&ifa_list) < 0) goto cleanup;
92+
93+    for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = intfNext)
94+    {
95+        intfNext = (PosixNetworkInterface*)(intf->coreIntf.next);
96+
97+        // Loopback interface(s) are handled later
98+        if (intf->coreIntf.Loopback) continue;
99+
100+        found = mDNSfalse;
101+        for (ifi = &ifa_list; *ifi != NULL; ifi = &(*ifi)->ifa_next)
102+        {
103+            if (InterfacesMatch(intf, *ifi))
104+            {
105+                found = mDNStrue;
106+                break;
107+            }
108+        }
109+
110+        // Removes changed and old interfaces from m->HostInterfaces
111+        if (!found) TearDownInterface(m, intf);
112+    }
113+
114+    // Add new and changed interfaces in ifa_list
115+    // Save off loopback interface in case it is needed later
116+    for (ifi = &ifa_list; *ifi != NULL; ifi = &(*ifi)->ifa_next)
117+    {
118+        found = mDNSfalse;
119+        for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = intfNext)
120+        {
121+            intfNext = (PosixNetworkInterface*)(intf->coreIntf.next);
122+
123+            // Loopback interface(s) are handled later
124+            if (intf->coreIntf.Loopback) continue;
125+
126+            if (InterfacesMatch(intf, *ifi))
127+            {
128+                found = mDNStrue;
129+                break;
130+            }
131+
132+            // Removes changed and old interfaces from m->HostInterfaces
133+        }
134+        if (found)
135+	    continue;
136+
137+        if ((ifa_loop4 == NULL) &&
138+            ((*ifi)->ifa_addr->sa_family == AF_INET) &&
139+            ((*ifi)->ifa_flags & IFF_UP) &&
140+            ((*ifi)->ifa_flags & IFF_LOOPBACK))
141+        {
142+            ifa_loop4 = *ifi;
143+            continue;
144+        }
145+
146+        if (     (((*ifi)->ifa_addr->sa_family == AF_INET)
147+#if HAVE_IPV6
148+                  || ((*ifi)->ifa_addr->sa_family == AF_INET6)
149+#endif
150+                  ) && ((*ifi)->ifa_flags & IFF_UP)
151+                    && !((*ifi)->ifa_flags & IFF_POINTOPOINT)
152+                    && !((*ifi)->ifa_flags & IFF_LOOPBACK))
153+        {
154+            struct ifaddrs *i = *ifi;
155+
156+#define ethernet_addr_len 6
157+            uint8_t hwaddr[ethernet_addr_len];
158+            int hwaddr_len = 0;
159+
160+#if defined(TARGET_OS_LINUX) && TARGET_OS_LINUX
161+            struct ifreq ifr;
162+            int sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
163+            if (sockfd >= 0)
164+            {
165+                /* Add hardware address */
166+                memcpy(ifr.ifr_name, i->ifa_name, IFNAMSIZ);
167+                if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) != -1)
168+                {
169+                    if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER)
170+                    {
171+                        memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ethernet_addr_len);
172+                        hwaddr_len = ethernet_addr_len;
173+                    }
174+                }
175+                close(sockfd);
176+            }
177+            else
178+            {
179+                memset(hwaddr, 0, sizeof(hwaddr));
180+            }
181+#endif // TARGET_OS_LINUX
182+            SetupOneInterface(m, i->ifa_addr, i->ifa_netmask,
183+                              hwaddr, hwaddr_len, i->ifa_name, if_nametoindex(i->ifa_name), i->ifa_flags);
184+        }
185+    }
186+
187+    // Determine if there is at least one non-loopback IPv4 interface. This is to work around issues
188+    // with multicast loopback on IPv6 interfaces -- see corresponding logic in SetupInterfaceList().
189+    foundav4 = mDNSfalse;
190+    for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = (PosixNetworkInterface*)(intf->coreIntf.next))
191+    {
192+        if (intf->sa_family == AF_INET && !intf->coreIntf.Loopback)
193+        {
194+            foundav4 = mDNStrue;
195+            break;
196+        }
197+    }
198+
199+    if (foundav4)
200+    {
201+        for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = intfNext)
202+        {
203+            intfNext = (PosixNetworkInterface*)(intf->coreIntf.next);
204+            if (intf->coreIntf.Loopback) TearDownInterface(m, intf);
205+        }
206+    }
207+    else
208+    {
209+        found = mDNSfalse;
210+
211+        for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = (PosixNetworkInterface*)(intf->coreIntf.next))
212+        {
213+            if (intf->coreIntf.Loopback)
214+            {
215+                found = mDNStrue;
216+                break;
217+            }
218+        }
219+
220+        if (!found && (ifa_loop4 != NULL))
221+        {
222+            SetupOneInterface(m, ifa_loop4->ifa_addr, ifa_loop4->ifa_netmask,
223+                              NULL, 0, ifa_loop4->ifa_name, if_nametoindex(ifa_loop4->ifa_name), ifa_loop4->ifa_flags);
224+        }
225+    }
226+
227+    if (ifa_list != NULL) freeifaddrs(ifa_list);
228
229+cleanup:
230     while ((changedInterface = (NetworkInterfaceIndex*)changedInterfaces.Head) != NULL)
231     {
232         RemoveFromList(&changedInterfaces, changedInterface);
233@@ -1947,15 +2113,11 @@ mDNSexport void mDNSPlatformClose(mDNS *
234 #endif
235 }
236
237-// This is used internally by InterfaceChangeCallback.
238-// It's also exported so that the Standalone Responder (mDNSResponderPosix)
239+// This is exported so that the Standalone Responder (mDNSResponderPosix)
240 // can call it in response to a SIGHUP (mainly for debugging purposes).
241 mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m)
242 {
243     int err;
244-    // This is a pretty heavyweight way to process interface changes --
245-    // destroying the entire interface list and then making fresh one from scratch.
246-    // We should make it like the OS X version, which leaves unchanged interfaces alone.
247     ClearInterfaceList(m);
248     err = SetupInterfaceList(m);
249     return PosixErrorToStatus(err);
250