Statistics
| Branch: | Revision:

iof-bird-daemon / nest / proto.c @ 02c1fbdd

History | View | Annotate | Download (11.4 KB)

1
/*
2
 *        BIRD -- Protocols
3
 *
4
 *        (c) 1998--1999 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 "conf/conf.h"
19
#include "nest/route.h"
20
#include "nest/iface.h"
21
#include "nest/cli.h"
22
#include "filter/filter.h"
23

    
24
static pool *proto_pool;
25

    
26
list protocol_list;
27
list proto_list;
28

    
29
static list inactive_proto_list;
30
static list initial_proto_list;
31
static list flush_proto_list;
32

    
33
static int proto_shutdown_counter;
34

    
35
static event *proto_flush_event;
36

    
37
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
38
static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
39

    
40
static int proto_flush_all(void *);
41

    
42
static void
43
proto_enqueue(list *l, struct proto *p)
44
{
45
  int pri = p->proto->priority;
46

    
47
  if (!pri)
48
    add_tail(l, &p->n);
49
  else
50
    {
51
      struct proto *q = HEAD(*l);
52
      while (q->n.next && q->proto->priority >= pri)
53
        q = (struct proto *) q->n.next;
54
      insert_node(&p->n, q->n.prev);
55
    }
56
  p->last_state_change = now;
57
}
58

    
59
static void
60
proto_relink(struct proto *p)
61
{
62
  list *l;
63

    
64
  rem_node(&p->n);
65
  switch (p->core_state)
66
    {
67
    case FS_HAPPY:
68
      l = &proto_list;
69
      break;
70
    case FS_FLUSHING:
71
      l = &flush_proto_list;
72
      break;
73
    default:
74
      l = &inactive_proto_list;
75
    }
76
  proto_enqueue(l, p);
77
}
78

    
79
void *
80
proto_new(struct proto_config *c, unsigned size)
81
{
82
  struct protocol *pr = c->protocol;
83
  struct proto *p = mb_allocz(proto_pool, size);
84

    
85
  p->cf = c;
86
  p->debug = c->debug;
87
  p->name = c->name;
88
  p->preference = c->preference;
89
  p->disabled = c->disabled;
90
  p->proto = pr;
91
  p->table = c->table->table;
92
  p->in_filter = c->in_filter;
93
  p->out_filter = c->out_filter;
94
  c->proto = p;
95
  return p;
96
}
97

    
98
static void
99
proto_init_instance(struct proto *p)
100
{
101
  /* Here we cannot use p->cf->name since it won't survive reconfiguration */
102
  p->pool = rp_new(proto_pool, p->proto->name);
103
  p->attn = ev_new(p->pool);
104
  p->attn->data = p;
