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))
49static void io_addr_set(io_addr_t *addr, const struct sockaddr *address);
50#endif
51
52#if _WIN32
53static NETIO_STATUS WINAPI ConvertLengthToIpv6Mask(
54 ULONG MaskLength, u_char Mask[16]);
55#endif
56
57#if _WIN32 || (defined(__linux__) && defined(HAVE_IFADDRS_H))
58
59int
60io_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)
104 flags |= IO_IF_POINTTOPOINT;
105 else if (paa->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
106 flags |= IO_IF_LOOPBACK;
107 else
108 flags |= IO_IF_BROADCAST;
109 if (!(paa->Flags & IP_ADAPTER_NO_MULTICAST))
110 flags |= IO_IF_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
251static void
252io_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
274static NETIO_STATUS WINAPI
275ConvertLengthToIpv6Mask(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
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
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...
int io_get_ifinfo(int maxinfo, struct io_ifinfo *info)
Obtains a list of network interfaces.
Definition if.c:60
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 Windows-specific I/O declarations.
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
union __io_addr::@5 addr
The network address.
int addrlen
The size (in bytes) of addr.
Definition addr.h:32
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