[ebc5ae5] | 1 | description: use getifaddrs() system call, rather than parsing /proc/net on linux |
---|
| 2 | author: Jiri Popelka <jpopelka@redhat.com> |
---|
| 3 | origin: http://pkgs.fedoraproject.org/cgit/dhcp.git/tree/dhcp-getifaddrs.patch?id=d12e0eb05e510268ce9b8dcb839e27d5eca9aff5 |
---|
| 4 | bug-debian: https://bugs.debian.org/605657 |
---|
| 5 | bug-ubuntu: https://bugs.launchpad.net/ubuntu/+source/isc-dhcp/+bug/1446767 |
---|
| 6 | bug-fedora: https://bugzilla.redhat.com/show_bug.cgi?id=449946 |
---|
| 7 | |
---|
| 8 | --- a/common/discover.c |
---|
| 9 | +++ b/common/discover.c |
---|
| 10 | @@ -373,392 +373,13 @@ end_iface_scan(struct iface_conf_list *i |
---|
| 11 | ifaces->sock = -1; |
---|
| 12 | } |
---|
| 13 | |
---|
| 14 | -#elif __linux /* !HAVE_SIOCGLIFCONF */ |
---|
| 15 | -/* |
---|
| 16 | - * Linux support |
---|
| 17 | - * ------------- |
---|
| 18 | - * |
---|
| 19 | - * In Linux, we use the /proc pseudo-filesystem to get information |
---|
| 20 | - * about interfaces, along with selected ioctl() calls. |
---|
| 21 | - * |
---|
| 22 | - * Linux low level access is documented in the netdevice man page. |
---|
| 23 | - */ |
---|
| 24 | - |
---|
| 25 | -/* |
---|
| 26 | - * Structure holding state about the scan. |
---|
| 27 | - */ |
---|
| 28 | -struct iface_conf_list { |
---|
| 29 | - int sock; /* file descriptor used to get information */ |
---|
| 30 | - FILE *fp; /* input from /proc/net/dev */ |
---|
| 31 | -#ifdef DHCPv6 |
---|
| 32 | - FILE *fp6; /* input from /proc/net/if_inet6 */ |
---|
| 33 | -#endif |
---|
| 34 | -}; |
---|
| 35 | - |
---|
| 36 | -/* |
---|
| 37 | - * Structure used to return information about a specific interface. |
---|
| 38 | - */ |
---|
| 39 | -struct iface_info { |
---|
| 40 | - char name[IFNAMSIZ]; /* name of the interface, e.g. "eth0" */ |
---|
| 41 | - struct sockaddr_storage addr; /* address information */ |
---|
| 42 | - isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */ |
---|
| 43 | -}; |
---|
| 44 | - |
---|
| 45 | -/* |
---|
| 46 | - * Start a scan of interfaces. |
---|
| 47 | - * |
---|
| 48 | - * The iface_conf_list structure maintains state for this process. |
---|
| 49 | - */ |
---|
| 50 | -int |
---|
| 51 | -begin_iface_scan(struct iface_conf_list *ifaces) { |
---|
| 52 | - char buf[IF_LINE_LENGTH]; |
---|
| 53 | - int len; |
---|
| 54 | - int i; |
---|
| 55 | - |
---|
| 56 | - ifaces->fp = fopen("/proc/net/dev", "r"); |
---|
| 57 | - if (ifaces->fp == NULL) { |
---|
| 58 | - log_error("Error opening '/proc/net/dev' to list interfaces"); |
---|
| 59 | - return 0; |
---|
| 60 | - } |
---|
| 61 | - |
---|
| 62 | - /* |
---|
| 63 | - * The first 2 lines are header information, so read and ignore them. |
---|
| 64 | - */ |
---|
| 65 | - for (i=0; i<2; i++) { |
---|
| 66 | - if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) { |
---|
| 67 | - log_error("Error reading headers from '/proc/net/dev'"); |
---|
| 68 | - fclose(ifaces->fp); |
---|
| 69 | - ifaces->fp = NULL; |
---|
| 70 | - return 0; |
---|
| 71 | - } |
---|
| 72 | - len = strlen(buf); |
---|
| 73 | - if ((len <= 0) || (buf[len-1] != '\n')) { |
---|
| 74 | - log_error("Bad header line in '/proc/net/dev'"); |
---|
| 75 | - fclose(ifaces->fp); |
---|
| 76 | - ifaces->fp = NULL; |
---|
| 77 | - return 0; |
---|
| 78 | - } |
---|
| 79 | - } |
---|
| 80 | - |
---|
| 81 | - ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
---|
| 82 | - if (ifaces->sock < 0) { |
---|
| 83 | - log_error("Error creating socket to list interfaces; %m"); |
---|
| 84 | - fclose(ifaces->fp); |
---|
| 85 | - ifaces->fp = NULL; |
---|
| 86 | - return 0; |
---|
| 87 | - } |
---|
| 88 | - |
---|
| 89 | -#ifdef DHCPv6 |
---|
| 90 | - if (local_family == AF_INET6) { |
---|
| 91 | - ifaces->fp6 = fopen("/proc/net/if_inet6", "r"); |
---|
| 92 | - if (ifaces->fp6 == NULL) { |
---|
| 93 | - log_error("Error opening '/proc/net/if_inet6' to " |
---|
| 94 | - "list IPv6 interfaces; %m"); |
---|
| 95 | - close(ifaces->sock); |
---|
| 96 | - ifaces->sock = -1; |
---|
| 97 | - fclose(ifaces->fp); |
---|
| 98 | - ifaces->fp = NULL; |
---|
| 99 | - return 0; |
---|
| 100 | - } |
---|
| 101 | - } |
---|
| 102 | -#endif |
---|
| 103 | - |
---|
| 104 | - return 1; |
---|
| 105 | -} |
---|
| 106 | - |
---|
| 107 | -/* |
---|
| 108 | - * Read our IPv4 interfaces from /proc/net/dev. |
---|
| 109 | - * |
---|
| 110 | - * The file looks something like this: |
---|
| 111 | - * |
---|
| 112 | - * Inter-| Receive ... |
---|
| 113 | - * face |bytes packets errs drop fifo frame ... |
---|
| 114 | - * lo: 1580562 4207 0 0 0 0 ... |
---|
| 115 | - * eth0: 0 0 0 0 0 0 ... |
---|
| 116 | - * eth1:1801552440 37895 0 14 0 ... |
---|
| 117 | - * |
---|
| 118 | - * We only care about the interface name, which is at the start of |
---|
| 119 | - * each line. |
---|
| 120 | - * |
---|
| 121 | - * We use an ioctl() to get the address and flags for each interface. |
---|
| 122 | - */ |
---|
| 123 | -static int |
---|
| 124 | -next_iface4(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { |
---|
| 125 | - char buf[IF_LINE_LENGTH]; |
---|
| 126 | - int len; |
---|
| 127 | - char *p; |
---|
| 128 | - char *name; |
---|
| 129 | - struct ifreq tmp; |
---|
| 130 | - |
---|
| 131 | - /* |
---|
| 132 | - * Loop exits when we find an interface that has an address, or |
---|
| 133 | - * when we run out of interfaces. |
---|
| 134 | - */ |
---|
| 135 | - for (;;) { |
---|
| 136 | - do { |
---|
| 137 | - /* |
---|
| 138 | - * Read the next line in the file. |
---|
| 139 | - */ |
---|
| 140 | - if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) { |
---|
| 141 | - if (ferror(ifaces->fp)) { |
---|
| 142 | - *err = 1; |
---|
| 143 | - log_error("Error reading interface " |
---|
| 144 | - "information"); |
---|
| 145 | - } else { |
---|
| 146 | - *err = 0; |
---|
| 147 | - } |
---|
| 148 | - return 0; |
---|
| 149 | - } |
---|
| 150 | - |
---|
| 151 | - /* |
---|
| 152 | - * Make sure the line is a nice, |
---|
| 153 | - * newline-terminated line. |
---|
| 154 | - */ |
---|
| 155 | - len = strlen(buf); |
---|
| 156 | - if ((len <= 0) || (buf[len-1] != '\n')) { |
---|
| 157 | - log_error("Bad line reading interface " |
---|
| 158 | - "information"); |
---|
| 159 | - *err = 1; |
---|
| 160 | - return 0; |
---|
| 161 | - } |
---|
| 162 | - |
---|
| 163 | - /* |
---|
| 164 | - * Figure out our name. |
---|
| 165 | - */ |
---|
| 166 | - p = strrchr(buf, ':'); |
---|
| 167 | - if (p == NULL) { |
---|
| 168 | - log_error("Bad line reading interface " |
---|
| 169 | - "information (no colon)"); |
---|
| 170 | - *err = 1; |
---|
| 171 | - return 0; |
---|
| 172 | - } |
---|
| 173 | - *p = '\0'; |
---|
| 174 | - name = buf; |
---|
| 175 | - while (isspace(*name)) { |
---|
| 176 | - name++; |
---|
| 177 | - } |
---|
| 178 | - |
---|
| 179 | - /* |
---|
| 180 | - * Copy our name into our interface structure. |
---|
| 181 | - */ |
---|
| 182 | - len = p - name; |
---|
| 183 | - if (len >= sizeof(info->name)) { |
---|
| 184 | - *err = 1; |
---|
| 185 | - log_error("Interface name '%s' too long", name); |
---|
| 186 | - return 0; |
---|
| 187 | - } |
---|
| 188 | - strncpy(info->name, name, sizeof(info->name) - 1); |
---|
| 189 | - |
---|
| 190 | -#ifdef ALIAS_NAMED_PERMUTED |
---|
| 191 | - /* interface aliases look like "eth0:1" or "wlan1:3" */ |
---|
| 192 | - s = strchr(info->name, ':'); |
---|
| 193 | - if (s != NULL) { |
---|
| 194 | - *s = '\0'; |
---|
| 195 | - } |
---|
| 196 | -#endif |
---|
| 197 | - |
---|
| 198 | -#ifdef SKIP_DUMMY_INTERFACES |
---|
| 199 | - } while (strncmp(info->name, "dummy", 5) == 0); |
---|
| 200 | -#else |
---|
| 201 | - } while (0); |
---|
| 202 | -#endif |
---|
| 203 | - |
---|
| 204 | - memset(&tmp, 0, sizeof(tmp)); |
---|
| 205 | - strncpy(tmp.ifr_name, name, sizeof(tmp.ifr_name) - 1); |
---|
| 206 | - if (ioctl(ifaces->sock, SIOCGIFADDR, &tmp) < 0) { |
---|
| 207 | - if (errno == EADDRNOTAVAIL) { |
---|
| 208 | - continue; |
---|
| 209 | - } |
---|
| 210 | - log_error("Error getting interface address " |
---|
| 211 | - "for '%s'; %m", name); |
---|
| 212 | - *err = 1; |
---|
| 213 | - return 0; |
---|
| 214 | - } |
---|
| 215 | - memcpy(&info->addr, &tmp.ifr_addr, sizeof(tmp.ifr_addr)); |
---|
| 216 | - |
---|
| 217 | - memset(&tmp, 0, sizeof(tmp)); |
---|
| 218 | - strncpy(tmp.ifr_name, name, sizeof(tmp.ifr_name) - 1); |
---|
| 219 | - if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) { |
---|
| 220 | - log_error("Error getting interface flags for '%s'; %m", |
---|
| 221 | - name); |
---|
| 222 | - *err = 1; |
---|
| 223 | - return 0; |
---|
| 224 | - } |
---|
| 225 | - info->flags = tmp.ifr_flags; |
---|
| 226 | - |
---|
| 227 | - *err = 0; |
---|
| 228 | - return 1; |
---|
| 229 | - } |
---|
| 230 | -} |
---|
| 231 | - |
---|
| 232 | -#ifdef DHCPv6 |
---|
| 233 | -/* |
---|
| 234 | - * Read our IPv6 interfaces from /proc/net/if_inet6. |
---|
| 235 | - * |
---|
| 236 | - * The file looks something like this: |
---|
| 237 | - * |
---|
| 238 | - * fe80000000000000025056fffec00008 05 40 20 80 vmnet8 |
---|
| 239 | - * 00000000000000000000000000000001 01 80 10 80 lo |
---|
| 240 | - * fe80000000000000025056fffec00001 06 40 20 80 vmnet1 |
---|
| 241 | - * 200108881936000202166ffffe497d9b 03 40 00 00 eth1 |
---|
| 242 | - * fe8000000000000002166ffffe497d9b 03 40 20 80 eth1 |
---|
| 243 | - * |
---|
| 244 | - * We get IPv6 address from the start, the interface name from the end, |
---|
| 245 | - * and ioctl() to get flags. |
---|
| 246 | - */ |
---|
| 247 | -static int |
---|
| 248 | -next_iface6(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { |
---|
| 249 | - char buf[IF_LINE_LENGTH]; |
---|
| 250 | - int len; |
---|
| 251 | - char *p; |
---|
| 252 | - char *name; |
---|
| 253 | - int i; |
---|
| 254 | - struct sockaddr_in6 addr; |
---|
| 255 | - struct ifreq tmp; |
---|
| 256 | - |
---|
| 257 | - do { |
---|
| 258 | - /* |
---|
| 259 | - * Read the next line in the file. |
---|
| 260 | - */ |
---|
| 261 | - if (fgets(buf, sizeof(buf), ifaces->fp6) == NULL) { |
---|
| 262 | - if (ferror(ifaces->fp6)) { |
---|
| 263 | - *err = 1; |
---|
| 264 | - log_error("Error reading IPv6 " |
---|
| 265 | - "interface information"); |
---|
| 266 | - } else { |
---|
| 267 | - *err = 0; |
---|
| 268 | - } |
---|
| 269 | - return 0; |
---|
| 270 | - } |
---|
| 271 | - |
---|
| 272 | - /* |
---|
| 273 | - * Make sure the line is a nice, newline-terminated line. |
---|
| 274 | - */ |
---|
| 275 | - len = strlen(buf); |
---|
| 276 | - if ((len <= 0) || (buf[len-1] != '\n')) { |
---|
| 277 | - log_error("Bad line reading IPv6 " |
---|
| 278 | - "interface information"); |
---|
| 279 | - *err = 1; |
---|
| 280 | - return 0; |
---|
| 281 | - } |
---|
| 282 | - |
---|
| 283 | - /* |
---|
| 284 | - * Figure out our name. |
---|
| 285 | - */ |
---|
| 286 | - buf[--len] = '\0'; |
---|
| 287 | - p = strrchr(buf, ' '); |
---|
| 288 | - if (p == NULL) { |
---|
| 289 | - log_error("Bad line reading IPv6 interface " |
---|
| 290 | - "information (no space)"); |
---|
| 291 | - *err = 1; |
---|
| 292 | - return 0; |
---|
| 293 | - } |
---|
| 294 | - name = p+1; |
---|
| 295 | - |
---|
| 296 | - /* |
---|
| 297 | - * Copy our name into our interface structure. |
---|
| 298 | - */ |
---|
| 299 | - len = strlen(name); |
---|
| 300 | - if (len >= sizeof(info->name)) { |
---|
| 301 | - *err = 1; |
---|
| 302 | - log_error("IPv6 interface name '%s' too long", name); |
---|
| 303 | - return 0; |
---|
| 304 | - } |
---|
| 305 | - strncpy(info->name, name, sizeof(info->name) - 1); |
---|
| 306 | - |
---|
| 307 | -#ifdef SKIP_DUMMY_INTERFACES |
---|
| 308 | - } while (strncmp(info->name, "dummy", 5) == 0); |
---|
| 309 | -#else |
---|
| 310 | - } while (0); |
---|
| 311 | -#endif |
---|
| 312 | - |
---|
| 313 | - /* |
---|
| 314 | - * Double-check we start with the IPv6 address. |
---|
| 315 | - */ |
---|
| 316 | - for (i=0; i<32; i++) { |
---|
| 317 | - if (!isxdigit(buf[i]) || isupper(buf[i])) { |
---|
| 318 | - *err = 1; |
---|
| 319 | - log_error("Bad line reading IPv6 interface address " |
---|
| 320 | - "for '%s'", name); |
---|
| 321 | - return 0; |
---|
| 322 | - } |
---|
| 323 | - } |
---|
| 324 | - |
---|
| 325 | - /* |
---|
| 326 | - * Load our socket structure. |
---|
| 327 | - */ |
---|
| 328 | - memset(&addr, 0, sizeof(addr)); |
---|
| 329 | - addr.sin6_family = AF_INET6; |
---|
| 330 | - for (i=0; i<16; i++) { |
---|
| 331 | - unsigned char byte; |
---|
| 332 | - static const char hex[] = "0123456789abcdef"; |
---|
| 333 | - byte = ((index(hex, buf[i * 2]) - hex) << 4) | |
---|
| 334 | - (index(hex, buf[i * 2 + 1]) - hex); |
---|
| 335 | - addr.sin6_addr.s6_addr[i] = byte; |
---|
| 336 | - } |
---|
| 337 | - memcpy(&info->addr, &addr, sizeof(addr)); |
---|
| 338 | - |
---|
| 339 | - /* |
---|
| 340 | - * Get our flags. |
---|
| 341 | - */ |
---|
| 342 | - memset(&tmp, 0, sizeof(tmp)); |
---|
| 343 | - strncpy(tmp.ifr_name, name, sizeof(tmp.ifr_name) - 1); |
---|
| 344 | - if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) { |
---|
| 345 | - log_error("Error getting interface flags for '%s'; %m", name); |
---|
| 346 | - *err = 1; |
---|
| 347 | - return 0; |
---|
| 348 | - } |
---|
| 349 | - info->flags = tmp.ifr_flags; |
---|
| 350 | - |
---|
| 351 | - *err = 0; |
---|
| 352 | - return 1; |
---|
| 353 | -} |
---|
| 354 | -#endif /* DHCPv6 */ |
---|
| 355 | - |
---|
| 356 | -/* |
---|
| 357 | - * Retrieve the next interface. |
---|
| 358 | - * |
---|
| 359 | - * Returns information in the info structure. |
---|
| 360 | - * Sets err to 1 if there is an error, otherwise 0. |
---|
| 361 | - */ |
---|
| 362 | -int |
---|
| 363 | -next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { |
---|
| 364 | - memset(info, 0, sizeof(struct iface_info)); |
---|
| 365 | - if (next_iface4(info, err, ifaces)) { |
---|
| 366 | - return 1; |
---|
| 367 | - } |
---|
| 368 | -#ifdef DHCPv6 |
---|
| 369 | - if (!(*err)) { |
---|
| 370 | - if (local_family == AF_INET6) |
---|
| 371 | - return next_iface6(info, err, ifaces); |
---|
| 372 | - } |
---|
| 373 | -#endif |
---|
| 374 | - return 0; |
---|
| 375 | -} |
---|
| 376 | - |
---|
| 377 | -/* |
---|
| 378 | - * End scan of interfaces. |
---|
| 379 | - */ |
---|
| 380 | -void |
---|
| 381 | -end_iface_scan(struct iface_conf_list *ifaces) { |
---|
| 382 | - fclose(ifaces->fp); |
---|
| 383 | - ifaces->fp = NULL; |
---|
| 384 | - close(ifaces->sock); |
---|
| 385 | - ifaces->sock = -1; |
---|
| 386 | -#ifdef DHCPv6 |
---|
| 387 | - if (local_family == AF_INET6) { |
---|
| 388 | - fclose(ifaces->fp6); |
---|
| 389 | - ifaces->fp6 = NULL; |
---|
| 390 | - } |
---|
| 391 | -#endif |
---|
| 392 | -} |
---|
| 393 | #else |
---|
| 394 | |
---|
| 395 | /* |
---|
| 396 | - * BSD support |
---|
| 397 | + * Unix support |
---|
| 398 | * ----------- |
---|
| 399 | * |
---|
| 400 | - * FreeBSD, NetBSD, OpenBSD, and OS X all have the getifaddrs() |
---|
| 401 | + * FreeBSD, NetBSD, OpenBSD, Linux, and OS X all have the getifaddrs() |
---|
| 402 | * function. |
---|
| 403 | * |
---|
| 404 | * The getifaddrs() man page describes the use. |
---|
| 405 | @@ -806,6 +427,8 @@ begin_iface_scan(struct iface_conf_list |
---|
| 406 | */ |
---|
| 407 | int |
---|
| 408 | next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { |
---|
| 409 | + size_t sa_len = 0; |
---|
| 410 | + |
---|
| 411 | if (ifaces->next == NULL) { |
---|
| 412 | *err = 0; |
---|
| 413 | return 0; |
---|
| 414 | @@ -818,8 +441,20 @@ next_iface(struct iface_info *info, int |
---|
| 415 | } |
---|
| 416 | memset(info, 0, sizeof(struct iface_info)); |
---|
| 417 | strncpy(info->name, ifaces->next->ifa_name, sizeof(info->name) - 1); |
---|
| 418 | - memcpy(&info->addr, ifaces->next->ifa_addr, |
---|
| 419 | - ifaces->next->ifa_addr->sa_len); |
---|
| 420 | + |
---|
| 421 | + memset(&info->addr, 0 , sizeof(info->addr)); |
---|
| 422 | + |
---|
| 423 | + if (ifaces->next->ifa_addr != NULL) { |
---|
| 424 | +#ifdef HAVE_SA_LEN |
---|
| 425 | + sa_len = ifaces->next->ifa_addr->sa_len; |
---|
| 426 | +#else |
---|
| 427 | + if (ifaces->next->ifa_addr->sa_family == AF_INET) |
---|
| 428 | + sa_len = sizeof(struct sockaddr_in); |
---|
| 429 | + else if (ifaces->next->ifa_addr->sa_family == AF_INET6) |
---|
| 430 | + sa_len = sizeof(struct sockaddr_in6); |
---|
| 431 | +#endif |
---|
| 432 | + memcpy(&info->addr, ifaces->next->ifa_addr, sa_len); |
---|
| 433 | + } |
---|
| 434 | info->flags = ifaces->next->ifa_flags; |
---|
| 435 | ifaces->next = ifaces->next->ifa_next; |
---|
| 436 | *err = 0; |
---|