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