Statistics
| Branch: | Revision:

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

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, struct iface *ifa)
62
{
63
  net *n;
64
  rta a;
65
  rte *e;
66

    
67
  if (r->installed > 0)
68
    return;
69

    
70
  DBG("Installing static route %N, rtd=%d\n", r->net, r->dest);
71
  bzero(&a, sizeof(a));
72
  a.src = p->main_source;
73
  a.source = (r->dest == RTD_DEVICE) ? RTS_STATIC_DEVICE : RTS_STATIC;
74
  a.scope = SCOPE_UNIVERSE;
75
  a.cast = RTC_UNICAST;
76
  a.dest = r->dest;
77
  a.gw = r->via;
78
  a.iface = ifa;
79

    
80
  if (r->dest == RTD_MULTIPATH)
81
    {
82
      struct static_route *r2;
83
      struct mpnh *nhs = NULL;
84
      struct mpnh **nhp = &nhs;
85

    
86
      for (r2 = r->mp_next; r2; r2 = r2->mp_next)
87
        if (r2->installed)
88
          {
89
            struct mpnh *nh = alloca(sizeof(struct mpnh));
90
            nh->gw = r2->via;
91
            nh->iface = r2->neigh->iface;
92
            nh->weight = r2->weight;
93
            nh->next = NULL;
94
            *nhp = nh;
95
            nhp = &(nh->next);
96
          }
97

    
98
      /* There is at least one nexthop */
99
      if (!nhs->next)
100
        {
101
          /* Fallback to unipath route for exactly one nexthop */
102
          a.dest = RTD_ROUTER;
103
          a.gw = nhs->gw;
104
          a.iface = nhs->iface;
105
        }
106
      else
107
        a.nexthops = nhs;
108
    }
109

    
110
  if (r->dest == RTDX_RECURSIVE)
111
    rta_set_recursive_next_hop(p->main_channel->table, &a, p_igp_table(p), &r->via, &r->via);
112

    
113
  /* We skip rta_lookup() here */
114

    
115
  n = net_get(p->main_channel->table, r->net);
116
  e = rte_get_temp(&a);
117
  e->net = n;
118
  e->pflags = 0;
119

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

    
123
  rte_update(p, n, e);
124
  r->installed = 1;
125

    
126
  if (r->cmds)
127
    lp_flush(static_lp);
128
}
129

    
130
static void
131
static_remove(struct proto *p, struct static_route *r)
132
{
133
  net *n;
134

    
135
  if (!r->installed)
136
    return;
137

    
138
  DBG("Removing static route %N via %I\n", r->net, r->via);
139
  n = net_find(p->main_channel->table, r->net);
140
  rte_update(p, n, NULL);
141
  r->installed = 0;
142
}
143

    
144
static void
145
static_bfd_notify(struct bfd_request *req);
146

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

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

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

    
167
static int
168
static_decide(struct static_config *cf, struct static_route *r)
169
{
170
  /* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
171
     the route also have to be valid (r->neigh != NULL) */
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
  return 1;
183
}
184

    
185

    
186
static void
187
static_add(struct proto *p, struct static_config *cf, struct static_route *r)
188
{
189
  DBG("static_add(%N,%d)\n", r->net, r->dest);
190
  switch (r->dest)
191
    {
192
    case RTD_ROUTER:
193
      {
194
        struct neighbor *n = neigh_find2(p, &r->via, r->via_if, NEF_STICKY);
195
        if (n)
196
          {
197
            r->chain = n->data;
198
            n->data = r;
199
            r->neigh = n;
200

    
201
            static_update_bfd(p, r);
202
            if (static_decide(cf, r))
203
              static_install(p, r, n->iface);
204
            else
205
              static_remove(p, r);
206
          }
207
        else
208
          {
209
            log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
210
            static_remove(p, r);
211
          }
212
        break;
213
      }
214

    
215
    case RTD_DEVICE:
216
      break;
217

    
218
    case RTD_MULTIPATH:
219
      {
220
        int count = 0;
221
        struct static_route *r2;
222

    
223
        for (r2 = r->mp_next; r2; r2 = r2->mp_next)
224
          {
225
            struct neighbor *n = neigh_find2(p, &r2->via, r2->via_if, NEF_STICKY);
226
            if (n)
227
              {
228
                r2->chain = n->data;
229
                n->data = r2;
230
                r2->neigh = n;
231

    
232
                static_update_bfd(p, r2);
233
                r2->installed = static_decide(cf, r2);
234
                count += r2->installed;
235
              }
236
            else
237
              {
238
                log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
239
                r2->installed = 0;
240
              }
241
          }
242

    
243
        if (count)
244
          static_install(p, r, NULL);
245
        else
246
          static_remove(p, r);
247
        break;
248
      }
249

    
250
    default:
251
      static_install(p, r, NULL);
252
    }
253
}
254

    
255
static void
256
static_rte_cleanup(struct proto *p, struct static_route *r)
257
{
258
  struct static_route *r2;
259

    
260
  if (r->bfd_req)
261
  {
262
    rfree(r->bfd_req);
263
    r->bfd_req = NULL;
264
  }
265

    
266
  if (r->dest == RTD_MULTIPATH)
267
    for (r2 = r->mp_next; r2; r2 = r2->mp_next)
268
      if (r2->bfd_req)
269
      {
270
        rfree(r2->bfd_req);
271
        r2->bfd_req = NULL;
272
      }
273
}
274

    
275
static int
276
static_start(struct proto *p)
277
{
278
  struct static_config *cf = (void *) p->cf;
279
  struct static_route *r;
280

    
281
  DBG("Static: take off!\n");
282

    
283
  if (!static_lp)
284
    static_lp = lp_new(&root_pool, 1008);
285

    
286
  if (cf->igp_table)
287
    rt_lock_table(cf->igp_table->table);
288

    
289
  /* We have to go UP before routes could be installed */
290
  proto_notify_state(p, PS_UP);
291

    
292
  WALK_LIST(r, cf->other_routes)
293
    static_add(p, cf, r);
294
  return PS_UP;
295
}
296

    
297
static int
298
static_shutdown(struct proto *p)
299
{
300
  struct static_config *cf = (void *) p->cf;
301
  struct static_route *r;
302

    
303
  /* Just reset the flag, the routes will be flushed by the nest */
304
  WALK_LIST(r, cf->iface_routes)
305
    r->installed = 0;
306
  WALK_LIST(r, cf->other_routes)
307
  {
308
    static_rte_cleanup(p, r);
309
    r->installed = 0;
310
  }
311

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

    
323
  return PS_DOWN;
324
}
325

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

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

    
335
static void
336
static_update_rte(struct proto *p, struct static_route *r)
337
{
338
  switch (r->dest)
339
  {
340
  case RTD_ROUTER:
341
    if (static_decide((struct static_config *) p->cf, r))
342
      static_install(p, r, r->neigh->iface);
343
    else
344
      static_remove(p, r);
345
    break;
346

    
347
  case RTD_NONE: /* a part of multipath route */
348
  {
349
    int decision = static_decide((struct static_config *) p->cf, r);
350
    if (decision == r->installed)
351
      break; /* no change */
352
    r->installed = decision;
353

    
354
    struct static_route *r1, *r2;
355
    int count = 0;
356
    r1 = (void *) r->if_name; /* really */
357
    for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
358
      count += r2->installed;
359

    
360
    if (count)
361
    {
362
      /* Set of nexthops changed - force reinstall */
363
      r1->installed = 0;
364
      static_install(p, r1, NULL);
365
    }
366
    else
367
      static_remove(p, r1);
368

    
369
    break;
370
  }
371
  }
372
}
373

    
374
static void
375
static_neigh_notify(struct neighbor *n)
376
{
377
  struct proto *p = n->proto;
378
  struct static_route *r;
379

    
380
  DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
381
  for(r=n->data; r; r=r->chain)
382
  {
383
    static_update_bfd(p, r);
384
    static_update_rte(p, r);
385
  }
386
}
387

    
388
static void
389
static_bfd_notify(struct bfd_request *req)
390
{
391
  struct static_route *r = req->data;
392
  struct proto *p = r->neigh->proto;
393

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

    
396
  static_update_rte(p, r);
397
}
398

    
399
static void
400
static_dump_rt(struct static_route *r)
401
{
402
  debug("%-1N: ", r->net);
403
  switch (r->dest)
404
    {
405
    case RTD_ROUTER:
406
      debug("via %I\n", r->via);
407
      break;
408
    case RTD_DEVICE:
409
      debug("dev %s\n", r->if_name);
410
      break;
411
    default:
412
      debug("rtd %d\n", r->dest);
413
      break;
414
    }
415
}
416

    
417
static void
418
static_dump(struct proto *p)
419
{
420
  struct static_config *c = (void *) p->cf;
421
  struct static_route *r;
422

    
423
  debug("Independent static routes:\n");
424
  WALK_LIST(r, c->other_routes)
425
    static_dump_rt(r);
426
  debug("Device static routes:\n");
427
  WALK_LIST(r, c->iface_routes)
428
    static_dump_rt(r);
429
}
430

    
431
static void
432
static_if_notify(struct proto *p, unsigned flags, struct iface *i)
433
{
434
  struct static_route *r;
435
  struct static_config *c = (void *) p->cf;
436

    
437
  if (flags & IF_CHANGE_UP)
438
    {
439
      WALK_LIST(r, c->iface_routes)
440
        if (!strcmp(r->if_name, i->name))
441
          static_install(p, r, i);
442
    }
443
  else if (flags & IF_CHANGE_DOWN)
444
    {
445
      WALK_LIST(r, c->iface_routes)
446
        if (!strcmp(r->if_name, i->name))
447
          static_remove(p, r);
448
    }
449
}
450

    
451
int
452
static_rte_mergable(rte *pri, rte *sec)
453
{
454
  return 1;
455
}
456

    
457
void
458
static_init_config(struct static_config *c)
459
{
460
  init_list(&c->iface_routes);
461
  init_list(&c->other_routes);
462
}
463

    
464
static void
465
static_postconfig(struct proto_config *CF)
466
{
467
  struct static_config *cf = (void *) CF;
468
  struct static_route *r;
469

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

    
473

    
474
  WALK_LIST(r, cf->iface_routes)
475
    if (r->net->type != CF->net_type)
476
      cf_error("Route %N incompatible with channel type", r->net);
477

    
478
  WALK_LIST(r, cf->other_routes)
479
    if (r->net->type != CF->net_type)
480
      cf_error("Route %N incompatible with channel type", r->net);
481
}
482

    
483

    
484
static struct proto *
485
static_init(struct proto_config *CF)
486
{
487
  struct proto *P = proto_new(CF);
488
  // struct static_proto *p = (void *) P;
489
  // struct static_config *cf = (void *) CF;
490

    
491
  P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
492

    
493
  P->neigh_notify = static_neigh_notify;
494
  P->if_notify = static_if_notify;
495
  P->rte_mergable = static_rte_mergable;
496

    
497
  return P;
498
}
499

    
500
static inline int
501
static_same_dest(struct static_route *x, struct static_route *y)
502
{
503
  if (x->dest != y->dest)
504
    return 0;
505

    
506
  switch (x->dest)
507
    {
508
    case RTD_ROUTER:
509
      return ipa_equal(x->via, y->via) && (x->via_if == y->via_if);
510

    
511
    case RTD_DEVICE:
512
      return !strcmp(x->if_name, y->if_name);
513

    
514
    case RTD_MULTIPATH:
515
      for (x = x->mp_next, y = y->mp_next;
516
           x && y;
517
           x = x->mp_next, y = y->mp_next)
518
        if (!ipa_equal(x->via, y->via) ||
519
            (x->via_if != y->via_if) ||
520
            (x->use_bfd != y->use_bfd) ||
521
            (x->weight != y->weight))
522
          return 0;
523
      return !x && !y;
524

    
525
    case RTDX_RECURSIVE:
526
      return ipa_equal(x->via, y->via);
527

    
528
    default:
529
      return 1;
530
    }
531
}
532

    
533
static inline int
534
static_same_rte(struct static_route *x, struct static_route *y)
535
{
536
  return static_same_dest(x, y) && i_same(x->cmds, y->cmds);
537
}
538

    
539

    
540
static void
541
static_match(struct proto *p, struct static_route *r, struct static_config *n)
542
{
543
  struct static_route *t;
544

    
545
  /*
546
   * For given old route *r we find whether a route to the same
547
   * network is also in the new route list. In that case, we keep the
548
   * route and possibly update the route later if destination changed.
549
   * Otherwise, we remove the route.
550
   */
551

    
552
  if (r->neigh)
553
    r->neigh->data = NULL;
554

    
555
  WALK_LIST(t, n->iface_routes)
556
    if (net_equal(r->net, t->net))
557
      goto found;
558

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

    
563
  static_remove(p, r);
564
  return;
565

    
566
 found:
567
  /* If destination is different, force reinstall */
568
  if ((r->installed > 0) && !static_same_rte(r, t))
569
    t->installed = -1;
570
  else
571
    t->installed = r->installed;
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->iface_routes)
595
    static_match(p, r, n);
