static.c 15.2 KB
Newer Older
1 2 3
/*
 *	BIRD -- Static Route Generator
 *
4
 *	(c) 1998--2000 Martin Mares <mj@ucw.cz>
5 6 7 8
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

9 10 11
/**
 * DOC: Static
 *
12 13 14 15 16 17 18 19 20
 * The Static protocol is implemented in a straightforward way. It keeps a list
 * of static routes. Routes of dest RTD_UNICAST have associated sticky node in
 * the neighbor cache to be notified about gaining or losing the neighbor and
 * about interface-related events (e.g. link down). They may also have a BFD
 * request if associated with a BFD session. When a route is notified,
 * static_decide() is used to see whether the route activeness is changed. In
 * such case, the route is marked as dirty and scheduled to be announced or
 * withdrawn, which is done asynchronously from event hook. Routes of other
 * types (e.g. black holes) are announced all the time.
21
 *
22 23 24 25 26
 * Multipath routes are a bit tricky. To represent additional next hops, dummy
 * static_route nodes are used, which are chained using @mp_next field and link
 * to the master node by @mp_head field. Each next hop has a separate neighbor
 * entry and an activeness state, but the master node is used for most purposes.
 * Note that most functions DO NOT accept dummy nodes as arguments.
27
 *
28 29
 * The only other thing worth mentioning is that when asked for reconfiguration,
 * Static not only compares the two configurations, but it also calculates
30 31
 * difference between the lists of static routes and it just inserts the newly
 * added routes, removes the obsolete ones and reannounces changed ones.
32 33
 */

34
#undef LOCAL_DEBUG
35

36 37
#include <stdlib.h>

38 39 40 41
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
42
#include "nest/cli.h"
43
#include "conf/conf.h"
44
#include "filter/filter.h"
45
#include "lib/string.h"
46
#include "lib/alloca.h"
47 48 49

#include "static.h"

50 51
static linpool *static_lp;

52
static void
53
static_announce_rte(struct static_proto *p, struct static_route *r)
54
{
55 56 57 58 59
  rta *a = allocz(RTA_MAX_SIZE);
  a->src = p->p.main_source;
  a->source = RTS_STATIC;
  a->scope = SCOPE_UNIVERSE;
  a->dest = r->dest;
60

61
  if (r->dest == RTD_UNICAST)
62 63 64 65 66
  {
    struct static_route *r2;
    struct nexthop *nhs = NULL;

    for (r2 = r; r2; r2 = r2->mp_next)
67
    {
68 69
      if (!r2->active)
	continue;
70

71 72 73
      struct nexthop *nh = allocz(NEXTHOP_MAX_SIZE);
      nh->gw = r2->via;
      nh->iface = r2->neigh->iface;
74
      nh->flags = r2->onlink ? RNF_ONLINK : 0;
75
      nh->weight = r2->weight;
76 77 78 79 80
      if (r2->mls)
      {
	nh->labels = r2->mls->len;
	memcpy(nh->label, r2->mls->stack, r2->mls->len * sizeof(u32));
      }
81

82 83
      nexthop_insert(&nhs, nh);
    }
84

85 86
    if (!nhs)
      goto withdraw;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
87

88 89
    nexthop_link(a, nhs);
  }
Ondřej Zajíček's avatar
Ondřej Zajíček committed
90

91
  if (r->dest == RTDX_RECURSIVE)
92 93 94 95
  {
    rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6;
    rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls);
  }
96

97 98 99
  /* Already announced */
  if (r->state == SRS_CLEAN)
    return;
100

101 102
  /* We skip rta_lookup() here */
  rte *e = rte_get_temp(a);
103
  e->pflags = 0;
104 105 106 107

  if (r->cmds)
    f_eval_rte(r->cmds, &e, static_lp);

108 109
  rte_update(&p->p, r->net, e);
  r->state = SRS_CLEAN;
110 111 112

  if (r->cmds)
    lp_flush(static_lp);
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

  return;

withdraw:
  if (r->state == SRS_DOWN)
    return;

  rte_update(&p->p, r->net, NULL);
  r->state = SRS_DOWN;
}

