blob: b0ed81c8cd176c2dd27167fccc9eece017fc32b5 [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>
Emeric Brun2b920a12010-09-23 18:30:22 +020014#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17
18#include <sys/socket.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21
Willy Tarreau8db34cc2021-10-06 17:53:19 +020022#include <import/eb32tree.h>
23#include <import/ebmbtree.h>
24#include <import/ebpttree.h>
25
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020026#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020027#include <haproxy/applet.h>
Willy Tarreauf1d32c42020-06-04 21:07:02 +020028#include <haproxy/channel.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020029#include <haproxy/cli.h>
Willy Tarreau3afc4c42020-06-03 18:23:19 +020030#include <haproxy/dict.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020031#include <haproxy/errors.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020032#include <haproxy/fd.h>
Willy Tarreau762d7a52020-06-04 11:23:07 +020033#include <haproxy/frontend.h>
Willy Tarreau6131d6a2020-06-02 16:48:09 +020034#include <haproxy/net_helper.h>
Willy Tarreau8efbdfb2020-06-04 11:29:21 +020035#include <haproxy/obj_type-t.h>
Willy Tarreau3c2a7c22020-06-04 18:38:21 +020036#include <haproxy/peers.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020037#include <haproxy/proxy.h>
Willy Tarreau5edca2f2022-05-27 09:25:10 +020038#include <haproxy/sc_strm.h>
Willy Tarreau48d25b32020-06-04 18:58:52 +020039#include <haproxy/session-t.h>
Willy Tarreau3727a8a2020-06-04 17:37:26 +020040#include <haproxy/signal.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020041#include <haproxy/stats-t.h>
Willy Tarreaucb086c62022-05-27 09:47:12 +020042#include <haproxy/stconn.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020043#include <haproxy/stick_table.h>
44#include <haproxy/stream.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020045#include <haproxy/task.h>
46#include <haproxy/thread.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020047#include <haproxy/time.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020048#include <haproxy/tools.h>
Frédéric Lécailled8659352020-11-10 16:18:03 +010049#include <haproxy/trace.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020050
Emeric Brun2b920a12010-09-23 18:30:22 +020051
52/*******************************/
53/* Current peer learning state */
54/*******************************/
55
56/******************************/
Emeric Brunb3971ab2015-05-12 18:49:09 +020057/* Current peers section resync state */
Emeric Brun2b920a12010-09-23 18:30:22 +020058/******************************/
Emeric Brunccdfbae2021-04-28 12:59:35 +020059#define PEERS_F_RESYNC_LOCAL 0x00000001 /* Learn from local finished or no more needed */
60#define PEERS_F_RESYNC_REMOTE 0x00000002 /* Learn from remote finished or no more needed */
61#define PEERS_F_RESYNC_ASSIGN 0x00000004 /* A peer was assigned to learn our lesson */
62#define PEERS_F_RESYNC_PROCESS 0x00000008 /* The assigned peer was requested for resync */
63#define PEERS_F_RESYNC_LOCALTIMEOUT 0x00000010 /* Timeout waiting for a full resync from a local node */
64#define PEERS_F_RESYNC_REMOTETIMEOUT 0x00000020 /* Timeout waiting for a full resync from a remote node */
65#define PEERS_F_RESYNC_LOCALABORT 0x00000040 /* Session aborted learning from a local node */
66#define PEERS_F_RESYNC_REMOTEABORT 0x00000080 /* Session aborted learning from a remote node */
67#define PEERS_F_RESYNC_LOCALFINISHED 0x00000100 /* A local node teach us and was fully up to date */
68#define PEERS_F_RESYNC_REMOTEFINISHED 0x00000200 /* A remote node teach us and was fully up to date */
69#define PEERS_F_RESYNC_LOCALPARTIAL 0x00000400 /* A local node teach us but was partially up to date */
70#define PEERS_F_RESYNC_REMOTEPARTIAL 0x00000800 /* A remote node teach us but was partially up to date */
71#define PEERS_F_RESYNC_LOCALASSIGN 0x00001000 /* A local node was assigned for a full resync */
72#define PEERS_F_RESYNC_REMOTEASSIGN 0x00002000 /* A remote node was assigned for a full resync */
73#define PEERS_F_RESYNC_REQUESTED 0x00004000 /* A resync was explicitly requested */
74#define PEERS_F_DONOTSTOP 0x00010000 /* Main table sync task block process during soft stop
75 to push data to new process */
Emeric Brun2b920a12010-09-23 18:30:22 +020076
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010077#define PEERS_RESYNC_STATEMASK (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE)
78#define PEERS_RESYNC_FROMLOCAL 0x00000000
79#define PEERS_RESYNC_FROMREMOTE PEERS_F_RESYNC_LOCAL
80#define PEERS_RESYNC_FINISHED (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE)
Emeric Brunb3971ab2015-05-12 18:49:09 +020081
82/***********************************/
83/* Current shared table sync state */
84/***********************************/
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010085#define SHTABLE_F_TEACH_STAGE1 0x00000001 /* Teach state 1 complete */
86#define SHTABLE_F_TEACH_STAGE2 0x00000002 /* Teach state 2 complete */
Emeric Brun2b920a12010-09-23 18:30:22 +020087
88/******************************/
89/* Remote peer teaching state */
90/******************************/
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010091#define PEER_F_TEACH_PROCESS 0x00000001 /* Teach a lesson to current peer */
92#define PEER_F_TEACH_FINISHED 0x00000008 /* Teach conclude, (wait for confirm) */
93#define PEER_F_TEACH_COMPLETE 0x00000010 /* All that we know already taught to current peer, used only for a local peer */
94#define PEER_F_LEARN_ASSIGN 0x00000100 /* Current peer was assigned for a lesson */
95#define PEER_F_LEARN_NOTUP2DATE 0x00000200 /* Learn from peer finished but peer is not up to date */
96#define PEER_F_ALIVE 0x20000000 /* Used to flag a peer a alive. */
97#define PEER_F_HEARTBEAT 0x40000000 /* Heartbeat message to send. */
98#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 +020099
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100100#define PEER_TEACH_RESET ~(PEER_F_TEACH_PROCESS|PEER_F_TEACH_FINISHED) /* PEER_F_TEACH_COMPLETE should never be reset */
101#define PEER_LEARN_RESET ~(PEER_F_LEARN_ASSIGN|PEER_F_LEARN_NOTUP2DATE)
Emeric Brun2b920a12010-09-23 18:30:22 +0200102
Frédéric Lécaille54bff832019-03-26 10:25:20 +0100103#define PEER_RESYNC_TIMEOUT 5000 /* 5 seconds */
104#define PEER_RECONNECT_TIMEOUT 5000 /* 5 seconds */
Frédéric Lécaille645635d2019-02-11 17:49:39 +0100105#define PEER_HEARTBEAT_TIMEOUT 3000 /* 3 seconds */
106
Willy Tarreau49962b52021-02-12 16:56:22 +0100107/* flags for "show peers" */
108#define PEERS_SHOW_F_DICT 0x00000001 /* also show the contents of the dictionary */
109
Emeric Brunb3971ab2015-05-12 18:49:09 +0200110/*****************************/
111/* Sync message class */
112/*****************************/
113enum {
114 PEER_MSG_CLASS_CONTROL = 0,
115 PEER_MSG_CLASS_ERROR,
116 PEER_MSG_CLASS_STICKTABLE = 10,
117 PEER_MSG_CLASS_RESERVED = 255,
118};
119
120/*****************************/
121/* control message types */
122/*****************************/
123enum {
124 PEER_MSG_CTRL_RESYNCREQ = 0,
125 PEER_MSG_CTRL_RESYNCFINISHED,
126 PEER_MSG_CTRL_RESYNCPARTIAL,
127 PEER_MSG_CTRL_RESYNCCONFIRM,
Frédéric Lécaille645635d2019-02-11 17:49:39 +0100128 PEER_MSG_CTRL_HEARTBEAT,
Emeric Brunb3971ab2015-05-12 18:49:09 +0200129};
130
131/*****************************/
132/* error message types */
133/*****************************/
134enum {
135 PEER_MSG_ERR_PROTOCOL = 0,
136 PEER_MSG_ERR_SIZELIMIT,
137};
138
Emeric Brun530ba382020-06-02 11:17:42 +0200139/* network key types;
140 * network types were directly and mistakenly
141 * mapped on sample types, to keep backward
142 * compatiblitiy we keep those values but
143 * we now use a internal/network mapping
144 * to avoid further mistakes adding or
145 * modifying internals types
146 */
147enum {
148 PEER_KT_ANY = 0, /* any type */
149 PEER_KT_RESV1, /* UNUSED */
150 PEER_KT_SINT, /* signed 64bits integer type */
151 PEER_KT_RESV3, /* UNUSED */
152 PEER_KT_IPV4, /* ipv4 type */
153 PEER_KT_IPV6, /* ipv6 type */
154 PEER_KT_STR, /* char string type */
155 PEER_KT_BIN, /* buffer type */
156 PEER_KT_TYPES /* number of types, must always be last */
157};
158
159/* Map used to retrieve network type from internal type
160 * Note: Undeclared mapping maps entry to PEER_KT_ANY == 0
161 */
162static int peer_net_key_type[SMP_TYPES] = {
163 [SMP_T_SINT] = PEER_KT_SINT,
164 [SMP_T_IPV4] = PEER_KT_IPV4,
165 [SMP_T_IPV6] = PEER_KT_IPV6,
166 [SMP_T_STR] = PEER_KT_STR,
167 [SMP_T_BIN] = PEER_KT_BIN,
168};
169
170/* Map used to retrieve internal type from external type
171 * Note: Undeclared mapping maps entry to SMP_T_ANY == 0
172 */
173static int peer_int_key_type[PEER_KT_TYPES] = {
174 [PEER_KT_SINT] = SMP_T_SINT,
175 [PEER_KT_IPV4] = SMP_T_IPV4,
176 [PEER_KT_IPV6] = SMP_T_IPV6,
177 [PEER_KT_STR] = SMP_T_STR,
178 [PEER_KT_BIN] = SMP_T_BIN,
179};
180
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100181/*
182 * Parameters used by functions to build peer protocol messages. */
183struct peer_prep_params {
184 struct {
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100185 struct peer *peer;
186 } hello;
187 struct {
188 unsigned int st1;
189 } error_status;
190 struct {
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100191 struct stksess *stksess;
192 struct shared_table *shared_table;
193 unsigned int updateid;
194 int use_identifier;
195 int use_timed;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200196 struct peer *peer;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100197 } updt;
198 struct {
199 struct shared_table *shared_table;
200 } swtch;
201 struct {
202 struct shared_table *shared_table;
203 } ack;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100204 struct {
205 unsigned char head[2];
206 } control;
207 struct {
208 unsigned char head[2];
209 } error;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100210};
Emeric Brunb3971ab2015-05-12 18:49:09 +0200211
212/*******************************/
213/* stick table sync mesg types */
214/* Note: ids >= 128 contains */
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500215/* id message contains data */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200216/*******************************/
Olivier Houchard33992262018-10-16 18:49:26 +0200217#define PEER_MSG_STKT_UPDATE 0x80
218#define PEER_MSG_STKT_INCUPDATE 0x81
219#define PEER_MSG_STKT_DEFINE 0x82
220#define PEER_MSG_STKT_SWITCH 0x83
221#define PEER_MSG_STKT_ACK 0x84
222#define PEER_MSG_STKT_UPDATE_TIMED 0x85
223#define PEER_MSG_STKT_INCUPDATE_TIMED 0x86
Frédéric Lécaille36fb77e2019-06-04 08:28:19 +0200224/* All the stick-table message identifiers abova have the #7 bit set */
225#define PEER_MSG_STKT_BIT 7
226#define PEER_MSG_STKT_BIT_MASK (1 << PEER_MSG_STKT_BIT)
Emeric Brun2b920a12010-09-23 18:30:22 +0200227
Frédéric Lécaille39143342019-05-24 14:32:27 +0200228/* The maximum length of an encoded data length. */
229#define PEER_MSG_ENC_LENGTH_MAXLEN 5
230
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200231/* Minimum 64-bits value encoded with 2 bytes */
232#define PEER_ENC_2BYTES_MIN 0xf0 /* 0xf0 (or 240) */
233/* 3 bytes */
234#define PEER_ENC_3BYTES_MIN ((1ULL << 11) | PEER_ENC_2BYTES_MIN) /* 0x8f0 (or 2288) */
235/* 4 bytes */
236#define PEER_ENC_4BYTES_MIN ((1ULL << 18) | PEER_ENC_3BYTES_MIN) /* 0x408f0 (or 264432) */
237/* 5 bytes */
238#define PEER_ENC_5BYTES_MIN ((1ULL << 25) | PEER_ENC_4BYTES_MIN) /* 0x20408f0 (or 33818864) */
239/* 6 bytes */
240#define PEER_ENC_6BYTES_MIN ((1ULL << 32) | PEER_ENC_5BYTES_MIN) /* 0x1020408f0 (or 4328786160) */
241/* 7 bytes */
242#define PEER_ENC_7BYTES_MIN ((1ULL << 39) | PEER_ENC_6BYTES_MIN) /* 0x81020408f0 (or 554084600048) */
243/* 8 bytes */
244#define PEER_ENC_8BYTES_MIN ((1ULL << 46) | PEER_ENC_7BYTES_MIN) /* 0x4081020408f0 (or 70922828777712) */
245/* 9 bytes */
246#define PEER_ENC_9BYTES_MIN ((1ULL << 53) | PEER_ENC_8BYTES_MIN) /* 0x204081020408f0 (or 9078122083518704) */
247/* 10 bytes */
248#define PEER_ENC_10BYTES_MIN ((1ULL << 60) | PEER_ENC_9BYTES_MIN) /* 0x10204081020408f0 (or 1161999626690365680) */
249
250/* #7 bit used to detect the last byte to be encoded */
251#define PEER_ENC_STOP_BIT 7
252/* The byte minimum value with #7 bit set */
253#define PEER_ENC_STOP_BYTE (1 << PEER_ENC_STOP_BIT)
254/* The left most number of bits set for PEER_ENC_2BYTES_MIN */
255#define PEER_ENC_2BYTES_MIN_BITS 4
256
Frédéric Lécaille39143342019-05-24 14:32:27 +0200257#define PEER_MSG_HEADER_LEN 2
258
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200259#define PEER_STKT_CACHE_MAX_ENTRIES 128
260
Emeric Brun2b920a12010-09-23 18:30:22 +0200261/**********************************/
262/* Peer Session IO handler states */
263/**********************************/
264
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100265enum {
266 PEER_SESS_ST_ACCEPT = 0, /* Initial state for session create by an accept, must be zero! */
267 PEER_SESS_ST_GETVERSION, /* Validate supported protocol version */
268 PEER_SESS_ST_GETHOST, /* Validate host ID correspond to local host id */
269 PEER_SESS_ST_GETPEER, /* Validate peer ID correspond to a known remote peer id */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100270 /* after this point, data were possibly exchanged */
271 PEER_SESS_ST_SENDSUCCESS, /* Send ret code 200 (success) and wait for message */
272 PEER_SESS_ST_CONNECT, /* Initial state for session create on a connect, push presentation into buffer */
273 PEER_SESS_ST_GETSTATUS, /* Wait for the welcome message */
274 PEER_SESS_ST_WAITMSG, /* Wait for data messages */
275 PEER_SESS_ST_EXIT, /* Exit with status code */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200276 PEER_SESS_ST_ERRPROTO, /* Send error proto message before exit */
277 PEER_SESS_ST_ERRSIZE, /* Send error size message before exit */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100278 PEER_SESS_ST_END, /* Killed session */
279};
Emeric Brun2b920a12010-09-23 18:30:22 +0200280
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100281/***************************************************/
282/* Peer Session status code - part of the protocol */
283/***************************************************/
Emeric Brun2b920a12010-09-23 18:30:22 +0200284
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100285#define PEER_SESS_SC_CONNECTCODE 100 /* connect in progress */
286#define PEER_SESS_SC_CONNECTEDCODE 110 /* tcp connect success */
Emeric Brun2b920a12010-09-23 18:30:22 +0200287
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100288#define PEER_SESS_SC_SUCCESSCODE 200 /* accept or connect successful */
Emeric Brun2b920a12010-09-23 18:30:22 +0200289
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100290#define PEER_SESS_SC_TRYAGAIN 300 /* try again later */
Emeric Brun2b920a12010-09-23 18:30:22 +0200291
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100292#define PEER_SESS_SC_ERRPROTO 501 /* error protocol */
293#define PEER_SESS_SC_ERRVERSION 502 /* unknown protocol version */
294#define PEER_SESS_SC_ERRHOST 503 /* bad host name */
295#define PEER_SESS_SC_ERRPEER 504 /* unknown peer */
Emeric Brun2b920a12010-09-23 18:30:22 +0200296
297#define PEER_SESSION_PROTO_NAME "HAProxyS"
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200298#define PEER_MAJOR_VER 2
299#define PEER_MINOR_VER 1
300#define PEER_DWNGRD_MINOR_VER 0
Emeric Brun2b920a12010-09-23 18:30:22 +0200301
Willy Tarreau6254a922019-01-29 17:45:23 +0100302static size_t proto_len = sizeof(PEER_SESSION_PROTO_NAME) - 1;
Frédéric Lécailleed2b4a62017-07-13 09:07:09 +0200303struct peers *cfg_peers = NULL;
Emeric Brun9ef2ad72019-04-02 17:22:01 +0200304static void peer_session_forceshutdown(struct peer *peer);
Emeric Brun2b920a12010-09-23 18:30:22 +0200305
Frédéric Lécaille6c391982019-06-06 11:34:03 +0200306static struct ebpt_node *dcache_tx_insert(struct dcache *dc,
307 struct dcache_tx_entry *i);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200308static inline void flush_dcache(struct peer *peer);
309
Frédéric Lécailled8659352020-11-10 16:18:03 +0100310/* trace source and events */
311static void peers_trace(enum trace_level level, uint64_t mask,
312 const struct trace_source *src,
313 const struct ist where, const struct ist func,
314 const void *a1, const void *a2, const void *a3, const void *a4);
315
316static const struct trace_event peers_trace_events[] = {
317#define PEERS_EV_UPDTMSG (1 << 0)
318 { .mask = PEERS_EV_UPDTMSG, .name = "updtmsg", .desc = "update message received" },
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100319#define PEERS_EV_ACKMSG (1 << 1)
320 { .mask = PEERS_EV_ACKMSG, .name = "ackmsg", .desc = "ack message received" },
321#define PEERS_EV_SWTCMSG (1 << 2)
322 { .mask = PEERS_EV_SWTCMSG, .name = "swtcmsg", .desc = "switch message received" },
323#define PEERS_EV_DEFMSG (1 << 3)
324 { .mask = PEERS_EV_DEFMSG, .name = "defmsg", .desc = "definition message received" },
325#define PEERS_EV_CTRLMSG (1 << 4)
326 { .mask = PEERS_EV_CTRLMSG, .name = "ctrlmsg", .desc = "control message sent/received" },
327#define PEERS_EV_SESSREL (1 << 5)
328 { .mask = PEERS_EV_SESSREL, .name = "sessrl", .desc = "peer session releasing" },
329#define PEERS_EV_PROTOERR (1 << 6)
330 { .mask = PEERS_EV_PROTOERR, .name = "protoerr", .desc = "protocol error" },
Frédéric Lécailled8659352020-11-10 16:18:03 +0100331};
332
333static const struct name_desc peers_trace_lockon_args[4] = {
334 /* arg1 */ { /* already used by the connection */ },
335 /* arg2 */ { .name="peers", .desc="Peers protocol" },
336 /* arg3 */ { },
337 /* arg4 */ { }
338};
339
340static const struct name_desc peers_trace_decoding[] = {
341#define PEERS_VERB_CLEAN 1
342 { .name="clean", .desc="only user-friendly stuff, generally suitable for level \"user\"" },
343 { /* end */ }
344};
345
346
347struct trace_source trace_peers = {
348 .name = IST("peers"),
349 .desc = "Peers protocol",
350 .arg_def = TRC_ARG1_CONN, /* TRACE()'s first argument is always a connection */
351 .default_cb = peers_trace,
352 .known_events = peers_trace_events,
353 .lockon_args = peers_trace_lockon_args,
354 .decoding = peers_trace_decoding,
355 .report_events = ~0, /* report everything by default */
356};
357
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100358/* Return peer control message types as strings (only for debugging purpose). */
359static inline char *ctrl_msg_type_str(unsigned int type)
360{
361 switch (type) {
362 case PEER_MSG_CTRL_RESYNCREQ:
363 return "RESYNCREQ";
364 case PEER_MSG_CTRL_RESYNCFINISHED:
365 return "RESYNCFINISHED";
366 case PEER_MSG_CTRL_RESYNCPARTIAL:
367 return "RESYNCPARTIAL";
368 case PEER_MSG_CTRL_RESYNCCONFIRM:
369 return "RESYNCCONFIRM";
370 case PEER_MSG_CTRL_HEARTBEAT:
371 return "HEARTBEAT";
372 default:
373 return "???";
374 }
375}
376
Frédéric Lécailled8659352020-11-10 16:18:03 +0100377#define TRACE_SOURCE &trace_peers
378INITCALL1(STG_REGISTER, trace_register_source, TRACE_SOURCE);
379
380static void peers_trace(enum trace_level level, uint64_t mask,
381 const struct trace_source *src,
382 const struct ist where, const struct ist func,
383 const void *a1, const void *a2, const void *a3, const void *a4)
384{
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100385 if (mask & (PEERS_EV_UPDTMSG|PEERS_EV_ACKMSG|PEERS_EV_SWTCMSG)) {
Frédéric Lécailled8659352020-11-10 16:18:03 +0100386 if (a2) {
387 const struct peer *peer = a2;
388
389 chunk_appendf(&trace_buf, " peer=%s", peer->id);
390 }
391 if (a3) {
392 const char *p = a3;
393
394 chunk_appendf(&trace_buf, " @%p", p);
395 }
396 if (a4) {
397 const size_t *val = a4;
398
399 chunk_appendf(&trace_buf, " %llu", (unsigned long long)*val);
400 }
401 }
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100402
403 if (mask & PEERS_EV_DEFMSG) {
404 if (a2) {
405 const struct peer *peer = a2;
406
407 chunk_appendf(&trace_buf, " peer=%s", peer->id);
408 }
409 if (a3) {
410 const char *p = a3;
411
412 chunk_appendf(&trace_buf, " @%p", p);
413 }
414 if (a4) {
415 const int *val = a4;
416
417 chunk_appendf(&trace_buf, " %d", *val);
418 }
419 }
420
421 if (mask & PEERS_EV_CTRLMSG) {
422 if (a2) {
423 const unsigned char *ctrl_msg_type = a2;
424
425 chunk_appendf(&trace_buf, " %s", ctrl_msg_type_str(*ctrl_msg_type));
426
427 }
428 if (a3) {
429 const char *local_peer = a3;
430
431 chunk_appendf(&trace_buf, " %s", local_peer);
432 }
433
434 if (a4) {
435 const char *remote_peer = a4;
436
437 chunk_appendf(&trace_buf, " -> %s", remote_peer);
438 }
439 }
440
441 if (mask & (PEERS_EV_SESSREL|PEERS_EV_PROTOERR)) {
442 if (a2) {
443 const struct peer *peer = a2;
444 struct peers *peers = NULL;
445
Christopher Faulet387e7972022-05-12 14:47:52 +0200446 if (peer->appctx)
447 peers = peer->peers;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100448
449 if (peers)
450 chunk_appendf(&trace_buf, " %s", peers->local->id);
Christopher Fauletd9e6b352021-11-15 09:40:57 +0100451 chunk_appendf(&trace_buf, " -> %s", peer->id);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +0100452 }
453
454 if (a3) {
455 const int *prev_state = a3;
456
457 chunk_appendf(&trace_buf, " prev_state=%d\n", *prev_state);
458 }
459 }
Frédéric Lécailled8659352020-11-10 16:18:03 +0100460}
461
Frédéric Lécaille95679dc2019-04-15 10:25:27 +0200462static const char *statuscode_str(int statuscode)
463{
464 switch (statuscode) {
465 case PEER_SESS_SC_CONNECTCODE:
466 return "CONN";
467 case PEER_SESS_SC_CONNECTEDCODE:
468 return "HSHK";
469 case PEER_SESS_SC_SUCCESSCODE:
470 return "ESTA";
471 case PEER_SESS_SC_TRYAGAIN:
472 return "RETR";
473 case PEER_SESS_SC_ERRPROTO:
474 return "PROT";
475 case PEER_SESS_SC_ERRVERSION:
476 return "VERS";
477 case PEER_SESS_SC_ERRHOST:
478 return "NAME";
479 case PEER_SESS_SC_ERRPEER:
480 return "UNKN";
481 default:
482 return "NONE";
483 }
484}
485
Emeric Brun18928af2017-03-29 16:32:53 +0200486/* This function encode an uint64 to 'dynamic' length format.
487 The encoded value is written at address *str, and the
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500488 caller must assure that size after *str is large enough.
Emeric Brun18928af2017-03-29 16:32:53 +0200489 At return, the *str is set at the next Byte after then
490 encoded integer. The function returns then length of the
491 encoded integer in Bytes */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200492int intencode(uint64_t i, char **str) {
493 int idx = 0;
494 unsigned char *msg;
495
Emeric Brunb3971ab2015-05-12 18:49:09 +0200496 msg = (unsigned char *)*str;
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200497 if (i < PEER_ENC_2BYTES_MIN) {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200498 msg[0] = (unsigned char)i;
499 *str = (char *)&msg[idx+1];
500 return (idx+1);
501 }
502
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200503 msg[idx] =(unsigned char)i | PEER_ENC_2BYTES_MIN;
504 i = (i - PEER_ENC_2BYTES_MIN) >> PEER_ENC_2BYTES_MIN_BITS;
505 while (i >= PEER_ENC_STOP_BYTE) {
506 msg[++idx] = (unsigned char)i | PEER_ENC_STOP_BYTE;
507 i = (i - PEER_ENC_STOP_BYTE) >> PEER_ENC_STOP_BIT;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200508 }
509 msg[++idx] = (unsigned char)i;
510 *str = (char *)&msg[idx+1];
511 return (idx+1);
512}
513
514
Emeric Brun5ea07d92021-06-30 13:21:58 +0200515/* This function returns a decoded 64bits unsigned integer
516 * from a varint
517 *
518 * Calling:
519 * - *str must point on the first byte of the buffer to decode.
520 * - end must point on the next byte after the end of the buffer
521 * we are authorized to parse (buf + buflen)
522 *
523 * At return:
524 *
525 * On success *str will point at the byte following
526 * the fully decoded integer into the buffer. and
527 * the decoded value is returned.
528 *
529 * If end is reached before the integer was fully decoded,
530 * *str is set to NULL and the caller have to check this
531 * to know there is a decoding error. In this case
532 * the returned integer is also forced to 0
533 */
Emeric Brun18928af2017-03-29 16:32:53 +0200534uint64_t intdecode(char **str, char *end)
535{
Emeric Brunb3971ab2015-05-12 18:49:09 +0200536 unsigned char *msg;
Emeric Brun18928af2017-03-29 16:32:53 +0200537 uint64_t i;
538 int shift;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200539
540 if (!*str)
541 return 0;
542
543 msg = (unsigned char *)*str;
Emeric Brun18928af2017-03-29 16:32:53 +0200544 if (msg >= (unsigned char *)end)
545 goto fail;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200546
Emeric Brun18928af2017-03-29 16:32:53 +0200547 i = *(msg++);
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200548 if (i >= PEER_ENC_2BYTES_MIN) {
549 shift = PEER_ENC_2BYTES_MIN_BITS;
Emeric Brun18928af2017-03-29 16:32:53 +0200550 do {
551 if (msg >= (unsigned char *)end)
552 goto fail;
553 i += (uint64_t)*msg << shift;
Frédéric Lécaille32b55732019-06-03 18:29:51 +0200554 shift += PEER_ENC_STOP_BIT;
555 } while (*(msg++) >= PEER_ENC_STOP_BYTE);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200556 }
Frédéric Lécaillea8725ec2019-01-22 10:31:39 +0100557 *str = (char *)msg;
558 return i;
Emeric Brun18928af2017-03-29 16:32:53 +0200559
Frédéric Lécaillea8725ec2019-01-22 10:31:39 +0100560 fail:
561 *str = NULL;
562 return 0;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200563}
Emeric Brun2b920a12010-09-23 18:30:22 +0200564
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100565/*
566 * Build a "hello" peer protocol message.
567 * Return the number of written bytes written to build this messages if succeeded,
568 * 0 if not.
569 */
570static int peer_prepare_hellomsg(char *msg, size_t size, struct peer_prep_params *p)
571{
572 int min_ver, ret;
573 struct peer *peer;
574
575 peer = p->hello.peer;
576 min_ver = (peer->flags & PEER_F_DWNGRD) ? PEER_DWNGRD_MINOR_VER : PEER_MINOR_VER;
577 /* Prepare headers */
Willy Tarreau2645b342022-04-12 08:28:18 +0200578 ret = snprintf(msg, size, PEER_SESSION_PROTO_NAME " %d.%d\n%s\n%s %d %d\n",
579 (int)PEER_MAJOR_VER, min_ver, peer->id, localpeer, (int)getpid(), (int)1);
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100580 if (ret >= size)
581 return 0;
582
583 return ret;
584}
585
586/*
587 * Build a "handshake succeeded" status message.
588 * Return the number of written bytes written to build this messages if succeeded,
589 * 0 if not.
590 */
591static int peer_prepare_status_successmsg(char *msg, size_t size, struct peer_prep_params *p)
592{
593 int ret;
594
Willy Tarreau2645b342022-04-12 08:28:18 +0200595 ret = snprintf(msg, size, "%d\n", (int)PEER_SESS_SC_SUCCESSCODE);
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100596 if (ret >= size)
597 return 0;
598
599 return ret;
600}
601
602/*
603 * Build an error status message.
604 * Return the number of written bytes written to build this messages if succeeded,
605 * 0 if not.
606 */
607static int peer_prepare_status_errormsg(char *msg, size_t size, struct peer_prep_params *p)
608{
609 int ret;
610 unsigned int st1;
611
612 st1 = p->error_status.st1;
613 ret = snprintf(msg, size, "%d\n", st1);
614 if (ret >= size)
615 return 0;
616
617 return ret;
618}
619
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200620/* Set the stick-table UPDATE message type byte at <msg_type> address,
621 * depending on <use_identifier> and <use_timed> boolean parameters.
622 * Always successful.
623 */
624static inline void peer_set_update_msg_type(char *msg_type, int use_identifier, int use_timed)
625{
626 if (use_timed) {
627 if (use_identifier)
628 *msg_type = PEER_MSG_STKT_UPDATE_TIMED;
629 else
630 *msg_type = PEER_MSG_STKT_INCUPDATE_TIMED;
631 }
632 else {
633 if (use_identifier)
634 *msg_type = PEER_MSG_STKT_UPDATE;
635 else
636 *msg_type = PEER_MSG_STKT_INCUPDATE;
637 }
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200638}
Emeric Brun2b920a12010-09-23 18:30:22 +0200639/*
Emeric Brunb3971ab2015-05-12 18:49:09 +0200640 * This prepare the data update message on the stick session <ts>, <st> is the considered
641 * stick table.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800642 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200643 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
644 * check size)
Emeric Brun2b920a12010-09-23 18:30:22 +0200645 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100646static int peer_prepare_updatemsg(char *msg, size_t size, struct peer_prep_params *p)
Emeric Brun2b920a12010-09-23 18:30:22 +0200647{
648 uint32_t netinteger;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200649 unsigned short datalen;
650 char *cursor, *datamsg;
Emeric Brun94900952015-06-11 18:25:54 +0200651 unsigned int data_type;
652 void *data_ptr;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100653 struct stksess *ts;
654 struct shared_table *st;
655 unsigned int updateid;
656 int use_identifier;
657 int use_timed;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200658 struct peer *peer;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100659
660 ts = p->updt.stksess;
661 st = p->updt.shared_table;
662 updateid = p->updt.updateid;
663 use_identifier = p->updt.use_identifier;
664 use_timed = p->updt.use_timed;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200665 peer = p->updt.peer;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200666
Frédéric Lécaille0e8db972019-05-24 14:34:34 +0200667 cursor = datamsg = msg + PEER_MSG_HEADER_LEN + PEER_MSG_ENC_LENGTH_MAXLEN;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200668
Emeric Brun2b920a12010-09-23 18:30:22 +0200669 /* construct message */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200670
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500671 /* check if we need to send the update identifier */
Emeric Brun819fc6f2017-06-13 19:37:32 +0200672 if (!st->last_pushed || updateid < st->last_pushed || ((updateid - st->last_pushed) != 1)) {
Emeric Bruna6a09982015-09-22 15:34:19 +0200673 use_identifier = 1;
Emeric Brun2b920a12010-09-23 18:30:22 +0200674 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200675
676 /* encode update identifier if needed */
677 if (use_identifier) {
Emeric Brun819fc6f2017-06-13 19:37:32 +0200678 netinteger = htonl(updateid);
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
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200683 if (use_timed) {
684 netinteger = htonl(tick_remain(now_ms, ts->expire));
685 memcpy(cursor, &netinteger, sizeof(netinteger));
686 cursor += sizeof(netinteger);
687 }
688
Emeric Brunb3971ab2015-05-12 18:49:09 +0200689 /* encode the key */
Thierry FOURNIER5d24ebc2015-07-24 08:46:42 +0200690 if (st->table->type == SMP_T_STR) {
Emeric Brun2b920a12010-09-23 18:30:22 +0200691 int stlen = strlen((char *)ts->key.key);
692
Emeric Brunb3971ab2015-05-12 18:49:09 +0200693 intencode(stlen, &cursor);
694 memcpy(cursor, ts->key.key, stlen);
695 cursor += stlen;
Emeric Brun2b920a12010-09-23 18:30:22 +0200696 }
Thierry FOURNIER5d24ebc2015-07-24 08:46:42 +0200697 else if (st->table->type == SMP_T_SINT) {
Willy Tarreau6cde5d82020-02-25 09:41:22 +0100698 netinteger = htonl(read_u32(ts->key.key));
Emeric Brunb3971ab2015-05-12 18:49:09 +0200699 memcpy(cursor, &netinteger, sizeof(netinteger));
700 cursor += sizeof(netinteger);
Emeric Brun2b920a12010-09-23 18:30:22 +0200701 }
702 else {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200703 memcpy(cursor, ts->key.key, st->table->key_size);
704 cursor += st->table->key_size;
Emeric Brun2b920a12010-09-23 18:30:22 +0200705 }
706
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100707 HA_RWLOCK_RDLOCK(STK_SESS_LOCK, &ts->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200708 /* encode values */
Emeric Brun94900952015-06-11 18:25:54 +0200709 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200710
Emeric Brun94900952015-06-11 18:25:54 +0200711 data_ptr = stktable_data_ptr(st->table, ts, data_type);
712 if (data_ptr) {
Emeric Brun90a9b672021-06-22 16:09:55 +0200713 /* in case of array all elements use
714 * the same std_type and they are linearly
715 * encoded.
716 */
717 if (stktable_data_types[data_type].is_array) {
718 unsigned int idx = 0;
719
720 switch (stktable_data_types[data_type].std_type) {
721 case STD_T_SINT: {
722 int data;
723
724 do {
725 data = stktable_data_cast(data_ptr, std_t_sint);
726 intencode(data, &cursor);
727
728 data_ptr = stktable_data_ptr_idx(st->table, ts, data_type, ++idx);
729 } while(data_ptr);
730 break;
731 }
732 case STD_T_UINT: {
733 unsigned int data;
734
735 do {
736 data = stktable_data_cast(data_ptr, std_t_uint);
737 intencode(data, &cursor);
738
739 data_ptr = stktable_data_ptr_idx(st->table, ts, data_type, ++idx);
740 } while(data_ptr);
741 break;
742 }
743 case STD_T_ULL: {
744 unsigned long long data;
745
746 do {
747 data = stktable_data_cast(data_ptr, std_t_ull);
748 intencode(data, &cursor);
749
750 data_ptr = stktable_data_ptr_idx(st->table, ts, data_type, ++idx);
751 } while(data_ptr);
752 break;
753 }
754 case STD_T_FRQP: {
755 struct freq_ctr *frqp;
756
757 do {
758 frqp = &stktable_data_cast(data_ptr, std_t_frqp);
759 intencode((unsigned int)(now_ms - frqp->curr_tick), &cursor);
760 intencode(frqp->curr_ctr, &cursor);
761 intencode(frqp->prev_ctr, &cursor);
762
763 data_ptr = stktable_data_ptr_idx(st->table, ts, data_type, ++idx);
764 } while(data_ptr);
765 break;
766 }
767 }
768
769 /* array elements fully encoded
770 * proceed next data_type.
771 */
772 continue;
773 }
Emeric Brun94900952015-06-11 18:25:54 +0200774 switch (stktable_data_types[data_type].std_type) {
775 case STD_T_SINT: {
776 int data;
777
778 data = stktable_data_cast(data_ptr, std_t_sint);
779 intencode(data, &cursor);
780 break;
781 }
782 case STD_T_UINT: {
783 unsigned int data;
784
785 data = stktable_data_cast(data_ptr, std_t_uint);
786 intencode(data, &cursor);
787 break;
788 }
789 case STD_T_ULL: {
790 unsigned long long data;
791
792 data = stktable_data_cast(data_ptr, std_t_ull);
793 intencode(data, &cursor);
794 break;
795 }
796 case STD_T_FRQP: {
Willy Tarreaufa1258f2021-04-10 23:00:53 +0200797 struct freq_ctr *frqp;
Emeric Brun94900952015-06-11 18:25:54 +0200798
799 frqp = &stktable_data_cast(data_ptr, std_t_frqp);
800 intencode((unsigned int)(now_ms - frqp->curr_tick), &cursor);
801 intencode(frqp->curr_ctr, &cursor);
802 intencode(frqp->prev_ctr, &cursor);
803 break;
804 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200805 case STD_T_DICT: {
806 struct dict_entry *de;
Frédéric Lécaille6c391982019-06-06 11:34:03 +0200807 struct ebpt_node *cached_de;
Willy Tarreau237f8ae2019-06-06 16:40:43 +0200808 struct dcache_tx_entry cde = { };
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200809 char *beg, *end;
810 size_t value_len, data_len;
811 struct dcache *dc;
812
813 de = stktable_data_cast(data_ptr, std_t_dict);
Frédéric Lécailleaf9990f2019-11-13 17:50:34 +0100814 if (!de) {
815 /* No entry */
816 intencode(0, &cursor);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200817 break;
Frédéric Lécailleaf9990f2019-11-13 17:50:34 +0100818 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200819
820 dc = peer->dcache;
Frédéric Lécaille6c391982019-06-06 11:34:03 +0200821 cde.entry.key = de;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200822 cached_de = dcache_tx_insert(dc, &cde);
Frédéric Lécaillefd827932019-06-07 10:34:04 +0200823 if (cached_de == &cde.entry) {
824 if (cde.id + 1 >= PEER_ENC_2BYTES_MIN)
825 break;
826 /* Encode the length of the remaining data -> 1 */
827 intencode(1, &cursor);
828 /* Encode the cache entry ID */
829 intencode(cde.id + 1, &cursor);
830 }
831 else {
832 /* Leave enough room to encode the remaining data length. */
833 end = beg = cursor + PEER_MSG_ENC_LENGTH_MAXLEN;
834 /* Encode the dictionary entry key */
835 intencode(cde.id + 1, &end);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200836 /* Encode the length of the dictionary entry data */
Frédéric Lécaillefd827932019-06-07 10:34:04 +0200837 value_len = de->len;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200838 intencode(value_len, &end);
839 /* Copy the data */
840 memcpy(end, de->value.key, value_len);
841 end += value_len;
Frédéric Lécaillefd827932019-06-07 10:34:04 +0200842 /* Encode the length of the data */
843 data_len = end - beg;
844 intencode(data_len, &cursor);
845 memmove(cursor, beg, data_len);
846 cursor += data_len;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200847 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +0200848 break;
849 }
Emeric Brun94900952015-06-11 18:25:54 +0200850 }
851 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200852 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100853 HA_RWLOCK_RDUNLOCK(STK_SESS_LOCK, &ts->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200854
855 /* Compute datalen */
856 datalen = (cursor - datamsg);
857
858 /* prepare message header */
859 msg[0] = PEER_MSG_CLASS_STICKTABLE;
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200860 peer_set_update_msg_type(&msg[1], use_identifier, use_timed);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200861 cursor = &msg[2];
862 intencode(datalen, &cursor);
863
864 /* move data after header */
865 memmove(cursor, datamsg, datalen);
866
867 /* return header size + data_len */
868 return (cursor - msg) + datalen;
869}
870
871/*
872 * This prepare the switch table message to targeted share table <st>.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800873 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200874 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
875 * check size)
876 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100877static int peer_prepare_switchmsg(char *msg, size_t size, struct peer_prep_params *params)
Emeric Brunb3971ab2015-05-12 18:49:09 +0200878{
879 int len;
880 unsigned short datalen;
Willy Tarreau83061a82018-07-13 11:56:34 +0200881 struct buffer *chunk;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200882 char *cursor, *datamsg, *chunkp, *chunkq;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200883 uint64_t data = 0;
Emeric Brun94900952015-06-11 18:25:54 +0200884 unsigned int data_type;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100885 struct shared_table *st;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200886
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100887 st = params->swtch.shared_table;
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
890 /* Encode data */
891
892 /* encode local id */
893 intencode(st->local_id, &cursor);
894
895 /* encode table name */
Frédéric Lécaille7fcc24d2019-03-20 15:09:45 +0100896 len = strlen(st->table->nid);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200897 intencode(len, &cursor);
Frédéric Lécaille7fcc24d2019-03-20 15:09:45 +0100898 memcpy(cursor, st->table->nid, len);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200899 cursor += len;
900
901 /* encode table type */
902
Emeric Brun530ba382020-06-02 11:17:42 +0200903 intencode(peer_net_key_type[st->table->type], &cursor);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200904
905 /* encode table key size */
906 intencode(st->table->key_size, &cursor);
907
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200908 chunk = get_trash_chunk();
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200909 chunkp = chunkq = chunk->area;
Emeric Brun94900952015-06-11 18:25:54 +0200910 /* encode available known data types in table */
911 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
912 if (st->table->data_ofs[data_type]) {
Emeric Brun90a9b672021-06-22 16:09:55 +0200913 /* stored data types parameters are all linearly encoded
914 * at the end of the 'table definition' message.
915 *
916 * Currently only array data_types and and data_types
917 * using freq_counter base type have parameters:
918 *
919 * - array has always at least one parameter set to the
920 * number of elements.
921 *
922 * - array of base-type freq_counters has an additional
923 * parameter set to the period used to compute those
924 * freq_counters.
925 *
926 * - simple freq counter has a parameter set to the period
927 * used to compute
928 *
929 * A set of parameter for a datatype MUST BE prefixed
930 * by the data-type id itself:
931 * This is useless because the data_types are ordered and
932 * the data_type bitfield already gives the information of
933 * stored types, but it was designed this way when the
934 * push of period parameter was added for freq counters
935 * and we don't want to break the compatibility.
936 *
937 */
938 if (stktable_data_types[data_type].is_array) {
939 /* This is an array type so we first encode
940 * the data_type itself to prefix parameters
941 */
942 intencode(data_type, &chunkq);
943
944 /* We encode the first parameter which is
945 * the number of elements of this array
946 */
947 intencode(st->table->data_nbelem[data_type], &chunkq);
948
Ilya Shipitsin01881082021-08-07 14:41:56 +0500949 /* for array of freq counters, there is an additional
Emeric Brun90a9b672021-06-22 16:09:55 +0200950 * period parameter to encode
951 */
952 if (stktable_data_types[data_type].std_type == STD_T_FRQP)
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200953 intencode(st->table->data_arg[data_type].u, &chunkq);
Emeric Brun94900952015-06-11 18:25:54 +0200954 }
Emeric Brun90a9b672021-06-22 16:09:55 +0200955 else if (stktable_data_types[data_type].std_type == STD_T_FRQP) {
956 /* this datatype is a simple freq counter not part
957 * of an array. We encode the data_type itself
958 * to prefix the 'period' parameter
959 */
960 intencode(data_type, &chunkq);
961 intencode(st->table->data_arg[data_type].u, &chunkq);
962 }
963 /* set the bit corresponding to stored data type */
964 data |= 1ULL << data_type;
Emeric Brun94900952015-06-11 18:25:54 +0200965 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200966 }
967 intencode(data, &cursor);
968
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200969 /* Encode stick-table entries duration. */
970 intencode(st->table->expire, &cursor);
971
972 if (chunkq > chunkp) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200973 chunk->data = chunkq - chunkp;
974 memcpy(cursor, chunk->area, chunk->data);
975 cursor += chunk->data;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200976 }
977
Emeric Brunb3971ab2015-05-12 18:49:09 +0200978 /* Compute datalen */
979 datalen = (cursor - datamsg);
Emeric Brun2b920a12010-09-23 18:30:22 +0200980
Emeric Brunb3971ab2015-05-12 18:49:09 +0200981 /* prepare message header */
982 msg[0] = PEER_MSG_CLASS_STICKTABLE;
983 msg[1] = PEER_MSG_STKT_DEFINE;
984 cursor = &msg[2];
985 intencode(datalen, &cursor);
Emeric Brun2b920a12010-09-23 18:30:22 +0200986
Emeric Brunb3971ab2015-05-12 18:49:09 +0200987 /* move data after header */
988 memmove(cursor, datamsg, datalen);
989
990 /* return header size + data_len */
991 return (cursor - msg) + datalen;
Emeric Brun2b920a12010-09-23 18:30:22 +0200992}
993
Emeric Brunb3971ab2015-05-12 18:49:09 +0200994/*
995 * This prepare the acknowledge message on the stick session <ts>, <st> is the considered
996 * stick table.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800997 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200998 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
999 * check size)
1000 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001001static int peer_prepare_ackmsg(char *msg, size_t size, struct peer_prep_params *p)
Emeric Brunb3971ab2015-05-12 18:49:09 +02001002{
1003 unsigned short datalen;
1004 char *cursor, *datamsg;
1005 uint32_t netinteger;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001006 struct shared_table *st;
Emeric Brunb3971ab2015-05-12 18:49:09 +02001007
Frédéric Lécaille39143342019-05-24 14:32:27 +02001008 cursor = datamsg = msg + PEER_MSG_HEADER_LEN + PEER_MSG_ENC_LENGTH_MAXLEN;
Emeric Brunb3971ab2015-05-12 18:49:09 +02001009
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001010 st = p->ack.shared_table;
Emeric Brunb3971ab2015-05-12 18:49:09 +02001011 intencode(st->remote_id, &cursor);
1012 netinteger = htonl(st->last_get);
1013 memcpy(cursor, &netinteger, sizeof(netinteger));
1014 cursor += sizeof(netinteger);
1015
1016 /* Compute datalen */
1017 datalen = (cursor - datamsg);
1018
1019 /* prepare message header */
1020 msg[0] = PEER_MSG_CLASS_STICKTABLE;
Emeric Brune1ab8082015-08-21 11:48:54 +02001021 msg[1] = PEER_MSG_STKT_ACK;
Emeric Brunb3971ab2015-05-12 18:49:09 +02001022 cursor = &msg[2];
1023 intencode(datalen, &cursor);
1024
1025 /* move data after header */
1026 memmove(cursor, datamsg, datalen);
1027
1028 /* return header size + data_len */
1029 return (cursor - msg) + datalen;
1030}
Emeric Brun2b920a12010-09-23 18:30:22 +02001031
1032/*
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001033 * Function to deinit connected peer
1034 */
1035void __peer_session_deinit(struct peer *peer)
1036{
Christopher Faulet387e7972022-05-12 14:47:52 +02001037 struct peers *peers = peer->peers;
Maciej Zdebd01be2a2022-05-16 17:26:20 +02001038 int thr;
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001039
Christopher Faulet387e7972022-05-12 14:47:52 +02001040 if (!peers || !peer->appctx)
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001041 return;
1042
Maciej Zdebd01be2a2022-05-16 17:26:20 +02001043 thr = my_ffsl(peer->appctx->t->thread_mask) - 1;
1044 HA_ATOMIC_DEC(&peers->applet_count[thr]);
1045
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001046 if (peer->appctx->st0 == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +02001047 HA_ATOMIC_DEC(&connected_peers);
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001048
Willy Tarreau4781b152021-04-06 13:53:36 +02001049 HA_ATOMIC_DEC(&active_peers);
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001050
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001051 flush_dcache(peer);
1052
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001053 /* Re-init current table pointers to force announcement on re-connect */
1054 peer->remote_table = peer->last_local_table = NULL;
1055 peer->appctx = NULL;
1056 if (peer->flags & PEER_F_LEARN_ASSIGN) {
1057 /* unassign current peer for learning */
1058 peer->flags &= ~(PEER_F_LEARN_ASSIGN);
1059 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
1060
Emeric Brunccdfbae2021-04-28 12:59:35 +02001061 if (peer->local)
1062 peers->flags |= PEERS_F_RESYNC_LOCALABORT;
1063 else
1064 peers->flags |= PEERS_F_RESYNC_REMOTEABORT;
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001065 /* reschedule a resync */
1066 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
1067 }
1068 /* reset teaching and learning flags to 0 */
1069 peer->flags &= PEER_TEACH_RESET;
1070 peer->flags &= PEER_LEARN_RESET;
1071 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
1072}
1073
Christopher Faulet6712dc62022-05-12 15:36:11 +02001074static int peer_session_init(struct appctx *appctx)
1075{
1076 struct peer *peer = appctx->svcctx;
1077 struct stream *s;
1078 struct sockaddr_storage *addr = NULL;
1079
1080 if (!sockaddr_alloc(&addr, &peer->addr, sizeof(peer->addr)))
1081 goto out_error;
1082
1083 if (appctx_finalize_startup(appctx, peer->peers->peers_fe, &BUF_NULL) == -1)
1084 goto out_free_addr;
1085
1086 s = appctx_strm(appctx);
1087 /* applet is waiting for data */
Willy Tarreau90e8b452022-05-25 18:21:43 +02001088 applet_need_more_data(appctx);
Christopher Faulet6712dc62022-05-12 15:36:11 +02001089 appctx_wakeup(appctx);
1090
1091 /* initiate an outgoing connection */
Willy Tarreau7cb9e6c2022-05-17 19:40:40 +02001092 s->scb->dst = addr;
Willy Tarreaucb041662022-05-17 19:44:42 +02001093 s->scb->flags |= SC_FL_NOLINGER;
Christopher Faulet6712dc62022-05-12 15:36:11 +02001094 s->flags = SF_ASSIGNED;
1095 s->target = peer_session_target(peer, s);
1096
1097 s->do_log = NULL;
1098 s->uniq_id = 0;
1099
1100 s->res.flags |= CF_READ_DONTWAIT;
1101
1102 _HA_ATOMIC_INC(&active_peers);
1103 return 0;
1104
1105 out_free_addr:
1106 sockaddr_free(&addr);
1107 out_error:
1108 return -1;
1109}
1110
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001111/*
Emeric Brun2b920a12010-09-23 18:30:22 +02001112 * Callback to release a session with a peer
1113 */
Willy Tarreau00a37f02015-04-13 12:05:19 +02001114static void peer_session_release(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +02001115{
Willy Tarreau455caef2022-05-05 20:16:16 +02001116 struct peer *peer = appctx->svcctx;
Emeric Brun2b920a12010-09-23 18:30:22 +02001117
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001118 TRACE_PROTO("releasing peer session", PEERS_EV_SESSREL, NULL, peer);
Willy Tarreau455caef2022-05-05 20:16:16 +02001119 /* appctx->svcctx is not a peer session */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001120 if (appctx->st0 < PEER_SESS_ST_SENDSUCCESS)
Emeric Brun2b920a12010-09-23 18:30:22 +02001121 return;
1122
1123 /* peer session identified */
Emeric Brunb3971ab2015-05-12 18:49:09 +02001124 if (peer) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001125 HA_SPIN_LOCK(PEER_LOCK, &peer->lock);
Emeric Brun9ef2ad72019-04-02 17:22:01 +02001126 if (peer->appctx == appctx)
1127 __peer_session_deinit(peer);
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02001128 peer->flags &= ~PEER_F_ALIVE;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001129 HA_SPIN_UNLOCK(PEER_LOCK, &peer->lock);
Emeric Brun2b920a12010-09-23 18:30:22 +02001130 }
1131}
1132
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02001133/* Retrieve the major and minor versions of peers protocol
1134 * announced by a remote peer. <str> is a null-terminated
1135 * string with the following format: "<maj_ver>.<min_ver>".
1136 */
1137static int peer_get_version(const char *str,
1138 unsigned int *maj_ver, unsigned int *min_ver)
1139{
1140 unsigned int majv, minv;
1141 const char *pos, *saved;
1142 const char *end;
1143
1144 saved = pos = str;
1145 end = str + strlen(str);
1146
1147 majv = read_uint(&pos, end);
1148 if (saved == pos || *pos++ != '.')
1149 return -1;
1150
1151 saved = pos;
1152 minv = read_uint(&pos, end);
1153 if (saved == pos || pos != end)
1154 return -1;
1155
1156 *maj_ver = majv;
1157 *min_ver = minv;
1158
1159 return 0;
1160}
Emeric Brun2b920a12010-09-23 18:30:22 +02001161
1162/*
Frédéric Lécaillece025572019-01-21 13:38:06 +01001163 * Parse a line terminated by an optional '\r' character, followed by a mandatory
1164 * '\n' character.
1165 * Returns 1 if succeeded or 0 if a '\n' character could not be found, and -1 if
1166 * a line could not be read because the communication channel is closed.
1167 */
1168static inline int peer_getline(struct appctx *appctx)
1169{
Willy Tarreau4596fe22022-05-17 19:07:51 +02001170 struct stconn *cs = appctx_cs(appctx);
Frédéric Lécaillece025572019-01-21 13:38:06 +01001171 int n;
Frédéric Lécaillece025572019-01-21 13:38:06 +01001172
Willy Tarreau40a9c322022-05-18 15:55:18 +02001173 n = co_getline(sc_oc(cs), trash.area, trash.size);
Frédéric Lécaillece025572019-01-21 13:38:06 +01001174 if (!n)
1175 return 0;
1176
1177 if (n < 0 || trash.area[n - 1] != '\n') {
1178 appctx->st0 = PEER_SESS_ST_END;
1179 return -1;
1180 }
1181
1182 if (n > 1 && (trash.area[n - 2] == '\r'))
1183 trash.area[n - 2] = 0;
1184 else
1185 trash.area[n - 1] = 0;
1186
Willy Tarreau40a9c322022-05-18 15:55:18 +02001187 co_skip(sc_oc(cs), n);
Frédéric Lécaillece025572019-01-21 13:38:06 +01001188
1189 return n;
1190}
1191
1192/*
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001193 * Send a message after having called <peer_prepare_msg> to build it.
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 appcxt st0
1197 * returned value equal to PEER_SESS_ST_END.
1198 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001199static inline int peer_send_msg(struct appctx *appctx,
1200 int (*peer_prepare_msg)(char *, size_t, struct peer_prep_params *),
1201 struct peer_prep_params *params)
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001202{
1203 int ret, msglen;
Willy Tarreau4596fe22022-05-17 19:07:51 +02001204 struct stconn *cs = appctx_cs(appctx);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001205
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001206 msglen = peer_prepare_msg(trash.area, trash.size, params);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001207 if (!msglen) {
1208 /* internal error: message does not fit in trash */
1209 appctx->st0 = PEER_SESS_ST_END;
1210 return 0;
1211 }
1212
1213 /* message to buffer */
Willy Tarreau40a9c322022-05-18 15:55:18 +02001214 ret = ci_putblk(sc_ic(cs), trash.area, msglen);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001215 if (ret <= 0) {
1216 if (ret == -1) {
1217 /* No more write possible */
Willy Tarreau99615ed2022-05-25 07:29:36 +02001218 sc_need_room(cs);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001219 return -1;
1220 }
1221 appctx->st0 = PEER_SESS_ST_END;
1222 }
1223
1224 return ret;
1225}
1226
1227/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001228 * Send a hello message.
1229 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1230 * Returns -1 if there was not enough room left to send the message,
1231 * any other negative returned value must be considered as an error with an appcxt st0
1232 * returned value equal to PEER_SESS_ST_END.
1233 */
1234static inline int peer_send_hellomsg(struct appctx *appctx, struct peer *peer)
1235{
1236 struct peer_prep_params p = {
1237 .hello.peer = peer,
1238 };
1239
1240 return peer_send_msg(appctx, peer_prepare_hellomsg, &p);
1241}
1242
1243/*
1244 * Send a success peer handshake status message.
1245 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1246 * Returns -1 if there was not enough room left to send the message,
1247 * any other negative returned value must be considered as an error with an appcxt st0
1248 * returned value equal to PEER_SESS_ST_END.
1249 */
1250static inline int peer_send_status_successmsg(struct appctx *appctx)
1251{
1252 return peer_send_msg(appctx, peer_prepare_status_successmsg, NULL);
1253}
1254
1255/*
1256 * Send a peer handshake status error message.
1257 * Return 0 if the message could not be built modifying the appcxt 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 appcxt st0
1260 * returned value equal to PEER_SESS_ST_END.
1261 */
1262static inline int peer_send_status_errormsg(struct appctx *appctx)
1263{
1264 struct peer_prep_params p = {
1265 .error_status.st1 = appctx->st1,
1266 };
1267
1268 return peer_send_msg(appctx, peer_prepare_status_errormsg, &p);
1269}
1270
1271/*
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001272 * Send a stick-table switch message.
1273 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1274 * Returns -1 if there was not enough room left to send the message,
1275 * any other negative returned value must be considered as an error with an appcxt st0
1276 * returned value equal to PEER_SESS_ST_END.
1277 */
1278static inline int peer_send_switchmsg(struct shared_table *st, struct appctx *appctx)
1279{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001280 struct peer_prep_params p = {
1281 .swtch.shared_table = st,
1282 };
1283
1284 return peer_send_msg(appctx, peer_prepare_switchmsg, &p);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001285}
1286
1287/*
1288 * Send a stick-table update acknowledgement message.
1289 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1290 * Returns -1 if there was not enough room left to send the message,
1291 * any other negative returned value must be considered as an error with an appcxt st0
1292 * returned value equal to PEER_SESS_ST_END.
1293 */
1294static inline int peer_send_ackmsg(struct shared_table *st, struct appctx *appctx)
1295{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001296 struct peer_prep_params p = {
1297 .ack.shared_table = st,
1298 };
1299
1300 return peer_send_msg(appctx, peer_prepare_ackmsg, &p);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +01001301}
1302
1303/*
Frédéric Lécaille87f554c2019-01-22 17:26:50 +01001304 * Send a stick-table update message.
1305 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1306 * Returns -1 if there was not enough room left to send the message,
1307 * any other negative returned value must be considered as an error with an appcxt st0
1308 * returned value equal to PEER_SESS_ST_END.
1309 */
1310static inline int peer_send_updatemsg(struct shared_table *st, struct appctx *appctx, struct stksess *ts,
1311 unsigned int updateid, int use_identifier, int use_timed)
1312{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001313 struct peer_prep_params p = {
Willy Tarreaua898f0c2020-07-03 19:09:29 +02001314 .updt = {
1315 .stksess = ts,
1316 .shared_table = st,
1317 .updateid = updateid,
1318 .use_identifier = use_identifier,
1319 .use_timed = use_timed,
Willy Tarreau455caef2022-05-05 20:16:16 +02001320 .peer = appctx->svcctx,
Willy Tarreaua898f0c2020-07-03 19:09:29 +02001321 },
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +01001322 };
1323
1324 return peer_send_msg(appctx, peer_prepare_updatemsg, &p);
Frédéric Lécaille87f554c2019-01-22 17:26:50 +01001325}
1326
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001327/*
1328 * Build a peer protocol control class message.
1329 * Returns the number of written bytes used to build the message if succeeded,
1330 * 0 if not.
1331 */
1332static int peer_prepare_control_msg(char *msg, size_t size, struct peer_prep_params *p)
1333{
1334 if (size < sizeof p->control.head)
1335 return 0;
1336
1337 msg[0] = p->control.head[0];
1338 msg[1] = p->control.head[1];
1339
1340 return 2;
1341}
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +01001342
1343/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001344 * Send a stick-table synchronization request message.
1345 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1346 * Returns -1 if there was not enough room left to send the message,
1347 * any other negative returned value must be considered as an error with an appctx st0
1348 * returned value equal to PEER_SESS_ST_END.
1349 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001350static inline int peer_send_resync_reqmsg(struct appctx *appctx,
1351 struct peer *peer, struct peers *peers)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001352{
1353 struct peer_prep_params p = {
1354 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCREQ, },
1355 };
1356
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001357 TRACE_PROTO("send control message", PEERS_EV_CTRLMSG,
1358 NULL, &p.control.head[1], peers->local->id, peer->id);
1359
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001360 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
1361}
1362
1363/*
1364 * Send a stick-table synchronization confirmation message.
1365 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1366 * Returns -1 if there was not enough room left to send the message,
1367 * any other negative returned value must be considered as an error with an appctx st0
1368 * returned value equal to PEER_SESS_ST_END.
1369 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001370static inline int peer_send_resync_confirmsg(struct appctx *appctx,
1371 struct peer *peer, struct peers *peers)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001372{
1373 struct peer_prep_params p = {
1374 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCCONFIRM, },
1375 };
1376
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001377 TRACE_PROTO("send control message", PEERS_EV_CTRLMSG,
1378 NULL, &p.control.head[1], peers->local->id, peer->id);
1379
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001380 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
1381}
1382
1383/*
1384 * Send a stick-table synchronization finished message.
1385 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1386 * Returns -1 if there was not enough room left to send the message,
1387 * any other negative returned value must be considered as an error with an appctx st0
1388 * returned value equal to PEER_SESS_ST_END.
1389 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001390static inline int peer_send_resync_finishedmsg(struct appctx *appctx,
1391 struct peer *peer, struct peers *peers)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001392{
1393 struct peer_prep_params p = {
1394 .control.head = { PEER_MSG_CLASS_CONTROL, },
1395 };
1396
Emeric Brun70de43b2020-03-16 10:51:01 +01001397 p.control.head[1] = (peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED ?
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001398 PEER_MSG_CTRL_RESYNCFINISHED : PEER_MSG_CTRL_RESYNCPARTIAL;
1399
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001400 TRACE_PROTO("send control message", PEERS_EV_CTRLMSG,
1401 NULL, &p.control.head[1], peers->local->id, peer->id);
1402
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001403 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
1404}
1405
1406/*
Frédéric Lécaille645635d2019-02-11 17:49:39 +01001407 * Send a heartbeat message.
1408 * Return 0 if the message could not be built modifying the appctx st0 to PEER_SESS_ST_END value.
1409 * Returns -1 if there was not enough room left to send the message,
1410 * any other negative returned value must be considered as an error with an appctx st0
1411 * returned value equal to PEER_SESS_ST_END.
1412 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001413static inline int peer_send_heartbeatmsg(struct appctx *appctx,
1414 struct peer *peer, struct peers *peers)
Frédéric Lécaille645635d2019-02-11 17:49:39 +01001415{
1416 struct peer_prep_params p = {
1417 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_HEARTBEAT, },
1418 };
1419
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01001420 TRACE_PROTO("send control message", PEERS_EV_CTRLMSG,
1421 NULL, &p.control.head[1], peers->local->id, peer->id);
1422
Frédéric Lécaille645635d2019-02-11 17:49:39 +01001423 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
1424}
1425
1426/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001427 * Build a peer protocol error class message.
1428 * Returns the number of written bytes used to build the message if succeeded,
1429 * 0 if not.
1430 */
1431static int peer_prepare_error_msg(char *msg, size_t size, struct peer_prep_params *p)
1432{
1433 if (size < sizeof p->error.head)
1434 return 0;
1435
1436 msg[0] = p->error.head[0];
1437 msg[1] = p->error.head[1];
1438
1439 return 2;
1440}
1441
1442/*
1443 * Send a "size limit reached" error message.
1444 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1445 * Returns -1 if there was not enough room left to send the message,
1446 * any other negative returned value must be considered as an error with an appctx st0
1447 * returned value equal to PEER_SESS_ST_END.
1448 */
1449static inline int peer_send_error_size_limitmsg(struct appctx *appctx)
1450{
1451 struct peer_prep_params p = {
1452 .error.head = { PEER_MSG_CLASS_ERROR, PEER_MSG_ERR_SIZELIMIT, },
1453 };
1454
1455 return peer_send_msg(appctx, peer_prepare_error_msg, &p);
1456}
1457
1458/*
1459 * Send a "peer protocol" error message.
1460 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1461 * Returns -1 if there was not enough room left to send the message,
1462 * any other negative returned value must be considered as an error with an appctx st0
1463 * returned value equal to PEER_SESS_ST_END.
1464 */
1465static inline int peer_send_error_protomsg(struct appctx *appctx)
1466{
1467 struct peer_prep_params p = {
1468 .error.head = { PEER_MSG_CLASS_ERROR, PEER_MSG_ERR_PROTOCOL, },
1469 };
1470
1471 return peer_send_msg(appctx, peer_prepare_error_msg, &p);
1472}
1473
1474/*
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +01001475 * Function used to lookup for recent stick-table updates associated with
1476 * <st> shared stick-table when a lesson must be taught a peer (PEER_F_LEARN_ASSIGN flag set).
1477 */
1478static inline struct stksess *peer_teach_process_stksess_lookup(struct shared_table *st)
1479{
1480 struct eb32_node *eb;
1481
1482 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
1483 if (!eb) {
1484 eb = eb32_first(&st->table->updates);
Emeric Brun8e7a13e2021-04-28 11:48:15 +02001485 if (!eb || (eb->key == st->last_pushed)) {
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +01001486 st->table->commitupdate = st->last_pushed = st->table->localupdate;
1487 return NULL;
1488 }
1489 }
1490
Emeric Brun8e7a13e2021-04-28 11:48:15 +02001491 /* if distance between the last pushed and the retrieved key
1492 * is greater than the distance last_pushed and the local_update
1493 * this means we are beyond localupdate.
1494 */
1495 if ((eb->key - st->last_pushed) > (st->table->localupdate - st->last_pushed)) {
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +01001496 st->table->commitupdate = st->last_pushed = st->table->localupdate;
1497 return NULL;
1498 }
1499
1500 return eb32_entry(eb, struct stksess, upd);
1501}
1502
1503/*
1504 * Function used to lookup for recent stick-table updates associated with
1505 * <st> shared stick-table during teach state 1 step.
1506 */
1507static inline struct stksess *peer_teach_stage1_stksess_lookup(struct shared_table *st)
1508{
1509 struct eb32_node *eb;
1510
1511 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
1512 if (!eb) {
1513 st->flags |= SHTABLE_F_TEACH_STAGE1;
1514 eb = eb32_first(&st->table->updates);
1515 if (eb)
1516 st->last_pushed = eb->key - 1;
1517 return NULL;
1518 }
1519
1520 return eb32_entry(eb, struct stksess, upd);
1521}
1522
1523/*
1524 * Function used to lookup for recent stick-table updates associated with
1525 * <st> shared stick-table during teach state 2 step.
1526 */
1527static inline struct stksess *peer_teach_stage2_stksess_lookup(struct shared_table *st)
1528{
1529 struct eb32_node *eb;
1530
1531 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
1532 if (!eb || eb->key > st->teaching_origin) {
1533 st->flags |= SHTABLE_F_TEACH_STAGE2;
1534 return NULL;
1535 }
1536
1537 return eb32_entry(eb, struct stksess, upd);
1538}
1539
1540/*
1541 * Generic function to emit update messages for <st> stick-table when a lesson must
1542 * be taught to the peer <p>.
1543 * <locked> must be set to 1 if the shared table <st> is already locked when entering
1544 * this function, 0 if not.
1545 *
1546 * This function temporary unlock/lock <st> when it sends stick-table updates or
1547 * when decrementing its refcount in case of any error when it sends this updates.
1548 *
1549 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1550 * Returns -1 if there was not enough room left to send the message,
1551 * any other negative returned value must be considered as an error with an appcxt st0
1552 * returned value equal to PEER_SESS_ST_END.
1553 * If it returns 0 or -1, this function leave <st> locked if already locked when entering this function
1554 * unlocked if not already locked when entering this function.
1555 */
1556static inline int peer_send_teachmsgs(struct appctx *appctx, struct peer *p,
1557 struct stksess *(*peer_stksess_lookup)(struct shared_table *),
1558 struct shared_table *st, int locked)
1559{
1560 int ret, new_pushed, use_timed;
1561
1562 ret = 1;
1563 use_timed = 0;
1564 if (st != p->last_local_table) {
1565 ret = peer_send_switchmsg(st, appctx);
1566 if (ret <= 0)
1567 return ret;
1568
1569 p->last_local_table = st;
1570 }
1571
1572 if (peer_stksess_lookup != peer_teach_process_stksess_lookup)
1573 use_timed = !(p->flags & PEER_F_DWNGRD);
1574
1575 /* We force new pushed to 1 to force identifier in update message */
1576 new_pushed = 1;
1577
1578 if (!locked)
1579 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1580
1581 while (1) {
1582 struct stksess *ts;
1583 unsigned updateid;
1584
1585 /* push local updates */
1586 ts = peer_stksess_lookup(st);
1587 if (!ts)
1588 break;
1589
1590 updateid = ts->upd.key;
1591 ts->ref_cnt++;
1592 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1593
1594 ret = peer_send_updatemsg(st, appctx, ts, updateid, new_pushed, use_timed);
1595 if (ret <= 0) {
1596 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1597 ts->ref_cnt--;
1598 if (!locked)
1599 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1600 return ret;
1601 }
1602
1603 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1604 ts->ref_cnt--;
1605 st->last_pushed = updateid;
1606
1607 if (peer_stksess_lookup == peer_teach_process_stksess_lookup &&
1608 (int)(st->last_pushed - st->table->commitupdate) > 0)
1609 st->table->commitupdate = st->last_pushed;
1610
1611 /* identifier may not needed in next update message */
1612 new_pushed = 0;
1613 }
1614
1615 out:
1616 if (!locked)
1617 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1618 return 1;
1619}
1620
1621/*
1622 * Function to emit update messages for <st> stick-table when a lesson must
1623 * be taught to the peer <p> (PEER_F_LEARN_ASSIGN flag set).
1624 *
1625 * Note that <st> shared stick-table is locked when calling this function.
1626 *
1627 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1628 * Returns -1 if there was not enough room left to send the message,
1629 * any other negative returned value must be considered as an error with an appcxt st0
1630 * returned value equal to PEER_SESS_ST_END.
1631 */
1632static inline int peer_send_teach_process_msgs(struct appctx *appctx, struct peer *p,
1633 struct shared_table *st)
1634{
1635 return peer_send_teachmsgs(appctx, p, peer_teach_process_stksess_lookup, st, 1);
1636}
1637
1638/*
1639 * Function to emit update messages for <st> stick-table when a lesson must
1640 * be taught to the peer <p> during teach state 1 step.
1641 *
1642 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1643 * Returns -1 if there was not enough room left to send the message,
1644 * any other negative returned value must be considered as an error with an appcxt st0
1645 * returned value equal to PEER_SESS_ST_END.
1646 */
1647static inline int peer_send_teach_stage1_msgs(struct appctx *appctx, struct peer *p,
1648 struct shared_table *st)
1649{
1650 return peer_send_teachmsgs(appctx, p, peer_teach_stage1_stksess_lookup, st, 0);
1651}
1652
1653/*
1654 * Function to emit update messages for <st> stick-table when a lesson must
1655 * be taught to the peer <p> during teach state 1 step.
1656 *
1657 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1658 * Returns -1 if there was not enough room left to send the message,
1659 * any other negative returned value must be considered as an error with an appcxt st0
1660 * returned value equal to PEER_SESS_ST_END.
1661 */
1662static inline int peer_send_teach_stage2_msgs(struct appctx *appctx, struct peer *p,
1663 struct shared_table *st)
1664{
1665 return peer_send_teachmsgs(appctx, p, peer_teach_stage2_stksess_lookup, st, 0);
1666}
1667
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001668
1669/*
1670 * Function used to parse a stick-table update message after it has been received
1671 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1672 * receipt buffer with <msg_end> being position of the end of the stick-table message.
1673 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1674 * was encountered.
1675 * <exp> must be set if the stick-table entry expires.
1676 * <updt> must be set for PEER_MSG_STKT_UPDATE or PEER_MSG_STKT_UPDATE_TIMED stick-table
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001677 * 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 +01001678 * update ID.
1679 * <totl> is the length of the stick-table update message computed upon receipt.
1680 */
Frédéric Lécaille444243c2019-01-24 15:40:11 +01001681static int peer_treat_updatemsg(struct appctx *appctx, struct peer *p, int updt, int exp,
1682 char **msg_cur, char *msg_end, int msg_len, int totl)
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001683{
Willy Tarreau4596fe22022-05-17 19:07:51 +02001684 struct stconn *cs = appctx_cs(appctx);
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001685 struct shared_table *st = p->remote_table;
1686 struct stksess *ts, *newts;
1687 uint32_t update;
1688 int expire;
1689 unsigned int data_type;
1690 void *data_ptr;
1691
Frédéric Lécailled8659352020-11-10 16:18:03 +01001692 TRACE_ENTER(PEERS_EV_UPDTMSG, NULL, p);
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001693 /* Here we have data message */
1694 if (!st)
1695 goto ignore_msg;
1696
1697 expire = MS_TO_TICKS(st->table->expire);
1698
1699 if (updt) {
Frédéric Lécailled8659352020-11-10 16:18:03 +01001700 if (msg_len < sizeof(update)) {
1701 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001702 goto malformed_exit;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001703 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001704
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001705 memcpy(&update, *msg_cur, sizeof(update));
1706 *msg_cur += sizeof(update);
1707 st->last_get = htonl(update);
1708 }
1709 else {
1710 st->last_get++;
1711 }
1712
1713 if (exp) {
1714 size_t expire_sz = sizeof expire;
1715
Frédéric Lécailled8659352020-11-10 16:18:03 +01001716 if (*msg_cur + expire_sz > msg_end) {
1717 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1718 NULL, p, *msg_cur);
1719 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1720 NULL, p, msg_end, &expire_sz);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001721 goto malformed_exit;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001722 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001723
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001724 memcpy(&expire, *msg_cur, expire_sz);
1725 *msg_cur += expire_sz;
1726 expire = ntohl(expire);
1727 }
1728
1729 newts = stksess_new(st->table, NULL);
1730 if (!newts)
1731 goto ignore_msg;
1732
1733 if (st->table->type == SMP_T_STR) {
1734 unsigned int to_read, to_store;
1735
1736 to_read = intdecode(msg_cur, msg_end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001737 if (!*msg_cur) {
1738 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001739 goto malformed_free_newts;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001740 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001741
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001742 to_store = MIN(to_read, st->table->key_size - 1);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001743 if (*msg_cur + to_store > msg_end) {
1744 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1745 NULL, p, *msg_cur);
1746 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1747 NULL, p, msg_end, &to_store);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001748 goto malformed_free_newts;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001749 }
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001750
1751 memcpy(newts->key.key, *msg_cur, to_store);
1752 newts->key.key[to_store] = 0;
1753 *msg_cur += to_read;
1754 }
1755 else if (st->table->type == SMP_T_SINT) {
1756 unsigned int netinteger;
1757
Frédéric Lécailled8659352020-11-10 16:18:03 +01001758 if (*msg_cur + sizeof(netinteger) > msg_end) {
1759 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1760 NULL, p, *msg_cur);
1761 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1762 NULL, p, msg_end);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001763 goto malformed_free_newts;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001764 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001765
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001766 memcpy(&netinteger, *msg_cur, sizeof(netinteger));
1767 netinteger = ntohl(netinteger);
1768 memcpy(newts->key.key, &netinteger, sizeof(netinteger));
1769 *msg_cur += sizeof(netinteger);
1770 }
1771 else {
Frédéric Lécailled8659352020-11-10 16:18:03 +01001772 if (*msg_cur + st->table->key_size > msg_end) {
1773 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1774 NULL, p, *msg_cur);
1775 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1776 NULL, p, msg_end, &st->table->key_size);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001777 goto malformed_free_newts;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001778 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001779
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001780 memcpy(newts->key.key, *msg_cur, st->table->key_size);
1781 *msg_cur += st->table->key_size;
1782 }
1783
1784 /* lookup for existing entry */
1785 ts = stktable_set_entry(st->table, newts);
1786 if (ts != newts) {
1787 stksess_free(st->table, newts);
1788 newts = NULL;
1789 }
1790
1791 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1792
1793 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
Willy Tarreau1e82a142019-01-29 11:08:06 +01001794 uint64_t decoded_int;
Emeric Brun90a9b672021-06-22 16:09:55 +02001795 unsigned int idx;
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001796 int ignore;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001797
Emeric Brun08b0f672021-07-01 18:54:05 +02001798 if (!((1ULL << data_type) & st->remote_data))
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001799 continue;
Willy Tarreaudb2ab822021-10-08 17:53:12 +02001800
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001801 ignore = stktable_data_types[data_type].is_local;
Willy Tarreaudb2ab822021-10-08 17:53:12 +02001802
Emeric Brun90a9b672021-06-22 16:09:55 +02001803 if (stktable_data_types[data_type].is_array) {
1804 /* in case of array all elements
1805 * use the same std_type and they
1806 * are linearly encoded.
1807 * The number of elements was provided
1808 * by table definition message
1809 */
1810 switch (stktable_data_types[data_type].std_type) {
1811 case STD_T_SINT:
1812 for (idx = 0; idx < st->remote_data_nbelem[data_type]; idx++) {
1813 decoded_int = intdecode(msg_cur, msg_end);
1814 if (!*msg_cur) {
1815 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
1816 goto malformed_unlock;
1817 }
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001818
Emeric Brun90a9b672021-06-22 16:09:55 +02001819 data_ptr = stktable_data_ptr_idx(st->table, ts, data_type, idx);
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001820 if (data_ptr && !ignore)
Emeric Brun90a9b672021-06-22 16:09:55 +02001821 stktable_data_cast(data_ptr, std_t_sint) = decoded_int;
1822 }
1823 break;
1824 case STD_T_UINT:
1825 for (idx = 0; idx < st->remote_data_nbelem[data_type]; idx++) {
1826 decoded_int = intdecode(msg_cur, msg_end);
1827 if (!*msg_cur) {
1828 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
1829 goto malformed_unlock;
1830 }
1831
1832 data_ptr = stktable_data_ptr_idx(st->table, ts, data_type, idx);
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001833 if (data_ptr && !ignore)
Emeric Brun90a9b672021-06-22 16:09:55 +02001834 stktable_data_cast(data_ptr, std_t_uint) = decoded_int;
1835 }
1836 break;
1837 case STD_T_ULL:
1838 for (idx = 0; idx < st->remote_data_nbelem[data_type]; idx++) {
1839 decoded_int = intdecode(msg_cur, msg_end);
1840 if (!*msg_cur) {
1841 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
1842 goto malformed_unlock;
1843 }
1844
1845 data_ptr = stktable_data_ptr_idx(st->table, ts, data_type, idx);
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001846 if (data_ptr && !ignore)
Emeric Brun90a9b672021-06-22 16:09:55 +02001847 stktable_data_cast(data_ptr, std_t_ull) = decoded_int;
1848 }
1849 break;
1850 case STD_T_FRQP:
1851 for (idx = 0; idx < st->remote_data_nbelem[data_type]; idx++) {
1852 struct freq_ctr data;
1853
1854 /* First bit is reserved for the freq_ctr lock
1855 * Note: here we're still protected by the stksess lock
1856 * so we don't need to update the update the freq_ctr
1857 * using its internal lock.
1858 */
1859
1860 decoded_int = intdecode(msg_cur, msg_end);
1861 if (!*msg_cur) {
1862 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
1863 goto malformed_unlock;
1864 }
1865
1866 data.curr_tick = tick_add(now_ms, -decoded_int) & ~0x1;
1867 data.curr_ctr = intdecode(msg_cur, msg_end);
1868 if (!*msg_cur) {
1869 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
1870 goto malformed_unlock;
1871 }
1872
1873 data.prev_ctr = intdecode(msg_cur, msg_end);
1874 if (!*msg_cur) {
1875 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
1876 goto malformed_unlock;
1877 }
1878
1879 data_ptr = stktable_data_ptr_idx(st->table, ts, data_type, idx);
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001880 if (data_ptr && !ignore)
Emeric Brun90a9b672021-06-22 16:09:55 +02001881 stktable_data_cast(data_ptr, std_t_frqp) = data;
1882 }
1883 break;
1884 }
1885
1886 /* array is fully decoded
1887 * proceed next data_type.
1888 */
1889 continue;
1890 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001891 decoded_int = intdecode(msg_cur, msg_end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001892 if (!*msg_cur) {
1893 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001894 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001895 }
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001896
Willy Tarreau1e82a142019-01-29 11:08:06 +01001897 switch (stktable_data_types[data_type].std_type) {
1898 case STD_T_SINT:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001899 data_ptr = stktable_data_ptr(st->table, ts, data_type);
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001900 if (data_ptr && !ignore)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001901 stktable_data_cast(data_ptr, std_t_sint) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001902 break;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001903
Willy Tarreau1e82a142019-01-29 11:08:06 +01001904 case STD_T_UINT:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001905 data_ptr = stktable_data_ptr(st->table, ts, data_type);
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001906 if (data_ptr && !ignore)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001907 stktable_data_cast(data_ptr, std_t_uint) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001908 break;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001909
Willy Tarreau1e82a142019-01-29 11:08:06 +01001910 case STD_T_ULL:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001911 data_ptr = stktable_data_ptr(st->table, ts, data_type);
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001912 if (data_ptr && !ignore)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001913 stktable_data_cast(data_ptr, std_t_ull) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001914 break;
Willy Tarreau1e82a142019-01-29 11:08:06 +01001915
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001916 case STD_T_FRQP: {
Willy Tarreaufa1258f2021-04-10 23:00:53 +02001917 struct freq_ctr data;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001918
Willy Tarreaufa1258f2021-04-10 23:00:53 +02001919 /* First bit is reserved for the freq_ctr lock
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001920 Note: here we're still protected by the stksess lock
Willy Tarreaufa1258f2021-04-10 23:00:53 +02001921 so we don't need to update the update the freq_ctr
Emeric Brun90a9b672021-06-22 16:09:55 +02001922 using its internal lock.
1923 */
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001924
Willy Tarreau1e82a142019-01-29 11:08:06 +01001925 data.curr_tick = tick_add(now_ms, -decoded_int) & ~0x1;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001926 data.curr_ctr = intdecode(msg_cur, msg_end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001927 if (!*msg_cur) {
1928 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001929 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001930 }
Willy Tarreau1e82a142019-01-29 11:08:06 +01001931
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001932 data.prev_ctr = intdecode(msg_cur, msg_end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001933 if (!*msg_cur) {
1934 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG, NULL, p);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001935 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001936 }
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001937
1938 data_ptr = stktable_data_ptr(st->table, ts, data_type);
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01001939 if (data_ptr && !ignore)
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001940 stktable_data_cast(data_ptr, std_t_frqp) = data;
1941 break;
1942 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001943 case STD_T_DICT: {
1944 struct buffer *chunk;
1945 size_t data_len, value_len;
1946 unsigned int id;
1947 struct dict_entry *de;
1948 struct dcache *dc;
Frédéric Lécaille344e9482019-06-05 10:20:09 +02001949 char *end;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001950
Frédéric Lécailleaf9990f2019-11-13 17:50:34 +01001951 if (!decoded_int) {
1952 /* No entry. */
1953 break;
1954 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001955 data_len = decoded_int;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001956 if (*msg_cur + data_len > msg_end) {
1957 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1958 NULL, p, *msg_cur);
1959 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1960 NULL, p, msg_end, &data_len);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001961 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001962 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001963
Frédéric Lécaille344e9482019-06-05 10:20:09 +02001964 /* Compute the end of the current data, <msg_end> being at the end of
1965 * the entire message.
1966 */
1967 end = *msg_cur + data_len;
1968 id = intdecode(msg_cur, end);
Frédéric Lécailled8659352020-11-10 16:18:03 +01001969 if (!*msg_cur || !id) {
1970 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1971 NULL, p, *msg_cur, &id);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001972 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001973 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001974
1975 dc = p->dcache;
Frédéric Lécaille344e9482019-06-05 10:20:09 +02001976 if (*msg_cur == end) {
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001977 /* Dictionary entry key without value. */
Frédéric Lécaillef9e51be2020-11-12 19:53:11 +01001978 if (id > dc->max_entries) {
1979 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1980 NULL, p, NULL, &id);
1981 goto malformed_unlock;
1982 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001983 /* IDs sent over the network are numbered from 1. */
1984 de = dc->rx[id - 1].de;
1985 }
1986 else {
1987 chunk = get_trash_chunk();
Frédéric Lécaille344e9482019-06-05 10:20:09 +02001988 value_len = intdecode(msg_cur, end);
1989 if (!*msg_cur || *msg_cur + value_len > end ||
Frédéric Lécailled8659352020-11-10 16:18:03 +01001990 unlikely(value_len + 1 >= chunk->size)) {
1991 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1992 NULL, p, *msg_cur, &value_len);
1993 TRACE_PROTO("malformed message", PEERS_EV_UPDTMSG,
1994 NULL, p, end, &chunk->size);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001995 goto malformed_unlock;
Frédéric Lécailled8659352020-11-10 16:18:03 +01001996 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02001997
1998 chunk_memcpy(chunk, *msg_cur, value_len);
1999 chunk->area[chunk->data] = '\0';
Frédéric Lécaille56aec0d2019-06-06 14:14:15 +02002000 *msg_cur += value_len;
2001
Thayne McCombs92149f92020-11-20 01:28:26 -07002002 de = dict_insert(&server_key_dict, chunk->area);
2003 dict_entry_unref(&server_key_dict, dc->rx[id - 1].de);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02002004 dc->rx[id - 1].de = de;
2005 }
2006 if (de) {
2007 data_ptr = stktable_data_ptr(st->table, ts, data_type);
Willy Tarreaub4ff6f42021-12-24 13:38:49 +01002008 if (data_ptr && !ignore) {
Willy Tarreau4781b152021-04-06 13:53:36 +02002009 HA_ATOMIC_INC(&de->refcount);
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02002010 stktable_data_cast(data_ptr, std_t_dict) = de;
Thayne McCombs92149f92020-11-20 01:28:26 -07002011 }
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02002012 }
2013 break;
2014 }
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01002015 }
2016 }
2017 /* Force new expiration */
2018 ts->expire = tick_add(now_ms, expire);
2019
2020 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
2021 stktable_touch_remote(st->table, ts, 1);
Frédéric Lécailled8659352020-11-10 16:18:03 +01002022 TRACE_LEAVE(PEERS_EV_UPDTMSG, NULL, p);
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01002023 return 1;
2024
2025 ignore_msg:
2026 /* skip consumed message */
Willy Tarreau40a9c322022-05-18 15:55:18 +02002027 co_skip(sc_oc(cs), totl);
Frédéric Lécailled8659352020-11-10 16:18:03 +01002028 TRACE_DEVEL("leaving in error", PEERS_EV_UPDTMSG);
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01002029 return 0;
Willy Tarreau1e82a142019-01-29 11:08:06 +01002030
2031 malformed_unlock:
2032 /* malformed message */
2033 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
2034 stktable_touch_remote(st->table, ts, 1);
2035 appctx->st0 = PEER_SESS_ST_ERRPROTO;
Frédéric Lécailled8659352020-11-10 16:18:03 +01002036 TRACE_DEVEL("leaving in error", PEERS_EV_UPDTMSG);
Willy Tarreau1e82a142019-01-29 11:08:06 +01002037 return 0;
2038
2039 malformed_free_newts:
2040 /* malformed message */
2041 stksess_free(st->table, newts);
2042 malformed_exit:
2043 appctx->st0 = PEER_SESS_ST_ERRPROTO;
Frédéric Lécailled8659352020-11-10 16:18:03 +01002044 TRACE_DEVEL("leaving in error", PEERS_EV_UPDTMSG);
Willy Tarreau1e82a142019-01-29 11:08:06 +01002045 return 0;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01002046}
2047
Frédéric Lécaille87f554c2019-01-22 17:26:50 +01002048/*
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002049 * Function used to parse a stick-table update acknowledgement message after it
2050 * has been received by <p> peer with <msg_cur> as address of the pointer to the position in the
2051 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
2052 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
2053 * was encountered.
2054 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
2055 */
2056static inline int peer_treat_ackmsg(struct appctx *appctx, struct peer *p,
2057 char **msg_cur, char *msg_end)
2058{
2059 /* ack message */
2060 uint32_t table_id ;
2061 uint32_t update;
2062 struct shared_table *st;
2063
Emeric Brunb0d60be2021-03-04 10:27:10 +01002064 /* ignore ack during teaching process */
2065 if (p->flags & PEER_F_TEACH_PROCESS)
2066 return 1;
2067
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002068 table_id = intdecode(msg_cur, msg_end);
2069 if (!*msg_cur || (*msg_cur + sizeof(update) > msg_end)) {
2070 /* malformed message */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002071
2072 TRACE_PROTO("malformed message", PEERS_EV_ACKMSG,
2073 NULL, p, *msg_cur);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002074 appctx->st0 = PEER_SESS_ST_ERRPROTO;
2075 return 0;
2076 }
2077
2078 memcpy(&update, *msg_cur, sizeof(update));
2079 update = ntohl(update);
2080
2081 for (st = p->tables; st; st = st->next) {
2082 if (st->local_id == table_id) {
2083 st->update = update;
2084 break;
2085 }
2086 }
2087
2088 return 1;
2089}
2090
2091/*
2092 * Function used to parse a stick-table switch message after it has been received
2093 * by <p> peer with <msg_cur> as address of the pointer to the position in the
2094 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
2095 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
2096 * was encountered.
2097 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
2098 */
2099static inline int peer_treat_switchmsg(struct appctx *appctx, struct peer *p,
2100 char **msg_cur, char *msg_end)
2101{
2102 struct shared_table *st;
2103 int table_id;
2104
2105 table_id = intdecode(msg_cur, msg_end);
2106 if (!*msg_cur) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002107 TRACE_PROTO("malformed message", PEERS_EV_SWTCMSG, NULL, p);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002108 /* malformed message */
2109 appctx->st0 = PEER_SESS_ST_ERRPROTO;
2110 return 0;
2111 }
2112
2113 p->remote_table = NULL;
2114 for (st = p->tables; st; st = st->next) {
2115 if (st->remote_id == table_id) {
2116 p->remote_table = st;
2117 break;
2118 }
2119 }
2120
2121 return 1;
2122}
2123
2124/*
2125 * Function used to parse a stick-table definition message after it has been received
2126 * by <p> peer with <msg_cur> as address of the pointer to the position in the
2127 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
2128 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
2129 * was encountered.
2130 * <totl> is the length of the stick-table update message computed upon receipt.
2131 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
2132 */
2133static inline int peer_treat_definemsg(struct appctx *appctx, struct peer *p,
2134 char **msg_cur, char *msg_end, int totl)
2135{
Willy Tarreau4596fe22022-05-17 19:07:51 +02002136 struct stconn *cs = appctx_cs(appctx);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002137 int table_id_len;
2138 struct shared_table *st;
2139 int table_type;
2140 int table_keylen;
2141 int table_id;
2142 uint64_t table_data;
2143
2144 table_id = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002145 if (!*msg_cur) {
2146 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01002147 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002148 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002149
2150 table_id_len = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002151 if (!*msg_cur) {
2152 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p, *msg_cur);
Willy Tarreau6f731f32019-01-29 11:11:23 +01002153 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002154 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002155
2156 p->remote_table = NULL;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002157 if (!table_id_len || (*msg_cur + table_id_len) >= msg_end) {
2158 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p, *msg_cur, &table_id_len);
Willy Tarreau6f731f32019-01-29 11:11:23 +01002159 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002160 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002161
2162 for (st = p->tables; st; st = st->next) {
2163 /* Reset IDs */
2164 if (st->remote_id == table_id)
2165 st->remote_id = 0;
2166
Frédéric Lécaille7fcc24d2019-03-20 15:09:45 +01002167 if (!p->remote_table && (table_id_len == strlen(st->table->nid)) &&
2168 (memcmp(st->table->nid, *msg_cur, table_id_len) == 0))
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002169 p->remote_table = st;
2170 }
2171
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002172 if (!p->remote_table) {
2173 TRACE_PROTO("ignored message", PEERS_EV_DEFMSG, NULL, p);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002174 goto ignore_msg;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002175 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002176
2177 *msg_cur += table_id_len;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002178 if (*msg_cur >= msg_end) {
2179 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01002180 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002181 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002182
2183 table_type = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002184 if (!*msg_cur) {
2185 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01002186 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002187 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002188
2189 table_keylen = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002190 if (!*msg_cur) {
2191 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01002192 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002193 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002194
2195 table_data = intdecode(msg_cur, msg_end);
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002196 if (!*msg_cur) {
2197 TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p);
Willy Tarreau6f731f32019-01-29 11:11:23 +01002198 goto malformed_exit;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002199 }
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002200
Emeric Brun530ba382020-06-02 11:17:42 +02002201 if (p->remote_table->table->type != peer_int_key_type[table_type]
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002202 || p->remote_table->table->key_size != table_keylen) {
2203 p->remote_table = NULL;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002204 TRACE_PROTO("ignored message", PEERS_EV_DEFMSG, NULL, p);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002205 goto ignore_msg;
2206 }
2207
Ilya Shipitsin01881082021-08-07 14:41:56 +05002208 /* Check if there there is the additional expire data */
Emeric Brun90a9b672021-06-22 16:09:55 +02002209 intdecode(msg_cur, msg_end);
2210 if (*msg_cur) {
2211 uint64_t data_type;
2212 uint64_t type;
2213
2214 /* This define contains the expire data so we consider
2215 * it also contain all data_types parameters.
2216 */
2217 for (data_type = 0; data_type < STKTABLE_DATA_TYPES; data_type++) {
2218 if (table_data & (1ULL << data_type)) {
2219 if (stktable_data_types[data_type].is_array) {
2220 /* This should be an array
2221 * so we parse the data_type prefix
2222 * because we must have parameters.
2223 */
2224 type = intdecode(msg_cur, msg_end);
2225 if (!*msg_cur) {
2226 p->remote_table = NULL;
2227 TRACE_PROTO("missing meta data for array", PEERS_EV_DEFMSG, NULL, p);
2228 goto ignore_msg;
2229 }
2230
2231 /* check if the data_type match the current from the bitfield */
2232 if (type != data_type) {
2233 p->remote_table = NULL;
Ilya Shipitsin01881082021-08-07 14:41:56 +05002234 TRACE_PROTO("meta data mismatch type", PEERS_EV_DEFMSG, NULL, p);
Emeric Brun90a9b672021-06-22 16:09:55 +02002235 goto ignore_msg;
2236 }
2237
2238 /* decode the nbelem of the array */
2239 p->remote_table->remote_data_nbelem[type] = intdecode(msg_cur, msg_end);
2240 if (!*msg_cur) {
2241 p->remote_table = NULL;
2242 TRACE_PROTO("missing array size meta data for array", PEERS_EV_DEFMSG, NULL, p);
2243 goto ignore_msg;
2244 }
2245
2246 /* if it is an array of frqp, we must also have the period to decode */
2247 if (stktable_data_types[data_type].std_type == STD_T_FRQP) {
2248 intdecode(msg_cur, msg_end);
2249 if (!*msg_cur) {
2250 p->remote_table = NULL;
2251 TRACE_PROTO("missing period for frqp", PEERS_EV_DEFMSG, NULL, p);
2252 goto ignore_msg;
2253 }
2254 }
2255 }
2256 else if (stktable_data_types[data_type].std_type == STD_T_FRQP) {
2257 /* This should be a std freq counter data_type
2258 * so we parse the data_type prefix
2259 * because we must have parameters.
2260 */
2261 type = intdecode(msg_cur, msg_end);
2262 if (!*msg_cur) {
2263 p->remote_table = NULL;
2264 TRACE_PROTO("missing meta data for frqp", PEERS_EV_DEFMSG, NULL, p);
2265 goto ignore_msg;
2266 }
2267
2268 /* check if the data_type match the current from the bitfield */
2269 if (type != data_type) {
2270 p->remote_table = NULL;
Ilya Shipitsin01881082021-08-07 14:41:56 +05002271 TRACE_PROTO("meta data mismatch type", PEERS_EV_DEFMSG, NULL, p);
Emeric Brun90a9b672021-06-22 16:09:55 +02002272 goto ignore_msg;
2273 }
2274
2275 /* decode the period */
2276 intdecode(msg_cur, msg_end);
2277 if (!*msg_cur) {
2278 p->remote_table = NULL;
2279 TRACE_PROTO("missing period for frqp", PEERS_EV_DEFMSG, NULL, p);
2280 goto ignore_msg;
2281 }
2282 }
2283 }
2284 }
2285 }
2286 else {
2287 uint64_t data_type;
2288
2289 /* There is not additional data but
2290 * array size parameter is mandatory to parse array
2291 * so we consider an error if an array data_type is define
2292 * but there is no additional data.
2293 */
2294 for (data_type = 0; data_type < STKTABLE_DATA_TYPES; data_type++) {
2295 if (table_data & (1ULL << data_type)) {
2296 if (stktable_data_types[data_type].is_array) {
2297 p->remote_table = NULL;
2298 TRACE_PROTO("missing array size meta data for array", PEERS_EV_DEFMSG, NULL, p);
2299 goto ignore_msg;
2300 }
2301 }
2302 }
2303 }
2304
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002305 p->remote_table->remote_data = table_data;
2306 p->remote_table->remote_id = table_id;
2307 return 1;
2308
2309 ignore_msg:
Willy Tarreau40a9c322022-05-18 15:55:18 +02002310 co_skip(sc_oc(cs), totl);
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002311 return 0;
Willy Tarreau6f731f32019-01-29 11:11:23 +01002312
2313 malformed_exit:
2314 /* malformed message */
2315 appctx->st0 = PEER_SESS_ST_ERRPROTO;
2316 return 0;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01002317}
2318
2319/*
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002320 * Receive a stick-table message or pre-parse any other message.
2321 * The message's header will be sent into <msg_head> which must be at least
2322 * <msg_head_sz> bytes long (at least 7 to store 32-bit variable lengths).
2323 * The first two bytes are always read, and the rest is only read if the
2324 * first bytes indicate a stick-table message. If the message is a stick-table
2325 * message, the varint is decoded and the equivalent number of bytes will be
2326 * copied into the trash at trash.area. <totl> is incremented by the number of
2327 * bytes read EVEN IN CASE OF INCOMPLETE MESSAGES.
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002328 * Returns 1 if there was no error, if not, returns 0 if not enough data were available,
2329 * -1 if there was an error updating the appctx state st0 accordingly.
2330 */
2331static inline int peer_recv_msg(struct appctx *appctx, char *msg_head, size_t msg_head_sz,
2332 uint32_t *msg_len, int *totl)
2333{
2334 int reql;
Willy Tarreau4596fe22022-05-17 19:07:51 +02002335 struct stconn *cs = appctx_cs(appctx);
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002336 char *cur;
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002337
Willy Tarreau40a9c322022-05-18 15:55:18 +02002338 reql = co_getblk(sc_oc(cs), msg_head, 2 * sizeof(char), *totl);
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002339 if (reql <= 0) /* closed or EOL not found */
2340 goto incomplete;
2341
2342 *totl += reql;
2343
Frédéric Lécaille36fb77e2019-06-04 08:28:19 +02002344 if (!(msg_head[1] & PEER_MSG_STKT_BIT_MASK))
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002345 return 1;
2346
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002347 /* This is a stick-table message, let's go on */
2348
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002349 /* Read and Decode message length */
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002350 msg_head += *totl;
2351 msg_head_sz -= *totl;
Willy Tarreau40a9c322022-05-18 15:55:18 +02002352 reql = co_data(sc_oc(cs)) - *totl;
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002353 if (reql > msg_head_sz)
2354 reql = msg_head_sz;
2355
Willy Tarreau40a9c322022-05-18 15:55:18 +02002356 reql = co_getblk(sc_oc(cs), msg_head, reql, *totl);
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002357 if (reql <= 0) /* closed */
2358 goto incomplete;
2359
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002360 cur = msg_head;
2361 *msg_len = intdecode(&cur, cur + reql);
2362 if (!cur) {
2363 /* the number is truncated, did we read enough ? */
2364 if (reql < msg_head_sz)
2365 goto incomplete;
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002366
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002367 /* malformed message */
2368 TRACE_PROTO("malformed message: too large length encoding", PEERS_EV_UPDTMSG);
2369 appctx->st0 = PEER_SESS_ST_ERRPROTO;
2370 return -1;
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002371 }
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01002372 *totl += cur - msg_head;
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002373
2374 /* Read message content */
2375 if (*msg_len) {
2376 if (*msg_len > trash.size) {
2377 /* Status code is not success, abort */
2378 appctx->st0 = PEER_SESS_ST_ERRSIZE;
2379 return -1;
2380 }
2381
Willy Tarreau40a9c322022-05-18 15:55:18 +02002382 reql = co_getblk(sc_oc(cs), trash.area, *msg_len, *totl);
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002383 if (reql <= 0) /* closed */
2384 goto incomplete;
2385 *totl += reql;
2386 }
2387
2388 return 1;
2389
2390 incomplete:
Willy Tarreau40a9c322022-05-18 15:55:18 +02002391 if (reql < 0 || (sc_oc(cs)->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
Willy Tarreau345ebcf2020-11-26 17:06:04 +01002392 /* there was an error or the message was truncated */
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002393 appctx->st0 = PEER_SESS_ST_END;
2394 return -1;
2395 }
2396
2397 return 0;
2398}
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002399
2400/*
2401 * Treat the awaited message with <msg_head> as header.*
2402 * Return 1 if succeeded, 0 if not.
2403 */
2404static inline int peer_treat_awaited_msg(struct appctx *appctx, struct peer *peer, unsigned char *msg_head,
2405 char **msg_cur, char *msg_end, int msg_len, int totl)
2406{
Christopher Faulet387e7972022-05-12 14:47:52 +02002407 struct peers *peers = peer->peers;
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002408
2409 if (msg_head[0] == PEER_MSG_CLASS_CONTROL) {
2410 if (msg_head[1] == PEER_MSG_CTRL_RESYNCREQ) {
2411 struct shared_table *st;
2412 /* Reset message: remote need resync */
2413
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002414 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2415 NULL, &msg_head[1], peers->local->id, peer->id);
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002416 /* prepare tables for a global push */
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002417 for (st = peer->tables; st; st = st->next) {
Emeric Brun437e48a2021-04-28 09:49:33 +02002418 st->teaching_origin = st->last_pushed = st->update;
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002419 st->flags = 0;
2420 }
2421
2422 /* reset teaching flags to 0 */
2423 peer->flags &= PEER_TEACH_RESET;
2424
2425 /* flag to start to teach lesson */
2426 peer->flags |= PEER_F_TEACH_PROCESS;
Emeric Brunccdfbae2021-04-28 12:59:35 +02002427 peers->flags |= PEERS_F_RESYNC_REQUESTED;
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002428 }
2429 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCFINISHED) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002430 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2431 NULL, &msg_head[1], peers->local->id, peer->id);
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002432 if (peer->flags & PEER_F_LEARN_ASSIGN) {
2433 peer->flags &= ~PEER_F_LEARN_ASSIGN;
2434 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
2435 peers->flags |= (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE);
Emeric Brunccdfbae2021-04-28 12:59:35 +02002436 if (peer->local)
2437 peers->flags |= PEERS_F_RESYNC_LOCALFINISHED;
2438 else
2439 peers->flags |= PEERS_F_RESYNC_REMOTEFINISHED;
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002440 }
2441 peer->confirm++;
2442 }
2443 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCPARTIAL) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002444 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2445 NULL, &msg_head[1], peers->local->id, peer->id);
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002446 if (peer->flags & PEER_F_LEARN_ASSIGN) {
2447 peer->flags &= ~PEER_F_LEARN_ASSIGN;
2448 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
2449
Emeric Brunccdfbae2021-04-28 12:59:35 +02002450 if (peer->local)
2451 peers->flags |= PEERS_F_RESYNC_LOCALPARTIAL;
2452 else
2453 peers->flags |= PEERS_F_RESYNC_REMOTEPARTIAL;
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002454 peer->flags |= PEER_F_LEARN_NOTUP2DATE;
Frédéric Lécaille54bff832019-03-26 10:25:20 +01002455 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(PEER_RESYNC_TIMEOUT));
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002456 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
2457 }
2458 peer->confirm++;
2459 }
2460 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCCONFIRM) {
2461 struct shared_table *st;
2462
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002463 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2464 NULL, &msg_head[1], peers->local->id, peer->id);
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002465 /* If stopping state */
2466 if (stopping) {
2467 /* Close session, push resync no more needed */
2468 peer->flags |= PEER_F_TEACH_COMPLETE;
2469 appctx->st0 = PEER_SESS_ST_END;
2470 return 0;
2471 }
2472 for (st = peer->tables; st; st = st->next) {
2473 st->update = st->last_pushed = st->teaching_origin;
2474 st->flags = 0;
2475 }
2476
2477 /* reset teaching flags to 0 */
2478 peer->flags &= PEER_TEACH_RESET;
2479 }
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002480 else if (msg_head[1] == PEER_MSG_CTRL_HEARTBEAT) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002481 TRACE_PROTO("received control message", PEERS_EV_CTRLMSG,
2482 NULL, &msg_head[1], peers->local->id, peer->id);
Frédéric Lécaille54bff832019-03-26 10:25:20 +01002483 peer->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT));
Frédéric Lécaille33cab3c2019-11-06 11:51:26 +01002484 peer->rx_hbt++;
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002485 }
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002486 }
2487 else if (msg_head[0] == PEER_MSG_CLASS_STICKTABLE) {
2488 if (msg_head[1] == PEER_MSG_STKT_DEFINE) {
2489 if (!peer_treat_definemsg(appctx, peer, msg_cur, msg_end, totl))
2490 return 0;
2491 }
2492 else if (msg_head[1] == PEER_MSG_STKT_SWITCH) {
2493 if (!peer_treat_switchmsg(appctx, peer, msg_cur, msg_end))
2494 return 0;
2495 }
2496 else if (msg_head[1] == PEER_MSG_STKT_UPDATE ||
2497 msg_head[1] == PEER_MSG_STKT_INCUPDATE ||
2498 msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED ||
2499 msg_head[1] == PEER_MSG_STKT_INCUPDATE_TIMED) {
2500 int update, expire;
2501
2502 update = msg_head[1] == PEER_MSG_STKT_UPDATE || msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED;
2503 expire = msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED || msg_head[1] == PEER_MSG_STKT_INCUPDATE_TIMED;
2504 if (!peer_treat_updatemsg(appctx, peer, update, expire,
2505 msg_cur, msg_end, msg_len, totl))
2506 return 0;
2507
2508 }
2509 else if (msg_head[1] == PEER_MSG_STKT_ACK) {
2510 if (!peer_treat_ackmsg(appctx, peer, msg_cur, msg_end))
2511 return 0;
2512 }
2513 }
2514 else if (msg_head[0] == PEER_MSG_CLASS_RESERVED) {
2515 appctx->st0 = PEER_SESS_ST_ERRPROTO;
2516 return 0;
2517 }
2518
2519 return 1;
2520}
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002521
2522
2523/*
2524 * Send any message to <peer> peer.
2525 * Returns 1 if succeeded, or -1 or 0 if failed.
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002526 * -1 means an internal error occurred, 0 is for a peer protocol error leading
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002527 * to a peer state change (from the peer I/O handler point of view).
2528 */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002529static inline int peer_send_msgs(struct appctx *appctx,
2530 struct peer *peer, struct peers *peers)
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002531{
2532 int repl;
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002533
2534 /* Need to request a resync */
2535 if ((peer->flags & PEER_F_LEARN_ASSIGN) &&
2536 (peers->flags & PEERS_F_RESYNC_ASSIGN) &&
2537 !(peers->flags & PEERS_F_RESYNC_PROCESS)) {
2538
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002539 repl = peer_send_resync_reqmsg(appctx, peer, peers);
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002540 if (repl <= 0)
2541 return repl;
2542
2543 peers->flags |= PEERS_F_RESYNC_PROCESS;
2544 }
2545
2546 /* Nothing to read, now we start to write */
2547 if (peer->tables) {
2548 struct shared_table *st;
2549 struct shared_table *last_local_table;
2550
2551 last_local_table = peer->last_local_table;
2552 if (!last_local_table)
2553 last_local_table = peer->tables;
2554 st = last_local_table->next;
2555
2556 while (1) {
2557 if (!st)
2558 st = peer->tables;
2559
2560 /* It remains some updates to ack */
2561 if (st->last_get != st->last_acked) {
2562 repl = peer_send_ackmsg(st, appctx);
2563 if (repl <= 0)
2564 return repl;
2565
2566 st->last_acked = st->last_get;
2567 }
2568
2569 if (!(peer->flags & PEER_F_TEACH_PROCESS)) {
2570 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
2571 if (!(peer->flags & PEER_F_LEARN_ASSIGN) &&
Emeric Brun8e7a13e2021-04-28 11:48:15 +02002572 (st->last_pushed != st->table->localupdate)) {
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002573
2574 repl = peer_send_teach_process_msgs(appctx, peer, st);
2575 if (repl <= 0) {
2576 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
2577 return repl;
2578 }
2579 }
2580 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
2581 }
Emeric Brun1675ada2021-04-22 18:13:13 +02002582 else if (!(peer->flags & PEER_F_TEACH_FINISHED)) {
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002583 if (!(st->flags & SHTABLE_F_TEACH_STAGE1)) {
2584 repl = peer_send_teach_stage1_msgs(appctx, peer, st);
2585 if (repl <= 0)
2586 return repl;
2587 }
2588
2589 if (!(st->flags & SHTABLE_F_TEACH_STAGE2)) {
2590 repl = peer_send_teach_stage2_msgs(appctx, peer, st);
2591 if (repl <= 0)
2592 return repl;
2593 }
2594 }
2595
2596 if (st == last_local_table)
2597 break;
2598 st = st->next;
2599 }
2600 }
2601
2602 if ((peer->flags & PEER_F_TEACH_PROCESS) && !(peer->flags & PEER_F_TEACH_FINISHED)) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002603 repl = peer_send_resync_finishedmsg(appctx, peer, peers);
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002604 if (repl <= 0)
2605 return repl;
2606
2607 /* flag finished message sent */
2608 peer->flags |= PEER_F_TEACH_FINISHED;
2609 }
2610
2611 /* Confirm finished or partial messages */
2612 while (peer->confirm) {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01002613 repl = peer_send_resync_confirmsg(appctx, peer, peers);
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002614 if (repl <= 0)
2615 return repl;
2616
2617 peer->confirm--;
2618 }
2619
2620 return 1;
2621}
2622
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002623/*
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002624 * Read and parse a first line of a "hello" peer protocol message.
2625 * Returns 0 if could not read a line, -1 if there was a read error or
2626 * the line is malformed, 1 if succeeded.
2627 */
2628static inline int peer_getline_version(struct appctx *appctx,
2629 unsigned int *maj_ver, unsigned int *min_ver)
2630{
2631 int reql;
2632
2633 reql = peer_getline(appctx);
2634 if (!reql)
2635 return 0;
2636
2637 if (reql < 0)
2638 return -1;
2639
2640 /* test protocol */
2641 if (strncmp(PEER_SESSION_PROTO_NAME " ", trash.area, proto_len + 1) != 0) {
2642 appctx->st0 = PEER_SESS_ST_EXIT;
2643 appctx->st1 = PEER_SESS_SC_ERRPROTO;
2644 return -1;
2645 }
2646 if (peer_get_version(trash.area + proto_len + 1, maj_ver, min_ver) == -1 ||
2647 *maj_ver != PEER_MAJOR_VER || *min_ver > PEER_MINOR_VER) {
2648 appctx->st0 = PEER_SESS_ST_EXIT;
2649 appctx->st1 = PEER_SESS_SC_ERRVERSION;
2650 return -1;
2651 }
2652
2653 return 1;
2654}
2655
2656/*
2657 * Read and parse a second line of a "hello" peer protocol message.
2658 * Returns 0 if could not read a line, -1 if there was a read error or
2659 * the line is malformed, 1 if succeeded.
2660 */
2661static inline int peer_getline_host(struct appctx *appctx)
2662{
2663 int reql;
2664
2665 reql = peer_getline(appctx);
2666 if (!reql)
2667 return 0;
2668
2669 if (reql < 0)
2670 return -1;
2671
2672 /* test hostname match */
2673 if (strcmp(localpeer, trash.area) != 0) {
2674 appctx->st0 = PEER_SESS_ST_EXIT;
2675 appctx->st1 = PEER_SESS_SC_ERRHOST;
2676 return -1;
2677 }
2678
2679 return 1;
2680}
2681
2682/*
2683 * Read and parse a last line of a "hello" peer protocol message.
2684 * Returns 0 if could not read a character, -1 if there was a read error or
2685 * the line is malformed, 1 if succeeded.
2686 * Set <curpeer> accordingly (the remote peer sending the "hello" message).
2687 */
2688static inline int peer_getline_last(struct appctx *appctx, struct peer **curpeer)
2689{
2690 char *p;
2691 int reql;
2692 struct peer *peer;
Willy Tarreau0698c802022-05-11 14:09:57 +02002693 struct stream *s = appctx_strm(appctx);
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002694 struct peers *peers = strm_fe(s)->parent;
2695
2696 reql = peer_getline(appctx);
2697 if (!reql)
2698 return 0;
2699
2700 if (reql < 0)
2701 return -1;
2702
2703 /* parse line "<peer name> <pid> <relative_pid>" */
2704 p = strchr(trash.area, ' ');
2705 if (!p) {
2706 appctx->st0 = PEER_SESS_ST_EXIT;
2707 appctx->st1 = PEER_SESS_SC_ERRPROTO;
2708 return -1;
2709 }
2710 *p = 0;
2711
2712 /* lookup known peer */
2713 for (peer = peers->remote; peer; peer = peer->next) {
2714 if (strcmp(peer->id, trash.area) == 0)
2715 break;
2716 }
2717
2718 /* if unknown peer */
2719 if (!peer) {
2720 appctx->st0 = PEER_SESS_ST_EXIT;
2721 appctx->st1 = PEER_SESS_SC_ERRPEER;
2722 return -1;
2723 }
2724 *curpeer = peer;
2725
2726 return 1;
2727}
2728
2729/*
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002730 * Init <peer> peer after having accepted it at peer protocol level.
2731 */
2732static inline void init_accepted_peer(struct peer *peer, struct peers *peers)
2733{
2734 struct shared_table *st;
2735
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002736 peer->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002737 /* Register status code */
2738 peer->statuscode = PEER_SESS_SC_SUCCESSCODE;
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02002739 peer->last_hdshk = now_ms;
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002740
2741 /* Awake main task */
2742 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
2743
2744 /* Init confirm counter */
2745 peer->confirm = 0;
2746
2747 /* Init cursors */
2748 for (st = peer->tables; st ; st = st->next) {
2749 st->last_get = st->last_acked = 0;
Emeric Brund9729da2021-02-23 16:50:53 +01002750 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
2751 /* if st->update appears to be in future it means
2752 * that the last acked value is very old and we
2753 * remain unconnected a too long time to use this
2754 * acknowlegement as a reset.
2755 * We should update the protocol to be able to
2756 * signal the remote peer that it needs a full resync.
2757 * Here a partial fix consist to set st->update at
2758 * the max past value
2759 */
2760 if ((int)(st->table->localupdate - st->update) < 0)
2761 st->update = st->table->localupdate + (2147483648U);
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002762 st->teaching_origin = st->last_pushed = st->update;
Emeric Brun1a6b43e2021-04-20 14:43:46 +02002763 st->flags = 0;
Emeric Bruncc9cce92021-02-23 17:08:08 +01002764 if ((int)(st->last_pushed - st->table->commitupdate) > 0)
2765 st->table->commitupdate = st->last_pushed;
Emeric Brund9729da2021-02-23 16:50:53 +01002766 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002767 }
2768
2769 /* reset teaching and learning flags to 0 */
2770 peer->flags &= PEER_TEACH_RESET;
2771 peer->flags &= PEER_LEARN_RESET;
2772
2773 /* if current peer is local */
2774 if (peer->local) {
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05002775 /* if current host need resyncfrom local and no process assigned */
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002776 if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL &&
2777 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
2778 /* assign local peer for a lesson, consider lesson already requested */
2779 peer->flags |= PEER_F_LEARN_ASSIGN;
2780 peers->flags |= (PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
Emeric Brunccdfbae2021-04-28 12:59:35 +02002781 peers->flags |= PEERS_F_RESYNC_LOCALASSIGN;
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002782 }
2783
2784 }
2785 else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
2786 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
2787 /* assign peer for a lesson */
2788 peer->flags |= PEER_F_LEARN_ASSIGN;
2789 peers->flags |= PEERS_F_RESYNC_ASSIGN;
Emeric Brunccdfbae2021-04-28 12:59:35 +02002790 peers->flags |= PEERS_F_RESYNC_REMOTEASSIGN;
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002791 }
2792}
2793
2794/*
2795 * Init <peer> peer after having connected it at peer protocol level.
2796 */
2797static inline void init_connected_peer(struct peer *peer, struct peers *peers)
2798{
2799 struct shared_table *st;
2800
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002801 peer->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002802 /* Init cursors */
2803 for (st = peer->tables; st ; st = st->next) {
2804 st->last_get = st->last_acked = 0;
Emeric Brund9729da2021-02-23 16:50:53 +01002805 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
2806 /* if st->update appears to be in future it means
2807 * that the last acked value is very old and we
2808 * remain unconnected a too long time to use this
2809 * acknowlegement as a reset.
2810 * We should update the protocol to be able to
2811 * signal the remote peer that it needs a full resync.
2812 * Here a partial fix consist to set st->update at
2813 * the max past value.
2814 */
2815 if ((int)(st->table->localupdate - st->update) < 0)
2816 st->update = st->table->localupdate + (2147483648U);
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002817 st->teaching_origin = st->last_pushed = st->update;
Emeric Brun1a6b43e2021-04-20 14:43:46 +02002818 st->flags = 0;
Emeric Bruncc9cce92021-02-23 17:08:08 +01002819 if ((int)(st->last_pushed - st->table->commitupdate) > 0)
2820 st->table->commitupdate = st->last_pushed;
Emeric Brund9729da2021-02-23 16:50:53 +01002821 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002822 }
2823
2824 /* Init confirm counter */
2825 peer->confirm = 0;
2826
2827 /* reset teaching and learning flags to 0 */
2828 peer->flags &= PEER_TEACH_RESET;
2829 peer->flags &= PEER_LEARN_RESET;
2830
2831 /* If current peer is local */
2832 if (peer->local) {
2833 /* flag to start to teach lesson */
2834 peer->flags |= PEER_F_TEACH_PROCESS;
2835 }
2836 else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
2837 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
2838 /* If peer is remote and resync from remote is needed,
2839 and no peer currently assigned */
2840
2841 /* assign peer for a lesson */
2842 peer->flags |= PEER_F_LEARN_ASSIGN;
2843 peers->flags |= PEERS_F_RESYNC_ASSIGN;
Emeric Brunccdfbae2021-04-28 12:59:35 +02002844 peers->flags |= PEERS_F_RESYNC_REMOTEASSIGN;
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002845 }
2846}
2847
2848/*
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05002849 * IO Handler to handle message exchange with a peer
Emeric Brun2b920a12010-09-23 18:30:22 +02002850 */
Willy Tarreau00a37f02015-04-13 12:05:19 +02002851static void peer_io_handler(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +02002852{
Willy Tarreau4596fe22022-05-17 19:07:51 +02002853 struct stconn *cs = appctx_cs(appctx);
Willy Tarreauea27f482022-05-18 16:10:52 +02002854 struct stream *s = __sc_strm(cs);
Vincent Bernat3c2f2f22016-04-03 13:48:42 +02002855 struct peers *curpeers = strm_fe(s)->parent;
Emeric Brun80527f52017-06-19 17:46:37 +02002856 struct peer *curpeer = NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002857 int reql = 0;
2858 int repl = 0;
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002859 unsigned int maj_ver, min_ver;
Willy Tarreau2d372c22018-11-05 17:12:27 +01002860 int prev_state;
Emeric Brun2b920a12010-09-23 18:30:22 +02002861
Joseph Herlant82b2f542018-11-15 12:19:14 -08002862 /* Check if the input buffer is available. */
Willy Tarreau40a9c322022-05-18 15:55:18 +02002863 if (sc_ib(cs)->size == 0) {
Willy Tarreau99615ed2022-05-25 07:29:36 +02002864 sc_need_room(cs);
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002865 goto out;
2866 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002867
Emeric Brun2b920a12010-09-23 18:30:22 +02002868 while (1) {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002869 prev_state = appctx->st0;
Emeric Brun2b920a12010-09-23 18:30:22 +02002870switchstate:
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002871 maj_ver = min_ver = (unsigned int)-1;
Willy Tarreau7b4b4992013-12-01 09:15:12 +01002872 switch(appctx->st0) {
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002873 case PEER_SESS_ST_ACCEPT:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002874 prev_state = appctx->st0;
Willy Tarreau455caef2022-05-05 20:16:16 +02002875 appctx->svcctx = NULL;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002876 appctx->st0 = PEER_SESS_ST_GETVERSION;
Emeric Brun2b920a12010-09-23 18:30:22 +02002877 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002878 case PEER_SESS_ST_GETVERSION:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002879 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002880 reql = peer_getline_version(appctx, &maj_ver, &min_ver);
2881 if (reql <= 0) {
2882 if (!reql)
2883 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002884 goto switchstate;
2885 }
2886
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002887 appctx->st0 = PEER_SESS_ST_GETHOST;
Emeric Brun2b920a12010-09-23 18:30:22 +02002888 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002889 case PEER_SESS_ST_GETHOST:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002890 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002891 reql = peer_getline_host(appctx);
2892 if (reql <= 0) {
2893 if (!reql)
2894 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002895 goto switchstate;
2896 }
2897
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002898 appctx->st0 = PEER_SESS_ST_GETPEER;
Emeric Brun2b920a12010-09-23 18:30:22 +02002899 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002900 case PEER_SESS_ST_GETPEER: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002901 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002902 reql = peer_getline_last(appctx, &curpeer);
2903 if (reql <= 0) {
2904 if (!reql)
2905 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002906 goto switchstate;
2907 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002908
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002909 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Willy Tarreau9df94c22016-10-31 18:42:52 +01002910 if (curpeer->appctx && curpeer->appctx != appctx) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002911 if (curpeer->local) {
2912 /* Local connection, reply a retry */
2913 appctx->st0 = PEER_SESS_ST_EXIT;
2914 appctx->st1 = PEER_SESS_SC_TRYAGAIN;
2915 goto switchstate;
Emeric Brun2b920a12010-09-23 18:30:22 +02002916 }
Emeric Brun80527f52017-06-19 17:46:37 +02002917
2918 /* we're killing a connection, we must apply a random delay before
2919 * retrying otherwise the other end will do the same and we can loop
2920 * for a while.
2921 */
Willy Tarreau52bf8392020-03-08 00:42:37 +01002922 curpeer->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
Emeric Brun9ef2ad72019-04-02 17:22:01 +02002923 peer_session_forceshutdown(curpeer);
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002924 curpeer->heartbeat = TICK_ETERNITY;
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02002925 curpeer->coll++;
Emeric Brun2b920a12010-09-23 18:30:22 +02002926 }
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002927 if (maj_ver != (unsigned int)-1 && min_ver != (unsigned int)-1) {
2928 if (min_ver == PEER_DWNGRD_MINOR_VER) {
2929 curpeer->flags |= PEER_F_DWNGRD;
2930 }
2931 else {
2932 curpeer->flags &= ~PEER_F_DWNGRD;
2933 }
2934 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002935 curpeer->appctx = appctx;
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02002936 curpeer->flags |= PEER_F_ALIVE;
Willy Tarreau455caef2022-05-05 20:16:16 +02002937 appctx->svcctx = curpeer;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002938 appctx->st0 = PEER_SESS_ST_SENDSUCCESS;
Willy Tarreau4781b152021-04-06 13:53:36 +02002939 _HA_ATOMIC_INC(&active_peers);
Emeric Brun2b920a12010-09-23 18:30:22 +02002940 }
Tim Duesterhus588b3142020-05-29 14:35:51 +02002941 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002942 case PEER_SESS_ST_SENDSUCCESS: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002943 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002944 if (!curpeer) {
Willy Tarreau455caef2022-05-05 20:16:16 +02002945 curpeer = appctx->svcctx;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002946 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002947 if (curpeer->appctx != appctx) {
2948 appctx->st0 = PEER_SESS_ST_END;
2949 goto switchstate;
2950 }
2951 }
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002952
2953 repl = peer_send_status_successmsg(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002954 if (repl <= 0) {
2955 if (repl == -1)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002956 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002957 goto switchstate;
2958 }
2959
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002960 init_accepted_peer(curpeer, curpeers);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002961
Emeric Brun2b920a12010-09-23 18:30:22 +02002962 /* switch to waiting message state */
Willy Tarreau4781b152021-04-06 13:53:36 +02002963 _HA_ATOMIC_INC(&connected_peers);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002964 appctx->st0 = PEER_SESS_ST_WAITMSG;
Emeric Brun2b920a12010-09-23 18:30:22 +02002965 goto switchstate;
2966 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002967 case PEER_SESS_ST_CONNECT: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002968 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002969 if (!curpeer) {
Willy Tarreau455caef2022-05-05 20:16:16 +02002970 curpeer = appctx->svcctx;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002971 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002972 if (curpeer->appctx != appctx) {
2973 appctx->st0 = PEER_SESS_ST_END;
2974 goto switchstate;
2975 }
2976 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002977
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002978 repl = peer_send_hellomsg(appctx, curpeer);
Emeric Brun2b920a12010-09-23 18:30:22 +02002979 if (repl <= 0) {
2980 if (repl == -1)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002981 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002982 goto switchstate;
2983 }
2984
2985 /* switch to the waiting statuscode state */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002986 appctx->st0 = PEER_SESS_ST_GETSTATUS;
Emeric Brun2b920a12010-09-23 18:30:22 +02002987 }
Tim Duesterhus588b3142020-05-29 14:35:51 +02002988 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002989 case PEER_SESS_ST_GETSTATUS: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002990 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002991 if (!curpeer) {
Willy Tarreau455caef2022-05-05 20:16:16 +02002992 curpeer = appctx->svcctx;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002993 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002994 if (curpeer->appctx != appctx) {
2995 appctx->st0 = PEER_SESS_ST_END;
2996 goto switchstate;
2997 }
2998 }
2999
Willy Tarreau40a9c322022-05-18 15:55:18 +02003000 if (sc_ic(cs)->flags & CF_WRITE_PARTIAL)
Emeric Brunb3971ab2015-05-12 18:49:09 +02003001 curpeer->statuscode = PEER_SESS_SC_CONNECTEDCODE;
Emeric Brun2b920a12010-09-23 18:30:22 +02003002
Frédéric Lécaillece025572019-01-21 13:38:06 +01003003 reql = peer_getline(appctx);
3004 if (!reql)
3005 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02003006
Frédéric Lécaillece025572019-01-21 13:38:06 +01003007 if (reql < 0)
3008 goto switchstate;
Emeric Brun2b920a12010-09-23 18:30:22 +02003009
3010 /* Register status code */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02003011 curpeer->statuscode = atoi(trash.area);
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003012 curpeer->last_hdshk = now_ms;
Emeric Brun2b920a12010-09-23 18:30:22 +02003013
3014 /* Awake main task */
Frédéric Lécailleed2b4a62017-07-13 09:07:09 +02003015 task_wakeup(curpeers->sync_task, TASK_WOKEN_MSG);
Emeric Brun2b920a12010-09-23 18:30:22 +02003016
3017 /* If status code is success */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003018 if (curpeer->statuscode == PEER_SESS_SC_SUCCESSCODE) {
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01003019 init_connected_peer(curpeer, curpeers);
Emeric Brun2b920a12010-09-23 18:30:22 +02003020 }
3021 else {
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02003022 if (curpeer->statuscode == PEER_SESS_SC_ERRVERSION)
3023 curpeer->flags |= PEER_F_DWNGRD;
Emeric Brun2b920a12010-09-23 18:30:22 +02003024 /* Status code is not success, abort */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003025 appctx->st0 = PEER_SESS_ST_END;
Emeric Brun2b920a12010-09-23 18:30:22 +02003026 goto switchstate;
3027 }
Willy Tarreau4781b152021-04-06 13:53:36 +02003028 _HA_ATOMIC_INC(&connected_peers);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003029 appctx->st0 = PEER_SESS_ST_WAITMSG;
Emeric Brun2b920a12010-09-23 18:30:22 +02003030 }
Tim Duesterhus588b3142020-05-29 14:35:51 +02003031 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003032 case PEER_SESS_ST_WAITMSG: {
Emeric Brunb3971ab2015-05-12 18:49:09 +02003033 uint32_t msg_len = 0;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02003034 char *msg_cur = trash.area;
3035 char *msg_end = trash.area;
Willy Tarreau1dfd4f12020-11-13 14:10:20 +01003036 unsigned char msg_head[7]; // 2 + 5 for varint32
Emeric Brun2b920a12010-09-23 18:30:22 +02003037 int totl = 0;
3038
Willy Tarreau2d372c22018-11-05 17:12:27 +01003039 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02003040 if (!curpeer) {
Willy Tarreau455caef2022-05-05 20:16:16 +02003041 curpeer = appctx->svcctx;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003042 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02003043 if (curpeer->appctx != appctx) {
3044 appctx->st0 = PEER_SESS_ST_END;
3045 goto switchstate;
3046 }
3047 }
3048
Frédéric Lécaille95203f22019-01-23 19:38:11 +01003049 reql = peer_recv_msg(appctx, (char *)msg_head, sizeof msg_head, &msg_len, &totl);
3050 if (reql <= 0) {
3051 if (reql == -1)
3052 goto switchstate;
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01003053 goto send_msgs;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003054 }
Willy Tarreau86a446e2013-11-25 23:02:37 +01003055
Frédéric Lécaille95203f22019-01-23 19:38:11 +01003056 msg_end += msg_len;
Frédéric Lécaille444243c2019-01-24 15:40:11 +01003057 if (!peer_treat_awaited_msg(appctx, curpeer, msg_head, &msg_cur, msg_end, msg_len, totl))
Emeric Brun2b920a12010-09-23 18:30:22 +02003058 goto switchstate;
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01003059
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01003060 curpeer->flags |= PEER_F_ALIVE;
3061
Emeric Brun2b920a12010-09-23 18:30:22 +02003062 /* skip consumed message */
Willy Tarreau40a9c322022-05-18 15:55:18 +02003063 co_skip(sc_oc(cs), totl);
Emeric Brun2b920a12010-09-23 18:30:22 +02003064 /* loop on that state to peek next message */
Willy Tarreau72d6c162013-04-11 16:14:13 +02003065 goto switchstate;
3066
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01003067send_msgs:
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003068 if (curpeer->flags & PEER_F_HEARTBEAT) {
3069 curpeer->flags &= ~PEER_F_HEARTBEAT;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01003070 repl = peer_send_heartbeatmsg(appctx, curpeer, curpeers);
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003071 if (repl <= 0) {
3072 if (repl == -1)
3073 goto out;
3074 goto switchstate;
3075 }
Frédéric Lécaille33cab3c2019-11-06 11:51:26 +01003076 curpeer->tx_hbt++;
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003077 }
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01003078 /* we get here when a peer_recv_msg() returns 0 in reql */
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01003079 repl = peer_send_msgs(appctx, curpeer, curpeers);
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01003080 if (repl <= 0) {
3081 if (repl == -1)
3082 goto out;
3083 goto switchstate;
Emeric Brun597b26e2016-08-12 11:23:31 +02003084 }
3085
Emeric Brun2b920a12010-09-23 18:30:22 +02003086 /* noting more to do */
3087 goto out;
3088 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003089 case PEER_SESS_ST_EXIT:
Willy Tarreau2d372c22018-11-05 17:12:27 +01003090 if (prev_state == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +02003091 _HA_ATOMIC_DEC(&connected_peers);
Willy Tarreau2d372c22018-11-05 17:12:27 +01003092 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01003093 if (peer_send_status_errormsg(appctx) == -1)
3094 goto out;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003095 appctx->st0 = PEER_SESS_ST_END;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003096 goto switchstate;
3097 case PEER_SESS_ST_ERRSIZE: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01003098 if (prev_state == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +02003099 _HA_ATOMIC_DEC(&connected_peers);
Willy Tarreau2d372c22018-11-05 17:12:27 +01003100 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01003101 if (peer_send_error_size_limitmsg(appctx) == -1)
3102 goto out;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003103 appctx->st0 = PEER_SESS_ST_END;
3104 goto switchstate;
3105 }
3106 case PEER_SESS_ST_ERRPROTO: {
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01003107 TRACE_PROTO("protocol error", PEERS_EV_PROTOERR,
3108 NULL, curpeer, &prev_state);
Frédéric Lécailleec1c10b2019-11-07 15:22:33 +01003109 if (curpeer)
3110 curpeer->proto_err++;
Willy Tarreau2d372c22018-11-05 17:12:27 +01003111 if (prev_state == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +02003112 _HA_ATOMIC_DEC(&connected_peers);
Willy Tarreau2d372c22018-11-05 17:12:27 +01003113 prev_state = appctx->st0;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01003114 if (peer_send_error_protomsg(appctx) == -1) {
3115 TRACE_PROTO("could not send error message", PEERS_EV_PROTOERR);
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01003116 goto out;
Frédéric Lécailleda2b0842021-01-15 16:21:28 +01003117 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02003118 appctx->st0 = PEER_SESS_ST_END;
Willy Tarreau2d372c22018-11-05 17:12:27 +01003119 prev_state = appctx->st0;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003120 }
Tim Duesterhus588b3142020-05-29 14:35:51 +02003121 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003122 case PEER_SESS_ST_END: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01003123 if (prev_state == PEER_SESS_ST_WAITMSG)
Willy Tarreau4781b152021-04-06 13:53:36 +02003124 _HA_ATOMIC_DEC(&connected_peers);
Willy Tarreau2d372c22018-11-05 17:12:27 +01003125 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02003126 if (curpeer) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003127 HA_SPIN_UNLOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02003128 curpeer = NULL;
3129 }
Willy Tarreauf61dd192022-05-27 09:00:19 +02003130 sc_shutw(cs);
3131 sc_shutr(cs);
Willy Tarreau40a9c322022-05-18 15:55:18 +02003132 sc_ic(cs)->flags |= CF_READ_NULL;
Willy Tarreau828824a2015-04-19 17:20:03 +02003133 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02003134 }
3135 }
3136 }
3137out:
Willy Tarreau40a9c322022-05-18 15:55:18 +02003138 sc_oc(cs)->flags |= CF_READ_DONTWAIT;
Emeric Brun80527f52017-06-19 17:46:37 +02003139
3140 if (curpeer)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003141 HA_SPIN_UNLOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun2b920a12010-09-23 18:30:22 +02003142 return;
3143}
3144
Willy Tarreau30576452015-04-13 13:50:30 +02003145static struct applet peer_applet = {
Willy Tarreau3fdb3662012-11-12 00:42:33 +01003146 .obj_type = OBJ_TYPE_APPLET,
Willy Tarreaub24281b2011-02-13 13:16:36 +01003147 .name = "<PEER>", /* used for logging */
3148 .fct = peer_io_handler,
Christopher Faulet6712dc62022-05-12 15:36:11 +02003149 .init = peer_session_init,
Aman Gupta9a13e842012-04-02 18:57:53 -07003150 .release = peer_session_release,
Willy Tarreaub24281b2011-02-13 13:16:36 +01003151};
Emeric Brun2b920a12010-09-23 18:30:22 +02003152
Emeric Brun9ef2ad72019-04-02 17:22:01 +02003153
Emeric Brun2b920a12010-09-23 18:30:22 +02003154/*
3155 * Use this function to force a close of a peer session
3156 */
Emeric Brun9ef2ad72019-04-02 17:22:01 +02003157static void peer_session_forceshutdown(struct peer *peer)
Emeric Brun2b920a12010-09-23 18:30:22 +02003158{
Emeric Brun9ef2ad72019-04-02 17:22:01 +02003159 struct appctx *appctx = peer->appctx;
3160
Frédéric Lécaille5df11902017-06-13 16:39:57 +02003161 /* Note that the peer sessions which have just been created
3162 * (->st0 == PEER_SESS_ST_CONNECT) must not
3163 * be shutdown, if not, the TCP session will never be closed
3164 * and stay in CLOSE_WAIT state after having been closed by
3165 * the remote side.
3166 */
3167 if (!appctx || appctx->st0 == PEER_SESS_ST_CONNECT)
Willy Tarreau7b4b4992013-12-01 09:15:12 +01003168 return;
3169
Willy Tarreau81bc3b02016-10-31 17:37:39 +01003170 if (appctx->applet != &peer_applet)
3171 return;
3172
Emeric Brun9ef2ad72019-04-02 17:22:01 +02003173 __peer_session_deinit(peer);
3174
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003175 appctx->st0 = PEER_SESS_ST_END;
Willy Tarreau78c0c502016-10-31 17:32:20 +01003176 appctx_wakeup(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02003177}
3178
Willy Tarreau91d96282015-03-13 15:47:26 +01003179/* Pre-configures a peers frontend to accept incoming connections */
3180void peers_setup_frontend(struct proxy *fe)
3181{
3182 fe->last_change = now.tv_sec;
Frédéric Lécaillec06b5d42018-04-26 10:06:41 +02003183 fe->cap = PR_CAP_FE | PR_CAP_BE;
Willy Tarreaua389c9e2020-10-07 17:49:42 +02003184 fe->mode = PR_MODE_PEERS;
Willy Tarreau91d96282015-03-13 15:47:26 +01003185 fe->maxconn = 0;
3186 fe->conn_retries = CONN_RETRIES;
3187 fe->timeout.client = MS_TO_TICKS(5000);
Willy Tarreaud1d48d42015-03-13 16:15:46 +01003188 fe->accept = frontend_accept;
Willy Tarreauf87ab942015-03-13 15:55:16 +01003189 fe->default_target = &peer_applet.obj_type;
Willy Tarreau91d96282015-03-13 15:47:26 +01003190 fe->options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
3191}
3192
Emeric Brun2b920a12010-09-23 18:30:22 +02003193/*
Willy Tarreaubd55e312010-11-11 10:55:09 +01003194 * Create a new peer session in assigned state (connect will start automatically)
Emeric Brun2b920a12010-09-23 18:30:22 +02003195 */
Willy Tarreau9df94c22016-10-31 18:42:52 +01003196static struct appctx *peer_session_create(struct peers *peers, struct peer *peer)
Emeric Brun2b920a12010-09-23 18:30:22 +02003197{
Willy Tarreau7b4b4992013-12-01 09:15:12 +01003198 struct appctx *appctx;
Maciej Zdeb34e40852022-05-16 17:27:28 +02003199 unsigned int thr = 0;
3200 int idx;
Emeric Brun2b920a12010-09-23 18:30:22 +02003201
Frédéric Lécaille2b0ba542021-01-18 15:14:39 +01003202 peer->new_conn++;
Frédéric Lécaille54bff832019-03-26 10:25:20 +01003203 peer->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT));
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02003204 peer->heartbeat = TICK_ETERNITY;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003205 peer->statuscode = PEER_SESS_SC_CONNECTCODE;
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003206 peer->last_hdshk = now_ms;
Willy Tarreaud990baf2015-04-05 00:32:03 +02003207
Maciej Zdeb34e40852022-05-16 17:27:28 +02003208 for (idx = 0; idx < global.nbthread; idx++)
3209 thr = peers->applet_count[idx] < peers->applet_count[thr] ? idx : thr;
3210 appctx = appctx_new_on(&peer_applet, NULL, thr);
Christopher Faulet2479e5f2022-01-19 14:50:11 +01003211 if (!appctx)
Christopher Fauleta9e8b392022-03-23 11:01:09 +01003212 goto out_close;
Willy Tarreau455caef2022-05-05 20:16:16 +02003213 appctx->svcctx = (void *)peer;
Willy Tarreaud990baf2015-04-05 00:32:03 +02003214
Christopher Faulet6712dc62022-05-12 15:36:11 +02003215 appctx->st0 = PEER_SESS_ST_CONNECT;
3216 peer->appctx = appctx;
Christopher Fauleta9e8b392022-03-23 11:01:09 +01003217
Maciej Zdebd01be2a2022-05-16 17:26:20 +02003218 HA_ATOMIC_INC(&peers->applet_count[thr]);
Maciej Zdeb34e40852022-05-16 17:27:28 +02003219 appctx_wakeup(appctx);
Willy Tarreau9df94c22016-10-31 18:42:52 +01003220 return appctx;
Emeric Brun2b920a12010-09-23 18:30:22 +02003221
Emeric Brun2b920a12010-09-23 18:30:22 +02003222 out_close:
Willy Tarreaub21d08e2016-10-31 17:46:57 +01003223 return NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02003224}
3225
3226/*
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003227 * Task processing function to manage re-connect, peer session
Willy Tarreauf6c88422021-01-29 12:38:42 +01003228 * tasks wakeup on local update and heartbeat. Let's keep it exported so that it
3229 * resolves in stack traces and "show tasks".
Emeric Brun2b920a12010-09-23 18:30:22 +02003230 */
Willy Tarreau144f84a2021-03-02 16:09:26 +01003231struct task *process_peer_sync(struct task * task, void *context, unsigned int state)
Emeric Brun2b920a12010-09-23 18:30:22 +02003232{
Olivier Houchard9f6af332018-05-25 14:04:04 +02003233 struct peers *peers = context;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003234 struct peer *ps;
3235 struct shared_table *st;
Emeric Brun2b920a12010-09-23 18:30:22 +02003236
3237 task->expire = TICK_ETERNITY;
3238
Emeric Brunb3971ab2015-05-12 18:49:09 +02003239 if (!peers->peers_fe) {
Willy Tarreau46dc1ca2015-05-01 18:32:13 +02003240 /* this one was never started, kill it */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003241 signal_unregister_handler(peers->sighandler);
Olivier Houchard3f795f72019-04-17 22:51:06 +02003242 task_destroy(peers->sync_task);
Willy Tarreau37bb7be2015-09-21 15:24:58 +02003243 peers->sync_task = NULL;
Willy Tarreau46dc1ca2015-05-01 18:32:13 +02003244 return NULL;
3245 }
3246
Emeric Brun80527f52017-06-19 17:46:37 +02003247 /* Acquire lock for all peers of the section */
3248 for (ps = peers->remote; ps; ps = ps->next)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003249 HA_SPIN_LOCK(PEER_LOCK, &ps->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02003250
Emeric Brun2b920a12010-09-23 18:30:22 +02003251 if (!stopping) {
3252 /* Normal case (not soft stop)*/
Emeric Brunb3971ab2015-05-12 18:49:09 +02003253
Emeric Brun2c4ab412021-04-21 16:06:35 +02003254 /* resync timeout set to TICK_ETERNITY means we just start
3255 * a new process and timer was not initialized.
3256 * We must arm this timer to switch to a request to a remote
3257 * node if incoming connection from old local process never
3258 * comes.
3259 */
3260 if (peers->resync_timeout == TICK_ETERNITY)
3261 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(PEER_RESYNC_TIMEOUT));
3262
Emeric Brunb3971ab2015-05-12 18:49:09 +02003263 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL) &&
3264 (!nb_oldpids || tick_is_expired(peers->resync_timeout, now_ms)) &&
3265 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003266 /* Resync from local peer needed
3267 no peer was assigned for the lesson
3268 and no old local peer found
3269 or resync timeout expire */
3270
3271 /* flag no more resync from local, to try resync from remotes */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003272 peers->flags |= PEERS_F_RESYNC_LOCAL;
Emeric Brunccdfbae2021-04-28 12:59:35 +02003273 peers->flags |= PEERS_F_RESYNC_LOCALTIMEOUT;
Emeric Brun2b920a12010-09-23 18:30:22 +02003274
3275 /* reschedule a resync */
Frédéric Lécaille54bff832019-03-26 10:25:20 +01003276 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(PEER_RESYNC_TIMEOUT));
Emeric Brun2b920a12010-09-23 18:30:22 +02003277 }
3278
3279 /* For each session */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003280 for (ps = peers->remote; ps; ps = ps->next) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003281 /* For each remote peers */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003282 if (!ps->local) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01003283 if (!ps->appctx) {
3284 /* no active peer connection */
Emeric Brun2b920a12010-09-23 18:30:22 +02003285 if (ps->statuscode == 0 ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003286 ((ps->statuscode == PEER_SESS_SC_CONNECTCODE ||
Willy Tarreaub4e34da2015-05-20 10:39:04 +02003287 ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003288 ps->statuscode == PEER_SESS_SC_CONNECTEDCODE) &&
Emeric Brun2b920a12010-09-23 18:30:22 +02003289 tick_is_expired(ps->reconnect, now_ms))) {
3290 /* connection never tried
Willy Tarreau9df94c22016-10-31 18:42:52 +01003291 * or previous peer connection established with success
3292 * or previous peer connection failed while connecting
Emeric Brun2b920a12010-09-23 18:30:22 +02003293 * and reconnection timer is expired */
3294
3295 /* retry a connect */
Willy Tarreau9df94c22016-10-31 18:42:52 +01003296 ps->appctx = peer_session_create(peers, ps);
Emeric Brun2b920a12010-09-23 18:30:22 +02003297 }
Willy Tarreaub4e34da2015-05-20 10:39:04 +02003298 else if (!tick_is_expired(ps->reconnect, now_ms)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003299 /* If previous session failed during connection
3300 * but reconnection timer is not expired */
3301
3302 /* reschedule task for reconnect */
3303 task->expire = tick_first(task->expire, ps->reconnect);
3304 }
3305 /* else do nothing */
Willy Tarreau9df94c22016-10-31 18:42:52 +01003306 } /* !ps->appctx */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003307 else if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01003308 /* current peer connection is active and established */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003309 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE) &&
3310 !(peers->flags & PEERS_F_RESYNC_ASSIGN) &&
Emeric Brun2b920a12010-09-23 18:30:22 +02003311 !(ps->flags & PEER_F_LEARN_NOTUP2DATE)) {
3312 /* Resync from a remote is needed
3313 * and no peer was assigned for lesson
3314 * and current peer may be up2date */
3315
3316 /* assign peer for the lesson */
3317 ps->flags |= PEER_F_LEARN_ASSIGN;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003318 peers->flags |= PEERS_F_RESYNC_ASSIGN;
Emeric Brunccdfbae2021-04-28 12:59:35 +02003319 peers->flags |= PEERS_F_RESYNC_REMOTEASSIGN;
Emeric Brun2b920a12010-09-23 18:30:22 +02003320
Willy Tarreau9df94c22016-10-31 18:42:52 +01003321 /* wake up peer handler to handle a request of resync */
Willy Tarreaue5843b32015-04-27 18:40:14 +02003322 appctx_wakeup(ps->appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02003323 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02003324 else {
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003325 int update_to_push = 0;
3326
Emeric Brunb3971ab2015-05-12 18:49:09 +02003327 /* Awake session if there is data to push */
3328 for (st = ps->tables; st ; st = st->next) {
Emeric Brun8e7a13e2021-04-28 11:48:15 +02003329 if (st->last_pushed != st->table->localupdate) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01003330 /* wake up the peer handler to push local updates */
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003331 update_to_push = 1;
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01003332 /* There is no need to send a heartbeat message
3333 * when some updates must be pushed. The remote
3334 * peer will consider <ps> peer as alive when it will
3335 * receive these updates.
3336 */
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003337 ps->flags &= ~PEER_F_HEARTBEAT;
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01003338 /* Re-schedule another one later. */
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003339 ps->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01003340 /* We are going to send updates, let's ensure we will
3341 * come back to send heartbeat messages or to reconnect.
3342 */
3343 task->expire = tick_first(ps->reconnect, ps->heartbeat);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003344 appctx_wakeup(ps->appctx);
3345 break;
3346 }
3347 }
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01003348 /* When there are updates to send we do not reconnect
3349 * and do not send heartbeat message either.
3350 */
3351 if (!update_to_push) {
3352 if (tick_is_expired(ps->reconnect, now_ms)) {
3353 if (ps->flags & PEER_F_ALIVE) {
3354 /* This peer was alive during a 'reconnect' period.
3355 * Flag it as not alive again for the next period.
3356 */
3357 ps->flags &= ~PEER_F_ALIVE;
Frédéric Lécaille54bff832019-03-26 10:25:20 +01003358 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT));
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01003359 }
3360 else {
Willy Tarreau52bf8392020-03-08 00:42:37 +01003361 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
Frédéric Lécaillebaeb9192020-10-14 11:50:26 +02003362 ps->heartbeat = TICK_ETERNITY;
Emeric Brun9ef2ad72019-04-02 17:22:01 +02003363 peer_session_forceshutdown(ps);
Frédéric Lécailleec1c10b2019-11-07 15:22:33 +01003364 ps->no_hbt++;
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01003365 }
3366 }
3367 else if (tick_is_expired(ps->heartbeat, now_ms)) {
3368 ps->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
3369 ps->flags |= PEER_F_HEARTBEAT;
3370 appctx_wakeup(ps->appctx);
3371 }
Frédéric Lécailleb7405c12019-03-27 14:32:39 +01003372 task->expire = tick_first(ps->reconnect, ps->heartbeat);
Frédéric Lécaille645635d2019-02-11 17:49:39 +01003373 }
Emeric Brun2b920a12010-09-23 18:30:22 +02003374 }
3375 /* else do nothing */
3376 } /* SUCCESSCODE */
3377 } /* !ps->peer->local */
3378 } /* for */
3379
3380 /* Resync from remotes expired: consider resync is finished */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003381 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE) &&
3382 !(peers->flags & PEERS_F_RESYNC_ASSIGN) &&
3383 tick_is_expired(peers->resync_timeout, now_ms)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003384 /* Resync from remote peer needed
3385 * no peer was assigned for the lesson
3386 * and resync timeout expire */
3387
3388 /* flag no more resync from remote, consider resync is finished */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003389 peers->flags |= PEERS_F_RESYNC_REMOTE;
Emeric Brunccdfbae2021-04-28 12:59:35 +02003390 peers->flags |= PEERS_F_RESYNC_REMOTETIMEOUT;
Emeric Brun2b920a12010-09-23 18:30:22 +02003391 }
3392
Emeric Brunb3971ab2015-05-12 18:49:09 +02003393 if ((peers->flags & PEERS_RESYNC_STATEMASK) != PEERS_RESYNC_FINISHED) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003394 /* Resync not finished*/
Frédéric Lécaille5d6e5f82017-05-29 13:47:16 +02003395 /* reschedule task to resync timeout if not expired, to ended resync if needed */
3396 if (!tick_is_expired(peers->resync_timeout, now_ms))
3397 task->expire = tick_first(task->expire, peers->resync_timeout);
Emeric Brun2b920a12010-09-23 18:30:22 +02003398 }
3399 } /* !stopping */
3400 else {
3401 /* soft stop case */
Willy Tarreau086735a2018-11-05 15:09:47 +01003402 if (state & TASK_WOKEN_SIGNAL) {
Joseph Herlant82b2f542018-11-15 12:19:14 -08003403 /* We've just received the signal */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003404 if (!(peers->flags & PEERS_F_DONOTSTOP)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003405 /* add DO NOT STOP flag if not present */
Willy Tarreau4781b152021-04-06 13:53:36 +02003406 _HA_ATOMIC_INC(&jobs);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003407 peers->flags |= PEERS_F_DONOTSTOP;
Emeric Brun2b920a12010-09-23 18:30:22 +02003408
Emeric Bruncbfe5eb2021-04-22 18:20:37 +02003409 /* disconnect all connected peers to process a local sync
3410 * this must be done only the first time we are switching
3411 * in stopping state
Emeric Brun80527f52017-06-19 17:46:37 +02003412 */
Emeric Bruncbfe5eb2021-04-22 18:20:37 +02003413 for (ps = peers->remote; ps; ps = ps->next) {
3414 /* we're killing a connection, we must apply a random delay before
3415 * retrying otherwise the other end will do the same and we can loop
3416 * for a while.
3417 */
3418 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
3419 if (ps->appctx) {
3420 peer_session_forceshutdown(ps);
3421 }
Emeric Brun2b920a12010-09-23 18:30:22 +02003422 }
3423 }
3424 }
Emeric Brun2b920a12010-09-23 18:30:22 +02003425
Emeric Brunb3971ab2015-05-12 18:49:09 +02003426 ps = peers->local;
Emeric Brun2b920a12010-09-23 18:30:22 +02003427 if (ps->flags & PEER_F_TEACH_COMPLETE) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02003428 if (peers->flags & PEERS_F_DONOTSTOP) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003429 /* resync of new process was complete, current process can die now */
Willy Tarreau4781b152021-04-06 13:53:36 +02003430 _HA_ATOMIC_DEC(&jobs);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003431 peers->flags &= ~PEERS_F_DONOTSTOP;
3432 for (st = ps->tables; st ; st = st->next)
Emeric Brun2cc201f2021-04-23 12:21:26 +02003433 HA_ATOMIC_DEC(&st->table->refcnt);
Emeric Brun2b920a12010-09-23 18:30:22 +02003434 }
3435 }
Willy Tarreau9df94c22016-10-31 18:42:52 +01003436 else if (!ps->appctx) {
3437 /* If there's no active peer connection */
Emeric Brun2b920a12010-09-23 18:30:22 +02003438 if (ps->statuscode == 0 ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01003439 ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
3440 ps->statuscode == PEER_SESS_SC_CONNECTEDCODE ||
3441 ps->statuscode == PEER_SESS_SC_TRYAGAIN) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003442 /* connection never tried
Willy Tarreau9df94c22016-10-31 18:42:52 +01003443 * or previous peer connection was successfully established
3444 * or previous tcp connect succeeded but init state incomplete
Emeric Brun2b920a12010-09-23 18:30:22 +02003445 * or during previous connect, peer replies a try again statuscode */
3446
Emeric Bruncbfe5eb2021-04-22 18:20:37 +02003447 /* connect to the local peer if we must push a local sync */
3448 if (peers->flags & PEERS_F_DONOTSTOP) {
3449 peer_session_create(peers, ps);
3450 }
Emeric Brun2b920a12010-09-23 18:30:22 +02003451 }
3452 else {
3453 /* Other error cases */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003454 if (peers->flags & PEERS_F_DONOTSTOP) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003455 /* unable to resync new process, current process can die now */
Willy Tarreau4781b152021-04-06 13:53:36 +02003456 _HA_ATOMIC_DEC(&jobs);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003457 peers->flags &= ~PEERS_F_DONOTSTOP;
3458 for (st = ps->tables; st ; st = st->next)
Emeric Brun2cc201f2021-04-23 12:21:26 +02003459 HA_ATOMIC_DEC(&st->table->refcnt);
Emeric Brun2b920a12010-09-23 18:30:22 +02003460 }
3461 }
3462 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02003463 else if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE ) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01003464 /* current peer connection is active and established
3465 * wake up all peer handlers to push remaining local updates */
Emeric Brunb3971ab2015-05-12 18:49:09 +02003466 for (st = ps->tables; st ; st = st->next) {
Emeric Brun8e7a13e2021-04-28 11:48:15 +02003467 if (st->last_pushed != st->table->localupdate) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02003468 appctx_wakeup(ps->appctx);
3469 break;
3470 }
3471 }
Emeric Brun2b920a12010-09-23 18:30:22 +02003472 }
3473 } /* stopping */
Emeric Brun80527f52017-06-19 17:46:37 +02003474
3475 /* Release lock for all peers of the section */
3476 for (ps = peers->remote; ps; ps = ps->next)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003477 HA_SPIN_UNLOCK(PEER_LOCK, &ps->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02003478
Emeric Brun2b920a12010-09-23 18:30:22 +02003479 /* Wakeup for re-connect */
3480 return task;
3481}
3482
Emeric Brunb3971ab2015-05-12 18:49:09 +02003483
Emeric Brun2b920a12010-09-23 18:30:22 +02003484/*
Willy Tarreaud9443442018-10-15 11:18:03 +02003485 * returns 0 in case of error.
Emeric Brun2b920a12010-09-23 18:30:22 +02003486 */
Willy Tarreaud9443442018-10-15 11:18:03 +02003487int peers_init_sync(struct peers *peers)
Emeric Brun2b920a12010-09-23 18:30:22 +02003488{
Emeric Brun2b920a12010-09-23 18:30:22 +02003489 struct peer * curpeer;
Emeric Brun2b920a12010-09-23 18:30:22 +02003490
Emeric Brun2b920a12010-09-23 18:30:22 +02003491 for (curpeer = peers->remote; curpeer; curpeer = curpeer->next) {
Emeric Brun2b920a12010-09-23 18:30:22 +02003492 peers->peers_fe->maxconn += 3;
3493 }
3494
Willy Tarreaubeeabf52021-10-01 18:23:30 +02003495 peers->sync_task = task_new_anywhere();
Willy Tarreaud9443442018-10-15 11:18:03 +02003496 if (!peers->sync_task)
3497 return 0;
3498
Maciej Zdebd01be2a2022-05-16 17:26:20 +02003499 memset(peers->applet_count, 0, sizeof(peers->applet_count));
Emeric Brunb3971ab2015-05-12 18:49:09 +02003500 peers->sync_task->process = process_peer_sync;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003501 peers->sync_task->context = (void *)peers;
3502 peers->sighandler = signal_register_task(0, peers->sync_task, 0);
3503 task_wakeup(peers->sync_task, TASK_WOKEN_INIT);
Willy Tarreaud9443442018-10-15 11:18:03 +02003504 return 1;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003505}
3506
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003507/*
3508 * Allocate a cache a dictionary entries used upon transmission.
3509 */
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003510static struct dcache_tx *new_dcache_tx(size_t max_entries)
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003511{
3512 struct dcache_tx *d;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003513 struct ebpt_node *entries;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003514
3515 d = malloc(sizeof *d);
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003516 entries = calloc(max_entries, sizeof *entries);
3517 if (!d || !entries)
3518 goto err;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003519
3520 d->lru_key = 0;
Frédéric Lécailleb65717f2019-06-07 14:25:25 +02003521 d->prev_lookup = NULL;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003522 d->cached_entries = EB_ROOT_UNIQUE;
3523 d->entries = entries;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003524
3525 return d;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003526
3527 err:
3528 free(d);
3529 free(entries);
3530 return NULL;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003531}
3532
3533/*
3534 * Allocate a cache of dictionary entries with <name> as name and <max_entries>
3535 * as maximum of entries.
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003536 * Return the dictionary cache if succeeded, NULL if not.
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003537 * Must be deallocated calling free_dcache().
3538 */
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003539static struct dcache *new_dcache(size_t max_entries)
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003540{
3541 struct dcache_tx *dc_tx;
3542 struct dcache *dc;
3543 struct dcache_rx *dc_rx;
3544
3545 dc = calloc(1, sizeof *dc);
3546 dc_tx = new_dcache_tx(max_entries);
3547 dc_rx = calloc(max_entries, sizeof *dc_rx);
3548 if (!dc || !dc_tx || !dc_rx)
3549 goto err;
3550
3551 dc->tx = dc_tx;
3552 dc->rx = dc_rx;
3553 dc->max_entries = max_entries;
3554
3555 return dc;
3556
3557 err:
3558 free(dc);
3559 free(dc_tx);
3560 free(dc_rx);
3561 return NULL;
3562}
3563
3564/*
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003565 * Look for the dictionary entry with the value of <i> in <d> cache of dictionary
3566 * entries used upon transmission.
3567 * Return the entry if found, NULL if not.
3568 */
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003569static struct ebpt_node *dcache_tx_lookup_value(struct dcache_tx *d,
3570 struct dcache_tx_entry *i)
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003571{
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003572 return ebpt_lookup(&d->cached_entries, i->entry.key);
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003573}
3574
3575/*
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003576 * Flush <dc> cache.
3577 * Always succeeds.
3578 */
3579static inline void flush_dcache(struct peer *peer)
3580{
3581 int i;
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02003582 struct dcache *dc = peer->dcache;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003583
Frédéric Lécailleea875e62020-11-12 21:01:54 +01003584 for (i = 0; i < dc->max_entries; i++) {
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003585 ebpt_delete(&dc->tx->entries[i]);
Frédéric Lécailleea875e62020-11-12 21:01:54 +01003586 dc->tx->entries[i].key = NULL;
Thayne McCombs92149f92020-11-20 01:28:26 -07003587 dict_entry_unref(&server_key_dict, dc->rx[i].de);
3588 dc->rx[i].de = NULL;
Frédéric Lécailleea875e62020-11-12 21:01:54 +01003589 }
3590 dc->tx->prev_lookup = NULL;
3591 dc->tx->lru_key = 0;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003592
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003593 memset(dc->rx, 0, dc->max_entries * sizeof *dc->rx);
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003594}
3595
3596/*
3597 * Insert a dictionary entry in <dc> cache part used upon transmission (->tx)
3598 * with information provided by <i> dictionary cache entry (especially the value
3599 * to be inserted if not already). Return <i> if already present in the cache
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003600 * or something different of <i> if not.
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003601 */
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003602static struct ebpt_node *dcache_tx_insert(struct dcache *dc, struct dcache_tx_entry *i)
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003603{
3604 struct dcache_tx *dc_tx;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003605 struct ebpt_node *o;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003606
3607 dc_tx = dc->tx;
Frédéric Lécailleb65717f2019-06-07 14:25:25 +02003608
3609 if (dc_tx->prev_lookup && dc_tx->prev_lookup->key == i->entry.key) {
3610 o = dc_tx->prev_lookup;
3611 } else {
3612 o = dcache_tx_lookup_value(dc_tx, i);
3613 if (o) {
3614 /* Save it */
3615 dc_tx->prev_lookup = o;
3616 }
3617 }
3618
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003619 if (o) {
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003620 /* Copy the ID. */
3621 i->id = o - dc->tx->entries;
3622 return &i->entry;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003623 }
3624
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003625 /* The new entry to put in cache */
Frédéric Lécailleb65717f2019-06-07 14:25:25 +02003626 dc_tx->prev_lookup = o = &dc_tx->entries[dc_tx->lru_key];
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003627
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003628 ebpt_delete(o);
3629 o->key = i->entry.key;
3630 ebpt_insert(&dc_tx->cached_entries, o);
3631 i->id = dc_tx->lru_key;
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003632
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003633 /* Update the index for the next entry to put in cache */
3634 dc_tx->lru_key = (dc_tx->lru_key + 1) & (dc->max_entries - 1);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003635
Frédéric Lécaille74167b22019-05-28 19:02:42 +02003636 return o;
3637}
Emeric Brunb3971ab2015-05-12 18:49:09 +02003638
3639/*
Frédéric Lécaille8d78fa72019-05-20 18:22:52 +02003640 * Allocate a dictionary cache for each peer of <peers> section.
3641 * Return 1 if succeeded, 0 if not.
3642 */
3643int peers_alloc_dcache(struct peers *peers)
3644{
3645 struct peer *p;
3646
3647 for (p = peers->remote; p; p = p->next) {
3648 p->dcache = new_dcache(PEER_STKT_CACHE_MAX_ENTRIES);
3649 if (!p->dcache)
3650 return 0;
3651 }
3652
3653 return 1;
3654}
3655
3656/*
Emeric Brunb3971ab2015-05-12 18:49:09 +02003657 * Function used to register a table for sync on a group of peers
Remi Tricot-Le Breton208ff012021-05-12 17:39:04 +02003658 * Returns 0 in case of success.
Emeric Brunb3971ab2015-05-12 18:49:09 +02003659 */
Remi Tricot-Le Breton208ff012021-05-12 17:39:04 +02003660int peers_register_table(struct peers *peers, struct stktable *table)
Emeric Brunb3971ab2015-05-12 18:49:09 +02003661{
3662 struct shared_table *st;
3663 struct peer * curpeer;
3664 int id = 0;
Remi Tricot-Le Breton208ff012021-05-12 17:39:04 +02003665 int retval = 0;
Emeric Brunb3971ab2015-05-12 18:49:09 +02003666
3667 for (curpeer = peers->remote; curpeer; curpeer = curpeer->next) {
Vincent Bernat02779b62016-04-03 13:48:43 +02003668 st = calloc(1,sizeof(*st));
Remi Tricot-Le Breton208ff012021-05-12 17:39:04 +02003669 if (!st) {
3670 retval = 1;
3671 break;
3672 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02003673 st->table = table;
3674 st->next = curpeer->tables;
3675 if (curpeer->tables)
3676 id = curpeer->tables->local_id;
3677 st->local_id = id + 1;
3678
Emeric Brun2cc201f2021-04-23 12:21:26 +02003679 /* If peer is local we inc table
3680 * refcnt to protect against flush
3681 * until this process pushed all
3682 * table content to the new one
3683 */
3684 if (curpeer->local)
3685 HA_ATOMIC_INC(&st->table->refcnt);
Emeric Brunb3971ab2015-05-12 18:49:09 +02003686 curpeer->tables = st;
3687 }
3688
3689 table->sync_task = peers->sync_task;
Remi Tricot-Le Breton208ff012021-05-12 17:39:04 +02003690
3691 return retval;
Emeric Brun2b920a12010-09-23 18:30:22 +02003692}
3693
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003694/* context used by a "show peers" command */
3695struct show_peers_ctx {
Willy Tarreau3a31e372022-05-03 14:58:47 +02003696 void *target; /* if non-null, dump only this section and stop */
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003697 struct peers *peers; /* "peers" section being currently dumped. */
3698 struct peer *peer; /* "peer" being currently dumped. */
3699 int flags; /* non-zero if "dict" dump requested */
Willy Tarreau3a31e372022-05-03 14:58:47 +02003700 enum {
Willy Tarreauce9123c2022-05-03 15:04:25 +02003701 STATE_HEAD = 0, /* dump the section's header */
Willy Tarreau3a31e372022-05-03 14:58:47 +02003702 STATE_PEER, /* dump the whole peer */
3703 STATE_DONE, /* finished */
3704 } state; /* parser's state */
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003705};
3706
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003707/*
3708 * Parse the "show peers" command arguments.
3709 * Returns 0 if succeeded, 1 if not with the ->msg of the appctx set as
3710 * error message.
3711 */
3712static int cli_parse_show_peers(char **args, char *payload, struct appctx *appctx, void *private)
3713{
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003714 struct show_peers_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003715
Willy Tarreau49962b52021-02-12 16:56:22 +01003716 if (strcmp(args[2], "dict") == 0) {
3717 /* show the dictionaries (large dump) */
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003718 ctx->flags |= PEERS_SHOW_F_DICT;
Willy Tarreau49962b52021-02-12 16:56:22 +01003719 args++;
3720 } else if (strcmp(args[2], "-") == 0)
3721 args++; // allows to show a section called "dict"
3722
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003723 if (*args[2]) {
3724 struct peers *p;
3725
3726 for (p = cfg_peers; p; p = p->next) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01003727 if (strcmp(p->id, args[2]) == 0) {
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003728 ctx->target = p;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003729 break;
3730 }
3731 }
3732
Willy Tarreau9d008692019-08-09 11:21:01 +02003733 if (!p)
3734 return cli_err(appctx, "No such peers\n");
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003735 }
3736
Willy Tarreauce9123c2022-05-03 15:04:25 +02003737 /* where to start from */
3738 ctx->peers = ctx->target ? ctx->target : cfg_peers;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003739 return 0;
3740}
3741
3742/*
3743 * This function dumps the peer state information of <peers> "peers" section.
3744 * Returns 0 if the output buffer is full and needs to be called again, non-zero if not.
3745 * Dedicated to be called by cli_io_handler_show_peers() cli I/O handler.
3746 */
Willy Tarreaud0a06d52022-05-18 15:07:19 +02003747static int peers_dump_head(struct buffer *msg, struct appctx *appctx, struct peers *peers)
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003748{
3749 struct tm tm;
3750
3751 get_localtime(peers->last_change, &tm);
Willy Tarreau1ad64ac2020-09-24 08:48:08 +02003752 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 +02003753 peers,
3754 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
3755 tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau1ad64ac2020-09-24 08:48:08 +02003756 peers->id, peers->disabled, peers->flags,
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003757 peers->resync_timeout ?
3758 tick_is_expired(peers->resync_timeout, now_ms) ? "<PAST>" :
3759 human_time(TICKS_TO_MS(peers->resync_timeout - now_ms),
Emeric Brun0bbec0f2019-04-18 11:39:43 +02003760 TICKS_TO_MS(1000)) : "<NEVER>",
3761 peers->sync_task ? peers->sync_task->calls : 0);
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003762
Willy Tarreaud0a06d52022-05-18 15:07:19 +02003763 if (applet_putchk(appctx, msg) == -1)
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003764 return 0;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003765
3766 return 1;
3767}
3768
3769/*
3770 * This function dumps <peer> state information.
3771 * Returns 0 if the output buffer is full and needs to be called again, non-zero
3772 * if not. Dedicated to be called by cli_io_handler_show_peers() cli I/O handler.
3773 */
Willy Tarreau4596fe22022-05-17 19:07:51 +02003774static int peers_dump_peer(struct buffer *msg, struct stconn *cs, struct peer *peer, int flags)
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003775{
3776 struct connection *conn;
3777 char pn[INET6_ADDRSTRLEN];
Willy Tarreau4596fe22022-05-17 19:07:51 +02003778 struct stconn *peer_cs;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003779 struct stream *peer_s;
3780 struct appctx *appctx;
3781 struct shared_table *st;
3782
3783 addr_to_str(&peer->addr, pn, sizeof pn);
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003784 chunk_appendf(msg, " %p: id=%s(%s,%s) addr=%s:%d last_status=%s",
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003785 peer, peer->id,
3786 peer->local ? "local" : "remote",
Frédéric Lécaillee7e2b212020-10-05 12:33:07 +02003787 peer->appctx ? "active" : "inactive",
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003788 pn, get_host_port(&peer->addr),
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003789 statuscode_str(peer->statuscode));
3790
3791 chunk_appendf(msg, " last_hdshk=%s\n",
3792 peer->last_hdshk ? human_time(TICKS_TO_MS(now_ms - peer->last_hdshk),
3793 TICKS_TO_MS(1000)) : "<NEVER>");
3794
3795 chunk_appendf(msg, " reconnect=%s",
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003796 peer->reconnect ?
3797 tick_is_expired(peer->reconnect, now_ms) ? "<PAST>" :
3798 human_time(TICKS_TO_MS(peer->reconnect - now_ms),
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003799 TICKS_TO_MS(1000)) : "<NEVER>");
3800
3801 chunk_appendf(msg, " heartbeat=%s",
3802 peer->heartbeat ?
3803 tick_is_expired(peer->heartbeat, now_ms) ? "<PAST>" :
3804 human_time(TICKS_TO_MS(peer->heartbeat - now_ms),
3805 TICKS_TO_MS(1000)) : "<NEVER>");
3806
3807 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 +01003808 peer->confirm, peer->tx_hbt, peer->rx_hbt,
Frédéric Lécaille3fc0fe02020-10-08 09:46:24 +02003809 peer->no_hbt, peer->new_conn, peer->proto_err, peer->coll);
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003810
3811 chunk_appendf(&trash, " flags=0x%x", peer->flags);
3812
3813 appctx = peer->appctx;
3814 if (!appctx)
Frédéric Lécaille470502b2019-11-06 10:41:03 +01003815 goto table_info;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003816
Emeric Brun0bbec0f2019-04-18 11:39:43 +02003817 chunk_appendf(&trash, " appctx:%p st0=%d st1=%d task_calls=%u", appctx, appctx->st0, appctx->st1,
3818 appctx->t ? appctx->t->calls : 0);
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003819
Willy Tarreau0698c802022-05-11 14:09:57 +02003820 peer_cs = appctx_cs(peer->appctx);
Willy Tarreauea27f482022-05-18 16:10:52 +02003821 peer_s = __sc_strm(peer_cs);
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003822
Willy Tarreau74568cf2022-05-27 09:03:30 +02003823 chunk_appendf(&trash, " state=%s", sc_state_str(sc_opposite(peer_cs)->state));
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003824
3825 conn = objt_conn(strm_orig(peer_s));
3826 if (conn)
3827 chunk_appendf(&trash, "\n xprt=%s", conn_get_xprt_name(conn));
3828
Willy Tarreau3ca14902019-07-17 14:53:15 +02003829 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 +02003830 case AF_INET:
3831 case AF_INET6:
Willy Tarreau3ca14902019-07-17 14:53:15 +02003832 chunk_appendf(&trash, " src=%s:%d", pn, get_host_port(conn->src));
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003833 break;
3834 case AF_UNIX:
3835 chunk_appendf(&trash, " src=unix:%d", strm_li(peer_s)->luid);
3836 break;
3837 }
3838
Willy Tarreau3ca14902019-07-17 14:53:15 +02003839 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 +02003840 case AF_INET:
3841 case AF_INET6:
Willy Tarreau3ca14902019-07-17 14:53:15 +02003842 chunk_appendf(&trash, " addr=%s:%d", pn, get_host_port(conn->dst));
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003843 break;
3844 case AF_UNIX:
3845 chunk_appendf(&trash, " addr=unix:%d", strm_li(peer_s)->luid);
3846 break;
3847 }
3848
Frédéric Lécaille470502b2019-11-06 10:41:03 +01003849 table_info:
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003850 if (peer->remote_table)
3851 chunk_appendf(&trash, "\n remote_table:%p id=%s local_id=%d remote_id=%d",
3852 peer->remote_table,
3853 peer->remote_table->table->id,
3854 peer->remote_table->local_id,
3855 peer->remote_table->remote_id);
3856
3857 if (peer->last_local_table)
3858 chunk_appendf(&trash, "\n last_local_table:%p id=%s local_id=%d remote_id=%d",
3859 peer->last_local_table,
3860 peer->last_local_table->table->id,
3861 peer->last_local_table->local_id,
3862 peer->last_local_table->remote_id);
3863
3864 if (peer->tables) {
3865 chunk_appendf(&trash, "\n shared tables:");
3866 for (st = peer->tables; st; st = st->next) {
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003867 int i, count;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003868 struct stktable *t;
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003869 struct dcache *dcache;
3870
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003871 t = st->table;
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003872 dcache = peer->dcache;
3873
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003874 chunk_appendf(&trash, "\n %p local_id=%d remote_id=%d "
3875 "flags=0x%x remote_data=0x%llx",
3876 st, st->local_id, st->remote_id,
3877 st->flags, (unsigned long long)st->remote_data);
3878 chunk_appendf(&trash, "\n last_acked=%u last_pushed=%u last_get=%u"
3879 " teaching_origin=%u update=%u",
3880 st->last_acked, st->last_pushed, st->last_get,
3881 st->teaching_origin, st->update);
3882 chunk_appendf(&trash, "\n table:%p id=%s update=%u localupdate=%u"
Emeric Brun2cc201f2021-04-23 12:21:26 +02003883 " commitupdate=%u refcnt=%u",
3884 t, t->id, t->update, t->localupdate, t->commitupdate, t->refcnt);
Willy Tarreau49962b52021-02-12 16:56:22 +01003885 if (flags & PEERS_SHOW_F_DICT) {
3886 chunk_appendf(&trash, "\n TX dictionary cache:");
3887 count = 0;
3888 for (i = 0; i < dcache->max_entries; i++) {
3889 struct ebpt_node *node;
3890 struct dict_entry *de;
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003891
Willy Tarreau49962b52021-02-12 16:56:22 +01003892 node = &dcache->tx->entries[i];
3893 if (!node->key)
3894 break;
Frédéric Lécaille6c391982019-06-06 11:34:03 +02003895
Willy Tarreau49962b52021-02-12 16:56:22 +01003896 if (!count++)
3897 chunk_appendf(&trash, "\n ");
3898 de = node->key;
3899 chunk_appendf(&trash, " %3u -> %s", i, (char *)de->value.key);
3900 count &= 0x3;
3901 }
3902 chunk_appendf(&trash, "\n RX dictionary cache:");
3903 count = 0;
3904 for (i = 0; i < dcache->max_entries; i++) {
3905 if (!count++)
3906 chunk_appendf(&trash, "\n ");
3907 chunk_appendf(&trash, " %3u -> %s", i,
3908 dcache->rx[i].de ?
3909 (char *)dcache->rx[i].de->value.key : "-");
3910 count &= 0x3;
3911 }
3912 } else {
3913 chunk_appendf(&trash, "\n Dictionary cache not dumped (use \"show peers dict\")");
Frédéric Lécaille62b0b0b2019-05-29 16:20:41 +02003914 }
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003915 }
3916 }
3917
3918 end:
3919 chunk_appendf(&trash, "\n");
Willy Tarreaud0a06d52022-05-18 15:07:19 +02003920 if (applet_putchk(appctx, msg) == -1)
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003921 return 0;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003922
3923 return 1;
3924}
3925
3926/*
3927 * This function dumps all the peers of "peers" section.
3928 * Returns 0 if the output buffer is full and needs to be called
3929 * again, non-zero if not. It proceeds in an isolated thread, so
3930 * there is no thread safety issue here.
3931 */
3932static int cli_io_handler_show_peers(struct appctx *appctx)
3933{
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003934 struct show_peers_ctx *ctx = appctx->svcctx;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003935 int ret = 0, first_peers = 1;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003936
3937 thread_isolate();
3938
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003939 chunk_reset(&trash);
3940
Willy Tarreau3a31e372022-05-03 14:58:47 +02003941 while (ctx->state != STATE_DONE) {
3942 switch (ctx->state) {
Willy Tarreau3a31e372022-05-03 14:58:47 +02003943 case STATE_HEAD:
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003944 if (!ctx->peers) {
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003945 /* No more peers list. */
Willy Tarreau3a31e372022-05-03 14:58:47 +02003946 ctx->state = STATE_DONE;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003947 }
3948 else {
3949 if (!first_peers)
3950 chunk_appendf(&trash, "\n");
3951 else
3952 first_peers = 0;
Willy Tarreaud0a06d52022-05-18 15:07:19 +02003953 if (!peers_dump_head(&trash, appctx, ctx->peers))
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003954 goto out;
3955
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003956 ctx->peer = ctx->peers->remote;
3957 ctx->peers = ctx->peers->next;
Willy Tarreau3a31e372022-05-03 14:58:47 +02003958 ctx->state = STATE_PEER;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003959 }
3960 break;
3961
Willy Tarreau3a31e372022-05-03 14:58:47 +02003962 case STATE_PEER:
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003963 if (!ctx->peer) {
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003964 /* End of peer list */
Willy Tarreauce9123c2022-05-03 15:04:25 +02003965 if (!ctx->target)
3966 ctx->state = STATE_HEAD; // next one
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003967 else
Willy Tarreau3a31e372022-05-03 14:58:47 +02003968 ctx->state = STATE_DONE;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003969 }
3970 else {
Willy Tarreau0698c802022-05-11 14:09:57 +02003971 if (!peers_dump_peer(&trash, appctx_cs(appctx), ctx->peer, ctx->flags))
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003972 goto out;
3973
Willy Tarreaucb8bf172022-05-03 14:26:31 +02003974 ctx->peer = ctx->peer->next;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003975 }
Willy Tarreau3a31e372022-05-03 14:58:47 +02003976 break;
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003977
Willy Tarreau3a31e372022-05-03 14:58:47 +02003978 default:
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003979 break;
3980 }
3981 }
3982 ret = 1;
3983 out:
3984 thread_release();
3985 return ret;
3986}
3987
3988/*
3989 * CLI keywords.
3990 */
3991static struct cli_kw_list cli_kws = {{ }, {
Willy Tarreaub205bfd2021-05-07 11:38:37 +02003992 { { "show", "peers", NULL }, "show peers [dict|-] [section] : dump some information about all the peers or this peers section", cli_parse_show_peers, cli_io_handler_show_peers, },
Frédéric Lécaille95679dc2019-04-15 10:25:27 +02003993 {},
3994}};
3995
3996/* Register cli keywords */
3997INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);