summaryrefslogtreecommitdiff
path: root/networking/route.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/route.c')
-rw-r--r--networking/route.c243
1 files changed, 240 insertions, 3 deletions
diff --git a/networking/route.c b/networking/route.c
index 26162ee..76e76b4 100644
--- a/networking/route.c
+++ b/networking/route.c
@@ -1,7 +1,7 @@
/* route
*
* Similar to the standard Unix route, but with only the necessary
- * parts for AF_INET
+ * parts for AF_INET and AF_INET6
*
* Bjorn Wesen, Axis Communications AB
*
@@ -15,16 +15,19 @@
* Foundation; either version 2 of the License, or (at
* your option) any later version.
*
- * $Id: route.c,v 1.16 2002/05/16 19:14:15 sandman Exp $
+ * $Id: route.c,v 1.17 2002/07/03 11:46:34 andersen Exp $
*
* displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
* adjustments by Larry Doolittle <LRDoolittle@lbl.gov>
+ *
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include "inet_common.h"
#include <net/route.h>
+#include <net/if.h>
#include <linux/param.h> // HZ
#include <stdio.h>
#include <errno.h>
@@ -325,6 +328,139 @@ INET_setroute(int action, int options, char **args)
return EXIT_SUCCESS;
}
+#if CONFIG_FEATURE_IPV6
+static int INET6_setroute(int action, int options, char **args)
+{
+ struct in6_rtmsg rt;
+ struct ifreq ifr;
+ struct sockaddr_in6 sa6;
+ char target[128], gateway[128] = "NONE";
+ int metric, prefix_len;
+ char *devname = NULL;
+ char *cp;
+ int skfd;
+
+ if (*args == NULL)
+ show_usage();
+
+ strcpy(target, *args++);
+ if (!strcmp(target, "default")) {
+ prefix_len = 0;
+ memset(&sa6, 0, sizeof(sa6));
+ } else {
+ if ((cp = strchr(target, '/'))) {
+ prefix_len = atol(cp + 1);
+ if ((prefix_len < 0) || (prefix_len > 128))
+ show_usage();
+ *cp = 0;
+ } else {
+ prefix_len = 128;
+ }
+ if (INET6_resolve(target, (struct sockaddr_in6 *)&sa6) < 0) {
+ error_msg(_("can't resolve %s"), target);
+ return EXIT_FAILURE; /* XXX change to E_something */
+ }
+ }
+
+ /* Clean out the RTREQ structure. */
+ memset((char *) &rt, 0, sizeof(struct in6_rtmsg));
+
+ memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
+
+ /* Fill in the other fields. */
+ rt.rtmsg_flags = RTF_UP;
+ if (prefix_len == 128)
+ rt.rtmsg_flags |= RTF_HOST;
+ rt.rtmsg_metric = 1;
+ rt.rtmsg_dst_len = prefix_len;
+
+ while (*args) {
+ if (!strcmp(*args, "metric")) {
+
+ args++;
+ if (!*args || !isdigit(**args))
+ show_usage();
+ metric = atoi(*args);
+ rt.rtmsg_metric = metric;
+ args++;
+ continue;
+ }
+ if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) {
+ args++;
+ if (!*args)
+ show_usage();
+ if (rt.rtmsg_flags & RTF_GATEWAY)
+ show_usage();
+ strcpy(gateway, *args);
+ if (INET6_resolve(gateway, (struct sockaddr_in6 *)&sa6) < 0) {
+ error_msg(_("can't resolve gw %s"), gateway);
+ return (E_LOOKUP);
+ }
+ memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ rt.rtmsg_flags |= RTF_GATEWAY;
+ args++;
+ continue;
+ }
+ if (!strcmp(*args, "mod")) {
+ args++;
+ rt.rtmsg_flags |= RTF_MODIFIED;
+ continue;
+ }
+ if (!strcmp(*args, "dyn")) {
+ args++;
+ rt.rtmsg_flags |= RTF_DYNAMIC;
+ continue;
+ }
+ if (!strcmp(*args, "device") || !strcmp(*args, "dev")) {
+ args++;
+ if (!*args)
+ show_usage();
+ } else if (args[1])
+ show_usage();
+
+ devname = *args;
+ args++;
+ }
+
+ /* Create a socket to the INET6 kernel. */
+ if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return (E_SOCK);
+ }
+ if (devname) {
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, devname);
+
+ if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
+ perror("SIOGIFINDEX");
+ return (E_SOCK);
+ }
+ rt.rtmsg_ifindex = ifr.ifr_ifindex;
+ } else
+ rt.rtmsg_ifindex = 0;
+
+ /* Tell the kernel to accept this route. */
+ if (action == RTACTION_DEL) {
+ if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
+ perror("SIOCDELRT");
+ close(skfd);
+ return (E_SOCK);
+ }
+ } else {
+ if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
+ perror("SIOCADDRT");
+ close(skfd);
+ return (E_SOCK);
+ }
+ }
+
+ /* Close the socket. */
+ (void) close(skfd);
+ return (0);
+}
+#endif
+
#ifndef RTF_UP
/* Keep this in sync with /usr/src/linux/include/linux/route.h */
#define RTF_UP 0x0001 /* route usable */
@@ -418,17 +554,103 @@ void displayroutes(int noresolve, int netstatfmt)
}
}
+#if CONFIG_FEATURE_IPV6
+static void INET6_displayroutes(int noresolve)
+{
+ char buff[256];
+ char iface[16], flags[16];
+ char addr6[128], naddr6[128];
+ struct sockaddr_in6 saddr6, snaddr6;
+ int iflags, metric, refcnt, use, prefix_len, slen;
+ int numeric;
+
+ char addr6p[8][5], saddr6p[8][5], naddr6p[8][5];
+
+ FILE *fp = xfopen("/proc/net/ipv6_route", "r");
+ flags[0]='U';
+
+ if(noresolve)
+ noresolve = 0x0fff;
+ numeric = noresolve | 0x8000; /* default instead of * */
+
+ printf("Kernel IPv6 routing table\n"
+ "Destination "
+ "Next Hop "
+ "Flags Metric Ref Use Iface\n");
+
+ while( fgets(buff, sizeof(buff), fp) != NULL ) {
+ int ifl;
+
+ if(sscanf(buff, "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
+ "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
+ "%4s%4s%4s%4s%4s%4s%4s%4s %08x %08x %08x %08x %s\n",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+ addr6p[4], addr6p[5], addr6p[6], addr6p[7],
+ &prefix_len,
+ saddr6p[0], saddr6p[1], saddr6p[2], saddr6p[3],
+ saddr6p[4], saddr6p[5], saddr6p[6], saddr6p[7],
+ &slen,
+ naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
+ naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7],
+ &metric, &use, &refcnt, &iflags, iface)!=31) {
+ error_msg_and_die( "Unsuported kernel route format\n");
+ }
+
+ ifl = 1; /* parse flags */
+ if (!(iflags & RTF_UP))
+ continue;
+ if (iflags & RTF_GATEWAY)
+ flags[ifl++]='G';
+ if (iflags & RTF_HOST)
+ flags[ifl++]='H';
+ if (iflags & RTF_DEFAULT)
+ flags[ifl++]='D';
+ if (iflags & RTF_ADDRCONF)
+ flags[ifl++]='A';
+ if (iflags & RTF_CACHE)
+ flags[ifl++]='C';
+ flags[ifl]=0;
+
+ /* Fetch and resolve the target address. */
+ snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+ addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &saddr6.sin6_addr);
+ saddr6.sin6_family=AF_INET6;
+
+ INET6_rresolve(addr6, sizeof(addr6), (struct sockaddr_in6 *) &saddr6, numeric);
+ snprintf(addr6, sizeof(addr6), "%s/%d", addr6, prefix_len);
+
+ /* Fetch and resolve the nexthop address. */
+ snprintf(naddr6, sizeof(naddr6), "%s:%s:%s:%s:%s:%s:%s:%s",
+ naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
+ naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7]);
+ inet_pton(AF_INET6, naddr6, (struct sockaddr *) &snaddr6.sin6_addr);
+ snaddr6.sin6_family=AF_INET6;
+
+ INET6_rresolve(naddr6, sizeof(naddr6), (struct sockaddr_in6 *) &snaddr6, numeric);
+
+ /* Print the info. */
+ printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
+ addr6, naddr6, flags, metric, refcnt, use, iface);
+ }
+}
+#endif
+
int route_main(int argc, char **argv)
{
int opt;
int what = 0;
+#if CONFIG_FEATURE_IPV6
+ int af=AF_INET;
+#endif
if ( !argv [1] || ( argv [1][0] == '-' )) {
/* check options */
int noresolve = 0;
int extended = 0;
- while ((opt = getopt(argc, argv, "ne")) > 0) {
+ while ((opt = getopt(argc, argv, "A:ne")) > 0) {
switch (opt) {
case 'n':
noresolve = 1;
@@ -436,11 +658,22 @@ int route_main(int argc, char **argv)
case 'e':
extended = 1;
break;
+ case 'A':
+#if CONFIG_FEATURE_IPV6
+ if (strcmp(optarg, "inet6")==0)
+ af=AF_INET6;
+ break;
+#endif
default:
show_usage ( );
}
}
+#if CONFIG_FEATURE_IPV6
+ if (af==AF_INET6)
+ INET6_displayroutes(*argv != NULL);
+ else
+#endif
displayroutes ( noresolve, extended );
return EXIT_SUCCESS;
} else {
@@ -455,5 +688,9 @@ int route_main(int argc, char **argv)
show_usage();
}
+#if CONFIG_FEATURE_IPV6
+ if (af==AF_INET6)
+ return INET6_setroute(what, 0, argv+2);
+#endif
return INET_setroute(what, 0, argv+2 );
}