blob: 3fad81a01c3120fc8439db3d2a96ba409e9d20df [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 *
8 * Author of the original route:
9 * 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 Andersen67991cf2001-02-14 21:23:06 +000018 * $Id: route.c,v 1.3 2001/02/14 21:23:06 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 Andersenec455952001-02-14 08:11:27 +000021 */
22
23#include "busybox.h"
24#include <sys/types.h>
25#include <sys/ioctl.h>
26#include <sys/socket.h>
27#include <net/route.h>
28#include <linux/param.h> // HZ
29#include <netinet/in.h>
30#include <arpa/inet.h>
31#include <stdio.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <stdlib.h>
35#include <string.h>
36#include <getopt.h>
37#include <unistd.h>
38#include <ctype.h>
39
40#define _(x) x
41
42#define RTACTION_ADD 1
43#define RTACTION_DEL 2
44#define RTACTION_HELP 3
45#define RTACTION_FLUSH 4
46#define RTACTION_SHOW 5
47
48#define E_NOTFOUND 8
49#define E_SOCK 7
50#define E_LOOKUP 6
51#define E_VERSION 5
52#define E_USAGE 4
53#define E_OPTERR 3
54#define E_INTERN 2
55#define E_NOSUPP 1
56
57/* resolve XXX.YYY.ZZZ.QQQ -> binary */
58
59static int
60INET_resolve(char *name, struct sockaddr *sa)
61{
62 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
63
64 sin->sin_family = AF_INET;
65 sin->sin_port = 0;
66
67 /* Default is special, meaning 0.0.0.0. */
68 if (!strcmp(name, "default")) {
69 sin->sin_addr.s_addr = INADDR_ANY;
70 return (1);
71 }
72 /* Look to see if it's a dotted quad. */
73 if (inet_aton(name, &sin->sin_addr)) {
74 return 0;
75 }
76 /* guess not.. */
77 return -1;
78}
79
80#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */
81#define HAVE_NEW_ADDRT 1
82#endif
83#ifdef RTF_IRTT /* route */
84#define HAVE_RTF_IRTT 1
85#endif
86#ifdef RTF_REJECT /* route */
87#define HAVE_RTF_REJECT 1
88#endif
89
90#if HAVE_NEW_ADDRT
91#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
92#define full_mask(x) (x)
93#else
94#define mask_in_addr(x) ((x).rt_genmask)
95#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
96#endif
97
98/* add or delete a route depending on action */
99
100static int
101INET_setroute(int action, int options, char **args)
102{
103 struct rtentry rt;
104 char target[128], gateway[128] = "NONE", netmask[128] = "default";
105 int xflag, isnet;
106 int skfd;
107
108 xflag = 0;
109
110 if (!strcmp(*args, "-net")) {
111 xflag = 1;
112 args++;
113 } else if (!strcmp(*args, "-host")) {
114 xflag = 2;
115 args++;
116 }
117 if (*args == NULL)
Eric Andersen67991cf2001-02-14 21:23:06 +0000118 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000119
120 safe_strncpy(target, *args++, (sizeof target));
121
122 /* Clean out the RTREQ structure. */
123 memset((char *) &rt, 0, sizeof(struct rtentry));
124
125
126 if ((isnet = INET_resolve(target, &rt.rt_dst)) < 0) {
127 fprintf(stderr, "cant resolve %s\n", target);
128 return (1);
129 }
130
131 switch (xflag) {
132 case 1:
133 isnet = 1;
134 break;
135
136 case 2:
137 isnet = 0;
138 break;
139
140 default:
141 break;
142 }
143
144 /* Fill in the other fields. */
145 rt.rt_flags = (RTF_UP | RTF_HOST);
146 if (isnet)
147 rt.rt_flags &= ~RTF_HOST;
148
149 while (*args) {
150 if (!strcmp(*args, "metric")) {
151 int metric;
152
153 args++;
154 if (!*args || !isdigit(**args))
Eric Andersen67991cf2001-02-14 21:23:06 +0000155 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000156 metric = atoi(*args);
157#if HAVE_NEW_ADDRT
158 rt.rt_metric = metric + 1;
159#else
160 ENOSUPP("inet_setroute", "NEW_ADDRT (metric)");
161#endif
162 args++;
163 continue;
164 }
165
166 if (!strcmp(*args, "netmask")) {
167 struct sockaddr mask;
168
169 args++;
170 if (!*args || mask_in_addr(rt))
Eric Andersen67991cf2001-02-14 21:23:06 +0000171 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000172 safe_strncpy(netmask, *args, (sizeof netmask));
173 if ((isnet = INET_resolve(netmask, &mask)) < 0) {
174 fprintf(stderr, "cant resolve netmask %s\n", netmask);
175 return (E_LOOKUP);
176 }
177 rt.rt_genmask = full_mask(mask);
178 args++;
179 continue;
180 }
181
182 if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) {
183 args++;
184 if (!*args)
Eric Andersen67991cf2001-02-14 21:23:06 +0000185 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000186 if (rt.rt_flags & RTF_GATEWAY)
Eric Andersen67991cf2001-02-14 21:23:06 +0000187 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000188 safe_strncpy(gateway, *args, (sizeof gateway));
189 if ((isnet = INET_resolve(gateway, &rt.rt_gateway)) < 0) {
190 fprintf(stderr, "cant resolve gw %s\n", gateway);
191 return (E_LOOKUP);
192 }
193 if (isnet) {
194 fprintf(stderr,
195 _("route: %s: cannot use a NETWORK as gateway!\n"),
196 gateway);
197 return (E_OPTERR);
198 }
199 rt.rt_flags |= RTF_GATEWAY;
200 args++;
201 continue;
202 }
203
204 if (!strcmp(*args, "mss")) {
205 args++;
206 rt.rt_flags |= RTF_MSS;
207 if (!*args)
Eric Andersen67991cf2001-02-14 21:23:06 +0000208 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000209 rt.rt_mss = atoi(*args);
210 args++;
211 if (rt.rt_mss < 64 || rt.rt_mss > 32768) {
212 fprintf(stderr, _("route: Invalid MSS.\n"));
213 return (E_OPTERR);
214 }
215 continue;
216 }
217
218 if (!strcmp(*args, "window")) {
219 args++;
220 if (!*args)
Eric Andersen67991cf2001-02-14 21:23:06 +0000221 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000222 rt.rt_flags |= RTF_WINDOW;
223 rt.rt_window = atoi(*args);
224 args++;
225 if (rt.rt_window < 128) {
226 fprintf(stderr, _("route: Invalid window.\n"));
227 return (E_OPTERR);
228 }
229 continue;
230 }
231
232 if (!strcmp(*args, "irtt")) {
233 args++;
234 if (!*args)
Eric Andersen67991cf2001-02-14 21:23:06 +0000235 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000236 args++;
237#if HAVE_RTF_IRTT
238 rt.rt_flags |= RTF_IRTT;
239 rt.rt_irtt = atoi(*(args - 1));
240 rt.rt_irtt *= (HZ / 100); /* FIXME */
241#if 0 /* FIXME: do we need to check anything of this? */
242 if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
243 fprintf(stderr, _("route: Invalid initial rtt.\n"));
244 return (E_OPTERR);
245 }
246#endif
247#else
248 ENOSUPP("inet_setroute", "RTF_IRTT");
249#endif
250 continue;
251 }
252
253 if (!strcmp(*args, "reject")) {
254 args++;
255#if HAVE_RTF_REJECT
256 rt.rt_flags |= RTF_REJECT;
257#else
258 ENOSUPP("inet_setroute", "RTF_REJECT");
259#endif
260 continue;
261 }
262 if (!strcmp(*args, "mod")) {
263 args++;
264 rt.rt_flags |= RTF_MODIFIED;
265 continue;
266 }
267 if (!strcmp(*args, "dyn")) {
268 args++;
269 rt.rt_flags |= RTF_DYNAMIC;
270 continue;
271 }
272 if (!strcmp(*args, "reinstate")) {
273 args++;
274 rt.rt_flags |= RTF_REINSTATE;
275 continue;
276 }
277 if (!strcmp(*args, "device") || !strcmp(*args, "dev")) {
278 args++;
279 if (rt.rt_dev || *args == NULL)
Eric Andersen67991cf2001-02-14 21:23:06 +0000280 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000281 rt.rt_dev = *args++;
282 continue;
283 }
284 /* nothing matches */
285 if (!rt.rt_dev) {
286 rt.rt_dev = *args++;
287 if (*args)
Eric Andersen67991cf2001-02-14 21:23:06 +0000288 show_usage(); /* must be last to catch typos */
Eric Andersenec455952001-02-14 08:11:27 +0000289 } else
Eric Andersen67991cf2001-02-14 21:23:06 +0000290 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000291 }
292
293#if HAVE_RTF_REJECT
294 if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev)
295 rt.rt_dev = "lo";
296#endif
297
298 /* sanity checks.. */
299 if (mask_in_addr(rt)) {
300 unsigned long mask = mask_in_addr(rt);
301 mask = ~ntohl(mask);
302 if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
303 fprintf(stderr,
304 _("route: netmask %.8x doesn't make sense with host route\n"),
305 (unsigned int)mask);
306 return (E_OPTERR);
307 }
308 if (mask & (mask + 1)) {
309 fprintf(stderr, _("route: bogus netmask %s\n"), netmask);
310 return (E_OPTERR);
311 }
312 mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
313 if (mask & ~mask_in_addr(rt)) {
314 fprintf(stderr, _("route: netmask doesn't match route address\n"));
315 return (E_OPTERR);
316 }
317 }
318 /* Fill out netmask if still unset */
319 if ((action == RTACTION_ADD) && rt.rt_flags & RTF_HOST)
320 mask_in_addr(rt) = 0xffffffff;
321
322 /* Create a socket to the INET kernel. */
323 if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
324 perror("socket");
325 return (E_SOCK);
326 }
327 /* Tell the kernel to accept this route. */
328 if (action == RTACTION_DEL) {
329 if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
330 perror("SIOCDELRT");
331 close(skfd);
332 return (E_SOCK);
333 }
334 } else {
335 if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
336 perror("SIOCADDRT");
337 close(skfd);
338 return (E_SOCK);
339 }
340 }
341
342 /* Close the socket. */
343 (void) close(skfd);
344 return (0);
345}
346
Eric Andersen68be2ab2001-02-14 19:26:39 +0000347void displayroutes(void)
348{
349 char buff[256];
350 int nl = 0 ;
351 struct in_addr dest;
352 struct in_addr gw;
353 struct in_addr mask;
354 int flgs, ref, use, metric;
355 char flags[4];
356 unsigned long int d,g,m;
357
358 char sdest[16], sgw[16];
359
360
361 FILE *fp = fopen("/proc/net/route", "r");
362
363 if(fp==0) {
364 perror_msg_and_die("/proc/net/route");
365 }
366 while( fgets(buff, sizeof(buff), fp) != NULL ) {
367 if(nl) {
368 int ifl = 0;
369 while(buff[ifl]!=' ' && buff[ifl]!='\t' && buff[ifl]!='\0')
370 ifl++;
371 buff[ifl]=0; /* interface */
372 if(sscanf(buff+ifl+1, "%lx%lx%d%d%d%d%lx",
373 &d, &g, &flgs, &ref, &use, &metric, &m)!=7) {
374 error_msg_and_die( "Unsuported kernel route format\n");
375 }
376 dest.s_addr = d;
377 gw.s_addr = g;
378 mask.s_addr = m;
379 if(nl==1)
380 printf("Kernel IP routing table\n\
381Destination\tGateway\t\tGenmask\t\tFlags Metric Ref Use Iface\n");
382
383 ifl = 0; /* parse flags */
384 if(flgs&1)
385 flags[ifl++]='U';
386 if(flgs&2)
387 flags[ifl++]='G';
388 if(flgs&4)
389 flags[ifl++]='H';
390 flags[ifl]=0;
391 strcpy(sdest, (dest.s_addr==0 ? "default" :
392 inet_ntoa(dest)));
393 strcpy(sgw, (gw.s_addr==0 ? "*" :
394 inet_ntoa(gw)));
395 printf("%-16s%-16s%-16s%-6s%-7d%-9d%-2d%s\n",
396 sdest, sgw,
397 inet_ntoa(mask),
398 flags, metric, ref, use, buff);
399 }
400 nl++;
401 }
402}
403
Eric Andersenec455952001-02-14 08:11:27 +0000404int route_main(int argc, char **argv)
405{
406 int what = 0;
407
408 argc--;
409 argv++;
410
411 if (*argv == NULL) {
Eric Andersen68be2ab2001-02-14 19:26:39 +0000412 displayroutes();
413 exit(EXIT_SUCCESS);
Eric Andersenec455952001-02-14 08:11:27 +0000414 } else {
415 /* check verb */
416 if (!strcmp(*argv, "add"))
417 what = RTACTION_ADD;
418 else if (!strcmp(*argv, "del") || !strcmp(*argv, "delete"))
419 what = RTACTION_DEL;
420 else if (!strcmp(*argv, "flush"))
421 what = RTACTION_FLUSH;
422 else
Eric Andersen67991cf2001-02-14 21:23:06 +0000423 show_usage();
Eric Andersenec455952001-02-14 08:11:27 +0000424 }
425
426 INET_setroute(what, 0, ++argv);
427
Eric Andersen68be2ab2001-02-14 19:26:39 +0000428 exit(EXIT_SUCCESS);
Eric Andersenec455952001-02-14 08:11:27 +0000429}