File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / bfd / bfd.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 19:50:23 2021 UTC (3 years, 4 months ago) by misho
Branches: bird, MAIN
CVS tags: v1_6_8p3, HEAD
bird 1.6.8

    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:   if (p->p.vrf_set && (p->p.vrf != req->vrf))
  628:     return 0;
  629: 
  630:   struct bfd_session *s = bfd_find_session_by_addr(p, req->addr);
  631:   u8 state, diag;
  632: 
  633:   if (!s)
  634:     s = bfd_add_session(p, req->addr, req->local, req->iface);
  635: 
  636:   rem_node(&req->n);
  637:   add_tail(&s->request_list, &req->n);
  638:   req->session = s;
  639: 
  640:   bfd_lock_sessions(p);
  641:   state = s->loc_state;
  642:   diag = s->loc_diag;
  643:   bfd_unlock_sessions(p);
  644: 
  645:   bfd_request_notify(req, state, diag);
  646: 
  647:   return 1;
  648: }
  649: 
  650: static void
  651: bfd_submit_request(struct bfd_request *req)
  652: {
  653:   node *n;
  654: 
  655:   WALK_LIST(n, bfd_proto_list)
  656:     if (bfd_add_request(SKIP_BACK(struct bfd_proto, bfd_node, n), req))
  657:       return;
  658: 
  659:   rem_node(&req->n);
  660:   add_tail(&bfd_wait_list, &req->n);
  661:   req->session = NULL;
  662:   bfd_request_notify(req, BFD_STATE_ADMIN_DOWN, 0);
  663: }
  664: 
  665: static void
  666: bfd_take_requests(struct bfd_proto *p)
  667: {
  668:   node *n, *nn;
  669: 
  670:   WALK_LIST_DELSAFE(n, nn, bfd_wait_list)
  671:     bfd_add_request(p, SKIP_BACK(struct bfd_request, n, n));
  672: }
  673: 
  674: static void
  675: bfd_drop_requests(struct bfd_proto *p)
  676: {
  677:   node *n;
  678: 
  679:   HASH_WALK(p->session_hash_id, next_id, s)
  680:   {
  681:     /* We assume that p is not in bfd_proto_list */
  682:     WALK_LIST_FIRST(n, s->request_list)
  683:       bfd_submit_request(SKIP_BACK(struct bfd_request, n, n));
  684:   }
  685:   HASH_WALK_END;
  686: }
  687: 
  688: static struct resclass bfd_request_class;
  689: 
  690: struct bfd_request *
  691: bfd_request_session(pool *p, ip_addr addr, ip_addr local,
  692: 		    struct iface *iface, struct iface *vrf,
  693: 		    void (*hook)(struct bfd_request *), void *data)
  694: {
  695:   struct bfd_request *req = ralloc(p, &bfd_request_class);
  696: 
  697:   /* Hack: self-link req->n, we will call rem_node() on it */
  698:   req->n.prev = req->n.next = &req->n;
  699: 
  700:   req->addr = addr;
  701:   req->local = local;
  702:   req->iface = iface;
  703:   req->vrf = vrf;
  704: 
  705:   bfd_submit_request(req);
  706: 
  707:   req->hook = hook;
  708:   req->data = data;
  709: 
  710:   return req;
  711: }
  712: 
  713: static void
  714: bfd_request_free(resource *r)
  715: {
  716:   struct bfd_request *req = (struct bfd_request *) r;
  717:   struct bfd_session *s = req->session;
  718: 
  719:   rem_node(&req->n);
  720: 
  721:   /* Remove the session if there is no request for it. Skip that if
  722:      inside notify hooks, will be handled by bfd_notify_hook() itself */
  723: 
  724:   if (s && EMPTY_LIST(s->request_list) && !s->notify_running)
  725:     bfd_remove_session(s->ifa->bfd, s);
  726: }
  727: 
  728: static void
  729: bfd_request_dump(resource *r)
  730: {
  731:   struct bfd_request *req = (struct bfd_request *) r;
  732: 
  733:   debug("(code %p, data %p)\n", req->hook, req->data);
  734: }
  735: 
  736: static struct resclass bfd_request_class = {
  737:   "BFD request",
  738:   sizeof(struct bfd_request),
  739:   bfd_request_free,
  740:   bfd_request_dump,
  741:   NULL,
  742:   NULL
  743: };
  744: 
  745: 
  746: /*
  747:  *	BFD neighbors
  748:  */
  749: 
  750: static void
  751: bfd_neigh_notify(struct neighbor *nb)
  752: {
  753:   struct bfd_proto *p = (struct bfd_proto *) nb->proto;
  754:   struct bfd_neighbor *n = nb->data;
  755: 
  756:   if (!n)
  757:     return;
  758: 
  759:   if ((nb->scope > 0) && !n->req)
  760:   {
  761:     ip_addr local = ipa_nonzero(n->local) ? n->local : nb->ifa->ip;
  762:     n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL);
  763:   }
  764: 
  765:   if ((nb->scope <= 0) && n->req)
  766:   {
  767:     rfree(n->req);
  768:     n->req = NULL;
  769:   }
  770: }
  771: 
  772: static void
  773: bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
  774: {
  775:   n->active = 1;
  776: 
  777:   if (n->multihop)
  778:   {
  779:     n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL);
  780:     return;
  781:   }
  782: 
  783:   struct neighbor *nb = neigh_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>