blob: 43180296d3588e65ed65503303472d065f2f8044 [file] [log] [blame]
Eric Andersenec455952001-02-14 08:11:27 +00001/* route
2 *
3 * Similar to the standard Unix route, but with only the necessary
4 * parts for AF_INET
5 *
6 * Bjorn Wesen, Axis Communications AB
7 *
Eric Andersenb9408502001-09-05 19:32:00 +00008 * Author of the original route:
Eric Andersenec455952001-02-14 08:11:27 +00009 * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
10 * (derived from FvK's 'route.c 1.70 01/04/94')
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software
15 * Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
Eric Andersencd8c4362001-11-10 11:22:46 +000018 * $Id: route.c,v 1.14 2001/11/10 11:22:43 andersen Exp $
Eric Andersenec455952001-02-14 08:11:27 +000019 *
Eric Andersen68be2ab2001-02-14 19:26:39 +000020 * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
Eric Andersen26d53eb2001-03-07 06:33:01 +000021 * adjustments by Larry Doolittle <LRDoolittle@lbl.gov>
Eric Andersenec455952001-02-14 08:11:27 +000022 */
23
Eric Andersenec455952001-02-14 08:11:27 +000024#include <sys/types.h>
25#include <sys/ioctl.h>
Eric Andersencd8c4362001-11-10 11:22:46 +000026#include "inet_common.h"
Eric Andersenec455952001-02-14 08:11:27 +000027#include <net/route.h>
28#include <linux/param.h> // HZ
Eric Andersenec455952001-02-14 08:11:27 +000029#include <stdio.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <stdlib.h>
33#include <string.h>
34#include <getopt.h>
35#include <unistd.h>
36#include <ctype.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000037#include "busybox.h"
Eric Andersenec455952001-02-14 08:11:27 +000038
39#define _(x) x
40
41#define RTACTION_ADD 1
42#define RTACTION_DEL 2
43#define RTACTION_HELP 3
44#define RTACTION_FLUSH 4
45#define RTACTION_SHOW 5
46
47#define E_NOTFOUND 8
48#define E_SOCK 7
49#define E_LOOKUP 6
50#define E_VERSION 5
51#define E_USAGE 4
52#define E_OPTERR 3
53#define E_INTERN 2
54#define E_NOSUPP 1
55
Eric Andersenec455952001-02-14 08:11:27 +000056#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */
57#define HAVE_NEW_ADDRT 1
58#endif
59#ifdef RTF_IRTT /* route */
60#define HAVE_RTF_IRTT 1
61#endif
62#ifdef RTF_REJECT /* route */
63#define HAVE_RTF_REJECT 1
64#endif
65
66#if HAVE_NEW_ADDRT
67#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
68#define full_mask(x) (x)
69#else
70#define mask_in_addr(x) ((x).rt_genmask)
71#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
72#endif
73
Eric Andersencd8c4362001-11-10 11:22:46 +000074
75
Eric Andersenec455952001-02-14 08:11:27 +000076/* add or delete a route depending on action */
77
78static int
79INET_setroute(int action, int options, char **args)
80{
81 struct rtentry rt;
Eric Andersencd8c4362001-11-10 11:22:46 +000082 char target[128], gateway[128] = "NONE";
83 const char *netmask = bb_INET_default;
Eric Andersenec455952001-02-14 08:11:27 +000084 int xflag, isnet;
85 int skfd;
86
87 xflag = 0;
88
Eric Andersena3c84812001-08-23 22:05:33 +000089 if (*args == NULL)
90 show_usage();
Mark Whitley99806ad2001-02-15 23:00:48 +000091 if (strcmp(*args, "-net")==0) {
Eric Andersenec455952001-02-14 08:11:27 +000092 xflag = 1;
93 args++;
Mark Whitley99806ad2001-02-15 23:00:48 +000094 } else if (strcmp(*args, "-host")==0) {
Eric Andersenec455952001-02-14 08:11:27 +000095 xflag = 2;
96 args++;
97 }
Eric Andersencd8c4362001-11-10 11:22:46 +000098 if (*args == NULL)
99 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000100 safe_strncpy(target, *args++, (sizeof target));
101
102 /* Clean out the RTREQ structure. */
103 memset((char *) &rt, 0, sizeof(struct rtentry));
104
105
Eric Andersencd8c4362001-11-10 11:22:46 +0000106 if ((isnet = INET_resolve(target, (struct sockaddr_in *)&rt.rt_dst, xflag!=1)) < 0) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000107 error_msg(_("can't resolve %s"), target);
108 return EXIT_FAILURE; /* XXX change to E_something */
Eric Andersenec455952001-02-14 08:11:27 +0000109 }
110
111 switch (xflag) {
112 case 1:
113 isnet = 1;
114 break;
Eric Andersenb9408502001-09-05 19:32:00 +0000115
Eric Andersenec455952001-02-14 08:11:27 +0000116 case 2:
117 isnet = 0;
118 break;
Eric Andersenb9408502001-09-05 19:32:00 +0000119
Eric Andersenec455952001-02-14 08:11:27 +0000120 default:
121 break;
122 }
Eric Andersenb9408502001-09-05 19:32:00 +0000123
Eric Andersenec455952001-02-14 08:11:27 +0000124 /* Fill in the other fields. */
125 rt.rt_flags = (RTF_UP | RTF_HOST);
126 if (isnet)
127 rt.rt_flags &= ~RTF_HOST;
128
129 while (*args) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000130 if (strcmp(*args, "metric")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000131 int metric;
Eric Andersenb9408502001-09-05 19:32:00 +0000132
Eric Andersenec455952001-02-14 08:11:27 +0000133 args++;
134 if (!*args || !isdigit(**args))
Eric Andersen67991cf2001-02-14 21:23:06 +0000135 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000136 metric = atoi(*args);
137#if HAVE_NEW_ADDRT
138 rt.rt_metric = metric + 1;
139#else
Mark Whitley99806ad2001-02-15 23:00:48 +0000140 ENOSUPP("inet_setroute", "NEW_ADDRT (metric)"); /* XXX Fixme */
Eric Andersenec455952001-02-14 08:11:27 +0000141#endif
142 args++;
143 continue;
144 }
145
Mark Whitley99806ad2001-02-15 23:00:48 +0000146 if (strcmp(*args, "netmask")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000147 struct sockaddr mask;
Eric Andersenb9408502001-09-05 19:32:00 +0000148
Eric Andersenec455952001-02-14 08:11:27 +0000149 args++;
150 if (!*args || mask_in_addr(rt))
Eric Andersen67991cf2001-02-14 21:23:06 +0000151 show_usage();
Eric Andersencd8c4362001-11-10 11:22:46 +0000152 netmask = *args;
153 if ((isnet = INET_resolve(netmask, (struct sockaddr_in *)&mask, 0)) < 0) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000154 error_msg(_("can't resolve netmask %s"), netmask);
155 return E_LOOKUP;
Eric Andersenec455952001-02-14 08:11:27 +0000156 }
157 rt.rt_genmask = full_mask(mask);
158 args++;
159 continue;
160 }
161
Mark Whitley99806ad2001-02-15 23:00:48 +0000162 if (strcmp(*args, "gw")==0 || strcmp(*args, "gateway")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000163 args++;
164 if (!*args)
Eric Andersen67991cf2001-02-14 21:23:06 +0000165 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000166 if (rt.rt_flags & RTF_GATEWAY)
Eric Andersen67991cf2001-02-14 21:23:06 +0000167 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000168 safe_strncpy(gateway, *args, (sizeof gateway));
Eric Andersencd8c4362001-11-10 11:22:46 +0000169 if ((isnet = INET_resolve(gateway, (struct sockaddr_in *)&rt.rt_gateway, 1)) < 0) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000170 error_msg(_("can't resolve gw %s"), gateway);
171 return E_LOOKUP;
Eric Andersenec455952001-02-14 08:11:27 +0000172 }
173 if (isnet) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000174 error_msg(
175 _("%s: cannot use a NETWORK as gateway!"),
Eric Andersenec455952001-02-14 08:11:27 +0000176 gateway);
Mark Whitley99806ad2001-02-15 23:00:48 +0000177 return E_OPTERR;
Eric Andersenec455952001-02-14 08:11:27 +0000178 }
179 rt.rt_flags |= RTF_GATEWAY;
180 args++;
181 continue;
182 }
183
Mark Whitley99806ad2001-02-15 23:00:48 +0000184 if (strcmp(*args, "mss")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000185 args++;
186 rt.rt_flags |= RTF_MSS;
187 if (!*args)
Eric Andersen67991cf2001-02-14 21:23:06 +0000188 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000189 rt.rt_mss = atoi(*args);
190 args++;
191 if (rt.rt_mss < 64 || rt.rt_mss > 32768) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000192 error_msg(_("Invalid MSS."));
193 return E_OPTERR;
Eric Andersenec455952001-02-14 08:11:27 +0000194 }
195 continue;
196 }
197
Mark Whitley99806ad2001-02-15 23:00:48 +0000198 if (strcmp(*args, "window")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000199 args++;
200 if (!*args)
Eric Andersen67991cf2001-02-14 21:23:06 +0000201 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000202 rt.rt_flags |= RTF_WINDOW;
203 rt.rt_window = atoi(*args);
204 args++;
205 if (rt.rt_window < 128) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000206 error_msg(_("Invalid window."));
207 return E_OPTERR;
Eric Andersenec455952001-02-14 08:11:27 +0000208 }
209 continue;
210 }
211
Mark Whitley99806ad2001-02-15 23:00:48 +0000212 if (strcmp(*args, "irtt")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000213 args++;
214 if (!*args)
Eric Andersen67991cf2001-02-14 21:23:06 +0000215 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000216 args++;
217#if HAVE_RTF_IRTT
218 rt.rt_flags |= RTF_IRTT;
219 rt.rt_irtt = atoi(*(args - 1));
Eric Andersenb9408502001-09-05 19:32:00 +0000220 rt.rt_irtt *= (HZ / 100); /* FIXME */
221#if 0 /* FIXME: do we need to check anything of this? */
Eric Andersenec455952001-02-14 08:11:27 +0000222 if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000223 error_msg(_("Invalid initial rtt."));
224 return E_OPTERR;
Eric Andersenec455952001-02-14 08:11:27 +0000225 }
226#endif
227#else
Mark Whitley99806ad2001-02-15 23:00:48 +0000228 ENOSUPP("inet_setroute", "RTF_IRTT"); /* XXX Fixme */
Eric Andersenec455952001-02-14 08:11:27 +0000229#endif
230 continue;
231 }
232
Mark Whitley99806ad2001-02-15 23:00:48 +0000233 if (strcmp(*args, "reject")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000234 args++;
235#if HAVE_RTF_REJECT
236 rt.rt_flags |= RTF_REJECT;
237#else
Mark Whitley99806ad2001-02-15 23:00:48 +0000238 ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */
Eric Andersenec455952001-02-14 08:11:27 +0000239#endif
240 continue;
241 }
Mark Whitley99806ad2001-02-15 23:00:48 +0000242 if (strcmp(*args, "mod")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000243 args++;
244 rt.rt_flags |= RTF_MODIFIED;
245 continue;
246 }
Mark Whitley99806ad2001-02-15 23:00:48 +0000247 if (strcmp(*args, "dyn")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000248 args++;
249 rt.rt_flags |= RTF_DYNAMIC;
250 continue;
251 }
Mark Whitley99806ad2001-02-15 23:00:48 +0000252 if (strcmp(*args, "reinstate")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000253 args++;
254 rt.rt_flags |= RTF_REINSTATE;
255 continue;
256 }
Mark Whitley99806ad2001-02-15 23:00:48 +0000257 if (strcmp(*args, "device")==0 || strcmp(*args, "dev")==0) {
Eric Andersenec455952001-02-14 08:11:27 +0000258 args++;
259 if (rt.rt_dev || *args == NULL)
Eric Andersen67991cf2001-02-14 21:23:06 +0000260 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000261 rt.rt_dev = *args++;
262 continue;
263 }
264 /* nothing matches */
265 if (!rt.rt_dev) {
266 rt.rt_dev = *args++;
267 if (*args)
Eric Andersenb9408502001-09-05 19:32:00 +0000268 show_usage(); /* must be last to catch typos */
Mark Whitley99806ad2001-02-15 23:00:48 +0000269 } else {
Eric Andersen67991cf2001-02-14 21:23:06 +0000270 show_usage();
Mark Whitley99806ad2001-02-15 23:00:48 +0000271 }
Eric Andersenec455952001-02-14 08:11:27 +0000272 }
273
274#if HAVE_RTF_REJECT
275 if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev)
276 rt.rt_dev = "lo";
277#endif
278
279 /* sanity checks.. */
280 if (mask_in_addr(rt)) {
281 unsigned long mask = mask_in_addr(rt);
282 mask = ~ntohl(mask);
283 if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000284 error_msg(
285 _("netmask %.8x doesn't make sense with host route"),
Eric Andersenec455952001-02-14 08:11:27 +0000286 (unsigned int)mask);
Mark Whitley99806ad2001-02-15 23:00:48 +0000287 return E_OPTERR;
Eric Andersenec455952001-02-14 08:11:27 +0000288 }
289 if (mask & (mask + 1)) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000290 error_msg(_("bogus netmask %s"), netmask);
291 return E_OPTERR;
Eric Andersenec455952001-02-14 08:11:27 +0000292 }
293 mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
294 if (mask & ~mask_in_addr(rt)) {
Mark Whitley99806ad2001-02-15 23:00:48 +0000295 error_msg(_("netmask doesn't match route address"));
296 return E_OPTERR;
Eric Andersenec455952001-02-14 08:11:27 +0000297 }
298 }
299 /* Fill out netmask if still unset */
300 if ((action == RTACTION_ADD) && rt.rt_flags & RTF_HOST)
301 mask_in_addr(rt) = 0xffffffff;
302
303 /* Create a socket to the INET kernel. */
304 if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
305 perror("socket");
Mark Whitley99806ad2001-02-15 23:00:48 +0000306 return E_SOCK;
Eric Andersenec455952001-02-14 08:11:27 +0000307 }
308 /* Tell the kernel to accept this route. */
309 if (action == RTACTION_DEL) {
310 if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
311 perror("SIOCDELRT");
312 close(skfd);
Mark Whitley99806ad2001-02-15 23:00:48 +0000313 return E_SOCK;
Eric Andersenec455952001-02-14 08:11:27 +0000314 }
315 } else {
316 if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
317 perror("SIOCADDRT");
318 close(skfd);
Mark Whitley99806ad2001-02-15 23:00:48 +0000319 return E_SOCK;
Eric Andersenec455952001-02-14 08:11:27 +0000320 }
321 }
Eric Andersenb9408502001-09-05 19:32:00 +0000322
Eric Andersenec455952001-02-14 08:11:27 +0000323 /* Close the socket. */
324 (void) close(skfd);
Mark Whitley99806ad2001-02-15 23:00:48 +0000325 return EXIT_SUCCESS;
Eric Andersenec455952001-02-14 08:11:27 +0000326}
327
Eric Andersen863a3e12001-08-27 17:57:27 +0000328#ifndef RTF_UP
329/* Keep this in sync with /usr/src/linux/include/linux/route.h */
330#define RTF_UP 0x0001 /* route usable */
331#define RTF_GATEWAY 0x0002 /* destination is a gateway */
332#define RTF_HOST 0x0004 /* host entry (net otherwise) */
333#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
334#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
335#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
336#define RTF_MTU 0x0040 /* specific MTU for this route */
337#ifndef RTF_MSS
338#define RTF_MSS RTF_MTU /* Compatibility :-( */
339#endif
340#define RTF_WINDOW 0x0080 /* per route window clamping */
341#define RTF_IRTT 0x0100 /* Initial round trip time */
342#define RTF_REJECT 0x0200 /* Reject route */
343#endif
344
Eric Andersencd8c4362001-11-10 11:22:46 +0000345static void displayroutes(int noresolve)
Eric Andersen68be2ab2001-02-14 19:26:39 +0000346{
347 char buff[256];
348 int nl = 0 ;
349 struct in_addr dest;
350 struct in_addr gw;
351 struct in_addr mask;
352 int flgs, ref, use, metric;
Eric Andersenb9408502001-09-05 19:32:00 +0000353 char flags[64];
Eric Andersen68be2ab2001-02-14 19:26:39 +0000354 unsigned long int d,g,m;
355
356 char sdest[16], sgw[16];
357
Eric Andersenf1bbb222001-02-18 20:12:25 +0000358 FILE *fp = xfopen("/proc/net/route", "r");
Eric Andersen68be2ab2001-02-14 19:26:39 +0000359
Eric Andersencd8c4362001-11-10 11:22:46 +0000360 if(noresolve)
361 noresolve = 0x0fff;
362
Eric Andersen68be2ab2001-02-14 19:26:39 +0000363 while( fgets(buff, sizeof(buff), fp) != NULL ) {
364 if(nl) {
365 int ifl = 0;
Eric Andersencd8c4362001-11-10 11:22:46 +0000366 int numeric;
367 struct sockaddr_in s_addr;
368
Eric Andersen68be2ab2001-02-14 19:26:39 +0000369 while(buff[ifl]!=' ' && buff[ifl]!='\t' && buff[ifl]!='\0')
370 ifl++;
371 buff[ifl]=0; /* interface */
Eric Andersenb9408502001-09-05 19:32:00 +0000372 if(sscanf(buff+ifl+1, "%lx%lx%X%d%d%d%lx",
Eric Andersen68be2ab2001-02-14 19:26:39 +0000373 &d, &g, &flgs, &ref, &use, &metric, &m)!=7) {
374 error_msg_and_die( "Unsuported kernel route format\n");
375 }
Eric Andersen863a3e12001-08-27 17:57:27 +0000376 if(nl==1)
Eric Andersenb9408502001-09-05 19:32:00 +0000377 printf("Kernel IP routing table\n"
378 "Destination Gateway Genmask Flags Metric Ref Use Iface\n");
Mark Whitley825ae5a2001-02-15 23:31:40 +0000379
Eric Andersen68be2ab2001-02-14 19:26:39 +0000380 ifl = 0; /* parse flags */
Eric Andersenb9408502001-09-05 19:32:00 +0000381 if(flgs&RTF_UP) {
382 if(flgs&RTF_REJECT)
383 flags[ifl++]='!';
384 else
385 flags[ifl++]='U';
386 if(flgs&RTF_GATEWAY)
387 flags[ifl++]='G';
388 if(flgs&RTF_HOST)
389 flags[ifl++]='H';
390 if(flgs&RTF_REINSTATE)
391 flags[ifl++]='R';
392 if(flgs&RTF_DYNAMIC)
393 flags[ifl++]='D';
394 if(flgs&RTF_MODIFIED)
395 flags[ifl++]='M';
396 flags[ifl]=0;
397 dest.s_addr = d;
398 gw.s_addr = g;
399 mask.s_addr = m;
Eric Andersencd8c4362001-11-10 11:22:46 +0000400 memset(&s_addr, 0, sizeof(struct sockaddr_in));
401 s_addr.sin_family = AF_INET;
402 s_addr.sin_addr = dest;
403 numeric = noresolve | 0x8000; /* default instead of * */
404 INET_rresolve(sdest, sizeof(sdest), &s_addr, numeric, m);
405 numeric = noresolve | 0x4000; /* host instead of net */
406 s_addr.sin_addr = gw;
407 INET_rresolve(sgw, sizeof(sgw), &s_addr, numeric, m);
Eric Andersenb9408502001-09-05 19:32:00 +0000408 printf("%-16s%-16s%-16s%-6s%-6d %-2d %7d %s\n",
409 sdest, sgw,
410 inet_ntoa(mask),
411 flags, metric, ref, use, buff);
412 }
413 }
414 nl++;
415 }
Eric Andersen68be2ab2001-02-14 19:26:39 +0000416}
417
Eric Andersenec455952001-02-14 08:11:27 +0000418int route_main(int argc, char **argv)
419{
420 int what = 0;
421
422 argc--;
423 argv++;
424
Eric Andersencd8c4362001-11-10 11:22:46 +0000425 if (*argv == NULL || (*(argv+1)==NULL && strcmp(*argv, "-n")==0)) {
426 displayroutes(*argv != NULL);
Eric Andersen26d53eb2001-03-07 06:33:01 +0000427 return EXIT_SUCCESS;
Eric Andersenec455952001-02-14 08:11:27 +0000428 } else {
429 /* check verb */
Mark Whitley99806ad2001-02-15 23:00:48 +0000430 if (strcmp(*argv, "add")==0)
Eric Andersenec455952001-02-14 08:11:27 +0000431 what = RTACTION_ADD;
Mark Whitley99806ad2001-02-15 23:00:48 +0000432 else if (strcmp(*argv, "del")==0 || strcmp(*argv, "delete")==0)
Eric Andersenec455952001-02-14 08:11:27 +0000433 what = RTACTION_DEL;
Mark Whitley99806ad2001-02-15 23:00:48 +0000434 else if (strcmp(*argv, "flush")==0)
Eric Andersenec455952001-02-14 08:11:27 +0000435 what = RTACTION_FLUSH;
436 else
Eric Andersen67991cf2001-02-14 21:23:06 +0000437 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000438 }
439
Mark Whitley99806ad2001-02-15 23:00:48 +0000440 return INET_setroute(what, 0, ++argv);
Eric Andersenec455952001-02-14 08:11:27 +0000441}