Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (15.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 some fields (masklen, if_name) 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->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 %I/%d, rtd=%d\n", r->net, r->masklen, 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->masklen; /* really */
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->table, &a, p_igp_table(p), &r->via, &r->via);
112

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

    
115
  n = net_get_ipa(p->table, r->net, r->masklen);
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 %I/%d via %I\n", r->net, r->masklen, r->via);
139
  n = net_find_ipa(p->table, r->net, r->masklen);
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(%I/%d,%d)\n", r->net, r->masklen, 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
  return PS_DOWN;
313
}
314

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

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

    
324
static void
325
static_update_rte(struct proto *p, struct static_route *r)
326
{
327
  switch (r->dest)
328
  {
329
  case RTD_ROUTER:
330
    if (static_decide((struct static_config *) p->cf, r))
331
      static_install(p, r, r->neigh->iface);
332
    else
333
      static_remove(p, r);
334
    break;
335

    
336
  case RTD_NONE: /* a part of multipath route */
337
  {
338
    int decision = static_decide((struct static_config *) p->cf, r);
339
    if (decision == r->installed)
340
      break; /* no change */
341
    r->installed = decision;
342

    
343
    struct static_route *r1, *r2;
344
    int count = 0;
345
    r1 = (void *) r->if_name; /* really */
346
    for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
347
      count += r2->installed;
348

    
349
    if (count)
350
    {
351
      /* Set of nexthops changed - force reinstall */
352
      r1->installed = 0;
353
      static_install(p, r1, NULL);
354
    }
355
    else
356
      static_remove(p, r1);
357

    
358
    break;
359
  }
360
  }
361
}
362

    
363
static void
364
static_neigh_notify(struct neighbor *n)
365
{
366
  struct proto *p = n->proto;
367
  struct static_route *r;
368

    
369
  DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
370
  for(r=n->data; r; r=r->chain)
371
  {
372
    static_update_bfd(p, r);
373
    static_update_rte(p, r);
374
  }
375
}
376

    
377
static void
378
static_bfd_notify(struct bfd_request *req)
379
{
380
  struct static_route *r = req->data;
381
  struct proto *p = r->neigh->proto;
382

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

    
385
  static_update_rte(p, r);
386
}
387

    
388
static void
389
static_dump_rt(struct static_route *r)
390
{
391
  debug("%-1I/%2d: ", r->net, r->masklen);
392
  switch (r->dest)
393
    {
394
    case RTD_ROUTER:
395
      debug("via %I\n", r->via);
396
      break;
397
    case RTD_DEVICE:
398
      debug("dev %s\n", r->if_name);
399
      break;
400
    default:
401
      debug("rtd %d\n", r->dest);
402
      break;
403
    }
404
}
405

    
406
static void
407
static_dump(struct proto *p)
408
{
409
  struct static_config *c = (void *) p->cf;
410
  struct static_route *r;
411

    
412
  debug("Independent static routes:\n");
413
  WALK_LIST(r, c->other_routes)
414
    static_dump_rt(r);
415
  debug("Device static routes:\n");
416
  WALK_LIST(r, c->iface_routes)
417
    static_dump_rt(r);
418
}
419

    
420
static void
421
static_if_notify(struct proto *p, unsigned flags, struct iface *i)
422
{
423
  struct static_route *r;
424
  struct static_config *c = (void *) p->cf;
425

    
426
  if (flags & IF_CHANGE_UP)
427
    {
428
      WALK_LIST(r, c->iface_routes)
429
        if (!strcmp(r->if_name, i->name))
430
          static_install(p, r, i);
431
    }
432
  else if (flags & IF_CHANGE_DOWN)
433
    {
434
      WALK_LIST(r, c->iface_routes)
435
        if (!strcmp(r->if_name, i->name))
436
          static_remove(p, r);
437
    }
438
}
439

    
440
int
441
static_rte_mergable(rte *pri, rte *sec)
442
{
443
  return 1;
444
}
445

    
446
void
447
static_init_config(struct static_config *c)
448
{
449
  init_list(&c->iface_routes);
450
  init_list(&c->other_routes);
451
}
452

    
453
static struct proto *
454
static_init(struct proto_config *c)
455
{
456
  struct proto *p = proto_new(c, sizeof(struct proto));
457

    
458
  p->neigh_notify = static_neigh_notify;
459
  p->if_notify = static_if_notify;
460
  p->rte_mergable = static_rte_mergable;
461

    
462
  return p;
463
}
464

    
465
static inline int
466
static_same_net(struct static_route *x, struct static_route *y)
467
{
468
  return ipa_equal(x->net, y->net) && (x->masklen == y->masklen);
469
}
470

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

    
477
  switch (x->dest)
478
    {
479
    case RTD_ROUTER:
480
      return ipa_equal(x->via, y->via) && (x->via_if == y->via_if);
481

    
482
    case RTD_DEVICE:
483
      return !strcmp(x->if_name, y->if_name);
484

    
485
    case RTD_MULTIPATH:
486
      for (x = x->mp_next, y = y->mp_next;
487
           x && y;
488
           x = x->mp_next, y = y->mp_next)
489
        if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if) || (x->use_bfd != y->use_bfd))
490
          return 0;
491
      return !x && !y;
492

    
493
    case RTDX_RECURSIVE:
494
      return ipa_equal(x->via, y->via);
495

    
496
    default:
497
      return 1;
498
    }
