Lely core libraries 1.9.2
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))
46static void io_addr_set(io_addr_t *addr, const struct sockaddr *address);
47#endif
48
49#ifdef _WIN32
50static 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
56int
57io_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 (__unlikely(dwErrCode != ERROR_BUFFER_OVERFLOW)) {
71 SetLastError(dwErrCode);
72 return -1;
73 }
74
75 PIP_ADAPTER_ADDRESSES pAdapterAddresses = malloc(Size);
76 if (__unlikely(!pAdapterAddresses))
77 return -1;
78
79 dwErrCode = GetAdaptersAddresses(
80 AF_UNSPEC, Flags, NULL, pAdapterAddresses, &Size);
81 if (__unlikely(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 (__unlikely(!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)
101 flags |= IO_IF_POINTTOPOINT;
102 else if (paa->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
103 flags |= IO_IF_LOOPBACK;
104 else
105 flags |= IO_IF_BROADCAST;
106 if (!(paa->Flags & IP_ADAPTER_NO_MULTICAST))
107 flags |= IO_IF_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 (__unlikely(!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 (__unlikely(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
248static void
249io_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
271static NETIO_STATUS WINAPI
272ConvertLengthToIpv6Mask(ULONG MaskLength, u_char Mask[16])
273{
274 if (__unlikely(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
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
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
This header file is part of the utilities library; it contains the native and platform-independent er...
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
int io_get_ifinfo(int maxinfo, struct io_ifinfo *info)
Obtains a list of network interfaces.
Definition: if.c:57
This header file is part of the I/O library; it contains network interface declarations.
#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
@ IO_IF_UP
The interface is running.
Definition: if.h:39
@ IO_IF_POINTTOPOINT
The interface is a point-to-point link.
Definition: if.h:45
@ IO_IF_LOOPBACK
The interface is a loopback interface.
Definition: if.h:43
@ IO_IF_BROADCAST
A valid broadcast address is set.
Definition: if.h:41
@ IO_IF_MULTICAST
The interface supports multicast.
Definition: if.h:47
This header file is part of the I/O library; it contains the network socket declarations.
@ IO_SOCK_IPV4
An IPv4 socket.
Definition: sock.h:31
@ IO_SOCK_IPV6
An IPv6 socket.
Definition: sock.h:33
This is the internal header file of the I/O library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <string....
An opaque network address type.
Definition: addr.h:30
int addrlen
The size (in bytes) of addr.
Definition: addr.h:32
union __io_addr::@2 addr
The network address.
A structure describing a network interface.
Definition: if.h:51
io_addr_t addr
The address of the interface.
Definition: if.h:68
char name[IO_IF_NAME_STRLEN]
The interface name.
Definition: if.h:55
unsigned int index
The interface index.
Definition: if.h:53
io_addr_t netmask
The netmask used by the interface.
Definition: if.h:70
int flags
The status of the interface (any combination of IO_IF_UP, IO_IF_BROADCAST, IO_IF_LOOPBACK,...
Definition: if.h:66
io_addr_t broadaddr
The broadcast address of the interface.
Definition: if.h:72
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