This patch, put together by Manuel Novoa III, is a merge of work
[platform/upstream/busybox.git] / networking / route.c
1 /* 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  *
18  * $Id: route.c,v 1.3 2001/02/14 21:23:06 andersen Exp $
19  *
20  * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
21  */
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
59 static int
60 INET_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
100 static int
101 INET_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)
118                 show_usage();
119
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))
155                                 show_usage();
156                         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))
171                                 show_usage();
172                         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)
185                                 show_usage();
186                         if (rt.rt_flags & RTF_GATEWAY)
187                                 show_usage();
188                         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)
208                                 show_usage();
209                         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)
221                                 show_usage();
222                         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)
235                                 show_usage();
236                         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)
280                                 show_usage();
281                         rt.rt_dev = *args++;
282                         continue;
283                 }
284                 /* nothing matches */
285                 if (!rt.rt_dev) {
286                         rt.rt_dev = *args++;
287                         if (*args)
288                                 show_usage();   /* must be last to catch typos */
289                 } else
290                         show_usage();
291         }
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
347 void 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\
381 Destination\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
404 int route_main(int argc, char **argv)
405 {
406         int what = 0;
407
408         argc--;
409         argv++;
410
411         if (*argv == NULL) {
412                 displayroutes();
413                 exit(EXIT_SUCCESS);
414         } 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
423                         show_usage();
424         }
425
426         INET_setroute(what, 0, ++argv);
427
428         exit(EXIT_SUCCESS);
429 }