Statistics
| Branch: | Revision:

iof-bird-daemon / proto / static / static.c @ d47c3d64

History | View | Annotate | Download (16.7 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
13
 * two lists of static routes: one containing interface routes and one
14
 * holding the remaining ones. Interface routes are inserted and removed according
15
 * to interface events received from the core via the if_notify() hook. Routes
16
 * pointing to a neighboring router use a sticky node in the neighbor cache
17
 * to be notified about gaining or losing the neighbor. Special
18
 * routes like black holes or rejects are inserted all the time.
19
 *
20
 * Multipath routes are tricky. Because these routes depends on
21
 * several neighbors we need to integrate that to the neighbor
22
 * notification handling, we use dummy static_route nodes, one for
23
 * each nexthop. Therefore, a multipath route consists of a master
24
 * static_route node (of dest RTD_MULTIPATH), which specifies prefix
25
 * and is used in most circumstances, and a list of dummy static_route
26
 * nodes (of dest RTD_NONE), which stores info about nexthops and are
27
 * connected to neighbor entries and neighbor notifications. Dummy
28
 * nodes are chained using mp_next, they aren't in other_routes list,
29
 * and abuse if_name field for other purposes.
30
 *
31
 * The only other thing worth mentioning is that when asked for reconfiguration,
32
 * Static not only compares the two configurations, but it also calculates
33
 * difference between the lists of static routes and it just inserts the
34
 * newly added routes and removes the obsolete ones.
35
 */
36

    
37
#undef LOCAL_DEBUG
38

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

    
49
#include "static.h"
50

    
51
static linpool *static_lp;
52

    
53
static inline rtable *
54
p_igp_table(struct proto *p)
55
{
56
  struct static_config *cf = (void *) p->cf;
57
  return cf->igp_table ? cf->igp_table->table : p->main_channel->table;
58
}
59

    
60
static void
61
static_install(struct proto *p, struct static_route *r)
62
{
63
  rta *ap = alloca(RTA_MAX_SIZE);
64
  rte *e;
65

    
66
  if (!(r->state & STS_WANT) && (r->state & (STS_INSTALLED | STS_FORCE)) && r->dest != RTD_UNICAST)
67
    goto drop;
68

    
69
  DBG("Installing static route %N, rtd=%d\n", r->net, r->dest);
70
  bzero(ap, RTA_MAX_SIZE);
71
  ap->src = p->main_source;
72
  ap->source = ((r->dest == RTD_UNICAST) && ipa_zero(r->via)) ? RTS_STATIC_DEVICE : RTS_STATIC;
73
  ap->scope = SCOPE_UNIVERSE;
74
  ap->dest = r->dest;
75

    
76
  if (r->dest == RTD_UNICAST)
77
    {
78
      struct static_route *r2;
79
      int num = 0, update = 0;
80

    
81
      for (r2 = r; r2; r2 = r2->mp_next)
82
      {
83

    
84
        if ((r2->state & STS_FORCE) ||
85
            (!!(r2->state & STS_INSTALLED) != !!(r2->state & STS_WANT)))
86
          update++;
87

    
88
        if (r2->state & STS_WANT)
89
          {
90
            struct nexthop *nh = (ap->nh.next) ? alloca(NEXTHOP_MAX_SIZE) : &(ap->nh);
91
            if (ipa_zero(r2->via)) // Device nexthop
92
              {
93
                nh->gw = IPA_NONE;
94
                nh->iface = r2->iface;
95
              }
96
            else // Router nexthop
97
              {
98
                nh->gw = r2->via;
99
                nh->iface = r2->neigh->iface;
100
              }
101
            nh->weight = r2->weight;
102
            nh->labels = r2->label_count;
103
            for (int i=0; i<nh->labels; i++)
104
              nh->label[i] = r2->label_stack[i];
105

    
106
            if (ap->nh.next)
107
              nexthop_insert(&(ap->nh), nh);
108
            r2->state |= STS_INSTALLED;
109
            num++;
110
          }
111
        else
112
          r2->state = 0;
113
      }
114

    
115
      if (!update) // Nothing changed
116
        return;
117

    
118
      r = r->mp_head;
119

    
120
      if (!num) // No nexthop to install
121
      {
122
drop:
123
        rte_update(p, r->net, NULL);
124
        return;
125
      }
126
    }
127
  else
128
    r->state |= STS_INSTALLED;
129
  
130
  if (r->dest == RTDX_RECURSIVE)
131
    {
132
      ap->nh.labels_append = ap->nh.labels = r->label_count;
133
      memcpy(ap->nh.label, r->label_stack, r->label_count * sizeof(u32));
134
      rta_set_recursive_next_hop(p->main_channel->table, ap, p_igp_table(p), r->via, IPA_NONE);
135
    }
136

    
137
  /* We skip rta_lookup() here */
138

    
139
  e = rte_get_temp(ap);
140
  e->pflags = 0;
141

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

    
145
  rte_update(p, r->net, e);
146

    
147
  if (r->cmds)
148
    lp_flush(static_lp);
149
}
150

    
151
static void
152
static_bfd_notify(struct bfd_request *req);
153

    
154
static void
155
static_update_bfd(struct proto *p, struct static_route *r)
156
{
157
  struct neighbor *nb = r->neigh;
158
  int bfd_up = (nb->scope > 0) && r->use_bfd;
159

    
160
  if (bfd_up && !r->bfd_req)
161
  {
162
    // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
163
    r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface,
164
                                     static_bfd_notify, r);
165
  }
166

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

    
174
static int
175
static_decide(struct static_config *cf, struct static_route *r)
176
{
177
  /* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
178
     the route also have to be valid (r->neigh != NULL) */
179

    
180
  r->state &= ~STS_WANT;
181

    
182
  if (r->neigh->scope < 0)
183
    return 0;
184

    
185
  if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
186
    return 0;
187

    
188
  if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP)
189
    return 0;
190

    
191
  r->state |= STS_WANT;
192
  return 1;
193
}
194

    
195

    
196
static void
197
static_add(struct proto *p, struct static_config *cf, struct static_route *r)
198
{
199
  if (r->mp_head && r != r->mp_head)
200
    return;
201

    
202
  DBG("static_add(%N,%d)\n", r->net, r->dest);
203
  switch (r->dest)
204
    {
205
    case RTD_UNICAST:
206
      {
207
        int count = 0;
208
        struct static_route *r2;
209

    
210
        for (r2 = r; r2; r2 = r2->mp_next)
211
          {
212
            if (ipa_zero(r2->via)) // No struct neighbor for device routes
213
              continue;
214

    
215
            struct neighbor *n = neigh_find2(p, &r2->via, r2->iface, NEF_STICKY);
216
            if (n)
217
              {
218
                r2->chain = n->data;
219
                n->data = r2;
220
                r2->neigh = n;
221

    
222
                static_update_bfd(p, r2);
223
                static_decide(cf,r2);
224
                count++;
225
              }
226
            else
227
              {
228
                log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
229
                r2->state = 0;
230
              }
231
          }
232

    
233
        if (count)
234
          static_install(p, r);
235

    
236
        break;
237
      }
238

    
239
    default:
240
      static_install(p, r);
241
    }
242
}
243

    
244
static void
245
static_rte_cleanup(struct proto *p UNUSED, struct static_route *r)
246
{
247
  if (r->mp_head && (r != r->mp_head))
248
    return;
249

    
250
  struct static_route *r2;
251
  
252
  for (r2 = r; r2; r2 = r2->mp_next)
253
    if (r2->bfd_req)
254
    {
255
      rfree(r2->bfd_req);
256
      r2->bfd_req = NULL;
257
    }
258
}
259

    
260
static int
261
static_start(struct proto *p)
262
{
263
  struct static_config *cf = (void *) p->cf;
264
  struct static_route *r;
265

    
266
  DBG("Static: take off!\n");
267

    
268
  if (!static_lp)
269
    static_lp = lp_new(&root_pool, 1008);
270

    
271
  if (cf->igp_table)
272
    rt_lock_table(cf->igp_table->table);
273

    
274
  /* We have to go UP before routes could be installed */
275
  proto_notify_state(p, PS_UP);
276

    
277
  WALK_LIST(r, cf->neigh_routes)
278
    static_add(p, cf, r);
279

    
280
  WALK_LIST(r, cf->iface_routes)
281
    static_add(p, cf, r);
282

    
283
  WALK_LIST(r, cf->other_routes)
284
    static_install(p, r);
285

    
286
  return PS_UP;
287
}
288

    
289
static int
290
static_shutdown(struct proto *p)
291
{
292
  struct static_config *cf = (void *) p->cf;
293
  struct static_route *r;
294

    
295
  /* Just reset the flag, the routes will be flushed by the nest */
296
  WALK_LIST(r, cf->other_routes)
297
  {
298
    static_rte_cleanup(p, r);
299
    r->state = 0;
300
  }
301
  WALK_LIST(r, cf->iface_routes)
302
    r->state = 0;
303
  WALK_LIST(r, cf->neigh_routes)
304
  {
305
    static_rte_cleanup(p, r);
306
    r->state = 0;
307
  }
308

    
309
  /* Handle failure during channel reconfigure */
310
  /* FIXME: This should be handled in a better way */
311
  cf = (void *) p->cf_new;
312
  if (cf)
313
  {
314
    WALK_LIST(r, cf->other_routes)
315
      r->state = 0;
316
    WALK_LIST(r, cf->iface_routes)
317
      r->state = 0;
318
    WALK_LIST(r, cf->neigh_routes)
319
      r->state = 0;
320
  }
321

    
322
  return PS_DOWN;
323
}
324

    
325
static void
326
static_cleanup(struct proto *p)
327
{
328
  struct static_config *cf = (void *) p->cf;
329

    
330
  if (cf->igp_table)
331
    rt_unlock_table(cf->igp_table->table);
332
}
333

    
334
static void
335
static_update_rte(struct proto *p, struct static_route *r)
336
{
337
  if (r->dest != RTD_UNICAST)
338
    return;
339

    
340
  static_decide((struct static_config *) p->cf, r);
341
  static_install(p, r);
342
}
343

    
344
static void
345
static_neigh_notify(struct neighbor *n)
346
{
347
  struct proto *p = n->proto;
348
  struct static_route *r;
349

    
350
  DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
351
  for(r=n->data; r; r=r->chain)
352
  {
353
    static_update_bfd(p, r);
354
    static_update_rte(p, r);
355
  }
356
}
357

    
358
static void
359
static_bfd_notify(struct bfd_request *req)
360
{
361
  struct static_route *r = req->data;
362
  struct proto *p = r->neigh->proto;
363

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

    
366
  static_update_rte(p, r);
367
}
368

    
369
static void
370
static_dump_rt(struct static_route *r)
371
{
372
  debug("%-1N: ", r->net);
373
  if (r->dest == RTD_UNICAST)
374
    if (ipa_zero(r->via))
375
      debug("dev %s\n", r->if_name);
376
    else
377
      debug("via %I\n", r->via);
378
  else
379
    debug("rtd %d\n", r->dest);
380
}
381

    
382
static void
383
static_dump(struct proto *p)
384
{
385
  struct static_config *c = (void *) p->cf;
386
  struct static_route *r;
387

    
388
  debug("Independent static nexthops:\n");
389
  WALK_LIST(r, c->neigh_routes)
390
    static_dump_rt(r);
391
  debug("Device static nexthops:\n");
392
  WALK_LIST(r, c->iface_routes)
393
    static_dump_rt(r);
394
  debug("Other static routes:\n");
395
  WALK_LIST(r, c->other_routes)
396
    static_dump_rt(r);
397
}
398

    
399
static void
400
static_if_notify(struct proto *p, unsigned flags, struct iface *i)
401
{
402
  struct static_route *r;
403
  struct static_config *c = (void *) p->cf;
404

    
405
  if (flags & IF_CHANGE_UP)
406
    {
407
      WALK_LIST(r, c->iface_routes)
408
        if (!strcmp(r->if_name, i->name))
409
        {
410
          r->state |= STS_WANT;
411
          r->iface = i;
412
          static_install(p, r);
413
        }
414
    }
415
  else if (flags & IF_CHANGE_DOWN)
416
    {
417
      WALK_LIST(r, c->iface_routes)
418
        if (!strcmp(r->if_name, i->name))
419
        {
420
          r->state &= ~STS_WANT;
421
          r->iface = NULL;
422
          static_install(p, r);
423
        }
424
    }
425
}
426

    
427
int
428
static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED)
429
{
430
  return 1;
431
}
432

    
433
void
434
static_init_config(struct static_config *c)
435
{
436
  init_list(&c->neigh_routes);
437
  init_list(&c->iface_routes);
438
  init_list(&c->other_routes);
439
}
440

    
441
static void
442
static_postconfig(struct proto_config *CF)
443
{
444
  struct static_config *cf = (void *) CF;
445
  struct static_route *r;
446

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

    
450

    
451
  WALK_LIST(r, cf->neigh_routes)
452
    if (r->net && (r->net->type != CF->net_type))
453
      cf_error("Route %N incompatible with channel type", r->net);
454

    
455
  WALK_LIST(r, cf->iface_routes)
456
    if (r->net && (r->net->type != CF->net_type))
457
      cf_error("Route %N incompatible with channel type", r->net);
458

    
459
  WALK_LIST(r, cf->other_routes)
460
    if (r->net->type != CF->net_type)
461
      cf_error("Route %N incompatible with channel type", r->net);
462
}
463

    
464

    
465
static struct proto *
466
static_init(struct proto_config *CF)
467
{
468
  struct proto *P = proto_new(CF);
469
  // struct static_proto *p = (void *) P;
470
  // struct static_config *cf = (void *) CF;
471

    
472
  P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
473

    
474
  P->neigh_notify = static_neigh_notify;
475
  P->if_notify = static_if_notify;
476
  P->rte_mergable = static_rte_mergable;
477

    
478
  return P;
479
}
480

    
481
static inline int
482
static_same_dest(struct static_route *x, struct static_route *y)
483
{
484
  if (x->dest != y->dest)
485
    return 0;
486

    
487
  switch (x->dest)
488
    {
489
    case RTD_UNICAST:
490
      {
491
        struct static_route *xc, *yc;
492
        for (xc = x, yc = y; xc && yc; xc = xc->mp_next, yc = yc->mp_next)
493
        {
494
          if (ipa_nonzero(xc->via) && ipa_nonzero(yc->via))
495
          {
496
            if (!ipa_equal(x->via, y->via) ||
497
                (x->iface != y->iface) ||
498
                (x->use_bfd != y->use_bfd) ||
499
                (x->weight != y->weight) ||
500
                (x->label_count != y->label_count))
501
              return 0;
502
            for (int i=0; i<x->label_count; i++)
503
              if (x->label_stack[i] != y->label_stack[i])
504
                return 0;
505
          }
506
          else
507
            if ((!x->if_name) || (!y->if_name) ||
508
                strcmp(x->if_name, y->if_name) ||
509
                (x->use_bfd != y->use_bfd) ||
510
                (x->weight != y->weight))
511
              return 0;
512

    
513
        }
514
        return 1;
515
      }
516

    
517
    case RTDX_RECURSIVE:
518
      return ipa_equal(x->via, y->via);
519

    
520
    default:
521
      return 1;
522
    }
523
}
524

    
525
static inline int
526
static_same_rte(struct static_route *x, struct static_route *y)
527
{
528
  return static_same_dest(x, y) && i_same(x->cmds, y->cmds);
529
}
530

    
531

    
532
static void
533
static_match(struct proto *p, struct static_route *r, struct static_config *n)
534
{
535
  struct static_route *t;
536

    
537
  if (r->mp_head && (r->mp_head != r))
538
    return;
539

    
540
  /*
541
   * For given old route *r we find whether a route to the same
542
   * network is also in the new route list. In that case, we keep the
543
   * route and possibly update the route later if destination changed.
544
   * Otherwise, we remove the route.
545
   */
546

    
547
  if (r->neigh)
548
    r->neigh->data = NULL;
549

    
550
  WALK_LIST(t, n->neigh_routes)
551
    if ((!t->mp_head || (t->mp_head == t)) && net_equal(r->net, t->net))
552
      goto found;
553

    
554
  WALK_LIST(t, n->iface_routes)
555
    if ((!t->mp_head || (t->mp_head == t)) && net_equal(r->net, t->net))
556
      goto found;
557

    
558
  WALK_LIST(t, n->other_routes)
559
    if (net_equal(r->net, t->net))
560
      goto found;
561

    
562
  r->state &= ~STS_WANT;
563
  static_install(p, r);
564
  return;
565

    
566
 found:
567
  t->state = r->state;
568

    
569
  /* If destination is different, force reinstall */
570
  if (!static_same_rte(r, t))
571
    t->state |= STS_FORCE;
572
}
573

    
574
static inline rtable *
575
cf_igp_table(struct static_config *cf)
576
{
577
  return cf->igp_table ? cf->igp_table->table : NULL;
578
}
579

    
580
static int
581
static_reconfigure(struct proto *p, struct proto_config *CF)
582
{
583
  struct static_config *o = (void *) p->cf;
584
  struct static_config *n = (void *) CF;
585
  struct static_route *r;
586

    
587
  if (cf_igp_table(o) != cf_igp_table(n))
588
    return 0;
589

    
590
  if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
591
    return 0;
592

    
593
  /* Delete all obsolete routes and reset neighbor entries */
594
  WALK_LIST(r, o->other_routes)
595
    static_match(p, r, n);
596
  WALK_LIST(r, o->iface_routes)
597
    static_match(p, r, n);
598
  WALK_LIST(r, o->neigh_routes)
599
    static_match(p, r, n);
600

    
601
  /* Now add all new routes, those not changed will be ignored by static_install() */
602
  WALK_LIST(r, n->neigh_routes)
603
    static_add(p, n, r);
604
  WALK_LIST(r, o->neigh_routes)
605
    static_rte_cleanup(p, r);
606

    
607
  WALK_LIST(r, n->iface_routes)
608
    {
609
      struct iface *ifa;
610
      if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
611
        {
612
          r->iface = ifa;
613
          static_install(p, r);
614
        }
615
    }
616

    
617
  WALK_LIST(r, n->other_routes)
618
  {
619
    r->state |= STS_WANT;
620
    static_install(p, r);
621
  }
622

    
623
  WALK_LIST(r, o->other_routes)
624
    static_rte_cleanup(p, r);
625

    
626
  return 1;
627
}
628

    
629
static void
630
static_copy_routes(list *dlst, list *slst)
631
{
632
  struct static_route *sr;
633

    
634
  init_list(dlst);
635
  WALK_LIST(sr, *slst)
636
    {
637
      struct static_route *srr, *drr = NULL;
638
      for (srr = sr->mp_head; srr; srr = srr->mp_next)
639
      {
640
        /* copy one route */
641
        struct static_route *dr = cfg_alloc(sizeof(struct static_route));
642
        if (drr)
643
          drr->mp_next = dr;
644
        else
645
          add_tail(dlst, &(dr->n));
646

    
647
        memcpy(dr, sr, sizeof(struct static_route));
648
        drr = dr;
649
      }
650
    }
651
}
652

    
653
static void
654
static_copy_config(struct proto_config *dest, struct proto_config *src)
655
{
656
  struct static_config *d = (struct static_config *) dest;
657
  struct static_config *s = (struct static_config *) src;
658

    
659
  /* Copy route lists */
660
  static_copy_routes(&d->neigh_routes, &s->neigh_routes);
661
  static_copy_routes(&d->iface_routes, &s->iface_routes);
662
  static_copy_routes(&d->other_routes, &s->other_routes);
663
}
664

    
665
struct protocol proto_static = {
666
  .name =                "Static",
667
  .template =                "static%d",
668
  .preference =                DEF_PREF_STATIC,
669
  .channel_mask =        NB_ANY,
670
  .proto_size =                sizeof(struct proto),
671
  .config_size =        sizeof(struct static_config),
672
  .postconfig =                static_postconfig,
673
  .init =                static_init,
674
  .dump =                static_dump,
675
  .start =                static_start,
676
  .shutdown =                static_shutdown,
677
  .cleanup =                static_cleanup,
678
  .reconfigure =        static_reconfigure,
679
  .copy_config =        static_copy_config
680
};
681

    
682
static byte *
683
static_format_via(struct static_route *r)
684
{
685
  static byte via[IPA_MAX_TEXT_LENGTH + 25];
686

    
687
  switch (r->dest)
688
    {
689
    case RTD_UNICAST:        if (ipa_zero(r->via)) bsprintf(via, "dev %s", r->if_name);
690
                        else bsprintf(via, "via %I%J", r->via, r->iface);
691
                        break;
692
    case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); break;
693
    case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
694
    case RTD_PROHIBIT:        bsprintf(via, "prohibited"); break;
695
    case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
696
    default:                bsprintf(via, "???");
697
    }
