krt-sock.c 22.7 KB
Newer Older
1
/*
2
 *	BIRD -- BSD Routing Table Syncing
3 4 5 6 7 8 9
 *
 *	(c) 2004 Ondrej Filip <feela@network.cz>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#include <stdio.h>
10
#include <stdlib.h>
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/route.h>
#include <net/if.h>
#include <net/if_dl.h>

#undef LOCAL_DEBUG

#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "lib/timer.h"
#include "lib/unix.h"
#include "lib/krt.h"
#include "lib/string.h"
#include "lib/socket.h"

37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
/*
 * There are significant differences in multiple tables support between BSD variants.
 *
 * OpenBSD has table_id field for routes in route socket protocol, therefore all
 * tables could be managed by one kernel socket. FreeBSD lacks such field,
 * therefore multiple sockets (locked to specific table using SO_SETFIB socket
 * option) must be used.
 *
 * Both FreeBSD and OpenBSD uses separate scans for each table. In OpenBSD,
 * table_id is specified explicitly as sysctl scan argument, while in FreeBSD it
 * is handled implicitly by changing default table using setfib() syscall.
 *
 * KRT_SHARED_SOCKET	- use shared kernel socked instead of one for each krt_proto
 * KRT_USE_SETFIB_SCAN	- use setfib() for sysctl() route scan
 * KRT_USE_SETFIB_SOCK	- use SO_SETFIB socket option for kernel sockets
 * KRT_USE_SYSCTL_7	- use 7-th arg of sysctl() as table id for route scans
 * KRT_USE_SYSCTL_NET_FIBS - use net.fibs sysctl() for dynamic max number of fibs
 */

#ifdef __FreeBSD__
#define KRT_MAX_TABLES 256
#define KRT_USE_SETFIB_SCAN
#define KRT_USE_SETFIB_SOCK
#define KRT_USE_SYSCTL_NET_FIBS
62 63
#endif

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
#ifdef __OpenBSD__
#define KRT_MAX_TABLES (RT_TABLEID_MAX+1)
#define KRT_SHARED_SOCKET
#define KRT_USE_SYSCTL_7
#endif

#ifndef KRT_MAX_TABLES
#define KRT_MAX_TABLES 1
#endif



/* Dynamic max number of tables */

int krt_max_tables;

#ifdef KRT_USE_SYSCTL_NET_FIBS

static int
krt_get_max_tables(void)
84
{
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
  int fibs;
  size_t fibs_len = sizeof(fibs);

  if (sysctlbyname("net.fibs", &fibs, &fibs_len, NULL, 0) < 0)
  {
    log(L_WARN "KRT: unable to get max number of fib tables: %m");
    return 1;
  }

  return MIN(fibs, KRT_MAX_TABLES);
}

#else

static int
krt_get_max_tables(void)
{
  return KRT_MAX_TABLES;
}

#endif /* KRT_USE_SYSCTL_NET_FIBS */


/* setfib() syscall for FreeBSD scans */

#ifdef KRT_USE_SETFIB_SCAN

/*
static int krt_default_fib;

static int
krt_get_active_fib(void)
{
  int fib;
  size_t fib_len = sizeof(fib);

  if (sysctlbyname("net.my_fibnum", &fib, &fib_len, NULL, 0) < 0)
  {
    log(L_WARN "KRT: unable to get active fib number: %m");
    return 0;
  }

  return fib;
}
*/

extern int setfib(int fib);
132

133
#endif /* KRT_USE_SETFIB_SCAN */
134

135 136 137 138 139 140

/* table_id -> krt_proto map */

#ifdef KRT_SHARED_SOCKET
static struct krt_proto *krt_table_map[KRT_MAX_TABLES];
#endif
141 142


143
/* Route socket message processing */
144 145 146 147 148 149 150 151 152 153 154 155 156 157

int
krt_capable(rte *e)
{
  rta *a = e->attrs;

  return
    a->cast == RTC_UNICAST &&
    (a->dest == RTD_ROUTER
     || a->dest == RTD_DEVICE
#ifdef RTF_REJECT
     || a->dest == RTD_UNREACHABLE
#endif
#ifdef RTF_BLACKHOLE
158
     || a->dest == RTD_BLACKHOLE
159 160 161 162
#endif
     );
}

163 164 165 166 167 168 169 170
#ifndef RTAX_MAX
#define RTAX_MAX 8
#endif

