Annotation of embedaddon/bird/nest/proto.c, revision 1.1
1.1 ! misho 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: #undef LOCAL_DEBUG
! 10:
! 11: #include "nest/bird.h"
! 12: #include "nest/protocol.h"
! 13: #include "lib/resource.h"
! 14: #include "lib/lists.h"
! 15: #include "lib/event.h"
! 16: #include "lib/string.h"
! 17: #include "conf/conf.h"
! 18: #include "nest/route.h"
! 19: #include "nest/iface.h"
! 20: #include "nest/cli.h"
! 21: #include "filter/filter.h"
! 22:
! 23: pool *proto_pool;
! 24:
! 25: static list protocol_list;
! 26: static list proto_list;
! 27:
! 28: #define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0)
! 29:
! 30: list active_proto_list;
! 31: static list inactive_proto_list;
! 32: static list initial_proto_list;
! 33: static list flush_proto_list;
! 34: static struct proto *initial_device_proto;
! 35:
! 36: static event *proto_flush_event;
! 37: static timer *proto_shutdown_timer;
! 38: static timer *gr_wait_timer;
! 39:
! 40: #define GRS_NONE 0
! 41: #define GRS_INIT 1
! 42: #define GRS_ACTIVE 2
! 43: #define GRS_DONE 3
! 44:
! 45: static int graceful_restart_state;
! 46: static u32 graceful_restart_locks;
! 47:
! 48: static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
! 49: static char *c_states[] = { "HUNGRY", "???", "HAPPY", "FLUSHING" };
! 50:
! 51: static void proto_flush_loop(void *);
! 52: static void proto_shutdown_loop(struct timer *);
! 53: static void proto_rethink_goal(struct proto *p);
! 54: static void proto_want_export_up(struct proto *p);
! 55: static void proto_fell_down(struct proto *p);
! 56: static char *proto_state_name(struct proto *p);
! 57:
! 58: static void
! 59: proto_relink(struct proto *p)
! 60: {
! 61: list *l = NULL;
! 62:
! 63: switch (p->core_state)
! 64: {
! 65: case FS_HUNGRY:
! 66: l = &inactive_proto_list;
! 67: break;
! 68: case FS_HAPPY:
! 69: l = &active_proto_list;
! 70: break;
! 71: case FS_FLUSHING:
! 72: l = &flush_proto_list;
! 73: break;
! 74: default:
! 75: ASSERT(0);
! 76: }
! 77:
! 78: rem_node(&p->n);
! 79: add_tail(l, &p->n);
! 80: }
! 81:
! 82: static void
! 83: proto_log_state_change(struct proto *p)
! 84: {
! 85: if (p->debug & D_STATES)
! 86: {
! 87: char *name = proto_state_name(p);
! 88: if (name != p->last_state_name_announced)
! 89: {
! 90: p->last_state_name_announced = name;
! 91: PD(p, "State changed to %s", proto_state_name(p));
! 92: }
! 93: }
! 94: else
! 95: p->last_state_name_announced = NULL;
! 96: }
! 97:
! 98:
! 99: /**
! 100: * proto_new - create a new protocol instance
! 101: * @c: protocol configuration
! 102: * @size: size of protocol data structure (each protocol instance is represented by
! 103: * a structure starting with generic part [struct &proto] and continued
! 104: * with data specific to the protocol)
! 105: *
! 106: * When a new configuration has been read in, the core code starts
! 107: * initializing all the protocol instances configured by calling their
! 108: * init() hooks with the corresponding instance configuration. The initialization
! 109: * code of the protocol is expected to create a new instance according to the
! 110: * configuration by calling this function and then modifying the default settings
! 111: * to values wanted by the protocol.
! 112: */
! 113: void *
! 114: proto_new(struct proto_config *c, unsigned size)
! 115: {
! 116: struct protocol *pr = c->protocol;
! 117: struct proto *p = mb_allocz(proto_pool, size);
! 118:
! 119: p->cf = c;
! 120: p->debug = c->debug;
! 121: p->mrtdump = c->mrtdump;
! 122: p->name = c->name;
! 123: p->preference = c->preference;
! 124: p->disabled = c->disabled;
! 125: p->proto = pr;
! 126: p->table = c->table->table;
! 127: p->hash_key = random_u32();
! 128: c->proto = p;
! 129: return p;
! 130: }
! 131:
! 132: static void
! 133: proto_init_instance(struct proto *p)
! 134: {
! 135: /* Here we cannot use p->cf->name since it won't survive reconfiguration */
! 136: p->pool = rp_new(proto_pool, p->proto->name);
! 137: p->attn = ev_new(p->pool);
! 138: p->attn->data = p;
! 139:
! 140: if (graceful_restart_state == GRS_INIT)
! 141: p->gr_recovery = 1;
! 142:
! 143: if (! p->proto->multitable)
! 144: rt_lock_table(p->table);
! 145: }
! 146:
! 147: extern pool *rt_table_pool;
! 148: /**
! 149: * proto_add_announce_hook - connect protocol to a routing table
! 150: * @p: protocol instance
! 151: * @t: routing table to connect to
! 152: * @stats: per-table protocol statistics
! 153: *
! 154: * This function creates a connection between the protocol instance @p and the
! 155: * routing table @t, making the protocol hear all changes in the table.
! 156: *
! 157: * The announce hook is linked in the protocol ahook list. Announce hooks are
! 158: * allocated from the routing table resource pool and when protocol accepts
! 159: * routes also in the table ahook list. The are linked to the table ahook list
! 160: * and unlinked from it depending on export_state (in proto_want_export_up() and
! 161: * proto_want_export_down()) and they are automatically freed after the protocol
! 162: * is flushed (in proto_fell_down()).
! 163: *
! 164: * Unless you want to listen to multiple routing tables (as the Pipe protocol
! 165: * does), you needn't to worry about this function since the connection to the
! 166: * protocol's primary routing table is initialized automatically by the core
! 167: * code.
! 168: */
! 169: struct announce_hook *
! 170: proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats)
! 171: {
! 172: struct announce_hook *h;
! 173:
! 174: DBG("Connecting protocol %s to table %s\n", p->name, t->name);
! 175: PD(p, "Connected to table %s", t->name);
! 176:
! 177: h = mb_allocz(rt_table_pool, sizeof(struct announce_hook));
! 178: h->table = t;
! 179: h->proto = p;
! 180: h->stats = stats;
! 181:
! 182: h->next = p->ahooks;
! 183: p->ahooks = h;
! 184:
! 185: if (p->rt_notify && (p->export_state != ES_DOWN))
! 186: add_tail(&t->hooks, &h->n);
! 187: return h;
! 188: }
! 189:
! 190: /**
! 191: * proto_find_announce_hook - find announce hooks
! 192: * @p: protocol instance
! 193: * @t: routing table
! 194: *
! 195: * Returns pointer to announce hook or NULL
! 196: */
! 197: struct announce_hook *
! 198: proto_find_announce_hook(struct proto *p, struct rtable *t)
! 199: {
! 200: struct announce_hook *a;
! 201:
! 202: for (a = p->ahooks; a; a = a->next)
! 203: if (a->table == t)
! 204: return a;
! 205:
! 206: return NULL;
! 207: }
! 208:
! 209: static void
! 210: proto_link_ahooks(struct proto *p)
! 211: {
! 212: struct announce_hook *h;
! 213:
! 214: if (p->rt_notify)
! 215: for(h=p->ahooks; h; h=h->next)
! 216: add_tail(&h->table->hooks, &h->n);
! 217: }
! 218:
! 219: static void
! 220: proto_unlink_ahooks(struct proto *p)
! 221: {
! 222: struct announce_hook *h;
! 223:
! 224: if (p->rt_notify)
! 225: for(h=p->ahooks; h; h=h->next)
! 226: rem_node(&h->n);
! 227: }
! 228:
! 229: static void
! 230: proto_free_ahooks(struct proto *p)
! 231: {
! 232: struct announce_hook *h, *hn;
! 233:
! 234: for(h = p->ahooks; h; h = hn)
! 235: {
! 236: hn = h->next;
! 237: mb_free(h);
! 238: }
! 239:
! 240: p->ahooks = NULL;
! 241: p->main_ahook = NULL;
! 242: }
! 243:
! 244:
! 245: /**
! 246: * proto_config_new - create a new protocol configuration
! 247: * @pr: protocol the configuration will belong to
! 248: * @class: SYM_PROTO or SYM_TEMPLATE
! 249: *
! 250: * Whenever the configuration file says that a new instance
! 251: * of a routing protocol should be created, the parser calls
! 252: * proto_config_new() to create a configuration entry for this
! 253: * instance (a structure staring with the &proto_config header
! 254: * containing all the generic items followed by protocol-specific
! 255: * ones). Also, the configuration entry gets added to the list
! 256: * of protocol instances kept in the configuration.
! 257: *
! 258: * The function is also used to create protocol templates (when class
! 259: * SYM_TEMPLATE is specified), the only difference is that templates
! 260: * are not added to the list of protocol instances and therefore not
! 261: * initialized during protos_commit()).
! 262: */
! 263: void *
! 264: proto_config_new(struct protocol *pr, int class)
! 265: {
! 266: struct proto_config *c = cfg_allocz(pr->config_size);
! 267:
! 268: if (class == SYM_PROTO)
! 269: add_tail(&new_config->protos, &c->n);
! 270: c->global = new_config;
! 271: c->protocol = pr;
! 272: c->name = pr->name;
! 273: c->preference = pr->preference;
! 274: c->class = class;
! 275: c->out_filter = FILTER_REJECT;
! 276: c->table = c->global->master_rtc;
! 277: c->debug = new_config->proto_default_debug;
! 278: c->mrtdump = new_config->proto_default_mrtdump;
! 279: return c;
! 280: }
! 281:
! 282: /**
! 283: * proto_copy_config - copy a protocol configuration
! 284: * @dest: destination protocol configuration
! 285: * @src: source protocol configuration
! 286: *
! 287: * Whenever a new instance of a routing protocol is created from the
! 288: * template, proto_copy_config() is called to copy a content of
! 289: * the source protocol configuration to the new protocol configuration.
! 290: * Name, class and a node in protos list of @dest are kept intact.
! 291: * copy_config() protocol hook is used to copy protocol-specific data.
! 292: */
! 293: void
! 294: proto_copy_config(struct proto_config *dest, struct proto_config *src)
! 295: {
! 296: node old_node;
! 297: int old_class;
! 298: char *old_name;
! 299:
! 300: if (dest->protocol != src->protocol)
! 301: cf_error("Can't copy configuration from a different protocol type");
! 302:
! 303: if (dest->protocol->copy_config == NULL)
! 304: cf_error("Inheriting configuration for %s is not supported", src->protocol->name);
! 305:
! 306: DBG("Copying configuration from %s to %s\n", src->name, dest->name);
! 307:
! 308: /*
! 309: * Copy struct proto_config here. Keep original node, class and name.
! 310: * protocol-specific config copy is handled by protocol copy_config() hook
! 311: */
! 312:
! 313: old_node = dest->n;
! 314: old_class = dest->class;
! 315: old_name = dest->name;
! 316:
! 317: memcpy(dest, src, sizeof(struct proto_config));
! 318:
! 319: dest->n = old_node;
! 320: dest->class = old_class;
! 321: dest->name = old_name;
! 322:
! 323: dest->protocol->copy_config(dest, src);
! 324: }
! 325:
! 326: /**
! 327: * protos_preconfig - pre-configuration processing
! 328: * @c: new configuration
! 329: *
! 330: * This function calls the preconfig() hooks of all routing
! 331: * protocols available to prepare them for reading of the new
! 332: * configuration.
! 333: */
! 334: void
! 335: protos_preconfig(struct config *c)
! 336: {
! 337: struct protocol *p;
! 338:
! 339: init_list(&c->protos);
! 340: DBG("Protocol preconfig:");
! 341: WALK_LIST(p, protocol_list)
! 342: {
! 343: DBG(" %s", p->name);
! 344: p->name_counter = 0;
! 345: if (p->preconfig)
! 346: p->preconfig(p, c);
! 347: }
! 348: DBG("\n");
! 349: }
! 350:
! 351: /**
! 352: * protos_postconfig - post-configuration processing
! 353: * @c: new configuration
! 354: *
! 355: * This function calls the postconfig() hooks of all protocol
! 356: * instances specified in configuration @c. The hooks are not
! 357: * called for protocol templates.
! 358: */
! 359: void
! 360: protos_postconfig(struct config *c)
! 361: {
! 362: struct proto_config *x;
! 363: struct protocol *p;
! 364:
! 365: DBG("Protocol postconfig:");
! 366: WALK_LIST(x, c->protos)
! 367: {
! 368: DBG(" %s", x->name);
! 369:
! 370: p = x->protocol;
! 371: if (p->postconfig)
! 372: p->postconfig(x);
! 373: }
! 374: DBG("\n");
! 375: }
! 376:
! 377: extern struct protocol proto_unix_iface;
! 378:
! 379: static struct proto *
! 380: proto_init(struct proto_config *c)
! 381: {
! 382: struct protocol *p = c->protocol;
! 383: struct proto *q = p->init(c);
! 384:
! 385: q->proto_state = PS_DOWN;
! 386: q->core_state = FS_HUNGRY;
! 387: q->export_state = ES_DOWN;
! 388: q->last_state_change = now;
! 389:
! 390: add_tail(&initial_proto_list, &q->n);
! 391:
! 392: if (p == &proto_unix_iface)
! 393: initial_device_proto = q;
! 394:
! 395: add_tail(&proto_list, &q->glob_node);
! 396: PD(q, "Initializing%s", q->disabled ? " [disabled]" : "");
! 397: return q;
! 398: }
! 399:
! 400: int proto_reconfig_type; /* Hack to propagate type info to pipe reconfigure hook */
! 401:
! 402: static int
! 403: proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type)
! 404: {
! 405: /* If the protocol is DOWN, we just restart it */
! 406: if (p->proto_state == PS_DOWN)
! 407: return 0;
! 408:
! 409: /* If there is a too big change in core attributes, ... */
! 410: if ((nc->protocol != oc->protocol) ||
! 411: (nc->disabled != p->disabled) ||
! 412: (nc->table->table != oc->table->table))
! 413: return 0;
! 414:
! 415: p->debug = nc->debug;
! 416: p->mrtdump = nc->mrtdump;
! 417: proto_reconfig_type = type;
! 418:
! 419: /* Execute protocol specific reconfigure hook */
! 420: if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc)))
! 421: return 0;
! 422:
! 423: DBG("\t%s: same\n", oc->name);
! 424: PD(p, "Reconfigured");
! 425: p->cf = nc;
! 426: p->name = nc->name;
! 427: p->preference = nc->preference;
! 428:
! 429:
! 430: /* Multitable protocols handle rest in their reconfigure hooks */
! 431: if (p->proto->multitable)
! 432: return 1;
! 433:
! 434: /* Update filters and limits in the main announce hook
! 435: Note that this also resets limit state */
! 436: if (p->main_ahook)
! 437: {
! 438: struct announce_hook *ah = p->main_ahook;
! 439: ah->in_filter = nc->in_filter;
! 440: ah->out_filter = nc->out_filter;
! 441: ah->rx_limit = nc->rx_limit;
! 442: ah->in_limit = nc->in_limit;
! 443: ah->out_limit = nc->out_limit;
! 444: ah->in_keep_filtered = nc->in_keep_filtered;
! 445: proto_verify_limits(ah);
! 446: }
! 447:
! 448: /* Update routes when filters changed. If the protocol in not UP,
! 449: it has no routes and we can ignore such changes */
! 450: if ((p->proto_state != PS_UP) || (type == RECONFIG_SOFT))
! 451: return 1;
! 452:
! 453: int import_changed = ! filter_same(nc->in_filter, oc->in_filter);
! 454: int export_changed = ! filter_same(nc->out_filter, oc->out_filter);
! 455:
! 456: /* We treat a change in preferences by reimporting routes */
! 457: if (nc->preference != oc->preference)
! 458: import_changed = 1;
! 459:
! 460: if (import_changed || export_changed)
! 461: log(L_INFO "Reloading protocol %s", p->name);
! 462:
! 463: /* If import filter changed, call reload hook */
! 464: if (import_changed && ! (p->reload_routes && p->reload_routes(p)))
! 465: {
! 466: /* Now, the protocol is reconfigured. But route reload failed
! 467: and we have to do regular protocol restart. */
! 468: log(L_INFO "Restarting protocol %s", p->name);
! 469: p->disabled = 1;
! 470: p->down_code = PDC_CF_RESTART;
! 471: proto_rethink_goal(p);
! 472: p->disabled = 0;
! 473: proto_rethink_goal(p);
! 474: return 1;
! 475: }
! 476:
! 477: if (export_changed)
! 478: proto_request_feeding(p);
! 479:
! 480: return 1;
! 481: }
! 482:
! 483: /**
! 484: * protos_commit - commit new protocol configuration
! 485: * @new: new configuration
! 486: * @old: old configuration or %NULL if it's boot time config
! 487: * @force_reconfig: force restart of all protocols (used for example
! 488: * when the router ID changes)
! 489: * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
! 490: *
! 491: * Scan differences between @old and @new configuration and adjust all
! 492: * protocol instances to conform to the new configuration.
! 493: *
! 494: * When a protocol exists in the new configuration, but it doesn't in the
! 495: * original one, it's immediately started. When a collision with the other
! 496: * running protocol would arise, the new protocol will be temporarily stopped
! 497: * by the locking mechanism.
! 498: *
! 499: * When a protocol exists in the old configuration, but it doesn't in the
! 500: * new one, it's shut down and deleted after the shutdown completes.
! 501: *
! 502: * When a protocol exists in both configurations, the core decides
! 503: * whether it's possible to reconfigure it dynamically - it checks all
! 504: * the core properties of the protocol (changes in filters are ignored
! 505: * if type is RECONFIG_SOFT) and if they match, it asks the
! 506: * reconfigure() hook of the protocol to see if the protocol is able
! 507: * to switch to the new configuration. If it isn't possible, the
! 508: * protocol is shut down and a new instance is started with the new
! 509: * configuration after the shutdown is completed.
! 510: */
! 511: void
! 512: protos_commit(struct config *new, struct config *old, int force_reconfig, int type)
! 513: {
! 514: struct proto_config *oc, *nc;
! 515: struct proto *p, *n;
! 516: struct symbol *sym;
! 517:
! 518: DBG("protos_commit:\n");
! 519: if (old)
! 520: {
! 521: WALK_LIST(oc, old->protos)
! 522: {
! 523: p = oc->proto;
! 524: sym = cf_find_symbol(new, oc->name);
! 525: if (sym && sym->class == SYM_PROTO && !new->shutdown)
! 526: {
! 527: /* Found match, let's check if we can smoothly switch to new configuration */
! 528: /* No need to check description */
! 529: nc = sym->def;
! 530: nc->proto = p;
! 531:
! 532: /* We will try to reconfigure protocol p */
! 533: if (! force_reconfig && proto_reconfigure(p, oc, nc, type))
! 534: continue;
! 535:
! 536: /* Unsuccessful, we will restart it */
! 537: if (!p->disabled && !nc->disabled)
! 538: log(L_INFO "Restarting protocol %s", p->name);
! 539: else if (p->disabled && !nc->disabled)
! 540: log(L_INFO "Enabling protocol %s", p->name);
! 541: else if (!p->disabled && nc->disabled)
! 542: log(L_INFO "Disabling protocol %s", p->name);
! 543:
! 544: p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
! 545: p->cf_new = nc;
! 546: }
! 547: else if (!new->shutdown)
! 548: {
! 549: log(L_INFO "Removing protocol %s", p->name);
! 550: p->down_code = PDC_CF_REMOVE;
! 551: p->cf_new = NULL;
! 552: }
! 553: else /* global shutdown */
! 554: {
! 555: p->down_code = PDC_CMD_SHUTDOWN;
! 556: p->cf_new = NULL;
! 557: }
! 558:
! 559: p->reconfiguring = 1;
! 560: config_add_obstacle(old);
! 561: proto_rethink_goal(p);
! 562: }
! 563: }
! 564:
! 565: WALK_LIST(nc, new->protos)
! 566: if (!nc->proto)
! 567: {
! 568: if (old) /* Not a first-time configuration */
! 569: log(L_INFO "Adding protocol %s", nc->name);
! 570: proto_init(nc);
! 571: }
! 572: DBG("\tdone\n");
! 573:
! 574: DBG("Protocol start\n");
! 575:
! 576: /* Start device protocol first */
! 577: if (initial_device_proto)
! 578: {
! 579: proto_rethink_goal(initial_device_proto);
! 580: initial_device_proto = NULL;
! 581: }
! 582:
! 583: /* Determine router ID for the first time - it has to be here and not in
! 584: global_commit() because it is postponed after start of device protocol */
! 585: if (!config->router_id)
! 586: {
! 587: config->router_id = if_choose_router_id(config->router_id_from, 0);
! 588: if (!config->router_id)
! 589: die("Cannot determine router ID, please configure it manually");
! 590: }
! 591:
! 592: /* Start all other protocols */
! 593: WALK_LIST_DELSAFE(p, n, initial_proto_list)
! 594: proto_rethink_goal(p);
! 595: }
! 596:
! 597: static void
! 598: proto_rethink_goal(struct proto *p)
! 599: {
! 600: struct protocol *q;
! 601: byte goal;
! 602:
! 603: if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
! 604: {
! 605: struct proto_config *nc = p->cf_new;
! 606: DBG("%s has shut down for reconfiguration\n", p->name);
! 607: p->cf->proto = NULL;
! 608: config_del_obstacle(p->cf->global);
! 609: rem_node(&p->n);
! 610: rem_node(&p->glob_node);
! 611: mb_free(p);
! 612: if (!nc)
! 613: return;
! 614: p = proto_init(nc);
! 615: }
! 616:
! 617: /* Determine what state we want to reach */
! 618: if (p->disabled || p->reconfiguring)
! 619: goal = PS_DOWN;
! 620: else
! 621: goal = PS_UP;
! 622:
! 623: q = p->proto;
! 624: if (goal == PS_UP) /* Going up */
! 625: {
! 626: if (p->proto_state == PS_DOWN && p->core_state == FS_HUNGRY)
! 627: {
! 628: DBG("Kicking %s up\n", p->name);
! 629: PD(p, "Starting");
! 630: proto_init_instance(p);
! 631: proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
! 632: }
! 633: }
! 634: else /* Going down */
! 635: {
! 636: if (p->proto_state == PS_START || p->proto_state == PS_UP)
! 637: {
! 638: DBG("Kicking %s down\n", p->name);
! 639: PD(p, "Shutting down");
! 640: proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
! 641: }
! 642: }
! 643: }
! 644:
! 645:
! 646: /**
! 647: * DOC: Graceful restart recovery
! 648: *
! 649: * Graceful restart of a router is a process when the routing plane (e.g. BIRD)
! 650: * restarts but both the forwarding plane (e.g kernel routing table) and routing
! 651: * neighbors keep proper routes, and therefore uninterrupted packet forwarding
! 652: * is maintained.
! 653: *
! 654: * BIRD implements graceful restart recovery by deferring export of routes to
! 655: * protocols until routing tables are refilled with the expected content. After
! 656: * start, protocols generate routes as usual, but routes are not propagated to
! 657: * them, until protocols report that they generated all routes. After that,
! 658: * graceful restart recovery is finished and the export (and the initial feed)
! 659: * to protocols is enabled.
! 660: *
! 661: * When graceful restart recovery need is detected during initialization, then
! 662: * enabled protocols are marked with @gr_recovery flag before start. Such
! 663: * protocols then decide how to proceed with graceful restart, participation is
! 664: * voluntary. Protocols could lock the recovery by proto_graceful_restart_lock()
! 665: * (stored in @gr_lock flag), which means that they want to postpone the end of
! 666: * the recovery until they converge and then unlock it. They also could set
! 667: * @gr_wait before advancing to %PS_UP, which means that the core should defer
! 668: * route export to that protocol until the end of the recovery. This should be
! 669: * done by protocols that expect their neigbors to keep the proper routes
! 670: * (kernel table, BGP sessions with BGP graceful restart capability).
! 671: *
! 672: * The graceful restart recovery is finished when either all graceful restart
! 673: * locks are unlocked or when graceful restart wait timer fires.
! 674: *
! 675: */
! 676:
! 677: static void graceful_restart_done(struct timer *t);
! 678:
! 679: /**
! 680: * graceful_restart_recovery - request initial graceful restart recovery
! 681: *
! 682: * Called by the platform initialization code if the need for recovery
! 683: * after graceful restart is detected during boot. Have to be called
! 684: * before protos_commit().
! 685: */
! 686: void
! 687: graceful_restart_recovery(void)
! 688: {
! 689: graceful_restart_state = GRS_INIT;
! 690: }
! 691:
! 692: /**
! 693: * graceful_restart_init - initialize graceful restart
! 694: *
! 695: * When graceful restart recovery was requested, the function starts an active
! 696: * phase of the recovery and initializes graceful restart wait timer. The
! 697: * function have to be called after protos_commit().
! 698: */
! 699: void
! 700: graceful_restart_init(void)
! 701: {
! 702: if (!graceful_restart_state)
! 703: return;
! 704:
! 705: log(L_INFO "Graceful restart started");
! 706:
! 707: if (!graceful_restart_locks)
! 708: {
! 709: graceful_restart_done(NULL);
! 710: return;
! 711: }
! 712:
! 713: graceful_restart_state = GRS_ACTIVE;
! 714: gr_wait_timer = tm_new(proto_pool);
! 715: gr_wait_timer->hook = graceful_restart_done;
! 716: tm_start(gr_wait_timer, config->gr_wait);
! 717: }
! 718:
! 719: /**
! 720: * graceful_restart_done - finalize graceful restart
! 721: * @t: unused
! 722: *
! 723: * When there are no locks on graceful restart, the functions finalizes the
! 724: * graceful restart recovery. Protocols postponing route export until the end of
! 725: * the recovery are awakened and the export to them is enabled. All other
! 726: * related state is cleared. The function is also called when the graceful
! 727: * restart wait timer fires (but there are still some locks).
! 728: */
! 729: static void
! 730: graceful_restart_done(struct timer *t UNUSED)
! 731: {
! 732: struct proto *p;
! 733: node *n;
! 734:
! 735: log(L_INFO "Graceful restart done");
! 736: graceful_restart_state = GRS_DONE;
! 737:
! 738: WALK_LIST2(p, n, proto_list, glob_node)
! 739: {
! 740: if (!p->gr_recovery)
! 741: continue;
! 742:
! 743: /* Resume postponed export of routes */
! 744: if ((p->proto_state == PS_UP) && p->gr_wait)
! 745: {
! 746: proto_want_export_up(p);
! 747: proto_log_state_change(p);
! 748: }
! 749:
! 750: /* Cleanup */
! 751: p->gr_recovery = 0;
! 752: p->gr_wait = 0;
! 753: p->gr_lock = 0;
! 754: }
! 755:
! 756: graceful_restart_locks = 0;
! 757: }
! 758:
! 759: void
! 760: graceful_restart_show_status(void)
! 761: {
! 762: if (graceful_restart_state != GRS_ACTIVE)
! 763: return;
! 764:
! 765: cli_msg(-24, "Graceful restart recovery in progress");
! 766: cli_msg(-24, " Waiting for %d protocols to recover", graceful_restart_locks);
! 767: cli_msg(-24, " Wait timer is %d/%d", tm_remains(gr_wait_timer), config->gr_wait);
! 768: }
! 769:
! 770: /**
! 771: * proto_graceful_restart_lock - lock graceful restart by protocol
! 772: * @p: protocol instance
! 773: *
! 774: * This function allows a protocol to postpone the end of graceful restart
! 775: * recovery until it converges. The lock is removed when the protocol calls
! 776: * proto_graceful_restart_unlock() or when the protocol is stopped.
! 777: *
! 778: * The function have to be called during the initial phase of graceful restart
! 779: * recovery and only for protocols that are part of graceful restart (i.e. their
! 780: * @gr_recovery is set), which means it should be called from protocol start
! 781: * hooks.
! 782: */
! 783: void
! 784: proto_graceful_restart_lock(struct proto *p)
! 785: {
! 786: ASSERT(graceful_restart_state == GRS_INIT);
! 787: ASSERT(p->gr_recovery);
! 788:
! 789: if (p->gr_lock)
! 790: return;
! 791:
! 792: p->gr_lock = 1;
! 793: graceful_restart_locks++;
! 794: }
! 795:
! 796: /**
! 797: * proto_graceful_restart_unlock - unlock graceful restart by protocol
! 798: * @p: protocol instance
! 799: *
! 800: * This function unlocks a lock from proto_graceful_restart_lock(). It is also
! 801: * automatically called when the lock holding protocol went down.
! 802: */
! 803: void
! 804: proto_graceful_restart_unlock(struct proto *p)
! 805: {
! 806: if (!p->gr_lock)
! 807: return;
! 808:
! 809: p->gr_lock = 0;
! 810: graceful_restart_locks--;
! 811:
! 812: if ((graceful_restart_state == GRS_ACTIVE) && !graceful_restart_locks)
! 813: tm_start(gr_wait_timer, 0);
! 814: }
! 815:
! 816:
! 817:
! 818: /**
! 819: * protos_dump_all - dump status of all protocols
! 820: *
! 821: * This function dumps status of all existing protocol instances to the
! 822: * debug output. It involves printing of general status information
! 823: * such as protocol states, its position on the protocol lists
! 824: * and also calling of a dump() hook of the protocol to print
! 825: * the internals.
! 826: */
! 827: void
! 828: protos_dump_all(void)
! 829: {
! 830: struct proto *p;
! 831: struct announce_hook *a;
! 832:
! 833: debug("Protocols:\n");
! 834:
! 835: WALK_LIST(p, active_proto_list)
! 836: {
! 837: debug(" protocol %s state %s/%s\n", p->name,
! 838: p_states[p->proto_state], c_states[p->core_state]);
! 839: for (a = p->ahooks; a; a = a->next)
! 840: {
! 841: debug("\tTABLE %s\n", a->table->name);
! 842: if (a->in_filter)
! 843: debug("\tInput filter: %s\n", filter_name(a->in_filter));
! 844: if (a->out_filter != FILTER_REJECT)
! 845: debug("\tOutput filter: %s\n", filter_name(a->out_filter));
! 846: }
! 847: if (p->disabled)
! 848: debug("\tDISABLED\n");
! 849: else if (p->proto->dump)
! 850: p->proto->dump(p);
! 851: }
! 852: WALK_LIST(p, inactive_proto_list)
! 853: debug(" inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]);
! 854: WALK_LIST(p, initial_proto_list)
! 855: debug(" initial %s\n", p->name);
! 856: WALK_LIST(p, flush_proto_list)
! 857: debug(" flushing %s\n", p->name);
! 858: }
! 859:
! 860: /**
! 861: * proto_build - make a single protocol available
! 862: * @p: the protocol
! 863: *
! 864: * After the platform specific initialization code uses protos_build()
! 865: * to add all the standard protocols, it should call proto_build() for
! 866: * all platform specific protocols to inform the core that they exist.
! 867: */
! 868: void
! 869: proto_build(struct protocol *p)
! 870: {
! 871: add_tail(&protocol_list, &p->n);
! 872: if (p->attr_class)
! 873: {
! 874: ASSERT(!attr_class_to_protocol[p->attr_class]);
! 875: attr_class_to_protocol[p->attr_class] = p;
! 876: }
! 877: }
! 878:
! 879: /* FIXME: convert this call to some protocol hook */
! 880: extern void bfd_init_all(void);
! 881:
! 882: /**
! 883: * protos_build - build a protocol list
! 884: *
! 885: * This function is called during BIRD startup to insert
! 886: * all standard protocols to the global protocol list. Insertion
! 887: * of platform specific protocols (such as the kernel syncer)
! 888: * is in the domain of competence of the platform dependent
! 889: * startup code.
! 890: */
! 891: void
! 892: protos_build(void)
! 893: {
! 894: init_list(&protocol_list);
! 895: init_list(&proto_list);
! 896: init_list(&active_proto_list);
! 897: init_list(&inactive_proto_list);
! 898: init_list(&initial_proto_list);
! 899: init_list(&flush_proto_list);
! 900: proto_build(&proto_device);
! 901: #ifdef CONFIG_RADV
! 902: proto_build(&proto_radv);
! 903: #endif
! 904: #ifdef CONFIG_RIP
! 905: proto_build(&proto_rip);
! 906: #endif
! 907: #ifdef CONFIG_STATIC
! 908: proto_build(&proto_static);
! 909: #endif
! 910: #ifdef CONFIG_OSPF
! 911: proto_build(&proto_ospf);
! 912: #endif
! 913: #ifdef CONFIG_PIPE
! 914: proto_build(&proto_pipe);
! 915: #endif
! 916: #ifdef CONFIG_BGP
! 917: proto_build(&proto_bgp);
! 918: #endif
! 919: #ifdef CONFIG_BFD
! 920: proto_build(&proto_bfd);
! 921: bfd_init_all();
! 922: #endif
! 923: #ifdef CONFIG_BABEL
! 924: proto_build(&proto_babel);
! 925: #endif
! 926:
! 927: proto_pool = rp_new(&root_pool, "Protocols");
! 928: proto_flush_event = ev_new(proto_pool);
! 929: proto_flush_event->hook = proto_flush_loop;
! 930: proto_shutdown_timer = tm_new(proto_pool);
! 931: proto_shutdown_timer->hook = proto_shutdown_loop;
! 932: }
! 933:
! 934: static void
! 935: proto_feed_more(void *P)
! 936: {
! 937: struct proto *p = P;
! 938:
! 939: if (p->export_state != ES_FEEDING)
! 940: return;
! 941:
! 942: DBG("Feeding protocol %s continued\n", p->name);
! 943: if (rt_feed_baby(p))
! 944: {
! 945: DBG("Feeding protocol %s finished\n", p->name);
! 946: p->export_state = ES_READY;
! 947: proto_log_state_change(p);
! 948:
! 949: if (p->feed_end)
! 950: p->feed_end(p);
! 951: }
! 952: else
! 953: {
! 954: p->attn->hook = proto_feed_more;
! 955: ev_schedule(p->attn); /* Will continue later... */
! 956: }
! 957: }
! 958:
! 959: static void
! 960: proto_feed_initial(void *P)
! 961: {
! 962: struct proto *p = P;
! 963:
! 964: if (p->export_state != ES_FEEDING)
! 965: return;
! 966:
! 967: DBG("Feeding protocol %s\n", p->name);
! 968:
! 969: if_feed_baby(p);
! 970: proto_feed_more(P);
! 971: }
! 972:
! 973: static void
! 974: proto_schedule_feed(struct proto *p, int initial)
! 975: {
! 976: DBG("%s: Scheduling meal\n", p->name);
! 977:
! 978: p->export_state = ES_FEEDING;
! 979: p->refeeding = !initial;
! 980:
! 981: p->attn->hook = initial ? proto_feed_initial : proto_feed_more;
! 982: ev_schedule(p->attn);
! 983:
! 984: if (p->feed_begin)
! 985: p->feed_begin(p, initial);
! 986: }
! 987:
! 988: /*
! 989: * Flushing loop is responsible for flushing routes and protocols
! 990: * after they went down. It runs in proto_flush_event. At the start of
! 991: * one round, protocols waiting to flush are marked in
! 992: * proto_schedule_flush_loop(). At the end of the round (when routing
! 993: * table flush is complete), marked protocols are flushed and a next
! 994: * round may start.
! 995: */
! 996:
! 997: static int flush_loop_state; /* 1 -> running */
! 998:
! 999: static void
! 1000: proto_schedule_flush_loop(void)
! 1001: {
! 1002: struct proto *p;
! 1003: struct announce_hook *h;
! 1004:
! 1005: if (flush_loop_state)
! 1006: return;
! 1007: flush_loop_state = 1;
! 1008:
! 1009: WALK_LIST(p, flush_proto_list)
! 1010: {
! 1011: p->flushing = 1;
! 1012: for (h=p->ahooks; h; h=h->next)
! 1013: rt_mark_for_prune(h->table);
! 1014: }
! 1015:
! 1016: ev_schedule(proto_flush_event);
! 1017: }
! 1018:
! 1019: static void
! 1020: proto_flush_loop(void *unused UNUSED)
! 1021: {
! 1022: struct proto *p;
! 1023:
! 1024: if (! rt_prune_loop())
! 1025: {
! 1026: /* Rtable pruning is not finished */
! 1027: ev_schedule(proto_flush_event);
! 1028: return;
! 1029: }
! 1030:
! 1031: rt_prune_sources();
! 1032:
! 1033: again:
! 1034: WALK_LIST(p, flush_proto_list)
! 1035: if (p->flushing)
! 1036: {
! 1037: /* This will flush interfaces in the same manner
! 1038: like rt_prune_all() flushes routes */
! 1039: if (p->proto == &proto_unix_iface)
! 1040: if_flush_ifaces(p);
! 1041:
! 1042: DBG("Flushing protocol %s\n", p->name);
! 1043: p->flushing = 0;
! 1044: p->core_state = FS_HUNGRY;
! 1045: proto_relink(p);
! 1046: proto_log_state_change(p);
! 1047: if (p->proto_state == PS_DOWN)
! 1048: proto_fell_down(p);
! 1049: goto again;
! 1050: }
! 1051:
! 1052: /* This round finished, perhaps there will be another one */
! 1053: flush_loop_state = 0;
! 1054: if (!EMPTY_LIST(flush_proto_list))
! 1055: proto_schedule_flush_loop();
! 1056: }
! 1057:
! 1058:
! 1059: /* Temporary hack to propagate restart to BGP */
! 1060: int proto_restart;
! 1061:
! 1062: static void
! 1063: proto_shutdown_loop(struct timer *t UNUSED)
! 1064: {
! 1065: struct proto *p, *p_next;
! 1066:
! 1067: WALK_LIST_DELSAFE(p, p_next, active_proto_list)
! 1068: if (p->down_sched)
! 1069: {
! 1070: proto_restart = (p->down_sched == PDS_RESTART);
! 1071:
! 1072: p->disabled = 1;
! 1073: proto_rethink_goal(p);
! 1074: if (proto_restart)
! 1075: {
! 1076: p->disabled = 0;
! 1077: proto_rethink_goal(p);
! 1078: }
! 1079: }
! 1080: }
! 1081:
! 1082: static inline void
! 1083: proto_schedule_down(struct proto *p, byte restart, byte code)
! 1084: {
! 1085: /* Does not work for other states (even PS_START) */
! 1086: ASSERT(p->proto_state == PS_UP);
! 1087:
! 1088: /* Scheduled restart may change to shutdown, but not otherwise */
! 1089: if (p->down_sched == PDS_DISABLE)
! 1090: return;
! 1091:
! 1092: p->down_sched = restart ? PDS_RESTART : PDS_DISABLE;
! 1093: p->down_code = code;
! 1094: tm_start_max(proto_shutdown_timer, restart ? 2 : 0);
! 1095: }
! 1096:
! 1097:
! 1098: /**
! 1099: * proto_request_feeding - request feeding routes to the protocol
! 1100: * @p: given protocol
! 1101: *
! 1102: * Sometimes it is needed to send again all routes to the
! 1103: * protocol. This is called feeding and can be requested by this
! 1104: * function. This would cause protocol export state transition
! 1105: * to ES_FEEDING (during feeding) and when completed, it will
! 1106: * switch back to ES_READY. This function can be called even
! 1107: * when feeding is already running, in that case it is restarted.
! 1108: */
! 1109: void
! 1110: proto_request_feeding(struct proto *p)
! 1111: {
! 1112: ASSERT(p->proto_state == PS_UP);
! 1113:
! 1114: /* Do nothing if we are still waiting for feeding */
! 1115: if (p->export_state == ES_DOWN)
! 1116: return;
! 1117:
! 1118: /* If we are already feeding, we want to restart it */
! 1119: if (p->export_state == ES_FEEDING)
! 1120: {
! 1121: /* Unless feeding is in initial state */
! 1122: if (p->attn->hook == proto_feed_initial)
! 1123: return;
! 1124:
! 1125: rt_feed_baby_abort(p);
! 1126: }
! 1127:
! 1128: /* FIXME: This should be changed for better support of multitable protos */
! 1129: struct announce_hook *ah;
! 1130: for (ah = p->ahooks; ah; ah = ah->next)
! 1131: proto_reset_limit(ah->out_limit);
! 1132:
! 1133: /* Hack: reset exp_routes during refeed, and do not decrease it later */
! 1134: p->stats.exp_routes = 0;
! 1135:
! 1136: proto_schedule_feed(p, 0);
! 1137: proto_log_state_change(p);
! 1138: }
! 1139:
! 1140: static const char *
! 1141: proto_limit_name(struct proto_limit *l)
! 1142: {
! 1143: const char *actions[] = {
! 1144: [PLA_WARN] = "warn",
! 1145: [PLA_BLOCK] = "block",
! 1146: [PLA_RESTART] = "restart",
! 1147: [PLA_DISABLE] = "disable",
! 1148: };
! 1149:
! 1150: return actions[l->action];
! 1151: }
! 1152:
! 1153: /**
! 1154: * proto_notify_limit: notify about limit hit and take appropriate action
! 1155: * @ah: announce hook
! 1156: * @l: limit being hit
! 1157: * @dir: limit direction (PLD_*)
! 1158: * @rt_count: the number of routes
! 1159: *
! 1160: * The function is called by the route processing core when limit @l
! 1161: * is breached. It activates the limit and tooks appropriate action
! 1162: * according to @l->action.
! 1163: */
! 1164: void
! 1165: proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count)
! 1166: {
! 1167: const char *dir_name[PLD_MAX] = { "receive", "import" , "export" };
! 1168: const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
! 1169: struct proto *p = ah->proto;
! 1170:
! 1171: if (l->state == PLS_BLOCKED)
! 1172: return;
! 1173:
! 1174: /* For warning action, we want the log message every time we hit the limit */
! 1175: if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit)))
! 1176: log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
! 1177: p->name, dir_name[dir], l->limit, proto_limit_name(l));
! 1178:
! 1179: switch (l->action)
! 1180: {
! 1181: case PLA_WARN:
! 1182: l->state = PLS_ACTIVE;
! 1183: break;
! 1184:
! 1185: case PLA_BLOCK:
! 1186: l->state = PLS_BLOCKED;
! 1187: break;
! 1188:
! 1189: case PLA_RESTART:
! 1190: case PLA_DISABLE:
! 1191: l->state = PLS_BLOCKED;
! 1192: if (p->proto_state == PS_UP)
! 1193: proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
! 1194: break;
! 1195: }
! 1196: }
! 1197:
! 1198: void
! 1199: proto_verify_limits(struct announce_hook *ah)
! 1200: {
! 1201: struct proto_limit *l;
! 1202: struct proto_stats *stats = ah->stats;
! 1203: u32 all_routes = stats->imp_routes + stats->filt_routes;
! 1204:
! 1205: l = ah->rx_limit;
! 1206: if (l && (all_routes > l->limit))
! 1207: proto_notify_limit(ah, l, PLD_RX, all_routes);
! 1208:
! 1209: l = ah->in_limit;
! 1210: if (l && (stats->imp_routes > l->limit))
! 1211: proto_notify_limit(ah, l, PLD_IN, stats->imp_routes);
! 1212:
! 1213: l = ah->out_limit;
! 1214: if (l && (stats->exp_routes > l->limit))
! 1215: proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes);
! 1216: }
! 1217:
! 1218:
! 1219: static void
! 1220: proto_want_core_up(struct proto *p)
! 1221: {
! 1222: ASSERT(p->core_state == FS_HUNGRY);
! 1223:
! 1224: if (!p->proto->multitable)
! 1225: {
! 1226: p->main_source = rt_get_source(p, 0);
! 1227: rt_lock_source(p->main_source);
! 1228:
! 1229: /* Connect protocol to routing table */
! 1230: p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats);
! 1231: p->main_ahook->in_filter = p->cf->in_filter;
! 1232: p->main_ahook->out_filter = p->cf->out_filter;
! 1233: p->main_ahook->rx_limit = p->cf->rx_limit;
! 1234: p->main_ahook->in_limit = p->cf->in_limit;
! 1235: p->main_ahook->out_limit = p->cf->out_limit;
! 1236: p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered;
! 1237:
! 1238: proto_reset_limit(p->main_ahook->rx_limit);
! 1239: proto_reset_limit(p->main_ahook->in_limit);
! 1240: proto_reset_limit(p->main_ahook->out_limit);
! 1241: }
! 1242:
! 1243: p->core_state = FS_HAPPY;
! 1244: proto_relink(p);
! 1245: }
! 1246:
! 1247: static void
! 1248: proto_want_export_up(struct proto *p)
! 1249: {
! 1250: ASSERT(p->core_state == FS_HAPPY);
! 1251: ASSERT(p->export_state == ES_DOWN);
! 1252:
! 1253: proto_link_ahooks(p);
! 1254: proto_schedule_feed(p, 1); /* Sets ES_FEEDING */
! 1255: }
! 1256:
! 1257: static void
! 1258: proto_want_export_down(struct proto *p)
! 1259: {
! 1260: ASSERT(p->export_state != ES_DOWN);
! 1261:
! 1262: /* Need to abort feeding */
! 1263: if (p->export_state == ES_FEEDING)
! 1264: rt_feed_baby_abort(p);
! 1265:
! 1266: p->export_state = ES_DOWN;
! 1267: p->stats.exp_routes = 0;
! 1268: proto_unlink_ahooks(p);
! 1269: }
! 1270:
! 1271: static void
! 1272: proto_want_core_down(struct proto *p)
! 1273: {
! 1274: ASSERT(p->core_state == FS_HAPPY);
! 1275: ASSERT(p->export_state == ES_DOWN);
! 1276:
! 1277: p->core_state = FS_FLUSHING;
! 1278: proto_relink(p);
! 1279: proto_schedule_flush_loop();
! 1280:
! 1281: if (!p->proto->multitable)
! 1282: {
! 1283: rt_unlock_source(p->main_source);
! 1284: p->main_source = NULL;
! 1285: }
! 1286: }
! 1287:
! 1288: static void
! 1289: proto_falling_down(struct proto *p)
! 1290: {
! 1291: p->gr_recovery = 0;
! 1292: p->gr_wait = 0;
! 1293: if (p->gr_lock)
! 1294: proto_graceful_restart_unlock(p);
! 1295: }
! 1296:
! 1297: static void
! 1298: proto_fell_down(struct proto *p)
! 1299: {
! 1300: DBG("Protocol %s down\n", p->name);
! 1301:
! 1302: u32 all_routes = p->stats.imp_routes + p->stats.filt_routes;
! 1303: if (all_routes != 0)
! 1304: log(L_ERR "Protocol %s is down but still has %d routes", p->name, all_routes);
! 1305:
! 1306: bzero(&p->stats, sizeof(struct proto_stats));
! 1307: proto_free_ahooks(p);
! 1308:
! 1309: if (! p->proto->multitable)
! 1310: rt_unlock_table(p->table);
! 1311:
! 1312: if (p->proto->cleanup)
! 1313: p->proto->cleanup(p);
! 1314:
! 1315: proto_rethink_goal(p);
! 1316: }
! 1317:
! 1318:
! 1319: /**
! 1320: * proto_notify_state - notify core about protocol state change
! 1321: * @p: protocol the state of which has changed
! 1322: * @ps: the new status
! 1323: *
! 1324: * Whenever a state of a protocol changes due to some event internal
! 1325: * to the protocol (i.e., not inside a start() or shutdown() hook),
! 1326: * it should immediately notify the core about the change by calling
! 1327: * proto_notify_state() which will write the new state to the &proto
! 1328: * structure and take all the actions necessary to adapt to the new
! 1329: * state. State change to PS_DOWN immediately frees resources of protocol
! 1330: * and might execute start callback of protocol; therefore,
! 1331: * it should be used at tail positions of protocol callbacks.
! 1332: */
! 1333: void
! 1334: proto_notify_state(struct proto *p, unsigned ps)
! 1335: {
! 1336: unsigned ops = p->proto_state;
! 1337: unsigned cs = p->core_state;
! 1338: unsigned es = p->export_state;
! 1339:
! 1340: DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]);
! 1341: if (ops == ps)
! 1342: return;
! 1343:
! 1344: p->proto_state = ps;
! 1345: p->last_state_change = now;
! 1346:
! 1347: switch (ps)
! 1348: {
! 1349: case PS_START:
! 1350: ASSERT(ops == PS_DOWN || ops == PS_UP);
! 1351: ASSERT(cs == FS_HUNGRY || cs == FS_HAPPY);
! 1352:
! 1353: if (es != ES_DOWN)
! 1354: proto_want_export_down(p);
! 1355: break;
! 1356:
! 1357: case PS_UP:
! 1358: ASSERT(ops == PS_DOWN || ops == PS_START);
! 1359: ASSERT(cs == FS_HUNGRY || cs == FS_HAPPY);
! 1360: ASSERT(es == ES_DOWN);
! 1361:
! 1362: if (cs == FS_HUNGRY)
! 1363: proto_want_core_up(p);
! 1364: if (!p->gr_wait)
! 1365: proto_want_export_up(p);
! 1366: break;
! 1367:
! 1368: case PS_STOP:
! 1369: ASSERT(ops == PS_START || ops == PS_UP);
! 1370:
! 1371: p->down_sched = 0;
! 1372:
! 1373: if (es != ES_DOWN)
! 1374: proto_want_export_down(p);
! 1375: if (cs == FS_HAPPY)
! 1376: proto_want_core_down(p);
! 1377: proto_falling_down(p);
! 1378: break;
! 1379:
! 1380: case PS_DOWN:
! 1381: p->down_code = 0;
! 1382: p->down_sched = 0;
! 1383:
! 1384: if (es != ES_DOWN)
! 1385: proto_want_export_down(p);
! 1386: if (cs == FS_HAPPY)
! 1387: proto_want_core_down(p);
! 1388: if (ops != PS_STOP)
! 1389: proto_falling_down(p);
! 1390:
! 1391: neigh_prune(); // FIXME convert neighbors to resource?
! 1392: rfree(p->pool);
! 1393: p->pool = NULL;
! 1394:
! 1395: if (cs == FS_HUNGRY) /* Shutdown finished */
! 1396: {
! 1397: proto_log_state_change(p);
! 1398: proto_fell_down(p);
! 1399: return; /* The protocol might have ceased to exist */
! 1400: }
! 1401: break;
! 1402:
! 1403: default:
! 1404: bug("%s: Invalid state %d", p->name, ps);
! 1405: }
! 1406:
! 1407: proto_log_state_change(p);
! 1408: }
! 1409:
! 1410: /*
! 1411: * CLI Commands
! 1412: */
! 1413:
! 1414: static char *
! 1415: proto_state_name(struct proto *p)
! 1416: {
! 1417: #define P(x,y) ((x << 4) | y)
! 1418: switch (P(p->proto_state, p->core_state))
! 1419: {
! 1420: case P(PS_DOWN, FS_HUNGRY): return "down";
! 1421: case P(PS_START, FS_HUNGRY):
! 1422: case P(PS_START, FS_HAPPY): return "start";
! 1423: case P(PS_UP, FS_HAPPY):
! 1424: switch (p->export_state)
! 1425: {
! 1426: case ES_DOWN: return "wait";
! 1427: case ES_FEEDING: return "feed";
! 1428: case ES_READY: return "up";
! 1429: default: return "???";
! 1430: }
! 1431: case P(PS_STOP, FS_HUNGRY):
! 1432: case P(PS_STOP, FS_FLUSHING): return "stop";
! 1433: case P(PS_DOWN, FS_FLUSHING): return "flush";
! 1434: default: return "???";
! 1435: }
! 1436: #undef P
! 1437: }
! 1438:
! 1439: static void
! 1440: proto_show_stats(struct proto_stats *s, int in_keep_filtered)
! 1441: {
! 1442: if (in_keep_filtered)
! 1443: cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred",
! 1444: s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes);
! 1445: else
! 1446: cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
! 1447: s->imp_routes, s->exp_routes, s->pref_routes);
! 1448:
! 1449: cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
! 1450: cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
! 1451: s->imp_updates_received, s->imp_updates_invalid,
! 1452: s->imp_updates_filtered, s->imp_updates_ignored,
! 1453: s->imp_updates_accepted);
! 1454: cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
! 1455: s->imp_withdraws_received, s->imp_withdraws_invalid,
! 1456: s->imp_withdraws_ignored, s->imp_withdraws_accepted);
! 1457: cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u",
! 1458: s->exp_updates_received, s->exp_updates_rejected,
! 1459: s->exp_updates_filtered, s->exp_updates_accepted);
! 1460: cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u",
! 1461: s->exp_withdraws_received, s->exp_withdraws_accepted);
! 1462: }
! 1463:
! 1464: void
! 1465: proto_show_limit(struct proto_limit *l, const char *dsc)
! 1466: {
! 1467: if (!l)
! 1468: return;
! 1469:
! 1470: cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : "");
! 1471: cli_msg(-1006, " Action: %s", proto_limit_name(l));
! 1472: }
! 1473:
! 1474: void
! 1475: proto_show_basic_info(struct proto *p)
! 1476: {
! 1477: // cli_msg(-1006, " Table: %s", p->table->name);
! 1478: cli_msg(-1006, " Preference: %d", p->preference);
! 1479: cli_msg(-1006, " Input filter: %s", filter_name(p->cf->in_filter));
! 1480: cli_msg(-1006, " Output filter: %s", filter_name(p->cf->out_filter));
! 1481:
! 1482: if (graceful_restart_state == GRS_ACTIVE)
! 1483: cli_msg(-1006, " GR recovery: %s%s",
! 1484: p->gr_lock ? " pending" : "",
! 1485: p->gr_wait ? " waiting" : "");
! 1486:
! 1487: proto_show_limit(p->cf->rx_limit, "Receive limit:");
! 1488: proto_show_limit(p->cf->in_limit, "Import limit:");
! 1489: proto_show_limit(p->cf->out_limit, "Export limit:");
! 1490:
! 1491: if (p->proto_state != PS_DOWN)
! 1492: proto_show_stats(&p->stats, p->cf->in_keep_filtered);
! 1493: }
! 1494:
! 1495: void
! 1496: proto_cmd_show(struct proto *p, uint verbose, int cnt)
! 1497: {
! 1498: byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE];
! 1499:
! 1500: /* First protocol - show header */
! 1501: if (!cnt)
! 1502: cli_msg(-2002, "name proto table state since info");
! 1503:
! 1504: buf[0] = 0;
! 1505: if (p->proto->get_status)
! 1506: p->proto->get_status(p, buf);
! 1507: tm_format_datetime(tbuf, &config->tf_proto, p->last_state_change);
! 1508: cli_msg(-1002, "%-8s %-8s %-8s %-5s %-10s %s",
! 1509: p->name,
! 1510: p->proto->name,
! 1511: p->table->name,
! 1512: proto_state_name(p),
! 1513: tbuf,
! 1514: buf);
! 1515: if (verbose)
! 1516: {
! 1517: if (p->cf->dsc)
! 1518: cli_msg(-1006, " Description: %s", p->cf->dsc);
! 1519: if (p->cf->router_id)
! 1520: cli_msg(-1006, " Router ID: %R", p->cf->router_id);
! 1521:
! 1522: if (p->proto->show_proto_info)
! 1523: p->proto->show_proto_info(p);
! 1524: else
! 1525: proto_show_basic_info(p);
! 1526:
! 1527: cli_msg(-1006, "");
! 1528: }
! 1529: }
! 1530:
! 1531: void
! 1532: proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
! 1533: {
! 1534: if (p->disabled)
! 1535: {
! 1536: cli_msg(-8, "%s: already disabled", p->name);
! 1537: return;
! 1538: }
! 1539:
! 1540: log(L_INFO "Disabling protocol %s", p->name);
! 1541: p->disabled = 1;
! 1542: p->down_code = PDC_CMD_DISABLE;
! 1543: proto_rethink_goal(p);
! 1544: cli_msg(-9, "%s: disabled", p->name);
! 1545: }
! 1546:
! 1547: void
! 1548: proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
! 1549: {
! 1550: if (!p->disabled)
! 1551: {
! 1552: cli_msg(-10, "%s: already enabled", p->name);
! 1553: return;
! 1554: }
! 1555:
! 1556: log(L_INFO "Enabling protocol %s", p->name);
! 1557: p->disabled = 0;
! 1558: proto_rethink_goal(p);
! 1559: cli_msg(-11, "%s: enabled", p->name);
! 1560: }
! 1561:
! 1562: void
! 1563: proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
! 1564: {
! 1565: if (p->disabled)
! 1566: {
! 1567: cli_msg(-8, "%s: already disabled", p->name);
! 1568: return;
! 1569: }
! 1570:
! 1571: log(L_INFO "Restarting protocol %s", p->name);
! 1572: p->disabled = 1;
! 1573: p->down_code = PDC_CMD_RESTART;
! 1574: proto_rethink_goal(p);
! 1575: p->disabled = 0;
! 1576: proto_rethink_goal(p);
! 1577: cli_msg(-12, "%s: restarted", p->name);
! 1578: }
! 1579:
! 1580: void
! 1581: proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED)
! 1582: {
! 1583: if (p->disabled)
! 1584: {
! 1585: cli_msg(-8, "%s: already disabled", p->name);
! 1586: return;
! 1587: }
! 1588:
! 1589: /* If the protocol in not UP, it has no routes */
! 1590: if (p->proto_state != PS_UP)
! 1591: return;
! 1592:
! 1593: log(L_INFO "Reloading protocol %s", p->name);
! 1594:
! 1595: /* re-importing routes */
! 1596: if (dir != CMD_RELOAD_OUT)
! 1597: {
! 1598: if (! (p->reload_routes && p->reload_routes(p)))
! 1599: {
! 1600: cli_msg(-8006, "%s: reload failed", p->name);
! 1601: return;
! 1602: }
! 1603:
! 1604: /*
! 1605: * Should be done before reload_routes() hook?
! 1606: * Perhaps, but these hooks work asynchronously.
! 1607: */
! 1608: if (!p->proto->multitable)
! 1609: {
! 1610: proto_reset_limit(p->main_ahook->rx_limit);
! 1611: proto_reset_limit(p->main_ahook->in_limit);
! 1612: }
! 1613: }
! 1614:
! 1615: /* re-exporting routes */
! 1616: if (dir != CMD_RELOAD_IN)
! 1617: proto_request_feeding(p);
! 1618:
! 1619: cli_msg(-15, "%s: reloading", p->name);
! 1620: }
! 1621:
! 1622: void
! 1623: proto_cmd_debug(struct proto *p, uint mask, int cnt UNUSED)
! 1624: {
! 1625: p->debug = mask;
! 1626: }
! 1627:
! 1628: void
! 1629: proto_cmd_mrtdump(struct proto *p, uint mask, int cnt UNUSED)
! 1630: {
! 1631: p->mrtdump = mask;
! 1632: }
! 1633:
! 1634: static void
! 1635: proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int), uint arg)
! 1636: {
! 1637: if (s->class != SYM_PROTO)
! 1638: {
! 1639: cli_msg(9002, "%s is not a protocol", s->name);
! 1640: return;
! 1641: }
! 1642:
! 1643: cmd(((struct proto_config *)s->def)->proto, arg, 0);
! 1644: cli_msg(0, "");
! 1645: }
! 1646:
! 1647: static void
! 1648: proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint arg)
! 1649: {
! 1650: int cnt = 0;
! 1651:
! 1652: node *nn;
! 1653: WALK_LIST(nn, proto_list)
! 1654: {
! 1655: struct proto *p = SKIP_BACK(struct proto, glob_node, nn);
! 1656:
! 1657: if (!patt || patmatch(patt, p->name))
! 1658: cmd(p, arg, cnt++);
! 1659: }
! 1660:
! 1661: if (!cnt)
! 1662: cli_msg(8003, "No protocols match");
! 1663: else
! 1664: cli_msg(0, "");
! 1665: }
! 1666:
! 1667: void
! 1668: proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int),
! 1669: int restricted, uint arg)
! 1670: {
! 1671: if (restricted && cli_access_restricted())
! 1672: return;
! 1673:
! 1674: if (ps.patt)
! 1675: proto_apply_cmd_patt(ps.ptr, cmd, arg);
! 1676: else
! 1677: proto_apply_cmd_symbol(ps.ptr, cmd, arg);
! 1678: }
! 1679:
! 1680: struct proto *
! 1681: proto_get_named(struct symbol *sym, struct protocol *pr)
! 1682: {
! 1683: struct proto *p, *q;
! 1684:
! 1685: if (sym)
! 1686: {
! 1687: if (sym->class != SYM_PROTO)
! 1688: cf_error("%s: Not a protocol", sym->name);
! 1689: p = ((struct proto_config *)sym->def)->proto;
! 1690: if (!p || p->proto != pr)
! 1691: cf_error("%s: Not a %s protocol", sym->name, pr->name);
! 1692: }
! 1693: else
! 1694: {
! 1695: p = NULL;
! 1696: WALK_LIST(q, active_proto_list)
! 1697: if (q->proto == pr)
! 1698: {
! 1699: if (p)
! 1700: cf_error("There are multiple %s protocols running", pr->name);
! 1701: p = q;
! 1702: }
! 1703: if (!p)
! 1704: cf_error("There is no %s protocol running", pr->name);
! 1705: }
! 1706: return p;
! 1707: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>