]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_keepalives.c
lib, bgpd: implement pthread lifecycle management
[mirror_frr.git] / bgpd / bgp_keepalives.c
1 /* BGP Keepalives.
2 *
3 * Implemented server-style in a pthread.
4 * --------------------------------------
5 * Copyright (C) 2017 Cumulus Networks, Inc.
6 *
7 * This file is part of Free Range Routing.
8 *
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
12 * version.
13 *
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
17 * more details.
18 *
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
22 * 02111-1307, USA.
23 */
24 #include <zebra.h>
25 #include <signal.h>
26 #include <sys/time.h>
27
28 #include "thread.h"
29 #include "log.h"
30 #include "vty.h"
31 #include "monotime.h"
32
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"
38
39 /**
40 * Peer KeepAlive Timer.
41 * Associates a peer with the time of its last keepalive.
42 */
43 struct pkat {
44 // the peer to send keepalives to
45 struct peer *peer;
46 // absolute time of last keepalive sent
47 struct timeval last;
48 };
49
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;
54
55 /* Thread control flag. */
56 bool bgp_keepalives_thread_run;
57
58 static struct pkat *pkat_new(struct peer *peer)
59 {
60 struct pkat *pkat = XMALLOC(MTYPE_TMP, sizeof(struct pkat));
61 pkat->peer = peer;
62 monotime(&pkat->last);
63 return pkat;
64 }
65
66 static void pkat_del(void *pkat)
67 {
68 XFREE(MTYPE_TMP, pkat);
69 }
70 /**
71 * Cleanup thread resources at termination.
72 *
73 * @param arg not used
74 */
75 static void cleanup_handler(void *arg)
76 {
77 if (peerlist)
78 list_delete(peerlist);
79
80 peerlist = NULL;
81
82 pthread_mutex_unlock(&peerlist_mtx);
83 pthread_cond_destroy(&peerlist_cond);
84 memset(&peerlist_cond, 0, sizeof(peerlist_cond));
85 }
86
87 /*
88 * Walks the list of peers, sending keepalives to those that are due for them.
89 *
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.
98 *
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.
102 *
103 * @return maximum time to wait until next update (0 if infinity)
104 */
105 static struct timeval update()
106 {
107 struct listnode *ln;
108 struct pkat *pkat;
109
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
115
116 // see function comment
117 static struct timeval tolerance = {0, 100000};
118
119 for (ALL_LIST_ELEMENTS_RO(peerlist, ln, pkat)) {
120 // calculate elapsed time since last keepalive
121 monotime_since(&pkat->last, &elapsed);
122
123 // calculate difference between elapsed time and configured time
124 ka.tv_sec = pkat->peer->v_keepalive;
125 timersub(&ka, &elapsed, &diff);
126
127 int send_keepalive = elapsed.tv_sec >= ka.tv_sec
128 || timercmp(&diff, &tolerance, <);
129
130 if (send_keepalive) {
131 if (bgp_debug_neighbor_events(pkat->peer))
132 zlog_debug(
133 "%s [FSM] Timer (keepalive timer expire)",
134 pkat->peer->host);
135
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
140 // keepalive time
141 }
142
143 // if calculated next update for this peer < current delay, use
144 // it
145 if (!update_set || timercmp(&diff, &next_update, <)) {
146 next_update = diff;
147 update_set = 1;
148 }
149 }
150
151 return next_update;
152 }
153
154 void *peer_keepalives_start(void *arg)
155 {
156 struct timeval currtime = {0, 0};
157 struct timeval next_update = {0, 0};
158 struct timespec next_update_ts = {0, 0};
159
160 // initialize synchronization primitives
161 pthread_mutex_lock(&peerlist_mtx);
162
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);
168
169 // initialize peerlist
170 peerlist = list_new();
171 peerlist->del = pkat_del;
172
173 // register cleanup handlers
174 pthread_cleanup_push(&cleanup_handler, NULL);
175
176 bgp_keepalives_thread_run = true;
177
178 while (bgp_keepalives_thread_run) {
179 if (peerlist->count > 0)
180 pthread_cond_timedwait(&peerlist_cond, &peerlist_mtx,
181 &next_update_ts);
182 else
183 while (peerlist->count == 0
184 && bgp_keepalives_thread_run)
185 pthread_cond_wait(&peerlist_cond,
186 &peerlist_mtx);
187
188 monotime(&currtime);
189 next_update = update();
190 timeradd(&currtime, &next_update, &next_update);
191 TIMEVAL_TO_TIMESPEC(&next_update, &next_update_ts);
192 }
193
194 // clean up
195 pthread_cleanup_pop(1);
196
197 return NULL;
198 }
199
200 /* --- thread external functions ------------------------------------------- */
201
202 void peer_keepalives_on(struct peer *peer)
203 {
204 pthread_mutex_lock(&peerlist_mtx);
205 {
206 struct listnode *ln, *nn;
207 struct pkat *pkat;
208
209 for (ALL_LIST_ELEMENTS(peerlist, ln, nn, pkat))
210 if (pkat->peer == peer) {
211 pthread_mutex_unlock(&peerlist_mtx);
212 return;
213 }
214
215 pkat = pkat_new(peer);
216 listnode_add(peerlist, pkat);
217 peer_lock(peer);
218 }
219 pthread_mutex_unlock(&peerlist_mtx);
220 peer_keepalives_wake();
221 }
222
223 void peer_keepalives_off(struct peer *peer)
224 {
225 pthread_mutex_lock(&peerlist_mtx);
226 {
227 struct listnode *ln, *nn;
228 struct pkat *pkat;
229
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);
234 peer_unlock(peer);
235 }
236 }
237 pthread_mutex_unlock(&peerlist_mtx);
238 }
239
240 void peer_keepalives_wake()
241 {
242 pthread_mutex_lock(&peerlist_mtx);
243 {
244 pthread_cond_signal(&peerlist_cond);
245 }
246 pthread_mutex_unlock(&peerlist_mtx);
247 }