struct ks_msg
{
  struct rt_msghdr rtm;
  struct sockaddr_storage buf[RTAX_MAX];
Ondřej Zajíček's avatar
Ondřej Zajíček committed
171
} PACKED;
172

173 174 175 176 177 178 179 180
#define ROUNDUP(a) \
        ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))

#define NEXTADDR(w, u) \
        if (msg.rtm.rtm_addrs & (w)) {\
          l = ROUNDUP(((struct sockaddr *)&(u))->sa_len);\
          memmove(body, &(u), l); body += l;}

181 182 183
#define GETADDR(p, F) \
  bzero(p, sizeof(*p));\
  if ((addrs & (F)) && ((struct sockaddr *)body)->sa_len) {\
Pavel Tvrdík's avatar
Pavel Tvrdík committed
184
    uint l = ROUNDUP(((struct sockaddr *)body)->sa_len);\
185 186 187
    memcpy(p, body, (l > sizeof(*p) ? sizeof(*p) : l));\
    body += l;}

188
static int
189
krt_send_route(struct krt_proto *p, int cmd, rte *e)
190 191 192 193 194 195 196 197 198
{
  net *net = e->net;
  rta *a = e->attrs;
  static int msg_seq;
  struct iface *j, *i = a->iface;
  int l;
  struct ks_msg msg;
  char *body = (char *)msg.buf;
  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

Ondřej Filip's avatar
Ondřej Filip committed
203
  bzero(&msg,sizeof (struct rt_msghdr));
204 205 206 207
  msg.rtm.rtm_version = RTM_VERSION;
  msg.rtm.rtm_type = cmd;
  msg.rtm.rtm_seq = msg_seq++;
  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 212 213
    msg.rtm.rtm_flags |= RTF_HOST;
  else
    msg.rtm.rtm_addrs |= RTA_NETMASK;
214 215 216 217

#ifdef KRT_SHARED_SOCKET
  msg.rtm.rtm_tableid = KRT_CF->sys.table_id;
#endif
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

#ifdef RTF_REJECT
  if(a->dest == RTD_UNREACHABLE)
    msg.rtm.rtm_flags |= RTF_REJECT;
#endif
#ifdef RTF_BLACKHOLE
  if(a->dest == RTD_BLACKHOLE)
    msg.rtm.rtm_flags |= RTF_BLACKHOLE;
#endif

  /* This is really very nasty, but I'm not able
   * to add "(reject|blackhole)" route without
   * gateway set
   */
  if(!i)
  {
Ondřej Filip's avatar
Ondřej Filip committed
234
    i = HEAD(iface_list);
Ondřej Filip's avatar
Ondřej Filip committed
235

Ondřej Filip's avatar
Ondřej Filip committed
236 237 238
    WALK_LIST(j, iface_list)
    {
      if (j->flags & IF_LOOPBACK)
239
      {
Ondřej Filip's avatar
Ondřej Filip committed
240 241
        i = j;
        break;
242 243 244 245
      }
    }
  }

246 247 248 249
  gw = a->gw;

#ifdef IPV6
  /* Embed interface ID to link-local address */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
250
  if (ipa_is_link_local(gw))
251 252 253
    _I0(gw) = 0xfe800000 | (i->index & 0x0000ffff);
#endif

254 255 256
  sockaddr_fill(&dst,  BIRD_AF, net->n.prefix, NULL, 0);
  sockaddr_fill(&mask, BIRD_AF, ipa_mkmask(net->n.pxlen), NULL, 0);
  sockaddr_fill(&gate, BIRD_AF, gw, NULL, 0);
257

258 259 260 261 262 263
  switch (a->dest)
  {
    case RTD_ROUTER:
      msg.rtm.rtm_flags |= RTF_GATEWAY;
      msg.rtm.rtm_addrs |= RTA_GATEWAY;
      break;
264

265 266 267 268 269 270 271 272 273
#ifdef RTF_REJECT
    case RTD_UNREACHABLE:
#endif
#ifdef RTF_BLACKHOLE
    case RTD_BLACKHOLE:
#endif
    case RTD_DEVICE:
      if(i)
      {
274
#ifdef RTF_CLONING
275 276
        if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS)	/* PTP */
          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 286 287 288 289 290 291
        msg.rtm.rtm_addrs |= RTA_GATEWAY;
      }
      break;
    default:
      bug("krt-sock: unknown flags, but not filtered");
  }

292
  msg.rtm.rtm_index = i->index;