499
}
500

    
501
static inline int
502
static_same_rte(struct static_route *x, struct static_route *y)
503
{
504
  return static_same_dest(x, y) && i_same(x->cmds, y->cmds);
505
}
506

    
507

    
508
static void
509
static_match(struct proto *p, struct static_route *r, struct static_config *n)
510
{
511
  struct static_route *t;
512

    
513
  /*
514
   * For given old route *r we find whether a route to the same
515
   * network is also in the new route list. In that case, we keep the
516
   * route and possibly update the route later if destination changed.
517
   * Otherwise, we remove the route.
518
   */
519

    
520
  if (r->neigh)
521
    r->neigh->data = NULL;
522

    
523
  WALK_LIST(t, n->iface_routes)
524
    if (static_same_net(r, t))
525
      goto found;
526

    
527
  WALK_LIST(t, n->other_routes)
528
    if (static_same_net(r, t))
529
      goto found;
530

    
531
  static_remove(p, r);
532
  return;
533

    
534
 found:
535
  /* If destination is different, force reinstall */
536
  if ((r->installed > 0) && !static_same_rte(r, t))
537
    t->installed = -1;
538
  else
539
    t->installed = r->installed;
540
}
541

    
542
static inline rtable *
543
cf_igp_table(struct static_config *cf)
544
{
545
  return cf->igp_table ? cf->igp_table->table : NULL;
546
}
547

    
548
static int
549
static_reconfigure(struct proto *p, struct proto_config *new)
550
{
551
  struct static_config *o = (void *) p->cf;
552
  struct static_config *n = (void *) new;
553
  struct static_route *r;
554

    
555
  if (cf_igp_table(o) != cf_igp_table(n))
556
    return 0;
557

    
558
  /* Delete all obsolete routes and reset neighbor entries */
559
  WALK_LIST(r, o->iface_routes)
560
    static_match(p, r, n);
561
  WALK_LIST(r, o->other_routes)
562
    static_match(p, r, n);
563

    
564
  /* Now add all new routes, those not changed will be ignored by static_install() */
565
  WALK_LIST(r, n->iface_routes)
566
    {
567
      struct iface *ifa;
568
      if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
569
        static_install(p, r, ifa);
570
    }
571
  WALK_LIST(r, n->other_routes)
572
    static_add(p, n, r);
573

    
574
  WALK_LIST(r, o->other_routes)
575
    static_rte_cleanup(p, r);
576

    
577
  return 1;
578
}
579

    
580
static void
581
static_copy_routes(list *dlst, list *slst)
582
{
583
  struct static_route *dr, *sr;
584

    
585
  init_list(dlst);
586
  WALK_LIST(sr, *slst)
587
    {
588
      /* copy one route */
589
      dr = cfg_alloc(sizeof(struct static_route));
590
      memcpy(dr, sr, sizeof(struct static_route));
591

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

    
595
      /* We need to copy multipath chain, because there are backptrs in 'if_name' */
596
      if (dr->dest == RTD_MULTIPATH)
597
        {
598
          struct static_route *md, *ms, **mp_last;
599

    
600
          mp_last = &(dr->mp_next);
601
          for (ms = sr->mp_next; ms; ms = ms->mp_next)
602
            {
603
              md = cfg_alloc(sizeof(struct static_route));
604
              memcpy(md, ms, sizeof(struct static_route));
605
              md->if_name = (void *) dr; /* really */
606

    
607
              *mp_last = md;
608
              mp_last = &(md->mp_next);
609
            }
610
          *mp_last = NULL;
611
        }
612

    
613
      add_tail(dlst, (node *) dr);
614
    }
615
}
616

    
617
static void
618
static_copy_config(struct proto_config *dest, struct proto_config *src)
619
{
620
  struct static_config *d = (struct static_config *) dest;
621
  struct static_config *s = (struct static_config *) src;
622

    
623
  /* Shallow copy of everything */
624
  proto_copy_rest(dest, src, sizeof(struct static_config));
625

    
626
  /* Copy route lists */
627
  static_copy_routes(&d->iface_routes, &s->iface_routes);
628
  static_copy_routes(&d->other_routes, &s->other_routes);
629
}
630

    
631

    
632
struct protocol proto_static = {
633
  .name =                "Static",
634
  .template =                "static%d",
635
  .preference =                DEF_PREF_STATIC,
636
  .config_size =        sizeof(struct static_config),
637
  .init =                static_init,
638
  .dump =                static_dump,
639
  .start =                static_start,
640
  .shutdown =                static_shutdown,
641
  .cleanup =                static_cleanup,
642
  .reconfigure =        static_reconfigure,
643
  .copy_config =        static_copy_config
644
};
645

    
646
static void
647
static_show_rt(struct static_route *r)
648
{
649
  byte via[STD_ADDRESS_P_LENGTH + 16];
650

    
651
  switch (r->dest)
652
    {
653
    case RTD_ROUTER:        bsprintf(via, "via %I%J", r->via, r->via_if); break;
654
    case RTD_DEVICE:        bsprintf(via, "dev %s", r->if_name); break;
655
    case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); break;
656
    case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
657
    case RTD_PROHIBIT:        bsprintf(via, "prohibited"); break;
658
    case RTD_MULTIPATH:        bsprintf(via, "multipath"); break;
659
    case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
660
    default:                bsprintf(via, "???");
661
    }
662
  cli_msg(-1009, "%I/%d %s%s%s", r->net, r->masklen, via,
663
          r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
664

    
665
  struct static_route *r2;
666
  if (r->dest == RTD_MULTIPATH)
667
    for (r2 = r->mp_next; r2; r2 = r2->mp_next)
668
      cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->masklen + 1, /* really */
669
              r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
670
}
671

    
672
void
673
static_show(struct proto *P)
674
{
675
  struct static_config *c = (void *) P->cf;
676
  struct static_route *r;
677

    
678
  WALK_LIST(r, c->other_routes)
679
    static_show_rt(r);
680
  WALK_LIST(r, c->iface_routes)
681
    static_show_rt(r);
682
  cli_msg(0, "");
683
}