1/* 2 Unix SMB/CIFS implementation. 3 return a list of network interfaces 4 Copyright (C) Andrew Tridgell 1998 5 Copyright (C) Jeremy Allison 2007 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21#include "includes.h" 22 23/**************************************************************************** 24 Create a struct sockaddr_storage with the netmask bits set to 1. 25****************************************************************************/ 26 27bool make_netmask(struct sockaddr_storage *pss_out, 28 const struct sockaddr_storage *pss_in, 29 unsigned long masklen) 30{ 31 *pss_out = *pss_in; 32 /* Now apply masklen bits of mask. */ 33#if defined(HAVE_IPV6) 34 if (pss_in->ss_family == AF_INET6) { 35 char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr; 36 unsigned int i; 37 38 if (masklen > 128) { 39 return false; 40 } 41 for (i = 0; masklen >= 8; masklen -= 8, i++) { 42 *p++ = 0xff; 43 } 44 /* Deal with the partial byte. */ 45 *p++ &= (0xff & ~(0xff>>masklen)); 46 i++; 47 for (;i < sizeof(struct in6_addr); i++) { 48 *p++ = '\0'; 49 } 50 return true; 51 } 52#endif 53 if (pss_in->ss_family == AF_INET) { 54 if (masklen > 32) { 55 return false; 56 } 57 ((struct sockaddr_in *)pss_out)->sin_addr.s_addr = 58 htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL)); 59 return true; 60 } 61 return false; 62} 63 64/**************************************************************************** 65 Create a struct sockaddr_storage set to the broadcast or network adress from 66 an incoming sockaddr_storage. 67****************************************************************************/ 68 69static void make_bcast_or_net(struct sockaddr_storage *pss_out, 70 const struct sockaddr_storage *pss_in, 71 const struct sockaddr_storage *nmask, 72 bool make_bcast_p) 73{ 74 unsigned int i = 0, len = 0; 75 char *pmask = NULL; 76 char *p = NULL; 77 *pss_out = *pss_in; 78 79 /* Set all zero netmask bits to 1. */ 80#if defined(HAVE_IPV6) 81 if (pss_in->ss_family == AF_INET6) { 82 p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr; 83 pmask = (char *)&((struct sockaddr_in6 *)nmask)->sin6_addr; 84 len = 16; 85 } 86#endif 87 if (pss_in->ss_family == AF_INET) { 88 p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr; 89 pmask = (char *)&((struct sockaddr_in *)nmask)->sin_addr; 90 len = 4; 91 } 92 93 for (i = 0; i < len; i++, p++, pmask++) { 94 if (make_bcast_p) { 95 *p = (*p & *pmask) | (*pmask ^ 0xff); 96 } else { 97 /* make_net */ 98 *p = (*p & *pmask); 99 } 100 } 101} 102 103void make_bcast(struct sockaddr_storage *pss_out, 104 const struct sockaddr_storage *pss_in, 105 const struct sockaddr_storage *nmask) 106{ 107 make_bcast_or_net(pss_out, pss_in, nmask, true); 108} 109 110void make_net(struct sockaddr_storage *pss_out, 111 const struct sockaddr_storage *pss_in, 112 const struct sockaddr_storage *nmask) 113{ 114 make_bcast_or_net(pss_out, pss_in, nmask, false); 115} 116 117/**************************************************************************** 118 Try the "standard" getifaddrs/freeifaddrs interfaces. 119 Also gets IPv6 interfaces. 120****************************************************************************/ 121 122/**************************************************************************** 123 Get the netmask address for a local interface. 124****************************************************************************/ 125 126static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces) 127{ 128 struct iface_struct *ifaces; 129 struct ifaddrs *iflist = NULL; 130 struct ifaddrs *ifptr = NULL; 131 int count; 132 int total = 0; 133 size_t copy_size; 134#ifndef HAVE_GETIFADDRS 135 if (getifaddrs_local(&iflist) < 0) { 136#else 137 if (getifaddrs(&iflist) < 0) { 138#endif 139 return -1; 140 } 141 142 count = 0; 143 for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) { 144 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) { 145 continue; 146 } 147 if (!(ifptr->ifa_flags & IFF_UP)) { 148 continue; 149 } 150 count += 1; 151 } 152 153 ifaces = talloc_array(mem_ctx, struct iface_struct, count); 154 if (ifaces == NULL) { 155 errno = ENOMEM; 156 return -1; 157 } 158 159 /* Loop through interfaces, looking for given IP address */ 160 for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) { 161 162 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) { 163 continue; 164 } 165 166 /* Check the interface is up. */ 167 if (!(ifptr->ifa_flags & IFF_UP)) { 168 continue; 169 } 170 171 memset(&ifaces[total], '\0', sizeof(ifaces[total])); 172 173 copy_size = sizeof(struct sockaddr_in); 174 175 ifaces[total].flags = ifptr->ifa_flags; 176 177#if defined(HAVE_IPV6) 178 if (ifptr->ifa_addr->sa_family == AF_INET6) { 179 copy_size = sizeof(struct sockaddr_in6); 180 } 181#endif 182 183 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size); 184 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size); 185 186 if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) { 187 make_bcast(&ifaces[total].bcast, 188 &ifaces[total].ip, 189 &ifaces[total].netmask); 190 } else if ((ifaces[total].flags & IFF_POINTOPOINT) && 191 ifptr->ifa_dstaddr ) { 192 memcpy(&ifaces[total].bcast, 193 ifptr->ifa_dstaddr, 194 copy_size); 195 } else { 196 continue; 197 } 198 199 strlcpy(ifaces[total].name, ifptr->ifa_name, 200 sizeof(ifaces[total].name)); 201 total++; 202 } 203#ifndef HAVE_GETIFADDRS 204 freeifaddrs_local(iflist); 205#else 206 freeifaddrs(iflist); 207#endif 208 209 *pifaces = ifaces; 210 return total; 211} 212 213static int iface_comp(struct iface_struct *i1, struct iface_struct *i2) 214{ 215 int r; 216 217#if defined(HAVE_IPV6) 218 /* 219 * If we have IPv6 - sort these interfaces lower 220 * than any IPv4 ones. 221 */ 222 if (i1->ip.ss_family == AF_INET6 && 223 i2->ip.ss_family == AF_INET) { 224 return -1; 225 } else if (i1->ip.ss_family == AF_INET && 226 i2->ip.ss_family == AF_INET6) { 227 return 1; 228 } 229 230 if (i1->ip.ss_family == AF_INET6) { 231 struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip; 232 struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip; 233 234 r = memcmp(&s1->sin6_addr, 235 &s2->sin6_addr, 236 sizeof(struct in6_addr)); 237 if (r) { 238 return r; 239 } 240 241 s1 = (struct sockaddr_in6 *)&i1->netmask; 242 s2 = (struct sockaddr_in6 *)&i2->netmask; 243 244 r = memcmp(&s1->sin6_addr, 245 &s2->sin6_addr, 246 sizeof(struct in6_addr)); 247 if (r) { 248 return r; 249 } 250 } 251#endif 252 253 /* AIX uses __ss_family instead of ss_family inside of 254 sockaddr_storage. Instead of trying to figure out which field to 255 use, we can just cast it to a sockaddr. 256 */ 257 258 if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) { 259 struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip; 260 struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip; 261 262 r = ntohl(s1->sin_addr.s_addr) - 263 ntohl(s2->sin_addr.s_addr); 264 if (r) { 265 return r; 266 } 267 268 s1 = (struct sockaddr_in *)&i1->netmask; 269 s2 = (struct sockaddr_in *)&i2->netmask; 270 271 return ntohl(s1->sin_addr.s_addr) - 272 ntohl(s2->sin_addr.s_addr); 273 } 274 return 0; 275} 276 277/* this wrapper is used to remove duplicates from the interface list generated 278 above */ 279int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces) 280{ 281 struct iface_struct *ifaces; 282 int total, i, j; 283 284 total = _get_interfaces(mem_ctx, &ifaces); 285 if (total <= 0) return total; 286 287 /* now we need to remove duplicates */ 288 qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp); 289 290 for (i=1;i<total;) { 291 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) { 292 for (j=i-1;j<total-1;j++) { 293 ifaces[j] = ifaces[j+1]; 294 } 295 total--; 296 } else { 297 i++; 298 } 299 } 300 301 *pifaces = ifaces; 302 return total; 303} 304 305