293 294 295 296 297 298 299 300

  NEXTADDR(RTA_DST, dst);
  NEXTADDR(RTA_GATEWAY, gate);
  NEXTADDR(RTA_NETMASK, mask);

  l = body - (char *)&msg;
  msg.rtm.rtm_msglen = l;

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 314
  int err = 0;

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 322 323 324

  if (err < 0)
    n->n.flags |= KRF_SYNC_ERROR;
  else
    n->n.flags &= ~KRF_SYNC_ERROR;
325 326
}

327 328
#define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0)

329
static void
330
krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
331
{
332 333
  /* p is NULL iff KRT_SHARED_SOCKET and !scan */

334 335
  rte *e;
  net *net;
336
  sockaddr dst, gate, mask;
337 338
  ip_addr idst, igate, imask;
  void *body = (char *)msg->buf;
339
  int new = (msg->rtm.rtm_type != RTM_DELETE);
340
  char *errmsg = "KRT: Invalid route received";
341 342
  int flags = msg->rtm.rtm_flags;
  int addrs = msg->rtm.rtm_addrs;
343 344
  int src;
  byte src2;
345

346 347
  if (!(flags & RTF_UP) && scan)
    SKIP("not up in scan\n");
348

349 350
  if (!(flags & RTF_DONE) && !scan)
    SKIP("not done in async\n");
351

352 353
  if (flags & RTF_LLINFO)
    SKIP("link-local\n");
354

355 356 357 358 359 360 361 362 363 364 365
#ifdef KRT_SHARED_SOCKET
  if (!scan)
  {
    int table_id = msg->rtm.rtm_tableid;
    p = (table_id < KRT_MAX_TABLES) ? krt_table_map[table_id] : NULL;

    if (!p)
      SKIP("unknown table id %d\n", table_id);
  }
#endif

366 367 368
  GETADDR(&dst, RTA_DST);
  GETADDR(&gate, RTA_GATEWAY);
  GETADDR(&mask, RTA_NETMASK);
369

370
  if (dst.sa.sa_family != BIRD_AF)
371
    SKIP("invalid DST");
372

373 374 375
  idst  = ipa_from_sa(&dst);
  imask = ipa_from_sa(&mask);
  igate = (gate.sa.sa_family == BIRD_AF) ? ipa_from_sa(&gate) : IPA_NONE;
376

377 378
  /* We do not test family for RTA_NETMASK, because BSD sends us
     some strange values, but interpreting them as IPv4/IPv6 works */
379

380

381 382 383
  int c = ipa_classify_net(idst);
  if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
    SKIP("strange class/scope\n");
384

Ondřej Zajíček's avatar
Ondřej Zajíček committed
385
  int pxlen = (flags & RTF_HOST) ? MAX_PREFIX_LENGTH : ipa_masklen(imask);
386 387
  if (pxlen < 0)
    { log(L_ERR "%s (%I) - netmask %I", errmsg, idst, imask); return; }
388

389 390 391 392
  if ((flags & RTF_GATEWAY) && ipa_zero(igate))
    { log(L_ERR "%s (%I/%d) - missing gateway", errmsg, idst, pxlen); return; }

  u32 self_mask = RTF_PROTO1;
393
  u32 alien_mask = RTF_STATIC | RTF_PROTO1 | RTF_GATEWAY;
394

395 396 397
  src2 = (flags & RTF_STATIC) ? 1 : 0;
  src2 |= (flags & RTF_PROTO1) ? 2 : 0;

398 399
#ifdef RTF_PROTO2
  alien_mask |= RTF_PROTO2;
400
  src2 |= (flags & RTF_PROTO2) ? 4 : 0;
401
#endif
402

403 404
#ifdef RTF_PROTO3
  alien_mask |= RTF_PROTO3;
405
  src2 |= (flags & RTF_PROTO3) ? 8 : 0;
406 407
#endif

408 409 410 411 412 413 414 415
#ifdef RTF_REJECT
  alien_mask |= RTF_REJECT;
#endif

#ifdef RTF_BLACKHOLE
  alien_mask |= RTF_BLACKHOLE;
#endif

416 417 418 419 420 421 422 423 424 425 426 427 428 429
  if (flags & (RTF_DYNAMIC | RTF_MODIFIED))
    src = KRT_SRC_REDIRECT;
  else if (flags & self_mask)
    {
      if (!scan)
	SKIP("echo\n");
      src = KRT_SRC_BIRD;
    }
  else if (flags & alien_mask)
    src = KRT_SRC_ALIEN;
  else
    src = KRT_SRC_KERNEL;

  net = net_get(p->p.table, idst, pxlen);
430

431
  rta a = {
432
    .src = p->p.main_source,
433 434 435 436
    .source = RTS_INHERIT,
    .scope = SCOPE_UNIVERSE,
    .cast = RTC_UNICAST
  };
437

438 439
  /* reject/blackhole routes have also set RTF_GATEWAY,
     we wil check them first. */
440 441 442 443

#ifdef RTF_REJECT
  if(flags & RTF_REJECT) {
    a.dest = RTD_UNREACHABLE;
444
    goto done;
445 446 447 448 449 450
  }
#endif

#ifdef RTF_BLACKHOLE
  if(flags & RTF_BLACKHOLE) {
    a.dest = RTD_BLACKHOLE;
451
    goto done;
452 453 454
  }
#endif

455 456 457 458 459 460 461 462 463
  a.iface = if_find_by_index(msg->rtm.rtm_index);
  if (!a.iface)
    {
      log(L_ERR "KRT: Received route %I/%d with unknown ifindex %u",
	  net->n.prefix, net->n.pxlen, msg->rtm.rtm_index);
      return;
    }

  if (flags & RTF_GATEWAY)
464
  {
465 466 467
    neighbor *ng;
    a.dest = RTD_ROUTER;
    a.gw = igate;
468

469 470
#ifdef IPV6
    /* Clean up embedded interface ID returned in link-local address */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
471
    if (ipa_is_link_local(a.gw))
472 473 474
      _I0(a.gw) = 0xfe800000;
#endif

475 476 477
    ng = neigh_find2(&p->p, &a.gw, a.iface, 0);
    if (!ng || (ng->scope == SCOPE_HOST))
      {
478 479 480 481 482
	/* Ignore routes with next-hop 127.0.0.1, host routes with such
	   next-hop appear on OpenBSD for address aliases. */
        if (ipa_classify(a.gw) == (IADDR_HOST | SCOPE_HOST))
          return;

483 484 485 486 487 488 489
	log(L_ERR "KRT: Received route %I/%d with strange next-hop %I",
	    net->n.prefix, net->n.pxlen, a.gw);
	return;
      }
  }
  else
    a.dest = RTD_DEVICE;
490

491
 done:
492 493 494
  e = rte_get_temp(&a);
  e->net = net;
  e->u.krt.src = src;
495
  e->u.krt.proto = src2;
496 497
  e->u.krt.seen = 0;
  e->u.krt.best = 0;
498 499 500 501 502 503 504 505
  e->u.krt.metric = 0;

  if (scan)
    krt_got_route(p, e);
  else
    krt_got_route_async(p, e, new);
}

