busybox/networking/route.c
Eric Andersen cd8c436d81 Latest patch from vodz:
-- reverse resolve network name and cache in route and ifconfig
	applets, fix print nslookup server name if compile without
	uClibc, fix route crashe 'route add', fix warnings compile
	networking and pwd_grp applets
2001-11-10 11:22:46 +00:00

442 lines
10 KiB
C

/* route
*
* Similar to the standard Unix route, but with only the necessary
* parts for AF_INET
*
* Bjorn Wesen, Axis Communications AB
*
* Author of the original route:
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
* (derived from FvK's 'route.c 1.70 01/04/94')
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* $Id: route.c,v 1.14 2001/11/10 11:22:43 andersen Exp $
*
* displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
* adjustments by Larry Doolittle <LRDoolittle@lbl.gov>
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include "inet_common.h"
#include <net/route.h>
#include <linux/param.h> // HZ
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <ctype.h>
#include "busybox.h"
#define _(x) x
#define RTACTION_ADD 1
#define RTACTION_DEL 2
#define RTACTION_HELP 3
#define RTACTION_FLUSH 4
#define RTACTION_SHOW 5
#define E_NOTFOUND 8
#define E_SOCK 7
#define E_LOOKUP 6
#define E_VERSION 5
#define E_USAGE 4
#define E_OPTERR 3
#define E_INTERN 2
#define E_NOSUPP 1
#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */
#define HAVE_NEW_ADDRT 1
#endif
#ifdef RTF_IRTT /* route */
#define HAVE_RTF_IRTT 1
#endif
#ifdef RTF_REJECT /* route */
#define HAVE_RTF_REJECT 1
#endif
#if HAVE_NEW_ADDRT
#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
#define full_mask(x) (x)
#else
#define mask_in_addr(x) ((x).rt_genmask)
#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
#endif
/* add or delete a route depending on action */
static int
INET_setroute(int action, int options, char **args)
{
struct rtentry rt;
char target[128], gateway[128] = "NONE";
const char *netmask = bb_INET_default;
int xflag, isnet;
int skfd;
xflag = 0;
if (*args == NULL)
show_usage();
if (strcmp(*args, "-net")==0) {
xflag = 1;
args++;
} else if (strcmp(*args, "-host")==0) {
xflag = 2;
args++;
}
if (*args == NULL)
show_usage();
safe_strncpy(target, *args++, (sizeof target));
/* Clean out the RTREQ structure. */
memset((char *) &rt, 0, sizeof(struct rtentry));
if ((isnet = INET_resolve(target, (struct sockaddr_in *)&rt.rt_dst, xflag!=1)) < 0) {
error_msg(_("can't resolve %s"), target);
return EXIT_FAILURE; /* XXX change to E_something */
}
switch (xflag) {
case 1:
isnet = 1;
break;
case 2:
isnet = 0;
break;
default:
break;
}
/* Fill in the other fields. */
rt.rt_flags = (RTF_UP | RTF_HOST);
if (isnet)
rt.rt_flags &= ~RTF_HOST;
while (*args) {
if (strcmp(*args, "metric")==0) {
int metric;
args++;
if (!*args || !isdigit(**args))
show_usage();
metric = atoi(*args);
#if HAVE_NEW_ADDRT
rt.rt_metric = metric + 1;
#else
ENOSUPP("inet_setroute", "NEW_ADDRT (metric)"); /* XXX Fixme */
#endif
args++;
continue;
}
if (strcmp(*args, "netmask")==0) {
struct sockaddr mask;
args++;
if (!*args || mask_in_addr(rt))
show_usage();
netmask = *args;
if ((isnet = INET_resolve(netmask, (struct sockaddr_in *)&mask, 0)) < 0) {
error_msg(_("can't resolve netmask %s"), netmask);
return E_LOOKUP;
}
rt.rt_genmask = full_mask(mask);
args++;
continue;
}
if (strcmp(*args, "gw")==0 || strcmp(*args, "gateway")==0) {
args++;
if (!*args)
show_usage();
if (rt.rt_flags & RTF_GATEWAY)
show_usage();
safe_strncpy(gateway, *args, (sizeof gateway));
if ((isnet = INET_resolve(gateway, (struct sockaddr_in *)&rt.rt_gateway, 1)) < 0) {
error_msg(_("can't resolve gw %s"), gateway);
return E_LOOKUP;
}
if (isnet) {
error_msg(
_("%s: cannot use a NETWORK as gateway!"),
gateway);
return E_OPTERR;
}
rt.rt_flags |= RTF_GATEWAY;
args++;
continue;
}
if (strcmp(*args, "mss")==0) {
args++;
rt.rt_flags |= RTF_MSS;
if (!*args)
show_usage();
rt.rt_mss = atoi(*args);
args++;
if (rt.rt_mss < 64 || rt.rt_mss > 32768) {
error_msg(_("Invalid MSS."));
return E_OPTERR;
}
continue;
}
if (strcmp(*args, "window")==0) {
args++;
if (!*args)
show_usage();
rt.rt_flags |= RTF_WINDOW;
rt.rt_window = atoi(*args);
args++;
if (rt.rt_window < 128) {
error_msg(_("Invalid window."));
return E_OPTERR;
}
continue;
}
if (strcmp(*args, "irtt")==0) {
args++;
if (!*args)
show_usage();
args++;
#if HAVE_RTF_IRTT
rt.rt_flags |= RTF_IRTT;
rt.rt_irtt = atoi(*(args - 1));
rt.rt_irtt *= (HZ / 100); /* FIXME */
#if 0 /* FIXME: do we need to check anything of this? */
if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
error_msg(_("Invalid initial rtt."));
return E_OPTERR;
}
#endif
#else
ENOSUPP("inet_setroute", "RTF_IRTT"); /* XXX Fixme */
#endif
continue;
}
if (strcmp(*args, "reject")==0) {
args++;
#if HAVE_RTF_REJECT
rt.rt_flags |= RTF_REJECT;
#else
ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */
#endif
continue;
}
if (strcmp(*args, "mod")==0) {
args++;
rt.rt_flags |= RTF_MODIFIED;
continue;
}
if (strcmp(*args, "dyn")==0) {
args++;
rt.rt_flags |= RTF_DYNAMIC;
continue;
}
if (strcmp(*args, "reinstate")==0) {
args++;
rt.rt_flags |= RTF_REINSTATE;
continue;
}
if (strcmp(*args, "device")==0 || strcmp(*args, "dev")==0) {
args++;
if (rt.rt_dev || *args == NULL)
show_usage();
rt.rt_dev = *args++;
continue;
}
/* nothing matches */
if (!rt.rt_dev) {
rt.rt_dev = *args++;
if (*args)
show_usage(); /* must be last to catch typos */
} else {
show_usage();
}
}
#if HAVE_RTF_REJECT
if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev)
rt.rt_dev = "lo";
#endif
/* sanity checks.. */
if (mask_in_addr(rt)) {
unsigned long mask = mask_in_addr(rt);
mask = ~ntohl(mask);
if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
error_msg(
_("netmask %.8x doesn't make sense with host route"),
(unsigned int)mask);
return E_OPTERR;
}
if (mask & (mask + 1)) {
error_msg(_("bogus netmask %s"), netmask);
return E_OPTERR;
}
mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
if (mask & ~mask_in_addr(rt)) {
error_msg(_("netmask doesn't match route address"));
return E_OPTERR;
}
}
/* Fill out netmask if still unset */
if ((action == RTACTION_ADD) && rt.rt_flags & RTF_HOST)
mask_in_addr(rt) = 0xffffffff;
/* Create a socket to the INET kernel. */
if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
return E_SOCK;
}
/* 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 EXIT_SUCCESS;
}
#ifndef RTF_UP
/* Keep this in sync with /usr/src/linux/include/linux/route.h */
#define RTF_UP 0x0001 /* route usable */
#define RTF_GATEWAY 0x0002 /* destination is a gateway */
#define RTF_HOST 0x0004 /* host entry (net otherwise) */
#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
#define RTF_MTU 0x0040 /* specific MTU for this route */
#ifndef RTF_MSS
#define RTF_MSS RTF_MTU /* Compatibility :-( */
#endif
#define RTF_WINDOW 0x0080 /* per route window clamping */
#define RTF_IRTT 0x0100 /* Initial round trip time */
#define RTF_REJECT 0x0200 /* Reject route */
#endif
static void displayroutes(int noresolve)
{
char buff[256];
int nl = 0 ;
struct in_addr dest;
struct in_addr gw;
struct in_addr mask;
int flgs, ref, use, metric;
char flags[64];
unsigned long int d,g,m;
char sdest[16], sgw[16];
FILE *fp = xfopen("/proc/net/route", "r");
if(noresolve)
noresolve = 0x0fff;
while( fgets(buff, sizeof(buff), fp) != NULL ) {
if(nl) {
int ifl = 0;
int numeric;
struct sockaddr_in s_addr;
while(buff[ifl]!=' ' && buff[ifl]!='\t' && buff[ifl]!='\0')
ifl++;
buff[ifl]=0; /* interface */
if(sscanf(buff+ifl+1, "%lx%lx%X%d%d%d%lx",
&d, &g, &flgs, &ref, &use, &metric, &m)!=7) {
error_msg_and_die( "Unsuported kernel route format\n");
}
if(nl==1)
printf("Kernel IP routing table\n"
"Destination Gateway Genmask Flags Metric Ref Use Iface\n");
ifl = 0; /* parse flags */
if(flgs&RTF_UP) {
if(flgs&RTF_REJECT)
flags[ifl++]='!';
else
flags[ifl++]='U';
if(flgs&RTF_GATEWAY)
flags[ifl++]='G';
if(flgs&RTF_HOST)
flags[ifl++]='H';
if(flgs&RTF_REINSTATE)
flags[ifl++]='R';
if(flgs&RTF_DYNAMIC)
flags[ifl++]='D';
if(flgs&RTF_MODIFIED)
flags[ifl++]='M';
flags[ifl]=0;
dest.s_addr = d;
gw.s_addr = g;
mask.s_addr = m;
memset(&s_addr, 0, sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
s_addr.sin_addr = dest;
numeric = noresolve | 0x8000; /* default instead of * */
INET_rresolve(sdest, sizeof(sdest), &s_addr, numeric, m);
numeric = noresolve | 0x4000; /* host instead of net */
s_addr.sin_addr = gw;
INET_rresolve(sgw, sizeof(sgw), &s_addr, numeric, m);
printf("%-16s%-16s%-16s%-6s%-6d %-2d %7d %s\n",
sdest, sgw,
inet_ntoa(mask),
flags, metric, ref, use, buff);
}
}
nl++;
}
}
int route_main(int argc, char **argv)
{
int what = 0;
argc--;
argv++;
if (*argv == NULL || (*(argv+1)==NULL && strcmp(*argv, "-n")==0)) {
displayroutes(*argv != NULL);
return EXIT_SUCCESS;
} else {
/* check verb */
if (strcmp(*argv, "add")==0)
what = RTACTION_ADD;
else if (strcmp(*argv, "del")==0 || strcmp(*argv, "delete")==0)
what = RTACTION_DEL;
else if (strcmp(*argv, "flush")==0)
what = RTACTION_FLUSH;
else
show_usage();
}
return INET_setroute(what, 0, ++argv);
}