Statistics
| Branch: | Revision:

iof-bird / bird-2.0.1 / proto / static / static.c @ 6b3f1a54

History | View | Annotate | Download (15.2 KB)

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

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

    
34
#undef LOCAL_DEBUG
35

    
36
#include <stdlib.h>
37

    
38
#include "nest/bird.h"
39
#include "nest/iface.h"
40
#include "nest/protocol.h"
41
#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
#include "static.h"
49

    
50
static linpool *static_lp;
51

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

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

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

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

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

    
85
    if (!nhs)
86
      goto withdraw;
87

    
88
    nexthop_link(a, nhs);
89
  }
90

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

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

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

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

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

    
111
  if (r->cmds)
112
    lp_flush(static_lp);
113

    
114
  return;
115

    
116
withdraw:
117
  if (r->state == SRS_DOWN)
118
    return;
119

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

    
124
static void
125
static_mark_rte(struct static_proto *p, struct static_route *r)
126
{
127
  if (r->state == SRS_DIRTY)
128
    return;
129

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

    
133
  if (!ev_active(p->event))
134
    ev_schedule(p->event);
135
}
136

    
137
static void
138
static_announce_marked(void *P)
139
{
140
  struct static_proto *p = P;
141

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

    
145
  BUFFER_FLUSH(p->marked);
146
}
147

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

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

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

    
159
  if (bfd_up && !r->bfd_req)
160
  {
161
    // 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
                                     static_bfd_notify, r);
164
  }
165

    
166
  if (!bfd_up && r->bfd_req)
167
  {
168
    rfree(r->bfd_req);
169
    r->bfd_req = NULL;
170
  }
171
}
172

    
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
  struct static_config *cf = (void *) p->p.cf;
179
  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
  if (r->bfd_req && (r->bfd_req->state != BFD_STATE_UP))
188
    goto fail;
189

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

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

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

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

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

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

    
223
      static_update_bfd(p, r2);
224
      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
  for (r2 = r; r2; r2 = r2->mp_next)
237
  {
238
    r2->neigh = NULL;
239
    r2->chain = NULL;
240

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

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

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

    
255
  static_reset_rte(p, r);
256
}
257

    
258

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

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

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

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

    
288
  case RTDX_RECURSIVE:
289
    if (!ipa_equal(x->via, y->via) ||
290
        (!x->mls != !y->mls) ||
291
        ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
292
      return 0;
293

    
294
    if (!x->mls)
295
      return 1;
296

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

    
301
    return 1;
302

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

    
308
static inline int
309
static_same_rte(struct static_route *or, struct static_route *nr)
310
{
311
  /* Note that i_same() requires arguments in (new, old) order */
312
  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
  if ((or->state == SRS_CLEAN) && !static_same_rte(or, nr))
319
    nr->state = SRS_DIRTY;
320
  else
321
    nr->state = or->state;
322

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

    
327

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

    
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
    if (static_decide(p, r))
340
      static_mark_rte(p, r->mp_head);
341
  }
342
}
343

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

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

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

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

    
362

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

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

    
372
  struct channel_config *cc = proto_cf_main_channel(CF);
373

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

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

    
382
  WALK_LIST(r, cf->routes)
383
    if (r->net && (r->net->type != CF->net_type))
384
      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
  struct static_proto *p = (void *) P;
392
  struct static_config *cf = (void *) CF;
393

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

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

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

    
402
  if (cf->igp_table_ip6)
403
    p->igp_table_ip6 = cf->igp_table_ip6->table;
404

    
405
  return P;
406
}
407

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

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

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

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

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

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

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

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

    
436
  return PS_UP;
437
}
438

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

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

    
450
  return PS_DOWN;
451
}
452

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

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

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

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

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

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

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

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

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

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

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

    
514
  p->p.cf = CF;
515

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

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

    
528
  if (!NODE_VALID(or) && !NODE_VALID(nr))
529
    return 1;
530

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

    
535
  for (or2 = or; NODE_VALID(or2); or2 = NODE_NEXT(or2))
536
    ornum++;
537

    
538
  for (nr2 = nr; NODE_VALID(nr2); nr2 = NODE_NEXT(nr2))
539
    nrnum++;
540

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

    
544
  for (i = 0, or2 = or; i < ornum; i++, or2 = NODE_NEXT(or2))
545
    orbuf[i] = or2;
546

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

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

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

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

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

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

    
573
  return 1;
574
}
575

    
576
static void
577
static_copy_config(struct proto_config *dest, struct proto_config *src)
578
{
579
  struct static_config *d = (struct static_config *) dest;
580
  struct static_config *s = (struct static_config *) src;
581

    
582
  struct static_route *srt, *snh;
583

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

    
590
    for (snh = srt; snh; snh = snh->mp_next)
591
    {
592
      dnh = cfg_alloc(sizeof(struct static_route));
593
      memcpy(dnh, snh, sizeof(struct static_route));
594

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

    
598
      *dnp = dnh;
599
      dnp = &(dnh->mp_next);
600

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

    
607
static void
608
static_show_rt(struct static_route *r)
609
{
610
  switch (r->dest)
611
  {
612
  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
        cli_msg(-1009, "\tdev %s%s", r2->iface->name,
621
                r2->active ? "" : " (dormant)");
622
      else
623
        cli_msg(-1009, "\tvia %I%J%s%s%s", r2->via, r2->iface,
624
                r2->onlink ? " onlink" : "",
625
                r2->bfd_req ? " (bfd)" : "",
626
                r2->active ? "" : " (dormant)");
627
    }
628
    break;
629
  }
630

    
631
  case RTD_NONE:
632
  case RTD_BLACKHOLE:
633
  case RTD_UNREACHABLE:
634
  case RTD_PROHIBIT:
635
    cli_msg(-1009, "%N\t%s", r->net, rta_dest_names[r->dest]);
636
    break;
637

    
638
  case RTDX_RECURSIVE:
639
    cli_msg(-1009, "%N\trecursive %I", r->net, r->via);
640
    break;
641
  }
642
}
643

    
644
void
645
static_show(struct proto *P)
646
{
647
  struct static_config *c = (void *) P->cf;
648
  struct static_route *r;
649

    
650
  WALK_LIST(r, c->routes)
651
    static_show_rt(r);
652
  cli_msg(0, "");
653
}
654

    
655

    
656
struct protocol proto_static = {
657
  .name =                "Static",
658
  .template =                "static%d",
659
  .preference =                DEF_PREF_STATIC,
660
  .channel_mask =        NB_ANY,
661
  .proto_size =                sizeof(struct static_proto),
662
  .config_size =        sizeof(struct static_config),
663
  .postconfig =                static_postconfig,
664
  .init =                static_init,
665
  .dump =                static_dump,
666
  .start =                static_start,
667
  .shutdown =                static_shutdown,
668
  .cleanup =                static_cleanup,
669
  .reconfigure =        static_reconfigure,
670
  .copy_config =        static_copy_config
671
};