506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
static void
krt_read_ifannounce(struct ks_msg *msg)
{
  struct if_announcemsghdr *ifam = (struct if_announcemsghdr *)&msg->rtm;

  if (ifam->ifan_what == IFAN_ARRIVAL)
  {
    /* Not enough info to create the iface, so we just trigger iface scan */
    kif_request_scan();
  }
  else if (ifam->ifan_what == IFAN_DEPARTURE)
  {
    struct iface *iface = if_find_by_index(ifam->ifan_index);

    /* Interface is destroyed */
    if (!iface)
    {
      DBG("KRT: unknown interface (%s, #%d) going down. Ignoring\n", ifam->ifan_name, ifam->ifan_index);
      return;
    }

    if_delete(iface);
  }

  DBG("KRT: IFANNOUNCE what: %d index %d name %s\n", ifam->ifan_what, ifam->ifan_index, ifam->ifan_name);
}

533
static void
534
krt_read_ifinfo(struct ks_msg *msg, int scan)
535 536 537 538
{
  struct if_msghdr *ifm = (struct if_msghdr *)&msg->rtm;
  void *body = (void *)(ifm + 1);
  struct sockaddr_dl *dl = NULL;
Pavel Tvrdík's avatar
Pavel Tvrdík committed
539
  uint i;
540
  struct iface *iface = NULL, f = {};
541
  int fl = ifm->ifm_flags;
542
  int nlen = 0;
543

544
  for (i = 1; i<=RTA_IFP; i <<= 1)
545
  {
546
    if (i & ifm->ifm_addrs)
547
    {
548
      if (i == RTA_IFP)
549 550 551 552
      {
        dl = (struct sockaddr_dl *)body;
        break;
      }
553
      body += ROUNDUP(((struct sockaddr *)&(body))->sa_len);
554 555 556
    }
  }

557
  if (dl && (dl->sdl_family != AF_LINK))
558
  {
559
    log(L_WARN "Ignoring strange IFINFO");
560 561 562
    return;
  }

563 564 565 566 567
  if (dl)
    nlen = MIN(sizeof(f.name)-1, dl->sdl_nlen);

  /* Note that asynchronous IFINFO messages do not contain iface
     name, so we have to found an existing iface by iface index */
568

569 570
  iface = if_find_by_index(ifm->ifm_index);
  if (!iface)
571 572
  {
    /* New interface */
573 574
    if (!dl)
      return;	/* No interface name, ignoring */
575

576 577 578 579 580 581 582 583
    memcpy(f.name, dl->sdl_data, nlen);
    DBG("New interface '%s' found\n", f.name);
  }
  else if (dl && memcmp(iface->name, dl->sdl_data, nlen))
  {
    /* Interface renamed */
    if_delete(iface);
    memcpy(f.name, dl->sdl_data, nlen);
584 585 586
  }
  else
  {
587 588
    /* Old interface */
    memcpy(f.name, iface->name, sizeof(f.name));
589 590
  }

591
  f.index = ifm->ifm_index;
592 593 594
  f.mtu = ifm->ifm_data.ifi_mtu;

  if (fl & IFF_UP)
595 596 597
    f.flags |= IF_ADMIN_UP;
  if (ifm->ifm_data.ifi_link_state != LINK_STATE_DOWN)
    f.flags |= IF_LINK_UP;          /* up or unknown */
598 599 600 601 602 603 604 605 606
  if (fl & IFF_LOOPBACK)            /* Loopback */
    f.flags |= IF_MULTIACCESS | IF_LOOPBACK | IF_IGNORE;
  else if (fl & IFF_POINTOPOINT)    /* PtP */
    f.flags |= IF_MULTICAST;
  else if (fl & IFF_BROADCAST)      /* Broadcast */
    f.flags |= IF_MULTIACCESS | IF_BROADCAST | IF_MULTICAST;
  else
    f.flags |= IF_MULTIACCESS;      /* NBMA */

607 608 609 610
  iface = if_update(&f);

  if (!scan)
    if_end_partial_update(iface);
611 612
}

