Annotation of embedaddon/bird2/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/timer.h"
! 17: #include "lib/string.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: pool *proto_pool;
! 25: list proto_list;
! 26:
! 27: static list protocol_list;
! 28: struct protocol *class_to_protocol[PROTOCOL__MAX];
! 29:
! 30: #define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0)
! 31:
! 32: static timer *proto_shutdown_timer;
! 33: static timer *gr_wait_timer;
! 34:
! 35: #define GRS_NONE 0
! 36: #define GRS_INIT 1
! 37: #define GRS_ACTIVE 2
! 38: #define GRS_DONE 3
! 39:
! 40: static int graceful_restart_state;
! 41: static u32 graceful_restart_locks;
! 42:
! 43: static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
! 44: static char *c_states[] = { "DOWN", "START", "UP", "FLUSHING" };
! 45:
! 46: extern struct protocol proto_unix_iface;
! 47:
! 48: static void proto_shutdown_loop(timer *);
! 49: static void proto_rethink_goal(struct proto *p);
! 50: static char *proto_state_name(struct proto *p);
! 51: static void channel_verify_limits(struct channel *c);
! 52: static inline void channel_reset_limit(struct channel_limit *l);
! 53:
! 54:
! 55: static inline int proto_is_done(struct proto *p)
! 56: { return (p->proto_state == PS_DOWN) && (p->active_channels == 0); }
! 57:
! 58: static inline int channel_is_active(struct channel *c)
! 59: { return (c->channel_state == CS_START) || (c->channel_state == CS_UP); }
! 60:
! 61: static void
! 62: proto_log_state_change(struct proto *p)
! 63: {
! 64: if (p->debug & D_STATES)
! 65: {
! 66: char *name = proto_state_name(p);
! 67: if (name != p->last_state_name_announced)
! 68: {
! 69: p->last_state_name_announced = name;
! 70: PD(p, "State changed to %s", proto_state_name(p));
! 71: }
! 72: }
! 73: else
! 74: p->last_state_name_announced = NULL;
! 75: }
! 76:
! 77:
! 78: struct channel_config *
! 79: proto_cf_find_channel(struct proto_config *pc, uint net_type)
! 80: {
! 81: struct channel_config *cc;
! 82:
! 83: WALK_LIST(cc, pc->channels)
! 84: if (cc->net_type == net_type)
! 85: return cc;
! 86:
! 87: return NULL;
! 88: }
! 89:
! 90: /**
! 91: * proto_find_channel_by_table - find channel connected to a routing table
! 92: * @p: protocol instance
! 93: * @t: routing table
! 94: *
! 95: * Returns pointer to channel or NULL
! 96: */
! 97: struct channel *
! 98: proto_find_channel_by_table(struct proto *p, struct rtable *t)
! 99: {
! 100: struct channel *c;
! 101:
! 102: WALK_LIST(c, p->channels)
! 103: if (c->table == t)
! 104: return c;
! 105:
! 106: return NULL;
! 107: }
! 108:
! 109: /**
! 110: * proto_find_channel_by_name - find channel by its name
! 111: * @p: protocol instance
! 112: * @n: channel name
! 113: *
! 114: * Returns pointer to channel or NULL
! 115: */
! 116: struct channel *
! 117: proto_find_channel_by_name(struct proto *p, const char *n)
! 118: {
! 119: struct channel *c;
! 120:
! 121: WALK_LIST(c, p->channels)
! 122: if (!strcmp(c->name, n))
! 123: return c;
! 124:
! 125: return NULL;
! 126: }
! 127:
! 128: /**
! 129: * proto_add_channel - connect protocol to a routing table
! 130: * @p: protocol instance
! 131: * @cf: channel configuration
! 132: *
! 133: * This function creates a channel between the protocol instance @p and the
! 134: * routing table specified in the configuration @cf, making the protocol hear
! 135: * all changes in the table and allowing the protocol to update routes in the
! 136: * table.
! 137: *
! 138: * The channel is linked in the protocol channel list and when active also in
! 139: * the table channel list. Channels are allocated from the global resource pool
! 140: * (@proto_pool) and they are automatically freed when the protocol is removed.
! 141: */
! 142:
! 143: struct channel *
! 144: proto_add_channel(struct proto *p, struct channel_config *cf)
! 145: {
! 146: struct channel *c = mb_allocz(proto_pool, cf->channel->channel_size);
! 147:
! 148: c->name = cf->name;
! 149: c->channel = cf->channel;
! 150: c->proto = p;
! 151: c->table = cf->table->table;
! 152:
! 153: c->in_filter = cf->in_filter;
! 154: c->out_filter = cf->out_filter;
! 155: c->rx_limit = cf->rx_limit;
! 156: c->in_limit = cf->in_limit;
! 157: c->out_limit = cf->out_limit;
! 158:
! 159: c->net_type = cf->net_type;
! 160: c->ra_mode = cf->ra_mode;
! 161: c->preference = cf->preference;
! 162: c->merge_limit = cf->merge_limit;
! 163: c->in_keep_filtered = cf->in_keep_filtered;
! 164:
! 165: c->channel_state = CS_DOWN;
! 166: c->export_state = ES_DOWN;
! 167: c->last_state_change = current_time();
! 168: c->last_tx_filter_change = current_time();
! 169: c->reloadable = 1;
! 170:
! 171: CALL(c->channel->init, c, cf);
! 172:
! 173: add_tail(&p->channels, &c->n);
! 174:
! 175: PD(p, "Channel %s connected to table %s", c->name, c->table->name);
! 176:
! 177: return c;
! 178: }
! 179:
! 180: void
! 181: proto_remove_channel(struct proto *p, struct channel *c)
! 182: {
! 183: ASSERT(c->channel_state == CS_DOWN);
! 184:
! 185: PD(p, "Channel %s removed", c->name);
! 186:
! 187: rem_node(&c->n);
! 188: mb_free(c);
! 189: }
! 190:
! 191:
! 192: static void
! 193: proto_start_channels(struct proto *p)
! 194: {
! 195: struct channel *c;
! 196: WALK_LIST(c, p->channels)
! 197: if (!c->disabled)
! 198: channel_set_state(c, CS_UP);
! 199: }
! 200:
! 201: static void
! 202: proto_pause_channels(struct proto *p)
! 203: {
! 204: struct channel *c;
! 205: WALK_LIST(c, p->channels)
! 206: if (!c->disabled && channel_is_active(c))
! 207: channel_set_state(c, CS_START);
! 208: }
! 209:
! 210: static void
! 211: proto_stop_channels(struct proto *p)
! 212: {
! 213: struct channel *c;
! 214: WALK_LIST(c, p->channels)
! 215: if (!c->disabled && channel_is_active(c))
! 216: channel_set_state(c, CS_FLUSHING);
! 217: }
! 218:
! 219: static void
! 220: proto_remove_channels(struct proto *p)
! 221: {
! 222: struct channel *c;
! 223: WALK_LIST_FIRST(c, p->channels)
! 224: proto_remove_channel(p, c);
! 225: }
! 226:
! 227: static void
! 228: channel_schedule_feed(struct channel *c, int initial)
! 229: {
! 230: // DBG("%s: Scheduling meal\n", p->name);
! 231: ASSERT(c->channel_state == CS_UP);
! 232:
! 233: c->export_state = ES_FEEDING;
! 234: c->refeeding = !initial;
! 235:
! 236: ev_schedule(c->feed_event);
! 237: }
! 238:
! 239: static void
! 240: channel_feed_loop(void *ptr)
! 241: {
! 242: struct channel *c = ptr;
! 243:
! 244: if (c->export_state != ES_FEEDING)
! 245: return;
! 246:
! 247: if (!c->feed_active)
! 248: if (c->proto->feed_begin)
! 249: c->proto->feed_begin(c, !c->refeeding);
! 250:
! 251: // DBG("Feeding protocol %s continued\n", p->name);
! 252: if (!rt_feed_channel(c))
! 253: {
! 254: ev_schedule(c->feed_event);
! 255: return;
! 256: }
! 257:
! 258: // DBG("Feeding protocol %s finished\n", p->name);
! 259: c->export_state = ES_READY;
! 260: // proto_log_state_change(p);
! 261:
! 262: if (c->proto->feed_end)
! 263: c->proto->feed_end(c);
! 264: }
! 265:
! 266:
! 267: static void
! 268: channel_start_export(struct channel *c)
! 269: {
! 270: ASSERT(c->channel_state == CS_UP);
! 271: ASSERT(c->export_state == ES_DOWN);
! 272:
! 273: channel_schedule_feed(c, 1); /* Sets ES_FEEDING */
! 274: }
! 275:
! 276: static void
! 277: channel_stop_export(struct channel *c)
! 278: {
! 279: /* Need to abort feeding */
! 280: if (c->export_state == ES_FEEDING)
! 281: rt_feed_channel_abort(c);
! 282:
! 283: c->export_state = ES_DOWN;
! 284: c->stats.exp_routes = 0;
! 285: }
! 286:
! 287:
! 288: /* Called by protocol for reload from in_table */
! 289: void
! 290: channel_schedule_reload(struct channel *c)
! 291: {
! 292: ASSERT(c->channel_state == CS_UP);
! 293:
! 294: rt_reload_channel_abort(c);
! 295: ev_schedule(c->reload_event);
! 296: }
! 297:
! 298: static void
! 299: channel_reload_loop(void *ptr)
! 300: {
! 301: struct channel *c = ptr;
! 302:
! 303: if (!rt_reload_channel(c))
! 304: {
! 305: ev_schedule(c->reload_event);
! 306: return;
! 307: }
! 308: }
! 309:
! 310: static void
! 311: channel_reset_import(struct channel *c)
! 312: {
! 313: /* Need to abort feeding */
! 314: ev_postpone(c->reload_event);
! 315: rt_reload_channel_abort(c);
! 316:
! 317: rt_prune_sync(c->in_table, 1);
! 318: }
! 319:
! 320: static void
! 321: channel_reset_export(struct channel *c)
! 322: {
! 323: /* Just free the routes */
! 324: rt_prune_sync(c->out_table, 1);
! 325: }
! 326:
! 327: /* Called by protocol to activate in_table */
! 328: void
! 329: channel_setup_in_table(struct channel *c)
! 330: {
! 331: struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
! 332: cf->name = "import";
! 333: cf->addr_type = c->net_type;
! 334:
! 335: c->in_table = mb_allocz(c->proto->pool, sizeof(struct rtable));
! 336: rt_setup(c->proto->pool, c->in_table, cf);
! 337:
! 338: c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c);
! 339: }
! 340:
! 341: /* Called by protocol to activate out_table */
! 342: void
! 343: channel_setup_out_table(struct channel *c)
! 344: {
! 345: struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
! 346: cf->name = "export";
! 347: cf->addr_type = c->net_type;
! 348:
! 349: c->out_table = mb_allocz(c->proto->pool, sizeof(struct rtable));
! 350: rt_setup(c->proto->pool, c->out_table, cf);
! 351: }
! 352:
! 353:
! 354: static void
! 355: channel_do_start(struct channel *c)
! 356: {
! 357: rt_lock_table(c->table);
! 358: add_tail(&c->table->channels, &c->table_node);
! 359: c->proto->active_channels++;
! 360:
! 361: c->feed_event = ev_new_init(c->proto->pool, channel_feed_loop, c);
! 362:
! 363: channel_reset_limit(&c->rx_limit);
! 364: channel_reset_limit(&c->in_limit);
! 365: channel_reset_limit(&c->out_limit);
! 366:
! 367: CALL(c->channel->start, c);
! 368: }
! 369:
! 370: static void
! 371: channel_do_flush(struct channel *c)
! 372: {
! 373: rt_schedule_prune(c->table);
! 374:
! 375: c->gr_wait = 0;
! 376: if (c->gr_lock)
! 377: channel_graceful_restart_unlock(c);
! 378:
! 379: CALL(c->channel->shutdown, c);
! 380: }
! 381:
! 382: static void
! 383: channel_do_down(struct channel *c)
! 384: {
! 385: ASSERT(!c->feed_active && !c->reload_active);
! 386:
! 387: rem_node(&c->table_node);
! 388: rt_unlock_table(c->table);
! 389: c->proto->active_channels--;
! 390:
! 391: if ((c->stats.imp_routes + c->stats.filt_routes) != 0)
! 392: log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name);
! 393:
! 394: memset(&c->stats, 0, sizeof(struct proto_stats));
! 395:
! 396: c->in_table = NULL;
! 397: c->reload_event = NULL;
! 398: c->out_table = NULL;
! 399:
! 400: CALL(c->channel->cleanup, c);
! 401:
! 402: /* Schedule protocol shutddown */
! 403: if (proto_is_done(c->proto))
! 404: ev_schedule(c->proto->event);
! 405: }
! 406:
! 407: void
! 408: channel_set_state(struct channel *c, uint state)
! 409: {
! 410: uint cs = c->channel_state;
! 411: uint es = c->export_state;
! 412:
! 413: DBG("%s reporting channel %s state transition %s -> %s\n", c->proto->name, c->name, c_states[cs], c_states[state]);
! 414: if (state == cs)
! 415: return;
! 416:
! 417: c->channel_state = state;
! 418: c->last_state_change = current_time();
! 419:
! 420: switch (state)
! 421: {
! 422: case CS_START:
! 423: ASSERT(cs == CS_DOWN || cs == CS_UP);
! 424:
! 425: if (cs == CS_DOWN)
! 426: channel_do_start(c);
! 427:
! 428: if (es != ES_DOWN)
! 429: channel_stop_export(c);
! 430:
! 431: if (c->in_table && (cs == CS_UP))
! 432: channel_reset_import(c);
! 433:
! 434: if (c->out_table && (cs == CS_UP))
! 435: channel_reset_export(c);
! 436:
! 437: break;
! 438:
! 439: case CS_UP:
! 440: ASSERT(cs == CS_DOWN || cs == CS_START);
! 441:
! 442: if (cs == CS_DOWN)
! 443: channel_do_start(c);
! 444:
! 445: if (!c->gr_wait && c->proto->rt_notify)
! 446: channel_start_export(c);
! 447:
! 448: break;
! 449:
! 450: case CS_FLUSHING:
! 451: ASSERT(cs == CS_START || cs == CS_UP);
! 452:
! 453: if (es != ES_DOWN)
! 454: channel_stop_export(c);
! 455:
! 456: if (c->in_table && (cs == CS_UP))
! 457: channel_reset_import(c);
! 458:
! 459: if (c->out_table && (cs == CS_UP))
! 460: channel_reset_export(c);
! 461:
! 462: channel_do_flush(c);
! 463: break;
! 464:
! 465: case CS_DOWN:
! 466: ASSERT(cs == CS_FLUSHING);
! 467:
! 468: channel_do_down(c);
! 469: break;
! 470:
! 471: default:
! 472: ASSERT(0);
! 473: }
! 474: // XXXX proto_log_state_change(c);
! 475: }
! 476:
! 477: /**
! 478: * channel_request_feeding - request feeding routes to the channel
! 479: * @c: given channel
! 480: *
! 481: * Sometimes it is needed to send again all routes to the channel. This is
! 482: * called feeding and can be requested by this function. This would cause
! 483: * channel export state transition to ES_FEEDING (during feeding) and when
! 484: * completed, it will switch back to ES_READY. This function can be called
! 485: * even when feeding is already running, in that case it is restarted.
! 486: */
! 487: void
! 488: channel_request_feeding(struct channel *c)
! 489: {
! 490: ASSERT(c->channel_state == CS_UP);
! 491:
! 492: /* Do nothing if we are still waiting for feeding */
! 493: if (c->export_state == ES_DOWN)
! 494: return;
! 495:
! 496: /* If we are already feeding, we want to restart it */
! 497: if (c->export_state == ES_FEEDING)
! 498: {
! 499: /* Unless feeding is in initial state */
! 500: if (!c->feed_active)
! 501: return;
! 502:
! 503: rt_feed_channel_abort(c);
! 504: }
! 505:
! 506: channel_reset_limit(&c->out_limit);
! 507:
! 508: /* Hack: reset exp_routes during refeed, and do not decrease it later */
! 509: c->stats.exp_routes = 0;
! 510:
! 511: channel_schedule_feed(c, 0); /* Sets ES_FEEDING */
! 512: // proto_log_state_change(c);
! 513: }
! 514:
! 515: static inline int
! 516: channel_reloadable(struct channel *c)
! 517: {
! 518: return c->proto->reload_routes && c->reloadable;
! 519: }
! 520:
! 521: static void
! 522: channel_request_reload(struct channel *c)
! 523: {
! 524: ASSERT(c->channel_state == CS_UP);
! 525: ASSERT(channel_reloadable(c));
! 526:
! 527: c->proto->reload_routes(c);
! 528:
! 529: /*
! 530: * Should this be done before reload_routes() hook?
! 531: * Perhaps, but routes are updated asynchronously.
! 532: */
! 533: channel_reset_limit(&c->rx_limit);
! 534: channel_reset_limit(&c->in_limit);
! 535: }
! 536:
! 537: const struct channel_class channel_basic = {
! 538: .channel_size = sizeof(struct channel),
! 539: .config_size = sizeof(struct channel_config)
! 540: };
! 541:
! 542: void *
! 543: channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto)
! 544: {
! 545: struct channel_config *cf = NULL;
! 546: struct rtable_config *tab = NULL;
! 547:
! 548: if (net_type)
! 549: {
! 550: if (!net_val_match(net_type, proto->protocol->channel_mask))
! 551: cf_error("Unsupported channel type");
! 552:
! 553: if (proto->net_type && (net_type != proto->net_type))
! 554: cf_error("Different channel type");
! 555:
! 556: tab = new_config->def_tables[net_type];
! 557: }
! 558:
! 559: if (!cc)
! 560: cc = &channel_basic;
! 561:
! 562: cf = cfg_allocz(cc->config_size);
! 563: cf->name = name;
! 564: cf->channel = cc;
! 565: cf->parent = proto;
! 566: cf->table = tab;
! 567: cf->out_filter = FILTER_REJECT;
! 568:
! 569: cf->net_type = net_type;
! 570: cf->ra_mode = RA_OPTIMAL;
! 571: cf->preference = proto->protocol->preference;
! 572:
! 573: add_tail(&proto->channels, &cf->n);
! 574:
! 575: return cf;
! 576: }
! 577:
! 578: void *
! 579: channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto)
! 580: {
! 581: struct channel_config *cf;
! 582:
! 583: /* We are using name as token, so no strcmp() */
! 584: WALK_LIST(cf, proto->channels)
! 585: if (cf->name == name)
! 586: {
! 587: /* Allow to redefine channel only if inherited from template */
! 588: if (cf->parent == proto)
! 589: cf_error("Multiple %s channels", name);
! 590:
! 591: cf->parent = proto;
! 592: return cf;
! 593: }
! 594:
! 595: return channel_config_new(cc, name, net_type, proto);
! 596: }
! 597:
! 598: struct channel_config *
! 599: channel_copy_config(struct channel_config *src, struct proto_config *proto)
! 600: {
! 601: struct channel_config *dst = cfg_alloc(src->channel->config_size);
! 602:
! 603: memcpy(dst, src, src->channel->config_size);
! 604: add_tail(&proto->channels, &dst->n);
! 605: CALL(src->channel->copy_config, dst, src);
! 606:
! 607: return dst;
! 608: }
! 609:
! 610:
! 611: static int reconfigure_type; /* Hack to propagate type info to channel_reconfigure() */
! 612:
! 613: int
! 614: channel_reconfigure(struct channel *c, struct channel_config *cf)
! 615: {
! 616: /* FIXME: better handle these changes, also handle in_keep_filtered */
! 617: if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode)))
! 618: return 0;
! 619:
! 620: /* Note that filter_same() requires arguments in (new, old) order */
! 621: int import_changed = !filter_same(cf->in_filter, c->in_filter);
! 622: int export_changed = !filter_same(cf->out_filter, c->out_filter);
! 623:
! 624: if (c->preference != cf->preference)
! 625: import_changed = 1;
! 626:
! 627: if (c->merge_limit != cf->merge_limit)
! 628: export_changed = 1;
! 629:
! 630: /* Reconfigure channel fields */
! 631: c->in_filter = cf->in_filter;
! 632: c->out_filter = cf->out_filter;
! 633: c->rx_limit = cf->rx_limit;
! 634: c->in_limit = cf->in_limit;
! 635: c->out_limit = cf->out_limit;
! 636:
! 637: // c->ra_mode = cf->ra_mode;
! 638: c->merge_limit = cf->merge_limit;
! 639: c->preference = cf->preference;
! 640: c->in_keep_filtered = cf->in_keep_filtered;
! 641:
! 642: channel_verify_limits(c);
! 643:
! 644: if (export_changed)
! 645: c->last_tx_filter_change = current_time();
! 646:
! 647: /* Execute channel-specific reconfigure hook */
! 648: if (c->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed))
! 649: return 0;
! 650:
! 651: /* If the channel is not open, it has no routes and we cannot reload it anyways */
! 652: if (c->channel_state != CS_UP)
! 653: return 1;
! 654:
! 655: if (reconfigure_type == RECONFIG_SOFT)
! 656: {
! 657: if (import_changed)
! 658: log(L_INFO "Channel %s.%s changed import", c->proto->name, c->name);
! 659:
! 660: if (export_changed)
! 661: log(L_INFO "Channel %s.%s changed export", c->proto->name, c->name);
! 662:
! 663: return 1;
! 664: }
! 665:
! 666: /* Route reload may be not supported */
! 667: if (import_changed && !channel_reloadable(c))
! 668: return 0;
! 669:
! 670: if (import_changed || export_changed)
! 671: log(L_INFO "Reloading channel %s.%s", c->proto->name, c->name);
! 672:
! 673: if (import_changed)
! 674: channel_request_reload(c);
! 675:
! 676: if (export_changed)
! 677: channel_request_feeding(c);
! 678:
! 679: return 1;
! 680: }
! 681:
! 682:
! 683: int
! 684: proto_configure_channel(struct proto *p, struct channel **pc, struct channel_config *cf)
! 685: {
! 686: struct channel *c = *pc;
! 687:
! 688: if (!c && cf)
! 689: {
! 690: /* We could add the channel, but currently it would just stay in down state
! 691: until protocol is restarted, so it is better to force restart anyways. */
! 692: if (p->proto_state != PS_DOWN)
! 693: {
! 694: log(L_INFO "Cannot add channel %s.%s", p->name, cf->name);
! 695: return 0;
! 696: }
! 697:
! 698: *pc = proto_add_channel(p, cf);
! 699: }
! 700: else if (c && !cf)
! 701: {
! 702: if (c->channel_state != CS_DOWN)
! 703: {
! 704: log(L_INFO "Cannot remove channel %s.%s", c->proto->name, c->name);
! 705: return 0;
! 706: }
! 707:
! 708: proto_remove_channel(p, c);
! 709: *pc = NULL;
! 710: }
! 711: else if (c && cf)
! 712: {
! 713: if (!channel_reconfigure(c, cf))
! 714: {
! 715: log(L_INFO "Cannot reconfigure channel %s.%s", c->proto->name, c->name);
! 716: return 0;
! 717: }
! 718: }
! 719:
! 720: return 1;
! 721: }
! 722:
! 723:
! 724: static void
! 725: proto_event(void *ptr)
! 726: {
! 727: struct proto *p = ptr;
! 728:
! 729: if (p->do_start)
! 730: {
! 731: if_feed_baby(p);
! 732: p->do_start = 0;
! 733: }
! 734:
! 735: if (p->do_stop)
! 736: {
! 737: if (p->proto == &proto_unix_iface)
! 738: if_flush_ifaces(p);
! 739: p->do_stop = 0;
! 740: }
! 741:
! 742: if (proto_is_done(p))
! 743: {
! 744: if (p->proto->cleanup)
! 745: p->proto->cleanup(p);
! 746:
! 747: p->active = 0;
! 748: proto_log_state_change(p);
! 749: proto_rethink_goal(p);
! 750: }
! 751: }
! 752:
! 753:
! 754: /**
! 755: * proto_new - create a new protocol instance
! 756: * @c: protocol configuration
! 757: *
! 758: * When a new configuration has been read in, the core code starts
! 759: * initializing all the protocol instances configured by calling their
! 760: * init() hooks with the corresponding instance configuration. The initialization
! 761: * code of the protocol is expected to create a new instance according to the
! 762: * configuration by calling this function and then modifying the default settings
! 763: * to values wanted by the protocol.
! 764: */
! 765: void *
! 766: proto_new(struct proto_config *cf)
! 767: {
! 768: struct proto *p = mb_allocz(proto_pool, cf->protocol->proto_size);
! 769:
! 770: p->cf = cf;
! 771: p->debug = cf->debug;
! 772: p->mrtdump = cf->mrtdump;
! 773: p->name = cf->name;
! 774: p->proto = cf->protocol;
! 775: p->net_type = cf->net_type;
! 776: p->disabled = cf->disabled;
! 777: p->hash_key = random_u32();
! 778: cf->proto = p;
! 779:
! 780: init_list(&p->channels);
! 781:
! 782: return p;
! 783: }
! 784:
! 785: static struct proto *
! 786: proto_init(struct proto_config *c, node *n)
! 787: {
! 788: struct protocol *pr = c->protocol;
! 789: struct proto *p = pr->init(c);
! 790:
! 791: p->proto_state = PS_DOWN;
! 792: p->last_state_change = current_time();
! 793: p->vrf = c->vrf;
! 794: p->vrf_set = c->vrf_set;
! 795: insert_node(&p->n, n);
! 796:
! 797: p->event = ev_new_init(proto_pool, proto_event, p);
! 798:
! 799: PD(p, "Initializing%s", p->disabled ? " [disabled]" : "");
! 800:
! 801: return p;
! 802: }
! 803:
! 804: static void
! 805: proto_start(struct proto *p)
! 806: {
! 807: /* Here we cannot use p->cf->name since it won't survive reconfiguration */
! 808: p->pool = rp_new(proto_pool, p->proto->name);
! 809:
! 810: if (graceful_restart_state == GRS_INIT)
! 811: p->gr_recovery = 1;
! 812: }
! 813:
! 814:
! 815: /**
! 816: * proto_config_new - create a new protocol configuration
! 817: * @pr: protocol the configuration will belong to
! 818: * @class: SYM_PROTO or SYM_TEMPLATE
! 819: *
! 820: * Whenever the configuration file says that a new instance
! 821: * of a routing protocol should be created, the parser calls
! 822: * proto_config_new() to create a configuration entry for this
! 823: * instance (a structure staring with the &proto_config header
! 824: * containing all the generic items followed by protocol-specific
! 825: * ones). Also, the configuration entry gets added to the list
! 826: * of protocol instances kept in the configuration.
! 827: *
! 828: * The function is also used to create protocol templates (when class
! 829: * SYM_TEMPLATE is specified), the only difference is that templates
! 830: * are not added to the list of protocol instances and therefore not
! 831: * initialized during protos_commit()).
! 832: */
! 833: void *
! 834: proto_config_new(struct protocol *pr, int class)
! 835: {
! 836: struct proto_config *cf = cfg_allocz(pr->config_size);
! 837:
! 838: if (class == SYM_PROTO)
! 839: add_tail(&new_config->protos, &cf->n);
! 840:
! 841: cf->global = new_config;
! 842: cf->protocol = pr;
! 843: cf->name = pr->name;
! 844: cf->class = class;
! 845: cf->debug = new_config->proto_default_debug;
! 846: cf->mrtdump = new_config->proto_default_mrtdump;
! 847:
! 848: init_list(&cf->channels);
! 849:
! 850: return cf;
! 851: }
! 852:
! 853:
! 854: /**
! 855: * proto_copy_config - copy a protocol configuration
! 856: * @dest: destination protocol configuration
! 857: * @src: source protocol configuration
! 858: *
! 859: * Whenever a new instance of a routing protocol is created from the
! 860: * template, proto_copy_config() is called to copy a content of
! 861: * the source protocol configuration to the new protocol configuration.
! 862: * Name, class and a node in protos list of @dest are kept intact.
! 863: * copy_config() protocol hook is used to copy protocol-specific data.
! 864: */
! 865: void
! 866: proto_copy_config(struct proto_config *dest, struct proto_config *src)
! 867: {
! 868: struct channel_config *cc;
! 869: node old_node;
! 870: int old_class;
! 871: char *old_name;
! 872:
! 873: if (dest->protocol != src->protocol)
! 874: cf_error("Can't copy configuration from a different protocol type");
! 875:
! 876: if (dest->protocol->copy_config == NULL)
! 877: cf_error("Inheriting configuration for %s is not supported", src->protocol->name);
! 878:
! 879: DBG("Copying configuration from %s to %s\n", src->name, dest->name);
! 880:
! 881: /*
! 882: * Copy struct proto_config here. Keep original node, class and name.
! 883: * protocol-specific config copy is handled by protocol copy_config() hook
! 884: */
! 885:
! 886: old_node = dest->n;
! 887: old_class = dest->class;
! 888: old_name = dest->name;
! 889:
! 890: memcpy(dest, src, src->protocol->config_size);
! 891:
! 892: dest->n = old_node;
! 893: dest->class = old_class;
! 894: dest->name = old_name;
! 895: init_list(&dest->channels);
! 896:
! 897: WALK_LIST(cc, src->channels)
! 898: channel_copy_config(cc, dest);
! 899:
! 900: /* FIXME: allow for undefined copy_config */
! 901: dest->protocol->copy_config(dest, src);
! 902: }
! 903:
! 904: void
! 905: proto_clone_config(struct symbol *sym, struct proto_config *parent)
! 906: {
! 907: struct proto_config *cf = proto_config_new(parent->protocol, SYM_PROTO);
! 908: proto_copy_config(cf, parent);
! 909: cf->name = sym->name;
! 910: cf->proto = NULL;
! 911: cf->parent = parent;
! 912:
! 913: sym->class = cf->class;
! 914: sym->proto = cf;
! 915: }
! 916:
! 917: static void
! 918: proto_undef_clone(struct symbol *sym, struct proto_config *cf)
! 919: {
! 920: rem_node(&cf->n);
! 921:
! 922: sym->class = SYM_VOID;
! 923: sym->proto = NULL;
! 924: }
! 925:
! 926: /**
! 927: * protos_preconfig - pre-configuration processing
! 928: * @c: new configuration
! 929: *
! 930: * This function calls the preconfig() hooks of all routing
! 931: * protocols available to prepare them for reading of the new
! 932: * configuration.
! 933: */
! 934: void
! 935: protos_preconfig(struct config *c)
! 936: {
! 937: struct protocol *p;
! 938:
! 939: init_list(&c->protos);
! 940: DBG("Protocol preconfig:");
! 941: WALK_LIST(p, protocol_list)
! 942: {
! 943: DBG(" %s", p->name);
! 944: p->name_counter = 0;
! 945: if (p->preconfig)
! 946: p->preconfig(p, c);
! 947: }
! 948: DBG("\n");
! 949: }
! 950:
! 951: static int
! 952: proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type)
! 953: {
! 954: /* If the protocol is DOWN, we just restart it */
! 955: if (p->proto_state == PS_DOWN)
! 956: return 0;
! 957:
! 958: /* If there is a too big change in core attributes, ... */
! 959: if ((nc->protocol != oc->protocol) ||
! 960: (nc->net_type != oc->net_type) ||
! 961: (nc->disabled != p->disabled) ||
! 962: (nc->vrf != oc->vrf) ||
! 963: (nc->vrf_set != oc->vrf_set))
! 964: return 0;
! 965:
! 966: p->name = nc->name;
! 967: p->debug = nc->debug;
! 968: p->mrtdump = nc->mrtdump;
! 969: reconfigure_type = type;
! 970:
! 971: /* Execute protocol specific reconfigure hook */
! 972: if (!p->proto->reconfigure || !p->proto->reconfigure(p, nc))
! 973: return 0;
! 974:
! 975: DBG("\t%s: same\n", oc->name);
! 976: PD(p, "Reconfigured");
! 977: p->cf = nc;
! 978:
! 979: return 1;
! 980: }
! 981:
! 982: /**
! 983: * protos_commit - commit new protocol configuration
! 984: * @new: new configuration
! 985: * @old: old configuration or %NULL if it's boot time config
! 986: * @force_reconfig: force restart of all protocols (used for example
! 987: * when the router ID changes)
! 988: * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
! 989: *
! 990: * Scan differences between @old and @new configuration and adjust all
! 991: * protocol instances to conform to the new configuration.
! 992: *
! 993: * When a protocol exists in the new configuration, but it doesn't in the
! 994: * original one, it's immediately started. When a collision with the other
! 995: * running protocol would arise, the new protocol will be temporarily stopped
! 996: * by the locking mechanism.
! 997: *
! 998: * When a protocol exists in the old configuration, but it doesn't in the
! 999: * new one, it's shut down and deleted after the shutdown completes.
! 1000: *
! 1001: * When a protocol exists in both configurations, the core decides
! 1002: * whether it's possible to reconfigure it dynamically - it checks all
! 1003: * the core properties of the protocol (changes in filters are ignored
! 1004: * if type is RECONFIG_SOFT) and if they match, it asks the
! 1005: * reconfigure() hook of the protocol to see if the protocol is able
! 1006: * to switch to the new configuration. If it isn't possible, the
! 1007: * protocol is shut down and a new instance is started with the new
! 1008: * configuration after the shutdown is completed.
! 1009: */
! 1010: void
! 1011: protos_commit(struct config *new, struct config *old, int force_reconfig, int type)
! 1012: {
! 1013: struct proto_config *oc, *nc;
! 1014: struct symbol *sym;
! 1015: struct proto *p;
! 1016: node *n;
! 1017:
! 1018:
! 1019: DBG("protos_commit:\n");
! 1020: if (old)
! 1021: {
! 1022: WALK_LIST(oc, old->protos)
! 1023: {
! 1024: p = oc->proto;
! 1025: sym = cf_find_symbol(new, oc->name);
! 1026:
! 1027: /* Handle dynamic protocols */
! 1028: if (!sym && oc->parent && !new->shutdown)
! 1029: {
! 1030: struct symbol *parsym = cf_find_symbol(new, oc->parent->name);
! 1031: if (parsym && parsym->class == SYM_PROTO)
! 1032: {
! 1033: /* This is hack, we would like to share config, but we need to copy it now */
! 1034: new_config = new;
! 1035: cfg_mem = new->mem;
! 1036: conf_this_scope = new->root_scope;
! 1037: sym = cf_get_symbol(oc->name);
! 1038: proto_clone_config(sym, parsym->proto);
! 1039: new_config = NULL;
! 1040: cfg_mem = NULL;
! 1041: }
! 1042: }
! 1043:
! 1044: if (sym && sym->class == SYM_PROTO && !new->shutdown)
! 1045: {
! 1046: /* Found match, let's check if we can smoothly switch to new configuration */
! 1047: /* No need to check description */
! 1048: nc = sym->proto;
! 1049: nc->proto = p;
! 1050:
! 1051: /* We will try to reconfigure protocol p */
! 1052: if (! force_reconfig && proto_reconfigure(p, oc, nc, type))
! 1053: continue;
! 1054:
! 1055: if (nc->parent)
! 1056: {
! 1057: proto_undef_clone(sym, nc);
! 1058: goto remove;
! 1059: }
! 1060:
! 1061: /* Unsuccessful, we will restart it */
! 1062: if (!p->disabled && !nc->disabled)
! 1063: log(L_INFO "Restarting protocol %s", p->name);
! 1064: else if (p->disabled && !nc->disabled)
! 1065: log(L_INFO "Enabling protocol %s", p->name);
! 1066: else if (!p->disabled && nc->disabled)
! 1067: log(L_INFO "Disabling protocol %s", p->name);
! 1068:
! 1069: p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
! 1070: p->cf_new = nc;
! 1071: }
! 1072: else if (!new->shutdown)
! 1073: {
! 1074: remove:
! 1075: log(L_INFO "Removing protocol %s", p->name);
! 1076: p->down_code = PDC_CF_REMOVE;
! 1077: p->cf_new = NULL;
! 1078: }
! 1079: else if (new->gr_down)
! 1080: {
! 1081: p->down_code = PDC_CMD_GR_DOWN;
! 1082: p->cf_new = NULL;
! 1083: }
! 1084: else /* global shutdown */
! 1085: {
! 1086: p->down_code = PDC_CMD_SHUTDOWN;
! 1087: p->cf_new = NULL;
! 1088: }
! 1089:
! 1090: p->reconfiguring = 1;
! 1091: config_add_obstacle(old);
! 1092: proto_rethink_goal(p);
! 1093: }
! 1094: }
! 1095:
! 1096: struct proto *first_dev_proto = NULL;
! 1097:
! 1098: n = NODE &(proto_list.head);
! 1099: WALK_LIST(nc, new->protos)
! 1100: if (!nc->proto)
! 1101: {
! 1102: /* Not a first-time configuration */
! 1103: if (old)
! 1104: log(L_INFO "Adding protocol %s", nc->name);
! 1105:
! 1106: p = proto_init(nc, n);
! 1107: n = NODE p;
! 1108:
! 1109: if (p->proto == &proto_unix_iface)
! 1110: first_dev_proto = p;
! 1111: }
! 1112: else
! 1113: n = NODE nc->proto;
! 1114:
! 1115: DBG("Protocol start\n");
! 1116:
! 1117: /* Start device protocol first */
! 1118: if (first_dev_proto)
! 1119: proto_rethink_goal(first_dev_proto);
! 1120:
! 1121: /* Determine router ID for the first time - it has to be here and not in
! 1122: global_commit() because it is postponed after start of device protocol */
! 1123: if (!config->router_id)
! 1124: {
! 1125: config->router_id = if_choose_router_id(config->router_id_from, 0);
! 1126: if (!config->router_id)
! 1127: die("Cannot determine router ID, please configure it manually");
! 1128: }
! 1129:
! 1130: /* Start all new protocols */
! 1131: WALK_LIST_DELSAFE(p, n, proto_list)
! 1132: proto_rethink_goal(p);
! 1133: }
! 1134:
! 1135: static void
! 1136: proto_rethink_goal(struct proto *p)
! 1137: {
! 1138: struct protocol *q;
! 1139: byte goal;
! 1140:
! 1141: if (p->reconfiguring && !p->active)
! 1142: {
! 1143: struct proto_config *nc = p->cf_new;
! 1144: node *n = p->n.prev;
! 1145: DBG("%s has shut down for reconfiguration\n", p->name);
! 1146: p->cf->proto = NULL;
! 1147: config_del_obstacle(p->cf->global);
! 1148: proto_remove_channels(p);
! 1149: rem_node(&p->n);
! 1150: rfree(p->event);
! 1151: mb_free(p->message);
! 1152: mb_free(p);
! 1153: if (!nc)
! 1154: return;
! 1155: p = proto_init(nc, n);
! 1156: }
! 1157:
! 1158: /* Determine what state we want to reach */
! 1159: if (p->disabled || p->reconfiguring)
! 1160: goal = PS_DOWN;
! 1161: else
! 1162: goal = PS_UP;
! 1163:
! 1164: q = p->proto;
! 1165: if (goal == PS_UP)
! 1166: {
! 1167: if (!p->active)
! 1168: {
! 1169: /* Going up */
! 1170: DBG("Kicking %s up\n", p->name);
! 1171: PD(p, "Starting");
! 1172: proto_start(p);
! 1173: proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
! 1174: }
! 1175: }
! 1176: else
! 1177: {
! 1178: if (p->proto_state == PS_START || p->proto_state == PS_UP)
! 1179: {
! 1180: /* Going down */
! 1181: DBG("Kicking %s down\n", p->name);
! 1182: PD(p, "Shutting down");
! 1183: proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
! 1184: }
! 1185: }
! 1186: }
! 1187:
! 1188: struct proto *
! 1189: proto_spawn(struct proto_config *cf, uint disabled)
! 1190: {
! 1191: struct proto *p = proto_init(cf, TAIL(proto_list));
! 1192: p->disabled = disabled;
! 1193: proto_rethink_goal(p);
! 1194: return p;
! 1195: }
! 1196:
! 1197:
! 1198: /**
! 1199: * DOC: Graceful restart recovery
! 1200: *
! 1201: * Graceful restart of a router is a process when the routing plane (e.g. BIRD)
! 1202: * restarts but both the forwarding plane (e.g kernel routing table) and routing
! 1203: * neighbors keep proper routes, and therefore uninterrupted packet forwarding
! 1204: * is maintained.
! 1205: *
! 1206: * BIRD implements graceful restart recovery by deferring export of routes to
! 1207: * protocols until routing tables are refilled with the expected content. After
! 1208: * start, protocols generate routes as usual, but routes are not propagated to
! 1209: * them, until protocols report that they generated all routes. After that,
! 1210: * graceful restart recovery is finished and the export (and the initial feed)
! 1211: * to protocols is enabled.
! 1212: *
! 1213: * When graceful restart recovery need is detected during initialization, then
! 1214: * enabled protocols are marked with @gr_recovery flag before start. Such
! 1215: * protocols then decide how to proceed with graceful restart, participation is
! 1216: * voluntary. Protocols could lock the recovery for each channel by function
! 1217: * channel_graceful_restart_lock() (state stored in @gr_lock flag), which means
! 1218: * that they want to postpone the end of the recovery until they converge and
! 1219: * then unlock it. They also could set @gr_wait before advancing to %PS_UP,
! 1220: * which means that the core should defer route export to that channel until
! 1221: * the end of the recovery. This should be done by protocols that expect their
! 1222: * neigbors to keep the proper routes (kernel table, BGP sessions with BGP
! 1223: * graceful restart capability).
! 1224: *
! 1225: * The graceful restart recovery is finished when either all graceful restart
! 1226: * locks are unlocked or when graceful restart wait timer fires.
! 1227: *
! 1228: */
! 1229:
! 1230: static void graceful_restart_done(timer *t);
! 1231:
! 1232: /**
! 1233: * graceful_restart_recovery - request initial graceful restart recovery
! 1234: *
! 1235: * Called by the platform initialization code if the need for recovery
! 1236: * after graceful restart is detected during boot. Have to be called
! 1237: * before protos_commit().
! 1238: */
! 1239: void
! 1240: graceful_restart_recovery(void)
! 1241: {
! 1242: graceful_restart_state = GRS_INIT;
! 1243: }
! 1244:
! 1245: /**
! 1246: * graceful_restart_init - initialize graceful restart
! 1247: *
! 1248: * When graceful restart recovery was requested, the function starts an active
! 1249: * phase of the recovery and initializes graceful restart wait timer. The
! 1250: * function have to be called after protos_commit().
! 1251: */
! 1252: void
! 1253: graceful_restart_init(void)
! 1254: {
! 1255: if (!graceful_restart_state)
! 1256: return;
! 1257:
! 1258: log(L_INFO "Graceful restart started");
! 1259:
! 1260: if (!graceful_restart_locks)
! 1261: {
! 1262: graceful_restart_done(NULL);
! 1263: return;
! 1264: }
! 1265:
! 1266: graceful_restart_state = GRS_ACTIVE;
! 1267: gr_wait_timer = tm_new_init(proto_pool, graceful_restart_done, NULL, 0, 0);
! 1268: tm_start(gr_wait_timer, config->gr_wait S);
! 1269: }
! 1270:
! 1271: /**
! 1272: * graceful_restart_done - finalize graceful restart
! 1273: * @t: unused
! 1274: *
! 1275: * When there are no locks on graceful restart, the functions finalizes the
! 1276: * graceful restart recovery. Protocols postponing route export until the end of
! 1277: * the recovery are awakened and the export to them is enabled. All other
! 1278: * related state is cleared. The function is also called when the graceful
! 1279: * restart wait timer fires (but there are still some locks).
! 1280: */
! 1281: static void
! 1282: graceful_restart_done(timer *t UNUSED)
! 1283: {
! 1284: log(L_INFO "Graceful restart done");
! 1285: graceful_restart_state = GRS_DONE;
! 1286:
! 1287: struct proto *p;
! 1288: WALK_LIST(p, proto_list)
! 1289: {
! 1290: if (!p->gr_recovery)
! 1291: continue;
! 1292:
! 1293: struct channel *c;
! 1294: WALK_LIST(c, p->channels)
! 1295: {
! 1296: /* Resume postponed export of routes */
! 1297: if ((c->channel_state == CS_UP) && c->gr_wait && c->proto->rt_notify)
! 1298: channel_start_export(c);
! 1299:
! 1300: /* Cleanup */
! 1301: c->gr_wait = 0;
! 1302: c->gr_lock = 0;
! 1303: }
! 1304:
! 1305: p->gr_recovery = 0;
! 1306: }
! 1307:
! 1308: graceful_restart_locks = 0;
! 1309: }
! 1310:
! 1311: void
! 1312: graceful_restart_show_status(void)
! 1313: {
! 1314: if (graceful_restart_state != GRS_ACTIVE)
! 1315: return;
! 1316:
! 1317: cli_msg(-24, "Graceful restart recovery in progress");
! 1318: cli_msg(-24, " Waiting for %d channels to recover", graceful_restart_locks);
! 1319: cli_msg(-24, " Wait timer is %t/%u", tm_remains(gr_wait_timer), config->gr_wait);
! 1320: }
! 1321:
! 1322: /**
! 1323: * channel_graceful_restart_lock - lock graceful restart by channel
! 1324: * @p: channel instance
! 1325: *
! 1326: * This function allows a protocol to postpone the end of graceful restart
! 1327: * recovery until it converges. The lock is removed when the protocol calls
! 1328: * channel_graceful_restart_unlock() or when the channel is closed.
! 1329: *
! 1330: * The function have to be called during the initial phase of graceful restart
! 1331: * recovery and only for protocols that are part of graceful restart (i.e. their
! 1332: * @gr_recovery is set), which means it should be called from protocol start
! 1333: * hooks.
! 1334: */
! 1335: void
! 1336: channel_graceful_restart_lock(struct channel *c)
! 1337: {
! 1338: ASSERT(graceful_restart_state == GRS_INIT);
! 1339: ASSERT(c->proto->gr_recovery);
! 1340:
! 1341: if (c->gr_lock)
! 1342: return;
! 1343:
! 1344: c->gr_lock = 1;
! 1345: graceful_restart_locks++;
! 1346: }
! 1347:
! 1348: /**
! 1349: * channel_graceful_restart_unlock - unlock graceful restart by channel
! 1350: * @p: channel instance
! 1351: *
! 1352: * This function unlocks a lock from channel_graceful_restart_lock(). It is also
! 1353: * automatically called when the lock holding protocol went down.
! 1354: */
! 1355: void
! 1356: channel_graceful_restart_unlock(struct channel *c)
! 1357: {
! 1358: if (!c->gr_lock)
! 1359: return;
! 1360:
! 1361: c->gr_lock = 0;
! 1362: graceful_restart_locks--;
! 1363:
! 1364: if ((graceful_restart_state == GRS_ACTIVE) && !graceful_restart_locks)
! 1365: tm_start(gr_wait_timer, 0);
! 1366: }
! 1367:
! 1368:
! 1369:
! 1370: /**
! 1371: * protos_dump_all - dump status of all protocols
! 1372: *
! 1373: * This function dumps status of all existing protocol instances to the
! 1374: * debug output. It involves printing of general status information
! 1375: * such as protocol states, its position on the protocol lists
! 1376: * and also calling of a dump() hook of the protocol to print
! 1377: * the internals.
! 1378: */
! 1379: void
! 1380: protos_dump_all(void)
! 1381: {
! 1382: debug("Protocols:\n");
! 1383:
! 1384: struct proto *p;
! 1385: WALK_LIST(p, proto_list)
! 1386: {
! 1387: debug(" protocol %s state %s\n", p->name, p_states[p->proto_state]);
! 1388:
! 1389: struct channel *c;
! 1390: WALK_LIST(c, p->channels)
! 1391: {
! 1392: debug("\tTABLE %s\n", c->table->name);
! 1393: if (c->in_filter)
! 1394: debug("\tInput filter: %s\n", filter_name(c->in_filter));
! 1395: if (c->out_filter)
! 1396: debug("\tOutput filter: %s\n", filter_name(c->out_filter));
! 1397: }
! 1398:
! 1399: if (p->proto->dump && (p->proto_state != PS_DOWN))
! 1400: p->proto->dump(p);
! 1401: }
! 1402: }
! 1403:
! 1404: /**
! 1405: * proto_build - make a single protocol available
! 1406: * @p: the protocol
! 1407: *
! 1408: * After the platform specific initialization code uses protos_build()
! 1409: * to add all the standard protocols, it should call proto_build() for
! 1410: * all platform specific protocols to inform the core that they exist.
! 1411: */
! 1412: void
! 1413: proto_build(struct protocol *p)
! 1414: {
! 1415: add_tail(&protocol_list, &p->n);
! 1416: ASSERT(p->class);
! 1417: ASSERT(!class_to_protocol[p->class]);
! 1418: class_to_protocol[p->class] = p;
! 1419: }
! 1420:
! 1421: /* FIXME: convert this call to some protocol hook */
! 1422: extern void bfd_init_all(void);
! 1423:
! 1424: /**
! 1425: * protos_build - build a protocol list
! 1426: *
! 1427: * This function is called during BIRD startup to insert
! 1428: * all standard protocols to the global protocol list. Insertion
! 1429: * of platform specific protocols (such as the kernel syncer)
! 1430: * is in the domain of competence of the platform dependent
! 1431: * startup code.
! 1432: */
! 1433: void
! 1434: protos_build(void)
! 1435: {
! 1436: init_list(&proto_list);
! 1437: init_list(&protocol_list);
! 1438:
! 1439: proto_build(&proto_device);
! 1440: #ifdef CONFIG_RADV
! 1441: proto_build(&proto_radv);
! 1442: #endif
! 1443: #ifdef CONFIG_RIP
! 1444: proto_build(&proto_rip);
! 1445: #endif
! 1446: #ifdef CONFIG_STATIC
! 1447: proto_build(&proto_static);
! 1448: #endif
! 1449: #ifdef CONFIG_MRT
! 1450: proto_build(&proto_mrt);
! 1451: #endif
! 1452: #ifdef CONFIG_OSPF
! 1453: proto_build(&proto_ospf);
! 1454: #endif
! 1455: #ifdef CONFIG_PIPE
! 1456: proto_build(&proto_pipe);
! 1457: #endif
! 1458: #ifdef CONFIG_BGP
! 1459: proto_build(&proto_bgp);
! 1460: #endif
! 1461: #ifdef CONFIG_BFD
! 1462: proto_build(&proto_bfd);
! 1463: bfd_init_all();
! 1464: #endif
! 1465: #ifdef CONFIG_BABEL
! 1466: proto_build(&proto_babel);
! 1467: #endif
! 1468: #ifdef CONFIG_RPKI
! 1469: proto_build(&proto_rpki);
! 1470: #endif
! 1471: #ifdef CONFIG_PERF
! 1472: proto_build(&proto_perf);
! 1473: #endif
! 1474:
! 1475: proto_pool = rp_new(&root_pool, "Protocols");
! 1476: proto_shutdown_timer = tm_new(proto_pool);
! 1477: proto_shutdown_timer->hook = proto_shutdown_loop;
! 1478: }
! 1479:
! 1480:
! 1481: /* Temporary hack to propagate restart to BGP */
! 1482: int proto_restart;
! 1483:
! 1484: static void
! 1485: proto_shutdown_loop(timer *t UNUSED)
! 1486: {
! 1487: struct proto *p, *p_next;
! 1488:
! 1489: WALK_LIST_DELSAFE(p, p_next, proto_list)
! 1490: if (p->down_sched)
! 1491: {
! 1492: proto_restart = (p->down_sched == PDS_RESTART);
! 1493:
! 1494: p->disabled = 1;
! 1495: proto_rethink_goal(p);
! 1496: if (proto_restart)
! 1497: {
! 1498: p->disabled = 0;
! 1499: proto_rethink_goal(p);
! 1500: }
! 1501: }
! 1502: }
! 1503:
! 1504: static inline void
! 1505: proto_schedule_down(struct proto *p, byte restart, byte code)
! 1506: {
! 1507: /* Does not work for other states (even PS_START) */
! 1508: ASSERT(p->proto_state == PS_UP);
! 1509:
! 1510: /* Scheduled restart may change to shutdown, but not otherwise */
! 1511: if (p->down_sched == PDS_DISABLE)
! 1512: return;
! 1513:
! 1514: p->down_sched = restart ? PDS_RESTART : PDS_DISABLE;
! 1515: p->down_code = code;
! 1516: tm_start_max(proto_shutdown_timer, restart ? 250 MS : 0);
! 1517: }
! 1518:
! 1519: /**
! 1520: * proto_set_message - set administrative message to protocol
! 1521: * @p: protocol
! 1522: * @msg: message
! 1523: * @len: message length (-1 for NULL-terminated string)
! 1524: *
! 1525: * The function sets administrative message (string) related to protocol state
! 1526: * change. It is called by the nest code for manual enable/disable/restart
! 1527: * commands all routes to the protocol, and by protocol-specific code when the
! 1528: * protocol state change is initiated by the protocol. Using NULL message clears
! 1529: * the last message. The message string may be either NULL-terminated or with an
! 1530: * explicit length.
! 1531: */
! 1532: void
! 1533: proto_set_message(struct proto *p, char *msg, int len)
! 1534: {
! 1535: mb_free(p->message);
! 1536: p->message = NULL;
! 1537:
! 1538: if (!msg || !len)
! 1539: return;
! 1540:
! 1541: if (len < 0)
! 1542: len = strlen(msg);
! 1543:
! 1544: if (!len)
! 1545: return;
! 1546:
! 1547: p->message = mb_alloc(proto_pool, len + 1);
! 1548: memcpy(p->message, msg, len);
! 1549: p->message[len] = 0;
! 1550: }
! 1551:
! 1552:
! 1553: static const char *
! 1554: channel_limit_name(struct channel_limit *l)
! 1555: {
! 1556: const char *actions[] = {
! 1557: [PLA_WARN] = "warn",
! 1558: [PLA_BLOCK] = "block",
! 1559: [PLA_RESTART] = "restart",
! 1560: [PLA_DISABLE] = "disable",
! 1561: };
! 1562:
! 1563: return actions[l->action];
! 1564: }
! 1565:
! 1566: /**
! 1567: * channel_notify_limit: notify about limit hit and take appropriate action
! 1568: * @c: channel
! 1569: * @l: limit being hit
! 1570: * @dir: limit direction (PLD_*)
! 1571: * @rt_count: the number of routes
! 1572: *
! 1573: * The function is called by the route processing core when limit @l
! 1574: * is breached. It activates the limit and tooks appropriate action
! 1575: * according to @l->action.
! 1576: */
! 1577: void
! 1578: channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count)
! 1579: {
! 1580: const char *dir_name[PLD_MAX] = { "receive", "import" , "export" };
! 1581: const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
! 1582: struct proto *p = c->proto;
! 1583:
! 1584: if (l->state == PLS_BLOCKED)
! 1585: return;
! 1586:
! 1587: /* For warning action, we want the log message every time we hit the limit */
! 1588: if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit)))
! 1589: log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
! 1590: p->name, dir_name[dir], l->limit, channel_limit_name(l));
! 1591:
! 1592: switch (l->action)
! 1593: {
! 1594: case PLA_WARN:
! 1595: l->state = PLS_ACTIVE;
! 1596: break;
! 1597:
! 1598: case PLA_BLOCK:
! 1599: l->state = PLS_BLOCKED;
! 1600: break;
! 1601:
! 1602: case PLA_RESTART:
! 1603: case PLA_DISABLE:
! 1604: l->state = PLS_BLOCKED;
! 1605: if (p->proto_state == PS_UP)
! 1606: proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
! 1607: break;
! 1608: }
! 1609: }
! 1610:
! 1611: static void
! 1612: channel_verify_limits(struct channel *c)
! 1613: {
! 1614: struct channel_limit *l;
! 1615: u32 all_routes = c->stats.imp_routes + c->stats.filt_routes;
! 1616:
! 1617: l = &c->rx_limit;
! 1618: if (l->action && (all_routes > l->limit))
! 1619: channel_notify_limit(c, l, PLD_RX, all_routes);
! 1620:
! 1621: l = &c->in_limit;
! 1622: if (l->action && (c->stats.imp_routes > l->limit))
! 1623: channel_notify_limit(c, l, PLD_IN, c->stats.imp_routes);
! 1624:
! 1625: l = &c->out_limit;
! 1626: if (l->action && (c->stats.exp_routes > l->limit))
! 1627: channel_notify_limit(c, l, PLD_OUT, c->stats.exp_routes);
! 1628: }
! 1629:
! 1630: static inline void
! 1631: channel_reset_limit(struct channel_limit *l)
! 1632: {
! 1633: if (l->action)
! 1634: l->state = PLS_INITIAL;
! 1635: }
! 1636:
! 1637: static inline void
! 1638: proto_do_start(struct proto *p)
! 1639: {
! 1640: p->active = 1;
! 1641: p->do_start = 1;
! 1642: ev_schedule(p->event);
! 1643: }
! 1644:
! 1645: static void
! 1646: proto_do_up(struct proto *p)
! 1647: {
! 1648: if (!p->main_source)
! 1649: {
! 1650: p->main_source = rt_get_source(p, 0);
! 1651: rt_lock_source(p->main_source);
! 1652: }
! 1653:
! 1654: proto_start_channels(p);
! 1655: }
! 1656:
! 1657: static inline void
! 1658: proto_do_pause(struct proto *p)
! 1659: {
! 1660: proto_pause_channels(p);
! 1661: }
! 1662:
! 1663: static void
! 1664: proto_do_stop(struct proto *p)
! 1665: {
! 1666: p->down_sched = 0;
! 1667: p->gr_recovery = 0;
! 1668:
! 1669: p->do_stop = 1;
! 1670: ev_schedule(p->event);
! 1671:
! 1672: if (p->main_source)
! 1673: {
! 1674: rt_unlock_source(p->main_source);
! 1675: p->main_source = NULL;
! 1676: }
! 1677:
! 1678: proto_stop_channels(p);
! 1679: }
! 1680:
! 1681: static void
! 1682: proto_do_down(struct proto *p)
! 1683: {
! 1684: p->down_code = 0;
! 1685: neigh_prune();
! 1686: rfree(p->pool);
! 1687: p->pool = NULL;
! 1688:
! 1689: /* Shutdown is finished in the protocol event */
! 1690: if (proto_is_done(p))
! 1691: ev_schedule(p->event);
! 1692: }
! 1693:
! 1694:
! 1695:
! 1696: /**
! 1697: * proto_notify_state - notify core about protocol state change
! 1698: * @p: protocol the state of which has changed
! 1699: * @ps: the new status
! 1700: *
! 1701: * Whenever a state of a protocol changes due to some event internal
! 1702: * to the protocol (i.e., not inside a start() or shutdown() hook),
! 1703: * it should immediately notify the core about the change by calling
! 1704: * proto_notify_state() which will write the new state to the &proto
! 1705: * structure and take all the actions necessary to adapt to the new
! 1706: * state. State change to PS_DOWN immediately frees resources of protocol
! 1707: * and might execute start callback of protocol; therefore,
! 1708: * it should be used at tail positions of protocol callbacks.
! 1709: */
! 1710: void
! 1711: proto_notify_state(struct proto *p, uint state)
! 1712: {
! 1713: uint ps = p->proto_state;
! 1714:
! 1715: DBG("%s reporting state transition %s -> %s\n", p->name, p_states[ps], p_states[state]);
! 1716: if (state == ps)
! 1717: return;
! 1718:
! 1719: p->proto_state = state;
! 1720: p->last_state_change = current_time();
! 1721:
! 1722: switch (state)
! 1723: {
! 1724: case PS_START:
! 1725: ASSERT(ps == PS_DOWN || ps == PS_UP);
! 1726:
! 1727: if (ps == PS_DOWN)
! 1728: proto_do_start(p);
! 1729: else
! 1730: proto_do_pause(p);
! 1731: break;
! 1732:
! 1733: case PS_UP:
! 1734: ASSERT(ps == PS_DOWN || ps == PS_START);
! 1735:
! 1736: if (ps == PS_DOWN)
! 1737: proto_do_start(p);
! 1738:
! 1739: proto_do_up(p);
! 1740: break;
! 1741:
! 1742: case PS_STOP:
! 1743: ASSERT(ps == PS_START || ps == PS_UP);
! 1744:
! 1745: proto_do_stop(p);
! 1746: break;
! 1747:
! 1748: case PS_DOWN:
! 1749: if (ps != PS_STOP)
! 1750: proto_do_stop(p);
! 1751:
! 1752: proto_do_down(p);
! 1753: break;
! 1754:
! 1755: default:
! 1756: bug("%s: Invalid state %d", p->name, ps);
! 1757: }
! 1758:
! 1759: proto_log_state_change(p);
! 1760: }
! 1761:
! 1762: /*
! 1763: * CLI Commands
! 1764: */
! 1765:
! 1766: static char *
! 1767: proto_state_name(struct proto *p)
! 1768: {
! 1769: switch (p->proto_state)
! 1770: {
! 1771: case PS_DOWN: return p->active ? "flush" : "down";
! 1772: case PS_START: return "start";
! 1773: case PS_UP: return "up";
! 1774: case PS_STOP: return "stop";
! 1775: default: return "???";
! 1776: }
! 1777: }
! 1778:
! 1779: static void
! 1780: channel_show_stats(struct channel *c)
! 1781: {
! 1782: struct proto_stats *s = &c->stats;
! 1783:
! 1784: if (c->in_keep_filtered)
! 1785: cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred",
! 1786: s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes);
! 1787: else
! 1788: cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
! 1789: s->imp_routes, s->exp_routes, s->pref_routes);
! 1790:
! 1791: cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
! 1792: cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
! 1793: s->imp_updates_received, s->imp_updates_invalid,
! 1794: s->imp_updates_filtered, s->imp_updates_ignored,
! 1795: s->imp_updates_accepted);
! 1796: cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
! 1797: s->imp_withdraws_received, s->imp_withdraws_invalid,
! 1798: s->imp_withdraws_ignored, s->imp_withdraws_accepted);
! 1799: cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u",
! 1800: s->exp_updates_received, s->exp_updates_rejected,
! 1801: s->exp_updates_filtered, s->exp_updates_accepted);
! 1802: cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u",
! 1803: s->exp_withdraws_received, s->exp_withdraws_accepted);
! 1804: }
! 1805:
! 1806: void
! 1807: channel_show_limit(struct channel_limit *l, const char *dsc)
! 1808: {
! 1809: if (!l->action)
! 1810: return;
! 1811:
! 1812: cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : "");
! 1813: cli_msg(-1006, " Action: %s", channel_limit_name(l));
! 1814: }
! 1815:
! 1816: void
! 1817: channel_show_info(struct channel *c)
! 1818: {
! 1819: cli_msg(-1006, " Channel %s", c->name);
! 1820: cli_msg(-1006, " State: %s", c_states[c->channel_state]);
! 1821: cli_msg(-1006, " Table: %s", c->table->name);
! 1822: cli_msg(-1006, " Preference: %d", c->preference);
! 1823: cli_msg(-1006, " Input filter: %s", filter_name(c->in_filter));
! 1824: cli_msg(-1006, " Output filter: %s", filter_name(c->out_filter));
! 1825:
! 1826: if (graceful_restart_state == GRS_ACTIVE)
! 1827: cli_msg(-1006, " GR recovery: %s%s",
! 1828: c->gr_lock ? " pending" : "",
! 1829: c->gr_wait ? " waiting" : "");
! 1830:
! 1831: channel_show_limit(&c->rx_limit, "Receive limit:");
! 1832: channel_show_limit(&c->in_limit, "Import limit:");
! 1833: channel_show_limit(&c->out_limit, "Export limit:");
! 1834:
! 1835: if (c->channel_state != CS_DOWN)
! 1836: channel_show_stats(c);
! 1837: }
! 1838:
! 1839: void
! 1840: proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
! 1841: {
! 1842: byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE];
! 1843:
! 1844: /* First protocol - show header */
! 1845: if (!cnt)
! 1846: cli_msg(-2002, "%-10s %-10s %-10s %-6s %-12s %s",
! 1847: "Name", "Proto", "Table", "State", "Since", "Info");
! 1848:
! 1849: buf[0] = 0;
! 1850: if (p->proto->get_status)
! 1851: p->proto->get_status(p, buf);
! 1852: tm_format_time(tbuf, &config->tf_proto, p->last_state_change);
! 1853: cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s",
! 1854: p->name,
! 1855: p->proto->name,
! 1856: p->main_channel ? p->main_channel->table->name : "---",
! 1857: proto_state_name(p),
! 1858: tbuf,
! 1859: buf);
! 1860:
! 1861: if (verbose)
! 1862: {
! 1863: if (p->cf->dsc)
! 1864: cli_msg(-1006, " Description: %s", p->cf->dsc);
! 1865: if (p->message)
! 1866: cli_msg(-1006, " Message: %s", p->message);
! 1867: if (p->cf->router_id)
! 1868: cli_msg(-1006, " Router ID: %R", p->cf->router_id);
! 1869: if (p->vrf_set)
! 1870: cli_msg(-1006, " VRF: %s", p->vrf ? p->vrf->name : "default");
! 1871:
! 1872: if (p->proto->show_proto_info)
! 1873: p->proto->show_proto_info(p);
! 1874: else
! 1875: {
! 1876: struct channel *c;
! 1877: WALK_LIST(c, p->channels)
! 1878: channel_show_info(c);
! 1879: }
! 1880:
! 1881: cli_msg(-1006, "");
! 1882: }
! 1883: }
! 1884:
! 1885: void
! 1886: proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED)
! 1887: {
! 1888: if (p->disabled)
! 1889: {
! 1890: cli_msg(-8, "%s: already disabled", p->name);
! 1891: return;
! 1892: }
! 1893:
! 1894: log(L_INFO "Disabling protocol %s", p->name);
! 1895: p->disabled = 1;
! 1896: p->down_code = PDC_CMD_DISABLE;
! 1897: proto_set_message(p, (char *) arg, -1);
! 1898: proto_rethink_goal(p);
! 1899: cli_msg(-9, "%s: disabled", p->name);
! 1900: }
! 1901:
! 1902: void
! 1903: proto_cmd_enable(struct proto *p, uintptr_t arg, int cnt UNUSED)
! 1904: {
! 1905: if (!p->disabled)
! 1906: {
! 1907: cli_msg(-10, "%s: already enabled", p->name);
! 1908: return;
! 1909: }
! 1910:
! 1911: log(L_INFO "Enabling protocol %s", p->name);
! 1912: p->disabled = 0;
! 1913: proto_set_message(p, (char *) arg, -1);
! 1914: proto_rethink_goal(p);
! 1915: cli_msg(-11, "%s: enabled", p->name);
! 1916: }
! 1917:
! 1918: void
! 1919: proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED)
! 1920: {
! 1921: if (p->disabled)
! 1922: {
! 1923: cli_msg(-8, "%s: already disabled", p->name);
! 1924: return;
! 1925: }
! 1926:
! 1927: log(L_INFO "Restarting protocol %s", p->name);
! 1928: p->disabled = 1;
! 1929: p->down_code = PDC_CMD_RESTART;
! 1930: proto_set_message(p, (char *) arg, -1);
! 1931: proto_rethink_goal(p);
! 1932: p->disabled = 0;
! 1933: proto_rethink_goal(p);
! 1934: cli_msg(-12, "%s: restarted", p->name);
! 1935: }
! 1936:
! 1937: void
! 1938: proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED)
! 1939: {
! 1940: struct channel *c;
! 1941:
! 1942: if (p->disabled)
! 1943: {
! 1944: cli_msg(-8, "%s: already disabled", p->name);
! 1945: return;
! 1946: }
! 1947:
! 1948: /* If the protocol in not UP, it has no routes */
! 1949: if (p->proto_state != PS_UP)
! 1950: return;
! 1951:
! 1952: /* All channels must support reload */
! 1953: if (dir != CMD_RELOAD_OUT)
! 1954: WALK_LIST(c, p->channels)
! 1955: if ((c->channel_state == CS_UP) && !channel_reloadable(c))
! 1956: {
! 1957: cli_msg(-8006, "%s: reload failed", p->name);
! 1958: return;
! 1959: }
! 1960:
! 1961: log(L_INFO "Reloading protocol %s", p->name);
! 1962:
! 1963: /* re-importing routes */
! 1964: if (dir != CMD_RELOAD_OUT)
! 1965: WALK_LIST(c, p->channels)
! 1966: if (c->channel_state == CS_UP)
! 1967: channel_request_reload(c);
! 1968:
! 1969: /* re-exporting routes */
! 1970: if (dir != CMD_RELOAD_IN)
! 1971: WALK_LIST(c, p->channels)
! 1972: if (c->channel_state == CS_UP)
! 1973: channel_request_feeding(c);
! 1974:
! 1975: cli_msg(-15, "%s: reloading", p->name);
! 1976: }
! 1977:
! 1978: void
! 1979: proto_cmd_debug(struct proto *p, uintptr_t mask, int cnt UNUSED)
! 1980: {
! 1981: p->debug = mask;
! 1982: }
! 1983:
! 1984: void
! 1985: proto_cmd_mrtdump(struct proto *p, uintptr_t mask, int cnt UNUSED)
! 1986: {
! 1987: p->mrtdump = mask;
! 1988: }
! 1989:
! 1990: static void
! 1991: proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
! 1992: {
! 1993: if (s->class != SYM_PROTO)
! 1994: {
! 1995: cli_msg(9002, "%s is not a protocol", s->name);
! 1996: return;
! 1997: }
! 1998:
! 1999: cmd(s->proto->proto, arg, 0);
! 2000: cli_msg(0, "");
! 2001: }
! 2002:
! 2003: static void
! 2004: proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
! 2005: {
! 2006: struct proto *p;
! 2007: int cnt = 0;
! 2008:
! 2009: WALK_LIST(p, proto_list)
! 2010: if (!patt || patmatch(patt, p->name))
! 2011: cmd(p, arg, cnt++);
! 2012:
! 2013: if (!cnt)
! 2014: cli_msg(8003, "No protocols match");
! 2015: else
! 2016: cli_msg(0, "");
! 2017: }
! 2018:
! 2019: void
! 2020: proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int),
! 2021: int restricted, uintptr_t arg)
! 2022: {
! 2023: if (restricted && cli_access_restricted())
! 2024: return;
! 2025:
! 2026: if (ps.patt)
! 2027: proto_apply_cmd_patt(ps.ptr, cmd, arg);
! 2028: else
! 2029: proto_apply_cmd_symbol(ps.ptr, cmd, arg);
! 2030: }
! 2031:
! 2032: struct proto *
! 2033: proto_get_named(struct symbol *sym, struct protocol *pr)
! 2034: {
! 2035: struct proto *p, *q;
! 2036:
! 2037: if (sym)
! 2038: {
! 2039: if (sym->class != SYM_PROTO)
! 2040: cf_error("%s: Not a protocol", sym->name);
! 2041:
! 2042: p = sym->proto->proto;
! 2043: if (!p || p->proto != pr)
! 2044: cf_error("%s: Not a %s protocol", sym->name, pr->name);
! 2045: }
! 2046: else
! 2047: {
! 2048: p = NULL;
! 2049: WALK_LIST(q, proto_list)
! 2050: if ((q->proto == pr) && (q->proto_state != PS_DOWN))
! 2051: {
! 2052: if (p)
! 2053: cf_error("There are multiple %s protocols running", pr->name);
! 2054: p = q;
! 2055: }
! 2056: if (!p)
! 2057: cf_error("There is no %s protocol running", pr->name);
! 2058: }
! 2059:
! 2060: return p;
! 2061: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>