MEDIUM: tcp: add register keyword system.
This patch introduces an action keyword registration system for TCP
rulesets similar to what is available for HTTP rulesets. This sytem
will be useful with lua.
diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h
index ac8b711..a28c531 100644
--- a/include/proto/proto_tcp.h
+++ b/include/proto/proto_tcp.h
@@ -40,6 +40,11 @@
int tcp_inspect_response(struct session *s, struct channel *rep, int an_bit);
int tcp_exec_req_rules(struct session *s);
+/* TCP keywords. */
+void tcp_req_conn_keywords_register(struct tcp_action_kw_list *kw_list);
+void tcp_req_cont_keywords_register(struct tcp_action_kw_list *kw_list);
+void tcp_res_cont_keywords_register(struct tcp_action_kw_list *kw_list);
+
/* Converts the INET/INET6 source address to a stick_table key usable for table
* lookups. <type> can be STKTABLE_TYPE_IP or STKTABLE_TYPE_IPV6. The function
* try to convert the incoming IP to the type expected by the sticktable.
diff --git a/include/types/proto_tcp.h b/include/types/proto_tcp.h
index e8f2d74..fcdc3c8 100644
--- a/include/types/proto_tcp.h
+++ b/include/types/proto_tcp.h
@@ -39,6 +39,7 @@
TCP_ACT_TRK_SCMAX = TCP_ACT_TRK_SC0 + MAX_SESS_STKCTR - 1,
TCP_ACT_CLOSE, /* close at the sender's */
TCP_ACT_CAPTURE, /* capture a fetched sample */
+ TCP_ACT_CUSTOM, /* Use for custom registered keywords. */
};
struct capture_prm {
@@ -50,12 +51,27 @@
struct list list;
struct acl_cond *cond;
int action;
+ int (*action_ptr)(struct tcp_rule *rule, struct proxy *px,
+ struct session *s);
union {
struct track_ctr_prm trk_ctr;
struct capture_prm cap;
+ void *data;
} act_prm;
};
+struct tcp_action_kw {
+ const char *kw;
+ int (*parse)(const char **args, int *cur_arg, struct proxy *px,
+ struct tcp_rule *rule, char **err);
+};
+
+struct tcp_action_kw_list {
+ const char *scope;
+ struct list list;
+ struct tcp_action_kw kw[VAR_ARRAY];
+};
+
#endif /* _TYPES_PROTO_TCP_H */
/*
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 7f94e81..f085497 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -63,6 +63,11 @@
static int tcp_bind_listeners(struct protocol *proto, char *errmsg, int errlen);
static int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen);
+/* List head of all known action keywords for "tcp-request connection" */
+struct list tcp_req_conn_keywords = LIST_HEAD_INIT(tcp_req_conn_keywords);
+struct list tcp_req_cont_keywords = LIST_HEAD_INIT(tcp_req_cont_keywords);
+struct list tcp_res_cont_keywords = LIST_HEAD_INIT(tcp_res_cont_keywords);
+
/* Note: must not be declared <const> as its list will be overwritten */
static struct protocol proto_tcpv4 = {
.name = "tcpv4",
@@ -109,6 +114,77 @@
.nb_listeners = 0,
};
+/*
+ * Register keywords.
+ */
+void tcp_req_conn_keywords_register(struct tcp_action_kw_list *kw_list)
+{
+ LIST_ADDQ(&tcp_req_conn_keywords, &kw_list->list);
+}
+
+void tcp_req_cont_keywords_register(struct tcp_action_kw_list *kw_list)
+{
+ LIST_ADDQ(&tcp_req_cont_keywords, &kw_list->list);
+}
+
+void tcp_res_cont_keywords_register(struct tcp_action_kw_list *kw_list)
+{
+ LIST_ADDQ(&tcp_res_cont_keywords, &kw_list->list);
+}
+
+/*
+ * Return the struct http_req_action_kw associated to a keyword.
+ */
+static struct tcp_action_kw *tcp_req_conn_action(const char *kw)
+{
+ struct tcp_action_kw_list *kw_list;
+ int i;
+
+ if (LIST_ISEMPTY(&tcp_req_conn_keywords))
+ return NULL;
+
+ list_for_each_entry(kw_list, &tcp_req_conn_keywords, list) {
+ for (i = 0; kw_list->kw[i].kw != NULL; i++) {
+ if (!strcmp(kw, kw_list->kw[i].kw))
+ return &kw_list->kw[i];
+ }
+ }
+ return NULL;
+}
+
+static struct tcp_action_kw *tcp_req_cont_action(const char *kw)
+{
+ struct tcp_action_kw_list *kw_list;
+ int i;
+
+ if (LIST_ISEMPTY(&tcp_req_cont_keywords))
+ return NULL;
+
+ list_for_each_entry(kw_list, &tcp_req_cont_keywords, list) {
+ for (i = 0; kw_list->kw[i].kw != NULL; i++) {
+ if (!strcmp(kw, kw_list->kw[i].kw))
+ return &kw_list->kw[i];
+ }
+ }
+ return NULL;
+}
+
+static struct tcp_action_kw *tcp_res_cont_action(const char *kw)
+{
+ struct tcp_action_kw_list *kw_list;
+ int i;
+
+ if (LIST_ISEMPTY(&tcp_res_cont_keywords))
+ return NULL;
+
+ list_for_each_entry(kw_list, &tcp_res_cont_keywords, list) {
+ for (i = 0; kw_list->kw[i].kw != NULL; i++) {
+ if (!strcmp(kw, kw_list->kw[i].kw))
+ return &kw_list->kw[i];
+ }
+ }
+ return NULL;
+}
/* Binds ipv4/ipv6 address <local> to socket <fd>, unless <flags> is set, in which
* case we try to bind <remote>. <flags> is a 2-bit field consisting of :
@@ -1124,6 +1200,10 @@
cap[h->index][len] = 0;
}
else {
+ /* Custom keywords. */
+ if (rule->action_ptr(rule, s->be, s) == 0)
+ goto missing_data;
+
/* otherwise accept */
break;
}
@@ -1223,6 +1303,9 @@
break;
}
else {
+ /* Custom keywords. */
+ rule->action_ptr(rule, s->be, s);
+
/* otherwise accept */
break;
}
@@ -1299,6 +1382,9 @@
conn_sock_want_recv(conn);
}
else {
+ /* Custom keywords. */
+ rule->action_ptr(rule, s->fe, s);
+
/* otherwise it's an accept */
break;
}
@@ -1333,10 +1419,18 @@
rule->action = TCP_ACT_CLOSE;
}
else {
- memprintf(err,
- "'%s %s' expects 'accept', 'close' or 'reject' in %s '%s' (got '%s')",
- args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]);
- return -1;
+ struct tcp_action_kw *kw;
+ kw = tcp_res_cont_action(args[arg]);
+ if (kw) {
+ arg++;
+ if (!kw->parse((const char **)args, &arg, curpx, rule, err))
+ return -1;
+ } else {
+ memprintf(err,
+ "'%s %s' expects 'accept', 'close' or 'reject' in %s '%s' (got '%s')",
+ args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]);
+ return -1;
+ }
}
if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
@@ -1527,11 +1621,23 @@
rule->action = TCP_ACT_EXPECT_PX;
}
else {
- memprintf(err,
- "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d' "
- " in %s '%s' (got '%s')",
- args[0], args[1], MAX_SESS_STKCTR-1, proxy_type_str(curpx), curpx->id, args[arg]);
- return -1;
+ struct tcp_action_kw *kw;
+ if (where & SMP_VAL_FE_CON_ACC)
+ kw = tcp_req_conn_action(args[arg]);
+ else
+ kw = tcp_req_cont_action(args[arg]);
+ if (kw) {
+ arg++;
+ if (!kw->parse((const char **)args, &arg, curpx, rule, err))
+ return -1;
+ } else {
+ memprintf(err,
+ "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d' "
+ " in %s '%s' (got '%s')",
+ args[0], args[1], MAX_SESS_STKCTR-1, proxy_type_str(curpx),
+ curpx->id, args[arg]);
+ return -1;
+ }
}
if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {