blob: f2b171e55b82210b20480682648cf302a2246e50 [file] [log] [blame]
Andrew Geissler82c905d2020-04-13 13:39:40 -05001From 92025cab86619f548bf3eb816a1804ef40507ca7 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 05/11] 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>
25---
26 mDNSPosix/mDNSPosix.c | 143 +++++++++++++++++++++++++++++++++++++++---
27 1 file changed, 133 insertions(+), 10 deletions(-)
28
29diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
30index 699855a..59a8b8c 100644
31--- a/mDNSPosix/mDNSPosix.c
32+++ b/mDNSPosix/mDNSPosix.c
33@@ -1247,14 +1247,38 @@ mDNSlocal void ProcessRoutingNotification(int sd, GenLinkedList *change
34
35 #endif // USES_NETLINK
36
37+// Test whether the given PosixNetworkInterface matches the given struct ifi_info
38+mDNSlocal mDNSBool InterfacesMatch(PosixNetworkInterface *intf, struct ifi_info *ifi)
39+{
40+ mDNSBool match = mDNSfalse;
41+ mDNSAddr ip, mask;
42+
43+ if((intf->index == ifi->ifi_index) &&
44+ (intf->sa_family == ifi->ifi_addr->sa_family) &&
45+ (strcmp(intf->coreIntf.ifname, ifi->ifi_name) == 0))
46+ {
47+ SockAddrTomDNSAddr(ifi->ifi_addr, &ip, NULL);
48+ SockAddrTomDNSAddr(ifi->ifi_netmask, &mask, NULL);
49+
50+ match = mDNSSameAddress(&intf->coreIntf.ip, &ip) &&
51+ mDNSSameAddress(&intf->coreIntf.mask, &mask);
52+ }
53+
54+ return match;
55+}
56+
57 // Called when data appears on interface change notification socket
58 mDNSlocal void InterfaceChangeCallback(int fd, short filter, void *context)
59 {
60 IfChangeRec *pChgRec = (IfChangeRec*) context;
61+ mDNS *m = pChgRec->mDNS;
62 fd_set readFDs;
63 GenLinkedList changedInterfaces;
64 NetworkInterfaceIndex *changedInterface;
65 struct timeval zeroTimeout = { 0, 0 };
66+ struct ifi_info *ifi_list, **ifi, *ifi_free, *ifi_loop4 = NULL;
67+ PosixNetworkInterface *intf, *intfNext;
68+ mDNSBool found, foundav4;
69
70 (void)fd; // Unused
71 (void)filter; // Unused
72@@ -1270,12 +1294,115 @@ mDNSlocal void InterfaceChangeCallback(int fd, short filter, void *context)
73 }
74 while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout));
75
76- // Currently we rebuild the entire interface list whenever any interface change is
77- // detected. If this ever proves to be a performance issue in a multi-homed
78- // configuration, more care should be paid to changedInterfaces.
79- if (changedInterfaces.Head != NULL)
80- mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS);
81+ CleanRecentInterfaces();
82+
83+ if (changedInterfaces.Head == NULL) goto cleanup;
84+
85+ ifi_list = get_ifi_info(AF_INET, mDNStrue);
86+ if (ifi_list == NULL) goto cleanup;
87+
88+#if HAVE_IPV6
89+ /* Link the IPv6 list to the end of the IPv4 list */
90+ ifi = &ifi_list;
91+ while (*ifi != NULL) ifi = &(*ifi)->ifi_next;
92+ *ifi = get_ifi_info(AF_INET6, mDNStrue);
93+#endif
94+
95+ for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = intfNext)
96+ {
97+ intfNext = (PosixNetworkInterface*)(intf->coreIntf.next);
98+
99+ // Loopback interface(s) are handled later
100+ if (intf->coreIntf.Loopback) continue;
101+
102+ found = mDNSfalse;
103+ for (ifi = &ifi_list; *ifi != NULL; ifi = &(*ifi)->ifi_next)
104+ {
105+ if (InterfacesMatch(intf, *ifi))
106+ {
107+ found = mDNStrue;
108+
109+ // Removes unchanged from ifi_list
110+ ifi_free = *ifi;
111+ *ifi = (*ifi)->ifi_next;
112+ ifi_free->ifi_next = NULL;
113+ free_ifi_info(ifi_free);
114+
115+ break;
116+ }
117+ }
118+
119+ // Removes changed and old interfaces from m->HostInterfaces
120+ if (!found) TearDownInterface(m, intf);
121+ }
122+
123+ // Add new and changed interfaces in ifi_list
124+ // Save off loopback interface in case it is needed later
125+ for (ifi = &ifi_list; *ifi != NULL; ifi = &(*ifi)->ifi_next)
126+ {
127+ if ((ifi_loop4 == NULL) &&
128+ ((*ifi)->ifi_addr->sa_family == AF_INET) &&
129+ ((*ifi)->ifi_flags & IFF_UP) &&
130+ ((*ifi)->ifi_flags & IFF_LOOPBACK))
131+ {
132+ ifi_loop4 = *ifi;
133+ continue;
134+ }
135+
136+ if ( (((*ifi)->ifi_addr->sa_family == AF_INET)
137+#if HAVE_IPV6
138+ || ((*ifi)->ifi_addr->sa_family == AF_INET6)
139+#endif
140+ ) && ((*ifi)->ifi_flags & IFF_UP)
141+ && !((*ifi)->ifi_flags & IFF_POINTOPOINT)
142+ && !((*ifi)->ifi_flags & IFF_LOOPBACK))
143+ {
144+ SetupOneInterface(m, *ifi);
145+ }
146+ }
147+
148+ // Determine if there is at least one non-loopback IPv4 interface. This is to work around issues
149+ // with multicast loopback on IPv6 interfaces -- see corresponding logic in SetupInterfaceList().
150+ foundav4 = mDNSfalse;
151+ for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = (PosixNetworkInterface*)(intf->coreIntf.next))
152+ {
153+ if (intf->sa_family == AF_INET && !intf->coreIntf.Loopback)
154+ {
155+ foundav4 = mDNStrue;
156+ break;
157+ }
158+ }
159+
160+ if (foundav4)
161+ {
162+ for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = intfNext)
163+ {
164+ intfNext = (PosixNetworkInterface*)(intf->coreIntf.next);
165+ if (intf->coreIntf.Loopback) TearDownInterface(m, intf);
166+ }
167+ }
168+ else
169+ {
170+ found = mDNSfalse;
171+
172+ for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = (PosixNetworkInterface*)(intf->coreIntf.next))
173+ {
174+ if (intf->coreIntf.Loopback)
175+ {
176+ found = mDNStrue;
177+ break;
178+ }
179+ }
180+
181+ if (!found && (ifi_loop4 != NULL))
182+ {
183+ SetupOneInterface(m, ifi_loop4);
184+ }
185+ }
186+
187+ if (ifi_list != NULL) free_ifi_info(ifi_list);
188
189+cleanup:
190 while ((changedInterface = (NetworkInterfaceIndex*)changedInterfaces.Head) != NULL)
191 {
192 RemoveFromList(&changedInterfaces, changedInterface);
193@@ -1400,15 +1527,11 @@ mDNSexport void mDNSPlatformClose(mDNS *const m)
194 #endif
195 }
196
197-// This is used internally by InterfaceChangeCallback.
198-// It's also exported so that the Standalone Responder (mDNSResponderPosix)
199+// This is exported so that the Standalone Responder (mDNSResponderPosix)
200 // can call it in response to a SIGHUP (mainly for debugging purposes).
201 mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m)
202 {
203 int err;
204- // This is a pretty heavyweight way to process interface changes --
205- // destroying the entire interface list and then making fresh one from scratch.
206- // We should make it like the OS X version, which leaves unchanged interfaces alone.
207 ClearInterfaceList(m);
208 err = SetupInterfaceList(m);
209 return PosixErrorToStatus(err);
210--
2112.17.1
212