Statistics
| Branch: | Revision:

iof-bird-daemon / proto / static / static.c @ 4a591d4b

History | View | Annotate | Download (13.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 "lib/string.h"
46
#include "lib/alloca.h"
47

    
48
#include "static.h"
49

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

    
57

    
58
static void
59
static_install(struct proto *p, struct static_route *r, struct iface *ifa)
60
{
61
  net *n;
62
  rta a, *aa;
63
  rte *e;
64

    
65
  if (r->installed > 0)
66
    return;
67

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

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

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

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

    
108
  if (r->dest == RTDX_RECURSIVE)
109
    rta_set_recursive_next_hop(p->table, &a, p_igp_table(p), &r->via, &r->via);
110

    
111
  aa = rta_lookup(&a);
112
  n = net_get(p->table, r->net, r->masklen);
113
  e = rte_get_temp(aa);
114
  e->net = n;
115
  e->pflags = 0;
116
  rte_update(p, n, e);
117
  r->installed = 1;
118
}
119

    
120
static void
121
static_remove(struct proto *p, struct static_route *r)
122
{
123
  net *n;
124

    
125
  if (!r->installed)
126
    return;
127

    
128
  DBG("Removing static route %I/%d via %I\n", r->net, r->masklen, r->via);
129
  n = net_find(p->table, r->net, r->masklen);
130
  rte_update(p, n, NULL);
131
  r->installed = 0;
132
}
133

    
134
static int
135
static_decide(struct static_config *cf, struct static_route *r)
136
{
137
  /* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
138
     the route also have to be valid (r->neigh != NULL) */
139

    
140
  if (r->neigh->scope < 0)
141
    return 0;
142

    
143
  if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
144
    return 0;
145

    
146
  return 1;
147
}
148

    
149

    
150
static void
151
static_add(struct proto *p, struct static_config *cf, struct static_route *r)
152
{
153
  DBG("static_add(%I/%d,%d)\n", r->net, r->masklen, r->dest);
154
  switch (r->dest)
155
    {
156
    case RTD_ROUTER:
157
      {
158
        struct neighbor *n = neigh_find2(p, &r->via, r->via_if, NEF_STICKY);
159
        if (n)
160
          {
161
            r->chain = n->data;
162
            n->data = r;
163
            r->neigh = n;
164
            if (static_decide(cf, r))
165
              static_install(p, r, n->iface);
166
            else
167
              static_remove(p, r);
168
          }
169
        else
170
          {
171
            log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
172
            static_remove(p, r);
173
          }
174
        break;
175
      }
176

    
177
    case RTD_DEVICE:
178
      break;
179

    
180
    case RTD_MULTIPATH:
181
      {
182
        int count = 0;
183
        struct static_route *r2;
184

    
185
        for (r2 = r->mp_next; r2; r2 = r2->mp_next)
186
          {
187
            struct neighbor *n = neigh_find2(p, &r2->via, r2->via_if, NEF_STICKY);
188
            if (n)
189
              {
190
                r2->chain = n->data;
191
                n->data = r2;
192
                r2->neigh = n;
193
                r2->installed = static_decide(cf, r2);
194
                count += r2->installed;
195
              }
196
            else
197
              {
198
                log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
199
                r2->installed = 0;
200
              }
201
          }
202

    
203
        if (count)
204
          static_install(p, r, NULL);
205
        else
206
          static_remove(p, r);
207
        break;
208
      }
209

    
210
    default:
211
      static_install(p, r, NULL);
212
    }
213
}
214

    
215
static int
216
static_start(struct proto *p)
217
{
218
  struct static_config *cf = (void *) p->cf;
219
  struct static_route *r;
220

    
221
  DBG("Static: take off!\n");
222

    
223
  if (cf->igp_table)
224
    rt_lock_table(cf->igp_table->table);
225

    
226
  /* We have to go UP before routes could be installed */
227
  proto_notify_state(p, PS_UP);
228

    
229
  WALK_LIST(r, cf->other_routes)
230
    static_add(p, cf, r);
231
  return PS_UP;
232
}
233

    
234
static int
235
static_shutdown(struct proto *p)
236
{
237
  struct static_config *cf = (void *) p->cf;
238
  struct static_route *r;
239

    
240
  /* Just reset the flag, the routes will be flushed by the nest */
241
  WALK_LIST(r, cf->iface_routes)
242
    r->installed = 0;
243
  WALK_LIST(r, cf->other_routes)
244
    r->installed = 0;
245

    
246
  return PS_DOWN;
247
}
248

    
249
static void
250
static_cleanup(struct proto *p)
251
{
252
  struct static_config *cf = (void *) p->cf;
253

    
254
  if (cf->igp_table)
255
    rt_unlock_table(cf->igp_table->table);
256
}
257

    
258

    
259
static void
260
static_neigh_notify(struct neighbor *n)
261
{
262
  struct proto *p = n->proto;
263
  struct static_route *r;
264

    
265
  DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
266
  for(r=n->data; r; r=r->chain)
267
    switch (r->dest)
268
      {
269
      case RTD_ROUTER:
270
        if (static_decide((struct static_config *) p->cf, r))
271
          static_install(p, r, n->iface);
272
        else
273
          static_remove(p, r);
274
        break;
275

    
276
      case RTD_NONE: /* a part of multipath route */
277
        {
278
          int decision = static_decide((struct static_config *) p->cf, r);
279
          if (decision == r->installed)
280
            break; /* no change */
281
          r->installed = decision;
282

    
283
          struct static_route *r1, *r2;
284
          int count = 0;
285
          r1 = (void *) r->if_name; /* really */
286
          for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
287
            count += r2->installed;
288

    
289
          if (count)
290
            {
291
              /* Set of nexthops changed - force reinstall */
292
              r1->installed = 0;
293
              static_install(p, r1, NULL);
294
            }
295
          else
296
            static_remove(p, r1);
297

    
298
          break;
299
        }
300
      }
301
}
302

    
303
static void
304
static_dump_rt(struct static_route *r)
305
{
306
  debug("%-1I/%2d: ", r->net, r->masklen);
307
  switch (r->dest)
308
    {
309
    case RTD_ROUTER:
310
      debug("via %I\n", r->via);
311
      break;
312
    case RTD_DEVICE:
313
      debug("dev %s\n", r->if_name);
314
      break;
315
    default:
316
      debug("rtd %d\n", r->dest);
317
      break;
318
    }
319
}
320

    
321
static void
322
static_dump(struct proto *p)
323
{
324
  struct static_config *c = (void *) p->cf;
325
  struct static_route *r;
326

    
327
  debug("Independent static routes:\n");
328
  WALK_LIST(r, c->other_routes)
329
    static_dump_rt(r);
330
  debug("Device static routes:\n");
331
  WALK_LIST(r, c->iface_routes)
332
    static_dump_rt(r);
333
}
334

    
335
static void
336
static_if_notify(struct proto *p, unsigned flags, struct iface *i)
337
{
338
  struct static_route *r;
339
  struct static_config *c = (void *) p->cf;
340

    
341
  if (flags & IF_CHANGE_UP)
342
    {
343
      WALK_LIST(r, c->iface_routes)
344
        if (!strcmp(r->if_name, i->name))
345
          static_install(p, r, i);
346
    }
347
  else if (flags & IF_CHANGE_DOWN)
348
    {
349
      WALK_LIST(r, c->iface_routes)
350
        if (!strcmp(r->if_name, i->name))
351
          static_remove(p, r);
352
    }
353
}
354

    
355
void
356
static_init_config(struct static_config *c)
357
{
358
  init_list(&c->iface_routes);
359
  init_list(&c->other_routes);
360
}
361

    
362
static struct proto *
363
static_init(struct proto_config *c)
364
{
365
  struct proto *p = proto_new(c, sizeof(struct proto));
366

    
367
  p->neigh_notify = static_neigh_notify;
368
  p->if_notify = static_if_notify;
369

    
370
  return p;
371
}
372

    
373
static inline int
374
static_same_net(struct static_route *x, struct static_route *y)
375
{
376
  return ipa_equal(x->net, y->net) && (x->masklen == y->masklen);
377
}
378

    
379
static inline int
380
static_same_dest(struct static_route *x, struct static_route *y)
381
{
382
  if (x->dest != y->dest)
383
    return 0;
384

    
385
  switch (x->dest)
386
    {
387
    case RTD_ROUTER:
388
      return ipa_equal(x->via, y->via) && (x->via_if == y->via_if);
389

    
390
    case RTD_DEVICE:
391
      return !strcmp(x->if_name, y->if_name);
392

    
393
    case RTD_MULTIPATH:
394
      for (x = x->mp_next, y = y->mp_next;
395
           x && y;
396
           x = x->mp_next, y = y->mp_next)
397
        if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if))
398
          return 0;
399
      return !x && !y;
400

    
401
    case RTDX_RECURSIVE:
402
      return ipa_equal(x->via, y->via);
403

    
404
    default:
405
      return 1;
406
    }
