xref: /haiku/src/bin/network/route/route.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include <SupportDefs.h>
11 
12 #include <arpa/inet.h>
13 #include <net/if.h>
14 #include <net/if_dl.h>
15 #include <net/if_types.h>
16 #include <netinet/in.h>
17 #include <sys/socket.h>
18 #include <sys/sockio.h>
19 
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 
27 extern const char* __progname;
28 const char* kProgramName = __progname;
29 
30 
31 enum modes {
32 	RTM_LIST = 0,
33 	RTM_DELETE,
34 	RTM_ADD,
35 	RTM_GET,
36 
37 	// TODO:
38 	RTM_CHANGE,
39 	RTM_FLUSH,
40 };
41 
42 struct address_family {
43 	int			family;
44 	const char*	name;
45 	const char*	identifiers[4];
46 	bool		(*parse_address)(const char* string, sockaddr* _address);
47 	const char*	(*address_to_string)(sockaddr* address);
48 };
49 
50 // AF_INET family
51 static bool inet_parse_address(const char* string, sockaddr* address);
52 static const char* inet_address_to_string(sockaddr* address);
53 
54 static const address_family kFamilies[] = {
55 	{
56 		AF_INET,
57 		"inet",
58 		{"AF_INET", "inet", "ipv4", NULL},
59 		inet_parse_address,
60 		inet_address_to_string,
61 	},
62 	{ -1, NULL, {NULL}, NULL, NULL }
63 };
64 
65 
66 static bool
67 inet_parse_address(const char* string, sockaddr* _address)
68 {
69 	in_addr inetAddress;
70 
71 	if (inet_aton(string, &inetAddress) != 1)
72 		return false;
73 
74 	sockaddr_in& address = *(sockaddr_in *)_address;
75 	address.sin_family = AF_INET;
76 	address.sin_len = sizeof(struct sockaddr_in);
77 	address.sin_port = 0;
78 	address.sin_addr = inetAddress;
79 	memset(&address.sin_zero[0], 0, sizeof(address.sin_zero));
80 
81 	return true;
82 }
83 
84 
85 static const char *
86 inet_address_to_string(sockaddr* address)
87 {
88 	if (address == NULL || address->sa_family != AF_INET)
89 		return "-";
90 
91 	return inet_ntoa(((sockaddr_in *)address)->sin_addr);
92 }
93 
94 
95 //	#pragma mark -
96 
97 
98 void
99 usage(int status)
100 {
101 	printf("usage: %s [command] [<interface>] [<address family>] <address|default> [<option/flags>...]\n"
102 		"Where <command> can be the one of:\n"
103 		"  add             - add a route for the specified interface\n"
104 		"  delete          - deletes the specified route\n"
105 		"  list            - list with filters [default]\n"
106 		"<option> can be the following:\n"
107 		"  netmask <addr>  - networking subnet mask\n"
108 		"  gw <addr>       - gateway address\n"
109 		"  mtu <bytes>     - maximal transfer unit\n"
110 		"And <flags> can be: reject, local, host\n\n"
111 		"Example:\n"
112 		"\t%s add /dev/net/ipro1000/0 default gw 192.168.0.254\n",
113 		kProgramName, kProgramName);
114 
115 	exit(status);
116 }
117 
118 
119 bool
120 prepare_request(struct ifreq& request, const char* name)
121 {
122 	if (strlen(name) > IF_NAMESIZE) {
123 		fprintf(stderr, "%s: interface name \"%s\" is too long.\n", kProgramName, name);
124 		return false;
125 	}
126 
127 	strcpy(request.ifr_name, name);
128 	return true;
129 }
130 
131 
132 bool
133 get_address_family(const char* argument, int32& familyIndex)
134 {
135 	for (int32 i = 0; kFamilies[i].family >= 0; i++) {
136 		for (int32 j = 0; kFamilies[i].identifiers[j]; j++) {
137 			if (!strcmp(argument, kFamilies[i].identifiers[j])) {
138 				// found a match
139 				familyIndex = i;
140 				return true;
141 			}
142 		}
143 	}
144 
145 	// defaults to AF_INET
146 	familyIndex = 0;
147 	return false;
148 }
149 
150 
151 bool
152 parse_address(int32 familyIndex, const char* argument, struct sockaddr_storage& address)
153 {
154 	if (argument == NULL)
155 		return false;
156 
157 	return kFamilies[familyIndex].parse_address(argument, (sockaddr *)&address);
158 }
159 
160 
161 //	#pragma mark -
162 
163 
164 void
165 list_routes(int socket, const char *interfaceName, route_entry &route)
166 {
167 	// get a list of all routes
168 
169 	ifconf config;
170 	config.ifc_len = sizeof(config.ifc_value);
171 	if (ioctl(socket, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0)
172 		return;
173 
174 	uint32 size = (uint32)config.ifc_value;
175 	if (size == 0) {
176 		fprintf(stderr, "%s: There are no routes!\n", kProgramName);
177 		return;
178 	}
179 
180 	void *buffer = malloc(size);
181 	if (buffer == NULL) {
182 		fprintf(stderr, "%s: Out of memory.\n", kProgramName);
183 		return;
184 	}
185 
186 	config.ifc_len = size;
187 	config.ifc_buf = buffer;
188 	if (ioctl(socket, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0)
189 		return;
190 
191 	ifreq *interface = (ifreq *)buffer;
192 	ifreq *end = (ifreq *)((uint8 *)buffer + size);
193 
194 	while (interface < end) {
195 		route_entry& route = interface->ifr_route;
196 
197 		// apply filters
198 		if (interfaceName == NULL || !strcmp(interfaceName, interface->ifr_name)) {
199 			// find family
200 			const address_family *family = NULL;
201 			for (int32 i = 0; kFamilies[i].family >= 0; i++) {
202 				if (route.destination->sa_family == kFamilies[i].family) {
203 					family = &kFamilies[i];
204 					break;
205 				}
206 			}
207 
208 			if (family != NULL) {
209 				printf("%15s ", family->address_to_string(route.destination));
210 				printf("mask %-15s ", family->address_to_string(route.mask));
211 
212 				if (route.flags & RTF_GATEWAY)
213 					printf("gateway %-15s ", family->address_to_string(route.gateway));
214 			} else {
215 				printf("unknown family ");
216 			}
217 
218 			printf("%s", interface->ifr_name);
219 
220 			if (route.flags != 0) {
221 				const struct {
222 					int			value;
223 					const char	*name;
224 				} kFlags[] = {
225 					{RTF_DEFAULT, "default"},
226 					{RTF_REJECT, "reject"},
227 					{RTF_HOST, "host"},
228 					{RTF_LOCAL, "local"},
229 					{RTF_DYNAMIC, "dynamic"},
230 					{RTF_MODIFIED, "modified"},
231 				};
232 				bool first = true;
233 
234 				for (uint32 i = 0; i < sizeof(kFlags) / sizeof(kFlags[0]); i++) {
235 					if ((route.flags & kFlags[i].value) != 0) {
236 						if (first) {
237 							printf(", ");
238 							first = false;
239 						} else
240 							putchar(' ');
241 						printf(kFlags[i].name);
242 					}
243 				}
244 			}
245 
246 			putchar('\n');
247 		}
248 
249 		int32 addressSize = 0;
250 		if (route.destination != NULL)
251 			addressSize += route.destination->sa_len;
252 		if (route.mask != NULL)
253 			addressSize += route.mask->sa_len;
254 		if (route.gateway != NULL)
255 			addressSize += route.gateway->sa_len;
256 
257 		interface = (ifreq *)((addr_t)interface + IF_NAMESIZE + sizeof(route_entry)
258 			+ addressSize);
259 	}
260 
261 	free(buffer);
262 }
263 
264 
265 void
266 delete_route(int socket, const char *interface, route_entry &route)
267 {
268 	ifreq request;
269 	if (!prepare_request(request, interface))
270 		return;
271 
272 	request.ifr_route = route;
273 
274 	if (ioctl(socket, SIOCDELRT, &request, sizeof(request)) < 0) {
275 		fprintf(stderr, "%s: Could not delete route for %s: %s\n",
276 			kProgramName, interface, strerror(errno));
277 	}
278 }
279 
280 
281 void
282 add_route(int socket, const char *interface, route_entry &route)
283 {
284 	ifreq request;
285 	if (!prepare_request(request, interface))
286 		return;
287 
288 	route.flags |= RTF_STATIC;
289 	request.ifr_route = route;
290 
291 	if (ioctl(socket, SIOCADDRT, &request, sizeof(request)) < 0) {
292 		fprintf(stderr, "%s: Could not add route for %s: %s\n",
293 			kProgramName, interface, strerror(errno));
294 	}
295 }
296 
297 
298 void
299 get_route(int socket, route_entry &route)
300 {
301 	union {
302 		route_entry request;
303 		uint8 buffer[512];
304 	};
305 
306 	request = route;
307 
308 	if (ioctl(socket, SIOCGETRT, buffer, sizeof(buffer)) < 0) {
309 		fprintf(stderr, "%s: Could not get route: %s\n",
310 			kProgramName, strerror(errno));
311 		return;
312 	}
313 
314 	// find family
315 	const address_family *family = NULL;
316 	for (int32 i = 0; kFamilies[i].family >= 0; i++) {
317 		if (route.destination->sa_family == kFamilies[i].family) {
318 			family = &kFamilies[i];
319 			break;
320 		}
321 	}
322 
323 	if (family != NULL) {
324 		printf("%s ", family->address_to_string(request.destination));
325 		printf("mask %s ", family->address_to_string(request.mask));
326 
327 		if (request.flags & RTF_GATEWAY)
328 			printf("gateway %s ", family->address_to_string(request.gateway));
329 
330 		printf("source %s\n", family->address_to_string(request.source));
331 	} else {
332 		printf("unknown family ");
333 	}
334 }
335 
336 
337 //	#pragma mark -
338 
339 
340 int
341 main(int argc, char** argv)
342 {
343 	if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
344 		usage(0);
345 
346 	int32 mode = RTM_LIST;
347 
348 	if (argc > 1) {
349 		if (!strcmp(argv[1], "delete")
350 			|| !strcmp(argv[1], "del")
351 			|| !strcmp(argv[1], "-d")) {
352 			// delete route
353 			if (argc < 3)
354 				usage(1);
355 
356 			mode = RTM_DELETE;
357 		} else if (!strcmp(argv[1], "add")
358 			|| !strcmp(argv[1], "-a")) {
359 			// add route
360 			if (argc < 3)
361 				usage(1);
362 
363 			mode = RTM_ADD;
364 		} else if (!strcmp(argv[1], "get")) {
365 			// get route for destination
366 			if (argc < 3)
367 				usage(1);
368 
369 			mode = RTM_GET;
370 		}
371 	}
372 
373 	int32 i = 2;
374 	int32 interfaceIndex = i;
375 	bool familySpecified = false;
376 	int32 familyIndex = 0;
377 	const char *interface = NULL;
378 	sockaddr_storage destination;
379 	sockaddr_storage mask;
380 	sockaddr_storage gateway;
381 	bool hasDestination = false, hasGateway = false, hasMask = false;
382 	bool defaultRoute = false;
383 
384 	route_entry route;
385 	memset(&route, 0, sizeof(route_entry));
386 
387 	while (i < argc && i < 5) {
388 		// try to parse address family
389 		if (i <= 3 && familyIndex == -1 && get_address_family(argv[i], familyIndex)) {
390 			familySpecified = true;
391 			if (i == 2)
392 				interfaceIndex = -1;
393 		}
394 
395 		if (!strcmp(argv[i], "default")) {
396 			defaultRoute = true;
397 			route.flags = RTF_DEFAULT;
398 			i++;
399 			break;
400 		} else if (parse_address(familyIndex, argv[i], destination)) {
401 			hasDestination = true;
402 			i++;
403 			break;
404 		}
405 
406 		i++;
407 	}
408 
409 	if (!defaultRoute && !hasDestination && mode != RTM_LIST)
410 		usage(1);
411 
412 	if (i == 3)
413 		interfaceIndex = -1;
414 	if (interfaceIndex != -1 && interfaceIndex < argc)
415 		interface = argv[interfaceIndex];
416 
417 	if (i < argc && parse_address(familyIndex, argv[i], mask)) {
418 		hasMask = true;
419 		i++;
420 	}
421 
422 	// parse options and flags
423 
424 	while (i < argc) {
425 		if (!strcmp(argv[i], "gw") || !strcmp(argv[i], "gateway")) {
426 			if (!parse_address(familyIndex, argv[i + 1], gateway)) {
427 				fprintf(stderr, "%s: Option 'gw' needs valid address parameter\n",
428 					kProgramName);
429 				exit(1);
430 			}
431 			hasGateway = true;
432 			i++;
433 		} else if (!strcmp(argv[i], "nm") || !strcmp(argv[i], "netmask")) {
434 			if (hasMask) {
435 				fprintf(stderr, "%s: Netmask is specified twice\n",
436 					kProgramName);
437 				exit(1);
438 			}
439 			if (!parse_address(familyIndex, argv[i + 1], mask)) {
440 				fprintf(stderr, "%s: Option 'netmask' needs valid address parameter\n",
441 					kProgramName);
442 				exit(1);
443 			}
444 			hasMask = true;
445 			i++;
446 		} else if (!strcmp(argv[i], "mtu")) {
447 			route.mtu = argv[i + 1] ? strtol(argv[i + 1], NULL, 0) : 0;
448 			if (route.mtu <= 500) {
449 				fprintf(stderr, "%s: Option 'mtu' exptected valid max transfer unit size\n",
450 					kProgramName);
451 				exit(1);
452 			}
453 			i++;
454 		} else if (!strcmp(argv[i], "host")) {
455 			route.flags |= RTF_HOST;
456 		} else if (!strcmp(argv[i], "local")) {
457 			route.flags |= RTF_LOCAL;
458 		} else if (!strcmp(argv[i], "reject")) {
459 			route.flags |= RTF_REJECT;
460 		} else
461 			usage(1);
462 
463 		i++;
464 	}
465 
466 	if (hasDestination)
467 		route.destination = (sockaddr *)&destination;
468 	if (hasMask)
469 		route.mask = (sockaddr *)&mask;
470 	if (hasGateway) {
471 		route.gateway = (sockaddr *)&gateway;
472 		route.flags |= RTF_GATEWAY;
473 	}
474 
475 	// we need a socket to talk to the networking stack
476 	int socket = ::socket(kFamilies[familyIndex].family, SOCK_DGRAM, 0);
477 	if (socket < 0) {
478 		fprintf(stderr, "%s: The requested address family is not available.\n",
479 			kProgramName);
480 		return 1;
481 	}
482 
483 	switch (mode) {
484 		case RTM_ADD:
485 			if (interface == NULL) {
486 				fprintf(stderr, "%s: You need to specify an interface when adding a route.\n",
487 					kProgramName);
488 				usage(1);
489 			}
490 
491 			add_route(socket, interface, route);
492 			break;
493 		case RTM_DELETE:
494 			if (interface == NULL) {
495 				fprintf(stderr, "%s: You need to specify an interface when removing a route.\n",
496 					kProgramName);
497 				usage(1);
498 			}
499 
500 			delete_route(socket, interface, route);
501 			break;
502 
503 		case RTM_LIST:
504 			if (familySpecified)
505 				list_routes(socket, interface, route);
506 			else {
507 				for (int32 i = 0; kFamilies[i].family >= 0; i++) {
508 					int socket = ::socket(kFamilies[familyIndex].family, SOCK_DGRAM, 0);
509 					if (socket < 0)
510 						continue;
511 
512 					list_routes(socket, interface, route);
513 					close(socket);
514 				}
515 			}
516 			break;
517 
518 		case RTM_GET:
519 			get_route(socket, route);
520 			break;
521 	}
522 
523 	close(socket);
524 	return 0;
525 }
526 
527