blob: 1de125f17d8e6d22289755b3ae869a7b207a7727 [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 Fauletf7e4e7e2016-10-27 22:29:49 +020052/* Minimal size for a frame */
53#define MIN_FRAME_SIZE 256
54
Christopher Fauletea62c2a2016-11-14 10:54:21 +010055/* Flags set on the SPOE agent */
56#define SPOE_FL_CONT_ON_ERR 0x00000001 /* Do not stop events processing when an error occurred */
57
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020058/* Flags set on the SPOE context */
59#define SPOE_CTX_FL_CLI_CONNECTED 0x00000001 /* Set after that on-client-session event was processed */
60#define SPOE_CTX_FL_SRV_CONNECTED 0x00000002 /* Set after that on-server-session event was processed */
61#define SPOE_CTX_FL_REQ_PROCESS 0x00000004 /* Set when SPOE is processing the request */
62#define SPOE_CTX_FL_RSP_PROCESS 0x00000008 /* Set when SPOE is processing the response */
63
64#define SPOE_CTX_FL_PROCESS (SPOE_CTX_FL_REQ_PROCESS|SPOE_CTX_FL_RSP_PROCESS)
65
Christopher Fauleta1cda022016-12-21 08:58:06 +010066/* Flags set on the SPOE applet */
67#define SPOE_APPCTX_FL_PIPELINING 0x00000001 /* Set if pipelining is supported */
68#define SPOE_APPCTX_FL_ASYNC 0x00000002 /* Set if asynchronus frames is supported */
69#define SPOE_APPCTX_FL_PERSIST 0x00000004 /* Set if the applet is persistent */
70
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020071#define SPOE_APPCTX_ERR_NONE 0x00000000 /* no error yet, leave it to zero */
72#define SPOE_APPCTX_ERR_TOUT 0x00000001 /* SPOE applet timeout */
73
74/* All possible states for a SPOE context */
75enum spoe_ctx_state {
76 SPOE_CTX_ST_NONE = 0,
77 SPOE_CTX_ST_READY,
78 SPOE_CTX_ST_SENDING_MSGS,
79 SPOE_CTX_ST_WAITING_ACK,
80 SPOE_CTX_ST_DONE,
81 SPOE_CTX_ST_ERROR,
82};
83
84/* All possible states for a SPOE applet */
85enum spoe_appctx_state {
86 SPOE_APPCTX_ST_CONNECT = 0,
87 SPOE_APPCTX_ST_CONNECTING,
Christopher Fauleta1cda022016-12-21 08:58:06 +010088 SPOE_APPCTX_ST_IDLE,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020089 SPOE_APPCTX_ST_PROCESSING,
90 SPOE_APPCTX_ST_DISCONNECT,
91 SPOE_APPCTX_ST_DISCONNECTING,
92 SPOE_APPCTX_ST_EXIT,
93 SPOE_APPCTX_ST_END,
94};
95
96/* All supported SPOE actions */
97enum spoe_action_type {
98 SPOE_ACT_T_SET_VAR = 1,
99 SPOE_ACT_T_UNSET_VAR,
100 SPOE_ACT_TYPES,
101};
102
103/* All supported SPOE events */
104enum spoe_event {
105 SPOE_EV_NONE = 0,
106
107 /* Request events */
108 SPOE_EV_ON_CLIENT_SESS = 1,
109 SPOE_EV_ON_TCP_REQ_FE,
110 SPOE_EV_ON_TCP_REQ_BE,
111 SPOE_EV_ON_HTTP_REQ_FE,
112 SPOE_EV_ON_HTTP_REQ_BE,
113
114 /* Response events */
115 SPOE_EV_ON_SERVER_SESS,
116 SPOE_EV_ON_TCP_RSP,
117 SPOE_EV_ON_HTTP_RSP,
118
119 SPOE_EV_EVENTS
120};
121
Christopher Fauletb067b062017-01-04 16:39:11 +0100122/* Errors triggered by streams */
123enum spoe_context_error {
124 SPOE_CTX_ERR_NONE = 0,
125 SPOE_CTX_ERR_TOUT,
126 SPOE_CTX_ERR_RES,
127 SPOE_CTX_ERR_UNKNOWN = 255,
128 SPOE_CTX_ERRS,
129};
130
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200131/* Errors triggerd by SPOE applet */
132enum spoe_frame_error {
133 SPOE_FRM_ERR_NONE = 0,
134 SPOE_FRM_ERR_IO,
135 SPOE_FRM_ERR_TOUT,
136 SPOE_FRM_ERR_TOO_BIG,
137 SPOE_FRM_ERR_INVALID,
138 SPOE_FRM_ERR_NO_VSN,
139 SPOE_FRM_ERR_NO_FRAME_SIZE,
140 SPOE_FRM_ERR_NO_CAP,
141 SPOE_FRM_ERR_BAD_VSN,
142 SPOE_FRM_ERR_BAD_FRAME_SIZE,
143 SPOE_FRM_ERR_UNKNOWN = 99,
144 SPOE_FRM_ERRS,
145};
146
147/* Scopes used for variables set by agents. It is a way to be agnotic to vars
148 * scope. */
149enum spoe_vars_scope {
150 SPOE_SCOPE_PROC = 0, /* <=> SCOPE_PROC */
151 SPOE_SCOPE_SESS, /* <=> SCOPE_SESS */
152 SPOE_SCOPE_TXN, /* <=> SCOPE_TXN */
153 SPOE_SCOPE_REQ, /* <=> SCOPE_REQ */
154 SPOE_SCOPE_RES, /* <=> SCOPE_RES */
155};
156
157
158/* Describe an argument that will be linked to a message. It is a sample fetch,
159 * with an optional name. */
160struct spoe_arg {
161 char *name; /* Name of the argument, may be NULL */
162 unsigned int name_len; /* The name length, 0 if NULL */
163 struct sample_expr *expr; /* Sample expression */
164 struct list list; /* Used to chain SPOE args */
165};
166
167/* Used during the config parsing only because, when a SPOE agent section is
168 * parsed, messages can be undefined. */
169struct spoe_msg_placeholder {
170 char *id; /* SPOE message placeholder id */
171 struct list list; /* Use to chain SPOE message placeholders */
172};
173
174/* Describe a message that will be sent in a NOTIFY frame. A message has a name,
175 * an argument list (see above) and it is linked to a specific event. */
176struct spoe_message {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100177 char *id; /* SPOE message id */
178 unsigned int id_len; /* The message id length */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200179 struct spoe_agent *agent; /* SPOE agent owning this SPOE message */
180 struct {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100181 char *file; /* file where the SPOE message appears */
182 int line; /* line where the SPOE message appears */
183 } conf; /* config information */
184 struct list args; /* Arguments added when the SPOE messages is sent */
185 struct list list; /* Used to chain SPOE messages */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200186
187 enum spoe_event event; /* SPOE_EV_* */
188};
189
190/* Describe a SPOE agent. */
191struct spoe_agent {
192 char *id; /* SPOE agent id (name) */
193 struct {
194 char *file; /* file where the SPOE agent appears */
195 int line; /* line where the SPOE agent appears */
196 } conf; /* config information */
197 union {
198 struct proxy *be; /* Backend used by this agent */
199 char *name; /* Backend name used during conf parsing */
200 } b;
201 struct {
Christopher Fauletf7a30922016-11-10 15:04:51 +0100202 unsigned int hello; /* Max time to receive AGENT-HELLO frame (in SPOE applet) */
203 unsigned int idle; /* Max Idle timeout (in SPOE applet) */
Christopher Fauletf7a30922016-11-10 15:04:51 +0100204 unsigned int processing; /* Max time to process an event (in the main stream) */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200205 } timeout;
206
Christopher Fauleta1cda022016-12-21 08:58:06 +0100207 /* Config info */
208 char *engine_id; /* engine-id string */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200209 char *var_pfx; /* Prefix used for vars set by the agent */
Christopher Faulet985532d2016-11-16 15:36:19 +0100210 char *var_on_error; /* Variable to set when an error occured, in the TXN scope */
Christopher Fauletea62c2a2016-11-14 10:54:21 +0100211 unsigned int flags; /* SPOE_FL_* */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100212 unsigned int cps_max; /* Maximum # of connections per second */
213 unsigned int eps_max; /* Maximum # of errors per second */
214 unsigned int max_frame_size; /* Maximum frame size for this agent, before any negotiation */
215 unsigned int min_applets; /* Minimum # applets alive at a time */
216 unsigned int max_fpa; /* Maximum # of frames handled per applet at once */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200217
218 struct list messages[SPOE_EV_EVENTS]; /* List of SPOE messages that will be sent
219 * for each supported events */
220
Christopher Fauleta1cda022016-12-21 08:58:06 +0100221 /* running info */
222 unsigned int applets_act; /* # of applets alive at a time */
223 unsigned int applets_idle; /* # of applets in the state SPOE_APPCTX_ST_IDLE */
224 unsigned int sending_rate; /* the global sending rate */
225
226 struct freq_ctr conn_per_sec; /* connections per second */
227 struct freq_ctr err_per_sec; /* connetion errors per second */
228
229 struct list applets; /* List of available SPOE applets */
230 struct list sending_queue; /* Queue of streams waiting to send data */
231 struct list waiting_queue; /* Queue of streams waiting for a ack, in async mode */
232
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200233};
234
235/* SPOE filter configuration */
236struct spoe_config {
237 struct proxy *proxy; /* Proxy owning the filter */
238 struct spoe_agent *agent; /* Agent used by this filter */
239 struct proxy agent_fe; /* Agent frontend */
240};
241
242/* SPOE context attached to a stream. It is the main structure that handles the
243 * processing offload */
244struct spoe_context {
245 struct filter *filter; /* The SPOE filter */
246 struct stream *strm; /* The stream that should be offloaded */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100247
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200248 struct list *messages; /* List of messages that will be sent during the stream processing */
249 struct buffer *buffer; /* Buffer used to store a NOTIFY or ACK frame */
Christopher Fauleta73e59b2016-12-09 17:30:18 +0100250 struct buffer_wait buffer_wait; /* position in the list of streams waiting for a buffer */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100251 struct list list;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200252
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200253 enum spoe_ctx_state state; /* SPOE_CTX_ST_* */
254 unsigned int flags; /* SPOE_CTX_FL_* */
Christopher Fauletb067b062017-01-04 16:39:11 +0100255 unsigned int status_code; /* SPOE_CTX_ERR_* */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200256
257 unsigned int stream_id; /* stream_id and frame_id are used */
258 unsigned int frame_id; /* to map NOTIFY and ACK frames */
Christopher Fauletf7a30922016-11-10 15:04:51 +0100259 unsigned int process_exp; /* expiration date to process an event */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200260};
261
Christopher Faulet42bfa462017-01-04 14:14:19 +0100262/* SPOE context inside a appctx */
263struct spoe_appctx {
264 struct appctx *owner; /* the owner */
265 struct task *task; /* task to handle applet timeouts */
266 struct spoe_agent *agent; /* agent on which the applet is attached */
267
268 unsigned int version; /* the negotiated version */
269 unsigned int max_frame_size; /* the negotiated max-frame-size value */
270 unsigned int flags; /* SPOE_APPCTX_FL_* */
271
Christopher Fauletb067b062017-01-04 16:39:11 +0100272 unsigned int status_code; /* SPOE_FRM_ERR_* */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100273 struct list waiting_queue; /* list of streams waiting for a ACK frame, in sync and pipelining mode */
274 struct list list; /* next spoe appctx for the same agent */
275};
276
277#define SPOE_APPCTX(appctx) ((struct spoe_appctx *)((appctx)->ctx.spoe.ptr))
278
Christopher Faulet3b386a32017-02-23 10:17:15 +0100279/* SPOE filter id. Used to identify SPOE filters */
280const char *spoe_filter_id = "SPOE filter";
281
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200282/* Set if the handle on SIGUSR1 is registered */
283static int sighandler_registered = 0;
284
285/* proxy used during the parsing */
286struct proxy *curproxy = NULL;
287
288/* The name of the SPOE engine, used during the parsing */
289char *curengine = NULL;
290
291/* SPOE agent used during the parsing */
292struct spoe_agent *curagent = NULL;
293
294/* SPOE message used during the parsing */
295struct spoe_message *curmsg = NULL;
296
297/* list of SPOE messages and placeholders used during the parsing */
298struct list curmsgs;
299struct list curmps;
300
Christopher Faulet42bfa462017-01-04 14:14:19 +0100301/* Pools used to allocate SPOE structs */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200302static struct pool_head *pool2_spoe_ctx = NULL;
Christopher Faulet42bfa462017-01-04 14:14:19 +0100303static struct pool_head *pool2_spoe_appctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200304
305/* Temporary variables used to ease error processing */
306int spoe_status_code = SPOE_FRM_ERR_NONE;
307char spoe_reason[256];
308
309struct flt_ops spoe_ops;
310
Christopher Fauleta1cda022016-12-21 08:58:06 +0100311static int queue_spoe_context(struct spoe_context *ctx);
312static int acquire_spoe_buffer(struct spoe_context *ctx);
313static void release_spoe_buffer(struct spoe_context *ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200314
315/********************************************************************
316 * helper functions/globals
317 ********************************************************************/
318static void
319release_spoe_msg_placeholder(struct spoe_msg_placeholder *mp)
320{
321 if (!mp)
322 return;
323 free(mp->id);
324 free(mp);
325}
326
327
328static void
329release_spoe_message(struct spoe_message *msg)
330{
331 struct spoe_arg *arg, *back;
332
333 if (!msg)
334 return;
335 free(msg->id);
336 free(msg->conf.file);
337 list_for_each_entry_safe(arg, back, &msg->args, list) {
338 release_sample_expr(arg->expr);
339 free(arg->name);
340 LIST_DEL(&arg->list);
341 free(arg);
342 }
343 free(msg);
344}
345
346static void
347release_spoe_agent(struct spoe_agent *agent)
348{
349 struct spoe_message *msg, *back;
350 int i;
351
352 if (!agent)
353 return;
354 free(agent->id);
355 free(agent->conf.file);
356 free(agent->var_pfx);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100357 free(agent->engine_id);
Christopher Faulet985532d2016-11-16 15:36:19 +0100358 free(agent->var_on_error);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200359 for (i = 0; i < SPOE_EV_EVENTS; ++i) {
360 list_for_each_entry_safe(msg, back, &agent->messages[i], list) {
361 LIST_DEL(&msg->list);
362 release_spoe_message(msg);
363 }
364 }
365 free(agent);
366}
367
368static const char *spoe_frm_err_reasons[SPOE_FRM_ERRS] = {
369 [SPOE_FRM_ERR_NONE] = "normal",
370 [SPOE_FRM_ERR_IO] = "I/O error",
371 [SPOE_FRM_ERR_TOUT] = "a timeout occurred",
372 [SPOE_FRM_ERR_TOO_BIG] = "frame is too big",
373 [SPOE_FRM_ERR_INVALID] = "invalid frame received",
374 [SPOE_FRM_ERR_NO_VSN] = "version value not found",
375 [SPOE_FRM_ERR_NO_FRAME_SIZE] = "max-frame-size value not found",
376 [SPOE_FRM_ERR_NO_CAP] = "capabilities value not found",
377 [SPOE_FRM_ERR_BAD_VSN] = "unsupported version",
378 [SPOE_FRM_ERR_BAD_FRAME_SIZE] = "max-frame-size too big or too small",
379 [SPOE_FRM_ERR_UNKNOWN] = "an unknown error occurred",
380};
381
382static const char *spoe_event_str[SPOE_EV_EVENTS] = {
383 [SPOE_EV_ON_CLIENT_SESS] = "on-client-session",
384 [SPOE_EV_ON_TCP_REQ_FE] = "on-frontend-tcp-request",
385 [SPOE_EV_ON_TCP_REQ_BE] = "on-backend-tcp-request",
386 [SPOE_EV_ON_HTTP_REQ_FE] = "on-frontend-http-request",
387 [SPOE_EV_ON_HTTP_REQ_BE] = "on-backend-http-request",
388
389 [SPOE_EV_ON_SERVER_SESS] = "on-server-session",
390 [SPOE_EV_ON_TCP_RSP] = "on-tcp-response",
391 [SPOE_EV_ON_HTTP_RSP] = "on-http-response",
392};
393
394
395#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
396
397static const char *spoe_ctx_state_str[SPOE_CTX_ST_ERROR+1] = {
398 [SPOE_CTX_ST_NONE] = "NONE",
399 [SPOE_CTX_ST_READY] = "READY",
400 [SPOE_CTX_ST_SENDING_MSGS] = "SENDING_MSGS",
401 [SPOE_CTX_ST_WAITING_ACK] = "WAITING_ACK",
402 [SPOE_CTX_ST_DONE] = "DONE",
403 [SPOE_CTX_ST_ERROR] = "ERROR",
404};
405
406static const char *spoe_appctx_state_str[SPOE_APPCTX_ST_END+1] = {
407 [SPOE_APPCTX_ST_CONNECT] = "CONNECT",
408 [SPOE_APPCTX_ST_CONNECTING] = "CONNECTING",
Christopher Fauleta1cda022016-12-21 08:58:06 +0100409 [SPOE_APPCTX_ST_IDLE] = "IDLE",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200410 [SPOE_APPCTX_ST_PROCESSING] = "PROCESSING",
411 [SPOE_APPCTX_ST_DISCONNECT] = "DISCONNECT",
412 [SPOE_APPCTX_ST_DISCONNECTING] = "DISCONNECTING",
413 [SPOE_APPCTX_ST_EXIT] = "EXIT",
414 [SPOE_APPCTX_ST_END] = "END",
415};
416
417#endif
Christopher Fauleta1cda022016-12-21 08:58:06 +0100418
419static char *
420generate_pseudo_uuid()
421{
422 static int init = 0;
423
424 const char uuid_fmt[] = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
425 const char uuid_chr[] = "0123456789ABCDEF-";
426 char *uuid;
427 int i;
428
429 if ((uuid = calloc(1, sizeof(uuid_fmt))) == NULL)
430 return NULL;
431
432 if (!init) {
433 srand(now_ms);
434 init = 1;
435 }
436
437 for (i = 0; i < sizeof(uuid_fmt)-1; i++) {
438 int r = rand () % 16;
439
440 switch (uuid_fmt[i]) {
441 case 'x' : uuid[i] = uuid_chr[r]; break;
442 case 'y' : uuid[i] = uuid_chr[(r & 0x03) | 0x08]; break;
443 default : uuid[i] = uuid_fmt[i]; break;
444 }
445 }
446 return uuid;
447}
448
449static inline unsigned int
450min_applets_act(struct spoe_agent *agent)
451{
452 unsigned int nbsrv;
453
454 if (agent->min_applets)
455 return agent->min_applets;
456
457 nbsrv = (agent->b.be->srv_act ? agent->b.be->srv_act : agent->b.be->srv_bck);
458 return 2*nbsrv;
459}
460
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200461/********************************************************************
462 * Functions that encode/decode SPOE frames
463 ********************************************************************/
464/* Frame Types sent by HAProxy and by agents */
465enum spoe_frame_type {
466 /* Frames sent by HAProxy */
467 SPOE_FRM_T_HAPROXY_HELLO = 1,
468 SPOE_FRM_T_HAPROXY_DISCON,
469 SPOE_FRM_T_HAPROXY_NOTIFY,
470
471 /* Frames sent by the agents */
472 SPOE_FRM_T_AGENT_HELLO = 101,
473 SPOE_FRM_T_AGENT_DISCON,
474 SPOE_FRM_T_AGENT_ACK
475};
476
477/* All supported data types */
478enum spoe_data_type {
479 SPOE_DATA_T_NULL = 0,
480 SPOE_DATA_T_BOOL,
481 SPOE_DATA_T_INT32,
482 SPOE_DATA_T_UINT32,
483 SPOE_DATA_T_INT64,
484 SPOE_DATA_T_UINT64,
485 SPOE_DATA_T_IPV4,
486 SPOE_DATA_T_IPV6,
487 SPOE_DATA_T_STR,
488 SPOE_DATA_T_BIN,
489 SPOE_DATA_TYPES
490};
491
492/* Masks to get data type or flags value */
493#define SPOE_DATA_T_MASK 0x0F
494#define SPOE_DATA_FL_MASK 0xF0
495
496/* Flags to set Boolean values */
497#define SPOE_DATA_FL_FALSE 0x00
498#define SPOE_DATA_FL_TRUE 0x10
499
500/* Helper to get static string length, excluding the terminating null byte */
501#define SLEN(str) (sizeof(str)-1)
502
503/* Predefined key used in HELLO/DISCONNECT frames */
504#define SUPPORTED_VERSIONS_KEY "supported-versions"
505#define VERSION_KEY "version"
506#define MAX_FRAME_SIZE_KEY "max-frame-size"
507#define CAPABILITIES_KEY "capabilities"
Christopher Fauleta1cda022016-12-21 08:58:06 +0100508#define ENGINE_ID_KEY "engine-id"
Christopher Fauletba7bc162016-11-07 21:07:38 +0100509#define HEALTHCHECK_KEY "healthcheck"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200510#define STATUS_CODE_KEY "status-code"
511#define MSG_KEY "message"
512
513struct spoe_version {
514 char *str;
515 int min;
516 int max;
517};
518
519/* All supported versions */
520static struct spoe_version supported_versions[] = {
521 {"1.0", 1000, 1000},
522 {NULL, 0, 0}
523};
524
525/* Comma-separated list of supported versions */
526#define SUPPORTED_VERSIONS_VAL "1.0"
527
528/* Comma-separated list of supported capabilities (none for now) */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100529//#define CAPABILITIES_VAL ""
530#define CAPABILITIES_VAL "pipelining,async"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200531
532static int
533decode_spoe_version(const char *str, size_t len)
534{
535 char tmp[len+1], *start, *end;
536 double d;
537 int vsn = -1;
538
539 memset(tmp, 0, len+1);
540 memcpy(tmp, str, len);
541
542 start = tmp;
543 while (isspace(*start))
544 start++;
545
546 d = strtod(start, &end);
547 if (d == 0 || start == end)
548 goto out;
549
550 if (*end) {
551 while (isspace(*end))
552 end++;
553 if (*end)
554 goto out;
555 }
556 vsn = (int)(d * 1000);
557 out:
558 return vsn;
559}
560
561/* Encode a variable-length integer. This function never fails and returns the
562 * number of written bytes. */
563static int
564encode_spoe_varint(uint64_t i, char *buf)
565{
566 int idx;
567
568 if (i < 240) {
569 buf[0] = (unsigned char)i;
570 return 1;
571 }
572
573 buf[0] = (unsigned char)i | 240;
574 i = (i - 240) >> 4;
575 for (idx = 1; i >= 128; ++idx) {
576 buf[idx] = (unsigned char)i | 128;
577 i = (i - 128) >> 7;
578 }
579 buf[idx++] = (unsigned char)i;
580 return idx;
581}
582
583/* Decode a varable-length integer. If the decoding fails, -1 is returned. This
584 * happens when the buffer's end in reached. On success, the number of read
585 * bytes is returned. */
586static int
587decode_spoe_varint(const char *buf, const char *end, uint64_t *i)
588{
589 unsigned char *msg = (unsigned char *)buf;
590 int idx = 0;
591
592 if (msg > (unsigned char *)end)
593 return -1;
594
595 if (msg[0] < 240) {
596 *i = msg[0];
597 return 1;
598 }
599 *i = msg[0];
600 do {
601 ++idx;
602 if (msg+idx > (unsigned char *)end)
603 return -1;
604 *i += (uint64_t)msg[idx] << (4 + 7 * (idx-1));
605 } while (msg[idx] >= 128);
606 return (idx + 1);
607}
608
609/* Encode a string. The string will be prefix by its length, encoded as a
610 * variable-length integer. This function never fails and returns the number of
611 * written bytes. */
612static int
613encode_spoe_string(const char *str, size_t len, char *dst)
614{
615 int idx = 0;
616
617 if (!len) {
618 dst[0] = 0;
619 return 1;
620 }
621
622 idx += encode_spoe_varint(len, dst);
623 memcpy(dst+idx, str, len);
624 return (idx + len);
625}
626
627/* Decode a string. Its length is decoded first as a variable-length integer. If
628 * it succeeds, and if the string length is valid, the begin of the string is
629 * saved in <*str>, its length is saved in <*len> and the total numbre of bytes
630 * read is returned. If an error occurred, -1 is returned and <*str> remains
631 * NULL. */
632static int
633decode_spoe_string(char *buf, char *end, char **str, uint64_t *len)
634{
635 int i, idx = 0;
636
637 *str = NULL;
638 *len = 0;
639
640 if ((i = decode_spoe_varint(buf, end, len)) == -1)
641 goto error;
642 idx += i;
643 if (buf + idx + *len > end)
644 goto error;
645
646 *str = buf+idx;
647 return (idx + *len);
648
649 error:
650 return -1;
651}
652
653/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number
654 * of bytes read is returned. A types data is composed of a type (1 byte) and
655 * corresponding data:
656 * - boolean: non additional data (0 bytes)
657 * - integers: a variable-length integer (see decode_spoe_varint)
658 * - ipv4: 4 bytes
659 * - ipv6: 16 bytes
660 * - binary and string: a buffer prefixed by its size, a variable-length
661 * integer (see decode_spoe_string) */
662static int
663skip_spoe_data(char *frame, char *end)
664{
665 uint64_t sz = 0;
666 int i, idx = 0;
667
668 if (frame > end)
669 return -1;
670
671 switch (frame[idx++] & SPOE_DATA_T_MASK) {
672 case SPOE_DATA_T_BOOL:
673 break;
674 case SPOE_DATA_T_INT32:
675 case SPOE_DATA_T_INT64:
676 case SPOE_DATA_T_UINT32:
677 case SPOE_DATA_T_UINT64:
678 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
679 return -1;
680 idx += i;
681 break;
682 case SPOE_DATA_T_IPV4:
683 idx += 4;
684 break;
685 case SPOE_DATA_T_IPV6:
686 idx += 16;
687 break;
688 case SPOE_DATA_T_STR:
689 case SPOE_DATA_T_BIN:
690 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
691 return -1;
692 idx += i + sz;
693 break;
694 }
695
696 if (frame+idx > end)
697 return -1;
698 return idx;
699}
700
701/* Decode a typed data. If an error occurred, -1 is returned, otherwise the
702 * number of read bytes is returned. See skip_spoe_data for details. */
703static int
704decode_spoe_data(char *frame, char *end, struct sample *smp)
705{
706 uint64_t sz = 0;
707 int type, i, idx = 0;
708
709 if (frame > end)
710 return -1;
711
712 type = frame[idx++];
713 switch (type & SPOE_DATA_T_MASK) {
714 case SPOE_DATA_T_BOOL:
715 smp->data.u.sint = ((type & SPOE_DATA_FL_TRUE) == SPOE_DATA_FL_TRUE);
716 smp->data.type = SMP_T_BOOL;
717 break;
718 case SPOE_DATA_T_INT32:
719 case SPOE_DATA_T_INT64:
720 case SPOE_DATA_T_UINT32:
721 case SPOE_DATA_T_UINT64:
722 if ((i = decode_spoe_varint(frame+idx, end, (uint64_t *)&smp->data.u.sint)) == -1)
723 return -1;
724 idx += i;
725 smp->data.type = SMP_T_SINT;
726 break;
727 case SPOE_DATA_T_IPV4:
728 if (frame+idx+4 > end)
729 return -1;
730 memcpy(&smp->data.u.ipv4, frame+idx, 4);
731 smp->data.type = SMP_T_IPV4;
732 idx += 4;
733 break;
734 case SPOE_DATA_T_IPV6:
735 if (frame+idx+16 > end)
736 return -1;
737 memcpy(&smp->data.u.ipv6, frame+idx, 16);
738 smp->data.type = SMP_T_IPV6;
739 idx += 16;
740 break;
741 case SPOE_DATA_T_STR:
742 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
743 return -1;
744 idx += i;
745 if (frame+idx+sz > end)
746 return -1;
747 smp->data.u.str.str = frame+idx;
748 smp->data.u.str.len = sz;
749 smp->data.type = SMP_T_STR;
750 idx += sz;
751 break;
752 case SPOE_DATA_T_BIN:
753 if ((i = decode_spoe_varint(frame+idx, end, &sz)) == -1)
754 return -1;
755 idx += i;
756 if (frame+idx+sz > end)
757 return -1;
758 smp->data.u.str.str = frame+idx;
759 smp->data.u.str.len = sz;
760 smp->data.type = SMP_T_BIN;
761 idx += sz;
762 break;
763 }
764
765 if (frame+idx > end)
766 return -1;
767 return idx;
768}
769
770/* Skip an action in a frame received from an agent. If an error occurred, -1 is
771 * returned, otherwise the number of read bytes is returned. An action is
772 * composed of the action type followed by a typed data. */
773static int
774skip_spoe_action(char *frame, char *end)
775{
776 int n, i, idx = 0;
777
778 if (frame+2 > end)
779 return -1;
780
781 idx++; /* Skip the action type */
782 n = frame[idx++];
783 while (n-- > 0) {
784 if ((i = skip_spoe_data(frame+idx, end)) == -1)
785 return -1;
786 idx += i;
787 }
788
789 if (frame+idx > end)
790 return -1;
791 return idx;
792}
793
794/* Encode HELLO frame sent by HAProxy to an agent. It returns the frame size on
795 * success, 0 if the frame can be ignored and -1 if an error occurred. */
796static int
797prepare_spoe_hahello_frame(struct appctx *appctx, char *frame, size_t size)
798{
Christopher Faulet42bfa462017-01-04 14:14:19 +0100799 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200800 int idx = 0;
801 size_t max = (7 /* TYPE + METADATA */
802 + 1 + SLEN(SUPPORTED_VERSIONS_KEY) + 1 + 1 + SLEN(SUPPORTED_VERSIONS_VAL)
803 + 1 + SLEN(MAX_FRAME_SIZE_KEY) + 1 + 4
Christopher Fauleta1cda022016-12-21 08:58:06 +0100804 + 1 + SLEN(CAPABILITIES_KEY) + 1 + 1 + SLEN(CAPABILITIES_VAL)
805 + 1 + SLEN(ENGINE_ID_KEY) + 1 + 1 + 36);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200806
Christopher Fauletb067b062017-01-04 16:39:11 +0100807 if (size < max) {
808 spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200809 return -1;
Christopher Fauletb067b062017-01-04 16:39:11 +0100810 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200811
812 /* Frame type */
813 frame[idx++] = SPOE_FRM_T_HAPROXY_HELLO;
814
815 /* No flags for now */
816 memset(frame+idx, 0, 4);
817 idx += 4;
818
819 /* No stream-id and frame-id for HELLO frames */
820 frame[idx++] = 0;
821 frame[idx++] = 0;
822
823 /* There are 3 mandatory items: "supported-versions", "max-frame-size"
824 * and "capabilities" */
825
826 /* "supported-versions" K/V item */
827 idx += encode_spoe_string(SUPPORTED_VERSIONS_KEY, SLEN(SUPPORTED_VERSIONS_KEY), frame+idx);
828 frame[idx++] = SPOE_DATA_T_STR;
829 idx += encode_spoe_string(SUPPORTED_VERSIONS_VAL, SLEN(SUPPORTED_VERSIONS_VAL), frame+idx);
830
831 /* "max-fram-size" K/V item */
832 idx += encode_spoe_string(MAX_FRAME_SIZE_KEY, SLEN(MAX_FRAME_SIZE_KEY), frame+idx);
833 frame[idx++] = SPOE_DATA_T_UINT32;
Christopher Faulet42bfa462017-01-04 14:14:19 +0100834 idx += encode_spoe_varint(SPOE_APPCTX(appctx)->max_frame_size, frame+idx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200835
836 /* "capabilities" K/V item */
837 idx += encode_spoe_string(CAPABILITIES_KEY, SLEN(CAPABILITIES_KEY), frame+idx);
838 frame[idx++] = SPOE_DATA_T_STR;
839 idx += encode_spoe_string(CAPABILITIES_VAL, SLEN(CAPABILITIES_VAL), frame+idx);
840
Christopher Fauleta1cda022016-12-21 08:58:06 +0100841 /* "engine-id" K/V item */
842 if (agent != NULL && agent->engine_id != NULL) {
843 idx += encode_spoe_string(ENGINE_ID_KEY, SLEN(ENGINE_ID_KEY), frame+idx);
844 frame[idx++] = SPOE_DATA_T_STR;
845 idx += encode_spoe_string(agent->engine_id, strlen(agent->engine_id), frame+idx);
846 }
847
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200848 return idx;
849}
850
851/* Encode DISCONNECT frame sent by HAProxy to an agent. It returns the frame
852 * size on success, 0 if the frame can be ignored and -1 if an error
853 * occurred. */
854static int
855prepare_spoe_hadiscon_frame(struct appctx *appctx, char *frame, size_t size)
856{
857 const char *reason;
858 int rlen, idx = 0;
859 size_t max = (7 /* TYPE + METADATA */
860 + 1 + SLEN(STATUS_CODE_KEY) + 1 + 2
861 + 1 + SLEN(MSG_KEY) + 1 + 2 + 255);
862
863 if (size < max)
864 return -1;
865
866 /* Get the message corresponding to the status code */
867 if (spoe_status_code >= SPOE_FRM_ERRS)
868 spoe_status_code = SPOE_FRM_ERR_UNKNOWN;
869 reason = spoe_frm_err_reasons[spoe_status_code];
870 rlen = strlen(reason);
871
872 /* Frame type */
873 frame[idx++] = SPOE_FRM_T_HAPROXY_DISCON;
874
875 /* No flags for now */
876 memset(frame+idx, 0, 4);
877 idx += 4;
878
879 /* No stream-id and frame-id for DISCONNECT frames */
880 frame[idx++] = 0;
881 frame[idx++] = 0;
882
883 /* There are 2 mandatory items: "status-code" and "message" */
884
885 /* "status-code" K/V item */
886 idx += encode_spoe_string(STATUS_CODE_KEY, SLEN(STATUS_CODE_KEY), frame+idx);
887 frame[idx++] = SPOE_DATA_T_UINT32;
888 idx += encode_spoe_varint(spoe_status_code, frame+idx);
889
890 /* "message" K/V item */
891 idx += encode_spoe_string(MSG_KEY, SLEN(MSG_KEY), frame+idx);
892 frame[idx++] = SPOE_DATA_T_STR;
893 idx += encode_spoe_string(reason, rlen, frame+idx);
894
895 return idx;
896}
897
898/* Encode NOTIFY frame sent by HAProxy to an agent. It returns the frame size on
899 * success, 0 if the frame can be ignored and -1 if an error occurred. */
900static int
Christopher Fauleta1cda022016-12-21 08:58:06 +0100901prepare_spoe_hanotify_frame(struct appctx *appctx, struct spoe_context *ctx,
902 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200903{
Christopher Fauleta1cda022016-12-21 08:58:06 +0100904 int idx = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200905
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200906 frame[idx++] = SPOE_FRM_T_HAPROXY_NOTIFY;
907
908 /* No flags for now */
909 memset(frame+idx, 0, 4);
910 idx += 4;
911
912 /* Set stream-id and frame-id */
913 idx += encode_spoe_varint(ctx->stream_id, frame+idx);
914 idx += encode_spoe_varint(ctx->frame_id, frame+idx);
915
916 /* Copy encoded messages */
Christopher Fauletb067b062017-01-04 16:39:11 +0100917 if (idx + ctx->buffer->i > size) {
918 spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100919 return 0;
Christopher Fauletb067b062017-01-04 16:39:11 +0100920 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100921
922 /* Copy encoded messages */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200923 memcpy(frame+idx, ctx->buffer->p, ctx->buffer->i);
924 idx += ctx->buffer->i;
925
926 return idx;
927}
928
929/* Decode HELLO frame sent by an agent. It returns the number of by read bytes
930 * on success, 0 if the frame can be ignored and -1 if an error occurred. */
931static int
932handle_spoe_agenthello_frame(struct appctx *appctx, char *frame, size_t size)
933{
Christopher Fauleta1cda022016-12-21 08:58:06 +0100934 int vsn, max_frame_size, flags;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200935 int i, idx = 0;
936 size_t min_size = (7 /* TYPE + METADATA */
937 + 1 + SLEN(VERSION_KEY) + 1 + 1 + 3
938 + 1 + SLEN(MAX_FRAME_SIZE_KEY) + 1 + 1
939 + 1 + SLEN(CAPABILITIES_KEY) + 1 + 1 + 0);
940
941 /* Check frame type */
942 if (frame[idx++] != SPOE_FRM_T_AGENT_HELLO)
943 return 0;
944
945 if (size < min_size) {
946 spoe_status_code = SPOE_FRM_ERR_INVALID;
947 return -1;
948 }
949
950 /* Skip flags: fragmentation is not supported for now */
951 idx += 4;
952
953 /* stream-id and frame-id must be cleared */
954 if (frame[idx] != 0 || frame[idx+1] != 0) {
955 spoe_status_code = SPOE_FRM_ERR_INVALID;
956 return -1;
957 }
958 idx += 2;
959
960 /* There are 3 mandatory items: "version", "max-frame-size" and
961 * "capabilities" */
962
963 /* Loop on K/V items */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100964 vsn = max_frame_size = flags = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200965 while (idx < size) {
966 char *str;
967 uint64_t sz;
968
969 /* Decode the item key */
970 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
971 if (str == NULL) {
972 spoe_status_code = SPOE_FRM_ERR_INVALID;
973 return -1;
974 }
975 /* Check "version" K/V item */
976 if (!memcmp(str, VERSION_KEY, sz)) {
977 /* The value must be a string */
978 if ((frame[idx++] & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
979 spoe_status_code = SPOE_FRM_ERR_INVALID;
980 return -1;
981 }
982 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
983 if (str == NULL) {
984 spoe_status_code = SPOE_FRM_ERR_INVALID;
985 return -1;
986 }
987
988 vsn = decode_spoe_version(str, sz);
989 if (vsn == -1) {
990 spoe_status_code = SPOE_FRM_ERR_BAD_VSN;
991 return -1;
992 }
993 for (i = 0; supported_versions[i].str != NULL; ++i) {
994 if (vsn >= supported_versions[i].min &&
995 vsn <= supported_versions[i].max)
996 break;
997 }
998 if (supported_versions[i].str == NULL) {
999 spoe_status_code = SPOE_FRM_ERR_BAD_VSN;
1000 return -1;
1001 }
1002 }
1003 /* Check "max-frame-size" K/V item */
1004 else if (!memcmp(str, MAX_FRAME_SIZE_KEY, sz)) {
1005 int type;
1006
1007 /* The value must be integer */
1008 type = frame[idx++];
1009 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1010 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1011 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1012 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
1013 spoe_status_code = SPOE_FRM_ERR_INVALID;
1014 return -1;
1015 }
1016 if ((i = decode_spoe_varint(frame+idx, frame+size, &sz)) == -1) {
1017 spoe_status_code = SPOE_FRM_ERR_INVALID;
1018 return -1;
1019 }
1020 idx += i;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001021 if (sz < MIN_FRAME_SIZE || sz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001022 spoe_status_code = SPOE_FRM_ERR_BAD_FRAME_SIZE;
1023 return -1;
1024 }
1025 max_frame_size = sz;
1026 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001027 /* Check "capabilities" K/V item */
1028 else if (!memcmp(str, CAPABILITIES_KEY, sz)) {
1029 int i;
1030
1031 /* The value must be a string */
1032 if ((frame[idx++] & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1033 spoe_status_code = SPOE_FRM_ERR_INVALID;
1034 return -1;
1035 }
1036 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1037 if (str == NULL)
1038 continue;
1039
1040 i = 0;
1041 while (i < sz) {
1042 char *delim;
1043
1044 /* Skip leading spaces */
1045 for (; isspace(str[i]) && i < sz; i++);
1046
1047 if (sz - i >= 10 && !strncmp(str + i, "pipelining", 10)) {
1048 i += 10;
1049 if (sz == i || isspace(str[i]) || str[i] == ',')
1050 flags |= SPOE_APPCTX_FL_PIPELINING;
1051 }
1052 else if (sz - i >= 5 && !strncmp(str + i, "async", 5)) {
1053 i += 5;
1054 if (sz == i || isspace(str[i]) || str[i] == ',')
1055 flags |= SPOE_APPCTX_FL_ASYNC;
1056 }
1057
1058 if (sz == i || (delim = memchr(str + i, ',', sz-i)) == NULL)
1059 break;
1060 i = (delim - str) + 1;
1061 }
1062 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001063 else {
1064 /* Silently ignore unknown item */
1065 if ((i = skip_spoe_data(frame+idx, frame+size)) == -1) {
1066 spoe_status_code = SPOE_FRM_ERR_INVALID;
1067 return -1;
1068 }
1069 idx += i;
1070 }
1071 }
1072
1073 /* Final checks */
1074 if (!vsn) {
1075 spoe_status_code = SPOE_FRM_ERR_NO_VSN;
1076 return -1;
1077 }
1078 if (!max_frame_size) {
1079 spoe_status_code = SPOE_FRM_ERR_NO_FRAME_SIZE;
1080 return -1;
1081 }
1082
Christopher Faulet42bfa462017-01-04 14:14:19 +01001083 SPOE_APPCTX(appctx)->version = (unsigned int)vsn;
1084 SPOE_APPCTX(appctx)->max_frame_size = (unsigned int)max_frame_size;
1085 SPOE_APPCTX(appctx)->flags |= flags;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001086 return idx;
1087}
1088
1089/* Decode DISCONNECT frame sent by an agent. It returns the number of by read
1090 * bytes on success, 0 if the frame can be ignored and -1 if an error
1091 * occurred. */
1092static int
1093handle_spoe_agentdiscon_frame(struct appctx *appctx, char *frame, size_t size)
1094{
1095 int i, idx = 0;
1096 size_t min_size = (7 /* TYPE + METADATA */
1097 + 1 + SLEN(STATUS_CODE_KEY) + 1 + 1
1098 + 1 + SLEN(MSG_KEY) + 1 + 1);
1099
1100 /* Check frame type */
1101 if (frame[idx++] != SPOE_FRM_T_AGENT_DISCON)
1102 return 0;
1103
1104 if (size < min_size) {
1105 spoe_status_code = SPOE_FRM_ERR_INVALID;
1106 return -1;
1107 }
1108
1109 /* Skip flags: fragmentation is not supported for now */
1110 idx += 4;
1111
1112 /* stream-id and frame-id must be cleared */
1113 if (frame[idx] != 0 || frame[idx+1] != 0) {
1114 spoe_status_code = SPOE_FRM_ERR_INVALID;
1115 return -1;
1116 }
1117 idx += 2;
1118
1119 /* There are 2 mandatory items: "status-code" and "message" */
1120
1121 /* Loop on K/V items */
1122 while (idx < size) {
1123 char *str;
1124 uint64_t sz;
1125
1126 /* Decode the item key */
1127 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1128 if (str == NULL) {
1129 spoe_status_code = SPOE_FRM_ERR_INVALID;
1130 return -1;
1131 }
1132
1133 /* Check "status-code" K/V item */
1134 if (!memcmp(str, STATUS_CODE_KEY, sz)) {
1135 int type;
1136
1137 /* The value must be an integer */
1138 type = frame[idx++];
1139 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
1140 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
1141 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
1142 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
1143 spoe_status_code = SPOE_FRM_ERR_INVALID;
1144 return -1;
1145 }
1146 if ((i = decode_spoe_varint(frame+idx, frame+size, &sz)) == -1) {
1147 spoe_status_code = SPOE_FRM_ERR_INVALID;
1148 return -1;
1149 }
1150 idx += i;
1151 spoe_status_code = sz;
1152 }
1153
1154 /* Check "message" K/V item */
1155 else if (sz && !memcmp(str, MSG_KEY, sz)) {
1156 /* The value must be a string */
1157 if ((frame[idx++] & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
1158 spoe_status_code = SPOE_FRM_ERR_INVALID;
1159 return -1;
1160 }
1161 idx += decode_spoe_string(frame+idx, frame+size, &str, &sz);
1162 if (str == NULL || sz > 255) {
1163 spoe_status_code = SPOE_FRM_ERR_INVALID;
1164 return -1;
1165 }
1166 memcpy(spoe_reason, str, sz);
1167 spoe_reason[sz] = 0;
1168 }
1169 else {
1170 /* Silently ignore unknown item */
1171 if ((i = skip_spoe_data(frame+idx, frame+size)) == -1) {
1172 spoe_status_code = SPOE_FRM_ERR_INVALID;
1173 return -1;
1174 }
1175 idx += i;
1176 }
1177 }
1178
1179 return idx;
1180}
1181
1182
Christopher Fauleta1cda022016-12-21 08:58:06 +01001183/* Decode ACK frame sent by an agent. It returns the number of read bytes on
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001184 * success, 0 if the frame can be ignored and -1 if an error occurred. */
1185static int
1186handle_spoe_agentack_frame(struct appctx *appctx, char *frame, size_t size)
1187{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001188 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001189 struct spoe_context *ctx, *back;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001190 uint64_t stream_id, frame_id;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001191 int i, idx = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001192 size_t min_size = (7 /* TYPE + METADATA */);
1193
1194 /* Check frame type */
1195 if (frame[idx++] != SPOE_FRM_T_AGENT_ACK)
1196 return 0;
1197
1198 if (size < min_size) {
1199 spoe_status_code = SPOE_FRM_ERR_INVALID;
1200 return -1;
1201 }
1202
1203 /* Skip flags: fragmentation is not supported for now */
1204 idx += 4;
1205
1206 /* Get the stream-id and the frame-id */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001207 if ((i = decode_spoe_varint(frame+idx, frame+size, &stream_id)) == -1)
1208 return 0;
1209 idx += i;
1210 if ((i= decode_spoe_varint(frame+idx, frame+size, &frame_id)) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001211 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001212 idx += i;
1213
Christopher Faulet42bfa462017-01-04 14:14:19 +01001214 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001215 list_for_each_entry_safe(ctx, back, &agent->waiting_queue, list) {
1216 if (ctx->stream_id == (unsigned int)stream_id &&
1217 ctx->frame_id == (unsigned int)frame_id)
1218 goto found;
1219 }
1220 }
1221 else {
Christopher Faulet42bfa462017-01-04 14:14:19 +01001222 list_for_each_entry_safe(ctx, back, &SPOE_APPCTX(appctx)->waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001223 if (ctx->stream_id == (unsigned int)stream_id &&
1224 ctx->frame_id == (unsigned int)frame_id)
1225 goto found;
1226 }
1227 }
1228
1229 /* No Stream found, ignore the frame */
1230 return 0;
1231
1232 found:
Christopher Fauletb067b062017-01-04 16:39:11 +01001233 if (!acquire_spoe_buffer(ctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001234 return 1; /* Retry later */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001235
1236 /* Copy encoded actions */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001237 memcpy(ctx->buffer->p, frame+idx, size-idx);
1238 ctx->buffer->i = size-idx;
1239
Christopher Fauleta1cda022016-12-21 08:58:06 +01001240 /* Notify the stream */
1241 LIST_DEL(&ctx->list);
1242 LIST_INIT(&ctx->list);
1243 ctx->state = SPOE_CTX_ST_DONE;
1244 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1245
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001246 return idx;
1247}
1248
Christopher Fauletba7bc162016-11-07 21:07:38 +01001249/* This function is used in cfgparse.c and declared in proto/checks.h. It
1250 * prepare the request to send to agents during a healthcheck. It returns 0 on
1251 * success and -1 if an error occurred. */
1252int
1253prepare_spoe_healthcheck_request(char **req, int *len)
1254{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001255 struct appctx appctx;
1256 struct spoe_appctx spoe_appctx;
1257 char *frame, buf[global.tune.bufsize];
1258 unsigned int framesz;
1259 int idx;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001260
Christopher Faulet42bfa462017-01-04 14:14:19 +01001261 memset(&appctx, 0, sizeof(appctx));
1262 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001263 memset(buf, 0, sizeof(buf));
Christopher Faulet42bfa462017-01-04 14:14:19 +01001264
1265 appctx.ctx.spoe.ptr = &spoe_appctx;
1266 SPOE_APPCTX(&appctx)->max_frame_size = global.tune.bufsize-4;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001267
1268 frame = buf+4;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001269 idx = prepare_spoe_hahello_frame(&appctx, frame, global.tune.bufsize-4);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001270 if (idx <= 0)
1271 return -1;
1272 if (idx + SLEN(HEALTHCHECK_KEY) + 1 > global.tune.bufsize-4)
1273 return -1;
1274
1275 /* "healthcheck" K/V item */
1276 idx += encode_spoe_string(HEALTHCHECK_KEY, SLEN(HEALTHCHECK_KEY), frame+idx);
1277 frame[idx++] = (SPOE_DATA_T_BOOL | SPOE_DATA_FL_TRUE);
1278
1279 framesz = htonl(idx);
1280 memcpy(buf, (char *)&framesz, 4);
1281
1282 if ((*req = malloc(idx+4)) == NULL)
1283 return -1;
1284 memcpy(*req, buf, idx+4);
1285 *len = idx+4;
1286 return 0;
1287}
1288
1289/* This function is used in checks.c and declared in proto/checks.h. It decode
1290 * the response received from an agent during a healthcheck. It returns 0 on
1291 * success and -1 if an error occurred. */
1292int
1293handle_spoe_healthcheck_response(char *frame, size_t size, char *err, int errlen)
1294{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001295 struct appctx appctx;
1296 struct spoe_appctx spoe_appctx;
1297 int r;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001298
Christopher Faulet42bfa462017-01-04 14:14:19 +01001299 memset(&appctx, 0, sizeof(appctx));
1300 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001301
Christopher Faulet42bfa462017-01-04 14:14:19 +01001302 appctx.ctx.spoe.ptr = &spoe_appctx;
1303 SPOE_APPCTX(&appctx)->max_frame_size = global.tune.bufsize-4;
1304
1305 if (handle_spoe_agentdiscon_frame(&appctx, frame, size) != 0)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001306 goto error;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001307 if ((r = handle_spoe_agenthello_frame(&appctx, frame, size)) <= 0) {
Christopher Fauletba7bc162016-11-07 21:07:38 +01001308 if (r == 0)
1309 spoe_status_code = SPOE_FRM_ERR_INVALID;
1310 goto error;
1311 }
1312
1313 return 0;
1314
1315 error:
1316 if (spoe_status_code >= SPOE_FRM_ERRS)
1317 spoe_status_code = SPOE_FRM_ERR_UNKNOWN;
1318 strncpy(err, spoe_frm_err_reasons[spoe_status_code], errlen);
1319 return -1;
1320}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001321
Christopher Fauleta1cda022016-12-21 08:58:06 +01001322/* Send a SPOE frame to an agent. It returns -1 when an error occurred, 0 when
1323 * the frame can be ignored, 1 to retry later, and the frame legnth on
1324 * success. */
1325static int
1326send_spoe_frame(struct appctx *appctx, char *buf, size_t framesz)
1327{
1328 struct stream_interface *si = appctx->owner;
1329 int ret;
1330 uint32_t netint;
1331
1332 if (si_ic(si)->buf == &buf_empty)
1333 return 1;
1334
1335 netint = htonl(framesz);
1336 memcpy(buf, (char *)&netint, 4);
1337 ret = bi_putblk(si_ic(si), buf, framesz+4);
1338
1339 if (ret <= 0) {
1340 if (ret == -1)
1341 return 1; /* retry */
Christopher Fauletb067b062017-01-04 16:39:11 +01001342 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001343 return -1; /* error */
1344 }
1345 return framesz;
1346}
1347
1348/* Receive a SPOE frame from an agent. It return -1 when an error occurred, 0
1349 * when the frame can be ignored, 1 to retry later and the frame length on
1350 * success. */
1351static int
1352recv_spoe_frame(struct appctx *appctx, char *buf, size_t framesz)
1353{
1354 struct stream_interface *si = appctx->owner;
1355 int ret;
1356 uint32_t netint;
1357
1358 if (si_oc(si)->buf == &buf_empty)
1359 return 1;
1360
1361 ret = bo_getblk(si_oc(si), (char *)&netint, 4, 0);
1362 if (ret > 0) {
1363 framesz = ntohl(netint);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001364 if (framesz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001365 spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
1366 return -1;
1367 }
1368 ret = bo_getblk(si_oc(si), trash.str, framesz, 4);
1369 }
1370 if (ret <= 0) {
1371 if (ret == 0)
1372 return 1; /* retry */
1373 spoe_status_code = SPOE_FRM_ERR_IO;
1374 return -1; /* error */
1375 }
1376 return framesz;
1377}
1378
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001379/********************************************************************
1380 * Functions that manage the SPOE applet
1381 ********************************************************************/
1382/* Callback function that catches applet timeouts. If a timeout occurred, we set
1383 * <appctx->st1> flag and the SPOE applet is woken up. */
1384static struct task *
1385process_spoe_applet(struct task * task)
1386{
1387 struct appctx *appctx = task->context;
1388
1389 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1390 if (tick_is_expired(task->expire, now_ms)) {
1391 task->expire = TICK_ETERNITY;
1392 appctx->st1 = SPOE_APPCTX_ERR_TOUT;
1393 }
1394 si_applet_want_get(appctx->owner);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001395 si_applet_want_put(appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001396 appctx_wakeup(appctx);
1397 return task;
1398}
1399
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001400/* Callback function that releases a SPOE applet. This happens when the
1401 * connection with the agent is closed. */
1402static void
1403release_spoe_applet(struct appctx *appctx)
1404{
1405 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001406 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001407 struct spoe_context *ctx, *back;
1408
1409 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p\n",
1410 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1411 __FUNCTION__, appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001412
Christopher Fauleta1cda022016-12-21 08:58:06 +01001413 agent->applets_act--;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001414 if (!LIST_ISEMPTY(&SPOE_APPCTX(appctx)->list)) {
1415 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1416 LIST_INIT(&SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001417 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001418
1419 if (appctx->st0 != SPOE_APPCTX_ST_END) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001420 if (appctx->st0 == SPOE_APPCTX_ST_IDLE)
1421 agent->applets_idle--;
1422
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001423 si_shutw(si);
1424 si_shutr(si);
1425 si_ic(si)->flags |= CF_READ_NULL;
1426 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Fauletb067b062017-01-04 16:39:11 +01001427 if (SPOE_APPCTX(appctx)->status_code == SPOE_FRM_ERR_NONE)
1428 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001429 }
1430
Christopher Faulet42bfa462017-01-04 14:14:19 +01001431 if (SPOE_APPCTX(appctx)->task) {
1432 task_delete(SPOE_APPCTX(appctx)->task);
1433 task_free(SPOE_APPCTX(appctx)->task);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001434 }
1435
Christopher Faulet42bfa462017-01-04 14:14:19 +01001436 list_for_each_entry_safe(ctx, back, &SPOE_APPCTX(appctx)->waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001437 LIST_DEL(&ctx->list);
1438 LIST_INIT(&ctx->list);
1439 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001440 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001441 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001442 }
1443
Christopher Faulet42bfa462017-01-04 14:14:19 +01001444 pool_free2(pool2_spoe_appctx, SPOE_APPCTX(appctx));
1445
Christopher Fauleta1cda022016-12-21 08:58:06 +01001446 if (!LIST_ISEMPTY(&agent->applets))
1447 return;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001448
Christopher Fauleta1cda022016-12-21 08:58:06 +01001449 list_for_each_entry_safe(ctx, back, &agent->sending_queue, list) {
1450 LIST_DEL(&ctx->list);
1451 LIST_INIT(&ctx->list);
1452 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001453 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001454 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001455 }
1456
Christopher Fauleta1cda022016-12-21 08:58:06 +01001457 list_for_each_entry_safe(ctx, back, &agent->waiting_queue, list) {
1458 LIST_DEL(&ctx->list);
1459 LIST_INIT(&ctx->list);
1460 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001461 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001462 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1463 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001464}
1465
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001466static int
Christopher Fauleta1cda022016-12-21 08:58:06 +01001467handle_connect_spoe_applet(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001468{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001469 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001470 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001471 char *frame = trash.str;
1472 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001473
Christopher Fauleta1cda022016-12-21 08:58:06 +01001474 if (si->state <= SI_ST_CON) {
1475 si_applet_want_put(si);
1476 task_wakeup(si_strm(si)->task, TASK_WOKEN_MSG);
1477 goto stop;
1478 }
Christopher Fauletb067b062017-01-04 16:39:11 +01001479 if (si->state != SI_ST_EST) {
1480 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001481 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001482 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001483
Christopher Fauleta1cda022016-12-21 08:58:06 +01001484 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1485 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p - Connection timed out\n",
1486 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__, appctx);
Christopher Fauletb067b062017-01-04 16:39:11 +01001487 spoe_status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001488 goto exit;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001489 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001490
Christopher Faulet42bfa462017-01-04 14:14:19 +01001491 if (SPOE_APPCTX(appctx)->task->expire == TICK_ETERNITY)
1492 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.hello);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001493
Christopher Faulet42bfa462017-01-04 14:14:19 +01001494 ret = prepare_spoe_hahello_frame(appctx, frame+4, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001495 if (ret > 1)
1496 ret = send_spoe_frame(appctx, frame, ret);
1497
1498 switch (ret) {
1499 case -1: /* error */
1500 goto exit;
1501
1502 case 0: /* ignore => an error, cannot be ignored */
1503 goto exit;
1504
1505 case 1: /* retry later */
1506 si_applet_cant_put(si);
1507 goto stop;
1508
1509 default: /* CONNECT frame successfully sent */
1510 appctx->st0 = SPOE_APPCTX_ST_CONNECTING;
1511 goto next;
1512 }
1513
1514 next:
1515 return 0;
1516 stop:
1517 return 1;
1518 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001519 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001520 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1521 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001522}
1523
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001524static int
Christopher Fauleta1cda022016-12-21 08:58:06 +01001525handle_connecting_spoe_applet(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001526{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001527 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001528 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001529 char *frame = trash.str;
1530 int ret, framesz = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001531
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001532
Christopher Fauletb067b062017-01-04 16:39:11 +01001533 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1534 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001535 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001536 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001537
Christopher Fauleta1cda022016-12-21 08:58:06 +01001538 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1539 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p - Connection timed out\n",
1540 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__, appctx);
Christopher Fauletb067b062017-01-04 16:39:11 +01001541 spoe_status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001542 goto exit;
1543 }
1544
Christopher Faulet42bfa462017-01-04 14:14:19 +01001545 ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001546 if (ret > 1) {
1547 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1548 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1549 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001550 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001551 framesz = ret;
1552 ret = handle_spoe_agenthello_frame(appctx, frame, framesz);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001553 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001554
Christopher Fauleta1cda022016-12-21 08:58:06 +01001555 switch (ret) {
1556 case -1: /* error */
1557 if (framesz)
1558 bo_skip(si_oc(si), framesz+4);
1559 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1560 goto next;
1561
1562 case 0: /* ignore */
1563 if (framesz)
1564 bo_skip(si_oc(si), framesz+4);
1565 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1566 goto next;
1567
1568 case 1: /* retry later */
1569 goto stop;
1570
1571 default:
1572 /* hello handshake is finished, set the idle timeout,
1573 * Add the appctx in the agent cache, decrease the
1574 * number of new applets and wake up waiting streams. */
1575 if (framesz)
1576 bo_skip(si_oc(si), framesz+4);
1577 agent->applets_idle++;
1578 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001579 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1580 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001581 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001582 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001583
Christopher Fauleta1cda022016-12-21 08:58:06 +01001584 next:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001585 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001586 return 0;
1587 stop:
1588 return 1;
1589 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001590 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001591 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1592 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001593}
1594
Christopher Fauleta1cda022016-12-21 08:58:06 +01001595static int
1596handle_processing_spoe_applet(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001597{
1598 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001599 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001600 struct spoe_context *ctx;
1601 char *frame = trash.str;
1602 unsigned int fpa = 0;
1603 int ret, framesz = 0, skip_sending = 0, skip_receiving = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001604
Christopher Fauletb067b062017-01-04 16:39:11 +01001605 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1606 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001607 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001608 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001609
Christopher Fauleta1cda022016-12-21 08:58:06 +01001610 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1611 spoe_status_code = SPOE_FRM_ERR_TOUT;
1612 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1613 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1614 goto next;
1615 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001616
Christopher Fauleta1cda022016-12-21 08:58:06 +01001617 process:
1618 if (fpa > agent->max_fpa || (skip_sending && skip_receiving))
1619 goto stop;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001620
Christopher Fauleta1cda022016-12-21 08:58:06 +01001621 /* Frames must be handled synchronously and a the applet is waiting for
1622 * a ACK frame */
Christopher Faulet42bfa462017-01-04 14:14:19 +01001623 if (!(SPOE_APPCTX(appctx)->flags & (SPOE_APPCTX_FL_ASYNC|SPOE_APPCTX_FL_PIPELINING)) &&
1624 !LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001625 if (skip_receiving)
1626 goto stop;
1627 goto recv_frame;
1628 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001629
Christopher Fauleta1cda022016-12-21 08:58:06 +01001630 if (LIST_ISEMPTY(&agent->sending_queue) || skip_sending) {
1631 skip_sending = 1;
1632 goto recv_frame;
1633 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001634
Christopher Fauleta1cda022016-12-21 08:58:06 +01001635 ctx = LIST_NEXT(&agent->sending_queue, typeof(ctx), list);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001636 ret = prepare_spoe_hanotify_frame(appctx, ctx, frame+4, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001637 if (ret > 1)
1638 ret = send_spoe_frame(appctx, frame, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001639
Christopher Fauleta1cda022016-12-21 08:58:06 +01001640 switch (ret) {
1641 case -1: /* error */
1642 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1643 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001644
Christopher Fauleta1cda022016-12-21 08:58:06 +01001645 case 0: /* ignore */
1646 agent->sending_rate++;
1647 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Fauletb067b062017-01-04 16:39:11 +01001648 ctx->status_code = (spoe_status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001649 release_spoe_buffer(ctx);
1650 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1651 LIST_DEL(&ctx->list);
1652 LIST_INIT(&ctx->list);
1653 fpa++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001654 break;
1655
Christopher Fauleta1cda022016-12-21 08:58:06 +01001656 case 1: /* retry */
1657 si_applet_cant_put(si);
1658 skip_sending = 1;
1659 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001660
Christopher Fauleta1cda022016-12-21 08:58:06 +01001661 default:
1662 agent->sending_rate++;
1663 ctx->state = SPOE_CTX_ST_WAITING_ACK;
1664 release_spoe_buffer(ctx);
1665 LIST_DEL(&ctx->list);
1666 LIST_INIT(&ctx->list);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001667 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001668 LIST_ADDQ(&agent->waiting_queue, &ctx->list);
1669 else
Christopher Faulet42bfa462017-01-04 14:14:19 +01001670 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001671 fpa++;
1672 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001673
Christopher Fauleta1cda022016-12-21 08:58:06 +01001674 if (fpa > agent->max_fpa)
1675 goto stop;
1676
1677 recv_frame:
1678 if (skip_receiving)
1679 goto process;
1680
1681 framesz = 0;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001682 ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001683 if (ret > 1) {
1684 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1685 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1686 goto next;
1687 }
1688 framesz = ret;
1689 ret = handle_spoe_agentack_frame(appctx, frame, framesz);
1690 }
1691
1692 switch (ret) {
1693 case -1: /* error */
1694 if (framesz)
1695 bo_skip(si_oc(si), framesz+4);
1696 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1697 goto next;
1698
1699 case 0: /* ignore */
1700 if (framesz)
1701 bo_skip(si_oc(si), framesz+4);
1702 fpa++;
1703 break;
1704
1705 case 1: /* retry */
1706 skip_receiving = 1;
1707 break;
1708
1709 default:
1710 if (framesz)
1711 bo_skip(si_oc(si), framesz+4);
1712 fpa++;
1713 }
1714 goto process;
1715
1716 next:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001717 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001718 return 0;
1719 stop:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001720 if ((SPOE_APPCTX(appctx)->flags & (SPOE_APPCTX_FL_ASYNC|SPOE_APPCTX_FL_PIPELINING)) ||
1721 LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001722 agent->applets_idle++;
1723 appctx->st0 = SPOE_APPCTX_ST_IDLE;
1724 }
Christopher Faulet42bfa462017-01-04 14:14:19 +01001725 if (fpa || (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PERSIST)) {
1726 LIST_DEL(&SPOE_APPCTX(appctx)->list);
1727 LIST_ADD(&agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001728 if (fpa)
Christopher Faulet42bfa462017-01-04 14:14:19 +01001729 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001730 }
1731 return 1;
1732
1733 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001734 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001735 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1736 return 0;
1737}
1738
1739static int
1740handle_disconnect_spoe_applet(struct appctx *appctx)
1741{
1742 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001743 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001744 char *frame = trash.str;
1745 int ret;
1746
Christopher Fauletb067b062017-01-04 16:39:11 +01001747 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
1748
Christopher Fauleta1cda022016-12-21 08:58:06 +01001749 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
1750 goto exit;
1751
1752 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT)
1753 goto exit;
1754
Christopher Faulet42bfa462017-01-04 14:14:19 +01001755 ret = prepare_spoe_hadiscon_frame(appctx, frame+4, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001756 if (ret > 1)
1757 ret = send_spoe_frame(appctx, frame, ret);
1758
1759 switch (ret) {
1760 case -1: /* error */
1761 goto exit;
1762
1763 case 0: /* ignore */
1764 goto exit;
1765
1766 case 1: /* retry */
1767 si_applet_cant_put(si);
1768 goto stop;
1769
1770 default:
1771 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1772 " - disconnected by HAProxy (%d): %s\n",
1773 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1774 __FUNCTION__, appctx, spoe_status_code,
1775 spoe_frm_err_reasons[spoe_status_code]);
1776
1777 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1778 goto next;
1779 }
1780
1781 next:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001782 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001783 return 0;
1784 stop:
1785 return 1;
1786 exit:
1787 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1788 return 0;
1789}
1790
1791static int
1792handle_disconnecting_spoe_applet(struct appctx *appctx)
1793{
1794 struct stream_interface *si = appctx->owner;
1795 char *frame = trash.str;
1796 int ret, framesz = 0;
1797
Christopher Fauletb067b062017-01-04 16:39:11 +01001798 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1799 spoe_status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001800 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001801 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001802
Christopher Fauletb067b062017-01-04 16:39:11 +01001803 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1804 spoe_status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001805 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001806 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001807
1808 framesz = 0;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001809 ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001810 if (ret > 1) {
1811 framesz = ret;
1812 ret = handle_spoe_agentdiscon_frame(appctx, frame, framesz);
1813 }
1814
1815 switch (ret) {
1816 case -1: /* error */
1817 if (framesz)
1818 bo_skip(si_oc(si), framesz+4);
1819 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1820 " - error on frame (%s)\n",
1821 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01001822 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01001823 __FUNCTION__, appctx,
1824 spoe_frm_err_reasons[spoe_status_code]);
1825 goto exit;
1826
1827 case 0: /* ignore */
1828 if (framesz)
1829 bo_skip(si_oc(si), framesz+4);
1830 goto next;
1831
1832 case 1: /* retry */
1833 goto stop;
1834
1835 default:
1836 if (framesz)
1837 bo_skip(si_oc(si), framesz+4);
1838 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1839 " - disconnected by peer (%d): %s\n",
1840 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01001841 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01001842 __FUNCTION__, appctx, spoe_status_code,
1843 spoe_reason);
1844 goto exit;
1845 }
1846
1847 next:
1848 return 0;
1849 stop:
1850 return 1;
1851 exit:
Christopher Fauletb067b062017-01-04 16:39:11 +01001852 if (SPOE_APPCTX(appctx)->status_code == SPOE_FRM_ERR_NONE)
1853 SPOE_APPCTX(appctx)->status_code = spoe_status_code;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001854 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1855 return 0;
1856}
1857
1858/* I/O Handler processing messages exchanged with the agent */
1859static void
1860handle_spoe_applet(struct appctx *appctx)
1861{
1862 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001863 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001864
Christopher Fauletb067b062017-01-04 16:39:11 +01001865 spoe_status_code = SPOE_FRM_ERR_NONE;
1866
Christopher Fauleta1cda022016-12-21 08:58:06 +01001867 switchstate:
1868 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1869 " - appctx-state=%s\n",
1870 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1871 __FUNCTION__, appctx, spoe_appctx_state_str[appctx->st0]);
1872
1873 switch (appctx->st0) {
1874 case SPOE_APPCTX_ST_CONNECT:
Christopher Fauleta1cda022016-12-21 08:58:06 +01001875 if (handle_connect_spoe_applet(appctx))
1876 goto out;
1877 goto switchstate;
1878
1879 case SPOE_APPCTX_ST_CONNECTING:
1880 if (handle_connecting_spoe_applet(appctx))
1881 goto out;
1882 goto switchstate;
1883
1884 case SPOE_APPCTX_ST_IDLE:
1885 if (stopping &&
1886 LIST_ISEMPTY(&agent->sending_queue) &&
Christopher Faulet42bfa462017-01-04 14:14:19 +01001887 LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
1888 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001889 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001890 goto switchstate;
1891 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001892 agent->applets_idle--;
1893 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1894 /* fall through */
1895
1896 case SPOE_APPCTX_ST_PROCESSING:
1897 if (handle_processing_spoe_applet(appctx))
1898 goto out;
1899 goto switchstate;
1900
1901 case SPOE_APPCTX_ST_DISCONNECT:
1902 if (handle_disconnect_spoe_applet(appctx))
1903 goto out;
1904 goto switchstate;
1905
1906 case SPOE_APPCTX_ST_DISCONNECTING:
1907 if (handle_disconnecting_spoe_applet(appctx))
1908 goto out;
1909 goto switchstate;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001910
1911 case SPOE_APPCTX_ST_EXIT:
1912 si_shutw(si);
1913 si_shutr(si);
1914 si_ic(si)->flags |= CF_READ_NULL;
1915 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001916 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001917 /* fall through */
1918
1919 case SPOE_APPCTX_ST_END:
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001920 return;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001921 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001922 out:
Christopher Faulet42bfa462017-01-04 14:14:19 +01001923 if (SPOE_APPCTX(appctx)->task->expire != TICK_ETERNITY)
1924 task_queue(SPOE_APPCTX(appctx)->task);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001925 si_oc(si)->flags |= CF_READ_DONTWAIT;
1926 task_wakeup(si_strm(si)->task, TASK_WOKEN_IO);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001927}
1928
1929struct applet spoe_applet = {
1930 .obj_type = OBJ_TYPE_APPLET,
1931 .name = "<SPOE>", /* used for logging */
1932 .fct = handle_spoe_applet,
1933 .release = release_spoe_applet,
1934};
1935
1936/* Create a SPOE applet. On success, the created applet is returned, else
1937 * NULL. */
1938static struct appctx *
1939create_spoe_appctx(struct spoe_config *conf)
1940{
1941 struct appctx *appctx;
1942 struct session *sess;
1943 struct task *task;
1944 struct stream *strm;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001945
1946 if ((appctx = appctx_new(&spoe_applet)) == NULL)
1947 goto out_error;
1948
Christopher Faulet42bfa462017-01-04 14:14:19 +01001949 appctx->ctx.spoe.ptr = pool_alloc_dirty(pool2_spoe_appctx);
1950 if (SPOE_APPCTX(appctx) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001951 goto out_free_appctx;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001952
Christopher Faulet42bfa462017-01-04 14:14:19 +01001953 appctx->st0 = SPOE_APPCTX_ST_CONNECT;
1954 if ((SPOE_APPCTX(appctx)->task = task_new()) == NULL)
1955 goto out_free_spoe_appctx;
1956
1957 SPOE_APPCTX(appctx)->owner = appctx;
1958 SPOE_APPCTX(appctx)->task->process = process_spoe_applet;
1959 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
1960 SPOE_APPCTX(appctx)->task->context = appctx;
1961 SPOE_APPCTX(appctx)->agent = conf->agent;
1962 SPOE_APPCTX(appctx)->version = 0;
1963 SPOE_APPCTX(appctx)->max_frame_size = conf->agent->max_frame_size;
1964 SPOE_APPCTX(appctx)->flags = 0;
Christopher Fauletb067b062017-01-04 16:39:11 +01001965 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001966
1967 LIST_INIT(&SPOE_APPCTX(appctx)->list);
1968 LIST_INIT(&SPOE_APPCTX(appctx)->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001969
Willy Tarreau5820a362016-12-22 15:59:02 +01001970 sess = session_new(&conf->agent_fe, NULL, &appctx->obj_type);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001971 if (!sess)
1972 goto out_free_spoe;
1973
1974 if ((task = task_new()) == NULL)
1975 goto out_free_sess;
1976
1977 if ((strm = stream_new(sess, task, &appctx->obj_type)) == NULL)
1978 goto out_free_task;
1979
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001980 stream_set_backend(strm, conf->agent->b.be);
1981
1982 /* applet is waiting for data */
1983 si_applet_cant_get(&strm->si[0]);
1984 appctx_wakeup(appctx);
1985
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001986 strm->do_log = NULL;
1987 strm->res.flags |= CF_READ_DONTWAIT;
1988
1989 conf->agent_fe.feconn++;
1990 jobs++;
1991 totalconn++;
1992
Christopher Faulet42bfa462017-01-04 14:14:19 +01001993 task_wakeup(SPOE_APPCTX(appctx)->task, TASK_WOKEN_INIT);
1994 LIST_ADDQ(&conf->agent->applets, &SPOE_APPCTX(appctx)->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001995 conf->agent->applets_act++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001996 return appctx;
1997
1998 /* Error unrolling */
1999 out_free_task:
2000 task_free(task);
2001 out_free_sess:
2002 session_free(sess);
2003 out_free_spoe:
Christopher Faulet42bfa462017-01-04 14:14:19 +01002004 task_free(SPOE_APPCTX(appctx)->task);
2005 out_free_spoe_appctx:
2006 pool_free2(pool2_spoe_appctx, SPOE_APPCTX(appctx));
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002007 out_free_appctx:
2008 appctx_free(appctx);
2009 out_error:
2010 return NULL;
2011}
2012
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002013static int
Christopher Fauleta1cda022016-12-21 08:58:06 +01002014queue_spoe_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002015{
2016 struct spoe_config *conf = FLT_CONF(ctx->filter);
2017 struct spoe_agent *agent = conf->agent;
2018 struct appctx *appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002019 struct spoe_appctx *spoe_appctx;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002020 unsigned int min_applets;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002021
Christopher Fauleta1cda022016-12-21 08:58:06 +01002022 min_applets = min_applets_act(agent);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002023
Christopher Fauleta1cda022016-12-21 08:58:06 +01002024 /* Check if we need to create a new SPOE applet or not. */
2025 if (agent->applets_act >= min_applets && agent->applets_idle && agent->sending_rate)
2026 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002027
2028 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002029 " - try to create new SPOE appctx\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002030 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2031 ctx->strm);
2032
Christopher Fauleta1cda022016-12-21 08:58:06 +01002033 /* Do not try to create a new applet if there is no server up for the
2034 * agent's backend. */
2035 if (!agent->b.be->srv_act && !agent->b.be->srv_bck) {
2036 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2037 " - cannot create SPOE appctx: no server up\n",
2038 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2039 __FUNCTION__, ctx->strm);
2040 goto end;
2041 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002042
Christopher Fauleta1cda022016-12-21 08:58:06 +01002043 /* Do not try to create a new applet if we have reached the maximum of
2044 * connection per seconds */
Christopher Faulet48026722016-11-16 15:01:12 +01002045 if (agent->cps_max > 0) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002046 if (!freq_ctr_remain(&agent->conn_per_sec, agent->cps_max, 0)) {
2047 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2048 " - cannot create SPOE appctx: max CPS reached\n",
2049 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2050 __FUNCTION__, ctx->strm);
2051 goto end;
2052 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002053 }
2054
Christopher Fauleta1cda022016-12-21 08:58:06 +01002055 appctx = create_spoe_appctx(conf);
2056 if (appctx == NULL) {
2057 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2058 " - failed to create SPOE appctx\n",
2059 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2060 __FUNCTION__, ctx->strm);
Christopher Faulet72bcc472017-01-04 16:39:41 +01002061 send_log(ctx->strm->be, LOG_EMERG,
2062 "SPOE: [%s] failed to create SPOE applet\n",
2063 agent->id);
2064
Christopher Fauleta1cda022016-12-21 08:58:06 +01002065 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002066 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002067 if (agent->applets_act <= min_applets)
Christopher Faulet42bfa462017-01-04 14:14:19 +01002068 SPOE_APPCTX(appctx)->flags |= SPOE_APPCTX_FL_PERSIST;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002069
Christopher Fauleta1cda022016-12-21 08:58:06 +01002070 /* Increase the per-process number of cumulated connections */
2071 if (agent->cps_max > 0)
2072 update_freq_ctr(&agent->conn_per_sec, 1);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002073
Christopher Fauleta1cda022016-12-21 08:58:06 +01002074 end:
2075 /* The only reason to return an error is when there is no applet */
2076 if (LIST_ISEMPTY(&agent->applets))
2077 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002078
Christopher Fauleta1cda022016-12-21 08:58:06 +01002079 /* Add the SPOE context in the sending queue and update all running
2080 * info */
2081 LIST_ADDQ(&agent->sending_queue, &ctx->list);
2082 if (agent->sending_rate)
2083 agent->sending_rate--;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002084
2085 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002086 " - Add stream in sending queue - applets_act=%u - applets_idle=%u"
2087 " - sending_rate=%u\n",
2088 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2089 ctx->strm, agent->applets_act, agent->applets_idle, agent->sending_rate);
Christopher Fauletf7a30922016-11-10 15:04:51 +01002090
Christopher Fauleta1cda022016-12-21 08:58:06 +01002091 /* Finally try to wakeup the first IDLE applet found and move it at the
2092 * end of the list. */
Christopher Faulet42bfa462017-01-04 14:14:19 +01002093 list_for_each_entry(spoe_appctx, &agent->applets, list) {
2094 appctx = spoe_appctx->owner;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002095 if (appctx->st0 == SPOE_APPCTX_ST_IDLE) {
2096 si_applet_want_get(appctx->owner);
2097 si_applet_want_put(appctx->owner);
2098 appctx_wakeup(appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01002099 LIST_DEL(&spoe_appctx->list);
2100 LIST_ADDQ(&agent->applets, &spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002101 break;
2102 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002103 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002104 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002105}
2106
2107/***************************************************************************
2108 * Functions that process SPOE messages and actions
2109 **************************************************************************/
2110/* Process SPOE messages for a specific event. During the processing, it returns
2111 * 0 and it returns 1 when the processing is finished. If an error occurred, -1
2112 * is returned. */
2113static int
2114process_spoe_messages(struct stream *s, struct spoe_context *ctx,
2115 struct list *messages, int dir)
2116{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002117 struct spoe_config *conf = FLT_CONF(ctx->filter);
2118 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002119 struct spoe_message *msg;
2120 struct sample *smp;
2121 struct spoe_arg *arg;
2122 char *p;
2123 size_t max_size;
2124 int off, flag, idx = 0;
2125
2126 /* Reserve 32 bytes from the frame Metadata */
Christopher Fauleta1cda022016-12-21 08:58:06 +01002127 max_size = agent->max_frame_size - 32;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002128
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002129 p = ctx->buffer->p;
2130
2131 /* Loop on messages */
2132 list_for_each_entry(msg, messages, list) {
2133 if (idx + msg->id_len + 1 > max_size)
2134 goto skip;
2135
2136 /* Set the message name */
2137 idx += encode_spoe_string(msg->id, msg->id_len, p+idx);
2138
2139 /* Save offset where to store the number of arguments for this
2140 * message */
2141 off = idx++;
2142 p[off] = 0;
2143
2144 /* Loop on arguments */
2145 list_for_each_entry(arg, &msg->args, list) {
2146 p[off]++; /* Increment the number of arguments */
2147
2148 if (idx + arg->name_len + 1 > max_size)
2149 goto skip;
2150
2151 /* Encode the arguement name as a string. It can by NULL */
2152 idx += encode_spoe_string(arg->name, arg->name_len, p+idx);
2153
2154 /* Fetch the arguement value */
2155 smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL);
2156 if (!smp) {
2157 /* If no value is available, set it to NULL */
2158 p[idx++] = SPOE_DATA_T_NULL;
2159 continue;
2160 }
2161
2162 /* Else, encode the arguement value */
2163 switch (smp->data.type) {
2164 case SMP_T_BOOL:
2165 flag = ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE);
2166 p[idx++] = (SPOE_DATA_T_BOOL | flag);
2167 break;
2168 case SMP_T_SINT:
2169 p[idx++] = SPOE_DATA_T_INT64;
2170 if (idx + 8 > max_size)
2171 goto skip;
2172 idx += encode_spoe_varint(smp->data.u.sint, p+idx);
2173 break;
2174 case SMP_T_IPV4:
2175 p[idx++] = SPOE_DATA_T_IPV4;
2176 if (idx + 4 > max_size)
2177 goto skip;
2178 memcpy(p+idx, &smp->data.u.ipv4, 4);
2179 idx += 4;
2180 break;
2181 case SMP_T_IPV6:
2182 p[idx++] = SPOE_DATA_T_IPV6;
2183 if (idx + 16 > max_size)
2184 goto skip;
2185 memcpy(p+idx, &smp->data.u.ipv6, 16);
2186 idx += 16;
2187 break;
2188 case SMP_T_STR:
2189 p[idx++] = SPOE_DATA_T_STR;
2190 if (idx + smp->data.u.str.len > max_size)
2191 goto skip;
2192 idx += encode_spoe_string(smp->data.u.str.str,
2193 smp->data.u.str.len,
2194 p+idx);
2195 break;
2196 case SMP_T_BIN:
2197 p[idx++] = SPOE_DATA_T_BIN;
2198 if (idx + smp->data.u.str.len > max_size)
2199 goto skip;
2200 idx += encode_spoe_string(smp->data.u.str.str,
2201 smp->data.u.str.len,
2202 p+idx);
2203 break;
2204 case SMP_T_METH:
2205 if (smp->data.u.meth.meth == HTTP_METH_OTHER) {
2206 p[idx++] = SPOE_DATA_T_STR;
2207 if (idx + http_known_methods[smp->data.u.meth.meth].len > max_size)
2208 goto skip;
2209 idx += encode_spoe_string(http_known_methods[smp->data.u.meth.meth].name,
2210 http_known_methods[smp->data.u.meth.meth].len,
2211 p+idx);
2212 }
2213 else {
2214 p[idx++] = SPOE_DATA_T_STR;
2215 if (idx + smp->data.u.str.len > max_size)
2216 goto skip;
2217 idx += encode_spoe_string(smp->data.u.meth.str.str,
2218 smp->data.u.meth.str.len,
2219 p+idx);
2220 }
2221 break;
2222 default:
2223 p[idx++] = SPOE_DATA_T_NULL;
2224 }
2225 }
2226 }
2227 ctx->buffer->i = idx;
2228 return 1;
2229
2230 skip:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002231 return 0;
2232}
2233
2234/* Helper function to set a variable */
2235static void
2236set_spoe_var(struct spoe_context *ctx, char *scope, char *name, int len,
2237 struct sample *smp)
2238{
2239 struct spoe_config *conf = FLT_CONF(ctx->filter);
2240 struct spoe_agent *agent = conf->agent;
2241 char varname[64];
2242
2243 memset(varname, 0, sizeof(varname));
2244 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2245 scope, agent->var_pfx, len, name);
2246 vars_set_by_name_ifexist(varname, len, smp);
2247}
2248
2249/* Helper function to unset a variable */
2250static void
2251unset_spoe_var(struct spoe_context *ctx, char *scope, char *name, int len,
2252 struct sample *smp)
2253{
2254 struct spoe_config *conf = FLT_CONF(ctx->filter);
2255 struct spoe_agent *agent = conf->agent;
2256 char varname[64];
2257
2258 memset(varname, 0, sizeof(varname));
2259 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2260 scope, agent->var_pfx, len, name);
2261 vars_unset_by_name_ifexist(varname, len, smp);
2262}
2263
2264
2265/* Process SPOE actions for a specific event. During the processing, it returns
2266 * 0 and it returns 1 when the processing is finished. If an error occurred, -1
2267 * is returned. */
2268static int
2269process_spoe_actions(struct stream *s, struct spoe_context *ctx,
2270 enum spoe_event ev, int dir)
2271{
2272 char *p;
2273 size_t size;
2274 int off, i, idx = 0;
2275
2276 p = ctx->buffer->p;
2277 size = ctx->buffer->i;
2278
2279 while (idx < size) {
2280 char *str;
2281 uint64_t sz;
2282 struct sample smp;
2283 enum spoe_action_type type;
2284
2285 off = idx;
2286 if (idx+2 > size)
2287 goto skip;
2288
2289 type = p[idx++];
2290 switch (type) {
2291 case SPOE_ACT_T_SET_VAR: {
2292 char *scope;
2293
2294 if (p[idx++] != 3)
2295 goto skip_action;
2296
2297 switch (p[idx++]) {
2298 case SPOE_SCOPE_PROC: scope = "proc"; break;
2299 case SPOE_SCOPE_SESS: scope = "sess"; break;
2300 case SPOE_SCOPE_TXN : scope = "txn"; break;
2301 case SPOE_SCOPE_REQ : scope = "req"; break;
2302 case SPOE_SCOPE_RES : scope = "res"; break;
2303 default: goto skip;
2304 }
2305
2306 idx += decode_spoe_string(p+idx, p+size, &str, &sz);
2307 if (str == NULL)
2308 goto skip;
2309 memset(&smp, 0, sizeof(smp));
2310 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletb5cff602016-11-24 14:53:22 +01002311
2312 if ((i = decode_spoe_data(p+idx, p+size, &smp)) == -1)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002313 goto skip;
Christopher Fauletb5cff602016-11-24 14:53:22 +01002314 idx += i;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002315
2316 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2317 " - set-var '%s.%s.%.*s'\n",
2318 (int)now.tv_sec, (int)now.tv_usec,
2319 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2320 __FUNCTION__, s, scope,
2321 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2322 (int)sz, str);
2323
2324 set_spoe_var(ctx, scope, str, sz, &smp);
2325 break;
2326 }
2327
2328 case SPOE_ACT_T_UNSET_VAR: {
2329 char *scope;
2330
2331 if (p[idx++] != 2)
2332 goto skip_action;
2333
2334 switch (p[idx++]) {
2335 case SPOE_SCOPE_PROC: scope = "proc"; break;
2336 case SPOE_SCOPE_SESS: scope = "sess"; break;
2337 case SPOE_SCOPE_TXN : scope = "txn"; break;
2338 case SPOE_SCOPE_REQ : scope = "req"; break;
2339 case SPOE_SCOPE_RES : scope = "res"; break;
2340 default: goto skip;
2341 }
2342
2343 idx += decode_spoe_string(p+idx, p+size, &str, &sz);
2344 if (str == NULL)
2345 goto skip;
2346 memset(&smp, 0, sizeof(smp));
2347 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
2348
2349 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2350 " - unset-var '%s.%s.%.*s'\n",
2351 (int)now.tv_sec, (int)now.tv_usec,
2352 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2353 __FUNCTION__, s, scope,
2354 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2355 (int)sz, str);
2356
2357 unset_spoe_var(ctx, scope, str, sz, &smp);
2358 break;
2359 }
2360
2361 default:
2362 skip_action:
2363 if ((i = skip_spoe_action(p+off, p+size)) == -1)
2364 goto skip;
2365 idx += i;
2366 }
2367 }
2368
2369 return 1;
2370 skip:
2371 return 0;
2372}
2373
Christopher Fauleta1cda022016-12-21 08:58:06 +01002374static int
2375start_event_processing(struct spoe_context *ctx, int dir)
2376{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002377 /* If a process is already started for this SPOE context, retry
2378 * later. */
2379 if (ctx->flags & SPOE_CTX_FL_PROCESS)
2380 goto wait;
2381
Christopher Fauletb067b062017-01-04 16:39:11 +01002382 if (!acquire_spoe_buffer(ctx))
2383 goto wait;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002384
2385 /* Set the right flag to prevent request and response processing
2386 * in same time. */
2387 ctx->flags |= ((dir == SMP_OPT_DIR_REQ)
2388 ? SPOE_CTX_FL_REQ_PROCESS
2389 : SPOE_CTX_FL_RSP_PROCESS);
2390
2391 return 1;
2392
2393 wait:
2394 return 0;
2395}
2396
2397static void
2398stop_event_processing(struct spoe_context *ctx)
2399{
2400 /* Reset the flag to allow next processing */
2401 ctx->flags &= ~SPOE_CTX_FL_PROCESS;
2402
Christopher Fauletb067b062017-01-04 16:39:11 +01002403 ctx->status_code = 0;
2404
Christopher Fauleta1cda022016-12-21 08:58:06 +01002405 /* Reset processing timer */
2406 ctx->process_exp = TICK_ETERNITY;
2407
2408 release_spoe_buffer(ctx);
2409
2410 if (!LIST_ISEMPTY(&ctx->list)) {
2411 LIST_DEL(&ctx->list);
2412 LIST_INIT(&ctx->list);
2413 }
2414}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002415
2416/* Process a SPOE event. First, this functions will process messages attached to
2417 * this event and send them to an agent in a NOTIFY frame. Then, it will wait a
2418 * ACK frame to process corresponding actions. During all the processing, it
2419 * returns 0 and it returns 1 when the processing is finished. If an error
2420 * occurred, -1 is returned. */
2421static int
2422process_spoe_event(struct stream *s, struct spoe_context *ctx,
2423 enum spoe_event ev)
2424{
Christopher Fauletf7a30922016-11-10 15:04:51 +01002425 struct spoe_config *conf = FLT_CONF(ctx->filter);
2426 struct spoe_agent *agent = conf->agent;
2427 int dir, ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002428
2429 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2430 " - ctx-state=%s - event=%s\n",
2431 (int)now.tv_sec, (int)now.tv_usec,
Christopher Fauletf7a30922016-11-10 15:04:51 +01002432 agent->id, __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002433 spoe_event_str[ev]);
2434
Christopher Faulet48026722016-11-16 15:01:12 +01002435
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002436 dir = ((ev < SPOE_EV_ON_SERVER_SESS) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
2437
2438 if (LIST_ISEMPTY(&(ctx->messages[ev])))
2439 goto out;
2440
2441 if (ctx->state == SPOE_CTX_ST_ERROR)
2442 goto error;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002443
2444 if (tick_is_expired(ctx->process_exp, now_ms) && ctx->state != SPOE_CTX_ST_DONE) {
2445 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2446 " - failed to process event '%s': timeout\n",
2447 (int)now.tv_sec, (int)now.tv_usec,
2448 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
Christopher Fauletb067b062017-01-04 16:39:11 +01002449 ctx->status_code = SPOE_CTX_ERR_TOUT;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002450 goto error;
2451 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002452
2453 if (ctx->state == SPOE_CTX_ST_READY) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002454 if (agent->eps_max > 0) {
2455 if (!freq_ctr_remain(&agent->err_per_sec, agent->eps_max, 0)) {
2456 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2457 " - skip event '%s': max EPS reached\n",
2458 (int)now.tv_sec, (int)now.tv_usec,
2459 agent->id, __FUNCTION__, s, spoe_event_str[ev]);
2460 goto skip;
2461 }
2462 }
2463
Christopher Fauletf7a30922016-11-10 15:04:51 +01002464 if (!tick_isset(ctx->process_exp)) {
2465 ctx->process_exp = tick_add_ifset(now_ms, agent->timeout.processing);
2466 s->task->expire = tick_first((tick_is_expired(s->task->expire, now_ms) ? 0 : s->task->expire),
2467 ctx->process_exp);
2468 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002469 ret = start_event_processing(ctx, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002470 if (!ret)
2471 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002472 ret = process_spoe_messages(s, ctx, &(ctx->messages[ev]), dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002473 if (!ret)
2474 goto skip;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002475
Christopher Fauletb067b062017-01-04 16:39:11 +01002476 if (!queue_spoe_context(ctx)) {
2477 ctx->status_code = SPOE_CTX_ERR_RES;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002478 goto error;
Christopher Fauletb067b062017-01-04 16:39:11 +01002479 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002480
2481 ctx->state = SPOE_CTX_ST_SENDING_MSGS;
2482 /* fall through */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002483 }
2484
Christopher Fauleta1cda022016-12-21 08:58:06 +01002485 if (ctx->state == SPOE_CTX_ST_SENDING_MSGS ||
2486 ctx->state == SPOE_CTX_ST_WAITING_ACK) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002487 ret = 0;
2488 goto out;
2489 }
2490
2491 if (ctx->state == SPOE_CTX_ST_DONE) {
2492 ret = process_spoe_actions(s, ctx, ev, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002493 if (!ret)
2494 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002495 ctx->frame_id++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002496 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002497 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002498 }
2499
2500 out:
2501 return ret;
2502
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002503 error:
Christopher Faulet48026722016-11-16 15:01:12 +01002504 if (agent->eps_max > 0)
2505 update_freq_ctr(&agent->err_per_sec, 1);
2506
Christopher Faulet985532d2016-11-16 15:36:19 +01002507 if (agent->var_on_error) {
2508 struct sample smp;
2509
Christopher Fauleta1cda022016-12-21 08:58:06 +01002510 // FIXME: Get the error code here
Christopher Faulet985532d2016-11-16 15:36:19 +01002511 memset(&smp, 0, sizeof(smp));
2512 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletb067b062017-01-04 16:39:11 +01002513 smp.data.u.sint = ctx->status_code;
Christopher Faulet985532d2016-11-16 15:36:19 +01002514 smp.data.type = SMP_T_BOOL;
2515
2516 set_spoe_var(ctx, "txn", agent->var_on_error,
2517 strlen(agent->var_on_error), &smp);
2518 }
Christopher Faulet72bcc472017-01-04 16:39:41 +01002519 send_log(ctx->strm->be, LOG_WARNING,
2520 "SPOE: [%s] failed to process event '%s': code=%u\n",
2521 agent->id, spoe_event_str[ev], ctx->status_code);
Christopher Faulet985532d2016-11-16 15:36:19 +01002522
Christopher Fauletea62c2a2016-11-14 10:54:21 +01002523 ctx->state = ((agent->flags & SPOE_FL_CONT_ON_ERR)
2524 ? SPOE_CTX_ST_READY
Christopher Fauletb067b062017-01-04 16:39:11 +01002525 : SPOE_CTX_ST_NONE);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002526 ret = 1;
2527 goto end;
2528
2529 skip:
2530 ctx->state = SPOE_CTX_ST_READY;
2531 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002532
Christopher Fauleta1cda022016-12-21 08:58:06 +01002533 end:
2534 stop_event_processing(ctx);
2535 return ret;
2536}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002537
2538/***************************************************************************
2539 * Functions that create/destroy SPOE contexts
2540 **************************************************************************/
Christopher Fauleta1cda022016-12-21 08:58:06 +01002541static int
2542acquire_spoe_buffer(struct spoe_context *ctx)
2543{
2544 if (ctx->buffer != &buf_empty)
2545 return 1;
2546
2547 if (!LIST_ISEMPTY(&ctx->buffer_wait.list)) {
2548 LIST_DEL(&ctx->buffer_wait.list);
2549 LIST_INIT(&ctx->buffer_wait.list);
2550 }
2551
2552 if (b_alloc_margin(&ctx->buffer, global.tune.reserved_bufs))
2553 return 1;
2554
2555 LIST_ADDQ(&buffer_wq, &ctx->buffer_wait.list);
2556 return 0;
2557}
2558
2559static void
2560release_spoe_buffer(struct spoe_context *ctx)
2561{
2562 if (!LIST_ISEMPTY(&ctx->buffer_wait.list)) {
2563 LIST_DEL(&ctx->buffer_wait.list);
2564 LIST_INIT(&ctx->buffer_wait.list);
2565 }
2566
2567 /* Release the buffer if needed */
2568 if (ctx->buffer != &buf_empty) {
2569 b_free(&ctx->buffer);
2570 offer_buffers(ctx, tasks_run_queue + applets_active_queue);
2571 }
2572}
2573
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002574static int wakeup_spoe_context(struct spoe_context *ctx)
2575{
2576 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2577 return 1;
2578}
2579
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002580static struct spoe_context *
2581create_spoe_context(struct filter *filter)
2582{
2583 struct spoe_config *conf = FLT_CONF(filter);
2584 struct spoe_context *ctx;
2585
2586 ctx = pool_alloc_dirty(pool2_spoe_ctx);
2587 if (ctx == NULL) {
2588 return NULL;
2589 }
2590 memset(ctx, 0, sizeof(*ctx));
Christopher Fauletb067b062017-01-04 16:39:11 +01002591 ctx->filter = filter;
2592 ctx->state = SPOE_CTX_ST_NONE;
2593 ctx->status_code = SPOE_CTX_ERR_NONE;
2594 ctx->flags = 0;
2595 ctx->messages = conf->agent->messages;
2596 ctx->buffer = &buf_empty;
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002597 LIST_INIT(&ctx->buffer_wait.list);
2598 ctx->buffer_wait.target = ctx;
2599 ctx->buffer_wait.wakeup_cb = (int (*)(void *))wakeup_spoe_context;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002600 LIST_INIT(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002601
Christopher Fauletf7a30922016-11-10 15:04:51 +01002602 ctx->stream_id = 0;
2603 ctx->frame_id = 1;
2604 ctx->process_exp = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002605
2606 return ctx;
2607}
2608
2609static void
2610destroy_spoe_context(struct spoe_context *ctx)
2611{
2612 if (!ctx)
2613 return;
2614
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002615 if (!LIST_ISEMPTY(&ctx->buffer_wait.list))
2616 LIST_DEL(&ctx->buffer_wait.list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002617 if (!LIST_ISEMPTY(&ctx->list))
2618 LIST_DEL(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002619 pool_free2(pool2_spoe_ctx, ctx);
2620}
2621
2622static void
2623reset_spoe_context(struct spoe_context *ctx)
2624{
2625 ctx->state = SPOE_CTX_ST_READY;
2626 ctx->flags &= ~SPOE_CTX_FL_PROCESS;
2627}
2628
2629
2630/***************************************************************************
2631 * Hooks that manage the filter lifecycle (init/check/deinit)
2632 **************************************************************************/
2633/* Signal handler: Do a soft stop, wakeup SPOE applet */
2634static void
2635sig_stop_spoe(struct sig_handler *sh)
2636{
2637 struct proxy *p;
2638
2639 p = proxy;
2640 while (p) {
2641 struct flt_conf *fconf;
2642
2643 list_for_each_entry(fconf, &p->filter_configs, list) {
Christopher Faulet3b386a32017-02-23 10:17:15 +01002644 struct spoe_config *conf;
2645 struct spoe_agent *agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002646 struct appctx *appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002647 struct spoe_appctx *spoe_appctx;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002648
Christopher Faulet3b386a32017-02-23 10:17:15 +01002649 if (fconf->id != spoe_filter_id)
2650 continue;
2651
2652 conf = fconf->conf;
2653 agent = conf->agent;
2654
Christopher Faulet42bfa462017-01-04 14:14:19 +01002655 list_for_each_entry(spoe_appctx, &agent->applets, list) {
2656 appctx = spoe_appctx->owner;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002657 si_applet_want_get(appctx->owner);
2658 si_applet_want_put(appctx->owner);
2659 appctx_wakeup(appctx);
2660 }
2661 }
2662 p = p->next;
2663 }
2664}
2665
2666
2667/* Initialize the SPOE filter. Returns -1 on error, else 0. */
2668static int
2669spoe_init(struct proxy *px, struct flt_conf *fconf)
2670{
2671 struct spoe_config *conf = fconf->conf;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002672
2673 memset(&conf->agent_fe, 0, sizeof(conf->agent_fe));
2674 init_new_proxy(&conf->agent_fe);
2675 conf->agent_fe.parent = conf->agent;
2676 conf->agent_fe.last_change = now.tv_sec;
2677 conf->agent_fe.id = conf->agent->id;
2678 conf->agent_fe.cap = PR_CAP_FE;
2679 conf->agent_fe.mode = PR_MODE_TCP;
2680 conf->agent_fe.maxconn = 0;
2681 conf->agent_fe.options2 |= PR_O2_INDEPSTR;
2682 conf->agent_fe.conn_retries = CONN_RETRIES;
2683 conf->agent_fe.accept = frontend_accept;
2684 conf->agent_fe.srv = NULL;
2685 conf->agent_fe.timeout.client = TICK_ETERNITY;
2686 conf->agent_fe.default_target = &spoe_applet.obj_type;
2687 conf->agent_fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
2688
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002689 if (!sighandler_registered) {
2690 signal_register_fct(0, sig_stop_spoe, 0);
2691 sighandler_registered = 1;
2692 }
2693
2694 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002695}
2696
2697/* Free ressources allocated by the SPOE filter. */
2698static void
2699spoe_deinit(struct proxy *px, struct flt_conf *fconf)
2700{
2701 struct spoe_config *conf = fconf->conf;
2702
2703 if (conf) {
2704 struct spoe_agent *agent = conf->agent;
2705 struct listener *l = LIST_NEXT(&conf->agent_fe.conf.listeners,
2706 struct listener *, by_fe);
2707
2708 free(l);
2709 release_spoe_agent(agent);
2710 free(conf);
2711 }
2712 fconf->conf = NULL;
2713}
2714
2715/* Check configuration of a SPOE filter for a specified proxy.
2716 * Return 1 on error, else 0. */
2717static int
2718spoe_check(struct proxy *px, struct flt_conf *fconf)
2719{
2720 struct spoe_config *conf = fconf->conf;
2721 struct proxy *target;
2722
2723 target = proxy_be_by_name(conf->agent->b.name);
2724 if (target == NULL) {
2725 Alert("Proxy %s : unknown backend '%s' used by SPOE agent '%s'"
2726 " declared at %s:%d.\n",
2727 px->id, conf->agent->b.name, conf->agent->id,
2728 conf->agent->conf.file, conf->agent->conf.line);
2729 return 1;
2730 }
2731 if (target->mode != PR_MODE_TCP) {
2732 Alert("Proxy %s : backend '%s' used by SPOE agent '%s' declared"
2733 " at %s:%d does not support HTTP mode.\n",
2734 px->id, target->id, conf->agent->id,
2735 conf->agent->conf.file, conf->agent->conf.line);
2736 return 1;
2737 }
2738
2739 free(conf->agent->b.name);
2740 conf->agent->b.name = NULL;
2741 conf->agent->b.be = target;
2742 return 0;
2743}
2744
2745/**************************************************************************
2746 * Hooks attached to a stream
2747 *************************************************************************/
2748/* Called when a filter instance is created and attach to a stream. It creates
2749 * the context that will be used to process this stream. */
2750static int
2751spoe_start(struct stream *s, struct filter *filter)
2752{
Christopher Faulet72bcc472017-01-04 16:39:41 +01002753 struct spoe_config *conf = FLT_CONF(filter);
2754 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002755 struct spoe_context *ctx;
2756
2757 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
Christopher Faulet72bcc472017-01-04 16:39:41 +01002758 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002759 __FUNCTION__, s);
2760
2761 ctx = create_spoe_context(filter);
2762 if (ctx == NULL) {
Christopher Faulet72bcc472017-01-04 16:39:41 +01002763 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2764 " - failed to create SPOE context\n",
2765 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2766 __FUNCTION__, ctx->strm);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002767 send_log(s->be, LOG_EMERG,
Christopher Faulet72bcc472017-01-04 16:39:41 +01002768 "SPOE: [%s] failed to create SPOE context\n",
2769 agent->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002770 return 0;
2771 }
2772
2773 ctx->strm = s;
2774 ctx->state = SPOE_CTX_ST_READY;
2775 filter->ctx = ctx;
2776
2777 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_FE]))
2778 filter->pre_analyzers |= AN_REQ_INSPECT_FE;
2779
2780 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_REQ_BE]))
2781 filter->pre_analyzers |= AN_REQ_INSPECT_BE;
2782
2783 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_TCP_RSP]))
2784 filter->pre_analyzers |= AN_RES_INSPECT;
2785
2786 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_FE]))
2787 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_FE;
2788
2789 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_REQ_BE]))
2790 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_BE;
2791
2792 if (!LIST_ISEMPTY(&ctx->messages[SPOE_EV_ON_HTTP_RSP]))
2793 filter->pre_analyzers |= AN_RES_HTTP_PROCESS_FE;
2794
2795 return 1;
2796}
2797
2798/* Called when a filter instance is detached from a stream. It release the
2799 * attached SPOE context. */
2800static void
2801spoe_stop(struct stream *s, struct filter *filter)
2802{
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002803 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
2804 (int)now.tv_sec, (int)now.tv_usec,
2805 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2806 __FUNCTION__, s);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002807 destroy_spoe_context(filter->ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002808}
2809
Christopher Fauletf7a30922016-11-10 15:04:51 +01002810
2811/*
2812 * Called when the stream is woken up because of expired timer.
2813 */
2814static void
2815spoe_check_timeouts(struct stream *s, struct filter *filter)
2816{
2817 struct spoe_context *ctx = filter->ctx;
2818
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002819 if (tick_is_expired(ctx->process_exp, now_ms)) {
2820 s->pending_events |= TASK_WOKEN_MSG;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002821 release_spoe_buffer(ctx);
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002822 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01002823}
2824
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002825/* Called when we are ready to filter data on a channel */
2826static int
2827spoe_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
2828{
2829 struct spoe_context *ctx = filter->ctx;
2830 int ret = 1;
2831
2832 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
2833 " - ctx-flags=0x%08x\n",
2834 (int)now.tv_sec, (int)now.tv_usec,
2835 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2836 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
2837
Christopher Fauletb067b062017-01-04 16:39:11 +01002838 if (ctx->state == SPOE_CTX_ST_NONE)
2839 goto out;
2840
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002841 if (!(chn->flags & CF_ISRESP)) {
2842 if (filter->pre_analyzers & AN_REQ_INSPECT_FE)
2843 chn->analysers |= AN_REQ_INSPECT_FE;
2844 if (filter->pre_analyzers & AN_REQ_INSPECT_BE)
2845 chn->analysers |= AN_REQ_INSPECT_BE;
2846
2847 if (ctx->flags & SPOE_CTX_FL_CLI_CONNECTED)
2848 goto out;
2849
2850 ctx->stream_id = s->uniq_id;
Christopher Fauletb067b062017-01-04 16:39:11 +01002851 ret = process_spoe_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
2852 if (!ret)
2853 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002854 ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
2855 }
2856 else {
2857 if (filter->pre_analyzers & SPOE_EV_ON_TCP_RSP)
2858 chn->analysers |= AN_RES_INSPECT;
2859
2860 if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
2861 goto out;
2862
Christopher Fauletb067b062017-01-04 16:39:11 +01002863 ret = process_spoe_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002864 if (!ret) {
2865 channel_dont_read(chn);
2866 channel_dont_close(chn);
Christopher Fauletb067b062017-01-04 16:39:11 +01002867 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002868 }
Christopher Fauletb067b062017-01-04 16:39:11 +01002869 ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002870 }
2871
2872 out:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002873 return ret;
2874}
2875
2876/* Called before a processing happens on a given channel */
2877static int
2878spoe_chn_pre_analyze(struct stream *s, struct filter *filter,
2879 struct channel *chn, unsigned an_bit)
2880{
2881 struct spoe_context *ctx = filter->ctx;
2882 int ret = 1;
2883
2884 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
2885 " - ctx-flags=0x%08x - ana=0x%08x\n",
2886 (int)now.tv_sec, (int)now.tv_usec,
2887 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2888 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
2889 ctx->flags, an_bit);
2890
Christopher Fauletb067b062017-01-04 16:39:11 +01002891 if (ctx->state == SPOE_CTX_ST_NONE)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002892 goto out;
2893
2894 switch (an_bit) {
2895 case AN_REQ_INSPECT_FE:
2896 ret = process_spoe_event(s, ctx, SPOE_EV_ON_TCP_REQ_FE);
2897 break;
2898 case AN_REQ_INSPECT_BE:
2899 ret = process_spoe_event(s, ctx, SPOE_EV_ON_TCP_REQ_BE);
2900 break;
2901 case AN_RES_INSPECT:
2902 ret = process_spoe_event(s, ctx, SPOE_EV_ON_TCP_RSP);
2903 break;
2904 case AN_REQ_HTTP_PROCESS_FE:
2905 ret = process_spoe_event(s, ctx, SPOE_EV_ON_HTTP_REQ_FE);
2906 break;
2907 case AN_REQ_HTTP_PROCESS_BE:
2908 ret = process_spoe_event(s, ctx, SPOE_EV_ON_HTTP_REQ_BE);
2909 break;
2910 case AN_RES_HTTP_PROCESS_FE:
2911 ret = process_spoe_event(s, ctx, SPOE_EV_ON_HTTP_RSP);
2912 break;
2913 }
2914
2915 out:
2916 if (!ret) {
2917 channel_dont_read(chn);
2918 channel_dont_close(chn);
2919 }
2920 return ret;
2921}
2922
2923/* Called when the filtering on the channel ends. */
2924static int
2925spoe_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
2926{
2927 struct spoe_context *ctx = filter->ctx;
2928
2929 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
2930 " - ctx-flags=0x%08x\n",
2931 (int)now.tv_sec, (int)now.tv_usec,
2932 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
2933 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
2934
2935 if (!(ctx->flags & SPOE_CTX_FL_PROCESS)) {
2936 reset_spoe_context(ctx);
2937 }
2938
2939 return 1;
2940}
2941
2942/********************************************************************
2943 * Functions that manage the filter initialization
2944 ********************************************************************/
2945struct flt_ops spoe_ops = {
2946 /* Manage SPOE filter, called for each filter declaration */
2947 .init = spoe_init,
2948 .deinit = spoe_deinit,
2949 .check = spoe_check,
2950
2951 /* Handle start/stop of SPOE */
Christopher Fauletf7a30922016-11-10 15:04:51 +01002952 .attach = spoe_start,
2953 .detach = spoe_stop,
2954 .check_timeouts = spoe_check_timeouts,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002955
2956 /* Handle channels activity */
2957 .channel_start_analyze = spoe_start_analyze,
2958 .channel_pre_analyze = spoe_chn_pre_analyze,
2959 .channel_end_analyze = spoe_end_analyze,
2960};
2961
2962
2963static int
2964cfg_parse_spoe_agent(const char *file, int linenum, char **args, int kwm)
2965{
2966 const char *err;
2967 int i, err_code = 0;
2968
2969 if ((cfg_scope == NULL && curengine != NULL) ||
2970 (cfg_scope != NULL && curengine == NULL) ||
2971 strcmp(curengine, cfg_scope))
2972 goto out;
2973
2974 if (!strcmp(args[0], "spoe-agent")) { /* new spoe-agent section */
2975 if (!*args[1]) {
2976 Alert("parsing [%s:%d] : missing name for spoe-agent section.\n",
2977 file, linenum);
2978 err_code |= ERR_ALERT | ERR_ABORT;
2979 goto out;
2980 }
2981 if (*args[2]) {
2982 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
2983 file, linenum, args[2]);
2984 err_code |= ERR_ALERT | ERR_ABORT;
2985 goto out;
2986 }
2987
2988 err = invalid_char(args[1]);
2989 if (err) {
2990 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
2991 file, linenum, *err, args[0], args[1]);
2992 err_code |= ERR_ALERT | ERR_ABORT;
2993 goto out;
2994 }
2995
2996 if (curagent != NULL) {
2997 Alert("parsing [%s:%d] : another spoe-agent section previously defined.\n",
2998 file, linenum);
2999 err_code |= ERR_ALERT | ERR_ABORT;
3000 goto out;
3001 }
3002 if ((curagent = calloc(1, sizeof(*curagent))) == NULL) {
3003 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3004 err_code |= ERR_ALERT | ERR_ABORT;
3005 goto out;
3006 }
3007
3008 curagent->id = strdup(args[1]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003009
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003010 curagent->conf.file = strdup(file);
3011 curagent->conf.line = linenum;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003012
3013 curagent->timeout.hello = TICK_ETERNITY;
3014 curagent->timeout.idle = TICK_ETERNITY;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003015 curagent->timeout.processing = TICK_ETERNITY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003016
3017 curagent->engine_id = NULL;
3018 curagent->var_pfx = NULL;
3019 curagent->var_on_error = NULL;
3020 curagent->flags = 0;
3021 curagent->cps_max = 0;
3022 curagent->eps_max = 0;
3023 curagent->max_frame_size = global.tune.bufsize - 4;
3024 curagent->min_applets = 0;
3025 curagent->max_fpa = 100;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003026
3027 for (i = 0; i < SPOE_EV_EVENTS; ++i)
3028 LIST_INIT(&curagent->messages[i]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003029
3030 curagent->applets_act = 0;
3031 curagent->applets_idle = 0;
3032 curagent->sending_rate = 0;
3033
3034 LIST_INIT(&curagent->applets);
3035 LIST_INIT(&curagent->sending_queue);
3036 LIST_INIT(&curagent->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003037 }
3038 else if (!strcmp(args[0], "use-backend")) {
3039 if (!*args[1]) {
3040 Alert("parsing [%s:%d] : '%s' expects a backend name.\n",
3041 file, linenum, args[0]);
3042 err_code |= ERR_ALERT | ERR_FATAL;
3043 goto out;
3044 }
3045 if (*args[2]) {
3046 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3047 file, linenum, args[2]);
3048 err_code |= ERR_ALERT | ERR_ABORT;
3049 goto out;
3050 }
3051 free(curagent->b.name);
3052 curagent->b.name = strdup(args[1]);
3053 }
3054 else if (!strcmp(args[0], "messages")) {
3055 int cur_arg = 1;
3056 while (*args[cur_arg]) {
3057 struct spoe_msg_placeholder *mp = NULL;
3058
3059 list_for_each_entry(mp, &curmps, list) {
3060 if (!strcmp(mp->id, args[cur_arg])) {
3061 Alert("parsing [%s:%d]: spoe-message message '%s' already declared.\n",
3062 file, linenum, args[cur_arg]);
3063 err_code |= ERR_ALERT | ERR_FATAL;
3064 goto out;
3065 }
3066 }
3067
3068 if ((mp = calloc(1, sizeof(*mp))) == NULL) {
3069 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3070 err_code |= ERR_ALERT | ERR_ABORT;
3071 goto out;
3072 }
3073 mp->id = strdup(args[cur_arg]);
3074 LIST_ADDQ(&curmps, &mp->list);
3075 cur_arg++;
3076 }
3077 }
3078 else if (!strcmp(args[0], "timeout")) {
3079 unsigned int *tv = NULL;
3080 const char *res;
3081 unsigned timeout;
3082
3083 if (!*args[1]) {
3084 Alert("parsing [%s:%d] : 'timeout' expects 'connect', 'idle' and 'ack'.\n",
3085 file, linenum);
3086 err_code |= ERR_ALERT | ERR_FATAL;
3087 goto out;
3088 }
3089 if (!strcmp(args[1], "hello"))
3090 tv = &curagent->timeout.hello;
3091 else if (!strcmp(args[1], "idle"))
3092 tv = &curagent->timeout.idle;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003093 else if (!strcmp(args[1], "processing"))
3094 tv = &curagent->timeout.processing;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003095 else {
Christopher Faulet03a34492016-11-19 16:47:56 +01003096 Alert("parsing [%s:%d] : 'timeout' supports 'connect', 'idle' or 'processing' (got %s).\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003097 file, linenum, args[1]);
3098 err_code |= ERR_ALERT | ERR_FATAL;
3099 goto out;
3100 }
3101 if (!*args[2]) {
3102 Alert("parsing [%s:%d] : 'timeout %s' expects an integer value (in milliseconds).\n",
3103 file, linenum, args[1]);
3104 err_code |= ERR_ALERT | ERR_FATAL;
3105 goto out;
3106 }
3107 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
3108 if (res) {
3109 Alert("parsing [%s:%d] : unexpected character '%c' in 'timeout %s'.\n",
3110 file, linenum, *res, args[1]);
3111 err_code |= ERR_ALERT | ERR_ABORT;
3112 goto out;
3113 }
3114 if (*args[3]) {
3115 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3116 file, linenum, args[3]);
3117 err_code |= ERR_ALERT | ERR_ABORT;
3118 goto out;
3119 }
3120 *tv = MS_TO_TICKS(timeout);
3121 }
3122 else if (!strcmp(args[0], "option")) {
3123 if (!*args[1]) {
3124 Alert("parsing [%s:%d]: '%s' expects an option name.\n",
3125 file, linenum, args[0]);
3126 err_code |= ERR_ALERT | ERR_FATAL;
3127 goto out;
3128 }
3129 if (!strcmp(args[1], "var-prefix")) {
3130 char *tmp;
3131
3132 if (!*args[2]) {
3133 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3134 file, linenum, args[0],
3135 args[1]);
3136 err_code |= ERR_ALERT | ERR_FATAL;
3137 goto out;
3138 }
3139 tmp = args[2];
3140 while (*tmp) {
3141 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3142 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3143 file, linenum, args[0], args[1]);
3144 err_code |= ERR_ALERT | ERR_FATAL;
3145 goto out;
3146 }
3147 tmp++;
3148 }
3149 curagent->var_pfx = strdup(args[2]);
3150 }
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003151 else if (!strcmp(args[1], "continue-on-error")) {
3152 if (*args[2]) {
3153 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
Christopher Faulet48026722016-11-16 15:01:12 +01003154 file, linenum, args[2]);
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003155 err_code |= ERR_ALERT | ERR_ABORT;
3156 goto out;
3157 }
3158 curagent->flags |= SPOE_FL_CONT_ON_ERR;
3159 }
Christopher Faulet985532d2016-11-16 15:36:19 +01003160 else if (!strcmp(args[1], "set-on-error")) {
3161 char *tmp;
3162
3163 if (!*args[2]) {
3164 Alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3165 file, linenum, args[0],
3166 args[1]);
3167 err_code |= ERR_ALERT | ERR_FATAL;
3168 goto out;
3169 }
3170 tmp = args[2];
3171 while (*tmp) {
3172 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3173 Alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z_-.] chars.\n",
3174 file, linenum, args[0], args[1]);
3175 err_code |= ERR_ALERT | ERR_FATAL;
3176 goto out;
3177 }
3178 tmp++;
3179 }
3180 curagent->var_on_error = strdup(args[2]);
3181 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003182 else {
3183 Alert("parsing [%s:%d]: option '%s' is not supported.\n",
3184 file, linenum, args[1]);
3185 err_code |= ERR_ALERT | ERR_FATAL;
3186 goto out;
3187 }
Christopher Faulet48026722016-11-16 15:01:12 +01003188 }
3189 else if (!strcmp(args[0], "maxconnrate")) {
3190 if (!*args[1]) {
3191 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3192 file, linenum, args[0]);
3193 err_code |= ERR_ALERT | ERR_FATAL;
3194 goto out;
3195 }
3196 if (*args[2]) {
3197 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3198 file, linenum, args[2]);
3199 err_code |= ERR_ALERT | ERR_ABORT;
3200 goto out;
3201 }
3202 curagent->cps_max = atol(args[1]);
3203 }
3204 else if (!strcmp(args[0], "maxerrrate")) {
3205 if (!*args[1]) {
3206 Alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3207 file, linenum, args[0]);
3208 err_code |= ERR_ALERT | ERR_FATAL;
3209 goto out;
3210 }
3211 if (*args[2]) {
3212 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3213 file, linenum, args[2]);
3214 err_code |= ERR_ALERT | ERR_ABORT;
3215 goto out;
3216 }
3217 curagent->eps_max = atol(args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003218 }
3219 else if (*args[0]) {
3220 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-agent section.\n",
3221 file, linenum, args[0]);
3222 err_code |= ERR_ALERT | ERR_FATAL;
3223 goto out;
3224 }
3225 out:
3226 return err_code;
3227}
3228
3229static int
3230cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
3231{
3232 struct spoe_message *msg;
3233 struct spoe_arg *arg;
3234 const char *err;
3235 char *errmsg = NULL;
3236 int err_code = 0;
3237
3238 if ((cfg_scope == NULL && curengine != NULL) ||
3239 (cfg_scope != NULL && curengine == NULL) ||
3240 strcmp(curengine, cfg_scope))
3241 goto out;
3242
3243 if (!strcmp(args[0], "spoe-message")) { /* new spoe-message section */
3244 if (!*args[1]) {
3245 Alert("parsing [%s:%d] : missing name for spoe-message section.\n",
3246 file, linenum);
3247 err_code |= ERR_ALERT | ERR_ABORT;
3248 goto out;
3249 }
3250 if (*args[2]) {
3251 Alert("parsing [%s:%d] : cannot handle unexpected argument '%s'.\n",
3252 file, linenum, args[2]);
3253 err_code |= ERR_ALERT | ERR_ABORT;
3254 goto out;
3255 }
3256
3257 err = invalid_char(args[1]);
3258 if (err) {
3259 Alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3260 file, linenum, *err, args[0], args[1]);
3261 err_code |= ERR_ALERT | ERR_ABORT;
3262 goto out;
3263 }
3264
3265 list_for_each_entry(msg, &curmsgs, list) {
3266 if (!strcmp(msg->id, args[1])) {
3267 Alert("parsing [%s:%d]: spoe-message section '%s' has the same"
3268 " name as another one declared at %s:%d.\n",
3269 file, linenum, args[1], msg->conf.file, msg->conf.line);
3270 err_code |= ERR_ALERT | ERR_FATAL;
3271 goto out;
3272 }
3273 }
3274
3275 if ((curmsg = calloc(1, sizeof(*curmsg))) == NULL) {
3276 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3277 err_code |= ERR_ALERT | ERR_ABORT;
3278 goto out;
3279 }
3280
3281 curmsg->id = strdup(args[1]);
3282 curmsg->id_len = strlen(curmsg->id);
3283 curmsg->event = SPOE_EV_NONE;
3284 curmsg->conf.file = strdup(file);
3285 curmsg->conf.line = linenum;
3286 LIST_INIT(&curmsg->args);
3287 LIST_ADDQ(&curmsgs, &curmsg->list);
3288 }
3289 else if (!strcmp(args[0], "args")) {
3290 int cur_arg = 1;
3291
3292 curproxy->conf.args.ctx = ARGC_SPOE;
3293 curproxy->conf.args.file = file;
3294 curproxy->conf.args.line = linenum;
3295 while (*args[cur_arg]) {
3296 char *delim = strchr(args[cur_arg], '=');
3297 int idx = 0;
3298
3299 if ((arg = calloc(1, sizeof(*arg))) == NULL) {
3300 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3301 err_code |= ERR_ALERT | ERR_ABORT;
3302 goto out;
3303 }
3304
3305 if (!delim) {
3306 arg->name = NULL;
3307 arg->name_len = 0;
3308 delim = args[cur_arg];
3309 }
3310 else {
3311 arg->name = my_strndup(args[cur_arg], delim - args[cur_arg]);
3312 arg->name_len = delim - args[cur_arg];
3313 delim++;
3314 }
Christopher Fauletb0b42382017-02-23 22:41:09 +01003315 arg->expr = sample_parse_expr((char*[]){delim, NULL},
3316 &idx, file, linenum, &errmsg,
3317 &curproxy->conf.args);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003318 if (arg->expr == NULL) {
3319 Alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
3320 err_code |= ERR_ALERT | ERR_FATAL;
3321 free(arg->name);
3322 free(arg);
3323 goto out;
3324 }
3325 LIST_ADDQ(&curmsg->args, &arg->list);
3326 cur_arg++;
3327 }
3328 curproxy->conf.args.file = NULL;
3329 curproxy->conf.args.line = 0;
3330 }
3331 else if (!strcmp(args[0], "event")) {
3332 if (!*args[1]) {
3333 Alert("parsing [%s:%d] : missing event name.\n", file, linenum);
3334 err_code |= ERR_ALERT | ERR_ABORT;
3335 goto out;
3336 }
3337 if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_CLIENT_SESS]))
3338 curmsg->event = SPOE_EV_ON_CLIENT_SESS;
3339 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_SERVER_SESS]))
3340 curmsg->event = SPOE_EV_ON_SERVER_SESS;
3341
3342 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_FE]))
3343 curmsg->event = SPOE_EV_ON_TCP_REQ_FE;
3344 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_BE]))
3345 curmsg->event = SPOE_EV_ON_TCP_REQ_BE;
3346 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_RSP]))
3347 curmsg->event = SPOE_EV_ON_TCP_RSP;
3348
3349 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_FE]))
3350 curmsg->event = SPOE_EV_ON_HTTP_REQ_FE;
3351 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_BE]))
3352 curmsg->event = SPOE_EV_ON_HTTP_REQ_BE;
3353 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_RSP]))
3354 curmsg->event = SPOE_EV_ON_HTTP_RSP;
3355 else {
3356 Alert("parsing [%s:%d] : unkown event '%s'.\n",
3357 file, linenum, args[1]);
3358 err_code |= ERR_ALERT | ERR_ABORT;
3359 goto out;
3360 }
3361 }
3362 else if (!*args[0]) {
3363 Alert("parsing [%s:%d] : unknown keyword '%s' in spoe-message section.\n",
3364 file, linenum, args[0]);
3365 err_code |= ERR_ALERT | ERR_FATAL;
3366 goto out;
3367 }
3368 out:
3369 free(errmsg);
3370 return err_code;
3371}
3372
3373/* Return -1 on error, else 0 */
3374static int
3375parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
3376 struct flt_conf *fconf, char **err, void *private)
3377{
3378 struct list backup_sections;
3379 struct spoe_config *conf;
3380 struct spoe_message *msg, *msgback;
3381 struct spoe_msg_placeholder *mp, *mpback;
3382 char *file = NULL, *engine = NULL;
3383 int ret, pos = *cur_arg + 1;
3384
3385 conf = calloc(1, sizeof(*conf));
3386 if (conf == NULL) {
3387 memprintf(err, "%s: out of memory", args[*cur_arg]);
3388 goto error;
3389 }
3390 conf->proxy = px;
3391
3392 while (*args[pos]) {
3393 if (!strcmp(args[pos], "config")) {
3394 if (!*args[pos+1]) {
3395 memprintf(err, "'%s' : '%s' option without value",
3396 args[*cur_arg], args[pos]);
3397 goto error;
3398 }
3399 file = args[pos+1];
3400 pos += 2;
3401 }
3402 else if (!strcmp(args[pos], "engine")) {
3403 if (!*args[pos+1]) {
3404 memprintf(err, "'%s' : '%s' option without value",
3405 args[*cur_arg], args[pos]);
3406 goto error;
3407 }
3408 engine = args[pos+1];
3409 pos += 2;
3410 }
3411 else {
3412 memprintf(err, "unknown keyword '%s'", args[pos]);
3413 goto error;
3414 }
3415 }
3416 if (file == NULL) {
3417 memprintf(err, "'%s' : missing config file", args[*cur_arg]);
3418 goto error;
3419 }
3420
3421 /* backup sections and register SPOE sections */
3422 LIST_INIT(&backup_sections);
3423 cfg_backup_sections(&backup_sections);
3424 cfg_register_section("spoe-agent", cfg_parse_spoe_agent);
3425 cfg_register_section("spoe-message", cfg_parse_spoe_message);
3426
3427 /* Parse SPOE filter configuration file */
3428 curengine = engine;
3429 curproxy = px;
3430 curagent = NULL;
3431 curmsg = NULL;
3432 ret = readcfgfile(file);
3433 curproxy = NULL;
3434
3435 /* unregister SPOE sections and restore previous sections */
3436 cfg_unregister_sections();
3437 cfg_restore_sections(&backup_sections);
3438
3439 if (ret == -1) {
3440 memprintf(err, "Could not open configuration file %s : %s",
3441 file, strerror(errno));
3442 goto error;
3443 }
3444 if (ret & (ERR_ABORT|ERR_FATAL)) {
3445 memprintf(err, "Error(s) found in configuration file %s", file);
3446 goto error;
3447 }
3448
3449 /* Check SPOE agent */
3450 if (curagent == NULL) {
3451 memprintf(err, "No SPOE agent found in file %s", file);
3452 goto error;
3453 }
3454 if (curagent->b.name == NULL) {
3455 memprintf(err, "No backend declared for SPOE agent '%s' declared at %s:%d",
3456 curagent->id, curagent->conf.file, curagent->conf.line);
3457 goto error;
3458 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01003459 if (curagent->timeout.hello == TICK_ETERNITY ||
3460 curagent->timeout.idle == TICK_ETERNITY ||
Christopher Fauletf7a30922016-11-10 15:04:51 +01003461 curagent->timeout.processing == TICK_ETERNITY) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003462 Warning("Proxy '%s': missing timeouts for SPOE agent '%s' declare at %s:%d.\n"
3463 " | While not properly invalid, you will certainly encounter various problems\n"
3464 " | with such a configuration. To fix this, please ensure that all following\n"
Christopher Faulet03a34492016-11-19 16:47:56 +01003465 " | timeouts are set to a non-zero value: 'hello', 'idle', 'processing'.\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003466 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3467 }
3468 if (curagent->var_pfx == NULL) {
3469 char *tmp = curagent->id;
3470
3471 while (*tmp) {
3472 if (!isalnum(*tmp) && *tmp != '_' && *tmp != '.') {
3473 memprintf(err, "Invalid variable prefix '%s' for SPOE agent '%s' declared at %s:%d. "
3474 "Use 'option var-prefix' to set it. Only [a-zA-Z0-9_.] chars are supported.\n",
3475 curagent->id, curagent->id, curagent->conf.file, curagent->conf.line);
3476 goto error;
3477 }
3478 tmp++;
3479 }
3480 curagent->var_pfx = strdup(curagent->id);
3481 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01003482 if (curagent->engine_id == NULL)
3483 curagent->engine_id = generate_pseudo_uuid();
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003484
3485 if (LIST_ISEMPTY(&curmps)) {
3486 Warning("Proxy '%s': No message used by SPOE agent '%s' declared at %s:%d.\n",
3487 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
3488 goto finish;
3489 }
3490
3491 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3492 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
Christopher Fauleta21b0642017-01-09 16:56:23 +01003493 struct spoe_arg *arg;
3494 unsigned int where;
3495
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003496 if (!strcmp(msg->id, mp->id)) {
3497 if ((px->cap & (PR_CAP_FE|PR_CAP_BE)) == (PR_CAP_FE|PR_CAP_BE)) {
3498 if (msg->event == SPOE_EV_ON_TCP_REQ_BE)
3499 msg->event = SPOE_EV_ON_TCP_REQ_FE;
3500 if (msg->event == SPOE_EV_ON_HTTP_REQ_BE)
3501 msg->event = SPOE_EV_ON_HTTP_REQ_FE;
3502 }
3503 if (!(px->cap & PR_CAP_FE) && (msg->event == SPOE_EV_ON_CLIENT_SESS ||
3504 msg->event == SPOE_EV_ON_TCP_REQ_FE ||
3505 msg->event == SPOE_EV_ON_HTTP_REQ_FE)) {
3506 Warning("Proxy '%s': frontend event used on a backend proxy at %s:%d.\n",
3507 px->id, msg->conf.file, msg->conf.line);
3508 goto next;
3509 }
3510 if (msg->event == SPOE_EV_NONE) {
3511 Warning("Proxy '%s': Ignore SPOE message without event at %s:%d.\n",
3512 px->id, msg->conf.file, msg->conf.line);
3513 goto next;
3514 }
Christopher Fauleta21b0642017-01-09 16:56:23 +01003515
3516 where = 0;
3517 switch (msg->event) {
3518 case SPOE_EV_ON_CLIENT_SESS:
3519 where |= SMP_VAL_FE_CON_ACC;
3520 break;
3521
3522 case SPOE_EV_ON_TCP_REQ_FE:
3523 where |= SMP_VAL_FE_REQ_CNT;
3524 break;
3525
3526 case SPOE_EV_ON_HTTP_REQ_FE:
3527 where |= SMP_VAL_FE_HRQ_HDR;
3528 break;
3529
3530 case SPOE_EV_ON_TCP_REQ_BE:
3531 if (px->cap & PR_CAP_FE)
3532 where |= SMP_VAL_FE_REQ_CNT;
3533 if (px->cap & PR_CAP_BE)
3534 where |= SMP_VAL_BE_REQ_CNT;
3535 break;
3536
3537 case SPOE_EV_ON_HTTP_REQ_BE:
3538 if (px->cap & PR_CAP_FE)
3539 where |= SMP_VAL_FE_HRQ_HDR;
3540 if (px->cap & PR_CAP_BE)
3541 where |= SMP_VAL_BE_HRQ_HDR;
3542 break;
3543
3544 case SPOE_EV_ON_SERVER_SESS:
3545 where |= SMP_VAL_BE_SRV_CON;
3546 break;
3547
3548 case SPOE_EV_ON_TCP_RSP:
3549 if (px->cap & PR_CAP_FE)
3550 where |= SMP_VAL_FE_RES_CNT;
3551 if (px->cap & PR_CAP_BE)
3552 where |= SMP_VAL_BE_RES_CNT;
3553 break;
3554
3555 case SPOE_EV_ON_HTTP_RSP:
3556 if (px->cap & PR_CAP_FE)
3557 where |= SMP_VAL_FE_HRS_HDR;
3558 if (px->cap & PR_CAP_BE)
3559 where |= SMP_VAL_BE_HRS_HDR;
3560 break;
3561
3562 default:
3563 break;
3564 }
3565
3566 list_for_each_entry(arg, &msg->args, list) {
3567 if (!(arg->expr->fetch->val & where)) {
3568 Warning("Proxy '%s': Ignore SPOE message at %s:%d: "
3569 "some args extract information from '%s', "
3570 "none of which is available here ('%s').\n",
3571 px->id, msg->conf.file, msg->conf.line,
3572 sample_ckp_names(arg->expr->fetch->use),
3573 sample_ckp_names(where));
3574 goto next;
3575 }
3576 }
3577
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003578 msg->agent = curagent;
3579 LIST_DEL(&msg->list);
3580 LIST_ADDQ(&curagent->messages[msg->event], &msg->list);
3581 goto next;
3582 }
3583 }
3584 memprintf(err, "SPOE agent '%s' try to use undefined SPOE message '%s' at %s:%d",
3585 curagent->id, mp->id, curagent->conf.file, curagent->conf.line);
3586 goto error;
3587 next:
3588 continue;
3589 }
3590
3591 finish:
3592 conf->agent = curagent;
3593 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3594 LIST_DEL(&mp->list);
3595 release_spoe_msg_placeholder(mp);
3596 }
3597 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
3598 Warning("Proxy '%s': Ignore unused SPOE messages '%s' declared at %s:%d.\n",
3599 px->id, msg->id, msg->conf.file, msg->conf.line);
3600 LIST_DEL(&msg->list);
3601 release_spoe_message(msg);
3602 }
3603
3604 *cur_arg = pos;
Christopher Faulet3b386a32017-02-23 10:17:15 +01003605 fconf->id = spoe_filter_id;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003606 fconf->ops = &spoe_ops;
3607 fconf->conf = conf;
3608 return 0;
3609
3610 error:
3611 release_spoe_agent(curagent);
3612 list_for_each_entry_safe(mp, mpback, &curmps, list) {
3613 LIST_DEL(&mp->list);
3614 release_spoe_msg_placeholder(mp);
3615 }
3616 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
3617 LIST_DEL(&msg->list);
3618 release_spoe_message(msg);
3619 }
3620 free(conf);
3621 return -1;
3622}
3623
3624
3625/* Declare the filter parser for "spoe" keyword */
3626static struct flt_kw_list flt_kws = { "SPOE", { }, {
3627 { "spoe", parse_spoe_flt, NULL },
3628 { NULL, NULL, NULL },
3629 }
3630};
3631
3632__attribute__((constructor))
3633static void __spoe_init(void)
3634{
3635 flt_register_keywords(&flt_kws);
3636
3637 LIST_INIT(&curmsgs);
3638 LIST_INIT(&curmps);
3639 pool2_spoe_ctx = create_pool("spoe_ctx", sizeof(struct spoe_context), MEM_F_SHARED);
Christopher Faulet42bfa462017-01-04 14:14:19 +01003640 pool2_spoe_appctx = create_pool("spoe_appctx", sizeof(struct spoe_appctx), MEM_F_SHARED);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003641}
3642
3643__attribute__((destructor))
3644static void
3645__spoe_deinit(void)
3646{
3647 pool_destroy2(pool2_spoe_ctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01003648 pool_destroy2(pool2_spoe_appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003649}