Return to bfd.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / bfd |
1.1 ! misho 1: /* ! 2: * BIRD -- Bidirectional Forwarding Detection (BFD) ! 3: * ! 4: * Can be freely distributed and used under the terms of the GNU GPL. ! 5: */ ! 6: ! 7: /** ! 8: * DOC: Bidirectional Forwarding Detection ! 9: * ! 10: * The BFD protocol is implemented in three files: |bfd.c| containing the ! 11: * protocol logic and the protocol glue with BIRD core, |packets.c| handling BFD ! 12: * packet processing, RX, TX and protocol sockets. |io.c| then contains generic ! 13: * code for the event loop, threads and event sources (sockets, microsecond ! 14: * timers). This generic code will be merged to the main BIRD I/O code in the ! 15: * future. ! 16: * ! 17: * The BFD implementation uses a separate thread with an internal event loop for ! 18: * handling the protocol logic, which requires high-res and low-latency timing, ! 19: * so it is not affected by the rest of BIRD, which has several low-granularity ! 20: * hooks in the main loop, uses second-based timers and cannot offer good ! 21: * latency. The core of BFD protocol (the code related to BFD sessions, ! 22: * interfaces and packets) runs in the BFD thread, while the rest (the code ! 23: * related to BFD requests, BFD neighbors and the protocol glue) runs in the ! 24: * main thread. ! 25: * ! 26: * BFD sessions are represented by structure &bfd_session that contains a state ! 27: * related to the session and two timers (TX timer for periodic packets and hold ! 28: * timer for session timeout). These sessions are allocated from @session_slab ! 29: * and are accessible by two hash tables, @session_hash_id (by session ID) and ! 30: * @session_hash_ip (by IP addresses of neighbors). Slab and both hashes are in ! 31: * the main protocol structure &bfd_proto. The protocol logic related to BFD ! 32: * sessions is implemented in internal functions bfd_session_*(), which are ! 33: * expected to be called from the context of BFD thread, and external functions ! 34: * bfd_add_session(), bfd_remove_session() and bfd_reconfigure_session(), which ! 35: * form an interface to the BFD core for the rest and are expected to be called ! 36: * from the context of main thread. ! 37: * ! 38: * Each BFD session has an associated BFD interface, represented by structure ! 39: * &bfd_iface. A BFD interface contains a socket used for TX (the one for RX is ! 40: * shared in &bfd_proto), an interface configuration and reference counter. ! 41: * Compared to interface structures of other protocols, these structures are not ! 42: * created and removed based on interface notification events, but according to ! 43: * the needs of BFD sessions. When a new session is created, it requests a ! 44: * proper BFD interface by function bfd_get_iface(), which either finds an ! 45: * existing one in &iface_list (from &bfd_proto) or allocates a new one. When a ! 46: * session is removed, an associated iface is discharged by bfd_free_iface(). ! 47: * ! 48: * BFD requests are the external API for the other protocols. When a protocol ! 49: * wants a BFD session, it calls bfd_request_session(), which creates a ! 50: * structure &bfd_request containing approprite information and an notify hook. ! 51: * This structure is a resource associated with the caller's resource pool. When ! 52: * a BFD protocol is available, a BFD request is submitted to the protocol, an ! 53: * appropriate BFD session is found or created and the request is attached to ! 54: * the session. When a session changes state, all attached requests (and related ! 55: * protocols) are notified. Note that BFD requests do not depend on BFD protocol ! 56: * running. When the BFD protocol is stopped or removed (or not available from ! 57: * beginning), related BFD requests are stored in @bfd_wait_list, where waits ! 58: * for a new protocol. ! 59: * ! 60: * BFD neighbors are just a way to statically configure BFD sessions without ! 61: * requests from other protocol. Structures &bfd_neighbor are part of BFD ! 62: * configuration (like static routes in the static protocol). BFD neighbors are ! 63: * handled by BFD protocol like it is a BFD client -- when a BFD neighbor is ! 64: * ready, the protocol just creates a BFD request like any other protocol. ! 65: * ! 66: * The protocol uses a new generic event loop (structure &birdloop) from |io.c|, ! 67: * which supports sockets, timers and events like the main loop. Timers ! 68: * (structure &timer2) are new microsecond based timers, while sockets and ! 69: * events are the same. A birdloop is associated with a thread (field @thread) ! 70: * in which event hooks are executed. Most functions for setting event sources ! 71: * (like sk_start() or tm2_start()) must be called from the context of that ! 72: * thread. Birdloop allows to temporarily acquire the context of that thread for ! 73: * the main thread by calling birdloop_enter() and then birdloop_leave(), which ! 74: * also ensures mutual exclusion with all event hooks. Note that resources ! 75: * associated with a birdloop (like timers) should be attached to the ! 76: * independent resource pool, detached from the main resource tree. ! 77: * ! 78: * There are two kinds of interaction between the BFD core (running in the BFD ! 79: * thread) and the rest of BFD (running in the main thread). The first kind are ! 80: * configuration calls from main thread to the BFD thread (like bfd_add_session()). ! 81: * These calls are synchronous and use birdloop_enter() mechanism for mutual ! 82: * exclusion. The second kind is a notification about session changes from the ! 83: * BFD thread to the main thread. This is done in an asynchronous way, sesions ! 84: * with pending notifications are linked (in the BFD thread) to @notify_list in ! 85: * &bfd_proto, and then bfd_notify_hook() in the main thread is activated using ! 86: * bfd_notify_kick() and a pipe. The hook then processes scheduled sessions and ! 87: * calls hooks from associated BFD requests. This @notify_list (and state fields ! 88: * in structure &bfd_session) is protected by a spinlock in &bfd_proto and ! 89: * functions bfd_lock_sessions() / bfd_unlock_sessions(). ! 90: * ! 91: * There are few data races (accessing @p->p.debug from TRACE() from the BFD ! 92: * thread and accessing some some private fields of %bfd_session from ! 93: * bfd_show_sessions() from the main thread, but these are harmless (i hope). ! 94: * ! 95: * TODO: document functions and access restrictions for fields in BFD structures. ! 96: * ! 97: * Supported standards: ! 98: * - RFC 5880 - main BFD standard ! 99: * - RFC 5881 - BFD for IP links ! 100: * - RFC 5882 - generic application of BFD ! 101: * - RFC 5883 - BFD for multihop paths ! 102: */ ! 103: ! 104: #include "bfd.h" ! 105: ! 106: ! 107: #define HASH_ID_KEY(n) n->loc_id ! 108: #define HASH_ID_NEXT(n) n->next_id ! 109: #define HASH_ID_EQ(a,b) a == b ! 110: #define HASH_ID_FN(k) k ! 111: ! 112: #define HASH_IP_KEY(n) n->addr ! 113: #define HASH_IP_NEXT(n) n->next_ip ! 114: #define HASH_IP_EQ(a,b) ipa_equal(a,b) ! 115: #define HASH_IP_FN(k) ipa_hash32(k) ! 116: ! 117: static list bfd_proto_list; ! 118: static list bfd_wait_list; ! 119: ! 120: const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" }; ! 121: ! 122: static void bfd_session_set_min_tx(struct bfd_session *s, u32 val); ! 123: static struct bfd_iface *bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface); ! 124: static void bfd_free_iface(struct bfd_iface *ifa); ! 125: static inline void bfd_notify_kick(struct bfd_proto *p); ! 126: ! 127: ! 128: /* ! 129: * BFD sessions ! 130: */ ! 131: ! 132: static void ! 133: bfd_session_update_state(struct bfd_session *s, uint state, uint diag) ! 134: { ! 135: struct bfd_proto *p = s->ifa->bfd; ! 136: uint old_state = s->loc_state; ! 137: int notify; ! 138: ! 139: if (state == old_state) ! 140: return; ! 141: ! 142: TRACE(D_EVENTS, "Session to %I changed state from %s to %s", ! 143: s->addr, bfd_state_names[old_state], bfd_state_names[state]); ! 144: ! 145: bfd_lock_sessions(p); ! 146: s->loc_state = state; ! 147: s->loc_diag = diag; ! 148: ! 149: notify = !NODE_VALID(&s->n); ! 150: if (notify) ! 151: add_tail(&p->notify_list, &s->n); ! 152: bfd_unlock_sessions(p); ! 153: ! 154: if (state == BFD_STATE_UP) ! 155: bfd_session_set_min_tx(s, s->ifa->cf->min_tx_int); ! 156: ! 157: if (old_state == BFD_STATE_UP) ! 158: bfd_session_set_min_tx(s, s->ifa->cf->idle_tx_int); ! 159: ! 160: if (notify) ! 161: bfd_notify_kick(p); ! 162: } ! 163: ! 164: static void ! 165: bfd_session_update_tx_interval(struct bfd_session *s) ! 166: { ! 167: u32 tx_int = MAX(s->des_min_tx_int, s->rem_min_rx_int); ! 168: u32 tx_int_l = tx_int - (tx_int / 4); // 75 % ! 169: u32 tx_int_h = tx_int - (tx_int / 10); // 90 % ! 170: ! 171: s->tx_timer->recurrent = tx_int_l; ! 172: s->tx_timer->randomize = tx_int_h - tx_int_l; ! 173: ! 174: /* Do not set timer if no previous event */ ! 175: if (!s->last_tx) ! 176: return; ! 177: ! 178: /* Set timer relative to last tx_timer event */ ! 179: tm2_set(s->tx_timer, s->last_tx + tx_int_l); ! 180: } ! 181: ! 182: static void ! 183: bfd_session_update_detection_time(struct bfd_session *s, int kick) ! 184: { ! 185: btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult; ! 186: ! 187: if (kick) ! 188: s->last_rx = current_time(); ! 189: ! 190: if (!s->last_rx) ! 191: return; ! 192: ! 193: tm2_set(s->hold_timer, s->last_rx + timeout); ! 194: } ! 195: ! 196: static void ! 197: bfd_session_control_tx_timer(struct bfd_session *s, int reset) ! 198: { ! 199: // if (!s->opened) goto stop; ! 200: ! 201: if (s->passive && (s->rem_id == 0)) ! 202: goto stop; ! 203: ! 204: if (s->rem_demand_mode && ! 205: !s->poll_active && ! 206: (s->loc_state == BFD_STATE_UP) && ! 207: (s->rem_state == BFD_STATE_UP)) ! 208: goto stop; ! 209: ! 210: if (s->rem_min_rx_int == 0) ! 211: goto stop; ! 212: ! 213: /* So TX timer should run */ ! 214: if (reset || !tm2_active(s->tx_timer)) ! 215: { ! 216: s->last_tx = 0; ! 217: tm2_start(s->tx_timer, 0); ! 218: } ! 219: ! 220: return; ! 221: ! 222: stop: ! 223: tm2_stop(s->tx_timer); ! 224: s->last_tx = 0; ! 225: } ! 226: ! 227: static void ! 228: bfd_session_request_poll(struct bfd_session *s, u8 request) ! 229: { ! 230: /* Not sure about this, but doing poll in this case does not make sense */ ! 231: if (s->rem_id == 0) ! 232: return; ! 233: ! 234: s->poll_scheduled |= request; ! 235: ! 236: if (s->poll_active) ! 237: return; ! 238: ! 239: s->poll_active = s->poll_scheduled; ! 240: s->poll_scheduled = 0; ! 241: ! 242: bfd_session_control_tx_timer(s, 1); ! 243: } ! 244: ! 245: static void ! 246: bfd_session_terminate_poll(struct bfd_session *s) ! 247: { ! 248: u8 poll_done = s->poll_active & ~s->poll_scheduled; ! 249: ! 250: if (poll_done & BFD_POLL_TX) ! 251: s->des_min_tx_int = s->des_min_tx_new; ! 252: ! 253: if (poll_done & BFD_POLL_RX) ! 254: s->req_min_rx_int = s->req_min_rx_new; ! 255: ! 256: s->poll_active = s->poll_scheduled; ! 257: s->poll_scheduled = 0; ! 258: ! 259: /* Timers are updated by caller - bfd_session_process_ctl() */ ! 260: } ! 261: ! 262: void ! 263: bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int) ! 264: { ! 265: if (s->poll_active && (flags & BFD_FLAG_FINAL)) ! 266: bfd_session_terminate_poll(s); ! 267: ! 268: if ((s->des_min_tx_int != old_tx_int) || (s->rem_min_rx_int != old_rx_int)) ! 269: bfd_session_update_tx_interval(s); ! 270: ! 271: bfd_session_update_detection_time(s, 1); ! 272: ! 273: /* Update session state */ ! 274: int next_state = 0; ! 275: int diag = BFD_DIAG_NOTHING; ! 276: ! 277: switch (s->loc_state) ! 278: { ! 279: case BFD_STATE_ADMIN_DOWN: ! 280: return; ! 281: ! 282: case BFD_STATE_DOWN: ! 283: if (s->rem_state == BFD_STATE_DOWN) next_state = BFD_STATE_INIT; ! 284: else if (s->rem_state == BFD_STATE_INIT) next_state = BFD_STATE_UP; ! 285: break; ! 286: ! 287: case BFD_STATE_INIT: ! 288: if (s->rem_state == BFD_STATE_ADMIN_DOWN) next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN; ! 289: else if (s->rem_state >= BFD_STATE_INIT) next_state = BFD_STATE_UP; ! 290: break; ! 291: ! 292: case BFD_STATE_UP: ! 293: if (s->rem_state <= BFD_STATE_DOWN) next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN; ! 294: break; ! 295: } ! 296: ! 297: if (next_state) ! 298: bfd_session_update_state(s, next_state, diag); ! 299: ! 300: bfd_session_control_tx_timer(s, 0); ! 301: ! 302: if (flags & BFD_FLAG_POLL) ! 303: bfd_send_ctl(s->ifa->bfd, s, 1); ! 304: } ! 305: ! 306: static void ! 307: bfd_session_timeout(struct bfd_session *s) ! 308: { ! 309: struct bfd_proto *p = s->ifa->bfd; ! 310: ! 311: TRACE(D_EVENTS, "Session to %I expired", s->addr); ! 312: ! 313: s->rem_state = BFD_STATE_DOWN; ! 314: s->rem_id = 0; ! 315: s->rem_min_tx_int = 0; ! 316: s->rem_min_rx_int = 1; ! 317: s->rem_demand_mode = 0; ! 318: s->rem_detect_mult = 0; ! 319: s->rx_csn_known = 0; ! 320: ! 321: s->poll_active = 0; ! 322: s->poll_scheduled = 0; ! 323: ! 324: bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT); ! 325: ! 326: bfd_session_control_tx_timer(s, 1); ! 327: } ! 328: ! 329: static void ! 330: bfd_session_set_min_tx(struct bfd_session *s, u32 val) ! 331: { ! 332: /* Note that des_min_tx_int <= des_min_tx_new */ ! 333: ! 334: if (val == s->des_min_tx_new) ! 335: return; ! 336: ! 337: s->des_min_tx_new = val; ! 338: ! 339: /* Postpone timer update if des_min_tx_int increases and the session is up */ ! 340: if ((s->loc_state != BFD_STATE_UP) || (val < s->des_min_tx_int)) ! 341: { ! 342: s->des_min_tx_int = val; ! 343: bfd_session_update_tx_interval(s); ! 344: } ! 345: ! 346: bfd_session_request_poll(s, BFD_POLL_TX); ! 347: } ! 348: ! 349: static void ! 350: bfd_session_set_min_rx(struct bfd_session *s, u32 val) ! 351: { ! 352: /* Note that req_min_rx_int >= req_min_rx_new */ ! 353: ! 354: if (val == s->req_min_rx_new) ! 355: return; ! 356: ! 357: s->req_min_rx_new = val; ! 358: ! 359: /* Postpone timer update if req_min_rx_int decreases and the session is up */ ! 360: if ((s->loc_state != BFD_STATE_UP) || (val > s->req_min_rx_int)) ! 361: { ! 362: s->req_min_rx_int = val; ! 363: bfd_session_update_detection_time(s, 0); ! 364: } ! 365: ! 366: bfd_session_request_poll(s, BFD_POLL_RX); ! 367: } ! 368: ! 369: struct bfd_session * ! 370: bfd_find_session_by_id(struct bfd_proto *p, u32 id) ! 371: { ! 372: return HASH_FIND(p->session_hash_id, HASH_ID, id); ! 373: } ! 374: ! 375: struct bfd_session * ! 376: bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr) ! 377: { ! 378: return HASH_FIND(p->session_hash_ip, HASH_IP, addr); ! 379: } ! 380: ! 381: static void ! 382: bfd_tx_timer_hook(timer2 *t) ! 383: { ! 384: struct bfd_session *s = t->data; ! 385: ! 386: s->last_tx = current_time(); ! 387: bfd_send_ctl(s->ifa->bfd, s, 0); ! 388: } ! 389: ! 390: static void ! 391: bfd_hold_timer_hook(timer2 *t) ! 392: { ! 393: bfd_session_timeout(t->data); ! 394: } ! 395: ! 396: static u32 ! 397: bfd_get_free_id(struct bfd_proto *p) ! 398: { ! 399: u32 id; ! 400: for (id = random_u32(); 1; id++) ! 401: if (id && !bfd_find_session_by_id(p, id)) ! 402: break; ! 403: ! 404: return id; ! 405: } ! 406: ! 407: static struct bfd_session * ! 408: bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface) ! 409: { ! 410: birdloop_enter(p->loop); ! 411: ! 412: struct bfd_iface *ifa = bfd_get_iface(p, local, iface); ! 413: ! 414: struct bfd_session *s = sl_alloc(p->session_slab); ! 415: bzero(s, sizeof(struct bfd_session)); ! 416: ! 417: s->addr = addr; ! 418: s->ifa = ifa; ! 419: s->loc_id = bfd_get_free_id(p); ! 420: ! 421: HASH_INSERT(p->session_hash_id, HASH_ID, s); ! 422: HASH_INSERT(p->session_hash_ip, HASH_IP, s); ! 423: ! 424: ! 425: /* Initialization of state variables - see RFC 5880 6.8.1 */ ! 426: s->loc_state = BFD_STATE_DOWN; ! 427: s->rem_state = BFD_STATE_DOWN; ! 428: s->des_min_tx_int = s->des_min_tx_new = ifa->cf->idle_tx_int; ! 429: s->req_min_rx_int = s->req_min_rx_new = ifa->cf->min_rx_int; ! 430: s->rem_min_rx_int = 1; ! 431: s->detect_mult = ifa->cf->multiplier; ! 432: s->passive = ifa->cf->passive; ! 433: s->tx_csn = random_u32(); ! 434: ! 435: s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0); ! 436: s->hold_timer = tm2_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0); ! 437: bfd_session_update_tx_interval(s); ! 438: bfd_session_control_tx_timer(s, 1); ! 439: ! 440: init_list(&s->request_list); ! 441: s->last_state_change = now; ! 442: ! 443: TRACE(D_EVENTS, "Session to %I added", s->addr); ! 444: ! 445: birdloop_leave(p->loop); ! 446: ! 447: return s; ! 448: } ! 449: ! 450: /* ! 451: static void ! 452: bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa) ! 453: { ! 454: birdloop_enter(p->loop); ! 455: ! 456: s->opened = 1; ! 457: ! 458: bfd_session_control_tx_timer(s); ! 459: ! 460: birdloop_leave(p->loop); ! 461: } ! 462: ! 463: static void ! 464: bfd_close_session(struct bfd_proto *p, struct bfd_session *s) ! 465: { ! 466: birdloop_enter(p->loop); ! 467: ! 468: s->opened = 0; ! 469: ! 470: bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN); ! 471: bfd_session_control_tx_timer(s); ! 472: ! 473: birdloop_leave(p->loop); ! 474: } ! 475: */ ! 476: ! 477: static void ! 478: bfd_remove_session(struct bfd_proto *p, struct bfd_session *s) ! 479: { ! 480: ip_addr ip = s->addr; ! 481: ! 482: /* Caller should ensure that request list is empty */ ! 483: ! 484: birdloop_enter(p->loop); ! 485: ! 486: /* Remove session from notify list if scheduled for notification */ ! 487: /* No need for bfd_lock_sessions(), we are already protected by birdloop_enter() */ ! 488: if (NODE_VALID(&s->n)) ! 489: rem_node(&s->n); ! 490: ! 491: bfd_free_iface(s->ifa); ! 492: ! 493: rfree(s->tx_timer); ! 494: rfree(s->hold_timer); ! 495: ! 496: HASH_REMOVE(p->session_hash_id, HASH_ID, s); ! 497: HASH_REMOVE(p->session_hash_ip, HASH_IP, s); ! 498: ! 499: sl_free(p->session_slab, s); ! 500: ! 501: TRACE(D_EVENTS, "Session to %I removed", ip); ! 502: ! 503: birdloop_leave(p->loop); ! 504: } ! 505: ! 506: static void ! 507: bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s) ! 508: { ! 509: birdloop_enter(p->loop); ! 510: ! 511: struct bfd_iface_config *cf = s->ifa->cf; ! 512: ! 513: u32 tx = (s->loc_state == BFD_STATE_UP) ? cf->min_tx_int : cf->idle_tx_int; ! 514: bfd_session_set_min_tx(s, tx); ! 515: bfd_session_set_min_rx(s, cf->min_rx_int); ! 516: s->detect_mult = cf->multiplier; ! 517: s->passive = cf->passive; ! 518: ! 519: bfd_session_control_tx_timer(s, 0); ! 520: ! 521: birdloop_leave(p->loop); ! 522: ! 523: TRACE(D_EVENTS, "Session to %I reconfigured", s->addr); ! 524: } ! 525: ! 526: ! 527: /* ! 528: * BFD interfaces ! 529: */ ! 530: ! 531: static struct bfd_iface_config bfd_default_iface = { ! 532: .min_rx_int = BFD_DEFAULT_MIN_RX_INT, ! 533: .min_tx_int = BFD_DEFAULT_MIN_TX_INT, ! 534: .idle_tx_int = BFD_DEFAULT_IDLE_TX_INT, ! 535: .multiplier = BFD_DEFAULT_MULTIPLIER ! 536: }; ! 537: ! 538: static inline struct bfd_iface_config * ! 539: bfd_find_iface_config(struct bfd_config *cf, struct iface *iface) ! 540: { ! 541: struct bfd_iface_config *ic; ! 542: ! 543: ic = iface ? (void *) iface_patt_find(&cf->patt_list, iface, NULL) : cf->multihop; ! 544: ! 545: return ic ? ic : &bfd_default_iface; ! 546: } ! 547: ! 548: static struct bfd_iface * ! 549: bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface) ! 550: { ! 551: struct bfd_iface *ifa; ! 552: ! 553: WALK_LIST(ifa, p->iface_list) ! 554: if (ipa_equal(ifa->local, local) && (ifa->iface == iface)) ! 555: return ifa->uc++, ifa; ! 556: ! 557: struct bfd_config *cf = (struct bfd_config *) (p->p.cf); ! 558: struct bfd_iface_config *ic = bfd_find_iface_config(cf, iface); ! 559: ! 560: ifa = mb_allocz(p->tpool, sizeof(struct bfd_iface)); ! 561: ifa->local = local; ! 562: ifa->iface = iface; ! 563: ifa->cf = ic; ! 564: ifa->bfd = p; ! 565: ! 566: ifa->sk = bfd_open_tx_sk(p, local, iface); ! 567: ifa->uc = 1; ! 568: ! 569: add_tail(&p->iface_list, &ifa->n); ! 570: ! 571: return ifa; ! 572: } ! 573: ! 574: static void ! 575: bfd_free_iface(struct bfd_iface *ifa) ! 576: { ! 577: if (!ifa || --ifa->uc) ! 578: return; ! 579: ! 580: if (ifa->sk) ! 581: { ! 582: sk_stop(ifa->sk); ! 583: rfree(ifa->sk); ! 584: } ! 585: ! 586: rem_node(&ifa->n); ! 587: mb_free(ifa); ! 588: } ! 589: ! 590: static void ! 591: bfd_reconfigure_iface(struct bfd_proto *p, struct bfd_iface *ifa, struct bfd_config *nc) ! 592: { ! 593: struct bfd_iface_config *nic = bfd_find_iface_config(nc, ifa->iface); ! 594: ifa->changed = !!memcmp(nic, ifa->cf, sizeof(struct bfd_iface_config)); ! 595: ! 596: /* This should be probably changed to not access ifa->cf from the BFD thread */ ! 597: birdloop_enter(p->loop); ! 598: ifa->cf = nic; ! 599: birdloop_leave(p->loop); ! 600: } ! 601: ! 602: ! 603: /* ! 604: * BFD requests ! 605: */ ! 606: ! 607: static void ! 608: bfd_request_notify(struct bfd_request *req, u8 state, u8 diag) ! 609: { ! 610: u8 old_state = req->state; ! 611: ! 612: if (state == old_state) ! 613: return; ! 614: ! 615: req->state = state; ! 616: req->diag = diag; ! 617: req->old_state = old_state; ! 618: req->down = (old_state == BFD_STATE_UP) && (state == BFD_STATE_DOWN); ! 619: ! 620: if (req->hook) ! 621: req->hook(req); ! 622: } ! 623: ! 624: static int ! 625: bfd_add_request(struct bfd_proto *p, struct bfd_request *req) ! 626: { ! 627: struct bfd_session *s = bfd_find_session_by_addr(p, req->addr); ! 628: u8 state, diag; ! 629: ! 630: if (!s) ! 631: s = bfd_add_session(p, req->addr, req->local, req->iface); ! 632: ! 633: rem_node(&req->n); ! 634: add_tail(&s->request_list, &req->n); ! 635: req->session = s; ! 636: ! 637: bfd_lock_sessions(p); ! 638: state = s->loc_state; ! 639: diag = s->loc_diag; ! 640: bfd_unlock_sessions(p); ! 641: ! 642: bfd_request_notify(req, state, diag); ! 643: ! 644: return 1; ! 645: } ! 646: ! 647: static void ! 648: bfd_submit_request(struct bfd_request *req) ! 649: { ! 650: node *n; ! 651: ! 652: WALK_LIST(n, bfd_proto_list) ! 653: if (bfd_add_request(SKIP_BACK(struct bfd_proto, bfd_node, n), req)) ! 654: return; ! 655: ! 656: rem_node(&req->n); ! 657: add_tail(&bfd_wait_list, &req->n); ! 658: req->session = NULL; ! 659: bfd_request_notify(req, BFD_STATE_ADMIN_DOWN, 0); ! 660: } ! 661: ! 662: static void ! 663: bfd_take_requests(struct bfd_proto *p) ! 664: { ! 665: node *n, *nn; ! 666: ! 667: WALK_LIST_DELSAFE(n, nn, bfd_wait_list) ! 668: bfd_add_request(p, SKIP_BACK(struct bfd_request, n, n)); ! 669: } ! 670: ! 671: static void ! 672: bfd_drop_requests(struct bfd_proto *p) ! 673: { ! 674: node *n; ! 675: ! 676: HASH_WALK(p->session_hash_id, next_id, s) ! 677: { ! 678: /* We assume that p is not in bfd_proto_list */ ! 679: WALK_LIST_FIRST(n, s->request_list) ! 680: bfd_submit_request(SKIP_BACK(struct bfd_request, n, n)); ! 681: } ! 682: HASH_WALK_END; ! 683: } ! 684: ! 685: static struct resclass bfd_request_class; ! 686: ! 687: struct bfd_request * ! 688: bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, ! 689: void (*hook)(struct bfd_request *), void *data) ! 690: { ! 691: struct bfd_request *req = ralloc(p, &bfd_request_class); ! 692: ! 693: /* Hack: self-link req->n, we will call rem_node() on it */ ! 694: req->n.prev = req->n.next = &req->n; ! 695: ! 696: req->addr = addr; ! 697: req->local = local; ! 698: req->iface = iface; ! 699: ! 700: bfd_submit_request(req); ! 701: ! 702: req->hook = hook; ! 703: req->data = data; ! 704: ! 705: return req; ! 706: } ! 707: ! 708: static void ! 709: bfd_request_free(resource *r) ! 710: { ! 711: struct bfd_request *req = (struct bfd_request *) r; ! 712: struct bfd_session *s = req->session; ! 713: ! 714: rem_node(&req->n); ! 715: ! 716: /* Remove the session if there is no request for it. Skip that if ! 717: inside notify hooks, will be handled by bfd_notify_hook() itself */ ! 718: ! 719: if (s && EMPTY_LIST(s->request_list) && !s->notify_running) ! 720: bfd_remove_session(s->ifa->bfd, s); ! 721: } ! 722: ! 723: static void ! 724: bfd_request_dump(resource *r) ! 725: { ! 726: struct bfd_request *req = (struct bfd_request *) r; ! 727: ! 728: debug("(code %p, data %p)\n", req->hook, req->data); ! 729: } ! 730: ! 731: static struct resclass bfd_request_class = { ! 732: "BFD request", ! 733: sizeof(struct bfd_request), ! 734: bfd_request_free, ! 735: bfd_request_dump, ! 736: NULL, ! 737: NULL ! 738: }; ! 739: ! 740: ! 741: /* ! 742: * BFD neighbors ! 743: */ ! 744: ! 745: static void ! 746: bfd_neigh_notify(struct neighbor *nb) ! 747: { ! 748: struct bfd_proto *p = (struct bfd_proto *) nb->proto; ! 749: struct bfd_neighbor *n = nb->data; ! 750: ! 751: if (!n) ! 752: return; ! 753: ! 754: if ((nb->scope > 0) && !n->req) ! 755: { ! 756: ip_addr local = ipa_nonzero(n->local) ? n->local : nb->ifa->ip; ! 757: n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, NULL, NULL); ! 758: } ! 759: ! 760: if ((nb->scope <= 0) && n->req) ! 761: { ! 762: rfree(n->req); ! 763: n->req = NULL; ! 764: } ! 765: } ! 766: ! 767: static void ! 768: bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n) ! 769: { ! 770: n->active = 1; ! 771: ! 772: if (n->multihop) ! 773: { ! 774: n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, NULL, NULL); ! 775: return; ! 776: } ! 777: ! 778: struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, NEF_STICKY); ! 779: if (!nb) ! 780: { ! 781: log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface); ! 782: return; ! 783: } ! 784: ! 785: if (nb->data) ! 786: { ! 787: log(L_ERR "%s: Duplicate neighbor %I", p->p.name, n->addr); ! 788: return; ! 789: } ! 790: ! 791: n->neigh = nb; ! 792: nb->data = n; ! 793: ! 794: if (nb->scope > 0) ! 795: bfd_neigh_notify(nb); ! 796: else ! 797: TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, n->iface); ! 798: } ! 799: ! 800: static void ! 801: bfd_stop_neighbor(struct bfd_proto *p UNUSED, struct bfd_neighbor *n) ! 802: { ! 803: if (n->neigh) ! 804: n->neigh->data = NULL; ! 805: n->neigh = NULL; ! 806: ! 807: rfree(n->req); ! 808: n->req = NULL; ! 809: } ! 810: ! 811: static inline int ! 812: bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y) ! 813: { ! 814: return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) && ! 815: (x->iface == y->iface) && (x->multihop == y->multihop); ! 816: } ! 817: ! 818: static void ! 819: bfd_reconfigure_neighbors(struct bfd_proto *p, struct bfd_config *new) ! 820: { ! 821: struct bfd_config *old = (struct bfd_config *) (p->p.cf); ! 822: struct bfd_neighbor *on, *nn; ! 823: ! 824: WALK_LIST(on, old->neigh_list) ! 825: { ! 826: WALK_LIST(nn, new->neigh_list) ! 827: if (bfd_same_neighbor(nn, on)) ! 828: { ! 829: nn->neigh = on->neigh; ! 830: if (nn->neigh) ! 831: nn->neigh->data = nn; ! 832: ! 833: nn->req = on->req; ! 834: nn->active = 1; ! 835: return; ! 836: } ! 837: ! 838: bfd_stop_neighbor(p, on); ! 839: } ! 840: ! 841: WALK_LIST(nn, new->neigh_list) ! 842: if (!nn->active) ! 843: bfd_start_neighbor(p, nn); ! 844: } ! 845: ! 846: ! 847: /* ! 848: * BFD notify socket ! 849: */ ! 850: ! 851: /* This core notify code should be replaced after main loop transition to birdloop */ ! 852: ! 853: int pipe(int pipefd[2]); ! 854: void pipe_drain(int fd); ! 855: void pipe_kick(int fd); ! 856: ! 857: static int ! 858: bfd_notify_hook(sock *sk, uint len UNUSED) ! 859: { ! 860: struct bfd_proto *p = sk->data; ! 861: struct bfd_session *s; ! 862: list tmp_list; ! 863: u8 state, diag; ! 864: node *n, *nn; ! 865: ! 866: pipe_drain(sk->fd); ! 867: ! 868: bfd_lock_sessions(p); ! 869: init_list(&tmp_list); ! 870: add_tail_list(&tmp_list, &p->notify_list); ! 871: init_list(&p->notify_list); ! 872: bfd_unlock_sessions(p); ! 873: ! 874: WALK_LIST_FIRST(s, tmp_list) ! 875: { ! 876: bfd_lock_sessions(p); ! 877: rem_node(&s->n); ! 878: state = s->loc_state; ! 879: diag = s->loc_diag; ! 880: bfd_unlock_sessions(p); ! 881: ! 882: /* FIXME: convert to btime and move to bfd_session_update_state() */ ! 883: s->last_state_change = now; ! 884: ! 885: s->notify_running = 1; ! 886: WALK_LIST_DELSAFE(n, nn, s->request_list) ! 887: bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag); ! 888: s->notify_running = 0; ! 889: ! 890: /* Remove the session if all requests were removed in notify hooks */ ! 891: if (EMPTY_LIST(s->request_list)) ! 892: bfd_remove_session(p, s); ! 893: } ! 894: ! 895: return 0; ! 896: } ! 897: ! 898: static inline void ! 899: bfd_notify_kick(struct bfd_proto *p) ! 900: { ! 901: pipe_kick(p->notify_ws->fd); ! 902: } ! 903: ! 904: static void ! 905: bfd_noterr_hook(sock *sk, int err) ! 906: { ! 907: struct bfd_proto *p = sk->data; ! 908: log(L_ERR "%s: Notify socket error: %m", p->p.name, err); ! 909: } ! 910: ! 911: static void ! 912: bfd_notify_init(struct bfd_proto *p) ! 913: { ! 914: int pfds[2]; ! 915: sock *sk; ! 916: ! 917: int rv = pipe(pfds); ! 918: if (rv < 0) ! 919: die("pipe: %m"); ! 920: ! 921: sk = sk_new(p->p.pool); ! 922: sk->type = SK_MAGIC; ! 923: sk->rx_hook = bfd_notify_hook; ! 924: sk->err_hook = bfd_noterr_hook; ! 925: sk->fd = pfds[0]; ! 926: sk->data = p; ! 927: if (sk_open(sk) < 0) ! 928: die("bfd: sk_open failed"); ! 929: p->notify_rs = sk; ! 930: ! 931: /* The write sock is not added to any event loop */ ! 932: sk = sk_new(p->p.pool); ! 933: sk->type = SK_MAGIC; ! 934: sk->fd = pfds[1]; ! 935: sk->data = p; ! 936: sk->flags = SKF_THREAD; ! 937: if (sk_open(sk) < 0) ! 938: die("bfd: sk_open failed"); ! 939: p->notify_ws = sk; ! 940: } ! 941: ! 942: ! 943: /* ! 944: * BFD protocol glue ! 945: */ ! 946: ! 947: void ! 948: bfd_init_all(void) ! 949: { ! 950: init_list(&bfd_proto_list); ! 951: init_list(&bfd_wait_list); ! 952: } ! 953: ! 954: static struct proto * ! 955: bfd_init(struct proto_config *c) ! 956: { ! 957: struct proto *p = proto_new(c, sizeof(struct bfd_proto)); ! 958: ! 959: p->neigh_notify = bfd_neigh_notify; ! 960: ! 961: return p; ! 962: } ! 963: ! 964: static int ! 965: bfd_start(struct proto *P) ! 966: { ! 967: struct bfd_proto *p = (struct bfd_proto *) P; ! 968: struct bfd_config *cf = (struct bfd_config *) (P->cf); ! 969: ! 970: p->loop = birdloop_new(); ! 971: p->tpool = rp_new(NULL, "BFD thread root"); ! 972: pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE); ! 973: ! 974: p->session_slab = sl_new(P->pool, sizeof(struct bfd_session)); ! 975: HASH_INIT(p->session_hash_id, P->pool, 8); ! 976: HASH_INIT(p->session_hash_ip, P->pool, 8); ! 977: ! 978: init_list(&p->iface_list); ! 979: ! 980: init_list(&p->notify_list); ! 981: bfd_notify_init(p); ! 982: ! 983: add_tail(&bfd_proto_list, &p->bfd_node); ! 984: ! 985: birdloop_enter(p->loop); ! 986: p->rx_1 = bfd_open_rx_sk(p, 0); ! 987: p->rx_m = bfd_open_rx_sk(p, 1); ! 988: birdloop_leave(p->loop); ! 989: ! 990: bfd_take_requests(p); ! 991: ! 992: struct bfd_neighbor *n; ! 993: WALK_LIST(n, cf->neigh_list) ! 994: bfd_start_neighbor(p, n); ! 995: ! 996: birdloop_start(p->loop); ! 997: ! 998: return PS_UP; ! 999: } ! 1000: ! 1001: ! 1002: static int ! 1003: bfd_shutdown(struct proto *P) ! 1004: { ! 1005: struct bfd_proto *p = (struct bfd_proto *) P; ! 1006: struct bfd_config *cf = (struct bfd_config *) (P->cf); ! 1007: ! 1008: rem_node(&p->bfd_node); ! 1009: ! 1010: birdloop_stop(p->loop); ! 1011: ! 1012: struct bfd_neighbor *n; ! 1013: WALK_LIST(n, cf->neigh_list) ! 1014: bfd_stop_neighbor(p, n); ! 1015: ! 1016: bfd_drop_requests(p); ! 1017: ! 1018: /* FIXME: This is hack */ ! 1019: birdloop_enter(p->loop); ! 1020: rfree(p->tpool); ! 1021: birdloop_leave(p->loop); ! 1022: ! 1023: birdloop_free(p->loop); ! 1024: ! 1025: return PS_DOWN; ! 1026: } ! 1027: ! 1028: static int ! 1029: bfd_reconfigure(struct proto *P, struct proto_config *c) ! 1030: { ! 1031: struct bfd_proto *p = (struct bfd_proto *) P; ! 1032: // struct bfd_config *old = (struct bfd_config *) (P->cf); ! 1033: struct bfd_config *new = (struct bfd_config *) c; ! 1034: struct bfd_iface *ifa; ! 1035: ! 1036: birdloop_mask_wakeups(p->loop); ! 1037: ! 1038: WALK_LIST(ifa, p->iface_list) ! 1039: bfd_reconfigure_iface(p, ifa, new); ! 1040: ! 1041: HASH_WALK(p->session_hash_id, next_id, s) ! 1042: { ! 1043: if (s->ifa->changed) ! 1044: bfd_reconfigure_session(p, s); ! 1045: } ! 1046: HASH_WALK_END; ! 1047: ! 1048: bfd_reconfigure_neighbors(p, new); ! 1049: ! 1050: birdloop_unmask_wakeups(p->loop); ! 1051: ! 1052: return 1; ! 1053: } ! 1054: ! 1055: /* Ensure one instance */ ! 1056: struct bfd_config *bfd_cf; ! 1057: ! 1058: static void ! 1059: bfd_preconfig(struct protocol *P UNUSED, struct config *c UNUSED) ! 1060: { ! 1061: bfd_cf = NULL; ! 1062: } ! 1063: ! 1064: static void ! 1065: bfd_copy_config(struct proto_config *dest, struct proto_config *src UNUSED) ! 1066: { ! 1067: struct bfd_config *d = (struct bfd_config *) dest; ! 1068: // struct bfd_config *s = (struct bfd_config *) src; ! 1069: ! 1070: /* We clean up patt_list and neigh_list, neighbors and ifaces are non-sharable */ ! 1071: init_list(&d->patt_list); ! 1072: init_list(&d->neigh_list); ! 1073: } ! 1074: ! 1075: void ! 1076: bfd_show_sessions(struct proto *P) ! 1077: { ! 1078: byte tbuf[TM_DATETIME_BUFFER_SIZE]; ! 1079: struct bfd_proto *p = (struct bfd_proto *) P; ! 1080: uint state, diag UNUSED; ! 1081: u32 tx_int, timeout; ! 1082: const char *ifname; ! 1083: ! 1084: if (p->p.proto_state != PS_UP) ! 1085: { ! 1086: cli_msg(-1020, "%s: is not up", p->p.name); ! 1087: cli_msg(0, ""); ! 1088: return; ! 1089: } ! 1090: ! 1091: cli_msg(-1020, "%s:", p->p.name); ! 1092: cli_msg(-1020, "%-25s %-10s %-10s %-10s %8s %8s", ! 1093: "IP address", "Interface", "State", "Since", "Interval", "Timeout"); ! 1094: ! 1095: ! 1096: HASH_WALK(p->session_hash_id, next_id, s) ! 1097: { ! 1098: /* FIXME: this is thread-unsafe, but perhaps harmless */ ! 1099: state = s->loc_state; ! 1100: diag = s->loc_diag; ! 1101: ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---"; ! 1102: tx_int = s->last_tx ? (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS) : 0; ! 1103: timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult; ! 1104: ! 1105: state = (state < 4) ? state : 0; ! 1106: tm_format_datetime(tbuf, &config->tf_proto, s->last_state_change); ! 1107: ! 1108: cli_msg(-1020, "%-25I %-10s %-10s %-10s %3u.%03u %3u.%03u", ! 1109: s->addr, ifname, bfd_state_names[state], tbuf, ! 1110: tx_int / 1000, tx_int % 1000, timeout / 1000, timeout % 1000); ! 1111: } ! 1112: HASH_WALK_END; ! 1113: ! 1114: cli_msg(0, ""); ! 1115: } ! 1116: ! 1117: ! 1118: struct protocol proto_bfd = { ! 1119: .name = "BFD", ! 1120: .template = "bfd%d", ! 1121: .config_size = sizeof(struct bfd_config), ! 1122: .init = bfd_init, ! 1123: .start = bfd_start, ! 1124: .shutdown = bfd_shutdown, ! 1125: .reconfigure = bfd_reconfigure, ! 1126: .preconfig = bfd_preconfig, ! 1127: .copy_config = bfd_copy_config, ! 1128: };