xref: /third_party/libuv/src/unix/bsd-ifaddrs.c (revision e66f31c5)
1/* Copyright libuv project contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22#include "uv.h"
23#include "internal.h"
24
25#include <errno.h>
26#include <stddef.h>
27
28#include <ifaddrs.h>
29#include <net/if.h>
30#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__)
31#include <net/if_dl.h>
32#endif
33
34#if defined(__HAIKU__)
35#define IFF_RUNNING IFF_LINK
36#endif
37
38static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
39  if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
40    return 1;
41  if (ent->ifa_addr == NULL)
42    return 1;
43#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__)
44  /*
45   * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, return whether `sa_family`
46   * equals `AF_LINK`. Otherwise, the result depends on the operating
47   * system with `AF_LINK` or `PF_INET`.
48   */
49  if (exclude_type == UV__EXCLUDE_IFPHYS)
50    return (ent->ifa_addr->sa_family != AF_LINK);
51#endif
52#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \
53    defined(__HAIKU__)
54  /*
55   * On BSD getifaddrs returns information related to the raw underlying
56   * devices. We're not interested in this information.
57   */
58  if (ent->ifa_addr->sa_family == AF_LINK)
59    return 1;
60#elif defined(__NetBSD__) || defined(__OpenBSD__)
61  if (ent->ifa_addr->sa_family != PF_INET &&
62      ent->ifa_addr->sa_family != PF_INET6)
63    return 1;
64#endif
65  return 0;
66}
67
68int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
69  struct ifaddrs* addrs;
70  struct ifaddrs* ent;
71  uv_interface_address_t* address;
72#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
73  int i;
74#endif
75
76  *count = 0;
77  *addresses = NULL;
78
79  if (getifaddrs(&addrs) != 0)
80    return UV__ERR(errno);
81
82  /* Count the number of interfaces */
83  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
84    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
85      continue;
86    (*count)++;
87  }
88
89  if (*count == 0) {
90    freeifaddrs(addrs);
91    return 0;
92  }
93
94  /* Make sure the memory is initiallized to zero using calloc() */
95  *addresses = uv__calloc(*count, sizeof(**addresses));
96
97  if (*addresses == NULL) {
98    freeifaddrs(addrs);
99    return UV_ENOMEM;
100  }
101
102  address = *addresses;
103
104  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
105    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
106      continue;
107
108    address->name = uv__strdup(ent->ifa_name);
109
110    if (ent->ifa_addr->sa_family == AF_INET6) {
111      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
112    } else {
113      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
114    }
115
116    if (ent->ifa_netmask == NULL) {
117      memset(&address->netmask, 0, sizeof(address->netmask));
118    } else if (ent->ifa_netmask->sa_family == AF_INET6) {
119      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
120    } else {
121      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
122    }
123
124    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
125
126    address++;
127  }
128
129#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
130  /* Fill in physical addresses for each interface */
131  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
132    if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
133      continue;
134
135    address = *addresses;
136
137    for (i = 0; i < *count; i++) {
138      if (strcmp(address->name, ent->ifa_name) == 0) {
139        struct sockaddr_dl* sa_addr;
140        sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
141        memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
142      }
143      address++;
144    }
145  }
146#endif
147
148  freeifaddrs(addrs);
149
150  return 0;
151}
152
153
154void uv_free_interface_addresses(uv_interface_address_t* addresses,
155                                 int count) {
156  int i;
157
158  for (i = 0; i < count; i++) {
159    uv__free(addresses[i].name);
160  }
161
162  uv__free(addresses);
163}
164