xref: /haiku/src/bin/network/route/route.cpp (revision e7d5c75dce28921de0dc981ed840205a67a0c0e5)
1 /*
2  * Copyright 2006-2010, 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 enum preferred_output_format {
43 	PREFER_OUTPUT_MASK,
44 	PREFER_OUTPUT_PREFIX_LENGTH,
45 };
46 
47 struct address_family {
48 	int			family;
49 	const char*	name;
50 	const char*	identifiers[4];
51 	preferred_output_format	preferred_format;
52 	bool		(*parse_address)(const char* string, sockaddr* _address);
53 	bool		(*prefix_length_to_mask)(uint8 prefixLength, sockaddr* mask);
54 	uint8		(*mask_to_prefix_length)(sockaddr* mask);
55 	const char*	(*address_to_string)(sockaddr* address);
56 };
57 
58 // AF_INET family
59 static bool inet_parse_address(const char* string, sockaddr* address);
60 static bool inet_prefix_length_to_mask(uint8 prefixLength, sockaddr* mask);
61 static uint8 inet_mask_to_prefix_length(sockaddr* mask);
62 static const char* inet_address_to_string(sockaddr* address);
63 
64 // AF_INET6 family
65 static bool inet6_parse_address(const char* string, sockaddr* address);
66 static bool inet6_prefix_length_to_mask(uint8 prefixLength, sockaddr* mask);
67 static uint8 inet6_mask_to_prefix_length(sockaddr* mask);
68 static const char* inet6_address_to_string(sockaddr* address);
69 
70 static const address_family kFamilies[] = {
71 	{
72 		AF_INET,
73 		"inet",
74 		{"AF_INET", "inet", "ipv4", NULL},
75 		PREFER_OUTPUT_MASK,
76 		inet_parse_address,
77 		inet_prefix_length_to_mask,
78 		inet_mask_to_prefix_length,
79 		inet_address_to_string,
80 	},
81 	{
82 		AF_INET6,
83 		"inet6",
84 		{"AF_INET6", "inet6", "ipv6", NULL},
85 		PREFER_OUTPUT_PREFIX_LENGTH,
86 		inet6_parse_address,
87 		inet6_prefix_length_to_mask,
88 		inet6_mask_to_prefix_length,
89 		inet6_address_to_string,
90 	},
91 	{ -1, NULL, {NULL}, PREFER_OUTPUT_MASK, NULL, NULL, NULL, NULL }
92 };
93 
94 
95 static bool
96 inet_parse_address(const char* string, sockaddr* _address)
97 {
98 	in_addr inetAddress;
99 
100 	if (inet_aton(string, &inetAddress) != 1)
101 		return false;
102 
103 	sockaddr_in& address = *(sockaddr_in*)_address;
104 	address.sin_family = AF_INET;
105 	address.sin_len = sizeof(struct sockaddr_in);
106 	address.sin_port = 0;
107 	address.sin_addr = inetAddress;
108 	memset(&address.sin_zero[0], 0, sizeof(address.sin_zero));
109 
110 	return true;
111 }
112 
113 
114 static bool
115 inet_prefix_length_to_mask(uint8 prefixLength, sockaddr* _mask)
116 {
117 	if (prefixLength > 32)
118 		return false;
119 
120 	sockaddr_in& mask = *(sockaddr_in*)_mask;
121 	mask.sin_family = AF_INET;
122         mask.sin_len = sizeof(sockaddr_in);
123 	mask.sin_port = 0;
124 	memset(&mask.sin_zero[0], 0, sizeof(mask.sin_zero));
125 
126 	uint32 hostMask = 0;
127 	for (uint8 i = 32; i > 32 - prefixLength; i--)
128 		hostMask |= 1 << (i - 1);
129 	mask.sin_addr.s_addr = htonl(hostMask);
130 
131 	return true;
132 }
133 
134 
135 static uint8
136 inet_mask_to_prefix_length(sockaddr* _mask)
137 {
138 	if (_mask == NULL)
139 		return 32;
140 
141 	sockaddr_in& mask = *(sockaddr_in*)_mask;
142 	if (mask.sin_family != AF_INET)
143 	    return (uint8)-1;
144 
145 	uint8 result = 0;
146 	uint32 hostMask = ntohl(mask.sin_addr.s_addr);
147 	for (uint8 i = 32; i > 0; i--) {
148 		if (hostMask & (1 << (i - 1)) == 0)
149 			break;
150 		result++;
151 	}
152 
153 	return result;
154 }
155 
156 
157 static const char*
158 inet_address_to_string(sockaddr* address)
159 {
160 	if (address == NULL || address->sa_family != AF_INET)
161 		return "-";
162 
163 	return inet_ntoa(((sockaddr_in*)address)->sin_addr);
164 }
165 
166 
167 static bool
168 inet6_parse_address(const char* string, sockaddr* _address)
169 {
170 	sockaddr_in6& address = *(sockaddr_in6*)_address;
171 
172 	if (inet_pton(AF_INET6, string, &address.sin6_addr) != 1)
173 		return false;
174 
175 	address.sin6_family = AF_INET6;
176 	address.sin6_len = sizeof(sockaddr_in6);
177 	address.sin6_port = 0;
178 	address.sin6_flowinfo = 0;
179 	address.sin6_scope_id = 0;
180 
181 	return true;
182 }
183 
184 
185 static bool
186 inet6_prefix_length_to_mask(uint8 prefixLength, sockaddr* _mask)
187 {
188 	if (prefixLength > 128)
189 		return false;
190 
191 	sockaddr_in6& mask = *(sockaddr_in6*)_mask;
192 	mask.sin6_family = AF_INET6;
193 	mask.sin6_len = sizeof(sockaddr_in6);
194 	mask.sin6_port = 0;
195 	mask.sin6_flowinfo = 0;
196 	mask.sin6_scope_id = 0;
197 	memset(mask.sin6_addr.s6_addr, 0, sizeof(in6_addr));
198 
199 	for (uint8 i = 0; i < sizeof(in6_addr); i++, prefixLength -= 8) {
200 		if (prefixLength < 8) {
201 			const uint8 masks[] = {
202 				0x00, 0x80, 0xc0, 0xe0,
203 				0xf0, 0xf8, 0xfc, 0xfe
204 			};
205 			mask.sin6_addr.s6_addr[i] = masks[prefixLength];
206 			break;
207 		}
208 
209 		mask.sin6_addr.s6_addr[i] = 0xff;
210 	}
211 
212 	return true;
213 }
214 
215 
216 static uint8
217 inet6_mask_to_prefix_length(sockaddr* _mask)
218 {
219 	if (_mask == NULL)
220 		return 128;
221 
222 	sockaddr_in6& mask = *(sockaddr_in6*)_mask;
223 	if (mask.sin6_family != AF_INET6)
224 	    return (uint8)-1;
225 
226 	uint8 result = 0;
227 	for (uint8 i = 0; i < sizeof(in6_addr); i++) {
228 		for (uint8 j = 0; j < 8; j++) {
229 			if (!(mask.sin6_addr.s6_addr[i] & (1 << j)))
230 				return result;
231 			result++;
232 		}
233 	}
234 
235 	return 128;
236 }
237 
238 
239 static const char*
240 inet6_address_to_string(sockaddr* address)
241 {
242 	if (address == NULL || address->sa_family != AF_INET6)
243 		return "-";
244 
245 	static char buffer[INET6_ADDRSTRLEN];
246 
247 	inet_ntop(AF_INET6, &((sockaddr_in6*)address)->sin6_addr,
248 		buffer, sizeof(buffer));
249 
250 	return buffer;
251 }
252 
253 
254 //	#pragma mark -
255 
256 
257 void
258 usage(int status)
259 {
260 	printf("usage: %s [command] [<interface>] [<address family>] "
261 			"<address|default> [<option/flags>...]\n"
262 		"Where <command> can be the one of:\n"
263 		"  add                - add a route for the specified interface\n"
264 		"  delete             - deletes the specified route\n"
265 		"  list               - list with filters [default]\n"
266 		"<option> can be the following:\n"
267 		"  netmask <addr>     - networking subnet mask\n"
268 		"  prefixlen <number> - subnet mask length in bits\n"
269 		"  gw <addr>          - gateway address\n"
270 		"  mtu <bytes>        - maximal transfer unit\n"
271 		"And <flags> can be: reject, local, host\n\n"
272 		"Example:\n"
273 		"\t%s add /dev/net/ipro1000/0 default gw 192.168.0.254\n",
274 		kProgramName, kProgramName);
275 
276 	exit(status);
277 }
278 
279 
280 bool
281 prepare_request(struct ifreq& request, const char* name)
282 {
283 	if (strlen(name) >= IF_NAMESIZE) {
284 		fprintf(stderr, "%s: interface name \"%s\" is too long.\n",
285 			kProgramName, name);
286 		return false;
287 	}
288 
289 	strcpy(request.ifr_name, name);
290 	return true;
291 }
292 
293 
294 bool
295 get_address_family(const char* argument, int32& familyIndex)
296 {
297 	for (int32 i = 0; kFamilies[i].family >= 0; i++) {
298 		for (int32 j = 0; kFamilies[i].identifiers[j]; j++) {
299 			if (!strcmp(argument, kFamilies[i].identifiers[j])) {
300 				// found a match
301 				familyIndex = i;
302 				return true;
303 			}
304 		}
305 	}
306 
307 	// defaults to AF_INET
308 	familyIndex = 0;
309 	return false;
310 }
311 
312 
313 bool
314 parse_address(int32 familyIndex, const char* argument,
315 	struct sockaddr_storage& address)
316 {
317 	if (argument == NULL)
318 		return false;
319 
320 	return kFamilies[familyIndex].parse_address(argument, (sockaddr*)&address);
321 }
322 
323 
324 bool
325 prefix_length_to_mask(int32 familyIndex, const char* argument,
326 	struct sockaddr_storage& mask)
327 {
328 	if (argument == NULL)
329 		return false;
330 
331 	char *end;
332 	uint32 prefixLength = strtoul(argument, &end, 10);
333 	if (end == argument)
334 		return false;
335 
336 	return kFamilies[familyIndex].prefix_length_to_mask(
337 		(uint8)prefixLength, (sockaddr*)&mask);
338 }
339 
340 
341 //	#pragma mark -
342 
343 
344 void
345 list_routes(int socket, const char *interfaceName, route_entry &route)
346 {
347 	// get a list of all routes
348 
349 	ifconf config;
350 	config.ifc_len = sizeof(config.ifc_value);
351 	if (ioctl(socket, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0)
352 		return;
353 
354 	uint32 size = (uint32)config.ifc_value;
355 	if (size == 0)
356 		return;
357 
358 	void *buffer = malloc(size);
359 	if (buffer == NULL) {
360 		fprintf(stderr, "%s: Out of memory.\n", kProgramName);
361 		return;
362 	}
363 
364 	config.ifc_len = size;
365 	config.ifc_buf = buffer;
366 	if (ioctl(socket, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0)
367 		return;
368 
369 	ifreq *interface = (ifreq*)buffer;
370 	ifreq *end = (ifreq*)((uint8*)buffer + size);
371 
372 	while (interface < end) {
373 		route_entry& route = interface->ifr_route;
374 
375 		// apply filters
376 		if (interfaceName == NULL
377 			|| !strcmp(interfaceName, interface->ifr_name)) {
378 			// find family
379 			const address_family *family = NULL;
380 			for (int32 i = 0; kFamilies[i].family >= 0; i++) {
381 				if (route.destination->sa_family == kFamilies[i].family) {
382 					family = &kFamilies[i];
383 					break;
384 				}
385 			}
386 
387 			if (family != NULL) {
388 				// TODO: is the %15s format OK for IPv6?
389 				printf("%15s", family->address_to_string(route.destination));
390 				switch (family->preferred_format) {
391 					case PREFER_OUTPUT_MASK:
392 						printf(" mask %-15s ",
393 							family->address_to_string(route.mask));
394 						break;
395 					case PREFER_OUTPUT_PREFIX_LENGTH:
396 						printf("/%u ",
397 							family->mask_to_prefix_length(route.mask));
398 						break;
399 				}
400 				if ((route.flags & RTF_GATEWAY) != 0) {
401 					printf("gateway %-15s ",
402 						family->address_to_string(route.gateway));
403 				}
404 			} else {
405 				printf("unknown family ");
406 			}
407 
408 			printf("%s", interface->ifr_name);
409 
410 			if (route.flags != 0) {
411 				const struct {
412 					int			value;
413 					const char	*name;
414 				} kFlags[] = {
415 					{RTF_DEFAULT, "default"},
416 					{RTF_REJECT, "reject"},
417 					{RTF_HOST, "host"},
418 					{RTF_LOCAL, "local"},
419 					{RTF_DYNAMIC, "dynamic"},
420 					{RTF_MODIFIED, "modified"},
421 				};
422 				bool first = true;
423 
424 				for (uint32 i = 0; i < sizeof(kFlags) / sizeof(kFlags[0]); i++) {
425 					if ((route.flags & kFlags[i].value) != 0) {
426 						if (first) {
427 							printf(", ");
428 							first = false;
429 						} else
430 							putchar(' ');
431 						printf(kFlags[i].name);
432 					}
433 				}
434 			}
435 
436 			putchar('\n');
437 		}
438 
439 		int32 addressSize = 0;
440 		if (route.destination != NULL)
441 			addressSize += route.destination->sa_len;
442 		if (route.mask != NULL)
443 			addressSize += route.mask->sa_len;
444 		if (route.gateway != NULL)
445 			addressSize += route.gateway->sa_len;
446 
447 		interface = (ifreq*)((addr_t)interface + IF_NAMESIZE + sizeof(route_entry)
448 			+ addressSize);
449 	}
450 
451 	free(buffer);
452 }
453 
454 
455 void
456 delete_route(int socket, const char *interface, route_entry &route)
457 {
458 	ifreq request;
459 	if (!prepare_request(request, interface))
460 		return;
461 
462 	request.ifr_route = route;
463 
464 	if (ioctl(socket, SIOCDELRT, &request, sizeof(request)) < 0) {
465 		fprintf(stderr, "%s: Could not delete route for %s: %s\n",
466 			kProgramName, interface, strerror(errno));
467 	}
468 }
469 
470 
471 void
472 add_route(int socket, const char *interface, route_entry &route)
473 {
474 	ifreq request;
475 	if (!prepare_request(request, interface))
476 		return;
477 
478 	route.flags |= RTF_STATIC;
479 	request.ifr_route = route;
480 
481 	if (ioctl(socket, SIOCADDRT, &request, sizeof(request)) < 0) {
482 		fprintf(stderr, "%s: Could not add route for %s: %s\n",
483 			kProgramName, interface, strerror(errno));
484 	}
485 }
486 
487 
488 void
489 get_route(int socket, route_entry &route)
490 {
491 	union {
492 		route_entry request;
493 		uint8 buffer[512];
494 	};
495 
496 	request = route;
497 
498 	if (ioctl(socket, SIOCGETRT, buffer, sizeof(buffer)) < 0) {
499 		fprintf(stderr, "%s: Could not get route: %s\n",
500 			kProgramName, strerror(errno));
501 		return;
502 	}
503 
504 	// find family
505 	const address_family *family = NULL;
506 	for (int32 i = 0; kFamilies[i].family >= 0; i++) {
507 		if (route.destination->sa_family == kFamilies[i].family) {
508 			family = &kFamilies[i];
509 			break;
510 		}
511 	}
512 
513 	if (family != NULL) {
514 		printf("%s", family->address_to_string(request.destination));
515 		switch (family->preferred_format) {
516 			case PREFER_OUTPUT_MASK:
517 				printf(" mask %s ",
518 					family->address_to_string(request.mask));
519 				break;
520 			case PREFER_OUTPUT_PREFIX_LENGTH:
521 				printf("/%u ",
522 					family->mask_to_prefix_length(request.mask));
523 				break;
524 		}
525 
526 		if (request.flags & RTF_GATEWAY)
527 			printf("gateway %s ", family->address_to_string(request.gateway));
528 
529 		printf("source %s\n", family->address_to_string(request.source));
530 	} else {
531 		printf("unknown family ");
532 	}
533 }
534 
535 
536 //	#pragma mark -
537 
538 
539 int
540 main(int argc, char** argv)
541 {
542 	if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
543 		usage(0);
544 
545 	int32 mode = RTM_LIST;
546 
547 	if (argc > 1) {
548 		if (!strcmp(argv[1], "delete")
549 			|| !strcmp(argv[1], "del")
550 			|| !strcmp(argv[1], "-d")) {
551 			// delete route
552 			if (argc < 3)
553 				usage(1);
554 
555 			mode = RTM_DELETE;
556 		} else if (!strcmp(argv[1], "add")
557 			|| !strcmp(argv[1], "-a")) {
558 			// add route
559 			if (argc < 3)
560 				usage(1);
561 
562 			mode = RTM_ADD;
563 		} else if (!strcmp(argv[1], "get")) {
564 			// get route for destination
565 			if (argc < 3)
566 				usage(1);
567 
568 			mode = RTM_GET;
569 		}
570 	}
571 
572 	int32 i = 2;
573 	int32 interfaceIndex = i;
574 	bool familySpecified = false;
575 	int32 familyIndex = 0;
576 	const char *interface = NULL;
577 	sockaddr_storage destination;
578 	sockaddr_storage mask;
579 	sockaddr_storage gateway;
580 	bool hasDestination = false, hasGateway = false, hasMask = false;
581 	bool defaultRoute = false;
582 
583 	route_entry route;
584 	memset(&route, 0, sizeof(route_entry));
585 
586 	while (i < argc && i < 5) {
587 		// try to parse address family
588 		if (i <= 3 && familySpecified == false
589 				&& get_address_family(argv[i], familyIndex)) {
590 			familySpecified = true;
591 			if (i == 2)
592 				interfaceIndex = -1;
593 		}
594 
595 		if (!strcmp(argv[i], "default")) {
596 			defaultRoute = true;
597 			route.flags = RTF_DEFAULT;
598 			i++;
599 			break;
600 		} else if (parse_address(familyIndex, argv[i], destination)) {
601 			hasDestination = true;
602 			i++;
603 			break;
604 		}
605 
606 		i++;
607 	}
608 
609 	if (!defaultRoute && !hasDestination && mode != RTM_LIST)
610 		usage(1);
611 
612 	if (i == 3)
613 		interfaceIndex = -1;
614 	if (interfaceIndex != -1 && interfaceIndex < argc)
615 		interface = argv[interfaceIndex];
616 
617 	if (i < argc && parse_address(familyIndex, argv[i], mask)) {
618 		hasMask = true;
619 		i++;
620 	}
621 
622 	// parse options and flags
623 
624 	while (i < argc) {
625 		if (!strcmp(argv[i], "gw") || !strcmp(argv[i], "gateway")) {
626 			if (!parse_address(familyIndex, argv[i + 1], gateway)) {
627 				fprintf(stderr, "%s: Option 'gw' needs valid address parameter\n",
628 					kProgramName);
629 				exit(1);
630 			}
631 			hasGateway = true;
632 			i++;
633 		} else if (!strcmp(argv[i], "nm") || !strcmp(argv[i], "netmask")) {
634 			if (hasMask) {
635 				fprintf(stderr, "%s: Netmask or prefix length is specified twice\n",
636 					kProgramName);
637 				exit(1);
638 			}
639 			if (!parse_address(familyIndex, argv[i + 1], mask)) {
640 				fprintf(stderr, "%s: Option 'netmask' needs valid address parameter\n",
641 					kProgramName);
642 				exit(1);
643 			}
644 			hasMask = true;
645 			i++;
646 		} else if (!strcmp(argv[i], "plen") || !strcmp(argv[i], "prefixlen")) {
647 			if (hasMask) {
648 				fprintf(stderr, "%s: Netmask or prefix length is specified twice\n",
649 					kProgramName);
650 				exit(1);
651 			}
652 			if (!prefix_length_to_mask(familyIndex, argv[i + 1], mask)) {
653 				fprintf(stderr, "%s: Option 'prefixlen' is invalid for this "
654 					"address family\n", kProgramName);
655 				exit(1);
656 			}
657 			hasMask = true;
658 			i++;
659 		} else if (!strcmp(argv[i], "mtu")) {
660 			route.mtu = argv[i + 1] ? strtol(argv[i + 1], NULL, 0) : 0;
661 			if (route.mtu <= 500) {
662 				fprintf(stderr, "%s: Option 'mtu' exptected valid max transfer unit size\n",
663 					kProgramName);
664 				exit(1);
665 			}
666 			i++;
667 		} else if (!strcmp(argv[i], "host")) {
668 			route.flags |= RTF_HOST;
669 		} else if (!strcmp(argv[i], "local")) {
670 			route.flags |= RTF_LOCAL;
671 		} else if (!strcmp(argv[i], "reject")) {
672 			route.flags |= RTF_REJECT;
673 		} else
674 			usage(1);
675 
676 		i++;
677 	}
678 
679 	if (hasDestination)
680 		route.destination = (sockaddr*)&destination;
681 	if (hasMask)
682 		route.mask = (sockaddr*)&mask;
683 	if (hasGateway) {
684 		route.gateway = (sockaddr*)&gateway;
685 		route.flags |= RTF_GATEWAY;
686 	}
687 
688 	// we need a socket to talk to the networking stack
689 	int socket = ::socket(kFamilies[familyIndex].family, SOCK_DGRAM, 0);
690 	if (socket < 0) {
691 		fprintf(stderr, "%s: The requested address family is not available.\n",
692 			kProgramName);
693 		return 1;
694 	}
695 
696 	switch (mode) {
697 		case RTM_ADD:
698 			if (interface == NULL) {
699 				fprintf(stderr, "%s: You need to specify an interface when adding a route.\n",
700 					kProgramName);
701 				usage(1);
702 			}
703 
704 			add_route(socket, interface, route);
705 			break;
706 		case RTM_DELETE:
707 			if (interface == NULL) {
708 				fprintf(stderr, "%s: You need to specify an interface when removing a route.\n",
709 					kProgramName);
710 				usage(1);
711 			}
712 
713 			delete_route(socket, interface, route);
714 			break;
715 
716 		case RTM_LIST:
717 			if (familySpecified)
718 				list_routes(socket, interface, route);
719 			else {
720 				for (int32 i = 0; kFamilies[i].family >= 0; i++) {
721 					int socket = ::socket(kFamilies[i].family, SOCK_DGRAM, 0);
722 					if (socket < 0)
723 						continue;
724 
725 					list_routes(socket, interface, route);
726 					close(socket);
727 				}
728 			}
729 			break;
730 
731 		case RTM_GET:
732 			get_route(socket, route);
733 			break;
734 	}
735 
736 	close(socket);
737 	return 0;
738 }
739 
740