613
static void
614
krt_read_addr(struct ks_msg *msg, int scan)
615 616 617 618 619 620 621 622 623 624 625 626
{
  struct ifa_msghdr *ifam = (struct ifa_msghdr *)&msg->rtm;
  void *body = (void *)(ifam + 1);
  sockaddr addr, mask, brd;
  struct iface *iface = NULL;
  struct ifa ifa;
  struct sockaddr null;
  ip_addr iaddr, imask, ibrd;
  int addrs = ifam->ifam_addrs;
  int scope, masklen = -1;
  int new = (ifam->ifam_type == RTM_NEWADDR);

627 628 629 630
  /* Strange messages with zero (invalid) ifindex appear on OpenBSD */
  if (ifam->ifam_index == 0)
    return;

631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
  if(!(iface = if_find_by_index(ifam->ifam_index)))
  {
    log(L_ERR "KIF: Received address message for unknown interface %d", ifam->ifam_index);
    return;
  }

  GETADDR (&null, RTA_DST);
  GETADDR (&null, RTA_GATEWAY);
  GETADDR (&mask, RTA_NETMASK);
  GETADDR (&null, RTA_GENMASK);
  GETADDR (&null, RTA_IFP);
  GETADDR (&addr, RTA_IFA);
  GETADDR (&null, RTA_AUTHOR);
  GETADDR (&brd, RTA_BRD);

646
  /* Some other family address */
647
  if (addr.sa.sa_family != BIRD_AF)
648
    return;
649

650 651 652 653
  iaddr = ipa_from_sa(&addr);
  imask = ipa_from_sa(&mask);
  ibrd  = ipa_from_sa(&brd);

654

Ondřej Zajíček's avatar
Ondřej Zajíček committed
655
  if ((masklen = ipa_masklen(imask)) < 0)
656
  {
657
    log(L_ERR "KIF: Invalid masklen %I for %s", imask, iface->name);
658 659 660
    return;
  }

661 662
#ifdef IPV6
  /* Clean up embedded interface ID returned in link-local address */
663

Ondřej Zajíček's avatar
Ondřej Zajíček committed
664
  if (ipa_is_link_local(iaddr))
665
    _I0(iaddr) = 0xfe800000;
666

Ondřej Zajíček's avatar
Ondřej Zajíček committed
667
  if (ipa_is_link_local(ibrd))
668 669
    _I0(ibrd) = 0xfe800000;
#endif
670 671


672 673 674
  bzero(&ifa, sizeof(ifa));
  ifa.iface = iface;
  ifa.ip = iaddr;
675 676 677 678 679 680 681 682 683 684
  ifa.pxlen = masklen;

  scope = ipa_classify(ifa.ip);
  if (scope < 0)
  {
    log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, iface->name);
    return;
  }
  ifa.scope = scope & IADDR_SCOPE_MASK;

