Statistics
| Branch: | Revision:

iof-bird-daemon / proto / static / static.c @ 62e64905

History | View | Annotate | Download (16.5 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 = allocz(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
  ap->src = p->main_source;
71
  ap->source = RTS_STATIC;
72
  ap->scope = SCOPE_UNIVERSE;
73
  ap->dest = r->dest;
74

    
75
  if (r->dest == RTD_UNICAST)
76
    {
77
      struct nexthop *nhs = NULL;
78
      struct static_route *r2;
79
      int update = 0;
80

    
81
      r = r->mp_head;
82
      for (r2 = r; r2; r2 = r2->mp_next)
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 = allocz(NEXTHOP_MAX_SIZE);
91

    
92
          nh->gw = r2->via;
93
          nh->iface = r2->neigh ? r2->neigh->iface : r2->iface;
94
          nh->weight = r2->weight;
95
          nh->labels = r2->label_count;
96
          memcpy(nh->label, r2->label_stack, r2->label_count * sizeof(u32));
97

    
98
          r2->state |= STS_INSTALLED;
99
          nexthop_insert(&nhs, nh);
100
        }
101
        else
102
          r2->state = 0;
103
      }
104

    
105
      if (!update) // Nothing changed
106
        return;
107

    
108
      if (!nhs) // No nexthop to install
109
      {
110
drop:
111
        rte_update(p, r->net, NULL);
112
        return;
113
      }
114

    
115
      ap->dest = RTD_UNICAST;
116
      nexthop_link(ap, nhs);
117
    }
118
  else
119
    r->state |= STS_INSTALLED;
120

    
121
  if (r->dest == RTDX_RECURSIVE)
122
    {
123
      ap->nh.labels_append = ap->nh.labels = r->label_count;
124
      memcpy(ap->nh.label, r->label_stack, r->label_count * sizeof(u32));
125
      rta_set_recursive_next_hop(p->main_channel->table, ap, p_igp_table(p), r->via, IPA_NONE);
126
    }
127

    
128
  /* We skip rta_lookup() here */
129

    
130
  e = rte_get_temp(ap);
131
  e->pflags = 0;
132

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

    
136
  rte_update(p, r->net, e);
137

    
138
  if (r->cmds)
139
    lp_flush(static_lp);
140
}
141

    
142
static void
143
static_bfd_notify(struct bfd_request *req);
144

    
145
static void
146
static_update_bfd(struct proto *p, struct static_route *r)
147
{
148
  struct neighbor *nb = r->neigh;
149
  int bfd_up = (nb->scope > 0) && r->use_bfd;
150

    
151
  if (bfd_up && !r->bfd_req)
152
  {
153
    // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
154
    r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface,
155
                                     static_bfd_notify, r);
156
  }
157

    
158
  if (!bfd_up && r->bfd_req)
159
  {
160
    rfree(r->bfd_req);
161
    r->bfd_req = NULL;
162
  }
163
}
164

    
165
static int
166
static_decide(struct static_config *cf, struct static_route *r)
167
{
168
  /* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
169
     the route also have to be valid (r->neigh != NULL) */
170

    
171
  r->state &= ~STS_WANT;
172

    
173
  if (r->neigh->scope < 0)
174
    return 0;
175

    
176
  if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
177
    return 0;
178

    
179
  if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP)
180
    return 0;
181

    
182
  r->state |= STS_WANT;
183
  return 1;
