Statistics
| Branch: | Revision:

iof-bird-daemon / nest / proto.c @ 54aaa89a

History | View | Annotate | Download (13.6 KB)

1
/*
2
 *        BIRD -- Protocols
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
#define LOCAL_DEBUG
10

    
11
#include <string.h>
12

    
13
#include "nest/bird.h"
14
#include "nest/protocol.h"
15
#include "lib/resource.h"
16
#include "lib/lists.h"
17
#include "lib/event.h"
18
#include "lib/string.h"
19
#include "conf/conf.h"
20
#include "nest/route.h"
21
#include "nest/iface.h"
22
#include "nest/cli.h"
23
#include "filter/filter.h"
24

    
25
static pool *proto_pool;
26

    
27
list protocol_list;
28
static list proto_list;
29

    
30
#define WALK_PROTO_LIST(p) do {                                                        \
31
        node *nn;                                                                \
32
        WALK_LIST(nn, proto_list) {                                                \
33
                struct proto *p = SKIP_BACK(struct proto, glob_node, nn);
34
#define WALK_PROTO_LIST_END } } while(0)
35

    
36
list active_proto_list;
37
static list inactive_proto_list;
38
static list initial_proto_list;
39
static list flush_proto_list;
40

    
41
static event *proto_flush_event;
42

    
43
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
44
static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
45

    
46
static int proto_flush_all(void *);
47
static void proto_rethink_goal(struct proto *p);
48

    
49
static void
50
proto_enqueue(list *l, struct proto *p)
51
{
52
  int pri = p->proto->priority;
53

    
54
  if (!pri)
55
    add_tail(l, &p->n);
56
  else
57
    {
58
      struct proto *q = HEAD(*l);
59
      while (q->n.next && q->proto->priority >= pri)
60
        q = (struct proto *) q->n.next;
61
      insert_node(&p->n, q->n.prev);
62
    }
63
  p->last_state_change = now;
64
}
65

    
66
static void
67
proto_relink(struct proto *p)
68
{
69
  list *l;
70

    
71
  rem_node(&p->n);
72
  switch (p->core_state)
73
    {
74
    case FS_HAPPY:
75
      l = &active_proto_list;
76
      break;
77
    case FS_FLUSHING:
78
      l = &flush_proto_list;
79
      break;
80
    default:
81
      l = &inactive_proto_list;
82
    }
83
  proto_enqueue(l, p);
84
}
85

    
86
void *
87
proto_new(struct proto_config *c, unsigned size)
88
{
89
  struct protocol *pr = c->protocol;
90
  struct proto *p = mb_allocz(proto_pool, size);
91

    
92
  p->cf = c;
93
  p->debug = c->debug;
94
  p->name = c->name;
95
  p->preference = c->preference;
96
  p->disabled = c->disabled;
97
  p->proto = pr;
98
  p->table = c->table->table;
99
  p->in_filter = c->in_filter;
100
  p->out_filter = c->out_filter;
101
  p->min_scope = SCOPE_SITE;
102
  c->proto = p;
103
  return p;
104
}
105

    
106
static void
107
proto_init_instance(struct proto *p)
108
{
109
  /* Here we cannot use p->cf->name since it won't survive reconfiguration */
110
  p->pool = rp_new(proto_pool, p->proto->name);
111
  p->attn = ev_new(p->pool);
112
  p->attn->data = p;
113
  rt_lock_table(p->table);