685
  if (masklen < BITS_PER_IP_ADDRESS)
686
  {
687
    ifa.prefix = ipa_and(ifa.ip, ipa_mkmask(masklen));
688

Ondřej Zajíček's avatar
Ondřej Zajíček committed
689
    if (masklen == (BITS_PER_IP_ADDRESS - 1))
690 691 692
      ifa.opposite = ipa_opposite_m1(ifa.ip);

#ifndef IPV6
Ondřej Zajíček's avatar
Ondřej Zajíček committed
693
    if (masklen == (BITS_PER_IP_ADDRESS - 2))
694 695
      ifa.opposite = ipa_opposite_m2(ifa.ip);
#endif
696

697 698 699
    if (iface->flags & IF_BROADCAST)
      ifa.brd = ibrd;

700
    if (!(iface->flags & IF_MULTIACCESS))
701
      ifa.opposite = ibrd;
702
  }
703
  else if (!(iface->flags & IF_MULTIACCESS) && ipa_nonzero(ibrd))
704
  {
705
    ifa.prefix = ifa.opposite = ibrd;
706
    ifa.flags |= IA_PEER;
707 708 709 710 711
  }
  else
  {
    ifa.prefix = ifa.ip;
    ifa.flags |= IA_HOST;
712 713
  }

714 715 716 717
  if (new)
    ifa_update(&ifa);
  else
    ifa_delete(&ifa);
718 719 720

  if (!scan)
    if_end_partial_update(iface);
721 722
}

723
static void
724 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 730 731 732 733
  switch (msg->rtm.rtm_type)
  {
    case RTM_GET:
      if(!scan) return;
    case RTM_ADD:
    case RTM_DELETE:
734
    case RTM_CHANGE:
735
      krt_read_route(msg, (struct krt_proto *)p, scan);
736
      break;
737 738 739
    case RTM_IFANNOUNCE:
      krt_read_ifannounce(msg);
      break;
740
    case RTM_IFINFO:
741
      krt_read_ifinfo(msg, scan);
742 743 744
      break;
    case RTM_NEWADDR:
    case RTM_DELADDR:
745
      krt_read_addr(msg, scan);
746 747 748 749 750 751
      break;
    default:
      break;
  }
}

752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781

/* Sysctl based scans */

static byte *krt_buffer;
static size_t krt_buflen, krt_bufmin;
static struct proto *krt_buffer_owner;

static byte *
krt_buffer_update(struct proto *p, size_t *needed)
{
  size_t req = *needed;

  if ((req > krt_buflen) ||
      ((p == krt_buffer_owner) && (req < krt_bufmin)))
  {
    /* min buflen is 32 kB, step is 8 kB, or 128 kB if > 1 MB */
    size_t step = (req < 0x100000) ? 0x2000 : 0x20000;
    krt_buflen = (req < 0x6000) ? 0x8000 : (req + step);
    krt_bufmin = (req < 0x8000) ? 0 : (req - 2*step);

    if (krt_buffer) 
      mb_free(krt_buffer);
    krt_buffer = mb_alloc(krt_pool, krt_buflen);
    krt_buffer_owner = p;
  }

  *needed = krt_buflen;
  return krt_buffer;
}

782
static void
783
krt_buffer_release(struct proto *p)
784
{
785 786 787 788 789 790 791 792 793
  if (p == krt_buffer_owner)
  {
    mb_free(krt_buffer);
    krt_buffer = NULL;
    krt_buflen = 0;
    krt_buffer_owner = 0;
  }
}

