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