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