3 * Implemented server-style in a pthread.
4 * --------------------------------------
5 * Copyright (C) 2017 Cumulus Networks, Inc.
7 * This file is part of Free Range Routing.
9 * Free Range Routing is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2, or (at your option) any later
14 * Free Range Routing is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * You should have received a copy of the GN5U General Public License along
20 * with Free Range Routing; see the file COPYING. If not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
33 #include "bgpd/bgpd.h"
34 #include "bgpd/bgp_keepalives.h"
35 #include "bgpd/bgp_debug.h"
36 #include "bgpd/bgp_attr.h"
37 #include "bgpd/bgp_packet.h"
40 * Peer KeepAlive Timer.
41 * Associates a peer with the time of its last keepalive.
44 // the peer to send keepalives to
46 // absolute time of last keepalive sent
50 /* List of peers we are sending keepalives for, and associated mutex. */
51 static pthread_mutex_t peerlist_mtx
= PTHREAD_MUTEX_INITIALIZER
;
52 static pthread_cond_t peerlist_cond
;
53 static struct list
*peerlist
;
55 /* Thread control flag. */
56 bool bgp_keepalives_thread_run
;
58 static struct pkat
*pkat_new(struct peer
*peer
)
60 struct pkat
*pkat
= XMALLOC(MTYPE_TMP
, sizeof(struct pkat
));
62 monotime(&pkat
->last
);
66 static void pkat_del(void *pkat
)
68 XFREE(MTYPE_TMP
, pkat
);
71 * Cleanup thread resources at termination.
75 static void cleanup_handler(void *arg
)
78 list_delete(peerlist
);
82 pthread_mutex_unlock(&peerlist_mtx
);
83 pthread_cond_destroy(&peerlist_cond
);
84 memset(&peerlist_cond
, 0, sizeof(peerlist_cond
));
88 * Walks the list of peers, sending keepalives to those that are due for them.
90 * For any given peer, if the elapsed time since its last keepalive exceeds its
91 * configured keepalive timer, a keepalive is sent to the peer and its
92 * last-sent time is reset. Additionally, If the elapsed time does not exceed
93 * the configured keepalive timer, but the time until the next keepalive is due
94 * is within a hardcoded tolerance, a keepalive is sent as if the configured
95 * timer was exceeded. Doing this helps alleviate nanosecond sleeps between
96 * ticks by grouping together peers who are due for keepalives at roughly the
97 * same time. This tolerance value is arbitrarily chosen to be 100ms.
99 * In addition, this function calculates the maximum amount of time that the
100 * keepalive thread can sleep before another tick needs to take place. This is
101 * equivalent to shortest time until a keepalive is due for any one peer.
103 * @return maximum time to wait until next update (0 if infinity)
105 static struct timeval
update()
110 int update_set
= 0; // whether next_update has been set
111 struct timeval next_update
; // max sleep until next tick
112 static struct timeval elapsed
; // elapsed time since keepalive
113 static struct timeval ka
= {0}; // peer->v_keepalive as a timeval
114 static struct timeval diff
; // ka - elapsed
116 // see function comment
117 static struct timeval tolerance
= {0, 100000};
119 for (ALL_LIST_ELEMENTS_RO(peerlist
, ln
, pkat
)) {
120 // calculate elapsed time since last keepalive
121 monotime_since(&pkat
->last
, &elapsed
);
123 // calculate difference between elapsed time and configured time
124 ka
.tv_sec
= pkat
->peer
->v_keepalive
;
125 timersub(&ka
, &elapsed
, &diff
);
127 int send_keepalive
= elapsed
.tv_sec
>= ka
.tv_sec
128 || timercmp(&diff
, &tolerance
, <);
130 if (send_keepalive
) {
131 if (bgp_debug_neighbor_events(pkat
->peer
))
133 "%s [FSM] Timer (keepalive timer expire)",
136 bgp_keepalive_send(pkat
->peer
);
137 monotime(&pkat
->last
);
138 memset(&elapsed
, 0x00, sizeof(struct timeval
));
139 diff
= ka
; // time until next keepalive == peer
143 // if calculated next update for this peer < current delay, use
145 if (!update_set
|| timercmp(&diff
, &next_update
, <)) {
154 void *peer_keepalives_start(void *arg
)
156 struct timeval currtime
= {0, 0};
157 struct timeval next_update
= {0, 0};
158 struct timespec next_update_ts
= {0, 0};
160 // initialize synchronization primitives
161 pthread_mutex_lock(&peerlist_mtx
);
163 // use monotonic clock with condition variable
164 pthread_condattr_t attrs
;
165 pthread_condattr_init(&attrs
);
166 pthread_condattr_setclock(&attrs
, CLOCK_MONOTONIC
);
167 pthread_cond_init(&peerlist_cond
, &attrs
);
169 // initialize peerlist
170 peerlist
= list_new();
171 peerlist
->del
= pkat_del
;
173 // register cleanup handlers
174 pthread_cleanup_push(&cleanup_handler
, NULL
);
176 bgp_keepalives_thread_run
= true;
178 while (bgp_keepalives_thread_run
) {
179 if (peerlist
->count
> 0)
180 pthread_cond_timedwait(&peerlist_cond
, &peerlist_mtx
,
183 while (peerlist
->count
== 0
184 && bgp_keepalives_thread_run
)
185 pthread_cond_wait(&peerlist_cond
,
189 next_update
= update();
190 timeradd(&currtime
, &next_update
, &next_update
);
191 TIMEVAL_TO_TIMESPEC(&next_update
, &next_update_ts
);
195 pthread_cleanup_pop(1);
200 /* --- thread external functions ------------------------------------------- */
202 void peer_keepalives_on(struct peer
*peer
)
204 pthread_mutex_lock(&peerlist_mtx
);
206 struct listnode
*ln
, *nn
;
209 for (ALL_LIST_ELEMENTS(peerlist
, ln
, nn
, pkat
))
210 if (pkat
->peer
== peer
) {
211 pthread_mutex_unlock(&peerlist_mtx
);
215 pkat
= pkat_new(peer
);
216 listnode_add(peerlist
, pkat
);
219 pthread_mutex_unlock(&peerlist_mtx
);
220 peer_keepalives_wake();
223 void peer_keepalives_off(struct peer
*peer
)
225 pthread_mutex_lock(&peerlist_mtx
);
227 struct listnode
*ln
, *nn
;
230 for (ALL_LIST_ELEMENTS(peerlist
, ln
, nn
, pkat
))
231 if (pkat
->peer
== peer
) {
232 XFREE(MTYPE_TMP
, pkat
);
233 list_delete_node(peerlist
, ln
);
237 pthread_mutex_unlock(&peerlist_mtx
);
240 void peer_keepalives_wake()
242 pthread_mutex_lock(&peerlist_mtx
);
244 pthread_cond_signal(&peerlist_cond
);
246 pthread_mutex_unlock(&peerlist_mtx
);