114
}
115

    
116
struct announce_hook *
117
proto_add_announce_hook(struct proto *p, struct rtable *t)
118
{
119
  struct announce_hook *h;
120

    
121
  if (!p->rt_notify)
122
    return NULL;
123
  DBG("Connecting protocol %s to table %s\n", p->name, t->name);
124
  h = mb_alloc(p->pool, sizeof(struct announce_hook));
125
  h->table = t;
126
  h->proto = p;
127
  h->next = p->ahooks;
128
  p->ahooks = h;
129
  add_tail(&t->hooks, &h->n);
130
  return h;
131
}
132

    
133
static void
134
proto_flush_hooks(struct proto *p)
135
{
136
  struct announce_hook *h;
137

    
138
  for(h=p->ahooks; h; h=h->next)
139
    rem_node(&h->n);
140
  p->ahooks = NULL;
141
}
142

    
143
void *
144
proto_config_new(struct protocol *pr, unsigned size)
145
{
146
  struct proto_config *c = cfg_allocz(size);
147

    
148
  add_tail(&new_config->protos, &c->n);
149
  c->global = new_config;
150
  c->protocol = pr;
151
  c->debug = pr->debug;
152
  c->name = pr->name;
153
  c->out_filter = FILTER_REJECT;
154
  c->table = c->global->master_rtc;
155
  return c;
156
}
157

    
158
void
159
protos_preconfig(struct config *c)
160
{
161
  struct protocol *p;
162

    
163
  init_list(&c->protos);
164
  debug("Protocol preconfig:");
165
  WALK_LIST(p, protocol_list)
166
    {
167
      debug(" %s", p->name);
168
      p->name_counter = 0;
169
      if (p->preconfig)
170
        p->preconfig(p, c);
171
    }
172
  debug("\n");
173
}
174

    
175
void
176
protos_postconfig(struct config *c)
177
{
178
  struct proto_config *x;
179
  struct protocol *p;
180

    
181
  debug("Protocol postconfig:");
182
  WALK_LIST(x, c->protos)
183
    {
184
      debug(" %s", x->name);
185
      p = x->protocol;
186
      if (p->postconfig)
187
        p->postconfig(x);
188
    }
189
  debug("\n");
190
}
191

    
192
static struct proto *
193
proto_init(struct proto_config *c)
194
{
195
  struct protocol *p = c->protocol;
196
  struct proto *q = p->init(c);
197

    
198
  q->proto_state = PS_DOWN;
199
  q->core_state = FS_HUNGRY;
200
  proto_enqueue(&initial_proto_list, q);
201
  add_tail(&proto_list, &q->glob_node);
202
  return q;
203
}
204

    
205
void
206
protos_commit(struct config *new, struct config *old, int force_reconfig)
207
{
208
  struct proto_config *oc, *nc;
209
  struct proto *p, *n;
210

    
211
  DBG("protos_commit:\n");
212
  if (old)
213
    {
214
      WALK_LIST(oc, old->protos)
215
        {
216
          struct proto *p = oc->proto;
217
          struct symbol *sym = cf_find_symbol(oc->name);
218
          if (sym && sym->class == SYM_PROTO && !new->shutdown)
219
            {
220
              /* Found match, let's check if we can smoothly switch to new configuration */
221
              nc = sym->def;
222
              if (!force_reconfig
223
                  && nc->protocol == oc->protocol
224
                  && nc->preference == oc->preference
225
                  && nc->disabled == oc->disabled
226
                  && nc->table->table == oc->table->table
227
                  && filter_same(nc->in_filter, oc->in_filter)
228
                  && filter_same(nc->out_filter, oc->out_filter)
229
                  && p->proto_state != PS_DOWN)
230
                {
231
                  /* Generic attributes match, try converting them and then ask the protocol */
232
                  p->debug = nc->debug;
233
                  if (p->proto->reconfigure && p->proto->reconfigure(p, nc))
234
                    {
235
                      DBG("\t%s: same\n", oc->name);
236
                      p->cf = nc;
237
                      p->name = nc->name;
238
                      nc->proto = p;
239
                      continue;
240
                    }
241
                }
242
              /* Unsuccessful, force reconfig */
243
              DBG("\t%s: power cycling\n", oc->name);
244
              p->cf_new = nc;
245
              nc->proto = p;
246
            }
247
          else
248
            {
249
              DBG("\t%s: deleting\n", oc->name);
250
              p->cf_new = NULL;
251
            }
252
          p->reconfiguring = 1;
253
          config_add_obstacle(old);
254
          proto_rethink_goal(p);
255
        }
256
    }
257

    
258
  WALK_LIST(nc, new->protos)
259
    if (!nc->proto)
260
      {
261
        DBG("\t%s: adding\n", nc->name);
262
        proto_init(nc);
263
      }
264
  DBG("\tdone\n");
265

    
266
  DBG("Protocol start\n");
267
  WALK_LIST_DELSAFE(p, n, initial_proto_list)
268
    proto_rethink_goal(p);
269
}
270

    
271
static void
272
proto_rethink_goal(struct proto *p)
273
{
274
  struct protocol *q;
275

    
276
  if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
277
    {
278
      struct proto_config *nc = p->cf_new;
279
      DBG("%s has shut down for reconfiguration\n", p->name);
280
      config_del_obstacle(p->cf->global);
281
      rem_node(&p->n);
282
      rem_node(&p->glob_node);
283
      mb_free(p);
284
      if (!nc)
285
        return;
286
      p = proto_init(nc);                /* FIXME: What about protocol priorities??? */
287
    }
288

    
289
  /* Determine what state we want to reach */
290
  if (p->disabled || p->reconfiguring)
291
    p->core_goal = FS_HUNGRY;
292
  else
293
    p->core_goal = FS_HAPPY;
294

    
295
  if (p->core_state == p->core_goal)
296
    return;
297

    
298
  q = p->proto;
299
  if (p->core_goal == FS_HAPPY)                /* Going up */
300
    {
301
      if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
302
        {
303
          DBG("Kicking %s up\n", p->name);
304
          proto_init_instance(p);
305
          proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
306
        }
307
    }
308
  else                                         /* Going down */
309
    {
310
      if (p->proto_state == PS_START || p->proto_state == PS_UP)
311
        {
312
          DBG("Kicking %s down\n", p->name);
313
          proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
314
        }
315
    }
316
}
317

    
318
void
319
protos_dump_all(void)
320
{
321
  struct proto *p;
322

    
323
  debug("Protocols:\n");
324

    
325
  WALK_LIST(p, active_proto_list)
326
    {
327
      debug("  protocol %s (pri=%d): state %s/%s\n", p->name, p->proto->priority,
328
            p_states[p->proto_state], c_states[p->core_state]);
329
      if (p->in_filter)
330
        debug("\tInput filter: %s\n", filter_name(p->in_filter));
331
      if (p->out_filter != FILTER_REJECT)
332
        debug("\tOutput filter: %s\n", filter_name(p->out_filter));
333
      if (p->disabled)
334
        debug("\tDISABLED\n");
335
      else if (p->proto->dump)
336
        p->proto->dump(p);
337
    }
338
  WALK_LIST(p, inactive_proto_list)
339
    debug("  inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]);
340
  WALK_LIST(p, initial_proto_list)
341
    debug("  initial %s\n", p->name);
342
  WALK_LIST(p, flush_proto_list)
343
    debug("  flushing %s\n", p->name);
344
}
345

    
346
void
347
protos_build(void)
348
{
349
  init_list(&protocol_list);
350
  init_list(&proto_list);
351
  init_list(&active_proto_list);
352
  init_list(&inactive_proto_list);
353
  init_list(&initial_proto_list);
354
  init_list(&flush_proto_list);
355
  add_tail(&protocol_list, &proto_device.n);
356
#ifdef CONFIG_RIP
357
  add_tail(&protocol_list, &proto_rip.n);
358
#endif
359
#ifdef CONFIG_STATIC
360
  add_tail(&protocol_list, &proto_static.n);
361
#endif
362
#ifdef CONFIG_OSPF
363
  add_tail(&protocol_list, &proto_ospf.n);
364
#endif
365
#ifdef CONFIG_PIPE
366
  add_tail(&protocol_list, &proto_pipe.n);
367
#endif
368
  proto_pool = rp_new(&root_pool, "Protocols");
369
  proto_flush_event = ev_new(proto_pool);
370
  proto_flush_event->hook = proto_flush_all;
371
}
372

    
373
static void
374
proto_fell_down(struct proto *p)
375
{
376
  DBG("Protocol %s down\n", p->name);
377
  rt_unlock_table(p->table);
378
  proto_rethink_goal(p);
379
}
380

    
381
static int
382
proto_feed(void *P)
383
{
384
  struct proto *p = P;
385

    
386
  DBG("Feeding protocol %s\n", p->name);
387
  proto_add_announce_hook(p, p->table);
388
  if_feed_baby(p);
389
  rt_feed_baby(p);
390
  p->core_state = FS_HAPPY;
391
  proto_relink(p);
392
  DBG("Protocol %s up and running\n", p->name);
393
  return 0;
394
}
395

    
396
void
397
proto_notify_state(struct proto *p, unsigned ps)
398
{
399
  unsigned ops = p->proto_state;
400
  unsigned cs = p->core_state;
401

    
402
  DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]);