105
}
106

    
107
struct announce_hook *
108
proto_add_announce_hook(struct proto *p, struct rtable *t)
109
{
110
  struct announce_hook *h;
111

    
112
  if (!p->rt_notify)
113
    return NULL;
114
  DBG("Connecting protocol %s to table %s\n", p->name, t->name);
115
  h = mb_alloc(p->pool, sizeof(struct announce_hook));
116
  h->table = t;
117
  h->proto = p;
118
  h->next = p->ahooks;
119
  p->ahooks = h;
120
  add_tail(&t->hooks, &h->n);
121
  return h;
122
}
123

    
124
static void
125
proto_flush_hooks(struct proto *p)
126
{
127
  struct announce_hook *h;
128

    
129
  for(h=p->ahooks; h; h=h->next)
130
    rem_node(&h->n);
131
  p->ahooks = NULL;
132
}
133

    
134
void *
135
proto_config_new(struct protocol *pr, unsigned size)
136
{
137
  struct proto_config *c = cfg_allocz(size);
138

    
139
  add_tail(&new_config->protos, &c->n);
140
  c->global = new_config;
141
  c->protocol = pr;
142
  c->debug = pr->debug;
143
  c->name = pr->name;
144
  c->out_filter = FILTER_REJECT;
145
  c->table = c->global->master_rtc;
146
  return c;
147
}
148

    
149
void
150
protos_preconfig(struct config *c)
151
{
152
  struct protocol *p;
153

    
154
  init_list(&proto_list);
155
  init_list(&inactive_proto_list);
156
  init_list(&initial_proto_list);
157
  init_list(&flush_proto_list);
158
  debug("Protocol preconfig:");
159
  WALK_LIST(p, protocol_list)
160
    {
161
      debug(" %s", p->name);
162
      p->name_counter = 0;
163
      if (p->preconfig)
164
        p->preconfig(p, c);
165
    }
166
  debug("\n");
167
}
168

    
169
void
170
protos_postconfig(struct config *c)
171
{
172
  struct proto_config *x;
173
  struct protocol *p;
174

    
175
  debug("Protocol postconfig:");
176
  WALK_LIST(x, c->protos)
177
    {
178
      debug(" %s", x->name);
179
      p = x->protocol;
180
      if (p->postconfig)
181
        p->postconfig(x);
182
    }
183
  debug("\n");
184
}
185

    
186
void
187
protos_commit(struct config *c)
188
{
189
  struct proto_config *x;
190
  struct protocol *p;
191
  struct proto *q;
192

    
193
  debug("Protocol commit:");
194
  WALK_LIST(x, c->protos)
195
    {
196
      debug(" %s", x->name);
197
      p = x->protocol;
198
      q = p->init(x);
199
      q->proto_state = PS_DOWN;
200
      q->core_state = FS_HUNGRY;
201
      proto_enqueue(&initial_proto_list, q);
202
      /*
203
       *  HACK ALERT!  In case of multiple kernel routing tables,
204
       *  the kernel syncer acts as multiple protocols which cooperate
205
       *  with each other.  In order to speed up their initialization,
206
       *  we need to know when we're initializing the last one, hence
207
       *  the startup counter.
208
       */
209
      if (!q->disabled)
210
        p->startup_counter++;
211
    }
212
  debug("\n");
213
}
214

    
215
static void
216
proto_rethink_goal(struct proto *p)
217
{
218
  struct protocol *q = p->proto;
219

    
220
  if (p->core_state == p->core_goal)
221
    return;
222
  if (p->core_goal == FS_HAPPY)                /* Going up */
223
    {
224
      if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
225
        {
226
          DBG("Kicking %s up\n", p->name);
227
          ASSERT(q->startup_counter > 0);
228
          q->startup_counter--;
229
          proto_init_instance(p);
230
          proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
231
        }
232
    }
233
  else                                         /* Going down */
234
    {
235
      if (p->proto_state == PS_START || p->proto_state == PS_UP)
236
        {
237
          DBG("Kicking %s down\n", p->name);
238
          proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
239
        }
240
    }
241
}
242

    
243
static void
244
proto_set_goal(struct proto *p, unsigned goal)
245
{
246
  if (p->disabled || shutting_down)
247
    goal = FS_HUNGRY;
248
  p->core_goal = goal;
249
  proto_rethink_goal(p);
250
}
251

    
252
void
253
protos_start(void)
254
{
255
  struct proto *p, *n;
256

    
257
  debug("Protocol start\n");
258
  WALK_LIST_DELSAFE(p, n, initial_proto_list)
259
    proto_set_goal(p, FS_HAPPY);
260
}
261

    
262
void
263
protos_shutdown(void)
264
{
265
  struct proto *p, *n;
266

    
267
  debug("Protocol shutdown\n");
268
  WALK_LIST_BACKWARDS_DELSAFE(p, n, inactive_proto_list)
269
    if (p->core_state != FS_HUNGRY || p->proto_state != PS_DOWN)
270
    {
271
      proto_shutdown_counter++;
272
      proto_set_goal(p, FS_HUNGRY);
273
    }
274
  WALK_LIST_BACKWARDS_DELSAFE(p, n, proto_list)
275
    {
276
      proto_shutdown_counter++;
277
      proto_set_goal(p, FS_HUNGRY);
278
    }
279
}
280

    
281
void
282
protos_dump_all(void)
283
{
284
  struct proto *p;
285

    
286
  debug("Protocols:\n");
287

    
288
  WALK_LIST(p, proto_list)
289
    {
290
      debug("  protocol %s (pri=%d): state %s/%s\n", p->name, p->proto->priority,
291
            p_states[p->proto_state], c_states[p->core_state]);
292
      if (p->in_filter)
293
        debug("\tInput filter: %s\n", filter_name(p->in_filter));
294
      if (p->out_filter != FILTER_REJECT)
295
        debug("\tOutput filter: %s\n", filter_name(p->out_filter));
296
      if (p->disabled)
297
        debug("\tDISABLED\n");
298
      else if (p->proto->dump)
299
        p->proto->dump(p);
300
    }
301
  WALK_LIST(p, inactive_proto_list)
302
    debug("  inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]);
303
  WALK_LIST(p, initial_proto_list)
304
    debug("  initial %s\n", p->name);
305
}
306

    
307
void
308
protos_build(void)
309
{
310
  init_list(&protocol_list);
311
  add_tail(&protocol_list, &proto_device.n);
312
#ifdef CONFIG_RIP
313
  add_tail(&protocol_list, &proto_rip.n);
314
#endif
315
#ifdef CONFIG_STATIC
316
  add_tail(&protocol_list, &proto_static.n);
317
#endif
318
#ifdef CONFIG_OSPF
319
  add_tail(&protocol_list, &proto_ospf.n);
320
#endif
321
  proto_pool = rp_new(&root_pool, "Protocols");
322
  proto_flush_event = ev_new(proto_pool);
323
  proto_flush_event->hook = proto_flush_all;
324
}
325

    
326
static void
327
proto_fell_down(struct proto *p)
328
{
329
  DBG("Protocol %s down\n", p->name);
330
  if (!--proto_shutdown_counter)
331
    protos_shutdown_notify();
332
  proto_rethink_goal(p);
333
}
334

    
335
static int
336
proto_feed(void *P)
337
{
338
  struct proto *p = P;
339

    
340
  DBG("Feeding protocol %s\n", p->name);
341
  proto_add_announce_hook(p, p->table);
342
  if_feed_baby(p);
343
  rt_feed_baby(p);
344
  p->core_state = FS_HAPPY;
345
  proto_relink(p);
346
  DBG("Protocol %s up and running\n", p->name);
347
  return 0;
348
}
349

    
350
void
351
proto_notify_state(struct proto *p, unsigned ps)
352
{
353
  unsigned ops = p->proto_state;
354
  unsigned cs = p->core_state;
355

    
356
  DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]);
