Annotation of embedaddon/bird/proto/bfd/bfd.c, revision 1.1
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: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>