403
  if (ops == ps)
404
    return;
405

    
406
  switch (ps)
407
    {
408
    case PS_DOWN:
409
      if (cs == FS_HUNGRY)                /* Shutdown finished */
410
        {
411
          proto_fell_down(p);
412
          return;                        /* The protocol might have ceased to exist */
413
        }
414
      else if (cs == FS_FLUSHING)        /* Still flushing... */
415
        ;
416
      else                                /* Need to start flushing */
417
        goto schedule_flush;
418
      break;
419
    case PS_START:
420
      ASSERT(ops == PS_DOWN);
421
      ASSERT(cs == FS_HUNGRY);
422
      break;
423
    case PS_UP:
424
      ASSERT(ops == PS_DOWN || ops == PS_START);
425
      ASSERT(cs == FS_HUNGRY);
426
      DBG("%s: Scheduling meal\n", p->name);
427
      if (p->proto->priority)                /* FIXME: Terrible hack to get synchronous device/kernel startup! */
428
        {
429
          p->proto_state = ps;
430
          p->core_state = FS_FEEDING;
431
          proto_feed(p);
432
          return;
433
        }
434
      cs = FS_FEEDING;
435
      p->attn->hook = proto_feed;
436
      ev_schedule(p->attn);
437
      break;
438
    case PS_STOP:
439
      if (cs == FS_FEEDING || cs == FS_HAPPY)
440
        {
441
        schedule_flush:
442
          DBG("%s: Scheduling flush\n", p->name);
443
          proto_flush_hooks(p);
444
          cs = FS_FLUSHING;
445
          ev_schedule(proto_flush_event);
446
        }
447
      break;
448
    default:
449
    error:
450
      bug("Invalid state transition for %s from %s/%s to */%s", p->name, c_states[cs], p_states[ops], p_states[ps]);
451
    }
