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