static void
static_mark_rte(struct static_proto *p, struct static_route *r)
{
  if (r->state == SRS_DIRTY)
    return;

  r->state = SRS_DIRTY;
  BUFFER_PUSH(p->marked) = r;

  if (!ev_active(p->event))
    ev_schedule(p->event);
}

static void
static_announce_marked(void *P)
{
  struct static_proto *p = P;

  BUFFER_WALK(p->marked, r)
    static_announce_rte(P, r);

  BUFFER_FLUSH(p->marked);
146 147
}

148 149 150 151
static void
static_bfd_notify(struct bfd_request *req);

static void
152
static_update_bfd(struct static_proto *p, struct static_route *r)
153
{
154 155
  /* The @r is a RTD_UNICAST next hop, may be a dummy node */

156 157 158 159 160 161
  struct neighbor *nb = r->neigh;
  int bfd_up = (nb->scope > 0) && r->use_bfd;

  if (bfd_up && !r->bfd_req)
  {
    // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
162
    r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip, nb->iface,
163 164 165 166 167 168 169 170 171 172
				     static_bfd_notify, r);
  }

  if (!bfd_up && r->bfd_req)
  {
    rfree(r->bfd_req);
    r->bfd_req = NULL;
  }
}

173
static int
174
static_decide(struct static_proto *p, struct static_route *r)
175
{
176
  /* The @r is a RTD_UNICAST next hop, may be a dummy node */
177

178 179
  struct static_config *cf = (void *) p->p.cf;
  uint old_active = r->active;
180

181
  if (r->neigh->scope < 0)
182
    goto fail;
183

184
  if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
185
    goto fail;
186

187 188
  if (r->bfd_req && (r->bfd_req->state != BFD_STATE_UP))
    goto fail;
189

190 191
  r->active = 1;
  return !old_active;
192

193 194 195 196
fail:
  r->active = 0;
  return old_active;
}
197

198
static void
199
static_add_rte(struct static_proto *p, struct static_route *r)
200
{
201 202 203 204
  if (r->dest == RTD_UNICAST)
  {
    struct static_route *r2;
    struct neighbor *n;
205

206
    for (r2 = r; r2; r2 = r2->mp_next)
207
    {
208
      n = ipa_nonzero(r2->via) ?
209 210
	neigh_find2(&p->p, &r2->via, r2->iface,
		    NEF_STICKY | (r2->onlink ? NEF_ONLINK : 0)) :
211 212 213
	neigh_find_iface(&p->p, r2->iface);

      if (!n)
214
      {
215 216
	log(L_WARN "Invalid next hop %I of static route %N", r2->via, r2->net);
	continue;
217 218
      }

219 220 221 222 223 224
      r2->neigh = n;
      r2->chain = n->data;
      n->data = r2;

      static_update_bfd(p, r2);
      static_decide(p, r2);
225
    }
226 227 228
  }

  static_announce_rte(p, r);
229 230
}

231
static void
232
static_reset_rte(struct static_proto *p UNUSED, struct static_route *r)
233 234
{
  struct static_route *r2;
235

236 237 238 239
  for (r2 = r; r2; r2 = r2->mp_next)
  {
    r2->neigh = NULL;
    r2->chain = NULL;
240

241 242
    r2->state = 0;
    r2->active = 0;
243

244 245 246 247
    rfree(r2->bfd_req);
    r2->bfd_req = NULL;
  }
}
248

249 250 251 252 253
static void
static_remove_rte(struct static_proto *p, struct static_route *r)
{
  if (r->state)
    rte_update(&p->p, r->net, NULL);
254

255
  static_reset_rte(p, r);
256 257
}

258 259 260

