]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_keepalives.c
bgpd: move bgp i/o to a separate source file
[mirror_frr.git] / bgpd / bgp_keepalives.c
CommitLineData
bd8b71e4
QY
1/*
2 * BGP Keepalives.
03014d48 3 *
bd8b71e4
QY
4 * Implements a producer thread to generate BGP keepalives for peers.
5 * ----------------------------------------
03014d48 6 * Copyright (C) 2017 Cumulus Networks, Inc.
bd8b71e4 7 * Quentin Young
03014d48 8 *
bd8b71e4 9 * This file is part of FRRouting.
03014d48 10 *
bd8b71e4
QY
11 * FRRouting is free software; you can redistribute it and/or modify it under
12 * the terms of the GNU General Public License as published by the Free
13 * Software Foundation; either version 2, or (at your option) any later
03014d48
QY
14 * version.
15 *
bd8b71e4
QY
16 * FRRouting is distributed in the hope that it will be useful, but WITHOUT ANY
17 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
19 * details.
03014d48 20 *
bd8b71e4
QY
21 * You should have received a copy of the GNU General Public License along with
22 * FRRouting; see the file COPYING. If not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
03014d48
QY
24 */
25#include <zebra.h>
26#include <signal.h>
27#include <sys/time.h>
28
29#include "thread.h"
30#include "log.h"
31#include "vty.h"
32#include "monotime.h"
bd8b71e4 33#include "hash.h"
0ca8b79f 34#include "frr_pthread.h"
03014d48
QY
35
36#include "bgpd/bgpd.h"
37#include "bgpd/bgp_keepalives.h"
38#include "bgpd/bgp_debug.h"
39#include "bgpd/bgp_attr.h"
40#include "bgpd/bgp_packet.h"
41
42/**
43 * Peer KeepAlive Timer.
44 * Associates a peer with the time of its last keepalive.
45 */
46struct pkat {
47 // the peer to send keepalives to
48 struct peer *peer;
49 // absolute time of last keepalive sent
50 struct timeval last;
51};
52
53/* List of peers we are sending keepalives for, and associated mutex. */
bd8b71e4
QY
54static pthread_mutex_t *peerhash_mtx;
55static pthread_cond_t *peerhash_cond;
56static struct hash *peerhash;
03014d48
QY
57
58/* Thread control flag. */
419dfe6a 59bool bgp_keepalives_thread_run = false;
03014d48
QY
60
61static struct pkat *pkat_new(struct peer *peer)
62{
63 struct pkat *pkat = XMALLOC(MTYPE_TMP, sizeof(struct pkat));
64 pkat->peer = peer;
65 monotime(&pkat->last);
66 return pkat;
67}
68
69static void pkat_del(void *pkat)
70{
71 XFREE(MTYPE_TMP, pkat);
72}
03014d48 73
bd8b71e4 74
03014d48
QY
75/*
76 * Walks the list of peers, sending keepalives to those that are due for them.
77 *
78 * For any given peer, if the elapsed time since its last keepalive exceeds its
79 * configured keepalive timer, a keepalive is sent to the peer and its
80 * last-sent time is reset. Additionally, If the elapsed time does not exceed
81 * the configured keepalive timer, but the time until the next keepalive is due
82 * is within a hardcoded tolerance, a keepalive is sent as if the configured
83 * timer was exceeded. Doing this helps alleviate nanosecond sleeps between
84 * ticks by grouping together peers who are due for keepalives at roughly the
85 * same time. This tolerance value is arbitrarily chosen to be 100ms.
86 *
87 * In addition, this function calculates the maximum amount of time that the
88 * keepalive thread can sleep before another tick needs to take place. This is
89 * equivalent to shortest time until a keepalive is due for any one peer.
90 *
91 * @return maximum time to wait until next update (0 if infinity)
92 */
bd8b71e4 93static void peer_process(struct hash_backet *hb, void *arg)
03014d48 94{
bd8b71e4
QY
95 struct pkat *pkat = hb->data;
96
97 struct timeval *next_update = arg;
03014d48 98
03014d48
QY
99 static struct timeval elapsed; // elapsed time since keepalive
100 static struct timeval ka = {0}; // peer->v_keepalive as a timeval
101 static struct timeval diff; // ka - elapsed
102
03014d48
QY
103 static struct timeval tolerance = {0, 100000};
104
bd8b71e4
QY
105 // calculate elapsed time since last keepalive
106 monotime_since(&pkat->last, &elapsed);
03014d48 107
bd8b71e4
QY
108 // calculate difference between elapsed time and configured time
109 ka.tv_sec = pkat->peer->v_keepalive;
110 timersub(&ka, &elapsed, &diff);
03014d48 111
bd8b71e4
QY
112 int send_keepalive =
113 elapsed.tv_sec >= ka.tv_sec || timercmp(&diff, &tolerance, <);
03014d48 114
bd8b71e4
QY
115 if (send_keepalive) {
116 if (bgp_debug_neighbor_events(pkat->peer))
117 zlog_debug("%s [FSM] Timer (keepalive timer expire)",
118 pkat->peer->host);
03014d48 119
bd8b71e4
QY
120 bgp_keepalive_send(pkat->peer);
121 monotime(&pkat->last);
122 memset(&elapsed, 0x00, sizeof(struct timeval));
123 diff = ka; // time until next keepalive == peer keepalive time
03014d48
QY
124 }
125
bd8b71e4
QY
126 // if calculated next update for this peer < current delay, use it
127 if (next_update->tv_sec <= 0 || timercmp(&diff, next_update, <))
128 *next_update = diff;
129}
130
131static int peer_hash_cmp(const void *f, const void *s)
132{
133 const struct pkat *p1 = f;
134 const struct pkat *p2 = s;
135 return p1->peer == p2->peer;
136}
137
138static unsigned int peer_hash_key(void *arg)
139{
140 struct pkat *pkat = arg;
141 return (uintptr_t)pkat->peer;
03014d48
QY
142}
143
419dfe6a 144void peer_keepalives_init()
03014d48 145{
bd8b71e4
QY
146 peerhash_mtx = XCALLOC(MTYPE_PTHREAD, sizeof(pthread_mutex_t));
147 peerhash_cond = XCALLOC(MTYPE_PTHREAD, sizeof(pthread_cond_t));
03014d48 148
419dfe6a 149 // initialize mutex
bd8b71e4 150 pthread_mutex_init(peerhash_mtx, NULL);
03014d48
QY
151
152 // use monotonic clock with condition variable
153 pthread_condattr_t attrs;
154 pthread_condattr_init(&attrs);
155 pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
bd8b71e4 156 pthread_cond_init(peerhash_cond, &attrs);
419dfe6a 157 pthread_condattr_destroy(&attrs);
03014d48 158
bd8b71e4
QY
159 // initialize peer hashtable
160 peerhash = hash_create_size(2048, peer_hash_key, peer_hash_cmp);
419dfe6a
QY
161}
162
163static void peer_keepalives_finish(void *arg)
164{
165 bgp_keepalives_thread_run = false;
166
bd8b71e4
QY
167 if (peerhash) {
168 hash_clean(peerhash, pkat_del);
169 hash_free(peerhash);
170 }
419dfe6a 171
bd8b71e4 172 peerhash = NULL;
419dfe6a 173
bd8b71e4
QY
174 pthread_mutex_unlock(peerhash_mtx);
175 pthread_mutex_destroy(peerhash_mtx);
176 pthread_cond_destroy(peerhash_cond);
419dfe6a 177
bd8b71e4
QY
178 XFREE(MTYPE_PTHREAD, peerhash_mtx);
179 XFREE(MTYPE_PTHREAD, peerhash_cond);
419dfe6a
QY
180}
181
182/**
183 * Entry function for peer keepalive generation pthread.
184 *
185 * peer_keepalives_init() must be called prior to this.
186 */
187void *peer_keepalives_start(void *arg)
188{
189 struct timeval currtime = {0, 0};
bd8b71e4 190 struct timeval aftertime = {0, 0};
419dfe6a
QY
191 struct timeval next_update = {0, 0};
192 struct timespec next_update_ts = {0, 0};
193
bd8b71e4 194 pthread_mutex_lock(peerhash_mtx);
03014d48 195
419dfe6a
QY
196 // register cleanup handler
197 pthread_cleanup_push(&peer_keepalives_finish, NULL);
03014d48
QY
198
199 bgp_keepalives_thread_run = true;
200
201 while (bgp_keepalives_thread_run) {
bd8b71e4
QY
202 if (peerhash->count > 0)
203 pthread_cond_timedwait(peerhash_cond, peerhash_mtx,
03014d48
QY
204 &next_update_ts);
205 else
bd8b71e4 206 while (peerhash->count == 0
03014d48 207 && bgp_keepalives_thread_run)
bd8b71e4 208 pthread_cond_wait(peerhash_cond, peerhash_mtx);
03014d48
QY
209
210 monotime(&currtime);
bd8b71e4
QY
211
212 next_update.tv_sec = -1;
213
214 hash_iterate(peerhash, peer_process, &next_update);
215 if (next_update.tv_sec == -1)
216 memset(&next_update, 0x00, sizeof(next_update));
217
218 monotime_since(&currtime, &aftertime);
219
03014d48
QY
220 timeradd(&currtime, &next_update, &next_update);
221 TIMEVAL_TO_TIMESPEC(&next_update, &next_update_ts);
222 }
223
224 // clean up
225 pthread_cleanup_pop(1);
226
227 return NULL;
228}
229
230/* --- thread external functions ------------------------------------------- */
231
232void peer_keepalives_on(struct peer *peer)
233{
bd8b71e4
QY
234 /* placeholder bucket data to use for fast key lookups */
235 static struct pkat holder = {0};
236
237 pthread_mutex_lock(peerhash_mtx);
03014d48 238 {
bd8b71e4
QY
239 holder.peer = peer;
240 if (!hash_lookup(peerhash, &holder)) {
241 struct pkat *pkat = pkat_new(peer);
242 hash_get(peerhash, pkat, hash_alloc_intern);
243 peer_lock(peer);
244 }
49507a6f 245 SET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON);
03014d48 246 }
bd8b71e4 247 pthread_mutex_unlock(peerhash_mtx);
03014d48
QY
248 peer_keepalives_wake();
249}
250
251void peer_keepalives_off(struct peer *peer)
252{
bd8b71e4
QY
253 /* placeholder bucket data to use for fast key lookups */
254 static struct pkat holder = {0};
49507a6f 255
bd8b71e4
QY
256 pthread_mutex_lock(peerhash_mtx);
257 {
258 holder.peer = peer;
259 struct pkat *res = hash_release(peerhash, &holder);
260 if (res) {
261 pkat_del(res);
262 peer_unlock(peer);
263 }
49507a6f 264 UNSET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON);
03014d48 265 }
bd8b71e4 266 pthread_mutex_unlock(peerhash_mtx);
03014d48
QY
267}
268
269void peer_keepalives_wake()
270{
bd8b71e4 271 pthread_mutex_lock(peerhash_mtx);
03014d48 272 {
bd8b71e4 273 pthread_cond_signal(peerhash_cond);
03014d48 274 }
bd8b71e4 275 pthread_mutex_unlock(peerhash_mtx);
03014d48 276}
0ca8b79f
QY
277
278int peer_keepalives_stop(void **result)
279{
280 struct frr_pthread *fpt = frr_pthread_get(PTHREAD_KEEPALIVES);
281 bgp_keepalives_thread_run = false;
282 peer_keepalives_wake();
283 pthread_join(fpt->thread, result);
284 return 0;
285}