Annotation of embedaddon/bird/proto/bfd/bfd.c, revision 1.1.1.2

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: {
1.1.1.2 ! misho     627:   if (p->p.vrf_set && (p->p.vrf != req->vrf))
        !           628:     return 0;
        !           629: 
1.1       misho     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 *
1.1.1.2 ! misho     691: bfd_request_session(pool *p, ip_addr addr, ip_addr local,
        !           692:                    struct iface *iface, struct iface *vrf,
1.1       misho     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;
1.1.1.2 ! misho     703:   req->vrf = vrf;
1.1       misho     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;
1.1.1.2 ! misho     762:     n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL);
1.1       misho     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:   {
1.1.1.2 ! misho     779:     n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL);
1.1       misho     780:     return;
                    781:   }
                    782: 
                    783:   struct neighbor *nb = neigh_find2(&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:        return;
                    841:       }
                    842: 
                    843:     bfd_stop_neighbor(p, on);
                    844:   }
                    845: 
                    846:   WALK_LIST(nn, new->neigh_list)
                    847:     if (!nn->active)
                    848:       bfd_start_neighbor(p, nn);
                    849: }
                    850: 
                    851: 
                    852: /*
                    853:  *     BFD notify socket
                    854:  */
                    855: 
                    856: /* This core notify code should be replaced after main loop transition to birdloop */
                    857: 
                    858: int pipe(int pipefd[2]);
                    859: void pipe_drain(int fd);
                    860: void pipe_kick(int fd);
                    861: 
                    862: static int
                    863: bfd_notify_hook(sock *sk, uint len UNUSED)
                    864: {
                    865:   struct bfd_proto *p = sk->data;
                    866:   struct bfd_session *s;
                    867:   list tmp_list;
                    868:   u8 state, diag;
                    869:   node *n, *nn;
                    870: 
                    871:   pipe_drain(sk->fd);
                    872: 
                    873:   bfd_lock_sessions(p);
                    874:   init_list(&tmp_list);
                    875:   add_tail_list(&tmp_list, &p->notify_list);
                    876:   init_list(&p->notify_list);
                    877:   bfd_unlock_sessions(p);
                    878: 
                    879:   WALK_LIST_FIRST(s, tmp_list)
                    880:   {
                    881:     bfd_lock_sessions(p);
                    882:     rem_node(&s->n);
                    883:     state = s->loc_state;
                    884:     diag = s->loc_diag;
                    885:     bfd_unlock_sessions(p);
                    886: 
                    887:     /* FIXME: convert to btime and move to bfd_session_update_state() */
                    888:     s->last_state_change = now;
                    889: 
                    890:     s->notify_running = 1;
                    891:     WALK_LIST_DELSAFE(n, nn, s->request_list)
                    892:       bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag);
                    893:     s->notify_running = 0;
                    894: 
                    895:     /* Remove the session if all requests were removed in notify hooks */
                    896:     if (EMPTY_LIST(s->request_list))
                    897:       bfd_remove_session(p, s);
                    898:   }
                    899: 
                    900:   return 0;
                    901: }
                    902: 
                    903: static inline void
                    904: bfd_notify_kick(struct bfd_proto *p)
                    905: {
                    906:   pipe_kick(p->notify_ws->fd);
                    907: }
                    908: 
                    909: static void
                    910: bfd_noterr_hook(sock *sk, int err)
                    911: {
                    912:   struct bfd_proto *p = sk->data;
                    913:   log(L_ERR "%s: Notify socket error: %m", p->p.name, err);
                    914: }
                    915: 
                    916: static void
                    917: bfd_notify_init(struct bfd_proto *p)
                    918: {
                    919:   int pfds[2];
                    920:   sock *sk;
                    921: 
                    922:   int rv = pipe(pfds);
                    923:   if (rv < 0)
                    924:     die("pipe: %m");
                    925: 
                    926:   sk = sk_new(p->p.pool);
                    927:   sk->type = SK_MAGIC;
                    928:   sk->rx_hook = bfd_notify_hook;
                    929:   sk->err_hook = bfd_noterr_hook;
                    930:   sk->fd = pfds[0];
                    931:   sk->data = p;
                    932:   if (sk_open(sk) < 0)
                    933:     die("bfd: sk_open failed");
                    934:   p->notify_rs = sk;
                    935: 
                    936:   /* The write sock is not added to any event loop */
                    937:   sk = sk_new(p->p.pool);
                    938:   sk->type = SK_MAGIC;
                    939:   sk->fd = pfds[1];
                    940:   sk->data = p;
                    941:   sk->flags = SKF_THREAD;
                    942:   if (sk_open(sk) < 0)
                    943:     die("bfd: sk_open failed");
                    944:   p->notify_ws = sk;
                    945: }
                    946: 
                    947: 
                    948: /*
                    949:  *     BFD protocol glue
                    950:  */
                    951: 
                    952: void
                    953: bfd_init_all(void)
                    954: {
                    955:   init_list(&bfd_proto_list);
                    956:   init_list(&bfd_wait_list);
                    957: }
                    958: 
                    959: static struct proto *
                    960: bfd_init(struct proto_config *c)
                    961: {
                    962:   struct proto *p = proto_new(c, sizeof(struct bfd_proto));
                    963: 
                    964:   p->neigh_notify = bfd_neigh_notify;
                    965: 
                    966:   return p;
                    967: }
                    968: 
                    969: static int
                    970: bfd_start(struct proto *P)
                    971: {
                    972:   struct bfd_proto *p = (struct bfd_proto *) P;
                    973:   struct bfd_config *cf = (struct bfd_config *) (P->cf);
                    974: 
                    975:   p->loop = birdloop_new();
                    976:   p->tpool = rp_new(NULL, "BFD thread root");
                    977:   pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
                    978: 
                    979:   p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
                    980:   HASH_INIT(p->session_hash_id, P->pool, 8);
                    981:   HASH_INIT(p->session_hash_ip, P->pool, 8);
                    982: 
                    983:   init_list(&p->iface_list);
                    984: 
                    985:   init_list(&p->notify_list);
                    986:   bfd_notify_init(p);
                    987: 
                    988:   add_tail(&bfd_proto_list, &p->bfd_node);
                    989: 
                    990:   birdloop_enter(p->loop);
                    991:   p->rx_1 = bfd_open_rx_sk(p, 0);
                    992:   p->rx_m = bfd_open_rx_sk(p, 1);
                    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:   u32 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 %-10s  %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) TO_MS) : 0;
                   1099:     timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult;
                   1100: 
                   1101:     state = (state < 4) ? state : 0;
                   1102:     tm_format_datetime(tbuf, &config->tf_proto, s->last_state_change);
                   1103: 
                   1104:     cli_msg(-1020, "%-25I %-10s %-10s %-10s  %3u.%03u  %3u.%03u",
                   1105:            s->addr, ifname, bfd_state_names[state], tbuf,
                   1106:            tx_int / 1000, tx_int % 1000, timeout / 1000, timeout % 1000);
                   1107:   }
                   1108:   HASH_WALK_END;
                   1109: 
                   1110:   cli_msg(0, "");
                   1111: }
                   1112: 
                   1113: 
                   1114: struct protocol proto_bfd = {
                   1115:   .name =              "BFD",
                   1116:   .template =          "bfd%d",
                   1117:   .config_size =       sizeof(struct bfd_config),
                   1118:   .init =              bfd_init,
                   1119:   .start =             bfd_start,
                   1120:   .shutdown =          bfd_shutdown,
                   1121:   .reconfigure =       bfd_reconfigure,
                   1122:   .copy_config =       bfd_copy_config,
                   1123: };

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>