diff options
Diffstat (limited to 'route.c')
-rw-r--r-- | route.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/route.c b/route.c new file mode 100644 index 000000000..76b2306fd --- /dev/null +++ b/route.c | |||
@@ -0,0 +1,372 @@ | |||
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 | } | ||