File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / bfd / bfd.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Aug 22 12:33:54 2017 UTC (6 years, 10 months ago) by misho
Branches: bird, MAIN
CVS tags: v1_6_3p0, v1_6_3, HEAD
bird 1.6.3

    1: /*
    2:  *	BIRD -- Bidirectional Forwarding Detection (BFD)
    3:  *
    4:  *	Can be freely distributed and used under the terms of the GNU GPL.
    5:  */
    6: 
    7: /**
    8:  * DOC: Bidirectional Forwarding Detection
    9:  *
   10:  * The BFD protocol is implemented in three files: |bfd.c| containing the
   11:  * protocol logic and the protocol glue with BIRD core, |packets.c| handling BFD
   12:  * packet processing, RX, TX and protocol sockets. |io.c| then contains generic
   13:  * code for the event loop, threads and event sources (sockets, microsecond
   14:  * timers). This generic code will be merged to the main BIRD I/O code in the
   15:  * future.
   16:  *
   17:  * The BFD implementation uses a separate thread with an internal event loop for
   18:  * handling the protocol logic, which requires high-res and low-latency timing,
   19:  * so it is not affected by the rest of BIRD, which has several low-granularity
   20:  * hooks in the main loop, uses second-based timers and cannot offer good
   21:  * latency. The core of BFD protocol (the code related to BFD sessions,
   22:  * interfaces and packets) runs in the BFD thread, while the rest (the code
   23:  * related to BFD requests, BFD neighbors and the protocol glue) runs in the
   24:  * main thread.
   25:  *
   26:  * BFD sessions are represented by structure &bfd_session that contains a state
   27:  * related to the session and two timers (TX timer for periodic packets and hold
   28:  * timer for session timeout). These sessions are allocated from @session_slab
   29:  * and are accessible by two hash tables, @session_hash_id (by session ID) and
   30:  * @session_hash_ip (by IP addresses of neighbors). Slab and both hashes are in
   31:  * the main protocol structure &bfd_proto. The protocol logic related to BFD
   32:  * sessions is implemented in internal functions bfd_session_*(), which are
   33:  * expected to be called from the context of BFD thread, and external functions
   34:  * bfd_add_session(), bfd_remove_session() and bfd_reconfigure_session(), which
   35:  * form an interface to the BFD core for the rest and are expected to be called
   36:  * from the context of main thread.
   37:  *
   38:  * Each BFD session has an associated BFD interface, represented by structure
   39:  * &bfd_iface. A BFD interface contains a socket used for TX (the one for RX is
   40:  * shared in &bfd_proto), an interface configuration and reference counter.
   41:  * Compared to interface structures of other protocols, these structures are not
   42:  * created and removed based on interface notification events, but according to
   43:  * the needs of BFD sessions. When a new session is created, it requests a
   44:  * proper BFD interface by function bfd_get_iface(), which either finds an
   45:  * existing one in &iface_list (from &bfd_proto) or allocates a new one. When a
   46:  * session is removed, an associated iface is discharged by bfd_free_iface().
   47:  *
   48:  * BFD requests are the external API for the other protocols. When a protocol
   49:  * wants a BFD session, it calls bfd_request_session(), which creates a
   50:  * structure &bfd_request containing approprite information and an notify hook.
   51:  * This structure is a resource associated with the caller's resource pool. When
   52:  * a BFD protocol is available, a BFD request is submitted to the protocol, an
   53:  * appropriate BFD session is found or created and the request is attached to
   54:  * the session. When a session changes state, all attached requests (and related
   55:  * protocols) are notified. Note that BFD requests do not depend on BFD protocol
   56:  * running. When the BFD protocol is stopped or removed (or not available from
   57:  * beginning), related BFD requests are stored in @bfd_wait_list, where waits
   58:  * for a new protocol.
   59:  *
   60:  * BFD neighbors are just a way to statically configure BFD sessions without
   61:  * requests from other protocol. Structures &bfd_neighbor are part of BFD
   62:  * configuration (like static routes in the static protocol). BFD neighbors are
   63:  * handled by BFD protocol like it is a BFD client -- when a BFD neighbor is
   64:  * ready, the protocol just creates a BFD request like any other protocol.
   65:  *
   66:  * The protocol uses a new generic event loop (structure &birdloop) from |io.c|,
   67:  * which supports sockets, timers and events like the main loop. Timers
   68:  * (structure &timer2) are new microsecond based timers, while sockets and
   69:  * events are the same. A birdloop is associated with a thread (field @thread)
   70:  * in which event hooks are executed. Most functions for setting event sources
   71:  * (like sk_start() or tm2_start()) must be called from the context of that
   72:  * thread. Birdloop allows to temporarily acquire the context of that thread for
   73:  * the main thread by calling birdloop_enter() and then birdloop_leave(), which
   74:  * also ensures mutual exclusion with all event hooks. Note that resources
   75:  * associated with a birdloop (like timers) should be attached to the
   76:  * independent resource pool, detached from the main resource tree.
   77:  *
   78:  * There are two kinds of interaction between the BFD core (running in the BFD
   79:  * thread) and the rest of BFD (running in the main thread). The first kind are
   80:  * configuration calls from main thread to the BFD thread (like bfd_add_session()).
   81:  * These calls are synchronous and use birdloop_enter() mechanism for mutual
   82:  * exclusion. The second kind is a notification about session changes from the
   83:  * BFD thread to the main thread. This is done in an asynchronous way, sesions
   84:  * with pending notifications are linked (in the BFD thread) to @notify_list in
   85:  * &bfd_proto, and then bfd_notify_hook() in the main thread is activated using
   86:  * bfd_notify_kick() and a pipe. The hook then processes scheduled sessions and
   87:  * calls hooks from associated BFD requests. This @notify_list (and state fields
   88:  * in structure &bfd_session) is protected by a spinlock in &bfd_proto and
   89:  * functions bfd_lock_sessions() / bfd_unlock_sessions().
   90:  *
   91:  * There are few data races (accessing @p->p.debug from TRACE() from the BFD
   92:  * thread and accessing some some private fields of %bfd_session from
   93:  * bfd_show_sessions() from the main thread, but these are harmless (i hope).
   94:  *
   95:  * TODO: document functions and access restrictions for fields in BFD structures.
   96:  *
   97:  * Supported standards:
   98:  * - RFC 5880 - main BFD standard
   99:  * - RFC 5881 - BFD for IP links
  100:  * - RFC 5882 - generic application of BFD
  101:  * - RFC 5883 - BFD for multihop paths
  102:  */
  103: 
  104: #include "bfd.h"
  105: 
  106: 
  107: #define HASH_ID_KEY(n)		n->loc_id
  108: #define HASH_ID_NEXT(n)		n->next_id
  109: #define HASH_ID_EQ(a,b)		a == b
  110: #define HASH_ID_FN(k)		k
  111: 
  112: #define HASH_IP_KEY(n)		n->addr
  113: #define HASH_IP_NEXT(n)		n->next_ip
  114: #define HASH_IP_EQ(a,b)		ipa_equal(a,b)
  115: #define HASH_IP_FN(k)		ipa_hash32(k)
  116: 
  117: static list bfd_proto_list;
  118: static list bfd_wait_list;
  119: 
  120: const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" };
  121: 
  122: static void bfd_session_set_min_tx(struct bfd_session *s, u32 val);
  123: static struct bfd_iface *bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface);
  124: static void bfd_free_iface(struct bfd_iface *ifa);
  125: static inline void bfd_notify_kick(struct bfd_proto *p);
  126: 
  127: 
  128: /*
  129:  *	BFD sessions
  130:  */
  131: 
  132: static void
  133: bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
  134: {
  135:   struct bfd_proto *p = s->ifa->bfd;
  136:   uint old_state = s->loc_state;
  137:   int notify;
  138: 
  139:   if (state == old_state)
  140:     return;
  141: 
  142:   TRACE(D_EVENTS, "Session to %I changed state from %s to %s",
  143: 	s->addr, bfd_state_names[old_state], bfd_state_names[state]);
  144: 
  145:   bfd_lock_sessions(p);
  146:   s->loc_state = state;
  147:   s->loc_diag = diag;
  148: 
  149:   notify = !NODE_VALID(&s->n);
  150:   if (notify)
  151:     add_tail(&p->notify_list, &s->n);
  152:   bfd_unlock_sessions(p);
  153: 
  154:   if (state == BFD_STATE_UP)
  155:     bfd_session_set_min_tx(s, s->ifa->cf->min_tx_int);
  156: 
  157:   if (old_state == BFD_STATE_UP)
  158:     bfd_session_set_min_tx(s, s->ifa->cf->idle_tx_int);
  159: 
  160:   if (notify)
  161:     bfd_notify_kick(p);
  162: }
  163: 
  164: static void
  165: bfd_session_update_tx_interval(struct bfd_session *s)
  166: {
  167:   u32 tx_int = MAX(s->des_min_tx_int, s->rem_min_rx_int);
  168:   u32 tx_int_l = tx_int - (tx_int / 4);	 // 75 %
  169:   u32 tx_int_h = tx_int - (tx_int / 10); // 90 %
  170: 
  171:   s->tx_timer->recurrent = tx_int_l;
  172:   s->tx_timer->randomize = tx_int_h - tx_int_l;
  173: 
  174:   /* Do not set timer if no previous event */
  175:   if (!s->last_tx)
  176:     return;
  177: 
  178:   /* Set timer relative to last tx_timer event */
  179:   tm2_set(s->tx_timer, s->last_tx + tx_int_l);
  180: }
  181: 
  182: static void
  183: bfd_session_update_detection_time(struct bfd_session *s, int kick)
  184: {
  185:   btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
  186: 
  187:   if (kick)
  188:     s->last_rx = current_time();
  189: 
  190:   if (!s->last_rx)
  191:     return;
  192: 
  193:   tm2_set(s->hold_timer, s->last_rx + timeout);
  194: }
  195: 
  196: static void
  197: bfd_session_control_tx_timer(struct bfd_session *s, int reset)
  198: {
  199:   // if (!s->opened) goto stop;
  200: 
  201:   if (s->passive && (s->rem_id == 0))
  202:     goto stop;
  203: 
  204:   if (s->rem_demand_mode &&
  205:       !s->poll_active &&
  206:       (s->loc_state == BFD_STATE_UP) &&
  207:       (s->rem_state == BFD_STATE_UP))
  208:     goto stop;
  209: 
  210:   if (s->rem_min_rx_int == 0)
  211:     goto stop;
  212: 
  213:   /* So TX timer should run */
  214:   if (reset || !tm2_active(s->tx_timer))
  215:   {
  216:     s->last_tx = 0;
  217:     tm2_start(s->tx_timer, 0);
  218:   }
  219: 
  220:   return;
  221: 
  222:  stop:
  223:   tm2_stop(s->tx_timer);
  224:   s->last_tx = 0;
  225: }
  226: 
  227: static void
  228: bfd_session_request_poll(struct bfd_session *s, u8 request)
  229: {
  230:   /* Not sure about this, but doing poll in this case does not make sense */
  231:   if (s->rem_id == 0)
  232:     return;
  233: 
  234:   s->poll_scheduled |= request;
  235: 
  236:   if (s->poll_active)
  237:     return;
  238: 
  239:   s->poll_active = s->poll_scheduled;
  240:   s->poll_scheduled = 0;
  241: 
  242:   bfd_session_control_tx_timer(s, 1);
  243: }
  244: 
  245: static void
  246: bfd_session_terminate_poll(struct bfd_session *s)
  247: {
  248:   u8 poll_done = s->poll_active & ~s->poll_scheduled;
  249: 
  250:   if (poll_done & BFD_POLL_TX)
  251:     s->des_min_tx_int = s->des_min_tx_new;
  252: 
  253:   if (poll_done & BFD_POLL_RX)
  254:     s->req_min_rx_int = s->req_min_rx_new;
  255: 
  256:   s->poll_active = s->poll_scheduled;
  257:   s->poll_scheduled = 0;
  258: 
  259:   /* Timers are updated by caller - bfd_session_process_ctl() */
  260: }
  261: 
  262: void
  263: bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int)
  264: {
  265:   if (s->poll_active && (flags & BFD_FLAG_FINAL))
  266:     bfd_session_terminate_poll(s);
  267: 
  268:   if ((s->des_min_tx_int != old_tx_int) || (s->rem_min_rx_int != old_rx_int))
  269:     bfd_session_update_tx_interval(s);
  270: 
  271:   bfd_session_update_detection_time(s, 1);
  272: 
  273:   /* Update session state */
  274:   int next_state = 0;
  275:   int diag = BFD_DIAG_NOTHING;
  276: 
  277:   switch (s->loc_state)
  278:   {
  279:   case BFD_STATE_ADMIN_DOWN:
  280:     return;
  281: 
  282:   case BFD_STATE_DOWN:
  283:     if (s->rem_state == BFD_STATE_DOWN)		next_state = BFD_STATE_INIT;
  284:     else if (s->rem_state == BFD_STATE_INIT)	next_state = BFD_STATE_UP;
  285:     break;
  286: 
  287:   case BFD_STATE_INIT:
  288:     if (s->rem_state == BFD_STATE_ADMIN_DOWN)	next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
  289:     else if (s->rem_state >= BFD_STATE_INIT)	next_state = BFD_STATE_UP;
  290:     break;
  291: 
  292:   case BFD_STATE_UP:
  293:     if (s->rem_state <= BFD_STATE_DOWN)		next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
  294:     break;
  295:   }
  296: 
  297:   if (next_state)
  298:     bfd_session_update_state(s, next_state, diag);
  299: 
  300:   bfd_session_control_tx_timer(s, 0);
  301: 
  302:   if (flags & BFD_FLAG_POLL)
  303:     bfd_send_ctl(s->ifa->bfd, s, 1);
  304: }
  305: 
  306: static void
  307: bfd_session_timeout(struct bfd_session *s)
  308: {
  309:   struct bfd_proto *p = s->ifa->bfd;
  310: 
  311:   TRACE(D_EVENTS, "Session to %I expired", s->addr);
  312: 
  313:   s->rem_state = BFD_STATE_DOWN;
  314:   s->rem_id = 0;
  315:   s->rem_min_tx_int = 0;
  316:   s->rem_min_rx_int = 1;
  317:   s->rem_demand_mode = 0;
  318:   s->rem_detect_mult = 0;
  319:   s->rx_csn_known = 0;
  320: 
  321:   s->poll_active = 0;
  322:   s->poll_scheduled = 0;
  323: 
  324:   bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT);
  325: 
  326:   bfd_session_control_tx_timer(s, 1);
  327: }
  328: 
  329: static void
  330: bfd_session_set_min_tx(struct bfd_session *s, u32 val)
  331: {
  332:   /* Note that des_min_tx_int <= des_min_tx_new */
  333: 
  334:   if (val == s->des_min_tx_new)
  335:     return;
  336: 
  337:   s->des_min_tx_new = val;
  338: 
  339:   /* Postpone timer update if des_min_tx_int increases and the session is up */
  340:   if ((s->loc_state != BFD_STATE_UP) || (val < s->des_min_tx_int))
  341:   {
  342:     s->des_min_tx_int = val;
  343:     bfd_session_update_tx_interval(s);
  344:   }
  345: 
  346:   bfd_session_request_poll(s, BFD_POLL_TX);
  347: }
  348: 
  349: static void
  350: bfd_session_set_min_rx(struct bfd_session *s, u32 val)
  351: {
  352:   /* Note that req_min_rx_int >= req_min_rx_new */
  353: 
  354:   if (val == s->req_min_rx_new)
  355:     return;
  356: 
  357:   s->req_min_rx_new = val;
  358: 
  359:   /* Postpone timer update if req_min_rx_int decreases and the session is up */
  360:   if ((s->loc_state != BFD_STATE_UP) || (val > s->req_min_rx_int))
  361:   {
  362:     s->req_min_rx_int = val;
  363:     bfd_session_update_detection_time(s, 0);
  364:   }
  365: 
  366:   bfd_session_request_poll(s, BFD_POLL_RX);
  367: }
  368: 
  369: struct bfd_session *
  370: bfd_find_session_by_id(struct bfd_proto *p, u32 id)
  371: {
  372:   return HASH_FIND(p->session_hash_id, HASH_ID, id);
  373: }
  374: 
  375: struct bfd_session *
  376: bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr)
  377: {
  378:   return HASH_FIND(p->session_hash_ip, HASH_IP, addr);
  379: }
  380: 
  381: static void
  382: bfd_tx_timer_hook(timer2 *t)
  383: {
  384:   struct bfd_session *s = t->data;
  385: 
  386:   s->last_tx = current_time();
  387:   bfd_send_ctl(s->ifa->bfd, s, 0);
  388: }
  389: 
  390: static void
  391: bfd_hold_timer_hook(timer2 *t)
  392: {
  393:   bfd_session_timeout(t->data);
  394: }
  395: 
  396: static u32
  397: bfd_get_free_id(struct bfd_proto *p)
  398: {
  399:   u32 id;
  400:   for (id = random_u32(); 1; id++)
  401:     if (id && !bfd_find_session_by_id(p, id))
  402:       break;
  403: 
  404:   return id;
  405: }
  406: 
  407: static struct bfd_session *
  408: bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface)
  409: {
  410:   birdloop_enter(p->loop);
  411: 
  412:   struct bfd_iface *ifa = bfd_get_iface(p, local, iface);
  413: 
  414:   struct bfd_session *s = sl_alloc(p->session_slab);
  415:   bzero(s, sizeof(struct bfd_session));
  416: 
  417:   s->addr = addr;
  418:   s->ifa = ifa;
  419:   s->loc_id = bfd_get_free_id(p);
  420: 
  421:   HASH_INSERT(p->session_hash_id, HASH_ID, s);
  422:   HASH_INSERT(p->session_hash_ip, HASH_IP, s);
  423: 
  424: 
  425:   /* Initialization of state variables - see RFC 5880 6.8.1 */
  426:   s->loc_state = BFD_STATE_DOWN;
  427:   s->rem_state = BFD_STATE_DOWN;
  428:   s->des_min_tx_int = s->des_min_tx_new = ifa->cf->idle_tx_int;
  429:   s->req_min_rx_int = s->req_min_rx_new = ifa->cf->min_rx_int;
  430:   s->rem_min_rx_int = 1;
  431:   s->detect_mult = ifa->cf->multiplier;
  432:   s->passive = ifa->cf->passive;
  433:   s->tx_csn = random_u32();
  434: 
  435:   s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
  436:   s->hold_timer = tm2_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
  437:   bfd_session_update_tx_interval(s);
  438:   bfd_session_control_tx_timer(s, 1);
  439: 
  440:   init_list(&s->request_list);
  441:   s->last_state_change = now;
  442: 
  443:   TRACE(D_EVENTS, "Session to %I added", s->addr);
  444: 
  445:   birdloop_leave(p->loop);
  446: 
  447:   return s;
  448: }
  449: 
  450: /*
  451: static void
  452: bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa)
  453: {
  454:   birdloop_enter(p->loop);
  455: 
  456:   s->opened = 1;
  457: 
  458:   bfd_session_control_tx_timer(s);
  459: 
  460:   birdloop_leave(p->loop);
  461: }
  462: 
  463: static void
  464: bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
  465: {
  466:   birdloop_enter(p->loop);
  467: 
  468:   s->opened = 0;
  469: 
  470:   bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN);
  471:   bfd_session_control_tx_timer(s);
  472: 
  473:   birdloop_leave(p->loop);
  474: }
  475: */
  476: 
  477: static void
  478: bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
  479: {
  480:   ip_addr ip = s->addr;
  481: 
  482:   /* Caller should ensure that request list is empty */
  483: 
  484:   birdloop_enter(p->loop);
  485: 
  486:   /* Remove session from notify list if scheduled for notification */
  487:   /* No need for bfd_lock_sessions(), we are already protected by birdloop_enter() */
  488:   if (NODE_VALID(&s->n))
  489:     rem_node(&s->n);
  490: 
  491:   bfd_free_iface(s->ifa);
  492: 
  493:   rfree(s->tx_timer);
  494:   rfree(s->hold_timer);
  495: 
  496:   HASH_REMOVE(p->session_hash_id, HASH_ID, s);
  497:   HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
  498: 
  499:   sl_free(p->session_slab, s);
  500: 
  501:   TRACE(D_EVENTS, "Session to %I removed", ip);
  502: 
  503:   birdloop_leave(p->loop);
  504: }
  505: 
  506: static void
  507: bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
  508: {
  509:   birdloop_enter(p->loop);
  510: 
  511:   struct bfd_iface_config *cf = s->ifa->cf;
  512: 
  513:   u32 tx = (s->loc_state == BFD_STATE_UP) ? cf->min_tx_int : cf->idle_tx_int;
  514:   bfd_session_set_min_tx(s, tx);
  515:   bfd_session_set_min_rx(s, cf->min_rx_int);
  516:   s->detect_mult = cf->multiplier;
  517:   s->passive = cf->passive;
  518: 
  519:   bfd_session_control_tx_timer(s, 0);
  520: 
  521:   birdloop_leave(p->loop);
  522: 
  523:   TRACE(D_EVENTS, "Session to %I reconfigured", s->addr);
  524: }
  525: 
  526: 
  527: /*
  528:  *	BFD interfaces
  529:  */
  530: 
  531: static struct bfd_iface_config bfd_default_iface = {
  532:   .min_rx_int = BFD_DEFAULT_MIN_RX_INT,
  533:   .min_tx_int = BFD_DEFAULT_MIN_TX_INT,
  534:   .idle_tx_int = BFD_DEFAULT_IDLE_TX_INT,
  535:   .multiplier = BFD_DEFAULT_MULTIPLIER
  536: };
  537: 
  538: static inline struct bfd_iface_config *
  539: bfd_find_iface_config(struct bfd_config *cf, struct iface *iface)
  540: {
  541:   struct bfd_iface_config *ic;
  542: 
  543:   ic = iface ? (void *) iface_patt_find(&cf->patt_list, iface, NULL) : cf->multihop;
  544: 
  545:   return ic ? ic : &bfd_default_iface;
  546: }
  547: 
  548: static struct bfd_iface *
  549: bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface)
  550: {
  551:   struct bfd_iface *ifa;
  552: 
  553:   WALK_LIST(ifa, p->iface_list)
  554:     if (ipa_equal(ifa->local, local) && (ifa->iface == iface))
  555:       return ifa->uc++, ifa;
  556: 
  557:   struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
  558:   struct bfd_iface_config *ic = bfd_find_iface_config(cf, iface);
  559: 
  560:   ifa = mb_allocz(p->tpool, sizeof(struct bfd_iface));
  561:   ifa->local = local;
  562:   ifa->iface = iface;
  563:   ifa->cf = ic;
  564:   ifa->bfd = p;
  565: 
  566:   ifa->sk = bfd_open_tx_sk(p, local, iface);
  567:   ifa->uc = 1;
  568: 
  569:   add_tail(&p->iface_list, &ifa->n);
  570: 
  571:   return ifa;
  572: }
  573: 
  574: static void
  575: bfd_free_iface(struct bfd_iface *ifa)
  576: {
  577:   if (!ifa || --ifa->uc)
  578:     return;
  579: 
  580:   if (ifa->sk)
  581:   {
  582:     sk_stop(ifa->sk);
  583:     rfree(ifa->sk);
  584:   }
  585: 
  586:   rem_node(&ifa->n);
  587:   mb_free(ifa);
  588: }
  589: 
  590: static void
  591: bfd_reconfigure_iface(struct bfd_proto *p, struct bfd_iface *ifa, struct bfd_config *nc)
  592: {
  593:   struct bfd_iface_config *nic = bfd_find_iface_config(nc, ifa->iface);
  594:   ifa->changed = !!memcmp(nic, ifa->cf, sizeof(struct bfd_iface_config));
  595: 
  596:   /* This should be probably changed to not access ifa->cf from the BFD thread */
  597:   birdloop_enter(p->loop);
  598:   ifa->cf = nic;
  599:   birdloop_leave(p->loop);
  600: }
  601: 
  602: 
  603: /*
  604:  *	BFD requests
  605:  */
  606: 
  607: static void
  608: bfd_request_notify(struct bfd_request *req, u8 state, u8 diag)
  609: {
  610:   u8 old_state = req->state;
  611: 
  612:   if (state == old_state)
  613:     return;
  614: 
  615:   req->state = state;
  616:   req->diag = diag;
  617:   req->old_state = old_state;
  618:   req->down = (old_state == BFD_STATE_UP) && (state == BFD_STATE_DOWN);
  619: 
  620:   if (req->hook)
  621:     req->hook(req);
  622: }
  623: 
  624: static int
  625: bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
  626: {
  627:   struct bfd_session *s = bfd_find_session_by_addr(p, req->addr);
  628:   u8 state, diag;
  629: 
  630:   if (!s)
  631:     s = bfd_add_session(p, req->addr, req->local, req->iface);
  632: 
  633:   rem_node(&req->n);
  634:   add_tail(&s->request_list, &req->n);
  635:   req->session = s;
  636: 
  637:   bfd_lock_sessions(p);
  638:   state = s->loc_state;
  639:   diag = s->loc_diag;
  640:   bfd_unlock_sessions(p);
  641: 
  642:   bfd_request_notify(req, state, diag);
  643: 
  644:   return 1;
  645: }
  646: 
  647: static void
  648: bfd_submit_request(struct bfd_request *req)
  649: {
  650:   node *n;
  651: 
  652:   WALK_LIST(n, bfd_proto_list)
  653:     if (bfd_add_request(SKIP_BACK(struct bfd_proto, bfd_node, n), req))
  654:       return;
  655: 
  656:   rem_node(&req->n);
  657:   add_tail(&bfd_wait_list, &req->n);
  658:   req->session = NULL;
  659:   bfd_request_notify(req, BFD_STATE_ADMIN_DOWN, 0);
  660: }
  661: 
  662: static void
  663: bfd_take_requests(struct bfd_proto *p)
  664: {
  665:   node *n, *nn;
  666: 
  667:   WALK_LIST_DELSAFE(n, nn, bfd_wait_list)
  668:     bfd_add_request(p, SKIP_BACK(struct bfd_request, n, n));
  669: }
  670: 
  671: static void
  672: bfd_drop_requests(struct bfd_proto *p)
  673: {
  674:   node *n;
  675: 
  676:   HASH_WALK(p->session_hash_id, next_id, s)
  677:   {
  678:     /* We assume that p is not in bfd_proto_list */
  679:     WALK_LIST_FIRST(n, s->request_list)
  680:       bfd_submit_request(SKIP_BACK(struct bfd_request, n, n));
  681:   }
  682:   HASH_WALK_END;
  683: }
  684: 
  685: static struct resclass bfd_request_class;
  686: 
  687: struct bfd_request *
  688: bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface,
  689: 		    void (*hook)(struct bfd_request *), void *data)
  690: {
  691:   struct bfd_request *req = ralloc(p, &bfd_request_class);
  692: 
  693:   /* Hack: self-link req->n, we will call rem_node() on it */
  694:   req->n.prev = req->n.next = &req->n;
  695: 
  696:   req->addr = addr;
  697:   req->local = local;
  698:   req->iface = iface;
  699: 
  700:   bfd_submit_request(req);
  701: 
  702:   req->hook = hook;
  703:   req->data = data;
  704: 
  705:   return req;
  706: }
  707: 
  708: static void
  709: bfd_request_free(resource *r)
  710: {
  711:   struct bfd_request *req = (struct bfd_request *) r;
  712:   struct bfd_session *s = req->session;
  713: 
  714:   rem_node(&req->n);
  715: 
  716:   /* Remove the session if there is no request for it. Skip that if
  717:      inside notify hooks, will be handled by bfd_notify_hook() itself */
  718: 
  719:   if (s && EMPTY_LIST(s->request_list) && !s->notify_running)
  720:     bfd_remove_session(s->ifa->bfd, s);
  721: }
  722: 
  723: static void
  724: bfd_request_dump(resource *r)
  725: {
  726:   struct bfd_request *req = (struct bfd_request *) r;
  727: 
  728:   debug("(code %p, data %p)\n", req->hook, req->data);
  729: }
  730: 
  731: static struct resclass bfd_request_class = {
  732:   "BFD request",
  733:   sizeof(struct bfd_request),
  734:   bfd_request_free,
  735:   bfd_request_dump,
  736:   NULL,
  737:   NULL
  738: };
  739: 
  740: 
  741: /*
  742:  *	BFD neighbors
  743:  */
  744: 
  745: static void
  746: bfd_neigh_notify(struct neighbor *nb)
  747: {
  748:   struct bfd_proto *p = (struct bfd_proto *) nb->proto;
  749:   struct bfd_neighbor *n = nb->data;
  750: 
  751:   if (!n)
  752:     return;
  753: 
  754:   if ((nb->scope > 0) && !n->req)
  755:   {
  756:     ip_addr local = ipa_nonzero(n->local) ? n->local : nb->ifa->ip;
  757:     n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, NULL, NULL);
  758:   }
  759: 
  760:   if ((nb->scope <= 0) && n->req)
  761:   {
  762:     rfree(n->req);
  763:     n->req = NULL;
  764:   }
  765: }
  766: 
  767: static void
  768: bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
  769: {
  770:   n->active = 1;
  771: 
  772:   if (n->multihop)
  773:   {
  774:     n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, NULL, NULL);
  775:     return;
  776:   }
  777: 
  778:   struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, NEF_STICKY);
  779:   if (!nb)
  780:   {
  781:     log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface);
  782:     return;
  783:   }
  784: 
  785:   if (nb->data)
  786:   {
  787:     log(L_ERR "%s: Duplicate neighbor %I", p->p.name, n->addr);
  788:     return;
  789:   }
  790: 
  791:   n->neigh = nb;
  792:   nb->data = n;
  793: 
  794:   if (nb->scope > 0)
  795:     bfd_neigh_notify(nb);
  796:   else
  797:     TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, n->iface);
  798: }
  799: 
  800: static void
  801: bfd_stop_neighbor(struct bfd_proto *p UNUSED, struct bfd_neighbor *n)
  802: {
  803:   if (n->neigh)
  804:     n->neigh->data = NULL;
  805:   n->neigh = NULL;
  806: 
  807:   rfree(n->req);
  808:   n->req = NULL;
  809: }
  810: 
  811: static inline int
  812: bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y)
  813: {
  814:   return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) &&
  815:     (x->iface == y->iface) && (x->multihop == y->multihop);
  816: }
  817: 
  818: static void
  819: bfd_reconfigure_neighbors(struct bfd_proto *p, struct bfd_config *new)
  820: {
  821:   struct bfd_config *old = (struct bfd_config *) (p->p.cf);
  822:   struct bfd_neighbor *on, *nn;
  823: 
  824:   WALK_LIST(on, old->neigh_list)
  825:   {
  826:     WALK_LIST(nn, new->neigh_list)
  827:       if (bfd_same_neighbor(nn, on))
  828:       {
  829: 	nn->neigh = on->neigh;
  830: 	if (nn->neigh)
  831: 	  nn->neigh->data = nn;
  832: 
  833: 	nn->req = on->req;
  834: 	nn->active = 1;
  835: 	return;
  836:       }
  837: 
  838:     bfd_stop_neighbor(p, on);
  839:   }
  840: 
  841:   WALK_LIST(nn, new->neigh_list)
  842:     if (!nn->active)
  843:       bfd_start_neighbor(p, nn);
  844: }
  845: 
  846: 
  847: /*
  848:  *	BFD notify socket
  849:  */
  850: 
  851: /* This core notify code should be replaced after main loop transition to birdloop */
  852: 
  853: int pipe(int pipefd[2]);
  854: void pipe_drain(int fd);
  855: void pipe_kick(int fd);
  856: 
  857: static int
  858: bfd_notify_hook(sock *sk, uint len UNUSED)
  859: {
  860:   struct bfd_proto *p = sk->data;
  861:   struct bfd_session *s;
  862:   list tmp_list;
  863:   u8 state, diag;
  864:   node *n, *nn;
  865: 
  866:   pipe_drain(sk->fd);
  867: 
  868:   bfd_lock_sessions(p);
  869:   init_list(&tmp_list);
  870:   add_tail_list(&tmp_list, &p->notify_list);
  871:   init_list(&p->notify_list);
  872:   bfd_unlock_sessions(p);
  873: 
  874:   WALK_LIST_FIRST(s, tmp_list)
  875:   {
  876:     bfd_lock_sessions(p);
  877:     rem_node(&s->n);
  878:     state = s->loc_state;
  879:     diag = s->loc_diag;
  880:     bfd_unlock_sessions(p);
  881: 
  882:     /* FIXME: convert to btime and move to bfd_session_update_state() */
  883:     s->last_state_change = now;
  884: 
  885:     s->notify_running = 1;
  886:     WALK_LIST_DELSAFE(n, nn, s->request_list)
  887:       bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag);
  888:     s->notify_running = 0;
  889: 
  890:     /* Remove the session if all requests were removed in notify hooks */
  891:     if (EMPTY_LIST(s->request_list))
  892:       bfd_remove_session(p, s);
  893:   }
  894: 
  895:   return 0;
  896: }
  897: 
  898: static inline void
  899: bfd_notify_kick(struct bfd_proto *p)
  900: {
  901:   pipe_kick(p->notify_ws->fd);
  902: }
  903: 
  904: static void
  905: bfd_noterr_hook(sock *sk, int err)
  906: {
  907:   struct bfd_proto *p = sk->data;
  908:   log(L_ERR "%s: Notify socket error: %m", p->p.name, err);
  909: }
  910: 
  911: static void
  912: bfd_notify_init(struct bfd_proto *p)
  913: {
  914:   int pfds[2];
  915:   sock *sk;
  916: 
  917:   int rv = pipe(pfds);
  918:   if (rv < 0)
  919:     die("pipe: %m");
  920: 
  921:   sk = sk_new(p->p.pool);
  922:   sk->type = SK_MAGIC;
  923:   sk->rx_hook = bfd_notify_hook;
  924:   sk->err_hook = bfd_noterr_hook;
  925:   sk->fd = pfds[0];
  926:   sk->data = p;
  927:   if (sk_open(sk) < 0)
  928:     die("bfd: sk_open failed");
  929:   p->notify_rs = sk;
  930: 
  931:   /* The write sock is not added to any event loop */
  932:   sk = sk_new(p->p.pool);
  933:   sk->type = SK_MAGIC;
  934:   sk->fd = pfds[1];
  935:   sk->data = p;
  936:   sk->flags = SKF_THREAD;
  937:   if (sk_open(sk) < 0)
  938:     die("bfd: sk_open failed");
  939:   p->notify_ws = sk;
  940: }
  941: 
  942: 
  943: /*
  944:  *	BFD protocol glue
  945:  */
  946: 
  947: void
  948: bfd_init_all(void)
  949: {
  950:   init_list(&bfd_proto_list);
  951:   init_list(&bfd_wait_list);
  952: }
  953: 
  954: static struct proto *
  955: bfd_init(struct proto_config *c)
  956: {
  957:   struct proto *p = proto_new(c, sizeof(struct bfd_proto));
  958: 
  959:   p->neigh_notify = bfd_neigh_notify;
  960: 
  961:   return p;
  962: }
  963: 
  964: static int
  965: bfd_start(struct proto *P)
  966: {
  967:   struct bfd_proto *p = (struct bfd_proto *) P;
  968:   struct bfd_config *cf = (struct bfd_config *) (P->cf);
  969: 
  970:   p->loop = birdloop_new();
  971:   p->tpool = rp_new(NULL, "BFD thread root");
  972:   pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
  973: 
  974:   p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
  975:   HASH_INIT(p->session_hash_id, P->pool, 8);
  976:   HASH_INIT(p->session_hash_ip, P->pool, 8);
  977: 
  978:   init_list(&p->iface_list);
  979: 
  980:   init_list(&p->notify_list);
  981:   bfd_notify_init(p);
  982: 
  983:   add_tail(&bfd_proto_list, &p->bfd_node);
  984: 
  985:   birdloop_enter(p->loop);
  986:   p->rx_1 = bfd_open_rx_sk(p, 0);
  987:   p->rx_m = bfd_open_rx_sk(p, 1);
  988:   birdloop_leave(p->loop);
  989: 
  990:   bfd_take_requests(p);
  991: 
  992:   struct bfd_neighbor *n;
  993:   WALK_LIST(n, cf->neigh_list)
  994:     bfd_start_neighbor(p, n);
  995: 
  996:   birdloop_start(p->loop);
  997: 
  998:   return PS_UP;
  999: }
 1000: 
 1001: 
 1002: static int
 1003: bfd_shutdown(struct proto *P)
 1004: {
 1005:   struct bfd_proto *p = (struct bfd_proto *) P;
 1006:   struct bfd_config *cf = (struct bfd_config *) (P->cf);
 1007: 
 1008:   rem_node(&p->bfd_node);
 1009: 
 1010:   birdloop_stop(p->loop);
 1011: 
 1012:   struct bfd_neighbor *n;
 1013:   WALK_LIST(n, cf->neigh_list)
 1014:     bfd_stop_neighbor(p, n);
 1015: 
 1016:   bfd_drop_requests(p);
 1017: 
 1018:   /* FIXME: This is hack */
 1019:   birdloop_enter(p->loop);
 1020:   rfree(p->tpool);
 1021:   birdloop_leave(p->loop);
 1022: 
 1023:   birdloop_free(p->loop);
 1024: 
 1025:   return PS_DOWN;
 1026: }
 1027: 
 1028: static int
 1029: bfd_reconfigure(struct proto *P, struct proto_config *c)
 1030: {
 1031:   struct bfd_proto *p = (struct bfd_proto *) P;
 1032:   // struct bfd_config *old = (struct bfd_config *) (P->cf);
 1033:   struct bfd_config *new = (struct bfd_config *) c;
 1034:   struct bfd_iface *ifa;
 1035: 
 1036:   birdloop_mask_wakeups(p->loop);
 1037: 
 1038:   WALK_LIST(ifa, p->iface_list)
 1039:     bfd_reconfigure_iface(p, ifa, new);
 1040: 
 1041:   HASH_WALK(p->session_hash_id, next_id, s)
 1042:   {
 1043:     if (s->ifa->changed)
 1044:       bfd_reconfigure_session(p, s);
 1045:   }
 1046:   HASH_WALK_END;
 1047: 
 1048:   bfd_reconfigure_neighbors(p, new);
 1049: 
 1050:   birdloop_unmask_wakeups(p->loop);
 1051: 
 1052:   return 1;
 1053: }
 1054: 
 1055: /* Ensure one instance */
 1056: struct bfd_config *bfd_cf;
 1057: 
 1058: static void
 1059: bfd_preconfig(struct protocol *P UNUSED, struct config *c UNUSED)
 1060: {
 1061:   bfd_cf = NULL;
 1062: }
 1063: 
 1064: static void
 1065: bfd_copy_config(struct proto_config *dest, struct proto_config *src UNUSED)
 1066: {
 1067:   struct bfd_config *d = (struct bfd_config *) dest;
 1068:   // struct bfd_config *s = (struct bfd_config *) src;
 1069: 
 1070:   /* We clean up patt_list and neigh_list, neighbors and ifaces are non-sharable */
 1071:   init_list(&d->patt_list);
 1072:   init_list(&d->neigh_list);
 1073: }
 1074: 
 1075: void
 1076: bfd_show_sessions(struct proto *P)
 1077: {
 1078:   byte tbuf[TM_DATETIME_BUFFER_SIZE];
 1079:   struct bfd_proto *p = (struct bfd_proto *) P;
 1080:   uint state, diag UNUSED;
 1081:   u32 tx_int, timeout;
 1082:   const char *ifname;
 1083: 
 1084:   if (p->p.proto_state != PS_UP)
 1085:   {
 1086:     cli_msg(-1020, "%s: is not up", p->p.name);
 1087:     cli_msg(0, "");
 1088:     return;
 1089:   }
 1090: 
 1091:   cli_msg(-1020, "%s:", p->p.name);
 1092:   cli_msg(-1020, "%-25s %-10s %-10s %-10s  %8s %8s",
 1093: 	  "IP address", "Interface", "State", "Since", "Interval", "Timeout");
 1094: 
 1095: 
 1096:   HASH_WALK(p->session_hash_id, next_id, s)
 1097:   {
 1098:     /* FIXME: this is thread-unsafe, but perhaps harmless */
 1099:     state = s->loc_state;
 1100:     diag = s->loc_diag;
 1101:     ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---";
 1102:     tx_int = s->last_tx ? (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS) : 0;
 1103:     timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult;
 1104: 
 1105:     state = (state < 4) ? state : 0;
 1106:     tm_format_datetime(tbuf, &config->tf_proto, s->last_state_change);
 1107: 
 1108:     cli_msg(-1020, "%-25I %-10s %-10s %-10s  %3u.%03u  %3u.%03u",
 1109: 	    s->addr, ifname, bfd_state_names[state], tbuf,
 1110: 	    tx_int / 1000, tx_int % 1000, timeout / 1000, timeout % 1000);
 1111:   }
 1112:   HASH_WALK_END;
 1113: 
 1114:   cli_msg(0, "");
 1115: }
 1116: 
 1117: 
 1118: struct protocol proto_bfd = {
 1119:   .name =		"BFD",
 1120:   .template =		"bfd%d",
 1121:   .config_size =	sizeof(struct bfd_config),
 1122:   .init =		bfd_init,
 1123:   .start =		bfd_start,
 1124:   .shutdown =		bfd_shutdown,
 1125:   .reconfigure =	bfd_reconfigure,
 1126:   .preconfig = 		bfd_preconfig,
 1127:   .copy_config =	bfd_copy_config,
 1128: };

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