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 |
a48a48cc | 14 | static char sccsid[] = "@(#)route.c 5.9 (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 | 202 | in.s_addr = ntohl(in.s_addr); |
9bd38ba8 | 203 | (void)sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), |
eac76d14 MK |
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 | ||
9bd38ba8 KB |
215 | (void)sprintf(line, "af %d: %x %x %x %x %x %x %x", |
216 | sa->sa_family, s[0], s[1], s[2], s[3], s[4], s[5], s[6]); | |
eac76d14 MK |
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; | |
e2fe1192 | 235 | register u_long i; |
a2280e64 | 236 | int subnetshift; |
a48a48cc | 237 | char *ns_print(); |
a2280e64 | 238 | |
eac76d14 MK |
239 | switch (sa->sa_family) { |
240 | ||
241 | case AF_INET: | |
242 | { struct in_addr in; | |
243 | in = ((struct sockaddr_in *)sa)->sin_addr; | |
244 | ||
e2fe1192 | 245 | i = in.s_addr = ntohl(in.s_addr); |
eac76d14 MK |
246 | if (in.s_addr == 0) |
247 | cp = "default"; | |
248 | else if (!nflag) { | |
249 | if (IN_CLASSA(i)) { | |
250 | mask = IN_CLASSA_NET; | |
251 | subnetshift = 8; | |
252 | } else if (IN_CLASSB(i)) { | |
253 | mask = IN_CLASSB_NET; | |
254 | subnetshift = 8; | |
255 | } else { | |
256 | mask = IN_CLASSC_NET; | |
257 | subnetshift = 4; | |
258 | } | |
259 | /* | |
260 | * If there are more bits than the standard mask | |
261 | * would suggest, subnets must be in use. | |
262 | * Guess at the subnet mask, assuming reasonable | |
263 | * width subnet fields. | |
264 | */ | |
265 | while (in.s_addr &~ mask) | |
266 | mask = (long)mask >> subnetshift; | |
267 | net = in.s_addr & mask; | |
268 | while ((mask & 1) == 0) | |
269 | mask >>= 1, net >>= 1; | |
270 | np = getnetbyaddr(net, AF_INET); | |
271 | if (np) | |
272 | cp = np->n_name; | |
a2280e64 | 273 | } |
eac76d14 MK |
274 | if (cp) |
275 | strcpy(line, cp); | |
276 | else if ((in.s_addr & 0xffffff) == 0) | |
9bd38ba8 | 277 | (void)sprintf(line, "%u", C(in.s_addr >> 24)); |
eac76d14 | 278 | else if ((in.s_addr & 0xffff) == 0) |
9bd38ba8 | 279 | (void)sprintf(line, "%u.%u", C(in.s_addr >> 24), |
eac76d14 MK |
280 | C(in.s_addr >> 16)); |
281 | else if ((in.s_addr & 0xff) == 0) | |
9bd38ba8 | 282 | (void)sprintf(line, "%u.%u.%u", C(in.s_addr >> 24), |
eac76d14 MK |
283 | C(in.s_addr >> 16), C(in.s_addr >> 8)); |
284 | else | |
9bd38ba8 | 285 | (void)sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), |
eac76d14 MK |
286 | C(in.s_addr >> 16), C(in.s_addr >> 8), |
287 | C(in.s_addr)); | |
288 | break; | |
289 | } | |
290 | ||
291 | case AF_NS: | |
292 | return (ns_print((struct sockaddr_ns *)sa)); | |
293 | break; | |
294 | ||
295 | default: | |
296 | { u_short *s = (u_short *)sa->sa_data; | |
297 | ||
9bd38ba8 KB |
298 | (void)sprintf(line, "af %d: %x %x %x %x %x %x %x", |
299 | sa->sa_family, s[0], s[1], s[2], s[3], s[4], s[5], s[6]); | |
eac76d14 MK |
300 | break; |
301 | } | |
63c817da SL |
302 | } |
303 | return (line); | |
4bec325f BJ |
304 | } |
305 | ||
306 | newroute(argc, argv) | |
307 | int argc; | |
308 | char *argv[]; | |
309 | { | |
310 | struct sockaddr_in *sin; | |
792b0612 MK |
311 | char *cmd, *dest, *gateway; |
312 | int ishost, metric = 0, ret, attempts, oerrno; | |
313 | struct hostent *hp; | |
314 | extern int errno; | |
4bec325f | 315 | |
183ef13b | 316 | cmd = argv[0]; |
a2280e64 MK |
317 | if ((strcmp(argv[1], "host")) == 0) { |
318 | forcehost++; | |
319 | argc--, argv++; | |
320 | } else if ((strcmp(argv[1], "net")) == 0) { | |
321 | forcenet++; | |
322 | argc--, argv++; | |
323 | } | |
6edb1941 MK |
324 | if (*cmd == 'a') { |
325 | if (argc != 4) { | |
326 | printf("usage: %s destination gateway metric\n", cmd); | |
327 | printf("(metric of 0 if gateway is this host)\n"); | |
328 | return; | |
329 | } | |
c4693c18 | 330 | metric = atoi(argv[3]); |
6edb1941 | 331 | } else { |
55e026df | 332 | if (argc < 3) { |
6edb1941 MK |
333 | printf("usage: %s destination gateway\n", cmd); |
334 | return; | |
335 | } | |
336 | } | |
792b0612 | 337 | sin = (struct sockaddr_in *)&route.rt_dst; |
a2280e64 | 338 | ishost = getaddr(argv[1], &route.rt_dst, &hp, &dest, forcenet); |
b3933da8 MK |
339 | if (forcehost) |
340 | ishost = 1; | |
341 | if (forcenet) | |
342 | ishost = 0; | |
792b0612 | 343 | sin = (struct sockaddr_in *)&route.rt_gateway; |
a2280e64 | 344 | (void) getaddr(argv[2], &route.rt_gateway, &hp, &gateway, 0); |
4bec325f | 345 | route.rt_flags = RTF_UP; |
6edb1941 | 346 | if (ishost) |
4bec325f | 347 | route.rt_flags |= RTF_HOST; |
c4693c18 | 348 | if (metric > 0) |
183ef13b | 349 | route.rt_flags |= RTF_GATEWAY; |
792b0612 MK |
350 | for (attempts = 1; ; attempts++) { |
351 | errno = 0; | |
352 | if ((ret = ioctl(s, *cmd == 'a' ? SIOCADDRT : SIOCDELRT, | |
353 | (caddr_t)&route)) == 0) | |
354 | break; | |
355 | if (errno != ENETUNREACH && errno != ESRCH) | |
356 | break; | |
357 | if (hp && hp->h_addr_list[1]) { | |
358 | hp->h_addr_list++; | |
359 | bcopy(hp->h_addr_list[0], (caddr_t)&sin->sin_addr, | |
360 | hp->h_length); | |
361 | } else | |
362 | break; | |
363 | } | |
364 | oerrno = errno; | |
365 | printf("%s %s %s: gateway %s", cmd, ishost? "host" : "net", | |
366 | dest, gateway); | |
367 | if (attempts > 1 && ret == 0) | |
368 | printf(" (%s)", | |
369 | inet_ntoa(((struct sockaddr_in *)&route.rt_gateway)->sin_addr)); | |
370 | if (ret == 0) | |
371 | printf("\n"); | |
372 | else { | |
373 | printf(": "); | |
374 | fflush(stdout); | |
375 | errno = oerrno; | |
376 | error(0); | |
377 | } | |
4bec325f BJ |
378 | } |
379 | ||
380 | changeroute(argc, argv) | |
381 | int argc; | |
382 | char *argv[]; | |
383 | { | |
384 | printf("not supported\n"); | |
385 | } | |
386 | ||
387 | error(cmd) | |
388 | char *cmd; | |
389 | { | |
a48a48cc | 390 | extern int errno; |
4bec325f | 391 | |
a48a48cc KB |
392 | switch(errno) { |
393 | case ESRCH: | |
4bec325f | 394 | fprintf(stderr, "not in table\n"); |
a48a48cc KB |
395 | break; |
396 | case EBUSY: | |
4bec325f | 397 | fprintf(stderr, "entry in use\n"); |
a48a48cc KB |
398 | break; |
399 | case ENOBUFS: | |
4bec325f | 400 | fprintf(stderr, "routing table overflow\n"); |
a48a48cc KB |
401 | break; |
402 | default: | |
4bec325f | 403 | perror(cmd); |
a48a48cc | 404 | } |
4bec325f BJ |
405 | } |
406 | ||
55e026df MK |
407 | char * |
408 | savestr(s) | |
409 | char *s; | |
410 | { | |
411 | char *sav; | |
412 | ||
413 | sav = malloc(strlen(s) + 1); | |
414 | if (sav == NULL) { | |
415 | fprintf("route: out of memory\n"); | |
416 | exit(1); | |
417 | } | |
418 | strcpy(sav, s); | |
419 | return (sav); | |
420 | } | |
421 | ||
6edb1941 MK |
422 | /* |
423 | * Interpret an argument as a network address of some kind, | |
424 | * returning 1 if a host address, 0 if a network address. | |
425 | */ | |
a2280e64 | 426 | getaddr(s, sin, hpp, name, isnet) |
4bec325f BJ |
427 | char *s; |
428 | struct sockaddr_in *sin; | |
792b0612 | 429 | struct hostent **hpp; |
c28affab | 430 | char **name; |
a2280e64 | 431 | int isnet; |
4bec325f | 432 | { |
accbc2e3 | 433 | struct hostent *hp; |
379dcc38 SL |
434 | struct netent *np; |
435 | u_long val; | |
accbc2e3 | 436 | |
792b0612 | 437 | *hpp = 0; |
89a108e6 SL |
438 | if (strcmp(s, "default") == 0) { |
439 | sin->sin_family = AF_INET; | |
440 | sin->sin_addr = inet_makeaddr(0, INADDR_ANY); | |
55e026df | 441 | *name = "default"; |
6edb1941 | 442 | return(0); |
89a108e6 | 443 | } |
792b0612 | 444 | sin->sin_family = AF_INET; |
a2280e64 MK |
445 | if (isnet == 0) { |
446 | val = inet_addr(s); | |
447 | if (val != -1) { | |
448 | sin->sin_addr.s_addr = val; | |
449 | *name = s; | |
450 | return(inet_lnaof(sin->sin_addr) != INADDR_ANY); | |
451 | } | |
792b0612 MK |
452 | } |
453 | val = inet_network(s); | |
454 | if (val != -1) { | |
455 | sin->sin_addr = inet_makeaddr(val, INADDR_ANY); | |
c28affab | 456 | *name = s; |
792b0612 MK |
457 | return(0); |
458 | } | |
379dcc38 SL |
459 | np = getnetbyname(s); |
460 | if (np) { | |
461 | sin->sin_family = np->n_addrtype; | |
462 | sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); | |
55e026df | 463 | *name = savestr(np->n_name); |
6edb1941 | 464 | return(0); |
379dcc38 | 465 | } |
78160c55 MK |
466 | hp = gethostbyname(s); |
467 | if (hp) { | |
792b0612 | 468 | *hpp = hp; |
78160c55 MK |
469 | sin->sin_family = hp->h_addrtype; |
470 | bcopy(hp->h_addr, &sin->sin_addr, hp->h_length); | |
55e026df | 471 | *name = savestr(hp->h_name); |
78160c55 MK |
472 | return(1); |
473 | } | |
379dcc38 SL |
474 | fprintf(stderr, "%s: bad value\n", s); |
475 | exit(1); | |
183ef13b | 476 | } |
eac76d14 MK |
477 | |
478 | short ns_nullh[] = {0,0,0}; | |
479 | short ns_bh[] = {-1,-1,-1}; | |
480 | ||
481 | char * | |
482 | ns_print(sns) | |
483 | struct sockaddr_ns *sns; | |
484 | { | |
485 | struct ns_addr work; | |
486 | union { union ns_net net_e; u_long long_e; } net; | |
487 | u_short port; | |
488 | static char mybuf[50], cport[10], chost[25]; | |
489 | char *host = ""; | |
490 | register char *p; register u_char *q; u_char *q_lim; | |
491 | ||
492 | work = sns->sns_addr; | |
493 | port = ntohs(work.x_port); | |
494 | work.x_port = 0; | |
495 | net.net_e = work.x_net; | |
496 | if (ns_nullhost(work) && net.long_e == 0) { | |
497 | if (port ) { | |
9bd38ba8 | 498 | (void)sprintf(mybuf, "*.%xH", port); |
eac76d14 MK |
499 | upHex(mybuf); |
500 | } else | |
9bd38ba8 | 501 | (void)sprintf(mybuf, "*.*"); |
eac76d14 MK |
502 | return (mybuf); |
503 | } | |
504 | ||
505 | if (bcmp(ns_bh, work.x_host.c_host, 6) == 0) { | |
506 | host = "any"; | |
507 | } else if (bcmp(ns_nullh, work.x_host.c_host, 6) == 0) { | |
508 | host = "*"; | |
509 | } else { | |
510 | q = work.x_host.c_host; | |
9bd38ba8 | 511 | (void)sprintf(chost, "%02x%02x%02x%02x%02x%02xH", |
eac76d14 MK |
512 | q[0], q[1], q[2], q[3], q[4], q[5]); |
513 | for (p = chost; *p == '0' && p < chost + 12; p++); | |
514 | host = p; | |
515 | } | |
516 | if (port) | |
9bd38ba8 | 517 | (void)sprintf(cport, ".%xH", htons(port)); |
eac76d14 MK |
518 | else |
519 | *cport = 0; | |
520 | ||
9bd38ba8 | 521 | (void)sprintf(mybuf,"%xH.%s%s", ntohl(net.long_e), host, cport); |
eac76d14 MK |
522 | upHex(mybuf); |
523 | return(mybuf); | |
524 | } | |
525 | ||
526 | upHex(p0) | |
527 | char *p0; | |
528 | { | |
529 | register char *p = p0; | |
530 | for (; *p; p++) switch (*p) { | |
531 | ||
532 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': | |
533 | *p += ('A' - 'a'); | |
534 | } | |
535 | } |