]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: tcp: add registration and processing of TCP L5 rules
authorWilly Tarreau <w@1wt.eu>
Fri, 21 Oct 2016 14:37:51 +0000 (16:37 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 21 Oct 2016 16:19:24 +0000 (18:19 +0200)
This commit introduces "tcp-request session" rules. These are very
much like "tcp-request connection" rules except that they're processed
after the handshake, so it is possible to consider SSL information and
addresses rewritten by the proxy protocol header in actions. This is
particularly useful to track proxied sources as this was not possible
before, given that tcp-request content rules are processed after each
HTTP request. Similarly it is possible to assign the proxied source
address or the client's cert to a variable.

include/common/cfgparse.h
include/proto/proto_tcp.h
include/types/action.h
include/types/listener.h
include/types/proxy.h
src/cfgparse.c
src/proto_tcp.c
src/proxy.c
src/session.c
src/stick_table.c
src/vars.c

index d7853277114c9707cc0fc5c995fd4175ace7688d..6dc6ad586c0c02924101cfbb2905ed2d381bf422 100644 (file)
@@ -75,6 +75,7 @@ int cfg_register_section(char *section_name,
                          int (*section_parser)(const char *, int, char **, int));
 void cfg_unregister_sections(void);
 int warnif_misplaced_tcp_conn(struct proxy *proxy, const char *file, int line, const char *arg);
+int warnif_misplaced_tcp_sess(struct proxy *proxy, const char *file, int line, const char *arg);
 int warnif_misplaced_tcp_cont(struct proxy *proxy, const char *file, int line, const char *arg);
 
 /*
index 05b6e0268915896cd63b6e4cd40bdef25b3634ba..f5b9e553633f828c0287e7294e479fa2cd008563 100644 (file)
@@ -39,9 +39,11 @@ int tcp_drain(int fd);
 int tcp_inspect_request(struct stream *s, struct channel *req, int an_bit);
 int tcp_inspect_response(struct stream *s, struct channel *rep, int an_bit);
 int tcp_exec_l4_rules(struct session *sess);
+int tcp_exec_l5_rules(struct session *sess);
 
 /* TCP keywords. */
 void tcp_req_conn_keywords_register(struct action_kw_list *kw_list);
+void tcp_req_sess_keywords_register(struct action_kw_list *kw_list);
 void tcp_req_cont_keywords_register(struct action_kw_list *kw_list);
 void tcp_res_cont_keywords_register(struct action_kw_list *kw_list);
 
index fce6bc87bbbd3e2986c4473c13bbcf57b195e956..5a70db06cf3fda5372c1bfa37414459eace98245 100644 (file)
@@ -29,6 +29,7 @@
 
 enum act_from {
        ACT_F_TCP_REQ_CON, /* tcp-request connection */
+       ACT_F_TCP_REQ_SES, /* tcp-request session */
        ACT_F_TCP_REQ_CNT, /* tcp-request content */
        ACT_F_TCP_RES_CNT, /* tcp-response content */
        ACT_F_HTTP_REQ,    /* http-request */
index 8cfe40b8e236f692e4018d761226100ec44fd1ac..1f14cc0bdfb7730e7505fe2f9f2d87a8c0b1aab2 100644 (file)
@@ -86,6 +86,7 @@ enum li_state {
 #define LI_O_NOQUICKACK         0x0004  /* disable quick ack of immediate data (linux) */
 #define LI_O_DEF_ACCEPT         0x0008  /* wait up to 1 second for data before accepting */
 #define LI_O_TCP_L4_RULES       0x0010  /* run TCP L4 rules checks on the incoming connection */
+#define LI_O_TCP_L5_RULES       0x0020  /* run TCP L5 rules checks on the incoming session */
 #define LI_O_CHK_MONNET         0x0040  /* check the source against a monitor-net rule */
 #define LI_O_ACC_PROXY          0x0080  /* find the proxied address in the first request line */
 #define LI_O_UNLIMITED          0x0100  /* listener not subject to global limits (peers & stats socket) */
index e23b4e4bfa7e99be64d482d871c2f32d957ad993..2f4f9b9cb574c0422e7199346246028cea95bb61 100644 (file)
@@ -269,6 +269,7 @@ struct proxy {
                unsigned int inspect_delay;     /* inspection delay */
                struct list inspect_rules;      /* inspection rules */
                struct list l4_rules;           /* layer4 rules */
+               struct list l5_rules;           /* layer5 rules */
        } tcp_req;
        struct {                                /* TCP request processing */
                unsigned int inspect_delay;     /* inspection delay */
index 17f9d19a4301adc552e04b45f4d63debef3fdc87..cc2f507e7d779d58b30ed528f8a7dbbb1072360a 100644 (file)
@@ -358,6 +358,19 @@ int alertif_too_many_args(int maxarg, const char *file, int linenum, char **args
        return alertif_too_many_args_idx(maxarg, 0, file, linenum, args, err_code);
 }
 
+/* Report a warning if a rule is placed after a 'tcp-request session' rule.
+ * Return 1 if the warning has been emitted, otherwise 0.
+ */
+int warnif_rule_after_tcp_sess(struct proxy *proxy, const char *file, int line, const char *arg)
+{
+       if (!LIST_ISEMPTY(&proxy->tcp_req.l5_rules)) {
+               Warning("parsing [%s:%d] : a '%s' rule placed after a 'tcp-request session' rule will still be processed before.\n",
+                       file, line, arg);
+               return 1;
+       }
+       return 0;
+}
+
 /* Report a warning if a rule is placed after a 'tcp-request content' rule.
  * Return 1 if the warning has been emitted, otherwise 0.
  */
@@ -464,6 +477,19 @@ int warnif_rule_after_use_server(struct proxy *proxy, const char *file, int line
 
 /* report a warning if a "tcp request connection" rule is dangerously placed */
 int warnif_misplaced_tcp_conn(struct proxy *proxy, const char *file, int line, const char *arg)
+{
+       return  warnif_rule_after_tcp_sess(proxy, file, line, arg) ||
+               warnif_rule_after_tcp_cont(proxy, file, line, arg) ||
+               warnif_rule_after_block(proxy, file, line, arg) ||
+               warnif_rule_after_http_req(proxy, file, line, arg) ||
+               warnif_rule_after_reqxxx(proxy, file, line, arg) ||
+               warnif_rule_after_reqadd(proxy, file, line, arg) ||
+               warnif_rule_after_redirect(proxy, file, line, arg) ||
+               warnif_rule_after_use_backend(proxy, file, line, arg) ||
+               warnif_rule_after_use_server(proxy, file, line, arg);
+}
+
+int warnif_misplaced_tcp_sess(struct proxy *proxy, const char *file, int line, const char *arg)
 {
        return  warnif_rule_after_tcp_cont(proxy, file, line, arg) ||
                warnif_rule_after_block(proxy, file, line, arg) ||
@@ -7810,6 +7836,45 @@ int check_config_validity()
                        }
                }
 
+               /* find the target table for 'tcp-request' layer 5 rules */
+               list_for_each_entry(trule, &curproxy->tcp_req.l5_rules, list) {
+                       struct proxy *target;
+
+                       if (trule->action < ACT_ACTION_TRK_SC0 || trule->action > ACT_ACTION_TRK_SCMAX)
+                               continue;
+
+                       if (trule->arg.trk_ctr.table.n)
+                               target = proxy_tbl_by_name(trule->arg.trk_ctr.table.n);
+                       else
+                               target = curproxy;
+
+                       if (!target) {
+                               Alert("Proxy '%s': unable to find table '%s' referenced by track-sc%d.\n",
+                                     curproxy->id, trule->arg.trk_ctr.table.n,
+                                     tcp_trk_idx(trule->action));
+                               cfgerr++;
+                       }
+                       else if (target->table.size == 0) {
+                               Alert("Proxy '%s': table '%s' used but not configured.\n",
+                                     curproxy->id, trule->arg.trk_ctr.table.n ? trule->arg.trk_ctr.table.n : curproxy->id);
+                               cfgerr++;
+                       }
+                       else if (!stktable_compatible_sample(trule->arg.trk_ctr.expr,  target->table.type)) {
+                               Alert("Proxy '%s': stick-table '%s' uses a type incompatible with the 'track-sc%d' rule.\n",
+                                     curproxy->id, trule->arg.trk_ctr.table.n ? trule->arg.trk_ctr.table.n : curproxy->id,
+                                     tcp_trk_idx(trule->action));
+                               cfgerr++;
+                       }
+                       else {
+                               free(trule->arg.trk_ctr.table.n);
+                               trule->arg.trk_ctr.table.t = &target->table;
+                               /* Note: if we decide to enhance the track-sc syntax, we may be able
+                                * to pass a list of counters to track and allocate them right here using
+                                * stktable_alloc_data_type().
+                                */
+                       }
+               }
+
                /* find the target table for 'tcp-request' layer 6 rules */
                list_for_each_entry(trule, &curproxy->tcp_req.inspect_rules, list) {
                        struct proxy *target;
@@ -8830,6 +8895,9 @@ out_uri_auth_compat:
                        if (!LIST_ISEMPTY(&curproxy->tcp_req.l4_rules))
                                listener->options |= LI_O_TCP_L4_RULES;
 
+                       if (!LIST_ISEMPTY(&curproxy->tcp_req.l5_rules))
+                               listener->options |= LI_O_TCP_L5_RULES;
+
                        if (curproxy->mon_mask.s_addr)
                                listener->options |= LI_O_CHK_MONNET;
 
index 0223b8e3daa492752d0238ddb654326022d917b6..8b3d546fd1e8bd819844d2d220f9dcedb47aba45 100644 (file)
@@ -70,6 +70,7 @@ 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_sess_keywords = LIST_HEAD_INIT(tcp_req_sess_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);
 
@@ -127,6 +128,11 @@ void tcp_req_conn_keywords_register(struct action_kw_list *kw_list)
        LIST_ADDQ(&tcp_req_conn_keywords, &kw_list->list);
 }
 
+void tcp_req_sess_keywords_register(struct action_kw_list *kw_list)
+{
+       LIST_ADDQ(&tcp_req_sess_keywords, &kw_list->list);
+}
+
 void tcp_req_cont_keywords_register(struct action_kw_list *kw_list)
 {
        LIST_ADDQ(&tcp_req_cont_keywords, &kw_list->list);
@@ -138,13 +144,18 @@ void tcp_res_cont_keywords_register(struct action_kw_list *kw_list)
 }
 
 /*
- * Return the struct http_req_action_kw associated to a keyword.
+ * Return the struct tcp_req_action_kw associated to a keyword.
  */
 static struct action_kw *tcp_req_conn_action(const char *kw)
 {
        return action_lookup(&tcp_req_conn_keywords, kw);
 }
 
+static struct action_kw *tcp_req_sess_action(const char *kw)
+{
+       return action_lookup(&tcp_req_sess_keywords, kw);
+}
+
 static struct action_kw *tcp_req_cont_action(const char *kw)
 {
        return action_lookup(&tcp_req_cont_keywords, kw);
@@ -1437,6 +1448,85 @@ int tcp_exec_l4_rules(struct session *sess)
        return result;
 }
 
+/* This function performs the TCP layer5 analysis on the current request. It
+ * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
+ * matches or if no more rule matches. It can only use rules which don't need
+ * any data. This only works on session-based client-facing stream interfaces.
+ * An example of valid use case is to track a stick-counter on the source
+ * address extracted from the proxy protocol.
+ */
+int tcp_exec_l5_rules(struct session *sess)
+{
+       struct act_rule *rule;
+       struct stksess *ts;
+       struct stktable *t = NULL;
+       int result = 1;
+       enum acl_test_res ret;
+
+       list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
+               ret = ACL_TEST_PASS;
+
+               if (rule->cond) {
+                       ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
+                       ret = acl_pass(ret);
+                       if (rule->cond->pol == ACL_COND_UNLESS)
+                               ret = !ret;
+               }
+
+               if (ret) {
+                       /* we have a matching rule. */
+                       if (rule->action == ACT_ACTION_ALLOW) {
+                               break;
+                       }
+                       else if (rule->action == ACT_ACTION_DENY) {
+                               sess->fe->fe_counters.denied_sess++;
+                               if (sess->listener->counters)
+                                       sess->listener->counters->denied_sess++;
+
+                               result = 0;
+                               break;
+                       }
+                       else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
+                               /* Note: only the first valid tracking parameter of each
+                                * applies.
+                                */
+                               struct stktable_key *key;
+
+                               if (stkctr_entry(&sess->stkctr[tcp_trk_idx(rule->action)]))
+                                       continue;
+
+                               t = rule->arg.trk_ctr.table.t;
+                               key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
+
+                               if (key && (ts = stktable_get_entry(t, key)))
+                                       stream_track_stkctr(&sess->stkctr[tcp_trk_idx(rule->action)], t, ts);
+                       }
+                       else {
+                               /* Custom keywords. */
+                               if (!rule->action_ptr)
+                                       break;
+                               switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
+                               case ACT_RET_YIELD:
+                                       /* yield is not allowed at this point. If this return code is
+                                        * used it is a bug, so I prefer to abort the process.
+                                        */
+                                       send_log(sess->fe, LOG_WARNING,
+                                                "Internal error: yield not allowed with tcp-request session actions.");
+                               case ACT_RET_STOP:
+                                       break;
+                               case ACT_RET_CONT:
+                                       continue;
+                               case ACT_RET_ERR:
+                                       result = 0;
+                                       break;
+                               }
+                               break; /* ACT_RET_STOP */
+                       }
+               }
+       }
+       return result;
+}
+
 /*
  * Execute the "set-src" action. May be called from {tcp,http}request.
  * It only changes the address and tries to preserve the original port. If the
@@ -1885,6 +1975,11 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
                        kw = tcp_req_conn_action(args[arg]);
                        rule->kw = kw;
                        rule->from = ACT_F_TCP_REQ_CON;
+               } else if (where & SMP_VAL_FE_SES_ACC) {
+                       /* L5 */
+                       kw = tcp_req_sess_action(args[arg]);
+                       rule->kw = kw;
+                       rule->from = ACT_F_TCP_REQ_SES;
                } else {
                        /* L6 */
                        kw = tcp_req_cont_action(args[arg]);
@@ -1898,6 +1993,8 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
                } else {
                        if (where & SMP_VAL_FE_CON_ACC)
                                action_build_list(&tcp_req_conn_keywords, &trash);
+                       else if (where & SMP_VAL_FE_SES_ACC)
+                               action_build_list(&tcp_req_sess_keywords, &trash);
                        else
                                action_build_list(&tcp_req_cont_keywords, &trash);
                        memprintf(err,
@@ -2174,6 +2271,50 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
                warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
                LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
        }
+       else if (strcmp(args[1], "session") == 0) {
+               arg++;
+
+               if (!(curpx->cap & PR_CAP_FE)) {
+                       memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
+                                 args[0], args[1], proxy_type_str(curpx), curpx->id);
+                       goto error;
+               }
+
+               where |= SMP_VAL_FE_SES_ACC;
+
+               if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
+                       goto error;
+
+               acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
+               if (acl) {
+                       if (acl->name && *acl->name)
+                               memprintf(err,
+                                         "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
+                                         acl->name, args[0], args[1], sample_ckp_names(where));
+                       else
+                               memprintf(err,
+                                         "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
+                                         args[0], args[1],
+                                         LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
+                                         sample_ckp_names(where));
+                       warn++;
+               }
+               else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
+                       if (acl->name && *acl->name)
+                               memprintf(err,
+                                         "acl '%s' involves keyword '%s' which is incompatible with '%s'",
+                                         acl->name, kw, sample_ckp_names(where));
+                       else
+                               memprintf(err,
+                                         "anonymous acl involves keyword '%s' which is incompatible with '%s'",
+                                         kw, sample_ckp_names(where));
+                       warn++;
+               }
+
+               /* the following function directly emits the warning */
+               warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
+               LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
+       }
        else {
                if (curpx == defpx)
                        memprintf(err,
@@ -2844,6 +2985,15 @@ static struct action_kw_list tcp_req_conn_actions = {ILH, {
        { /* END */ }
 }};
 
+static struct action_kw_list tcp_req_sess_actions = {ILH, {
+       { "silent-drop",  tcp_parse_silent_drop },
+       { "set-src",      tcp_parse_set_src_dst },
+       { "set-src-port", tcp_parse_set_src_dst },
+       { "set-dst"     , tcp_parse_set_src_dst },
+       { "set-dst-port", tcp_parse_set_src_dst },
+       { /* END */ }
+}};
+
 static struct action_kw_list tcp_req_cont_actions = {ILH, {
        { "silent-drop", tcp_parse_silent_drop },
        { /* END */ }
@@ -2880,6 +3030,7 @@ static void __tcp_protocol_init(void)
        bind_register_keywords(&bind_kws);
        srv_register_keywords(&srv_kws);
        tcp_req_conn_keywords_register(&tcp_req_conn_actions);
+       tcp_req_sess_keywords_register(&tcp_req_sess_actions);
        tcp_req_cont_keywords_register(&tcp_req_cont_actions);
        tcp_res_cont_keywords_register(&tcp_res_cont_actions);
        http_req_keywords_register(&http_req_actions);
index b90773fa4eae8f2d6015fe836d44e8a2ed6e4700..1955d823d6083763acc78637da239b4db689a28d 100644 (file)
@@ -737,6 +737,7 @@ void init_new_proxy(struct proxy *p)
        LIST_INIT(&p->tcp_req.inspect_rules);
        LIST_INIT(&p->tcp_rep.inspect_rules);
        LIST_INIT(&p->tcp_req.l4_rules);
+       LIST_INIT(&p->tcp_req.l5_rules);
        LIST_INIT(&p->req_add);
        LIST_INIT(&p->rsp_add);
        LIST_INIT(&p->listener_queue);
index d160a05749057e29e46f8ccfdc959a2b19588d0c..cdf57e388a26ddb034f3194827ac869c0fb3f625 100644 (file)
@@ -267,6 +267,10 @@ int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr
        if (sess->fe->to_log & LW_XPRT)
                cli_conn->flags |= CO_FL_XPRT_TRACKED;
 
+       /* we may have some tcp-request-session rules */
+       if ((l->options & LI_O_TCP_L5_RULES) && !tcp_exec_l5_rules(sess))
+               goto out_free_sess;
+
        session_count_new(sess);
        strm = stream_new(sess, t, &cli_conn->obj_type);
        if (!strm)
@@ -435,6 +439,10 @@ static int conn_complete_session(struct connection *conn)
        if (sess->fe->to_log & LW_XPRT)
                conn->flags |= CO_FL_XPRT_TRACKED;
 
+       /* we may have some tcp-request-session rules */
+       if ((sess->listener->options & LI_O_TCP_L5_RULES) && !tcp_exec_l5_rules(sess))
+               goto fail;
+
        session_count_new(sess);
        task->process = sess->listener->handler;
        strm = stream_new(sess, task, &conn->obj_type);
index b269bc1ea254fd46e1879d445f7c3e5296302f0e..7a2fcc21af8bb33643d985ab65877f2710c90813 100644 (file)
@@ -1519,6 +1519,12 @@ static struct action_kw_list tcp_conn_kws = { { }, {
        { /* END */ }
 }};
 
+static struct action_kw_list tcp_sess_kws = { { }, {
+       { "sc-inc-gpc0", parse_inc_gpc0, 1 },
+       { "sc-set-gpt0", parse_set_gpt0, 1 },
+       { /* END */ }
+}};
+
 static struct action_kw_list tcp_req_kws = { { }, {
        { "sc-inc-gpc0", parse_inc_gpc0, 1 },
        { "sc-set-gpt0", parse_set_gpt0, 1 },
@@ -1572,6 +1578,7 @@ static void __stick_table_init(void)
 {
        /* register som action keywords. */
        tcp_req_conn_keywords_register(&tcp_conn_kws);
+       tcp_req_sess_keywords_register(&tcp_sess_kws);
        tcp_req_cont_keywords_register(&tcp_req_kws);
        tcp_res_cont_keywords_register(&tcp_res_kws);
        http_req_keywords_register(&http_req_kws);
index a3dd85ce4bc1b19096b2a9a2f38c31104734e238..6f9b16aa976f5d1824bb21386ef3d3a775d90225 100644 (file)
@@ -359,7 +359,7 @@ static int sample_store(struct vars *vars, const char *name, struct sample *smp)
        return 1;
 }
 
-/* Returns 0 if fails, else returns 1. */
+/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
 static inline int sample_store_stream(const char *name, enum vars_scope scope, struct sample *smp)
 {
        struct vars *vars;
@@ -504,6 +504,7 @@ static enum act_return action_store(struct act_rule *rule, struct proxy *px,
        int dir;
 
        switch (rule->from) {
+       case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
        case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
        case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
        case ACT_F_HTTP_REQ:    dir = SMP_OPT_DIR_REQ; break;
@@ -587,6 +588,7 @@ static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy
                return ACT_RET_PRS_ERR;
 
        switch (rule->from) {
+       case ACT_F_TCP_REQ_SES: flags = SMP_VAL_FE_SES_ACC; break;
        case ACT_F_TCP_REQ_CNT: flags = SMP_VAL_FE_REQ_CNT; break;
        case ACT_F_TCP_RES_CNT: flags = SMP_VAL_BE_RES_CNT; break;
        case ACT_F_HTTP_REQ:    flags = SMP_VAL_FE_HRQ_HDR; break;
@@ -663,7 +665,12 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
        { /* END */ },
 }};
 
-static struct action_kw_list tcp_req_kws = { { }, {
+static struct action_kw_list tcp_req_sess_kws = { { }, {
+       { "set-var", parse_store, 1 },
+       { /* END */ }
+}};
+
+static struct action_kw_list tcp_req_cont_kws = { { }, {
        { "set-var", parse_store, 1 },
        { /* END */ }
 }};
@@ -698,7 +705,8 @@ static void __http_protocol_init(void)
 
        sample_register_fetches(&sample_fetch_keywords);
        sample_register_convs(&sample_conv_kws);
-       tcp_req_cont_keywords_register(&tcp_req_kws);
+       tcp_req_sess_keywords_register(&tcp_req_sess_kws);
+       tcp_req_cont_keywords_register(&tcp_req_cont_kws);
        tcp_res_cont_keywords_register(&tcp_res_kws);
        http_req_keywords_register(&http_req_kws);
        http_res_keywords_register(&http_res_kws);