1/*
2 * Copyright 2013-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <NetworkRoute.h>
7
8#include <errno.h>
9#include <net/if.h>
10#include <string.h>
11#include <sys/sockio.h>
12
13#include <AutoDeleter.h>
14
15
16BNetworkRoute::BNetworkRoute()
17{
18	memset(&fRouteEntry, 0, sizeof(route_entry));
19}
20
21
22BNetworkRoute::~BNetworkRoute()
23{
24	UnsetDestination();
25	UnsetMask();
26	UnsetGateway();
27	UnsetSource();
28}
29
30
31status_t
32BNetworkRoute::SetTo(const BNetworkRoute& other)
33{
34	return SetTo(other.RouteEntry());
35}
36
37
38status_t
39BNetworkRoute::SetTo(const route_entry& routeEntry)
40{
41	#define SET_ADDRESS(address, setFunction) \
42		if (routeEntry.address != NULL) { \
43			result = setFunction(*routeEntry.address); \
44			if (result != B_OK) \
45				return result; \
46		}
47
48	status_t result;
49	SET_ADDRESS(destination, SetDestination)
50	SET_ADDRESS(mask, SetMask)
51	SET_ADDRESS(gateway, SetGateway)
52	SET_ADDRESS(source, SetSource)
53
54	SetFlags(routeEntry.flags);
55	SetMTU(routeEntry.mtu);
56	return B_OK;
57}
58
59
60void
61BNetworkRoute::Adopt(BNetworkRoute& other)
62{
63	memcpy(&fRouteEntry, &other.fRouteEntry, sizeof(route_entry));
64	memset(&other.fRouteEntry, 0, sizeof(route_entry));
65}
66
67
68const route_entry&
69BNetworkRoute::RouteEntry() const
70{
71	return fRouteEntry;
72}
73
74
75const sockaddr*
76BNetworkRoute::Destination() const
77{
78	return fRouteEntry.destination;
79}
80
81
82status_t
83BNetworkRoute::SetDestination(const sockaddr& destination)
84{
85	return _AllocateAndSetAddress(destination, fRouteEntry.destination);
86}
87
88
89void
90BNetworkRoute::UnsetDestination()
91{
92	_FreeAndUnsetAddress(fRouteEntry.destination);
93}
94
95
96const sockaddr*
97BNetworkRoute::Mask() const
98{
99	return fRouteEntry.mask;
100}
101
102
103status_t
104BNetworkRoute::SetMask(const sockaddr& mask)
105{
106	return _AllocateAndSetAddress(mask, fRouteEntry.mask);
107}
108
109
110void
111BNetworkRoute::UnsetMask()
112{
113	_FreeAndUnsetAddress(fRouteEntry.mask);
114}
115
116
117const sockaddr*
118BNetworkRoute::Gateway() const
119{
120	return fRouteEntry.gateway;
121}
122
123
124status_t
125BNetworkRoute::SetGateway(const sockaddr& gateway)
126{
127	return _AllocateAndSetAddress(gateway, fRouteEntry.gateway);
128}
129
130
131void
132BNetworkRoute::UnsetGateway()
133{
134	_FreeAndUnsetAddress(fRouteEntry.gateway);
135}
136
137
138const sockaddr*
139BNetworkRoute::Source() const
140{
141	return fRouteEntry.source;
142}
143
144
145status_t
146BNetworkRoute::SetSource(const sockaddr& source)
147{
148	return _AllocateAndSetAddress(source, fRouteEntry.source);
149}
150
151
152void
153BNetworkRoute::UnsetSource()
154{
155	_FreeAndUnsetAddress(fRouteEntry.source);
156}
157
158
159uint32
160BNetworkRoute::Flags() const
161{
162	return fRouteEntry.flags;
163}
164
165
166void
167BNetworkRoute::SetFlags(uint32 flags)
168{
169	fRouteEntry.flags = flags;
170}
171
172
173uint32
174BNetworkRoute::MTU() const
175{
176	return fRouteEntry.mtu;
177}
178
179
180void
181BNetworkRoute::SetMTU(uint32 mtu)
182{
183	fRouteEntry.mtu = mtu;
184}
185
186
187int
188BNetworkRoute::AddressFamily() const
189{
190	#define RETURN_FAMILY_IF_SET(address) \
191		if (fRouteEntry.address != NULL \
192			&& fRouteEntry.address->sa_family != AF_UNSPEC) { \
193			return fRouteEntry.address->sa_family; \
194		}
195
196	RETURN_FAMILY_IF_SET(destination)
197	RETURN_FAMILY_IF_SET(mask)
198	RETURN_FAMILY_IF_SET(gateway)
199	RETURN_FAMILY_IF_SET(source)
200
201	return AF_UNSPEC;
202}
203
204
205status_t
206BNetworkRoute::GetDefaultRoute(int family, const char* interfaceName,
207	BNetworkRoute& route)
208{
209	BObjectList<BNetworkRoute> routes(1, true);
210	status_t result = GetRoutes(family, interfaceName, RTF_DEFAULT, routes);
211	if (result != B_OK)
212		return result;
213
214	if (routes.CountItems() == 0)
215		return B_ENTRY_NOT_FOUND;
216
217	route.Adopt(*routes.ItemAt(0));
218	return B_OK;
219}
220
221
222status_t
223BNetworkRoute::GetDefaultGateway(int family, const char* interfaceName,
224	sockaddr& gateway)
225{
226	BNetworkRoute route;
227	status_t result = GetDefaultRoute(family, interfaceName, route);
228	if (result != B_OK)
229		return result;
230
231	const sockaddr* defaultGateway = route.Gateway();
232	if (defaultGateway == NULL)
233		return B_ENTRY_NOT_FOUND;
234
235	memcpy(&gateway, defaultGateway, defaultGateway->sa_len);
236	return B_OK;
237}
238
239
240status_t
241BNetworkRoute::GetRoutes(int family, BObjectList<BNetworkRoute>& routes)
242{
243	return GetRoutes(family, NULL, 0, routes);
244}
245
246
247status_t
248BNetworkRoute::GetRoutes(int family, const char* interfaceName,
249	BObjectList<BNetworkRoute>& routes)
250{
251	return GetRoutes(family, interfaceName, 0, routes);
252}
253
254
255status_t
256BNetworkRoute::GetRoutes(int family, const char* interfaceName,
257	uint32 filterFlags, BObjectList<BNetworkRoute>& routes)
258{
259	FileDescriptorCloser socket(::socket(family, SOCK_DGRAM, 0));
260	if (!socket.IsSet())
261		return errno;
262
263	ifconf config;
264	config.ifc_len = sizeof(config.ifc_value);
265	if (ioctl(socket.Get(), SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0)
266		return errno;
267
268	uint32 size = (uint32)config.ifc_value;
269	if (size == 0)
270		return B_OK;
271
272	void* buffer = malloc(size);
273	if (buffer == NULL)
274		return B_NO_MEMORY;
275
276	MemoryDeleter bufferDeleter(buffer);
277	config.ifc_len = size;
278	config.ifc_buf = buffer;
279
280	if (ioctl(socket.Get(), SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0)
281		return errno;
282
283	ifreq* interface = (ifreq*)buffer;
284	ifreq* end = (ifreq*)((uint8*)buffer + size);
285
286	while (interface < end) {
287		route_entry& routeEntry = interface->ifr_route;
288
289		if ((interfaceName == NULL
290				|| strcmp(interface->ifr_name, interfaceName) == 0)
291			&& (filterFlags == 0 || (routeEntry.flags & filterFlags) != 0)) {
292
293			BNetworkRoute* route = new(std::nothrow) BNetworkRoute;
294			if (route == NULL)
295				return B_NO_MEMORY;
296
297			// Note that source is not provided in the buffer.
298			routeEntry.source = NULL;
299
300			status_t result = route->SetTo(routeEntry);
301			if (result != B_OK) {
302				delete route;
303				return result;
304			}
305
306			if (!routes.AddItem(route)) {
307				delete route;
308				return B_NO_MEMORY;
309			}
310		}
311
312		size_t addressSize = 0;
313		if (routeEntry.destination != NULL)
314			addressSize += routeEntry.destination->sa_len;
315		if (routeEntry.mask != NULL)
316			addressSize += routeEntry.mask->sa_len;
317		if (routeEntry.gateway != NULL)
318			addressSize += routeEntry.gateway->sa_len;
319
320		interface = (ifreq *)((addr_t)interface + IF_NAMESIZE
321			+ sizeof(route_entry) + addressSize);
322	}
323
324	return B_OK;
325}
326
327
328status_t
329BNetworkRoute::_AllocateAndSetAddress(const sockaddr& from,
330	sockaddr*& to)
331{
332	if (from.sa_len > sizeof(sockaddr_storage))
333		return B_BAD_VALUE;
334
335	if (to == NULL) {
336		to = (sockaddr*)malloc(sizeof(sockaddr_storage));
337		if (to == NULL)
338			return B_NO_MEMORY;
339	}
340
341	memcpy(to, &from, from.sa_len);
342	return B_OK;
343}
344
345
346void
347BNetworkRoute::_FreeAndUnsetAddress(sockaddr*& address)
348{
349	free(address);
350	address = NULL;
351}
352