452
  p->proto_state = ps;
453
  p->core_state = cs;
454
  proto_relink(p);
455
}
456

    
457
static int
458
proto_flush_all(void *unused)
459
{
460
  struct proto *p;
461

    
462
  rt_prune_all();
463
  neigh_prune();
464
  while ((p = HEAD(flush_proto_list))->n.next)
465
    {
466
      DBG("Flushing protocol %s\n", p->name);
467
      rfree(p->pool);
468
      p->pool = NULL;
469
      p->core_state = FS_HUNGRY;
470
      proto_relink(p);
471
      proto_fell_down(p);
472
    }
473
  return 0;
474
}
475

    
476
/*
477
 *  CLI Commands
478
 */
479

    
480
static char *
481
proto_state_name(struct proto *p)
482
{
483
#define P(x,y) ((x << 4) | y)
484
  switch (P(p->proto_state, p->core_state))
485
    {
486
    case P(PS_DOWN, FS_HUNGRY):                return "down";
487
    case P(PS_START, FS_HUNGRY):        return "start";
488
    case P(PS_UP, FS_HUNGRY):
489
    case P(PS_UP, FS_FEEDING):                return "feed";
490
    case P(PS_STOP, FS_HUNGRY):                return "stop";
491
    case P(PS_UP, FS_HAPPY):                return "up";
492
    case P(PS_STOP, FS_FLUSHING):
493
    case P(PS_DOWN, FS_FLUSHING):        return "flush";
494
    default:                              return "???";
495
    }
496
#undef P
497
}
498

    
499
static void
500
proto_do_show(struct proto *p, int verbose)
501
{
502
  byte buf[256], reltime[TM_RELTIME_BUFFER_SIZE];
503

    
504
  buf[0] = 0;
505
  if (p->proto->get_status)
506
    p->proto->get_status(p, buf);
507
  tm_format_reltime(reltime, p->last_state_change);
508
  cli_msg(-1002, "%-8s %-8s %-8s %-5s %-5s %s",
509
          p->name,
510
          p->proto->name,
511
          p->table->name,
512
          proto_state_name(p),
513
          reltime,
514
          buf);
515
  if (verbose)
516
    {
517
      cli_msg(-1006, "\tPreference: %d", p->preference);
518
      cli_msg(-1006, "\tInput filter: %s", filter_name(p->in_filter));
519
      cli_msg(-1006, "\tOutput filter: %s", filter_name(p->out_filter));
520
    }
521
}
522

    
523
void
524
proto_show(struct symbol *s, int verbose)
525
{
526
  if (s && s->class != SYM_PROTO)
527
    {
528
      cli_msg(9002, "%s is not a protocol", s->name);
529
      return;
530
    }
531
  cli_msg(-2002, "name     proto    table    state since info");
532
  if (s)
533
    proto_do_show(((struct proto_config *)s->def)->proto, verbose);
534
  else
535
    {
536
      WALK_PROTO_LIST(p)
537
        proto_do_show(p, verbose);
538
      WALK_PROTO_LIST_END;
539
    }
540
  cli_msg(0, "");
541
}
542

    
543
struct proto *
544
proto_get_named(struct symbol *sym, struct protocol *pr)
545
{
546
  struct proto *p, *q;
547

    
548
  if (sym)
549
    {
550
      if (sym->class != SYM_PROTO)
551
        cf_error("%s: Not a protocol", sym->name);
552
      p = ((struct proto_config *)sym->def)->proto;
553
      if (!p || p->proto != pr)
554
        cf_error("%s: Not a %s protocol", sym->name, pr->name);
555
    }
556
  else
557
    {
558
      p = NULL;
559
      WALK_LIST(q, active_proto_list)
560
        if (q->proto == pr)
561
          {
562
            if (p)
563
              cf_error("There are multiple %s protocols running", pr->name);
564
            p = q;
565
          }
566
      if (!p)
567
        cf_error("There is no %s protocol running", pr->name);
568
    }
569
  return p;
570
}
571

    
572
void
573
proto_xxable(char *pattern, int xx)
574
{
575
  int cnt = 0;
576
  WALK_PROTO_LIST(p)
577
    if (patmatch(pattern, p->name))
578
      {
579
        cnt++;
580
        switch (xx)
581
          {
582
          case 0:
583
            if (p->disabled)
584
              cli_msg(-8, "%s: already disabled", p->name);
585
            else
586
              {
587
                cli_msg(-9, "%s: disabled", p->name);
588
                p->disabled = 1;
589
              }
590
            break;
591
          case 1:
592
            if (!p->disabled)
593
              cli_msg(-10, "%s: already enabled", p->name);
594
            else
595
              {
596
                cli_msg(-11, "%s: enabled", p->name);
597
                p->disabled = 0;
598
              }
599
            break;
600
          case 2:
601
            if (p->disabled)
602
              cli_msg(-8, "%s: already disabled", p->name);
603
            else
604
              {
605
                p->disabled = 1;
606
                proto_rethink_goal(p);
607
                p->disabled = 0;
608
                cli_msg(-12, "%s: restarted", p->name);
609
              }
610
            break;
611
          default:
612
            ASSERT(0);
613
          }
614
        proto_rethink_goal(p);
615
      }
616
  WALK_PROTO_LIST_END;
617
  if (!cnt)
618
    cli_msg(8003, "No protocols match");
619
  else
620
    cli_msg(0, "");
621
}