Statistics
| Branch: | Revision:

iof-bird-daemon / proto / static / static.c @ 4116db18

History | View | Annotate | Download (12 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)
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.proto = p;
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->table, n, p, p, 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\n", r->net, r->masklen);
129
  n = net_find(p->table, r->net, r->masklen);
130
  if (n)
131
    rte_update(p->table, n, p, p, NULL);
132
  r->installed = 0;
133
}
134

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

    
141
  struct iface *ifa = r->neigh->iface;
142

    
143
  if (!ifa)
144
    return 0;
145

    
146
  if (cf->check_link && !(ifa->flags & IF_LINK_UP))
147
    return 0;
148

    
149
  return 1;
150
}
151

    
152

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

    
180
    case RTD_DEVICE:
181
      break;
182

    
183
    case RTD_MULTIPATH:
184
      {
185
        int count = 0;
186
        struct static_route *r2;
187

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

    
206
        if (count)
207
          static_install(p, r, NULL);
208
        else
209
          static_remove(p, r);
210
        break;
211
      }
212

    
213
    default:
214
      static_install(p, r, NULL);
215
    }
216
}
217

    
218
static int
219
static_start(struct proto *p)
220
{
221
  struct static_config *cf = (void *) p->cf;
222
  struct static_route *r;
223

    
224
  DBG("Static: take off!\n");
225

    
226
  if (cf->igp_table)
227
    rt_lock_table(cf->igp_table->table);
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
  c->c.preference = DEF_PREF_STATIC;
359
  init_list(&c->iface_routes);
360
  init_list(&c->other_routes);
361
}
362

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

    
368
  p->neigh_notify = static_neigh_notify;
369
  p->if_notify = static_if_notify;
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);
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))
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
  WALK_LIST(t, n->iface_routes)
424
    if (static_same_net(r, t))
425
      {
426
        t->installed = r->installed && static_same_dest(r, t);
427
        return;
428
      }
429
  WALK_LIST(t, n->other_routes)
430
    if (static_same_net(r, t))
431
      {
432
        t->installed = r->installed && static_same_dest(r, t);
433
        return;
434
      }
435
  static_remove(p, r);
436
}
437

    
438
static inline rtable *
439
cf_igp_table(struct static_config *cf)
440
{
441
  return cf->igp_table ? cf->igp_table->table : NULL;
442
}
443

    
444
static int
445
static_reconfigure(struct proto *p, struct proto_config *new)
446
{
447
  struct static_config *o = (void *) p->cf;
448
  struct static_config *n = (void *) new;
449
  struct static_route *r;
450

    
451
  if (cf_igp_table(o) != cf_igp_table(n))
452
    return 0;
453

    
454
  /* Delete all obsolete routes and reset neighbor entries */
455
  WALK_LIST(r, o->iface_routes)
456
    static_match(p, r, n);
457
  WALK_LIST(r, o->other_routes)
458
    static_match(p, r, n);
459

    
460
  /* Now add all new routes, those not changed will be ignored by static_install() */
461
  WALK_LIST(r, n->iface_routes)
462
    {
463
      struct iface *ifa;
464
      if (ifa = if_find_by_name(r->if_name))
465
        static_install(p, r, ifa);
466
    }
467
  WALK_LIST(r, n->other_routes)
468
    static_add(p, n, r);
469

    
470
  return 1;
471
}
472

    
473
struct protocol proto_static = {
474
  name:                "Static",
475
  template:        "static%d",
476
  init:                static_init,
477
  dump:                static_dump,
478
  start:        static_start,
479
  shutdown:        static_shutdown,
480
  cleanup:        static_cleanup,
481
  reconfigure:        static_reconfigure,
482
};
483

    
484
static void
485
static_show_rt(struct static_route *r)
486
{
487
  byte via[STD_ADDRESS_P_LENGTH + 16];
488

    
489
  switch (r->dest)
490
    {
491
    case RTD_ROUTER:        bsprintf(via, "via %I", r->via); break;
492
    case RTD_DEVICE:        bsprintf(via, "dev %s", r->if_name); break;
493
    case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); break;
494
    case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
495
    case RTD_PROHIBIT:        bsprintf(via, "prohibited"); break;
496
    case RTD_MULTIPATH:        bsprintf(via, "multipath"); break;
497
    case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
498
    default:                bsprintf(via, "???");
499
    }
500
  cli_msg(-1009, "%I/%d %s%s", r->net, r->masklen, via, r->installed ? "" : " (dormant)");
501

    
502
  struct static_route *r2;
503
  if (r->dest == RTD_MULTIPATH)
504
    for (r2 = r->mp_next; r2; r2 = r2->mp_next)
505
      cli_msg(-1009, "\tvia %I weight %d%s", r2->via, r2->masklen + 1, /* really */
506
              r2->installed ? "" : " (dormant)");
507
}
508

    
509
void
510
static_show(struct proto *P)
511
{
512
  struct static_config *c = (void *) P->cf;
513
  struct static_route *r;
514

    
515
  WALK_LIST(r, c->other_routes)
516
    static_show_rt(r);
517
  WALK_LIST(r, c->iface_routes)
518
    static_show_rt(r);
519
  cli_msg(0, "");
520
}