vfs: check userland buffers before reading them.
[haiku.git] / src / bin / network / route / route.cpp
blobca2c310718e29a036fbe6b67cfd4a8bccf822bd8
1 /*
2 * Copyright 2006-2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Alexander von Gluck <kallisti5@unixzen.com>
8 */
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>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
25 #include <NetworkAddress.h>
28 extern const char* __progname;
29 const char* kProgramName = __progname;
32 enum modes {
33 RTM_LIST = 0,
34 RTM_DELETE,
35 RTM_ADD,
36 RTM_GET,
38 // TODO:
39 RTM_CHANGE,
40 RTM_FLUSH,
43 enum preferredPrefixFormat {
44 PREFIX_PREFER_NETMASK = 0,
45 PREFIX_PREFER_CIDR,
48 struct address_family {
49 int family;
50 const char* name;
51 const char* identifiers[4];
52 int maxAddressLength;
53 int preferredPrefixFormat;
56 static const address_family kFamilies[] = {
58 AF_INET,
59 "IPv4",
60 {"AF_INET", "inet", "ipv4", NULL},
61 15,
62 PREFIX_PREFER_NETMASK,
65 AF_INET6,
66 "IPv6",
67 {"AF_INET6", "inet6", "ipv6", NULL},
68 39,
69 PREFIX_PREFER_CIDR,
71 { -1, NULL, {NULL}, -1, -1 }
75 void
76 usage(int status)
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);
94 exit(status);
98 bool
99 prepare_request(struct ifreq& request, const char* name)
101 if (strlen(name) >= IF_NAMESIZE) {
102 fprintf(stderr, "%s: interface name \"%s\" is too long.\n",
103 kProgramName, name);
104 return false;
107 strcpy(request.ifr_name, name);
108 return true;
112 bool
113 get_address_family(const char* argument, int32& familyIndex)
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;
125 // defaults to AF_INET
126 familyIndex = 0;
127 return false;
131 bool
132 parse_address(int32 familyIndex, const char* argument, BNetworkAddress& address)
134 if (argument == NULL)
135 return false;
137 return address.SetTo(kFamilies[familyIndex].family, argument,
138 (uint16)0, B_NO_ADDRESS_RESOLUTION) == B_OK;
142 bool
143 prefix_length_to_mask(int32 familyIndex, const char* argument,
144 BNetworkAddress& mask)
146 if (argument == NULL)
147 return false;
149 char* end;
150 uint32 prefixLength = strtoul(argument, &end, 10);
151 if (end == argument)
152 return false;
154 return mask.SetToMask(kFamilies[familyIndex].family, prefixLength) == B_OK;
158 // #pragma mark -
161 void
162 list_routes(int socket, const char *interfaceName, route_entry &route)
164 // get a list of all routes
166 ifconf config;
167 config.ifc_len = sizeof(config.ifc_value);
168 if (ioctl(socket, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0)
169 return;
171 uint32 size = (uint32)config.ifc_value;
172 if (size == 0)
173 return;
175 void *buffer = malloc(size);
176 if (buffer == NULL) {
177 fprintf(stderr, "%s: Out of memory.\n", kProgramName);
178 return;
181 config.ifc_len = size;
182 config.ifc_buf = buffer;
183 if (ioctl(socket, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0)
184 return;
186 ifreq *interface = (ifreq*)buffer;
187 ifreq *end = (ifreq*)((uint8*)buffer + size);
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;
200 int addressLength = family->maxAddressLength;
202 printf("%s routing table:\n", family->name);
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");
212 while (interface < end) {
213 route_entry& route = interface->ifr_route;
215 // apply filters
216 if (interfaceName == NULL
217 || !strcmp(interfaceName, interface->ifr_name)) {
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());
232 } else {
233 if (family->preferredPrefixFormat
234 == PREFIX_PREFER_NETMASK) {
235 printf(" %*s ", addressLength, "-");
236 } else
237 printf(" ");
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 ");
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"},
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('-');
269 printf(" ");
270 } else
271 printf("------ ");
273 printf("%s", interface->ifr_name);
274 putchar('\n');
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;
285 interface = (ifreq*)((addr_t)interface + IF_NAMESIZE
286 + sizeof(route_entry) + addressSize);
289 putchar('\n');
290 free(buffer);
294 void
295 delete_route(int socket, const char *interface, route_entry &route)
297 ifreq request;
298 if (!prepare_request(request, interface))
299 return;
301 request.ifr_route = route;
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));
310 void
311 add_route(int socket, const char *interface, route_entry &route)
313 ifreq request;
314 if (!prepare_request(request, interface))
315 return;
317 route.flags |= RTF_STATIC;
318 request.ifr_route = route;
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));
327 void
328 get_route(int socket, route_entry &route)
330 union {
331 route_entry request;
332 uint8 buffer[512];
335 request = route;
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;
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;
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());
358 BNetworkAddress gateway(*request.gateway);
359 if (request.flags & RTF_GATEWAY)
360 printf("gateway %s ", gateway.ToString().String());
362 BNetworkAddress source(*request.source);
363 printf("source %s\n", source.ToString().String());
364 } else {
365 printf("unknown family ");
370 // #pragma mark -
374 main(int argc, char** argv)
376 if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
377 usage(0);
379 int32 mode = RTM_LIST;
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);
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);
396 mode = RTM_ADD;
397 } else if (!strcmp(argv[1], "get")) {
398 // get route for destination
399 if (argc < 3)
400 usage(1);
402 mode = RTM_GET;
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;
416 route_entry route;
417 memset(&route, 0, sizeof(route_entry));
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;
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;
438 i++;
441 if (!defaultRoute && destination.IsEmpty() && mode != RTM_LIST)
442 usage(1);
444 if (i == 3)
445 interfaceIndex = -1;
446 if (interfaceIndex != -1 && interfaceIndex < argc)
447 interface = argv[interfaceIndex];
449 if (i < argc && parse_address(familyIndex, argv[i], mask))
450 i++;
452 // parse options and flags
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);
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);
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);
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);
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);
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);
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);
504 i++;
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;
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;
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);
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);
541 delete_route(socket, interface, route);
542 break;
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;
553 list_routes(socket, interface, route);
554 close(socket);
557 break;
559 case RTM_GET:
560 get_route(socket, route);
561 break;
564 close(socket);
565 return 0;