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