Annotation of embedaddon/bird2/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. A birdloop is
! 68: * associated with a thread (field @thread) in which event hooks are executed.
! 69: * Most functions for setting event sources (like sk_start() or tm_start()) must
! 70: * be called from the context of that thread. Birdloop allows to temporarily
! 71: * acquire the context of that thread for the main thread by calling
! 72: * birdloop_enter() and then birdloop_leave(), which also ensures mutual
! 73: * exclusion with all event hooks. Note that resources associated with a
! 74: * birdloop (like timers) should be attached to the independent resource pool,
! 75: * detached from the main resource tree.
! 76: *
! 77: * There are two kinds of interaction between the BFD core (running in the BFD
! 78: * thread) and the rest of BFD (running in the main thread). The first kind are
! 79: * configuration calls from main thread to the BFD thread (like bfd_add_session()).
! 80: * These calls are synchronous and use birdloop_enter() mechanism for mutual
! 81: * exclusion. The second kind is a notification about session changes from the
! 82: * BFD thread to the main thread. This is done in an asynchronous way, sesions
! 83: * with pending notifications are linked (in the BFD thread) to @notify_list in
! 84: * &bfd_proto, and then bfd_notify_hook() in the main thread is activated using
! 85: * bfd_notify_kick() and a pipe. The hook then processes scheduled sessions and
! 86: * calls hooks from associated BFD requests. This @notify_list (and state fields
! 87: * in structure &bfd_session) is protected by a spinlock in &bfd_proto and
! 88: * functions bfd_lock_sessions() / bfd_unlock_sessions().
! 89: *
! 90: * There are few data races (accessing @p->p.debug from TRACE() from the BFD
! 91: * thread and accessing some some private fields of %bfd_session from
! 92: * bfd_show_sessions() from the main thread, but these are harmless (i hope).
! 93: *
! 94: * TODO: document functions and access restrictions for fields in BFD structures.
! 95: *
! 96: * Supported standards:
! 97: * - RFC 5880 - main BFD standard
! 98: * - RFC 5881 - BFD for IP links
! 99: * - RFC 5882 - generic application of BFD
! 100: * - RFC 5883 - BFD for multihop paths
! 101: */
! 102:
! 103: #include "bfd.h"
! 104:
! 105:
! 106: #define HASH_ID_KEY(n) n->loc_id
! 107: #define HASH_ID_NEXT(n) n->next_id
! 108: #define HASH_ID_EQ(a,b) a == b
! 109: #define HASH_ID_FN(k) k
! 110:
! 111: #define HASH_IP_KEY(n) n->addr
! 112: #define HASH_IP_NEXT(n) n->next_ip
! 113: #define HASH_IP_EQ(a,b) ipa_equal(a,b)
! 114: #define HASH_IP_FN(k) ipa_hash(k)
! 115:
! 116: static list bfd_proto_list;
! 117: static list bfd_wait_list;
! 118:
! 119: const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" };
! 120:
! 121: static void bfd_session_set_min_tx(struct bfd_session *s, u32 val);
! 122: static struct bfd_iface *bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface);
! 123: static void bfd_free_iface(struct bfd_iface *ifa);
! 124: static inline void bfd_notify_kick(struct bfd_proto *p);
! 125:
! 126:
! 127: /*
! 128: * BFD sessions
! 129: */
! 130:
! 131: static void
! 132: bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
! 133: {
! 134: struct bfd_proto *p = s->ifa->bfd;
! 135: uint old_state = s->loc_state;
! 136: int notify;
! 137:
! 138: if (state == old_state)
! 139: return;
! 140:
! 141: TRACE(D_EVENTS, "Session to %I changed state from %s to %s",
! 142: s->addr, bfd_state_names[old_state], bfd_state_names[state]);
! 143:
! 144: bfd_lock_sessions(p);
! 145: s->loc_state = state;
! 146: s->loc_diag = diag;
! 147: s->last_state_change = current_time();
! 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: tm_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: tm_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 || !tm_active(s->tx_timer))
! 215: {
! 216: s->last_tx = 0;
! 217: tm_start(s->tx_timer, 0);
! 218: }
! 219:
! 220: return;
! 221:
! 222: stop:
! 223: tm_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(timer *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(timer *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 = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
! 436: s->hold_timer = tm_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 = current_time();
! 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: if (p->p.vrf_set && (p->p.vrf != req->vrf))
! 628: return 0;
! 629:
! 630: struct bfd_session *s = bfd_find_session_by_addr(p, req->addr);
! 631: u8 state, diag;
! 632:
! 633: if (!s)
! 634: s = bfd_add_session(p, req->addr, req->local, req->iface);
! 635:
! 636: rem_node(&req->n);
! 637: add_tail(&s->request_list, &req->n);
! 638: req->session = s;
! 639:
! 640: bfd_lock_sessions(p);
! 641: state = s->loc_state;
! 642: diag = s->loc_diag;
! 643: bfd_unlock_sessions(p);
! 644:
! 645: bfd_request_notify(req, state, diag);
! 646:
! 647: return 1;
! 648: }
! 649:
! 650: static void
! 651: bfd_submit_request(struct bfd_request *req)
! 652: {
! 653: node *n;
! 654:
! 655: WALK_LIST(n, bfd_proto_list)
! 656: if (bfd_add_request(SKIP_BACK(struct bfd_proto, bfd_node, n), req))
! 657: return;
! 658:
! 659: rem_node(&req->n);
! 660: add_tail(&bfd_wait_list, &req->n);
! 661: req->session = NULL;
! 662: bfd_request_notify(req, BFD_STATE_ADMIN_DOWN, 0);
! 663: }
! 664:
! 665: static void
! 666: bfd_take_requests(struct bfd_proto *p)
! 667: {
! 668: node *n, *nn;
! 669:
! 670: WALK_LIST_DELSAFE(n, nn, bfd_wait_list)
! 671: bfd_add_request(p, SKIP_BACK(struct bfd_request, n, n));
! 672: }
! 673:
! 674: static void
! 675: bfd_drop_requests(struct bfd_proto *p)
! 676: {
! 677: node *n;
! 678:
! 679: HASH_WALK(p->session_hash_id, next_id, s)
! 680: {
! 681: /* We assume that p is not in bfd_proto_list */
! 682: WALK_LIST_FIRST(n, s->request_list)
! 683: bfd_submit_request(SKIP_BACK(struct bfd_request, n, n));
! 684: }
! 685: HASH_WALK_END;
! 686: }
! 687:
! 688: static struct resclass bfd_request_class;
! 689:
! 690: struct bfd_request *
! 691: bfd_request_session(pool *p, ip_addr addr, ip_addr local,
! 692: struct iface *iface, struct iface *vrf,
! 693: void (*hook)(struct bfd_request *), void *data)
! 694: {
! 695: struct bfd_request *req = ralloc(p, &bfd_request_class);
! 696:
! 697: /* Hack: self-link req->n, we will call rem_node() on it */
! 698: req->n.prev = req->n.next = &req->n;
! 699:
! 700: req->addr = addr;
! 701: req->local = local;
! 702: req->iface = iface;
! 703: req->vrf = vrf;
! 704:
! 705: bfd_submit_request(req);
! 706:
! 707: req->hook = hook;
! 708: req->data = data;
! 709:
! 710: return req;
! 711: }
! 712:
! 713: static void
! 714: bfd_request_free(resource *r)
! 715: {
! 716: struct bfd_request *req = (struct bfd_request *) r;
! 717: struct bfd_session *s = req->session;
! 718:
! 719: rem_node(&req->n);
! 720:
! 721: /* Remove the session if there is no request for it. Skip that if
! 722: inside notify hooks, will be handled by bfd_notify_hook() itself */
! 723:
! 724: if (s && EMPTY_LIST(s->request_list) && !s->notify_running)
! 725: bfd_remove_session(s->ifa->bfd, s);
! 726: }
! 727:
! 728: static void
! 729: bfd_request_dump(resource *r)
! 730: {
! 731: struct bfd_request *req = (struct bfd_request *) r;
! 732:
! 733: debug("(code %p, data %p)\n", req->hook, req->data);
! 734: }
! 735:
! 736: static struct resclass bfd_request_class = {
! 737: "BFD request",
! 738: sizeof(struct bfd_request),
! 739: bfd_request_free,
! 740: bfd_request_dump,
! 741: NULL,
! 742: NULL
! 743: };
! 744:
! 745:
! 746: /*
! 747: * BFD neighbors
! 748: */
! 749:
! 750: static void
! 751: bfd_neigh_notify(struct neighbor *nb)
! 752: {
! 753: struct bfd_proto *p = (struct bfd_proto *) nb->proto;
! 754: struct bfd_neighbor *n = nb->data;
! 755:
! 756: if (!n)
! 757: return;
! 758:
! 759: if ((nb->scope > 0) && !n->req)
! 760: {
! 761: ip_addr local = ipa_nonzero(n->local) ? n->local : nb->ifa->ip;
! 762: n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL);
! 763: }
! 764:
! 765: if ((nb->scope <= 0) && n->req)
! 766: {
! 767: rfree(n->req);
! 768: n->req = NULL;
! 769: }
! 770: }
! 771:
! 772: static void
! 773: bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
! 774: {
! 775: n->active = 1;
! 776:
! 777: if (n->multihop)
! 778: {
! 779: n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL);
! 780: return;
! 781: }
! 782:
! 783: struct neighbor *nb = neigh_find(&p->p, n->addr, n->iface, NEF_STICKY);
! 784: if (!nb)
! 785: {
! 786: log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface);
! 787: return;
! 788: }
! 789:
! 790: if (nb->data)
! 791: {
! 792: log(L_ERR "%s: Duplicate neighbor %I", p->p.name, n->addr);
! 793: return;
! 794: }
! 795:
! 796: n->neigh = nb;
! 797: nb->data = n;
! 798:
! 799: if (nb->scope > 0)
! 800: bfd_neigh_notify(nb);
! 801: else
! 802: TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, n->iface);
! 803: }
! 804:
! 805: static void
! 806: bfd_stop_neighbor(struct bfd_proto *p UNUSED, struct bfd_neighbor *n)
! 807: {
! 808: if (n->neigh)
! 809: n->neigh->data = NULL;
! 810: n->neigh = NULL;
! 811:
! 812: rfree(n->req);
! 813: n->req = NULL;
! 814: }
! 815:
! 816: static inline int
! 817: bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y)
! 818: {
! 819: return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) &&
! 820: (x->iface == y->iface) && (x->multihop == y->multihop);
! 821: }
! 822:
! 823: static void
! 824: bfd_reconfigure_neighbors(struct bfd_proto *p, struct bfd_config *new)
! 825: {
! 826: struct bfd_config *old = (struct bfd_config *) (p->p.cf);
! 827: struct bfd_neighbor *on, *nn;
! 828:
! 829: WALK_LIST(on, old->neigh_list)
! 830: {
! 831: WALK_LIST(nn, new->neigh_list)
! 832: if (bfd_same_neighbor(nn, on))
! 833: {
! 834: nn->neigh = on->neigh;
! 835: if (nn->neigh)
! 836: nn->neigh->data = nn;
! 837:
! 838: nn->req = on->req;
! 839: nn->active = 1;
! 840: goto next;
! 841: }
! 842:
! 843: bfd_stop_neighbor(p, on);
! 844: next:;
! 845: }
! 846:
! 847: WALK_LIST(nn, new->neigh_list)
! 848: if (!nn->active)
! 849: bfd_start_neighbor(p, nn);
! 850: }
! 851:
! 852:
! 853: /*
! 854: * BFD notify socket
! 855: */
! 856:
! 857: /* This core notify code should be replaced after main loop transition to birdloop */
! 858:
! 859: int pipe(int pipefd[2]);
! 860: void pipe_drain(int fd);
! 861: void pipe_kick(int fd);
! 862:
! 863: static int
! 864: bfd_notify_hook(sock *sk, uint len UNUSED)
! 865: {
! 866: struct bfd_proto *p = sk->data;
! 867: struct bfd_session *s;
! 868: list tmp_list;
! 869: u8 state, diag;
! 870: node *n, *nn;
! 871:
! 872: pipe_drain(sk->fd);
! 873:
! 874: bfd_lock_sessions(p);
! 875: init_list(&tmp_list);
! 876: add_tail_list(&tmp_list, &p->notify_list);
! 877: init_list(&p->notify_list);
! 878: bfd_unlock_sessions(p);
! 879:
! 880: WALK_LIST_FIRST(s, tmp_list)
! 881: {
! 882: bfd_lock_sessions(p);
! 883: rem_node(&s->n);
! 884: state = s->loc_state;
! 885: diag = s->loc_diag;
! 886: bfd_unlock_sessions(p);
! 887:
! 888: s->notify_running = 1;
! 889: WALK_LIST_DELSAFE(n, nn, s->request_list)
! 890: bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag);
! 891: s->notify_running = 0;
! 892:
! 893: /* Remove the session if all requests were removed in notify hooks */
! 894: if (EMPTY_LIST(s->request_list))
! 895: bfd_remove_session(p, s);
! 896: }
! 897:
! 898: return 0;
! 899: }
! 900:
! 901: static inline void
! 902: bfd_notify_kick(struct bfd_proto *p)
! 903: {
! 904: pipe_kick(p->notify_ws->fd);
! 905: }
! 906:
! 907: static void
! 908: bfd_noterr_hook(sock *sk, int err)
! 909: {
! 910: struct bfd_proto *p = sk->data;
! 911: log(L_ERR "%s: Notify socket error: %m", p->p.name, err);
! 912: }
! 913:
! 914: static void
! 915: bfd_notify_init(struct bfd_proto *p)
! 916: {
! 917: int pfds[2];
! 918: sock *sk;
! 919:
! 920: int rv = pipe(pfds);
! 921: if (rv < 0)
! 922: die("pipe: %m");
! 923:
! 924: sk = sk_new(p->p.pool);
! 925: sk->type = SK_MAGIC;
! 926: sk->rx_hook = bfd_notify_hook;
! 927: sk->err_hook = bfd_noterr_hook;
! 928: sk->fd = pfds[0];
! 929: sk->data = p;
! 930: if (sk_open(sk) < 0)
! 931: die("bfd: sk_open failed");
! 932: p->notify_rs = sk;
! 933:
! 934: /* The write sock is not added to any event loop */
! 935: sk = sk_new(p->p.pool);
! 936: sk->type = SK_MAGIC;
! 937: sk->fd = pfds[1];
! 938: sk->data = p;
! 939: sk->flags = SKF_THREAD;
! 940: if (sk_open(sk) < 0)
! 941: die("bfd: sk_open failed");
! 942: p->notify_ws = sk;
! 943: }
! 944:
! 945:
! 946: /*
! 947: * BFD protocol glue
! 948: */
! 949:
! 950: void
! 951: bfd_init_all(void)
! 952: {
! 953: init_list(&bfd_proto_list);
! 954: init_list(&bfd_wait_list);
! 955: }
! 956:
! 957: static struct proto *
! 958: bfd_init(struct proto_config *c)
! 959: {
! 960: struct proto *p = proto_new(c);
! 961:
! 962: p->neigh_notify = bfd_neigh_notify;
! 963:
! 964: return p;
! 965: }
! 966:
! 967: static int
! 968: bfd_start(struct proto *P)
! 969: {
! 970: struct bfd_proto *p = (struct bfd_proto *) P;
! 971: struct bfd_config *cf = (struct bfd_config *) (P->cf);
! 972:
! 973: p->loop = birdloop_new();
! 974: p->tpool = rp_new(NULL, "BFD thread root");
! 975: pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
! 976:
! 977: p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
! 978: HASH_INIT(p->session_hash_id, P->pool, 8);
! 979: HASH_INIT(p->session_hash_ip, P->pool, 8);
! 980:
! 981: init_list(&p->iface_list);
! 982:
! 983: init_list(&p->notify_list);
! 984: bfd_notify_init(p);
! 985:
! 986: add_tail(&bfd_proto_list, &p->bfd_node);
! 987:
! 988: birdloop_enter(p->loop);
! 989: p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
! 990: p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
! 991: p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
! 992: p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
! 993: birdloop_leave(p->loop);
! 994:
! 995: bfd_take_requests(p);
! 996:
! 997: struct bfd_neighbor *n;
! 998: WALK_LIST(n, cf->neigh_list)
! 999: bfd_start_neighbor(p, n);
! 1000:
! 1001: birdloop_start(p->loop);
! 1002:
! 1003: return PS_UP;
! 1004: }
! 1005:
! 1006:
! 1007: static int
! 1008: bfd_shutdown(struct proto *P)
! 1009: {
! 1010: struct bfd_proto *p = (struct bfd_proto *) P;
! 1011: struct bfd_config *cf = (struct bfd_config *) (P->cf);
! 1012:
! 1013: rem_node(&p->bfd_node);
! 1014:
! 1015: birdloop_stop(p->loop);
! 1016:
! 1017: struct bfd_neighbor *n;
! 1018: WALK_LIST(n, cf->neigh_list)
! 1019: bfd_stop_neighbor(p, n);
! 1020:
! 1021: bfd_drop_requests(p);
! 1022:
! 1023: /* FIXME: This is hack */
! 1024: birdloop_enter(p->loop);
! 1025: rfree(p->tpool);
! 1026: birdloop_leave(p->loop);
! 1027:
! 1028: birdloop_free(p->loop);
! 1029:
! 1030: return PS_DOWN;
! 1031: }
! 1032:
! 1033: static int
! 1034: bfd_reconfigure(struct proto *P, struct proto_config *c)
! 1035: {
! 1036: struct bfd_proto *p = (struct bfd_proto *) P;
! 1037: // struct bfd_config *old = (struct bfd_config *) (P->cf);
! 1038: struct bfd_config *new = (struct bfd_config *) c;
! 1039: struct bfd_iface *ifa;
! 1040:
! 1041: birdloop_mask_wakeups(p->loop);
! 1042:
! 1043: WALK_LIST(ifa, p->iface_list)
! 1044: bfd_reconfigure_iface(p, ifa, new);
! 1045:
! 1046: HASH_WALK(p->session_hash_id, next_id, s)
! 1047: {
! 1048: if (s->ifa->changed)
! 1049: bfd_reconfigure_session(p, s);
! 1050: }
! 1051: HASH_WALK_END;
! 1052:
! 1053: bfd_reconfigure_neighbors(p, new);
! 1054:
! 1055: birdloop_unmask_wakeups(p->loop);
! 1056:
! 1057: return 1;
! 1058: }
! 1059:
! 1060: static void
! 1061: bfd_copy_config(struct proto_config *dest, struct proto_config *src UNUSED)
! 1062: {
! 1063: struct bfd_config *d = (struct bfd_config *) dest;
! 1064: // struct bfd_config *s = (struct bfd_config *) src;
! 1065:
! 1066: /* We clean up patt_list and neigh_list, neighbors and ifaces are non-sharable */
! 1067: init_list(&d->patt_list);
! 1068: init_list(&d->neigh_list);
! 1069: }
! 1070:
! 1071: void
! 1072: bfd_show_sessions(struct proto *P)
! 1073: {
! 1074: byte tbuf[TM_DATETIME_BUFFER_SIZE];
! 1075: struct bfd_proto *p = (struct bfd_proto *) P;
! 1076: uint state, diag UNUSED;
! 1077: btime tx_int, timeout;
! 1078: const char *ifname;
! 1079:
! 1080: if (p->p.proto_state != PS_UP)
! 1081: {
! 1082: cli_msg(-1020, "%s: is not up", p->p.name);
! 1083: cli_msg(0, "");
! 1084: return;
! 1085: }
! 1086:
! 1087: cli_msg(-1020, "%s:", p->p.name);
! 1088: cli_msg(-1020, "%-25s %-10s %-10s %-12s %8s %8s",
! 1089: "IP address", "Interface", "State", "Since", "Interval", "Timeout");
! 1090:
! 1091:
! 1092: HASH_WALK(p->session_hash_id, next_id, s)
! 1093: {
! 1094: /* FIXME: this is thread-unsafe, but perhaps harmless */
! 1095: state = s->loc_state;
! 1096: diag = s->loc_diag;
! 1097: ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---";
! 1098: tx_int = s->last_tx ? MAX(s->des_min_tx_int, s->rem_min_rx_int) : 0;
! 1099: timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
! 1100:
! 1101: state = (state < 4) ? state : 0;
! 1102: tm_format_time(tbuf, &config->tf_proto, s->last_state_change);
! 1103:
! 1104: cli_msg(-1020, "%-25I %-10s %-10s %-12s %7t %7t",
! 1105: s->addr, ifname, bfd_state_names[state], tbuf, tx_int, timeout);
! 1106: }
! 1107: HASH_WALK_END;
! 1108:
! 1109: cli_msg(0, "");
! 1110: }
! 1111:
! 1112:
! 1113: struct protocol proto_bfd = {
! 1114: .name = "BFD",
! 1115: .template = "bfd%d",
! 1116: .class = PROTOCOL_BFD,
! 1117: .proto_size = sizeof(struct bfd_proto),
! 1118: .config_size = sizeof(struct bfd_config),
! 1119: .init = bfd_init,
! 1120: .start = bfd_start,
! 1121: .shutdown = bfd_shutdown,
! 1122: .reconfigure = bfd_reconfigure,
! 1123: .copy_config = bfd_copy_config,
! 1124: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>