698
  return via;
699
}
700

    
701
static void
702
static_show_rt(struct static_route *r)
703
{
704
  if (r->mp_head && (r != r->mp_head))
705
    return;
706
  if (r->mp_next)
707
  {
708
    cli_msg(-1009, "%N", r->net);
709
    struct static_route *r2;
710
    for (r2 = r; r2; r2 = r2->mp_next)
711
    {
712
      cli_msg(-1009, "\t%s weight %d%s%s", static_format_via(r2), r2->weight + 1,
713
              r2->bfd_req ? " (bfd)" : "", (r2->state & STS_INSTALLED) ? "" : " (dormant)");
714
      if (r2->mp_next == r)
715
        break;
716
    }
717
  }
718
  else
719
    cli_msg(-1009, "%N %s%s%s", r->net, static_format_via(r),
720
          r->bfd_req ? " (bfd)" : "", (r->state & STS_INSTALLED) ? "" : " (dormant)");
721
}
722

    
723
void
724
static_show(struct proto *P)
725
{
726
  struct static_config *c = (void *) P->cf;
727
  struct static_route *r;
728

    
729
  WALK_LIST(r, c->neigh_routes)
730
    static_show_rt(r);
731
  WALK_LIST(r, c->iface_routes)
732
    static_show_rt(r);
733
  WALK_LIST(r, c->other_routes)
734
    static_show_rt(r);
735
  cli_msg(0, "");
736
}