blob: 0bf347bcc3ecd6c6ab38a2a16b056a9cb88c3640 [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>
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/api.h>
29#include <haproxy/arg.h>
30#include <haproxy/errors.h>
31#include <haproxy/list.h>
32#include <haproxy/listener.h>
33#include <haproxy/namespace.h>
34#include <haproxy/proxy-t.h>
35#include <haproxy/server.h>
36#include <haproxy/tools.h>
37
38
39#ifdef IPV6_V6ONLY
40/* parse the "v4v6" bind keyword */
41static int bind_parse_v4v6(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
42{
Willy Tarreau3fd3bdc2020-09-01 15:12:08 +020043 conf->settings.options |= RX_O_V4V6;
Willy Tarreaude70ca52020-08-28 11:49:31 +020044 return 0;
45}
46
47/* parse the "v6only" bind keyword */
48static int bind_parse_v6only(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
49{
Willy Tarreau3fd3bdc2020-09-01 15:12:08 +020050 conf->settings.options |= RX_O_V6ONLY;
Willy Tarreaude70ca52020-08-28 11:49:31 +020051 return 0;
52}
53#endif
54
55#ifdef CONFIG_HAP_TRANSPARENT
56/* parse the "transparent" bind keyword */
57static int bind_parse_transparent(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
58{
Willy Tarreau3fd3bdc2020-09-01 15:12:08 +020059 conf->settings.options |= RX_O_FOREIGN;
Willy Tarreaude70ca52020-08-28 11:49:31 +020060 return 0;
61}
62#endif
63
64#ifdef TCP_DEFER_ACCEPT
65/* parse the "defer-accept" bind keyword */
66static int bind_parse_defer_accept(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
67{
68 struct listener *l;
69
70 list_for_each_entry(l, &conf->listeners, by_bind) {
Willy Tarreau37159062020-08-27 07:48:42 +020071 if (l->rx.addr.ss_family == AF_INET || l->rx.addr.ss_family == AF_INET6)
Willy Tarreaude70ca52020-08-28 11:49:31 +020072 l->options |= LI_O_DEF_ACCEPT;
73 }
74
75 return 0;
76}
77#endif
78
79#ifdef TCP_FASTOPEN
80/* parse the "tfo" bind keyword */
81static int bind_parse_tfo(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
82{
83 struct listener *l;
84
85 list_for_each_entry(l, &conf->listeners, by_bind) {
Willy Tarreau37159062020-08-27 07:48:42 +020086 if (l->rx.addr.ss_family == AF_INET || l->rx.addr.ss_family == AF_INET6)
Willy Tarreaude70ca52020-08-28 11:49:31 +020087 l->options |= LI_O_TCP_FO;
88 }
89
90 return 0;
91}
92#endif
93
94#ifdef TCP_MAXSEG
95/* parse the "mss" bind keyword */
96static int bind_parse_mss(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
97{
98 struct listener *l;
99 int mss;
100
101 if (!*args[cur_arg + 1]) {
102 memprintf(err, "'%s' : missing MSS value", args[cur_arg]);
103 return ERR_ALERT | ERR_FATAL;
104 }
105
106 mss = atoi(args[cur_arg + 1]);
107 if (!mss || abs(mss) > 65535) {
108 memprintf(err, "'%s' : expects an MSS with and absolute value between 1 and 65535", args[cur_arg]);
109 return ERR_ALERT | ERR_FATAL;
110 }
111
112 list_for_each_entry(l, &conf->listeners, by_bind) {
Willy Tarreau37159062020-08-27 07:48:42 +0200113 if (l->rx.addr.ss_family == AF_INET || l->rx.addr.ss_family == AF_INET6)
Willy Tarreaude70ca52020-08-28 11:49:31 +0200114 l->maxseg = mss;
115 }
116
117 return 0;
118}
119#endif
120
121#ifdef TCP_USER_TIMEOUT
122/* parse the "tcp-ut" bind keyword */
123static int bind_parse_tcp_ut(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
124{
125 const char *ptr = NULL;
126 struct listener *l;
127 unsigned int timeout;
128
129 if (!*args[cur_arg + 1]) {
130 memprintf(err, "'%s' : missing TCP User Timeout value", args[cur_arg]);
131 return ERR_ALERT | ERR_FATAL;
132 }
133
134 ptr = parse_time_err(args[cur_arg + 1], &timeout, TIME_UNIT_MS);
135 if (ptr == PARSE_TIME_OVER) {
136 memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)",
137 args[cur_arg+1], args[cur_arg]);
138 return ERR_ALERT | ERR_FATAL;
139 }
140 else if (ptr == PARSE_TIME_UNDER) {
141 memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)",
142 args[cur_arg+1], args[cur_arg]);
143 return ERR_ALERT | ERR_FATAL;
144 }
145 else if (ptr) {
146 memprintf(err, "'%s' : expects a positive delay in milliseconds", args[cur_arg]);
147 return ERR_ALERT | ERR_FATAL;
148 }
149
150 list_for_each_entry(l, &conf->listeners, by_bind) {
Willy Tarreau37159062020-08-27 07:48:42 +0200151 if (l->rx.addr.ss_family == AF_INET || l->rx.addr.ss_family == AF_INET6)
Willy Tarreaude70ca52020-08-28 11:49:31 +0200152 l->tcp_ut = timeout;
153 }
154
155 return 0;
156}
157#endif
158
159#ifdef SO_BINDTODEVICE
160/* parse the "interface" bind keyword */
161static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
162{
Willy Tarreaude70ca52020-08-28 11:49:31 +0200163 if (!*args[cur_arg + 1]) {
164 memprintf(err, "'%s' : missing interface name", args[cur_arg]);
165 return ERR_ALERT | ERR_FATAL;
166 }
167
Willy Tarreau7e307212020-09-03 07:23:34 +0200168 conf->settings.interface = strdup(args[cur_arg + 1]);
Willy Tarreaude70ca52020-08-28 11:49:31 +0200169 return 0;
170}
171#endif
172
173#ifdef USE_NS
174/* parse the "namespace" bind keyword */
175static int bind_parse_namespace(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
176{
Willy Tarreaude70ca52020-08-28 11:49:31 +0200177 char *namespace = NULL;
178
179 if (!*args[cur_arg + 1]) {
180 memprintf(err, "'%s' : missing namespace id", args[cur_arg]);
181 return ERR_ALERT | ERR_FATAL;
182 }
183 namespace = args[cur_arg + 1];
184
Willy Tarreaube56c102020-09-03 07:27:34 +0200185 conf->settings.netns = netns_store_lookup(namespace, strlen(namespace));
Willy Tarreaude70ca52020-08-28 11:49:31 +0200186
Willy Tarreaube56c102020-09-03 07:27:34 +0200187 if (conf->settings.netns == NULL)
188 conf->settings.netns = netns_store_insert(namespace);
Willy Tarreaude70ca52020-08-28 11:49:31 +0200189
Willy Tarreaube56c102020-09-03 07:27:34 +0200190 if (conf->settings.netns == NULL) {
191 ha_alert("Cannot open namespace '%s'.\n", args[cur_arg + 1]);
192 return ERR_ALERT | ERR_FATAL;
Willy Tarreaude70ca52020-08-28 11:49:31 +0200193 }
194 return 0;
195}
196#endif
197
198#ifdef TCP_USER_TIMEOUT
199/* parse the "tcp-ut" server keyword */
200static int srv_parse_tcp_ut(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
201{
202 const char *ptr = NULL;
203 unsigned int timeout;
204
205 if (!*args[*cur_arg + 1]) {
206 memprintf(err, "'%s' : missing TCP User Timeout value", args[*cur_arg]);
207 return ERR_ALERT | ERR_FATAL;
208 }
209
210 ptr = parse_time_err(args[*cur_arg + 1], &timeout, TIME_UNIT_MS);
211 if (ptr == PARSE_TIME_OVER) {
212 memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)",
213 args[*cur_arg+1], args[*cur_arg]);
214 return ERR_ALERT | ERR_FATAL;
215 }
216 else if (ptr == PARSE_TIME_UNDER) {
217 memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)",
218 args[*cur_arg+1], args[*cur_arg]);
219 return ERR_ALERT | ERR_FATAL;
220 }
221 else if (ptr) {
222 memprintf(err, "'%s' : expects a positive delay in milliseconds", args[*cur_arg]);
223 return ERR_ALERT | ERR_FATAL;
224 }
225
226 if (newsrv->addr.ss_family == AF_INET || newsrv->addr.ss_family == AF_INET6)
227 newsrv->tcp_ut = timeout;
228
229 return 0;
230}
231#endif
232
233
234/************************************************************************/
235/* All supported bind keywords must be declared here. */
236/************************************************************************/
237
238/* Note: must not be declared <const> as its list will be overwritten.
239 * Please take care of keeping this list alphabetically sorted, doing so helps
240 * all code contributors.
241 * Optional keywords are also declared with a NULL ->parse() function so that
242 * the config parser can report an appropriate error when a known keyword was
243 * not enabled.
244 */
245static struct bind_kw_list bind_kws = { "TCP", { }, {
246#ifdef TCP_DEFER_ACCEPT
247 { "defer-accept", bind_parse_defer_accept, 0 }, /* wait for some data for 1 second max before doing accept */
248#endif
249#ifdef SO_BINDTODEVICE
250 { "interface", bind_parse_interface, 1 }, /* specifically bind to this interface */
251#endif
252#ifdef TCP_MAXSEG
253 { "mss", bind_parse_mss, 1 }, /* set MSS of listening socket */
254#endif
255#ifdef TCP_USER_TIMEOUT
256 { "tcp-ut", bind_parse_tcp_ut, 1 }, /* set User Timeout on listening socket */
257#endif
258#ifdef TCP_FASTOPEN
259 { "tfo", bind_parse_tfo, 0 }, /* enable TCP_FASTOPEN of listening socket */
260#endif
261#ifdef CONFIG_HAP_TRANSPARENT
262 { "transparent", bind_parse_transparent, 0 }, /* transparently bind to the specified addresses */
263#endif
264#ifdef IPV6_V6ONLY
265 { "v4v6", bind_parse_v4v6, 0 }, /* force socket to bind to IPv4+IPv6 */
266 { "v6only", bind_parse_v6only, 0 }, /* force socket to bind to IPv6 only */
267#endif
268#ifdef USE_NS
269 { "namespace", bind_parse_namespace, 1 },
270#endif
271 /* the versions with the NULL parse function*/
272 { "defer-accept", NULL, 0 },
273 { "interface", NULL, 1 },
274 { "mss", NULL, 1 },
275 { "transparent", NULL, 0 },
276 { "v4v6", NULL, 0 },
277 { "v6only", NULL, 0 },
278 { NULL, NULL, 0 },
279}};
280
281INITCALL1(STG_REGISTER, bind_register_keywords, &bind_kws);
282
283static struct srv_kw_list srv_kws = { "TCP", { }, {
284#ifdef TCP_USER_TIMEOUT
285 { "tcp-ut", srv_parse_tcp_ut, 1, 1 }, /* set TCP user timeout on server */
286#endif
287 { NULL, NULL, 0 },
288}};
289
290INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
291
292
293REGISTER_BUILD_OPTS("Built with transparent proxy support using:"
294#if defined(IP_TRANSPARENT)
295 " IP_TRANSPARENT"
296#endif
297#if defined(IPV6_TRANSPARENT)
298 " IPV6_TRANSPARENT"
299#endif
300#if defined(IP_FREEBIND)
301 " IP_FREEBIND"
302#endif
303#if defined(IP_BINDANY)
304 " IP_BINDANY"
305#endif
306#if defined(IPV6_BINDANY)
307 " IPV6_BINDANY"
308#endif
309#if defined(SO_BINDANY)
310 " SO_BINDANY"
311#endif
312 "");
313
314
315/*
316 * Local variables:
317 * c-indent-level: 8
318 * c-basic-offset: 8
319 * End:
320 */