xref: /haiku/src/system/libnetwork/getifaddrs.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
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 	FileDescriptorCloser socket (::socket(domain, SOCK_DGRAM, 0));
57 	if (!socket.IsSet())
58 		return -1;
59 
60 	// Get interfaces configuration
61 	ifconf config;
62 	config.ifc_buf = buffer;
63 	config.ifc_len = len;
64 	if (ioctl(socket.Get(), SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0)
65 		return -1;
66 
67 	ifreq* interfaces = (ifreq*)buffer;
68 	ifreq* end = (ifreq*)(buffer + config.ifc_len);
69 
70 	while (interfaces < end) {
71 		struct ifaddrs* current = new(std::nothrow) ifaddrs();
72 		if (current == NULL) {
73 			errno = B_NO_MEMORY;
74 			return -1;
75 		}
76 
77 		// Chain this interface with the next one
78 		current->ifa_next = *previous;
79 		*previous = current;
80 
81 		current->ifa_name = strdup(interfaces[0].ifr_name);
82 		current->ifa_addr = copy_address(interfaces[0].ifr_addr);
83 		current->ifa_netmask = NULL;
84 		current->ifa_dstaddr = NULL;
85 		current->ifa_data = NULL;
86 
87 		ifreq request;
88 		strlcpy(request.ifr_name, interfaces[0].ifr_name, IF_NAMESIZE);
89 
90 		if (ioctl(socket.Get(), SIOCGIFFLAGS, &request, sizeof(struct ifreq))
91 				== 0)
92 			current->ifa_flags = request.ifr_flags;
93 		if (ioctl(socket.Get(), SIOCGIFNETMASK, &request, sizeof(struct ifreq))
94 				== 0) {
95 			current->ifa_netmask = copy_address(request.ifr_mask);
96 		}
97 		if (ioctl(socket.Get(), 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 	FileDescriptorCloser socket(::socket(AF_INET, SOCK_DGRAM, 0));
121 	if (!socket.IsSet())
122 		return -1;
123 
124 	// Get interface count
125 	ifconf config;
126 	config.ifc_len = sizeof(config.ifc_value);
127 	if (ioctl(socket.Get(), SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0)
128 		return -1;
129 
130 	socket.Unset();
131 
132 	size_t count = (size_t)config.ifc_value;
133 	if (count == 0) {
134 		// No interfaces found
135 		*_ifaddrs = NULL;
136 		return 0;
137 	}
138 
139 	// Allocate a buffer for ifreqs for all interfaces
140 	size_t buflen = count * sizeof(struct ifreq);
141 	ArrayDeleter<char> buffer(new(std::nothrow) char[buflen]);
142 	if (!buffer.IsSet()) {
143 		errno = B_NO_MEMORY;
144 		return -1;
145 	}
146 
147 	struct ifaddrs* previous = NULL;
148 	int serrno = errno;
149 	if (_getifaddrs(AF_INET, buffer.Get(), buflen, &previous) < 0 &&
150 		errno != B_UNSUPPORTED) {
151 		freeifaddrs(previous);
152 		return -1;
153 	}
154 	if (_getifaddrs(AF_INET6, buffer.Get(), buflen, &previous) < 0 &&
155 		errno != B_UNSUPPORTED) {
156 		freeifaddrs(previous);
157 		return -1;
158 	}
159 	if (_getifaddrs(AF_LINK, buffer.Get(), buflen, &previous) < 0 &&
160 		errno != B_UNSUPPORTED) {
161 		freeifaddrs(previous);
162 		return -1;
163 	}
164 	if (previous == NULL)
165 		return -1;
166 	errno = serrno;
167 
168 	*_ifaddrs = previous;
169 	return 0;
170 }
171 
172 
173 void
174 freeifaddrs(struct ifaddrs* ifa)
175 {
176 	while (ifa != NULL) {
177 		free((void*)ifa->ifa_name);
178 		delete ifa->ifa_addr;
179 		delete ifa->ifa_netmask;
180 		delete ifa->ifa_dstaddr;
181 
182 		struct ifaddrs* next = ifa->ifa_next;
183 		delete ifa;
184 		ifa = next;
185 	}
186 }
187 
188