blob: 6367802caf83521c9cf2fa7da333a357da4dea29 [file] [log] [blame]
Emeric Brun2b920a12010-09-23 18:30:22 +02001/*
Emeric Brunb3971ab2015-05-12 18:49:09 +02002 * Peer synchro management.
Emeric Brun2b920a12010-09-23 18:30:22 +02003 *
4 * Copyright 2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <errno.h>
14#include <fcntl.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18
19#include <sys/socket.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22
23#include <common/compat.h>
24#include <common/config.h>
25#include <common/time.h>
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +020026#include <common/standard.h>
Christopher Faulet8d8aa0d2017-05-30 15:36:50 +020027#include <common/hathreads.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020028
29#include <types/global.h>
Willy Tarreau3fdb3662012-11-12 00:42:33 +010030#include <types/listener.h>
31#include <types/obj_type.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020032#include <types/peers.h>
33
34#include <proto/acl.h>
Willy Tarreau8a8d83b2015-04-13 13:24:54 +020035#include <proto/applet.h>
Willy Tarreauc7e42382012-08-24 19:22:53 +020036#include <proto/channel.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020037#include <proto/fd.h>
Willy Tarreaud1d48d42015-03-13 16:15:46 +010038#include <proto/frontend.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020039#include <proto/log.h>
40#include <proto/hdr_idx.h>
Willy Tarreau53a47662017-08-28 10:53:00 +020041#include <proto/mux_pt.h>
Frédéric Lécaille1055e682018-04-26 14:35:21 +020042#include <proto/peers.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020043#include <proto/proxy.h>
Willy Tarreaufeb76402015-04-03 14:10:06 +020044#include <proto/session.h>
Willy Tarreau87b09662015-04-03 00:22:06 +020045#include <proto/stream.h>
Willy Tarreau22ec1ea2014-11-27 20:45:39 +010046#include <proto/signal.h>
47#include <proto/stick_table.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020048#include <proto/stream_interface.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020049#include <proto/task.h>
Emeric Brun2b920a12010-09-23 18:30:22 +020050
51
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/******************************/
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010059#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_DONOTSTOP 0x00010000 /* Main table sync task block process during soft stop
64 to push data to new process */
Emeric Brun2b920a12010-09-23 18:30:22 +020065
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010066#define PEERS_RESYNC_STATEMASK (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE)
67#define PEERS_RESYNC_FROMLOCAL 0x00000000
68#define PEERS_RESYNC_FROMREMOTE PEERS_F_RESYNC_LOCAL
69#define PEERS_RESYNC_FINISHED (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE)
Emeric Brunb3971ab2015-05-12 18:49:09 +020070
71/***********************************/
72/* Current shared table sync state */
73/***********************************/
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010074#define SHTABLE_F_TEACH_STAGE1 0x00000001 /* Teach state 1 complete */
75#define SHTABLE_F_TEACH_STAGE2 0x00000002 /* Teach state 2 complete */
Emeric Brun2b920a12010-09-23 18:30:22 +020076
77/******************************/
78/* Remote peer teaching state */
79/******************************/
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010080#define PEER_F_TEACH_PROCESS 0x00000001 /* Teach a lesson to current peer */
81#define PEER_F_TEACH_FINISHED 0x00000008 /* Teach conclude, (wait for confirm) */
82#define PEER_F_TEACH_COMPLETE 0x00000010 /* All that we know already taught to current peer, used only for a local peer */
83#define PEER_F_LEARN_ASSIGN 0x00000100 /* Current peer was assigned for a lesson */
84#define PEER_F_LEARN_NOTUP2DATE 0x00000200 /* Learn from peer finished but peer is not up to date */
85#define PEER_F_ALIVE 0x20000000 /* Used to flag a peer a alive. */
86#define PEER_F_HEARTBEAT 0x40000000 /* Heartbeat message to send. */
87#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 +020088
Frédéric Lécailleaba44a22019-03-26 10:18:07 +010089#define PEER_TEACH_RESET ~(PEER_F_TEACH_PROCESS|PEER_F_TEACH_FINISHED) /* PEER_F_TEACH_COMPLETE should never be reset */
90#define PEER_LEARN_RESET ~(PEER_F_LEARN_ASSIGN|PEER_F_LEARN_NOTUP2DATE)
Emeric Brun2b920a12010-09-23 18:30:22 +020091
Frédéric Lécaille645635d2019-02-11 17:49:39 +010092#define PEER_HEARTBEAT_TIMEOUT 3000 /* 3 seconds */
93
Emeric Brunb3971ab2015-05-12 18:49:09 +020094/*****************************/
95/* Sync message class */
96/*****************************/
97enum {
98 PEER_MSG_CLASS_CONTROL = 0,
99 PEER_MSG_CLASS_ERROR,
100 PEER_MSG_CLASS_STICKTABLE = 10,
101 PEER_MSG_CLASS_RESERVED = 255,
102};
103
104/*****************************/
105/* control message types */
106/*****************************/
107enum {
108 PEER_MSG_CTRL_RESYNCREQ = 0,
109 PEER_MSG_CTRL_RESYNCFINISHED,
110 PEER_MSG_CTRL_RESYNCPARTIAL,
111 PEER_MSG_CTRL_RESYNCCONFIRM,
Frédéric Lécaille645635d2019-02-11 17:49:39 +0100112 PEER_MSG_CTRL_HEARTBEAT,
Emeric Brunb3971ab2015-05-12 18:49:09 +0200113};
114
115/*****************************/
116/* error message types */
117/*****************************/
118enum {
119 PEER_MSG_ERR_PROTOCOL = 0,
120 PEER_MSG_ERR_SIZELIMIT,
121};
122
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100123/*
124 * Parameters used by functions to build peer protocol messages. */
125struct peer_prep_params {
126 struct {
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100127 struct peer *peer;
128 } hello;
129 struct {
130 unsigned int st1;
131 } error_status;
132 struct {
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100133 struct stksess *stksess;
134 struct shared_table *shared_table;
135 unsigned int updateid;
136 int use_identifier;
137 int use_timed;
138 } updt;
139 struct {
140 struct shared_table *shared_table;
141 } swtch;
142 struct {
143 struct shared_table *shared_table;
144 } ack;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100145 struct {
146 unsigned char head[2];
147 } control;
148 struct {
149 unsigned char head[2];
150 } error;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100151};
Emeric Brunb3971ab2015-05-12 18:49:09 +0200152
153/*******************************/
154/* stick table sync mesg types */
155/* Note: ids >= 128 contains */
156/* id message cotains data */
157/*******************************/
Olivier Houchard33992262018-10-16 18:49:26 +0200158#define PEER_MSG_STKT_UPDATE 0x80
159#define PEER_MSG_STKT_INCUPDATE 0x81
160#define PEER_MSG_STKT_DEFINE 0x82
161#define PEER_MSG_STKT_SWITCH 0x83
162#define PEER_MSG_STKT_ACK 0x84
163#define PEER_MSG_STKT_UPDATE_TIMED 0x85
164#define PEER_MSG_STKT_INCUPDATE_TIMED 0x86
Emeric Brun2b920a12010-09-23 18:30:22 +0200165
166/**********************************/
167/* Peer Session IO handler states */
168/**********************************/
169
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100170enum {
171 PEER_SESS_ST_ACCEPT = 0, /* Initial state for session create by an accept, must be zero! */
172 PEER_SESS_ST_GETVERSION, /* Validate supported protocol version */
173 PEER_SESS_ST_GETHOST, /* Validate host ID correspond to local host id */
174 PEER_SESS_ST_GETPEER, /* Validate peer ID correspond to a known remote peer id */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100175 /* after this point, data were possibly exchanged */
176 PEER_SESS_ST_SENDSUCCESS, /* Send ret code 200 (success) and wait for message */
177 PEER_SESS_ST_CONNECT, /* Initial state for session create on a connect, push presentation into buffer */
178 PEER_SESS_ST_GETSTATUS, /* Wait for the welcome message */
179 PEER_SESS_ST_WAITMSG, /* Wait for data messages */
180 PEER_SESS_ST_EXIT, /* Exit with status code */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200181 PEER_SESS_ST_ERRPROTO, /* Send error proto message before exit */
182 PEER_SESS_ST_ERRSIZE, /* Send error size message before exit */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100183 PEER_SESS_ST_END, /* Killed session */
184};
Emeric Brun2b920a12010-09-23 18:30:22 +0200185
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100186/***************************************************/
187/* Peer Session status code - part of the protocol */
188/***************************************************/
Emeric Brun2b920a12010-09-23 18:30:22 +0200189
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100190#define PEER_SESS_SC_CONNECTCODE 100 /* connect in progress */
191#define PEER_SESS_SC_CONNECTEDCODE 110 /* tcp connect success */
Emeric Brun2b920a12010-09-23 18:30:22 +0200192
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100193#define PEER_SESS_SC_SUCCESSCODE 200 /* accept or connect successful */
Emeric Brun2b920a12010-09-23 18:30:22 +0200194
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100195#define PEER_SESS_SC_TRYAGAIN 300 /* try again later */
Emeric Brun2b920a12010-09-23 18:30:22 +0200196
Frédéric Lécailleaba44a22019-03-26 10:18:07 +0100197#define PEER_SESS_SC_ERRPROTO 501 /* error protocol */
198#define PEER_SESS_SC_ERRVERSION 502 /* unknown protocol version */
199#define PEER_SESS_SC_ERRHOST 503 /* bad host name */
200#define PEER_SESS_SC_ERRPEER 504 /* unknown peer */
Emeric Brun2b920a12010-09-23 18:30:22 +0200201
202#define PEER_SESSION_PROTO_NAME "HAProxyS"
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200203#define PEER_MAJOR_VER 2
204#define PEER_MINOR_VER 1
205#define PEER_DWNGRD_MINOR_VER 0
Emeric Brun2b920a12010-09-23 18:30:22 +0200206
Willy Tarreau6254a922019-01-29 17:45:23 +0100207static size_t proto_len = sizeof(PEER_SESSION_PROTO_NAME) - 1;
Frédéric Lécailleed2b4a62017-07-13 09:07:09 +0200208struct peers *cfg_peers = NULL;
Willy Tarreau81bc3b02016-10-31 17:37:39 +0100209static void peer_session_forceshutdown(struct appctx *appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +0200210
Emeric Brun18928af2017-03-29 16:32:53 +0200211/* This function encode an uint64 to 'dynamic' length format.
212 The encoded value is written at address *str, and the
213 caller must assure that size after *str is large enought.
214 At return, the *str is set at the next Byte after then
215 encoded integer. The function returns then length of the
216 encoded integer in Bytes */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200217int intencode(uint64_t i, char **str) {
218 int idx = 0;
219 unsigned char *msg;
220
Emeric Brunb3971ab2015-05-12 18:49:09 +0200221 msg = (unsigned char *)*str;
222 if (i < 240) {
223 msg[0] = (unsigned char)i;
224 *str = (char *)&msg[idx+1];
225 return (idx+1);
226 }
227
228 msg[idx] =(unsigned char)i | 240;
229 i = (i - 240) >> 4;
230 while (i >= 128) {
231 msg[++idx] = (unsigned char)i | 128;
232 i = (i - 128) >> 7;
233 }
234 msg[++idx] = (unsigned char)i;
235 *str = (char *)&msg[idx+1];
236 return (idx+1);
237}
238
239
240/* This function returns the decoded integer or 0
241 if decode failed
242 *str point on the beginning of the integer to decode
243 at the end of decoding *str point on the end of the
244 encoded integer or to null if end is reached */
Emeric Brun18928af2017-03-29 16:32:53 +0200245uint64_t intdecode(char **str, char *end)
246{
Emeric Brunb3971ab2015-05-12 18:49:09 +0200247 unsigned char *msg;
Emeric Brun18928af2017-03-29 16:32:53 +0200248 uint64_t i;
249 int shift;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200250
251 if (!*str)
252 return 0;
253
254 msg = (unsigned char *)*str;
Emeric Brun18928af2017-03-29 16:32:53 +0200255 if (msg >= (unsigned char *)end)
256 goto fail;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200257
Emeric Brun18928af2017-03-29 16:32:53 +0200258 i = *(msg++);
259 if (i >= 240) {
260 shift = 4;
261 do {
262 if (msg >= (unsigned char *)end)
263 goto fail;
264 i += (uint64_t)*msg << shift;
265 shift += 7;
266 } while (*(msg++) >= 128);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200267 }
Frédéric Lécaillea8725ec2019-01-22 10:31:39 +0100268 *str = (char *)msg;
269 return i;
Emeric Brun18928af2017-03-29 16:32:53 +0200270
Frédéric Lécaillea8725ec2019-01-22 10:31:39 +0100271 fail:
272 *str = NULL;
273 return 0;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200274}
Emeric Brun2b920a12010-09-23 18:30:22 +0200275
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100276/*
277 * Build a "hello" peer protocol message.
278 * Return the number of written bytes written to build this messages if succeeded,
279 * 0 if not.
280 */
281static int peer_prepare_hellomsg(char *msg, size_t size, struct peer_prep_params *p)
282{
283 int min_ver, ret;
284 struct peer *peer;
285
286 peer = p->hello.peer;
287 min_ver = (peer->flags & PEER_F_DWNGRD) ? PEER_DWNGRD_MINOR_VER : PEER_MINOR_VER;
288 /* Prepare headers */
289 ret = snprintf(msg, size, PEER_SESSION_PROTO_NAME " %u.%u\n%s\n%s %d %d\n",
290 PEER_MAJOR_VER, min_ver, peer->id, localpeer, (int)getpid(), relative_pid);
291 if (ret >= size)
292 return 0;
293
294 return ret;
295}
296
297/*
298 * Build a "handshake succeeded" status message.
299 * Return the number of written bytes written to build this messages if succeeded,
300 * 0 if not.
301 */
302static int peer_prepare_status_successmsg(char *msg, size_t size, struct peer_prep_params *p)
303{
304 int ret;
305
306 ret = snprintf(msg, size, "%d\n", PEER_SESS_SC_SUCCESSCODE);
307 if (ret >= size)
308 return 0;
309
310 return ret;
311}
312
313/*
314 * Build an error status message.
315 * Return the number of written bytes written to build this messages if succeeded,
316 * 0 if not.
317 */
318static int peer_prepare_status_errormsg(char *msg, size_t size, struct peer_prep_params *p)
319{
320 int ret;
321 unsigned int st1;
322
323 st1 = p->error_status.st1;
324 ret = snprintf(msg, size, "%d\n", st1);
325 if (ret >= size)
326 return 0;
327
328 return ret;
329}
330
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200331/* Set the stick-table UPDATE message type byte at <msg_type> address,
332 * depending on <use_identifier> and <use_timed> boolean parameters.
333 * Always successful.
334 */
335static inline void peer_set_update_msg_type(char *msg_type, int use_identifier, int use_timed)
336{
337 if (use_timed) {
338 if (use_identifier)
339 *msg_type = PEER_MSG_STKT_UPDATE_TIMED;
340 else
341 *msg_type = PEER_MSG_STKT_INCUPDATE_TIMED;
342 }
343 else {
344 if (use_identifier)
345 *msg_type = PEER_MSG_STKT_UPDATE;
346 else
347 *msg_type = PEER_MSG_STKT_INCUPDATE;
348 }
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200349}
Emeric Brun2b920a12010-09-23 18:30:22 +0200350/*
Emeric Brunb3971ab2015-05-12 18:49:09 +0200351 * This prepare the data update message on the stick session <ts>, <st> is the considered
352 * stick table.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800353 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200354 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
355 * check size)
Emeric Brun2b920a12010-09-23 18:30:22 +0200356 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100357static int peer_prepare_updatemsg(char *msg, size_t size, struct peer_prep_params *p)
Emeric Brun2b920a12010-09-23 18:30:22 +0200358{
359 uint32_t netinteger;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200360 unsigned short datalen;
361 char *cursor, *datamsg;
Emeric Brun94900952015-06-11 18:25:54 +0200362 unsigned int data_type;
363 void *data_ptr;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100364 struct stksess *ts;
365 struct shared_table *st;
366 unsigned int updateid;
367 int use_identifier;
368 int use_timed;
369
370 ts = p->updt.stksess;
371 st = p->updt.shared_table;
372 updateid = p->updt.updateid;
373 use_identifier = p->updt.use_identifier;
374 use_timed = p->updt.use_timed;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200375
376 cursor = datamsg = msg + 1 + 5;
377
Emeric Brun2b920a12010-09-23 18:30:22 +0200378 /* construct message */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200379
380 /* check if we need to send the update identifer */
Emeric Brun819fc6f2017-06-13 19:37:32 +0200381 if (!st->last_pushed || updateid < st->last_pushed || ((updateid - st->last_pushed) != 1)) {
Emeric Bruna6a09982015-09-22 15:34:19 +0200382 use_identifier = 1;
Emeric Brun2b920a12010-09-23 18:30:22 +0200383 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200384
385 /* encode update identifier if needed */
386 if (use_identifier) {
Emeric Brun819fc6f2017-06-13 19:37:32 +0200387 netinteger = htonl(updateid);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200388 memcpy(cursor, &netinteger, sizeof(netinteger));
389 cursor += sizeof(netinteger);
Emeric Brun2b920a12010-09-23 18:30:22 +0200390 }
391
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200392 if (use_timed) {
393 netinteger = htonl(tick_remain(now_ms, ts->expire));
394 memcpy(cursor, &netinteger, sizeof(netinteger));
395 cursor += sizeof(netinteger);
396 }
397
Emeric Brunb3971ab2015-05-12 18:49:09 +0200398 /* encode the key */
Thierry FOURNIER5d24ebc2015-07-24 08:46:42 +0200399 if (st->table->type == SMP_T_STR) {
Emeric Brun2b920a12010-09-23 18:30:22 +0200400 int stlen = strlen((char *)ts->key.key);
401
Emeric Brunb3971ab2015-05-12 18:49:09 +0200402 intencode(stlen, &cursor);
403 memcpy(cursor, ts->key.key, stlen);
404 cursor += stlen;
Emeric Brun2b920a12010-09-23 18:30:22 +0200405 }
Thierry FOURNIER5d24ebc2015-07-24 08:46:42 +0200406 else if (st->table->type == SMP_T_SINT) {
Emeric Brun2b920a12010-09-23 18:30:22 +0200407 netinteger = htonl(*((uint32_t *)ts->key.key));
Emeric Brunb3971ab2015-05-12 18:49:09 +0200408 memcpy(cursor, &netinteger, sizeof(netinteger));
409 cursor += sizeof(netinteger);
Emeric Brun2b920a12010-09-23 18:30:22 +0200410 }
411 else {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200412 memcpy(cursor, ts->key.key, st->table->key_size);
413 cursor += st->table->key_size;
Emeric Brun2b920a12010-09-23 18:30:22 +0200414 }
415
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100416 HA_RWLOCK_RDLOCK(STK_SESS_LOCK, &ts->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200417 /* encode values */
Emeric Brun94900952015-06-11 18:25:54 +0200418 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200419
Emeric Brun94900952015-06-11 18:25:54 +0200420 data_ptr = stktable_data_ptr(st->table, ts, data_type);
421 if (data_ptr) {
422 switch (stktable_data_types[data_type].std_type) {
423 case STD_T_SINT: {
424 int data;
425
426 data = stktable_data_cast(data_ptr, std_t_sint);
427 intencode(data, &cursor);
428 break;
429 }
430 case STD_T_UINT: {
431 unsigned int data;
432
433 data = stktable_data_cast(data_ptr, std_t_uint);
434 intencode(data, &cursor);
435 break;
436 }
437 case STD_T_ULL: {
438 unsigned long long data;
439
440 data = stktable_data_cast(data_ptr, std_t_ull);
441 intencode(data, &cursor);
442 break;
443 }
444 case STD_T_FRQP: {
445 struct freq_ctr_period *frqp;
446
447 frqp = &stktable_data_cast(data_ptr, std_t_frqp);
448 intencode((unsigned int)(now_ms - frqp->curr_tick), &cursor);
449 intencode(frqp->curr_ctr, &cursor);
450 intencode(frqp->prev_ctr, &cursor);
451 break;
452 }
453 }
454 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200455 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100456 HA_RWLOCK_RDUNLOCK(STK_SESS_LOCK, &ts->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200457
458 /* Compute datalen */
459 datalen = (cursor - datamsg);
460
461 /* prepare message header */
462 msg[0] = PEER_MSG_CLASS_STICKTABLE;
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200463 peer_set_update_msg_type(&msg[1], use_identifier, use_timed);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200464 cursor = &msg[2];
465 intencode(datalen, &cursor);
466
467 /* move data after header */
468 memmove(cursor, datamsg, datalen);
469
470 /* return header size + data_len */
471 return (cursor - msg) + datalen;
472}
473
474/*
475 * This prepare the switch table message to targeted share table <st>.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800476 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200477 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
478 * check size)
479 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100480static int peer_prepare_switchmsg(char *msg, size_t size, struct peer_prep_params *params)
Emeric Brunb3971ab2015-05-12 18:49:09 +0200481{
482 int len;
483 unsigned short datalen;
Willy Tarreau83061a82018-07-13 11:56:34 +0200484 struct buffer *chunk;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200485 char *cursor, *datamsg, *chunkp, *chunkq;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200486 uint64_t data = 0;
Emeric Brun94900952015-06-11 18:25:54 +0200487 unsigned int data_type;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100488 struct shared_table *st;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200489
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100490 st = params->swtch.shared_table;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200491 cursor = datamsg = msg + 2 + 5;
492
493 /* Encode data */
494
495 /* encode local id */
496 intencode(st->local_id, &cursor);
497
498 /* encode table name */
499 len = strlen(st->table->id);
500 intencode(len, &cursor);
501 memcpy(cursor, st->table->id, len);
502 cursor += len;
503
504 /* encode table type */
505
506 intencode(st->table->type, &cursor);
507
508 /* encode table key size */
509 intencode(st->table->key_size, &cursor);
510
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200511 chunk = get_trash_chunk();
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200512 chunkp = chunkq = chunk->area;
Emeric Brun94900952015-06-11 18:25:54 +0200513 /* encode available known data types in table */
514 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
515 if (st->table->data_ofs[data_type]) {
516 switch (stktable_data_types[data_type].std_type) {
517 case STD_T_SINT:
518 case STD_T_UINT:
519 case STD_T_ULL:
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200520 data |= 1 << data_type;
521 break;
Emeric Brun94900952015-06-11 18:25:54 +0200522 case STD_T_FRQP:
523 data |= 1 << data_type;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200524 intencode(data_type, &chunkq);
525 intencode(st->table->data_arg[data_type].u, &chunkq);
Emeric Brun94900952015-06-11 18:25:54 +0200526 break;
527 }
528 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200529 }
530 intencode(data, &cursor);
531
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200532 /* Encode stick-table entries duration. */
533 intencode(st->table->expire, &cursor);
534
535 if (chunkq > chunkp) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200536 chunk->data = chunkq - chunkp;
537 memcpy(cursor, chunk->area, chunk->data);
538 cursor += chunk->data;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200539 }
540
Emeric Brunb3971ab2015-05-12 18:49:09 +0200541 /* Compute datalen */
542 datalen = (cursor - datamsg);
Emeric Brun2b920a12010-09-23 18:30:22 +0200543
Emeric Brunb3971ab2015-05-12 18:49:09 +0200544 /* prepare message header */
545 msg[0] = PEER_MSG_CLASS_STICKTABLE;
546 msg[1] = PEER_MSG_STKT_DEFINE;
547 cursor = &msg[2];
548 intencode(datalen, &cursor);
Emeric Brun2b920a12010-09-23 18:30:22 +0200549
Emeric Brunb3971ab2015-05-12 18:49:09 +0200550 /* move data after header */
551 memmove(cursor, datamsg, datalen);
552
553 /* return header size + data_len */
554 return (cursor - msg) + datalen;
Emeric Brun2b920a12010-09-23 18:30:22 +0200555}
556
Emeric Brunb3971ab2015-05-12 18:49:09 +0200557/*
558 * This prepare the acknowledge message on the stick session <ts>, <st> is the considered
559 * stick table.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800560 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200561 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
562 * check size)
563 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100564static int peer_prepare_ackmsg(char *msg, size_t size, struct peer_prep_params *p)
Emeric Brunb3971ab2015-05-12 18:49:09 +0200565{
566 unsigned short datalen;
567 char *cursor, *datamsg;
568 uint32_t netinteger;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100569 struct shared_table *st;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200570
Emeric Brunb058f1c2015-09-22 15:50:18 +0200571 cursor = datamsg = msg + 2 + 5;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200572
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100573 st = p->ack.shared_table;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200574 intencode(st->remote_id, &cursor);
575 netinteger = htonl(st->last_get);
576 memcpy(cursor, &netinteger, sizeof(netinteger));
577 cursor += sizeof(netinteger);
578
579 /* Compute datalen */
580 datalen = (cursor - datamsg);
581
582 /* prepare message header */
583 msg[0] = PEER_MSG_CLASS_STICKTABLE;
Emeric Brune1ab8082015-08-21 11:48:54 +0200584 msg[1] = PEER_MSG_STKT_ACK;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200585 cursor = &msg[2];
586 intencode(datalen, &cursor);
587
588 /* move data after header */
589 memmove(cursor, datamsg, datalen);
590
591 /* return header size + data_len */
592 return (cursor - msg) + datalen;
593}
Emeric Brun2b920a12010-09-23 18:30:22 +0200594
595/*
596 * Callback to release a session with a peer
597 */
Willy Tarreau00a37f02015-04-13 12:05:19 +0200598static void peer_session_release(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +0200599{
Willy Tarreau00a37f02015-04-13 12:05:19 +0200600 struct stream_interface *si = appctx->owner;
Willy Tarreau87b09662015-04-03 00:22:06 +0200601 struct stream *s = si_strm(si);
Vincent Bernat3c2f2f22016-04-03 13:48:42 +0200602 struct peer *peer = appctx->ctx.peers.ptr;
603 struct peers *peers = strm_fe(s)->parent;
Emeric Brun2b920a12010-09-23 18:30:22 +0200604
Willy Tarreau7b4b4992013-12-01 09:15:12 +0100605 /* appctx->ctx.peers.ptr is not a peer session */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100606 if (appctx->st0 < PEER_SESS_ST_SENDSUCCESS)
Emeric Brun2b920a12010-09-23 18:30:22 +0200607 return;
608
609 /* peer session identified */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200610 if (peer) {
Willy Tarreau2d372c22018-11-05 17:12:27 +0100611 if (appctx->st0 == PEER_SESS_ST_WAITMSG)
Olivier Houcharded879892019-03-08 18:53:43 +0100612 _HA_ATOMIC_SUB(&connected_peers, 1);
613 _HA_ATOMIC_SUB(&active_peers, 1);
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100614 HA_SPIN_LOCK(PEER_LOCK, &peer->lock);
Willy Tarreau9df94c22016-10-31 18:42:52 +0100615 if (peer->appctx == appctx) {
Emeric Brunb157d732015-08-21 12:00:30 +0200616 /* Re-init current table pointers to force announcement on re-connect */
617 peer->remote_table = peer->last_local_table = NULL;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200618 peer->appctx = NULL;
619 if (peer->flags & PEER_F_LEARN_ASSIGN) {
Emeric Brun2b920a12010-09-23 18:30:22 +0200620 /* unassign current peer for learning */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200621 peer->flags &= ~(PEER_F_LEARN_ASSIGN);
622 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
Emeric Brun2b920a12010-09-23 18:30:22 +0200623
624 /* reschedule a resync */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200625 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
Emeric Brun2b920a12010-09-23 18:30:22 +0200626 }
627 /* reset teaching and learning flags to 0 */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200628 peer->flags &= PEER_TEACH_RESET;
629 peer->flags &= PEER_LEARN_RESET;
Emeric Brun2b920a12010-09-23 18:30:22 +0200630 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100631 HA_SPIN_UNLOCK(PEER_LOCK, &peer->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200632 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
Emeric Brun2b920a12010-09-23 18:30:22 +0200633 }
634}
635
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200636/* Retrieve the major and minor versions of peers protocol
637 * announced by a remote peer. <str> is a null-terminated
638 * string with the following format: "<maj_ver>.<min_ver>".
639 */
640static int peer_get_version(const char *str,
641 unsigned int *maj_ver, unsigned int *min_ver)
642{
643 unsigned int majv, minv;
644 const char *pos, *saved;
645 const char *end;
646
647 saved = pos = str;
648 end = str + strlen(str);
649
650 majv = read_uint(&pos, end);
651 if (saved == pos || *pos++ != '.')
652 return -1;
653
654 saved = pos;
655 minv = read_uint(&pos, end);
656 if (saved == pos || pos != end)
657 return -1;
658
659 *maj_ver = majv;
660 *min_ver = minv;
661
662 return 0;
663}
Emeric Brun2b920a12010-09-23 18:30:22 +0200664
665/*
Frédéric Lécaillece025572019-01-21 13:38:06 +0100666 * Parse a line terminated by an optional '\r' character, followed by a mandatory
667 * '\n' character.
668 * Returns 1 if succeeded or 0 if a '\n' character could not be found, and -1 if
669 * a line could not be read because the communication channel is closed.
670 */
671static inline int peer_getline(struct appctx *appctx)
672{
673 int n;
674 struct stream_interface *si = appctx->owner;
675
676 n = co_getline(si_oc(si), trash.area, trash.size);
677 if (!n)
678 return 0;
679
680 if (n < 0 || trash.area[n - 1] != '\n') {
681 appctx->st0 = PEER_SESS_ST_END;
682 return -1;
683 }
684
685 if (n > 1 && (trash.area[n - 2] == '\r'))
686 trash.area[n - 2] = 0;
687 else
688 trash.area[n - 1] = 0;
689
690 co_skip(si_oc(si), n);
691
692 return n;
693}
694
695/*
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100696 * Send a message after having called <peer_prepare_msg> to build it.
697 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
698 * Returns -1 if there was not enough room left to send the message,
699 * any other negative returned value must be considered as an error with an appcxt st0
700 * returned value equal to PEER_SESS_ST_END.
701 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100702static inline int peer_send_msg(struct appctx *appctx,
703 int (*peer_prepare_msg)(char *, size_t, struct peer_prep_params *),
704 struct peer_prep_params *params)
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100705{
706 int ret, msglen;
707 struct stream_interface *si = appctx->owner;
708
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100709 msglen = peer_prepare_msg(trash.area, trash.size, params);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100710 if (!msglen) {
711 /* internal error: message does not fit in trash */
712 appctx->st0 = PEER_SESS_ST_END;
713 return 0;
714 }
715
716 /* message to buffer */
717 ret = ci_putblk(si_ic(si), trash.area, msglen);
718 if (ret <= 0) {
719 if (ret == -1) {
720 /* No more write possible */
721 si_rx_room_blk(si);
722 return -1;
723 }
724 appctx->st0 = PEER_SESS_ST_END;
725 }
726
727 return ret;
728}
729
730/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100731 * Send a hello message.
732 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
733 * Returns -1 if there was not enough room left to send the message,
734 * any other negative returned value must be considered as an error with an appcxt st0
735 * returned value equal to PEER_SESS_ST_END.
736 */
737static inline int peer_send_hellomsg(struct appctx *appctx, struct peer *peer)
738{
739 struct peer_prep_params p = {
740 .hello.peer = peer,
741 };
742
743 return peer_send_msg(appctx, peer_prepare_hellomsg, &p);
744}
745
746/*
747 * Send a success peer handshake status message.
748 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
749 * Returns -1 if there was not enough room left to send the message,
750 * any other negative returned value must be considered as an error with an appcxt st0
751 * returned value equal to PEER_SESS_ST_END.
752 */
753static inline int peer_send_status_successmsg(struct appctx *appctx)
754{
755 return peer_send_msg(appctx, peer_prepare_status_successmsg, NULL);
756}
757
758/*
759 * Send a peer handshake status error message.
760 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
761 * Returns -1 if there was not enough room left to send the message,
762 * any other negative returned value must be considered as an error with an appcxt st0
763 * returned value equal to PEER_SESS_ST_END.
764 */
765static inline int peer_send_status_errormsg(struct appctx *appctx)
766{
767 struct peer_prep_params p = {
768 .error_status.st1 = appctx->st1,
769 };
770
771 return peer_send_msg(appctx, peer_prepare_status_errormsg, &p);
772}
773
774/*
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100775 * Send a stick-table switch message.
776 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
777 * Returns -1 if there was not enough room left to send the message,
778 * any other negative returned value must be considered as an error with an appcxt st0
779 * returned value equal to PEER_SESS_ST_END.
780 */
781static inline int peer_send_switchmsg(struct shared_table *st, struct appctx *appctx)
782{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100783 struct peer_prep_params p = {
784 .swtch.shared_table = st,
785 };
786
787 return peer_send_msg(appctx, peer_prepare_switchmsg, &p);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100788}
789
790/*
791 * Send a stick-table update acknowledgement message.
792 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
793 * Returns -1 if there was not enough room left to send the message,
794 * any other negative returned value must be considered as an error with an appcxt st0
795 * returned value equal to PEER_SESS_ST_END.
796 */
797static inline int peer_send_ackmsg(struct shared_table *st, struct appctx *appctx)
798{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100799 struct peer_prep_params p = {
800 .ack.shared_table = st,
801 };
802
803 return peer_send_msg(appctx, peer_prepare_ackmsg, &p);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100804}
805
806/*
Frédéric Lécaille87f554c2019-01-22 17:26:50 +0100807 * Send a stick-table update message.
808 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
809 * Returns -1 if there was not enough room left to send the message,
810 * any other negative returned value must be considered as an error with an appcxt st0
811 * returned value equal to PEER_SESS_ST_END.
812 */
813static inline int peer_send_updatemsg(struct shared_table *st, struct appctx *appctx, struct stksess *ts,
814 unsigned int updateid, int use_identifier, int use_timed)
815{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100816 struct peer_prep_params p = {
817 .updt.stksess = ts,
818 .updt.shared_table = st,
819 .updt.updateid = updateid,
820 .updt.use_identifier = use_identifier,
821 .updt.use_timed = use_timed,
822 };
823
824 return peer_send_msg(appctx, peer_prepare_updatemsg, &p);
Frédéric Lécaille87f554c2019-01-22 17:26:50 +0100825}
826
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100827/*
828 * Build a peer protocol control class message.
829 * Returns the number of written bytes used to build the message if succeeded,
830 * 0 if not.
831 */
832static int peer_prepare_control_msg(char *msg, size_t size, struct peer_prep_params *p)
833{
834 if (size < sizeof p->control.head)
835 return 0;
836
837 msg[0] = p->control.head[0];
838 msg[1] = p->control.head[1];
839
840 return 2;
841}
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +0100842
843/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100844 * Send a stick-table synchronization request message.
845 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
846 * Returns -1 if there was not enough room left to send the message,
847 * any other negative returned value must be considered as an error with an appctx st0
848 * returned value equal to PEER_SESS_ST_END.
849 */
850static inline int peer_send_resync_reqmsg(struct appctx *appctx)
851{
852 struct peer_prep_params p = {
853 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCREQ, },
854 };
855
856 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
857}
858
859/*
860 * Send a stick-table synchronization confirmation message.
861 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
862 * Returns -1 if there was not enough room left to send the message,
863 * any other negative returned value must be considered as an error with an appctx st0
864 * returned value equal to PEER_SESS_ST_END.
865 */
866static inline int peer_send_resync_confirmsg(struct appctx *appctx)
867{
868 struct peer_prep_params p = {
869 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCCONFIRM, },
870 };
871
872 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
873}
874
875/*
876 * Send a stick-table synchronization finished message.
877 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
878 * Returns -1 if there was not enough room left to send the message,
879 * any other negative returned value must be considered as an error with an appctx st0
880 * returned value equal to PEER_SESS_ST_END.
881 */
882static inline int peer_send_resync_finishedmsg(struct appctx *appctx, struct peer *peer)
883{
884 struct peer_prep_params p = {
885 .control.head = { PEER_MSG_CLASS_CONTROL, },
886 };
887
888 p.control.head[1] = (peer->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED ?
889 PEER_MSG_CTRL_RESYNCFINISHED : PEER_MSG_CTRL_RESYNCPARTIAL;
890
891 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
892}
893
894/*
Frédéric Lécaille645635d2019-02-11 17:49:39 +0100895 * Send a heartbeat message.
896 * Return 0 if the message could not be built modifying the appctx st0 to PEER_SESS_ST_END value.
897 * Returns -1 if there was not enough room left to send the message,
898 * any other negative returned value must be considered as an error with an appctx st0
899 * returned value equal to PEER_SESS_ST_END.
900 */
901static inline int peer_send_heartbeatmsg(struct appctx *appctx)
902{
903 struct peer_prep_params p = {
904 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_HEARTBEAT, },
905 };
906
907 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
908}
909
910/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100911 * Build a peer protocol error class message.
912 * Returns the number of written bytes used to build the message if succeeded,
913 * 0 if not.
914 */
915static int peer_prepare_error_msg(char *msg, size_t size, struct peer_prep_params *p)
916{
917 if (size < sizeof p->error.head)
918 return 0;
919
920 msg[0] = p->error.head[0];
921 msg[1] = p->error.head[1];
922
923 return 2;
924}
925
926/*
927 * Send a "size limit reached" error message.
928 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
929 * Returns -1 if there was not enough room left to send the message,
930 * any other negative returned value must be considered as an error with an appctx st0
931 * returned value equal to PEER_SESS_ST_END.
932 */
933static inline int peer_send_error_size_limitmsg(struct appctx *appctx)
934{
935 struct peer_prep_params p = {
936 .error.head = { PEER_MSG_CLASS_ERROR, PEER_MSG_ERR_SIZELIMIT, },
937 };
938
939 return peer_send_msg(appctx, peer_prepare_error_msg, &p);
940}
941
942/*
943 * Send a "peer protocol" error message.
944 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
945 * Returns -1 if there was not enough room left to send the message,
946 * any other negative returned value must be considered as an error with an appctx st0
947 * returned value equal to PEER_SESS_ST_END.
948 */
949static inline int peer_send_error_protomsg(struct appctx *appctx)
950{
951 struct peer_prep_params p = {
952 .error.head = { PEER_MSG_CLASS_ERROR, PEER_MSG_ERR_PROTOCOL, },
953 };
954
955 return peer_send_msg(appctx, peer_prepare_error_msg, &p);
956}
957
958/*
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +0100959 * Function used to lookup for recent stick-table updates associated with
960 * <st> shared stick-table when a lesson must be taught a peer (PEER_F_LEARN_ASSIGN flag set).
961 */
962static inline struct stksess *peer_teach_process_stksess_lookup(struct shared_table *st)
963{
964 struct eb32_node *eb;
965
966 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
967 if (!eb) {
968 eb = eb32_first(&st->table->updates);
969 if (!eb || ((int)(eb->key - st->last_pushed) <= 0)) {
970 st->table->commitupdate = st->last_pushed = st->table->localupdate;
971 return NULL;
972 }
973 }
974
975 if ((int)(eb->key - st->table->localupdate) > 0) {
976 st->table->commitupdate = st->last_pushed = st->table->localupdate;
977 return NULL;
978 }
979
980 return eb32_entry(eb, struct stksess, upd);
981}
982
983/*
984 * Function used to lookup for recent stick-table updates associated with
985 * <st> shared stick-table during teach state 1 step.
986 */
987static inline struct stksess *peer_teach_stage1_stksess_lookup(struct shared_table *st)
988{
989 struct eb32_node *eb;
990
991 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
992 if (!eb) {
993 st->flags |= SHTABLE_F_TEACH_STAGE1;
994 eb = eb32_first(&st->table->updates);
995 if (eb)
996 st->last_pushed = eb->key - 1;
997 return NULL;
998 }
999
1000 return eb32_entry(eb, struct stksess, upd);
1001}
1002
1003/*
1004 * Function used to lookup for recent stick-table updates associated with
1005 * <st> shared stick-table during teach state 2 step.
1006 */
1007static inline struct stksess *peer_teach_stage2_stksess_lookup(struct shared_table *st)
1008{
1009 struct eb32_node *eb;
1010
1011 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
1012 if (!eb || eb->key > st->teaching_origin) {
1013 st->flags |= SHTABLE_F_TEACH_STAGE2;
1014 return NULL;
1015 }
1016
1017 return eb32_entry(eb, struct stksess, upd);
1018}
1019
1020/*
1021 * Generic function to emit update messages for <st> stick-table when a lesson must
1022 * be taught to the peer <p>.
1023 * <locked> must be set to 1 if the shared table <st> is already locked when entering
1024 * this function, 0 if not.
1025 *
1026 * This function temporary unlock/lock <st> when it sends stick-table updates or
1027 * when decrementing its refcount in case of any error when it sends this updates.
1028 *
1029 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1030 * Returns -1 if there was not enough room left to send the message,
1031 * any other negative returned value must be considered as an error with an appcxt st0
1032 * returned value equal to PEER_SESS_ST_END.
1033 * If it returns 0 or -1, this function leave <st> locked if already locked when entering this function
1034 * unlocked if not already locked when entering this function.
1035 */
1036static inline int peer_send_teachmsgs(struct appctx *appctx, struct peer *p,
1037 struct stksess *(*peer_stksess_lookup)(struct shared_table *),
1038 struct shared_table *st, int locked)
1039{
1040 int ret, new_pushed, use_timed;
1041
1042 ret = 1;
1043 use_timed = 0;
1044 if (st != p->last_local_table) {
1045 ret = peer_send_switchmsg(st, appctx);
1046 if (ret <= 0)
1047 return ret;
1048
1049 p->last_local_table = st;
1050 }
1051
1052 if (peer_stksess_lookup != peer_teach_process_stksess_lookup)
1053 use_timed = !(p->flags & PEER_F_DWNGRD);
1054
1055 /* We force new pushed to 1 to force identifier in update message */
1056 new_pushed = 1;
1057
1058 if (!locked)
1059 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1060
1061 while (1) {
1062 struct stksess *ts;
1063 unsigned updateid;
1064
1065 /* push local updates */
1066 ts = peer_stksess_lookup(st);
1067 if (!ts)
1068 break;
1069
1070 updateid = ts->upd.key;
1071 ts->ref_cnt++;
1072 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1073
1074 ret = peer_send_updatemsg(st, appctx, ts, updateid, new_pushed, use_timed);
1075 if (ret <= 0) {
1076 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1077 ts->ref_cnt--;
1078 if (!locked)
1079 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1080 return ret;
1081 }
1082
1083 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1084 ts->ref_cnt--;
1085 st->last_pushed = updateid;
1086
1087 if (peer_stksess_lookup == peer_teach_process_stksess_lookup &&
1088 (int)(st->last_pushed - st->table->commitupdate) > 0)
1089 st->table->commitupdate = st->last_pushed;
1090
1091 /* identifier may not needed in next update message */
1092 new_pushed = 0;
1093 }
1094
1095 out:
1096 if (!locked)
1097 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1098 return 1;
1099}
1100
1101/*
1102 * Function to emit update messages for <st> stick-table when a lesson must
1103 * be taught to the peer <p> (PEER_F_LEARN_ASSIGN flag set).
1104 *
1105 * Note that <st> shared stick-table is locked when calling this function.
1106 *
1107 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1108 * Returns -1 if there was not enough room left to send the message,
1109 * any other negative returned value must be considered as an error with an appcxt st0
1110 * returned value equal to PEER_SESS_ST_END.
1111 */
1112static inline int peer_send_teach_process_msgs(struct appctx *appctx, struct peer *p,
1113 struct shared_table *st)
1114{
1115 return peer_send_teachmsgs(appctx, p, peer_teach_process_stksess_lookup, st, 1);
1116}
1117
1118/*
1119 * Function to emit update messages for <st> stick-table when a lesson must
1120 * be taught to the peer <p> during teach state 1 step.
1121 *
1122 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1123 * Returns -1 if there was not enough room left to send the message,
1124 * any other negative returned value must be considered as an error with an appcxt st0
1125 * returned value equal to PEER_SESS_ST_END.
1126 */
1127static inline int peer_send_teach_stage1_msgs(struct appctx *appctx, struct peer *p,
1128 struct shared_table *st)
1129{
1130 return peer_send_teachmsgs(appctx, p, peer_teach_stage1_stksess_lookup, st, 0);
1131}
1132
1133/*
1134 * Function to emit update messages for <st> stick-table when a lesson must
1135 * be taught to the peer <p> during teach state 1 step.
1136 *
1137 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1138 * Returns -1 if there was not enough room left to send the message,
1139 * any other negative returned value must be considered as an error with an appcxt st0
1140 * returned value equal to PEER_SESS_ST_END.
1141 */
1142static inline int peer_send_teach_stage2_msgs(struct appctx *appctx, struct peer *p,
1143 struct shared_table *st)
1144{
1145 return peer_send_teachmsgs(appctx, p, peer_teach_stage2_stksess_lookup, st, 0);
1146}
1147
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001148
1149/*
1150 * Function used to parse a stick-table update message after it has been received
1151 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1152 * receipt buffer with <msg_end> being position of the end of the stick-table message.
1153 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1154 * was encountered.
1155 * <exp> must be set if the stick-table entry expires.
1156 * <updt> must be set for PEER_MSG_STKT_UPDATE or PEER_MSG_STKT_UPDATE_TIMED stick-table
1157 * messages, in this case the stick-table udpate message is received with a stick-table
1158 * update ID.
1159 * <totl> is the length of the stick-table update message computed upon receipt.
1160 */
Frédéric Lécaille444243c2019-01-24 15:40:11 +01001161static int peer_treat_updatemsg(struct appctx *appctx, struct peer *p, int updt, int exp,
1162 char **msg_cur, char *msg_end, int msg_len, int totl)
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001163{
1164 struct stream_interface *si = appctx->owner;
1165 struct shared_table *st = p->remote_table;
1166 struct stksess *ts, *newts;
1167 uint32_t update;
1168 int expire;
1169 unsigned int data_type;
1170 void *data_ptr;
1171
1172 /* Here we have data message */
1173 if (!st)
1174 goto ignore_msg;
1175
1176 expire = MS_TO_TICKS(st->table->expire);
1177
1178 if (updt) {
Willy Tarreau1e82a142019-01-29 11:08:06 +01001179 if (msg_len < sizeof(update))
1180 goto malformed_exit;
1181
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001182 memcpy(&update, *msg_cur, sizeof(update));
1183 *msg_cur += sizeof(update);
1184 st->last_get = htonl(update);
1185 }
1186 else {
1187 st->last_get++;
1188 }
1189
1190 if (exp) {
1191 size_t expire_sz = sizeof expire;
1192
Willy Tarreau1e82a142019-01-29 11:08:06 +01001193 if (*msg_cur + expire_sz > msg_end)
1194 goto malformed_exit;
1195
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001196 memcpy(&expire, *msg_cur, expire_sz);
1197 *msg_cur += expire_sz;
1198 expire = ntohl(expire);
1199 }
1200
1201 newts = stksess_new(st->table, NULL);
1202 if (!newts)
1203 goto ignore_msg;
1204
1205 if (st->table->type == SMP_T_STR) {
1206 unsigned int to_read, to_store;
1207
1208 to_read = intdecode(msg_cur, msg_end);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001209 if (!*msg_cur)
1210 goto malformed_free_newts;
1211
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001212 to_store = MIN(to_read, st->table->key_size - 1);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001213 if (*msg_cur + to_store > msg_end)
1214 goto malformed_free_newts;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001215
1216 memcpy(newts->key.key, *msg_cur, to_store);
1217 newts->key.key[to_store] = 0;
1218 *msg_cur += to_read;
1219 }
1220 else if (st->table->type == SMP_T_SINT) {
1221 unsigned int netinteger;
1222
Willy Tarreau1e82a142019-01-29 11:08:06 +01001223 if (*msg_cur + sizeof(netinteger) > msg_end)
1224 goto malformed_free_newts;
1225
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001226 memcpy(&netinteger, *msg_cur, sizeof(netinteger));
1227 netinteger = ntohl(netinteger);
1228 memcpy(newts->key.key, &netinteger, sizeof(netinteger));
1229 *msg_cur += sizeof(netinteger);
1230 }
1231 else {
Willy Tarreau1e82a142019-01-29 11:08:06 +01001232 if (*msg_cur + st->table->key_size > msg_end)
1233 goto malformed_free_newts;
1234
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001235 memcpy(newts->key.key, *msg_cur, st->table->key_size);
1236 *msg_cur += st->table->key_size;
1237 }
1238
1239 /* lookup for existing entry */
1240 ts = stktable_set_entry(st->table, newts);
1241 if (ts != newts) {
1242 stksess_free(st->table, newts);
1243 newts = NULL;
1244 }
1245
1246 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1247
1248 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
Willy Tarreau1e82a142019-01-29 11:08:06 +01001249 uint64_t decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001250
1251 if (!((1 << data_type) & st->remote_data))
1252 continue;
1253
Willy Tarreau1e82a142019-01-29 11:08:06 +01001254 decoded_int = intdecode(msg_cur, msg_end);
1255 if (!*msg_cur)
1256 goto malformed_unlock;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001257
Willy Tarreau1e82a142019-01-29 11:08:06 +01001258 switch (stktable_data_types[data_type].std_type) {
1259 case STD_T_SINT:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001260 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1261 if (data_ptr)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001262 stktable_data_cast(data_ptr, std_t_sint) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001263 break;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001264
Willy Tarreau1e82a142019-01-29 11:08:06 +01001265 case STD_T_UINT:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001266 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1267 if (data_ptr)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001268 stktable_data_cast(data_ptr, std_t_uint) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001269 break;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001270
Willy Tarreau1e82a142019-01-29 11:08:06 +01001271 case STD_T_ULL:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001272 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1273 if (data_ptr)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001274 stktable_data_cast(data_ptr, std_t_ull) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001275 break;
Willy Tarreau1e82a142019-01-29 11:08:06 +01001276
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001277 case STD_T_FRQP: {
1278 struct freq_ctr_period data;
1279
1280 /* First bit is reserved for the freq_ctr_period lock
1281 Note: here we're still protected by the stksess lock
1282 so we don't need to update the update the freq_ctr_period
1283 using its internal lock */
1284
Willy Tarreau1e82a142019-01-29 11:08:06 +01001285 data.curr_tick = tick_add(now_ms, -decoded_int) & ~0x1;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001286 data.curr_ctr = intdecode(msg_cur, msg_end);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001287 if (!*msg_cur)
1288 goto malformed_unlock;
1289
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001290 data.prev_ctr = intdecode(msg_cur, msg_end);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001291 if (!*msg_cur)
1292 goto malformed_unlock;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001293
1294 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1295 if (data_ptr)
1296 stktable_data_cast(data_ptr, std_t_frqp) = data;
1297 break;
1298 }
1299 }
1300 }
1301 /* Force new expiration */
1302 ts->expire = tick_add(now_ms, expire);
1303
1304 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1305 stktable_touch_remote(st->table, ts, 1);
1306 return 1;
1307
1308 ignore_msg:
1309 /* skip consumed message */
1310 co_skip(si_oc(si), totl);
1311 return 0;
Willy Tarreau1e82a142019-01-29 11:08:06 +01001312
1313 malformed_unlock:
1314 /* malformed message */
1315 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1316 stktable_touch_remote(st->table, ts, 1);
1317 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1318 return 0;
1319
1320 malformed_free_newts:
1321 /* malformed message */
1322 stksess_free(st->table, newts);
1323 malformed_exit:
1324 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1325 return 0;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001326}
1327
Frédéric Lécaille87f554c2019-01-22 17:26:50 +01001328/*
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001329 * Function used to parse a stick-table update acknowledgement message after it
1330 * has been received by <p> peer with <msg_cur> as address of the pointer to the position in the
1331 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
1332 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1333 * was encountered.
1334 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
1335 */
1336static inline int peer_treat_ackmsg(struct appctx *appctx, struct peer *p,
1337 char **msg_cur, char *msg_end)
1338{
1339 /* ack message */
1340 uint32_t table_id ;
1341 uint32_t update;
1342 struct shared_table *st;
1343
1344 table_id = intdecode(msg_cur, msg_end);
1345 if (!*msg_cur || (*msg_cur + sizeof(update) > msg_end)) {
1346 /* malformed message */
1347 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1348 return 0;
1349 }
1350
1351 memcpy(&update, *msg_cur, sizeof(update));
1352 update = ntohl(update);
1353
1354 for (st = p->tables; st; st = st->next) {
1355 if (st->local_id == table_id) {
1356 st->update = update;
1357 break;
1358 }
1359 }
1360
1361 return 1;
1362}
1363
1364/*
1365 * Function used to parse a stick-table switch message after it has been received
1366 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1367 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
1368 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1369 * was encountered.
1370 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
1371 */
1372static inline int peer_treat_switchmsg(struct appctx *appctx, struct peer *p,
1373 char **msg_cur, char *msg_end)
1374{
1375 struct shared_table *st;
1376 int table_id;
1377
1378 table_id = intdecode(msg_cur, msg_end);
1379 if (!*msg_cur) {
1380 /* malformed message */
1381 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1382 return 0;
1383 }
1384
1385 p->remote_table = NULL;
1386 for (st = p->tables; st; st = st->next) {
1387 if (st->remote_id == table_id) {
1388 p->remote_table = st;
1389 break;
1390 }
1391 }
1392
1393 return 1;
1394}
1395
1396/*
1397 * Function used to parse a stick-table definition message after it has been received
1398 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1399 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
1400 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1401 * was encountered.
1402 * <totl> is the length of the stick-table update message computed upon receipt.
1403 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
1404 */
1405static inline int peer_treat_definemsg(struct appctx *appctx, struct peer *p,
1406 char **msg_cur, char *msg_end, int totl)
1407{
1408 struct stream_interface *si = appctx->owner;
1409 int table_id_len;
1410 struct shared_table *st;
1411 int table_type;
1412 int table_keylen;
1413 int table_id;
1414 uint64_t table_data;
1415
1416 table_id = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001417 if (!*msg_cur)
1418 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001419
1420 table_id_len = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001421 if (!*msg_cur)
1422 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001423
1424 p->remote_table = NULL;
Willy Tarreau6f731f32019-01-29 11:11:23 +01001425 if (!table_id_len || (*msg_cur + table_id_len) >= msg_end)
1426 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001427
1428 for (st = p->tables; st; st = st->next) {
1429 /* Reset IDs */
1430 if (st->remote_id == table_id)
1431 st->remote_id = 0;
1432
1433 if (!p->remote_table && (table_id_len == strlen(st->table->id)) &&
1434 (memcmp(st->table->id, *msg_cur, table_id_len) == 0))
1435 p->remote_table = st;
1436 }
1437
1438 if (!p->remote_table)
1439 goto ignore_msg;
1440
1441 *msg_cur += table_id_len;
Willy Tarreau6f731f32019-01-29 11:11:23 +01001442 if (*msg_cur >= msg_end)
1443 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001444
1445 table_type = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001446 if (!*msg_cur)
1447 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001448
1449 table_keylen = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001450 if (!*msg_cur)
1451 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001452
1453 table_data = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001454 if (!*msg_cur)
1455 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001456
1457 if (p->remote_table->table->type != table_type
1458 || p->remote_table->table->key_size != table_keylen) {
1459 p->remote_table = NULL;
1460 goto ignore_msg;
1461 }
1462
1463 p->remote_table->remote_data = table_data;
1464 p->remote_table->remote_id = table_id;
1465 return 1;
1466
1467 ignore_msg:
1468 co_skip(si_oc(si), totl);
1469 return 0;
Willy Tarreau6f731f32019-01-29 11:11:23 +01001470
1471 malformed_exit:
1472 /* malformed message */
1473 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1474 return 0;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001475}
1476
1477/*
Frédéric Lécaille95203f22019-01-23 19:38:11 +01001478 * Receive a stick-table message.
1479 * Returns 1 if there was no error, if not, returns 0 if not enough data were available,
1480 * -1 if there was an error updating the appctx state st0 accordingly.
1481 */
1482static inline int peer_recv_msg(struct appctx *appctx, char *msg_head, size_t msg_head_sz,
1483 uint32_t *msg_len, int *totl)
1484{
1485 int reql;
1486 struct stream_interface *si = appctx->owner;
1487
1488 reql = co_getblk(si_oc(si), msg_head, 2 * sizeof(char), *totl);
1489 if (reql <= 0) /* closed or EOL not found */
1490 goto incomplete;
1491
1492 *totl += reql;
1493
1494 if ((unsigned int)msg_head[1] < 128)
1495 return 1;
1496
1497 /* Read and Decode message length */
1498 reql = co_getblk(si_oc(si), &msg_head[2], sizeof(char), *totl);
1499 if (reql <= 0) /* closed */
1500 goto incomplete;
1501
1502 *totl += reql;
1503
1504 if ((unsigned int)msg_head[2] < 240) {
1505 *msg_len = msg_head[2];
1506 }
1507 else {
1508 int i;
1509 char *cur;
1510 char *end;
1511
1512 for (i = 3 ; i < msg_head_sz ; i++) {
1513 reql = co_getblk(si_oc(si), &msg_head[i], sizeof(char), *totl);
1514 if (reql <= 0) /* closed */
1515 goto incomplete;
1516
1517 *totl += reql;
1518
1519 if (!(msg_head[i] & 0x80))
1520 break;
1521 }
1522
1523 if (i == msg_head_sz) {
1524 /* malformed message */
1525 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1526 return -1;
1527 }
1528 end = msg_head + msg_head_sz;
1529 cur = &msg_head[2];
1530 *msg_len = intdecode(&cur, end);
1531 if (!cur) {
1532 /* malformed message */
1533 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1534 return -1;
1535 }
1536 }
1537
1538 /* Read message content */
1539 if (*msg_len) {
1540 if (*msg_len > trash.size) {
1541 /* Status code is not success, abort */
1542 appctx->st0 = PEER_SESS_ST_ERRSIZE;
1543 return -1;
1544 }
1545
1546 reql = co_getblk(si_oc(si), trash.area, *msg_len, *totl);
1547 if (reql <= 0) /* closed */
1548 goto incomplete;
1549 *totl += reql;
1550 }
1551
1552 return 1;
1553
1554 incomplete:
1555 if (reql < 0) {
1556 /* there was an error */
1557 appctx->st0 = PEER_SESS_ST_END;
1558 return -1;
1559 }
1560
1561 return 0;
1562}
Frédéric Lécaille444243c2019-01-24 15:40:11 +01001563
1564/*
1565 * Treat the awaited message with <msg_head> as header.*
1566 * Return 1 if succeeded, 0 if not.
1567 */
1568static inline int peer_treat_awaited_msg(struct appctx *appctx, struct peer *peer, unsigned char *msg_head,
1569 char **msg_cur, char *msg_end, int msg_len, int totl)
1570{
1571 struct stream_interface *si = appctx->owner;
1572 struct stream *s = si_strm(si);
1573 struct peers *peers = strm_fe(s)->parent;
1574
1575 if (msg_head[0] == PEER_MSG_CLASS_CONTROL) {
1576 if (msg_head[1] == PEER_MSG_CTRL_RESYNCREQ) {
1577 struct shared_table *st;
1578 /* Reset message: remote need resync */
1579
1580 /* prepare tables fot a global push */
1581 for (st = peer->tables; st; st = st->next) {
1582 st->teaching_origin = st->last_pushed = st->table->update;
1583 st->flags = 0;
1584 }
1585
1586 /* reset teaching flags to 0 */
1587 peer->flags &= PEER_TEACH_RESET;
1588
1589 /* flag to start to teach lesson */
1590 peer->flags |= PEER_F_TEACH_PROCESS;
1591 }
1592 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCFINISHED) {
1593 if (peer->flags & PEER_F_LEARN_ASSIGN) {
1594 peer->flags &= ~PEER_F_LEARN_ASSIGN;
1595 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
1596 peers->flags |= (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE);
1597 }
1598 peer->confirm++;
1599 }
1600 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCPARTIAL) {
1601 if (peer->flags & PEER_F_LEARN_ASSIGN) {
1602 peer->flags &= ~PEER_F_LEARN_ASSIGN;
1603 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
1604
1605 peer->flags |= PEER_F_LEARN_NOTUP2DATE;
1606 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
1607 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
1608 }
1609 peer->confirm++;
1610 }
1611 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCCONFIRM) {
1612 struct shared_table *st;
1613
1614 /* If stopping state */
1615 if (stopping) {
1616 /* Close session, push resync no more needed */
1617 peer->flags |= PEER_F_TEACH_COMPLETE;
1618 appctx->st0 = PEER_SESS_ST_END;
1619 return 0;
1620 }
1621 for (st = peer->tables; st; st = st->next) {
1622 st->update = st->last_pushed = st->teaching_origin;
1623 st->flags = 0;
1624 }
1625
1626 /* reset teaching flags to 0 */
1627 peer->flags &= PEER_TEACH_RESET;
1628 }
Frédéric Lécaille645635d2019-02-11 17:49:39 +01001629 else if (msg_head[1] == PEER_MSG_CTRL_HEARTBEAT) {
1630 peer->reconnect = tick_add(now_ms, MS_TO_TICKS(5000));
1631 }
Frédéric Lécaille444243c2019-01-24 15:40:11 +01001632 }
1633 else if (msg_head[0] == PEER_MSG_CLASS_STICKTABLE) {
1634 if (msg_head[1] == PEER_MSG_STKT_DEFINE) {
1635 if (!peer_treat_definemsg(appctx, peer, msg_cur, msg_end, totl))
1636 return 0;
1637 }
1638 else if (msg_head[1] == PEER_MSG_STKT_SWITCH) {
1639 if (!peer_treat_switchmsg(appctx, peer, msg_cur, msg_end))
1640 return 0;
1641 }
1642 else if (msg_head[1] == PEER_MSG_STKT_UPDATE ||
1643 msg_head[1] == PEER_MSG_STKT_INCUPDATE ||
1644 msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED ||
1645 msg_head[1] == PEER_MSG_STKT_INCUPDATE_TIMED) {
1646 int update, expire;
1647
1648 update = msg_head[1] == PEER_MSG_STKT_UPDATE || msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED;
1649 expire = msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED || msg_head[1] == PEER_MSG_STKT_INCUPDATE_TIMED;
1650 if (!peer_treat_updatemsg(appctx, peer, update, expire,
1651 msg_cur, msg_end, msg_len, totl))
1652 return 0;
1653
1654 }
1655 else if (msg_head[1] == PEER_MSG_STKT_ACK) {
1656 if (!peer_treat_ackmsg(appctx, peer, msg_cur, msg_end))
1657 return 0;
1658 }
1659 }
1660 else if (msg_head[0] == PEER_MSG_CLASS_RESERVED) {
1661 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1662 return 0;
1663 }
1664
1665 return 1;
1666}
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01001667
1668
1669/*
1670 * Send any message to <peer> peer.
1671 * Returns 1 if succeeded, or -1 or 0 if failed.
1672 * -1 means an internal error occured, 0 is for a peer protocol error leading
1673 * to a peer state change (from the peer I/O handler point of view).
1674 */
1675static inline int peer_send_msgs(struct appctx *appctx, struct peer *peer)
1676{
1677 int repl;
1678 struct stream_interface *si = appctx->owner;
1679 struct stream *s = si_strm(si);
1680 struct peers *peers = strm_fe(s)->parent;
1681
1682 /* Need to request a resync */
1683 if ((peer->flags & PEER_F_LEARN_ASSIGN) &&
1684 (peers->flags & PEERS_F_RESYNC_ASSIGN) &&
1685 !(peers->flags & PEERS_F_RESYNC_PROCESS)) {
1686
1687 repl = peer_send_resync_reqmsg(appctx);
1688 if (repl <= 0)
1689 return repl;
1690
1691 peers->flags |= PEERS_F_RESYNC_PROCESS;
1692 }
1693
1694 /* Nothing to read, now we start to write */
1695 if (peer->tables) {
1696 struct shared_table *st;
1697 struct shared_table *last_local_table;
1698
1699 last_local_table = peer->last_local_table;
1700 if (!last_local_table)
1701 last_local_table = peer->tables;
1702 st = last_local_table->next;
1703
1704 while (1) {
1705 if (!st)
1706 st = peer->tables;
1707
1708 /* It remains some updates to ack */
1709 if (st->last_get != st->last_acked) {
1710 repl = peer_send_ackmsg(st, appctx);
1711 if (repl <= 0)
1712 return repl;
1713
1714 st->last_acked = st->last_get;
1715 }
1716
1717 if (!(peer->flags & PEER_F_TEACH_PROCESS)) {
1718 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1719 if (!(peer->flags & PEER_F_LEARN_ASSIGN) &&
1720 ((int)(st->last_pushed - st->table->localupdate) < 0)) {
1721
1722 repl = peer_send_teach_process_msgs(appctx, peer, st);
1723 if (repl <= 0) {
1724 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1725 return repl;
1726 }
1727 }
1728 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1729 }
1730 else {
1731 if (!(st->flags & SHTABLE_F_TEACH_STAGE1)) {
1732 repl = peer_send_teach_stage1_msgs(appctx, peer, st);
1733 if (repl <= 0)
1734 return repl;
1735 }
1736
1737 if (!(st->flags & SHTABLE_F_TEACH_STAGE2)) {
1738 repl = peer_send_teach_stage2_msgs(appctx, peer, st);
1739 if (repl <= 0)
1740 return repl;
1741 }
1742 }
1743
1744 if (st == last_local_table)
1745 break;
1746 st = st->next;
1747 }
1748 }
1749
1750 if ((peer->flags & PEER_F_TEACH_PROCESS) && !(peer->flags & PEER_F_TEACH_FINISHED)) {
1751 repl = peer_send_resync_finishedmsg(appctx, peer);
1752 if (repl <= 0)
1753 return repl;
1754
1755 /* flag finished message sent */
1756 peer->flags |= PEER_F_TEACH_FINISHED;
1757 }
1758
1759 /* Confirm finished or partial messages */
1760 while (peer->confirm) {
1761 repl = peer_send_resync_confirmsg(appctx);
1762 if (repl <= 0)
1763 return repl;
1764
1765 peer->confirm--;
1766 }
1767
1768 return 1;
1769}
1770
Frédéric Lécaille95203f22019-01-23 19:38:11 +01001771/*
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01001772 * Read and parse a first line of a "hello" peer protocol message.
1773 * Returns 0 if could not read a line, -1 if there was a read error or
1774 * the line is malformed, 1 if succeeded.
1775 */
1776static inline int peer_getline_version(struct appctx *appctx,
1777 unsigned int *maj_ver, unsigned int *min_ver)
1778{
1779 int reql;
1780
1781 reql = peer_getline(appctx);
1782 if (!reql)
1783 return 0;
1784
1785 if (reql < 0)
1786 return -1;
1787
1788 /* test protocol */
1789 if (strncmp(PEER_SESSION_PROTO_NAME " ", trash.area, proto_len + 1) != 0) {
1790 appctx->st0 = PEER_SESS_ST_EXIT;
1791 appctx->st1 = PEER_SESS_SC_ERRPROTO;
1792 return -1;
1793 }
1794 if (peer_get_version(trash.area + proto_len + 1, maj_ver, min_ver) == -1 ||
1795 *maj_ver != PEER_MAJOR_VER || *min_ver > PEER_MINOR_VER) {
1796 appctx->st0 = PEER_SESS_ST_EXIT;
1797 appctx->st1 = PEER_SESS_SC_ERRVERSION;
1798 return -1;
1799 }
1800
1801 return 1;
1802}
1803
1804/*
1805 * Read and parse a second line of a "hello" peer protocol message.
1806 * Returns 0 if could not read a line, -1 if there was a read error or
1807 * the line is malformed, 1 if succeeded.
1808 */
1809static inline int peer_getline_host(struct appctx *appctx)
1810{
1811 int reql;
1812
1813 reql = peer_getline(appctx);
1814 if (!reql)
1815 return 0;
1816
1817 if (reql < 0)
1818 return -1;
1819
1820 /* test hostname match */
1821 if (strcmp(localpeer, trash.area) != 0) {
1822 appctx->st0 = PEER_SESS_ST_EXIT;
1823 appctx->st1 = PEER_SESS_SC_ERRHOST;
1824 return -1;
1825 }
1826
1827 return 1;
1828}
1829
1830/*
1831 * Read and parse a last line of a "hello" peer protocol message.
1832 * Returns 0 if could not read a character, -1 if there was a read error or
1833 * the line is malformed, 1 if succeeded.
1834 * Set <curpeer> accordingly (the remote peer sending the "hello" message).
1835 */
1836static inline int peer_getline_last(struct appctx *appctx, struct peer **curpeer)
1837{
1838 char *p;
1839 int reql;
1840 struct peer *peer;
1841 struct stream_interface *si = appctx->owner;
1842 struct stream *s = si_strm(si);
1843 struct peers *peers = strm_fe(s)->parent;
1844
1845 reql = peer_getline(appctx);
1846 if (!reql)
1847 return 0;
1848
1849 if (reql < 0)
1850 return -1;
1851
1852 /* parse line "<peer name> <pid> <relative_pid>" */
1853 p = strchr(trash.area, ' ');
1854 if (!p) {
1855 appctx->st0 = PEER_SESS_ST_EXIT;
1856 appctx->st1 = PEER_SESS_SC_ERRPROTO;
1857 return -1;
1858 }
1859 *p = 0;
1860
1861 /* lookup known peer */
1862 for (peer = peers->remote; peer; peer = peer->next) {
1863 if (strcmp(peer->id, trash.area) == 0)
1864 break;
1865 }
1866
1867 /* if unknown peer */
1868 if (!peer) {
1869 appctx->st0 = PEER_SESS_ST_EXIT;
1870 appctx->st1 = PEER_SESS_SC_ERRPEER;
1871 return -1;
1872 }
1873 *curpeer = peer;
1874
1875 return 1;
1876}
1877
1878/*
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01001879 * Init <peer> peer after having accepted it at peer protocol level.
1880 */
1881static inline void init_accepted_peer(struct peer *peer, struct peers *peers)
1882{
1883 struct shared_table *st;
1884
1885 /* Register status code */
1886 peer->statuscode = PEER_SESS_SC_SUCCESSCODE;
1887
1888 /* Awake main task */
1889 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
1890
1891 /* Init confirm counter */
1892 peer->confirm = 0;
1893
1894 /* Init cursors */
1895 for (st = peer->tables; st ; st = st->next) {
1896 st->last_get = st->last_acked = 0;
1897 st->teaching_origin = st->last_pushed = st->update;
1898 }
1899
1900 /* reset teaching and learning flags to 0 */
1901 peer->flags &= PEER_TEACH_RESET;
1902 peer->flags &= PEER_LEARN_RESET;
1903
1904 /* if current peer is local */
1905 if (peer->local) {
1906 /* if current host need resyncfrom local and no process assined */
1907 if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL &&
1908 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
1909 /* assign local peer for a lesson, consider lesson already requested */
1910 peer->flags |= PEER_F_LEARN_ASSIGN;
1911 peers->flags |= (PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
1912 }
1913
1914 }
1915 else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
1916 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
1917 /* assign peer for a lesson */
1918 peer->flags |= PEER_F_LEARN_ASSIGN;
1919 peers->flags |= PEERS_F_RESYNC_ASSIGN;
1920 }
1921}
1922
1923/*
1924 * Init <peer> peer after having connected it at peer protocol level.
1925 */
1926static inline void init_connected_peer(struct peer *peer, struct peers *peers)
1927{
1928 struct shared_table *st;
1929
1930 /* Init cursors */
1931 for (st = peer->tables; st ; st = st->next) {
1932 st->last_get = st->last_acked = 0;
1933 st->teaching_origin = st->last_pushed = st->update;
1934 }
1935
1936 /* Init confirm counter */
1937 peer->confirm = 0;
1938
1939 /* reset teaching and learning flags to 0 */
1940 peer->flags &= PEER_TEACH_RESET;
1941 peer->flags &= PEER_LEARN_RESET;
1942
1943 /* If current peer is local */
1944 if (peer->local) {
1945 /* flag to start to teach lesson */
1946 peer->flags |= PEER_F_TEACH_PROCESS;
1947 }
1948 else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
1949 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
1950 /* If peer is remote and resync from remote is needed,
1951 and no peer currently assigned */
1952
1953 /* assign peer for a lesson */
1954 peer->flags |= PEER_F_LEARN_ASSIGN;
1955 peers->flags |= PEERS_F_RESYNC_ASSIGN;
1956 }
1957}
1958
1959/*
Emeric Brun2b920a12010-09-23 18:30:22 +02001960 * IO Handler to handle message exchance with a peer
1961 */
Willy Tarreau00a37f02015-04-13 12:05:19 +02001962static void peer_io_handler(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +02001963{
Willy Tarreau00a37f02015-04-13 12:05:19 +02001964 struct stream_interface *si = appctx->owner;
Willy Tarreau87b09662015-04-03 00:22:06 +02001965 struct stream *s = si_strm(si);
Vincent Bernat3c2f2f22016-04-03 13:48:42 +02001966 struct peers *curpeers = strm_fe(s)->parent;
Emeric Brun80527f52017-06-19 17:46:37 +02001967 struct peer *curpeer = NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02001968 int reql = 0;
1969 int repl = 0;
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02001970 unsigned int maj_ver, min_ver;
Willy Tarreau2d372c22018-11-05 17:12:27 +01001971 int prev_state;
Emeric Brun2b920a12010-09-23 18:30:22 +02001972
Joseph Herlant82b2f542018-11-15 12:19:14 -08001973 /* Check if the input buffer is available. */
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001974 if (si_ic(si)->buf.size == 0) {
1975 si_rx_room_blk(si);
1976 goto out;
1977 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001978
Emeric Brun2b920a12010-09-23 18:30:22 +02001979 while (1) {
Willy Tarreau2d372c22018-11-05 17:12:27 +01001980 prev_state = appctx->st0;
Emeric Brun2b920a12010-09-23 18:30:22 +02001981switchstate:
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02001982 maj_ver = min_ver = (unsigned int)-1;
Willy Tarreau7b4b4992013-12-01 09:15:12 +01001983 switch(appctx->st0) {
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001984 case PEER_SESS_ST_ACCEPT:
Willy Tarreau2d372c22018-11-05 17:12:27 +01001985 prev_state = appctx->st0;
Willy Tarreau7b4b4992013-12-01 09:15:12 +01001986 appctx->ctx.peers.ptr = NULL;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001987 appctx->st0 = PEER_SESS_ST_GETVERSION;
Emeric Brun2b920a12010-09-23 18:30:22 +02001988 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001989 case PEER_SESS_ST_GETVERSION:
Willy Tarreau2d372c22018-11-05 17:12:27 +01001990 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01001991 reql = peer_getline_version(appctx, &maj_ver, &min_ver);
1992 if (reql <= 0) {
1993 if (!reql)
1994 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02001995 goto switchstate;
1996 }
1997
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001998 appctx->st0 = PEER_SESS_ST_GETHOST;
Emeric Brun2b920a12010-09-23 18:30:22 +02001999 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002000 case PEER_SESS_ST_GETHOST:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002001 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002002 reql = peer_getline_host(appctx);
2003 if (reql <= 0) {
2004 if (!reql)
2005 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002006 goto switchstate;
2007 }
2008
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002009 appctx->st0 = PEER_SESS_ST_GETPEER;
Emeric Brun2b920a12010-09-23 18:30:22 +02002010 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002011 case PEER_SESS_ST_GETPEER: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002012 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01002013 reql = peer_getline_last(appctx, &curpeer);
2014 if (reql <= 0) {
2015 if (!reql)
2016 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002017 goto switchstate;
2018 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002019
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002020 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Willy Tarreau9df94c22016-10-31 18:42:52 +01002021 if (curpeer->appctx && curpeer->appctx != appctx) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002022 if (curpeer->local) {
2023 /* Local connection, reply a retry */
2024 appctx->st0 = PEER_SESS_ST_EXIT;
2025 appctx->st1 = PEER_SESS_SC_TRYAGAIN;
2026 goto switchstate;
Emeric Brun2b920a12010-09-23 18:30:22 +02002027 }
Emeric Brun80527f52017-06-19 17:46:37 +02002028
2029 /* we're killing a connection, we must apply a random delay before
2030 * retrying otherwise the other end will do the same and we can loop
2031 * for a while.
2032 */
2033 curpeer->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
Willy Tarreau81bc3b02016-10-31 17:37:39 +01002034 peer_session_forceshutdown(curpeer->appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002035 }
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002036 if (maj_ver != (unsigned int)-1 && min_ver != (unsigned int)-1) {
2037 if (min_ver == PEER_DWNGRD_MINOR_VER) {
2038 curpeer->flags |= PEER_F_DWNGRD;
2039 }
2040 else {
2041 curpeer->flags &= ~PEER_F_DWNGRD;
2042 }
2043 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002044 curpeer->appctx = appctx;
2045 appctx->ctx.peers.ptr = curpeer;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002046 appctx->st0 = PEER_SESS_ST_SENDSUCCESS;
Olivier Houcharded879892019-03-08 18:53:43 +01002047 _HA_ATOMIC_ADD(&active_peers, 1);
Emeric Brun2b920a12010-09-23 18:30:22 +02002048 /* fall through */
2049 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002050 case PEER_SESS_ST_SENDSUCCESS: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002051 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002052 if (!curpeer) {
2053 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002054 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002055 if (curpeer->appctx != appctx) {
2056 appctx->st0 = PEER_SESS_ST_END;
2057 goto switchstate;
2058 }
2059 }
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002060
2061 repl = peer_send_status_successmsg(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002062 if (repl <= 0) {
2063 if (repl == -1)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002064 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002065 goto switchstate;
2066 }
2067
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002068 init_accepted_peer(curpeer, curpeers);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002069
Emeric Brun2b920a12010-09-23 18:30:22 +02002070 /* switch to waiting message state */
Olivier Houcharded879892019-03-08 18:53:43 +01002071 _HA_ATOMIC_ADD(&connected_peers, 1);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002072 appctx->st0 = PEER_SESS_ST_WAITMSG;
Emeric Brun2b920a12010-09-23 18:30:22 +02002073 goto switchstate;
2074 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002075 case PEER_SESS_ST_CONNECT: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002076 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002077 if (!curpeer) {
2078 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002079 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002080 if (curpeer->appctx != appctx) {
2081 appctx->st0 = PEER_SESS_ST_END;
2082 goto switchstate;
2083 }
2084 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002085
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002086 repl = peer_send_hellomsg(appctx, curpeer);
Emeric Brun2b920a12010-09-23 18:30:22 +02002087 if (repl <= 0) {
2088 if (repl == -1)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002089 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002090 goto switchstate;
2091 }
2092
2093 /* switch to the waiting statuscode state */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002094 appctx->st0 = PEER_SESS_ST_GETSTATUS;
Emeric Brun2b920a12010-09-23 18:30:22 +02002095 /* fall through */
2096 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002097 case PEER_SESS_ST_GETSTATUS: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002098 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002099 if (!curpeer) {
2100 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002101 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002102 if (curpeer->appctx != appctx) {
2103 appctx->st0 = PEER_SESS_ST_END;
2104 goto switchstate;
2105 }
2106 }
2107
Willy Tarreau2bb4a962014-11-28 11:11:05 +01002108 if (si_ic(si)->flags & CF_WRITE_PARTIAL)
Emeric Brunb3971ab2015-05-12 18:49:09 +02002109 curpeer->statuscode = PEER_SESS_SC_CONNECTEDCODE;
Emeric Brun2b920a12010-09-23 18:30:22 +02002110
Frédéric Lécaillece025572019-01-21 13:38:06 +01002111 reql = peer_getline(appctx);
2112 if (!reql)
2113 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002114
Frédéric Lécaillece025572019-01-21 13:38:06 +01002115 if (reql < 0)
2116 goto switchstate;
Emeric Brun2b920a12010-09-23 18:30:22 +02002117
2118 /* Register status code */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002119 curpeer->statuscode = atoi(trash.area);
Emeric Brun2b920a12010-09-23 18:30:22 +02002120
2121 /* Awake main task */
Frédéric Lécailleed2b4a62017-07-13 09:07:09 +02002122 task_wakeup(curpeers->sync_task, TASK_WOKEN_MSG);
Emeric Brun2b920a12010-09-23 18:30:22 +02002123
2124 /* If status code is success */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002125 if (curpeer->statuscode == PEER_SESS_SC_SUCCESSCODE) {
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002126 init_connected_peer(curpeer, curpeers);
Emeric Brun2b920a12010-09-23 18:30:22 +02002127 }
2128 else {
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002129 if (curpeer->statuscode == PEER_SESS_SC_ERRVERSION)
2130 curpeer->flags |= PEER_F_DWNGRD;
Emeric Brun2b920a12010-09-23 18:30:22 +02002131 /* Status code is not success, abort */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002132 appctx->st0 = PEER_SESS_ST_END;
Emeric Brun2b920a12010-09-23 18:30:22 +02002133 goto switchstate;
2134 }
Olivier Houcharded879892019-03-08 18:53:43 +01002135 _HA_ATOMIC_ADD(&connected_peers, 1);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002136 appctx->st0 = PEER_SESS_ST_WAITMSG;
Emeric Brun2b920a12010-09-23 18:30:22 +02002137 /* fall through */
2138 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002139 case PEER_SESS_ST_WAITMSG: {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002140 uint32_t msg_len = 0;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002141 char *msg_cur = trash.area;
2142 char *msg_end = trash.area;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002143 unsigned char msg_head[7];
Emeric Brun2b920a12010-09-23 18:30:22 +02002144 int totl = 0;
2145
Willy Tarreau2d372c22018-11-05 17:12:27 +01002146 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002147 if (!curpeer) {
2148 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002149 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002150 if (curpeer->appctx != appctx) {
2151 appctx->st0 = PEER_SESS_ST_END;
2152 goto switchstate;
2153 }
2154 }
2155
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002156 reql = peer_recv_msg(appctx, (char *)msg_head, sizeof msg_head, &msg_len, &totl);
2157 if (reql <= 0) {
2158 if (reql == -1)
2159 goto switchstate;
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002160 goto send_msgs;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002161 }
Willy Tarreau86a446e2013-11-25 23:02:37 +01002162
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002163 msg_end += msg_len;
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002164 if (!peer_treat_awaited_msg(appctx, curpeer, msg_head, &msg_cur, msg_end, msg_len, totl))
Emeric Brun2b920a12010-09-23 18:30:22 +02002165 goto switchstate;
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002166
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002167 curpeer->flags |= PEER_F_ALIVE;
2168
Emeric Brun2b920a12010-09-23 18:30:22 +02002169 /* skip consumed message */
Willy Tarreau06d80a92017-10-19 14:32:15 +02002170 co_skip(si_oc(si), totl);
Emeric Brun2b920a12010-09-23 18:30:22 +02002171 /* loop on that state to peek next message */
Willy Tarreau72d6c162013-04-11 16:14:13 +02002172 goto switchstate;
2173
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002174send_msgs:
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002175 if (curpeer->flags & PEER_F_HEARTBEAT) {
2176 curpeer->flags &= ~PEER_F_HEARTBEAT;
2177 repl = peer_send_heartbeatmsg(appctx);
2178 if (repl <= 0) {
2179 if (repl == -1)
2180 goto out;
2181 goto switchstate;
2182 }
2183 }
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002184 /* we get here when a peer_recv_msg() returns 0 in reql */
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002185 repl = peer_send_msgs(appctx, curpeer);
2186 if (repl <= 0) {
2187 if (repl == -1)
2188 goto out;
2189 goto switchstate;
Emeric Brun597b26e2016-08-12 11:23:31 +02002190 }
2191
Emeric Brun2b920a12010-09-23 18:30:22 +02002192 /* noting more to do */
2193 goto out;
2194 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002195 case PEER_SESS_ST_EXIT:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002196 if (prev_state == PEER_SESS_ST_WAITMSG)
Olivier Houcharded879892019-03-08 18:53:43 +01002197 _HA_ATOMIC_SUB(&connected_peers, 1);
Willy Tarreau2d372c22018-11-05 17:12:27 +01002198 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002199 if (peer_send_status_errormsg(appctx) == -1)
2200 goto out;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002201 appctx->st0 = PEER_SESS_ST_END;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002202 goto switchstate;
2203 case PEER_SESS_ST_ERRSIZE: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002204 if (prev_state == PEER_SESS_ST_WAITMSG)
Olivier Houcharded879892019-03-08 18:53:43 +01002205 _HA_ATOMIC_SUB(&connected_peers, 1);
Willy Tarreau2d372c22018-11-05 17:12:27 +01002206 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002207 if (peer_send_error_size_limitmsg(appctx) == -1)
2208 goto out;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002209 appctx->st0 = PEER_SESS_ST_END;
2210 goto switchstate;
2211 }
2212 case PEER_SESS_ST_ERRPROTO: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002213 if (prev_state == PEER_SESS_ST_WAITMSG)
Olivier Houcharded879892019-03-08 18:53:43 +01002214 _HA_ATOMIC_SUB(&connected_peers, 1);
Willy Tarreau2d372c22018-11-05 17:12:27 +01002215 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002216 if (peer_send_error_protomsg(appctx) == -1)
2217 goto out;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002218 appctx->st0 = PEER_SESS_ST_END;
Willy Tarreau2d372c22018-11-05 17:12:27 +01002219 prev_state = appctx->st0;
Emeric Brun2b920a12010-09-23 18:30:22 +02002220 /* fall through */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002221 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002222 case PEER_SESS_ST_END: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002223 if (prev_state == PEER_SESS_ST_WAITMSG)
Olivier Houcharded879892019-03-08 18:53:43 +01002224 _HA_ATOMIC_SUB(&connected_peers, 1);
Willy Tarreau2d372c22018-11-05 17:12:27 +01002225 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002226 if (curpeer) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002227 HA_SPIN_UNLOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002228 curpeer = NULL;
2229 }
Willy Tarreau73b013b2012-05-21 16:31:45 +02002230 si_shutw(si);
2231 si_shutr(si);
Willy Tarreau2bb4a962014-11-28 11:11:05 +01002232 si_ic(si)->flags |= CF_READ_NULL;
Willy Tarreau828824a2015-04-19 17:20:03 +02002233 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002234 }
2235 }
2236 }
2237out:
Willy Tarreau2bb4a962014-11-28 11:11:05 +01002238 si_oc(si)->flags |= CF_READ_DONTWAIT;
Emeric Brun80527f52017-06-19 17:46:37 +02002239
2240 if (curpeer)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002241 HA_SPIN_UNLOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun2b920a12010-09-23 18:30:22 +02002242 return;
2243}
2244
Willy Tarreau30576452015-04-13 13:50:30 +02002245static struct applet peer_applet = {
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002246 .obj_type = OBJ_TYPE_APPLET,
Willy Tarreaub24281b2011-02-13 13:16:36 +01002247 .name = "<PEER>", /* used for logging */
2248 .fct = peer_io_handler,
Aman Gupta9a13e842012-04-02 18:57:53 -07002249 .release = peer_session_release,
Willy Tarreaub24281b2011-02-13 13:16:36 +01002250};
Emeric Brun2b920a12010-09-23 18:30:22 +02002251
2252/*
2253 * Use this function to force a close of a peer session
2254 */
Willy Tarreau81bc3b02016-10-31 17:37:39 +01002255static void peer_session_forceshutdown(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +02002256{
Frédéric Lécaille5df11902017-06-13 16:39:57 +02002257 /* Note that the peer sessions which have just been created
2258 * (->st0 == PEER_SESS_ST_CONNECT) must not
2259 * be shutdown, if not, the TCP session will never be closed
2260 * and stay in CLOSE_WAIT state after having been closed by
2261 * the remote side.
2262 */
2263 if (!appctx || appctx->st0 == PEER_SESS_ST_CONNECT)
Willy Tarreau7b4b4992013-12-01 09:15:12 +01002264 return;
2265
Willy Tarreau81bc3b02016-10-31 17:37:39 +01002266 if (appctx->applet != &peer_applet)
2267 return;
2268
Willy Tarreau2d372c22018-11-05 17:12:27 +01002269 if (appctx->st0 == PEER_SESS_ST_WAITMSG)
Olivier Houcharded879892019-03-08 18:53:43 +01002270 _HA_ATOMIC_SUB(&connected_peers, 1);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002271 appctx->st0 = PEER_SESS_ST_END;
Willy Tarreau78c0c502016-10-31 17:32:20 +01002272 appctx_wakeup(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002273}
2274
Willy Tarreau91d96282015-03-13 15:47:26 +01002275/* Pre-configures a peers frontend to accept incoming connections */
2276void peers_setup_frontend(struct proxy *fe)
2277{
2278 fe->last_change = now.tv_sec;
Frédéric Lécaillec06b5d42018-04-26 10:06:41 +02002279 fe->cap = PR_CAP_FE | PR_CAP_BE;
Willy Tarreau91d96282015-03-13 15:47:26 +01002280 fe->maxconn = 0;
2281 fe->conn_retries = CONN_RETRIES;
2282 fe->timeout.client = MS_TO_TICKS(5000);
Willy Tarreaud1d48d42015-03-13 16:15:46 +01002283 fe->accept = frontend_accept;
Willy Tarreauf87ab942015-03-13 15:55:16 +01002284 fe->default_target = &peer_applet.obj_type;
Willy Tarreau91d96282015-03-13 15:47:26 +01002285 fe->options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
Willy Tarreau0fca4832015-05-01 19:12:05 +02002286 fe->bind_proc = 0; /* will be filled by users */
Willy Tarreau91d96282015-03-13 15:47:26 +01002287}
2288
Emeric Brun2b920a12010-09-23 18:30:22 +02002289/*
Willy Tarreaubd55e312010-11-11 10:55:09 +01002290 * Create a new peer session in assigned state (connect will start automatically)
Emeric Brun2b920a12010-09-23 18:30:22 +02002291 */
Willy Tarreau9df94c22016-10-31 18:42:52 +01002292static struct appctx *peer_session_create(struct peers *peers, struct peer *peer)
Emeric Brun2b920a12010-09-23 18:30:22 +02002293{
Willy Tarreau04b92862017-09-15 11:01:04 +02002294 struct proxy *p = peers->peers_fe; /* attached frontend */
Willy Tarreau7b4b4992013-12-01 09:15:12 +01002295 struct appctx *appctx;
Willy Tarreau15b5e142015-04-04 14:38:25 +02002296 struct session *sess;
Willy Tarreau87b09662015-04-03 00:22:06 +02002297 struct stream *s;
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002298 struct connection *conn;
Olivier Houchard9aaf7782017-09-13 18:30:23 +02002299 struct conn_stream *cs;
Emeric Brun2b920a12010-09-23 18:30:22 +02002300
Emeric Brunb3971ab2015-05-12 18:49:09 +02002301 peer->reconnect = tick_add(now_ms, MS_TO_TICKS(5000));
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002302 peer->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
Emeric Brunb3971ab2015-05-12 18:49:09 +02002303 peer->statuscode = PEER_SESS_SC_CONNECTCODE;
Willy Tarreaud990baf2015-04-05 00:32:03 +02002304 s = NULL;
2305
Emeric Brun1138fd02017-06-19 12:38:55 +02002306 appctx = appctx_new(&peer_applet, tid_bit);
Willy Tarreaud990baf2015-04-05 00:32:03 +02002307 if (!appctx)
2308 goto out_close;
2309
2310 appctx->st0 = PEER_SESS_ST_CONNECT;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002311 appctx->ctx.peers.ptr = (void *)peer;
Willy Tarreaud990baf2015-04-05 00:32:03 +02002312
Willy Tarreau04b92862017-09-15 11:01:04 +02002313 sess = session_new(p, NULL, &appctx->obj_type);
Willy Tarreau15b5e142015-04-04 14:38:25 +02002314 if (!sess) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002315 ha_alert("out of memory in peer_session_create().\n");
Willy Tarreaud990baf2015-04-05 00:32:03 +02002316 goto out_free_appctx;
Emeric Brun2b920a12010-09-23 18:30:22 +02002317 }
2318
Willy Tarreau87787ac2017-08-28 16:22:54 +02002319 if ((s = stream_new(sess, &appctx->obj_type)) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002320 ha_alert("Failed to initialize stream in peer_session_create().\n");
Willy Tarreau87787ac2017-08-28 16:22:54 +02002321 goto out_free_sess;
Willy Tarreau8baf9062015-04-05 00:46:36 +02002322 }
2323
Willy Tarreau342bfb12015-04-05 01:35:34 +02002324 /* The tasks below are normally what is supposed to be done by
2325 * fe->accept().
2326 */
Willy Tarreaue7dff022015-04-03 01:14:29 +02002327 s->flags = SF_ASSIGNED|SF_ADDR_SET;
Emeric Brun2b920a12010-09-23 18:30:22 +02002328
Willy Tarreau6e2979c2015-04-27 13:21:15 +02002329 /* applet is waiting for data */
Willy Tarreau0cd3bd62018-11-06 18:46:37 +01002330 si_cant_get(&s->si[0]);
Willy Tarreau6e2979c2015-04-27 13:21:15 +02002331 appctx_wakeup(appctx);
2332
Willy Tarreau3ed35ef2013-10-24 11:51:38 +02002333 /* initiate an outgoing connection */
Willy Tarreaudbd02672017-12-06 17:39:53 +01002334 s->si[1].flags |= SI_FL_NOLINGER;
Willy Tarreau3ed35ef2013-10-24 11:51:38 +02002335 si_set_state(&s->si[1], SI_ST_ASS);
Willy Tarreau3ed35ef2013-10-24 11:51:38 +02002336
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002337 /* automatically prepare the stream interface to connect to the
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002338 * pre-initialized connection in si->conn.
2339 */
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002340 if (unlikely((conn = conn_new()) == NULL))
Willy Tarreau8baf9062015-04-05 00:46:36 +02002341 goto out_free_strm;
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002342
Olivier Houchard9aaf7782017-09-13 18:30:23 +02002343 if (unlikely((cs = cs_new(conn)) == NULL))
2344 goto out_free_conn;
2345
Frédéric Lécaille1055e682018-04-26 14:35:21 +02002346 conn->target = s->target = peer_session_target(peer, s);
Willy Tarreaube373152018-09-06 11:45:30 +02002347 memcpy(&conn->addr.to, &peer->addr, sizeof(conn->addr.to));
2348
Frédéric Lécaille1055e682018-04-26 14:35:21 +02002349 conn_prepare(conn, peer->proto, peer_xprt(peer));
Olivier Houchardef60ff32019-01-29 19:00:33 +01002350 if (conn_install_mux(conn, &mux_pt_ops, cs, s->be, NULL) < 0)
2351 goto out_free_cs;
Olivier Houchard9aaf7782017-09-13 18:30:23 +02002352 si_attach_cs(&s->si[1], cs);
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002353
Emeric Brun2b920a12010-09-23 18:30:22 +02002354 s->do_log = NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002355 s->uniq_id = 0;
Emeric Brun2b920a12010-09-23 18:30:22 +02002356
Willy Tarreau22ec1ea2014-11-27 20:45:39 +01002357 s->res.flags |= CF_READ_DONTWAIT;
Willy Tarreau696a2912014-11-24 11:36:57 +01002358
Emeric Brunb3971ab2015-05-12 18:49:09 +02002359 peer->appctx = appctx;
Willy Tarreau87787ac2017-08-28 16:22:54 +02002360 task_wakeup(s->task, TASK_WOKEN_INIT);
Olivier Houcharded879892019-03-08 18:53:43 +01002361 _HA_ATOMIC_ADD(&active_peers, 1);
Willy Tarreau9df94c22016-10-31 18:42:52 +01002362 return appctx;
Emeric Brun2b920a12010-09-23 18:30:22 +02002363
2364 /* Error unrolling */
Olivier Houchardef60ff32019-01-29 19:00:33 +01002365out_free_cs:
2366 cs_free(cs);
Olivier Houchard9aaf7782017-09-13 18:30:23 +02002367 out_free_conn:
2368 conn_free(conn);
Willy Tarreau15b5e142015-04-04 14:38:25 +02002369 out_free_strm:
Emeric Brun2b920a12010-09-23 18:30:22 +02002370 LIST_DEL(&s->list);
Willy Tarreaubafbe012017-11-24 17:34:44 +01002371 pool_free(pool_head_stream, s);
Willy Tarreau15b5e142015-04-04 14:38:25 +02002372 out_free_sess:
Willy Tarreau11c36242015-04-04 15:54:03 +02002373 session_free(sess);
Willy Tarreaud990baf2015-04-05 00:32:03 +02002374 out_free_appctx:
2375 appctx_free(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002376 out_close:
Willy Tarreaub21d08e2016-10-31 17:46:57 +01002377 return NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002378}
2379
2380/*
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002381 * Task processing function to manage re-connect, peer session
2382 * tasks wakeup on local update and heartbeat.
Emeric Brun2b920a12010-09-23 18:30:22 +02002383 */
Olivier Houchard9f6af332018-05-25 14:04:04 +02002384static struct task *process_peer_sync(struct task * task, void *context, unsigned short state)
Emeric Brun2b920a12010-09-23 18:30:22 +02002385{
Olivier Houchard9f6af332018-05-25 14:04:04 +02002386 struct peers *peers = context;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002387 struct peer *ps;
2388 struct shared_table *st;
Emeric Brun2b920a12010-09-23 18:30:22 +02002389
2390 task->expire = TICK_ETERNITY;
2391
Emeric Brunb3971ab2015-05-12 18:49:09 +02002392 if (!peers->peers_fe) {
Willy Tarreau46dc1ca2015-05-01 18:32:13 +02002393 /* this one was never started, kill it */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002394 signal_unregister_handler(peers->sighandler);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002395 task_delete(peers->sync_task);
2396 task_free(peers->sync_task);
Willy Tarreau37bb7be2015-09-21 15:24:58 +02002397 peers->sync_task = NULL;
Willy Tarreau46dc1ca2015-05-01 18:32:13 +02002398 return NULL;
2399 }
2400
Emeric Brun80527f52017-06-19 17:46:37 +02002401 /* Acquire lock for all peers of the section */
2402 for (ps = peers->remote; ps; ps = ps->next)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002403 HA_SPIN_LOCK(PEER_LOCK, &ps->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002404
Emeric Brun2b920a12010-09-23 18:30:22 +02002405 if (!stopping) {
2406 /* Normal case (not soft stop)*/
Emeric Brunb3971ab2015-05-12 18:49:09 +02002407
2408 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL) &&
2409 (!nb_oldpids || tick_is_expired(peers->resync_timeout, now_ms)) &&
2410 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002411 /* Resync from local peer needed
2412 no peer was assigned for the lesson
2413 and no old local peer found
2414 or resync timeout expire */
2415
2416 /* flag no more resync from local, to try resync from remotes */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002417 peers->flags |= PEERS_F_RESYNC_LOCAL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002418
2419 /* reschedule a resync */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002420 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
Emeric Brun2b920a12010-09-23 18:30:22 +02002421 }
2422
2423 /* For each session */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002424 for (ps = peers->remote; ps; ps = ps->next) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002425 /* For each remote peers */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002426 if (!ps->local) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002427 if (!ps->appctx) {
2428 /* no active peer connection */
Emeric Brun2b920a12010-09-23 18:30:22 +02002429 if (ps->statuscode == 0 ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002430 ((ps->statuscode == PEER_SESS_SC_CONNECTCODE ||
Willy Tarreaub4e34da2015-05-20 10:39:04 +02002431 ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002432 ps->statuscode == PEER_SESS_SC_CONNECTEDCODE) &&
Emeric Brun2b920a12010-09-23 18:30:22 +02002433 tick_is_expired(ps->reconnect, now_ms))) {
2434 /* connection never tried
Willy Tarreau9df94c22016-10-31 18:42:52 +01002435 * or previous peer connection established with success
2436 * or previous peer connection failed while connecting
Emeric Brun2b920a12010-09-23 18:30:22 +02002437 * and reconnection timer is expired */
2438
2439 /* retry a connect */
Willy Tarreau9df94c22016-10-31 18:42:52 +01002440 ps->appctx = peer_session_create(peers, ps);
Emeric Brun2b920a12010-09-23 18:30:22 +02002441 }
Willy Tarreaub4e34da2015-05-20 10:39:04 +02002442 else if (!tick_is_expired(ps->reconnect, now_ms)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002443 /* If previous session failed during connection
2444 * but reconnection timer is not expired */
2445
2446 /* reschedule task for reconnect */
2447 task->expire = tick_first(task->expire, ps->reconnect);
2448 }
2449 /* else do nothing */
Willy Tarreau9df94c22016-10-31 18:42:52 +01002450 } /* !ps->appctx */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002451 else if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002452 /* current peer connection is active and established */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002453 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE) &&
2454 !(peers->flags & PEERS_F_RESYNC_ASSIGN) &&
Emeric Brun2b920a12010-09-23 18:30:22 +02002455 !(ps->flags & PEER_F_LEARN_NOTUP2DATE)) {
2456 /* Resync from a remote is needed
2457 * and no peer was assigned for lesson
2458 * and current peer may be up2date */
2459
2460 /* assign peer for the lesson */
2461 ps->flags |= PEER_F_LEARN_ASSIGN;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002462 peers->flags |= PEERS_F_RESYNC_ASSIGN;
Emeric Brun2b920a12010-09-23 18:30:22 +02002463
Willy Tarreau9df94c22016-10-31 18:42:52 +01002464 /* wake up peer handler to handle a request of resync */
Willy Tarreaue5843b32015-04-27 18:40:14 +02002465 appctx_wakeup(ps->appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002466 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002467 else {
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002468 int update_to_push = 0;
2469
Emeric Brunb3971ab2015-05-12 18:49:09 +02002470 /* Awake session if there is data to push */
2471 for (st = ps->tables; st ; st = st->next) {
2472 if ((int)(st->last_pushed - st->table->localupdate) < 0) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002473 /* wake up the peer handler to push local updates */
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002474 update_to_push = 1;
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002475 /* There is no need to send a heartbeat message
2476 * when some updates must be pushed. The remote
2477 * peer will consider <ps> peer as alive when it will
2478 * receive these updates.
2479 */
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002480 ps->flags &= ~PEER_F_HEARTBEAT;
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002481 /* Re-schedule another one later. */
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002482 ps->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002483 /* We are going to send updates, let's ensure we will
2484 * come back to send heartbeat messages or to reconnect.
2485 */
2486 task->expire = tick_first(ps->reconnect, ps->heartbeat);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002487 appctx_wakeup(ps->appctx);
2488 break;
2489 }
2490 }
Frédéric Lécaille045e0d42019-03-21 11:12:32 +01002491 /* When there are updates to send we do not reconnect
2492 * and do not send heartbeat message either.
2493 */
2494 if (!update_to_push) {
2495 if (tick_is_expired(ps->reconnect, now_ms)) {
2496 if (ps->flags & PEER_F_ALIVE) {
2497 /* This peer was alive during a 'reconnect' period.
2498 * Flag it as not alive again for the next period.
2499 */
2500 ps->flags &= ~PEER_F_ALIVE;
2501 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(5000));
2502 }
2503 else {
2504 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
2505 peer_session_forceshutdown(ps->appctx);
2506 ps->appctx = NULL;
2507 }
2508 }
2509 else if (tick_is_expired(ps->heartbeat, now_ms)) {
2510 ps->heartbeat = tick_add(now_ms, MS_TO_TICKS(PEER_HEARTBEAT_TIMEOUT));
2511 ps->flags |= PEER_F_HEARTBEAT;
2512 appctx_wakeup(ps->appctx);
2513 }
2514 if (ps->appctx)
2515 task->expire = tick_first(ps->reconnect, ps->heartbeat);
Frédéric Lécaille645635d2019-02-11 17:49:39 +01002516 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002517 }
2518 /* else do nothing */
2519 } /* SUCCESSCODE */
2520 } /* !ps->peer->local */
2521 } /* for */
2522
2523 /* Resync from remotes expired: consider resync is finished */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002524 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE) &&
2525 !(peers->flags & PEERS_F_RESYNC_ASSIGN) &&
2526 tick_is_expired(peers->resync_timeout, now_ms)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002527 /* Resync from remote peer needed
2528 * no peer was assigned for the lesson
2529 * and resync timeout expire */
2530
2531 /* flag no more resync from remote, consider resync is finished */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002532 peers->flags |= PEERS_F_RESYNC_REMOTE;
Emeric Brun2b920a12010-09-23 18:30:22 +02002533 }
2534
Emeric Brunb3971ab2015-05-12 18:49:09 +02002535 if ((peers->flags & PEERS_RESYNC_STATEMASK) != PEERS_RESYNC_FINISHED) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002536 /* Resync not finished*/
Frédéric Lécaille5d6e5f82017-05-29 13:47:16 +02002537 /* reschedule task to resync timeout if not expired, to ended resync if needed */
2538 if (!tick_is_expired(peers->resync_timeout, now_ms))
2539 task->expire = tick_first(task->expire, peers->resync_timeout);
Emeric Brun2b920a12010-09-23 18:30:22 +02002540 }
2541 } /* !stopping */
2542 else {
2543 /* soft stop case */
Willy Tarreau086735a2018-11-05 15:09:47 +01002544 if (state & TASK_WOKEN_SIGNAL) {
Joseph Herlant82b2f542018-11-15 12:19:14 -08002545 /* We've just received the signal */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002546 if (!(peers->flags & PEERS_F_DONOTSTOP)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002547 /* add DO NOT STOP flag if not present */
Olivier Houcharded879892019-03-08 18:53:43 +01002548 _HA_ATOMIC_ADD(&jobs, 1);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002549 peers->flags |= PEERS_F_DONOTSTOP;
2550 ps = peers->local;
2551 for (st = ps->tables; st ; st = st->next)
2552 st->table->syncing++;
Emeric Brun2b920a12010-09-23 18:30:22 +02002553 }
2554
2555 /* disconnect all connected peers */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002556 for (ps = peers->remote; ps; ps = ps->next) {
Emeric Brun80527f52017-06-19 17:46:37 +02002557 /* we're killing a connection, we must apply a random delay before
2558 * retrying otherwise the other end will do the same and we can loop
2559 * for a while.
2560 */
2561 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
Willy Tarreau9df94c22016-10-31 18:42:52 +01002562 if (ps->appctx) {
Willy Tarreau81bc3b02016-10-31 17:37:39 +01002563 peer_session_forceshutdown(ps->appctx);
Willy Tarreaue5843b32015-04-27 18:40:14 +02002564 ps->appctx = NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002565 }
2566 }
2567 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002568
Emeric Brunb3971ab2015-05-12 18:49:09 +02002569 ps = peers->local;
Emeric Brun2b920a12010-09-23 18:30:22 +02002570 if (ps->flags & PEER_F_TEACH_COMPLETE) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002571 if (peers->flags & PEERS_F_DONOTSTOP) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002572 /* resync of new process was complete, current process can die now */
Olivier Houcharded879892019-03-08 18:53:43 +01002573 _HA_ATOMIC_SUB(&jobs, 1);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002574 peers->flags &= ~PEERS_F_DONOTSTOP;
2575 for (st = ps->tables; st ; st = st->next)
2576 st->table->syncing--;
Emeric Brun2b920a12010-09-23 18:30:22 +02002577 }
2578 }
Willy Tarreau9df94c22016-10-31 18:42:52 +01002579 else if (!ps->appctx) {
2580 /* If there's no active peer connection */
Emeric Brun2b920a12010-09-23 18:30:22 +02002581 if (ps->statuscode == 0 ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002582 ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
2583 ps->statuscode == PEER_SESS_SC_CONNECTEDCODE ||
2584 ps->statuscode == PEER_SESS_SC_TRYAGAIN) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002585 /* connection never tried
Willy Tarreau9df94c22016-10-31 18:42:52 +01002586 * or previous peer connection was successfully established
2587 * or previous tcp connect succeeded but init state incomplete
Emeric Brun2b920a12010-09-23 18:30:22 +02002588 * or during previous connect, peer replies a try again statuscode */
2589
2590 /* connect to the peer */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002591 peer_session_create(peers, ps);
Emeric Brun2b920a12010-09-23 18:30:22 +02002592 }
2593 else {
2594 /* Other error cases */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002595 if (peers->flags & PEERS_F_DONOTSTOP) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002596 /* unable to resync new process, current process can die now */
Olivier Houcharded879892019-03-08 18:53:43 +01002597 _HA_ATOMIC_SUB(&jobs, 1);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002598 peers->flags &= ~PEERS_F_DONOTSTOP;
2599 for (st = ps->tables; st ; st = st->next)
2600 st->table->syncing--;
Emeric Brun2b920a12010-09-23 18:30:22 +02002601 }
2602 }
2603 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002604 else if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE ) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002605 /* current peer connection is active and established
2606 * wake up all peer handlers to push remaining local updates */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002607 for (st = ps->tables; st ; st = st->next) {
2608 if ((int)(st->last_pushed - st->table->localupdate) < 0) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002609 appctx_wakeup(ps->appctx);
2610 break;
2611 }
2612 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002613 }
2614 } /* stopping */
Emeric Brun80527f52017-06-19 17:46:37 +02002615
2616 /* Release lock for all peers of the section */
2617 for (ps = peers->remote; ps; ps = ps->next)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002618 HA_SPIN_UNLOCK(PEER_LOCK, &ps->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002619
Emeric Brun2b920a12010-09-23 18:30:22 +02002620 /* Wakeup for re-connect */
2621 return task;
2622}
2623
Emeric Brunb3971ab2015-05-12 18:49:09 +02002624
Emeric Brun2b920a12010-09-23 18:30:22 +02002625/*
Willy Tarreaud9443442018-10-15 11:18:03 +02002626 * returns 0 in case of error.
Emeric Brun2b920a12010-09-23 18:30:22 +02002627 */
Willy Tarreaud9443442018-10-15 11:18:03 +02002628int peers_init_sync(struct peers *peers)
Emeric Brun2b920a12010-09-23 18:30:22 +02002629{
Emeric Brun2b920a12010-09-23 18:30:22 +02002630 struct peer * curpeer;
Emeric Brun2b920a12010-09-23 18:30:22 +02002631
Emeric Brun2b920a12010-09-23 18:30:22 +02002632 for (curpeer = peers->remote; curpeer; curpeer = curpeer->next) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002633 peers->peers_fe->maxconn += 3;
2634 }
2635
Emeric Brunc60def82017-09-27 14:59:38 +02002636 peers->sync_task = task_new(MAX_THREADS_MASK);
Willy Tarreaud9443442018-10-15 11:18:03 +02002637 if (!peers->sync_task)
2638 return 0;
2639
Emeric Brunb3971ab2015-05-12 18:49:09 +02002640 peers->sync_task->process = process_peer_sync;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002641 peers->sync_task->context = (void *)peers;
2642 peers->sighandler = signal_register_task(0, peers->sync_task, 0);
2643 task_wakeup(peers->sync_task, TASK_WOKEN_INIT);
Willy Tarreaud9443442018-10-15 11:18:03 +02002644 return 1;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002645}
2646
2647
2648
2649/*
2650 * Function used to register a table for sync on a group of peers
2651 *
2652 */
2653void peers_register_table(struct peers *peers, struct stktable *table)
2654{
2655 struct shared_table *st;
2656 struct peer * curpeer;
2657 int id = 0;
2658
2659 for (curpeer = peers->remote; curpeer; curpeer = curpeer->next) {
Vincent Bernat02779b62016-04-03 13:48:43 +02002660 st = calloc(1,sizeof(*st));
Emeric Brunb3971ab2015-05-12 18:49:09 +02002661 st->table = table;
2662 st->next = curpeer->tables;
2663 if (curpeer->tables)
2664 id = curpeer->tables->local_id;
2665 st->local_id = id + 1;
2666
2667 curpeer->tables = st;
2668 }
2669
2670 table->sync_task = peers->sync_task;
Emeric Brun2b920a12010-09-23 18:30:22 +02002671}
2672