iof-bird-daemon / sysdep / bsd / krt-sock.c @ ae80a2de
History | View | Annotate | Download (22.5 KB)
1 |
/*
|
---|---|
2 |
* BIRD -- BSD Routing Table Syncing
|
3 |
*
|
4 |
* (c) 2004 Ondrej Filip <feela@network.cz>
|
5 |
*
|
6 |
* Can be freely distributed and used under the terms of the GNU GPL.
|
7 |
*/
|
8 |
|
9 |
#include <stdio.h> |
10 |
#include <stdlib.h> |
11 |
#include <ctype.h> |
12 |
#include <fcntl.h> |
13 |
#include <unistd.h> |
14 |
#include <sys/param.h> |
15 |
#include <sys/types.h> |
16 |
#include <sys/socket.h> |
17 |
#include <sys/sysctl.h> |
18 |
#include <sys/ioctl.h> |
19 |
#include <netinet/in.h> |
20 |
#include <net/route.h> |
21 |
#include <net/if.h> |
22 |
#include <net/if_dl.h> |
23 |
|
24 |
#undef LOCAL_DEBUG
|
25 |
|
26 |
#include "nest/bird.h" |
27 |
#include "nest/iface.h" |
28 |
#include "nest/route.h" |
29 |
#include "nest/protocol.h" |
30 |
#include "nest/iface.h" |
31 |
#include "lib/timer.h" |
32 |
#include "lib/unix.h" |
33 |
#include "lib/krt.h" |
34 |
#include "lib/string.h" |
35 |
#include "lib/socket.h" |
36 |
|
37 |
|
38 |
/*
|
39 |
* There are significant differences in multiple tables support between BSD variants.
|
40 |
*
|
41 |
* OpenBSD has table_id field for routes in route socket protocol, therefore all
|
42 |
* tables could be managed by one kernel socket. FreeBSD lacks such field,
|
43 |
* therefore multiple sockets (locked to specific table using SO_SETFIB socket
|
44 |
* option) must be used.
|
45 |
*
|
46 |
* Both FreeBSD and OpenBSD uses separate scans for each table. In OpenBSD,
|
47 |
* table_id is specified explicitly as sysctl scan argument, while in FreeBSD it
|
48 |
* is handled implicitly by changing default table using setfib() syscall.
|
49 |
*
|
50 |
* KRT_SHARED_SOCKET - use shared kernel socked instead of one for each krt_proto
|
51 |
* KRT_USE_SETFIB_SCAN - use setfib() for sysctl() route scan
|
52 |
* KRT_USE_SETFIB_SOCK - use SO_SETFIB socket option for kernel sockets
|
53 |
* KRT_USE_SYSCTL_7 - use 7-th arg of sysctl() as table id for route scans
|
54 |
* KRT_USE_SYSCTL_NET_FIBS - use net.fibs sysctl() for dynamic max number of fibs
|
55 |
*/
|
56 |
|
57 |
#ifdef __FreeBSD__
|
58 |
#define KRT_MAX_TABLES 256 |
59 |
#define KRT_USE_SETFIB_SCAN
|
60 |
#define KRT_USE_SETFIB_SOCK
|
61 |
#define KRT_USE_SYSCTL_NET_FIBS
|
62 |
#endif
|
63 |
|
64 |
#ifdef __OpenBSD__
|
65 |
#define KRT_MAX_TABLES (RT_TABLEID_MAX+1) |
66 |
#define KRT_SHARED_SOCKET
|
67 |
#define KRT_USE_SYSCTL_7
|
68 |
#endif
|
69 |
|
70 |
#ifndef KRT_MAX_TABLES
|
71 |
#define KRT_MAX_TABLES 1 |
72 |
#endif
|
73 |
|
74 |
|
75 |
|
76 |
/* Dynamic max number of tables */
|
77 |
|
78 |
int krt_max_tables;
|
79 |
|
80 |
#ifdef KRT_USE_SYSCTL_NET_FIBS
|
81 |
|
82 |
static int |
83 |
krt_get_max_tables(void)
|
84 |
{ |
85 |
int fibs;
|
86 |
size_t fibs_len = sizeof(fibs);
|
87 |
|
88 |
if (sysctlbyname("net.fibs", &fibs, &fibs_len, NULL, 0) < 0) |
89 |
{ |
90 |
log(L_WARN "KRT: unable to get max number of fib tables: %m");
|
91 |
return 1; |
92 |
} |
93 |
|
94 |
return MIN(fibs, KRT_MAX_TABLES);
|
95 |
} |
96 |
|
97 |
#else
|
98 |
|
99 |
static int |
100 |
krt_get_max_tables(void)
|
101 |
{ |
102 |
return KRT_MAX_TABLES;
|
103 |
} |
104 |
|
105 |
#endif /* KRT_USE_SYSCTL_NET_FIBS */ |
106 |
|
107 |
|
108 |
/* setfib() syscall for FreeBSD scans */
|
109 |
|
110 |
#ifdef KRT_USE_SETFIB_SCAN
|
111 |
|
112 |
/*
|
113 |
static int krt_default_fib;
|
114 |
|
115 |
static int
|
116 |
krt_get_active_fib(void)
|
117 |
{
|
118 |
int fib;
|
119 |
size_t fib_len = sizeof(fib);
|
120 |
|
121 |
if (sysctlbyname("net.my_fibnum", &fib, &fib_len, NULL, 0) < 0)
|
122 |
{
|
123 |
log(L_WARN "KRT: unable to get active fib number: %m");
|
124 |
return 0;
|
125 |
}
|
126 |
|
127 |
return fib;
|
128 |
}
|
129 |
*/
|
130 |
|
131 |
extern int setfib(int fib); |
132 |
|
133 |
#endif /* KRT_USE_SETFIB_SCAN */ |
134 |
|
135 |
|
136 |
/* table_id -> krt_proto map */
|
137 |
|
138 |
#ifdef KRT_SHARED_SOCKET
|
139 |
static struct krt_proto *krt_table_map[KRT_MAX_TABLES]; |
140 |
#endif
|
141 |
|
142 |
|
143 |
/* Route socket message processing */
|
144 |
|
145 |
int
|
146 |
krt_capable(rte *e) |
147 |
{ |
148 |
rta *a = e->attrs; |
149 |
|
150 |
return
|
151 |
a->cast == RTC_UNICAST && |
152 |
(a->dest == RTD_ROUTER |
153 |
|| a->dest == RTD_DEVICE |
154 |
#ifdef RTF_REJECT
|
155 |
|| a->dest == RTD_UNREACHABLE |
156 |
#endif
|
157 |
#ifdef RTF_BLACKHOLE
|
158 |
|| a->dest == RTD_BLACKHOLE |
159 |
#endif
|
160 |
); |
161 |
} |
162 |
|
163 |
#ifndef RTAX_MAX
|
164 |
#define RTAX_MAX 8 |
165 |
#endif
|
166 |
|
167 |
struct ks_msg
|
168 |
{ |
169 |
struct rt_msghdr rtm;
|
170 |
struct sockaddr_storage buf[RTAX_MAX];
|
171 |
}; |
172 |
|
173 |
#define ROUNDUP(a) \
|
174 |
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) |
175 |
|
176 |
#define NEXTADDR(w, u) \
|
177 |
if (msg.rtm.rtm_addrs & (w)) {\
|
178 |
l = ROUNDUP(((struct sockaddr *)&(u))->sa_len);\
|
179 |
memmove(body, &(u), l); body += l;} |
180 |
|
181 |
#define GETADDR(p, F) \
|
182 |
bzero(p, sizeof(*p));\
|
183 |
if ((addrs & (F)) && ((struct sockaddr *)body)->sa_len) {\ |
184 |
uint l = ROUNDUP(((struct sockaddr *)body)->sa_len);\
|
185 |
memcpy(p, body, (l > sizeof(*p) ? sizeof(*p) : l));\ |
186 |
body += l;} |
187 |
|
188 |
static int |
189 |
krt_send_route(struct krt_proto *p, int cmd, rte *e) |
190 |
{ |
191 |
net *net = e->net; |
192 |
rta *a = e->attrs; |
193 |
static int msg_seq; |
194 |
struct iface *j, *i = a->iface;
|
195 |
int l;
|
196 |
struct ks_msg msg;
|
197 |
char *body = (char *)msg.buf; |
198 |
sockaddr gate, mask, dst; |
199 |
ip_addr gw; |
200 |
|
201 |
DBG("krt-sock: send %I/%d via %I\n", net->n.prefix, net->n.pxlen, a->gw);
|
202 |
|
203 |
bzero(&msg,sizeof (struct rt_msghdr)); |
204 |
msg.rtm.rtm_version = RTM_VERSION; |
205 |
msg.rtm.rtm_type = cmd; |
206 |
msg.rtm.rtm_seq = msg_seq++; |
207 |
msg.rtm.rtm_addrs = RTA_DST; |
208 |
msg.rtm.rtm_flags = RTF_UP | RTF_PROTO1; |
209 |
|
210 |
if (net->n.pxlen == MAX_PREFIX_LENGTH)
|
211 |
msg.rtm.rtm_flags |= RTF_HOST; |
212 |
else
|
213 |
msg.rtm.rtm_addrs |= RTA_NETMASK; |
214 |
|
215 |
#ifdef KRT_SHARED_SOCKET
|
216 |
msg.rtm.rtm_tableid = KRT_CF->sys.table_id; |
217 |
#endif
|
218 |
|
219 |
#ifdef RTF_REJECT
|
220 |
if(a->dest == RTD_UNREACHABLE)
|
221 |
msg.rtm.rtm_flags |= RTF_REJECT; |
222 |
#endif
|
223 |
#ifdef RTF_BLACKHOLE
|
224 |
if(a->dest == RTD_BLACKHOLE)
|
225 |
msg.rtm.rtm_flags |= RTF_BLACKHOLE; |
226 |
#endif
|
227 |
|
228 |
/* This is really very nasty, but I'm not able
|
229 |
* to add "(reject|blackhole)" route without
|
230 |
* gateway set
|
231 |
*/
|
232 |
if(!i)
|
233 |
{ |
234 |
i = HEAD(iface_list); |
235 |
|
236 |
WALK_LIST(j, iface_list) |
237 |
{ |
238 |
if (j->flags & IF_LOOPBACK)
|
239 |
{ |
240 |
i = j; |
241 |
break;
|
242 |
} |
243 |
} |
244 |
} |
245 |
|
246 |
gw = a->gw; |
247 |
|
248 |
#ifdef IPV6
|
249 |
/* Embed interface ID to link-local address */
|
250 |
if (ipa_is_link_local(gw))
|
251 |
_I0(gw) = 0xfe800000 | (i->index & 0x0000ffff); |
252 |
#endif
|
253 |
|
254 |
sockaddr_fill(&dst, BIRD_AF, net->n.prefix, NULL, 0); |
255 |
sockaddr_fill(&mask, BIRD_AF, ipa_mkmask(net->n.pxlen), NULL, 0); |
256 |
sockaddr_fill(&gate, BIRD_AF, gw, NULL, 0); |
257 |
|
258 |
switch (a->dest)
|
259 |
{ |
260 |
case RTD_ROUTER:
|
261 |
msg.rtm.rtm_flags |= RTF_GATEWAY; |
262 |
msg.rtm.rtm_addrs |= RTA_GATEWAY; |
263 |
break;
|
264 |
|
265 |
#ifdef RTF_REJECT
|
266 |
case RTD_UNREACHABLE:
|
267 |
#endif
|
268 |
#ifdef RTF_BLACKHOLE
|
269 |
case RTD_BLACKHOLE:
|
270 |
#endif
|
271 |
case RTD_DEVICE:
|
272 |
if(i)
|
273 |
{ |
274 |
#ifdef RTF_CLONING
|
275 |
if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS) /* PTP */ |
276 |
msg.rtm.rtm_flags |= RTF_CLONING; |
277 |
#endif
|
278 |
|
279 |
if(!i->addr) {
|
280 |
log(L_ERR "KRT: interface %s has no IP addess", i->name);
|
281 |
return -1; |
282 |
} |
283 |
|
284 |
sockaddr_fill(&gate, BIRD_AF, i->addr->ip, NULL, 0); |
285 |
msg.rtm.rtm_addrs |= RTA_GATEWAY; |
286 |
} |
287 |
break;
|
288 |
default:
|
289 |
bug("krt-sock: unknown flags, but not filtered");
|
290 |
} |
291 |
|
292 |
msg.rtm.rtm_index = i->index; |
293 |
|
294 |
NEXTADDR(RTA_DST, dst); |
295 |
NEXTADDR(RTA_GATEWAY, gate); |
296 |
NEXTADDR(RTA_NETMASK, mask); |
297 |
|
298 |
l = body - (char *)&msg;
|
299 |
msg.rtm.rtm_msglen = l; |
300 |
|
301 |
if ((l = write(p->sys.sk->fd, (char *)&msg, l)) < 0) { |
302 |
log(L_ERR "KRT: Error sending route %I/%d to kernel: %m", net->n.prefix, net->n.pxlen);
|
303 |
return -1; |
304 |
} |
305 |
|
306 |
return 0; |
307 |
} |
308 |
|
309 |
void
|
310 |
krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old,
|
311 |
struct ea_list *eattrs UNUSED)
|
312 |
{ |
313 |
int err = 0; |
314 |
|
315 |
if (old)
|
316 |
krt_send_route(p, RTM_DELETE, old); |
317 |
|
318 |
if (new)
|
319 |
err = krt_send_route(p, RTM_ADD, new); |
320 |
|
321 |
if (err < 0) |
322 |
n->n.flags |= KRF_SYNC_ERROR; |
323 |
else
|
324 |
n->n.flags &= ~KRF_SYNC_ERROR; |
325 |
} |
326 |
|
327 |
#define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0) |
328 |
|
329 |
static void |
330 |
krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) |
331 |
{ |
332 |
/* p is NULL iff KRT_SHARED_SOCKET and !scan */
|
333 |
|
334 |
rte *e; |
335 |
net *net; |
336 |
sockaddr dst, gate, mask; |
337 |
ip_addr idst, igate, imask; |
338 |
void *body = (char *)msg->buf; |
339 |
int new = (msg->rtm.rtm_type != RTM_DELETE);
|
340 |
char *errmsg = "KRT: Invalid route received"; |
341 |
int flags = msg->rtm.rtm_flags;
|
342 |
int addrs = msg->rtm.rtm_addrs;
|
343 |
int src;
|
344 |
byte src2; |
345 |
|
346 |
if (!(flags & RTF_UP) && scan)
|
347 |
SKIP("not up in scan\n");
|
348 |
|
349 |
if (!(flags & RTF_DONE) && !scan)
|
350 |
SKIP("not done in async\n");
|
351 |
|
352 |
if (flags & RTF_LLINFO)
|
353 |
SKIP("link-local\n");
|
354 |
|
355 |
#ifdef KRT_SHARED_SOCKET
|
356 |
if (!scan)
|
357 |
{ |
358 |
int table_id = msg->rtm.rtm_tableid;
|
359 |
p = (table_id < KRT_MAX_TABLES) ? krt_table_map[table_id] : NULL;
|
360 |
|
361 |
if (!p)
|
362 |
SKIP("unknown table id %d\n", table_id);
|
363 |
} |
364 |
#endif
|
365 |
|
366 |
GETADDR(&dst, RTA_DST); |
367 |
GETADDR(&gate, RTA_GATEWAY); |
368 |
GETADDR(&mask, RTA_NETMASK); |
369 |
|
370 |
if (dst.sa.sa_family != BIRD_AF)
|
371 |
SKIP("invalid DST");
|
372 |
|
373 |
idst = ipa_from_sa(&dst); |
374 |
imask = ipa_from_sa(&mask); |
375 |
igate = (gate.sa.sa_family == BIRD_AF) ? ipa_from_sa(&gate) : IPA_NONE; |
376 |
|
377 |
/* We do not test family for RTA_NETMASK, because BSD sends us
|
378 |
some strange values, but interpreting them as IPv4/IPv6 works */
|
379 |
|
380 |
|
381 |
int c = ipa_classify_net(idst);
|
382 |
if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) |
383 |
SKIP("strange class/scope\n");
|
384 |
|
385 |
int pxlen = (flags & RTF_HOST) ? MAX_PREFIX_LENGTH : ipa_masklen(imask);
|
386 |
if (pxlen < 0) |
387 |
{ log(L_ERR "%s (%I) - netmask %I", errmsg, idst, imask); return; } |
388 |
|
389 |
if ((flags & RTF_GATEWAY) && ipa_zero(igate))
|
390 |
{ log(L_ERR "%s (%I/%d) - missing gateway", errmsg, idst, pxlen); return; } |
391 |
|
392 |
u32 self_mask = RTF_PROTO1; |
393 |
u32 alien_mask = RTF_STATIC | RTF_PROTO1 | RTF_GATEWAY; |
394 |
|
395 |
src2 = (flags & RTF_STATIC) ? 1 : 0; |
396 |
src2 |= (flags & RTF_PROTO1) ? 2 : 0; |
397 |
|
398 |
#ifdef RTF_PROTO2
|
399 |
alien_mask |= RTF_PROTO2; |
400 |
src2 |= (flags & RTF_PROTO2) ? 4 : 0; |
401 |
#endif
|
402 |
|
403 |
#ifdef RTF_PROTO3
|
404 |
alien_mask |= RTF_PROTO3; |
405 |
src2 |= (flags & RTF_PROTO3) ? 8 : 0; |
406 |
#endif
|
407 |
|
408 |
#ifdef RTF_REJECT
|
409 |
alien_mask |= RTF_REJECT; |
410 |
#endif
|
411 |
|
412 |
#ifdef RTF_BLACKHOLE
|
413 |
alien_mask |= RTF_BLACKHOLE; |
414 |
#endif
|
415 |
|
416 |
if (flags & (RTF_DYNAMIC | RTF_MODIFIED))
|
417 |
src = KRT_SRC_REDIRECT; |
418 |
else if (flags & self_mask) |
419 |
{ |
420 |
if (!scan)
|
421 |
SKIP("echo\n");
|
422 |
src = KRT_SRC_BIRD; |
423 |
} |
424 |
else if (flags & alien_mask) |
425 |
src = KRT_SRC_ALIEN; |
426 |
else
|
427 |
src = KRT_SRC_KERNEL; |
428 |
|
429 |
net = net_get(p->p.table, idst, pxlen); |
430 |
|
431 |
rta a = { |
432 |
.src = p->p.main_source, |
433 |
.source = RTS_INHERIT, |
434 |
.scope = SCOPE_UNIVERSE, |
435 |
.cast = RTC_UNICAST |
436 |
}; |
437 |
|
438 |
/* reject/blackhole routes have also set RTF_GATEWAY,
|
439 |
we wil check them first. */
|
440 |
|
441 |
#ifdef RTF_REJECT
|
442 |
if(flags & RTF_REJECT) {
|
443 |
a.dest = RTD_UNREACHABLE; |
444 |
goto done;
|
445 |
} |
446 |
#endif
|
447 |
|
448 |
#ifdef RTF_BLACKHOLE
|
449 |
if(flags & RTF_BLACKHOLE) {
|
450 |
a.dest = RTD_BLACKHOLE; |
451 |
goto done;
|
452 |
} |
453 |
#endif
|
454 |
|
455 |
a.iface = if_find_by_index(msg->rtm.rtm_index); |
456 |
if (!a.iface)
|
457 |
{ |
458 |
log(L_ERR "KRT: Received route %I/%d with unknown ifindex %u",
|
459 |
net->n.prefix, net->n.pxlen, msg->rtm.rtm_index); |
460 |
return;
|
461 |
} |
462 |
|
463 |
if (flags & RTF_GATEWAY)
|
464 |
{ |
465 |
neighbor *ng; |
466 |
a.dest = RTD_ROUTER; |
467 |
a.gw = igate; |
468 |
|
469 |
#ifdef IPV6
|
470 |
/* Clean up embedded interface ID returned in link-local address */
|
471 |
if (ipa_is_link_local(a.gw))
|
472 |
_I0(a.gw) = 0xfe800000;
|
473 |
#endif
|
474 |
|
475 |
ng = neigh_find2(&p->p, &a.gw, a.iface, 0);
|
476 |
if (!ng || (ng->scope == SCOPE_HOST))
|
477 |
{ |
478 |
/* Ignore routes with next-hop 127.0.0.1, host routes with such
|
479 |
next-hop appear on OpenBSD for address aliases. */
|
480 |
if (ipa_classify(a.gw) == (IADDR_HOST | SCOPE_HOST))
|
481 |
return;
|
482 |
|
483 |
log(L_ERR "KRT: Received route %I/%d with strange next-hop %I",
|
484 |
net->n.prefix, net->n.pxlen, a.gw); |
485 |
return;
|
486 |
} |
487 |
} |
488 |
else
|
489 |
a.dest = RTD_DEVICE; |
490 |
|
491 |
done:
|
492 |
e = rte_get_temp(&a); |
493 |
e->net = net; |
494 |
e->u.krt.src = src; |
495 |
e->u.krt.proto = src2; |
496 |
|
497 |
/* These are probably too Linux-specific */
|
498 |
e->u.krt.type = 0;
|
499 |
e->u.krt.metric = 0;
|
500 |
|
501 |
if (scan)
|
502 |
krt_got_route(p, e); |
503 |
else
|
504 |
krt_got_route_async(p, e, new); |
505 |
} |
506 |
|
507 |
static void |
508 |
krt_read_ifannounce(struct ks_msg *msg)
|
509 |
{ |
510 |
struct if_announcemsghdr *ifam = (struct if_announcemsghdr *)&msg->rtm; |
511 |
|
512 |
if (ifam->ifan_what == IFAN_ARRIVAL)
|
513 |
{ |
514 |
/* Not enough info to create the iface, so we just trigger iface scan */
|
515 |
kif_request_scan(); |
516 |
} |
517 |
else if (ifam->ifan_what == IFAN_DEPARTURE) |
518 |
{ |
519 |
struct iface *iface = if_find_by_index(ifam->ifan_index);
|
520 |
|
521 |
/* Interface is destroyed */
|
522 |
if (!iface)
|
523 |
{ |
524 |
DBG("KRT: unknown interface (%s, #%d) going down. Ignoring\n", ifam->ifan_name, ifam->ifan_index);
|
525 |
return;
|
526 |
} |
527 |
|
528 |
if_delete(iface); |
529 |
} |
530 |
|
531 |
DBG("KRT: IFANNOUNCE what: %d index %d name %s\n", ifam->ifan_what, ifam->ifan_index, ifam->ifan_name);
|
532 |
} |
533 |
|
534 |
static void |
535 |
krt_read_ifinfo(struct ks_msg *msg, int scan) |
536 |
{ |
537 |
struct if_msghdr *ifm = (struct if_msghdr *)&msg->rtm; |
538 |
void *body = (void *)(ifm + 1); |
539 |
struct sockaddr_dl *dl = NULL; |
540 |
uint i; |
541 |
struct iface *iface = NULL, f = {}; |
542 |
int fl = ifm->ifm_flags;
|
543 |
int nlen = 0; |
544 |
|
545 |
for (i = 1; i<=RTA_IFP; i <<= 1) |
546 |
{ |
547 |
if (i & ifm->ifm_addrs)
|
548 |
{ |
549 |
if (i == RTA_IFP)
|
550 |
{ |
551 |
dl = (struct sockaddr_dl *)body;
|
552 |
break;
|
553 |
} |
554 |
body += ROUNDUP(((struct sockaddr *)&(body))->sa_len);
|
555 |
} |
556 |
} |
557 |
|
558 |
if (dl && (dl->sdl_family != AF_LINK))
|
559 |
{ |
560 |
log(L_WARN "Ignoring strange IFINFO");
|
561 |
return;
|
562 |
} |
563 |
|
564 |
if (dl)
|
565 |
nlen = MIN(sizeof(f.name)-1, dl->sdl_nlen); |
566 |
|
567 |
/* Note that asynchronous IFINFO messages do not contain iface
|
568 |
name, so we have to found an existing iface by iface index */
|
569 |
|
570 |
iface = if_find_by_index(ifm->ifm_index); |
571 |
if (!iface)
|
572 |
{ |
573 |
/* New interface */
|
574 |
if (!dl)
|
575 |
return; /* No interface name, ignoring */ |
576 |
|
577 |
memcpy(f.name, dl->sdl_data, nlen); |
578 |
DBG("New interface '%s' found\n", f.name);
|
579 |
} |
580 |
else if (dl && memcmp(iface->name, dl->sdl_data, nlen)) |
581 |
{ |
582 |
/* Interface renamed */
|
583 |
if_delete(iface); |
584 |
memcpy(f.name, dl->sdl_data, nlen); |
585 |
} |
586 |
else
|
587 |
{ |
588 |
/* Old interface */
|
589 |
memcpy(f.name, iface->name, sizeof(f.name));
|
590 |
} |
591 |
|
592 |
f.index = ifm->ifm_index; |
593 |
f.mtu = ifm->ifm_data.ifi_mtu; |
594 |
|
595 |
if (fl & IFF_UP)
|
596 |
f.flags |= IF_ADMIN_UP; |
597 |
if (ifm->ifm_data.ifi_link_state != LINK_STATE_DOWN)
|
598 |
f.flags |= IF_LINK_UP; /* up or unknown */
|
599 |
if (fl & IFF_LOOPBACK) /* Loopback */ |
600 |
f.flags |= IF_MULTIACCESS | IF_LOOPBACK | IF_IGNORE; |
601 |
else if (fl & IFF_POINTOPOINT) /* PtP */ |
602 |
f.flags |= IF_MULTICAST; |
603 |
else if (fl & IFF_BROADCAST) /* Broadcast */ |
604 |
f.flags |= IF_MULTIACCESS | IF_BROADCAST | IF_MULTICAST; |
605 |
else
|
606 |
f.flags |= IF_MULTIACCESS; /* NBMA */
|
607 |
|
608 |
iface = if_update(&f); |
609 |
|
610 |
if (!scan)
|
611 |
if_end_partial_update(iface); |
612 |
} |
613 |
|
614 |
static void |
615 |
krt_read_addr(struct ks_msg *msg, int scan) |
616 |
{ |
617 |
struct ifa_msghdr *ifam = (struct ifa_msghdr *)&msg->rtm; |
618 |
void *body = (void *)(ifam + 1); |
619 |
sockaddr addr, mask, brd; |
620 |
struct iface *iface = NULL; |
621 |
struct ifa ifa;
|
622 |
struct sockaddr null;
|
623 |
ip_addr iaddr, imask, ibrd; |
624 |
int addrs = ifam->ifam_addrs;
|
625 |
int scope, masklen = -1; |
626 |
int new = (ifam->ifam_type == RTM_NEWADDR);
|
627 |
|
628 |
/* Strange messages with zero (invalid) ifindex appear on OpenBSD */
|
629 |
if (ifam->ifam_index == 0) |
630 |
return;
|
631 |
|
632 |
if(!(iface = if_find_by_index(ifam->ifam_index)))
|
633 |
{ |
634 |
log(L_ERR "KIF: Received address message for unknown interface %d", ifam->ifam_index);
|
635 |
return;
|
636 |
} |
637 |
|
638 |
GETADDR (&null, RTA_DST); |
639 |
GETADDR (&null, RTA_GATEWAY); |
640 |
GETADDR (&mask, RTA_NETMASK); |
641 |
GETADDR (&null, RTA_GENMASK); |
642 |
GETADDR (&null, RTA_IFP); |
643 |
GETADDR (&addr, RTA_IFA); |
644 |
GETADDR (&null, RTA_AUTHOR); |
645 |
GETADDR (&brd, RTA_BRD); |
646 |
|
647 |
/* Some other family address */
|
648 |
if (addr.sa.sa_family != BIRD_AF)
|
649 |
return;
|
650 |
|
651 |
iaddr = ipa_from_sa(&addr); |
652 |
imask = ipa_from_sa(&mask); |
653 |
ibrd = ipa_from_sa(&brd); |
654 |
|
655 |
|
656 |
if ((masklen = ipa_masklen(imask)) < 0) |
657 |
{ |
658 |
log(L_ERR "KIF: Invalid masklen %I for %s", imask, iface->name);
|
659 |
return;
|
660 |
} |
661 |
|
662 |
#ifdef IPV6
|
663 |
/* Clean up embedded interface ID returned in link-local address */
|
664 |
|
665 |
if (ipa_is_link_local(iaddr))
|
666 |
_I0(iaddr) = 0xfe800000;
|
667 |
|
668 |
if (ipa_is_link_local(ibrd))
|
669 |
_I0(ibrd) = 0xfe800000;
|
670 |
#endif
|
671 |
|
672 |
|
673 |
bzero(&ifa, sizeof(ifa));
|
674 |
ifa.iface = iface; |
675 |
ifa.ip = iaddr; |
676 |
ifa.pxlen = masklen; |
677 |
|
678 |
scope = ipa_classify(ifa.ip); |
679 |
if (scope < 0) |
680 |
{ |
681 |
log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, iface->name);
|
682 |
return;
|
683 |
} |
684 |
ifa.scope = scope & IADDR_SCOPE_MASK; |
685 |
|
686 |
if (masklen < BITS_PER_IP_ADDRESS)
|
687 |
{ |
688 |
ifa.prefix = ipa_and(ifa.ip, ipa_mkmask(masklen)); |
689 |
|
690 |
if (masklen == (BITS_PER_IP_ADDRESS - 1)) |
691 |
ifa.opposite = ipa_opposite_m1(ifa.ip); |
692 |
|
693 |
#ifndef IPV6
|
694 |
if (masklen == (BITS_PER_IP_ADDRESS - 2)) |
695 |
ifa.opposite = ipa_opposite_m2(ifa.ip); |
696 |
#endif
|
697 |
|
698 |
if (iface->flags & IF_BROADCAST)
|
699 |
ifa.brd = ibrd; |
700 |
|
701 |
if (!(iface->flags & IF_MULTIACCESS))
|
702 |
ifa.opposite = ibrd; |
703 |
} |
704 |
else if (!(iface->flags & IF_MULTIACCESS) && ipa_nonzero(ibrd)) |
705 |
{ |
706 |
ifa.prefix = ifa.opposite = ibrd; |
707 |
ifa.flags |= IA_PEER; |
708 |
} |
709 |
else
|
710 |
{ |
711 |
ifa.prefix = ifa.ip; |
712 |
ifa.flags |= IA_HOST; |
713 |
} |
714 |
|
715 |
if (new)
|
716 |
ifa_update(&ifa); |
717 |
else
|
718 |
ifa_delete(&ifa); |
719 |
|
720 |
if (!scan)
|
721 |
if_end_partial_update(iface); |
722 |
} |
723 |
|
724 |
static void |
725 |
krt_read_msg(struct proto *p, struct ks_msg *msg, int scan) |
726 |
{ |
727 |
/* p is NULL iff KRT_SHARED_SOCKET and !scan */
|
728 |
|
729 |
switch (msg->rtm.rtm_type)
|
730 |
{ |
731 |
case RTM_GET:
|
732 |
if(!scan) return; |
733 |
case RTM_ADD:
|
734 |
case RTM_DELETE:
|
735 |
case RTM_CHANGE:
|
736 |
krt_read_route(msg, (struct krt_proto *)p, scan);
|
737 |
break;
|
738 |
case RTM_IFANNOUNCE:
|
739 |
krt_read_ifannounce(msg); |
740 |
break;
|
741 |
case RTM_IFINFO:
|
742 |
krt_read_ifinfo(msg, scan); |
743 |
break;
|
744 |
case RTM_NEWADDR:
|
745 |
case RTM_DELADDR:
|
746 |
krt_read_addr(msg, scan); |
747 |
break;
|
748 |
default:
|
749 |
break;
|
750 |
} |
751 |
} |
752 |
|
753 |
|
754 |
/* Sysctl based scans */
|
755 |
|
756 |
static byte *krt_buffer;
|
757 |
static size_t krt_buflen, krt_bufmin;
|
758 |
static struct proto *krt_buffer_owner; |
759 |
|
760 |
static byte *
|
761 |
krt_buffer_update(struct proto *p, size_t *needed)
|
762 |
{ |
763 |
size_t req = *needed; |
764 |
|
765 |
if ((req > krt_buflen) ||
|
766 |
((p == krt_buffer_owner) && (req < krt_bufmin))) |
767 |
{ |
768 |
/* min buflen is 32 kB, step is 8 kB, or 128 kB if > 1 MB */
|
769 |
size_t step = (req < 0x100000) ? 0x2000 : 0x20000; |
770 |
krt_buflen = (req < 0x6000) ? 0x8000 : (req + step); |
771 |
krt_bufmin = (req < 0x8000) ? 0 : (req - 2*step); |
772 |
|
773 |
if (krt_buffer)
|
774 |
mb_free(krt_buffer); |
775 |
krt_buffer = mb_alloc(krt_pool, krt_buflen); |
776 |
krt_buffer_owner = p; |
777 |
} |
778 |
|
779 |
*needed = krt_buflen; |
780 |
return krt_buffer;
|
781 |
} |
782 |
|
783 |
static void |
784 |
krt_buffer_release(struct proto *p)
|
785 |
{ |
786 |
if (p == krt_buffer_owner)
|
787 |
{ |
788 |
mb_free(krt_buffer); |
789 |
krt_buffer = NULL;
|
790 |
krt_buflen = 0;
|
791 |
krt_buffer_owner = 0;
|
792 |
} |
793 |
} |
794 |
|
795 |
static void |
796 |
krt_sysctl_scan(struct proto *p, int cmd, int table_id) |
797 |
{ |
798 |
byte *buf, *next; |
799 |
int mib[7], mcnt; |
800 |
size_t needed; |
801 |
struct ks_msg *m;
|
802 |
int retries = 3; |
803 |
int rv;
|
804 |
|
805 |
mib[0] = CTL_NET;
|
806 |
mib[1] = PF_ROUTE;
|
807 |
mib[2] = 0; |
808 |
mib[3] = BIRD_AF;
|
809 |
mib[4] = cmd;
|
810 |
mib[5] = 0; |
811 |
mcnt = 6;
|
812 |
|
813 |
#ifdef KRT_USE_SYSCTL_7
|
814 |
if (table_id >= 0) |
815 |
{ |
816 |
mib[6] = table_id;
|
817 |
mcnt = 7;
|
818 |
} |
819 |
#endif
|
820 |
|
821 |
#ifdef KRT_USE_SETFIB_SCAN
|
822 |
if (table_id > 0) |
823 |
if (setfib(table_id) < 0) |
824 |
{ |
825 |
log(L_ERR "KRT: setfib(%d) failed: %m", table_id);
|
826 |
return;
|
827 |
} |
828 |
#endif
|
829 |
|
830 |
try:
|
831 |
rv = sysctl(mib, mcnt, NULL, &needed, NULL, 0); |
832 |
if (rv < 0) |
833 |
{ |
834 |
/* OpenBSD returns EINVAL for not yet used tables */
|
835 |
if ((errno == EINVAL) && (table_id > 0)) |
836 |
goto exit;
|
837 |
|
838 |
log(L_ERR "KRT: Route scan estimate failed: %m");
|
839 |
goto exit;
|
840 |
} |
841 |
|
842 |
/* The table is empty */
|
843 |
if (needed == 0) |
844 |
goto exit;
|
845 |
|
846 |
buf = krt_buffer_update(p, &needed); |
847 |
|
848 |
rv = sysctl(mib, mcnt, buf, &needed, NULL, 0); |
849 |
if (rv < 0) |
850 |
{ |
851 |
/* The buffer size changed since last sysctl ('needed' is not changed) */
|
852 |
if ((errno == ENOMEM) && retries--)
|
853 |
goto try;
|
854 |
|
855 |
log(L_ERR "KRT: Route scan failed: %m");
|
856 |
goto exit;
|
857 |
} |
858 |
|
859 |
#ifdef KRT_USE_SETFIB_SCAN
|
860 |
if (table_id > 0) |
861 |
if (setfib(0) < 0) |
862 |
die("KRT: setfib(%d) failed: %m", 0); |
863 |
#endif
|
864 |
|
865 |
/* Process received messages */
|
866 |
for (next = buf; next < (buf + needed); next += m->rtm.rtm_msglen)
|
867 |
{ |
868 |
m = (struct ks_msg *)next;
|
869 |
krt_read_msg(p, m, 1);
|
870 |
} |
871 |
|
872 |
return;
|
873 |
|
874 |
exit:
|
875 |
krt_buffer_release(p); |
876 |
|
877 |
#ifdef KRT_USE_SETFIB_SCAN
|
878 |
if (table_id > 0) |
879 |
if (setfib(0) < 0) |
880 |
die("KRT: setfib(%d) failed: %m", 0); |
881 |
#endif
|
882 |
} |
883 |
|
884 |
void
|
885 |
krt_do_scan(struct krt_proto *p)
|
886 |
{ |
887 |
krt_sysctl_scan(&p->p, NET_RT_DUMP, KRT_CF->sys.table_id); |
888 |
} |
889 |
|
890 |
void
|
891 |
kif_do_scan(struct kif_proto *p)
|
892 |
{ |
893 |
if_start_update(); |
894 |
krt_sysctl_scan(&p->p, NET_RT_IFLIST, -1);
|
895 |
if_end_update(); |
896 |
} |
897 |
|
898 |
|
899 |
/* Kernel sockets */
|
900 |
|
901 |
static int |
902 |
krt_sock_hook(sock *sk, int size UNUSED)
|
903 |
{ |
904 |
struct ks_msg msg;
|
905 |
int l = read(sk->fd, (char *)&msg, sizeof(msg)); |
906 |
|
907 |
if (l <= 0) |
908 |
log(L_ERR "krt-sock: read failed");
|
909 |
else
|
910 |
krt_read_msg((struct proto *) sk->data, &msg, 0); |
911 |
|
912 |
return 0; |
913 |
} |
914 |
|
915 |
static sock *
|
916 |
krt_sock_open(pool *pool, void *data, int table_id) |
917 |
{ |
918 |
sock *sk; |
919 |
int fd;
|
920 |
|
921 |
fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); |
922 |
if (fd < 0) |
923 |
die("Cannot open kernel socket for routes");
|
924 |
|
925 |
#ifdef KRT_USE_SETFIB_SOCK
|
926 |
if (table_id > 0) |
927 |
{ |
928 |
if (setsockopt(fd, SOL_SOCKET, SO_SETFIB, &table_id, sizeof(table_id)) < 0) |
929 |
die("Cannot set FIB %d for kernel socket: %m", table_id);
|
930 |
} |
931 |
#endif
|
932 |
|
933 |
sk = sk_new(pool); |
934 |
sk->type = SK_MAGIC; |
935 |
sk->rx_hook = krt_sock_hook; |
936 |
sk->fd = fd; |
937 |
sk->data = data; |
938 |
|
939 |
if (sk_open(sk) < 0) |
940 |
bug("krt-sock: sk_open failed");
|
941 |
|
942 |
return sk;
|
943 |
} |
944 |
|
945 |
|
946 |
#ifdef KRT_SHARED_SOCKET
|
947 |
|
948 |
static sock *krt_sock;
|
949 |
static int krt_sock_count; |
950 |
|
951 |
|
952 |
static void |
953 |
krt_sock_open_shared(void)
|
954 |
{ |
955 |
if (!krt_sock_count)
|
956 |
krt_sock = krt_sock_open(krt_pool, NULL, -1); |
957 |
|
958 |
krt_sock_count++; |
959 |
} |
960 |
|
961 |
static void |
962 |
krt_sock_close_shared(void)
|
963 |
{ |
964 |
krt_sock_count--; |
965 |
|
966 |
if (!krt_sock_count)
|
967 |
{ |
968 |
rfree(krt_sock); |
969 |
krt_sock = NULL;
|
970 |
} |
971 |
} |
972 |
|
973 |
void
|
974 |
krt_sys_start(struct krt_proto *p)
|
975 |
{ |
976 |
krt_table_map[KRT_CF->sys.table_id] = p; |
977 |
|
978 |
krt_sock_open_shared(); |
979 |
p->sys.sk = krt_sock; |
980 |
} |
981 |
|
982 |
void
|
983 |
krt_sys_shutdown(struct krt_proto *p)
|
984 |
{ |
985 |
krt_sock_close_shared(); |
986 |
p->sys.sk = NULL;
|
987 |
|
988 |
krt_table_map[KRT_CF->sys.table_id] = NULL;
|
989 |
|
990 |
krt_buffer_release(&p->p); |
991 |
} |
992 |
|
993 |
#else
|
994 |
|
995 |
void
|
996 |
krt_sys_start(struct krt_proto *p)
|
997 |
{ |
998 |
p->sys.sk = krt_sock_open(p->p.pool, p, KRT_CF->sys.table_id); |
999 |
} |
1000 |
|
1001 |
void
|
1002 |
krt_sys_shutdown(struct krt_proto *p)
|
1003 |
{ |
1004 |
rfree(p->sys.sk); |
1005 |
p->sys.sk = NULL;
|
1006 |
|
1007 |
krt_buffer_release(&p->p); |
1008 |
} |
1009 |
|
1010 |
#endif /* KRT_SHARED_SOCKET */ |
1011 |
|
1012 |
|
1013 |
/* KRT configuration callbacks */
|
1014 |
|
1015 |
static u32 krt_table_cf[(KRT_MAX_TABLES+31) / 32]; |
1016 |
|
1017 |
int
|
1018 |
krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o) |
1019 |
{ |
1020 |
return n->sys.table_id == o->sys.table_id;
|
1021 |
} |
1022 |
|
1023 |
void
|
1024 |
krt_sys_preconfig(struct config *c UNUSED)
|
1025 |
{ |
1026 |
krt_max_tables = krt_get_max_tables(); |
1027 |
bzero(&krt_table_cf, sizeof(krt_table_cf));
|
1028 |
} |
1029 |
|
1030 |
void
|
1031 |
krt_sys_postconfig(struct krt_config *x)
|
1032 |
{ |
1033 |
u32 *tbl = krt_table_cf; |
1034 |
int id = x->sys.table_id;
|
1035 |
|
1036 |
if (tbl[id/32] & (1 << (id%32))) |
1037 |
cf_error("Multiple kernel syncers defined for table #%d", id);
|
1038 |
|
1039 |
tbl[id/32] |= (1 << (id%32)); |
1040 |
} |
1041 |
|
1042 |
void krt_sys_init_config(struct krt_config *c) |
1043 |
{ |
1044 |
c->sys.table_id = 0; /* Default table */ |
1045 |
} |
1046 |
|
1047 |
void krt_sys_copy_config(struct krt_config *d, struct krt_config *s) |
1048 |
{ |
1049 |
d->sys.table_id = s->sys.table_id; |
1050 |
} |
1051 |
|
1052 |
|
1053 |
/* KIF misc code */
|
1054 |
|
1055 |
void
|
1056 |
kif_sys_start(struct kif_proto *p UNUSED)
|
1057 |
{ |
1058 |
} |
1059 |
|
1060 |
void
|
1061 |
kif_sys_shutdown(struct kif_proto *p)
|
1062 |
{ |
1063 |
krt_buffer_release(&p->p); |
1064 |
} |
1065 |
|
1066 |
|
1067 |
struct ifa *
|
1068 |
kif_get_primary_ip(struct iface *i)
|
1069 |
{ |
1070 |
#ifndef IPV6
|
1071 |
static int fd = -1; |
1072 |
|
1073 |
if (fd < 0) |
1074 |
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
1075 |
|
1076 |
struct ifreq ifr;
|
1077 |
memset(&ifr, 0, sizeof(ifr)); |
1078 |
strncpy(ifr.ifr_name, i->name, IFNAMSIZ); |
1079 |
|
1080 |
int rv = ioctl(fd, SIOCGIFADDR, (char *) &ifr); |
1081 |
if (rv < 0) |
1082 |
return NULL; |
1083 |
|
1084 |
ip_addr addr; |
1085 |
struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; |
1086 |
memcpy(&addr, &sin->sin_addr.s_addr, sizeof(ip_addr));
|
1087 |
ipa_ntoh(addr); |
1088 |
|
1089 |
struct ifa *a;
|
1090 |
WALK_LIST(a, i->addrs) |
1091 |
{ |
1092 |
if (ipa_equal(a->ip, addr))
|
1093 |
return a;
|
1094 |
} |
1095 |
#endif
|
1096 |
|
1097 |
return NULL; |
1098 |
} |