407
}
408

    
409
static void
410
static_match(struct proto *p, struct static_route *r, struct static_config *n)
411
{
412
  struct static_route *t;
413

    
414
  /*
415
   * For given old route *r we find whether a route to the same
416
   * network is also in the new route list. In that case, we keep the
417
   * route and possibly update the route later if destination changed.
418
   * Otherwise, we remove the route.
419
   */
420

    
421
  if (r->neigh)
422
    r->neigh->data = NULL;
423

    
424
  WALK_LIST(t, n->iface_routes)
425
    if (static_same_net(r, t))
426
      goto found;
427

    
428
  WALK_LIST(t, n->other_routes)
429
    if (static_same_net(r, t))
430
      goto found;
431

    
432
  static_remove(p, r);
433
  return;
434

    
435
 found:
436
  /* If destination is different, force reinstall */
437
  if ((r->installed > 0) && !static_same_dest(r, t))
438
    t->installed = -1;
439
  else
440
    t->installed = r->installed;
441
}
442

    
443
static inline rtable *
444
cf_igp_table(struct static_config *cf)
445
{
446
  return cf->igp_table ? cf->igp_table->table : NULL;
447
}
448

    
449
static int
450
static_reconfigure(struct proto *p, struct proto_config *new)
451
{
452
  struct static_config *o = (void *) p->cf;
453
  struct static_config *n = (void *) new;
454
  struct static_route *r;
455

    
456
  if (cf_igp_table(o) != cf_igp_table(n))
457
    return 0;
458

    
459
  /* Delete all obsolete routes and reset neighbor entries */
460
  WALK_LIST(r, o->iface_routes)
461
    static_match(p, r, n);
462
  WALK_LIST(r, o->other_routes)
463
    static_match(p, r, n);
464

    
465
  /* Now add all new routes, those not changed will be ignored by static_install() */
466
  WALK_LIST(r, n->iface_routes)
467
    {
468
      struct iface *ifa;
469
      if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
470
        static_install(p, r, ifa);
471
    }
472
  WALK_LIST(r, n->other_routes)
473
    static_add(p, n, r);
474

    
475
  return 1;
476
}
477

    
478
static void
479
static_copy_routes(list *dlst, list *slst)
480
{
481
  struct static_route *dr, *sr;
482

    
483
  init_list(dlst);
484
  WALK_LIST(sr, *slst)
485
    {
486
      /* copy one route */
487
      dr = cfg_alloc(sizeof(struct static_route));
488
      memcpy(dr, sr, sizeof(struct static_route));
489

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

    
493
      /* We need to copy multipath chain, because there are backptrs in 'if_name' */
494
      if (dr->dest == RTD_MULTIPATH)
495
        {
496
          struct static_route *md, *ms, **mp_last;
497

    
498
          mp_last = &(dr->mp_next);
499
          for (ms = sr->mp_next; ms; ms = ms->mp_next)
500
            {
501
              md = cfg_alloc(sizeof(struct static_route));
502
              memcpy(md, ms, sizeof(struct static_route));
503
              md->if_name = (void *) dr; /* really */
504

    
505
              *mp_last = md;
506
              mp_last = &(md->mp_next);
507
            }
508
          *mp_last = NULL;
509
        }
510

    
511
      add_tail(dlst, (node *) dr);
512
    }
513
}
514

    
515
static void
516
static_copy_config(struct proto_config *dest, struct proto_config *src)
517
{
518
  struct static_config *d = (struct static_config *) dest;
519
  struct static_config *s = (struct static_config *) src;
520

    
521
  /* Shallow copy of everything */
522
  proto_copy_rest(dest, src, sizeof(struct static_config));
523

    
524
  /* Copy route lists */
525
  static_copy_routes(&d->iface_routes, &s->iface_routes);
526
  static_copy_routes(&d->other_routes, &s->other_routes);
527
}
528

    
529

    
530
struct protocol proto_static = {
531
  .name =                "Static",
532
  .template =                "static%d",
533
  .preference =                DEF_PREF_STATIC,
534
  .init =                static_init,
535
  .dump =                static_dump,
536
  .start =                static_start,
537
  .shutdown =                static_shutdown,
538
  .cleanup =                static_cleanup,
539
  .reconfigure =        static_reconfigure,
540
  .copy_config =        static_copy_config
541
};
542

    
543
static void
544
static_show_rt(struct static_route *r)
545
{
546
  byte via[STD_ADDRESS_P_LENGTH + 16];
547

    
548
  switch (r->dest)
549
    {
550
    case RTD_ROUTER:        bsprintf(via, "via %I%J", r->via, r->via_if); break;
551
    case RTD_DEVICE:        bsprintf(via, "dev %s", r->if_name); break;
552
    case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); break;
553
    case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
554
    case RTD_PROHIBIT:        bsprintf(via, "prohibited"); break;
555
    case RTD_MULTIPATH:        bsprintf(via, "multipath"); break;
556
    case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
557
    default:                bsprintf(via, "???");
558
    }
559
  cli_msg(-1009, "%I/%d %s%s", r->net, r->masklen, via, r->installed ? "" : " (dormant)");
560

    
561
  struct static_route *r2;
562
  if (r->dest == RTD_MULTIPATH)
563
    for (r2 = r->mp_next; r2; r2 = r2->mp_next)
564
      cli_msg(-1009, "\tvia %I%J weight %d%s", r2->via, r2->via_if, r2->masklen + 1, /* really */
565
              r2->installed ? "" : " (dormant)");
566
}
567

    
568
void
569
static_show(struct proto *P)
570
{
571
  struct static_config *c = (void *) P->cf;
572
  struct static_route *r;
573

    
574
  WALK_LIST(r, c->other_routes)
575
    static_show_rt(r);
576
  WALK_LIST(r, c->iface_routes)
577
    static_show_rt(r);
578
  cli_msg(0, "");
579
}