184
}
185

    
186

    
187
static void
188
static_add(struct proto *p, struct static_config *cf, struct static_route *r)
189
{
190
  if (r->mp_head && r != r->mp_head)
191
    return;
192

    
193
  DBG("static_add(%N,%d)\n", r->net, r->dest);
194
  switch (r->dest)
195
    {
196
    case RTD_UNICAST:
197
      {
198
        int count = 0;
199
        struct static_route *r2;
200

    
201
        for (r2 = r; r2; r2 = r2->mp_next)
202
          {
203
            if (ipa_zero(r2->via)) // No struct neighbor for device routes
204
              continue;
205

    
206
            struct neighbor *n = neigh_find2(p, &r2->via, r2->iface, NEF_STICKY);
207
            if (n)
208
              {
209
                r2->chain = n->data;
210
                n->data = r2;
211
                r2->neigh = n;
212

    
213
                static_update_bfd(p, r2);
214
                static_decide(cf,r2);
215
                count++;
216
              }
217
            else
218
              {
219
                log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
220
                r2->state = 0;
221
              }
222
          }
223

    
224
        if (count)
225
          static_install(p, r);
226

    
227
        break;
228
      }
229

    
230
    default:
231
      static_install(p, r);
232
    }
233
}
234

    
235
static void
236
static_rte_cleanup(struct proto *p UNUSED, struct static_route *r)
237
{
238
  if (r->mp_head && (r != r->mp_head))
239
    return;
240

    
241
  struct static_route *r2;
242
  
243
  for (r2 = r; r2; r2 = r2->mp_next)
244
    if (r2->bfd_req)
245
    {
246
      rfree(r2->bfd_req);
247
      r2->bfd_req = NULL;
248
    }
249
}
250

    
251
static int
252
static_start(struct proto *p)
253
{
254
  struct static_config *cf = (void *) p->cf;
255
  struct static_route *r;
256

    
257
  DBG("Static: take off!\n");
258

    
259
  if (!static_lp)
260
    static_lp = lp_new(&root_pool, 1008);
261

    
262
  if (cf->igp_table)
263
    rt_lock_table(cf->igp_table->table);
264

    
265
  /* We have to go UP before routes could be installed */
266
  proto_notify_state(p, PS_UP);
267

    
268
  WALK_LIST(r, cf->neigh_routes)
269
    static_add(p, cf, r);
270

    
271
  WALK_LIST(r, cf->iface_routes)
272
    static_add(p, cf, r);
273

    
274
  WALK_LIST(r, cf->other_routes)
275
    static_install(p, r);
276

    
277
  return PS_UP;
278
}
279

    
280
static int
281
static_shutdown(struct proto *p)
282
{
283
  struct static_config *cf = (void *) p->cf;
284
  struct static_route *r;
285

    
286
  /* Just reset the flag, the routes will be flushed by the nest */
287
  WALK_LIST(r, cf->other_routes)
288
  {
289
    static_rte_cleanup(p, r);
290
    r->state = 0;
291
  }
292
  WALK_LIST(r, cf->iface_routes)
293
    r->state = 0;
294
  WALK_LIST(r, cf->neigh_routes)
295
  {
296
    static_rte_cleanup(p, r);
297
    r->state = 0;
298
  }
299

    
300
  /* Handle failure during channel reconfigure */
301
  /* FIXME: This should be handled in a better way */
302
  cf = (void *) p->cf_new;
303
  if (cf)
304
  {
305
    WALK_LIST(r, cf->other_routes)
306
      r->state = 0;
307
    WALK_LIST(r, cf->iface_routes)
308
      r->state = 0;
309
    WALK_LIST(r, cf->neigh_routes)
310
      r->state = 0;
311
  }
312

    
313
  return PS_DOWN;
314
}
315

    
316
static void
317
static_cleanup(struct proto *p)
318
{
319
  struct static_config *cf = (void *) p->cf;
320

    
321
  if (cf->igp_table)
322
    rt_unlock_table(cf->igp_table->table);
323
}
324

    
325
static void
326
static_update_rte(struct proto *p, struct static_route *r)
327
{
328
  if (r->dest != RTD_UNICAST)
329
    return;
330

    
331
  static_decide((struct static_config *) p->cf, r);
332
  static_install(p, r);
333
}
334

    
335
static void
336
static_neigh_notify(struct neighbor *n)
337
{
338
  struct proto *p = n->proto;
339
  struct static_route *r;
340

    
341
  DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
342
  for(r=n->data; r; r=r->chain)
343
  {
344
    static_update_bfd(p, r);
345
    static_update_rte(p, r);
346
  }
347
}
348

    
349
static void
350
static_bfd_notify(struct bfd_request *req)
351
{
352
  struct static_route *r = req->data;
353
  struct proto *p = r->neigh->proto;
354

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

    
357
  static_update_rte(p, r);
358
}
359

    
360
static void
361
static_dump_rt(struct static_route *r)
362
{
363
  debug("%-1N: ", r->net);
364
  if (r->dest == RTD_UNICAST)
365
    if (ipa_zero(r->via))
366
      debug("dev %s\n", r->if_name);
367
    else
368
      debug("via %I\n", r->via);
369
  else
370
    debug("rtd %d\n", r->dest);
371
}
372

    
373
static void
374
static_dump(struct proto *p)
375
{
376
  struct static_config *c = (void *) p->cf;
377
  struct static_route *r;
378

    
379
  debug("Independent static nexthops:\n");
380
  WALK_LIST(r, c->neigh_routes)
381
    static_dump_rt(r);
382
  debug("Device static nexthops:\n");
383
  WALK_LIST(r, c->iface_routes)
384
    static_dump_rt(r);
385
  debug("Other static routes:\n");
386
  WALK_LIST(r, c->other_routes)
387
    static_dump_rt(r);
388
}
389

    
390
static void
391
static_if_notify(struct proto *p, unsigned flags, struct iface *i)
392
{
393
  struct static_route *r;
394
  struct static_config *c = (void *) p->cf;
395

    
396
  if (flags & IF_CHANGE_UP)
397
    {
398
      WALK_LIST(r, c->iface_routes)
399
        if (!strcmp(r->if_name, i->name))
400
        {
401
          r->state |= STS_WANT;
402
          r->iface = i;
403
          static_install(p, r);
404
        }
405
    }
406
  else if (flags & IF_CHANGE_DOWN)
407
    {
408
      WALK_LIST(r, c->iface_routes)
409
        if (!strcmp(r->if_name, i->name))
410
        {
411
          r->state &= ~STS_WANT;
412
          r->iface = NULL;
413
          static_install(p, r);
414
        }
415
    }
416
}
417

    
418
int
419
static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED)
420
{
421
  return 1;
422
}
423

    
424
void
425
static_init_config(struct static_config *c)
426
{
427
  init_list(&c->neigh_routes);
428
  init_list(&c->iface_routes);
429
  init_list(&c->other_routes);
430
}
431

    
432
static void
433
static_postconfig(struct proto_config *CF)
434
{
435
  struct static_config *cf = (void *) CF;
436
  struct static_route *r;
437

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

    
441

    
442
  WALK_LIST(r, cf->neigh_routes)
443
    if (r->net && (r->net->type != CF->net_type))
444
      cf_error("Route %N incompatible with channel type", r->net);
445

    
446
  WALK_LIST(r, cf->iface_routes)
447
    if (r->net && (r->net->type != CF->net_type))
448
      cf_error("Route %N incompatible with channel type", r->net);
449

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

    
455

    
456
static struct proto *
457
static_init(struct proto_config *CF)
458
{
459
  struct proto *P = proto_new(CF);
460
  // struct static_proto *p = (void *) P;
461
  // struct static_config *cf = (void *) CF;
462

    
463
  P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
464

    
465
  P->neigh_notify = static_neigh_notify;
466
  P->if_notify = static_if_notify;
467
  P->rte_mergable = static_rte_mergable;
468

    
469
  return P;
470
}
471

    
472
static inline int
473
static_same_dest(struct static_route *x, struct static_route *y)
474
{
475
  if (x->dest != y->dest)
476
    return 0;
477

    
478
  switch (x->dest)
479
    {
480
    case RTD_UNICAST:
481
      {
482
        struct static_route *xc, *yc;
483
        for (xc = x, yc = y; xc && yc; xc = xc->mp_next, yc = yc->mp_next)
484
        {
485
          if (ipa_nonzero(xc->via) && ipa_nonzero(yc->via))
486
          {
487
            if (!ipa_equal(x->via, y->via) ||
488
                (x->iface != y->iface) ||
489
                (x->use_bfd != y->use_bfd) ||
490
                (x->weight != y->weight) ||
491
                (x->label_count != y->label_count))
492
              return 0;
493
            for (int i=0; i<x->label_count; i++)
494
              if (x->label_stack[i] != y->label_stack[i])
495
                return 0;
496
          }
497
          else
498
            if ((!x->if_name) || (!y->if_name) ||
499
                strcmp(x->if_name, y->if_name) ||
500
                (x->use_bfd != y->use_bfd) ||
501
                (x->weight != y->weight))
502
              return 0;
503

    
504
        }
505
        return 1;
506
      }
507

    
508
    case RTDX_RECURSIVE:
509
      return ipa_equal(x->via, y->via);
510

    
511
    default:
512
      return 1;
513
    }
514
}
515

    
516
static inline int
517
static_same_rte(struct static_route *x, struct static_route *y)
518
{
519
  return static_same_dest(x, y) && i_same(x->cmds, y->cmds);
520
}
521

    
522

    
523
static void
524
static_match(struct proto *p, struct static_route *r, struct static_config *n)
525
{
526
  struct static_route *t;
527

    
528
  if (r->mp_head && (r->mp_head != r))
529
    return;
530

    
531
  /*
532
   * For given old route *r we find whether a route to the same
533
   * network is also in the new route list. In that case, we keep the
534
   * route and possibly update the route later if destination changed.
535
   * Otherwise, we remove the route.
536
   */
537

    
538
  if (r->neigh)
539
    r->neigh->data = NULL;
540

    
541
  WALK_LIST(t, n->neigh_routes)
542
    if ((!t->mp_head || (t->mp_head == t)) && net_equal(r->net, t->net))
543
      goto found;
544

    
545
  WALK_LIST(t, n->iface_routes)
546
    if ((!t->mp_head || (t->mp_head == t)) && net_equal(r->net, t->net))
547
      goto found;
548

    
549
  WALK_LIST(t, n->other_routes)
550
    if (net_equal(r->net, t->net))
551
      goto found;
552

    
553
  r->state &= ~STS_WANT;
554
  static_install(p, r);
555
  return;
556

    
557
 found:
558
  t->state = r->state;
559

    
560
  /* If destination is different, force reinstall */
561
  if (!static_same_rte(r, t))
562
    t->state |= STS_FORCE;
563
}
564

    
565
static inline rtable *
566
cf_igp_table(struct static_config *cf)
567
{
568
  return cf->igp_table ? cf->igp_table->table : NULL;
569
}
570

    
571
static int
572
static_reconfigure(struct proto *p, struct proto_config *CF)
573
{
574
  struct static_config *o = (void *) p->cf;
575
  struct static_config *n = (void *) CF;
576
  struct static_route *r;
577

    
578
  if (cf_igp_table(o) != cf_igp_table(n))
579
    return 0;
580

    
581
  if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
582
    return 0;
583

    
584
  /* Delete all obsolete routes and reset neighbor entries */
585
  WALK_LIST(r, o->other_routes)
586
    static_match(p, r, n);
587
  WALK_LIST(r, o->iface_routes)
588
    static_match(p, r, n);
589
  WALK_LIST(r, o->neigh_routes)
590
    static_match(p, r, n);
591

    
592
  /* Now add all new routes, those not changed will be ignored by static_install() */
593
  WALK_LIST(r, n->neigh_routes)
594
    static_add(p, n, r);
595
  WALK_LIST(r, o->neigh_routes)
596
    static_rte_cleanup(p, r);
597

    
598
  WALK_LIST(r, n->iface_routes)
599
    {
600
      struct iface *ifa;
601
      if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
602
        {
603
          r->iface = ifa;
604
          static_install(p, r);
605
        }
606
    }
607

    
608
  WALK_LIST(r, n->other_routes)
609
  {
610
    r->state |= STS_WANT;
611
    static_install(p, r);
612
  }
613

    
614
  WALK_LIST(r, o->other_routes)
615
    static_rte_cleanup(p, r);
616

    
617
  return 1;
618
}
619

    
620
static void
621
static_copy_routes(list *dlst, list *slst)
622
{
623
  struct static_route *sr;
624

    
625
  init_list(dlst);
626
  WALK_LIST(sr, *slst)
627
    {
628
      struct static_route *srr, *drr = NULL;
629
      for (srr = sr->mp_head; srr; srr = srr->mp_next)
630
      {
631
        /* copy one route */
632
        struct static_route *dr = cfg_alloc(sizeof(struct static_route));
633
        if (drr)
634
          drr->mp_next = dr;
635
        else
636
          add_tail(dlst, &(dr->n));
637

    
638
        memcpy(dr, sr, sizeof(struct static_route));
639
        drr = dr;
640
      }
641
    }
642
}
643

    
644
static void
645
static_copy_config(struct proto_config *dest, struct proto_config *src)
646
{
647
  struct static_config *d = (struct static_config *) dest;
648
  struct static_config *s = (struct static_config *) src;
649

    
650
  /* Copy route lists */
651
  static_copy_routes(&d->neigh_routes, &s->neigh_routes);
652
  static_copy_routes(&d->iface_routes, &s->iface_routes);
653
  static_copy_routes(&d->other_routes, &s->other_routes);
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 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
};
672

    
673
static byte *
674
static_format_via(struct static_route *r)
675
{
676
  static byte via[IPA_MAX_TEXT_LENGTH + 25];
677

    
678
  switch (r->dest)
679
    {
680
    case RTD_UNICAST:        if (ipa_zero(r->via)) bsprintf(via, "dev %s", r->if_name);
681
                        else bsprintf(via, "via %I%J", r->via, r->iface);
682
                        break;
683
    case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); break;
684
    case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
685
    case RTD_PROHIBIT:        bsprintf(via, "prohibited"); break;
686
    case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
687
    default:                bsprintf(via, "???");
688
    }
689
  return via;
690
}
691

    
692
static void
693
static_show_rt(struct static_route *r)
694
{
695
  if (r->mp_head && (r != r->mp_head))
696
    return;
697
  if (r->mp_next)
698
  {
699
    cli_msg(-1009, "%N", r->net);
700
    struct static_route *r2;
701
    for (r2 = r; r2; r2 = r2->mp_next)
702
    {
703
      cli_msg(-1009, "\t%s weight %d%s%s", static_format_via(r2), r2->weight + 1,
704
              r2->bfd_req ? " (bfd)" : "", (r2->state & STS_INSTALLED) ? "" : " (dormant)");
705
      if (r2->mp_next == r)
706
        break;
707
    }
708
  }
709
  else
710
    cli_msg(-1009, "%N %s%s%s", r->net, static_format_via(r),
711
          r->bfd_req ? " (bfd)" : "", (r->state & STS_INSTALLED) ? "" : " (dormant)");
712
}
713

    
714
void
715
static_show(struct proto *P)
716
{
717
  struct static_config *c = (void *) P->cf;
718
  struct static_route *r;
719

    
720
  WALK_LIST(r, c->neigh_routes)
721
    static_show_rt(r);
722
  WALK_LIST(r, c->iface_routes)
723
    static_show_rt(r);
724
  WALK_LIST(r, c->other_routes)
725
    static_show_rt(r);
726
  cli_msg(0, "");
727
}