Commit | Line | Data |
---|---|---|
5ff67f98 DF |
1 | /* |
2 | * Copyright (c) 1983 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
7 | #ifndef lint | |
8 | char copyright[] = | |
9 | "@(#) Copyright (c) 1983 Regents of the University of California.\n\ | |
10 | All rights reserved.\n"; | |
11 | #endif not lint | |
12 | ||
4bec325f | 13 | #ifndef lint |
eac76d14 | 14 | static char sccsid[] = "@(#)route.c 5.6 (Berkeley) %G%"; |
5ff67f98 | 15 | #endif not lint |
4bec325f | 16 | |
a2280e64 | 17 | #include <sys/param.h> |
4bec325f BJ |
18 | #include <sys/socket.h> |
19 | #include <sys/ioctl.h> | |
63c817da | 20 | #include <sys/mbuf.h> |
94a2d2a7 | 21 | |
4bec325f | 22 | #include <net/route.h> |
94a2d2a7 | 23 | #include <netinet/in.h> |
eac76d14 | 24 | #include <netns/ns.h> |
94a2d2a7 SL |
25 | |
26 | #include <stdio.h> | |
4bec325f BJ |
27 | #include <errno.h> |
28 | #include <ctype.h> | |
accbc2e3 | 29 | #include <netdb.h> |
4bec325f BJ |
30 | |
31 | struct rtentry route; | |
4bec325f | 32 | int s; |
a2280e64 | 33 | int forcehost, forcenet, doflush, nflag; |
4bec325f | 34 | struct sockaddr_in sin = { AF_INET }; |
379dcc38 | 35 | struct in_addr inet_makeaddr(); |
55e026df | 36 | char *malloc(); |
4bec325f BJ |
37 | |
38 | main(argc, argv) | |
39 | int argc; | |
40 | char *argv[]; | |
41 | { | |
42 | ||
43 | if (argc < 2) | |
a2280e64 | 44 | printf("usage: route [ -n ] [ -f ] [ cmd [ net | host ] args ]\n"), |
b3933da8 | 45 | exit(1); |
f508555e | 46 | s = socket(AF_INET, SOCK_RAW, 0); |
4bec325f | 47 | if (s < 0) { |
5a635333 | 48 | perror("route: socket"); |
4bec325f BJ |
49 | exit(1); |
50 | } | |
51 | argc--, argv++; | |
b3933da8 MK |
52 | for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) { |
53 | for (argv[0]++; *argv[0]; argv[0]++) | |
54 | switch (*argv[0]) { | |
55 | case 'f': | |
a2280e64 | 56 | doflush++; |
b3933da8 MK |
57 | break; |
58 | case 'n': | |
a2280e64 | 59 | nflag++; |
b3933da8 MK |
60 | break; |
61 | } | |
62 | } | |
a2280e64 MK |
63 | if (doflush) |
64 | flushroutes(); | |
63c817da SL |
65 | if (argc > 0) { |
66 | if (strcmp(*argv, "add") == 0) | |
67 | newroute(argc, argv); | |
68 | else if (strcmp(*argv, "delete") == 0) | |
69 | newroute(argc, argv); | |
70 | else if (strcmp(*argv, "change") == 0) | |
71 | changeroute(argc-1, argv+1); | |
72 | else | |
73 | printf("%s: huh?\n", *argv); | |
74 | } | |
75 | } | |
76 | ||
77 | /* | |
78 | * Purge all entries in the routing tables not | |
79 | * associated with network interfaces. | |
80 | */ | |
81 | #include <nlist.h> | |
82 | ||
83 | struct nlist nl[] = { | |
84 | #define N_RTHOST 0 | |
85 | { "_rthost" }, | |
86 | #define N_RTNET 1 | |
87 | { "_rtnet" }, | |
f508555e MK |
88 | #define N_RTHASHSIZE 2 |
89 | { "_rthashsize" }, | |
63c817da SL |
90 | "", |
91 | }; | |
92 | ||
93 | flushroutes() | |
94 | { | |
95 | struct mbuf mb; | |
96 | register struct rtentry *rt; | |
97 | register struct mbuf *m; | |
f508555e MK |
98 | struct mbuf **routehash; |
99 | int rthashsize, i, doinghost = 1, kmem; | |
a2280e64 | 100 | char *routename(), *netname(); |
63c817da SL |
101 | |
102 | nlist("/vmunix", nl); | |
103 | if (nl[N_RTHOST].n_value == 0) { | |
104 | printf("route: \"rthost\", symbol not in namelist\n"); | |
105 | exit(1); | |
106 | } | |
107 | if (nl[N_RTNET].n_value == 0) { | |
108 | printf("route: \"rtnet\", symbol not in namelist\n"); | |
109 | exit(1); | |
110 | } | |
f508555e MK |
111 | if (nl[N_RTHASHSIZE].n_value == 0) { |
112 | printf("route: \"rthashsize\", symbol not in namelist\n"); | |
113 | exit(1); | |
114 | } | |
63c817da SL |
115 | kmem = open("/dev/kmem", 0); |
116 | if (kmem < 0) { | |
117 | perror("route: /dev/kmem"); | |
118 | exit(1); | |
119 | } | |
f508555e MK |
120 | lseek(kmem, nl[N_RTHASHSIZE].n_value, 0); |
121 | read(kmem, &rthashsize, sizeof (rthashsize)); | |
122 | routehash = (struct mbuf **)malloc(rthashsize*sizeof (struct mbuf *)); | |
123 | ||
63c817da | 124 | lseek(kmem, nl[N_RTHOST].n_value, 0); |
f508555e | 125 | read(kmem, routehash, rthashsize*sizeof (struct mbuf *)); |
63c817da SL |
126 | printf("Flushing routing tables:\n"); |
127 | again: | |
f508555e | 128 | for (i = 0; i < rthashsize; i++) { |
63c817da SL |
129 | if (routehash[i] == 0) |
130 | continue; | |
131 | m = routehash[i]; | |
132 | while (m) { | |
133 | lseek(kmem, m, 0); | |
134 | read(kmem, &mb, sizeof (mb)); | |
135 | rt = mtod(&mb, struct rtentry *); | |
136 | if (rt->rt_flags & RTF_GATEWAY) { | |
eac76d14 MK |
137 | printf("%-20.20s ", doinghost ? |
138 | routename(&rt->rt_dst) : | |
139 | netname(&rt->rt_dst)); | |
140 | printf("%-20.20s ", routename(&rt->rt_gateway)); | |
63c817da SL |
141 | if (ioctl(s, SIOCDELRT, (caddr_t)rt) < 0) |
142 | error("delete"); | |
143 | else | |
144 | printf("done\n"); | |
145 | } | |
146 | m = mb.m_next; | |
147 | } | |
148 | } | |
149 | if (doinghost) { | |
150 | lseek(kmem, nl[N_RTNET].n_value, 0); | |
f508555e | 151 | read(kmem, routehash, rthashsize*sizeof (struct mbuf *)); |
63c817da SL |
152 | doinghost = 0; |
153 | goto again; | |
154 | } | |
155 | close(kmem); | |
f508555e | 156 | free(routehash); |
63c817da SL |
157 | } |
158 | ||
159 | char * | |
eac76d14 MK |
160 | routename(sa) |
161 | struct sockaddr *sa; | |
63c817da | 162 | { |
a2280e64 | 163 | register char *cp; |
63c817da | 164 | static char line[50]; |
6edb1941 | 165 | struct hostent *hp; |
a2280e64 MK |
166 | static char domain[MAXHOSTNAMELEN + 1]; |
167 | static int first = 1; | |
168 | char *index(); | |
eac76d14 | 169 | char *ns_print(); |
63c817da | 170 | |
a2280e64 MK |
171 | if (first) { |
172 | first = 0; | |
173 | if (gethostname(domain, MAXHOSTNAMELEN) == 0 && | |
174 | (cp = index(domain, '.'))) | |
175 | (void) strcpy(domain, cp + 1); | |
176 | else | |
177 | domain[0] = 0; | |
178 | } | |
eac76d14 MK |
179 | switch (sa->sa_family) { |
180 | ||
181 | case AF_INET: | |
182 | { struct in_addr in; | |
183 | in = ((struct sockaddr_in *)sa)->sin_addr; | |
184 | ||
185 | cp = 0; | |
186 | if (in.s_addr == INADDR_ANY) | |
187 | cp = "default"; | |
188 | if (cp == 0 && !nflag) { | |
189 | hp = gethostbyaddr(&in, sizeof (struct in_addr), | |
190 | AF_INET); | |
191 | if (hp) { | |
192 | if ((cp = index(hp->h_name, '.')) && | |
193 | !strcmp(cp + 1, domain)) | |
194 | *cp = 0; | |
195 | cp = hp->h_name; | |
196 | } | |
a2280e64 | 197 | } |
eac76d14 MK |
198 | if (cp) |
199 | strcpy(line, cp); | |
200 | else { | |
a2280e64 | 201 | #define C(x) ((x) & 0xff) |
eac76d14 MK |
202 | in.s_addr = ntohl(in.s_addr); |
203 | sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), | |
204 | C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); | |
205 | } | |
206 | break; | |
207 | } | |
208 | ||
209 | case AF_NS: | |
210 | return (ns_print((struct sockaddr_ns *)sa)); | |
211 | ||
212 | default: | |
213 | { u_short *s = (u_short *)sa->sa_data; | |
214 | ||
215 | sprintf(line, "af %d: %x %x %x %x %x %x %x", sa->sa_family, | |
216 | s[0], s[1], s[2], s[3], s[4], s[5], s[6]); | |
217 | break; | |
218 | } | |
a2280e64 MK |
219 | } |
220 | return (line); | |
221 | } | |
222 | ||
223 | /* | |
224 | * Return the name of the network whose address is given. | |
225 | * The address is assumed to be that of a net or subnet, not a host. | |
226 | */ | |
227 | char * | |
eac76d14 MK |
228 | netname(sa) |
229 | struct sockaddr *sa; | |
a2280e64 MK |
230 | { |
231 | char *cp = 0; | |
232 | static char line[50]; | |
233 | struct netent *np = 0; | |
234 | u_long net, mask; | |
235 | register i; | |
236 | int subnetshift; | |
237 | ||
eac76d14 MK |
238 | switch (sa->sa_family) { |
239 | ||
240 | case AF_INET: | |
241 | { struct in_addr in; | |
242 | in = ((struct sockaddr_in *)sa)->sin_addr; | |
243 | ||
244 | in.s_addr = ntohl(in.s_addr); | |
245 | if (in.s_addr == 0) | |
246 | cp = "default"; | |
247 | else if (!nflag) { | |
248 | if (IN_CLASSA(i)) { | |
249 | mask = IN_CLASSA_NET; | |
250 | subnetshift = 8; | |
251 | } else if (IN_CLASSB(i)) { | |
252 | mask = IN_CLASSB_NET; | |
253 | subnetshift = 8; | |
254 | } else { | |
255 | mask = IN_CLASSC_NET; | |
256 | subnetshift = 4; | |
257 | } | |
258 | /* | |
259 | * If there are more bits than the standard mask | |
260 | * would suggest, subnets must be in use. | |
261 | * Guess at the subnet mask, assuming reasonable | |
262 | * width subnet fields. | |
263 | */ | |
264 | while (in.s_addr &~ mask) | |
265 | mask = (long)mask >> subnetshift; | |
266 | net = in.s_addr & mask; | |
267 | while ((mask & 1) == 0) | |
268 | mask >>= 1, net >>= 1; | |
269 | np = getnetbyaddr(net, AF_INET); | |
270 | if (np) | |
271 | cp = np->n_name; | |
a2280e64 | 272 | } |
eac76d14 MK |
273 | if (cp) |
274 | strcpy(line, cp); | |
275 | else if ((in.s_addr & 0xffffff) == 0) | |
276 | sprintf(line, "%u", C(in.s_addr >> 24)); | |
277 | else if ((in.s_addr & 0xffff) == 0) | |
278 | sprintf(line, "%u.%u", C(in.s_addr >> 24), | |
279 | C(in.s_addr >> 16)); | |
280 | else if ((in.s_addr & 0xff) == 0) | |
281 | sprintf(line, "%u.%u.%u", C(in.s_addr >> 24), | |
282 | C(in.s_addr >> 16), C(in.s_addr >> 8)); | |
283 | else | |
284 | sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), | |
285 | C(in.s_addr >> 16), C(in.s_addr >> 8), | |
286 | C(in.s_addr)); | |
287 | break; | |
288 | } | |
289 | ||
290 | case AF_NS: | |
291 | return (ns_print((struct sockaddr_ns *)sa)); | |
292 | break; | |
293 | ||
294 | default: | |
295 | { u_short *s = (u_short *)sa->sa_data; | |
296 | ||
297 | sprintf(line, "af %d: %x %x %x %x %x %x %x", sa->sa_family, | |
298 | s[0], s[1], s[2], s[3], s[4], s[5], s[6]); | |
299 | break; | |
300 | } | |
63c817da SL |
301 | } |
302 | return (line); | |
4bec325f BJ |
303 | } |
304 | ||
305 | newroute(argc, argv) | |
306 | int argc; | |
307 | char *argv[]; | |
308 | { | |
309 | struct sockaddr_in *sin; | |
792b0612 MK |
310 | char *cmd, *dest, *gateway; |
311 | int ishost, metric = 0, ret, attempts, oerrno; | |
312 | struct hostent *hp; | |
313 | extern int errno; | |
4bec325f | 314 | |
183ef13b | 315 | cmd = argv[0]; |
a2280e64 MK |
316 | if ((strcmp(argv[1], "host")) == 0) { |
317 | forcehost++; | |
318 | argc--, argv++; | |
319 | } else if ((strcmp(argv[1], "net")) == 0) { | |
320 | forcenet++; | |
321 | argc--, argv++; | |
322 | } | |
6edb1941 MK |
323 | if (*cmd == 'a') { |
324 | if (argc != 4) { | |
325 | printf("usage: %s destination gateway metric\n", cmd); | |
326 | printf("(metric of 0 if gateway is this host)\n"); | |
327 | return; | |
328 | } | |
c4693c18 | 329 | metric = atoi(argv[3]); |
6edb1941 | 330 | } else { |
55e026df | 331 | if (argc < 3) { |
6edb1941 MK |
332 | printf("usage: %s destination gateway\n", cmd); |
333 | return; | |
334 | } | |
335 | } | |
792b0612 | 336 | sin = (struct sockaddr_in *)&route.rt_dst; |
a2280e64 | 337 | ishost = getaddr(argv[1], &route.rt_dst, &hp, &dest, forcenet); |
b3933da8 MK |
338 | if (forcehost) |
339 | ishost = 1; | |
340 | if (forcenet) | |
341 | ishost = 0; | |
792b0612 | 342 | sin = (struct sockaddr_in *)&route.rt_gateway; |
a2280e64 | 343 | (void) getaddr(argv[2], &route.rt_gateway, &hp, &gateway, 0); |
4bec325f | 344 | route.rt_flags = RTF_UP; |
6edb1941 | 345 | if (ishost) |
4bec325f | 346 | route.rt_flags |= RTF_HOST; |
c4693c18 | 347 | if (metric > 0) |
183ef13b | 348 | route.rt_flags |= RTF_GATEWAY; |
792b0612 MK |
349 | for (attempts = 1; ; attempts++) { |
350 | errno = 0; | |
351 | if ((ret = ioctl(s, *cmd == 'a' ? SIOCADDRT : SIOCDELRT, | |
352 | (caddr_t)&route)) == 0) | |
353 | break; | |
354 | if (errno != ENETUNREACH && errno != ESRCH) | |
355 | break; | |
356 | if (hp && hp->h_addr_list[1]) { | |
357 | hp->h_addr_list++; | |
358 | bcopy(hp->h_addr_list[0], (caddr_t)&sin->sin_addr, | |
359 | hp->h_length); | |
360 | } else | |
361 | break; | |
362 | } | |
363 | oerrno = errno; | |
364 | printf("%s %s %s: gateway %s", cmd, ishost? "host" : "net", | |
365 | dest, gateway); | |
366 | if (attempts > 1 && ret == 0) | |
367 | printf(" (%s)", | |
368 | inet_ntoa(((struct sockaddr_in *)&route.rt_gateway)->sin_addr)); | |
369 | if (ret == 0) | |
370 | printf("\n"); | |
371 | else { | |
372 | printf(": "); | |
373 | fflush(stdout); | |
374 | errno = oerrno; | |
375 | error(0); | |
376 | } | |
4bec325f BJ |
377 | } |
378 | ||
379 | changeroute(argc, argv) | |
380 | int argc; | |
381 | char *argv[]; | |
382 | { | |
383 | printf("not supported\n"); | |
384 | } | |
385 | ||
386 | error(cmd) | |
387 | char *cmd; | |
388 | { | |
4bec325f BJ |
389 | |
390 | if (errno == ESRCH) | |
391 | fprintf(stderr, "not in table\n"); | |
392 | else if (errno == EBUSY) | |
393 | fprintf(stderr, "entry in use\n"); | |
394 | else if (errno == ENOBUFS) | |
395 | fprintf(stderr, "routing table overflow\n"); | |
396 | else | |
397 | perror(cmd); | |
398 | } | |
399 | ||
55e026df MK |
400 | char * |
401 | savestr(s) | |
402 | char *s; | |
403 | { | |
404 | char *sav; | |
405 | ||
406 | sav = malloc(strlen(s) + 1); | |
407 | if (sav == NULL) { | |
408 | fprintf("route: out of memory\n"); | |
409 | exit(1); | |
410 | } | |
411 | strcpy(sav, s); | |
412 | return (sav); | |
413 | } | |
414 | ||
6edb1941 MK |
415 | /* |
416 | * Interpret an argument as a network address of some kind, | |
417 | * returning 1 if a host address, 0 if a network address. | |
418 | */ | |
a2280e64 | 419 | getaddr(s, sin, hpp, name, isnet) |
4bec325f BJ |
420 | char *s; |
421 | struct sockaddr_in *sin; | |
792b0612 | 422 | struct hostent **hpp; |
c28affab | 423 | char **name; |
a2280e64 | 424 | int isnet; |
4bec325f | 425 | { |
accbc2e3 | 426 | struct hostent *hp; |
379dcc38 SL |
427 | struct netent *np; |
428 | u_long val; | |
accbc2e3 | 429 | |
792b0612 | 430 | *hpp = 0; |
89a108e6 SL |
431 | if (strcmp(s, "default") == 0) { |
432 | sin->sin_family = AF_INET; | |
433 | sin->sin_addr = inet_makeaddr(0, INADDR_ANY); | |
55e026df | 434 | *name = "default"; |
6edb1941 | 435 | return(0); |
89a108e6 | 436 | } |
792b0612 | 437 | sin->sin_family = AF_INET; |
a2280e64 MK |
438 | if (isnet == 0) { |
439 | val = inet_addr(s); | |
440 | if (val != -1) { | |
441 | sin->sin_addr.s_addr = val; | |
442 | *name = s; | |
443 | return(inet_lnaof(sin->sin_addr) != INADDR_ANY); | |
444 | } | |
792b0612 MK |
445 | } |
446 | val = inet_network(s); | |
447 | if (val != -1) { | |
448 | sin->sin_addr = inet_makeaddr(val, INADDR_ANY); | |
c28affab | 449 | *name = s; |
792b0612 MK |
450 | return(0); |
451 | } | |
379dcc38 SL |
452 | np = getnetbyname(s); |
453 | if (np) { | |
454 | sin->sin_family = np->n_addrtype; | |
455 | sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); | |
55e026df | 456 | *name = savestr(np->n_name); |
6edb1941 | 457 | return(0); |
379dcc38 | 458 | } |
78160c55 MK |
459 | hp = gethostbyname(s); |
460 | if (hp) { | |
792b0612 | 461 | *hpp = hp; |
78160c55 MK |
462 | sin->sin_family = hp->h_addrtype; |
463 | bcopy(hp->h_addr, &sin->sin_addr, hp->h_length); | |
55e026df | 464 | *name = savestr(hp->h_name); |
78160c55 MK |
465 | return(1); |
466 | } | |
379dcc38 SL |
467 | fprintf(stderr, "%s: bad value\n", s); |
468 | exit(1); | |
183ef13b | 469 | } |
eac76d14 MK |
470 | |
471 | short ns_nullh[] = {0,0,0}; | |
472 | short ns_bh[] = {-1,-1,-1}; | |
473 | ||
474 | char * | |
475 | ns_print(sns) | |
476 | struct sockaddr_ns *sns; | |
477 | { | |
478 | struct ns_addr work; | |
479 | union { union ns_net net_e; u_long long_e; } net; | |
480 | u_short port; | |
481 | static char mybuf[50], cport[10], chost[25]; | |
482 | char *host = ""; | |
483 | register char *p; register u_char *q; u_char *q_lim; | |
484 | ||
485 | work = sns->sns_addr; | |
486 | port = ntohs(work.x_port); | |
487 | work.x_port = 0; | |
488 | net.net_e = work.x_net; | |
489 | if (ns_nullhost(work) && net.long_e == 0) { | |
490 | if (port ) { | |
491 | sprintf(mybuf, "*.%xH", port); | |
492 | upHex(mybuf); | |
493 | } else | |
494 | sprintf(mybuf, "*.*"); | |
495 | return (mybuf); | |
496 | } | |
497 | ||
498 | if (bcmp(ns_bh, work.x_host.c_host, 6) == 0) { | |
499 | host = "any"; | |
500 | } else if (bcmp(ns_nullh, work.x_host.c_host, 6) == 0) { | |
501 | host = "*"; | |
502 | } else { | |
503 | q = work.x_host.c_host; | |
504 | sprintf(chost, "%02x%02x%02x%02x%02x%02xH", | |
505 | q[0], q[1], q[2], q[3], q[4], q[5]); | |
506 | for (p = chost; *p == '0' && p < chost + 12; p++); | |
507 | host = p; | |
508 | } | |
509 | if (port) | |
510 | sprintf(cport, ".%xH", htons(port)); | |
511 | else | |
512 | *cport = 0; | |
513 | ||
514 | sprintf(mybuf,"%xH.%s%s", ntohl(net.long_e), host, cport); | |
515 | upHex(mybuf); | |
516 | return(mybuf); | |
517 | } | |
518 | ||
519 | upHex(p0) | |
520 | char *p0; | |
521 | { | |
522 | register char *p = p0; | |
523 | for (; *p; p++) switch (*p) { | |
524 | ||
525 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': | |
526 | *p += ('A' - 'a'); | |
527 | } | |
528 | } |