Add in ifconfig and route
[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.1 2001/02/14 08:11:27 andersen Exp $
19  *
20  */
21
22 #include "busybox.h"
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <sys/socket.h>
26 #include <net/route.h>
27 #include <linux/param.h>  // HZ
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <getopt.h>
36 #include <unistd.h>
37 #include <ctype.h>
38
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
56 /* resolve XXX.YYY.ZZZ.QQQ -> binary */
57
58 static int
59 INET_resolve(char *name, struct sockaddr *sa)
60 {
61         struct sockaddr_in *sin = (struct sockaddr_in *)sa;
62         
63         sin->sin_family = AF_INET;
64         sin->sin_port = 0;
65
66         /* Default is special, meaning 0.0.0.0. */
67         if (!strcmp(name, "default")) {
68                 sin->sin_addr.s_addr = INADDR_ANY;
69                 return (1);
70         }
71         /* Look to see if it's a dotted quad. */
72         if (inet_aton(name, &sin->sin_addr)) {
73                 return 0;
74         }
75         /* guess not.. */
76         return -1;
77 }
78
79 #if defined (SIOCADDRTOLD) || defined (RTF_IRTT)        /* route */
80 #define HAVE_NEW_ADDRT 1
81 #endif
82 #ifdef RTF_IRTT                 /* route */
83 #define HAVE_RTF_IRTT 1
84 #endif
85 #ifdef RTF_REJECT               /* route */
86 #define HAVE_RTF_REJECT 1
87 #endif
88
89 #if HAVE_NEW_ADDRT
90 #define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
91 #define full_mask(x) (x)
92 #else
93 #define mask_in_addr(x) ((x).rt_genmask)
94 #define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
95 #endif
96
97 /* add or delete a route depending on action */
98
99 static int
100 INET_setroute(int action, int options, char **args)
101 {
102         struct rtentry rt;
103         char target[128], gateway[128] = "NONE", netmask[128] = "default";
104         int xflag, isnet;
105         int skfd;
106
107         xflag = 0;
108
109         if (!strcmp(*args, "-net")) {
110                 xflag = 1;
111                 args++;
112         } else if (!strcmp(*args, "-host")) {
113                 xflag = 2;
114                 args++;
115         }
116         if (*args == NULL)
117                 usage(route_usage);
118
119         safe_strncpy(target, *args++, (sizeof target));
120
121         /* Clean out the RTREQ structure. */
122         memset((char *) &rt, 0, sizeof(struct rtentry));
123
124
125         if ((isnet = INET_resolve(target, &rt.rt_dst)) < 0) {
126                 fprintf(stderr, "cant resolve %s\n", target);
127                 return (1);
128         }
129
130         switch (xflag) {
131                 case 1:
132                         isnet = 1;
133                         break;
134                         
135                 case 2:
136                         isnet = 0;
137                         break;
138                         
139                 default:
140                         break;
141         }
142         
143         /* Fill in the other fields. */
144         rt.rt_flags = (RTF_UP | RTF_HOST);
145         if (isnet)
146                 rt.rt_flags &= ~RTF_HOST;
147
148         while (*args) {
149                 if (!strcmp(*args, "metric")) {
150                         int metric;
151                         
152                         args++;
153                         if (!*args || !isdigit(**args))
154                                 usage(route_usage);
155                         metric = atoi(*args);
156 #if HAVE_NEW_ADDRT
157                         rt.rt_metric = metric + 1;
158 #else
159                         ENOSUPP("inet_setroute", "NEW_ADDRT (metric)");
160 #endif
161                         args++;
162                         continue;
163                 }
164
165                 if (!strcmp(*args, "netmask")) {
166                         struct sockaddr mask;
167                         
168                         args++;
169                         if (!*args || mask_in_addr(rt))
170                                 usage(route_usage);
171                         safe_strncpy(netmask, *args, (sizeof netmask));
172                         if ((isnet = INET_resolve(netmask, &mask)) < 0) {
173                                 fprintf(stderr, "cant resolve netmask %s\n", netmask);
174                                 return (E_LOOKUP);
175                         }
176                         rt.rt_genmask = full_mask(mask);
177                         args++;
178                         continue;
179                 }
180
181                 if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) {
182                         args++;
183                         if (!*args)
184                                 usage(route_usage);
185                         if (rt.rt_flags & RTF_GATEWAY)
186                                 usage(route_usage);
187                         safe_strncpy(gateway, *args, (sizeof gateway));
188                         if ((isnet = INET_resolve(gateway, &rt.rt_gateway)) < 0) {
189                                 fprintf(stderr, "cant resolve gw %s\n", gateway);
190                                 return (E_LOOKUP);
191                         }
192                         if (isnet) {
193                                 fprintf(stderr,
194                                         _("route: %s: cannot use a NETWORK as gateway!\n"),
195                                         gateway);
196                                 return (E_OPTERR);
197                         }
198                         rt.rt_flags |= RTF_GATEWAY;
199                         args++;
200                         continue;
201                 }
202
203                 if (!strcmp(*args, "mss")) {
204                         args++;
205                         rt.rt_flags |= RTF_MSS;
206                         if (!*args)
207                                 usage(route_usage);
208                         rt.rt_mss = atoi(*args);
209                         args++;
210                         if (rt.rt_mss < 64 || rt.rt_mss > 32768) {
211                                 fprintf(stderr, _("route: Invalid MSS.\n"));
212                                 return (E_OPTERR);
213                         }
214                         continue;
215                 }
216
217                 if (!strcmp(*args, "window")) {
218                         args++;
219                         if (!*args)
220                                 usage(route_usage);
221                         rt.rt_flags |= RTF_WINDOW;
222                         rt.rt_window = atoi(*args);
223                         args++;
224                         if (rt.rt_window < 128) {
225                                 fprintf(stderr, _("route: Invalid window.\n"));
226                                 return (E_OPTERR);
227                         }
228                         continue;
229                 }
230
231                 if (!strcmp(*args, "irtt")) {
232                         args++;
233                         if (!*args)
234                                 usage(route_usage);
235                         args++;
236 #if HAVE_RTF_IRTT
237                         rt.rt_flags |= RTF_IRTT;
238                         rt.rt_irtt = atoi(*(args - 1));
239                         rt.rt_irtt *= (HZ / 100);       /* FIXME */
240 #if 0                           /* FIXME: do we need to check anything of this? */
241                         if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
242                                 fprintf(stderr, _("route: Invalid initial rtt.\n"));
243                                 return (E_OPTERR);
244                         }
245 #endif
246 #else
247                         ENOSUPP("inet_setroute", "RTF_IRTT");
248 #endif
249                         continue;
250                 }
251
252                 if (!strcmp(*args, "reject")) {
253                         args++;
254 #if HAVE_RTF_REJECT
255                         rt.rt_flags |= RTF_REJECT;
256 #else
257                         ENOSUPP("inet_setroute", "RTF_REJECT");
258 #endif
259                         continue;
260                 }
261                 if (!strcmp(*args, "mod")) {
262                         args++;
263                         rt.rt_flags |= RTF_MODIFIED;
264                         continue;
265                 }
266                 if (!strcmp(*args, "dyn")) {
267                         args++;
268                         rt.rt_flags |= RTF_DYNAMIC;
269                         continue;
270                 }
271                 if (!strcmp(*args, "reinstate")) {
272                         args++;
273                         rt.rt_flags |= RTF_REINSTATE;
274                         continue;
275                 }
276                 if (!strcmp(*args, "device") || !strcmp(*args, "dev")) {
277                         args++;
278                         if (rt.rt_dev || *args == NULL)
279                                 usage(route_usage);
280                         rt.rt_dev = *args++;
281                         continue;
282                 }
283                 /* nothing matches */
284                 if (!rt.rt_dev) {
285                         rt.rt_dev = *args++;
286                         if (*args)
287                                 usage(route_usage);     /* must be last to catch typos */
288                 } else
289                         usage(route_usage);
290         }
291
292 #if HAVE_RTF_REJECT
293         if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev)
294                 rt.rt_dev = "lo";
295 #endif
296
297         /* sanity checks.. */
298         if (mask_in_addr(rt)) {
299                 unsigned long mask = mask_in_addr(rt);
300                 mask = ~ntohl(mask);
301                 if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
302                         fprintf(stderr,
303                                 _("route: netmask %.8x doesn't make sense with host route\n"),
304                                 (unsigned int)mask);
305                         return (E_OPTERR);
306                 }
307                 if (mask & (mask + 1)) {
308                         fprintf(stderr, _("route: bogus netmask %s\n"), netmask);
309                         return (E_OPTERR);
310                 }
311                 mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
312                 if (mask & ~mask_in_addr(rt)) {
313                         fprintf(stderr, _("route: netmask doesn't match route address\n"));
314                         return (E_OPTERR);
315                 }
316         }
317         /* Fill out netmask if still unset */
318         if ((action == RTACTION_ADD) && rt.rt_flags & RTF_HOST)
319                 mask_in_addr(rt) = 0xffffffff;
320
321         /* Create a socket to the INET kernel. */
322         if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
323                 perror("socket");
324                 return (E_SOCK);
325         }
326         /* Tell the kernel to accept this route. */
327         if (action == RTACTION_DEL) {
328                 if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
329                         perror("SIOCDELRT");
330                         close(skfd);
331                         return (E_SOCK);
332                 }
333         } else {
334                 if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
335                         perror("SIOCADDRT");
336                         close(skfd);
337                         return (E_SOCK);
338                 }
339         }
340         
341         /* Close the socket. */
342         (void) close(skfd);
343         return (0);
344 }
345
346 int route_main(int argc, char **argv)
347 {
348         int what = 0;
349
350         argc--;
351         argv++;
352
353         if (*argv == NULL) {
354                 //displayroutes();
355                 fprintf(stderr, "print routes is not implemented yet\n");
356                 usage(route_usage);
357         } else {
358                 /* check verb */
359                 if (!strcmp(*argv, "add"))
360                         what = RTACTION_ADD;
361                 else if (!strcmp(*argv, "del") || !strcmp(*argv, "delete"))
362                         what = RTACTION_DEL;
363                 else if (!strcmp(*argv, "flush"))
364                         what = RTACTION_FLUSH;
365                 else
366                         usage(route_usage);
367         }
368
369         INET_setroute(what, 0, ++argv);
370
371         exit(0);
372 }