357
  if (ops == ps)
358
    return;
359

    
360
  switch (ps)
361
    {
362
    case PS_DOWN:
363
      if (cs == FS_HUNGRY)                /* Shutdown finished */
364
        proto_fell_down(p);
365
      else if (cs == FS_FLUSHING)        /* Still flushing... */
366
        ;
367
      else                                /* Need to start flushing */
368
        goto schedule_flush;
369
      break;
370
    case PS_START:
371
      ASSERT(ops == PS_DOWN);
372
      ASSERT(cs == FS_HUNGRY);
373
      break;
374
    case PS_UP:
375
      ASSERT(ops == PS_DOWN || ops == PS_START);
376
      ASSERT(cs == FS_HUNGRY);
377
      DBG("%s: Scheduling meal\n", p->name);
378
      if (p->proto->priority)                /* FIXME: Terrible hack to get synchronous device/kernel startup! */
379
        {
380
          p->proto_state = ps;
381
          p->core_state = FS_FEEDING;
382
          proto_feed(p);
383
          return;
384
        }
385
      cs = FS_FEEDING;
386
      p->attn->hook = proto_feed;
387
      ev_schedule(p->attn);
388
      break;
389
    case PS_STOP:
390
      if (cs == FS_FEEDING || cs == FS_HAPPY)
391
        {
392
        schedule_flush:
393
          DBG("%s: Scheduling flush\n", p->name);
394
          proto_flush_hooks(p);
395
          cs = FS_FLUSHING;
396
          ev_schedule(proto_flush_event);
397
        }
398
      break;
399
    default:
400
    error:
401
      bug("Invalid state transition for %s from %s/%s to */%s", p->name, c_states[cs], p_states[ops], p_states[ps]);
402
    }
403
  p->proto_state = ps;
404
  p->core_state = cs;
