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