blob: dd3d9dd63069bf78115b438285178d880b342285 [file] [log] [blame]
Emeric Brun2b920a12010-09-23 18:30:22 +02001/*
Emeric Brunb3971ab2015-05-12 18:49:09 +02002 * Peer synchro management.
Emeric Brun2b920a12010-09-23 18:30:22 +02003 *
4 * Copyright 2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <errno.h>
14#include <fcntl.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18
19#include <sys/socket.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020023#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020024#include <haproxy/applet.h>
Willy Tarreauf1d32c42020-06-04 21:07:02 +020025#include <haproxy/channel.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020026#include <haproxy/cli.h>
Willy Tarreau3afc4c42020-06-03 18:23:19 +020027#include <haproxy/dict.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020028#include <haproxy/errors.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020029#include <haproxy/fd.h>
Willy Tarreau762d7a52020-06-04 11:23:07 +020030#include <haproxy/frontend.h>
Willy Tarreau6131d6a2020-06-02 16:48:09 +020031#include <haproxy/net_helper.h>
Willy Tarreau8efbdfb2020-06-04 11:29:21 +020032#include <haproxy/obj_type-t.h>
Willy Tarreau3c2a7c22020-06-04 18:38:21 +020033#include <haproxy/peers.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020034#include <haproxy/proxy.h>
Willy Tarreau48d25b32020-06-04 18:58:52 +020035#include <haproxy/session-t.h>
Willy Tarreau3727a8a2020-06-04 17:37:26 +020036#include <haproxy/signal.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020037#include <haproxy/stats-t.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020038#include <haproxy/stick_table.h>
39#include <haproxy/stream.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020040#include <haproxy/stream_interface.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020041#include <haproxy/task.h>
42#include <haproxy/thread.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020043#include <haproxy/time.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020044#include <haproxy/tools.h>
Frédéric Lécailled8659352020-11-10 16:18:03 +010045#include <haproxy/trace.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020046
Emeric Brun2b920a12010-09-23 18:30:22 +020047
48/*******************************/
49/* Current peer learning state */
50/*******************************/
51
52/******************************/
Emeric Brunb3971ab2015-05-12 18:49:09 +020053/* Current peers section resync state */
Emeric Brun2b920a12010-09-23 18:30:22 +020054/******************************/
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010055#define PEERS_F_RESYNC_LOCAL 0x00000001 /* Learn from local finished or no more needed */
56#define PEERS_F_RESYNC_REMOTE 0x00000002 /* Learn from remote finished or no more needed */
57#define PEERS_F_RESYNC_ASSIGN 0x00000004 /* A peer was assigned to learn our lesson */
58#define PEERS_F_RESYNC_PROCESS 0x00000008 /* The assigned peer was requested for resync */
59#define PEERS_F_DONOTSTOP 0x00010000 /* Main table sync task block process during soft stop
60 to push data to new process */
Emeric Brun2b920a12010-09-23 18:30:22 +020061
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010062#define PEERS_RESYNC_STATEMASK (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE)
63#define PEERS_RESYNC_FROMLOCAL 0x00000000
64#define PEERS_RESYNC_FROMREMOTE PEERS_F_RESYNC_LOCAL
65#define PEERS_RESYNC_FINISHED (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE)
Emeric Brunb3971ab2015-05-12 18:49:09 +020066
67/***********************************/
68/* Current shared table sync state */
69/***********************************/
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010070#define SHTABLE_F_TEACH_STAGE1 0x00000001 /* Teach state 1 complete */
71#define SHTABLE_F_TEACH_STAGE2 0x00000002 /* Teach state 2 complete */
Emeric Brun2b920a12010-09-23 18:30:22 +020072
73/******************************/
74/* Remote peer teaching state */
75/******************************/
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010076#define PEER_F_TEACH_PROCESS 0x00000001 /* Teach a lesson to current peer */
77#define PEER_F_TEACH_FINISHED 0x00000008 /* Teach conclude, (wait for confirm) */
78#define PEER_F_TEACH_COMPLETE 0x00000010 /* All that we know already taught to current peer, used only for a local peer */
79#define PEER_F_LEARN_ASSIGN 0x00000100 /* Current peer was assigned for a lesson */
80#define PEER_F_LEARN_NOTUP2DATE 0x00000200 /* Learn from peer finished but peer is not up to date */
81#define PEER_F_ALIVE 0x20000000 /* Used to flag a peer a alive. */
82#define PEER_F_HEARTBEAT 0x40000000 /* Heartbeat message to send. */
83#define PEER_F_DWNGRD 0x80000000 /* When this flag is enabled, we must downgrade the supported version announced during peer sessions. */
Emeric Brun2b920a12010-09-23 18:30:22 +020084
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010085#define PEER_TEACH_RESET ~(PEER_F_TEACH_PROCESS|PEER_F_TEACH_FINISHED) /* PEER_F_TEACH_COMPLETE should never be reset */
86#define PEER_LEARN_RESET ~(PEER_F_LEARN_ASSIGN|PEER_F_LEARN_NOTUP2DATE)
Emeric Brun2b920a12010-09-23 18:30:22 +020087
Frédéric Lécaille54bff832019-03-26 10:25:20 +010088#define PEER_RESYNC_TIMEOUT 5000 /* 5 seconds */
89#define PEER_RECONNECT_TIMEOUT 5000 /* 5 seconds */
Frédéric Lécaille645635d2019-02-11 17:49:39 +010090#define PEER_HEARTBEAT_TIMEOUT 3000 /* 3 seconds */
91
Willy Tarreau49962b52021-02-12 16:56:22 +010092/* flags for "show peers" */
93#define PEERS_SHOW_F_DICT 0x00000001 /* also show the contents of the dictionary */
94
Emeric Brunb3971ab2015-05-12 18:49:09 +020095/*****************************/
96/* Sync message class */
97/*****************************/
98enum {
99 PEER_MSG_CLASS_CONTROL = 0,
100 PEER_MSG_CLASS_ERROR,
101 PEER_MSG_CLASS_STICKTABLE = 10,
102 PEER_MSG_CLASS_RESERVED = 255,
103};
104
105/*****************************/
106/* control message types */
107/*****************************/
108enum {
109 PEER_MSG_CTRL_RESYNCREQ = 0,
110 PEER_MSG_CTRL_RESYNCFINISHED,
111 PEER_MSG_CTRL_RESYNCPARTIAL,
112 PEER_MSG_CTRL_RESYNCCONFIRM,
Frédéric Lécaille645635d2019-02-11 17:49:39 +0100113 PEER_MSG_CTRL_HEARTBEAT,
Emeric Brunb3971ab2015-05-12 18:49:09 +0200114};
115
116/*****************************/
117/* error message types */
118/*****************************/
119enum {
120 PEER_MSG_ERR_PROTOCOL = 0,
121 PEER_MSG_ERR_SIZELIMIT,
122};
123
Emeric Brun530ba382020-06-02 11:17:42 +0200124/* network key types;
125 * network types were directly and mistakenly
126 * mapped on sample types, to keep backward
127 * compatiblitiy we keep those values but
128 * we now use a internal/network mapping
129 * to avoid further mistakes adding or
130 * modifying internals types
131 */
132enum {
133 PEER_KT_ANY = 0, /* any type */
134 PEER_KT_RESV1, /* UNUSED */
135 PEER_KT_SINT, /* signed 64bits integer type */
136 PEER_KT_RESV3, /* UNUSED */
137 PEER_KT_IPV4, /* ipv4 type */
138 PEER_KT_IPV6, /* ipv6 type */
139 PEER_KT_STR, /* char string type */
140 PEER_KT_BIN, /* buffer type */
141 PEER_KT_TYPES /* number of types, must always be last */
142};
143
144/* Map used to retrieve network type from internal type
145 * Note: Undeclared mapping maps entry to PEER_KT_ANY == 0
146 */
147static int peer_net_key_type[SMP_TYPES] = {
148 [SMP_T_SINT] = PEER_KT_SINT,
149 [SMP_T_IPV4] = PEER_KT_IPV4,
150 [SMP_T_IPV6] = PEER_KT_IPV6,
151 [SMP_T_STR] = PEER_KT_STR,
152 [SMP_T_BIN] = PEER_KT_BIN,
153};
154
155/* Map used to retrieve internal type from external type
156 * Note: Undeclared mapping maps entry to SMP_T_ANY == 0
157 */
158static int peer_int_key_type[PEER_KT_TYPES] = {
159 [PEER_KT_SINT] = SMP_T_SINT,
160 [PEER_KT_IPV4] = SMP_T_IPV4,
161 [PEER_KT_IPV6] = SMP_T_IPV6,
162 [PEER_KT_STR] = SMP_T_STR,
163 [PEER_KT_BIN] = SMP_T_BIN,
164};
165
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100166/*
167 * Parameters used by functions to build peer protocol messages. */
168struct peer_prep_params {
169 struct {
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100170 struct peer *peer;
171 } hello;
172 struct {
173 unsigned int st1;
174 } error_status;
175 struct {
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100176 struct stksess *stksess;
177 struct shared_table *shared_table;
178 unsigned int updateid;
179 int use_identifier;
180 int use_timed;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200181 struct peer *peer;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100182 } updt;
183 struct {
184 struct shared_table *shared_table;
185 } swtch;
186 struct {
187 struct shared_table *shared_table;
188 } ack;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100189 struct {
190 unsigned char head[2];
191 } control;
192 struct {
193 unsigned char head[2];
194 } error;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100195};
Emeric Brunb3971ab2015-05-12 18:49:09 +0200196
197/*******************************/
198/* stick table sync mesg types */
199/* Note: ids >= 128 contains */
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500200/* id message contains data */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200201/*******************************/
Olivier Houchard33992262018-10-16 18:49:26 +0200202#define PEER_MSG_STKT_UPDATE 0x80
203#define PEER_MSG_STKT_INCUPDATE 0x81
204#define PEER_MSG_STKT_DEFINE 0x82
205#define PEER_MSG_STKT_SWITCH 0x83
206#define PEER_MSG_STKT_ACK 0x84
207#define PEER_MSG_STKT_UPDATE_TIMED 0x85
208#define PEER_MSG_STKT_INCUPDATE_TIMED 0x86
Frédéric Lécaille36fb77e2019-06-04 08:28:19 +0200209/* All the stick-table message identifiers abova have the #7 bit set */
210#define PEER_MSG_STKT_BIT 7
211#define PEER_MSG_STKT_BIT_MASK (1 << PEER_MSG_STKT_BIT)
Emeric Brun2b920a12010-09-23 18:30:22 +0200212
Frédéric Lécaille39143342019-05-24 14:32:27 +0200213/* The maximum length of an encoded data length. */
214#define PEER_MSG_ENC_LENGTH_MAXLEN 5
215
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200216/* Minimum 64-bits value encoded with 2 bytes */
217#define PEER_ENC_2BYTES_MIN 0xf0 /* 0xf0 (or 240) */
218/* 3 bytes */
219#define PEER_ENC_3BYTES_MIN ((1ULL << 11) | PEER_ENC_2BYTES_MIN) /* 0x8f0 (or 2288) */
220/* 4 bytes */
221#define PEER_ENC_4BYTES_MIN ((1ULL << 18) | PEER_ENC_3BYTES_MIN) /* 0x408f0 (or 264432) */
222/* 5 bytes */
223#define PEER_ENC_5BYTES_MIN ((1ULL << 25) | PEER_ENC_4BYTES_MIN) /* 0x20408f0 (or 33818864) */
224/* 6 bytes */
225#define PEER_ENC_6BYTES_MIN ((1ULL << 32) | PEER_ENC_5BYTES_MIN) /* 0x1020408f0 (or 4328786160) */
226/* 7 bytes */
227#define PEER_ENC_7BYTES_MIN ((1ULL << 39) | PEER_ENC_6BYTES_MIN) /* 0x81020408f0 (or 554084600048) */
228/* 8 bytes */
229#define PEER_ENC_8BYTES_MIN ((1ULL << 46) | PEER_ENC_7BYTES_MIN) /* 0x4081020408f0 (or 70922828777712) */
230/* 9 bytes */
231#define PEER_ENC_9BYTES_MIN ((1ULL << 53) | PEER_ENC_8BYTES_MIN) /* 0x204081020408f0 (or 9078122083518704) */
232/* 10 bytes */
233#define PEER_ENC_10BYTES_MIN ((1ULL << 60) | PEER_ENC_9BYTES_MIN) /* 0x10204081020408f0 (or 1161999626690365680) */
234
235/* #7 bit used to detect the last byte to be encoded */
236#define PEER_ENC_STOP_BIT 7
237/* The byte minimum value with #7 bit set */
238#define PEER_ENC_STOP_BYTE (1 << PEER_ENC_STOP_BIT)
239/* The left most number of bits set for PEER_ENC_2BYTES_MIN */
240#define PEER_ENC_2BYTES_MIN_BITS 4
241
Frédéric Lécaille39143342019-05-24 14:32:27 +0200242#define PEER_MSG_HEADER_LEN 2
243
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200244#define PEER_STKT_CACHE_MAX_ENTRIES 128
245
Emeric Brun2b920a12010-09-23 18:30:22 +0200246/**********************************/
247/* Peer Session IO handler states */
248/**********************************/
249
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100250enum {
251 PEER_SESS_ST_ACCEPT = 0, /* Initial state for session create by an accept, must be zero! */
252 PEER_SESS_ST_GETVERSION, /* Validate supported protocol version */
253 PEER_SESS_ST_GETHOST, /* Validate host ID correspond to local host id */
254 PEER_SESS_ST_GETPEER, /* Validate peer ID correspond to a known remote peer id */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100255 /* after this point, data were possibly exchanged */
256 PEER_SESS_ST_SENDSUCCESS, /* Send ret code 200 (success) and wait for message */
257 PEER_SESS_ST_CONNECT, /* Initial state for session create on a connect, push presentation into buffer */
258 PEER_SESS_ST_GETSTATUS, /* Wait for the welcome message */
259 PEER_SESS_ST_WAITMSG, /* Wait for data messages */
260 PEER_SESS_ST_EXIT, /* Exit with status code */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200261 PEER_SESS_ST_ERRPROTO, /* Send error proto message before exit */
262 PEER_SESS_ST_ERRSIZE, /* Send error size message before exit */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100263 PEER_SESS_ST_END, /* Killed session */
264};
Emeric Brun2b920a12010-09-23 18:30:22 +0200265
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100266/***************************************************/
267/* Peer Session status code - part of the protocol */
268/***************************************************/
Emeric Brun2b920a12010-09-23 18:30:22 +0200269
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100270#define PEER_SESS_SC_CONNECTCODE 100 /* connect in progress */
271#define PEER_SESS_SC_CONNECTEDCODE 110 /* tcp connect success */
Emeric Brun2b920a12010-09-23 18:30:22 +0200272
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100273#define PEER_SESS_SC_SUCCESSCODE 200 /* accept or connect successful */
Emeric Brun2b920a12010-09-23 18:30:22 +0200274
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100275#define PEER_SESS_SC_TRYAGAIN 300 /* try again later */
Emeric Brun2b920a12010-09-23 18:30:22 +0200276
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100277#define PEER_SESS_SC_ERRPROTO 501 /* error protocol */
278#define PEER_SESS_SC_ERRVERSION 502 /* unknown protocol version */
279#define PEER_SESS_SC_ERRHOST 503 /* bad host name */
280#define PEER_SESS_SC_ERRPEER 504 /* unknown peer */
Emeric Brun2b920a12010-09-23 18:30:22 +0200281
282#define PEER_SESSION_PROTO_NAME "HAProxyS"
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200283#define PEER_MAJOR_VER 2
284#define PEER_MINOR_VER 1
285#define PEER_DWNGRD_MINOR_VER 0
Emeric Brun2b920a12010-09-23 18:30:22 +0200286
Willy Tarreau6254a922019-01-29 17:45:23 +0100287static size_t proto_len = sizeof(PEER_SESSION_PROTO_NAME) - 1;
Frédéric Lécailleed2b4a62017-07-13 09:07:09 +0200288struct peers *cfg_peers = NULL;
Emeric Brun9ef2ad72019-04-02 17:22:01 +0200289static void peer_session_forceshutdown(struct peer *peer);
Emeric Brun2b920a12010-09-23 18:30:22 +0200290
Frédéric Lécaille6c391982019-06-06 11:34:03 +0200291static struct ebpt_node *dcache_tx_insert(struct dcache *dc,
292 struct dcache_tx_entry *i);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200293static inline void flush_dcache(struct peer *peer);
294
Frédéric Lécailled8659352020-11-10 16:18:03 +0100295/* trace source and events */
296static void peers_trace(enum trace_level level, uint64_t mask,
297 const struct trace_source *src,
298 const struct ist where, const struct ist func,
299 const void *a1, const void *a2, const void *a3, const void *a4);
300
301static const struct trace_event peers_trace_events[] = {
302#define PEERS_EV_UPDTMSG (1 << 0)
303 { .mask = PEERS_EV_UPDTMSG, .name = "updtmsg", .desc = "update message received" },
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100304#define PEERS_EV_ACKMSG (1 << 1)
305 { .mask = PEERS_EV_ACKMSG, .name = "ackmsg", .desc = "ack message received" },
306#define PEERS_EV_SWTCMSG (1 << 2)
307 { .mask = PEERS_EV_SWTCMSG, .name = "swtcmsg", .desc = "switch message received" },
308#define PEERS_EV_DEFMSG (1 << 3)
309 { .mask = PEERS_EV_DEFMSG, .name = "defmsg", .desc = "definition message received" },
310#define PEERS_EV_CTRLMSG (1 << 4)
311 { .mask = PEERS_EV_CTRLMSG, .name = "ctrlmsg", .desc = "control message sent/received" },
312#define PEERS_EV_SESSREL (1 << 5)
313 { .mask = PEERS_EV_SESSREL, .name = "sessrl", .desc = "peer session releasing" },
314#define PEERS_EV_PROTOERR (1 << 6)
315 { .mask = PEERS_EV_PROTOERR, .name = "protoerr", .desc = "protocol error" },
Frédéric Lécailled8659352020-11-10 16:18:03 +0100316};
317
318static const struct name_desc peers_trace_lockon_args[4] = {
319 /* arg1 */ { /* already used by the connection */ },
320 /* arg2 */ { .name="peers", .desc="Peers protocol" },
321 /* arg3 */ { },
322 /* arg4 */ { }
323};
324
325static const struct name_desc peers_trace_decoding[] = {
326#define PEERS_VERB_CLEAN 1
327 { .name="clean", .desc="only user-friendly stuff, generally suitable for level \"user\"" },
328 { /* end */ }
329};
330
331
332struct trace_source trace_peers = {
333 .name = IST("peers"),
334 .desc = "Peers protocol",
335 .arg_def = TRC_ARG1_CONN, /* TRACE()'s first argument is always a connection */
336 .default_cb = peers_trace,
337 .known_events = peers_trace_events,
338 .lockon_args = peers_trace_lockon_args,
339 .decoding = peers_trace_decoding,
340 .report_events = ~0, /* report everything by default */
341};
342
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100343/* Return peer control message types as strings (only for debugging purpose). */
344static inline char *ctrl_msg_type_str(unsigned int type)
345{
346 switch (type) {
347 case PEER_MSG_CTRL_RESYNCREQ:
348 return "RESYNCREQ";
349 case PEER_MSG_CTRL_RESYNCFINISHED:
350 return "RESYNCFINISHED";
351 case PEER_MSG_CTRL_RESYNCPARTIAL:
352 return "RESYNCPARTIAL";
353 case PEER_MSG_CTRL_RESYNCCONFIRM:
354 return "RESYNCCONFIRM";
355 case PEER_MSG_CTRL_HEARTBEAT:
356 return "HEARTBEAT";
357 default:
358 return "???";
359 }
360}
361
Frédéric Lécailled8659352020-11-10 16:18:03 +0100362#define TRACE_SOURCE &trace_peers
363INITCALL1(STG_REGISTER, trace_register_source, TRACE_SOURCE);
364
365static void peers_trace(enum trace_level level, uint64_t mask,
366 const struct trace_source *src,
367 const struct ist where, const struct ist func,
368 const void *a1, const void *a2, const void *a3, const void *a4)
369{
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100370 if (mask & (PEERS_EV_UPDTMSG|PEERS_EV_ACKMSG|PEERS_EV_SWTCMSG)) {
Frédéric Lécailled8659352020-11-10 16:18:03 +0100371 if (a2) {
372 const struct peer *peer = a2;
373
374 chunk_appendf(&trace_buf, " peer=%s", peer->id);
375 }
376 if (a3) {
377 const char *p = a3;
378
379 chunk_appendf(&trace_buf, " @%p", p);
380 }
381 if (a4) {
382 const size_t *val = a4;
383
384 chunk_appendf(&trace_buf, " %llu", (unsigned long long)*val);
385 }
386 }
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100387
388 if (mask & PEERS_EV_DEFMSG) {
389 if (a2) {
390 const struct peer *peer = a2;
391
392 chunk_appendf(&trace_buf, " peer=%s", peer->id);
393 }
394 if (a3) {
395 const char *p = a3;
396
397 chunk_appendf(&trace_buf, " @%p", p);
398 }
399 if (a4) {
400 const int *val = a4;
401
402 chunk_appendf(&trace_buf, " %d", *val);
403 }
404 }
405
406 if (mask & PEERS_EV_CTRLMSG) {
407 if (a2) {
408 const unsigned char *ctrl_msg_type = a2;
409
410 chunk_appendf(&trace_buf, " %s", ctrl_msg_type_str(*ctrl_msg_type));
411
412 }
413 if (a3) {
414 const char *local_peer = a3;
415
416 chunk_appendf(&trace_buf, " %s", local_peer);
417 }
418
419 if (a4) {
420 const char *remote_peer = a4;
421
422 chunk_appendf(&trace_buf, " -> %s", remote_peer);
423 }
424 }
425
426 if (mask & (PEERS_EV_SESSREL|PEERS_EV_PROTOERR)) {
427 if (a2) {
428 const struct peer *peer = a2;
429 struct peers *peers = NULL;
430
Frédéric Lécaille4b1a05f2021-01-17 13:08:39 +0100431 if (peer && peer->appctx) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100432 struct stream_interface *si;
433
434 si = peer->appctx->owner;
435 if (si) {
436 struct stream *s = si_strm(si);
437
438 peers = strm_fe(s)->parent;
439 }
440 }
441
442 if (peers)
443 chunk_appendf(&trace_buf, " %s", peers->local->id);
444 if (peer)
445 chunk_appendf(&trace_buf, " -> %s", peer->id);
446 }
447
448 if (a3) {
449 const int *prev_state = a3;
450
451 chunk_appendf(&trace_buf, " prev_state=%d\n", *prev_state);
452 }
453 }
Frédéric Lécailled8659352020-11-10 16:18:03 +0100454}
455
Frédéric Lécaille95679dc2019-04-15 10:25:27 +0200456static const char *statuscode_str(int statuscode)
457{
458 switch (statuscode) {
459 case PEER_SESS_SC_CONNECTCODE:
460 return "CONN";
461 case PEER_SESS_SC_CONNECTEDCODE:
462 return "HSHK";
463 case PEER_SESS_SC_SUCCESSCODE:
464 return "ESTA";
465 case PEER_SESS_SC_TRYAGAIN:
466 return "RETR";
467 case PEER_SESS_SC_ERRPROTO:
468 return "PROT";
469 case PEER_SESS_SC_ERRVERSION:
470 return "VERS";
471 case PEER_SESS_SC_ERRHOST:
472 return "NAME";
473 case PEER_SESS_SC_ERRPEER:
474 return "UNKN";
475 default:
476 return "NONE";
477 }
478}
479
Emeric Brun18928af2017-03-29 16:32:53 +0200480/* This function encode an uint64 to 'dynamic' length format.
481 The encoded value is written at address *str, and the
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500482 caller must assure that size after *str is large enough.
Emeric Brun18928af2017-03-29 16:32:53 +0200483 At return, the *str is set at the next Byte after then
484 encoded integer. The function returns then length of the
485 encoded integer in Bytes */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200486int intencode(uint64_t i, char **str) {
487 int idx = 0;
488 unsigned char *msg;
489
Emeric Brunb3971ab2015-05-12 18:49:09 +0200490 msg = (unsigned char *)*str;
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200491 if (i < PEER_ENC_2BYTES_MIN) {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200492 msg[0] = (unsigned char)i;
493 *str = (char *)&msg[idx+1];
494 return (idx+1);
495 }
496
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200497 msg[idx] =(unsigned char)i | PEER_ENC_2BYTES_MIN;
498 i = (i - PEER_ENC_2BYTES_MIN) >> PEER_ENC_2BYTES_MIN_BITS;
499 while (i >= PEER_ENC_STOP_BYTE) {
500 msg[++idx] = (unsigned char)i | PEER_ENC_STOP_BYTE;
501 i = (i - PEER_ENC_STOP_BYTE) >> PEER_ENC_STOP_BIT;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200502 }
503 msg[++idx] = (unsigned char)i;
504 *str = (char *)&msg[idx+1];
505 return (idx+1);
506}
507
508
509/* This function returns the decoded integer or 0
510 if decode failed
511 *str point on the beginning of the integer to decode
512 at the end of decoding *str point on the end of the
513 encoded integer or to null if end is reached */
Emeric Brun18928af2017-03-29 16:32:53 +0200514uint64_t intdecode(char **str, char *end)
515{
Emeric Brunb3971ab2015-05-12 18:49:09 +0200516 unsigned char *msg;
Emeric Brun18928af2017-03-29 16:32:53 +0200517 uint64_t i;
518 int shift;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200519
520 if (!*str)
521 return 0;
522
523 msg = (unsigned char *)*str;
Emeric Brun18928af2017-03-29 16:32:53 +0200524 if (msg >= (unsigned char *)end)
525 goto fail;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200526
Emeric Brun18928af2017-03-29 16:32:53 +0200527 i = *(msg++);
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200528 if (i >= PEER_ENC_2BYTES_MIN) {
529 shift = PEER_ENC_2BYTES_MIN_BITS;
Emeric Brun18928af2017-03-29 16:32:53 +0200530 do {
531 if (msg >= (unsigned char *)end)
532 goto fail;
533 i += (uint64_t)*msg << shift;
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200534 shift += PEER_ENC_STOP_BIT;
535 } while (*(msg++) >= PEER_ENC_STOP_BYTE);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200536 }
Frédéric Lécaillea8725ec2019-01-22 10:31:39 +0100537 *str = (char *)msg;
538 return i;
Emeric Brun18928af2017-03-29 16:32:53 +0200539
Frédéric Lécaillea8725ec2019-01-22 10:31:39 +0100540 fail:
541 *str = NULL;
542 return 0;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200543}
Emeric Brun2b920a12010-09-23 18:30:22 +0200544
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100545/*
546 * Build a "hello" peer protocol message.
547 * Return the number of written bytes written to build this messages if succeeded,
548 * 0 if not.
549 */
550static int peer_prepare_hellomsg(char *msg, size_t size, struct peer_prep_params *p)
551{
552 int min_ver, ret;
553 struct peer *peer;
554
555 peer = p->hello.peer;
556 min_ver = (peer->flags & PEER_F_DWNGRD) ? PEER_DWNGRD_MINOR_VER : PEER_MINOR_VER;
557 /* Prepare headers */
558 ret = snprintf(msg, size, PEER_SESSION_PROTO_NAME " %u.%u\n%s\n%s %d %d\n",
559 PEER_MAJOR_VER, min_ver, peer->id, localpeer, (int)getpid(), relative_pid);
560 if (ret >= size)
561 return 0;
562
563 return ret;
564}
565
566/*
567 * Build a "handshake succeeded" status message.
568 * Return the number of written bytes written to build this messages if succeeded,
569 * 0 if not.
570 */
571static int peer_prepare_status_successmsg(char *msg, size_t size, struct peer_prep_params *p)
572{
573 int ret;
574
575 ret = snprintf(msg, size, "%d\n", PEER_SESS_SC_SUCCESSCODE);
576 if (ret >= size)
577 return 0;
578
579 return ret;
580}
581
582/*
583 * Build an error status message.
584 * Return the number of written bytes written to build this messages if succeeded,
585 * 0 if not.
586 */
587static int peer_prepare_status_errormsg(char *msg, size_t size, struct peer_prep_params *p)
588{
589 int ret;
590 unsigned int st1;
591
592 st1 = p->error_status.st1;
593 ret = snprintf(msg, size, "%d\n", st1);
594 if (ret >= size)
595 return 0;
596
597 return ret;
598}
599
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200600/* Set the stick-table UPDATE message type byte at <msg_type> address,
601 * depending on <use_identifier> and <use_timed> boolean parameters.
602 * Always successful.
603 */
604static inline void peer_set_update_msg_type(char *msg_type, int use_identifier, int use_timed)
605{
606 if (use_timed) {
607 if (use_identifier)
608 *msg_type = PEER_MSG_STKT_UPDATE_TIMED;
609 else
610 *msg_type = PEER_MSG_STKT_INCUPDATE_TIMED;
611 }
612 else {
613 if (use_identifier)
614 *msg_type = PEER_MSG_STKT_UPDATE;
615 else
616 *msg_type = PEER_MSG_STKT_INCUPDATE;
617 }
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200618}
Emeric Brun2b920a12010-09-23 18:30:22 +0200619/*
Emeric Brunb3971ab2015-05-12 18:49:09 +0200620 * This prepare the data update message on the stick session <ts>, <st> is the considered
621 * stick table.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800622 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200623 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
624 * check size)
Emeric Brun2b920a12010-09-23 18:30:22 +0200625 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100626static int peer_prepare_updatemsg(char *msg, size_t size, struct peer_prep_params *p)
Emeric Brun2b920a12010-09-23 18:30:22 +0200627{
628 uint32_t netinteger;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200629 unsigned short datalen;
630 char *cursor, *datamsg;
Emeric Brun94900952015-06-11 18:25:54 +0200631 unsigned int data_type;
632 void *data_ptr;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100633 struct stksess *ts;
634 struct shared_table *st;
635 unsigned int updateid;
636 int use_identifier;
637 int use_timed;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200638 struct peer *peer;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100639
640 ts = p->updt.stksess;
641 st = p->updt.shared_table;
642 updateid = p->updt.updateid;
643 use_identifier = p->updt.use_identifier;
644 use_timed = p->updt.use_timed;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200645 peer = p->updt.peer;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200646
Frédéric Lécaille0e8db972019-05-24 14:34:34 +0200647 cursor = datamsg = msg + PEER_MSG_HEADER_LEN + PEER_MSG_ENC_LENGTH_MAXLEN;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200648
Emeric Brun2b920a12010-09-23 18:30:22 +0200649 /* construct message */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200650
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500651 /* check if we need to send the update identifier */
Emeric Brun819fc6f2017-06-13 19:37:32 +0200652 if (!st->last_pushed || updateid < st->last_pushed || ((updateid - st->last_pushed) != 1)) {
Emeric Bruna6a09982015-09-22 15:34:19 +0200653 use_identifier = 1;
Emeric Brun2b920a12010-09-23 18:30:22 +0200654 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200655
656 /* encode update identifier if needed */
657 if (use_identifier) {
Emeric Brun819fc6f2017-06-13 19:37:32 +0200658 netinteger = htonl(updateid);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200659 memcpy(cursor, &netinteger, sizeof(netinteger));
660 cursor += sizeof(netinteger);
Emeric Brun2b920a12010-09-23 18:30:22 +0200661 }
662
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200663 if (use_timed) {
664 netinteger = htonl(tick_remain(now_ms, ts->expire));
665 memcpy(cursor, &netinteger, sizeof(netinteger));
666 cursor += sizeof(netinteger);
667 }
668
Emeric Brunb3971ab2015-05-12 18:49:09 +0200669 /* encode the key */
Thierry FOURNIER5d24ebc2015-07-24 08:46:42 +0200670 if (st->table->type == SMP_T_STR) {
Emeric Brun2b920a12010-09-23 18:30:22 +0200671 int stlen = strlen((char *)ts->key.key);
672
Emeric Brunb3971ab2015-05-12 18:49:09 +0200673 intencode(stlen, &cursor);
674 memcpy(cursor, ts->key.key, stlen);
675 cursor += stlen;
Emeric Brun2b920a12010-09-23 18:30:22 +0200676 }
Thierry FOURNIER5d24ebc2015-07-24 08:46:42 +0200677 else if (st->table->type == SMP_T_SINT) {
Willy Tarreau6cde5d82020-02-25 09:41:22 +0100678 netinteger = htonl(read_u32(ts->key.key));
Emeric Brunb3971ab2015-05-12 18:49:09 +0200679 memcpy(cursor, &netinteger, sizeof(netinteger));
680 cursor += sizeof(netinteger);
Emeric Brun2b920a12010-09-23 18:30:22 +0200681 }
682 else {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200683 memcpy(cursor, ts->key.key, st->table->key_size);
684 cursor += st->table->key_size;
Emeric Brun2b920a12010-09-23 18:30:22 +0200685 }
686
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100687 HA_RWLOCK_RDLOCK(STK_SESS_LOCK, &ts->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200688 /* encode values */
Emeric Brun94900952015-06-11 18:25:54 +0200689 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200690
Emeric Brun94900952015-06-11 18:25:54 +0200691 data_ptr = stktable_data_ptr(st->table, ts, data_type);
692 if (data_ptr) {
693 switch (stktable_data_types[data_type].std_type) {
694 case STD_T_SINT: {
695 int data;
696
697 data = stktable_data_cast(data_ptr, std_t_sint);
698 intencode(data, &cursor);
699 break;
700 }
701 case STD_T_UINT: {
702 unsigned int data;
703
704 data = stktable_data_cast(data_ptr, std_t_uint);
705 intencode(data, &cursor);
706 break;
707 }
708 case STD_T_ULL: {
709 unsigned long long data;
710
711 data = stktable_data_cast(data_ptr, std_t_ull);
712 intencode(data, &cursor);
713 break;
714 }
715 case STD_T_FRQP: {
716 struct freq_ctr_period *frqp;
717
718 frqp = &stktable_data_cast(data_ptr, std_t_frqp);
719 intencode((unsigned int)(now_ms - frqp->curr_tick), &cursor);
720 intencode(frqp->curr_ctr, &cursor);
721 intencode(frqp->prev_ctr, &cursor);
722 break;
723 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200724 case STD_T_DICT: {
725 struct dict_entry *de;
Frédéric Lécaille6c391982019-06-06 11:34:03 +0200726 struct ebpt_node *cached_de;
Willy Tarreau237f8ae2019-06-06 16:40:43 +0200727 struct dcache_tx_entry cde = { };
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200728 char *beg, *end;
729 size_t value_len, data_len;
730 struct dcache *dc;
731
732 de = stktable_data_cast(data_ptr, std_t_dict);
Frédéric Lécailleaf9990f2019-11-13 17:50:34 +0100733 if (!de) {
734 /* No entry */
735 intencode(0, &cursor);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200736 break;
Frédéric Lécailleaf9990f2019-11-13 17:50:34 +0100737 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200738
739 dc = peer->dcache;
Frédéric Lécaille6c391982019-06-06 11:34:03 +0200740 cde.entry.key = de;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200741 cached_de = dcache_tx_insert(dc, &cde);
Frédéric Lécaillefd827932019-06-07 10:34:04 +0200742 if (cached_de == &cde.entry) {
743 if (cde.id + 1 >= PEER_ENC_2BYTES_MIN)
744 break;
745 /* Encode the length of the remaining data -> 1 */
746 intencode(1, &cursor);
747 /* Encode the cache entry ID */
748 intencode(cde.id + 1, &cursor);
749 }
750 else {
751 /* Leave enough room to encode the remaining data length. */
752 end = beg = cursor + PEER_MSG_ENC_LENGTH_MAXLEN;
753 /* Encode the dictionary entry key */
754 intencode(cde.id + 1, &end);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200755 /* Encode the length of the dictionary entry data */
Frédéric Lécaillefd827932019-06-07 10:34:04 +0200756 value_len = de->len;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200757 intencode(value_len, &end);
758 /* Copy the data */
759 memcpy(end, de->value.key, value_len);
760 end += value_len;
Frédéric Lécaillefd827932019-06-07 10:34:04 +0200761 /* Encode the length of the data */
762 data_len = end - beg;
763 intencode(data_len, &cursor);
764 memmove(cursor, beg, data_len);
765 cursor += data_len;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200766 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200767 break;
768 }
Emeric Brun94900952015-06-11 18:25:54 +0200769 }
770 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200771 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100772 HA_RWLOCK_RDUNLOCK(STK_SESS_LOCK, &ts->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200773
774 /* Compute datalen */
775 datalen = (cursor - datamsg);
776
777 /* prepare message header */
778 msg[0] = PEER_MSG_CLASS_STICKTABLE;
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200779 peer_set_update_msg_type(&msg[1], use_identifier, use_timed);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200780 cursor = &msg[2];
781 intencode(datalen, &cursor);
782
783 /* move data after header */
784 memmove(cursor, datamsg, datalen);
785
786 /* return header size + data_len */
787 return (cursor - msg) + datalen;
788}
789
790/*
791 * This prepare the switch table message to targeted share table <st>.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800792 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200793 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
794 * check size)
795 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100796static int peer_prepare_switchmsg(char *msg, size_t size, struct peer_prep_params *params)
Emeric Brunb3971ab2015-05-12 18:49:09 +0200797{
798 int len;
799 unsigned short datalen;
Willy Tarreau83061a82018-07-13 11:56:34 +0200800 struct buffer *chunk;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200801 char *cursor, *datamsg, *chunkp, *chunkq;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200802 uint64_t data = 0;
Emeric Brun94900952015-06-11 18:25:54 +0200803 unsigned int data_type;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100804 struct shared_table *st;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200805
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100806 st = params->swtch.shared_table;
Frédéric Lécaille39143342019-05-24 14:32:27 +0200807 cursor = datamsg = msg + PEER_MSG_HEADER_LEN + PEER_MSG_ENC_LENGTH_MAXLEN;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200808
809 /* Encode data */
810
811 /* encode local id */
812 intencode(st->local_id, &cursor);
813
814 /* encode table name */
Frédéric Lécaille7fcc24d2019-03-20 15:09:45 +0100815 len = strlen(st->table->nid);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200816 intencode(len, &cursor);
Frédéric Lécaille7fcc24d2019-03-20 15:09:45 +0100817 memcpy(cursor, st->table->nid, len);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200818 cursor += len;
819
820 /* encode table type */
821
Emeric Brun530ba382020-06-02 11:17:42 +0200822 intencode(peer_net_key_type[st->table->type], &cursor);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200823
824 /* encode table key size */
825 intencode(st->table->key_size, &cursor);
826
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200827 chunk = get_trash_chunk();
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200828 chunkp = chunkq = chunk->area;
Emeric Brun94900952015-06-11 18:25:54 +0200829 /* encode available known data types in table */
830 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
831 if (st->table->data_ofs[data_type]) {
832 switch (stktable_data_types[data_type].std_type) {
833 case STD_T_SINT:
834 case STD_T_UINT:
835 case STD_T_ULL:
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200836 case STD_T_DICT:
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200837 data |= 1 << data_type;
838 break;
Emeric Brun94900952015-06-11 18:25:54 +0200839 case STD_T_FRQP:
840 data |= 1 << data_type;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200841 intencode(data_type, &chunkq);
842 intencode(st->table->data_arg[data_type].u, &chunkq);
Emeric Brun94900952015-06-11 18:25:54 +0200843 break;
844 }
845 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200846 }
847 intencode(data, &cursor);
848
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200849 /* Encode stick-table entries duration. */
850 intencode(st->table->expire, &cursor);
851
852 if (chunkq > chunkp) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200853 chunk->data = chunkq - chunkp;
854 memcpy(cursor, chunk->area, chunk->data);
855 cursor += chunk->data;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200856 }
857
Emeric Brunb3971ab2015-05-12 18:49:09 +0200858 /* Compute datalen */
859 datalen = (cursor - datamsg);
Emeric Brun2b920a12010-09-23 18:30:22 +0200860
Emeric Brunb3971ab2015-05-12 18:49:09 +0200861 /* prepare message header */
862 msg[0] = PEER_MSG_CLASS_STICKTABLE;
863 msg[1] = PEER_MSG_STKT_DEFINE;
864 cursor = &msg[2];
865 intencode(datalen, &cursor);
Emeric Brun2b920a12010-09-23 18:30:22 +0200866
Emeric Brunb3971ab2015-05-12 18:49:09 +0200867 /* move data after header */
868 memmove(cursor, datamsg, datalen);
869
870 /* return header size + data_len */
871 return (cursor - msg) + datalen;
Emeric Brun2b920a12010-09-23 18:30:22 +0200872}
873
Emeric Brunb3971ab2015-05-12 18:49:09 +0200874/*
875 * This prepare the acknowledge message on the stick session <ts>, <st> is the considered
876 * stick table.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800877 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200878 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
879 * check size)
880 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100881static int peer_prepare_ackmsg(char *msg, size_t size, struct peer_prep_params *p)
Emeric Brunb3971ab2015-05-12 18:49:09 +0200882{
883 unsigned short datalen;
884 char *cursor, *datamsg;
885 uint32_t netinteger;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100886 struct shared_table *st;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200887
Frédéric Lécaille39143342019-05-24 14:32:27 +0200888 cursor = datamsg = msg + PEER_MSG_HEADER_LEN + PEER_MSG_ENC_LENGTH_MAXLEN;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200889
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100890 st = p->ack.shared_table;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200891 intencode(st->remote_id, &cursor);
892 netinteger = htonl(st->last_get);
893 memcpy(cursor, &netinteger, sizeof(netinteger));
894 cursor += sizeof(netinteger);
895
896 /* Compute datalen */
897 datalen = (cursor - datamsg);
898
899 /* prepare message header */
900 msg[0] = PEER_MSG_CLASS_STICKTABLE;
Emeric Brune1ab8082015-08-21 11:48:54 +0200901 msg[1] = PEER_MSG_STKT_ACK;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200902 cursor = &msg[2];
903 intencode(datalen, &cursor);
904
905 /* move data after header */
906 memmove(cursor, datamsg, datalen);
907
908 /* return header size + data_len */
909 return (cursor - msg) + datalen;
910}
Emeric Brun2b920a12010-09-23 18:30:22 +0200911
912/*
Emeric Brun9ef2ad72019-04-02 17:22:01 +0200913 * Function to deinit connected peer
914 */
915void __peer_session_deinit(struct peer *peer)
916{
917 struct stream_interface *si;
918 struct stream *s;
919 struct peers *peers;
920
921 if (!peer->appctx)
922 return;
923
924 si = peer->appctx->owner;
925 if (!si)
926 return;
927
928 s = si_strm(si);
929 if (!s)
930 return;
931
932 peers = strm_fe(s)->parent;
933 if (!peers)
934 return;
935
936 if (peer->appctx->st0 == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +0200937 HA_ATOMIC_DEC(&connected_peers);
Emeric Brun9ef2ad72019-04-02 17:22:01 +0200938
Willy Tarreau4781b152021-04-06 13:53:36 +0200939 HA_ATOMIC_DEC(&active_peers);
Emeric Brun9ef2ad72019-04-02 17:22:01 +0200940
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200941 flush_dcache(peer);
942
Emeric Brun9ef2ad72019-04-02 17:22:01 +0200943 /* Re-init current table pointers to force announcement on re-connect */
944 peer->remote_table = peer->last_local_table = NULL;
945 peer->appctx = NULL;
946 if (peer->flags & PEER_F_LEARN_ASSIGN) {
947 /* unassign current peer for learning */
948 peer->flags &= ~(PEER_F_LEARN_ASSIGN);
949 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
950
951 /* reschedule a resync */
952 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
953 }
954 /* reset teaching and learning flags to 0 */
955 peer->flags &= PEER_TEACH_RESET;
956 peer->flags &= PEER_LEARN_RESET;
957 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
958}
959
960/*
Emeric Brun2b920a12010-09-23 18:30:22 +0200961 * Callback to release a session with a peer
962 */
Willy Tarreau00a37f02015-04-13 12:05:19 +0200963static void peer_session_release(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +0200964{
Vincent Bernat3c2f2f22016-04-03 13:48:42 +0200965 struct peer *peer = appctx->ctx.peers.ptr;
Emeric Brun2b920a12010-09-23 18:30:22 +0200966
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100967 TRACE_PROTO("releasing peer session", PEERS_EV_SESSREL, NULL, peer);
Willy Tarreau7b4b4992013-12-01 09:15:12 +0100968 /* appctx->ctx.peers.ptr is not a peer session */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100969 if (appctx->st0 < PEER_SESS_ST_SENDSUCCESS)
Emeric Brun2b920a12010-09-23 18:30:22 +0200970 return;
971
972 /* peer session identified */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200973 if (peer) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100974 HA_SPIN_LOCK(PEER_LOCK, &peer->lock);
Emeric Brun9ef2ad72019-04-02 17:22:01 +0200975 if (peer->appctx == appctx)
976 __peer_session_deinit(peer);
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +0200977 peer->flags &= ~PEER_F_ALIVE;
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100978 HA_SPIN_UNLOCK(PEER_LOCK, &peer->lock);
Emeric Brun2b920a12010-09-23 18:30:22 +0200979 }
980}
981
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200982/* Retrieve the major and minor versions of peers protocol
983 * announced by a remote peer. <str> is a null-terminated
984 * string with the following format: "<maj_ver>.<min_ver>".
985 */
986static int peer_get_version(const char *str,
987 unsigned int *maj_ver, unsigned int *min_ver)
988{
989 unsigned int majv, minv;
990 const char *pos, *saved;
991 const char *end;
992
993 saved = pos = str;
994 end = str + strlen(str);
995
996 majv = read_uint(&pos, end);
997 if (saved == pos || *pos++ != '.')
998 return -1;
999
1000 saved = pos;
1001 minv = read_uint(&pos, end);
1002 if (saved == pos || pos != end)
1003 return -1;
1004
1005 *maj_ver = majv;
1006 *min_ver = minv;
1007
1008 return 0;
1009}
Emeric Brun2b920a12010-09-23 18:30:22 +02001010
1011/*
Frédéric Lécaillece025572019-01-21 13:38:06 +01001012 * Parse a line terminated by an optional '\r' character, followed by a mandatory
1013 * '\n' character.
1014 * Returns 1 if succeeded or 0 if a '\n' character could not be found, and -1 if
1015 * a line could not be read because the communication channel is closed.
1016 */
1017static inline int peer_getline(struct appctx *appctx)
1018{
1019 int n;
1020 struct stream_interface *si = appctx->owner;
1021
1022 n = co_getline(si_oc(si), trash.area, trash.size);
1023 if (!n)
1024 return 0;
1025
1026 if (n < 0 || trash.area[n - 1] != '\n') {
1027 appctx->st0 = PEER_SESS_ST_END;
1028 return -1;
1029 }
1030
1031 if (n > 1 && (trash.area[n - 2] == '\r'))
1032 trash.area[n - 2] = 0;
1033 else
1034 trash.area[n - 1] = 0;
1035
1036 co_skip(si_oc(si), n);
1037
1038 return n;
1039}
1040
1041/*
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001042 * Send a message after having called <peer_prepare_msg> to build it.
1043 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1044 * Returns -1 if there was not enough room left to send the message,
1045 * any other negative returned value must be considered as an error with an appcxt st0
1046 * returned value equal to PEER_SESS_ST_END.
1047 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001048static inline int peer_send_msg(struct appctx *appctx,
1049 int (*peer_prepare_msg)(char *, size_t, struct peer_prep_params *),
1050 struct peer_prep_params *params)
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001051{
1052 int ret, msglen;
1053 struct stream_interface *si = appctx->owner;
1054
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001055 msglen = peer_prepare_msg(trash.area, trash.size, params);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001056 if (!msglen) {
1057 /* internal error: message does not fit in trash */
1058 appctx->st0 = PEER_SESS_ST_END;
1059 return 0;
1060 }
1061
1062 /* message to buffer */
1063 ret = ci_putblk(si_ic(si), trash.area, msglen);
1064 if (ret <= 0) {
1065 if (ret == -1) {
1066 /* No more write possible */
1067 si_rx_room_blk(si);
1068 return -1;
1069 }
1070 appctx->st0 = PEER_SESS_ST_END;
1071 }
1072
1073 return ret;
1074}
1075
1076/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001077 * Send a hello message.
1078 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1079 * Returns -1 if there was not enough room left to send the message,
1080 * any other negative returned value must be considered as an error with an appcxt st0
1081 * returned value equal to PEER_SESS_ST_END.
1082 */
1083static inline int peer_send_hellomsg(struct appctx *appctx, struct peer *peer)
1084{
1085 struct peer_prep_params p = {
1086 .hello.peer = peer,
1087 };
1088
1089 return peer_send_msg(appctx, peer_prepare_hellomsg, &p);
1090}
1091
1092/*
1093 * Send a success peer handshake status message.
1094 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1095 * Returns -1 if there was not enough room left to send the message,
1096 * any other negative returned value must be considered as an error with an appcxt st0
1097 * returned value equal to PEER_SESS_ST_END.
1098 */
1099static inline int peer_send_status_successmsg(struct appctx *appctx)
1100{
1101 return peer_send_msg(appctx, peer_prepare_status_successmsg, NULL);
1102}
1103
1104/*
1105 * Send a peer handshake status error message.
1106 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1107 * Returns -1 if there was not enough room left to send the message,
1108 * any other negative returned value must be considered as an error with an appcxt st0
1109 * returned value equal to PEER_SESS_ST_END.
1110 */
1111static inline int peer_send_status_errormsg(struct appctx *appctx)
1112{
1113 struct peer_prep_params p = {
1114 .error_status.st1 = appctx->st1,
1115 };
1116
1117 return peer_send_msg(appctx, peer_prepare_status_errormsg, &p);
1118}
1119
1120/*
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001121 * Send a stick-table switch message.
1122 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1123 * Returns -1 if there was not enough room left to send the message,
1124 * any other negative returned value must be considered as an error with an appcxt st0
1125 * returned value equal to PEER_SESS_ST_END.
1126 */
1127static inline int peer_send_switchmsg(struct shared_table *st, struct appctx *appctx)
1128{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001129 struct peer_prep_params p = {
1130 .swtch.shared_table = st,
1131 };
1132
1133 return peer_send_msg(appctx, peer_prepare_switchmsg, &p);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001134}
1135
1136/*
1137 * Send a stick-table update acknowledgement message.
1138 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1139 * Returns -1 if there was not enough room left to send the message,
1140 * any other negative returned value must be considered as an error with an appcxt st0
1141 * returned value equal to PEER_SESS_ST_END.
1142 */
1143static inline int peer_send_ackmsg(struct shared_table *st, struct appctx *appctx)
1144{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001145 struct peer_prep_params p = {
1146 .ack.shared_table = st,
1147 };
1148
1149 return peer_send_msg(appctx, peer_prepare_ackmsg, &p);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001150}
1151
1152/*
Frédéric Lécaille87f554c2019-01-22 17:26:50 +01001153 * Send a stick-table update message.
1154 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1155 * Returns -1 if there was not enough room left to send the message,
1156 * any other negative returned value must be considered as an error with an appcxt st0
1157 * returned value equal to PEER_SESS_ST_END.
1158 */
1159static inline int peer_send_updatemsg(struct shared_table *st, struct appctx *appctx, struct stksess *ts,
1160 unsigned int updateid, int use_identifier, int use_timed)
1161{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001162 struct peer_prep_params p = {
Willy Tarreaua898f0c2020-07-03 19:09:29 +02001163 .updt = {
1164 .stksess = ts,
1165 .shared_table = st,
1166 .updateid = updateid,
1167 .use_identifier = use_identifier,
1168 .use_timed = use_timed,
1169 .peer = appctx->ctx.peers.ptr,
1170 },
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001171 };
1172
1173 return peer_send_msg(appctx, peer_prepare_updatemsg, &p);
Frédéric Lécaille87f554c2019-01-22 17:26:50 +01001174}
1175
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001176/*
1177 * Build a peer protocol control class message.
1178 * Returns the number of written bytes used to build the message if succeeded,
1179 * 0 if not.
1180 */
1181static int peer_prepare_control_msg(char *msg, size_t size, struct peer_prep_params *p)
1182{
1183 if (size < sizeof p->control.head)
1184 return 0;
1185
1186 msg[0] = p->control.head[0];
1187 msg[1] = p->control.head[1];
1188
1189 return 2;
1190}
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +01001191
1192/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001193 * Send a stick-table synchronization request message.
1194 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1195 * Returns -1 if there was not enough room left to send the message,
1196 * any other negative returned value must be considered as an error with an appctx st0
1197 * returned value equal to PEER_SESS_ST_END.
1198 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001199static inline int peer_send_resync_reqmsg(struct appctx *appctx,
1200 struct peer *peer, struct peers *peers)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001201{
1202 struct peer_prep_params p = {
1203 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCREQ, },
1204 };
1205
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001206 TRACE_PROTO("send control message", PEERS_EV_CTRLMSG,
1207 NULL, &p.control.head[1], peers->local->id, peer->id);
1208
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001209 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
1210}
1211
1212/*
1213 * Send a stick-table synchronization confirmation message.
1214 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1215 * Returns -1 if there was not enough room left to send the message,
1216 * any other negative returned value must be considered as an error with an appctx st0
1217 * returned value equal to PEER_SESS_ST_END.
1218 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001219static inline int peer_send_resync_confirmsg(struct appctx *appctx,
1220 struct peer *peer, struct peers *peers)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001221{
1222 struct peer_prep_params p = {
1223 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCCONFIRM, },
1224 };
1225
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001226 TRACE_PROTO("send control message", PEERS_EV_CTRLMSG,
1227 NULL, &p.control.head[1], peers->local->id, peer->id);
1228
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001229 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
1230}
1231
1232/*
1233 * Send a stick-table synchronization finished message.
1234 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1235 * Returns -1 if there was not enough room left to send the message,
1236 * any other negative returned value must be considered as an error with an appctx st0
1237 * returned value equal to PEER_SESS_ST_END.
1238 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001239static inline int peer_send_resync_finishedmsg(struct appctx *appctx,
1240 struct peer *peer, struct peers *peers)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001241{
1242 struct peer_prep_params p = {
1243 .control.head = { PEER_MSG_CLASS_CONTROL, },
1244 };
1245
Emeric Brun70de43b2020-03-16 10:51:01 +01001246 p.control.head[1] = (peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED ?
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001247 PEER_MSG_CTRL_RESYNCFINISHED : PEER_MSG_CTRL_RESYNCPARTIAL;
1248
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001249 TRACE_PROTO("send control message", PEERS_EV_CTRLMSG,
1250 NULL, &p.control.head[1], peers->local->id, peer->id);
1251
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001252 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
1253}
1254
1255/*
Frédéric Lécaille645635d2019-02-11 17:49:39 +01001256 * Send a heartbeat message.
1257 * Return 0 if the message could not be built modifying the appctx st0 to PEER_SESS_ST_END value.
1258 * Returns -1 if there was not enough room left to send the message,
1259 * any other negative returned value must be considered as an error with an appctx st0
1260 * returned value equal to PEER_SESS_ST_END.
1261 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001262static inline int peer_send_heartbeatmsg(struct appctx *appctx,
1263 struct peer *peer, struct peers *peers)
Frédéric Lécaille645635d2019-02-11 17:49:39 +01001264{
1265 struct peer_prep_params p = {
1266 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_HEARTBEAT, },
1267 };
1268
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001269 TRACE_PROTO("send control message", PEERS_EV_CTRLMSG,
1270 NULL, &p.control.head[1], peers->local->id, peer->id);
1271
Frédéric Lécaille645635d2019-02-11 17:49:39 +01001272 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
1273}
1274
1275/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001276 * Build a peer protocol error class message.
1277 * Returns the number of written bytes used to build the message if succeeded,
1278 * 0 if not.
1279 */
1280static int peer_prepare_error_msg(char *msg, size_t size, struct peer_prep_params *p)
1281{
1282 if (size < sizeof p->error.head)
1283 return 0;
1284
1285 msg[0] = p->error.head[0];
1286 msg[1] = p->error.head[1];
1287
1288 return 2;
1289}
1290
1291/*
1292 * Send a "size limit reached" error message.
1293 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1294 * Returns -1 if there was not enough room left to send the message,
1295 * any other negative returned value must be considered as an error with an appctx st0
1296 * returned value equal to PEER_SESS_ST_END.
1297 */
1298static inline int peer_send_error_size_limitmsg(struct appctx *appctx)
1299{
1300 struct peer_prep_params p = {
1301 .error.head = { PEER_MSG_CLASS_ERROR, PEER_MSG_ERR_SIZELIMIT, },
1302 };
1303
1304 return peer_send_msg(appctx, peer_prepare_error_msg, &p);
1305}
1306
1307/*
1308 * Send a "peer protocol" error message.
1309 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1310 * Returns -1 if there was not enough room left to send the message,
1311 * any other negative returned value must be considered as an error with an appctx st0
1312 * returned value equal to PEER_SESS_ST_END.
1313 */
1314static inline int peer_send_error_protomsg(struct appctx *appctx)
1315{
1316 struct peer_prep_params p = {
1317 .error.head = { PEER_MSG_CLASS_ERROR, PEER_MSG_ERR_PROTOCOL, },
1318 };
1319
1320 return peer_send_msg(appctx, peer_prepare_error_msg, &p);
1321}
1322
1323/*
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +01001324 * Function used to lookup for recent stick-table updates associated with
1325 * <st> shared stick-table when a lesson must be taught a peer (PEER_F_LEARN_ASSIGN flag set).
1326 */
1327static inline struct stksess *peer_teach_process_stksess_lookup(struct shared_table *st)
1328{
1329 struct eb32_node *eb;
1330
1331 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
1332 if (!eb) {
1333 eb = eb32_first(&st->table->updates);
1334 if (!eb || ((int)(eb->key - st->last_pushed) <= 0)) {
1335 st->table->commitupdate = st->last_pushed = st->table->localupdate;
1336 return NULL;
1337 }
1338 }
1339
1340 if ((int)(eb->key - st->table->localupdate) > 0) {
1341 st->table->commitupdate = st->last_pushed = st->table->localupdate;
1342 return NULL;
1343 }
1344
1345 return eb32_entry(eb, struct stksess, upd);
1346}
1347
1348/*
1349 * Function used to lookup for recent stick-table updates associated with
1350 * <st> shared stick-table during teach state 1 step.
1351 */
1352static inline struct stksess *peer_teach_stage1_stksess_lookup(struct shared_table *st)
1353{
1354 struct eb32_node *eb;
1355
1356 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
1357 if (!eb) {
1358 st->flags |= SHTABLE_F_TEACH_STAGE1;
1359 eb = eb32_first(&st->table->updates);
1360 if (eb)
1361 st->last_pushed = eb->key - 1;
1362 return NULL;
1363 }
1364
1365 return eb32_entry(eb, struct stksess, upd);
1366}
1367
1368/*
1369 * Function used to lookup for recent stick-table updates associated with
1370 * <st> shared stick-table during teach state 2 step.
1371 */
1372static inline struct stksess *peer_teach_stage2_stksess_lookup(struct shared_table *st)
1373{
1374 struct eb32_node *eb;
1375
1376 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
1377 if (!eb || eb->key > st->teaching_origin) {
1378 st->flags |= SHTABLE_F_TEACH_STAGE2;
1379 return NULL;
1380 }
1381
1382 return eb32_entry(eb, struct stksess, upd);
1383}
1384
1385/*
1386 * Generic function to emit update messages for <st> stick-table when a lesson must
1387 * be taught to the peer <p>.
1388 * <locked> must be set to 1 if the shared table <st> is already locked when entering
1389 * this function, 0 if not.
1390 *
1391 * This function temporary unlock/lock <st> when it sends stick-table updates or
1392 * when decrementing its refcount in case of any error when it sends this updates.
1393 *
1394 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1395 * Returns -1 if there was not enough room left to send the message,
1396 * any other negative returned value must be considered as an error with an appcxt st0
1397 * returned value equal to PEER_SESS_ST_END.
1398 * If it returns 0 or -1, this function leave <st> locked if already locked when entering this function
1399 * unlocked if not already locked when entering this function.
1400 */
1401static inline int peer_send_teachmsgs(struct appctx *appctx, struct peer *p,
1402 struct stksess *(*peer_stksess_lookup)(struct shared_table *),
1403 struct shared_table *st, int locked)
1404{
1405 int ret, new_pushed, use_timed;
1406
1407 ret = 1;
1408 use_timed = 0;
1409 if (st != p->last_local_table) {
1410 ret = peer_send_switchmsg(st, appctx);
1411 if (ret <= 0)
1412 return ret;
1413
1414 p->last_local_table = st;
1415 }
1416
1417 if (peer_stksess_lookup != peer_teach_process_stksess_lookup)
1418 use_timed = !(p->flags & PEER_F_DWNGRD);
1419
1420 /* We force new pushed to 1 to force identifier in update message */
1421 new_pushed = 1;
1422
1423 if (!locked)
1424 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1425
1426 while (1) {
1427 struct stksess *ts;
1428 unsigned updateid;
1429
1430 /* push local updates */
1431 ts = peer_stksess_lookup(st);
1432 if (!ts)
1433 break;
1434
1435 updateid = ts->upd.key;
1436 ts->ref_cnt++;
1437 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1438
1439 ret = peer_send_updatemsg(st, appctx, ts, updateid, new_pushed, use_timed);
1440 if (ret <= 0) {
1441 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1442 ts->ref_cnt--;
1443 if (!locked)
1444 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1445 return ret;
1446 }
1447
1448 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1449 ts->ref_cnt--;
1450 st->last_pushed = updateid;
1451
1452 if (peer_stksess_lookup == peer_teach_process_stksess_lookup &&
1453 (int)(st->last_pushed - st->table->commitupdate) > 0)
1454 st->table->commitupdate = st->last_pushed;
1455
1456 /* identifier may not needed in next update message */
1457 new_pushed = 0;
1458 }
1459
1460 out:
1461 if (!locked)
1462 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1463 return 1;
1464}
1465
1466/*
1467 * Function to emit update messages for <st> stick-table when a lesson must
1468 * be taught to the peer <p> (PEER_F_LEARN_ASSIGN flag set).
1469 *
1470 * Note that <st> shared stick-table is locked when calling this function.
1471 *
1472 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1473 * Returns -1 if there was not enough room left to send the message,
1474 * any other negative returned value must be considered as an error with an appcxt st0
1475 * returned value equal to PEER_SESS_ST_END.
1476 */
1477static inline int peer_send_teach_process_msgs(struct appctx *appctx, struct peer *p,
1478 struct shared_table *st)
1479{
1480 return peer_send_teachmsgs(appctx, p, peer_teach_process_stksess_lookup, st, 1);
1481}
1482
1483/*
1484 * Function to emit update messages for <st> stick-table when a lesson must
1485 * be taught to the peer <p> during teach state 1 step.
1486 *
1487 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1488 * Returns -1 if there was not enough room left to send the message,
1489 * any other negative returned value must be considered as an error with an appcxt st0
1490 * returned value equal to PEER_SESS_ST_END.
1491 */
1492static inline int peer_send_teach_stage1_msgs(struct appctx *appctx, struct peer *p,
1493 struct shared_table *st)
1494{
1495 return peer_send_teachmsgs(appctx, p, peer_teach_stage1_stksess_lookup, st, 0);
1496}
1497
1498/*
1499 * Function to emit update messages for <st> stick-table when a lesson must
1500 * be taught to the peer <p> during teach state 1 step.
1501 *
1502 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1503 * Returns -1 if there was not enough room left to send the message,
1504 * any other negative returned value must be considered as an error with an appcxt st0
1505 * returned value equal to PEER_SESS_ST_END.
1506 */
1507static inline int peer_send_teach_stage2_msgs(struct appctx *appctx, struct peer *p,
1508 struct shared_table *st)
1509{
1510 return peer_send_teachmsgs(appctx, p, peer_teach_stage2_stksess_lookup, st, 0);
1511}
1512
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001513
1514/*
1515 * Function used to parse a stick-table update message after it has been received
1516 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1517 * receipt buffer with <msg_end> being position of the end of the stick-table message.
1518 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1519 * was encountered.
1520 * <exp> must be set if the stick-table entry expires.
1521 * <updt> must be set for PEER_MSG_STKT_UPDATE or PEER_MSG_STKT_UPDATE_TIMED stick-table
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001522 * messages, in this case the stick-table update message is received with a stick-table
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001523 * update ID.
1524 * <totl> is the length of the stick-table update message computed upon receipt.
1525 */
Frédéric Lécaille444243c2019-01-24 15:40:11 +01001526static int peer_treat_updatemsg(struct appctx *appctx, struct peer *p, int updt, int exp,
1527 char **msg_cur, char *msg_end, int msg_len, int totl)
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001528{
1529 struct stream_interface *si = appctx->owner;
1530 struct shared_table *st = p->remote_table;
1531 struct stksess *ts, *newts;
1532 uint32_t update;
1533 int expire;
1534 unsigned int data_type;
1535 void *data_ptr;
1536
Frédéric Lécailled8659352020-11-10 16:18:03 +01001537 TRACE_ENTER(PEERS_EV_UPDTMSG, NULL, p);
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001538 /* Here we have data message */
1539 if (!st)
1540 goto ignore_msg;
1541
1542 expire = MS_TO_TICKS(st->table->expire);
1543
1544 if (updt) {
Frédéric Lécailled8659352020-11-10 16:18:03 +01001545 if (msg_len < sizeof(update)) {
1546 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001547 goto malformed_exit;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001548 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001549
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001550 memcpy(&update, *msg_cur, sizeof(update));
1551 *msg_cur += sizeof(update);
1552 st->last_get = htonl(update);
1553 }
1554 else {
1555 st->last_get++;
1556 }
1557
1558 if (exp) {
1559 size_t expire_sz = sizeof expire;
1560
Frédéric Lécailled8659352020-11-10 16:18:03 +01001561 if (*msg_cur + expire_sz > msg_end) {
1562 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1563 NULL, p, *msg_cur);
1564 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1565 NULL, p, msg_end, &expire_sz);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001566 goto malformed_exit;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001567 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001568
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001569 memcpy(&expire, *msg_cur, expire_sz);
1570 *msg_cur += expire_sz;
1571 expire = ntohl(expire);
1572 }
1573
1574 newts = stksess_new(st->table, NULL);
1575 if (!newts)
1576 goto ignore_msg;
1577
1578 if (st->table->type == SMP_T_STR) {
1579 unsigned int to_read, to_store;
1580
1581 to_read = intdecode(msg_cur, msg_end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001582 if (!*msg_cur) {
1583 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001584 goto malformed_free_newts;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001585 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001586
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001587 to_store = MIN(to_read, st->table->key_size - 1);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001588 if (*msg_cur + to_store > msg_end) {
1589 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1590 NULL, p, *msg_cur);
1591 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1592 NULL, p, msg_end, &to_store);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001593 goto malformed_free_newts;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001594 }
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001595
1596 memcpy(newts->key.key, *msg_cur, to_store);
1597 newts->key.key[to_store] = 0;
1598 *msg_cur += to_read;
1599 }
1600 else if (st->table->type == SMP_T_SINT) {
1601 unsigned int netinteger;
1602
Frédéric Lécailled8659352020-11-10 16:18:03 +01001603 if (*msg_cur + sizeof(netinteger) > msg_end) {
1604 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1605 NULL, p, *msg_cur);
1606 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1607 NULL, p, msg_end);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001608 goto malformed_free_newts;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001609 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001610
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001611 memcpy(&netinteger, *msg_cur, sizeof(netinteger));
1612 netinteger = ntohl(netinteger);
1613 memcpy(newts->key.key, &netinteger, sizeof(netinteger));
1614 *msg_cur += sizeof(netinteger);
1615 }
1616 else {
Frédéric Lécailled8659352020-11-10 16:18:03 +01001617 if (*msg_cur + st->table->key_size > msg_end) {
1618 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1619 NULL, p, *msg_cur);
1620 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1621 NULL, p, msg_end, &st->table->key_size);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001622 goto malformed_free_newts;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001623 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001624
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001625 memcpy(newts->key.key, *msg_cur, st->table->key_size);
1626 *msg_cur += st->table->key_size;
1627 }
1628
1629 /* lookup for existing entry */
1630 ts = stktable_set_entry(st->table, newts);
1631 if (ts != newts) {
1632 stksess_free(st->table, newts);
1633 newts = NULL;
1634 }
1635
1636 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1637
1638 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
Willy Tarreau1e82a142019-01-29 11:08:06 +01001639 uint64_t decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001640
1641 if (!((1 << data_type) & st->remote_data))
1642 continue;
1643
Willy Tarreau1e82a142019-01-29 11:08:06 +01001644 decoded_int = intdecode(msg_cur, msg_end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001645 if (!*msg_cur) {
1646 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001647 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001648 }
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001649
Willy Tarreau1e82a142019-01-29 11:08:06 +01001650 switch (stktable_data_types[data_type].std_type) {
1651 case STD_T_SINT:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001652 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1653 if (data_ptr)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001654 stktable_data_cast(data_ptr, std_t_sint) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001655 break;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001656
Willy Tarreau1e82a142019-01-29 11:08:06 +01001657 case STD_T_UINT:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001658 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1659 if (data_ptr)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001660 stktable_data_cast(data_ptr, std_t_uint) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001661 break;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001662
Willy Tarreau1e82a142019-01-29 11:08:06 +01001663 case STD_T_ULL:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001664 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1665 if (data_ptr)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001666 stktable_data_cast(data_ptr, std_t_ull) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001667 break;
Willy Tarreau1e82a142019-01-29 11:08:06 +01001668
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001669 case STD_T_FRQP: {
1670 struct freq_ctr_period data;
1671
1672 /* First bit is reserved for the freq_ctr_period lock
1673 Note: here we're still protected by the stksess lock
1674 so we don't need to update the update the freq_ctr_period
1675 using its internal lock */
1676
Willy Tarreau1e82a142019-01-29 11:08:06 +01001677 data.curr_tick = tick_add(now_ms, -decoded_int) & ~0x1;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001678 data.curr_ctr = intdecode(msg_cur, msg_end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001679 if (!*msg_cur) {
1680 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001681 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001682 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001683
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001684 data.prev_ctr = intdecode(msg_cur, msg_end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001685 if (!*msg_cur) {
1686 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001687 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001688 }
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001689
1690 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1691 if (data_ptr)
1692 stktable_data_cast(data_ptr, std_t_frqp) = data;
1693 break;
1694 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001695 case STD_T_DICT: {
1696 struct buffer *chunk;
1697 size_t data_len, value_len;
1698 unsigned int id;
1699 struct dict_entry *de;
1700 struct dcache *dc;
Frédéric Lécaille344e9482019-06-05 10:20:09 +02001701 char *end;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001702
Frédéric Lécailleaf9990f2019-11-13 17:50:34 +01001703 if (!decoded_int) {
1704 /* No entry. */
1705 break;
1706 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001707 data_len = decoded_int;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001708 if (*msg_cur + data_len > msg_end) {
1709 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1710 NULL, p, *msg_cur);
1711 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1712 NULL, p, msg_end, &data_len);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001713 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001714 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001715
Frédéric Lécaille344e9482019-06-05 10:20:09 +02001716 /* Compute the end of the current data, <msg_end> being at the end of
1717 * the entire message.
1718 */
1719 end = *msg_cur + data_len;
1720 id = intdecode(msg_cur, end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001721 if (!*msg_cur || !id) {
1722 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1723 NULL, p, *msg_cur, &id);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001724 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001725 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001726
1727 dc = p->dcache;
Frédéric Lécaille344e9482019-06-05 10:20:09 +02001728 if (*msg_cur == end) {
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001729 /* Dictionary entry key without value. */
Frédéric Lécaillef9e51be2020-11-12 19:53:11 +01001730 if (id > dc->max_entries) {
1731 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1732 NULL, p, NULL, &id);
1733 goto malformed_unlock;
1734 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001735 /* IDs sent over the network are numbered from 1. */
1736 de = dc->rx[id - 1].de;
1737 }
1738 else {
1739 chunk = get_trash_chunk();
Frédéric Lécaille344e9482019-06-05 10:20:09 +02001740 value_len = intdecode(msg_cur, end);
1741 if (!*msg_cur || *msg_cur + value_len > end ||
Frédéric Lécailled8659352020-11-10 16:18:03 +01001742 unlikely(value_len + 1 >= chunk->size)) {
1743 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1744 NULL, p, *msg_cur, &value_len);
1745 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1746 NULL, p, end, &chunk->size);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001747 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001748 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001749
1750 chunk_memcpy(chunk, *msg_cur, value_len);
1751 chunk->area[chunk->data] = '\0';
Frédéric Lécaille56aec0d2019-06-06 14:14:15 +02001752 *msg_cur += value_len;
1753
Thayne McCombs92149f92020-11-20 01:28:26 -07001754 de = dict_insert(&server_key_dict, chunk->area);
1755 dict_entry_unref(&server_key_dict, dc->rx[id - 1].de);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001756 dc->rx[id - 1].de = de;
1757 }
1758 if (de) {
1759 data_ptr = stktable_data_ptr(st->table, ts, data_type);
Thayne McCombs92149f92020-11-20 01:28:26 -07001760 if (data_ptr) {
Willy Tarreau4781b152021-04-06 13:53:36 +02001761 HA_ATOMIC_INC(&de->refcount);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001762 stktable_data_cast(data_ptr, std_t_dict) = de;
Thayne McCombs92149f92020-11-20 01:28:26 -07001763 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001764 }
1765 break;
1766 }
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001767 }
1768 }
1769 /* Force new expiration */
1770 ts->expire = tick_add(now_ms, expire);
1771
1772 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1773 stktable_touch_remote(st->table, ts, 1);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001774 TRACE_LEAVE(PEERS_EV_UPDTMSG, NULL, p);
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001775 return 1;
1776
1777 ignore_msg:
1778 /* skip consumed message */
1779 co_skip(si_oc(si), totl);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001780 TRACE_DEVEL("leaving in error", PEERS_EV_UPDTMSG);
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001781 return 0;
Willy Tarreau1e82a142019-01-29 11:08:06 +01001782
1783 malformed_unlock:
1784 /* malformed message */
1785 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1786 stktable_touch_remote(st->table, ts, 1);
1787 appctx->st0 = PEER_SESS_ST_ERRPROTO;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001788 TRACE_DEVEL("leaving in error", PEERS_EV_UPDTMSG);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001789 return 0;
1790
1791 malformed_free_newts:
1792 /* malformed message */
1793 stksess_free(st->table, newts);
1794 malformed_exit:
1795 appctx->st0 = PEER_SESS_ST_ERRPROTO;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001796 TRACE_DEVEL("leaving in error", PEERS_EV_UPDTMSG);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001797 return 0;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001798}
1799
Frédéric Lécaille87f554c2019-01-22 17:26:50 +01001800/*
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001801 * Function used to parse a stick-table update acknowledgement message after it
1802 * has been received by <p> peer with <msg_cur> as address of the pointer to the position in the
1803 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
1804 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1805 * was encountered.
1806 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
1807 */
1808static inline int peer_treat_ackmsg(struct appctx *appctx, struct peer *p,
1809 char **msg_cur, char *msg_end)
1810{
1811 /* ack message */
1812 uint32_t table_id ;
1813 uint32_t update;
1814 struct shared_table *st;
1815
1816 table_id = intdecode(msg_cur, msg_end);
1817 if (!*msg_cur || (*msg_cur + sizeof(update) > msg_end)) {
1818 /* malformed message */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001819
1820 TRACE_PROTO("malformed message", PEERS_EV_ACKMSG,
1821 NULL, p, *msg_cur);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001822 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1823 return 0;
1824 }
1825
1826 memcpy(&update, *msg_cur, sizeof(update));
1827 update = ntohl(update);
1828
1829 for (st = p->tables; st; st = st->next) {
1830 if (st->local_id == table_id) {
1831 st->update = update;
1832 break;
1833 }
1834 }
1835
1836 return 1;
1837}
1838
1839/*
1840 * Function used to parse a stick-table switch message after it has been received
1841 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1842 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
1843 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1844 * was encountered.
1845 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
1846 */
1847static inline int peer_treat_switchmsg(struct appctx *appctx, struct peer *p,
1848 char **msg_cur, char *msg_end)
1849{
1850 struct shared_table *st;
1851 int table_id;
1852
1853 table_id = intdecode(msg_cur, msg_end);
1854 if (!*msg_cur) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001855 TRACE_PROTO("malformed message", PEERS_EV_SWTCMSG, NULL, p);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001856 /* malformed message */
1857 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1858 return 0;
1859 }
1860
1861 p->remote_table = NULL;
1862 for (st = p->tables; st; st = st->next) {
1863 if (st->remote_id == table_id) {
1864 p->remote_table = st;
1865 break;
1866 }
1867 }
1868
1869 return 1;
1870}
1871
1872/*
1873 * Function used to parse a stick-table definition message after it has been received
1874 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1875 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
1876 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1877 * was encountered.
1878 * <totl> is the length of the stick-table update message computed upon receipt.
1879 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
1880 */
1881static inline int peer_treat_definemsg(struct appctx *appctx, struct peer *p,
1882 char **msg_cur, char *msg_end, int totl)
1883{
1884 struct stream_interface *si = appctx->owner;
1885 int table_id_len;
1886 struct shared_table *st;
1887 int table_type;
1888 int table_keylen;
1889 int table_id;
1890 uint64_t table_data;
1891
1892 table_id = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001893 if (!*msg_cur) {
1894 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001895 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001896 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001897
1898 table_id_len = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001899 if (!*msg_cur) {
1900 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p, *msg_cur);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001901 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001902 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001903
1904 p->remote_table = NULL;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001905 if (!table_id_len || (*msg_cur + table_id_len) >= msg_end) {
1906 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p, *msg_cur, &table_id_len);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001907 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001908 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001909
1910 for (st = p->tables; st; st = st->next) {
1911 /* Reset IDs */
1912 if (st->remote_id == table_id)
1913 st->remote_id = 0;
1914
Frédéric Lécaille7fcc24d2019-03-20 15:09:45 +01001915 if (!p->remote_table && (table_id_len == strlen(st->table->nid)) &&
1916 (memcmp(st->table->nid, *msg_cur, table_id_len) == 0))
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001917 p->remote_table = st;
1918 }
1919
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001920 if (!p->remote_table) {
1921 TRACE_PROTO("ignored message", PEERS_EV_DEFMSG, NULL, p);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001922 goto ignore_msg;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001923 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001924
1925 *msg_cur += table_id_len;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001926 if (*msg_cur >= msg_end) {
1927 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001928 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001929 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001930
1931 table_type = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001932 if (!*msg_cur) {
1933 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001934 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001935 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001936
1937 table_keylen = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001938 if (!*msg_cur) {
1939 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001940 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001941 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001942
1943 table_data = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001944 if (!*msg_cur) {
1945 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001946 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001947 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001948
Emeric Brun530ba382020-06-02 11:17:42 +02001949 if (p->remote_table->table->type != peer_int_key_type[table_type]
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001950 || p->remote_table->table->key_size != table_keylen) {
1951 p->remote_table = NULL;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001952 TRACE_PROTO("ignored message", PEERS_EV_DEFMSG, NULL, p);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001953 goto ignore_msg;
1954 }
1955
1956 p->remote_table->remote_data = table_data;
1957 p->remote_table->remote_id = table_id;
1958 return 1;
1959
1960 ignore_msg:
1961 co_skip(si_oc(si), totl);
1962 return 0;
Willy Tarreau6f731f32019-01-29 11:11:23 +01001963
1964 malformed_exit:
1965 /* malformed message */
1966 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1967 return 0;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001968}
1969
1970/*
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01001971 * Receive a stick-table message or pre-parse any other message.
1972 * The message's header will be sent into <msg_head> which must be at least
1973 * <msg_head_sz> bytes long (at least 7 to store 32-bit variable lengths).
1974 * The first two bytes are always read, and the rest is only read if the
1975 * first bytes indicate a stick-table message. If the message is a stick-table
1976 * message, the varint is decoded and the equivalent number of bytes will be
1977 * copied into the trash at trash.area. <totl> is incremented by the number of
1978 * bytes read EVEN IN CASE OF INCOMPLETE MESSAGES.
Frédéric Lécaille95203f22019-01-23 19:38:11 +01001979 * Returns 1 if there was no error, if not, returns 0 if not enough data were available,
1980 * -1 if there was an error updating the appctx state st0 accordingly.
1981 */
1982static inline int peer_recv_msg(struct appctx *appctx, char *msg_head, size_t msg_head_sz,
1983 uint32_t *msg_len, int *totl)
1984{
1985 int reql;
1986 struct stream_interface *si = appctx->owner;
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01001987 char *cur;
Frédéric Lécaille95203f22019-01-23 19:38:11 +01001988
1989 reql = co_getblk(si_oc(si), msg_head, 2 * sizeof(char), *totl);
1990 if (reql <= 0) /* closed or EOL not found */
1991 goto incomplete;
1992
1993 *totl += reql;
1994
Frédéric Lécaille36fb77e2019-06-04 08:28:19 +02001995 if (!(msg_head[1] & PEER_MSG_STKT_BIT_MASK))
Frédéric Lécaille95203f22019-01-23 19:38:11 +01001996 return 1;
1997
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01001998 /* This is a stick-table message, let's go on */
1999
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002000 /* Read and Decode message length */
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002001 msg_head += *totl;
2002 msg_head_sz -= *totl;
2003 reql = co_data(si_oc(si)) - *totl;
2004 if (reql > msg_head_sz)
2005 reql = msg_head_sz;
2006
2007 reql = co_getblk(si_oc(si), msg_head, reql, *totl);
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002008 if (reql <= 0) /* closed */
2009 goto incomplete;
2010
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002011 cur = msg_head;
2012 *msg_len = intdecode(&cur, cur + reql);
2013 if (!cur) {
2014 /* the number is truncated, did we read enough ? */
2015 if (reql < msg_head_sz)
2016 goto incomplete;
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002017
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002018 /* malformed message */
2019 TRACE_PROTO("malformed message: too large length encoding", PEERS_EV_UPDTMSG);
2020 appctx->st0 = PEER_SESS_ST_ERRPROTO;
2021 return -1;
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002022 }
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002023 *totl += cur - msg_head;
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002024
2025 /* Read message content */
2026 if (*msg_len) {
2027 if (*msg_len > trash.size) {
2028 /* Status code is not success, abort */
2029 appctx->st0 = PEER_SESS_ST_ERRSIZE;
2030 return -1;
2031 }
2032
2033 reql = co_getblk(si_oc(si), trash.area, *msg_len, *totl);
2034 if (reql <= 0) /* closed */
2035 goto incomplete;
2036 *totl += reql;
2037 }
2038
2039 return 1;
2040
2041 incomplete:
Willy Tarreau345ebcf2020-11-26 17:06:04 +01002042 if (reql < 0 || (si_oc(si)->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
2043 /* there was an error or the message was truncated */
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002044 appctx->st0 = PEER_SESS_ST_END;
2045 return -1;
2046 }
2047
2048 return 0;
2049}
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002050
2051/*
2052 * Treat the awaited message with <msg_head> as header.*
2053 * Return 1 if succeeded, 0 if not.
2054 */
2055static inline int peer_treat_awaited_msg(struct appctx *appctx, struct peer *peer, unsigned char *msg_head,
2056 char **msg_cur, char *msg_end, int msg_len, int totl)
2057{
2058 struct stream_interface *si = appctx->owner;
2059 struct stream *s = si_strm(si);
2060 struct peers *peers = strm_fe(s)->parent;
2061
2062 if (msg_head[0] == PEER_MSG_CLASS_CONTROL) {
2063 if (msg_head[1] == PEER_MSG_CTRL_RESYNCREQ) {
2064 struct shared_table *st;
2065 /* Reset message: remote need resync */
2066
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002067 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2068 NULL, &msg_head[1], peers->local->id, peer->id);
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002069 /* prepare tables for a global push */
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002070 for (st = peer->tables; st; st = st->next) {
2071 st->teaching_origin = st->last_pushed = st->table->update;
2072 st->flags = 0;
2073 }
2074
2075 /* reset teaching flags to 0 */
2076 peer->flags &= PEER_TEACH_RESET;
2077
2078 /* flag to start to teach lesson */
2079 peer->flags |= PEER_F_TEACH_PROCESS;
2080 }
2081 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCFINISHED) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002082 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2083 NULL, &msg_head[1], peers->local->id, peer->id);
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002084 if (peer->flags & PEER_F_LEARN_ASSIGN) {
2085 peer->flags &= ~PEER_F_LEARN_ASSIGN;
2086 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
2087 peers->flags |= (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE);
2088 }
2089 peer->confirm++;
2090 }
2091 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCPARTIAL) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002092 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2093 NULL, &msg_head[1], peers->local->id, peer->id);
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002094 if (peer->flags & PEER_F_LEARN_ASSIGN) {
2095 peer->flags &= ~PEER_F_LEARN_ASSIGN;
2096 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
2097
2098 peer->flags |= PEER_F_LEARN_NOTUP2DATE;
Frédéric Lécaille54bff832019-03-26 10:25:20 +01002099 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(PEER_RESYNC_TIMEOUT));
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002100 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
2101 }
2102 peer->confirm++;
2103 }
2104 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCCONFIRM) {
2105 struct shared_table *st;
2106
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002107 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2108 NULL, &msg_head[1], peers->local->id, peer->id);
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002109 /* If stopping state */
2110 if (stopping) {
2111 /* Close session, push resync no more needed */
2112 peer->flags |= PEER_F_TEACH_COMPLETE;
2113 appctx->st0 = PEER_SESS_ST_END;
2114 return 0;
2115 }
2116 for (st = peer->tables; st; st = st->next) {
2117 st->update = st->last_pushed = st->teaching_origin;
2118 st->flags = 0;
2119 }
2120
2121 /* reset teaching flags to 0 */
2122 peer->flags &= PEER_TEACH_RESET;
2123 }
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002124 else if (msg_head[1] == PEER_MSG_CTRL_HEARTBEAT) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002125 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2126 NULL, &msg_head[1], peers->local->id, peer->id);
Frédéric Lécaille54bff832019-03-26 10:25:20 +01002127 peer->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT));
Frédéric Lécaille33cab3c2019-11-06 11:51:26 +01002128 peer->rx_hbt++;
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002129 }
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002130 }
2131 else if (msg_head[0] == PEER_MSG_CLASS_STICKTABLE) {
2132 if (msg_head[1] == PEER_MSG_STKT_DEFINE) {
2133 if (!peer_treat_definemsg(appctx, peer, msg_cur, msg_end, totl))
2134 return 0;
2135 }
2136 else if (msg_head[1] == PEER_MSG_STKT_SWITCH) {
2137 if (!peer_treat_switchmsg(appctx, peer, msg_cur, msg_end))
2138 return 0;
2139 }
2140 else if (msg_head[1] == PEER_MSG_STKT_UPDATE ||
2141 msg_head[1] == PEER_MSG_STKT_INCUPDATE ||
2142 msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED ||
2143 msg_head[1] == PEER_MSG_STKT_INCUPDATE_TIMED) {
2144 int update, expire;
2145
2146 update = msg_head[1] == PEER_MSG_STKT_UPDATE || msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED;
2147 expire = msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED || msg_head[1] == PEER_MSG_STKT_INCUPDATE_TIMED;
2148 if (!peer_treat_updatemsg(appctx, peer, update, expire,
2149 msg_cur, msg_end, msg_len, totl))
2150 return 0;
2151
2152 }
2153 else if (msg_head[1] == PEER_MSG_STKT_ACK) {
2154 if (!peer_treat_ackmsg(appctx, peer, msg_cur, msg_end))
2155 return 0;
2156 }
2157 }
2158 else if (msg_head[0] == PEER_MSG_CLASS_RESERVED) {
2159 appctx->st0 = PEER_SESS_ST_ERRPROTO;
2160 return 0;
2161 }
2162
2163 return 1;
2164}
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002165
2166
2167/*
2168 * Send any message to <peer> peer.
2169 * Returns 1 if succeeded, or -1 or 0 if failed.
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002170 * -1 means an internal error occurred, 0 is for a peer protocol error leading
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002171 * to a peer state change (from the peer I/O handler point of view).
2172 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002173static inline int peer_send_msgs(struct appctx *appctx,
2174 struct peer *peer, struct peers *peers)
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002175{
2176 int repl;
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002177
2178 /* Need to request a resync */
2179 if ((peer->flags & PEER_F_LEARN_ASSIGN) &&
2180 (peers->flags & PEERS_F_RESYNC_ASSIGN) &&
2181 !(peers->flags & PEERS_F_RESYNC_PROCESS)) {
2182
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002183 repl = peer_send_resync_reqmsg(appctx, peer, peers);
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002184 if (repl <= 0)
2185 return repl;
2186
2187 peers->flags |= PEERS_F_RESYNC_PROCESS;
2188 }
2189
2190 /* Nothing to read, now we start to write */
2191 if (peer->tables) {
2192 struct shared_table *st;
2193 struct shared_table *last_local_table;
2194
2195 last_local_table = peer->last_local_table;
2196 if (!last_local_table)
2197 last_local_table = peer->tables;
2198 st = last_local_table->next;
2199
2200 while (1) {
2201 if (!st)
2202 st = peer->tables;
2203
2204 /* It remains some updates to ack */
2205 if (st->last_get != st->last_acked) {
2206 repl = peer_send_ackmsg(st, appctx);
2207 if (repl <= 0)
2208 return repl;
2209
2210 st->last_acked = st->last_get;
2211 }
2212
2213 if (!(peer->flags & PEER_F_TEACH_PROCESS)) {
2214 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
2215 if (!(peer->flags & PEER_F_LEARN_ASSIGN) &&
2216 ((int)(st->last_pushed - st->table->localupdate) < 0)) {
2217
2218 repl = peer_send_teach_process_msgs(appctx, peer, st);
2219 if (repl <= 0) {
2220 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
2221 return repl;
2222 }
2223 }
2224 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
2225 }
2226 else {
2227 if (!(st->flags & SHTABLE_F_TEACH_STAGE1)) {
2228 repl = peer_send_teach_stage1_msgs(appctx, peer, st);
2229 if (repl <= 0)
2230 return repl;
2231 }
2232
2233 if (!(st->flags & SHTABLE_F_TEACH_STAGE2)) {
2234 repl = peer_send_teach_stage2_msgs(appctx, peer, st);
2235 if (repl <= 0)
2236 return repl;
2237 }
2238 }
2239
2240 if (st == last_local_table)
2241 break;
2242 st = st->next;
2243 }
2244 }
2245
2246 if ((peer->flags & PEER_F_TEACH_PROCESS) && !(peer->flags & PEER_F_TEACH_FINISHED)) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002247 repl = peer_send_resync_finishedmsg(appctx, peer, peers);
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002248 if (repl <= 0)
2249 return repl;
2250
2251 /* flag finished message sent */
2252 peer->flags |= PEER_F_TEACH_FINISHED;
2253 }
2254
2255 /* Confirm finished or partial messages */
2256 while (peer->confirm) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002257 repl = peer_send_resync_confirmsg(appctx, peer, peers);
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002258 if (repl <= 0)
2259 return repl;
2260
2261 peer->confirm--;
2262 }
2263
2264 return 1;
2265}
2266
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002267/*
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002268 * Read and parse a first line of a "hello" peer protocol message.
2269 * Returns 0 if could not read a line, -1 if there was a read error or
2270 * the line is malformed, 1 if succeeded.
2271 */
2272static inline int peer_getline_version(struct appctx *appctx,
2273 unsigned int *maj_ver, unsigned int *min_ver)
2274{
2275 int reql;
2276
2277 reql = peer_getline(appctx);
2278 if (!reql)
2279 return 0;
2280
2281 if (reql < 0)
2282 return -1;
2283
2284 /* test protocol */
2285 if (strncmp(PEER_SESSION_PROTO_NAME " ", trash.area, proto_len + 1) != 0) {
2286 appctx->st0 = PEER_SESS_ST_EXIT;
2287 appctx->st1 = PEER_SESS_SC_ERRPROTO;
2288 return -1;
2289 }
2290 if (peer_get_version(trash.area + proto_len + 1, maj_ver, min_ver) == -1 ||
2291 *maj_ver != PEER_MAJOR_VER || *min_ver > PEER_MINOR_VER) {
2292 appctx->st0 = PEER_SESS_ST_EXIT;
2293 appctx->st1 = PEER_SESS_SC_ERRVERSION;
2294 return -1;
2295 }
2296
2297 return 1;
2298}
2299
2300/*
2301 * Read and parse a second line of a "hello" peer protocol message.
2302 * Returns 0 if could not read a line, -1 if there was a read error or
2303 * the line is malformed, 1 if succeeded.
2304 */
2305static inline int peer_getline_host(struct appctx *appctx)
2306{
2307 int reql;
2308
2309 reql = peer_getline(appctx);
2310 if (!reql)
2311 return 0;
2312
2313 if (reql < 0)
2314 return -1;
2315
2316 /* test hostname match */
2317 if (strcmp(localpeer, trash.area) != 0) {
2318 appctx->st0 = PEER_SESS_ST_EXIT;
2319 appctx->st1 = PEER_SESS_SC_ERRHOST;
2320 return -1;
2321 }
2322
2323 return 1;
2324}
2325
2326/*
2327 * Read and parse a last line of a "hello" peer protocol message.
2328 * Returns 0 if could not read a character, -1 if there was a read error or
2329 * the line is malformed, 1 if succeeded.
2330 * Set <curpeer> accordingly (the remote peer sending the "hello" message).
2331 */
2332static inline int peer_getline_last(struct appctx *appctx, struct peer **curpeer)
2333{
2334 char *p;
2335 int reql;
2336 struct peer *peer;
2337 struct stream_interface *si = appctx->owner;
2338 struct stream *s = si_strm(si);
2339 struct peers *peers = strm_fe(s)->parent;
2340
2341 reql = peer_getline(appctx);
2342 if (!reql)
2343 return 0;
2344
2345 if (reql < 0)
2346 return -1;
2347
2348 /* parse line "<peer name> <pid> <relative_pid>" */
2349 p = strchr(trash.area, ' ');
2350 if (!p) {
2351 appctx->st0 = PEER_SESS_ST_EXIT;
2352 appctx->st1 = PEER_SESS_SC_ERRPROTO;
2353 return -1;
2354 }
2355 *p = 0;
2356
2357 /* lookup known peer */
2358 for (peer = peers->remote; peer; peer = peer->next) {
2359 if (strcmp(peer->id, trash.area) == 0)
2360 break;
2361 }
2362
2363 /* if unknown peer */
2364 if (!peer) {
2365 appctx->st0 = PEER_SESS_ST_EXIT;
2366 appctx->st1 = PEER_SESS_SC_ERRPEER;
2367 return -1;
2368 }
2369 *curpeer = peer;
2370
2371 return 1;
2372}
2373
2374/*
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002375 * Init <peer> peer after having accepted it at peer protocol level.
2376 */
2377static inline void init_accepted_peer(struct peer *peer, struct peers *peers)
2378{
2379 struct shared_table *st;
2380
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002381 peer->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002382 /* Register status code */
2383 peer->statuscode = PEER_SESS_SC_SUCCESSCODE;
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02002384 peer->last_hdshk = now_ms;
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002385
2386 /* Awake main task */
2387 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
2388
2389 /* Init confirm counter */
2390 peer->confirm = 0;
2391
2392 /* Init cursors */
2393 for (st = peer->tables; st ; st = st->next) {
2394 st->last_get = st->last_acked = 0;
2395 st->teaching_origin = st->last_pushed = st->update;
2396 }
2397
2398 /* reset teaching and learning flags to 0 */
2399 peer->flags &= PEER_TEACH_RESET;
2400 peer->flags &= PEER_LEARN_RESET;
2401
2402 /* if current peer is local */
2403 if (peer->local) {
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05002404 /* if current host need resyncfrom local and no process assigned */
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002405 if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL &&
2406 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
2407 /* assign local peer for a lesson, consider lesson already requested */
2408 peer->flags |= PEER_F_LEARN_ASSIGN;
2409 peers->flags |= (PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
2410 }
2411
2412 }
2413 else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
2414 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
2415 /* assign peer for a lesson */
2416 peer->flags |= PEER_F_LEARN_ASSIGN;
2417 peers->flags |= PEERS_F_RESYNC_ASSIGN;
2418 }
2419}
2420
2421/*
2422 * Init <peer> peer after having connected it at peer protocol level.
2423 */
2424static inline void init_connected_peer(struct peer *peer, struct peers *peers)
2425{
2426 struct shared_table *st;
2427
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002428 peer->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002429 /* Init cursors */
2430 for (st = peer->tables; st ; st = st->next) {
2431 st->last_get = st->last_acked = 0;
2432 st->teaching_origin = st->last_pushed = st->update;
2433 }
2434
2435 /* Init confirm counter */
2436 peer->confirm = 0;
2437
2438 /* reset teaching and learning flags to 0 */
2439 peer->flags &= PEER_TEACH_RESET;
2440 peer->flags &= PEER_LEARN_RESET;
2441
2442 /* If current peer is local */
2443 if (peer->local) {
2444 /* flag to start to teach lesson */
2445 peer->flags |= PEER_F_TEACH_PROCESS;
2446 }
2447 else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
2448 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
2449 /* If peer is remote and resync from remote is needed,
2450 and no peer currently assigned */
2451
2452 /* assign peer for a lesson */
2453 peer->flags |= PEER_F_LEARN_ASSIGN;
2454 peers->flags |= PEERS_F_RESYNC_ASSIGN;
2455 }
2456}
2457
2458/*
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05002459 * IO Handler to handle message exchange with a peer
Emeric Brun2b920a12010-09-23 18:30:22 +02002460 */
Willy Tarreau00a37f02015-04-13 12:05:19 +02002461static void peer_io_handler(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +02002462{
Willy Tarreau00a37f02015-04-13 12:05:19 +02002463 struct stream_interface *si = appctx->owner;
Willy Tarreau87b09662015-04-03 00:22:06 +02002464 struct stream *s = si_strm(si);
Vincent Bernat3c2f2f22016-04-03 13:48:42 +02002465 struct peers *curpeers = strm_fe(s)->parent;
Emeric Brun80527f52017-06-19 17:46:37 +02002466 struct peer *curpeer = NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002467 int reql = 0;
2468 int repl = 0;
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002469 unsigned int maj_ver, min_ver;
Willy Tarreau2d372c22018-11-05 17:12:27 +01002470 int prev_state;
Emeric Brun2b920a12010-09-23 18:30:22 +02002471
Joseph Herlant82b2f542018-11-15 12:19:14 -08002472 /* Check if the input buffer is available. */
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002473 if (si_ic(si)->buf.size == 0) {
2474 si_rx_room_blk(si);
2475 goto out;
2476 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002477
Emeric Brun2b920a12010-09-23 18:30:22 +02002478 while (1) {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002479 prev_state = appctx->st0;
Emeric Brun2b920a12010-09-23 18:30:22 +02002480switchstate:
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002481 maj_ver = min_ver = (unsigned int)-1;
Willy Tarreau7b4b4992013-12-01 09:15:12 +01002482 switch(appctx->st0) {
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002483 case PEER_SESS_ST_ACCEPT:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002484 prev_state = appctx->st0;
Willy Tarreau7b4b4992013-12-01 09:15:12 +01002485 appctx->ctx.peers.ptr = NULL;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002486 appctx->st0 = PEER_SESS_ST_GETVERSION;
Emeric Brun2b920a12010-09-23 18:30:22 +02002487 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002488 case PEER_SESS_ST_GETVERSION:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002489 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002490 reql = peer_getline_version(appctx, &maj_ver, &min_ver);
2491 if (reql <= 0) {
2492 if (!reql)
2493 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002494 goto switchstate;
2495 }
2496
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002497 appctx->st0 = PEER_SESS_ST_GETHOST;
Emeric Brun2b920a12010-09-23 18:30:22 +02002498 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002499 case PEER_SESS_ST_GETHOST:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002500 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002501 reql = peer_getline_host(appctx);
2502 if (reql <= 0) {
2503 if (!reql)
2504 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002505 goto switchstate;
2506 }
2507
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002508 appctx->st0 = PEER_SESS_ST_GETPEER;
Emeric Brun2b920a12010-09-23 18:30:22 +02002509 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002510 case PEER_SESS_ST_GETPEER: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002511 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002512 reql = peer_getline_last(appctx, &curpeer);
2513 if (reql <= 0) {
2514 if (!reql)
2515 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002516 goto switchstate;
2517 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002518
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002519 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Willy Tarreau9df94c22016-10-31 18:42:52 +01002520 if (curpeer->appctx && curpeer->appctx != appctx) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002521 if (curpeer->local) {
2522 /* Local connection, reply a retry */
2523 appctx->st0 = PEER_SESS_ST_EXIT;
2524 appctx->st1 = PEER_SESS_SC_TRYAGAIN;
2525 goto switchstate;
Emeric Brun2b920a12010-09-23 18:30:22 +02002526 }
Emeric Brun80527f52017-06-19 17:46:37 +02002527
2528 /* we're killing a connection, we must apply a random delay before
2529 * retrying otherwise the other end will do the same and we can loop
2530 * for a while.
2531 */
Willy Tarreau52bf8392020-03-08 00:42:37 +01002532 curpeer->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
Emeric Brun9ef2ad72019-04-02 17:22:01 +02002533 peer_session_forceshutdown(curpeer);
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002534 curpeer->heartbeat = TICK_ETERNITY;
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02002535 curpeer->coll++;
Emeric Brun2b920a12010-09-23 18:30:22 +02002536 }
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002537 if (maj_ver != (unsigned int)-1 && min_ver != (unsigned int)-1) {
2538 if (min_ver == PEER_DWNGRD_MINOR_VER) {
2539 curpeer->flags |= PEER_F_DWNGRD;
2540 }
2541 else {
2542 curpeer->flags &= ~PEER_F_DWNGRD;
2543 }
2544 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002545 curpeer->appctx = appctx;
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002546 curpeer->flags |= PEER_F_ALIVE;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002547 appctx->ctx.peers.ptr = curpeer;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002548 appctx->st0 = PEER_SESS_ST_SENDSUCCESS;
Willy Tarreau4781b152021-04-06 13:53:36 +02002549 _HA_ATOMIC_INC(&active_peers);
Emeric Brun2b920a12010-09-23 18:30:22 +02002550 }
Tim Duesterhus588b3142020-05-29 14:35:51 +02002551 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002552 case PEER_SESS_ST_SENDSUCCESS: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002553 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002554 if (!curpeer) {
2555 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002556 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002557 if (curpeer->appctx != appctx) {
2558 appctx->st0 = PEER_SESS_ST_END;
2559 goto switchstate;
2560 }
2561 }
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002562
2563 repl = peer_send_status_successmsg(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002564 if (repl <= 0) {
2565 if (repl == -1)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002566 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002567 goto switchstate;
2568 }
2569
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002570 init_accepted_peer(curpeer, curpeers);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002571
Emeric Brun2b920a12010-09-23 18:30:22 +02002572 /* switch to waiting message state */
Willy Tarreau4781b152021-04-06 13:53:36 +02002573 _HA_ATOMIC_INC(&connected_peers);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002574 appctx->st0 = PEER_SESS_ST_WAITMSG;
Emeric Brun2b920a12010-09-23 18:30:22 +02002575 goto switchstate;
2576 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002577 case PEER_SESS_ST_CONNECT: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002578 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002579 if (!curpeer) {
2580 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002581 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002582 if (curpeer->appctx != appctx) {
2583 appctx->st0 = PEER_SESS_ST_END;
2584 goto switchstate;
2585 }
2586 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002587
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002588 repl = peer_send_hellomsg(appctx, curpeer);
Emeric Brun2b920a12010-09-23 18:30:22 +02002589 if (repl <= 0) {
2590 if (repl == -1)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002591 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002592 goto switchstate;
2593 }
2594
2595 /* switch to the waiting statuscode state */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002596 appctx->st0 = PEER_SESS_ST_GETSTATUS;
Emeric Brun2b920a12010-09-23 18:30:22 +02002597 }
Tim Duesterhus588b3142020-05-29 14:35:51 +02002598 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002599 case PEER_SESS_ST_GETSTATUS: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002600 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002601 if (!curpeer) {
2602 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002603 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002604 if (curpeer->appctx != appctx) {
2605 appctx->st0 = PEER_SESS_ST_END;
2606 goto switchstate;
2607 }
2608 }
2609
Willy Tarreau2bb4a962014-11-28 11:11:05 +01002610 if (si_ic(si)->flags & CF_WRITE_PARTIAL)
Emeric Brunb3971ab2015-05-12 18:49:09 +02002611 curpeer->statuscode = PEER_SESS_SC_CONNECTEDCODE;
Emeric Brun2b920a12010-09-23 18:30:22 +02002612
Frédéric Lécaillece025572019-01-21 13:38:06 +01002613 reql = peer_getline(appctx);
2614 if (!reql)
2615 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002616
Frédéric Lécaillece025572019-01-21 13:38:06 +01002617 if (reql < 0)
2618 goto switchstate;
Emeric Brun2b920a12010-09-23 18:30:22 +02002619
2620 /* Register status code */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002621 curpeer->statuscode = atoi(trash.area);
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02002622 curpeer->last_hdshk = now_ms;
Emeric Brun2b920a12010-09-23 18:30:22 +02002623
2624 /* Awake main task */
Frédéric Lécailleed2b4a62017-07-13 09:07:09 +02002625 task_wakeup(curpeers->sync_task, TASK_WOKEN_MSG);
Emeric Brun2b920a12010-09-23 18:30:22 +02002626
2627 /* If status code is success */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002628 if (curpeer->statuscode == PEER_SESS_SC_SUCCESSCODE) {
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002629 init_connected_peer(curpeer, curpeers);
Emeric Brun2b920a12010-09-23 18:30:22 +02002630 }
2631 else {
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002632 if (curpeer->statuscode == PEER_SESS_SC_ERRVERSION)
2633 curpeer->flags |= PEER_F_DWNGRD;
Emeric Brun2b920a12010-09-23 18:30:22 +02002634 /* Status code is not success, abort */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002635 appctx->st0 = PEER_SESS_ST_END;
Emeric Brun2b920a12010-09-23 18:30:22 +02002636 goto switchstate;
2637 }
Willy Tarreau4781b152021-04-06 13:53:36 +02002638 _HA_ATOMIC_INC(&connected_peers);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002639 appctx->st0 = PEER_SESS_ST_WAITMSG;
Emeric Brun2b920a12010-09-23 18:30:22 +02002640 }
Tim Duesterhus588b3142020-05-29 14:35:51 +02002641 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002642 case PEER_SESS_ST_WAITMSG: {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002643 uint32_t msg_len = 0;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002644 char *msg_cur = trash.area;
2645 char *msg_end = trash.area;
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002646 unsigned char msg_head[7]; // 2 + 5 for varint32
Emeric Brun2b920a12010-09-23 18:30:22 +02002647 int totl = 0;
2648
Willy Tarreau2d372c22018-11-05 17:12:27 +01002649 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002650 if (!curpeer) {
2651 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002652 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002653 if (curpeer->appctx != appctx) {
2654 appctx->st0 = PEER_SESS_ST_END;
2655 goto switchstate;
2656 }
2657 }
2658
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002659 reql = peer_recv_msg(appctx, (char *)msg_head, sizeof msg_head, &msg_len, &totl);
2660 if (reql <= 0) {
2661 if (reql == -1)
2662 goto switchstate;
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002663 goto send_msgs;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002664 }
Willy Tarreau86a446e2013-11-25 23:02:37 +01002665
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002666 msg_end += msg_len;
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002667 if (!peer_treat_awaited_msg(appctx, curpeer, msg_head, &msg_cur, msg_end, msg_len, totl))
Emeric Brun2b920a12010-09-23 18:30:22 +02002668 goto switchstate;
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002669
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002670 curpeer->flags |= PEER_F_ALIVE;
2671
Emeric Brun2b920a12010-09-23 18:30:22 +02002672 /* skip consumed message */
Willy Tarreau06d80a92017-10-19 14:32:15 +02002673 co_skip(si_oc(si), totl);
Emeric Brun2b920a12010-09-23 18:30:22 +02002674 /* loop on that state to peek next message */
Willy Tarreau72d6c162013-04-11 16:14:13 +02002675 goto switchstate;
2676
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002677send_msgs:
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002678 if (curpeer->flags & PEER_F_HEARTBEAT) {
2679 curpeer->flags &= ~PEER_F_HEARTBEAT;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002680 repl = peer_send_heartbeatmsg(appctx, curpeer, curpeers);
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002681 if (repl <= 0) {
2682 if (repl == -1)
2683 goto out;
2684 goto switchstate;
2685 }
Frédéric Lécaille33cab3c2019-11-06 11:51:26 +01002686 curpeer->tx_hbt++;
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002687 }
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002688 /* we get here when a peer_recv_msg() returns 0 in reql */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002689 repl = peer_send_msgs(appctx, curpeer, curpeers);
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002690 if (repl <= 0) {
2691 if (repl == -1)
2692 goto out;
2693 goto switchstate;
Emeric Brun597b26e2016-08-12 11:23:31 +02002694 }
2695
Emeric Brun2b920a12010-09-23 18:30:22 +02002696 /* noting more to do */
2697 goto out;
2698 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002699 case PEER_SESS_ST_EXIT:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002700 if (prev_state == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +02002701 _HA_ATOMIC_DEC(&connected_peers);
Willy Tarreau2d372c22018-11-05 17:12:27 +01002702 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002703 if (peer_send_status_errormsg(appctx) == -1)
2704 goto out;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002705 appctx->st0 = PEER_SESS_ST_END;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002706 goto switchstate;
2707 case PEER_SESS_ST_ERRSIZE: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002708 if (prev_state == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +02002709 _HA_ATOMIC_DEC(&connected_peers);
Willy Tarreau2d372c22018-11-05 17:12:27 +01002710 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002711 if (peer_send_error_size_limitmsg(appctx) == -1)
2712 goto out;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002713 appctx->st0 = PEER_SESS_ST_END;
2714 goto switchstate;
2715 }
2716 case PEER_SESS_ST_ERRPROTO: {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002717 TRACE_PROTO("protocol error", PEERS_EV_PROTOERR,
2718 NULL, curpeer, &prev_state);
Frédéric Lécailleec1c10b2019-11-07 15:22:33 +01002719 if (curpeer)
2720 curpeer->proto_err++;
Willy Tarreau2d372c22018-11-05 17:12:27 +01002721 if (prev_state == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +02002722 _HA_ATOMIC_DEC(&connected_peers);
Willy Tarreau2d372c22018-11-05 17:12:27 +01002723 prev_state = appctx->st0;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002724 if (peer_send_error_protomsg(appctx) == -1) {
2725 TRACE_PROTO("could not send error message", PEERS_EV_PROTOERR);
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002726 goto out;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002727 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002728 appctx->st0 = PEER_SESS_ST_END;
Willy Tarreau2d372c22018-11-05 17:12:27 +01002729 prev_state = appctx->st0;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002730 }
Tim Duesterhus588b3142020-05-29 14:35:51 +02002731 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002732 case PEER_SESS_ST_END: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002733 if (prev_state == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +02002734 _HA_ATOMIC_DEC(&connected_peers);
Willy Tarreau2d372c22018-11-05 17:12:27 +01002735 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002736 if (curpeer) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002737 HA_SPIN_UNLOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002738 curpeer = NULL;
2739 }
Willy Tarreau73b013b2012-05-21 16:31:45 +02002740 si_shutw(si);
2741 si_shutr(si);
Willy Tarreau2bb4a962014-11-28 11:11:05 +01002742 si_ic(si)->flags |= CF_READ_NULL;
Willy Tarreau828824a2015-04-19 17:20:03 +02002743 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002744 }
2745 }
2746 }
2747out:
Willy Tarreau2bb4a962014-11-28 11:11:05 +01002748 si_oc(si)->flags |= CF_READ_DONTWAIT;
Emeric Brun80527f52017-06-19 17:46:37 +02002749
2750 if (curpeer)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002751 HA_SPIN_UNLOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun2b920a12010-09-23 18:30:22 +02002752 return;
2753}
2754
Willy Tarreau30576452015-04-13 13:50:30 +02002755static struct applet peer_applet = {
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002756 .obj_type = OBJ_TYPE_APPLET,
Willy Tarreaub24281b2011-02-13 13:16:36 +01002757 .name = "<PEER>", /* used for logging */
2758 .fct = peer_io_handler,
Aman Gupta9a13e842012-04-02 18:57:53 -07002759 .release = peer_session_release,
Willy Tarreaub24281b2011-02-13 13:16:36 +01002760};
Emeric Brun2b920a12010-09-23 18:30:22 +02002761
Emeric Brun9ef2ad72019-04-02 17:22:01 +02002762
Emeric Brun2b920a12010-09-23 18:30:22 +02002763/*
2764 * Use this function to force a close of a peer session
2765 */
Emeric Brun9ef2ad72019-04-02 17:22:01 +02002766static void peer_session_forceshutdown(struct peer *peer)
Emeric Brun2b920a12010-09-23 18:30:22 +02002767{
Emeric Brun9ef2ad72019-04-02 17:22:01 +02002768 struct appctx *appctx = peer->appctx;
2769
Frédéric Lécaille5df11902017-06-13 16:39:57 +02002770 /* Note that the peer sessions which have just been created
2771 * (->st0 == PEER_SESS_ST_CONNECT) must not
2772 * be shutdown, if not, the TCP session will never be closed
2773 * and stay in CLOSE_WAIT state after having been closed by
2774 * the remote side.
2775 */
2776 if (!appctx || appctx->st0 == PEER_SESS_ST_CONNECT)
Willy Tarreau7b4b4992013-12-01 09:15:12 +01002777 return;
2778
Willy Tarreau81bc3b02016-10-31 17:37:39 +01002779 if (appctx->applet != &peer_applet)
2780 return;
2781
Emeric Brun9ef2ad72019-04-02 17:22:01 +02002782 __peer_session_deinit(peer);
2783
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002784 appctx->st0 = PEER_SESS_ST_END;
Willy Tarreau78c0c502016-10-31 17:32:20 +01002785 appctx_wakeup(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002786}
2787
Willy Tarreau91d96282015-03-13 15:47:26 +01002788/* Pre-configures a peers frontend to accept incoming connections */
2789void peers_setup_frontend(struct proxy *fe)
2790{
2791 fe->last_change = now.tv_sec;
Frédéric Lécaillec06b5d42018-04-26 10:06:41 +02002792 fe->cap = PR_CAP_FE | PR_CAP_BE;
Willy Tarreaua389c9e2020-10-07 17:49:42 +02002793 fe->mode = PR_MODE_PEERS;
Willy Tarreau91d96282015-03-13 15:47:26 +01002794 fe->maxconn = 0;
2795 fe->conn_retries = CONN_RETRIES;
2796 fe->timeout.client = MS_TO_TICKS(5000);
Willy Tarreaud1d48d42015-03-13 16:15:46 +01002797 fe->accept = frontend_accept;
Willy Tarreauf87ab942015-03-13 15:55:16 +01002798 fe->default_target = &peer_applet.obj_type;
Willy Tarreau91d96282015-03-13 15:47:26 +01002799 fe->options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
Willy Tarreau0fca4832015-05-01 19:12:05 +02002800 fe->bind_proc = 0; /* will be filled by users */
Willy Tarreau91d96282015-03-13 15:47:26 +01002801}
2802
Emeric Brun2b920a12010-09-23 18:30:22 +02002803/*
Willy Tarreaubd55e312010-11-11 10:55:09 +01002804 * Create a new peer session in assigned state (connect will start automatically)
Emeric Brun2b920a12010-09-23 18:30:22 +02002805 */
Willy Tarreau9df94c22016-10-31 18:42:52 +01002806static struct appctx *peer_session_create(struct peers *peers, struct peer *peer)
Emeric Brun2b920a12010-09-23 18:30:22 +02002807{
Willy Tarreau04b92862017-09-15 11:01:04 +02002808 struct proxy *p = peers->peers_fe; /* attached frontend */
Willy Tarreau7b4b4992013-12-01 09:15:12 +01002809 struct appctx *appctx;
Willy Tarreau15b5e142015-04-04 14:38:25 +02002810 struct session *sess;
Willy Tarreau87b09662015-04-03 00:22:06 +02002811 struct stream *s;
Emeric Brun2b920a12010-09-23 18:30:22 +02002812
Frédéric Lécaille2b0ba542021-01-18 15:14:39 +01002813 peer->new_conn++;
Frédéric Lécaille54bff832019-03-26 10:25:20 +01002814 peer->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT));
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002815 peer->heartbeat = TICK_ETERNITY;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002816 peer->statuscode = PEER_SESS_SC_CONNECTCODE;
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02002817 peer->last_hdshk = now_ms;
Willy Tarreaud990baf2015-04-05 00:32:03 +02002818 s = NULL;
2819
Emeric Brun1138fd02017-06-19 12:38:55 +02002820 appctx = appctx_new(&peer_applet, tid_bit);
Willy Tarreaud990baf2015-04-05 00:32:03 +02002821 if (!appctx)
2822 goto out_close;
2823
2824 appctx->st0 = PEER_SESS_ST_CONNECT;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002825 appctx->ctx.peers.ptr = (void *)peer;
Willy Tarreaud990baf2015-04-05 00:32:03 +02002826
Willy Tarreau04b92862017-09-15 11:01:04 +02002827 sess = session_new(p, NULL, &appctx->obj_type);
Willy Tarreau15b5e142015-04-04 14:38:25 +02002828 if (!sess) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002829 ha_alert("out of memory in peer_session_create().\n");
Willy Tarreaud990baf2015-04-05 00:32:03 +02002830 goto out_free_appctx;
Emeric Brun2b920a12010-09-23 18:30:22 +02002831 }
2832
Christopher Faulet26256f82020-09-14 11:40:13 +02002833 if ((s = stream_new(sess, &appctx->obj_type, &BUF_NULL)) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002834 ha_alert("Failed to initialize stream in peer_session_create().\n");
Willy Tarreau87787ac2017-08-28 16:22:54 +02002835 goto out_free_sess;
Willy Tarreau8baf9062015-04-05 00:46:36 +02002836 }
2837
Willy Tarreau6e2979c2015-04-27 13:21:15 +02002838 /* applet is waiting for data */
Willy Tarreau0cd3bd62018-11-06 18:46:37 +01002839 si_cant_get(&s->si[0]);
Willy Tarreau6e2979c2015-04-27 13:21:15 +02002840 appctx_wakeup(appctx);
2841
Willy Tarreau3ed35ef2013-10-24 11:51:38 +02002842 /* initiate an outgoing connection */
Willy Tarreau1c8d32b2019-07-18 15:47:45 +02002843 s->target = peer_session_target(peer, s);
Willy Tarreau9b7587a2020-10-15 07:32:10 +02002844 if (!sockaddr_alloc(&s->target_addr, &peer->addr, sizeof(peer->addr)))
Willy Tarreau1c8d32b2019-07-18 15:47:45 +02002845 goto out_free_strm;
Willy Tarreau02efeda2019-07-18 17:21:24 +02002846 s->flags = SF_ASSIGNED|SF_ADDR_SET;
Willy Tarreaudbd02672017-12-06 17:39:53 +01002847 s->si[1].flags |= SI_FL_NOLINGER;
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002848
Emeric Brun2b920a12010-09-23 18:30:22 +02002849 s->do_log = NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002850 s->uniq_id = 0;
Emeric Brun2b920a12010-09-23 18:30:22 +02002851
Willy Tarreau22ec1ea2014-11-27 20:45:39 +01002852 s->res.flags |= CF_READ_DONTWAIT;
Willy Tarreau696a2912014-11-24 11:36:57 +01002853
Emeric Brunb3971ab2015-05-12 18:49:09 +02002854 peer->appctx = appctx;
Willy Tarreau87787ac2017-08-28 16:22:54 +02002855 task_wakeup(s->task, TASK_WOKEN_INIT);
Willy Tarreau4781b152021-04-06 13:53:36 +02002856 _HA_ATOMIC_INC(&active_peers);
Willy Tarreau9df94c22016-10-31 18:42:52 +01002857 return appctx;
Emeric Brun2b920a12010-09-23 18:30:22 +02002858
2859 /* Error unrolling */
Willy Tarreau15b5e142015-04-04 14:38:25 +02002860 out_free_strm:
Emeric Brun2b920a12010-09-23 18:30:22 +02002861 LIST_DEL(&s->list);
Willy Tarreaubafbe012017-11-24 17:34:44 +01002862 pool_free(pool_head_stream, s);
Willy Tarreau15b5e142015-04-04 14:38:25 +02002863 out_free_sess:
Willy Tarreau11c36242015-04-04 15:54:03 +02002864 session_free(sess);
Willy Tarreaud990baf2015-04-05 00:32:03 +02002865 out_free_appctx:
2866 appctx_free(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002867 out_close:
Willy Tarreaub21d08e2016-10-31 17:46:57 +01002868 return NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002869}
2870
2871/*
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002872 * Task processing function to manage re-connect, peer session
Willy Tarreauf6c88422021-01-29 12:38:42 +01002873 * tasks wakeup on local update and heartbeat. Let's keep it exported so that it
2874 * resolves in stack traces and "show tasks".
Emeric Brun2b920a12010-09-23 18:30:22 +02002875 */
Willy Tarreau144f84a2021-03-02 16:09:26 +01002876struct task *process_peer_sync(struct task * task, void *context, unsigned int state)
Emeric Brun2b920a12010-09-23 18:30:22 +02002877{
Olivier Houchard9f6af332018-05-25 14:04:04 +02002878 struct peers *peers = context;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002879 struct peer *ps;
2880 struct shared_table *st;
Emeric Brun2b920a12010-09-23 18:30:22 +02002881
2882 task->expire = TICK_ETERNITY;
2883
Emeric Brunb3971ab2015-05-12 18:49:09 +02002884 if (!peers->peers_fe) {
Willy Tarreau46dc1ca2015-05-01 18:32:13 +02002885 /* this one was never started, kill it */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002886 signal_unregister_handler(peers->sighandler);
Olivier Houchard3f795f72019-04-17 22:51:06 +02002887 task_destroy(peers->sync_task);
Willy Tarreau37bb7be2015-09-21 15:24:58 +02002888 peers->sync_task = NULL;
Willy Tarreau46dc1ca2015-05-01 18:32:13 +02002889 return NULL;
2890 }
2891
Emeric Brun80527f52017-06-19 17:46:37 +02002892 /* Acquire lock for all peers of the section */
2893 for (ps = peers->remote; ps; ps = ps->next)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002894 HA_SPIN_LOCK(PEER_LOCK, &ps->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002895
Emeric Brun2b920a12010-09-23 18:30:22 +02002896 if (!stopping) {
2897 /* Normal case (not soft stop)*/
Emeric Brunb3971ab2015-05-12 18:49:09 +02002898
2899 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL) &&
2900 (!nb_oldpids || tick_is_expired(peers->resync_timeout, now_ms)) &&
2901 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002902 /* Resync from local peer needed
2903 no peer was assigned for the lesson
2904 and no old local peer found
2905 or resync timeout expire */
2906
2907 /* flag no more resync from local, to try resync from remotes */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002908 peers->flags |= PEERS_F_RESYNC_LOCAL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002909
2910 /* reschedule a resync */
Frédéric Lécaille54bff832019-03-26 10:25:20 +01002911 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(PEER_RESYNC_TIMEOUT));
Emeric Brun2b920a12010-09-23 18:30:22 +02002912 }
2913
2914 /* For each session */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002915 for (ps = peers->remote; ps; ps = ps->next) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002916 /* For each remote peers */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002917 if (!ps->local) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002918 if (!ps->appctx) {
2919 /* no active peer connection */
Emeric Brun2b920a12010-09-23 18:30:22 +02002920 if (ps->statuscode == 0 ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002921 ((ps->statuscode == PEER_SESS_SC_CONNECTCODE ||
Willy Tarreaub4e34da2015-05-20 10:39:04 +02002922 ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002923 ps->statuscode == PEER_SESS_SC_CONNECTEDCODE) &&
Emeric Brun2b920a12010-09-23 18:30:22 +02002924 tick_is_expired(ps->reconnect, now_ms))) {
2925 /* connection never tried
Willy Tarreau9df94c22016-10-31 18:42:52 +01002926 * or previous peer connection established with success
2927 * or previous peer connection failed while connecting
Emeric Brun2b920a12010-09-23 18:30:22 +02002928 * and reconnection timer is expired */
2929
2930 /* retry a connect */
Willy Tarreau9df94c22016-10-31 18:42:52 +01002931 ps->appctx = peer_session_create(peers, ps);
Emeric Brun2b920a12010-09-23 18:30:22 +02002932 }
Willy Tarreaub4e34da2015-05-20 10:39:04 +02002933 else if (!tick_is_expired(ps->reconnect, now_ms)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002934 /* If previous session failed during connection
2935 * but reconnection timer is not expired */
2936
2937 /* reschedule task for reconnect */
2938 task->expire = tick_first(task->expire, ps->reconnect);
2939 }
2940 /* else do nothing */
Willy Tarreau9df94c22016-10-31 18:42:52 +01002941 } /* !ps->appctx */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002942 else if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002943 /* current peer connection is active and established */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002944 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE) &&
2945 !(peers->flags & PEERS_F_RESYNC_ASSIGN) &&
Emeric Brun2b920a12010-09-23 18:30:22 +02002946 !(ps->flags & PEER_F_LEARN_NOTUP2DATE)) {
2947 /* Resync from a remote is needed
2948 * and no peer was assigned for lesson
2949 * and current peer may be up2date */
2950
2951 /* assign peer for the lesson */
2952 ps->flags |= PEER_F_LEARN_ASSIGN;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002953 peers->flags |= PEERS_F_RESYNC_ASSIGN;
Emeric Brun2b920a12010-09-23 18:30:22 +02002954
Willy Tarreau9df94c22016-10-31 18:42:52 +01002955 /* wake up peer handler to handle a request of resync */
Willy Tarreaue5843b32015-04-27 18:40:14 +02002956 appctx_wakeup(ps->appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002957 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002958 else {
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002959 int update_to_push = 0;
2960
Emeric Brunb3971ab2015-05-12 18:49:09 +02002961 /* Awake session if there is data to push */
2962 for (st = ps->tables; st ; st = st->next) {
2963 if ((int)(st->last_pushed - st->table->localupdate) < 0) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002964 /* wake up the peer handler to push local updates */
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002965 update_to_push = 1;
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002966 /* There is no need to send a heartbeat message
2967 * when some updates must be pushed. The remote
2968 * peer will consider <ps> peer as alive when it will
2969 * receive these updates.
2970 */
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002971 ps->flags &= ~PEER_F_HEARTBEAT;
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002972 /* Re-schedule another one later. */
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002973 ps->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002974 /* We are going to send updates, let's ensure we will
2975 * come back to send heartbeat messages or to reconnect.
2976 */
2977 task->expire = tick_first(ps->reconnect, ps->heartbeat);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002978 appctx_wakeup(ps->appctx);
2979 break;
2980 }
2981 }
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002982 /* When there are updates to send we do not reconnect
2983 * and do not send heartbeat message either.
2984 */
2985 if (!update_to_push) {
2986 if (tick_is_expired(ps->reconnect, now_ms)) {
2987 if (ps->flags & PEER_F_ALIVE) {
2988 /* This peer was alive during a 'reconnect' period.
2989 * Flag it as not alive again for the next period.
2990 */
2991 ps->flags &= ~PEER_F_ALIVE;
Frédéric Lécaille54bff832019-03-26 10:25:20 +01002992 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT));
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002993 }
2994 else {
Willy Tarreau52bf8392020-03-08 00:42:37 +01002995 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002996 ps->heartbeat = TICK_ETERNITY;
Emeric Brun9ef2ad72019-04-02 17:22:01 +02002997 peer_session_forceshutdown(ps);
Frédéric Lécailleec1c10b2019-11-07 15:22:33 +01002998 ps->no_hbt++;
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002999 }
3000 }
3001 else if (tick_is_expired(ps->heartbeat, now_ms)) {
3002 ps->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
3003 ps->flags |= PEER_F_HEARTBEAT;
3004 appctx_wakeup(ps->appctx);
3005 }
Frédéric Lécailleb7405c12019-03-27 14:32:39 +01003006 task->expire = tick_first(ps->reconnect, ps->heartbeat);
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003007 }
Emeric Brun2b920a12010-09-23 18:30:22 +02003008 }
3009 /* else do nothing */
3010 } /* SUCCESSCODE */
3011 } /* !ps->peer->local */
3012 } /* for */
3013
3014 /* Resync from remotes expired: consider resync is finished */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003015 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE) &&
3016 !(peers->flags & PEERS_F_RESYNC_ASSIGN) &&
3017 tick_is_expired(peers->resync_timeout, now_ms)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003018 /* Resync from remote peer needed
3019 * no peer was assigned for the lesson
3020 * and resync timeout expire */
3021
3022 /* flag no more resync from remote, consider resync is finished */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003023 peers->flags |= PEERS_F_RESYNC_REMOTE;
Emeric Brun2b920a12010-09-23 18:30:22 +02003024 }
3025
Emeric Brunb3971ab2015-05-12 18:49:09 +02003026 if ((peers->flags & PEERS_RESYNC_STATEMASK) != PEERS_RESYNC_FINISHED) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003027 /* Resync not finished*/
Frédéric Lécaille5d6e5f82017-05-29 13:47:16 +02003028 /* reschedule task to resync timeout if not expired, to ended resync if needed */
3029 if (!tick_is_expired(peers->resync_timeout, now_ms))
3030 task->expire = tick_first(task->expire, peers->resync_timeout);
Emeric Brun2b920a12010-09-23 18:30:22 +02003031 }
3032 } /* !stopping */
3033 else {
3034 /* soft stop case */
Willy Tarreau086735a2018-11-05 15:09:47 +01003035 if (state & TASK_WOKEN_SIGNAL) {
Joseph Herlant82b2f542018-11-15 12:19:14 -08003036 /* We've just received the signal */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003037 if (!(peers->flags & PEERS_F_DONOTSTOP)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003038 /* add DO NOT STOP flag if not present */
Willy Tarreau4781b152021-04-06 13:53:36 +02003039 _HA_ATOMIC_INC(&jobs);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003040 peers->flags |= PEERS_F_DONOTSTOP;
3041 ps = peers->local;
3042 for (st = ps->tables; st ; st = st->next)
3043 st->table->syncing++;
Emeric Brun2b920a12010-09-23 18:30:22 +02003044 }
3045
3046 /* disconnect all connected peers */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003047 for (ps = peers->remote; ps; ps = ps->next) {
Emeric Brun80527f52017-06-19 17:46:37 +02003048 /* we're killing a connection, we must apply a random delay before
3049 * retrying otherwise the other end will do the same and we can loop
3050 * for a while.
3051 */
Willy Tarreau52bf8392020-03-08 00:42:37 +01003052 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
Willy Tarreau9df94c22016-10-31 18:42:52 +01003053 if (ps->appctx) {
Emeric Brun9ef2ad72019-04-02 17:22:01 +02003054 peer_session_forceshutdown(ps);
Emeric Brun2b920a12010-09-23 18:30:22 +02003055 }
3056 }
3057 }
Emeric Brun2b920a12010-09-23 18:30:22 +02003058
Emeric Brunb3971ab2015-05-12 18:49:09 +02003059 ps = peers->local;
Emeric Brun2b920a12010-09-23 18:30:22 +02003060 if (ps->flags & PEER_F_TEACH_COMPLETE) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02003061 if (peers->flags & PEERS_F_DONOTSTOP) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003062 /* resync of new process was complete, current process can die now */
Willy Tarreau4781b152021-04-06 13:53:36 +02003063 _HA_ATOMIC_DEC(&jobs);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003064 peers->flags &= ~PEERS_F_DONOTSTOP;
3065 for (st = ps->tables; st ; st = st->next)
3066 st->table->syncing--;
Emeric Brun2b920a12010-09-23 18:30:22 +02003067 }
3068 }
Willy Tarreau9df94c22016-10-31 18:42:52 +01003069 else if (!ps->appctx) {
3070 /* If there's no active peer connection */
Emeric Brun2b920a12010-09-23 18:30:22 +02003071 if (ps->statuscode == 0 ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003072 ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
3073 ps->statuscode == PEER_SESS_SC_CONNECTEDCODE ||
3074 ps->statuscode == PEER_SESS_SC_TRYAGAIN) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003075 /* connection never tried
Willy Tarreau9df94c22016-10-31 18:42:52 +01003076 * or previous peer connection was successfully established
3077 * or previous tcp connect succeeded but init state incomplete
Emeric Brun2b920a12010-09-23 18:30:22 +02003078 * or during previous connect, peer replies a try again statuscode */
3079
3080 /* connect to the peer */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003081 peer_session_create(peers, ps);
Emeric Brun2b920a12010-09-23 18:30:22 +02003082 }
3083 else {
3084 /* Other error cases */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003085 if (peers->flags & PEERS_F_DONOTSTOP) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003086 /* unable to resync new process, current process can die now */
Willy Tarreau4781b152021-04-06 13:53:36 +02003087 _HA_ATOMIC_DEC(&jobs);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003088 peers->flags &= ~PEERS_F_DONOTSTOP;
3089 for (st = ps->tables; st ; st = st->next)
3090 st->table->syncing--;
Emeric Brun2b920a12010-09-23 18:30:22 +02003091 }
3092 }
3093 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02003094 else if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE ) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01003095 /* current peer connection is active and established
3096 * wake up all peer handlers to push remaining local updates */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003097 for (st = ps->tables; st ; st = st->next) {
3098 if ((int)(st->last_pushed - st->table->localupdate) < 0) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02003099 appctx_wakeup(ps->appctx);
3100 break;
3101 }
3102 }
Emeric Brun2b920a12010-09-23 18:30:22 +02003103 }
3104 } /* stopping */
Emeric Brun80527f52017-06-19 17:46:37 +02003105
3106 /* Release lock for all peers of the section */
3107 for (ps = peers->remote; ps; ps = ps->next)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003108 HA_SPIN_UNLOCK(PEER_LOCK, &ps->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02003109
Emeric Brun2b920a12010-09-23 18:30:22 +02003110 /* Wakeup for re-connect */
3111 return task;
3112}
3113
Emeric Brunb3971ab2015-05-12 18:49:09 +02003114
Emeric Brun2b920a12010-09-23 18:30:22 +02003115/*
Willy Tarreaud9443442018-10-15 11:18:03 +02003116 * returns 0 in case of error.
Emeric Brun2b920a12010-09-23 18:30:22 +02003117 */
Willy Tarreaud9443442018-10-15 11:18:03 +02003118int peers_init_sync(struct peers *peers)
Emeric Brun2b920a12010-09-23 18:30:22 +02003119{
Emeric Brun2b920a12010-09-23 18:30:22 +02003120 struct peer * curpeer;
Emeric Brun2b920a12010-09-23 18:30:22 +02003121
Emeric Brun2b920a12010-09-23 18:30:22 +02003122 for (curpeer = peers->remote; curpeer; curpeer = curpeer->next) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003123 peers->peers_fe->maxconn += 3;
3124 }
3125
Emeric Brunc60def82017-09-27 14:59:38 +02003126 peers->sync_task = task_new(MAX_THREADS_MASK);
Willy Tarreaud9443442018-10-15 11:18:03 +02003127 if (!peers->sync_task)
3128 return 0;
3129
Emeric Brunb3971ab2015-05-12 18:49:09 +02003130 peers->sync_task->process = process_peer_sync;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003131 peers->sync_task->context = (void *)peers;
3132 peers->sighandler = signal_register_task(0, peers->sync_task, 0);
3133 task_wakeup(peers->sync_task, TASK_WOKEN_INIT);
Willy Tarreaud9443442018-10-15 11:18:03 +02003134 return 1;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003135}
3136
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003137/*
3138 * Allocate a cache a dictionary entries used upon transmission.
3139 */
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003140static struct dcache_tx *new_dcache_tx(size_t max_entries)
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003141{
3142 struct dcache_tx *d;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003143 struct ebpt_node *entries;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003144
3145 d = malloc(sizeof *d);
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003146 entries = calloc(max_entries, sizeof *entries);
3147 if (!d || !entries)
3148 goto err;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003149
3150 d->lru_key = 0;
Frédéric Lécailleb65717f2019-06-07 14:25:25 +02003151 d->prev_lookup = NULL;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003152 d->cached_entries = EB_ROOT_UNIQUE;
3153 d->entries = entries;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003154
3155 return d;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003156
3157 err:
3158 free(d);
3159 free(entries);
3160 return NULL;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003161}
3162
3163/*
3164 * Allocate a cache of dictionary entries with <name> as name and <max_entries>
3165 * as maximum of entries.
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003166 * Return the dictionary cache if succeeded, NULL if not.
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003167 * Must be deallocated calling free_dcache().
3168 */
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003169static struct dcache *new_dcache(size_t max_entries)
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003170{
3171 struct dcache_tx *dc_tx;
3172 struct dcache *dc;
3173 struct dcache_rx *dc_rx;
3174
3175 dc = calloc(1, sizeof *dc);
3176 dc_tx = new_dcache_tx(max_entries);
3177 dc_rx = calloc(max_entries, sizeof *dc_rx);
3178 if (!dc || !dc_tx || !dc_rx)
3179 goto err;
3180
3181 dc->tx = dc_tx;
3182 dc->rx = dc_rx;
3183 dc->max_entries = max_entries;
3184
3185 return dc;
3186
3187 err:
3188 free(dc);
3189 free(dc_tx);
3190 free(dc_rx);
3191 return NULL;
3192}
3193
3194/*
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003195 * Look for the dictionary entry with the value of <i> in <d> cache of dictionary
3196 * entries used upon transmission.
3197 * Return the entry if found, NULL if not.
3198 */
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003199static struct ebpt_node *dcache_tx_lookup_value(struct dcache_tx *d,
3200 struct dcache_tx_entry *i)
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003201{
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003202 return ebpt_lookup(&d->cached_entries, i->entry.key);
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003203}
3204
3205/*
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003206 * Flush <dc> cache.
3207 * Always succeeds.
3208 */
3209static inline void flush_dcache(struct peer *peer)
3210{
3211 int i;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02003212 struct dcache *dc = peer->dcache;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003213
Frédéric Lécailleea875e62020-11-12 21:01:54 +01003214 for (i = 0; i < dc->max_entries; i++) {
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003215 ebpt_delete(&dc->tx->entries[i]);
Frédéric Lécailleea875e62020-11-12 21:01:54 +01003216 dc->tx->entries[i].key = NULL;
Thayne McCombs92149f92020-11-20 01:28:26 -07003217 dict_entry_unref(&server_key_dict, dc->rx[i].de);
3218 dc->rx[i].de = NULL;
Frédéric Lécailleea875e62020-11-12 21:01:54 +01003219 }
3220 dc->tx->prev_lookup = NULL;
3221 dc->tx->lru_key = 0;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003222
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003223 memset(dc->rx, 0, dc->max_entries * sizeof *dc->rx);
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003224}
3225
3226/*
3227 * Insert a dictionary entry in <dc> cache part used upon transmission (->tx)
3228 * with information provided by <i> dictionary cache entry (especially the value
3229 * to be inserted if not already). Return <i> if already present in the cache
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003230 * or something different of <i> if not.
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003231 */
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003232static struct ebpt_node *dcache_tx_insert(struct dcache *dc, struct dcache_tx_entry *i)
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003233{
3234 struct dcache_tx *dc_tx;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003235 struct ebpt_node *o;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003236
3237 dc_tx = dc->tx;
Frédéric Lécailleb65717f2019-06-07 14:25:25 +02003238
3239 if (dc_tx->prev_lookup && dc_tx->prev_lookup->key == i->entry.key) {
3240 o = dc_tx->prev_lookup;
3241 } else {
3242 o = dcache_tx_lookup_value(dc_tx, i);
3243 if (o) {
3244 /* Save it */
3245 dc_tx->prev_lookup = o;
3246 }
3247 }
3248
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003249 if (o) {
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003250 /* Copy the ID. */
3251 i->id = o - dc->tx->entries;
3252 return &i->entry;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003253 }
3254
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003255 /* The new entry to put in cache */
Frédéric Lécailleb65717f2019-06-07 14:25:25 +02003256 dc_tx->prev_lookup = o = &dc_tx->entries[dc_tx->lru_key];
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003257
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003258 ebpt_delete(o);
3259 o->key = i->entry.key;
3260 ebpt_insert(&dc_tx->cached_entries, o);
3261 i->id = dc_tx->lru_key;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003262
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003263 /* Update the index for the next entry to put in cache */
3264 dc_tx->lru_key = (dc_tx->lru_key + 1) & (dc->max_entries - 1);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003265
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003266 return o;
3267}
Emeric Brunb3971ab2015-05-12 18:49:09 +02003268
3269/*
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02003270 * Allocate a dictionary cache for each peer of <peers> section.
3271 * Return 1 if succeeded, 0 if not.
3272 */
3273int peers_alloc_dcache(struct peers *peers)
3274{
3275 struct peer *p;
3276
3277 for (p = peers->remote; p; p = p->next) {
3278 p->dcache = new_dcache(PEER_STKT_CACHE_MAX_ENTRIES);
3279 if (!p->dcache)
3280 return 0;
3281 }
3282
3283 return 1;
3284}
3285
3286/*
Emeric Brunb3971ab2015-05-12 18:49:09 +02003287 * Function used to register a table for sync on a group of peers
3288 *
3289 */
3290void peers_register_table(struct peers *peers, struct stktable *table)
3291{
3292 struct shared_table *st;
3293 struct peer * curpeer;
3294 int id = 0;
3295
3296 for (curpeer = peers->remote; curpeer; curpeer = curpeer->next) {
Vincent Bernat02779b62016-04-03 13:48:43 +02003297 st = calloc(1,sizeof(*st));
Emeric Brunb3971ab2015-05-12 18:49:09 +02003298 st->table = table;
3299 st->next = curpeer->tables;
3300 if (curpeer->tables)
3301 id = curpeer->tables->local_id;
3302 st->local_id = id + 1;
3303
3304 curpeer->tables = st;
3305 }
3306
3307 table->sync_task = peers->sync_task;
Emeric Brun2b920a12010-09-23 18:30:22 +02003308}
3309
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003310/*
3311 * Parse the "show peers" command arguments.
3312 * Returns 0 if succeeded, 1 if not with the ->msg of the appctx set as
3313 * error message.
3314 */
3315static int cli_parse_show_peers(char **args, char *payload, struct appctx *appctx, void *private)
3316{
3317 appctx->ctx.cfgpeers.target = NULL;
3318
Willy Tarreau49962b52021-02-12 16:56:22 +01003319 if (strcmp(args[2], "dict") == 0) {
3320 /* show the dictionaries (large dump) */
3321 appctx->ctx.cfgpeers.flags |= PEERS_SHOW_F_DICT;
3322 args++;
3323 } else if (strcmp(args[2], "-") == 0)
3324 args++; // allows to show a section called "dict"
3325
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003326 if (*args[2]) {
3327 struct peers *p;
3328
3329 for (p = cfg_peers; p; p = p->next) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01003330 if (strcmp(p->id, args[2]) == 0) {
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003331 appctx->ctx.cfgpeers.target = p;
3332 break;
3333 }
3334 }
3335
Willy Tarreau9d008692019-08-09 11:21:01 +02003336 if (!p)
3337 return cli_err(appctx, "No such peers\n");
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003338 }
3339
3340 return 0;
3341}
3342
3343/*
3344 * This function dumps the peer state information of <peers> "peers" section.
3345 * Returns 0 if the output buffer is full and needs to be called again, non-zero if not.
3346 * Dedicated to be called by cli_io_handler_show_peers() cli I/O handler.
3347 */
3348static int peers_dump_head(struct buffer *msg, struct stream_interface *si, struct peers *peers)
3349{
3350 struct tm tm;
3351
3352 get_localtime(peers->last_change, &tm);
Willy Tarreau1ad64ac2020-09-24 08:48:08 +02003353 chunk_appendf(msg, "%p: [%02d/%s/%04d:%02d:%02d:%02d] id=%s disabled=%d flags=0x%x resync_timeout=%s task_calls=%u\n",
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003354 peers,
3355 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
3356 tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau1ad64ac2020-09-24 08:48:08 +02003357 peers->id, peers->disabled, peers->flags,
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003358 peers->resync_timeout ?
3359 tick_is_expired(peers->resync_timeout, now_ms) ? "<PAST>" :
3360 human_time(TICKS_TO_MS(peers->resync_timeout - now_ms),
Emeric Brun0bbec0f2019-04-18 11:39:43 +02003361 TICKS_TO_MS(1000)) : "<NEVER>",
3362 peers->sync_task ? peers->sync_task->calls : 0);
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003363
3364 if (ci_putchk(si_ic(si), msg) == -1) {
3365 si_rx_room_blk(si);
3366 return 0;
3367 }
3368
3369 return 1;
3370}
3371
3372/*
3373 * This function dumps <peer> state information.
3374 * Returns 0 if the output buffer is full and needs to be called again, non-zero
3375 * if not. Dedicated to be called by cli_io_handler_show_peers() cli I/O handler.
3376 */
Willy Tarreau49962b52021-02-12 16:56:22 +01003377static int peers_dump_peer(struct buffer *msg, struct stream_interface *si, struct peer *peer, int flags)
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003378{
3379 struct connection *conn;
3380 char pn[INET6_ADDRSTRLEN];
3381 struct stream_interface *peer_si;
3382 struct stream *peer_s;
3383 struct appctx *appctx;
3384 struct shared_table *st;
3385
3386 addr_to_str(&peer->addr, pn, sizeof pn);
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003387 chunk_appendf(msg, " %p: id=%s(%s,%s) addr=%s:%d last_status=%s",
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003388 peer, peer->id,
3389 peer->local ? "local" : "remote",
Frédéric Lécaillee7e2b212020-10-05 12:33:07 +02003390 peer->appctx ? "active" : "inactive",
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003391 pn, get_host_port(&peer->addr),
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003392 statuscode_str(peer->statuscode));
3393
3394 chunk_appendf(msg, " last_hdshk=%s\n",
3395 peer->last_hdshk ? human_time(TICKS_TO_MS(now_ms - peer->last_hdshk),
3396 TICKS_TO_MS(1000)) : "<NEVER>");
3397
3398 chunk_appendf(msg, " reconnect=%s",
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003399 peer->reconnect ?
3400 tick_is_expired(peer->reconnect, now_ms) ? "<PAST>" :
3401 human_time(TICKS_TO_MS(peer->reconnect - now_ms),
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003402 TICKS_TO_MS(1000)) : "<NEVER>");
3403
3404 chunk_appendf(msg, " heartbeat=%s",
3405 peer->heartbeat ?
3406 tick_is_expired(peer->heartbeat, now_ms) ? "<PAST>" :
3407 human_time(TICKS_TO_MS(peer->heartbeat - now_ms),
3408 TICKS_TO_MS(1000)) : "<NEVER>");
3409
3410 chunk_appendf(msg, " confirm=%u tx_hbt=%u rx_hbt=%u no_hbt=%u new_conn=%u proto_err=%u coll=%u\n",
Frédéric Lécailleec1c10b2019-11-07 15:22:33 +01003411 peer->confirm, peer->tx_hbt, peer->rx_hbt,
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003412 peer->no_hbt, peer->new_conn, peer->proto_err, peer->coll);
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003413
3414 chunk_appendf(&trash, " flags=0x%x", peer->flags);
3415
3416 appctx = peer->appctx;
3417 if (!appctx)
Frédéric Lécaille470502b2019-11-06 10:41:03 +01003418 goto table_info;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003419
Emeric Brun0bbec0f2019-04-18 11:39:43 +02003420 chunk_appendf(&trash, " appctx:%p st0=%d st1=%d task_calls=%u", appctx, appctx->st0, appctx->st1,
3421 appctx->t ? appctx->t->calls : 0);
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003422
3423 peer_si = peer->appctx->owner;
3424 if (!peer_si)
Frédéric Lécaille470502b2019-11-06 10:41:03 +01003425 goto table_info;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003426
3427 peer_s = si_strm(peer_si);
3428 if (!peer_s)
Frédéric Lécaille470502b2019-11-06 10:41:03 +01003429 goto table_info;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003430
3431 chunk_appendf(&trash, " state=%s", si_state_str(si_opposite(peer_si)->state));
3432
3433 conn = objt_conn(strm_orig(peer_s));
3434 if (conn)
3435 chunk_appendf(&trash, "\n xprt=%s", conn_get_xprt_name(conn));
3436
Willy Tarreau3ca14902019-07-17 14:53:15 +02003437 switch (conn && conn_get_src(conn) ? addr_to_str(conn->src, pn, sizeof(pn)) : AF_UNSPEC) {
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003438 case AF_INET:
3439 case AF_INET6:
Willy Tarreau3ca14902019-07-17 14:53:15 +02003440 chunk_appendf(&trash, " src=%s:%d", pn, get_host_port(conn->src));
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003441 break;
3442 case AF_UNIX:
3443 chunk_appendf(&trash, " src=unix:%d", strm_li(peer_s)->luid);
3444 break;
3445 }
3446
Willy Tarreau3ca14902019-07-17 14:53:15 +02003447 switch (conn && conn_get_dst(conn) ? addr_to_str(conn->dst, pn, sizeof(pn)) : AF_UNSPEC) {
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003448 case AF_INET:
3449 case AF_INET6:
Willy Tarreau3ca14902019-07-17 14:53:15 +02003450 chunk_appendf(&trash, " addr=%s:%d", pn, get_host_port(conn->dst));
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003451 break;
3452 case AF_UNIX:
3453 chunk_appendf(&trash, " addr=unix:%d", strm_li(peer_s)->luid);
3454 break;
3455 }
3456
Frédéric Lécaille470502b2019-11-06 10:41:03 +01003457 table_info:
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003458 if (peer->remote_table)
3459 chunk_appendf(&trash, "\n remote_table:%p id=%s local_id=%d remote_id=%d",
3460 peer->remote_table,
3461 peer->remote_table->table->id,
3462 peer->remote_table->local_id,
3463 peer->remote_table->remote_id);
3464
3465 if (peer->last_local_table)
3466 chunk_appendf(&trash, "\n last_local_table:%p id=%s local_id=%d remote_id=%d",
3467 peer->last_local_table,
3468 peer->last_local_table->table->id,
3469 peer->last_local_table->local_id,
3470 peer->last_local_table->remote_id);
3471
3472 if (peer->tables) {
3473 chunk_appendf(&trash, "\n shared tables:");
3474 for (st = peer->tables; st; st = st->next) {
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003475 int i, count;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003476 struct stktable *t;
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003477 struct dcache *dcache;
3478
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003479 t = st->table;
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003480 dcache = peer->dcache;
3481
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003482 chunk_appendf(&trash, "\n %p local_id=%d remote_id=%d "
3483 "flags=0x%x remote_data=0x%llx",
3484 st, st->local_id, st->remote_id,
3485 st->flags, (unsigned long long)st->remote_data);
3486 chunk_appendf(&trash, "\n last_acked=%u last_pushed=%u last_get=%u"
3487 " teaching_origin=%u update=%u",
3488 st->last_acked, st->last_pushed, st->last_get,
3489 st->teaching_origin, st->update);
3490 chunk_appendf(&trash, "\n table:%p id=%s update=%u localupdate=%u"
3491 " commitupdate=%u syncing=%u",
3492 t, t->id, t->update, t->localupdate, t->commitupdate, t->syncing);
Willy Tarreau49962b52021-02-12 16:56:22 +01003493 if (flags & PEERS_SHOW_F_DICT) {
3494 chunk_appendf(&trash, "\n TX dictionary cache:");
3495 count = 0;
3496 for (i = 0; i < dcache->max_entries; i++) {
3497 struct ebpt_node *node;
3498 struct dict_entry *de;
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003499
Willy Tarreau49962b52021-02-12 16:56:22 +01003500 node = &dcache->tx->entries[i];
3501 if (!node->key)
3502 break;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003503
Willy Tarreau49962b52021-02-12 16:56:22 +01003504 if (!count++)
3505 chunk_appendf(&trash, "\n ");
3506 de = node->key;
3507 chunk_appendf(&trash, " %3u -> %s", i, (char *)de->value.key);
3508 count &= 0x3;
3509 }
3510 chunk_appendf(&trash, "\n RX dictionary cache:");
3511 count = 0;
3512 for (i = 0; i < dcache->max_entries; i++) {
3513 if (!count++)
3514 chunk_appendf(&trash, "\n ");
3515 chunk_appendf(&trash, " %3u -> %s", i,
3516 dcache->rx[i].de ?
3517 (char *)dcache->rx[i].de->value.key : "-");
3518 count &= 0x3;
3519 }
3520 } else {
3521 chunk_appendf(&trash, "\n Dictionary cache not dumped (use \"show peers dict\")");
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003522 }
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003523 }
3524 }
3525
3526 end:
3527 chunk_appendf(&trash, "\n");
3528 if (ci_putchk(si_ic(si), msg) == -1) {
3529 si_rx_room_blk(si);
3530 return 0;
3531 }
3532
3533 return 1;
3534}
3535
3536/*
3537 * This function dumps all the peers of "peers" section.
3538 * Returns 0 if the output buffer is full and needs to be called
3539 * again, non-zero if not. It proceeds in an isolated thread, so
3540 * there is no thread safety issue here.
3541 */
3542static int cli_io_handler_show_peers(struct appctx *appctx)
3543{
3544 int show_all;
3545 int ret = 0, first_peers = 1;
3546 struct stream_interface *si = appctx->owner;
3547
3548 thread_isolate();
3549
3550 show_all = !appctx->ctx.cfgpeers.target;
3551
3552 chunk_reset(&trash);
3553
3554 while (appctx->st2 != STAT_ST_FIN) {
3555 switch (appctx->st2) {
3556 case STAT_ST_INIT:
3557 if (show_all)
3558 appctx->ctx.cfgpeers.peers = cfg_peers;
3559 else
3560 appctx->ctx.cfgpeers.peers = appctx->ctx.cfgpeers.target;
3561
3562 appctx->st2 = STAT_ST_LIST;
3563 /* fall through */
3564
3565 case STAT_ST_LIST:
3566 if (!appctx->ctx.cfgpeers.peers) {
3567 /* No more peers list. */
3568 appctx->st2 = STAT_ST_END;
3569 }
3570 else {
3571 if (!first_peers)
3572 chunk_appendf(&trash, "\n");
3573 else
3574 first_peers = 0;
3575 if (!peers_dump_head(&trash, si, appctx->ctx.cfgpeers.peers))
3576 goto out;
3577
3578 appctx->ctx.cfgpeers.peer = appctx->ctx.cfgpeers.peers->remote;
3579 appctx->ctx.cfgpeers.peers = appctx->ctx.cfgpeers.peers->next;
3580 appctx->st2 = STAT_ST_INFO;
3581 }
3582 break;
3583
3584 case STAT_ST_INFO:
3585 if (!appctx->ctx.cfgpeers.peer) {
3586 /* End of peer list */
3587 if (show_all)
3588 appctx->st2 = STAT_ST_LIST;
3589 else
3590 appctx->st2 = STAT_ST_END;
3591 }
3592 else {
Willy Tarreau49962b52021-02-12 16:56:22 +01003593 if (!peers_dump_peer(&trash, si, appctx->ctx.cfgpeers.peer, appctx->ctx.cfgpeers.flags))
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003594 goto out;
3595
3596 appctx->ctx.cfgpeers.peer = appctx->ctx.cfgpeers.peer->next;
3597 }
3598 break;
3599
3600 case STAT_ST_END:
3601 appctx->st2 = STAT_ST_FIN;
3602 break;
3603 }
3604 }
3605 ret = 1;
3606 out:
3607 thread_release();
3608 return ret;
3609}
3610
3611/*
3612 * CLI keywords.
3613 */
3614static struct cli_kw_list cli_kws = {{ }, {
3615 { { "show", "peers", NULL }, "show peers [peers section]: dump some information about all the peers or this peers section", cli_parse_show_peers, cli_io_handler_show_peers, },
3616 {},
3617}};
3618
3619/* Register cli keywords */
3620INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3621