405
  proto_relink(p);
406
}
407

    
408
static int
409
proto_flush_all(void *unused)
410
{
411
  struct proto *p;
412

    
413
  rt_prune_all();
414
  neigh_prune();
415
  while ((p = HEAD(flush_proto_list))->n.next)
416
    {
417
      DBG("Flushing protocol %s\n", p->name);
418
      rfree(p->pool);
419
      p->pool = NULL;
420
      p->core_state = FS_HUNGRY;
421
      proto_relink(p);
422
      proto_fell_down(p);
423
    }
424
  return 0;
425
}
426

    
427
/*
428
 *  CLI Commands
429
 */
430

    
431
static char *
432
proto_state_name(struct proto *p)
433
{
434
#define P(x,y) ((x << 4) | y)
435
  switch (P(p->proto_state, p->core_state))
436
    {
437
    case P(PS_DOWN, FS_HUNGRY):                return "down";
438
    case P(PS_START, FS_HUNGRY):        return "start";
439
    case P(PS_UP, FS_HUNGRY):
440
    case P(PS_UP, FS_FEEDING):                return "feed";
441
    case P(PS_STOP, FS_HUNGRY):                return "stop";
442
    case P(PS_UP, FS_HAPPY):                return "up";
443
    case P(PS_STOP, FS_FLUSHING):
444
    case P(PS_DOWN, FS_FLUSHING):        return "flush";
445
    default:                              return "???";
446
    }
447
#undef P
448
}
449

    
450
static void
451
proto_do_show(struct proto *p, int verbose)
452
{
453
  byte buf[256], reltime[TM_RELTIME_BUFFER_SIZE];
454

    
455
  buf[0] = 0;
456
  if (p->proto->get_status)
457
    p->proto->get_status(p, buf);
458
  tm_format_reltime(reltime, p->last_state_change);
459
  cli_msg(-1002, "%-8s %-8s %-8s %-5s %-5s %s",
460
          p->name,
461
          p->proto->name,
462
          p->table->name,
463
          proto_state_name(p),
464
          reltime,
465
          buf);
466
  if (verbose)
467
    {
468
      cli_msg(-1006, "\tPreference: %d", p->preference);
469
      cli_msg(-1006, "\tInput filter: %s", filter_name(p->in_filter));
470
      cli_msg(-1006, "\tOutput filter: %s", filter_name(p->out_filter));
471
    }
472
}
473

    
474
static void
475
proto_do_show_list(list *l, int verbose)
476
{
477
  struct proto *p;
478

    
479
  WALK_LIST(p, *l)
480
    proto_do_show(p, verbose);
481
}
482

    
483
void
484
proto_show(struct symbol *s, int verbose)
485
{
486
  if (s && s->class != SYM_PROTO)
487
    {
488
      cli_msg(9002, "%s is not a protocol", s->name);
489
      return;
490
    }
491
  cli_msg(-2002, "name     proto    table    state since info");
492
  if (s)
493
    proto_do_show(((struct proto_config *)s->def)->proto, verbose);
494
  else
495
    {
496
      proto_do_show_list(&proto_list, verbose);
497
      proto_do_show_list(&flush_proto_list, verbose);
498
      proto_do_show_list(&inactive_proto_list, verbose);
499
    }
500
  cli_msg(0, "");
501
}
502

    
503
struct proto *
504
proto_get_named(struct symbol *sym, struct protocol *pr)
505
{
506
  struct proto *p, *q;
507

    
508
  if (sym)
509
    {
510
      if (sym->class != SYM_PROTO)
511
        cf_error("%s: Not a protocol", sym->name);
512
      p = ((struct proto_config *)sym->def)->proto;
513
      if (!p || p->proto != pr)
514
        cf_error("%s: Not a %s protocol", sym->name, pr->name);
515
    }
516
  else
517
    {
518
      p = NULL;
519
      WALK_LIST(q, proto_list)
520
        if (q->proto == pr)
521
          {
522
            if (p)
523
              cf_error("There are multiple %s protocols running", pr->name);
524
            p = q;
525
          }
526
      if (!p)
527
        cf_error("There is no %s protocol running", pr->name);
528
    }
529
  return p;
530
}