Annotation of embedaddon/bird/proto/bfd/bfd.c, revision 1.1.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>