From 51347ed94c54297c0b69954e4fc9c6b7cd4cc4ed Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 11 Jun 2013 19:34:13 +0200 Subject: [PATCH] MEDIUM: http: add the "set-mark" action on http-request/http-response rules "set-mark" is used to set the Netfilter MARK on all packets sent to the client to the value passed in on platforms which support it. This value is an unsigned 32 bit value which can be matched by netfilter and by the routing table. It can be expressed both in decimal or hexadecimal format (prefixed by "0x"). This can be useful to force certain packets to take a different route (for example a cheaper network path for bulk downloads). This works on Linux kernels 2.6.32 and above and requires admin privileges. --- doc/configuration.txt | 25 +++++++++++++-- include/types/proto_http.h | 4 +++ src/proto_http.c | 66 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index c59b9665c..a04c6f763 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -2669,7 +2669,8 @@ http-check send-state http-request { allow | deny | tarpit | auth [realm ] | redirect | add-header | set-header | - set-nice | set-log-level | set-tos } + set-nice | set-log-level | set-tos | + set-mark } [ { if | unless } ] Access control for Layer 7 requests @@ -2752,6 +2753,15 @@ http-request { allow | deny | tarpit | auth [realm ] | redirect | border routers based on some information from the request. See RFC 2474, 2597, 3260 and 4594 for more information. + - "set-mark" is used to set the Netfilter MARK on all packets sent to the + client to the value passed in on platforms which support it. This + value is an unsigned 32 bit value which can be matched by netfilter and + by the routing table. It can be expressed both in decimal or hexadecimal + format (prefixed by "0x"). This can be useful to force certain packets to + take a different route (for example a cheaper network path for bulk + downloads). This works on Linux kernels 2.6.32 and above and requires + admin privileges. + There is no limit to the number of http-request statements per instance. It is important to know that http-request rules are processed very early in @@ -2788,8 +2798,8 @@ http-request { allow | deny | tarpit | auth [realm ] | redirect | about ACL usage. http-response { allow | deny | add-header | set-nice | - set-header | set-log-level } - [ { if | unless } ] + set-header | set-log-level | + set-mark } [ { if | unless } ] Access control for Layer 7 responses May be used in sections: defaults | frontend | listen | backend @@ -2849,6 +2859,15 @@ http-response { allow | deny | add-header | set-nice | border routers based on some information from the request. See RFC 2474, 2597, 3260 and 4594 for more information. + - "set-mark" is used to set the Netfilter MARK on all packets sent to the + client to the value passed in on platforms which support it. This + value is an unsigned 32 bit value which can be matched by netfilter and + by the routing table. It can be expressed both in decimal or hexadecimal + format (prefixed by "0x"). This can be useful to force certain packets to + take a different route (for example a cheaper network path for bulk + downloads). This works on Linux kernels 2.6.32 and above and requires + admin privileges. + There is no limit to the number of http-response statements per instance. It is important to know that http-reqsponse rules are processed very early in diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 89980b16e..1d7c92ffe 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -249,6 +249,7 @@ enum { HTTP_REQ_ACT_SET_NICE, HTTP_REQ_ACT_SET_LOGL, HTTP_REQ_ACT_SET_TOS, + HTTP_REQ_ACT_SET_MARK, HTTP_REQ_ACT_MAX /* must always be last */ }; @@ -262,6 +263,7 @@ enum { HTTP_RES_ACT_SET_NICE, HTTP_RES_ACT_SET_LOGL, HTTP_RES_ACT_SET_TOS, + HTTP_RES_ACT_SET_MARK, HTTP_RES_ACT_MAX /* must always be last */ }; @@ -377,6 +379,7 @@ struct http_req_rule { int nice; /* nice value for HTTP_REQ_ACT_SET_NICE */ int loglevel; /* log-level value for HTTP_REQ_ACT_SET_LOGL */ int tos; /* tos value for HTTP_REQ_ACT_SET_TOS */ + int mark; /* nfmark value for HTTP_REQ_ACT_SET_MARK */ } arg; /* arguments used by some actions */ }; @@ -393,6 +396,7 @@ struct http_res_rule { int nice; /* nice value for HTTP_RES_ACT_SET_NICE */ int loglevel; /* log-level value for HTTP_RES_ACT_SET_LOGL */ int tos; /* tos value for HTTP_RES_ACT_SET_TOS */ + int mark; /* nfmark value for HTTP_RES_ACT_SET_MARK */ } arg; /* arguments used by some actions */ }; diff --git a/src/proto_http.c b/src/proto_http.c index ea9e14cb7..2164a85b0 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -3218,6 +3218,12 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct session #endif break; + case HTTP_REQ_ACT_SET_MARK: +#ifdef SO_MARK + setsockopt(s->req->prod->conn->t.sock.fd, SOL_SOCKET, SO_MARK, &rule->arg.mark, sizeof(rule->arg.mark)); +#endif + break; + case HTTP_REQ_ACT_SET_LOGL: s->logs.level = rule->arg.loglevel; break; @@ -3298,6 +3304,12 @@ http_res_get_intercept_rule(struct proxy *px, struct list *rules, struct session #endif break; + case HTTP_RES_ACT_SET_MARK: +#ifdef SO_MARK + setsockopt(s->req->prod->conn->t.sock.fd, SOL_SOCKET, SO_MARK, &rule->arg.mark, sizeof(rule->arg.mark)); +#endif + break; + case HTTP_RES_ACT_SET_LOGL: s->logs.level = rule->arg.loglevel; break; @@ -8457,6 +8469,31 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i #else Alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]); goto out_err; +#endif + } else if (!strcmp(args[0], "set-mark")) { +#ifdef SO_MARK + char *err; + rule->action = HTTP_REQ_ACT_SET_MARK; + cur_arg = 1; + + if (!*args[cur_arg] || + (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) { + Alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.mark = strtoul(args[cur_arg], &err, 0); + if (err && *err != '\0') { + Alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n", + file, linenum, err, args[0]); + goto out_err; + } + cur_arg++; + global.last_checks |= LSTCHK_NETADM; +#else + Alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]); + goto out_err; #endif } else if (!strcmp(args[0], "set-log-level")) { rule->action = HTTP_REQ_ACT_SET_LOGL; @@ -8513,7 +8550,7 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i cur_arg = 2; return rule; } else { - Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-log-level', but got '%s'%s.\n", + Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n", file, linenum, args[0], *args[0] ? "" : " (missing argument)"); goto out_err; } @@ -8600,6 +8637,31 @@ struct http_res_rule *parse_http_res_cond(const char **args, const char *file, i #else Alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]); goto out_err; +#endif + } else if (!strcmp(args[0], "set-mark")) { +#ifdef SO_MARK + char *err; + rule->action = HTTP_RES_ACT_SET_MARK; + cur_arg = 1; + + if (!*args[cur_arg] || + (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) { + Alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.mark = strtoul(args[cur_arg], &err, 0); + if (err && *err != '\0') { + Alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n", + file, linenum, err, args[0]); + goto out_err; + } + cur_arg++; + global.last_checks |= LSTCHK_NETADM; +#else + Alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]); + goto out_err; #endif } else if (!strcmp(args[0], "set-log-level")) { rule->action = HTTP_RES_ACT_SET_LOGL; @@ -8637,7 +8699,7 @@ struct http_res_rule *parse_http_res_cond(const char **args, const char *file, i (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR); cur_arg += 2; } else { - Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-log-level', but got '%s'%s.\n", + Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n", file, linenum, args[0], *args[0] ? "" : " (missing argument)"); goto out_err; }