static inline int
static_same_dest(struct static_route *x, struct static_route *y)
261
{
262 263
  if (x->dest != y->dest)
    return 0;
264

265
  switch (x->dest)
266
  {
267 268 269 270 271
  case RTD_UNICAST:
    for (; x && y; x = x->mp_next, y = y->mp_next)
    {
      if (!ipa_equal(x->via, y->via) ||
	  (x->iface != y->iface) ||
272
	  (x->onlink != y->onlink) ||
273
	  (x->weight != y->weight) ||
274
	  (x->use_bfd != y->use_bfd) ||
275 276
	  (!x->mls != !y->mls) ||
	  ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
277 278
	return 0;

279 280 281 282 283
      if (!x->mls)
	continue;

      for (uint i = 0; i < x->mls->len; i++)
	if (x->mls->stack[i] != y->mls->stack[i])
284 285 286
	  return 0;
    }
    return !x && !y;
287

288
  case RTDX_RECURSIVE:
289 290 291 292 293 294 295 296 297 298 299 300 301
    if (!ipa_equal(x->via, y->via) ||
	(!x->mls != !y->mls) ||
	((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
      return 0;

    if (!x->mls)
      return 1;

    for (uint i = 0; i < x->mls->len; i++)
      if (x->mls->stack[i] != y->mls->stack[i])
	return 0;

    return 1;
302

303 304 305
  default:
    return 1;
  }
306 307
}

308 309
static inline int
static_same_rte(struct static_route *or, struct static_route *nr)
310
{
311 312
  /* Note that i_same() requires arguments in (new, old) order */
  return static_same_dest(or, nr) && i_same(nr->cmds, or->cmds);
313 314
}

315
static void
316
static_reconfigure_rte(struct static_proto *p, struct static_route *or, struct static_route *nr)
317
{
318 319 320 321
  if ((or->state == SRS_CLEAN) && !static_same_rte(or, nr))
    nr->state = SRS_DIRTY;
  else
    nr->state = or->state;
322

323 324
  static_add_rte(p, nr);
  static_reset_rte(p, or);
325
}
326

327

328 329 330
static void
static_neigh_notify(struct neighbor *n)
{
331
  struct static_proto *p = (void *) n->proto;
332 333
  struct static_route *r;

334
  DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
335
  for (r = n->data; r; r = r->chain)
336 337
  {
    static_update_bfd(p, r);
338 339 340

    if (static_decide(p, r))
      static_mark_rte(p, r->mp_head);
341 342
  }
}
343

344 345 346 347
static void
static_bfd_notify(struct bfd_request *req)
{
  struct static_route *r = req->data;
348
  struct static_proto *p = (void *) r->neigh->proto;
349

350 351
  // if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX);

352 353
  if (static_decide(p, r))
    static_mark_rte(p, r->mp_head);
354 355
}

356
static int
357
static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED)
358 359 360 361
{
  return 1;
}

362

363 364 365 366 367 368 369 370 371
static void
static_postconfig(struct proto_config *CF)
{
  struct static_config *cf = (void *) CF;
  struct static_route *r;

  if (EMPTY_LIST(CF->channels))
    cf_error("Channel not specified");

372 373 374 375 376 377 378 379 380 381
  struct channel_config *cc = proto_cf_main_channel(CF);

  if (!cf->igp_table_ip4)
    cf->igp_table_ip4 = (cc->table->addr_type == NET_IP4) ?
      cc->table : cf->c.global->def_tables[NET_IP4];

  if (!cf->igp_table_ip6)
    cf->igp_table_ip6 = (cc->table->addr_type == NET_IP6) ?
      cc->table : cf->c.global->def_tables[NET_IP6];

382
  WALK_LIST(r, cf->routes)
383 384
    if (r->net && (r->net->type != CF->net_type))
      cf_error("Route %N incompatible with channel type", r->net);
385 386
}

387
static struct proto *
388
static_init(struct proto_config *CF)
389
{
390
  struct proto *P = proto_new(CF);
391 392
  struct static_proto *p = (void *) P;
  struct static_config *cf = (void *) CF;
393

394
  P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
395

396 397 398
  P->neigh_notify = static_neigh_notify;
  P->rte_mergable = static_rte_mergable;

399 400 401 402 403 404
  if (cf->igp_table_ip4)
    p->igp_table_ip4 = cf->igp_table_ip4->table;

  if (cf->igp_table_ip6)
    p->igp_table_ip6 = cf->igp_table_ip6->table;

405
  return P;
406 407
}

408 409
static int
static_start(struct proto *P)
410
{
411 412 413
  struct static_proto *p = (void *) P;
  struct static_config *cf = (void *) P->cf;
  struct static_route *r;
414

415
  if (!static_lp)
416
    static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024));
417

418 419 420 421 422
  if (p->igp_table_ip4)
    rt_lock_table(p->igp_table_ip4);

  if (p->igp_table_ip6)
    rt_lock_table(p->igp_table_ip6);
423

424 425 426
  p->event = ev_new(p->p.pool);
  p->event->hook = static_announce_marked;
  p->event->data = p;
427

428
  BUFFER_INIT(p->marked, p->p.pool, 4);
429

430 431
  /* We have to go UP before routes could be installed */
  proto_notify_state(P, PS_UP);
432

433 434
  WALK_LIST(r, cf->routes)
    static_add_rte(p, r);
435

436 437
  return PS_UP;
}
438

439 440 441 442 443 444
static int
static_shutdown(struct proto *P)
{
  struct static_proto *p = (void *) P;
  struct static_config *cf = (void *) P->cf;
  struct static_route *r;
445

446 447 448
  /* Just reset the flag, the routes will be flushed by the nest */
  WALK_LIST(r, cf->routes)
    static_reset_rte(p, r);
449

450 451
  return PS_DOWN;
}
452

453 454 455
static void
static_cleanup(struct proto *P)
{
456 457 458 459
  struct static_proto *p = (void *) P;

  if (p->igp_table_ip4)
    rt_unlock_table(p->igp_table_ip4);
460

461 462
  if (p->igp_table_ip6)
    rt_unlock_table(p->igp_table_ip6);
463
}
464

465 466 467 468 469 470 471 472 473 474 475 476
static void
static_dump_rte(struct static_route *r)
{
  debug("%-1N: ", r->net);
  if (r->dest == RTD_UNICAST)
    if (r->iface && ipa_zero(r->via))
      debug("dev %s\n", r->iface->name);
    else
      debug("via %I%J\n", r->via, r->iface);
  else
    debug("rtd %d\n", r->dest);
}
477

478 479 480 481 482
static void
static_dump(struct proto *P)
{
  struct static_config *c = (void *) P->cf;
  struct static_route *r;
483

484 485 486
  debug("Static routes:\n");
  WALK_LIST(r, c->routes)
    static_dump_rte(r);
487 488
}

489
#define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL )
490

491 492 493 494 495 496 497
static inline int
static_cmp_rte(const void *X, const void *Y)
{
  struct static_route *x = *(void **)X, *y = *(void **)Y;
  return net_compare(x->net, y->net);
}

498
static int
499
static_reconfigure(struct proto *P, struct proto_config *CF)
500
{
501 502
  struct static_proto *p = (void *) P;
  struct static_config *o = (void *) P->cf;
503
  struct static_config *n = (void *) CF;
504
  struct static_route *r, *r2, *or, *nr;
505

506 507 508
  /* Check change in IGP tables */
  if ((IGP_TABLE(o, ip4) != IGP_TABLE(n, ip4)) ||
      (IGP_TABLE(o, ip6) != IGP_TABLE(n, ip6)))
509 510
    return 0;

511
  if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
512 513
    return 0;

514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
  p->p.cf = CF;

  /* Reset route lists in neighbor entries */
  WALK_LIST(r, o->routes)
    for (r2 = r; r2; r2 = r2->mp_next)
      if (r2->neigh)
	r2->neigh->data = NULL;

  /* Reconfigure initial matching sequence */
  for (or = HEAD(o->routes), nr = HEAD(n->routes);
       NODE_VALID(or) && NODE_VALID(nr) && net_equal(or->net, nr->net);
       or = NODE_NEXT(or), nr = NODE_NEXT(nr))
    static_reconfigure_rte(p, or, nr);

  if (!NODE_VALID(or) && !NODE_VALID(nr))
    return 1;

  /* Reconfigure remaining routes, sort them to find matching pairs */
  struct static_route *or2, *nr2, **orbuf, **nrbuf;
  uint ornum = 0, nrnum = 0, orpos = 0, nrpos = 0, i;

  for (or2 = or; NODE_VALID(or2); or2 = NODE_NEXT(or2))
    ornum++;

  for (nr2 = nr; NODE_VALID(nr2); nr2 = NODE_NEXT(nr2))
    nrnum++;

  orbuf = xmalloc(ornum * sizeof(void *));
  nrbuf = xmalloc(nrnum * sizeof(void *));
543

544 545 546 547 548 549 550 551 552 553
  for (i = 0, or2 = or; i < ornum; i++, or2 = NODE_NEXT(or2))
    orbuf[i] = or2;

  for (i = 0, nr2 = nr; i < nrnum; i++, nr2 = NODE_NEXT(nr2))
    nrbuf[i] = nr2;

  qsort(orbuf, ornum, sizeof(struct static_route *), static_cmp_rte);
  qsort(nrbuf, nrnum, sizeof(struct static_route *), static_cmp_rte);

  while ((orpos < ornum) && (nrpos < nrnum))
554
  {
555 556 557 558 559 560 561
    int x = net_compare(orbuf[orpos]->net, nrbuf[nrpos]->net);
    if (x < 0)
      static_remove_rte(p, orbuf[orpos++]);
    else if (x > 0)
      static_add_rte(p, nrbuf[nrpos++]);
    else
      static_reconfigure_rte(p, orbuf[orpos++], nrbuf[nrpos++]);
562
  }
563

564 565
  while (orpos < ornum)
    static_remove_rte(p, orbuf[orpos++]);
566

567 568
  while (nrpos < nrnum)
    static_add_rte(p, nrbuf[nrpos++]);
569

570 571
  xfree(orbuf);
  xfree(nrbuf);
572

573
  return 1;
574 575 576 577 578 579 580 581
}

static void
static_copy_config(struct proto_config *dest, struct proto_config *src)
{
  struct static_config *d = (struct static_config *) dest;
  struct static_config *s = (struct static_config *) src;

582
  struct static_route *srt, *snh;
583

584 585 586 587 588
  /* Copy route list */
  init_list(&d->routes);
  WALK_LIST(srt, s->routes)
  {
    struct static_route *drt = NULL, *dnh = NULL, **dnp = &drt;
589

590
    for (snh = srt; snh; snh = snh->mp_next)
591
    {
592 593 594 595 596 597 598 599 600 601 602
      dnh = cfg_alloc(sizeof(struct static_route));
      memcpy(dnh, snh, sizeof(struct static_route));

      if (!drt)
	add_tail(&d->routes, &(dnh->n));

      *dnp = dnh;
      dnp = &(dnh->mp_next);

      if (snh->mp_head)
	dnh->mp_head = drt;
603
    }
604
  }
605
}
606

607 608 609
static void
static_show_rt(struct static_route *r)
{
610 611 612
  switch (r->dest)
  {
  case RTD_UNICAST:
613 614
  {
    struct static_route *r2;
615 616

    cli_msg(-1009, "%N", r->net);
617
    for (r2 = r; r2; r2 = r2->mp_next)
618
    {
619
      if (r2->iface && ipa_zero(r2->via))
620 621
	cli_msg(-1009, "\tdev %s%s", r2->iface->name,
		r2->active ? "" : " (dormant)");
622
      else
623 624 625 626
	cli_msg(-1009, "\tvia %I%J%s%s%s", r2->via, r2->iface,
		r2->onlink ? " onlink" : "",
		r2->bfd_req ? " (bfd)" : "",
		r2->active ? "" : " (dormant)");
627
    }
628 629 630 631 632 633 634 635 636 637 638 639 640
    break;
  }

  case RTD_NONE:
  case RTD_BLACKHOLE:
  case RTD_UNREACHABLE:
  case RTD_PROHIBIT:
    cli_msg(-1009, "%N\t%s", r->net, rta_dest_names[r->dest]);
    break;

  case RTDX_RECURSIVE:
    cli_msg(-1009, "%N\trecursive %I", r->net, r->via);
    break;
641
  }
642 643 644 645 646 647 648 649
}

void
static_show(struct proto *P)
{
  struct static_config *c = (void *) P->cf;
  struct static_route *r;

650
  WALK_LIST(r, c->routes)
651
    static_show_rt(r);
652 653
  cli_msg(0, "");
}
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671


struct protocol proto_static = {
  .name =		"Static",
  .template =		"static%d",
  .preference =		DEF_PREF_STATIC,
  .channel_mask =	NB_ANY,
  .proto_size =		sizeof(struct static_proto),
  .config_size =	sizeof(struct static_config),
  .postconfig =		static_postconfig,
  .init =		static_init,
  .dump =		static_dump,
  .start =		static_start,
  .shutdown =		static_shutdown,
  .cleanup =		static_cleanup,
  .reconfigure =	static_reconfigure,
  .copy_config =	static_copy_config
};