blob: 9179a80d05c72687138a120daa88969e5ad8cbd0 [file] [log] [blame]
Willy Tarreauaeae66c2020-08-28 11:03:28 +02001/*
2 * AF_INET/AF_INET6 SOCK_STREAM protocol layer (tcp)
3 *
4 * Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
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
13#include <ctype.h>
14#include <errno.h>
15#include <fcntl.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <time.h>
20
21#include <sys/param.h>
22#include <sys/socket.h>
23#include <sys/types.h>
24
25#include <netinet/tcp.h>
26#include <netinet/in.h>
27
28#include <haproxy/action-t.h>
29#include <haproxy/api.h>
30#include <haproxy/arg.h>
31#include <haproxy/channel.h>
32#include <haproxy/connection.h>
33#include <haproxy/global.h>
34#include <haproxy/http_rules.h>
35#include <haproxy/proto_tcp.h>
36#include <haproxy/proxy-t.h>
37#include <haproxy/sample.h>
38#include <haproxy/stream-t.h>
39#include <haproxy/tcp_rules.h>
40#include <haproxy/tools.h>
41
42/*
43 * Execute the "set-src" action. May be called from {tcp,http}request.
44 * It only changes the address and tries to preserve the original port. If the
45 * previous family was neither AF_INET nor AF_INET6, the port is set to zero.
46 */
47static enum act_return tcp_action_req_set_src(struct act_rule *rule, struct proxy *px,
48 struct session *sess, struct stream *s, int flags)
49{
50 struct connection *cli_conn;
51
52 if ((cli_conn = objt_conn(sess->origin)) && conn_get_src(cli_conn)) {
53 struct sample *smp;
54
55 smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
56 if (smp) {
57 int port = get_net_port(cli_conn->src);
58
59 if (smp->data.type == SMP_T_IPV4) {
60 ((struct sockaddr_in *)cli_conn->src)->sin_family = AF_INET;
61 ((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
62 ((struct sockaddr_in *)cli_conn->src)->sin_port = port;
63 } else if (smp->data.type == SMP_T_IPV6) {
64 ((struct sockaddr_in6 *)cli_conn->src)->sin6_family = AF_INET6;
65 memcpy(&((struct sockaddr_in6 *)cli_conn->src)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
66 ((struct sockaddr_in6 *)cli_conn->src)->sin6_port = port;
67 }
68 }
69 cli_conn->flags |= CO_FL_ADDR_FROM_SET;
70 }
71 return ACT_RET_CONT;
72}
73
74/*
75 * Execute the "set-dst" action. May be called from {tcp,http}request.
76 * It only changes the address and tries to preserve the original port. If the
77 * previous family was neither AF_INET nor AF_INET6, the port is set to zero.
78 */
79static enum act_return tcp_action_req_set_dst(struct act_rule *rule, struct proxy *px,
80 struct session *sess, struct stream *s, int flags)
81{
82 struct connection *cli_conn;
83
84 if ((cli_conn = objt_conn(sess->origin)) && conn_get_dst(cli_conn)) {
85 struct sample *smp;
86
87 smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
88 if (smp) {
89 int port = get_net_port(cli_conn->dst);
90
91 if (smp->data.type == SMP_T_IPV4) {
92 ((struct sockaddr_in *)cli_conn->dst)->sin_family = AF_INET;
93 ((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
94 } else if (smp->data.type == SMP_T_IPV6) {
95 ((struct sockaddr_in6 *)cli_conn->dst)->sin6_family = AF_INET6;
96 memcpy(&((struct sockaddr_in6 *)cli_conn->dst)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
97 ((struct sockaddr_in6 *)cli_conn->dst)->sin6_port = port;
98 }
99 cli_conn->flags |= CO_FL_ADDR_TO_SET;
100 }
101 }
102 return ACT_RET_CONT;
103}
104
105/*
106 * Execute the "set-src-port" action. May be called from {tcp,http}request.
107 * We must test the sin_family before setting the port. If the address family
108 * is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
109 * and the port is assigned.
110 */
111static enum act_return tcp_action_req_set_src_port(struct act_rule *rule, struct proxy *px,
112 struct session *sess, struct stream *s, int flags)
113{
114 struct connection *cli_conn;
115
116 if ((cli_conn = objt_conn(sess->origin)) && conn_get_src(cli_conn)) {
117 struct sample *smp;
118
119 smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
120 if (smp) {
121 if (cli_conn->src->ss_family == AF_INET6) {
122 ((struct sockaddr_in6 *)cli_conn->src)->sin6_port = htons(smp->data.u.sint);
123 } else {
124 if (cli_conn->src->ss_family != AF_INET) {
125 cli_conn->src->ss_family = AF_INET;
126 ((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr = 0;
127 }
128 ((struct sockaddr_in *)cli_conn->src)->sin_port = htons(smp->data.u.sint);
129 }
130 }
131 }
132 return ACT_RET_CONT;
133}
134
135/*
136 * Execute the "set-dst-port" action. May be called from {tcp,http}request.
137 * We must test the sin_family before setting the port. If the address family
138 * is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
139 * and the port is assigned.
140 */
141static enum act_return tcp_action_req_set_dst_port(struct act_rule *rule, struct proxy *px,
142 struct session *sess, struct stream *s, int flags)
143{
144 struct connection *cli_conn;
145
146 if ((cli_conn = objt_conn(sess->origin)) && conn_get_dst(cli_conn)) {
147 struct sample *smp;
148
149 smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
150 if (smp) {
151 if (cli_conn->dst->ss_family == AF_INET6) {
152 ((struct sockaddr_in6 *)cli_conn->dst)->sin6_port = htons(smp->data.u.sint);
153 } else {
154 if (cli_conn->dst->ss_family != AF_INET) {
155 cli_conn->dst->ss_family = AF_INET;
156 ((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr = 0;
157 }
158 ((struct sockaddr_in *)cli_conn->dst)->sin_port = htons(smp->data.u.sint);
159 }
160 }
161 }
162 return ACT_RET_CONT;
163}
164
165/* Executes the "silent-drop" action. May be called from {tcp,http}{request,response} */
166static enum act_return tcp_exec_action_silent_drop(struct act_rule *rule, struct proxy *px,
167 struct session *sess, struct stream *strm, int flags)
168{
169 struct connection *conn = objt_conn(sess->origin);
170
171 if (!conn)
172 goto out;
173
174 if (!conn_ctrl_ready(conn))
175 goto out;
176
177#ifdef TCP_QUICKACK
178 /* drain is needed only to send the quick ACK */
Willy Tarreau2ded48d2020-12-11 16:20:34 +0100179 conn_ctrl_drain(conn);
Willy Tarreauaeae66c2020-08-28 11:03:28 +0200180
181 /* re-enable quickack if it was disabled to ack all data and avoid
182 * retransmits from the client that might trigger a real reset.
183 */
184 setsockopt(conn->handle.fd, SOL_TCP, TCP_QUICKACK, &one, sizeof(one));
185#endif
186 /* lingering must absolutely be disabled so that we don't send a
187 * shutdown(), this is critical to the TCP_REPAIR trick. When no stream
188 * is present, returning with ERR will cause lingering to be disabled.
189 */
190 if (strm)
191 strm->si[0].flags |= SI_FL_NOLINGER;
192
193 /* We're on the client-facing side, we must force to disable lingering to
194 * ensure we will use an RST exclusively and kill any pending data.
195 */
196 fdtab[conn->handle.fd].linger_risk = 1;
197
198#ifdef TCP_REPAIR
199 if (setsockopt(conn->handle.fd, SOL_TCP, TCP_REPAIR, &one, sizeof(one)) == 0) {
200 /* socket will be quiet now */
201 goto out;
202 }
203#endif
204 /* either TCP_REPAIR is not defined or it failed (eg: permissions).
205 * Let's fall back on the TTL trick, though it only works for routed
206 * network and has no effect on local net.
207 */
208#ifdef IP_TTL
209 setsockopt(conn->handle.fd, SOL_IP, IP_TTL, &one, sizeof(one));
210#endif
211 out:
212 /* kill the stream if any */
213 if (strm) {
214 channel_abort(&strm->req);
215 channel_abort(&strm->res);
216 strm->req.analysers &= AN_REQ_FLT_END;
217 strm->res.analysers &= AN_RES_FLT_END;
218 if (strm->flags & SF_BE_ASSIGNED)
219 _HA_ATOMIC_ADD(&strm->be->be_counters.denied_req, 1);
220 if (!(strm->flags & SF_ERR_MASK))
221 strm->flags |= SF_ERR_PRXCOND;
222 if (!(strm->flags & SF_FINST_MASK))
223 strm->flags |= SF_FINST_R;
224 }
225
226 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
227 if (sess->listener->counters)
228 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
229
230 return ACT_RET_ABRT;
231}
232
233/* parse "set-{src,dst}[-port]" action */
234static enum act_parse_ret tcp_parse_set_src_dst(const char **args, int *orig_arg, struct proxy *px,
235 struct act_rule *rule, char **err)
236{
237 int cur_arg;
238 struct sample_expr *expr;
239 unsigned int where;
240
241 cur_arg = *orig_arg;
242 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args, NULL);
243 if (!expr)
244 return ACT_RET_PRS_ERR;
245
246 where = 0;
247 if (px->cap & PR_CAP_FE)
248 where |= SMP_VAL_FE_HRQ_HDR;
249 if (px->cap & PR_CAP_BE)
250 where |= SMP_VAL_BE_HRQ_HDR;
251
252 if (!(expr->fetch->val & where)) {
253 memprintf(err,
254 "fetch method '%s' extracts information from '%s', none of which is available here",
255 args[cur_arg-1], sample_src_names(expr->fetch->use));
256 free(expr);
257 return ACT_RET_PRS_ERR;
258 }
259 rule->arg.expr = expr;
260 rule->action = ACT_CUSTOM;
261
262 if (!strcmp(args[*orig_arg-1], "set-src")) {
263 rule->action_ptr = tcp_action_req_set_src;
264 } else if (!strcmp(args[*orig_arg-1], "set-src-port")) {
265 rule->action_ptr = tcp_action_req_set_src_port;
266 } else if (!strcmp(args[*orig_arg-1], "set-dst")) {
267 rule->action_ptr = tcp_action_req_set_dst;
268 } else if (!strcmp(args[*orig_arg-1], "set-dst-port")) {
269 rule->action_ptr = tcp_action_req_set_dst_port;
270 } else {
271 return ACT_RET_PRS_ERR;
272 }
273
274 (*orig_arg)++;
275
276 return ACT_RET_PRS_OK;
277}
278
279
280/* Parse a "silent-drop" action. It takes no argument. It returns ACT_RET_PRS_OK on
281 * success, ACT_RET_PRS_ERR on error.
282 */
283static enum act_parse_ret tcp_parse_silent_drop(const char **args, int *orig_arg, struct proxy *px,
284 struct act_rule *rule, char **err)
285{
286 rule->action = ACT_CUSTOM;
287 rule->action_ptr = tcp_exec_action_silent_drop;
288 return ACT_RET_PRS_OK;
289}
290
291
292static struct action_kw_list tcp_req_conn_actions = {ILH, {
293 { "set-src", tcp_parse_set_src_dst },
294 { "set-src-port", tcp_parse_set_src_dst },
295 { "set-dst" , tcp_parse_set_src_dst },
296 { "set-dst-port", tcp_parse_set_src_dst },
297 { "silent-drop", tcp_parse_silent_drop },
298 { /* END */ }
299}};
300
301INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_actions);
302
303static struct action_kw_list tcp_req_sess_actions = {ILH, {
304 { "set-src", tcp_parse_set_src_dst },
305 { "set-src-port", tcp_parse_set_src_dst },
306 { "set-dst" , tcp_parse_set_src_dst },
307 { "set-dst-port", tcp_parse_set_src_dst },
308 { "silent-drop", tcp_parse_silent_drop },
309 { /* END */ }
310}};
311
312INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_actions);
313
314static struct action_kw_list tcp_req_cont_actions = {ILH, {
315 { "set-dst" , tcp_parse_set_src_dst },
316 { "set-dst-port", tcp_parse_set_src_dst },
317 { "silent-drop", tcp_parse_silent_drop },
318 { /* END */ }
319}};
320
321INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_actions);
322
323static struct action_kw_list tcp_res_cont_actions = {ILH, {
324 { "silent-drop", tcp_parse_silent_drop },
325 { /* END */ }
326}};
327
328INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_cont_actions);
329
330static struct action_kw_list http_req_actions = {ILH, {
331 { "silent-drop", tcp_parse_silent_drop },
332 { "set-src", tcp_parse_set_src_dst },
333 { "set-src-port", tcp_parse_set_src_dst },
334 { "set-dst", tcp_parse_set_src_dst },
335 { "set-dst-port", tcp_parse_set_src_dst },
336 { /* END */ }
337}};
338
339INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
340
341static struct action_kw_list http_res_actions = {ILH, {
342 { "silent-drop", tcp_parse_silent_drop },
343 { /* END */ }
344}};
345
346INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
347
348
349/*
350 * Local variables:
351 * c-indent-level: 8
352 * c-basic-offset: 8
353 * End:
354 */