blob: 13d433e059dabfe6dc796863e7db04525aa6dab4 [file] [log] [blame]
Willy Tarreaude70ca52020-08-28 11:49:31 +02001/*
2 * Configuration parsing for TCP (bind and server keywords)
3 *
4 * Copyright 2000-2020 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>
Willy Tarreaude70ca52020-08-28 11:49:31 +020015#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <time.h>
19
20#include <sys/param.h>
21#include <sys/socket.h>
22#include <sys/types.h>
23
24#include <netinet/tcp.h>
25#include <netinet/in.h>
26
27#include <haproxy/api.h>
28#include <haproxy/arg.h>
29#include <haproxy/errors.h>
30#include <haproxy/list.h>
31#include <haproxy/listener.h>
32#include <haproxy/namespace.h>
33#include <haproxy/proxy-t.h>
34#include <haproxy/server.h>
35#include <haproxy/tools.h>
36
37
38#ifdef IPV6_V6ONLY
39/* parse the "v4v6" bind keyword */
40static int bind_parse_v4v6(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
41{
Willy Tarreau3fd3bdc2020-09-01 15:12:08 +020042 conf->settings.options |= RX_O_V4V6;
Willy Tarreaude70ca52020-08-28 11:49:31 +020043 return 0;
44}
45
46/* parse the "v6only" bind keyword */
47static int bind_parse_v6only(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
48{
Willy Tarreau3fd3bdc2020-09-01 15:12:08 +020049 conf->settings.options |= RX_O_V6ONLY;
Willy Tarreaude70ca52020-08-28 11:49:31 +020050 return 0;
51}
52#endif
53
54#ifdef CONFIG_HAP_TRANSPARENT
55/* parse the "transparent" bind keyword */
56static int bind_parse_transparent(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
57{
Willy Tarreau3fd3bdc2020-09-01 15:12:08 +020058 conf->settings.options |= RX_O_FOREIGN;
Willy Tarreaude70ca52020-08-28 11:49:31 +020059 return 0;
60}
61#endif
62
David Carlier1eb595b2021-02-06 12:11:11 +000063#if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER)
Willy Tarreaude70ca52020-08-28 11:49:31 +020064/* parse the "defer-accept" bind keyword */
65static int bind_parse_defer_accept(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
66{
67 struct listener *l;
68
69 list_for_each_entry(l, &conf->listeners, by_bind) {
Willy Tarreau37159062020-08-27 07:48:42 +020070 if (l->rx.addr.ss_family == AF_INET || l->rx.addr.ss_family == AF_INET6)
Willy Tarreaude70ca52020-08-28 11:49:31 +020071 l->options |= LI_O_DEF_ACCEPT;
72 }
73
74 return 0;
75}
76#endif
77
78#ifdef TCP_FASTOPEN
79/* parse the "tfo" bind keyword */
80static int bind_parse_tfo(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
81{
82 struct listener *l;
83
84 list_for_each_entry(l, &conf->listeners, by_bind) {
Willy Tarreau37159062020-08-27 07:48:42 +020085 if (l->rx.addr.ss_family == AF_INET || l->rx.addr.ss_family == AF_INET6)
Willy Tarreaude70ca52020-08-28 11:49:31 +020086 l->options |= LI_O_TCP_FO;
87 }
88
89 return 0;
90}
91#endif
92
93#ifdef TCP_MAXSEG
94/* parse the "mss" bind keyword */
95static int bind_parse_mss(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
96{
97 struct listener *l;
98 int mss;
99
100 if (!*args[cur_arg + 1]) {
101 memprintf(err, "'%s' : missing MSS value", args[cur_arg]);
102 return ERR_ALERT | ERR_FATAL;
103 }
104
105 mss = atoi(args[cur_arg + 1]);
106 if (!mss || abs(mss) > 65535) {
107 memprintf(err, "'%s' : expects an MSS with and absolute value between 1 and 65535", args[cur_arg]);
108 return ERR_ALERT | ERR_FATAL;
109 }
110
111 list_for_each_entry(l, &conf->listeners, by_bind) {
Willy Tarreau37159062020-08-27 07:48:42 +0200112 if (l->rx.addr.ss_family == AF_INET || l->rx.addr.ss_family == AF_INET6)
Willy Tarreaude70ca52020-08-28 11:49:31 +0200113 l->maxseg = mss;
114 }
115
116 return 0;
117}
118#endif
119
120#ifdef TCP_USER_TIMEOUT
121/* parse the "tcp-ut" bind keyword */
122static int bind_parse_tcp_ut(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
123{
124 const char *ptr = NULL;
125 struct listener *l;
126 unsigned int timeout;
127
128 if (!*args[cur_arg + 1]) {
129 memprintf(err, "'%s' : missing TCP User Timeout value", args[cur_arg]);
130 return ERR_ALERT | ERR_FATAL;
131 }
132
133 ptr = parse_time_err(args[cur_arg + 1], &timeout, TIME_UNIT_MS);
134 if (ptr == PARSE_TIME_OVER) {
135 memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)",
136 args[cur_arg+1], args[cur_arg]);
137 return ERR_ALERT | ERR_FATAL;
138 }
139 else if (ptr == PARSE_TIME_UNDER) {
140 memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)",
141 args[cur_arg+1], args[cur_arg]);
142 return ERR_ALERT | ERR_FATAL;
143 }
144 else if (ptr) {
145 memprintf(err, "'%s' : expects a positive delay in milliseconds", args[cur_arg]);
146 return ERR_ALERT | ERR_FATAL;
147 }
148
149 list_for_each_entry(l, &conf->listeners, by_bind) {
Willy Tarreau37159062020-08-27 07:48:42 +0200150 if (l->rx.addr.ss_family == AF_INET || l->rx.addr.ss_family == AF_INET6)
Willy Tarreaude70ca52020-08-28 11:49:31 +0200151 l->tcp_ut = timeout;
152 }
153
154 return 0;
155}
156#endif
157
158#ifdef SO_BINDTODEVICE
159/* parse the "interface" bind keyword */
160static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
161{
Willy Tarreaude70ca52020-08-28 11:49:31 +0200162 if (!*args[cur_arg + 1]) {
163 memprintf(err, "'%s' : missing interface name", args[cur_arg]);
164 return ERR_ALERT | ERR_FATAL;
165 }
166
Willy Tarreau7e307212020-09-03 07:23:34 +0200167 conf->settings.interface = strdup(args[cur_arg + 1]);
Willy Tarreaude70ca52020-08-28 11:49:31 +0200168 return 0;
169}
170#endif
171
172#ifdef USE_NS
173/* parse the "namespace" bind keyword */
174static int bind_parse_namespace(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
175{
Willy Tarreaude70ca52020-08-28 11:49:31 +0200176 char *namespace = NULL;
177
178 if (!*args[cur_arg + 1]) {
179 memprintf(err, "'%s' : missing namespace id", args[cur_arg]);
180 return ERR_ALERT | ERR_FATAL;
181 }
182 namespace = args[cur_arg + 1];
183
Willy Tarreaube56c102020-09-03 07:27:34 +0200184 conf->settings.netns = netns_store_lookup(namespace, strlen(namespace));
Willy Tarreaude70ca52020-08-28 11:49:31 +0200185
Willy Tarreaube56c102020-09-03 07:27:34 +0200186 if (conf->settings.netns == NULL)
187 conf->settings.netns = netns_store_insert(namespace);
Willy Tarreaude70ca52020-08-28 11:49:31 +0200188
Willy Tarreaube56c102020-09-03 07:27:34 +0200189 if (conf->settings.netns == NULL) {
190 ha_alert("Cannot open namespace '%s'.\n", args[cur_arg + 1]);
191 return ERR_ALERT | ERR_FATAL;
Willy Tarreaude70ca52020-08-28 11:49:31 +0200192 }
193 return 0;
194}
195#endif
196
197#ifdef TCP_USER_TIMEOUT
198/* parse the "tcp-ut" server keyword */
199static int srv_parse_tcp_ut(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
200{
201 const char *ptr = NULL;
202 unsigned int timeout;
203
204 if (!*args[*cur_arg + 1]) {
205 memprintf(err, "'%s' : missing TCP User Timeout value", args[*cur_arg]);
206 return ERR_ALERT | ERR_FATAL;
207 }
208
209 ptr = parse_time_err(args[*cur_arg + 1], &timeout, TIME_UNIT_MS);
210 if (ptr == PARSE_TIME_OVER) {
211 memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)",
212 args[*cur_arg+1], args[*cur_arg]);
213 return ERR_ALERT | ERR_FATAL;
214 }
215 else if (ptr == PARSE_TIME_UNDER) {
216 memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)",
217 args[*cur_arg+1], args[*cur_arg]);
218 return ERR_ALERT | ERR_FATAL;
219 }
220 else if (ptr) {
221 memprintf(err, "'%s' : expects a positive delay in milliseconds", args[*cur_arg]);
222 return ERR_ALERT | ERR_FATAL;
223 }
224
225 if (newsrv->addr.ss_family == AF_INET || newsrv->addr.ss_family == AF_INET6)
226 newsrv->tcp_ut = timeout;
227
228 return 0;
229}
230#endif
231
232
233/************************************************************************/
234/* All supported bind keywords must be declared here. */
235/************************************************************************/
236
237/* Note: must not be declared <const> as its list will be overwritten.
238 * Please take care of keeping this list alphabetically sorted, doing so helps
239 * all code contributors.
240 * Optional keywords are also declared with a NULL ->parse() function so that
241 * the config parser can report an appropriate error when a known keyword was
242 * not enabled.
243 */
244static struct bind_kw_list bind_kws = { "TCP", { }, {
David Carlier1eb595b2021-02-06 12:11:11 +0000245#if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER)
Willy Tarreaude70ca52020-08-28 11:49:31 +0200246 { "defer-accept", bind_parse_defer_accept, 0 }, /* wait for some data for 1 second max before doing accept */
247#endif
248#ifdef SO_BINDTODEVICE
249 { "interface", bind_parse_interface, 1 }, /* specifically bind to this interface */
250#endif
251#ifdef TCP_MAXSEG
252 { "mss", bind_parse_mss, 1 }, /* set MSS of listening socket */
253#endif
254#ifdef TCP_USER_TIMEOUT
255 { "tcp-ut", bind_parse_tcp_ut, 1 }, /* set User Timeout on listening socket */
256#endif
257#ifdef TCP_FASTOPEN
258 { "tfo", bind_parse_tfo, 0 }, /* enable TCP_FASTOPEN of listening socket */
259#endif
260#ifdef CONFIG_HAP_TRANSPARENT
261 { "transparent", bind_parse_transparent, 0 }, /* transparently bind to the specified addresses */
262#endif
263#ifdef IPV6_V6ONLY
264 { "v4v6", bind_parse_v4v6, 0 }, /* force socket to bind to IPv4+IPv6 */
265 { "v6only", bind_parse_v6only, 0 }, /* force socket to bind to IPv6 only */
266#endif
267#ifdef USE_NS
268 { "namespace", bind_parse_namespace, 1 },
269#endif
270 /* the versions with the NULL parse function*/
271 { "defer-accept", NULL, 0 },
272 { "interface", NULL, 1 },
273 { "mss", NULL, 1 },
274 { "transparent", NULL, 0 },
275 { "v4v6", NULL, 0 },
276 { "v6only", NULL, 0 },
277 { NULL, NULL, 0 },
278}};
279
280INITCALL1(STG_REGISTER, bind_register_keywords, &bind_kws);
281
282static struct srv_kw_list srv_kws = { "TCP", { }, {
283#ifdef TCP_USER_TIMEOUT
Amaury Denoyelle76e10e72021-03-08 17:08:01 +0100284 { "tcp-ut", srv_parse_tcp_ut, 1, 1, 0 }, /* set TCP user timeout on server */
Willy Tarreaude70ca52020-08-28 11:49:31 +0200285#endif
286 { NULL, NULL, 0 },
287}};
288
289INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
290
Willy Tarreaude70ca52020-08-28 11:49:31 +0200291/*
292 * Local variables:
293 * c-indent-level: 8
294 * c-basic-offset: 8
295 * End:
296 */