794
static void
795
krt_sysctl_scan(struct proto *p, int cmd, int table_id)
796
{
797 798 799
  byte *buf, *next;
  int mib[7], mcnt;
  size_t needed;
800
  struct ks_msg *m;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
801
  int retries = 3;
802
  int rv;
803 804 805 806

  mib[0] = CTL_NET;
  mib[1] = PF_ROUTE;
  mib[2] = 0;
807
  mib[3] = BIRD_AF;
808 809
  mib[4] = cmd;
  mib[5] = 0;
810
  mcnt = 6;
811

812 813 814 815 816 817 818
#ifdef KRT_USE_SYSCTL_7
  if (table_id >= 0)
  {
    mib[6] = table_id;
    mcnt = 7;
  }
#endif
819

820 821 822 823 824 825 826 827
#ifdef KRT_USE_SETFIB_SCAN
  if (table_id > 0)
    if (setfib(table_id) < 0)
    {
      log(L_ERR "KRT: setfib(%d) failed: %m", table_id);
      return;
    }
#endif
828

829 830 831
 try:
  rv = sysctl(mib, mcnt, NULL, &needed, NULL, 0);
  if (rv < 0)
832
  {
833 834 835 836 837 838
    /* OpenBSD returns EINVAL for not yet used tables */
    if ((errno == EINVAL) && (table_id > 0))
      goto exit;

    log(L_ERR "KRT: Route scan estimate failed: %m");
    goto exit;
839 840
  }

841 842 843 844 845 846 847 848
  /* The table is empty */
  if (needed == 0)
    goto exit;

  buf = krt_buffer_update(p, &needed);

  rv = sysctl(mib, mcnt, buf, &needed, NULL, 0);
  if (rv < 0)
849
  {
850 851 852
    /* The buffer size changed since last sysctl ('needed' is not changed) */
    if ((errno == ENOMEM) && retries--)
      goto try;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
853

854 855
    log(L_ERR "KRT: Route scan failed: %m");
    goto exit;
856 857
  }

858 859 860 861 862 863 864 865
#ifdef KRT_USE_SETFIB_SCAN
  if (table_id > 0)
    if (setfib(0) < 0)
      die("KRT: setfib(%d) failed: %m", 0);
#endif

  /* Process received messages */
  for (next = buf; next < (buf + needed); next += m->rtm.rtm_msglen)
866 867 868 869 870
  {
    m = (struct ks_msg *)next;
    krt_read_msg(p, m, 1);
  }

871 872 873 874 875 876 877 878 879 880 881
  return;

 exit:
  krt_buffer_release(p);

#ifdef KRT_USE_SETFIB_SCAN
  if (table_id > 0)
    if (setfib(0) < 0)
      die("KRT: setfib(%d) failed: %m", 0);
#endif
}
882

883
void
884
krt_do_scan(struct krt_proto *p)
885
{
886
  krt_sysctl_scan(&p->p, NET_RT_DUMP, KRT_CF->sys.table_id);
887 888 889
}

void
890
kif_do_scan(struct kif_proto *p)
891 892
{
  if_start_update();
893
  krt_sysctl_scan(&p->p, NET_RT_IFLIST, -1);
894 895 896
  if_end_update();
}

897 898 899

/* Kernel sockets */

900
static int
901
krt_sock_hook(sock *sk, uint size UNUSED)
902 903 904 905
{
  struct ks_msg msg;
  int l = read(sk->fd, (char *)&msg, sizeof(msg));

906
  if (l <= 0)
907 908
    log(L_ERR "krt-sock: read failed");
  else
909
    krt_read_msg((struct proto *) sk->data, &msg, 0);
910 911 912

  return 0;
}
913

914 915 916 917 918 919
static void
krt_sock_err_hook(sock *sk, int e UNUSED)
{
  krt_sock_hook(sk, 0);
}

920
static sock *
921
krt_sock_open(pool *pool, void *data, int table_id UNUSED)
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
{
  sock *sk;
  int fd;

  fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
  if (fd < 0)
    die("Cannot open kernel socket for routes");

#ifdef KRT_USE_SETFIB_SOCK
  if (table_id > 0)
  {
    if (setsockopt(fd, SOL_SOCKET, SO_SETFIB, &table_id, sizeof(table_id)) < 0)
      die("Cannot set FIB %d for kernel socket: %m", table_id);
  }
#endif

  sk = sk_new(pool);
  sk->type = SK_MAGIC;
  sk->rx_hook = krt_sock_hook;
941
  sk->err_hook = krt_sock_err_hook;
942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
  sk->fd = fd;
  sk->data = data;

  if (sk_open(sk) < 0)
    bug("krt-sock: sk_open failed");

  return sk;
}


