1 |
/* $MidnightBSD$ */ |
2 |
/*- |
3 |
* Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org> |
4 |
* based on work by Toshiharu OHNO <tony-o@iij.ad.jp> |
5 |
* Internet Initiative Japan, Inc (IIJ) |
6 |
* All rights reserved. |
7 |
* |
8 |
* Redistribution and use in source and binary forms, with or without |
9 |
* modification, are permitted provided that the following conditions |
10 |
* are met: |
11 |
* 1. Redistributions of source code must retain the above copyright |
12 |
* notice, this list of conditions and the following disclaimer. |
13 |
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
* notice, this list of conditions and the following disclaimer in the |
15 |
* documentation and/or other materials provided with the distribution. |
16 |
* |
17 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
21 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 |
* SUCH DAMAGE. |
28 |
* |
29 |
* $FreeBSD: stable/10/usr.sbin/ppp/route.c 330805 2018-03-12 17:37:38Z eugen $ |
30 |
*/ |
31 |
|
32 |
#include <sys/param.h> |
33 |
#include <sys/socket.h> |
34 |
#include <net/if_types.h> |
35 |
#include <net/route.h> |
36 |
#include <net/if.h> |
37 |
#include <netinet/in.h> |
38 |
#include <arpa/inet.h> |
39 |
#include <net/if_dl.h> |
40 |
#include <netinet/in_systm.h> |
41 |
#include <netinet/ip.h> |
42 |
#include <sys/un.h> |
43 |
|
44 |
#include <errno.h> |
45 |
#include <stdarg.h> |
46 |
#include <stdio.h> |
47 |
#include <stdlib.h> |
48 |
#include <string.h> |
49 |
#include <sys/sysctl.h> |
50 |
#include <termios.h> |
51 |
#include <unistd.h> |
52 |
|
53 |
#include "layer.h" |
54 |
#include "defs.h" |
55 |
#include "command.h" |
56 |
#include "mbuf.h" |
57 |
#include "log.h" |
58 |
#include "iplist.h" |
59 |
#include "timer.h" |
60 |
#include "throughput.h" |
61 |
#include "lqr.h" |
62 |
#include "hdlc.h" |
63 |
#include "fsm.h" |
64 |
#include "lcp.h" |
65 |
#include "ccp.h" |
66 |
#include "link.h" |
67 |
#include "slcompress.h" |
68 |
#include "ncpaddr.h" |
69 |
#include "ipcp.h" |
70 |
#include "filter.h" |
71 |
#include "descriptor.h" |
72 |
#include "mp.h" |
73 |
#ifndef NORADIUS |
74 |
#include "radius.h" |
75 |
#endif |
76 |
#include "ipv6cp.h" |
77 |
#include "ncp.h" |
78 |
#include "bundle.h" |
79 |
#include "route.h" |
80 |
#include "prompt.h" |
81 |
#include "iface.h" |
82 |
#include "id.h" |
83 |
|
84 |
|
85 |
static void |
86 |
p_sockaddr(struct prompt *prompt, struct sockaddr *phost, |
87 |
struct sockaddr *pmask, int width) |
88 |
{ |
89 |
struct ncprange range; |
90 |
char buf[29]; |
91 |
struct sockaddr_dl *dl = (struct sockaddr_dl *)phost; |
92 |
|
93 |
if (log_IsKept(LogDEBUG)) { |
94 |
char tmp[50]; |
95 |
|
96 |
log_Printf(LogDEBUG, "Found the following sockaddr:\n"); |
97 |
log_Printf(LogDEBUG, " Family %d, len %d\n", |
98 |
(int)phost->sa_family, (int)phost->sa_len); |
99 |
inet_ntop(phost->sa_family, phost->sa_data, tmp, sizeof tmp); |
100 |
log_Printf(LogDEBUG, " Addr %s\n", tmp); |
101 |
if (pmask) { |
102 |
inet_ntop(pmask->sa_family, pmask->sa_data, tmp, sizeof tmp); |
103 |
log_Printf(LogDEBUG, " Mask %s\n", tmp); |
104 |
} |
105 |
} |
106 |
|
107 |
switch (phost->sa_family) { |
108 |
case AF_INET: |
109 |
#ifndef NOINET6 |
110 |
case AF_INET6: |
111 |
#endif |
112 |
ncprange_setsa(&range, phost, pmask); |
113 |
if (ncprange_isdefault(&range)) |
114 |
prompt_Printf(prompt, "%-*s ", width - 1, "default"); |
115 |
else |
116 |
prompt_Printf(prompt, "%-*s ", width - 1, ncprange_ntoa(&range)); |
117 |
return; |
118 |
|
119 |
case AF_LINK: |
120 |
if (dl->sdl_nlen) |
121 |
snprintf(buf, sizeof buf, "%.*s", dl->sdl_nlen, dl->sdl_data); |
122 |
else if (dl->sdl_alen) { |
123 |
if (dl->sdl_type == IFT_ETHER) { |
124 |
if (dl->sdl_alen < sizeof buf / 3) { |
125 |
int f; |
126 |
u_char *MAC; |
127 |
|
128 |
MAC = (u_char *)dl->sdl_data + dl->sdl_nlen; |
129 |
for (f = 0; f < dl->sdl_alen; f++) |
130 |
sprintf(buf+f*3, "%02x:", MAC[f]); |
131 |
buf[f*3-1] = '\0'; |
132 |
} else |
133 |
strcpy(buf, "??:??:??:??:??:??"); |
134 |
} else |
135 |
sprintf(buf, "<IFT type %d>", dl->sdl_type); |
136 |
} else if (dl->sdl_slen) |
137 |
sprintf(buf, "<slen %d?>", dl->sdl_slen); |
138 |
else |
139 |
sprintf(buf, "link#%d", dl->sdl_index); |
140 |
break; |
141 |
|
142 |
default: |
143 |
sprintf(buf, "<AF type %d>", phost->sa_family); |
144 |
break; |
145 |
} |
146 |
|
147 |
prompt_Printf(prompt, "%-*s ", width-1, buf); |
148 |
} |
149 |
|
150 |
static struct bits { |
151 |
u_int32_t b_mask; |
152 |
char b_val; |
153 |
} bits[] = { |
154 |
{ RTF_UP, 'U' }, |
155 |
{ RTF_GATEWAY, 'G' }, |
156 |
{ RTF_HOST, 'H' }, |
157 |
{ RTF_REJECT, 'R' }, |
158 |
{ RTF_DYNAMIC, 'D' }, |
159 |
{ RTF_MODIFIED, 'M' }, |
160 |
{ RTF_DONE, 'd' }, |
161 |
{ RTF_XRESOLVE, 'X' }, |
162 |
{ RTF_STATIC, 'S' }, |
163 |
{ RTF_PROTO1, '1' }, |
164 |
{ RTF_PROTO2, '2' }, |
165 |
{ RTF_BLACKHOLE, 'B' }, |
166 |
#ifdef RTF_LLINFO |
167 |
{ RTF_LLINFO, 'L' }, |
168 |
#endif |
169 |
#ifdef RTF_CLONING |
170 |
{ RTF_CLONING, 'C' }, |
171 |
#endif |
172 |
#ifdef RTF_PROTO3 |
173 |
{ RTF_PROTO3, '3' }, |
174 |
#endif |
175 |
#ifdef RTF_BROADCAST |
176 |
{ RTF_BROADCAST, 'b' }, |
177 |
#endif |
178 |
{ 0, '\0' } |
179 |
}; |
180 |
|
181 |
static void |
182 |
p_flags(struct prompt *prompt, u_int32_t f, unsigned max) |
183 |
{ |
184 |
char name[33], *flags; |
185 |
register struct bits *p = bits; |
186 |
|
187 |
if (max > sizeof name - 1) |
188 |
max = sizeof name - 1; |
189 |
|
190 |
for (flags = name; p->b_mask && flags - name < (int)max; p++) |
191 |
if (p->b_mask & f) |
192 |
*flags++ = p->b_val; |
193 |
*flags = '\0'; |
194 |
prompt_Printf(prompt, "%-*.*s", (int)max, (int)max, name); |
195 |
} |
196 |
|
197 |
static int route_nifs = -1; |
198 |
|
199 |
const char * |
200 |
Index2Nam(int idx) |
201 |
{ |
202 |
/* |
203 |
* XXX: Maybe we should select() on the routing socket so that we can |
204 |
* notice interfaces that come & go (PCCARD support). |
205 |
* Or we could even support a signal that resets these so that |
206 |
* the PCCARD insert/remove events can signal ppp. |
207 |
*/ |
208 |
static char **ifs; /* Figure these out once */ |
209 |
static int debug_done; /* Debug once */ |
210 |
|
211 |
if (idx > route_nifs || (idx > 0 && ifs[idx-1] == NULL)) { |
212 |
int mib[6], have, had; |
213 |
size_t needed; |
214 |
char *buf, *ptr, *end; |
215 |
struct sockaddr_dl *dl; |
216 |
struct if_msghdr *ifm; |
217 |
|
218 |
if (ifs) { |
219 |
free(ifs); |
220 |
ifs = NULL; |
221 |
route_nifs = 0; |
222 |
} |
223 |
debug_done = 0; |
224 |
|
225 |
mib[0] = CTL_NET; |
226 |
mib[1] = PF_ROUTE; |
227 |
mib[2] = 0; |
228 |
mib[3] = 0; |
229 |
mib[4] = NET_RT_IFLIST; |
230 |
mib[5] = 0; |
231 |
|
232 |
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { |
233 |
log_Printf(LogERROR, "Index2Nam: sysctl: estimate: %s\n", |
234 |
strerror(errno)); |
235 |
return NumStr(idx, NULL, 0); |
236 |
} |
237 |
if ((buf = malloc(needed)) == NULL) |
238 |
return NumStr(idx, NULL, 0); |
239 |
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { |
240 |
free(buf); |
241 |
return NumStr(idx, NULL, 0); |
242 |
} |
243 |
end = buf + needed; |
244 |
|
245 |
have = 0; |
246 |
for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) { |
247 |
ifm = (struct if_msghdr *)ptr; |
248 |
if (ifm->ifm_type != RTM_IFINFO) |
249 |
continue; |
250 |
dl = (struct sockaddr_dl *)(ifm + 1); |
251 |
if (ifm->ifm_index > 0) { |
252 |
if (ifm->ifm_index > have) { |
253 |
char **newifs; |
254 |
|
255 |
had = have; |
256 |
have = ifm->ifm_index + 5; |
257 |
if (had) |
258 |
newifs = (char **)realloc(ifs, sizeof(char *) * have); |
259 |
else |
260 |
newifs = (char **)malloc(sizeof(char *) * have); |
261 |
if (!newifs) { |
262 |
log_Printf(LogDEBUG, "Index2Nam: %s\n", strerror(errno)); |
263 |
route_nifs = 0; |
264 |
if (ifs) { |
265 |
free(ifs); |
266 |
ifs = NULL; |
267 |
} |
268 |
free(buf); |
269 |
return NumStr(idx, NULL, 0); |
270 |
} |
271 |
ifs = newifs; |
272 |
memset(ifs + had, '\0', sizeof(char *) * (have - had)); |
273 |
} |
274 |
if (ifs[ifm->ifm_index-1] == NULL) { |
275 |
ifs[ifm->ifm_index-1] = (char *)malloc(dl->sdl_nlen+1); |
276 |
if (ifs[ifm->ifm_index-1] == NULL) |
277 |
log_Printf(LogDEBUG, "Skipping interface %d: Out of memory\n", |
278 |
ifm->ifm_index); |
279 |
else { |
280 |
memcpy(ifs[ifm->ifm_index-1], dl->sdl_data, dl->sdl_nlen); |
281 |
ifs[ifm->ifm_index-1][dl->sdl_nlen] = '\0'; |
282 |
if (route_nifs < ifm->ifm_index) |
283 |
route_nifs = ifm->ifm_index; |
284 |
} |
285 |
} |
286 |
} else if (log_IsKept(LogDEBUG)) |
287 |
log_Printf(LogDEBUG, "Skipping out-of-range interface %d!\n", |
288 |
ifm->ifm_index); |
289 |
} |
290 |
free(buf); |
291 |
} |
292 |
|
293 |
if (log_IsKept(LogDEBUG) && !debug_done) { |
294 |
int f; |
295 |
|
296 |
log_Printf(LogDEBUG, "Found the following interfaces:\n"); |
297 |
for (f = 0; f < route_nifs; f++) |
298 |
if (ifs[f] != NULL) |
299 |
log_Printf(LogDEBUG, " Index %d, name \"%s\"\n", f+1, ifs[f]); |
300 |
debug_done = 1; |
301 |
} |
302 |
|
303 |
if (idx < 1 || idx > route_nifs || ifs[idx-1] == NULL) |
304 |
return NumStr(idx, NULL, 0); |
305 |
|
306 |
return ifs[idx-1]; |
307 |
} |
308 |
|
309 |
void |
310 |
route_ParseHdr(struct rt_msghdr *rtm, struct sockaddr *sa[RTAX_MAX]) |
311 |
{ |
312 |
char *wp; |
313 |
int rtax; |
314 |
|
315 |
wp = (char *)(rtm + 1); |
316 |
|
317 |
for (rtax = 0; rtax < RTAX_MAX; rtax++) |
318 |
if (rtm->rtm_addrs & (1 << rtax)) { |
319 |
sa[rtax] = (struct sockaddr *)wp; |
320 |
wp += ROUNDUP(sa[rtax]->sa_len); |
321 |
if (sa[rtax]->sa_family == 0) |
322 |
sa[rtax] = NULL; /* ??? */ |
323 |
} else |
324 |
sa[rtax] = NULL; |
325 |
} |
326 |
|
327 |
int |
328 |
route_Show(struct cmdargs const *arg) |
329 |
{ |
330 |
struct rt_msghdr *rtm; |
331 |
struct sockaddr *sa[RTAX_MAX]; |
332 |
char *sp, *ep, *cp; |
333 |
size_t needed; |
334 |
int mib[6]; |
335 |
|
336 |
mib[0] = CTL_NET; |
337 |
mib[1] = PF_ROUTE; |
338 |
mib[2] = 0; |
339 |
mib[3] = 0; |
340 |
mib[4] = NET_RT_DUMP; |
341 |
mib[5] = 0; |
342 |
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { |
343 |
log_Printf(LogERROR, "route_Show: sysctl: estimate: %s\n", strerror(errno)); |
344 |
return (1); |
345 |
} |
346 |
sp = malloc(needed); |
347 |
if (sp == NULL) |
348 |
return (1); |
349 |
if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) { |
350 |
log_Printf(LogERROR, "route_Show: sysctl: getroute: %s\n", strerror(errno)); |
351 |
free(sp); |
352 |
return (1); |
353 |
} |
354 |
ep = sp + needed; |
355 |
|
356 |
prompt_Printf(arg->prompt, "%-20s%-20sFlags Netif\n", |
357 |
"Destination", "Gateway"); |
358 |
for (cp = sp; cp < ep; cp += rtm->rtm_msglen) { |
359 |
rtm = (struct rt_msghdr *)cp; |
360 |
|
361 |
route_ParseHdr(rtm, sa); |
362 |
|
363 |
if (sa[RTAX_DST] && sa[RTAX_GATEWAY]) { |
364 |
p_sockaddr(arg->prompt, sa[RTAX_DST], sa[RTAX_NETMASK], 20); |
365 |
p_sockaddr(arg->prompt, sa[RTAX_GATEWAY], NULL, 20); |
366 |
|
367 |
p_flags(arg->prompt, rtm->rtm_flags, 6); |
368 |
prompt_Printf(arg->prompt, " %s\n", Index2Nam(rtm->rtm_index)); |
369 |
} else |
370 |
prompt_Printf(arg->prompt, "<can't parse routing entry>\n"); |
371 |
} |
372 |
free(sp); |
373 |
return 0; |
374 |
} |
375 |
|
376 |
/* |
377 |
* Delete routes associated with our interface |
378 |
*/ |
379 |
void |
380 |
route_IfDelete(struct bundle *bundle, int all) |
381 |
{ |
382 |
struct rt_msghdr *rtm; |
383 |
struct sockaddr *sa[RTAX_MAX]; |
384 |
struct ncprange range; |
385 |
int pass; |
386 |
size_t needed; |
387 |
char *sp, *cp, *ep; |
388 |
int mib[6]; |
389 |
|
390 |
log_Printf(LogDEBUG, "route_IfDelete (%d)\n", bundle->iface->index); |
391 |
|
392 |
mib[0] = CTL_NET; |
393 |
mib[1] = PF_ROUTE; |
394 |
mib[2] = 0; |
395 |
mib[3] = 0; |
396 |
mib[4] = NET_RT_DUMP; |
397 |
mib[5] = 0; |
398 |
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { |
399 |
log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n", |
400 |
strerror(errno)); |
401 |
return; |
402 |
} |
403 |
|
404 |
sp = malloc(needed); |
405 |
if (sp == NULL) |
406 |
return; |
407 |
|
408 |
if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) { |
409 |
log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n", |
410 |
strerror(errno)); |
411 |
free(sp); |
412 |
return; |
413 |
} |
414 |
ep = sp + needed; |
415 |
|
416 |
for (pass = 0; pass < 2; pass++) { |
417 |
/* |
418 |
* We do 2 passes. The first deletes all cloned routes. The second |
419 |
* deletes all non-cloned routes. This is done to avoid |
420 |
* potential errors from trying to delete route X after route Y where |
421 |
* route X was cloned from route Y (and is no longer there 'cos it |
422 |
* may have gone with route Y). |
423 |
*/ |
424 |
if (pass == 0) |
425 |
/* So we can't tell ! */ |
426 |
continue; |
427 |
for (cp = sp; cp < ep; cp += rtm->rtm_msglen) { |
428 |
rtm = (struct rt_msghdr *)cp; |
429 |
route_ParseHdr(rtm, sa); |
430 |
if (rtm->rtm_index == bundle->iface->index && |
431 |
sa[RTAX_DST] && sa[RTAX_GATEWAY] && |
432 |
(sa[RTAX_DST]->sa_family == AF_INET |
433 |
#ifndef NOINET6 |
434 |
|| sa[RTAX_DST]->sa_family == AF_INET6 |
435 |
#endif |
436 |
) && |
437 |
(all || (rtm->rtm_flags & RTF_GATEWAY))) { |
438 |
if (log_IsKept(LogDEBUG)) { |
439 |
char gwstr[NCP_ASCIIBUFFERSIZE]; |
440 |
struct ncpaddr gw; |
441 |
ncprange_setsa(&range, sa[RTAX_DST], sa[RTAX_NETMASK]); |
442 |
ncpaddr_setsa(&gw, sa[RTAX_GATEWAY]); |
443 |
snprintf(gwstr, sizeof gwstr, "%s", ncpaddr_ntoa(&gw)); |
444 |
log_Printf(LogDEBUG, "Found %s %s\n", ncprange_ntoa(&range), gwstr); |
445 |
} |
446 |
if (sa[RTAX_GATEWAY]->sa_family == AF_INET || |
447 |
#ifndef NOINET6 |
448 |
sa[RTAX_GATEWAY]->sa_family == AF_INET6 || |
449 |
#endif |
450 |
sa[RTAX_GATEWAY]->sa_family == AF_LINK) { |
451 |
if (pass == 1) { |
452 |
ncprange_setsa(&range, sa[RTAX_DST], sa[RTAX_NETMASK]); |
453 |
rt_Set(bundle, RTM_DELETE, &range, NULL, 0, 0); |
454 |
} else |
455 |
log_Printf(LogDEBUG, "route_IfDelete: Skip it (pass %d)\n", pass); |
456 |
} else |
457 |
log_Printf(LogDEBUG, |
458 |
"route_IfDelete: Can't remove routes for family %d\n", |
459 |
sa[RTAX_GATEWAY]->sa_family); |
460 |
} |
461 |
} |
462 |
} |
463 |
free(sp); |
464 |
} |
465 |
|
466 |
|
467 |
/* |
468 |
* Update the MTU on all routes for the given interface |
469 |
*/ |
470 |
void |
471 |
route_UpdateMTU(struct bundle *bundle) |
472 |
{ |
473 |
struct rt_msghdr *rtm; |
474 |
struct sockaddr *sa[RTAX_MAX]; |
475 |
struct ncprange dst; |
476 |
size_t needed; |
477 |
char *sp, *cp, *ep; |
478 |
int mib[6]; |
479 |
|
480 |
log_Printf(LogDEBUG, "route_UpdateMTU (%d)\n", bundle->iface->index); |
481 |
|
482 |
mib[0] = CTL_NET; |
483 |
mib[1] = PF_ROUTE; |
484 |
mib[2] = 0; |
485 |
mib[3] = 0; |
486 |
mib[4] = NET_RT_DUMP; |
487 |
mib[5] = 0; |
488 |
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { |
489 |
log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n", |
490 |
strerror(errno)); |
491 |
return; |
492 |
} |
493 |
|
494 |
sp = malloc(needed); |
495 |
if (sp == NULL) |
496 |
return; |
497 |
|
498 |
if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) { |
499 |
log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n", |
500 |
strerror(errno)); |
501 |
free(sp); |
502 |
return; |
503 |
} |
504 |
ep = sp + needed; |
505 |
|
506 |
for (cp = sp; cp < ep; cp += rtm->rtm_msglen) { |
507 |
rtm = (struct rt_msghdr *)cp; |
508 |
route_ParseHdr(rtm, sa); |
509 |
if (sa[RTAX_DST] && (sa[RTAX_DST]->sa_family == AF_INET |
510 |
#ifndef NOINET6 |
511 |
|| sa[RTAX_DST]->sa_family == AF_INET6 |
512 |
#endif |
513 |
) && |
514 |
sa[RTAX_GATEWAY] && rtm->rtm_index == bundle->iface->index) { |
515 |
if (log_IsKept(LogTCPIP)) { |
516 |
ncprange_setsa(&dst, sa[RTAX_DST], sa[RTAX_NETMASK]); |
517 |
log_Printf(LogTCPIP, "route_UpdateMTU: Netif: %d (%s), dst %s," |
518 |
" mtu %lu\n", rtm->rtm_index, Index2Nam(rtm->rtm_index), |
519 |
ncprange_ntoa(&dst), bundle->iface->mtu); |
520 |
} |
521 |
rt_Update(bundle, sa[RTAX_DST], sa[RTAX_GATEWAY], sa[RTAX_NETMASK], |
522 |
sa[RTAX_IFP], sa[RTAX_IFA]); |
523 |
} |
524 |
} |
525 |
|
526 |
free(sp); |
527 |
} |
528 |
|
529 |
int |
530 |
GetIfIndex(char *name) |
531 |
{ |
532 |
int idx; |
533 |
|
534 |
idx = 1; |
535 |
while (route_nifs == -1 || idx < route_nifs) |
536 |
if (strcmp(Index2Nam(idx), name) == 0) |
537 |
return idx; |
538 |
else |
539 |
idx++; |
540 |
return -1; |
541 |
} |
542 |
|
543 |
void |
544 |
route_Change(struct bundle *bundle, struct sticky_route *r, |
545 |
const struct ncpaddr *me, const struct ncpaddr *peer) |
546 |
{ |
547 |
struct ncpaddr dst; |
548 |
|
549 |
for (; r; r = r->next) { |
550 |
ncprange_getaddr(&r->dst, &dst); |
551 |
if (ncpaddr_family(me) == AF_INET) { |
552 |
if ((r->type & ROUTE_DSTMYADDR) && !ncpaddr_equal(&dst, me)) { |
553 |
rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0); |
554 |
ncprange_sethost(&r->dst, me); |
555 |
if (r->type & ROUTE_GWHISADDR) |
556 |
ncpaddr_copy(&r->gw, peer); |
557 |
} else if ((r->type & ROUTE_DSTHISADDR) && !ncpaddr_equal(&dst, peer)) { |
558 |
rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0); |
559 |
ncprange_sethost(&r->dst, peer); |
560 |
if (r->type & ROUTE_GWHISADDR) |
561 |
ncpaddr_copy(&r->gw, peer); |
562 |
} else if ((r->type & ROUTE_DSTDNS0) && !ncpaddr_equal(&dst, peer)) { |
563 |
if (bundle->ncp.ipcp.ns.dns[0].s_addr == INADDR_NONE) |
564 |
continue; |
565 |
rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0); |
566 |
if (r->type & ROUTE_GWHISADDR) |
567 |
ncpaddr_copy(&r->gw, peer); |
568 |
} else if ((r->type & ROUTE_DSTDNS1) && !ncpaddr_equal(&dst, peer)) { |
569 |
if (bundle->ncp.ipcp.ns.dns[1].s_addr == INADDR_NONE) |
570 |
continue; |
571 |
rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0); |
572 |
if (r->type & ROUTE_GWHISADDR) |
573 |
ncpaddr_copy(&r->gw, peer); |
574 |
} else if ((r->type & ROUTE_GWHISADDR) && !ncpaddr_equal(&r->gw, peer)) |
575 |
ncpaddr_copy(&r->gw, peer); |
576 |
#ifndef NOINET6 |
577 |
} else if (ncpaddr_family(me) == AF_INET6) { |
578 |
if ((r->type & ROUTE_DSTMYADDR6) && !ncpaddr_equal(&dst, me)) { |
579 |
rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0); |
580 |
ncprange_sethost(&r->dst, me); |
581 |
if (r->type & ROUTE_GWHISADDR) |
582 |
ncpaddr_copy(&r->gw, peer); |
583 |
} else if ((r->type & ROUTE_DSTHISADDR6) && !ncpaddr_equal(&dst, peer)) { |
584 |
rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0); |
585 |
ncprange_sethost(&r->dst, peer); |
586 |
if (r->type & ROUTE_GWHISADDR) |
587 |
ncpaddr_copy(&r->gw, peer); |
588 |
} else if ((r->type & ROUTE_GWHISADDR6) && !ncpaddr_equal(&r->gw, peer)) |
589 |
ncpaddr_copy(&r->gw, peer); |
590 |
#endif |
591 |
} |
592 |
rt_Set(bundle, RTM_ADD, &r->dst, &r->gw, 1, 0); |
593 |
} |
594 |
} |
595 |
|
596 |
void |
597 |
route_Add(struct sticky_route **rp, int type, const struct ncprange *dst, |
598 |
const struct ncpaddr *gw) |
599 |
{ |
600 |
struct sticky_route *r; |
601 |
int dsttype = type & ROUTE_DSTANY; |
602 |
|
603 |
r = NULL; |
604 |
while (*rp) { |
605 |
if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) || |
606 |
(!dsttype && ncprange_equal(&(*rp)->dst, dst))) { |
607 |
/* Oops, we already have this route - unlink it */ |
608 |
free(r); /* impossible really */ |
609 |
r = *rp; |
610 |
*rp = r->next; |
611 |
} else |
612 |
rp = &(*rp)->next; |
613 |
} |
614 |
|
615 |
if (r == NULL) { |
616 |
r = (struct sticky_route *)malloc(sizeof(struct sticky_route)); |
617 |
if (r == NULL) { |
618 |
log_Printf(LogERROR, "route_Add: Out of memory!\n"); |
619 |
return; |
620 |
} |
621 |
} |
622 |
r->type = type; |
623 |
r->next = NULL; |
624 |
ncprange_copy(&r->dst, dst); |
625 |
ncpaddr_copy(&r->gw, gw); |
626 |
*rp = r; |
627 |
} |
628 |
|
629 |
void |
630 |
route_Delete(struct sticky_route **rp, int type, const struct ncprange *dst) |
631 |
{ |
632 |
struct sticky_route *r; |
633 |
int dsttype = type & ROUTE_DSTANY; |
634 |
|
635 |
for (; *rp; rp = &(*rp)->next) { |
636 |
if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) || |
637 |
(!dsttype && ncprange_equal(dst, &(*rp)->dst))) { |
638 |
r = *rp; |
639 |
*rp = r->next; |
640 |
free(r); |
641 |
break; |
642 |
} |
643 |
} |
644 |
} |
645 |
|
646 |
void |
647 |
route_DeleteAll(struct sticky_route **rp) |
648 |
{ |
649 |
struct sticky_route *r, *rn; |
650 |
|
651 |
for (r = *rp; r; r = rn) { |
652 |
rn = r->next; |
653 |
free(r); |
654 |
} |
655 |
*rp = NULL; |
656 |
} |
657 |
|
658 |
void |
659 |
route_ShowSticky(struct prompt *p, struct sticky_route *r, const char *tag, |
660 |
int indent) |
661 |
{ |
662 |
int tlen = strlen(tag); |
663 |
|
664 |
if (tlen + 2 > indent) |
665 |
prompt_Printf(p, "%s:\n%*s", tag, indent, ""); |
666 |
else |
667 |
prompt_Printf(p, "%s:%*s", tag, indent - tlen - 1, ""); |
668 |
|
669 |
for (; r; r = r->next) { |
670 |
prompt_Printf(p, "%*sadd ", tlen ? 0 : indent, ""); |
671 |
tlen = 0; |
672 |
if (r->type & ROUTE_DSTMYADDR) |
673 |
prompt_Printf(p, "MYADDR"); |
674 |
else if (r->type & ROUTE_DSTMYADDR6) |
675 |
prompt_Printf(p, "MYADDR6"); |
676 |
else if (r->type & ROUTE_DSTHISADDR) |
677 |
prompt_Printf(p, "HISADDR"); |
678 |
else if (r->type & ROUTE_DSTHISADDR6) |
679 |
prompt_Printf(p, "HISADDR6"); |
680 |
else if (r->type & ROUTE_DSTDNS0) |
681 |
prompt_Printf(p, "DNS0"); |
682 |
else if (r->type & ROUTE_DSTDNS1) |
683 |
prompt_Printf(p, "DNS1"); |
684 |
else if (ncprange_isdefault(&r->dst)) |
685 |
prompt_Printf(p, "default"); |
686 |
else |
687 |
prompt_Printf(p, "%s", ncprange_ntoa(&r->dst)); |
688 |
|
689 |
if (r->type & ROUTE_GWHISADDR) |
690 |
prompt_Printf(p, " HISADDR\n"); |
691 |
else if (r->type & ROUTE_GWHISADDR6) |
692 |
prompt_Printf(p, " HISADDR6\n"); |
693 |
else |
694 |
prompt_Printf(p, " %s\n", ncpaddr_ntoa(&r->gw)); |
695 |
} |
696 |
} |
697 |
|
698 |
struct rtmsg { |
699 |
struct rt_msghdr m_rtm; |
700 |
char m_space[256]; |
701 |
}; |
702 |
|
703 |
static size_t |
704 |
memcpy_roundup(char *cp, const void *data, size_t len) |
705 |
{ |
706 |
size_t padlen; |
707 |
|
708 |
padlen = ROUNDUP(len); |
709 |
memcpy(cp, data, len); |
710 |
if (padlen > len) |
711 |
memset(cp + len, '\0', padlen - len); |
712 |
|
713 |
return padlen; |
714 |
} |
715 |
|
716 |
#if defined(__KAME__) && !defined(NOINET6) |
717 |
static void |
718 |
add_scope(struct sockaddr *sa, int ifindex) |
719 |
{ |
720 |
struct sockaddr_in6 *sa6; |
721 |
|
722 |
if (sa->sa_family != AF_INET6) |
723 |
return; |
724 |
sa6 = (struct sockaddr_in6 *)sa; |
725 |
if (!IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) && |
726 |
!IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr)) |
727 |
return; |
728 |
if (*(u_int16_t *)&sa6->sin6_addr.s6_addr[2] != 0) |
729 |
return; |
730 |
*(u_int16_t *)&sa6->sin6_addr.s6_addr[2] = htons(ifindex); |
731 |
} |
732 |
#endif |
733 |
|
734 |
int |
735 |
rt_Set(struct bundle *bundle, int cmd, const struct ncprange *dst, |
736 |
const struct ncpaddr *gw, int bang, int quiet) |
737 |
{ |
738 |
struct rtmsg rtmes; |
739 |
int s, nb, wb; |
740 |
char *cp; |
741 |
const char *cmdstr; |
742 |
struct sockaddr_storage sadst, samask, sagw; |
743 |
int result = 1; |
744 |
|
745 |
if (bang) |
746 |
cmdstr = (cmd == RTM_ADD ? "Add!" : "Delete!"); |
747 |
else |
748 |
cmdstr = (cmd == RTM_ADD ? "Add" : "Delete"); |
749 |
s = ID0socket(PF_ROUTE, SOCK_RAW, 0); |
750 |
if (s < 0) { |
751 |
log_Printf(LogERROR, "rt_Set: socket(): %s\n", strerror(errno)); |
752 |
return result; |
753 |
} |
754 |
memset(&rtmes, '\0', sizeof rtmes); |
755 |
rtmes.m_rtm.rtm_version = RTM_VERSION; |
756 |
rtmes.m_rtm.rtm_type = cmd; |
757 |
rtmes.m_rtm.rtm_addrs = RTA_DST; |
758 |
rtmes.m_rtm.rtm_seq = ++bundle->routing_seq; |
759 |
rtmes.m_rtm.rtm_pid = getpid(); |
760 |
rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; |
761 |
|
762 |
if (cmd == RTM_ADD) { |
763 |
if (bundle->ncp.cfg.sendpipe > 0) { |
764 |
rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.cfg.sendpipe; |
765 |
rtmes.m_rtm.rtm_inits |= RTV_SPIPE; |
766 |
} |
767 |
if (bundle->ncp.cfg.recvpipe > 0) { |
768 |
rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.cfg.recvpipe; |
769 |
rtmes.m_rtm.rtm_inits |= RTV_RPIPE; |
770 |
} |
771 |
} |
772 |
|
773 |
ncprange_getsa(dst, &sadst, &samask); |
774 |
#if defined(__KAME__) && !defined(NOINET6) |
775 |
add_scope((struct sockaddr *)&sadst, bundle->iface->index); |
776 |
#endif |
777 |
|
778 |
cp = rtmes.m_space; |
779 |
cp += memcpy_roundup(cp, &sadst, sadst.ss_len); |
780 |
if (cmd == RTM_ADD) { |
781 |
if (gw == NULL) { |
782 |
log_Printf(LogERROR, "rt_Set: Program error\n"); |
783 |
close(s); |
784 |
return result; |
785 |
} |
786 |
ncpaddr_getsa(gw, &sagw); |
787 |
#if defined(__KAME__) && !defined(NOINET6) |
788 |
add_scope((struct sockaddr *)&sagw, bundle->iface->index); |
789 |
#endif |
790 |
if (ncpaddr_isdefault(gw)) { |
791 |
if (!quiet) |
792 |
log_Printf(LogERROR, "rt_Set: Cannot add a route with" |
793 |
" gateway 0.0.0.0\n"); |
794 |
close(s); |
795 |
return result; |
796 |
} else { |
797 |
cp += memcpy_roundup(cp, &sagw, sagw.ss_len); |
798 |
rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY; |
799 |
} |
800 |
} |
801 |
|
802 |
if (!ncprange_ishost(dst)) { |
803 |
cp += memcpy_roundup(cp, &samask, samask.ss_len); |
804 |
rtmes.m_rtm.rtm_addrs |= RTA_NETMASK; |
805 |
} |
806 |
|
807 |
nb = cp - (char *)&rtmes; |
808 |
rtmes.m_rtm.rtm_msglen = nb; |
809 |
wb = ID0write(s, &rtmes, nb); |
810 |
if (wb < 0) { |
811 |
log_Printf(LogTCPIP, "rt_Set failure:\n"); |
812 |
log_Printf(LogTCPIP, "rt_Set: Cmd = %s\n", cmdstr); |
813 |
log_Printf(LogTCPIP, "rt_Set: Dst = %s\n", ncprange_ntoa(dst)); |
814 |
if (gw != NULL) |
815 |
log_Printf(LogTCPIP, "rt_Set: Gateway = %s\n", ncpaddr_ntoa(gw)); |
816 |
failed: |
817 |
if (cmd == RTM_ADD && (rtmes.m_rtm.rtm_errno == EEXIST || |
818 |
(rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST))) { |
819 |
if (!bang) { |
820 |
log_Printf(LogWARN, "Add route failed: %s already exists\n", |
821 |
ncprange_ntoa(dst)); |
822 |
result = 0; /* Don't add to our dynamic list */ |
823 |
} else { |
824 |
rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE; |
825 |
if ((wb = ID0write(s, &rtmes, nb)) < 0) |
826 |
goto failed; |
827 |
} |
828 |
} else if (cmd == RTM_DELETE && |
829 |
(rtmes.m_rtm.rtm_errno == ESRCH || |
830 |
(rtmes.m_rtm.rtm_errno == 0 && errno == ESRCH))) { |
831 |
if (!bang) |
832 |
log_Printf(LogWARN, "Del route failed: %s: Non-existent\n", |
833 |
ncprange_ntoa(dst)); |
834 |
} else if (rtmes.m_rtm.rtm_errno == 0) { |
835 |
if (!quiet || errno != ENETUNREACH) |
836 |
log_Printf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr, |
837 |
ncprange_ntoa(dst), strerror(errno)); |
838 |
} else |
839 |
log_Printf(LogWARN, "%s route failed: %s: %s\n", |
840 |
cmdstr, ncprange_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno)); |
841 |
} |
842 |
|
843 |
if (log_IsKept(LogDEBUG)) { |
844 |
char gwstr[NCP_ASCIIBUFFERSIZE]; |
845 |
|
846 |
if (gw) |
847 |
snprintf(gwstr, sizeof gwstr, "%s", ncpaddr_ntoa(gw)); |
848 |
else |
849 |
snprintf(gwstr, sizeof gwstr, "<none>"); |
850 |
log_Printf(LogDEBUG, "wrote %d: cmd = %s, dst = %s, gateway = %s\n", |
851 |
wb, cmdstr, ncprange_ntoa(dst), gwstr); |
852 |
} |
853 |
close(s); |
854 |
|
855 |
return result; |
856 |
} |
857 |
|
858 |
void |
859 |
rt_Update(struct bundle *bundle, const struct sockaddr *dst, |
860 |
const struct sockaddr *gw, const struct sockaddr *mask, |
861 |
const struct sockaddr *ifp, const struct sockaddr *ifa) |
862 |
{ |
863 |
struct ncprange ncpdst; |
864 |
struct rtmsg rtmes; |
865 |
char *p; |
866 |
int s, wb; |
867 |
|
868 |
s = ID0socket(PF_ROUTE, SOCK_RAW, 0); |
869 |
if (s < 0) { |
870 |
log_Printf(LogERROR, "rt_Update: socket(): %s\n", strerror(errno)); |
871 |
return; |
872 |
} |
873 |
|
874 |
memset(&rtmes, '\0', sizeof rtmes); |
875 |
rtmes.m_rtm.rtm_version = RTM_VERSION; |
876 |
rtmes.m_rtm.rtm_type = RTM_CHANGE; |
877 |
rtmes.m_rtm.rtm_addrs = 0; |
878 |
rtmes.m_rtm.rtm_seq = ++bundle->routing_seq; |
879 |
rtmes.m_rtm.rtm_pid = getpid(); |
880 |
rtmes.m_rtm.rtm_flags = RTF_UP | RTF_STATIC; |
881 |
|
882 |
if (bundle->ncp.cfg.sendpipe > 0) { |
883 |
rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.cfg.sendpipe; |
884 |
rtmes.m_rtm.rtm_inits |= RTV_SPIPE; |
885 |
} |
886 |
|
887 |
if (bundle->ncp.cfg.recvpipe > 0) { |
888 |
rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.cfg.recvpipe; |
889 |
rtmes.m_rtm.rtm_inits |= RTV_RPIPE; |
890 |
} |
891 |
|
892 |
rtmes.m_rtm.rtm_rmx.rmx_mtu = bundle->iface->mtu; |
893 |
rtmes.m_rtm.rtm_inits |= RTV_MTU; |
894 |
p = rtmes.m_space; |
895 |
|
896 |
if (dst) { |
897 |
rtmes.m_rtm.rtm_addrs |= RTA_DST; |
898 |
p += memcpy_roundup(p, dst, dst->sa_len); |
899 |
} |
900 |
|
901 |
if (gw) { |
902 |
rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY; |
903 |
p += memcpy_roundup(p, gw, gw->sa_len); |
904 |
} |
905 |
|
906 |
if (mask) { |
907 |
rtmes.m_rtm.rtm_addrs |= RTA_NETMASK; |
908 |
p += memcpy_roundup(p, mask, mask->sa_len); |
909 |
} |
910 |
|
911 |
if (ifa && ifp && ifp->sa_family == AF_LINK) { |
912 |
rtmes.m_rtm.rtm_addrs |= RTA_IFP; |
913 |
p += memcpy_roundup(p, ifp, ifp->sa_len); |
914 |
rtmes.m_rtm.rtm_addrs |= RTA_IFA; |
915 |
p += memcpy_roundup(p, ifa, ifa->sa_len); |
916 |
} |
917 |
|
918 |
rtmes.m_rtm.rtm_msglen = p - (char *)&rtmes; |
919 |
|
920 |
wb = ID0write(s, &rtmes, rtmes.m_rtm.rtm_msglen); |
921 |
if (wb < 0) { |
922 |
ncprange_setsa(&ncpdst, dst, mask); |
923 |
|
924 |
log_Printf(LogTCPIP, "rt_Update failure:\n"); |
925 |
log_Printf(LogTCPIP, "rt_Update: Dst = %s\n", ncprange_ntoa(&ncpdst)); |
926 |
|
927 |
if (rtmes.m_rtm.rtm_errno == 0) |
928 |
log_Printf(LogWARN, "%s: Change route failed: errno: %s\n", |
929 |
ncprange_ntoa(&ncpdst), strerror(errno)); |
930 |
else |
931 |
log_Printf(LogWARN, "%s: Change route failed: %s\n", |
932 |
ncprange_ntoa(&ncpdst), strerror(rtmes.m_rtm.rtm_errno)); |
933 |
} |
934 |
close(s); |
935 |
} |