596
  WALK_LIST(r, o->other_routes)
597
    static_match(p, r, n);
598

    
599
  /* Now add all new routes, those not changed will be ignored by static_install() */
600
  WALK_LIST(r, n->iface_routes)
601
    {
602
      struct iface *ifa;
603
      if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
604
        static_install(p, r, ifa);
605
    }
606
  WALK_LIST(r, n->other_routes)
607
    static_add(p, n, r);
608

    
609
  WALK_LIST(r, o->other_routes)
610
    static_rte_cleanup(p, r);
611

    
612
  return 1;
613
}
614

    
615
static void
616
static_copy_routes(list *dlst, list *slst)
617
{
618
  struct static_route *dr, *sr;
619

    
620
  init_list(dlst);
621
  WALK_LIST(sr, *slst)
622
    {
623
      /* copy one route */
624
      dr = cfg_alloc(sizeof(struct static_route));
625
      memcpy(dr, sr, sizeof(struct static_route));
626

    
627
      /* This fn is supposed to be called on fresh src routes, which have 'live'
628
         fields (like .chain, .neigh or .installed) zero, so no need to zero them */
629

    
630
      /* We need to copy multipath chain, because there are backptrs in 'if_name' */
631
      if (dr->dest == RTD_MULTIPATH)
632
        {
633
          struct static_route *md, *ms, **mp_last;
634

    
635
          mp_last = &(dr->mp_next);
636
          for (ms = sr->mp_next; ms; ms = ms->mp_next)
637
            {
638
              md = cfg_alloc(sizeof(struct static_route));
639
              memcpy(md, ms, sizeof(struct static_route));
640
              md->if_name = (void *) dr; /* really */
641

    
642
              *mp_last = md;
643
              mp_last = &(md->mp_next);
644
            }
645
          *mp_last = NULL;
646
        }
647

    
648
      add_tail(dlst, (node *) dr);
649
    }
650
}
651

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

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

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

    
680
static void
681
static_show_rt(struct static_route *r)
682
{
683
  byte via[IPA_MAX_TEXT_LENGTH + 25];
684

    
685
  switch (r->dest)
686
    {
687
    case RTD_ROUTER:        bsprintf(via, "via %I%J", r->via, r->via_if); break;
688
    case RTD_DEVICE:        bsprintf(via, "dev %s", r->if_name); break;
689
    case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); break;
690
    case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
691
    case RTD_PROHIBIT:        bsprintf(via, "prohibited"); break;
692
    case RTD_MULTIPATH:        bsprintf(via, "multipath"); break;
693
    case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
694
    default:                bsprintf(via, "???");
695
    }
696
  cli_msg(-1009, "%N %s%s%s", r->net, via,
697
          r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
698

    
699
  struct static_route *r2;
700
  if (r->dest == RTD_MULTIPATH)
701
    for (r2 = r->mp_next; r2; r2 = r2->mp_next)
702
      cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->weight + 1,
703
              r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
704
}
705

    
706
void
707
static_show(struct proto *P)
708
{
709
  struct static_config *c = (void *) P->cf;
710
  struct static_route *r;
711

    
712
  WALK_LIST(r, c->other_routes)
713
    static_show_rt(r);
714
  WALK_LIST(r, c->iface_routes)
715
    static_show_rt(r);
716
  cli_msg(0, "");
717
}