Lely core libraries  2.2.5
if.c
Go to the documentation of this file.
1 
24 #include "io.h"
25 #include <lely/io/if.h>
26 #include <lely/io/sock.h>
27 #include <lely/util/errnum.h>
28 
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #ifdef _WIN32
34 #ifdef _MSC_VER
35 #pragma comment(lib, "iphlpapi.lib")
36 #endif
37 // clang-format off
38 #include <wincrypt.h>
39 #include <iphlpapi.h>
40 // clang-format on
41 #elif defined(__linux__) && defined(HAVE_IFADDRS_H)
42 #include <ifaddrs.h>
43 #endif
44 
45 #if defined(_WIN32) || (defined(__linux__) && defined(HAVE_IFADDRS_H))
46 static void io_addr_set(io_addr_t *addr, const struct sockaddr *address);
47 #endif
48 
49 #ifdef _WIN32
50 static NETIO_STATUS WINAPI ConvertLengthToIpv6Mask(
51  ULONG MaskLength, u_char Mask[16]);
52 #endif
53 
54 #if defined(_WIN32) || (defined(__linux__) && defined(HAVE_IFADDRS_H))
55 
56 int
57 io_get_ifinfo(int maxinfo, struct io_ifinfo *info)
58 {
59  if (!info)
60  maxinfo = 0;
61 
62 #ifdef _WIN32
63  ULONG Flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
64  | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX
65  | GAA_FLAG_SKIP_FRIENDLY_NAME
66  | GAA_FLAG_INCLUDE_ALL_INTERFACES;
67  DWORD Size = 0;
68  DWORD dwErrCode = GetAdaptersAddresses(
69  AF_UNSPEC, Flags, NULL, NULL, &Size);
70  if (dwErrCode != ERROR_BUFFER_OVERFLOW) {
71  SetLastError(dwErrCode);
72  return -1;
73  }
74 
75  PIP_ADAPTER_ADDRESSES pAdapterAddresses = malloc(Size);
76  if (!pAdapterAddresses)
77  return -1;
78 
79  dwErrCode = GetAdaptersAddresses(
80  AF_UNSPEC, Flags, NULL, pAdapterAddresses, &Size);
81  if (dwErrCode != ERROR_SUCCESS) {
82  free(pAdapterAddresses);
83  SetLastError(dwErrCode);
84  return -1;
85  }
86 
87  int ninfo = 0;
88  for (PIP_ADAPTER_ADDRESSES paa = pAdapterAddresses; paa;
89  paa = paa->Next) {
90  // Skip interfaces with invalid indices.
91  unsigned int index =
92  paa->IfIndex ? paa->IfIndex : paa->Ipv6IfIndex;
93  if (!index)
94  continue;
95 
96  // Copy the status.
97  int flags = 0;
98  if (paa->OperStatus == IfOperStatusUp)
99  flags |= IO_IF_UP;
100  if (paa->IfType == IF_TYPE_PPP)
102  else if (paa->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
104  else
106  if (!(paa->Flags & IP_ADAPTER_NO_MULTICAST))
108 
109  // Every unicast address represents a network interface.
110  for (PIP_ADAPTER_UNICAST_ADDRESS paua =
111  paa->FirstUnicastAddress;
112  paua; paua = paua->Next) {
113  LPSOCKADDR lpSockaddr = paua->Address.lpSockaddr;
114  if (!lpSockaddr)
115  continue;
116 
117  // We only support IPv4 and IPv6.
118  int domain;
119  switch (lpSockaddr->sa_family) {
120  case AF_INET: domain = IO_SOCK_IPV4; break;
121  case AF_INET6: domain = IO_SOCK_IPV6; break;
122  default: continue;
123  }
124 
125  if (++ninfo > maxinfo)
126  continue;
127 
128  memset(info, 0, sizeof(*info));
129  info->addr.addrlen = 0;
130  info->netmask.addrlen = 0;
131  info->broadaddr.addrlen = 0;
132 
133  // Copy the index and obtain the interface name.
134  info->index = index;
135  if_indextoname(info->index, info->name);
136 
137  info->domain = domain;
138 
139  info->flags = flags;
140 
141  // Copy the interface address.
142  io_addr_set(&info->addr, lpSockaddr);
143 
144  if (domain == IO_SOCK_IPV4) {
145  // Construct the netmask from the prefix length.
146  ULONG Mask;
147  ConvertLengthToIpv4Mask(
148  paua->OnLinkPrefixLength,
149  &Mask);
151  (uint8_t *)&Mask, 0);
152 
153  if (info->flags & IO_IF_BROADCAST) {
154  // Obtain the broadcast address from the
155  // interface address and the netmask.
156  ULONG BCast = ((struct sockaddr_in *)lpSockaddr)
157  ->sin_addr
158  .s_addr
159  | ~Mask;
161  (uint8_t *)&BCast, 0);
162  }
163  } else if (domain == IO_SOCK_IPV6) {
164  // Construct the netmask from the prefix length.
165  u_char Mask[16];
166  ConvertLengthToIpv6Mask(
167  paua->OnLinkPrefixLength, Mask);
168  io_addr_set_ipv6_n(&info->netmask, Mask, 0);
169 
170  // IPv6 does not support broadcast.
171  info->flags &= ~IO_IF_BROADCAST;
172  }
173 
174  info++;
175  }
176  }
177 
178  free(pAdapterAddresses);
179 #else
180  struct ifaddrs *res = NULL;
181  if (getifaddrs(&res) == -1)
182  return -1;
183 
184  int ninfo = 0;
185  for (struct ifaddrs *ifa = res; ifa; ifa = ifa->ifa_next) {
186  // Obtain the domain from the interface address.
187  int domain = 0;
188  if (ifa->ifa_addr) {
189  switch (ifa->ifa_addr->sa_family) {
190  case AF_INET: domain = IO_SOCK_IPV4; break;
191  case AF_INET6: domain = IO_SOCK_IPV4; break;
192  }
193  }
194  // Skip network interfaces with unknown domains.
195  if (!domain)
196  continue;
197 
198  if (++ninfo > maxinfo)
199  continue;
200 
201  memset(info, 0, sizeof(*info));
202  info->addr.addrlen = 0;
203  info->netmask.addrlen = 0;
204  info->broadaddr.addrlen = 0;
205 
206  // Obtain the interface index and copy the name.
207  info->index = if_nametoindex(ifa->ifa_name);
208  strncpy(info->name, ifa->ifa_name, IO_IF_NAME_STRLEN - 1);
209 
210  info->domain = domain;
211 
212  // Copy the status.
213  info->flags = 0;
214  if (ifa->ifa_flags & IFF_UP)
215  info->flags |= IO_IF_UP;
216  if (ifa->ifa_flags & IFF_BROADCAST)
217  info->flags |= IO_IF_BROADCAST;
218  if (ifa->ifa_flags & IFF_LOOPBACK)
219  info->flags |= IO_IF_LOOPBACK;
220  if (ifa->ifa_flags & IFF_POINTOPOINT)
221  info->flags |= IO_IF_POINTTOPOINT;
222  if (ifa->ifa_flags & IFF_MULTICAST)
223  info->flags |= IO_IF_MULTICAST;
224 
225  // Copy the interface address.
226  if (ifa->ifa_addr)
227  io_addr_set(&info->addr, ifa->ifa_addr);
228 
229  // Copy the netmask.
230  if (ifa->ifa_netmask)
231  io_addr_set(&info->netmask, ifa->ifa_netmask);
232 
233  // Copy the broadcast or point-to-point destination address.
234  if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr)
235  io_addr_set(&info->broadaddr, ifa->ifa_broadaddr);
236  else if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr)
237  io_addr_set(&info->broadaddr, ifa->ifa_dstaddr);
238 
239  info++;
240  }
241 
242  freeifaddrs(res);
243 #endif
244 
245  return ninfo;
246 }
247 
248 static void
249 io_addr_set(io_addr_t *addr, const struct sockaddr *address)
250 {
251  assert(addr);
252  assert(address);
253 
254  switch (address->sa_family) {
255 #if defined(__linux__) && defined(HAVE_LINUX_CAN_H)
256  case AF_CAN: addr->addrlen = sizeof(struct sockaddr_can); break;
257 #endif
258  case AF_INET: addr->addrlen = sizeof(struct sockaddr_in); break;
259  case AF_INET6: addr->addrlen = sizeof(struct sockaddr_in6); break;
260 #if _POSIX_C_SOURCE >= 200112L
261  case AF_UNIX: addr->addrlen = sizeof(struct sockaddr_un); break;
262 #endif
263  default: addr->addrlen = 0; break;
264  }
265  memcpy(&addr->addr, address, addr->addrlen);
266 }
267 
268 #endif // _WIN32 || (__linux__ && HAVE_IFADDRS_H)
269 
270 #ifdef _WIN32
271 static NETIO_STATUS WINAPI
272 ConvertLengthToIpv6Mask(ULONG MaskLength, u_char Mask[16])
273 {
274  if (MaskLength > 128) {
275  for (int i = 0; i < 16; i++)
276  Mask[i] = 0;
277  return ERROR_INVALID_PARAMETER;
278  }
279 
280  for (LONG i = MaskLength, j = 0; i > 0; i -= 8, j++)
281  Mask[j] = i >= 8 ? 0xff : ((0xff << (8 - i)) & 0xff);
282  return NO_ERROR;
283 }
284 #endif
io_ifinfo
A structure describing a network interface.
Definition: if.h:51
IO_IF_BROADCAST
@ IO_IF_BROADCAST
A valid broadcast address is set.
Definition: if.h:41
string.h
__io_addr
An opaque network address type.
Definition: addr.h:30
if.h
io_ifinfo::netmask
io_addr_t netmask
The netmask used by the interface.
Definition: if.h:70
io_addr_set_ipv4_n
void io_addr_set_ipv4_n(io_addr_t *addr, const uint8_t ip[4], int port)
Initializes a network address from an IPv4 address and port number.
Definition: addr.c:329
io.h
IO_IF_MULTICAST
@ IO_IF_MULTICAST
The interface supports multicast.
Definition: if.h:47
errnum.h
IO_IF_LOOPBACK
@ IO_IF_LOOPBACK
The interface is a loopback interface.
Definition: if.h:43
__io_addr::addr
union __io_addr::@5 addr
The network address.
io_handle::flags
int flags
The I/O device flags (any combination of IO_FLAG_NO_CLOSE and IO_FLAG_NONBLOCK).
Definition: handle.h:62
IO_IF_UP
@ IO_IF_UP
The interface is running.
Definition: if.h:39
io_ifinfo::flags
int flags
The status of the interface (any combination of IO_IF_UP, IO_IF_BROADCAST, IO_IF_LOOPBACK,...
Definition: if.h:66
io_ifinfo::domain
int domain
The domain of the interface (one of IO_SOCK_BTH, IO_SOCK_IPV4, IO_SOCK_IPV6 or IO_SOCK_UNIX).
Definition: if.h:60
IO_IF_POINTTOPOINT
@ IO_IF_POINTTOPOINT
The interface is a point-to-point link.
Definition: if.h:45
io_ifinfo::index
unsigned int index
The interface index.
Definition: if.h:53
__io_addr::addrlen
int addrlen
The size (in bytes) of addr.
Definition: addr.h:32
io_addr_set_ipv6_n
void io_addr_set_ipv6_n(io_addr_t *addr, const uint8_t ip[16], int port)
Initializes a network address from an IPv6 address and port number.
Definition: addr.c:448
IO_IF_NAME_STRLEN
#define IO_IF_NAME_STRLEN
The maximum number of bytes required to hold the name of a network interface, including the terminati...
Definition: if.h:34
stdlib.h
IO_SOCK_IPV6
@ IO_SOCK_IPV6
An IPv6 socket.
Definition: sock.h:33
IO_SOCK_IPV4
@ IO_SOCK_IPV4
An IPv4 socket.
Definition: sock.h:31
sock.h
io_ifinfo::broadaddr
io_addr_t broadaddr
The broadcast address of the interface.
Definition: if.h:72
io_ifinfo::addr
io_addr_t addr
The address of the interface.
Definition: if.h:68
io_ifinfo::name
char name[IO_IF_NAME_STRLEN]
The interface name.
Definition: if.h:55
io_get_ifinfo
int io_get_ifinfo(int maxinfo, struct io_ifinfo *info)
Obtains a list of network interfaces.
Definition: if.c:57