xref: /haiku/src/system/libnetwork/getifaddrs.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
1 /*
2  * Copyright 2015, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Adrien Destugues, pulkomandy@pulkomandy.tk
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 #include <algorithm>
12 #include <new>
13 
14 #include <errno.h>
15 #include <net/if.h>
16 #include <netinet/in.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/socket.h>
20 #include <sys/sockio.h>
21 #include <unistd.h>
22 
23 #include <AutoDeleter.h>
24 
25 #include <ifaddrs.h>
26 
27 
28 static sockaddr*
29 copy_address(const sockaddr& address)
30 {
31 	if (address.sa_family == AF_UNSPEC)
32 		return NULL;
33 
34 	sockaddr_storage* copy = new (std::nothrow) sockaddr_storage;
35 	if (copy == NULL)
36 		return NULL;
37 
38 	size_t length = std::min(sizeof(sockaddr_storage), (size_t)address.sa_len);
39 	switch (address.sa_family) {
40 		case AF_INET:
41 			length = sizeof(sockaddr_in);
42 			break;
43 		case AF_INET6:
44 			length = sizeof(sockaddr_in6);
45 			break;
46 	}
47 	memcpy(copy, &address, length);
48 	copy->ss_len = length;
49 	return (sockaddr*)copy;
50 }
51 
52 
53 static int
54 _getifaddrs(int domain, char* buffer, size_t len, struct ifaddrs** previous)
55 {
56 	int socket = ::socket(domain, SOCK_DGRAM, 0);
57 	if (socket < 0)
58 		return -1;
59 	FileDescriptorCloser closer(socket);
60 
61 	// Get interfaces configuration
62 	ifconf config;
63 	config.ifc_buf = buffer;
64 	config.ifc_len = len;
65 	if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0)
66 		return -1;
67 
68 	ifreq* interfaces = (ifreq*)buffer;
69 	ifreq* end = (ifreq*)(buffer + config.ifc_len);
70 
71 	while (interfaces < end) {
72 		struct ifaddrs* current = new(std::nothrow) ifaddrs();
73 		if (current == NULL) {
74 			errno = B_NO_MEMORY;
75 			return -1;
76 		}
77 
78 		// Chain this interface with the next one
79 		current->ifa_next = *previous;
80 		*previous = current;
81 
82 		current->ifa_name = strdup(interfaces[0].ifr_name);
83 		current->ifa_addr = copy_address(interfaces[0].ifr_addr);
84 		current->ifa_netmask = NULL;
85 		current->ifa_dstaddr = NULL;
86 		current->ifa_data = NULL;
87 
88 		ifreq request;
89 		strlcpy(request.ifr_name, interfaces[0].ifr_name, IF_NAMESIZE);
90 
91 		if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0)
92 			current->ifa_flags = request.ifr_flags;
93 		if (ioctl(socket, SIOCGIFNETMASK, &request, sizeof(struct ifreq))
94 				== 0) {
95 			current->ifa_netmask = copy_address(request.ifr_mask);
96 		}
97 		if (ioctl(socket, SIOCGIFDSTADDR, &request, sizeof(struct ifreq))
98 				== 0) {
99 			current->ifa_dstaddr = copy_address(request.ifr_dstaddr);
100 		}
101 
102 		// Move on to next interface
103 		interfaces = (ifreq*)((uint8_t*)interfaces
104 			+ _SIZEOF_ADDR_IFREQ(interfaces[0]));
105 	}
106 
107 	return 0;
108 }
109 
110 
111 /*! Returns a chained list of all interfaces. */
112 int
113 getifaddrs(struct ifaddrs** _ifaddrs)
114 {
115 	if (_ifaddrs == NULL) {
116 		errno = B_BAD_VALUE;
117 		return -1;
118 	}
119 
120 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
121 	if (socket < 0)
122 		return -1;
123 
124 	FileDescriptorCloser closer(socket);
125 
126 	// Get interface count
127 	ifconf config;
128 	config.ifc_len = sizeof(config.ifc_value);
129 	if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0)
130 		return -1;
131 
132 	socket = -1;
133 	closer.Unset();
134 
135 	size_t count = (size_t)config.ifc_value;
136 	if (count == 0) {
137 		// No interfaces found
138 		*_ifaddrs = NULL;
139 		return 0;
140 	}
141 
142 	// Allocate a buffer for ifreqs for all interfaces
143 	size_t buflen = count * sizeof(struct ifreq);
144 	char* buffer = (char*)malloc(buflen);
145 	if (buffer == NULL) {
146 		errno = B_NO_MEMORY;
147 		return -1;
148 	}
149 
150 	MemoryDeleter deleter(buffer);
151 
152 	struct ifaddrs* previous = NULL;
153 	int serrno = errno;
154 	if (_getifaddrs(AF_INET, buffer, buflen, &previous) < 0 &&
155 		errno != B_UNSUPPORTED) {
156 		freeifaddrs(previous);
157 		return -1;
158 	}
159 	if (_getifaddrs(AF_INET6, buffer, buflen, &previous) < 0 &&
160 		errno != B_UNSUPPORTED) {
161 		freeifaddrs(previous);
162 		return -1;
163 	}
164 	if (_getifaddrs(AF_LINK, buffer, buflen, &previous) < 0 &&
165 		errno != B_UNSUPPORTED) {
166 		freeifaddrs(previous);
167 		return -1;
168 	}
169 	if (previous == NULL)
170 		return -1;
171 	errno = serrno;
172 
173 	*_ifaddrs = previous;
174 	return 0;
175 }
176 
177 
178 void
179 freeifaddrs(struct ifaddrs* ifa)
180 {
181 	while (ifa != NULL) {
182 		free((void*)ifa->ifa_name);
183 		delete ifa->ifa_addr;
184 		delete ifa->ifa_netmask;
185 		delete ifa->ifa_dstaddr;
186 
187 		struct ifaddrs* next = ifa->ifa_next;
188 		delete ifa;
189 		ifa = next;
190 	}
191 }
192 
193