blob: dc5bdeaf8d14bb1018e27cfb488581935e02fe3a [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/******************************/
Emeric Brunb3971ab2015-05-12 18:49:09 +020059#define PEERS_F_RESYNC_LOCAL 0x00000001 /* Learn from local finished or no more needed */
60#define PEERS_F_RESYNC_REMOTE 0x00000002 /* Learn from remote finished or no more needed */
61#define PEERS_F_RESYNC_ASSIGN 0x00000004 /* A peer was assigned to learn our lesson */
62#define PEERS_F_RESYNC_PROCESS 0x00000008 /* The assigned peer was requested for resync */
63#define PEERS_F_DONOTSTOP 0x00010000 /* Main table sync task block process during soft stop
Emeric Brun2b920a12010-09-23 18:30:22 +020064 to push data to new process */
65
Emeric Brunb3971ab2015-05-12 18:49:09 +020066#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)
70
71/***********************************/
72/* Current shared table sync state */
73/***********************************/
74#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/******************************/
80#define PEER_F_TEACH_PROCESS 0x00000001 /* Teach a lesson to current peer */
Emeric Brun2b920a12010-09-23 18:30:22 +020081#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 */
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +020085#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 +020086
Emeric Brunb3971ab2015-05-12 18:49:09 +020087#define PEER_TEACH_RESET ~(PEER_F_TEACH_PROCESS|PEER_F_TEACH_FINISHED) /* PEER_F_TEACH_COMPLETE should never be reset */
Emeric Brun2b920a12010-09-23 18:30:22 +020088#define PEER_LEARN_RESET ~(PEER_F_LEARN_ASSIGN|PEER_F_LEARN_NOTUP2DATE)
89
Emeric Brunb3971ab2015-05-12 18:49:09 +020090/*****************************/
91/* Sync message class */
92/*****************************/
93enum {
94 PEER_MSG_CLASS_CONTROL = 0,
95 PEER_MSG_CLASS_ERROR,
96 PEER_MSG_CLASS_STICKTABLE = 10,
97 PEER_MSG_CLASS_RESERVED = 255,
98};
99
100/*****************************/
101/* control message types */
102/*****************************/
103enum {
104 PEER_MSG_CTRL_RESYNCREQ = 0,
105 PEER_MSG_CTRL_RESYNCFINISHED,
106 PEER_MSG_CTRL_RESYNCPARTIAL,
107 PEER_MSG_CTRL_RESYNCCONFIRM,
108};
109
110/*****************************/
111/* error message types */
112/*****************************/
113enum {
114 PEER_MSG_ERR_PROTOCOL = 0,
115 PEER_MSG_ERR_SIZELIMIT,
116};
117
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100118/*
119 * Parameters used by functions to build peer protocol messages. */
120struct peer_prep_params {
121 struct {
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100122 struct peer *peer;
123 } hello;
124 struct {
125 unsigned int st1;
126 } error_status;
127 struct {
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100128 struct stksess *stksess;
129 struct shared_table *shared_table;
130 unsigned int updateid;
131 int use_identifier;
132 int use_timed;
133 } updt;
134 struct {
135 struct shared_table *shared_table;
136 } swtch;
137 struct {
138 struct shared_table *shared_table;
139 } ack;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100140 struct {
141 unsigned char head[2];
142 } control;
143 struct {
144 unsigned char head[2];
145 } error;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100146};
Emeric Brunb3971ab2015-05-12 18:49:09 +0200147
148/*******************************/
149/* stick table sync mesg types */
150/* Note: ids >= 128 contains */
151/* id message cotains data */
152/*******************************/
Olivier Houchard33992262018-10-16 18:49:26 +0200153#define PEER_MSG_STKT_UPDATE 0x80
154#define PEER_MSG_STKT_INCUPDATE 0x81
155#define PEER_MSG_STKT_DEFINE 0x82
156#define PEER_MSG_STKT_SWITCH 0x83
157#define PEER_MSG_STKT_ACK 0x84
158#define PEER_MSG_STKT_UPDATE_TIMED 0x85
159#define PEER_MSG_STKT_INCUPDATE_TIMED 0x86
Emeric Brun2b920a12010-09-23 18:30:22 +0200160
161/**********************************/
162/* Peer Session IO handler states */
163/**********************************/
164
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100165enum {
166 PEER_SESS_ST_ACCEPT = 0, /* Initial state for session create by an accept, must be zero! */
167 PEER_SESS_ST_GETVERSION, /* Validate supported protocol version */
168 PEER_SESS_ST_GETHOST, /* Validate host ID correspond to local host id */
169 PEER_SESS_ST_GETPEER, /* Validate peer ID correspond to a known remote peer id */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100170 /* after this point, data were possibly exchanged */
171 PEER_SESS_ST_SENDSUCCESS, /* Send ret code 200 (success) and wait for message */
172 PEER_SESS_ST_CONNECT, /* Initial state for session create on a connect, push presentation into buffer */
173 PEER_SESS_ST_GETSTATUS, /* Wait for the welcome message */
174 PEER_SESS_ST_WAITMSG, /* Wait for data messages */
175 PEER_SESS_ST_EXIT, /* Exit with status code */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200176 PEER_SESS_ST_ERRPROTO, /* Send error proto message before exit */
177 PEER_SESS_ST_ERRSIZE, /* Send error size message before exit */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100178 PEER_SESS_ST_END, /* Killed session */
179};
Emeric Brun2b920a12010-09-23 18:30:22 +0200180
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100181/***************************************************/
182/* Peer Session status code - part of the protocol */
183/***************************************************/
Emeric Brun2b920a12010-09-23 18:30:22 +0200184
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100185#define PEER_SESS_SC_CONNECTCODE 100 /* connect in progress */
186#define PEER_SESS_SC_CONNECTEDCODE 110 /* tcp connect success */
Emeric Brun2b920a12010-09-23 18:30:22 +0200187
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100188#define PEER_SESS_SC_SUCCESSCODE 200 /* accept or connect successful */
Emeric Brun2b920a12010-09-23 18:30:22 +0200189
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100190#define PEER_SESS_SC_TRYAGAIN 300 /* try again later */
Emeric Brun2b920a12010-09-23 18:30:22 +0200191
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100192#define PEER_SESS_SC_ERRPROTO 501 /* error protocol */
193#define PEER_SESS_SC_ERRVERSION 502 /* unknown protocol version */
194#define PEER_SESS_SC_ERRHOST 503 /* bad host name */
195#define PEER_SESS_SC_ERRPEER 504 /* unknown peer */
Emeric Brun2b920a12010-09-23 18:30:22 +0200196
197#define PEER_SESSION_PROTO_NAME "HAProxyS"
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200198#define PEER_MAJOR_VER 2
199#define PEER_MINOR_VER 1
200#define PEER_DWNGRD_MINOR_VER 0
Emeric Brun2b920a12010-09-23 18:30:22 +0200201
Willy Tarreau6254a922019-01-29 17:45:23 +0100202static size_t proto_len = sizeof(PEER_SESSION_PROTO_NAME) - 1;
Frédéric Lécailleed2b4a62017-07-13 09:07:09 +0200203struct peers *cfg_peers = NULL;
Willy Tarreau81bc3b02016-10-31 17:37:39 +0100204static void peer_session_forceshutdown(struct appctx *appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +0200205
Emeric Brun18928af2017-03-29 16:32:53 +0200206/* This function encode an uint64 to 'dynamic' length format.
207 The encoded value is written at address *str, and the
208 caller must assure that size after *str is large enought.
209 At return, the *str is set at the next Byte after then
210 encoded integer. The function returns then length of the
211 encoded integer in Bytes */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200212int intencode(uint64_t i, char **str) {
213 int idx = 0;
214 unsigned char *msg;
215
Emeric Brunb3971ab2015-05-12 18:49:09 +0200216 msg = (unsigned char *)*str;
217 if (i < 240) {
218 msg[0] = (unsigned char)i;
219 *str = (char *)&msg[idx+1];
220 return (idx+1);
221 }
222
223 msg[idx] =(unsigned char)i | 240;
224 i = (i - 240) >> 4;
225 while (i >= 128) {
226 msg[++idx] = (unsigned char)i | 128;
227 i = (i - 128) >> 7;
228 }
229 msg[++idx] = (unsigned char)i;
230 *str = (char *)&msg[idx+1];
231 return (idx+1);
232}
233
234
235/* This function returns the decoded integer or 0
236 if decode failed
237 *str point on the beginning of the integer to decode
238 at the end of decoding *str point on the end of the
239 encoded integer or to null if end is reached */
Emeric Brun18928af2017-03-29 16:32:53 +0200240uint64_t intdecode(char **str, char *end)
241{
Emeric Brunb3971ab2015-05-12 18:49:09 +0200242 unsigned char *msg;
Emeric Brun18928af2017-03-29 16:32:53 +0200243 uint64_t i;
244 int shift;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200245
246 if (!*str)
247 return 0;
248
249 msg = (unsigned char *)*str;
Emeric Brun18928af2017-03-29 16:32:53 +0200250 if (msg >= (unsigned char *)end)
251 goto fail;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200252
Emeric Brun18928af2017-03-29 16:32:53 +0200253 i = *(msg++);
254 if (i >= 240) {
255 shift = 4;
256 do {
257 if (msg >= (unsigned char *)end)
258 goto fail;
259 i += (uint64_t)*msg << shift;
260 shift += 7;
261 } while (*(msg++) >= 128);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200262 }
Frédéric Lécaillea8725ec2019-01-22 10:31:39 +0100263 *str = (char *)msg;
264 return i;
Emeric Brun18928af2017-03-29 16:32:53 +0200265
Frédéric Lécaillea8725ec2019-01-22 10:31:39 +0100266 fail:
267 *str = NULL;
268 return 0;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200269}
Emeric Brun2b920a12010-09-23 18:30:22 +0200270
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100271/*
272 * Build a "hello" peer protocol message.
273 * Return the number of written bytes written to build this messages if succeeded,
274 * 0 if not.
275 */
276static int peer_prepare_hellomsg(char *msg, size_t size, struct peer_prep_params *p)
277{
278 int min_ver, ret;
279 struct peer *peer;
280
281 peer = p->hello.peer;
282 min_ver = (peer->flags & PEER_F_DWNGRD) ? PEER_DWNGRD_MINOR_VER : PEER_MINOR_VER;
283 /* Prepare headers */
284 ret = snprintf(msg, size, PEER_SESSION_PROTO_NAME " %u.%u\n%s\n%s %d %d\n",
285 PEER_MAJOR_VER, min_ver, peer->id, localpeer, (int)getpid(), relative_pid);
286 if (ret >= size)
287 return 0;
288
289 return ret;
290}
291
292/*
293 * Build a "handshake succeeded" status message.
294 * Return the number of written bytes written to build this messages if succeeded,
295 * 0 if not.
296 */
297static int peer_prepare_status_successmsg(char *msg, size_t size, struct peer_prep_params *p)
298{
299 int ret;
300
301 ret = snprintf(msg, size, "%d\n", PEER_SESS_SC_SUCCESSCODE);
302 if (ret >= size)
303 return 0;
304
305 return ret;
306}
307
308/*
309 * Build an error status message.
310 * Return the number of written bytes written to build this messages if succeeded,
311 * 0 if not.
312 */
313static int peer_prepare_status_errormsg(char *msg, size_t size, struct peer_prep_params *p)
314{
315 int ret;
316 unsigned int st1;
317
318 st1 = p->error_status.st1;
319 ret = snprintf(msg, size, "%d\n", st1);
320 if (ret >= size)
321 return 0;
322
323 return ret;
324}
325
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200326/* Set the stick-table UPDATE message type byte at <msg_type> address,
327 * depending on <use_identifier> and <use_timed> boolean parameters.
328 * Always successful.
329 */
330static inline void peer_set_update_msg_type(char *msg_type, int use_identifier, int use_timed)
331{
332 if (use_timed) {
333 if (use_identifier)
334 *msg_type = PEER_MSG_STKT_UPDATE_TIMED;
335 else
336 *msg_type = PEER_MSG_STKT_INCUPDATE_TIMED;
337 }
338 else {
339 if (use_identifier)
340 *msg_type = PEER_MSG_STKT_UPDATE;
341 else
342 *msg_type = PEER_MSG_STKT_INCUPDATE;
343 }
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200344}
Emeric Brun2b920a12010-09-23 18:30:22 +0200345/*
Emeric Brunb3971ab2015-05-12 18:49:09 +0200346 * This prepare the data update message on the stick session <ts>, <st> is the considered
347 * stick table.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800348 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200349 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
350 * check size)
Emeric Brun2b920a12010-09-23 18:30:22 +0200351 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100352static int peer_prepare_updatemsg(char *msg, size_t size, struct peer_prep_params *p)
Emeric Brun2b920a12010-09-23 18:30:22 +0200353{
354 uint32_t netinteger;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200355 unsigned short datalen;
356 char *cursor, *datamsg;
Emeric Brun94900952015-06-11 18:25:54 +0200357 unsigned int data_type;
358 void *data_ptr;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100359 struct stksess *ts;
360 struct shared_table *st;
361 unsigned int updateid;
362 int use_identifier;
363 int use_timed;
364
365 ts = p->updt.stksess;
366 st = p->updt.shared_table;
367 updateid = p->updt.updateid;
368 use_identifier = p->updt.use_identifier;
369 use_timed = p->updt.use_timed;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200370
371 cursor = datamsg = msg + 1 + 5;
372
Emeric Brun2b920a12010-09-23 18:30:22 +0200373 /* construct message */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200374
375 /* check if we need to send the update identifer */
Emeric Brun819fc6f2017-06-13 19:37:32 +0200376 if (!st->last_pushed || updateid < st->last_pushed || ((updateid - st->last_pushed) != 1)) {
Emeric Bruna6a09982015-09-22 15:34:19 +0200377 use_identifier = 1;
Emeric Brun2b920a12010-09-23 18:30:22 +0200378 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200379
380 /* encode update identifier if needed */
381 if (use_identifier) {
Emeric Brun819fc6f2017-06-13 19:37:32 +0200382 netinteger = htonl(updateid);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200383 memcpy(cursor, &netinteger, sizeof(netinteger));
384 cursor += sizeof(netinteger);
Emeric Brun2b920a12010-09-23 18:30:22 +0200385 }
386
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200387 if (use_timed) {
388 netinteger = htonl(tick_remain(now_ms, ts->expire));
389 memcpy(cursor, &netinteger, sizeof(netinteger));
390 cursor += sizeof(netinteger);
391 }
392
Emeric Brunb3971ab2015-05-12 18:49:09 +0200393 /* encode the key */
Thierry FOURNIER5d24ebc2015-07-24 08:46:42 +0200394 if (st->table->type == SMP_T_STR) {
Emeric Brun2b920a12010-09-23 18:30:22 +0200395 int stlen = strlen((char *)ts->key.key);
396
Emeric Brunb3971ab2015-05-12 18:49:09 +0200397 intencode(stlen, &cursor);
398 memcpy(cursor, ts->key.key, stlen);
399 cursor += stlen;
Emeric Brun2b920a12010-09-23 18:30:22 +0200400 }
Thierry FOURNIER5d24ebc2015-07-24 08:46:42 +0200401 else if (st->table->type == SMP_T_SINT) {
Emeric Brun2b920a12010-09-23 18:30:22 +0200402 netinteger = htonl(*((uint32_t *)ts->key.key));
Emeric Brunb3971ab2015-05-12 18:49:09 +0200403 memcpy(cursor, &netinteger, sizeof(netinteger));
404 cursor += sizeof(netinteger);
Emeric Brun2b920a12010-09-23 18:30:22 +0200405 }
406 else {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200407 memcpy(cursor, ts->key.key, st->table->key_size);
408 cursor += st->table->key_size;
Emeric Brun2b920a12010-09-23 18:30:22 +0200409 }
410
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100411 HA_RWLOCK_RDLOCK(STK_SESS_LOCK, &ts->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200412 /* encode values */
Emeric Brun94900952015-06-11 18:25:54 +0200413 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
Emeric Brunb3971ab2015-05-12 18:49:09 +0200414
Emeric Brun94900952015-06-11 18:25:54 +0200415 data_ptr = stktable_data_ptr(st->table, ts, data_type);
416 if (data_ptr) {
417 switch (stktable_data_types[data_type].std_type) {
418 case STD_T_SINT: {
419 int data;
420
421 data = stktable_data_cast(data_ptr, std_t_sint);
422 intencode(data, &cursor);
423 break;
424 }
425 case STD_T_UINT: {
426 unsigned int data;
427
428 data = stktable_data_cast(data_ptr, std_t_uint);
429 intencode(data, &cursor);
430 break;
431 }
432 case STD_T_ULL: {
433 unsigned long long data;
434
435 data = stktable_data_cast(data_ptr, std_t_ull);
436 intencode(data, &cursor);
437 break;
438 }
439 case STD_T_FRQP: {
440 struct freq_ctr_period *frqp;
441
442 frqp = &stktable_data_cast(data_ptr, std_t_frqp);
443 intencode((unsigned int)(now_ms - frqp->curr_tick), &cursor);
444 intencode(frqp->curr_ctr, &cursor);
445 intencode(frqp->prev_ctr, &cursor);
446 break;
447 }
448 }
449 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200450 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100451 HA_RWLOCK_RDUNLOCK(STK_SESS_LOCK, &ts->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200452
453 /* Compute datalen */
454 datalen = (cursor - datamsg);
455
456 /* prepare message header */
457 msg[0] = PEER_MSG_CLASS_STICKTABLE;
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200458 peer_set_update_msg_type(&msg[1], use_identifier, use_timed);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200459 cursor = &msg[2];
460 intencode(datalen, &cursor);
461
462 /* move data after header */
463 memmove(cursor, datamsg, datalen);
464
465 /* return header size + data_len */
466 return (cursor - msg) + datalen;
467}
468
469/*
470 * This prepare the switch table message to targeted share table <st>.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800471 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200472 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
473 * check size)
474 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100475static int peer_prepare_switchmsg(char *msg, size_t size, struct peer_prep_params *params)
Emeric Brunb3971ab2015-05-12 18:49:09 +0200476{
477 int len;
478 unsigned short datalen;
Willy Tarreau83061a82018-07-13 11:56:34 +0200479 struct buffer *chunk;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200480 char *cursor, *datamsg, *chunkp, *chunkq;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200481 uint64_t data = 0;
Emeric Brun94900952015-06-11 18:25:54 +0200482 unsigned int data_type;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100483 struct shared_table *st;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200484
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100485 st = params->swtch.shared_table;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200486 cursor = datamsg = msg + 2 + 5;
487
488 /* Encode data */
489
490 /* encode local id */
491 intencode(st->local_id, &cursor);
492
493 /* encode table name */
494 len = strlen(st->table->id);
495 intencode(len, &cursor);
496 memcpy(cursor, st->table->id, len);
497 cursor += len;
498
499 /* encode table type */
500
501 intencode(st->table->type, &cursor);
502
503 /* encode table key size */
504 intencode(st->table->key_size, &cursor);
505
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200506 chunk = get_trash_chunk();
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200507 chunkp = chunkq = chunk->area;
Emeric Brun94900952015-06-11 18:25:54 +0200508 /* encode available known data types in table */
509 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
510 if (st->table->data_ofs[data_type]) {
511 switch (stktable_data_types[data_type].std_type) {
512 case STD_T_SINT:
513 case STD_T_UINT:
514 case STD_T_ULL:
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200515 data |= 1 << data_type;
516 break;
Emeric Brun94900952015-06-11 18:25:54 +0200517 case STD_T_FRQP:
518 data |= 1 << data_type;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200519 intencode(data_type, &chunkq);
520 intencode(st->table->data_arg[data_type].u, &chunkq);
Emeric Brun94900952015-06-11 18:25:54 +0200521 break;
522 }
523 }
Emeric Brunb3971ab2015-05-12 18:49:09 +0200524 }
525 intencode(data, &cursor);
526
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200527 /* Encode stick-table entries duration. */
528 intencode(st->table->expire, &cursor);
529
530 if (chunkq > chunkp) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200531 chunk->data = chunkq - chunkp;
532 memcpy(cursor, chunk->area, chunk->data);
533 cursor += chunk->data;
Frédéric Lécaille37a72542017-07-06 15:02:16 +0200534 }
535
Emeric Brunb3971ab2015-05-12 18:49:09 +0200536 /* Compute datalen */
537 datalen = (cursor - datamsg);
Emeric Brun2b920a12010-09-23 18:30:22 +0200538
Emeric Brunb3971ab2015-05-12 18:49:09 +0200539 /* prepare message header */
540 msg[0] = PEER_MSG_CLASS_STICKTABLE;
541 msg[1] = PEER_MSG_STKT_DEFINE;
542 cursor = &msg[2];
543 intencode(datalen, &cursor);
Emeric Brun2b920a12010-09-23 18:30:22 +0200544
Emeric Brunb3971ab2015-05-12 18:49:09 +0200545 /* move data after header */
546 memmove(cursor, datamsg, datalen);
547
548 /* return header size + data_len */
549 return (cursor - msg) + datalen;
Emeric Brun2b920a12010-09-23 18:30:22 +0200550}
551
Emeric Brunb3971ab2015-05-12 18:49:09 +0200552/*
553 * This prepare the acknowledge message on the stick session <ts>, <st> is the considered
554 * stick table.
Joseph Herlant82b2f542018-11-15 12:19:14 -0800555 * <msg> is a buffer of <size> to receive data message content
Emeric Brunb3971ab2015-05-12 18:49:09 +0200556 * If function returns 0, the caller should consider we were unable to encode this message (TODO:
557 * check size)
558 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100559static int peer_prepare_ackmsg(char *msg, size_t size, struct peer_prep_params *p)
Emeric Brunb3971ab2015-05-12 18:49:09 +0200560{
561 unsigned short datalen;
562 char *cursor, *datamsg;
563 uint32_t netinteger;
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100564 struct shared_table *st;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200565
Emeric Brunb058f1c2015-09-22 15:50:18 +0200566 cursor = datamsg = msg + 2 + 5;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200567
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100568 st = p->ack.shared_table;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200569 intencode(st->remote_id, &cursor);
570 netinteger = htonl(st->last_get);
571 memcpy(cursor, &netinteger, sizeof(netinteger));
572 cursor += sizeof(netinteger);
573
574 /* Compute datalen */
575 datalen = (cursor - datamsg);
576
577 /* prepare message header */
578 msg[0] = PEER_MSG_CLASS_STICKTABLE;
Emeric Brune1ab8082015-08-21 11:48:54 +0200579 msg[1] = PEER_MSG_STKT_ACK;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200580 cursor = &msg[2];
581 intencode(datalen, &cursor);
582
583 /* move data after header */
584 memmove(cursor, datamsg, datalen);
585
586 /* return header size + data_len */
587 return (cursor - msg) + datalen;
588}
Emeric Brun2b920a12010-09-23 18:30:22 +0200589
590/*
591 * Callback to release a session with a peer
592 */
Willy Tarreau00a37f02015-04-13 12:05:19 +0200593static void peer_session_release(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +0200594{
Willy Tarreau00a37f02015-04-13 12:05:19 +0200595 struct stream_interface *si = appctx->owner;
Willy Tarreau87b09662015-04-03 00:22:06 +0200596 struct stream *s = si_strm(si);
Vincent Bernat3c2f2f22016-04-03 13:48:42 +0200597 struct peer *peer = appctx->ctx.peers.ptr;
598 struct peers *peers = strm_fe(s)->parent;
Emeric Brun2b920a12010-09-23 18:30:22 +0200599
Willy Tarreau7b4b4992013-12-01 09:15:12 +0100600 /* appctx->ctx.peers.ptr is not a peer session */
Willy Tarreaue4d927a2013-12-01 12:47:35 +0100601 if (appctx->st0 < PEER_SESS_ST_SENDSUCCESS)
Emeric Brun2b920a12010-09-23 18:30:22 +0200602 return;
603
604 /* peer session identified */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200605 if (peer) {
Willy Tarreau2d372c22018-11-05 17:12:27 +0100606 if (appctx->st0 == PEER_SESS_ST_WAITMSG)
607 HA_ATOMIC_SUB(&connected_peers, 1);
Willy Tarreau199ad242018-11-05 16:31:22 +0100608 HA_ATOMIC_SUB(&active_peers, 1);
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100609 HA_SPIN_LOCK(PEER_LOCK, &peer->lock);
Willy Tarreau9df94c22016-10-31 18:42:52 +0100610 if (peer->appctx == appctx) {
Emeric Brunb157d732015-08-21 12:00:30 +0200611 /* Re-init current table pointers to force announcement on re-connect */
612 peer->remote_table = peer->last_local_table = NULL;
Emeric Brunb3971ab2015-05-12 18:49:09 +0200613 peer->appctx = NULL;
614 if (peer->flags & PEER_F_LEARN_ASSIGN) {
Emeric Brun2b920a12010-09-23 18:30:22 +0200615 /* unassign current peer for learning */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200616 peer->flags &= ~(PEER_F_LEARN_ASSIGN);
617 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
Emeric Brun2b920a12010-09-23 18:30:22 +0200618
619 /* reschedule a resync */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200620 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
Emeric Brun2b920a12010-09-23 18:30:22 +0200621 }
622 /* reset teaching and learning flags to 0 */
Emeric Brunb3971ab2015-05-12 18:49:09 +0200623 peer->flags &= PEER_TEACH_RESET;
624 peer->flags &= PEER_LEARN_RESET;
Emeric Brun2b920a12010-09-23 18:30:22 +0200625 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100626 HA_SPIN_UNLOCK(PEER_LOCK, &peer->lock);
Emeric Brunb3971ab2015-05-12 18:49:09 +0200627 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
Emeric Brun2b920a12010-09-23 18:30:22 +0200628 }
629}
630
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +0200631/* Retrieve the major and minor versions of peers protocol
632 * announced by a remote peer. <str> is a null-terminated
633 * string with the following format: "<maj_ver>.<min_ver>".
634 */
635static int peer_get_version(const char *str,
636 unsigned int *maj_ver, unsigned int *min_ver)
637{
638 unsigned int majv, minv;
639 const char *pos, *saved;
640 const char *end;
641
642 saved = pos = str;
643 end = str + strlen(str);
644
645 majv = read_uint(&pos, end);
646 if (saved == pos || *pos++ != '.')
647 return -1;
648
649 saved = pos;
650 minv = read_uint(&pos, end);
651 if (saved == pos || pos != end)
652 return -1;
653
654 *maj_ver = majv;
655 *min_ver = minv;
656
657 return 0;
658}
Emeric Brun2b920a12010-09-23 18:30:22 +0200659
660/*
Frédéric Lécaillece025572019-01-21 13:38:06 +0100661 * Parse a line terminated by an optional '\r' character, followed by a mandatory
662 * '\n' character.
663 * Returns 1 if succeeded or 0 if a '\n' character could not be found, and -1 if
664 * a line could not be read because the communication channel is closed.
665 */
666static inline int peer_getline(struct appctx *appctx)
667{
668 int n;
669 struct stream_interface *si = appctx->owner;
670
671 n = co_getline(si_oc(si), trash.area, trash.size);
672 if (!n)
673 return 0;
674
675 if (n < 0 || trash.area[n - 1] != '\n') {
676 appctx->st0 = PEER_SESS_ST_END;
677 return -1;
678 }
679
680 if (n > 1 && (trash.area[n - 2] == '\r'))
681 trash.area[n - 2] = 0;
682 else
683 trash.area[n - 1] = 0;
684
685 co_skip(si_oc(si), n);
686
687 return n;
688}
689
690/*
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100691 * Send a message after having called <peer_prepare_msg> to build it.
692 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
693 * Returns -1 if there was not enough room left to send the message,
694 * any other negative returned value must be considered as an error with an appcxt st0
695 * returned value equal to PEER_SESS_ST_END.
696 */
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100697static inline int peer_send_msg(struct appctx *appctx,
698 int (*peer_prepare_msg)(char *, size_t, struct peer_prep_params *),
699 struct peer_prep_params *params)
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100700{
701 int ret, msglen;
702 struct stream_interface *si = appctx->owner;
703
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100704 msglen = peer_prepare_msg(trash.area, trash.size, params);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100705 if (!msglen) {
706 /* internal error: message does not fit in trash */
707 appctx->st0 = PEER_SESS_ST_END;
708 return 0;
709 }
710
711 /* message to buffer */
712 ret = ci_putblk(si_ic(si), trash.area, msglen);
713 if (ret <= 0) {
714 if (ret == -1) {
715 /* No more write possible */
716 si_rx_room_blk(si);
717 return -1;
718 }
719 appctx->st0 = PEER_SESS_ST_END;
720 }
721
722 return ret;
723}
724
725/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100726 * Send a hello message.
727 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
728 * Returns -1 if there was not enough room left to send the message,
729 * any other negative returned value must be considered as an error with an appcxt st0
730 * returned value equal to PEER_SESS_ST_END.
731 */
732static inline int peer_send_hellomsg(struct appctx *appctx, struct peer *peer)
733{
734 struct peer_prep_params p = {
735 .hello.peer = peer,
736 };
737
738 return peer_send_msg(appctx, peer_prepare_hellomsg, &p);
739}
740
741/*
742 * Send a success peer handshake status message.
743 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
744 * Returns -1 if there was not enough room left to send the message,
745 * any other negative returned value must be considered as an error with an appcxt st0
746 * returned value equal to PEER_SESS_ST_END.
747 */
748static inline int peer_send_status_successmsg(struct appctx *appctx)
749{
750 return peer_send_msg(appctx, peer_prepare_status_successmsg, NULL);
751}
752
753/*
754 * Send a peer handshake status error message.
755 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
756 * Returns -1 if there was not enough room left to send the message,
757 * any other negative returned value must be considered as an error with an appcxt st0
758 * returned value equal to PEER_SESS_ST_END.
759 */
760static inline int peer_send_status_errormsg(struct appctx *appctx)
761{
762 struct peer_prep_params p = {
763 .error_status.st1 = appctx->st1,
764 };
765
766 return peer_send_msg(appctx, peer_prepare_status_errormsg, &p);
767}
768
769/*
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100770 * Send a stick-table switch message.
771 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
772 * Returns -1 if there was not enough room left to send the message,
773 * any other negative returned value must be considered as an error with an appcxt st0
774 * returned value equal to PEER_SESS_ST_END.
775 */
776static inline int peer_send_switchmsg(struct shared_table *st, struct appctx *appctx)
777{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100778 struct peer_prep_params p = {
779 .swtch.shared_table = st,
780 };
781
782 return peer_send_msg(appctx, peer_prepare_switchmsg, &p);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100783}
784
785/*
786 * Send a stick-table update acknowledgement message.
787 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
788 * Returns -1 if there was not enough room left to send the message,
789 * any other negative returned value must be considered as an error with an appcxt st0
790 * returned value equal to PEER_SESS_ST_END.
791 */
792static inline int peer_send_ackmsg(struct shared_table *st, struct appctx *appctx)
793{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100794 struct peer_prep_params p = {
795 .ack.shared_table = st,
796 };
797
798 return peer_send_msg(appctx, peer_prepare_ackmsg, &p);
Frédéric Lécailleec44ea82019-01-22 15:54:53 +0100799}
800
801/*
Frédéric Lécaille87f554c2019-01-22 17:26:50 +0100802 * Send a stick-table update message.
803 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
804 * Returns -1 if there was not enough room left to send the message,
805 * any other negative returned value must be considered as an error with an appcxt st0
806 * returned value equal to PEER_SESS_ST_END.
807 */
808static inline int peer_send_updatemsg(struct shared_table *st, struct appctx *appctx, struct stksess *ts,
809 unsigned int updateid, int use_identifier, int use_timed)
810{
Frédéric Lécailled5fe14b2019-01-24 10:33:40 +0100811 struct peer_prep_params p = {
812 .updt.stksess = ts,
813 .updt.shared_table = st,
814 .updt.updateid = updateid,
815 .updt.use_identifier = use_identifier,
816 .updt.use_timed = use_timed,
817 };
818
819 return peer_send_msg(appctx, peer_prepare_updatemsg, &p);
Frédéric Lécaille87f554c2019-01-22 17:26:50 +0100820}
821
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100822/*
823 * Build a peer protocol control class message.
824 * Returns the number of written bytes used to build the message if succeeded,
825 * 0 if not.
826 */
827static int peer_prepare_control_msg(char *msg, size_t size, struct peer_prep_params *p)
828{
829 if (size < sizeof p->control.head)
830 return 0;
831
832 msg[0] = p->control.head[0];
833 msg[1] = p->control.head[1];
834
835 return 2;
836}
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +0100837
838/*
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +0100839 * Send a stick-table synchronization request message.
840 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
841 * Returns -1 if there was not enough room left to send the message,
842 * any other negative returned value must be considered as an error with an appctx st0
843 * returned value equal to PEER_SESS_ST_END.
844 */
845static inline int peer_send_resync_reqmsg(struct appctx *appctx)
846{
847 struct peer_prep_params p = {
848 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCREQ, },
849 };
850
851 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
852}
853
854/*
855 * Send a stick-table synchronization confirmation message.
856 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
857 * Returns -1 if there was not enough room left to send the message,
858 * any other negative returned value must be considered as an error with an appctx st0
859 * returned value equal to PEER_SESS_ST_END.
860 */
861static inline int peer_send_resync_confirmsg(struct appctx *appctx)
862{
863 struct peer_prep_params p = {
864 .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCCONFIRM, },
865 };
866
867 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
868}
869
870/*
871 * Send a stick-table synchronization finished message.
872 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
873 * Returns -1 if there was not enough room left to send the message,
874 * any other negative returned value must be considered as an error with an appctx st0
875 * returned value equal to PEER_SESS_ST_END.
876 */
877static inline int peer_send_resync_finishedmsg(struct appctx *appctx, struct peer *peer)
878{
879 struct peer_prep_params p = {
880 .control.head = { PEER_MSG_CLASS_CONTROL, },
881 };
882
883 p.control.head[1] = (peer->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED ?
884 PEER_MSG_CTRL_RESYNCFINISHED : PEER_MSG_CTRL_RESYNCPARTIAL;
885
886 return peer_send_msg(appctx, peer_prepare_control_msg, &p);
887}
888
889/*
890 * Build a peer protocol error class message.
891 * Returns the number of written bytes used to build the message if succeeded,
892 * 0 if not.
893 */
894static int peer_prepare_error_msg(char *msg, size_t size, struct peer_prep_params *p)
895{
896 if (size < sizeof p->error.head)
897 return 0;
898
899 msg[0] = p->error.head[0];
900 msg[1] = p->error.head[1];
901
902 return 2;
903}
904
905/*
906 * Send a "size limit reached" error message.
907 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
908 * Returns -1 if there was not enough room left to send the message,
909 * any other negative returned value must be considered as an error with an appctx st0
910 * returned value equal to PEER_SESS_ST_END.
911 */
912static inline int peer_send_error_size_limitmsg(struct appctx *appctx)
913{
914 struct peer_prep_params p = {
915 .error.head = { PEER_MSG_CLASS_ERROR, PEER_MSG_ERR_SIZELIMIT, },
916 };
917
918 return peer_send_msg(appctx, peer_prepare_error_msg, &p);
919}
920
921/*
922 * Send a "peer protocol" error message.
923 * Return 0 if the message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
924 * Returns -1 if there was not enough room left to send the message,
925 * any other negative returned value must be considered as an error with an appctx st0
926 * returned value equal to PEER_SESS_ST_END.
927 */
928static inline int peer_send_error_protomsg(struct appctx *appctx)
929{
930 struct peer_prep_params p = {
931 .error.head = { PEER_MSG_CLASS_ERROR, PEER_MSG_ERR_PROTOCOL, },
932 };
933
934 return peer_send_msg(appctx, peer_prepare_error_msg, &p);
935}
936
937/*
Frédéric Lécaille6a8303d2019-01-22 22:25:17 +0100938 * Function used to lookup for recent stick-table updates associated with
939 * <st> shared stick-table when a lesson must be taught a peer (PEER_F_LEARN_ASSIGN flag set).
940 */
941static inline struct stksess *peer_teach_process_stksess_lookup(struct shared_table *st)
942{
943 struct eb32_node *eb;
944
945 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
946 if (!eb) {
947 eb = eb32_first(&st->table->updates);
948 if (!eb || ((int)(eb->key - st->last_pushed) <= 0)) {
949 st->table->commitupdate = st->last_pushed = st->table->localupdate;
950 return NULL;
951 }
952 }
953
954 if ((int)(eb->key - st->table->localupdate) > 0) {
955 st->table->commitupdate = st->last_pushed = st->table->localupdate;
956 return NULL;
957 }
958
959 return eb32_entry(eb, struct stksess, upd);
960}
961
962/*
963 * Function used to lookup for recent stick-table updates associated with
964 * <st> shared stick-table during teach state 1 step.
965 */
966static inline struct stksess *peer_teach_stage1_stksess_lookup(struct shared_table *st)
967{
968 struct eb32_node *eb;
969
970 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
971 if (!eb) {
972 st->flags |= SHTABLE_F_TEACH_STAGE1;
973 eb = eb32_first(&st->table->updates);
974 if (eb)
975 st->last_pushed = eb->key - 1;
976 return NULL;
977 }
978
979 return eb32_entry(eb, struct stksess, upd);
980}
981
982/*
983 * Function used to lookup for recent stick-table updates associated with
984 * <st> shared stick-table during teach state 2 step.
985 */
986static inline struct stksess *peer_teach_stage2_stksess_lookup(struct shared_table *st)
987{
988 struct eb32_node *eb;
989
990 eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
991 if (!eb || eb->key > st->teaching_origin) {
992 st->flags |= SHTABLE_F_TEACH_STAGE2;
993 return NULL;
994 }
995
996 return eb32_entry(eb, struct stksess, upd);
997}
998
999/*
1000 * Generic function to emit update messages for <st> stick-table when a lesson must
1001 * be taught to the peer <p>.
1002 * <locked> must be set to 1 if the shared table <st> is already locked when entering
1003 * this function, 0 if not.
1004 *
1005 * This function temporary unlock/lock <st> when it sends stick-table updates or
1006 * when decrementing its refcount in case of any error when it sends this updates.
1007 *
1008 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1009 * Returns -1 if there was not enough room left to send the message,
1010 * any other negative returned value must be considered as an error with an appcxt st0
1011 * returned value equal to PEER_SESS_ST_END.
1012 * If it returns 0 or -1, this function leave <st> locked if already locked when entering this function
1013 * unlocked if not already locked when entering this function.
1014 */
1015static inline int peer_send_teachmsgs(struct appctx *appctx, struct peer *p,
1016 struct stksess *(*peer_stksess_lookup)(struct shared_table *),
1017 struct shared_table *st, int locked)
1018{
1019 int ret, new_pushed, use_timed;
1020
1021 ret = 1;
1022 use_timed = 0;
1023 if (st != p->last_local_table) {
1024 ret = peer_send_switchmsg(st, appctx);
1025 if (ret <= 0)
1026 return ret;
1027
1028 p->last_local_table = st;
1029 }
1030
1031 if (peer_stksess_lookup != peer_teach_process_stksess_lookup)
1032 use_timed = !(p->flags & PEER_F_DWNGRD);
1033
1034 /* We force new pushed to 1 to force identifier in update message */
1035 new_pushed = 1;
1036
1037 if (!locked)
1038 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1039
1040 while (1) {
1041 struct stksess *ts;
1042 unsigned updateid;
1043
1044 /* push local updates */
1045 ts = peer_stksess_lookup(st);
1046 if (!ts)
1047 break;
1048
1049 updateid = ts->upd.key;
1050 ts->ref_cnt++;
1051 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1052
1053 ret = peer_send_updatemsg(st, appctx, ts, updateid, new_pushed, use_timed);
1054 if (ret <= 0) {
1055 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1056 ts->ref_cnt--;
1057 if (!locked)
1058 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1059 return ret;
1060 }
1061
1062 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1063 ts->ref_cnt--;
1064 st->last_pushed = updateid;
1065
1066 if (peer_stksess_lookup == peer_teach_process_stksess_lookup &&
1067 (int)(st->last_pushed - st->table->commitupdate) > 0)
1068 st->table->commitupdate = st->last_pushed;
1069
1070 /* identifier may not needed in next update message */
1071 new_pushed = 0;
1072 }
1073
1074 out:
1075 if (!locked)
1076 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1077 return 1;
1078}
1079
1080/*
1081 * Function to emit update messages for <st> stick-table when a lesson must
1082 * be taught to the peer <p> (PEER_F_LEARN_ASSIGN flag set).
1083 *
1084 * Note that <st> shared stick-table is locked when calling this function.
1085 *
1086 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1087 * Returns -1 if there was not enough room left to send the message,
1088 * any other negative returned value must be considered as an error with an appcxt st0
1089 * returned value equal to PEER_SESS_ST_END.
1090 */
1091static inline int peer_send_teach_process_msgs(struct appctx *appctx, struct peer *p,
1092 struct shared_table *st)
1093{
1094 return peer_send_teachmsgs(appctx, p, peer_teach_process_stksess_lookup, st, 1);
1095}
1096
1097/*
1098 * Function to emit update messages for <st> stick-table when a lesson must
1099 * be taught to the peer <p> during teach state 1 step.
1100 *
1101 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1102 * Returns -1 if there was not enough room left to send the message,
1103 * any other negative returned value must be considered as an error with an appcxt st0
1104 * returned value equal to PEER_SESS_ST_END.
1105 */
1106static inline int peer_send_teach_stage1_msgs(struct appctx *appctx, struct peer *p,
1107 struct shared_table *st)
1108{
1109 return peer_send_teachmsgs(appctx, p, peer_teach_stage1_stksess_lookup, st, 0);
1110}
1111
1112/*
1113 * Function to emit update messages for <st> stick-table when a lesson must
1114 * be taught to the peer <p> during teach state 1 step.
1115 *
1116 * Return 0 if any message could not be built modifying the appcxt st0 to PEER_SESS_ST_END value.
1117 * Returns -1 if there was not enough room left to send the message,
1118 * any other negative returned value must be considered as an error with an appcxt st0
1119 * returned value equal to PEER_SESS_ST_END.
1120 */
1121static inline int peer_send_teach_stage2_msgs(struct appctx *appctx, struct peer *p,
1122 struct shared_table *st)
1123{
1124 return peer_send_teachmsgs(appctx, p, peer_teach_stage2_stksess_lookup, st, 0);
1125}
1126
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001127
1128/*
1129 * Function used to parse a stick-table update message after it has been received
1130 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1131 * receipt buffer with <msg_end> being position of the end of the stick-table message.
1132 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1133 * was encountered.
1134 * <exp> must be set if the stick-table entry expires.
1135 * <updt> must be set for PEER_MSG_STKT_UPDATE or PEER_MSG_STKT_UPDATE_TIMED stick-table
1136 * messages, in this case the stick-table udpate message is received with a stick-table
1137 * update ID.
1138 * <totl> is the length of the stick-table update message computed upon receipt.
1139 */
Frédéric Lécaille444243c2019-01-24 15:40:11 +01001140static int peer_treat_updatemsg(struct appctx *appctx, struct peer *p, int updt, int exp,
1141 char **msg_cur, char *msg_end, int msg_len, int totl)
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001142{
1143 struct stream_interface *si = appctx->owner;
1144 struct shared_table *st = p->remote_table;
1145 struct stksess *ts, *newts;
1146 uint32_t update;
1147 int expire;
1148 unsigned int data_type;
1149 void *data_ptr;
1150
1151 /* Here we have data message */
1152 if (!st)
1153 goto ignore_msg;
1154
1155 expire = MS_TO_TICKS(st->table->expire);
1156
1157 if (updt) {
Willy Tarreau1e82a142019-01-29 11:08:06 +01001158 if (msg_len < sizeof(update))
1159 goto malformed_exit;
1160
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001161 memcpy(&update, *msg_cur, sizeof(update));
1162 *msg_cur += sizeof(update);
1163 st->last_get = htonl(update);
1164 }
1165 else {
1166 st->last_get++;
1167 }
1168
1169 if (exp) {
1170 size_t expire_sz = sizeof expire;
1171
Willy Tarreau1e82a142019-01-29 11:08:06 +01001172 if (*msg_cur + expire_sz > msg_end)
1173 goto malformed_exit;
1174
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001175 memcpy(&expire, *msg_cur, expire_sz);
1176 *msg_cur += expire_sz;
1177 expire = ntohl(expire);
1178 }
1179
1180 newts = stksess_new(st->table, NULL);
1181 if (!newts)
1182 goto ignore_msg;
1183
1184 if (st->table->type == SMP_T_STR) {
1185 unsigned int to_read, to_store;
1186
1187 to_read = intdecode(msg_cur, msg_end);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001188 if (!*msg_cur)
1189 goto malformed_free_newts;
1190
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001191 to_store = MIN(to_read, st->table->key_size - 1);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001192 if (*msg_cur + to_store > msg_end)
1193 goto malformed_free_newts;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001194
1195 memcpy(newts->key.key, *msg_cur, to_store);
1196 newts->key.key[to_store] = 0;
1197 *msg_cur += to_read;
1198 }
1199 else if (st->table->type == SMP_T_SINT) {
1200 unsigned int netinteger;
1201
Willy Tarreau1e82a142019-01-29 11:08:06 +01001202 if (*msg_cur + sizeof(netinteger) > msg_end)
1203 goto malformed_free_newts;
1204
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001205 memcpy(&netinteger, *msg_cur, sizeof(netinteger));
1206 netinteger = ntohl(netinteger);
1207 memcpy(newts->key.key, &netinteger, sizeof(netinteger));
1208 *msg_cur += sizeof(netinteger);
1209 }
1210 else {
Willy Tarreau1e82a142019-01-29 11:08:06 +01001211 if (*msg_cur + st->table->key_size > msg_end)
1212 goto malformed_free_newts;
1213
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001214 memcpy(newts->key.key, *msg_cur, st->table->key_size);
1215 *msg_cur += st->table->key_size;
1216 }
1217
1218 /* lookup for existing entry */
1219 ts = stktable_set_entry(st->table, newts);
1220 if (ts != newts) {
1221 stksess_free(st->table, newts);
1222 newts = NULL;
1223 }
1224
1225 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1226
1227 for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
Willy Tarreau1e82a142019-01-29 11:08:06 +01001228 uint64_t decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001229
1230 if (!((1 << data_type) & st->remote_data))
1231 continue;
1232
Willy Tarreau1e82a142019-01-29 11:08:06 +01001233 decoded_int = intdecode(msg_cur, msg_end);
1234 if (!*msg_cur)
1235 goto malformed_unlock;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001236
Willy Tarreau1e82a142019-01-29 11:08:06 +01001237 switch (stktable_data_types[data_type].std_type) {
1238 case STD_T_SINT:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001239 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1240 if (data_ptr)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001241 stktable_data_cast(data_ptr, std_t_sint) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001242 break;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001243
Willy Tarreau1e82a142019-01-29 11:08:06 +01001244 case STD_T_UINT:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001245 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1246 if (data_ptr)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001247 stktable_data_cast(data_ptr, std_t_uint) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001248 break;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001249
Willy Tarreau1e82a142019-01-29 11:08:06 +01001250 case STD_T_ULL:
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001251 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1252 if (data_ptr)
Willy Tarreau1e82a142019-01-29 11:08:06 +01001253 stktable_data_cast(data_ptr, std_t_ull) = decoded_int;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001254 break;
Willy Tarreau1e82a142019-01-29 11:08:06 +01001255
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001256 case STD_T_FRQP: {
1257 struct freq_ctr_period data;
1258
1259 /* First bit is reserved for the freq_ctr_period lock
1260 Note: here we're still protected by the stksess lock
1261 so we don't need to update the update the freq_ctr_period
1262 using its internal lock */
1263
Willy Tarreau1e82a142019-01-29 11:08:06 +01001264 data.curr_tick = tick_add(now_ms, -decoded_int) & ~0x1;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001265 data.curr_ctr = intdecode(msg_cur, msg_end);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001266 if (!*msg_cur)
1267 goto malformed_unlock;
1268
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001269 data.prev_ctr = intdecode(msg_cur, msg_end);
Willy Tarreau1e82a142019-01-29 11:08:06 +01001270 if (!*msg_cur)
1271 goto malformed_unlock;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001272
1273 data_ptr = stktable_data_ptr(st->table, ts, data_type);
1274 if (data_ptr)
1275 stktable_data_cast(data_ptr, std_t_frqp) = data;
1276 break;
1277 }
1278 }
1279 }
1280 /* Force new expiration */
1281 ts->expire = tick_add(now_ms, expire);
1282
1283 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1284 stktable_touch_remote(st->table, ts, 1);
1285 return 1;
1286
1287 ignore_msg:
1288 /* skip consumed message */
1289 co_skip(si_oc(si), totl);
1290 return 0;
Willy Tarreau1e82a142019-01-29 11:08:06 +01001291
1292 malformed_unlock:
1293 /* malformed message */
1294 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1295 stktable_touch_remote(st->table, ts, 1);
1296 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1297 return 0;
1298
1299 malformed_free_newts:
1300 /* malformed message */
1301 stksess_free(st->table, newts);
1302 malformed_exit:
1303 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1304 return 0;
Frédéric Lécaille168a34b2019-01-23 11:16:57 +01001305}
1306
Frédéric Lécaille87f554c2019-01-22 17:26:50 +01001307/*
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001308 * Function used to parse a stick-table update acknowledgement message after it
1309 * has been received by <p> peer with <msg_cur> as address of the pointer to the position in the
1310 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
1311 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1312 * was encountered.
1313 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
1314 */
1315static inline int peer_treat_ackmsg(struct appctx *appctx, struct peer *p,
1316 char **msg_cur, char *msg_end)
1317{
1318 /* ack message */
1319 uint32_t table_id ;
1320 uint32_t update;
1321 struct shared_table *st;
1322
1323 table_id = intdecode(msg_cur, msg_end);
1324 if (!*msg_cur || (*msg_cur + sizeof(update) > msg_end)) {
1325 /* malformed message */
1326 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1327 return 0;
1328 }
1329
1330 memcpy(&update, *msg_cur, sizeof(update));
1331 update = ntohl(update);
1332
1333 for (st = p->tables; st; st = st->next) {
1334 if (st->local_id == table_id) {
1335 st->update = update;
1336 break;
1337 }
1338 }
1339
1340 return 1;
1341}
1342
1343/*
1344 * Function used to parse a stick-table switch message after it has been received
1345 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1346 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
1347 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1348 * was encountered.
1349 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
1350 */
1351static inline int peer_treat_switchmsg(struct appctx *appctx, struct peer *p,
1352 char **msg_cur, char *msg_end)
1353{
1354 struct shared_table *st;
1355 int table_id;
1356
1357 table_id = intdecode(msg_cur, msg_end);
1358 if (!*msg_cur) {
1359 /* malformed message */
1360 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1361 return 0;
1362 }
1363
1364 p->remote_table = NULL;
1365 for (st = p->tables; st; st = st->next) {
1366 if (st->remote_id == table_id) {
1367 p->remote_table = st;
1368 break;
1369 }
1370 }
1371
1372 return 1;
1373}
1374
1375/*
1376 * Function used to parse a stick-table definition message after it has been received
1377 * by <p> peer with <msg_cur> as address of the pointer to the position in the
1378 * receipt buffer with <msg_end> being the position of the end of the stick-table message.
1379 * Update <msg_curr> accordingly to the peer protocol specs if no peer protocol error
1380 * was encountered.
1381 * <totl> is the length of the stick-table update message computed upon receipt.
1382 * Return 1 if succeeded, 0 if not with the appctx state st0 set to PEER_SESS_ST_ERRPROTO.
1383 */
1384static inline int peer_treat_definemsg(struct appctx *appctx, struct peer *p,
1385 char **msg_cur, char *msg_end, int totl)
1386{
1387 struct stream_interface *si = appctx->owner;
1388 int table_id_len;
1389 struct shared_table *st;
1390 int table_type;
1391 int table_keylen;
1392 int table_id;
1393 uint64_t table_data;
1394
1395 table_id = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001396 if (!*msg_cur)
1397 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001398
1399 table_id_len = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001400 if (!*msg_cur)
1401 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001402
1403 p->remote_table = NULL;
Willy Tarreau6f731f32019-01-29 11:11:23 +01001404 if (!table_id_len || (*msg_cur + table_id_len) >= msg_end)
1405 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001406
1407 for (st = p->tables; st; st = st->next) {
1408 /* Reset IDs */
1409 if (st->remote_id == table_id)
1410 st->remote_id = 0;
1411
1412 if (!p->remote_table && (table_id_len == strlen(st->table->id)) &&
1413 (memcmp(st->table->id, *msg_cur, table_id_len) == 0))
1414 p->remote_table = st;
1415 }
1416
1417 if (!p->remote_table)
1418 goto ignore_msg;
1419
1420 *msg_cur += table_id_len;
Willy Tarreau6f731f32019-01-29 11:11:23 +01001421 if (*msg_cur >= msg_end)
1422 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001423
1424 table_type = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001425 if (!*msg_cur)
1426 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001427
1428 table_keylen = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001429 if (!*msg_cur)
1430 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001431
1432 table_data = intdecode(msg_cur, msg_end);
Willy Tarreau6f731f32019-01-29 11:11:23 +01001433 if (!*msg_cur)
1434 goto malformed_exit;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001435
1436 if (p->remote_table->table->type != table_type
1437 || p->remote_table->table->key_size != table_keylen) {
1438 p->remote_table = NULL;
1439 goto ignore_msg;
1440 }
1441
1442 p->remote_table->remote_data = table_data;
1443 p->remote_table->remote_id = table_id;
1444 return 1;
1445
1446 ignore_msg:
1447 co_skip(si_oc(si), totl);
1448 return 0;
Willy Tarreau6f731f32019-01-29 11:11:23 +01001449
1450 malformed_exit:
1451 /* malformed message */
1452 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1453 return 0;
Frédéric Lécailled27b0942019-01-23 17:31:37 +01001454}
1455
1456/*
Frédéric Lécaille95203f22019-01-23 19:38:11 +01001457 * Receive a stick-table message.
1458 * Returns 1 if there was no error, if not, returns 0 if not enough data were available,
1459 * -1 if there was an error updating the appctx state st0 accordingly.
1460 */
1461static inline int peer_recv_msg(struct appctx *appctx, char *msg_head, size_t msg_head_sz,
1462 uint32_t *msg_len, int *totl)
1463{
1464 int reql;
1465 struct stream_interface *si = appctx->owner;
1466
1467 reql = co_getblk(si_oc(si), msg_head, 2 * sizeof(char), *totl);
1468 if (reql <= 0) /* closed or EOL not found */
1469 goto incomplete;
1470
1471 *totl += reql;
1472
1473 if ((unsigned int)msg_head[1] < 128)
1474 return 1;
1475
1476 /* Read and Decode message length */
1477 reql = co_getblk(si_oc(si), &msg_head[2], sizeof(char), *totl);
1478 if (reql <= 0) /* closed */
1479 goto incomplete;
1480
1481 *totl += reql;
1482
1483 if ((unsigned int)msg_head[2] < 240) {
1484 *msg_len = msg_head[2];
1485 }
1486 else {
1487 int i;
1488 char *cur;
1489 char *end;
1490
1491 for (i = 3 ; i < msg_head_sz ; i++) {
1492 reql = co_getblk(si_oc(si), &msg_head[i], sizeof(char), *totl);
1493 if (reql <= 0) /* closed */
1494 goto incomplete;
1495
1496 *totl += reql;
1497
1498 if (!(msg_head[i] & 0x80))
1499 break;
1500 }
1501
1502 if (i == msg_head_sz) {
1503 /* malformed message */
1504 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1505 return -1;
1506 }
1507 end = msg_head + msg_head_sz;
1508 cur = &msg_head[2];
1509 *msg_len = intdecode(&cur, end);
1510 if (!cur) {
1511 /* malformed message */
1512 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1513 return -1;
1514 }
1515 }
1516
1517 /* Read message content */
1518 if (*msg_len) {
1519 if (*msg_len > trash.size) {
1520 /* Status code is not success, abort */
1521 appctx->st0 = PEER_SESS_ST_ERRSIZE;
1522 return -1;
1523 }
1524
1525 reql = co_getblk(si_oc(si), trash.area, *msg_len, *totl);
1526 if (reql <= 0) /* closed */
1527 goto incomplete;
1528 *totl += reql;
1529 }
1530
1531 return 1;
1532
1533 incomplete:
1534 if (reql < 0) {
1535 /* there was an error */
1536 appctx->st0 = PEER_SESS_ST_END;
1537 return -1;
1538 }
1539
1540 return 0;
1541}
Frédéric Lécaille444243c2019-01-24 15:40:11 +01001542
1543/*
1544 * Treat the awaited message with <msg_head> as header.*
1545 * Return 1 if succeeded, 0 if not.
1546 */
1547static inline int peer_treat_awaited_msg(struct appctx *appctx, struct peer *peer, unsigned char *msg_head,
1548 char **msg_cur, char *msg_end, int msg_len, int totl)
1549{
1550 struct stream_interface *si = appctx->owner;
1551 struct stream *s = si_strm(si);
1552 struct peers *peers = strm_fe(s)->parent;
1553
1554 if (msg_head[0] == PEER_MSG_CLASS_CONTROL) {
1555 if (msg_head[1] == PEER_MSG_CTRL_RESYNCREQ) {
1556 struct shared_table *st;
1557 /* Reset message: remote need resync */
1558
1559 /* prepare tables fot a global push */
1560 for (st = peer->tables; st; st = st->next) {
1561 st->teaching_origin = st->last_pushed = st->table->update;
1562 st->flags = 0;
1563 }
1564
1565 /* reset teaching flags to 0 */
1566 peer->flags &= PEER_TEACH_RESET;
1567
1568 /* flag to start to teach lesson */
1569 peer->flags |= PEER_F_TEACH_PROCESS;
1570 }
1571 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCFINISHED) {
1572 if (peer->flags & PEER_F_LEARN_ASSIGN) {
1573 peer->flags &= ~PEER_F_LEARN_ASSIGN;
1574 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
1575 peers->flags |= (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE);
1576 }
1577 peer->confirm++;
1578 }
1579 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCPARTIAL) {
1580 if (peer->flags & PEER_F_LEARN_ASSIGN) {
1581 peer->flags &= ~PEER_F_LEARN_ASSIGN;
1582 peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
1583
1584 peer->flags |= PEER_F_LEARN_NOTUP2DATE;
1585 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
1586 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
1587 }
1588 peer->confirm++;
1589 }
1590 else if (msg_head[1] == PEER_MSG_CTRL_RESYNCCONFIRM) {
1591 struct shared_table *st;
1592
1593 /* If stopping state */
1594 if (stopping) {
1595 /* Close session, push resync no more needed */
1596 peer->flags |= PEER_F_TEACH_COMPLETE;
1597 appctx->st0 = PEER_SESS_ST_END;
1598 return 0;
1599 }
1600 for (st = peer->tables; st; st = st->next) {
1601 st->update = st->last_pushed = st->teaching_origin;
1602 st->flags = 0;
1603 }
1604
1605 /* reset teaching flags to 0 */
1606 peer->flags &= PEER_TEACH_RESET;
1607 }
1608 }
1609 else if (msg_head[0] == PEER_MSG_CLASS_STICKTABLE) {
1610 if (msg_head[1] == PEER_MSG_STKT_DEFINE) {
1611 if (!peer_treat_definemsg(appctx, peer, msg_cur, msg_end, totl))
1612 return 0;
1613 }
1614 else if (msg_head[1] == PEER_MSG_STKT_SWITCH) {
1615 if (!peer_treat_switchmsg(appctx, peer, msg_cur, msg_end))
1616 return 0;
1617 }
1618 else if (msg_head[1] == PEER_MSG_STKT_UPDATE ||
1619 msg_head[1] == PEER_MSG_STKT_INCUPDATE ||
1620 msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED ||
1621 msg_head[1] == PEER_MSG_STKT_INCUPDATE_TIMED) {
1622 int update, expire;
1623
1624 update = msg_head[1] == PEER_MSG_STKT_UPDATE || msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED;
1625 expire = msg_head[1] == PEER_MSG_STKT_UPDATE_TIMED || msg_head[1] == PEER_MSG_STKT_INCUPDATE_TIMED;
1626 if (!peer_treat_updatemsg(appctx, peer, update, expire,
1627 msg_cur, msg_end, msg_len, totl))
1628 return 0;
1629
1630 }
1631 else if (msg_head[1] == PEER_MSG_STKT_ACK) {
1632 if (!peer_treat_ackmsg(appctx, peer, msg_cur, msg_end))
1633 return 0;
1634 }
1635 }
1636 else if (msg_head[0] == PEER_MSG_CLASS_RESERVED) {
1637 appctx->st0 = PEER_SESS_ST_ERRPROTO;
1638 return 0;
1639 }
1640
1641 return 1;
1642}
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01001643
1644
1645/*
1646 * Send any message to <peer> peer.
1647 * Returns 1 if succeeded, or -1 or 0 if failed.
1648 * -1 means an internal error occured, 0 is for a peer protocol error leading
1649 * to a peer state change (from the peer I/O handler point of view).
1650 */
1651static inline int peer_send_msgs(struct appctx *appctx, struct peer *peer)
1652{
1653 int repl;
1654 struct stream_interface *si = appctx->owner;
1655 struct stream *s = si_strm(si);
1656 struct peers *peers = strm_fe(s)->parent;
1657
1658 /* Need to request a resync */
1659 if ((peer->flags & PEER_F_LEARN_ASSIGN) &&
1660 (peers->flags & PEERS_F_RESYNC_ASSIGN) &&
1661 !(peers->flags & PEERS_F_RESYNC_PROCESS)) {
1662
1663 repl = peer_send_resync_reqmsg(appctx);
1664 if (repl <= 0)
1665 return repl;
1666
1667 peers->flags |= PEERS_F_RESYNC_PROCESS;
1668 }
1669
1670 /* Nothing to read, now we start to write */
1671 if (peer->tables) {
1672 struct shared_table *st;
1673 struct shared_table *last_local_table;
1674
1675 last_local_table = peer->last_local_table;
1676 if (!last_local_table)
1677 last_local_table = peer->tables;
1678 st = last_local_table->next;
1679
1680 while (1) {
1681 if (!st)
1682 st = peer->tables;
1683
1684 /* It remains some updates to ack */
1685 if (st->last_get != st->last_acked) {
1686 repl = peer_send_ackmsg(st, appctx);
1687 if (repl <= 0)
1688 return repl;
1689
1690 st->last_acked = st->last_get;
1691 }
1692
1693 if (!(peer->flags & PEER_F_TEACH_PROCESS)) {
1694 HA_SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
1695 if (!(peer->flags & PEER_F_LEARN_ASSIGN) &&
1696 ((int)(st->last_pushed - st->table->localupdate) < 0)) {
1697
1698 repl = peer_send_teach_process_msgs(appctx, peer, st);
1699 if (repl <= 0) {
1700 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1701 return repl;
1702 }
1703 }
1704 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
1705 }
1706 else {
1707 if (!(st->flags & SHTABLE_F_TEACH_STAGE1)) {
1708 repl = peer_send_teach_stage1_msgs(appctx, peer, st);
1709 if (repl <= 0)
1710 return repl;
1711 }
1712
1713 if (!(st->flags & SHTABLE_F_TEACH_STAGE2)) {
1714 repl = peer_send_teach_stage2_msgs(appctx, peer, st);
1715 if (repl <= 0)
1716 return repl;
1717 }
1718 }
1719
1720 if (st == last_local_table)
1721 break;
1722 st = st->next;
1723 }
1724 }
1725
1726 if ((peer->flags & PEER_F_TEACH_PROCESS) && !(peer->flags & PEER_F_TEACH_FINISHED)) {
1727 repl = peer_send_resync_finishedmsg(appctx, peer);
1728 if (repl <= 0)
1729 return repl;
1730
1731 /* flag finished message sent */
1732 peer->flags |= PEER_F_TEACH_FINISHED;
1733 }
1734
1735 /* Confirm finished or partial messages */
1736 while (peer->confirm) {
1737 repl = peer_send_resync_confirmsg(appctx);
1738 if (repl <= 0)
1739 return repl;
1740
1741 peer->confirm--;
1742 }
1743
1744 return 1;
1745}
1746
Frédéric Lécaille95203f22019-01-23 19:38:11 +01001747/*
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01001748 * Read and parse a first line of a "hello" peer protocol message.
1749 * Returns 0 if could not read a line, -1 if there was a read error or
1750 * the line is malformed, 1 if succeeded.
1751 */
1752static inline int peer_getline_version(struct appctx *appctx,
1753 unsigned int *maj_ver, unsigned int *min_ver)
1754{
1755 int reql;
1756
1757 reql = peer_getline(appctx);
1758 if (!reql)
1759 return 0;
1760
1761 if (reql < 0)
1762 return -1;
1763
1764 /* test protocol */
1765 if (strncmp(PEER_SESSION_PROTO_NAME " ", trash.area, proto_len + 1) != 0) {
1766 appctx->st0 = PEER_SESS_ST_EXIT;
1767 appctx->st1 = PEER_SESS_SC_ERRPROTO;
1768 return -1;
1769 }
1770 if (peer_get_version(trash.area + proto_len + 1, maj_ver, min_ver) == -1 ||
1771 *maj_ver != PEER_MAJOR_VER || *min_ver > PEER_MINOR_VER) {
1772 appctx->st0 = PEER_SESS_ST_EXIT;
1773 appctx->st1 = PEER_SESS_SC_ERRVERSION;
1774 return -1;
1775 }
1776
1777 return 1;
1778}
1779
1780/*
1781 * Read and parse a second line of a "hello" peer protocol message.
1782 * Returns 0 if could not read a line, -1 if there was a read error or
1783 * the line is malformed, 1 if succeeded.
1784 */
1785static inline int peer_getline_host(struct appctx *appctx)
1786{
1787 int reql;
1788
1789 reql = peer_getline(appctx);
1790 if (!reql)
1791 return 0;
1792
1793 if (reql < 0)
1794 return -1;
1795
1796 /* test hostname match */
1797 if (strcmp(localpeer, trash.area) != 0) {
1798 appctx->st0 = PEER_SESS_ST_EXIT;
1799 appctx->st1 = PEER_SESS_SC_ERRHOST;
1800 return -1;
1801 }
1802
1803 return 1;
1804}
1805
1806/*
1807 * Read and parse a last line of a "hello" peer protocol message.
1808 * Returns 0 if could not read a character, -1 if there was a read error or
1809 * the line is malformed, 1 if succeeded.
1810 * Set <curpeer> accordingly (the remote peer sending the "hello" message).
1811 */
1812static inline int peer_getline_last(struct appctx *appctx, struct peer **curpeer)
1813{
1814 char *p;
1815 int reql;
1816 struct peer *peer;
1817 struct stream_interface *si = appctx->owner;
1818 struct stream *s = si_strm(si);
1819 struct peers *peers = strm_fe(s)->parent;
1820
1821 reql = peer_getline(appctx);
1822 if (!reql)
1823 return 0;
1824
1825 if (reql < 0)
1826 return -1;
1827
1828 /* parse line "<peer name> <pid> <relative_pid>" */
1829 p = strchr(trash.area, ' ');
1830 if (!p) {
1831 appctx->st0 = PEER_SESS_ST_EXIT;
1832 appctx->st1 = PEER_SESS_SC_ERRPROTO;
1833 return -1;
1834 }
1835 *p = 0;
1836
1837 /* lookup known peer */
1838 for (peer = peers->remote; peer; peer = peer->next) {
1839 if (strcmp(peer->id, trash.area) == 0)
1840 break;
1841 }
1842
1843 /* if unknown peer */
1844 if (!peer) {
1845 appctx->st0 = PEER_SESS_ST_EXIT;
1846 appctx->st1 = PEER_SESS_SC_ERRPEER;
1847 return -1;
1848 }
1849 *curpeer = peer;
1850
1851 return 1;
1852}
1853
1854/*
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01001855 * Init <peer> peer after having accepted it at peer protocol level.
1856 */
1857static inline void init_accepted_peer(struct peer *peer, struct peers *peers)
1858{
1859 struct shared_table *st;
1860
1861 /* Register status code */
1862 peer->statuscode = PEER_SESS_SC_SUCCESSCODE;
1863
1864 /* Awake main task */
1865 task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
1866
1867 /* Init confirm counter */
1868 peer->confirm = 0;
1869
1870 /* Init cursors */
1871 for (st = peer->tables; st ; st = st->next) {
1872 st->last_get = st->last_acked = 0;
1873 st->teaching_origin = st->last_pushed = st->update;
1874 }
1875
1876 /* reset teaching and learning flags to 0 */
1877 peer->flags &= PEER_TEACH_RESET;
1878 peer->flags &= PEER_LEARN_RESET;
1879
1880 /* if current peer is local */
1881 if (peer->local) {
1882 /* if current host need resyncfrom local and no process assined */
1883 if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL &&
1884 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
1885 /* assign local peer for a lesson, consider lesson already requested */
1886 peer->flags |= PEER_F_LEARN_ASSIGN;
1887 peers->flags |= (PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
1888 }
1889
1890 }
1891 else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
1892 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
1893 /* assign peer for a lesson */
1894 peer->flags |= PEER_F_LEARN_ASSIGN;
1895 peers->flags |= PEERS_F_RESYNC_ASSIGN;
1896 }
1897}
1898
1899/*
1900 * Init <peer> peer after having connected it at peer protocol level.
1901 */
1902static inline void init_connected_peer(struct peer *peer, struct peers *peers)
1903{
1904 struct shared_table *st;
1905
1906 /* Init cursors */
1907 for (st = peer->tables; st ; st = st->next) {
1908 st->last_get = st->last_acked = 0;
1909 st->teaching_origin = st->last_pushed = st->update;
1910 }
1911
1912 /* Init confirm counter */
1913 peer->confirm = 0;
1914
1915 /* reset teaching and learning flags to 0 */
1916 peer->flags &= PEER_TEACH_RESET;
1917 peer->flags &= PEER_LEARN_RESET;
1918
1919 /* If current peer is local */
1920 if (peer->local) {
1921 /* flag to start to teach lesson */
1922 peer->flags |= PEER_F_TEACH_PROCESS;
1923 }
1924 else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
1925 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
1926 /* If peer is remote and resync from remote is needed,
1927 and no peer currently assigned */
1928
1929 /* assign peer for a lesson */
1930 peer->flags |= PEER_F_LEARN_ASSIGN;
1931 peers->flags |= PEERS_F_RESYNC_ASSIGN;
1932 }
1933}
1934
1935/*
Emeric Brun2b920a12010-09-23 18:30:22 +02001936 * IO Handler to handle message exchance with a peer
1937 */
Willy Tarreau00a37f02015-04-13 12:05:19 +02001938static void peer_io_handler(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +02001939{
Willy Tarreau00a37f02015-04-13 12:05:19 +02001940 struct stream_interface *si = appctx->owner;
Willy Tarreau87b09662015-04-03 00:22:06 +02001941 struct stream *s = si_strm(si);
Vincent Bernat3c2f2f22016-04-03 13:48:42 +02001942 struct peers *curpeers = strm_fe(s)->parent;
Emeric Brun80527f52017-06-19 17:46:37 +02001943 struct peer *curpeer = NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02001944 int reql = 0;
1945 int repl = 0;
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02001946 unsigned int maj_ver, min_ver;
Willy Tarreau2d372c22018-11-05 17:12:27 +01001947 int prev_state;
Emeric Brun2b920a12010-09-23 18:30:22 +02001948
Joseph Herlant82b2f542018-11-15 12:19:14 -08001949 /* Check if the input buffer is available. */
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01001950 if (si_ic(si)->buf.size == 0) {
1951 si_rx_room_blk(si);
1952 goto out;
1953 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001954
Emeric Brun2b920a12010-09-23 18:30:22 +02001955 while (1) {
Willy Tarreau2d372c22018-11-05 17:12:27 +01001956 prev_state = appctx->st0;
Emeric Brun2b920a12010-09-23 18:30:22 +02001957switchstate:
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02001958 maj_ver = min_ver = (unsigned int)-1;
Willy Tarreau7b4b4992013-12-01 09:15:12 +01001959 switch(appctx->st0) {
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001960 case PEER_SESS_ST_ACCEPT:
Willy Tarreau2d372c22018-11-05 17:12:27 +01001961 prev_state = appctx->st0;
Willy Tarreau7b4b4992013-12-01 09:15:12 +01001962 appctx->ctx.peers.ptr = NULL;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001963 appctx->st0 = PEER_SESS_ST_GETVERSION;
Emeric Brun2b920a12010-09-23 18:30:22 +02001964 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001965 case PEER_SESS_ST_GETVERSION:
Willy Tarreau2d372c22018-11-05 17:12:27 +01001966 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01001967 reql = peer_getline_version(appctx, &maj_ver, &min_ver);
1968 if (reql <= 0) {
1969 if (!reql)
1970 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02001971 goto switchstate;
1972 }
1973
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001974 appctx->st0 = PEER_SESS_ST_GETHOST;
Emeric Brun2b920a12010-09-23 18:30:22 +02001975 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001976 case PEER_SESS_ST_GETHOST:
Willy Tarreau2d372c22018-11-05 17:12:27 +01001977 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01001978 reql = peer_getline_host(appctx);
1979 if (reql <= 0) {
1980 if (!reql)
1981 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02001982 goto switchstate;
1983 }
1984
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001985 appctx->st0 = PEER_SESS_ST_GETPEER;
Emeric Brun2b920a12010-09-23 18:30:22 +02001986 /* fall through */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01001987 case PEER_SESS_ST_GETPEER: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01001988 prev_state = appctx->st0;
Frédéric Lécaille3f0fb9d2019-01-25 08:30:29 +01001989 reql = peer_getline_last(appctx, &curpeer);
1990 if (reql <= 0) {
1991 if (!reql)
1992 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02001993 goto switchstate;
1994 }
Emeric Brun2b920a12010-09-23 18:30:22 +02001995
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001996 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Willy Tarreau9df94c22016-10-31 18:42:52 +01001997 if (curpeer->appctx && curpeer->appctx != appctx) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02001998 if (curpeer->local) {
1999 /* Local connection, reply a retry */
2000 appctx->st0 = PEER_SESS_ST_EXIT;
2001 appctx->st1 = PEER_SESS_SC_TRYAGAIN;
2002 goto switchstate;
Emeric Brun2b920a12010-09-23 18:30:22 +02002003 }
Emeric Brun80527f52017-06-19 17:46:37 +02002004
2005 /* we're killing a connection, we must apply a random delay before
2006 * retrying otherwise the other end will do the same and we can loop
2007 * for a while.
2008 */
2009 curpeer->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
Willy Tarreau81bc3b02016-10-31 17:37:39 +01002010 peer_session_forceshutdown(curpeer->appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002011 }
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002012 if (maj_ver != (unsigned int)-1 && min_ver != (unsigned int)-1) {
2013 if (min_ver == PEER_DWNGRD_MINOR_VER) {
2014 curpeer->flags |= PEER_F_DWNGRD;
2015 }
2016 else {
2017 curpeer->flags &= ~PEER_F_DWNGRD;
2018 }
2019 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002020 curpeer->appctx = appctx;
2021 appctx->ctx.peers.ptr = curpeer;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002022 appctx->st0 = PEER_SESS_ST_SENDSUCCESS;
Willy Tarreau199ad242018-11-05 16:31:22 +01002023 HA_ATOMIC_ADD(&active_peers, 1);
Emeric Brun2b920a12010-09-23 18:30:22 +02002024 /* fall through */
2025 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002026 case PEER_SESS_ST_SENDSUCCESS: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002027 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002028 if (!curpeer) {
2029 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002030 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002031 if (curpeer->appctx != appctx) {
2032 appctx->st0 = PEER_SESS_ST_END;
2033 goto switchstate;
2034 }
2035 }
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002036
2037 repl = peer_send_status_successmsg(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002038 if (repl <= 0) {
2039 if (repl == -1)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002040 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002041 goto switchstate;
2042 }
2043
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002044 init_accepted_peer(curpeer, curpeers);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002045
Emeric Brun2b920a12010-09-23 18:30:22 +02002046 /* switch to waiting message state */
Willy Tarreau2d372c22018-11-05 17:12:27 +01002047 HA_ATOMIC_ADD(&connected_peers, 1);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002048 appctx->st0 = PEER_SESS_ST_WAITMSG;
Emeric Brun2b920a12010-09-23 18:30:22 +02002049 goto switchstate;
2050 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002051 case PEER_SESS_ST_CONNECT: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002052 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002053 if (!curpeer) {
2054 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002055 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002056 if (curpeer->appctx != appctx) {
2057 appctx->st0 = PEER_SESS_ST_END;
2058 goto switchstate;
2059 }
2060 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002061
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002062 repl = peer_send_hellomsg(appctx, curpeer);
Emeric Brun2b920a12010-09-23 18:30:22 +02002063 if (repl <= 0) {
2064 if (repl == -1)
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002065 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002066 goto switchstate;
2067 }
2068
2069 /* switch to the waiting statuscode state */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002070 appctx->st0 = PEER_SESS_ST_GETSTATUS;
Emeric Brun2b920a12010-09-23 18:30:22 +02002071 /* fall through */
2072 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002073 case PEER_SESS_ST_GETSTATUS: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002074 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002075 if (!curpeer) {
2076 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002077 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002078 if (curpeer->appctx != appctx) {
2079 appctx->st0 = PEER_SESS_ST_END;
2080 goto switchstate;
2081 }
2082 }
2083
Willy Tarreau2bb4a962014-11-28 11:11:05 +01002084 if (si_ic(si)->flags & CF_WRITE_PARTIAL)
Emeric Brunb3971ab2015-05-12 18:49:09 +02002085 curpeer->statuscode = PEER_SESS_SC_CONNECTEDCODE;
Emeric Brun2b920a12010-09-23 18:30:22 +02002086
Frédéric Lécaillece025572019-01-21 13:38:06 +01002087 reql = peer_getline(appctx);
2088 if (!reql)
2089 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002090
Frédéric Lécaillece025572019-01-21 13:38:06 +01002091 if (reql < 0)
2092 goto switchstate;
Emeric Brun2b920a12010-09-23 18:30:22 +02002093
2094 /* Register status code */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002095 curpeer->statuscode = atoi(trash.area);
Emeric Brun2b920a12010-09-23 18:30:22 +02002096
2097 /* Awake main task */
Frédéric Lécailleed2b4a62017-07-13 09:07:09 +02002098 task_wakeup(curpeers->sync_task, TASK_WOKEN_MSG);
Emeric Brun2b920a12010-09-23 18:30:22 +02002099
2100 /* If status code is success */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002101 if (curpeer->statuscode == PEER_SESS_SC_SUCCESSCODE) {
Frédéric Lécaille4b2fd9b2019-01-25 08:58:41 +01002102 init_connected_peer(curpeer, curpeers);
Emeric Brun2b920a12010-09-23 18:30:22 +02002103 }
2104 else {
Frédéric Lécaille523cc9e2016-10-12 17:30:30 +02002105 if (curpeer->statuscode == PEER_SESS_SC_ERRVERSION)
2106 curpeer->flags |= PEER_F_DWNGRD;
Emeric Brun2b920a12010-09-23 18:30:22 +02002107 /* Status code is not success, abort */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002108 appctx->st0 = PEER_SESS_ST_END;
Emeric Brun2b920a12010-09-23 18:30:22 +02002109 goto switchstate;
2110 }
Willy Tarreau2d372c22018-11-05 17:12:27 +01002111 HA_ATOMIC_ADD(&connected_peers, 1);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002112 appctx->st0 = PEER_SESS_ST_WAITMSG;
Emeric Brun2b920a12010-09-23 18:30:22 +02002113 /* fall through */
2114 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002115 case PEER_SESS_ST_WAITMSG: {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002116 uint32_t msg_len = 0;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002117 char *msg_cur = trash.area;
2118 char *msg_end = trash.area;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002119 unsigned char msg_head[7];
Emeric Brun2b920a12010-09-23 18:30:22 +02002120 int totl = 0;
2121
Willy Tarreau2d372c22018-11-05 17:12:27 +01002122 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002123 if (!curpeer) {
2124 curpeer = appctx->ctx.peers.ptr;
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002125 HA_SPIN_LOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002126 if (curpeer->appctx != appctx) {
2127 appctx->st0 = PEER_SESS_ST_END;
2128 goto switchstate;
2129 }
2130 }
2131
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002132 reql = peer_recv_msg(appctx, (char *)msg_head, sizeof msg_head, &msg_len, &totl);
2133 if (reql <= 0) {
2134 if (reql == -1)
2135 goto switchstate;
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002136 goto send_msgs;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002137 }
Willy Tarreau86a446e2013-11-25 23:02:37 +01002138
Frédéric Lécaille95203f22019-01-23 19:38:11 +01002139 msg_end += msg_len;
Frédéric Lécaille444243c2019-01-24 15:40:11 +01002140 if (!peer_treat_awaited_msg(appctx, curpeer, msg_head, &msg_cur, msg_end, msg_len, totl))
Emeric Brun2b920a12010-09-23 18:30:22 +02002141 goto switchstate;
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002142
Emeric Brun2b920a12010-09-23 18:30:22 +02002143 /* skip consumed message */
Willy Tarreau06d80a92017-10-19 14:32:15 +02002144 co_skip(si_oc(si), totl);
Emeric Brun2b920a12010-09-23 18:30:22 +02002145 /* loop on that state to peek next message */
Willy Tarreau72d6c162013-04-11 16:14:13 +02002146 goto switchstate;
2147
Frédéric Lécaillebe825e52019-01-24 18:28:44 +01002148send_msgs:
2149 /* we get here when a peer_recv_msg() returns 0 in reql */
Frédéric Lécaille25e1d5e2019-01-24 17:33:48 +01002150 repl = peer_send_msgs(appctx, curpeer);
2151 if (repl <= 0) {
2152 if (repl == -1)
2153 goto out;
2154 goto switchstate;
Emeric Brun597b26e2016-08-12 11:23:31 +02002155 }
2156
Emeric Brun2b920a12010-09-23 18:30:22 +02002157 /* noting more to do */
2158 goto out;
2159 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002160 case PEER_SESS_ST_EXIT:
Willy Tarreau2d372c22018-11-05 17:12:27 +01002161 if (prev_state == PEER_SESS_ST_WAITMSG)
2162 HA_ATOMIC_SUB(&connected_peers, 1);
2163 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002164 if (peer_send_status_errormsg(appctx) == -1)
2165 goto out;
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002166 appctx->st0 = PEER_SESS_ST_END;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002167 goto switchstate;
2168 case PEER_SESS_ST_ERRSIZE: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002169 if (prev_state == PEER_SESS_ST_WAITMSG)
2170 HA_ATOMIC_SUB(&connected_peers, 1);
2171 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002172 if (peer_send_error_size_limitmsg(appctx) == -1)
2173 goto out;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002174 appctx->st0 = PEER_SESS_ST_END;
2175 goto switchstate;
2176 }
2177 case PEER_SESS_ST_ERRPROTO: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002178 if (prev_state == PEER_SESS_ST_WAITMSG)
2179 HA_ATOMIC_SUB(&connected_peers, 1);
2180 prev_state = appctx->st0;
Frédéric Lécaille7d0ceee2019-01-24 14:24:05 +01002181 if (peer_send_error_protomsg(appctx) == -1)
2182 goto out;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002183 appctx->st0 = PEER_SESS_ST_END;
Willy Tarreau2d372c22018-11-05 17:12:27 +01002184 prev_state = appctx->st0;
Emeric Brun2b920a12010-09-23 18:30:22 +02002185 /* fall through */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002186 }
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002187 case PEER_SESS_ST_END: {
Willy Tarreau2d372c22018-11-05 17:12:27 +01002188 if (prev_state == PEER_SESS_ST_WAITMSG)
2189 HA_ATOMIC_SUB(&connected_peers, 1);
2190 prev_state = appctx->st0;
Emeric Brun80527f52017-06-19 17:46:37 +02002191 if (curpeer) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002192 HA_SPIN_UNLOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002193 curpeer = NULL;
2194 }
Willy Tarreau73b013b2012-05-21 16:31:45 +02002195 si_shutw(si);
2196 si_shutr(si);
Willy Tarreau2bb4a962014-11-28 11:11:05 +01002197 si_ic(si)->flags |= CF_READ_NULL;
Willy Tarreau828824a2015-04-19 17:20:03 +02002198 goto out;
Emeric Brun2b920a12010-09-23 18:30:22 +02002199 }
2200 }
2201 }
2202out:
Willy Tarreau2bb4a962014-11-28 11:11:05 +01002203 si_oc(si)->flags |= CF_READ_DONTWAIT;
Emeric Brun80527f52017-06-19 17:46:37 +02002204
2205 if (curpeer)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002206 HA_SPIN_UNLOCK(PEER_LOCK, &curpeer->lock);
Emeric Brun2b920a12010-09-23 18:30:22 +02002207 return;
2208}
2209
Willy Tarreau30576452015-04-13 13:50:30 +02002210static struct applet peer_applet = {
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002211 .obj_type = OBJ_TYPE_APPLET,
Willy Tarreaub24281b2011-02-13 13:16:36 +01002212 .name = "<PEER>", /* used for logging */
2213 .fct = peer_io_handler,
Aman Gupta9a13e842012-04-02 18:57:53 -07002214 .release = peer_session_release,
Willy Tarreaub24281b2011-02-13 13:16:36 +01002215};
Emeric Brun2b920a12010-09-23 18:30:22 +02002216
2217/*
2218 * Use this function to force a close of a peer session
2219 */
Willy Tarreau81bc3b02016-10-31 17:37:39 +01002220static void peer_session_forceshutdown(struct appctx *appctx)
Emeric Brun2b920a12010-09-23 18:30:22 +02002221{
Frédéric Lécaille5df11902017-06-13 16:39:57 +02002222 /* Note that the peer sessions which have just been created
2223 * (->st0 == PEER_SESS_ST_CONNECT) must not
2224 * be shutdown, if not, the TCP session will never be closed
2225 * and stay in CLOSE_WAIT state after having been closed by
2226 * the remote side.
2227 */
2228 if (!appctx || appctx->st0 == PEER_SESS_ST_CONNECT)
Willy Tarreau7b4b4992013-12-01 09:15:12 +01002229 return;
2230
Willy Tarreau81bc3b02016-10-31 17:37:39 +01002231 if (appctx->applet != &peer_applet)
2232 return;
2233
Willy Tarreau2d372c22018-11-05 17:12:27 +01002234 if (appctx->st0 == PEER_SESS_ST_WAITMSG)
2235 HA_ATOMIC_SUB(&connected_peers, 1);
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002236 appctx->st0 = PEER_SESS_ST_END;
Willy Tarreau78c0c502016-10-31 17:32:20 +01002237 appctx_wakeup(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002238}
2239
Willy Tarreau91d96282015-03-13 15:47:26 +01002240/* Pre-configures a peers frontend to accept incoming connections */
2241void peers_setup_frontend(struct proxy *fe)
2242{
2243 fe->last_change = now.tv_sec;
Frédéric Lécaillec06b5d42018-04-26 10:06:41 +02002244 fe->cap = PR_CAP_FE | PR_CAP_BE;
Willy Tarreau91d96282015-03-13 15:47:26 +01002245 fe->maxconn = 0;
2246 fe->conn_retries = CONN_RETRIES;
2247 fe->timeout.client = MS_TO_TICKS(5000);
Willy Tarreaud1d48d42015-03-13 16:15:46 +01002248 fe->accept = frontend_accept;
Willy Tarreauf87ab942015-03-13 15:55:16 +01002249 fe->default_target = &peer_applet.obj_type;
Willy Tarreau91d96282015-03-13 15:47:26 +01002250 fe->options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
Willy Tarreau0fca4832015-05-01 19:12:05 +02002251 fe->bind_proc = 0; /* will be filled by users */
Willy Tarreau91d96282015-03-13 15:47:26 +01002252}
2253
Emeric Brun2b920a12010-09-23 18:30:22 +02002254/*
Willy Tarreaubd55e312010-11-11 10:55:09 +01002255 * Create a new peer session in assigned state (connect will start automatically)
Emeric Brun2b920a12010-09-23 18:30:22 +02002256 */
Willy Tarreau9df94c22016-10-31 18:42:52 +01002257static struct appctx *peer_session_create(struct peers *peers, struct peer *peer)
Emeric Brun2b920a12010-09-23 18:30:22 +02002258{
Willy Tarreau04b92862017-09-15 11:01:04 +02002259 struct proxy *p = peers->peers_fe; /* attached frontend */
Willy Tarreau7b4b4992013-12-01 09:15:12 +01002260 struct appctx *appctx;
Willy Tarreau15b5e142015-04-04 14:38:25 +02002261 struct session *sess;
Willy Tarreau87b09662015-04-03 00:22:06 +02002262 struct stream *s;
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002263 struct connection *conn;
Olivier Houchard9aaf7782017-09-13 18:30:23 +02002264 struct conn_stream *cs;
Emeric Brun2b920a12010-09-23 18:30:22 +02002265
Emeric Brunb3971ab2015-05-12 18:49:09 +02002266 peer->reconnect = tick_add(now_ms, MS_TO_TICKS(5000));
2267 peer->statuscode = PEER_SESS_SC_CONNECTCODE;
Willy Tarreaud990baf2015-04-05 00:32:03 +02002268 s = NULL;
2269
Emeric Brun1138fd02017-06-19 12:38:55 +02002270 appctx = appctx_new(&peer_applet, tid_bit);
Willy Tarreaud990baf2015-04-05 00:32:03 +02002271 if (!appctx)
2272 goto out_close;
2273
2274 appctx->st0 = PEER_SESS_ST_CONNECT;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002275 appctx->ctx.peers.ptr = (void *)peer;
Willy Tarreaud990baf2015-04-05 00:32:03 +02002276
Willy Tarreau04b92862017-09-15 11:01:04 +02002277 sess = session_new(p, NULL, &appctx->obj_type);
Willy Tarreau15b5e142015-04-04 14:38:25 +02002278 if (!sess) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002279 ha_alert("out of memory in peer_session_create().\n");
Willy Tarreaud990baf2015-04-05 00:32:03 +02002280 goto out_free_appctx;
Emeric Brun2b920a12010-09-23 18:30:22 +02002281 }
2282
Willy Tarreau87787ac2017-08-28 16:22:54 +02002283 if ((s = stream_new(sess, &appctx->obj_type)) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002284 ha_alert("Failed to initialize stream in peer_session_create().\n");
Willy Tarreau87787ac2017-08-28 16:22:54 +02002285 goto out_free_sess;
Willy Tarreau8baf9062015-04-05 00:46:36 +02002286 }
2287
Willy Tarreau342bfb12015-04-05 01:35:34 +02002288 /* The tasks below are normally what is supposed to be done by
2289 * fe->accept().
2290 */
Willy Tarreaue7dff022015-04-03 01:14:29 +02002291 s->flags = SF_ASSIGNED|SF_ADDR_SET;
Emeric Brun2b920a12010-09-23 18:30:22 +02002292
Willy Tarreau6e2979c2015-04-27 13:21:15 +02002293 /* applet is waiting for data */
Willy Tarreau0cd3bd62018-11-06 18:46:37 +01002294 si_cant_get(&s->si[0]);
Willy Tarreau6e2979c2015-04-27 13:21:15 +02002295 appctx_wakeup(appctx);
2296
Willy Tarreau3ed35ef2013-10-24 11:51:38 +02002297 /* initiate an outgoing connection */
Willy Tarreaudbd02672017-12-06 17:39:53 +01002298 s->si[1].flags |= SI_FL_NOLINGER;
Willy Tarreau3ed35ef2013-10-24 11:51:38 +02002299 si_set_state(&s->si[1], SI_ST_ASS);
Willy Tarreau3ed35ef2013-10-24 11:51:38 +02002300
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002301 /* automatically prepare the stream interface to connect to the
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002302 * pre-initialized connection in si->conn.
2303 */
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002304 if (unlikely((conn = conn_new()) == NULL))
Willy Tarreau8baf9062015-04-05 00:46:36 +02002305 goto out_free_strm;
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002306
Olivier Houchard9aaf7782017-09-13 18:30:23 +02002307 if (unlikely((cs = cs_new(conn)) == NULL))
2308 goto out_free_conn;
2309
Frédéric Lécaille1055e682018-04-26 14:35:21 +02002310 conn->target = s->target = peer_session_target(peer, s);
Willy Tarreaube373152018-09-06 11:45:30 +02002311 memcpy(&conn->addr.to, &peer->addr, sizeof(conn->addr.to));
2312
Frédéric Lécaille1055e682018-04-26 14:35:21 +02002313 conn_prepare(conn, peer->proto, peer_xprt(peer));
Olivier Houchardef60ff32019-01-29 19:00:33 +01002314 if (conn_install_mux(conn, &mux_pt_ops, cs, s->be, NULL) < 0)
2315 goto out_free_cs;
Olivier Houchard9aaf7782017-09-13 18:30:23 +02002316 si_attach_cs(&s->si[1], cs);
Willy Tarreau32e3c6a2013-10-11 19:34:20 +02002317
Emeric Brun2b920a12010-09-23 18:30:22 +02002318 s->do_log = NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002319 s->uniq_id = 0;
Emeric Brun2b920a12010-09-23 18:30:22 +02002320
Willy Tarreau22ec1ea2014-11-27 20:45:39 +01002321 s->res.flags |= CF_READ_DONTWAIT;
Willy Tarreau696a2912014-11-24 11:36:57 +01002322
Emeric Brunb3971ab2015-05-12 18:49:09 +02002323 peer->appctx = appctx;
Willy Tarreau87787ac2017-08-28 16:22:54 +02002324 task_wakeup(s->task, TASK_WOKEN_INIT);
Willy Tarreau199ad242018-11-05 16:31:22 +01002325 HA_ATOMIC_ADD(&active_peers, 1);
Willy Tarreau9df94c22016-10-31 18:42:52 +01002326 return appctx;
Emeric Brun2b920a12010-09-23 18:30:22 +02002327
2328 /* Error unrolling */
Olivier Houchardef60ff32019-01-29 19:00:33 +01002329out_free_cs:
2330 cs_free(cs);
Olivier Houchard9aaf7782017-09-13 18:30:23 +02002331 out_free_conn:
2332 conn_free(conn);
Willy Tarreau15b5e142015-04-04 14:38:25 +02002333 out_free_strm:
Emeric Brun2b920a12010-09-23 18:30:22 +02002334 LIST_DEL(&s->list);
Willy Tarreaubafbe012017-11-24 17:34:44 +01002335 pool_free(pool_head_stream, s);
Willy Tarreau15b5e142015-04-04 14:38:25 +02002336 out_free_sess:
Willy Tarreau11c36242015-04-04 15:54:03 +02002337 session_free(sess);
Willy Tarreaud990baf2015-04-05 00:32:03 +02002338 out_free_appctx:
2339 appctx_free(appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002340 out_close:
Willy Tarreaub21d08e2016-10-31 17:46:57 +01002341 return NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002342}
2343
2344/*
2345 * Task processing function to manage re-connect and peer session
2346 * tasks wakeup on local update.
2347 */
Olivier Houchard9f6af332018-05-25 14:04:04 +02002348static struct task *process_peer_sync(struct task * task, void *context, unsigned short state)
Emeric Brun2b920a12010-09-23 18:30:22 +02002349{
Olivier Houchard9f6af332018-05-25 14:04:04 +02002350 struct peers *peers = context;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002351 struct peer *ps;
2352 struct shared_table *st;
Emeric Brun2b920a12010-09-23 18:30:22 +02002353
2354 task->expire = TICK_ETERNITY;
2355
Emeric Brunb3971ab2015-05-12 18:49:09 +02002356 if (!peers->peers_fe) {
Willy Tarreau46dc1ca2015-05-01 18:32:13 +02002357 /* this one was never started, kill it */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002358 signal_unregister_handler(peers->sighandler);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002359 task_delete(peers->sync_task);
2360 task_free(peers->sync_task);
Willy Tarreau37bb7be2015-09-21 15:24:58 +02002361 peers->sync_task = NULL;
Willy Tarreau46dc1ca2015-05-01 18:32:13 +02002362 return NULL;
2363 }
2364
Emeric Brun80527f52017-06-19 17:46:37 +02002365 /* Acquire lock for all peers of the section */
2366 for (ps = peers->remote; ps; ps = ps->next)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002367 HA_SPIN_LOCK(PEER_LOCK, &ps->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002368
Emeric Brun2b920a12010-09-23 18:30:22 +02002369 if (!stopping) {
2370 /* Normal case (not soft stop)*/
Emeric Brunb3971ab2015-05-12 18:49:09 +02002371
2372 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL) &&
2373 (!nb_oldpids || tick_is_expired(peers->resync_timeout, now_ms)) &&
2374 !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002375 /* Resync from local peer needed
2376 no peer was assigned for the lesson
2377 and no old local peer found
2378 or resync timeout expire */
2379
2380 /* flag no more resync from local, to try resync from remotes */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002381 peers->flags |= PEERS_F_RESYNC_LOCAL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002382
2383 /* reschedule a resync */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002384 peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
Emeric Brun2b920a12010-09-23 18:30:22 +02002385 }
2386
2387 /* For each session */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002388 for (ps = peers->remote; ps; ps = ps->next) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002389 /* For each remote peers */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002390 if (!ps->local) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002391 if (!ps->appctx) {
2392 /* no active peer connection */
Emeric Brun2b920a12010-09-23 18:30:22 +02002393 if (ps->statuscode == 0 ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002394 ((ps->statuscode == PEER_SESS_SC_CONNECTCODE ||
Willy Tarreaub4e34da2015-05-20 10:39:04 +02002395 ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002396 ps->statuscode == PEER_SESS_SC_CONNECTEDCODE) &&
Emeric Brun2b920a12010-09-23 18:30:22 +02002397 tick_is_expired(ps->reconnect, now_ms))) {
2398 /* connection never tried
Willy Tarreau9df94c22016-10-31 18:42:52 +01002399 * or previous peer connection established with success
2400 * or previous peer connection failed while connecting
Emeric Brun2b920a12010-09-23 18:30:22 +02002401 * and reconnection timer is expired */
2402
2403 /* retry a connect */
Willy Tarreau9df94c22016-10-31 18:42:52 +01002404 ps->appctx = peer_session_create(peers, ps);
Emeric Brun2b920a12010-09-23 18:30:22 +02002405 }
Willy Tarreaub4e34da2015-05-20 10:39:04 +02002406 else if (!tick_is_expired(ps->reconnect, now_ms)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002407 /* If previous session failed during connection
2408 * but reconnection timer is not expired */
2409
2410 /* reschedule task for reconnect */
2411 task->expire = tick_first(task->expire, ps->reconnect);
2412 }
2413 /* else do nothing */
Willy Tarreau9df94c22016-10-31 18:42:52 +01002414 } /* !ps->appctx */
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002415 else if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002416 /* current peer connection is active and established */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002417 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE) &&
2418 !(peers->flags & PEERS_F_RESYNC_ASSIGN) &&
Emeric Brun2b920a12010-09-23 18:30:22 +02002419 !(ps->flags & PEER_F_LEARN_NOTUP2DATE)) {
2420 /* Resync from a remote is needed
2421 * and no peer was assigned for lesson
2422 * and current peer may be up2date */
2423
2424 /* assign peer for the lesson */
2425 ps->flags |= PEER_F_LEARN_ASSIGN;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002426 peers->flags |= PEERS_F_RESYNC_ASSIGN;
Emeric Brun2b920a12010-09-23 18:30:22 +02002427
Willy Tarreau9df94c22016-10-31 18:42:52 +01002428 /* wake up peer handler to handle a request of resync */
Willy Tarreaue5843b32015-04-27 18:40:14 +02002429 appctx_wakeup(ps->appctx);
Emeric Brun2b920a12010-09-23 18:30:22 +02002430 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002431 else {
2432 /* Awake session if there is data to push */
2433 for (st = ps->tables; st ; st = st->next) {
2434 if ((int)(st->last_pushed - st->table->localupdate) < 0) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002435 /* wake up the peer handler to push local updates */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002436 appctx_wakeup(ps->appctx);
2437 break;
2438 }
2439 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002440 }
2441 /* else do nothing */
2442 } /* SUCCESSCODE */
2443 } /* !ps->peer->local */
2444 } /* for */
2445
2446 /* Resync from remotes expired: consider resync is finished */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002447 if (((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE) &&
2448 !(peers->flags & PEERS_F_RESYNC_ASSIGN) &&
2449 tick_is_expired(peers->resync_timeout, now_ms)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002450 /* Resync from remote peer needed
2451 * no peer was assigned for the lesson
2452 * and resync timeout expire */
2453
2454 /* flag no more resync from remote, consider resync is finished */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002455 peers->flags |= PEERS_F_RESYNC_REMOTE;
Emeric Brun2b920a12010-09-23 18:30:22 +02002456 }
2457
Emeric Brunb3971ab2015-05-12 18:49:09 +02002458 if ((peers->flags & PEERS_RESYNC_STATEMASK) != PEERS_RESYNC_FINISHED) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002459 /* Resync not finished*/
Frédéric Lécaille5d6e5f82017-05-29 13:47:16 +02002460 /* reschedule task to resync timeout if not expired, to ended resync if needed */
2461 if (!tick_is_expired(peers->resync_timeout, now_ms))
2462 task->expire = tick_first(task->expire, peers->resync_timeout);
Emeric Brun2b920a12010-09-23 18:30:22 +02002463 }
2464 } /* !stopping */
2465 else {
2466 /* soft stop case */
Willy Tarreau086735a2018-11-05 15:09:47 +01002467 if (state & TASK_WOKEN_SIGNAL) {
Joseph Herlant82b2f542018-11-15 12:19:14 -08002468 /* We've just received the signal */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002469 if (!(peers->flags & PEERS_F_DONOTSTOP)) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002470 /* add DO NOT STOP flag if not present */
Christopher Faulet8d8aa0d2017-05-30 15:36:50 +02002471 HA_ATOMIC_ADD(&jobs, 1);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002472 peers->flags |= PEERS_F_DONOTSTOP;
2473 ps = peers->local;
2474 for (st = ps->tables; st ; st = st->next)
2475 st->table->syncing++;
Emeric Brun2b920a12010-09-23 18:30:22 +02002476 }
2477
2478 /* disconnect all connected peers */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002479 for (ps = peers->remote; ps; ps = ps->next) {
Emeric Brun80527f52017-06-19 17:46:37 +02002480 /* we're killing a connection, we must apply a random delay before
2481 * retrying otherwise the other end will do the same and we can loop
2482 * for a while.
2483 */
2484 ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
Willy Tarreau9df94c22016-10-31 18:42:52 +01002485 if (ps->appctx) {
Willy Tarreau81bc3b02016-10-31 17:37:39 +01002486 peer_session_forceshutdown(ps->appctx);
Willy Tarreaue5843b32015-04-27 18:40:14 +02002487 ps->appctx = NULL;
Emeric Brun2b920a12010-09-23 18:30:22 +02002488 }
2489 }
2490 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002491
Emeric Brunb3971ab2015-05-12 18:49:09 +02002492 ps = peers->local;
Emeric Brun2b920a12010-09-23 18:30:22 +02002493 if (ps->flags & PEER_F_TEACH_COMPLETE) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002494 if (peers->flags & PEERS_F_DONOTSTOP) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002495 /* resync of new process was complete, current process can die now */
Willy Tarreaucea85372017-11-29 14:49:30 +01002496 HA_ATOMIC_SUB(&jobs, 1);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002497 peers->flags &= ~PEERS_F_DONOTSTOP;
2498 for (st = ps->tables; st ; st = st->next)
2499 st->table->syncing--;
Emeric Brun2b920a12010-09-23 18:30:22 +02002500 }
2501 }
Willy Tarreau9df94c22016-10-31 18:42:52 +01002502 else if (!ps->appctx) {
2503 /* If there's no active peer connection */
Emeric Brun2b920a12010-09-23 18:30:22 +02002504 if (ps->statuscode == 0 ||
Willy Tarreaue4d927a2013-12-01 12:47:35 +01002505 ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
2506 ps->statuscode == PEER_SESS_SC_CONNECTEDCODE ||
2507 ps->statuscode == PEER_SESS_SC_TRYAGAIN) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002508 /* connection never tried
Willy Tarreau9df94c22016-10-31 18:42:52 +01002509 * or previous peer connection was successfully established
2510 * or previous tcp connect succeeded but init state incomplete
Emeric Brun2b920a12010-09-23 18:30:22 +02002511 * or during previous connect, peer replies a try again statuscode */
2512
2513 /* connect to the peer */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002514 peer_session_create(peers, ps);
Emeric Brun2b920a12010-09-23 18:30:22 +02002515 }
2516 else {
2517 /* Other error cases */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002518 if (peers->flags & PEERS_F_DONOTSTOP) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002519 /* unable to resync new process, current process can die now */
Christopher Faulet8d8aa0d2017-05-30 15:36:50 +02002520 HA_ATOMIC_SUB(&jobs, 1);
Emeric Brunb3971ab2015-05-12 18:49:09 +02002521 peers->flags &= ~PEERS_F_DONOTSTOP;
2522 for (st = ps->tables; st ; st = st->next)
2523 st->table->syncing--;
Emeric Brun2b920a12010-09-23 18:30:22 +02002524 }
2525 }
2526 }
Emeric Brunb3971ab2015-05-12 18:49:09 +02002527 else if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE ) {
Willy Tarreau9df94c22016-10-31 18:42:52 +01002528 /* current peer connection is active and established
2529 * wake up all peer handlers to push remaining local updates */
Emeric Brunb3971ab2015-05-12 18:49:09 +02002530 for (st = ps->tables; st ; st = st->next) {
2531 if ((int)(st->last_pushed - st->table->localupdate) < 0) {
Emeric Brunb3971ab2015-05-12 18:49:09 +02002532 appctx_wakeup(ps->appctx);
2533 break;
2534 }
2535 }
Emeric Brun2b920a12010-09-23 18:30:22 +02002536 }
2537 } /* stopping */
Emeric Brun80527f52017-06-19 17:46:37 +02002538
2539 /* Release lock for all peers of the section */
2540 for (ps = peers->remote; ps; ps = ps->next)
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002541 HA_SPIN_UNLOCK(PEER_LOCK, &ps->lock);
Emeric Brun80527f52017-06-19 17:46:37 +02002542
Emeric Brun2b920a12010-09-23 18:30:22 +02002543 /* Wakeup for re-connect */
2544 return task;
2545}
2546
Emeric Brunb3971ab2015-05-12 18:49:09 +02002547
Emeric Brun2b920a12010-09-23 18:30:22 +02002548/*
Willy Tarreaud9443442018-10-15 11:18:03 +02002549 * returns 0 in case of error.
Emeric Brun2b920a12010-09-23 18:30:22 +02002550 */
Willy Tarreaud9443442018-10-15 11:18:03 +02002551int peers_init_sync(struct peers *peers)
Emeric Brun2b920a12010-09-23 18:30:22 +02002552{
Emeric Brun2b920a12010-09-23 18:30:22 +02002553 struct peer * curpeer;
Willy Tarreau4348fad2012-09-20 16:48:07 +02002554 struct listener *listener;
Emeric Brun2b920a12010-09-23 18:30:22 +02002555
Emeric Brun2b920a12010-09-23 18:30:22 +02002556 for (curpeer = peers->remote; curpeer; curpeer = curpeer->next) {
Emeric Brun2b920a12010-09-23 18:30:22 +02002557 peers->peers_fe->maxconn += 3;
2558 }
2559
Willy Tarreau4348fad2012-09-20 16:48:07 +02002560 list_for_each_entry(listener, &peers->peers_fe->conf.listeners, by_fe)
2561 listener->maxconn = peers->peers_fe->maxconn;
Emeric Brunc60def82017-09-27 14:59:38 +02002562 peers->sync_task = task_new(MAX_THREADS_MASK);
Willy Tarreaud9443442018-10-15 11:18:03 +02002563 if (!peers->sync_task)
2564 return 0;
2565
Emeric Brunb3971ab2015-05-12 18:49:09 +02002566 peers->sync_task->process = process_peer_sync;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002567 peers->sync_task->context = (void *)peers;
2568 peers->sighandler = signal_register_task(0, peers->sync_task, 0);
2569 task_wakeup(peers->sync_task, TASK_WOKEN_INIT);
Willy Tarreaud9443442018-10-15 11:18:03 +02002570 return 1;
Emeric Brunb3971ab2015-05-12 18:49:09 +02002571}
2572
2573
2574
2575/*
2576 * Function used to register a table for sync on a group of peers
2577 *
2578 */
2579void peers_register_table(struct peers *peers, struct stktable *table)
2580{
2581 struct shared_table *st;
2582 struct peer * curpeer;
2583 int id = 0;
2584
2585 for (curpeer = peers->remote; curpeer; curpeer = curpeer->next) {
Vincent Bernat02779b62016-04-03 13:48:43 +02002586 st = calloc(1,sizeof(*st));
Emeric Brunb3971ab2015-05-12 18:49:09 +02002587 st->table = table;
2588 st->next = curpeer->tables;
2589 if (curpeer->tables)
2590 id = curpeer->tables->local_id;
2591 st->local_id = id + 1;
2592
2593 curpeer->tables = st;
2594 }
2595
2596 table->sync_task = peers->sync_task;
Emeric Brun2b920a12010-09-23 18:30:22 +02002597}
2598