blob: 8e2c7a7ac232c0dfb30ac1e9be6882d0fa86d274 [file] [log] [blame]
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001/*
2 * Stream processing offload engine management.
3 *
4 * Copyright 2016 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
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#include <ctype.h>
13#include <errno.h>
14
15#include <common/buffer.h>
16#include <common/cfgparse.h>
17#include <common/compat.h>
18#include <common/config.h>
19#include <common/debug.h>
20#include <common/memory.h>
21#include <common/time.h>
22
23#include <types/arg.h>
24#include <types/filters.h>
25#include <types/global.h>
26#include <types/proxy.h>
27#include <types/sample.h>
28#include <types/stream.h>
29
30#include <proto/arg.h>
31#include <proto/backend.h>
32#include <proto/filters.h>
Christopher Faulet48026722016-11-16 15:01:12 +010033#include <proto/freq_ctr.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020034#include <proto/frontend.h>
35#include <proto/log.h>
36#include <proto/proto_http.h>
37#include <proto/proxy.h>
38#include <proto/sample.h>
39#include <proto/session.h>
40#include <proto/signal.h>
41#include <proto/stream.h>
42#include <proto/stream_interface.h>
43#include <proto/task.h>
44#include <proto/vars.h>
45
46#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
47#define SPOE_PRINTF(x...) fprintf(x)
48#else
49#define SPOE_PRINTF(x...)
50#endif
51
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +010052/* Reserved 4 bytes to the frame size. So a frame and its size can be written
53 * together in a buffer */
54#define MAX_FRAME_SIZE global.tune.bufsize - 4
55
56/* The minimum size for a frame */
57#define MIN_FRAME_SIZE 256
58
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010059/* Reserved for the metadata and the frame type.
60 * So <MAX_FRAME_SIZE> - <FRAME_HDR_SIZE> is the maximum payload size */
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +010061#define FRAME_HDR_SIZE 32
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020062
Christopher Fauletea62c2a2016-11-14 10:54:21 +010063/* Flags set on the SPOE agent */
64#define SPOE_FL_CONT_ON_ERR 0x00000001 /* Do not stop events processing when an error occurred */
65
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020066/* Flags set on the SPOE context */
67#define SPOE_CTX_FL_CLI_CONNECTED 0x00000001 /* Set after that on-client-session event was processed */
68#define SPOE_CTX_FL_SRV_CONNECTED 0x00000002 /* Set after that on-server-session event was processed */
69#define SPOE_CTX_FL_REQ_PROCESS 0x00000004 /* Set when SPOE is processing the request */
70#define SPOE_CTX_FL_RSP_PROCESS 0x00000008 /* Set when SPOE is processing the response */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010071#define SPOE_CTX_FL_FRAGMENTED 0x00000010 /* Set when a fragmented frame is processing */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020072
73#define SPOE_CTX_FL_PROCESS (SPOE_CTX_FL_REQ_PROCESS|SPOE_CTX_FL_RSP_PROCESS)
74
Christopher Fauleta1cda022016-12-21 08:58:06 +010075/* Flags set on the SPOE applet */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010076#define SPOE_APPCTX_FL_PIPELINING 0x00000001 /* Set if pipelining is supported */
77#define SPOE_APPCTX_FL_ASYNC 0x00000002 /* Set if asynchronus frames is supported */
78#define SPOE_APPCTX_FL_FRAGMENTATION 0x00000004 /* Set if fragmentation is supported */
79#define SPOE_APPCTX_FL_PERSIST 0x00000008 /* Set if the applet is persistent */
Christopher Fauleta1cda022016-12-21 08:58:06 +010080
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020081#define SPOE_APPCTX_ERR_NONE 0x00000000 /* no error yet, leave it to zero */
82#define SPOE_APPCTX_ERR_TOUT 0x00000001 /* SPOE applet timeout */
83
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010084/* Flags set on the SPOE frame */
85#define SPOE_FRM_FL_FIN 0x00000001
86#define SPOE_FRM_FL_ABRT 0x00000002
87
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020088/* All possible states for a SPOE context */
89enum spoe_ctx_state {
90 SPOE_CTX_ST_NONE = 0,
91 SPOE_CTX_ST_READY,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010092 SPOE_CTX_ST_ENCODING_MSGS,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020093 SPOE_CTX_ST_SENDING_MSGS,
94 SPOE_CTX_ST_WAITING_ACK,
95 SPOE_CTX_ST_DONE,
96 SPOE_CTX_ST_ERROR,
97};
98
99/* All possible states for a SPOE applet */
100enum spoe_appctx_state {
101 SPOE_APPCTX_ST_CONNECT = 0,
102 SPOE_APPCTX_ST_CONNECTING,
Christopher Fauleta1cda022016-12-21 08:58:06 +0100103 SPOE_APPCTX_ST_IDLE,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200104 SPOE_APPCTX_ST_PROCESSING,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100105 SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY,
106 SPOE_APPCTX_ST_WAITING_SYNC_ACK,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200107 SPOE_APPCTX_ST_DISCONNECT,
108 SPOE_APPCTX_ST_DISCONNECTING,
109 SPOE_APPCTX_ST_EXIT,
110 SPOE_APPCTX_ST_END,
111};
112
113/* All supported SPOE actions */
114enum spoe_action_type {
115 SPOE_ACT_T_SET_VAR = 1,
116 SPOE_ACT_T_UNSET_VAR,
117 SPOE_ACT_TYPES,
118};
119
120/* All supported SPOE events */
121enum spoe_event {
122 SPOE_EV_NONE = 0,
123
124 /* Request events */
125 SPOE_EV_ON_CLIENT_SESS = 1,
126 SPOE_EV_ON_TCP_REQ_FE,
127 SPOE_EV_ON_TCP_REQ_BE,
128 SPOE_EV_ON_HTTP_REQ_FE,
129 SPOE_EV_ON_HTTP_REQ_BE,
130
131 /* Response events */
132 SPOE_EV_ON_SERVER_SESS,
133 SPOE_EV_ON_TCP_RSP,
134 SPOE_EV_ON_HTTP_RSP,
135
136 SPOE_EV_EVENTS
137};
138
Christopher Fauletb067b062017-01-04 16:39:11 +0100139/* Errors triggered by streams */
140enum spoe_context_error {
141 SPOE_CTX_ERR_NONE = 0,
142 SPOE_CTX_ERR_TOUT,
143 SPOE_CTX_ERR_RES,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100144 SPOE_CTX_ERR_TOO_BIG,
Christopher Fauletb067b062017-01-04 16:39:11 +0100145 SPOE_CTX_ERR_UNKNOWN = 255,
146 SPOE_CTX_ERRS,
147};
148
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200149/* Errors triggerd by SPOE applet */
150enum spoe_frame_error {
151 SPOE_FRM_ERR_NONE = 0,
152 SPOE_FRM_ERR_IO,
153 SPOE_FRM_ERR_TOUT,
154 SPOE_FRM_ERR_TOO_BIG,
155 SPOE_FRM_ERR_INVALID,
156 SPOE_FRM_ERR_NO_VSN,
157 SPOE_FRM_ERR_NO_FRAME_SIZE,
158 SPOE_FRM_ERR_NO_CAP,
159 SPOE_FRM_ERR_BAD_VSN,
160 SPOE_FRM_ERR_BAD_FRAME_SIZE,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100161 SPOE_FRM_ERR_FRAG_NOT_SUPPORTED,
162 SPOE_FRM_ERR_INTERLACED_FRAMES,
163 SPOE_FRM_ERR_RES,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200164 SPOE_FRM_ERR_UNKNOWN = 99,
165 SPOE_FRM_ERRS,
166};
167
168/* Scopes used for variables set by agents. It is a way to be agnotic to vars
169 * scope. */
170enum spoe_vars_scope {
171 SPOE_SCOPE_PROC = 0, /* <=> SCOPE_PROC */
172 SPOE_SCOPE_SESS, /* <=> SCOPE_SESS */
173 SPOE_SCOPE_TXN, /* <=> SCOPE_TXN */
174 SPOE_SCOPE_REQ, /* <=> SCOPE_REQ */
175 SPOE_SCOPE_RES, /* <=> SCOPE_RES */
176};
177
178
179/* Describe an argument that will be linked to a message. It is a sample fetch,
180 * with an optional name. */
181struct spoe_arg {
182 char *name; /* Name of the argument, may be NULL */
183 unsigned int name_len; /* The name length, 0 if NULL */
184 struct sample_expr *expr; /* Sample expression */
185 struct list list; /* Used to chain SPOE args */
186};
187
188/* Used during the config parsing only because, when a SPOE agent section is
189 * parsed, messages can be undefined. */
190struct spoe_msg_placeholder {
191 char *id; /* SPOE message placeholder id */
192 struct list list; /* Use to chain SPOE message placeholders */
193};
194
195/* Describe a message that will be sent in a NOTIFY frame. A message has a name,
196 * an argument list (see above) and it is linked to a specific event. */
197struct spoe_message {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100198 char *id; /* SPOE message id */
199 unsigned int id_len; /* The message id length */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200200 struct spoe_agent *agent; /* SPOE agent owning this SPOE message */
201 struct {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100202 char *file; /* file where the SPOE message appears */
203 int line; /* line where the SPOE message appears */
204 } conf; /* config information */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100205 unsigned int nargs; /* # of arguments */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100206 struct list args; /* Arguments added when the SPOE messages is sent */
207 struct list list; /* Used to chain SPOE messages */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200208
209 enum spoe_event event; /* SPOE_EV_* */
210};
211
212/* Describe a SPOE agent. */
213struct spoe_agent {
214 char *id; /* SPOE agent id (name) */
215 struct {
216 char *file; /* file where the SPOE agent appears */
217 int line; /* line where the SPOE agent appears */
218 } conf; /* config information */
219 union {
220 struct proxy *be; /* Backend used by this agent */
221 char *name; /* Backend name used during conf parsing */
222 } b;
223 struct {
Christopher Fauletf7a30922016-11-10 15:04:51 +0100224 unsigned int hello; /* Max time to receive AGENT-HELLO frame (in SPOE applet) */
225 unsigned int idle; /* Max Idle timeout (in SPOE applet) */
Christopher Fauletf7a30922016-11-10 15:04:51 +0100226 unsigned int processing; /* Max time to process an event (in the main stream) */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200227 } timeout;
228
Christopher Fauleta1cda022016-12-21 08:58:06 +0100229 /* Config info */
230 char *engine_id; /* engine-id string */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200231 char *var_pfx; /* Prefix used for vars set by the agent */
Christopher Faulet985532d2016-11-16 15:36:19 +0100232 char *var_on_error; /* Variable to set when an error occured, in the TXN scope */
Christopher Fauletea62c2a2016-11-14 10:54:21 +0100233 unsigned int flags; /* SPOE_FL_* */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100234 unsigned int cps_max; /* Maximum # of connections per second */
235 unsigned int eps_max; /* Maximum # of errors per second */
236 unsigned int max_frame_size; /* Maximum frame size for this agent, before any negotiation */
237 unsigned int min_applets; /* Minimum # applets alive at a time */
238 unsigned int max_fpa; /* Maximum # of frames handled per applet at once */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200239
240 struct list messages[SPOE_EV_EVENTS]; /* List of SPOE messages that will be sent
241 * for each supported events */
242
Christopher Fauleta1cda022016-12-21 08:58:06 +0100243 /* running info */
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +0100244 unsigned int frame_size; /* current maximum frame size, only used to encode messages */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100245 unsigned int applets_act; /* # of applets alive at a time */
246 unsigned int applets_idle; /* # of applets in the state SPOE_APPCTX_ST_IDLE */
247 unsigned int sending_rate; /* the global sending rate */
248
249 struct freq_ctr conn_per_sec; /* connections per second */
250 struct freq_ctr err_per_sec; /* connetion errors per second */
251
252 struct list applets; /* List of available SPOE applets */
253 struct list sending_queue; /* Queue of streams waiting to send data */
254 struct list waiting_queue; /* Queue of streams waiting for a ack, in async mode */
255
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200256};
257
258/* SPOE filter configuration */
259struct spoe_config {
260 struct proxy *proxy; /* Proxy owning the filter */
261 struct spoe_agent *agent; /* Agent used by this filter */
262 struct proxy agent_fe; /* Agent frontend */
263};
264
265/* SPOE context attached to a stream. It is the main structure that handles the
266 * processing offload */
267struct spoe_context {
268 struct filter *filter; /* The SPOE filter */
269 struct stream *strm; /* The stream that should be offloaded */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100270
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200271 struct list *messages; /* List of messages that will be sent during the stream processing */
Christopher Faulet4596fb72017-01-11 14:05:19 +0100272 struct buffer *buffer; /* Buffer used to store a encoded messages */
273 struct buffer_wait buffer_wait; /* position in the list of ressources waiting for a buffer */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100274 struct list list;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200275
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200276 enum spoe_ctx_state state; /* SPOE_CTX_ST_* */
277 unsigned int flags; /* SPOE_CTX_FL_* */
Christopher Fauletb067b062017-01-04 16:39:11 +0100278 unsigned int status_code; /* SPOE_CTX_ERR_* */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200279
280 unsigned int stream_id; /* stream_id and frame_id are used */
281 unsigned int frame_id; /* to map NOTIFY and ACK frames */
Christopher Fauletf7a30922016-11-10 15:04:51 +0100282 unsigned int process_exp; /* expiration date to process an event */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100283
284 struct {
285 struct spoe_appctx *spoe_appctx; /* SPOE appctx sending the fragmented frame */
286 struct spoe_message *curmsg; /* SPOE message from which to resume encoding */
287 struct spoe_arg *curarg; /* SPOE arg in <curmsg> from which to resume encoding */
288 unsigned int curoff; /* offset in <curarg> from which to resume encoding */
289 unsigned int flags; /* SPOE_FRM_FL_* */
290 } frag_ctx; /* Info about fragmented frames, valid on if SPOE_CTX_FL_FRAGMENTED is set */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200291};
292
Christopher Faulet42bfa462017-01-04 14:14:19 +0100293/* SPOE context inside a appctx */
294struct spoe_appctx {
295 struct appctx *owner; /* the owner */
296 struct task *task; /* task to handle applet timeouts */
297 struct spoe_agent *agent; /* agent on which the applet is attached */
298
299 unsigned int version; /* the negotiated version */
300 unsigned int max_frame_size; /* the negotiated max-frame-size value */
301 unsigned int flags; /* SPOE_APPCTX_FL_* */
302
Christopher Fauletb067b062017-01-04 16:39:11 +0100303 unsigned int status_code; /* SPOE_FRM_ERR_* */
Christopher Faulet4596fb72017-01-11 14:05:19 +0100304 struct buffer *buffer; /* Buffer used to store a encoded messages */
305 struct buffer_wait buffer_wait; /* position in the list of ressources waiting for a buffer */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100306 struct list waiting_queue; /* list of streams waiting for a ACK frame, in sync and pipelining mode */
307 struct list list; /* next spoe appctx for the same agent */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100308
309 struct {
310 struct spoe_context *ctx; /* SPOE context owning the fragmented frame */
311 unsigned int cursid; /* stream-id of the fragmented frame. used if the processing is aborted */
312 unsigned int curfid; /* frame-id of the fragmented frame. used if the processing is aborted */
313 } frag_ctx; /* Info about fragmented frames, unused for unfragmented frames */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100314};
315
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100316
317/* Helper to get SPOE ctx inside an appctx */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100318#define SPOE_APPCTX(appctx) ((struct spoe_appctx *)((appctx)->ctx.spoe.ptr))
319
Christopher Faulet3b386a32017-02-23 10:17:15 +0100320/* SPOE filter id. Used to identify SPOE filters */
321const char *spoe_filter_id = "SPOE filter";
322
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200323/* Set if the handle on SIGUSR1 is registered */
324static int sighandler_registered = 0;
325
326/* proxy used during the parsing */
327struct proxy *curproxy = NULL;
328
329/* The name of the SPOE engine, used during the parsing */
330char *curengine = NULL;
331
332/* SPOE agent used during the parsing */
333struct spoe_agent *curagent = NULL;
334
335/* SPOE message used during the parsing */
336struct spoe_message *curmsg = NULL;
337
338/* list of SPOE messages and placeholders used during the parsing */
339struct list curmsgs;
340struct list curmps;
341
Christopher Faulet42bfa462017-01-04 14:14:19 +0100342/* Pools used to allocate SPOE structs */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200343static struct pool_head *pool2_spoe_ctx = NULL;
Christopher Faulet42bfa462017-01-04 14:14:19 +0100344static struct pool_head *pool2_spoe_appctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200345
346/* Temporary variables used to ease error processing */
347int spoe_status_code = SPOE_FRM_ERR_NONE;
348char spoe_reason[256];
349
350struct flt_ops spoe_ops;
351
Christopher Fauleta1cda022016-12-21 08:58:06 +0100352static int queue_spoe_context(struct spoe_context *ctx);
Christopher Faulet4596fb72017-01-11 14:05:19 +0100353static int acquire_spoe_buffer(struct buffer **buf, struct buffer_wait *buffer_wait);
354static void release_spoe_buffer(struct buffer **buf, struct buffer_wait *buffer_wait);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200355
356/********************************************************************
357 * helper functions/globals
358 ********************************************************************/
359static void
360release_spoe_msg_placeholder(struct spoe_msg_placeholder *mp)
361{
362 if (!mp)
363 return;
364 free(mp->id);
365 free(mp);
366}
367
368
369static void
370release_spoe_message(struct spoe_message *msg)
371{
372 struct spoe_arg *arg, *back;
373
374 if (!msg)
375 return;
376 free(msg->id);
377 free(msg->conf.file);
378 list_for_each_entry_safe(arg, back, &msg->args, list) {
379 release_sample_expr(arg->expr);
380 free(arg->name);
381 LIST_DEL(&arg->list);
382 free(arg);
383 }
384 free(msg);
385}
386
387static void
388release_spoe_agent(struct spoe_agent *agent)
389{
390 struct spoe_message *msg, *back;
391 int i;
392
393 if (!agent)
394 return;
395 free(agent->id);
396 free(agent->conf.file);
397 free(agent->var_pfx);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100398 free(agent->engine_id);
Christopher Faulet985532d2016-11-16 15:36:19 +0100399 free(agent->var_on_error);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200400 for (i = 0; i < SPOE_EV_EVENTS; ++i) {
401 list_for_each_entry_safe(msg, back, &agent->messages[i], list) {
402 LIST_DEL(&msg->list);
403 release_spoe_message(msg);
404 }
405 }
406 free(agent);
407}
408
409static const char *spoe_frm_err_reasons[SPOE_FRM_ERRS] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100410 [SPOE_FRM_ERR_NONE] = "normal",
411 [SPOE_FRM_ERR_IO] = "I/O error",
412 [SPOE_FRM_ERR_TOUT] = "a timeout occurred",
413 [SPOE_FRM_ERR_TOO_BIG] = "frame is too big",
414 [SPOE_FRM_ERR_INVALID] = "invalid frame received",
415 [SPOE_FRM_ERR_NO_VSN] = "version value not found",
416 [SPOE_FRM_ERR_NO_FRAME_SIZE] = "max-frame-size value not found",
417 [SPOE_FRM_ERR_NO_CAP] = "capabilities value not found",
418 [SPOE_FRM_ERR_BAD_VSN] = "unsupported version",
419 [SPOE_FRM_ERR_BAD_FRAME_SIZE] = "max-frame-size too big or too small",
420 [SPOE_FRM_ERR_FRAG_NOT_SUPPORTED] = "fragmentation not supported",
421 [SPOE_FRM_ERR_INTERLACED_FRAMES] = "invalid interlaced frames",
422 [SPOE_FRM_ERR_RES] = "resource allocation error",
423 [SPOE_FRM_ERR_UNKNOWN] = "an unknown error occurred",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200424};
425
426static const char *spoe_event_str[SPOE_EV_EVENTS] = {
427 [SPOE_EV_ON_CLIENT_SESS] = "on-client-session",
428 [SPOE_EV_ON_TCP_REQ_FE] = "on-frontend-tcp-request",
429 [SPOE_EV_ON_TCP_REQ_BE] = "on-backend-tcp-request",
430 [SPOE_EV_ON_HTTP_REQ_FE] = "on-frontend-http-request",
431 [SPOE_EV_ON_HTTP_REQ_BE] = "on-backend-http-request",
432
433 [SPOE_EV_ON_SERVER_SESS] = "on-server-session",
434 [SPOE_EV_ON_TCP_RSP] = "on-tcp-response",
435 [SPOE_EV_ON_HTTP_RSP] = "on-http-response",
436};
437
438
439#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
440
441static const char *spoe_ctx_state_str[SPOE_CTX_ST_ERROR+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100442 [SPOE_CTX_ST_NONE] = "NONE",
443 [SPOE_CTX_ST_READY] = "READY",
444 [SPOE_CTX_ST_ENCODING_MSGS] = "ENCODING_MSGS",
445 [SPOE_CTX_ST_SENDING_MSGS] = "SENDING_MSGS",
446 [SPOE_CTX_ST_WAITING_ACK] = "WAITING_ACK",
447 [SPOE_CTX_ST_DONE] = "DONE",
448 [SPOE_CTX_ST_ERROR] = "ERROR",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200449};
450
451static const char *spoe_appctx_state_str[SPOE_APPCTX_ST_END+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100452 [SPOE_APPCTX_ST_CONNECT] = "CONNECT",
453 [SPOE_APPCTX_ST_CONNECTING] = "CONNECTING",
454 [SPOE_APPCTX_ST_IDLE] = "IDLE",
455 [SPOE_APPCTX_ST_PROCESSING] = "PROCESSING",
456 [SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY] = "SENDING_FRAG_NOTIFY",
457 [SPOE_APPCTX_ST_WAITING_SYNC_ACK] = "WAITING_SYNC_ACK",
458 [SPOE_APPCTX_ST_DISCONNECT] = "DISCONNECT",
459 [SPOE_APPCTX_ST_DISCONNECTING] = "DISCONNECTING",
460 [SPOE_APPCTX_ST_EXIT] = "EXIT",
461 [SPOE_APPCTX_ST_END] = "END",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200462};
463
464#endif
Christopher Fauleta1cda022016-12-21 08:58:06 +0100465
466static char *
467generate_pseudo_uuid()
468{
469 static int init = 0;
470
471 const char uuid_fmt[] = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
472 const char uuid_chr[] = "0123456789ABCDEF-";
473 char *uuid;
474 int i;
475
476 if ((uuid = calloc(1, sizeof(uuid_fmt))) == NULL)
477 return NULL;
478
479 if (!init) {
480 srand(now_ms);
481 init = 1;
482 }
483
484 for (i = 0; i < sizeof(uuid_fmt)-1; i++) {
485 int r = rand () % 16;
486
487 switch (uuid_fmt[i]) {
488 case 'x' : uuid[i] = uuid_chr[r]; break;
489 case 'y' : uuid[i] = uuid_chr[(r & 0x03) | 0x08]; break;
490 default : uuid[i] = uuid_fmt[i]; break;
491 }
492 }
493 return uuid;
494}
495
496static inline unsigned int
497min_applets_act(struct spoe_agent *agent)
498{
499 unsigned int nbsrv;
500
501 if (agent->min_applets)
502 return agent->min_applets;
503
504 nbsrv = (agent->b.be->srv_act ? agent->b.be->srv_act : agent->b.be->srv_bck);
505 return 2*nbsrv;
506}
507
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200508/********************************************************************
509 * Functions that encode/decode SPOE frames
510 ********************************************************************/
511/* Frame Types sent by HAProxy and by agents */
512enum spoe_frame_type {
513 /* Frames sent by HAProxy */
514 SPOE_FRM_T_HAPROXY_HELLO = 1,
515 SPOE_FRM_T_HAPROXY_DISCON,
516 SPOE_FRM_T_HAPROXY_NOTIFY,
517
518 /* Frames sent by the agents */
519 SPOE_FRM_T_AGENT_HELLO = 101,
520 SPOE_FRM_T_AGENT_DISCON,
521 SPOE_FRM_T_AGENT_ACK
522};
523
524/* All supported data types */
525enum spoe_data_type {
526 SPOE_DATA_T_NULL = 0,
527 SPOE_DATA_T_BOOL,
528 SPOE_DATA_T_INT32,
529 SPOE_DATA_T_UINT32,
530 SPOE_DATA_T_INT64,
531 SPOE_DATA_T_UINT64,
532 SPOE_DATA_T_IPV4,
533 SPOE_DATA_T_IPV6,
534 SPOE_DATA_T_STR,
535 SPOE_DATA_T_BIN,
536 SPOE_DATA_TYPES
537};
538
539/* Masks to get data type or flags value */
540#define SPOE_DATA_T_MASK 0x0F
541#define SPOE_DATA_FL_MASK 0xF0
542
543/* Flags to set Boolean values */
544#define SPOE_DATA_FL_FALSE 0x00
545#define SPOE_DATA_FL_TRUE 0x10
546
547/* Helper to get static string length, excluding the terminating null byte */
548#define SLEN(str) (sizeof(str)-1)
549
550/* Predefined key used in HELLO/DISCONNECT frames */
551#define SUPPORTED_VERSIONS_KEY "supported-versions"
552#define VERSION_KEY "version"
553#define MAX_FRAME_SIZE_KEY "max-frame-size"
554#define CAPABILITIES_KEY "capabilities"
Christopher Fauleta1cda022016-12-21 08:58:06 +0100555#define ENGINE_ID_KEY "engine-id"
Christopher Fauletba7bc162016-11-07 21:07:38 +0100556#define HEALTHCHECK_KEY "healthcheck"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200557#define STATUS_CODE_KEY "status-code"
558#define MSG_KEY "message"
559
560struct spoe_version {
561 char *str;
562 int min;
563 int max;
564};
565
566/* All supported versions */
567static struct spoe_version supported_versions[] = {
568 {"1.0", 1000, 1000},
569 {NULL, 0, 0}
570};
571
572/* Comma-separated list of supported versions */
573#define SUPPORTED_VERSIONS_VAL "1.0"
574
575/* Comma-separated list of supported capabilities (none for now) */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100576//#define CAPABILITIES_VAL ""
577#define CAPABILITIES_VAL "pipelining,async"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200578
579static int
580decode_spoe_version(const char *str, size_t len)
581{
582 char tmp[len+1], *start, *end;
583 double d;
584 int vsn = -1;
585
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200586 memcpy(tmp, str, len);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100587 tmp[len] = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200588
589 start = tmp;
590 while (isspace(*start))
591 start++;
592
593 d = strtod(start, &end);
594 if (d == 0 || start == end)
595 goto out;
596
597 if (*end) {
598 while (isspace(*end))
599 end++;
600 if (*end)
601 goto out;
602 }
603 vsn = (int)(d * 1000);
604 out:
605 return vsn;
606}
607
608/* Encode a variable-length integer. This function never fails and returns the
609 * number of written bytes. */
610static int
611encode_spoe_varint(uint64_t i, char *buf)
612{
613 int idx;
614
615 if (i < 240) {
616 buf[0] = (unsigned char)i;
617 return 1;
618 }
619
620 buf[0] = (unsigned char)i | 240;
621 i = (i - 240) >> 4;
622 for (idx = 1; i >= 128; ++idx) {
623 buf[idx] = (unsigned char)i | 128;
624 i = (i - 128) >> 7;
625 }
626 buf[idx++] = (unsigned char)i;
627 return idx;
628}
629
630/* Decode a varable-length integer. If the decoding fails, -1 is returned. This
631 * happens when the buffer's end in reached. On success, the number of read
632 * bytes is returned. */
633static int
634decode_spoe_varint(const char *buf, const char *end, uint64_t *i)
635{
636 unsigned char *msg = (unsigned char *)buf;
637 int idx = 0;
638
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100639 if (msg >= (unsigned char *)end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200640 return -1;
641
642 if (msg[0] < 240) {
643 *i = msg[0];
644 return 1;
645 }
646 *i = msg[0];
647 do {
648 ++idx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100649 if (msg+idx >= (unsigned char *)end)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200650 return -1;
651 *i += (uint64_t)msg[idx] << (4 + 7 * (idx-1));
652 } while (msg[idx] >= 128);
653 return (idx + 1);
654}
655
656/* Encode a string. The string will be prefix by its length, encoded as a
657 * variable-length integer. This function never fails and returns the number of
658 * written bytes. */
659static int
660encode_spoe_string(const char *str, size_t len, char *dst)
661{
662 int idx = 0;
663
664 if (!len) {
665 dst[0] = 0;
666 return 1;
667 }
668
669 idx += encode_spoe_varint(len, dst);
670 memcpy(dst+idx, str, len);
671 return (idx + len);
672}
673
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100674/* Encode first part of a fragmented string. The string will be prefix by its
675 * length, encoded as a variable-length integer. This function never fails and
676 * returns the number of written bytes. */
677static int
678encode_frag_spoe_string(const char *str, size_t sz, size_t len, char *dst)
679{
680 int idx = 0;
681
682 if (!sz) {
683 dst[0] = 0;
684 return 1;
685 }
686
687 idx += encode_spoe_varint(sz, dst);
688 memcpy(dst+idx, str, len);
689 return (idx + len);
690}
691
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200692/* Decode a string. Its length is decoded first as a variable-length integer. If
693 * it succeeds, and if the string length is valid, the begin of the string is
694 * saved in <*str>, its length is saved in <*len> and the total numbre of bytes
695 * read is returned. If an error occurred, -1 is returned and <*str> remains
696 * NULL. */
697static int
698decode_spoe_string(char *buf, char *end, char **str, uint64_t *len)
699{
700 int i, idx = 0;
701
702 *str = NULL;
703 *len = 0;
704
705 if ((i = decode_spoe_varint(buf, end, len)) == -1)
706 goto error;
707 idx += i;
708 if (buf + idx + *len > end)
709 goto error;
710
711 *str = buf+idx;
712 return (idx + *len);
713
714 error:
715 return -1;
716}
717
718/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
719 * of bytes read is returned. A types data is composed of a type (1 byte) and
720 * corresponding data:
721 * - boolean: non additional data (0 bytes)
722 * - integers: a variable-length integer (see decode_spoe_varint)
723 * - ipv4: 4 bytes
724 * - ipv6: 16 bytes
725 * - binary and string: a buffer prefixed by its size, a variable-length
726 * integer (see decode_spoe_string) */
727static int
728skip_spoe_data(char *frame, char *end)
729{
730 uint64_t sz = 0;
731 int i, idx = 0;
732
733 if (frame > end)
734 return -1;
735
736 switch (frame[idx++] & SPOE_DATA_T_MASK) {
737 case SPOE_DATA_T_BOOL:
738 break;
739 case SPOE_DATA_T_INT32:
740 case SPOE_DATA_T_INT64:
741 case SPOE_DATA_T_UINT32:
742 case SPOE_DATA_T_UINT64:
743 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
744 return -1;
745 idx += i;
746 break;
747 case SPOE_DATA_T_IPV4:
748 idx += 4;
749 break;
750 case SPOE_DATA_T_IPV6:
751 idx += 16;
752 break;
753 case SPOE_DATA_T_STR:
754 case SPOE_DATA_T_BIN:
755 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
756 return -1;
757 idx += i + sz;
758 break;
759 }
760
761 if (frame+idx > end)
762 return -1;
763 return idx;
764}
765
766/* Decode a typed data. If an error occurred, -1 is returned, otherwise the
767 * number of read bytes is returned. See skip_spoe_data for details. */
768static int
769decode_spoe_data(char *frame, char *end, struct sample *smp)
770{
771 uint64_t sz = 0;
772 int type, i, idx = 0;
773
774 if (frame > end)
775 return -1;
776
777 type = frame[idx++];
778 switch (type & SPOE_DATA_T_MASK) {
779 case SPOE_DATA_T_BOOL:
780 smp->data.u.sint = ((type & SPOE_DATA_FL_TRUE) == SPOE_DATA_FL_TRUE);
781 smp->data.type = SMP_T_BOOL;
782 break;
783 case SPOE_DATA_T_INT32:
784 case SPOE_DATA_T_INT64:
785 case SPOE_DATA_T_UINT32:
786 case SPOE_DATA_T_UINT64:
787 if ((i = decode_spoe_varint(frame+idx, end, (uint64_t *)&smp->data.u.sint)) == -1)
788 return -1;
789 idx += i;
790 smp->data.type = SMP_T_SINT;
791 break;
792 case SPOE_DATA_T_IPV4:
793 if (frame+idx+4 > end)
794 return -1;
795 memcpy(&smp->data.u.ipv4, frame+idx, 4);
796 smp->data.type = SMP_T_IPV4;
797 idx += 4;
798 break;
799 case SPOE_DATA_T_IPV6:
800 if (frame+idx+16 > end)
801 return -1;
802 memcpy(&smp->data.u.ipv6, frame+idx, 16);
803 smp->data.type = SMP_T_IPV6;
804 idx += 16;
805 break;
806 case SPOE_DATA_T_STR:
807 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
808 return -1;
809 idx += i;
810 if (frame+idx+sz > end)
811 return -1;
812 smp->data.u.str.str = frame+idx;
813 smp->data.u.str.len = sz;
814 smp->data.type = SMP_T_STR;
815 idx += sz;
816 break;
817 case SPOE_DATA_T_BIN:
818 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
819 return -1;
820 idx += i;
821 if (frame+idx+sz > end)
822 return -1;
823 smp->data.u.str.str = frame+idx;
824 smp->data.u.str.len = sz;
825 smp->data.type = SMP_T_BIN;
826 idx += sz;
827 break;
828 }
829
830 if (frame+idx > end)
831 return -1;
832 return idx;
833}
834
835/* Skip an action in a frame received from an agent. If an error occurred, -1 is
836 * returned, otherwise the number of read bytes is returned. An action is
837 * composed of the action type followed by a typed data. */
838static int
839skip_spoe_action(char *frame, char *end)
840{
841 int n, i, idx = 0;
842
843 if (frame+2 > end)
844 return -1;
845
846 idx++; /* Skip the action type */
847 n = frame[idx++];
848 while (n-- > 0) {
849 if ((i = skip_spoe_data(frame+idx, end)) == -1)
850 return -1;
851 idx += i;
852 }
853
854 if (frame+idx > end)
855 return -1;
856 return idx;
857}
858
859/* Encode HELLO frame sent by HAProxy to an agent. It returns the frame size on
860 * success, 0 if the frame can be ignored and -1 if an error occurred. */
861static int
862prepare_spoe_hahello_frame(struct appctx *appctx, char *frame, size_t size)
863{
Christopher Faulet42bfa462017-01-04 14:14:19 +0100864 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100865 unsigned int flags = SPOE_FRM_FL_FIN;
866 int idx = 0;
867 size_t max = (7 /* TYPE + METADATA */
868 + 1 + SLEN(SUPPORTED_VERSIONS_KEY) + 1 + 1 + SLEN(SUPPORTED_VERSIONS_VAL)
869 + 1 + SLEN(MAX_FRAME_SIZE_KEY) + 1 + 4
870 + 1 + SLEN(CAPABILITIES_KEY) + 1 + 1 + SLEN(CAPABILITIES_VAL)
871 + 1 + SLEN(ENGINE_ID_KEY) + 1 + 1 + 36);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200872
Christopher Fauletb067b062017-01-04 16:39:11 +0100873 if (size < max) {
874 spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200875 return -1;
Christopher Fauletb067b062017-01-04 16:39:11 +0100876 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200877
878 /* Frame type */
879 frame[idx++] = SPOE_FRM_T_HAPROXY_HELLO;
880
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100881 /* Set flags */
882 //flags = htonl(flags);
883 memcpy(frame+idx, (char *)&flags, 4);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200884 idx += 4;
885
886 /* No stream-id and frame-id for HELLO frames */
887 frame[idx++] = 0;
888 frame[idx++] = 0;
889
890 /* There are 3 mandatory items: "supported-versions", "max-frame-size"
891 * and "capabilities" */
892
893 /* "supported-versions" K/V item */
894 idx += encode_spoe_string(SUPPORTED_VERSIONS_KEY, SLEN(SUPPORTED_VERSIONS_KEY), frame+idx);
895 frame[idx++] = SPOE_DATA_T_STR;
896 idx += encode_spoe_string(SUPPORTED_VERSIONS_VAL, SLEN(SUPPORTED_VERSIONS_VAL), frame+idx);
897
898 /* "max-fram-size" K/V item */
899 idx += encode_spoe_string(MAX_FRAME_SIZE_KEY, SLEN(MAX_FRAME_SIZE_KEY), frame+idx);
900 frame[idx++] = SPOE_DATA_T_UINT32;
Christopher Faulet42bfa462017-01-04 14:14:19 +0100901 idx += encode_spoe_varint(SPOE_APPCTX(appctx)->max_frame_size, frame+idx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200902
903 /* "capabilities" K/V item */
904 idx += encode_spoe_string(CAPABILITIES_KEY, SLEN(CAPABILITIES_KEY), frame+idx);
905 frame[idx++] = SPOE_DATA_T_STR;
906 idx += encode_spoe_string(CAPABILITIES_VAL, SLEN(CAPABILITIES_VAL), frame+idx);
907
Christopher Fauleta1cda022016-12-21 08:58:06 +0100908 /* "engine-id" K/V item */
909 if (agent != NULL && agent->engine_id != NULL) {
910 idx += encode_spoe_string(ENGINE_ID_KEY, SLEN(ENGINE_ID_KEY), frame+idx);
911 frame[idx++] = SPOE_DATA_T_STR;
912 idx += encode_spoe_string(agent->engine_id, strlen(agent->engine_id), frame+idx);
913 }
914
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200915 return idx;
916}
917
918/* Encode DISCONNECT frame sent by HAProxy to an agent. It returns the frame
919 * size on success, 0 if the frame can be ignored and -1 if an error
920 * occurred. */
921static int
922prepare_spoe_hadiscon_frame(struct appctx *appctx, char *frame, size_t size)
923{
924 const char *reason;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100925 unsigned int flags = SPOE_FRM_FL_FIN;
926 int rlen, idx = 0;
927 size_t max = (7 /* TYPE + METADATA */
928 + 1 + SLEN(STATUS_CODE_KEY) + 1 + 2
929 + 1 + SLEN(MSG_KEY) + 1 + 2 + 255);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200930
931 if (size < max)
932 return -1;
933
934 /* Get the message corresponding to the status code */
935 if (spoe_status_code >= SPOE_FRM_ERRS)
936 spoe_status_code = SPOE_FRM_ERR_UNKNOWN;
937 reason = spoe_frm_err_reasons[spoe_status_code];
938 rlen = strlen(reason);
939
940 /* Frame type */
941 frame[idx++] = SPOE_FRM_T_HAPROXY_DISCON;
942
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100943 /* Set flags */
944 memcpy(frame+idx, (char *)&flags, 4);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200945 idx += 4;
946
947 /* No stream-id and frame-id for DISCONNECT frames */
948 frame[idx++] = 0;
949 frame[idx++] = 0;
950
951 /* There are 2 mandatory items: "status-code" and "message" */
952
953 /* "status-code" K/V item */
954 idx += encode_spoe_string(STATUS_CODE_KEY, SLEN(STATUS_CODE_KEY), frame+idx);
955 frame[idx++] = SPOE_DATA_T_UINT32;
956 idx += encode_spoe_varint(spoe_status_code, frame+idx);
957
958 /* "message" K/V item */
959 idx += encode_spoe_string(MSG_KEY, SLEN(MSG_KEY), frame+idx);
960 frame[idx++] = SPOE_DATA_T_STR;
961 idx += encode_spoe_string(reason, rlen, frame+idx);
962
963 return idx;
964}
965
966/* Encode NOTIFY frame sent by HAProxy to an agent. It returns the frame size on
967 * success, 0 if the frame can be ignored and -1 if an error occurred. */
968static int
Christopher Fauleta1cda022016-12-21 08:58:06 +0100969prepare_spoe_hanotify_frame(struct appctx *appctx, struct spoe_context *ctx,
970 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200971{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100972 int idx = 0;
973 unsigned int stream_id, frame_id, flags = SPOE_FRM_FL_FIN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200974
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200975 frame[idx++] = SPOE_FRM_T_HAPROXY_NOTIFY;
976
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100977 if (ctx == NULL) {
978 flags |= SPOE_FRM_FL_ABRT;
979 stream_id = SPOE_APPCTX(appctx)->frag_ctx.cursid;
980 frame_id = SPOE_APPCTX(appctx)->frag_ctx.curfid;
981 }
982 else {
983 stream_id = ctx->stream_id;
984 frame_id = ctx->frame_id;
985
986 if (ctx->flags & SPOE_CTX_FL_FRAGMENTED) {
987 if (!(SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_FRAGMENTATION)) {
988 spoe_status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
989 return 0;
990 }
991 flags = ctx->frag_ctx.flags;
992 }
993 }
994
995 /* Set flags */
996 memcpy(frame+idx, (char *)&flags, 4);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200997 idx += 4;
998
999 /* Set stream-id and frame-id */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001000 idx += encode_spoe_varint(stream_id, frame+idx);
1001 idx += encode_spoe_varint(frame_id, frame+idx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001002
Christopher Faulet4596fb72017-01-11 14:05:19 +01001003 /* check the buffer size */
1004 if (idx + SPOE_APPCTX(appctx)->buffer->i > size) {
Christopher Fauletb067b062017-01-04 16:39:11 +01001005 spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001006 return 0;
Christopher Fauletb067b062017-01-04 16:39:11 +01001007 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001008
1009 /* Copy encoded messages */
Christopher Faulet4596fb72017-01-11 14:05:19 +01001010 memcpy(frame+idx, SPOE_APPCTX(appctx)->buffer->p, SPOE_APPCTX(appctx)->buffer->i);
1011 idx += SPOE_APPCTX(appctx)->buffer->i;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001012 return idx;
1013}
1014
1015/* Decode HELLO frame sent by an agent. It returns the number of by read bytes
1016 * on success, 0 if the frame can be ignored and -1 if an error occurred. */
1017static int
1018handle_spoe_agenthello_frame(struct appctx *appctx, char *frame, size_t size)
1019{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001020 int vsn, max_frame_size, i, idx = 0;
1021 unsigned int flags;
1022 size_t min_size = (7 /* TYPE + METADATA */
1023 + 1 + SLEN(VERSION_KEY) + 1 + 1 + 3
1024 + 1 + SLEN(MAX_FRAME_SIZE_KEY) + 1 + 1
1025 + 1 + SLEN(CAPABILITIES_KEY) + 1 + 1 + 0);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001026
1027 /* Check frame type */
1028 if (frame[idx++] != SPOE_FRM_T_AGENT_HELLO)
1029 return 0;
1030
1031 if (size < min_size) {
1032 spoe_status_code = SPOE_FRM_ERR_INVALID;
1033 return -1;
1034 }
1035
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001036 /* Retrieve flags */
1037 memcpy((char *)&flags, frame+idx, 4);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001038 idx += 4;
1039
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001040 /* Fragmentation is not supported for HELLO frame */
1041 if (!(flags & SPOE_FRM_FL_FIN)) {
1042 spoe_status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
1043 return -1;
1044 }
1045
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001046 /* stream-id and frame-id must be cleared */
1047 if (frame[idx] != 0 || frame[idx+1] != 0) {
1048 spoe_status_code = SPOE_FRM_ERR_INVALID;
1049 return -1;
1050 }
1051 idx += 2;
1052
1053 /* There are 3 mandatory items: "version", "max-frame-size" and
1054 * "capabilities" */
1055
1056 /* Loop on K/V items */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001057 vsn = max_frame_size = flags = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001058 while (idx < size) {
1059 char *str;
1060 uint64_t sz;
1061
1062 /* Decode the item key */
1063 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1064 if (str == NULL) {
1065 spoe_status_code = SPOE_FRM_ERR_INVALID;
1066 return -1;
1067 }
1068 /* Check "version" K/V item */
1069 if (!memcmp(str, VERSION_KEY, sz)) {
1070 /* The value must be a string */
1071 if ((frame[idx++] & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1072 spoe_status_code = SPOE_FRM_ERR_INVALID;
1073 return -1;
1074 }
1075 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1076 if (str == NULL) {
1077 spoe_status_code = SPOE_FRM_ERR_INVALID;
1078 return -1;
1079 }
1080
1081 vsn = decode_spoe_version(str, sz);
1082 if (vsn == -1) {
1083 spoe_status_code = SPOE_FRM_ERR_BAD_VSN;
1084 return -1;
1085 }
1086 for (i = 0; supported_versions[i].str != NULL; ++i) {
1087 if (vsn >= supported_versions[i].min &&
1088 vsn <= supported_versions[i].max)
1089 break;
1090 }
1091 if (supported_versions[i].str == NULL) {
1092 spoe_status_code = SPOE_FRM_ERR_BAD_VSN;
1093 return -1;
1094 }
1095 }
1096 /* Check "max-frame-size" K/V item */
1097 else if (!memcmp(str, MAX_FRAME_SIZE_KEY, sz)) {
1098 int type;
1099
1100 /* The value must be integer */
1101 type = frame[idx++];
1102 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1103 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1104 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1105 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
1106 spoe_status_code = SPOE_FRM_ERR_INVALID;
1107 return -1;
1108 }
1109 if ((i = decode_spoe_varint(frame+idx, frame+size, &sz)) == -1) {
1110 spoe_status_code = SPOE_FRM_ERR_INVALID;
1111 return -1;
1112 }
1113 idx += i;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001114 if (sz < MIN_FRAME_SIZE || sz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001115 spoe_status_code = SPOE_FRM_ERR_BAD_FRAME_SIZE;
1116 return -1;
1117 }
1118 max_frame_size = sz;
1119 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001120 /* Check "capabilities" K/V item */
1121 else if (!memcmp(str, CAPABILITIES_KEY, sz)) {
1122 int i;
1123
1124 /* The value must be a string */
1125 if ((frame[idx++] & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1126 spoe_status_code = SPOE_FRM_ERR_INVALID;
1127 return -1;
1128 }
1129 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1130 if (str == NULL)
1131 continue;
1132
1133 i = 0;
1134 while (i < sz) {
1135 char *delim;
1136
1137 /* Skip leading spaces */
1138 for (; isspace(str[i]) && i < sz; i++);
1139
1140 if (sz - i >= 10 && !strncmp(str + i, "pipelining", 10)) {
1141 i += 10;
1142 if (sz == i || isspace(str[i]) || str[i] == ',')
1143 flags |= SPOE_APPCTX_FL_PIPELINING;
1144 }
1145 else if (sz - i >= 5 && !strncmp(str + i, "async", 5)) {
1146 i += 5;
1147 if (sz == i || isspace(str[i]) || str[i] == ',')
1148 flags |= SPOE_APPCTX_FL_ASYNC;
1149 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001150 else if (sz - i >= 13 && !strncmp(str + i, "fragmentation", 13)) {
1151 i += 13;
1152 if (sz == i || isspace(str[i]) || str[i] == ',')
1153 flags |= SPOE_APPCTX_FL_FRAGMENTATION;
1154 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001155
1156 if (sz == i || (delim = memchr(str + i, ',', sz-i)) == NULL)
1157 break;
1158 i = (delim - str) + 1;
1159 }
1160 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001161 else {
1162 /* Silently ignore unknown item */
1163 if ((i = skip_spoe_data(frame+idx, frame+size)) == -1) {
1164 spoe_status_code = SPOE_FRM_ERR_INVALID;
1165 return -1;
1166 }
1167 idx += i;
1168 }
1169 }
1170
1171 /* Final checks */
1172 if (!vsn) {
1173 spoe_status_code = SPOE_FRM_ERR_NO_VSN;
1174 return -1;
1175 }
1176 if (!max_frame_size) {
1177 spoe_status_code = SPOE_FRM_ERR_NO_FRAME_SIZE;
1178 return -1;
1179 }
1180
Christopher Faulet42bfa462017-01-04 14:14:19 +01001181 SPOE_APPCTX(appctx)->version = (unsigned int)vsn;
1182 SPOE_APPCTX(appctx)->max_frame_size = (unsigned int)max_frame_size;
1183 SPOE_APPCTX(appctx)->flags |= flags;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001184 return idx;
1185}
1186
1187/* Decode DISCONNECT frame sent by an agent. It returns the number of by read
1188 * bytes on success, 0 if the frame can be ignored and -1 if an error
1189 * occurred. */
1190static int
1191handle_spoe_agentdiscon_frame(struct appctx *appctx, char *frame, size_t size)
1192{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001193 int i, idx = 0;
1194 unsigned int flags;
1195 size_t min_size = (7 /* TYPE + METADATA */
1196 + 1 + SLEN(STATUS_CODE_KEY) + 1 + 1
1197 + 1 + SLEN(MSG_KEY) + 1 + 1);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001198
1199 /* Check frame type */
1200 if (frame[idx++] != SPOE_FRM_T_AGENT_DISCON)
1201 return 0;
1202
1203 if (size < min_size) {
1204 spoe_status_code = SPOE_FRM_ERR_INVALID;
1205 return -1;
1206 }
1207
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001208 /* Retrieve flags */
1209 memcpy((char *)&flags, frame+idx, 4);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001210 idx += 4;
1211
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001212 /* Fragmentation is not supported for DISCONNECT frame */
1213 if (!(flags & SPOE_FRM_FL_FIN)) {
1214 spoe_status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
1215 return -1;
1216 }
1217
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001218 /* stream-id and frame-id must be cleared */
1219 if (frame[idx] != 0 || frame[idx+1] != 0) {
1220 spoe_status_code = SPOE_FRM_ERR_INVALID;
1221 return -1;
1222 }
1223 idx += 2;
1224
1225 /* There are 2 mandatory items: "status-code" and "message" */
1226
1227 /* Loop on K/V items */
1228 while (idx < size) {
1229 char *str;
1230 uint64_t sz;
1231
1232 /* Decode the item key */
1233 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1234 if (str == NULL) {
1235 spoe_status_code = SPOE_FRM_ERR_INVALID;
1236 return -1;
1237 }
1238
1239 /* Check "status-code" K/V item */
1240 if (!memcmp(str, STATUS_CODE_KEY, sz)) {
1241 int type;
1242
1243 /* The value must be an integer */
1244 type = frame[idx++];
1245 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1246 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1247 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1248 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
1249 spoe_status_code = SPOE_FRM_ERR_INVALID;
1250 return -1;
1251 }
1252 if ((i = decode_spoe_varint(frame+idx, frame+size, &sz)) == -1) {
1253 spoe_status_code = SPOE_FRM_ERR_INVALID;
1254 return -1;
1255 }
1256 idx += i;
1257 spoe_status_code = sz;
1258 }
1259
1260 /* Check "message" K/V item */
1261 else if (sz && !memcmp(str, MSG_KEY, sz)) {
1262 /* The value must be a string */
1263 if ((frame[idx++] & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1264 spoe_status_code = SPOE_FRM_ERR_INVALID;
1265 return -1;
1266 }
1267 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1268 if (str == NULL || sz > 255) {
1269 spoe_status_code = SPOE_FRM_ERR_INVALID;
1270 return -1;
1271 }
1272 memcpy(spoe_reason, str, sz);
1273 spoe_reason[sz] = 0;
1274 }
1275 else {
1276 /* Silently ignore unknown item */
1277 if ((i = skip_spoe_data(frame+idx, frame+size)) == -1) {
1278 spoe_status_code = SPOE_FRM_ERR_INVALID;
1279 return -1;
1280 }
1281 idx += i;
1282 }
1283 }
1284
1285 return idx;
1286}
1287
1288
Christopher Fauleta1cda022016-12-21 08:58:06 +01001289/* Decode ACK frame sent by an agent. It returns the number of read bytes on
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001290 * success, 0 if the frame can be ignored and -1 if an error occurred. */
1291static int
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001292handle_spoe_agentack_frame(struct appctx *appctx, struct spoe_context **ctx,
1293 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001294{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001295 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
1296 uint64_t stream_id, frame_id;
1297 int i, idx = 0;
1298 unsigned int flags;
1299 size_t min_size = (7 /* TYPE + METADATA */);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001300
1301 /* Check frame type */
1302 if (frame[idx++] != SPOE_FRM_T_AGENT_ACK)
1303 return 0;
1304
1305 if (size < min_size) {
1306 spoe_status_code = SPOE_FRM_ERR_INVALID;
1307 return -1;
1308 }
1309
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001310 /* Retrieve flags */
1311 memcpy((char *)&flags, frame+idx, 4);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001312 idx += 4;
1313
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001314 /* Fragmentation is not supported for now */
1315 if (!(flags & SPOE_FRM_FL_FIN)) {
1316 spoe_status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
1317 return -1;
1318 }
1319
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001320 /* Get the stream-id and the frame-id */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001321 if ((i = decode_spoe_varint(frame+idx, frame+size, &stream_id)) == -1)
1322 return 0;
1323 idx += i;
1324 if ((i= decode_spoe_varint(frame+idx, frame+size, &frame_id)) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001325 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001326 idx += i;
1327
Christopher Faulet42bfa462017-01-04 14:14:19 +01001328 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001329 list_for_each_entry((*ctx), &agent->waiting_queue, list) {
1330 if ((*ctx)->stream_id == (unsigned int)stream_id &&
1331 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001332 goto found;
1333 }
1334 }
1335 else {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001336 list_for_each_entry((*ctx), &SPOE_APPCTX(appctx)->waiting_queue, list) {
1337 if ((*ctx)->stream_id == (unsigned int)stream_id &&
1338 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001339 goto found;
1340 }
1341 }
1342
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001343 /* FIXME: check if ABRT bit is set for a unfinished fragmented frame */
1344
Christopher Fauleta1cda022016-12-21 08:58:06 +01001345 /* No Stream found, ignore the frame */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001346 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p - Ignore ACK frame"
1347 " - stream-id=%u - frame-id=%u\n",
1348 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1349 __FUNCTION__, appctx,
1350 (unsigned int)stream_id, (unsigned int)frame_id);
1351
1352 *ctx = NULL;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001353 return 0;
1354
1355 found:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001356 if (!acquire_spoe_buffer(&SPOE_APPCTX(appctx)->buffer, &SPOE_APPCTX(appctx)->buffer_wait)) {
1357 *ctx = NULL;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001358 return 1; /* Retry later */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001359 }
Christopher Faulet4596fb72017-01-11 14:05:19 +01001360
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001361 /* Copy encoded actions */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001362 memcpy(SPOE_APPCTX(appctx)->buffer->p, frame+idx, size-idx);
1363 SPOE_APPCTX(appctx)->buffer->i = size-idx;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001364
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001365 /* Transfer the buffer ownership to the SPOE context */
1366 (*ctx)->buffer = SPOE_APPCTX(appctx)->buffer;
1367 SPOE_APPCTX(appctx)->buffer = &buf_empty;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001368
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001369 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1370 " - ACK frame received - ctx=%p - stream-id=%u - frame-id=%u\n",
1371 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1372 __FUNCTION__, appctx,
1373 *ctx, (*ctx)->stream_id, (*ctx)->frame_id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001374 return idx;
1375}
1376
Christopher Fauletba7bc162016-11-07 21:07:38 +01001377/* This function is used in cfgparse.c and declared in proto/checks.h. It
1378 * prepare the request to send to agents during a healthcheck. It returns 0 on
1379 * success and -1 if an error occurred. */
1380int
1381prepare_spoe_healthcheck_request(char **req, int *len)
1382{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001383 struct appctx appctx;
1384 struct spoe_appctx spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001385 char *frame, buf[MAX_FRAME_SIZE+4];
Christopher Faulet42bfa462017-01-04 14:14:19 +01001386 unsigned int framesz;
1387 int idx;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001388
Christopher Faulet42bfa462017-01-04 14:14:19 +01001389 memset(&appctx, 0, sizeof(appctx));
1390 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001391 memset(buf, 0, sizeof(buf));
Christopher Faulet42bfa462017-01-04 14:14:19 +01001392
1393 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001394 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001395
1396 frame = buf+4;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001397 idx = prepare_spoe_hahello_frame(&appctx, frame, MAX_FRAME_SIZE);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001398 if (idx <= 0)
1399 return -1;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001400 if (idx + SLEN(HEALTHCHECK_KEY) + 1 > MAX_FRAME_SIZE)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001401 return -1;
1402
1403 /* "healthcheck" K/V item */
1404 idx += encode_spoe_string(HEALTHCHECK_KEY, SLEN(HEALTHCHECK_KEY), frame+idx);
1405 frame[idx++] = (SPOE_DATA_T_BOOL | SPOE_DATA_FL_TRUE);
1406
1407 framesz = htonl(idx);
1408 memcpy(buf, (char *)&framesz, 4);
1409
1410 if ((*req = malloc(idx+4)) == NULL)
1411 return -1;
1412 memcpy(*req, buf, idx+4);
1413 *len = idx+4;
1414 return 0;
1415}
1416
1417/* This function is used in checks.c and declared in proto/checks.h. It decode
1418 * the response received from an agent during a healthcheck. It returns 0 on
1419 * success and -1 if an error occurred. */
1420int
1421handle_spoe_healthcheck_response(char *frame, size_t size, char *err, int errlen)
1422{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001423 struct appctx appctx;
1424 struct spoe_appctx spoe_appctx;
1425 int r;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001426
Christopher Faulet42bfa462017-01-04 14:14:19 +01001427 memset(&appctx, 0, sizeof(appctx));
1428 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001429
Christopher Faulet42bfa462017-01-04 14:14:19 +01001430 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001431 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001432
1433 if (handle_spoe_agentdiscon_frame(&appctx, frame, size) != 0)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001434 goto error;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001435 if ((r = handle_spoe_agenthello_frame(&appctx, frame, size)) <= 0) {
Christopher Fauletba7bc162016-11-07 21:07:38 +01001436 if (r == 0)
1437 spoe_status_code = SPOE_FRM_ERR_INVALID;
1438 goto error;
1439 }
1440
1441 return 0;
1442
1443 error:
1444 if (spoe_status_code >= SPOE_FRM_ERRS)
1445 spoe_status_code = SPOE_FRM_ERR_UNKNOWN;
1446 strncpy(err, spoe_frm_err_reasons[spoe_status_code], errlen);
1447 return -1;
1448}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001449
Christopher Fauleta1cda022016-12-21 08:58:06 +01001450/* Send a SPOE frame to an agent. It returns -1 when an error occurred, 0 when
1451 * the frame can be ignored, 1 to retry later, and the frame legnth on
1452 * success. */
1453static int
1454send_spoe_frame(struct appctx *appctx, char *buf, size_t framesz)
1455{
1456 struct stream_interface *si = appctx->owner;
1457 int ret;
1458 uint32_t netint;
1459
1460 if (si_ic(si)->buf == &buf_empty)
1461 return 1;
1462
1463 netint = htonl(framesz);
1464 memcpy(buf, (char *)&netint, 4);
1465 ret = bi_putblk(si_ic(si), buf, framesz+4);
1466
1467 if (ret <= 0) {
1468 if (ret == -1)
1469 return 1; /* retry */
Christopher Fauletb067b062017-01-04 16:39:11 +01001470 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001471 return -1; /* error */
1472 }
1473 return framesz;
1474}
1475
1476/* Receive a SPOE frame from an agent. It return -1 when an error occurred, 0
1477 * when the frame can be ignored, 1 to retry later and the frame length on
1478 * success. */
1479static int
1480recv_spoe_frame(struct appctx *appctx, char *buf, size_t framesz)
1481{
1482 struct stream_interface *si = appctx->owner;
1483 int ret;
1484 uint32_t netint;
1485
1486 if (si_oc(si)->buf == &buf_empty)
1487 return 1;
1488
1489 ret = bo_getblk(si_oc(si), (char *)&netint, 4, 0);
1490 if (ret > 0) {
1491 framesz = ntohl(netint);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001492 if (framesz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001493 spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
1494 return -1;
1495 }
1496 ret = bo_getblk(si_oc(si), trash.str, framesz, 4);
1497 }
1498 if (ret <= 0) {
1499 if (ret == 0)
1500 return 1; /* retry */
1501 spoe_status_code = SPOE_FRM_ERR_IO;
1502 return -1; /* error */
1503 }
1504 return framesz;
1505}
1506
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001507/********************************************************************
1508 * Functions that manage the SPOE applet
1509 ********************************************************************/
Christopher Faulet4596fb72017-01-11 14:05:19 +01001510static int
1511wakeup_spoe_appctx(struct appctx *appctx)
1512{
1513 si_applet_want_get(appctx->owner);
1514 si_applet_want_put(appctx->owner);
1515 appctx_wakeup(appctx);
1516 return 1;
1517}
1518
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001519/* Callback function that catches applet timeouts. If a timeout occurred, we set
1520 * <appctx->st1> flag and the SPOE applet is woken up. */
1521static struct task *
1522process_spoe_applet(struct task * task)
1523{
1524 struct appctx *appctx = task->context;
1525
1526 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1527 if (tick_is_expired(task->expire, now_ms)) {
1528 task->expire = TICK_ETERNITY;
1529 appctx->st1 = SPOE_APPCTX_ERR_TOUT;
1530 }
Christopher Faulet4596fb72017-01-11 14:05:19 +01001531 wakeup_spoe_appctx(appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001532 return task;
1533}
1534
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001535/* Callback function that releases a SPOE applet. This happens when the
1536 * connection with the agent is closed. */
1537static void
1538release_spoe_applet(struct appctx *appctx)
1539{
1540 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001541 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001542 struct spoe_context *ctx, *back;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001543 struct spoe_appctx *spoe_appctx;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001544
1545 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p\n",
1546 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1547 __FUNCTION__, appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001548
Christopher Fauleta1cda022016-12-21 08:58:06 +01001549 agent->applets_act--;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001550 if (!LIST_ISEMPTY(&SPOE_APPCTX(appctx)->list)) {
1551 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1552 LIST_INIT(&SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001553 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001554
1555 if (appctx->st0 != SPOE_APPCTX_ST_END) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001556 if (appctx->st0 == SPOE_APPCTX_ST_IDLE)
1557 agent->applets_idle--;
1558
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001559 si_shutw(si);
1560 si_shutr(si);
1561 si_ic(si)->flags |= CF_READ_NULL;
1562 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Fauletb067b062017-01-04 16:39:11 +01001563 if (SPOE_APPCTX(appctx)->status_code == SPOE_FRM_ERR_NONE)
1564 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001565 }
1566
Christopher Faulet42bfa462017-01-04 14:14:19 +01001567 if (SPOE_APPCTX(appctx)->task) {
1568 task_delete(SPOE_APPCTX(appctx)->task);
1569 task_free(SPOE_APPCTX(appctx)->task);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001570 }
1571
Christopher Faulet42bfa462017-01-04 14:14:19 +01001572 list_for_each_entry_safe(ctx, back, &SPOE_APPCTX(appctx)->waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001573 LIST_DEL(&ctx->list);
1574 LIST_INIT(&ctx->list);
1575 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001576 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001577 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001578 }
1579
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001580 if (SPOE_APPCTX(appctx)->frag_ctx.ctx) {
1581 ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
1582 ctx->frag_ctx.spoe_appctx = NULL;
1583 ctx->state = SPOE_CTX_ST_ERROR;
1584 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
1585 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1586 }
1587
Christopher Faulet4596fb72017-01-11 14:05:19 +01001588 release_spoe_buffer(&SPOE_APPCTX(appctx)->buffer, &SPOE_APPCTX(appctx)->buffer_wait);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001589 pool_free2(pool2_spoe_appctx, SPOE_APPCTX(appctx));
1590
Christopher Fauleta1cda022016-12-21 08:58:06 +01001591 if (!LIST_ISEMPTY(&agent->applets))
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001592 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001593
Christopher Fauleta1cda022016-12-21 08:58:06 +01001594 list_for_each_entry_safe(ctx, back, &agent->sending_queue, list) {
1595 LIST_DEL(&ctx->list);
1596 LIST_INIT(&ctx->list);
1597 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001598 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001599 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001600 }
1601
Christopher Fauleta1cda022016-12-21 08:58:06 +01001602 list_for_each_entry_safe(ctx, back, &agent->waiting_queue, list) {
1603 LIST_DEL(&ctx->list);
1604 LIST_INIT(&ctx->list);
1605 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001606 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001607 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1608 }
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001609
1610 end:
1611 /* Update runtinme agent info */
1612 agent->frame_size = agent->max_frame_size;
1613 list_for_each_entry(spoe_appctx, &agent->applets, list)
1614 agent->frame_size = MIN(spoe_appctx->max_frame_size, agent->frame_size);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001615}
1616
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001617static int
Christopher Fauleta1cda022016-12-21 08:58:06 +01001618handle_connect_spoe_applet(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001619{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001620 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001621 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001622 char *frame = trash.str;
1623 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001624
Christopher Fauleta1cda022016-12-21 08:58:06 +01001625 if (si->state <= SI_ST_CON) {
1626 si_applet_want_put(si);
1627 task_wakeup(si_strm(si)->task, TASK_WOKEN_MSG);
1628 goto stop;
1629 }
Christopher Fauletb067b062017-01-04 16:39:11 +01001630 if (si->state != SI_ST_EST) {
1631 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001632 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001633 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001634
Christopher Fauleta1cda022016-12-21 08:58:06 +01001635 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1636 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p - Connection timed out\n",
1637 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__, appctx);
Christopher Fauletb067b062017-01-04 16:39:11 +01001638 spoe_status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001639 goto exit;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001640 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001641
Christopher Faulet42bfa462017-01-04 14:14:19 +01001642 if (SPOE_APPCTX(appctx)->task->expire == TICK_ETERNITY)
1643 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.hello);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001644
Christopher Faulet42bfa462017-01-04 14:14:19 +01001645 ret = prepare_spoe_hahello_frame(appctx, frame+4, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001646 if (ret > 1)
1647 ret = send_spoe_frame(appctx, frame, ret);
1648
1649 switch (ret) {
1650 case -1: /* error */
1651 goto exit;
1652
1653 case 0: /* ignore => an error, cannot be ignored */
1654 goto exit;
1655
1656 case 1: /* retry later */
1657 si_applet_cant_put(si);
1658 goto stop;
1659
1660 default: /* CONNECT frame successfully sent */
1661 appctx->st0 = SPOE_APPCTX_ST_CONNECTING;
1662 goto next;
1663 }
1664
1665 next:
1666 return 0;
1667 stop:
1668 return 1;
1669 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001670 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001671 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1672 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001673}
1674
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001675static int
Christopher Fauleta1cda022016-12-21 08:58:06 +01001676handle_connecting_spoe_applet(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001677{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001678 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001679 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001680 char *frame = trash.str;
1681 int ret, framesz = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001682
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001683
Christopher Fauletb067b062017-01-04 16:39:11 +01001684 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1685 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001686 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001687 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001688
Christopher Fauleta1cda022016-12-21 08:58:06 +01001689 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1690 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p - Connection timed out\n",
1691 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__, appctx);
Christopher Fauletb067b062017-01-04 16:39:11 +01001692 spoe_status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001693 goto exit;
1694 }
1695
Christopher Faulet42bfa462017-01-04 14:14:19 +01001696 ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001697 if (ret > 1) {
1698 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1699 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1700 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001701 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001702 framesz = ret;
1703 ret = handle_spoe_agenthello_frame(appctx, frame, framesz);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001704 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001705
Christopher Fauleta1cda022016-12-21 08:58:06 +01001706 switch (ret) {
1707 case -1: /* error */
1708 if (framesz)
1709 bo_skip(si_oc(si), framesz+4);
1710 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1711 goto next;
1712
1713 case 0: /* ignore */
1714 if (framesz)
1715 bo_skip(si_oc(si), framesz+4);
1716 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1717 goto next;
1718
1719 case 1: /* retry later */
1720 goto stop;
1721
1722 default:
1723 /* hello handshake is finished, set the idle timeout,
1724 * Add the appctx in the agent cache, decrease the
1725 * number of new applets and wake up waiting streams. */
1726 if (framesz)
1727 bo_skip(si_oc(si), framesz+4);
1728 agent->applets_idle++;
1729 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001730 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1731 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001732
1733 /* Update runtinme agent info */
1734 agent->frame_size = MIN(SPOE_APPCTX(appctx)->max_frame_size, agent->frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001735 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001736 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001737
Christopher Fauleta1cda022016-12-21 08:58:06 +01001738 next:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001739 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001740 return 0;
1741 stop:
1742 return 1;
1743 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001744 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001745 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1746 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001747}
1748
Christopher Fauleta1cda022016-12-21 08:58:06 +01001749static int
1750handle_processing_spoe_applet(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001751{
1752 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001753 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001754 struct spoe_context *ctx = NULL;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001755 char *frame = trash.str;
1756 unsigned int fpa = 0;
1757 int ret, framesz = 0, skip_sending = 0, skip_receiving = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001758
Christopher Fauletb067b062017-01-04 16:39:11 +01001759 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1760 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001761 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001762 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001763
Christopher Fauleta1cda022016-12-21 08:58:06 +01001764 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1765 spoe_status_code = SPOE_FRM_ERR_TOUT;
1766 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1767 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1768 goto next;
1769 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001770
Christopher Fauleta1cda022016-12-21 08:58:06 +01001771 process:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001772 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1773 " - process: fpa=%u/%u - skip_sending=%d - skip_receiving=%d"
1774 " - appctx-state=%s\n",
1775 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1776 __FUNCTION__, appctx, fpa, agent->max_fpa,
1777 skip_sending, skip_receiving, spoe_appctx_state_str[appctx->st0]);
1778
Christopher Fauleta1cda022016-12-21 08:58:06 +01001779 if (fpa > agent->max_fpa || (skip_sending && skip_receiving))
1780 goto stop;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001781 else if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001782 if (skip_receiving)
1783 goto stop;
1784 goto recv_frame;
1785 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001786 else if (skip_sending)
1787 goto recv_frame;
1788 else if (appctx->st0 == SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY) {
1789 ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
1790 goto send_frame;
1791 }
1792 else if (LIST_ISEMPTY(&agent->sending_queue)) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001793 skip_sending = 1;
1794 goto recv_frame;
1795 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001796 ctx = LIST_NEXT(&agent->sending_queue, typeof(ctx), list);
Christopher Faulet4596fb72017-01-11 14:05:19 +01001797
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001798 send_frame:
Christopher Faulet4596fb72017-01-11 14:05:19 +01001799 /* Transfer the buffer ownership to the SPOE appctx */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001800 if (ctx) {
1801 SPOE_APPCTX(appctx)->buffer = ctx->buffer;
1802 ctx->buffer = &buf_empty;
1803 }
Christopher Faulet4596fb72017-01-11 14:05:19 +01001804
Christopher Faulet42bfa462017-01-04 14:14:19 +01001805 ret = prepare_spoe_hanotify_frame(appctx, ctx, frame+4, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001806 if (ret > 1)
1807 ret = send_spoe_frame(appctx, frame, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001808
Christopher Fauleta1cda022016-12-21 08:58:06 +01001809 switch (ret) {
1810 case -1: /* error */
1811 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1812 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001813
Christopher Fauleta1cda022016-12-21 08:58:06 +01001814 case 0: /* ignore */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001815 release_spoe_buffer(&SPOE_APPCTX(appctx)->buffer, &SPOE_APPCTX(appctx)->buffer_wait);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001816 agent->sending_rate++;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001817 fpa++;
1818
1819 LIST_DEL(&ctx->list);
1820 LIST_INIT(&ctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001821 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001822 ctx->status_code = (spoe_status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001823 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001824 break;
1825
Christopher Fauleta1cda022016-12-21 08:58:06 +01001826 case 1: /* retry */
1827 si_applet_cant_put(si);
1828 skip_sending = 1;
1829 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001830
Christopher Fauleta1cda022016-12-21 08:58:06 +01001831 default:
Christopher Faulet4596fb72017-01-11 14:05:19 +01001832 release_spoe_buffer(&SPOE_APPCTX(appctx)->buffer, &SPOE_APPCTX(appctx)->buffer_wait);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001833 agent->sending_rate++;
1834 fpa++;
1835
1836 if (ctx == NULL) {
1837 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1838 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1839 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1840 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1841 break;
1842 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001843 LIST_DEL(&ctx->list);
1844 LIST_INIT(&ctx->list);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001845
1846 if (ctx->flags & SPOE_CTX_FL_FRAGMENTED) {
1847 if (ctx->frag_ctx.flags & SPOE_FRM_FL_FIN) {
1848 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
1849 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1850 LIST_ADDQ(&agent->waiting_queue, &ctx->list);
1851 }
1852 else if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PIPELINING) {
1853 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1854 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1855 }
1856 else {
1857 appctx->st0 = SPOE_APPCTX_ST_WAITING_SYNC_ACK;
1858 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1859 }
1860 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1861 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1862 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1863
1864 ctx->frag_ctx.spoe_appctx = NULL;
1865 ctx->state = SPOE_CTX_ST_WAITING_ACK;
1866 }
1867 else {
1868 appctx->st0 = SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY;
1869 SPOE_APPCTX(appctx)->frag_ctx.ctx = ctx;
1870 SPOE_APPCTX(appctx)->frag_ctx.cursid = ctx->stream_id;
1871 SPOE_APPCTX(appctx)->frag_ctx.curfid = ctx->frame_id;
1872
1873 ctx->frag_ctx.spoe_appctx = SPOE_APPCTX(appctx);
1874 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
1875 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1876 skip_sending = 1;
1877 }
1878 }
1879 else {
1880 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
1881 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1882 LIST_ADDQ(&agent->waiting_queue, &ctx->list);
1883 }
1884 else if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PIPELINING) {
1885 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1886 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1887 }
1888 else {
1889 appctx->st0 = SPOE_APPCTX_ST_WAITING_SYNC_ACK;
1890 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1891 }
1892
1893 ctx->state = SPOE_CTX_ST_WAITING_ACK;
1894 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001895 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001896
Christopher Fauleta1cda022016-12-21 08:58:06 +01001897 if (fpa > agent->max_fpa)
1898 goto stop;
1899
1900 recv_frame:
1901 if (skip_receiving)
1902 goto process;
1903
1904 framesz = 0;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001905 ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001906 if (ret > 1) {
1907 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1908 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1909 goto next;
1910 }
1911 framesz = ret;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001912 ret = handle_spoe_agentack_frame(appctx, &ctx, frame, framesz);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001913 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001914 switch (ret) {
1915 case -1: /* error */
1916 if (framesz)
1917 bo_skip(si_oc(si), framesz+4);
1918 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1919 goto next;
1920
1921 case 0: /* ignore */
1922 if (framesz)
1923 bo_skip(si_oc(si), framesz+4);
1924 fpa++;
1925 break;
1926
1927 case 1: /* retry */
1928 skip_receiving = 1;
1929 break;
1930
1931 default:
1932 if (framesz)
1933 bo_skip(si_oc(si), framesz+4);
1934 fpa++;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001935
1936 LIST_DEL(&ctx->list);
1937 LIST_INIT(&ctx->list);
1938
1939 if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK)
1940 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1941
1942 ctx->state = SPOE_CTX_ST_DONE;
1943 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001944 }
1945 goto process;
1946
1947 next:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001948 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001949 return 0;
1950 stop:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001951 if (appctx->st0 == SPOE_APPCTX_ST_PROCESSING) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001952 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001953 agent->applets_idle++;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001954 }
Christopher Faulet42bfa462017-01-04 14:14:19 +01001955 if (fpa || (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PERSIST)) {
1956 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1957 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001958 if (fpa)
Christopher Faulet42bfa462017-01-04 14:14:19 +01001959 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001960 }
1961 return 1;
1962
1963 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001964 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001965 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1966 return 0;
1967}
1968
1969static int
1970handle_disconnect_spoe_applet(struct appctx *appctx)
1971{
1972 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001973 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001974 char *frame = trash.str;
1975 int ret;
1976
Christopher Fauletb067b062017-01-04 16:39:11 +01001977 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
1978
Christopher Fauleta1cda022016-12-21 08:58:06 +01001979 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
1980 goto exit;
1981
1982 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT)
1983 goto exit;
1984
Christopher Faulet42bfa462017-01-04 14:14:19 +01001985 ret = prepare_spoe_hadiscon_frame(appctx, frame+4, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001986 if (ret > 1)
1987 ret = send_spoe_frame(appctx, frame, ret);
1988
1989 switch (ret) {
1990 case -1: /* error */
1991 goto exit;
1992
1993 case 0: /* ignore */
1994 goto exit;
1995
1996 case 1: /* retry */
1997 si_applet_cant_put(si);
1998 goto stop;
1999
2000 default:
2001 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2002 " - disconnected by HAProxy (%d): %s\n",
2003 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2004 __FUNCTION__, appctx, spoe_status_code,
2005 spoe_frm_err_reasons[spoe_status_code]);
2006
2007 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
2008 goto next;
2009 }
2010
2011 next:
Christopher Faulet42bfa462017-01-04 14:14:19 +01002012 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002013 return 0;
2014 stop:
2015 return 1;
2016 exit:
2017 appctx->st0 = SPOE_APPCTX_ST_EXIT;
2018 return 0;
2019}
2020
2021static int
2022handle_disconnecting_spoe_applet(struct appctx *appctx)
2023{
2024 struct stream_interface *si = appctx->owner;
2025 char *frame = trash.str;
2026 int ret, framesz = 0;
2027
Christopher Fauletb067b062017-01-04 16:39:11 +01002028 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
2029 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002030 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01002031 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002032
Christopher Fauletb067b062017-01-04 16:39:11 +01002033 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
2034 spoe_status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002035 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01002036 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002037
2038 framesz = 0;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002039 ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002040 if (ret > 1) {
2041 framesz = ret;
2042 ret = handle_spoe_agentdiscon_frame(appctx, frame, framesz);
2043 }
2044
2045 switch (ret) {
2046 case -1: /* error */
2047 if (framesz)
2048 bo_skip(si_oc(si), framesz+4);
2049 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2050 " - error on frame (%s)\n",
2051 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01002052 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01002053 __FUNCTION__, appctx,
2054 spoe_frm_err_reasons[spoe_status_code]);
2055 goto exit;
2056
2057 case 0: /* ignore */
2058 if (framesz)
2059 bo_skip(si_oc(si), framesz+4);
2060 goto next;
2061
2062 case 1: /* retry */
2063 goto stop;
2064
2065 default:
2066 if (framesz)
2067 bo_skip(si_oc(si), framesz+4);
2068 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2069 " - disconnected by peer (%d): %s\n",
2070 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01002071 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01002072 __FUNCTION__, appctx, spoe_status_code,
2073 spoe_reason);
2074 goto exit;
2075 }
2076
2077 next:
2078 return 0;
2079 stop:
2080 return 1;
2081 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01002082 if (SPOE_APPCTX(appctx)->status_code == SPOE_FRM_ERR_NONE)
2083 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002084 appctx->st0 = SPOE_APPCTX_ST_EXIT;
2085 return 0;
2086}
2087
2088/* I/O Handler processing messages exchanged with the agent */
2089static void
2090handle_spoe_applet(struct appctx *appctx)
2091{
2092 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002093 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002094
Christopher Fauletb067b062017-01-04 16:39:11 +01002095 spoe_status_code = SPOE_FRM_ERR_NONE;
2096
Christopher Fauleta1cda022016-12-21 08:58:06 +01002097 switchstate:
2098 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
2099 " - appctx-state=%s\n",
2100 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2101 __FUNCTION__, appctx, spoe_appctx_state_str[appctx->st0]);
2102
2103 switch (appctx->st0) {
2104 case SPOE_APPCTX_ST_CONNECT:
Christopher Fauleta1cda022016-12-21 08:58:06 +01002105 if (handle_connect_spoe_applet(appctx))
2106 goto out;
2107 goto switchstate;
2108
2109 case SPOE_APPCTX_ST_CONNECTING:
2110 if (handle_connecting_spoe_applet(appctx))
2111 goto out;
2112 goto switchstate;
2113
2114 case SPOE_APPCTX_ST_IDLE:
2115 if (stopping &&
2116 LIST_ISEMPTY(&agent->sending_queue) &&
Christopher Faulet42bfa462017-01-04 14:14:19 +01002117 LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
2118 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002119 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002120 goto switchstate;
2121 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002122 agent->applets_idle--;
2123 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
2124 /* fall through */
2125
2126 case SPOE_APPCTX_ST_PROCESSING:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002127 case SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY:
2128 case SPOE_APPCTX_ST_WAITING_SYNC_ACK:
Christopher Fauleta1cda022016-12-21 08:58:06 +01002129 if (handle_processing_spoe_applet(appctx))
2130 goto out;
2131 goto switchstate;
2132
2133 case SPOE_APPCTX_ST_DISCONNECT:
2134 if (handle_disconnect_spoe_applet(appctx))
2135 goto out;
2136 goto switchstate;
2137
2138 case SPOE_APPCTX_ST_DISCONNECTING:
2139 if (handle_disconnecting_spoe_applet(appctx))
2140 goto out;
2141 goto switchstate;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002142
2143 case SPOE_APPCTX_ST_EXIT:
2144 si_shutw(si);
2145 si_shutr(si);
2146 si_ic(si)->flags |= CF_READ_NULL;
2147 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002148 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002149 /* fall through */
2150
2151 case SPOE_APPCTX_ST_END:
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002152 return;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002153 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002154 out:
Christopher Faulet42bfa462017-01-04 14:14:19 +01002155 if (SPOE_APPCTX(appctx)->task->expire != TICK_ETERNITY)
2156 task_queue(SPOE_APPCTX(appctx)->task);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002157 si_oc(si)->flags |= CF_READ_DONTWAIT;
2158 task_wakeup(si_strm(si)->task, TASK_WOKEN_IO);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002159}
2160
2161struct applet spoe_applet = {
2162 .obj_type = OBJ_TYPE_APPLET,
2163 .name = "<SPOE>", /* used for logging */
2164 .fct = handle_spoe_applet,
2165 .release = release_spoe_applet,
2166};
2167
2168/* Create a SPOE applet. On success, the created applet is returned, else
2169 * NULL. */
2170static struct appctx *
2171create_spoe_appctx(struct spoe_config *conf)
2172{
2173 struct appctx *appctx;
2174 struct session *sess;
2175 struct task *task;
2176 struct stream *strm;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002177
2178 if ((appctx = appctx_new(&spoe_applet)) == NULL)
2179 goto out_error;
2180
Christopher Faulet42bfa462017-01-04 14:14:19 +01002181 appctx->ctx.spoe.ptr = pool_alloc_dirty(pool2_spoe_appctx);
2182 if (SPOE_APPCTX(appctx) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002183 goto out_free_appctx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002184 memset(appctx->ctx.spoe.ptr, 0, pool2_spoe_appctx->size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002185
Christopher Faulet42bfa462017-01-04 14:14:19 +01002186 appctx->st0 = SPOE_APPCTX_ST_CONNECT;
2187 if ((SPOE_APPCTX(appctx)->task = task_new()) == NULL)
2188 goto out_free_spoe_appctx;
2189
2190 SPOE_APPCTX(appctx)->owner = appctx;
2191 SPOE_APPCTX(appctx)->task->process = process_spoe_applet;
2192 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
2193 SPOE_APPCTX(appctx)->task->context = appctx;
2194 SPOE_APPCTX(appctx)->agent = conf->agent;
2195 SPOE_APPCTX(appctx)->version = 0;
2196 SPOE_APPCTX(appctx)->max_frame_size = conf->agent->max_frame_size;
2197 SPOE_APPCTX(appctx)->flags = 0;
Christopher Fauletb067b062017-01-04 16:39:11 +01002198 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
Christopher Faulet4596fb72017-01-11 14:05:19 +01002199 SPOE_APPCTX(appctx)->buffer = &buf_empty;
2200
2201 LIST_INIT(&SPOE_APPCTX(appctx)->buffer_wait.list);
2202 SPOE_APPCTX(appctx)->buffer_wait.target = appctx;
2203 SPOE_APPCTX(appctx)->buffer_wait.wakeup_cb = (int (*)(void *))wakeup_spoe_appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002204
2205 LIST_INIT(&SPOE_APPCTX(appctx)->list);
2206 LIST_INIT(&SPOE_APPCTX(appctx)->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002207
Willy Tarreau5820a362016-12-22 15:59:02 +01002208 sess = session_new(&conf->agent_fe, NULL, &appctx->obj_type);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002209 if (!sess)
2210 goto out_free_spoe;
2211
2212 if ((task = task_new()) == NULL)
2213 goto out_free_sess;
2214
2215 if ((strm = stream_new(sess, task, &appctx->obj_type)) == NULL)
2216 goto out_free_task;
2217
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002218 stream_set_backend(strm, conf->agent->b.be);
2219
2220 /* applet is waiting for data */
2221 si_applet_cant_get(&strm->si[0]);
2222 appctx_wakeup(appctx);
2223
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002224 strm->do_log = NULL;
2225 strm->res.flags |= CF_READ_DONTWAIT;
2226
2227 conf->agent_fe.feconn++;
2228 jobs++;
2229 totalconn++;
2230
Christopher Faulet42bfa462017-01-04 14:14:19 +01002231 task_wakeup(SPOE_APPCTX(appctx)->task, TASK_WOKEN_INIT);
2232 LIST_ADDQ(&conf->agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002233 conf->agent->applets_act++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002234 return appctx;
2235
2236 /* Error unrolling */
2237 out_free_task:
2238 task_free(task);
2239 out_free_sess:
2240 session_free(sess);
2241 out_free_spoe:
Christopher Faulet42bfa462017-01-04 14:14:19 +01002242 task_free(SPOE_APPCTX(appctx)->task);
2243 out_free_spoe_appctx:
2244 pool_free2(pool2_spoe_appctx, SPOE_APPCTX(appctx));
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002245 out_free_appctx:
2246 appctx_free(appctx);
2247 out_error:
2248 return NULL;
2249}
2250
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002251static int
Christopher Fauleta1cda022016-12-21 08:58:06 +01002252queue_spoe_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002253{
2254 struct spoe_config *conf = FLT_CONF(ctx->filter);
2255 struct spoe_agent *agent = conf->agent;
2256 struct appctx *appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002257 struct spoe_appctx *spoe_appctx;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002258 unsigned int min_applets;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002259
Christopher Fauleta1cda022016-12-21 08:58:06 +01002260 min_applets = min_applets_act(agent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002261
Christopher Fauleta1cda022016-12-21 08:58:06 +01002262 /* Check if we need to create a new SPOE applet or not. */
2263 if (agent->applets_act >= min_applets && agent->applets_idle && agent->sending_rate)
2264 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002265
2266 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002267 " - try to create new SPOE appctx\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002268 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2269 ctx->strm);
2270
Christopher Fauleta1cda022016-12-21 08:58:06 +01002271 /* Do not try to create a new applet if there is no server up for the
2272 * agent's backend. */
2273 if (!agent->b.be->srv_act && !agent->b.be->srv_bck) {
2274 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2275 " - cannot create SPOE appctx: no server up\n",
2276 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2277 __FUNCTION__, ctx->strm);
2278 goto end;
2279 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002280
Christopher Fauleta1cda022016-12-21 08:58:06 +01002281 /* Do not try to create a new applet if we have reached the maximum of
2282 * connection per seconds */
Christopher Faulet48026722016-11-16 15:01:12 +01002283 if (agent->cps_max > 0) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002284 if (!freq_ctr_remain(&agent->conn_per_sec, agent->cps_max, 0)) {
2285 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2286 " - cannot create SPOE appctx: max CPS reached\n",
2287 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2288 __FUNCTION__, ctx->strm);
2289 goto end;
2290 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002291 }
2292
Christopher Fauleta1cda022016-12-21 08:58:06 +01002293 appctx = create_spoe_appctx(conf);
2294 if (appctx == NULL) {
2295 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2296 " - failed to create SPOE appctx\n",
2297 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2298 __FUNCTION__, ctx->strm);
Christopher Faulet72bcc472017-01-04 16:39:41 +01002299 send_log(ctx->strm->be, LOG_EMERG,
2300 "SPOE: [%s] failed to create SPOE applet\n",
2301 agent->id);
2302
Christopher Fauleta1cda022016-12-21 08:58:06 +01002303 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002304 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002305 if (agent->applets_act <= min_applets)
Christopher Faulet42bfa462017-01-04 14:14:19 +01002306 SPOE_APPCTX(appctx)->flags |= SPOE_APPCTX_FL_PERSIST;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002307
Christopher Fauleta1cda022016-12-21 08:58:06 +01002308 /* Increase the per-process number of cumulated connections */
2309 if (agent->cps_max > 0)
2310 update_freq_ctr(&agent->conn_per_sec, 1);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002311
Christopher Fauleta1cda022016-12-21 08:58:06 +01002312 end:
2313 /* The only reason to return an error is when there is no applet */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002314 if (LIST_ISEMPTY(&agent->applets)) {
2315 ctx->status_code = SPOE_CTX_ERR_RES;
2316 return -1;
2317 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002318
Christopher Fauleta1cda022016-12-21 08:58:06 +01002319 /* Add the SPOE context in the sending queue and update all running
2320 * info */
2321 LIST_ADDQ(&agent->sending_queue, &ctx->list);
2322 if (agent->sending_rate)
2323 agent->sending_rate--;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002324
2325 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002326 " - Add stream in sending queue - applets_act=%u - applets_idle=%u"
2327 " - sending_rate=%u\n",
2328 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2329 ctx->strm, agent->applets_act, agent->applets_idle, agent->sending_rate);
Christopher Fauletf7a30922016-11-10 15:04:51 +01002330
Christopher Fauleta1cda022016-12-21 08:58:06 +01002331 /* Finally try to wakeup the first IDLE applet found and move it at the
2332 * end of the list. */
Christopher Faulet42bfa462017-01-04 14:14:19 +01002333 list_for_each_entry(spoe_appctx, &agent->applets, list) {
2334 appctx = spoe_appctx->owner;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002335 if (appctx->st0 == SPOE_APPCTX_ST_IDLE) {
Christopher Faulet4596fb72017-01-11 14:05:19 +01002336 wakeup_spoe_appctx(appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01002337 LIST_DEL(&spoe_appctx->list);
2338 LIST_ADDQ(&agent->applets, &spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002339 break;
2340 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002341 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002342 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002343}
2344
2345/***************************************************************************
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002346 * Functions that encode SPOE messages
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002347 **************************************************************************/
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002348static inline int
2349encode_spoe_arg_string(struct spoe_context *ctx, struct sample *smp,
2350 char *p, size_t max_size)
2351{
2352 struct chunk *chk = &smp->data.u.str;
2353 int idx = 0;
2354
2355 /* Here, we need to know if the sample has already been partially
2356 * encoded. If yes, we only need to encode the remaining, <curoff>
2357 * reprensenting the number of bytes already encoded in previous
2358 * frames. Else, <curoff> == 0 */
2359
2360 if (!ctx->frag_ctx.curoff) {
2361 /* First evaluation of the sample : encode the type (string or
2362 * binary) and check its size against <max_size> */
2363
2364 /* the string/binary length must not exceed 4 Gb. So 5 bytes is
2365 * reserved to encode its size. */
2366 if (max_size < 6)
2367 return 0;
2368
2369 p[idx++] = (smp->data.type == SMP_T_STR) ? SPOE_DATA_T_STR : SPOE_DATA_T_BIN;
2370 max_size -= (idx + 5);
2371
2372 if (chk->len > max_size) {
2373 /* The sample is too big, we will fragment it. <curoff>
2374 * will be updated accordingly. */
2375 idx += encode_frag_spoe_string(chk->str, chk->len, max_size, p+idx);
2376 ctx->frag_ctx.curoff = max_size;
2377 }
2378 else {
2379 /* No fragmentation needed, all the sample is encoded
2380 * and <curoff> remains 0 */
2381 idx += encode_spoe_string(chk->str, chk->len, p+idx);
2382 }
2383 }
2384 else {
2385 /* Continue the sample fragmentation, the type was already set
2386 * in a previous frame. So just do a copy of data. */
2387
2388 idx = chk->len - ctx->frag_ctx.curoff; /* Remaining data */
2389 if (idx > max_size) {
2390 /* The sample still needs to be fragmented. <curoff>
2391 * will be incremented accordingly. */
2392 memcpy(p, chk->str + ctx->frag_ctx.curoff, max_size);
2393 idx = max_size;
2394 ctx->frag_ctx.curoff += max_size;
2395 }
2396 else {
2397 /* Finish the fragmentation. <curoff> will be reset. */
2398 memcpy(p, chk->str + ctx->frag_ctx.curoff, idx);
2399 ctx->frag_ctx.curoff = 0;
2400 }
2401 }
2402 return idx;
2403}
2404
2405static inline int
2406encode_spoe_arg_method(struct spoe_context *ctx, struct sample *smp,
2407 char *p, size_t max_size)
2408{
2409 int idx = 0;
2410
2411 /* method length must not exceed 2288 bytes. So 3 bytes is reserved to
2412 * encode its size. */
2413
2414 if (smp->data.u.meth.meth != HTTP_METH_OTHER) {
2415 const struct http_method_name *meth =
2416 &http_known_methods[smp->data.u.meth.meth];
2417
2418 if (meth->len + 3 > max_size)
2419 return 0;
2420 p[idx++] = SPOE_DATA_T_STR;
2421 idx += encode_spoe_string(meth->name, meth->len, p+idx);
2422 }
2423 else {
2424 struct chunk *meth = &smp->data.u.meth.str;
2425
2426 if (meth->len + 3 > max_size)
2427 return 0;
2428 p[idx++] = SPOE_DATA_T_STR;
2429 idx += encode_spoe_string(meth->str, meth->len, p+idx);
2430 }
2431 return idx;
2432}
2433
2434static inline int
2435encode_spoe_arg_ipv6(struct spoe_context *ctx, struct sample *smp,
2436 char *p, size_t max_size)
2437{
2438 int idx = 0;
2439
2440 if (max_size < 17)
2441 return 0;
2442 p[idx++] = SPOE_DATA_T_IPV6;
2443 memcpy(p+idx, &smp->data.u.ipv6, 16);
2444 idx += 16;
2445 return idx;
2446}
2447
2448
2449static inline int
2450encode_spoe_arg_ipv4(struct spoe_context *ctx, struct sample *smp,
2451 char *p, size_t max_size)
2452{
2453 int idx = 0;
2454
2455 if (max_size < 5)
2456 return 0;
2457 p[idx++] = SPOE_DATA_T_IPV4;
2458 memcpy(p+idx, &smp->data.u.ipv6, 4);
2459 idx += 4;
2460 return idx;
2461}
2462
2463static inline int
2464encode_spoe_arg_sint(struct spoe_context *ctx, struct sample *smp,
2465 char *p, size_t max_size)
2466{
2467 int idx = 0;
2468
2469 if (max_size < 9)
2470 return 0;
2471 p[idx++] = SPOE_DATA_T_INT64;
2472 idx += encode_spoe_varint(smp->data.u.sint, p+idx);
2473
2474 return idx;
2475}
2476
2477static inline int
2478encode_spoe_arg_bool(struct spoe_context *ctx, struct sample *smp,
2479 char *p, size_t max_size)
2480{
2481 int flag, idx = 0;
2482
2483 if (max_size < 1)
2484 return 0;
2485 flag = ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE);
2486 p[idx++] = (SPOE_DATA_T_BOOL | flag);
2487
2488 return idx;
2489}
2490
2491/* Encode SPOE messages for a specific event.
2492 *
2493 *
2494 * It returns 0 if During the processing, it returns
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002495 * 0 and it returns 1 when the processing is finished. If an error occurred, -1
2496 * is returned. */
2497static int
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002498encode_spoe_messages(struct stream *s, struct spoe_context *ctx,
2499 struct list *messages, int dir)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002500{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002501 struct spoe_config *conf = FLT_CONF(ctx->filter);
2502 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002503 struct spoe_message *msg;
2504 struct sample *smp;
2505 struct spoe_arg *arg;
2506 char *p;
2507 size_t max_size;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002508 int r, idx = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002509
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01002510 max_size = agent->frame_size - FRAME_HDR_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002511
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002512 p = ctx->buffer->p;
2513
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002514 /* Resume encoding of a SPOE message */
2515 if (ctx->frag_ctx.curmsg != NULL) {
2516 msg = ctx->frag_ctx.curmsg;
2517 goto encode_message;
2518 }
2519
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002520 /* Loop on messages */
2521 list_for_each_entry(msg, messages, list) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002522 ctx->frag_ctx.curmsg = msg;
2523 ctx->frag_ctx.curarg = NULL;
2524 ctx->frag_ctx.curoff = UINT_MAX;
2525
2526 encode_message:
2527 /* Resume encoding of a SPOE argument */
2528 if (ctx->frag_ctx.curarg != NULL) {
2529 arg = ctx->frag_ctx.curarg;
2530 goto encode_argument;
2531 }
2532
2533 if (ctx->frag_ctx.curoff != UINT_MAX)
2534 goto encode_msg_payload;
2535
2536 /* <idx> + <string> + <nb-args>.
2537 * Implies <id_len> is encoded on 2 bytes, at most (< 2288). */
2538 if (idx + 3 + msg->id_len + 1 > max_size)
2539 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002540
2541 /* Set the message name */
2542 idx += encode_spoe_string(msg->id, msg->id_len, p+idx);
2543
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002544 /* Store the number of arguments for this message */
2545 p[idx++] = msg->nargs;
2546
2547 ctx->frag_ctx.curoff = 0;
2548 encode_msg_payload:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002549
2550 /* Loop on arguments */
2551 list_for_each_entry(arg, &msg->args, list) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002552 ctx->frag_ctx.curarg = arg;
2553 ctx->frag_ctx.curoff = UINT_MAX;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002554
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002555 encode_argument:
2556 if (ctx->frag_ctx.curoff != UINT_MAX)
2557 goto encode_arg_value;
2558
2559 /* <idx> + <string>.
2560 * Implies <name_len> is encoded on 2 bytes, at most (< 2288). */
2561 if (idx + 3 + arg->name_len > max_size)
2562 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002563
2564 /* Encode the arguement name as a string. It can by NULL */
2565 idx += encode_spoe_string(arg->name, arg->name_len, p+idx);
2566
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002567 ctx->frag_ctx.curoff = 0;
2568 encode_arg_value:
2569
2570 if (idx + 1 > max_size)
2571 goto too_big;
2572
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002573 /* Fetch the arguement value */
2574 smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL);
2575 if (!smp) {
2576 /* If no value is available, set it to NULL */
2577 p[idx++] = SPOE_DATA_T_NULL;
2578 continue;
2579 }
2580
2581 /* Else, encode the arguement value */
2582 switch (smp->data.type) {
2583 case SMP_T_BOOL:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002584 if (!(r = encode_spoe_arg_bool(ctx, smp, p+idx, max_size-idx)))
2585 goto too_big;
2586 idx += r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002587 break;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002588
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002589 case SMP_T_SINT:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002590 if (!(r = encode_spoe_arg_sint(ctx, smp, p+idx, max_size-idx)))
2591 goto too_big;
2592 idx += r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002593 break;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002594
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002595 case SMP_T_IPV4:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002596 if (!(r = encode_spoe_arg_ipv4(ctx, smp, p+idx, max_size-idx)))
2597 goto too_big;
2598 idx += r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002599 break;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002600
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002601 case SMP_T_IPV6:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002602 if (!(r = encode_spoe_arg_ipv6(ctx, smp, p+idx, max_size-idx)))
2603 goto too_big;
2604 idx += r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002605 break;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002606
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002607 case SMP_T_STR:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002608 case SMP_T_BIN:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002609 idx += encode_spoe_arg_string(ctx, smp, p+idx, max_size-idx);
2610 if (ctx->frag_ctx.curoff)
2611 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002612 break;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002613
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002614 case SMP_T_METH:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002615 if (!(r = encode_spoe_arg_method(ctx, smp, p+idx, max_size-idx)))
2616 goto too_big;
2617 idx += r;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002618 break;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002619
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002620 default:
2621 p[idx++] = SPOE_DATA_T_NULL;
2622 }
2623 }
2624 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002625
2626 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2627 " - encode %s messages - spoe_appctx=%p - max_size=%lu - idx=%u\n",
2628 (int)now.tv_sec, (int)now.tv_usec,
2629 agent->id, __FUNCTION__, s,
2630 ((ctx->flags & SPOE_CTX_FL_FRAGMENTED) ? "last fragment of" : "unfragmented"),
2631 ctx->frag_ctx.spoe_appctx, max_size, idx);
2632
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002633 ctx->buffer->i = idx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002634 ctx->frag_ctx.curmsg = NULL;
2635 ctx->frag_ctx.curarg = NULL;
2636 ctx->frag_ctx.curoff = 0;
2637 ctx->frag_ctx.flags = SPOE_FRM_FL_FIN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002638 return 1;
2639
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002640 too_big:
2641 // FIXME: if fragmentation not supported =>
2642 // ctx->status_code = SPOE_CTX_ERR_TOO_BIG;
2643 // return -1;
2644
2645 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2646 " - encode fragmented messages - spoe_appctx=%p - curmsg=%p - curarg=%p - curoff=%u"
2647 " - max_size=%lu - idx=%u\n",
2648 (int)now.tv_sec, (int)now.tv_usec,
2649 agent->id, __FUNCTION__, s, ctx->frag_ctx.spoe_appctx,
2650 ctx->frag_ctx.curmsg, ctx->frag_ctx.curarg, ctx->frag_ctx.curoff,
2651 max_size, idx);
2652
2653 ctx->buffer->i = idx;
2654 ctx->flags |= SPOE_CTX_FL_FRAGMENTED;
2655 ctx->frag_ctx.flags &= ~SPOE_FRM_FL_FIN;
2656 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002657}
2658
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002659
2660/***************************************************************************
2661 * Functions that handle SPOE actions
2662 **************************************************************************/
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002663/* Helper function to set a variable */
2664static void
2665set_spoe_var(struct spoe_context *ctx, char *scope, char *name, int len,
2666 struct sample *smp)
2667{
2668 struct spoe_config *conf = FLT_CONF(ctx->filter);
2669 struct spoe_agent *agent = conf->agent;
2670 char varname[64];
2671
2672 memset(varname, 0, sizeof(varname));
2673 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2674 scope, agent->var_pfx, len, name);
2675 vars_set_by_name_ifexist(varname, len, smp);
2676}
2677
2678/* Helper function to unset a variable */
2679static void
2680unset_spoe_var(struct spoe_context *ctx, char *scope, char *name, int len,
2681 struct sample *smp)
2682{
2683 struct spoe_config *conf = FLT_CONF(ctx->filter);
2684 struct spoe_agent *agent = conf->agent;
2685 char varname[64];
2686
2687 memset(varname, 0, sizeof(varname));
2688 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2689 scope, agent->var_pfx, len, name);
2690 vars_unset_by_name_ifexist(varname, len, smp);
2691}
2692
2693
2694/* Process SPOE actions for a specific event. During the processing, it returns
2695 * 0 and it returns 1 when the processing is finished. If an error occurred, -1
2696 * is returned. */
2697static int
2698process_spoe_actions(struct stream *s, struct spoe_context *ctx,
2699 enum spoe_event ev, int dir)
2700{
2701 char *p;
2702 size_t size;
2703 int off, i, idx = 0;
2704
2705 p = ctx->buffer->p;
2706 size = ctx->buffer->i;
2707
2708 while (idx < size) {
2709 char *str;
2710 uint64_t sz;
2711 struct sample smp;
2712 enum spoe_action_type type;
2713
2714 off = idx;
2715 if (idx+2 > size)
2716 goto skip;
2717
2718 type = p[idx++];
2719 switch (type) {
2720 case SPOE_ACT_T_SET_VAR: {
2721 char *scope;
2722
2723 if (p[idx++] != 3)
2724 goto skip_action;
2725
2726 switch (p[idx++]) {
2727 case SPOE_SCOPE_PROC: scope = "proc"; break;
2728 case SPOE_SCOPE_SESS: scope = "sess"; break;
2729 case SPOE_SCOPE_TXN : scope = "txn"; break;
2730 case SPOE_SCOPE_REQ : scope = "req"; break;
2731 case SPOE_SCOPE_RES : scope = "res"; break;
2732 default: goto skip;
2733 }
2734
2735 idx += decode_spoe_string(p+idx, p+size, &str, &sz);
2736 if (str == NULL)
2737 goto skip;
2738 memset(&smp, 0, sizeof(smp));
2739 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletb5cff602016-11-24 14:53:22 +01002740
2741 if ((i = decode_spoe_data(p+idx, p+size, &smp)) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002742 goto skip;
Christopher Fauletb5cff602016-11-24 14:53:22 +01002743 idx += i;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002744
2745 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2746 " - set-var '%s.%s.%.*s'\n",
2747 (int)now.tv_sec, (int)now.tv_usec,
2748 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2749 __FUNCTION__, s, scope,
2750 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2751 (int)sz, str);
2752
2753 set_spoe_var(ctx, scope, str, sz, &smp);
2754 break;
2755 }
2756
2757 case SPOE_ACT_T_UNSET_VAR: {
2758 char *scope;
2759
2760 if (p[idx++] != 2)
2761 goto skip_action;
2762
2763 switch (p[idx++]) {
2764 case SPOE_SCOPE_PROC: scope = "proc"; break;
2765 case SPOE_SCOPE_SESS: scope = "sess"; break;
2766 case SPOE_SCOPE_TXN : scope = "txn"; break;
2767 case SPOE_SCOPE_REQ : scope = "req"; break;
2768 case SPOE_SCOPE_RES : scope = "res"; break;
2769 default: goto skip;
2770 }
2771
2772 idx += decode_spoe_string(p+idx, p+size, &str, &sz);
2773 if (str == NULL)
2774 goto skip;
2775 memset(&smp, 0, sizeof(smp));
2776 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
2777
2778 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2779 " - unset-var '%s.%s.%.*s'\n",
2780 (int)now.tv_sec, (int)now.tv_usec,
2781 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2782 __FUNCTION__, s, scope,
2783 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2784 (int)sz, str);
2785
2786 unset_spoe_var(ctx, scope, str, sz, &smp);
2787 break;
2788 }
2789
2790 default:
2791 skip_action:
2792 if ((i = skip_spoe_action(p+off, p+size)) == -1)
2793 goto skip;
2794 idx += i;
2795 }
2796 }
2797
2798 return 1;
2799 skip:
2800 return 0;
2801}
2802
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002803/***************************************************************************
2804 * Functions that process SPOE events
2805 **************************************************************************/
2806static inline int
Christopher Fauleta1cda022016-12-21 08:58:06 +01002807start_event_processing(struct spoe_context *ctx, int dir)
2808{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002809 /* If a process is already started for this SPOE context, retry
2810 * later. */
2811 if (ctx->flags & SPOE_CTX_FL_PROCESS)
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002812 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002813
2814 /* Set the right flag to prevent request and response processing
2815 * in same time. */
2816 ctx->flags |= ((dir == SMP_OPT_DIR_REQ)
2817 ? SPOE_CTX_FL_REQ_PROCESS
2818 : SPOE_CTX_FL_RSP_PROCESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002819 return 1;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002820}
2821
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002822static inline void
Christopher Fauleta1cda022016-12-21 08:58:06 +01002823stop_event_processing(struct spoe_context *ctx)
2824{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002825 struct spoe_appctx *sa = ctx->frag_ctx.spoe_appctx;
2826
2827 if (sa) {
2828 sa->frag_ctx.ctx = NULL;
2829 wakeup_spoe_appctx(sa->owner);
2830 }
2831
Christopher Fauleta1cda022016-12-21 08:58:06 +01002832 /* Reset the flag to allow next processing */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002833 ctx->flags &= ~(SPOE_CTX_FL_PROCESS|SPOE_CTX_FL_FRAGMENTED);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002834
Christopher Fauletb067b062017-01-04 16:39:11 +01002835 ctx->status_code = 0;
2836
Christopher Fauleta1cda022016-12-21 08:58:06 +01002837 /* Reset processing timer */
2838 ctx->process_exp = TICK_ETERNITY;
2839
Christopher Faulet4596fb72017-01-11 14:05:19 +01002840 release_spoe_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002841
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002842 ctx->frag_ctx.spoe_appctx = NULL;
2843 ctx->frag_ctx.curmsg = NULL;
2844 ctx->frag_ctx.curarg = NULL;
2845 ctx->frag_ctx.curoff = 0;
2846 ctx->frag_ctx.flags = 0;
2847
Christopher Fauleta1cda022016-12-21 08:58:06 +01002848 if (!LIST_ISEMPTY(&ctx->list)) {
2849 LIST_DEL(&ctx->list);
2850 LIST_INIT(&ctx->list);
2851 }
2852}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002853
2854/* Process a SPOE event. First, this functions will process messages attached to
2855 * this event and send them to an agent in a NOTIFY frame. Then, it will wait a
2856 * ACK frame to process corresponding actions. During all the processing, it
2857 * returns 0 and it returns 1 when the processing is finished. If an error
2858 * occurred, -1 is returned. */
2859static int
2860process_spoe_event(struct stream *s, struct spoe_context *ctx,
2861 enum spoe_event ev)
2862{
Christopher Fauletf7a30922016-11-10 15:04:51 +01002863 struct spoe_config *conf = FLT_CONF(ctx->filter);
2864 struct spoe_agent *agent = conf->agent;
2865 int dir, ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002866
2867 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2868 " - ctx-state=%s - event=%s\n",
2869 (int)now.tv_sec, (int)now.tv_usec,
Christopher Fauletf7a30922016-11-10 15:04:51 +01002870 agent->id, __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002871 spoe_event_str[ev]);
2872
2873 dir = ((ev < SPOE_EV_ON_SERVER_SESS) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
2874
2875 if (LIST_ISEMPTY(&(ctx->messages[ev])))
2876 goto out;
2877
2878 if (ctx->state == SPOE_CTX_ST_ERROR)
2879 goto error;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002880
2881 if (tick_is_expired(ctx->process_exp, now_ms) && ctx->state != SPOE_CTX_ST_DONE) {
2882 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2883 " - failed to process event '%s': timeout\n",
2884 (int)now.tv_sec, (int)now.tv_usec,
2885 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
Christopher Fauletb067b062017-01-04 16:39:11 +01002886 ctx->status_code = SPOE_CTX_ERR_TOUT;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002887 goto error;
2888 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002889
2890 if (ctx->state == SPOE_CTX_ST_READY) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002891 if (agent->eps_max > 0) {
2892 if (!freq_ctr_remain(&agent->err_per_sec, agent->eps_max, 0)) {
2893 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2894 " - skip event '%s': max EPS reached\n",
2895 (int)now.tv_sec, (int)now.tv_usec,
2896 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
2897 goto skip;
2898 }
2899 }
2900
Christopher Fauletf7a30922016-11-10 15:04:51 +01002901 if (!tick_isset(ctx->process_exp)) {
2902 ctx->process_exp = tick_add_ifset(now_ms, agent->timeout.processing);
2903 s->task->expire = tick_first((tick_is_expired(s->task->expire, now_ms) ? 0 : s->task->expire),
2904 ctx->process_exp);
2905 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002906 ret = start_event_processing(ctx, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002907 if (!ret)
2908 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002909
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002910 if (queue_spoe_context(ctx) < 0)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002911 goto error;
2912
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002913 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002914 /* fall through */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002915 }
2916
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002917 if (ctx->state == SPOE_CTX_ST_ENCODING_MSGS) {
2918 if (!acquire_spoe_buffer(&ctx->buffer, &ctx->buffer_wait))
2919 goto out;
2920 ret = encode_spoe_messages(s, ctx, &(ctx->messages[ev]), dir);
2921 if (ret < 0)
2922 goto error;
2923 ctx->state = SPOE_CTX_ST_SENDING_MSGS;
2924 }
2925
2926 if (ctx->state == SPOE_CTX_ST_SENDING_MSGS) {
2927 if (ctx->frag_ctx.spoe_appctx)
2928 wakeup_spoe_appctx(ctx->frag_ctx.spoe_appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002929 ret = 0;
2930 goto out;
2931 }
2932
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002933 if (ctx->state == SPOE_CTX_ST_WAITING_ACK) {
2934 ret = 0;
2935 goto out;
2936 }
2937
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002938 if (ctx->state == SPOE_CTX_ST_DONE) {
2939 ret = process_spoe_actions(s, ctx, ev, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002940 if (!ret)
2941 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002942 ctx->frame_id++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002943 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002944 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002945 }
2946
2947 out:
2948 return ret;
2949
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002950 error:
Christopher Faulet48026722016-11-16 15:01:12 +01002951 if (agent->eps_max > 0)
2952 update_freq_ctr(&agent->err_per_sec, 1);
2953
Christopher Faulet985532d2016-11-16 15:36:19 +01002954 if (agent->var_on_error) {
2955 struct sample smp;
2956
2957 memset(&smp, 0, sizeof(smp));
2958 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletb067b062017-01-04 16:39:11 +01002959 smp.data.u.sint = ctx->status_code;
Christopher Faulet985532d2016-11-16 15:36:19 +01002960 smp.data.type = SMP_T_BOOL;
2961
2962 set_spoe_var(ctx, "txn", agent->var_on_error,
2963 strlen(agent->var_on_error), &smp);
2964 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002965 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2966 " - failed to create process event '%s': code=%u\n",
2967 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2968 __FUNCTION__, ctx->strm, spoe_event_str[ev],
2969 ctx->status_code);
Christopher Faulet72bcc472017-01-04 16:39:41 +01002970 send_log(ctx->strm->be, LOG_WARNING,
2971 "SPOE: [%s] failed to process event '%s': code=%u\n",
2972 agent->id, spoe_event_str[ev], ctx->status_code);
Christopher Faulet985532d2016-11-16 15:36:19 +01002973
Christopher Fauletea62c2a2016-11-14 10:54:21 +01002974 ctx->state = ((agent->flags & SPOE_FL_CONT_ON_ERR)
2975 ? SPOE_CTX_ST_READY
Christopher Fauletb067b062017-01-04 16:39:11 +01002976 : SPOE_CTX_ST_NONE);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002977 ret = 1;
2978 goto end;
2979
2980 skip:
2981 ctx->state = SPOE_CTX_ST_READY;
2982 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002983
Christopher Fauleta1cda022016-12-21 08:58:06 +01002984 end:
2985 stop_event_processing(ctx);
2986 return ret;
2987}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002988
2989/***************************************************************************
2990 * Functions that create/destroy SPOE contexts
2991 **************************************************************************/
Christopher Fauleta1cda022016-12-21 08:58:06 +01002992static int
Christopher Faulet4596fb72017-01-11 14:05:19 +01002993acquire_spoe_buffer(struct buffer **buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002994{
Christopher Faulet4596fb72017-01-11 14:05:19 +01002995 if (*buf != &buf_empty)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002996 return 1;
2997
Christopher Faulet4596fb72017-01-11 14:05:19 +01002998 if (!LIST_ISEMPTY(&buffer_wait->list)) {
2999 LIST_DEL(&buffer_wait->list);
3000 LIST_INIT(&buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003001 }
3002
Christopher Faulet4596fb72017-01-11 14:05:19 +01003003 if (b_alloc_margin(buf, global.tune.reserved_bufs))
Christopher Fauleta1cda022016-12-21 08:58:06 +01003004 return 1;
3005
Christopher Faulet4596fb72017-01-11 14:05:19 +01003006 LIST_ADDQ(&buffer_wq, &buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003007 return 0;
3008}
3009
3010static void
Christopher Faulet4596fb72017-01-11 14:05:19 +01003011release_spoe_buffer(struct buffer **buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01003012{
Christopher Faulet4596fb72017-01-11 14:05:19 +01003013 if (!LIST_ISEMPTY(&buffer_wait->list)) {
3014 LIST_DEL(&buffer_wait->list);
3015 LIST_INIT(&buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003016 }
3017
3018 /* Release the buffer if needed */
Christopher Faulet4596fb72017-01-11 14:05:19 +01003019 if (*buf != &buf_empty) {
3020 b_free(buf);
3021 offer_buffers(buffer_wait->target,
3022 tasks_run_queue + applets_active_queue);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003023 }
3024}
3025
Christopher Faulet4596fb72017-01-11 14:05:19 +01003026static int
3027wakeup_spoe_context(struct spoe_context *ctx)
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003028{
3029 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
3030 return 1;
3031}
3032
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003033static struct spoe_context *
3034create_spoe_context(struct filter *filter)
3035{
3036 struct spoe_config *conf = FLT_CONF(filter);
3037 struct spoe_context *ctx;
3038
3039 ctx = pool_alloc_dirty(pool2_spoe_ctx);
3040 if (ctx == NULL) {
3041 return NULL;
3042 }
3043 memset(ctx, 0, sizeof(*ctx));
Christopher Fauletb067b062017-01-04 16:39:11 +01003044 ctx->filter = filter;
3045 ctx->state = SPOE_CTX_ST_NONE;
3046 ctx->status_code = SPOE_CTX_ERR_NONE;
3047 ctx->flags = 0;
3048 ctx->messages = conf->agent->messages;
3049 ctx->buffer = &buf_empty;
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003050 LIST_INIT(&ctx->buffer_wait.list);
3051 ctx->buffer_wait.target = ctx;
3052 ctx->buffer_wait.wakeup_cb = (int (*)(void *))wakeup_spoe_context;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003053 LIST_INIT(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003054
Christopher Fauletf7a30922016-11-10 15:04:51 +01003055 ctx->stream_id = 0;
3056 ctx->frame_id = 1;
3057 ctx->process_exp = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003058
3059 return ctx;
3060}
3061
3062static void
3063destroy_spoe_context(struct spoe_context *ctx)
3064{
3065 if (!ctx)
3066 return;
3067
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003068 if (!LIST_ISEMPTY(&ctx->buffer_wait.list))
3069 LIST_DEL(&ctx->buffer_wait.list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003070 if (!LIST_ISEMPTY(&ctx->list))
3071 LIST_DEL(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003072 pool_free2(pool2_spoe_ctx, ctx);
3073}
3074
3075static void
3076reset_spoe_context(struct spoe_context *ctx)
3077{
3078 ctx->state = SPOE_CTX_ST_READY;
3079 ctx->flags &= ~SPOE_CTX_FL_PROCESS;
3080}
3081
3082
3083/***************************************************************************
3084 * Hooks that manage the filter lifecycle (init/check/deinit)
3085 **************************************************************************/
3086/* Signal handler: Do a soft stop, wakeup SPOE applet */
3087static void
3088sig_stop_spoe(struct sig_handler *sh)
3089{
3090 struct proxy *p;
3091
3092 p = proxy;
3093 while (p) {
3094 struct flt_conf *fconf;
3095
3096 list_for_each_entry(fconf, &p->filter_configs, list) {
Christopher Faulet3b386a32017-02-23 10:17:15 +01003097 struct spoe_config *conf;
3098 struct spoe_agent *agent;
Christopher Faulet42bfa462017-01-04 14:14:19 +01003099 struct spoe_appctx *spoe_appctx;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003100
Christopher Faulet3b386a32017-02-23 10:17:15 +01003101 if (fconf->id != spoe_filter_id)
3102 continue;
3103
3104 conf = fconf->conf;
3105 agent = conf->agent;
3106
Christopher Faulet42bfa462017-01-04 14:14:19 +01003107 list_for_each_entry(spoe_appctx, &agent->applets, list) {
Christopher Faulet4596fb72017-01-11 14:05:19 +01003108 wakeup_spoe_appctx(spoe_appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003109 }
3110 }
3111 p = p->next;
3112 }
3113}
3114
3115
3116/* Initialize the SPOE filter. Returns -1 on error, else 0. */
3117static int
3118spoe_init(struct proxy *px, struct flt_conf *fconf)
3119{
3120 struct spoe_config *conf = fconf->conf;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003121
3122 memset(&conf->agent_fe, 0, sizeof(conf->agent_fe));
3123 init_new_proxy(&conf->agent_fe);
3124 conf->agent_fe.parent = conf->agent;
3125 conf->agent_fe.last_change = now.tv_sec;
3126 conf->agent_fe.id = conf->agent->id;
3127 conf->agent_fe.cap = PR_CAP_FE;
3128 conf->agent_fe.mode = PR_MODE_TCP;
3129 conf->agent_fe.maxconn = 0;
3130 conf->agent_fe.options2 |= PR_O2_INDEPSTR;
3131 conf->agent_fe.conn_retries = CONN_RETRIES;
3132 conf->agent_fe.accept = frontend_accept;
3133 conf->agent_fe.srv = NULL;
3134 conf->agent_fe.timeout.client = TICK_ETERNITY;
3135 conf->agent_fe.default_target = &spoe_applet.obj_type;
3136 conf->agent_fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
3137
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003138 if (!sighandler_registered) {
3139 signal_register_fct(0, sig_stop_spoe, 0);
3140 sighandler_registered = 1;
3141 }
3142
3143 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003144}
3145
3146/* Free ressources allocated by the SPOE filter. */
3147static void
3148spoe_deinit(struct proxy *px, struct flt_conf *fconf)
3149{
3150 struct spoe_config *conf = fconf->conf;
3151
3152 if (conf) {
3153 struct spoe_agent *agent = conf->agent;
3154 struct listener *l = LIST_NEXT(&conf->agent_fe.conf.listeners,
3155 struct listener *, by_fe);
3156
3157 free(l);
3158 release_spoe_agent(agent);
3159 free(conf);
3160 }
3161 fconf->conf = NULL;
3162}
3163
3164/* Check configuration of a SPOE filter for a specified proxy.
3165 * Return 1 on error, else 0. */
3166static int
3167spoe_check(struct proxy *px, struct flt_conf *fconf)
3168{
3169 struct spoe_config *conf = fconf->conf;
3170 struct proxy *target;
3171
3172 target = proxy_be_by_name(conf->agent->b.name);
3173 if (target == NULL) {
3174 Alert("Proxy %s : unknown backend '%s' used by SPOE agent '%s'"
3175 " declared at %s:%d.\n",
3176 px->id, conf->agent->b.name, conf->agent->id,
3177 conf->agent->conf.file, conf->agent->conf.line);
3178 return 1;
3179 }
3180 if (target->mode != PR_MODE_TCP) {
3181 Alert("Proxy %s : backend '%s' used by SPOE agent '%s' declared"
3182 " at %s:%d does not support HTTP mode.\n",
3183 px->id, target->id, conf->agent->id,
3184 conf->agent->conf.file, conf->agent->conf.line);
3185 return 1;
3186 }
3187
3188 free(conf->agent->b.name);
3189 conf->agent->b.name = NULL;
3190 conf->agent->b.be = target;
3191 return 0;
3192}
3193
3194/**************************************************************************
3195 * Hooks attached to a stream
3196 *************************************************************************/
3197/* Called when a filter instance is created and attach to a stream. It creates
3198 * the context that will be used to process this stream. */
3199static int
3200spoe_start(struct stream *s, struct filter *filter)
3201{
Christopher Faulet72bcc472017-01-04 16:39:41 +01003202 struct spoe_config *conf = FLT_CONF(filter);
3203 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003204 struct spoe_context *ctx;
3205
3206 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
Christopher Faulet72bcc472017-01-04 16:39:41 +01003207 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003208 __FUNCTION__, s);
3209
3210 ctx = create_spoe_context(filter);
3211 if (ctx == NULL) {
Christopher Faulet72bcc472017-01-04 16:39:41 +01003212 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
3213 " - failed to create SPOE context\n",
3214 (int)now.tv_sec, (int)now.tv_usec, agent->id,
3215 __FUNCTION__, ctx->strm);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003216 send_log(s->be, LOG_EMERG,
Christopher Faulet72bcc472017-01-04 16:39:41 +01003217 "SPOE: [%s] failed to create SPOE context\n",
3218 agent->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003219 return 0;
3220 }
3221
3222 ctx->strm = s;
3223 ctx->state = SPOE_CTX_ST_READY;
3224 filter->ctx = ctx;
3225
3226 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_FE]))
3227 filter->pre_analyzers |= AN_REQ_INSPECT_FE;
3228
3229 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_BE]))
3230 filter->pre_analyzers |= AN_REQ_INSPECT_BE;
3231
3232 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_RSP]))
3233 filter->pre_analyzers |= AN_RES_INSPECT;
3234
3235 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_FE]))
3236 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_FE;
3237
3238 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_BE]))
3239 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_BE;
3240
3241 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_RSP]))
3242 filter->pre_analyzers |= AN_RES_HTTP_PROCESS_FE;
3243
3244 return 1;
3245}
3246
3247/* Called when a filter instance is detached from a stream. It release the
3248 * attached SPOE context. */
3249static void
3250spoe_stop(struct stream *s, struct filter *filter)
3251{
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003252 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
3253 (int)now.tv_sec, (int)now.tv_usec,
3254 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3255 __FUNCTION__, s);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003256 destroy_spoe_context(filter->ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003257}
3258
Christopher Fauletf7a30922016-11-10 15:04:51 +01003259
3260/*
3261 * Called when the stream is woken up because of expired timer.
3262 */
3263static void
3264spoe_check_timeouts(struct stream *s, struct filter *filter)
3265{
3266 struct spoe_context *ctx = filter->ctx;
3267
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003268 if (tick_is_expired(ctx->process_exp, now_ms)) {
3269 s->pending_events |= TASK_WOKEN_MSG;
Christopher Faulet4596fb72017-01-11 14:05:19 +01003270 release_spoe_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003271 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01003272}
3273
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003274/* Called when we are ready to filter data on a channel */
3275static int
3276spoe_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
3277{
3278 struct spoe_context *ctx = filter->ctx;
3279 int ret = 1;
3280
3281 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3282 " - ctx-flags=0x%08x\n",
3283 (int)now.tv_sec, (int)now.tv_usec,
3284 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3285 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
3286
Christopher Fauletb067b062017-01-04 16:39:11 +01003287 if (ctx->state == SPOE_CTX_ST_NONE)
3288 goto out;
3289
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003290 if (!(chn->flags & CF_ISRESP)) {
3291 if (filter->pre_analyzers & AN_REQ_INSPECT_FE)
3292 chn->analysers |= AN_REQ_INSPECT_FE;
3293 if (filter->pre_analyzers & AN_REQ_INSPECT_BE)
3294 chn->analysers |= AN_REQ_INSPECT_BE;
3295
3296 if (ctx->flags & SPOE_CTX_FL_CLI_CONNECTED)
3297 goto out;
3298
3299 ctx->stream_id = s->uniq_id;
Christopher Fauletb067b062017-01-04 16:39:11 +01003300 ret = process_spoe_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
3301 if (!ret)
3302 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003303 ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
3304 }
3305 else {
3306 if (filter->pre_analyzers & SPOE_EV_ON_TCP_RSP)
3307 chn->analysers |= AN_RES_INSPECT;
3308
3309 if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
3310 goto out;
3311
Christopher Fauletb067b062017-01-04 16:39:11 +01003312 ret = process_spoe_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003313 if (!ret) {
3314 channel_dont_read(chn);
3315 channel_dont_close(chn);
Christopher Fauletb067b062017-01-04 16:39:11 +01003316 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003317 }
Christopher Fauletb067b062017-01-04 16:39:11 +01003318 ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003319 }
3320
3321 out:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003322 return ret;
3323}
3324
3325/* Called before a processing happens on a given channel */
3326static int
3327spoe_chn_pre_analyze(struct stream *s, struct filter *filter,
3328 struct channel *chn, unsigned an_bit)
3329{
3330 struct spoe_context *ctx = filter->ctx;
3331 int ret = 1;
3332
3333 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3334 " - ctx-flags=0x%08x - ana=0x%08x\n",
3335 (int)now.tv_sec, (int)now.tv_usec,
3336 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3337 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
3338 ctx->flags, an_bit);
3339
Christopher Fauletb067b062017-01-04 16:39:11 +01003340 if (ctx->state == SPOE_CTX_ST_NONE)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003341 goto out;
3342
3343 switch (an_bit) {
3344 case AN_REQ_INSPECT_FE:
3345 ret = process_spoe_event(s, ctx, SPOE_EV_ON_TCP_REQ_FE);
3346 break;
3347 case AN_REQ_INSPECT_BE:
3348 ret = process_spoe_event(s, ctx, SPOE_EV_ON_TCP_REQ_BE);
3349 break;
3350 case AN_RES_INSPECT:
3351 ret = process_spoe_event(s, ctx, SPOE_EV_ON_TCP_RSP);
3352 break;
3353 case AN_REQ_HTTP_PROCESS_FE:
3354 ret = process_spoe_event(s, ctx, SPOE_EV_ON_HTTP_REQ_FE);
3355 break;
3356 case AN_REQ_HTTP_PROCESS_BE:
3357 ret = process_spoe_event(s, ctx, SPOE_EV_ON_HTTP_REQ_BE);
3358 break;
3359 case AN_RES_HTTP_PROCESS_FE:
3360 ret = process_spoe_event(s, ctx, SPOE_EV_ON_HTTP_RSP);
3361 break;
3362 }
3363
3364 out:
3365 if (!ret) {
3366 channel_dont_read(chn);
3367 channel_dont_close(chn);
3368 }
3369 return ret;
3370}
3371
3372/* Called when the filtering on the channel ends. */
3373static int
3374spoe_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
3375{
3376 struct spoe_context *ctx = filter->ctx;
3377
3378 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3379 " - ctx-flags=0x%08x\n",
3380 (int)now.tv_sec, (int)now.tv_usec,
3381 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3382 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
3383
3384 if (!(ctx->flags & SPOE_CTX_FL_PROCESS)) {
3385 reset_spoe_context(ctx);
3386 }
3387
3388 return 1;
3389}
3390
3391/********************************************************************
3392 * Functions that manage the filter initialization
3393 ********************************************************************/
3394struct flt_ops spoe_ops = {
3395 /* Manage SPOE filter, called for each filter declaration */
3396 .init = spoe_init,
3397 .deinit = spoe_deinit,
3398 .check = spoe_check,
3399
3400 /* Handle start/stop of SPOE */
Christopher Fauletf7a30922016-11-10 15:04:51 +01003401 .attach = spoe_start,
3402 .detach = spoe_stop,
3403 .check_timeouts = spoe_check_timeouts,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003404
3405 /* Handle channels activity */
3406 .channel_start_analyze = spoe_start_analyze,
3407 .channel_pre_analyze = spoe_chn_pre_analyze,
3408 .channel_end_analyze = spoe_end_analyze,
3409};
3410
3411
3412static int
3413cfg_parse_spoe_agent(const char *file, int linenum, char **args, int kwm)
3414{
3415 const char *err;
3416 int i, err_code = 0;
3417
3418 if ((cfg_scope == NULL && curengine != NULL) ||
3419 (cfg_scope != NULL && curengine == NULL) ||
3420 strcmp(curengine, cfg_scope))
3421 goto out;
3422
3423 if (!strcmp(args[0], "spoe-agent")) { /* new spoe-agent section */
3424 if (!*args[1]) {
3425 Alert("parsing [%s:%d] : missing name for spoe-agent section.\n",
3426 file, linenum);
3427 err_code |= ERR_ALERT | ERR_ABORT;
3428 goto out;
3429 }
3430 if (*args[2]) {
3431 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3432 file, linenum, args[2]);
3433 err_code |= ERR_ALERT | ERR_ABORT;
3434 goto out;
3435 }
3436
3437 err = invalid_char(args[1]);
3438 if (err) {
3439 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3440 file, linenum, *err, args[0], args[1]);
3441 err_code |= ERR_ALERT | ERR_ABORT;
3442 goto out;
3443 }
3444
3445 if (curagent != NULL) {
3446 Alert("parsing [%s:%d] : another spoe-agent section previously defined.\n",
3447 file, linenum);
3448 err_code |= ERR_ALERT | ERR_ABORT;
3449 goto out;
3450 }
3451 if ((curagent = calloc(1, sizeof(*curagent))) == NULL) {
3452 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3453 err_code |= ERR_ALERT | ERR_ABORT;
3454 goto out;
3455 }
3456
3457 curagent->id = strdup(args[1]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003458
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003459 curagent->conf.file = strdup(file);
3460 curagent->conf.line = linenum;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003461
3462 curagent->timeout.hello = TICK_ETERNITY;
3463 curagent->timeout.idle = TICK_ETERNITY;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003464 curagent->timeout.processing = TICK_ETERNITY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003465
3466 curagent->engine_id = NULL;
3467 curagent->var_pfx = NULL;
3468 curagent->var_on_error = NULL;
3469 curagent->flags = 0;
3470 curagent->cps_max = 0;
3471 curagent->eps_max = 0;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01003472 curagent->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003473 curagent->min_applets = 0;
3474 curagent->max_fpa = 100;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003475
3476 for (i = 0; i < SPOE_EV_EVENTS; ++i)
3477 LIST_INIT(&curagent->messages[i]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003478
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01003479 curagent->frame_size = curagent->max_frame_size;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003480 curagent->applets_act = 0;
3481 curagent->applets_idle = 0;
3482 curagent->sending_rate = 0;
3483
3484 LIST_INIT(&curagent->applets);
3485 LIST_INIT(&curagent->sending_queue);
3486 LIST_INIT(&curagent->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003487 }
3488 else if (!strcmp(args[0], "use-backend")) {
3489 if (!*args[1]) {
3490 Alert("parsing [%s:%d] : '%s' expects a backend name.\n",
3491 file, linenum, args[0]);
3492 err_code |= ERR_ALERT | ERR_FATAL;
3493 goto out;
3494 }
3495 if (*args[2]) {
3496 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3497 file, linenum, args[2]);
3498 err_code |= ERR_ALERT | ERR_ABORT;
3499 goto out;
3500 }
3501 free(curagent->b.name);
3502 curagent->b.name = strdup(args[1]);
3503 }
3504 else if (!strcmp(args[0], "messages")) {
3505 int cur_arg = 1;
3506 while (*args[cur_arg]) {
3507 struct spoe_msg_placeholder *mp = NULL;
3508
3509 list_for_each_entry(mp, &curmps, list) {
3510 if (!strcmp(mp->id, args[cur_arg])) {
3511 Alert("parsing [%s:%d]: spoe-message message '%s' already declared.\n",
3512 file, linenum, args[cur_arg]);
3513 err_code |= ERR_ALERT | ERR_FATAL;
3514 goto out;
3515 }
3516 }
3517
3518 if ((mp = calloc(1, sizeof(*mp))) == NULL) {
3519 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3520 err_code |= ERR_ALERT | ERR_ABORT;
3521 goto out;
3522 }
3523 mp->id = strdup(args[cur_arg]);
3524 LIST_ADDQ(&curmps, &mp->list);
3525 cur_arg++;
3526 }
3527 }
3528 else if (!strcmp(args[0], "timeout")) {
3529 unsigned int *tv = NULL;
3530 const char *res;
3531 unsigned timeout;
3532
3533 if (!*args[1]) {
3534 Alert("parsing [%s:%d] : 'timeout' expects 'connect', 'idle' and 'ack'.\n",
3535 file, linenum);
3536 err_code |= ERR_ALERT | ERR_FATAL;
3537 goto out;
3538 }
3539 if (!strcmp(args[1], "hello"))
3540 tv = &curagent->timeout.hello;
3541 else if (!strcmp(args[1], "idle"))
3542 tv = &curagent->timeout.idle;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003543 else if (!strcmp(args[1], "processing"))
3544 tv = &curagent->timeout.processing;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003545 else {
Christopher Faulet03a34492016-11-19 16:47:56 +01003546 Alert("parsing [%s:%d] : 'timeout' supports 'connect', 'idle' or 'processing' (got %s).\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003547 file, linenum, args[1]);
3548 err_code |= ERR_ALERT | ERR_FATAL;
3549 goto out;
3550 }
3551 if (!*args[2]) {
3552 Alert("parsing [%s:%d] : 'timeout %s' expects an integer value (in milliseconds).\n",
3553 file, linenum, args[1]);
3554 err_code |= ERR_ALERT | ERR_FATAL;
3555 goto out;
3556 }
3557 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
3558 if (res) {
3559 Alert("parsing [%s:%d] : unexpected character '%c' in 'timeout %s'.\n",
3560 file, linenum, *res, args[1]);
3561 err_code |= ERR_ALERT | ERR_ABORT;
3562 goto out;
3563 }
3564 if (*args[3]) {
3565 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3566 file, linenum, args[3]);
3567 err_code |= ERR_ALERT | ERR_ABORT;
3568 goto out;
3569 }
3570 *tv = MS_TO_TICKS(timeout);
3571 }
3572 else if (!strcmp(args[0], "option")) {
3573 if (!*args[1]) {
3574 Alert("parsing [%s:%d]: '%s' expects an option name.\n",
3575 file, linenum, args[0]);
3576 err_code |= ERR_ALERT | ERR_FATAL;
3577 goto out;
3578 }
3579 if (!strcmp(args[1], "var-prefix")) {
3580 char *tmp;
3581
3582 if (!*args[2]) {
3583 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3584 file, linenum, args[0],
3585 args[1]);
3586 err_code |= ERR_ALERT | ERR_FATAL;
3587 goto out;
3588 }
3589 tmp = args[2];
3590 while (*tmp) {
3591 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3592 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3593 file, linenum, args[0], args[1]);
3594 err_code |= ERR_ALERT | ERR_FATAL;
3595 goto out;
3596 }
3597 tmp++;
3598 }
3599 curagent->var_pfx = strdup(args[2]);
3600 }
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003601 else if (!strcmp(args[1], "continue-on-error")) {
3602 if (*args[2]) {
3603 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
Christopher Faulet48026722016-11-16 15:01:12 +01003604 file, linenum, args[2]);
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003605 err_code |= ERR_ALERT | ERR_ABORT;
3606 goto out;
3607 }
3608 curagent->flags |= SPOE_FL_CONT_ON_ERR;
3609 }
Christopher Faulet985532d2016-11-16 15:36:19 +01003610 else if (!strcmp(args[1], "set-on-error")) {
3611 char *tmp;
3612
3613 if (!*args[2]) {
3614 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3615 file, linenum, args[0],
3616 args[1]);
3617 err_code |= ERR_ALERT | ERR_FATAL;
3618 goto out;
3619 }
3620 tmp = args[2];
3621 while (*tmp) {
3622 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3623 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3624 file, linenum, args[0], args[1]);
3625 err_code |= ERR_ALERT | ERR_FATAL;
3626 goto out;
3627 }
3628 tmp++;
3629 }
3630 curagent->var_on_error = strdup(args[2]);
3631 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003632 else {
3633 Alert("parsing [%s:%d]: option '%s' is not supported.\n",
3634 file, linenum, args[1]);
3635 err_code |= ERR_ALERT | ERR_FATAL;
3636 goto out;
3637 }
Christopher Faulet48026722016-11-16 15:01:12 +01003638 }
3639 else if (!strcmp(args[0], "maxconnrate")) {
3640 if (!*args[1]) {
3641 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3642 file, linenum, args[0]);
3643 err_code |= ERR_ALERT | ERR_FATAL;
3644 goto out;
3645 }
3646 if (*args[2]) {
3647 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3648 file, linenum, args[2]);
3649 err_code |= ERR_ALERT | ERR_ABORT;
3650 goto out;
3651 }
3652 curagent->cps_max = atol(args[1]);
3653 }
3654 else if (!strcmp(args[0], "maxerrrate")) {
3655 if (!*args[1]) {
3656 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3657 file, linenum, args[0]);
3658 err_code |= ERR_ALERT | ERR_FATAL;
3659 goto out;
3660 }
3661 if (*args[2]) {
3662 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3663 file, linenum, args[2]);
3664 err_code |= ERR_ALERT | ERR_ABORT;
3665 goto out;
3666 }
3667 curagent->eps_max = atol(args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003668 }
3669 else if (*args[0]) {
3670 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-agent section.\n",
3671 file, linenum, args[0]);
3672 err_code |= ERR_ALERT | ERR_FATAL;
3673 goto out;
3674 }
3675 out:
3676 return err_code;
3677}
3678
3679static int
3680cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
3681{
3682 struct spoe_message *msg;
3683 struct spoe_arg *arg;
3684 const char *err;
3685 char *errmsg = NULL;
3686 int err_code = 0;
3687
3688 if ((cfg_scope == NULL && curengine != NULL) ||
3689 (cfg_scope != NULL && curengine == NULL) ||
3690 strcmp(curengine, cfg_scope))
3691 goto out;
3692
3693 if (!strcmp(args[0], "spoe-message")) { /* new spoe-message section */
3694 if (!*args[1]) {
3695 Alert("parsing [%s:%d] : missing name for spoe-message section.\n",
3696 file, linenum);
3697 err_code |= ERR_ALERT | ERR_ABORT;
3698 goto out;
3699 }
3700 if (*args[2]) {
3701 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3702 file, linenum, args[2]);
3703 err_code |= ERR_ALERT | ERR_ABORT;
3704 goto out;
3705 }
3706
3707 err = invalid_char(args[1]);
3708 if (err) {
3709 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3710 file, linenum, *err, args[0], args[1]);
3711 err_code |= ERR_ALERT | ERR_ABORT;
3712 goto out;
3713 }
3714
3715 list_for_each_entry(msg, &curmsgs, list) {
3716 if (!strcmp(msg->id, args[1])) {
3717 Alert("parsing [%s:%d]: spoe-message section '%s' has the same"
3718 " name as another one declared at %s:%d.\n",
3719 file, linenum, args[1], msg->conf.file, msg->conf.line);
3720 err_code |= ERR_ALERT | ERR_FATAL;
3721 goto out;
3722 }
3723 }
3724
3725 if ((curmsg = calloc(1, sizeof(*curmsg))) == NULL) {
3726 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3727 err_code |= ERR_ALERT | ERR_ABORT;
3728 goto out;
3729 }
3730
3731 curmsg->id = strdup(args[1]);
3732 curmsg->id_len = strlen(curmsg->id);
3733 curmsg->event = SPOE_EV_NONE;
3734 curmsg->conf.file = strdup(file);
3735 curmsg->conf.line = linenum;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003736 curmsg->nargs = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003737 LIST_INIT(&curmsg->args);
3738 LIST_ADDQ(&curmsgs, &curmsg->list);
3739 }
3740 else if (!strcmp(args[0], "args")) {
3741 int cur_arg = 1;
3742
3743 curproxy->conf.args.ctx = ARGC_SPOE;
3744 curproxy->conf.args.file = file;
3745 curproxy->conf.args.line = linenum;
3746 while (*args[cur_arg]) {
3747 char *delim = strchr(args[cur_arg], '=');
3748 int idx = 0;
3749
3750 if ((arg = calloc(1, sizeof(*arg))) == NULL) {
3751 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3752 err_code |= ERR_ALERT | ERR_ABORT;
3753 goto out;
3754 }
3755
3756 if (!delim) {
3757 arg->name = NULL;
3758 arg->name_len = 0;
3759 delim = args[cur_arg];
3760 }
3761 else {
3762 arg->name = my_strndup(args[cur_arg], delim - args[cur_arg]);
3763 arg->name_len = delim - args[cur_arg];
3764 delim++;
3765 }
Christopher Fauletb0b42382017-02-23 22:41:09 +01003766 arg->expr = sample_parse_expr((char*[]){delim, NULL},
3767 &idx, file, linenum, &errmsg,
3768 &curproxy->conf.args);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003769 if (arg->expr == NULL) {
3770 Alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
3771 err_code |= ERR_ALERT | ERR_FATAL;
3772 free(arg->name);
3773 free(arg);
3774 goto out;
3775 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003776 curmsg->nargs++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003777 LIST_ADDQ(&curmsg->args, &arg->list);
3778 cur_arg++;
3779 }
3780 curproxy->conf.args.file = NULL;
3781 curproxy->conf.args.line = 0;
3782 }
3783 else if (!strcmp(args[0], "event")) {
3784 if (!*args[1]) {
3785 Alert("parsing [%s:%d] : missing event name.\n", file, linenum);
3786 err_code |= ERR_ALERT | ERR_ABORT;
3787 goto out;
3788 }
3789 if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_CLIENT_SESS]))
3790 curmsg->event = SPOE_EV_ON_CLIENT_SESS;
3791 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_SERVER_SESS]))
3792 curmsg->event = SPOE_EV_ON_SERVER_SESS;
3793
3794 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_FE]))
3795 curmsg->event = SPOE_EV_ON_TCP_REQ_FE;
3796 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_BE]))
3797 curmsg->event = SPOE_EV_ON_TCP_REQ_BE;
3798 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_RSP]))
3799 curmsg->event = SPOE_EV_ON_TCP_RSP;
3800
3801 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_FE]))
3802 curmsg->event = SPOE_EV_ON_HTTP_REQ_FE;
3803 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_BE]))
3804 curmsg->event = SPOE_EV_ON_HTTP_REQ_BE;
3805 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_RSP]))
3806 curmsg->event = SPOE_EV_ON_HTTP_RSP;
3807 else {
3808 Alert("parsing [%s:%d] : unkown event '%s'.\n",
3809 file, linenum, args[1]);
3810 err_code |= ERR_ALERT | ERR_ABORT;
3811 goto out;
3812 }
3813 }
3814 else if (!*args[0]) {
3815 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-message section.\n",
3816 file, linenum, args[0]);
3817 err_code |= ERR_ALERT | ERR_FATAL;
3818 goto out;
3819 }
3820 out:
3821 free(errmsg);
3822 return err_code;
3823}
3824
3825/* Return -1 on error, else 0 */
3826static int
3827parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
3828 struct flt_conf *fconf, char **err, void *private)
3829{
3830 struct list backup_sections;
3831 struct spoe_config *conf;
3832 struct spoe_message *msg, *msgback;
3833 struct spoe_msg_placeholder *mp, *mpback;
3834 char *file = NULL, *engine = NULL;
3835 int ret, pos = *cur_arg + 1;
3836
3837 conf = calloc(1, sizeof(*conf));
3838 if (conf == NULL) {
3839 memprintf(err, "%s: out of memory", args[*cur_arg]);
3840 goto error;
3841 }
3842 conf->proxy = px;
3843
3844 while (*args[pos]) {
3845 if (!strcmp(args[pos], "config")) {
3846 if (!*args[pos+1]) {
3847 memprintf(err, "'%s' : '%s' option without value",
3848 args[*cur_arg], args[pos]);
3849 goto error;
3850 }
3851 file = args[pos+1];
3852 pos += 2;
3853 }
3854 else if (!strcmp(args[pos], "engine")) {
3855 if (!*args[pos+1]) {
3856 memprintf(err, "'%s' : '%s' option without value",
3857 args[*cur_arg], args[pos]);
3858 goto error;
3859 }
3860 engine = args[pos+1];
3861 pos += 2;
3862 }
3863 else {
3864 memprintf(err, "unknown keyword '%s'", args[pos]);
3865 goto error;
3866 }
3867 }
3868 if (file == NULL) {
3869 memprintf(err, "'%s' : missing config file", args[*cur_arg]);
3870 goto error;
3871 }
3872
3873 /* backup sections and register SPOE sections */
3874 LIST_INIT(&backup_sections);
3875 cfg_backup_sections(&backup_sections);
3876 cfg_register_section("spoe-agent", cfg_parse_spoe_agent);
3877 cfg_register_section("spoe-message", cfg_parse_spoe_message);
3878
3879 /* Parse SPOE filter configuration file */
3880 curengine = engine;
3881 curproxy = px;
3882 curagent = NULL;
3883 curmsg = NULL;
3884 ret = readcfgfile(file);
3885 curproxy = NULL;
3886
3887 /* unregister SPOE sections and restore previous sections */
3888 cfg_unregister_sections();
3889 cfg_restore_sections(&backup_sections);
3890
3891 if (ret == -1) {
3892 memprintf(err, "Could not open configuration file %s : %s",
3893 file, strerror(errno));
3894 goto error;
3895 }
3896 if (ret & (ERR_ABORT|ERR_FATAL)) {
3897 memprintf(err, "Error(s) found in configuration file %s", file);
3898 goto error;
3899 }
3900
3901 /* Check SPOE agent */
3902 if (curagent == NULL) {
3903 memprintf(err, "No SPOE agent found in file %s", file);
3904 goto error;
3905 }
3906 if (curagent->b.name == NULL) {
3907 memprintf(err, "No backend declared for SPOE agent '%s' declared at %s:%d",
3908 curagent->id, curagent->conf.file, curagent->conf.line);
3909 goto error;
3910 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01003911 if (curagent->timeout.hello == TICK_ETERNITY ||
3912 curagent->timeout.idle == TICK_ETERNITY ||
Christopher Fauletf7a30922016-11-10 15:04:51 +01003913 curagent->timeout.processing == TICK_ETERNITY) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003914 Warning("Proxy '%s': missing timeouts for SPOE agent '%s' declare at %s:%d.\n"
3915 " | While not properly invalid, you will certainly encounter various problems\n"
3916 " | with such a configuration. To fix this, please ensure that all following\n"
Christopher Faulet03a34492016-11-19 16:47:56 +01003917 " | timeouts are set to a non-zero value: 'hello', 'idle', 'processing'.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003918 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3919 }
3920 if (curagent->var_pfx == NULL) {
3921 char *tmp = curagent->id;
3922
3923 while (*tmp) {
3924 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3925 memprintf(err, "Invalid variable prefix '%s' for SPOE agent '%s' declared at %s:%d. "
3926 "Use 'option var-prefix' to set it. Only [a-zA-Z0-9_.] chars are supported.\n",
3927 curagent->id, curagent->id, curagent->conf.file, curagent->conf.line);
3928 goto error;
3929 }
3930 tmp++;
3931 }
3932 curagent->var_pfx = strdup(curagent->id);
3933 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01003934 if (curagent->engine_id == NULL)
3935 curagent->engine_id = generate_pseudo_uuid();
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003936
3937 if (LIST_ISEMPTY(&curmps)) {
3938 Warning("Proxy '%s': No message used by SPOE agent '%s' declared at %s:%d.\n",
3939 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3940 goto finish;
3941 }
3942
3943 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3944 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
Christopher Fauleta21b0642017-01-09 16:56:23 +01003945 struct spoe_arg *arg;
3946 unsigned int where;
3947
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003948 if (!strcmp(msg->id, mp->id)) {
3949 if ((px->cap & (PR_CAP_FE|PR_CAP_BE)) == (PR_CAP_FE|PR_CAP_BE)) {
3950 if (msg->event == SPOE_EV_ON_TCP_REQ_BE)
3951 msg->event = SPOE_EV_ON_TCP_REQ_FE;
3952 if (msg->event == SPOE_EV_ON_HTTP_REQ_BE)
3953 msg->event = SPOE_EV_ON_HTTP_REQ_FE;
3954 }
3955 if (!(px->cap & PR_CAP_FE) && (msg->event == SPOE_EV_ON_CLIENT_SESS ||
3956 msg->event == SPOE_EV_ON_TCP_REQ_FE ||
3957 msg->event == SPOE_EV_ON_HTTP_REQ_FE)) {
3958 Warning("Proxy '%s': frontend event used on a backend proxy at %s:%d.\n",
3959 px->id, msg->conf.file, msg->conf.line);
3960 goto next;
3961 }
3962 if (msg->event == SPOE_EV_NONE) {
3963 Warning("Proxy '%s': Ignore SPOE message without event at %s:%d.\n",
3964 px->id, msg->conf.file, msg->conf.line);
3965 goto next;
3966 }
Christopher Fauleta21b0642017-01-09 16:56:23 +01003967
3968 where = 0;
3969 switch (msg->event) {
3970 case SPOE_EV_ON_CLIENT_SESS:
3971 where |= SMP_VAL_FE_CON_ACC;
3972 break;
3973
3974 case SPOE_EV_ON_TCP_REQ_FE:
3975 where |= SMP_VAL_FE_REQ_CNT;
3976 break;
3977
3978 case SPOE_EV_ON_HTTP_REQ_FE:
3979 where |= SMP_VAL_FE_HRQ_HDR;
3980 break;
3981
3982 case SPOE_EV_ON_TCP_REQ_BE:
3983 if (px->cap & PR_CAP_FE)
3984 where |= SMP_VAL_FE_REQ_CNT;
3985 if (px->cap & PR_CAP_BE)
3986 where |= SMP_VAL_BE_REQ_CNT;
3987 break;
3988
3989 case SPOE_EV_ON_HTTP_REQ_BE:
3990 if (px->cap & PR_CAP_FE)
3991 where |= SMP_VAL_FE_HRQ_HDR;
3992 if (px->cap & PR_CAP_BE)
3993 where |= SMP_VAL_BE_HRQ_HDR;
3994 break;
3995
3996 case SPOE_EV_ON_SERVER_SESS:
3997 where |= SMP_VAL_BE_SRV_CON;
3998 break;
3999
4000 case SPOE_EV_ON_TCP_RSP:
4001 if (px->cap & PR_CAP_FE)
4002 where |= SMP_VAL_FE_RES_CNT;
4003 if (px->cap & PR_CAP_BE)
4004 where |= SMP_VAL_BE_RES_CNT;
4005 break;
4006
4007 case SPOE_EV_ON_HTTP_RSP:
4008 if (px->cap & PR_CAP_FE)
4009 where |= SMP_VAL_FE_HRS_HDR;
4010 if (px->cap & PR_CAP_BE)
4011 where |= SMP_VAL_BE_HRS_HDR;
4012 break;
4013
4014 default:
4015 break;
4016 }
4017
4018 list_for_each_entry(arg, &msg->args, list) {
4019 if (!(arg->expr->fetch->val & where)) {
4020 Warning("Proxy '%s': Ignore SPOE message at %s:%d: "
4021 "some args extract information from '%s', "
4022 "none of which is available here ('%s').\n",
4023 px->id, msg->conf.file, msg->conf.line,
4024 sample_ckp_names(arg->expr->fetch->use),
4025 sample_ckp_names(where));
4026 goto next;
4027 }
4028 }
4029
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004030 msg->agent = curagent;
4031 LIST_DEL(&msg->list);
4032 LIST_ADDQ(&curagent->messages[msg->event], &msg->list);
4033 goto next;
4034 }
4035 }
4036 memprintf(err, "SPOE agent '%s' try to use undefined SPOE message '%s' at %s:%d",
4037 curagent->id, mp->id, curagent->conf.file, curagent->conf.line);
4038 goto error;
4039 next:
4040 continue;
4041 }
4042
4043 finish:
4044 conf->agent = curagent;
4045 list_for_each_entry_safe(mp, mpback, &curmps, list) {
4046 LIST_DEL(&mp->list);
4047 release_spoe_msg_placeholder(mp);
4048 }
4049 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
4050 Warning("Proxy '%s': Ignore unused SPOE messages '%s' declared at %s:%d.\n",
4051 px->id, msg->id, msg->conf.file, msg->conf.line);
4052 LIST_DEL(&msg->list);
4053 release_spoe_message(msg);
4054 }
4055
4056 *cur_arg = pos;
Christopher Faulet3b386a32017-02-23 10:17:15 +01004057 fconf->id = spoe_filter_id;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004058 fconf->ops = &spoe_ops;
4059 fconf->conf = conf;
4060 return 0;
4061
4062 error:
4063 release_spoe_agent(curagent);
4064 list_for_each_entry_safe(mp, mpback, &curmps, list) {
4065 LIST_DEL(&mp->list);
4066 release_spoe_msg_placeholder(mp);
4067 }
4068 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
4069 LIST_DEL(&msg->list);
4070 release_spoe_message(msg);
4071 }
4072 free(conf);
4073 return -1;
4074}
4075
4076
4077/* Declare the filter parser for "spoe" keyword */
4078static struct flt_kw_list flt_kws = { "SPOE", { }, {
4079 { "spoe", parse_spoe_flt, NULL },
4080 { NULL, NULL, NULL },
4081 }
4082};
4083
4084__attribute__((constructor))
4085static void __spoe_init(void)
4086{
4087 flt_register_keywords(&flt_kws);
4088
4089 LIST_INIT(&curmsgs);
4090 LIST_INIT(&curmps);
4091 pool2_spoe_ctx = create_pool("spoe_ctx", sizeof(struct spoe_context), MEM_F_SHARED);
Christopher Faulet42bfa462017-01-04 14:14:19 +01004092 pool2_spoe_appctx = create_pool("spoe_appctx", sizeof(struct spoe_appctx), MEM_F_SHARED);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004093}
4094
4095__attribute__((destructor))
4096static void
4097__spoe_deinit(void)
4098{
4099 pool_destroy2(pool2_spoe_ctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01004100 pool_destroy2(pool2_spoe_appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004101}