#ifdef KRT_SHARED_SOCKET

static sock *krt_sock;
static int krt_sock_count;


static void
krt_sock_open_shared(void)
{
  if (!krt_sock_count)
    krt_sock = krt_sock_open(krt_pool, NULL, -1);
  
  krt_sock_count++;
}

static void
krt_sock_close_shared(void)
{
  krt_sock_count--;

  if (!krt_sock_count)
  {
    rfree(krt_sock);
    krt_sock = NULL;
  }
}

979
int
980
krt_sys_start(struct krt_proto *p)
981
{
982
  krt_table_map[KRT_CF->sys.table_id] = p;
983

984 985
  krt_sock_open_shared();
  p->sys.sk = krt_sock;
986 987

  return 1;
988
}
989

990 991 992 993 994
void
krt_sys_shutdown(struct krt_proto *p)
{
  krt_sock_close_shared();
  p->sys.sk = NULL;
995

996
  krt_table_map[KRT_CF->sys.table_id] = NULL;
997

998 999
  krt_buffer_release(&p->p);
}
1000

1001 1002
#else

1003
int
1004 1005 1006
krt_sys_start(struct krt_proto *p)
{
  p->sys.sk = krt_sock_open(p->p.pool, p, KRT_CF->sys.table_id);
1007
  return 1;
1008 1009 1010
}

void
1011
krt_sys_shutdown(struct krt_proto *p)
1012
{
1013 1014
  rfree(p->sys.sk);
  p->sys.sk = NULL;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1015

1016
  krt_buffer_release(&p->p);
1017
}
1018

1019 1020
#endif /* KRT_SHARED_SOCKET */

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1021

1022 1023 1024 1025 1026 1027 1028 1029
/* KRT configuration callbacks */

static u32 krt_table_cf[(KRT_MAX_TABLES+31) / 32];

int
krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o)
{
  return n->sys.table_id == o->sys.table_id;
1030
}
1031 1032

void
1033
krt_sys_preconfig(struct config *c UNUSED)
1034
{
1035 1036
  krt_max_tables = krt_get_max_tables();
  bzero(&krt_table_cf, sizeof(krt_table_cf));
1037 1038 1039
}

void
1040
krt_sys_postconfig(struct krt_config *x)
1041
{
1042 1043 1044 1045 1046 1047 1048
  u32 *tbl = krt_table_cf;
  int id = x->sys.table_id;

  if (tbl[id/32] & (1 << (id%32)))
    cf_error("Multiple kernel syncers defined for table #%d", id);

  tbl[id/32] |= (1 << (id%32));
1049 1050
}

1051 1052 1053 1054
void krt_sys_init_config(struct krt_config *c)
{
  c->sys.table_id = 0; /* Default table */
}
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1055

1056 1057 1058 1059 1060 1061 1062 1063
void krt_sys_copy_config(struct krt_config *d, struct krt_config *s)
{
  d->sys.table_id = s->sys.table_id;
}


/* KIF misc code */

1064
void
1065
kif_sys_start(struct kif_proto *p UNUSED)
1066
{
1067
}
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1068

1069 1070 1071 1072
void
kif_sys_shutdown(struct kif_proto *p)
{
  krt_buffer_release(&p->p);
1073 1074
}

1075 1076

struct ifa *
1077
kif_get_primary_ip(struct iface *i UNUSED6)
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
{
#ifndef IPV6
  static int fd = -1;
  
  if (fd < 0)
    fd = socket(AF_INET, SOCK_DGRAM, 0);

  struct ifreq ifr;
  memset(&ifr, 0, sizeof(ifr));
  strncpy(ifr.ifr_name, i->name, IFNAMSIZ);

  int rv = ioctl(fd, SIOCGIFADDR, (char *) &ifr);
  if (rv < 0)
    return NULL;

  ip_addr addr;
  struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
  memcpy(&addr, &sin->sin_addr.s_addr, sizeof(ip_addr));
  ipa_ntoh(addr);

  struct ifa *a;
  WALK_LIST(a, i->addrs)
  {
    if (ipa_equal(a->ip, addr))
      return a;
  }
#endif

  return NULL;
}