blob: e91e7a3d5687aac3db320e81f1832eb3082f210c [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
David Carlier1eb595b2021-02-06 12:11:11 +000064#if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER)
Willy Tarreaude70ca52020-08-28 11:49:31 +020065/* 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
Aurelien DARRAGON6c637fd2023-06-01 09:57:15 +0200168 ha_free(&conf->settings.interface);
Willy Tarreau7e307212020-09-03 07:23:34 +0200169 conf->settings.interface = strdup(args[cur_arg + 1]);
Willy Tarreaude70ca52020-08-28 11:49:31 +0200170 return 0;
171}
172#endif
173
174#ifdef USE_NS
175/* parse the "namespace" bind keyword */
176static int bind_parse_namespace(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
177{
Willy Tarreaude70ca52020-08-28 11:49:31 +0200178 char *namespace = NULL;
179
180 if (!*args[cur_arg + 1]) {
181 memprintf(err, "'%s' : missing namespace id", args[cur_arg]);
182 return ERR_ALERT | ERR_FATAL;
183 }
184 namespace = args[cur_arg + 1];
185
Willy Tarreaube56c102020-09-03 07:27:34 +0200186 conf->settings.netns = netns_store_lookup(namespace, strlen(namespace));
Willy Tarreaude70ca52020-08-28 11:49:31 +0200187
Willy Tarreaube56c102020-09-03 07:27:34 +0200188 if (conf->settings.netns == NULL)
189 conf->settings.netns = netns_store_insert(namespace);
Willy Tarreaude70ca52020-08-28 11:49:31 +0200190
Willy Tarreaube56c102020-09-03 07:27:34 +0200191 if (conf->settings.netns == NULL) {
192 ha_alert("Cannot open namespace '%s'.\n", args[cur_arg + 1]);
193 return ERR_ALERT | ERR_FATAL;
Willy Tarreaude70ca52020-08-28 11:49:31 +0200194 }
195 return 0;
196}
197#endif
198
199#ifdef TCP_USER_TIMEOUT
200/* parse the "tcp-ut" server keyword */
201static int srv_parse_tcp_ut(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
202{
203 const char *ptr = NULL;
204 unsigned int timeout;
205
206 if (!*args[*cur_arg + 1]) {
207 memprintf(err, "'%s' : missing TCP User Timeout value", args[*cur_arg]);
208 return ERR_ALERT | ERR_FATAL;
209 }
210
211 ptr = parse_time_err(args[*cur_arg + 1], &timeout, TIME_UNIT_MS);
212 if (ptr == PARSE_TIME_OVER) {
213 memprintf(err, "timer overflow in argument '%s' to '%s' (maximum value is 2147483647 ms or ~24.8 days)",
214 args[*cur_arg+1], args[*cur_arg]);
215 return ERR_ALERT | ERR_FATAL;
216 }
217 else if (ptr == PARSE_TIME_UNDER) {
218 memprintf(err, "timer underflow in argument '%s' to '%s' (minimum non-null value is 1 ms)",
219 args[*cur_arg+1], args[*cur_arg]);
220 return ERR_ALERT | ERR_FATAL;
221 }
222 else if (ptr) {
223 memprintf(err, "'%s' : expects a positive delay in milliseconds", args[*cur_arg]);
224 return ERR_ALERT | ERR_FATAL;
225 }
226
227 if (newsrv->addr.ss_family == AF_INET || newsrv->addr.ss_family == AF_INET6)
228 newsrv->tcp_ut = timeout;
229
230 return 0;
231}
232#endif
233
234
235/************************************************************************/
236/* All supported bind keywords must be declared here. */
237/************************************************************************/
238
239/* Note: must not be declared <const> as its list will be overwritten.
240 * Please take care of keeping this list alphabetically sorted, doing so helps
241 * all code contributors.
242 * Optional keywords are also declared with a NULL ->parse() function so that
243 * the config parser can report an appropriate error when a known keyword was
244 * not enabled.
245 */
246static struct bind_kw_list bind_kws = { "TCP", { }, {
David Carlier1eb595b2021-02-06 12:11:11 +0000247#if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER)
Willy Tarreaude70ca52020-08-28 11:49:31 +0200248 { "defer-accept", bind_parse_defer_accept, 0 }, /* wait for some data for 1 second max before doing accept */
249#endif
250#ifdef SO_BINDTODEVICE
251 { "interface", bind_parse_interface, 1 }, /* specifically bind to this interface */
252#endif
253#ifdef TCP_MAXSEG
254 { "mss", bind_parse_mss, 1 }, /* set MSS of listening socket */
255#endif
256#ifdef TCP_USER_TIMEOUT
257 { "tcp-ut", bind_parse_tcp_ut, 1 }, /* set User Timeout on listening socket */
258#endif
259#ifdef TCP_FASTOPEN
260 { "tfo", bind_parse_tfo, 0 }, /* enable TCP_FASTOPEN of listening socket */
261#endif
262#ifdef CONFIG_HAP_TRANSPARENT
263 { "transparent", bind_parse_transparent, 0 }, /* transparently bind to the specified addresses */
264#endif
265#ifdef IPV6_V6ONLY
266 { "v4v6", bind_parse_v4v6, 0 }, /* force socket to bind to IPv4+IPv6 */
267 { "v6only", bind_parse_v6only, 0 }, /* force socket to bind to IPv6 only */
268#endif
269#ifdef USE_NS
270 { "namespace", bind_parse_namespace, 1 },
271#endif
272 /* the versions with the NULL parse function*/
273 { "defer-accept", NULL, 0 },
274 { "interface", NULL, 1 },
275 { "mss", NULL, 1 },
276 { "transparent", NULL, 0 },
277 { "v4v6", NULL, 0 },
278 { "v6only", NULL, 0 },
279 { NULL, NULL, 0 },
280}};
281
282INITCALL1(STG_REGISTER, bind_register_keywords, &bind_kws);
283
284static struct srv_kw_list srv_kws = { "TCP", { }, {
285#ifdef TCP_USER_TIMEOUT
Amaury Denoyelle76e10e72021-03-08 17:08:01 +0100286 { "tcp-ut", srv_parse_tcp_ut, 1, 1, 0 }, /* set TCP user timeout on server */
Willy Tarreaude70ca52020-08-28 11:49:31 +0200287#endif
288 { NULL, NULL, 0 },
289}};
290
291INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
292
Willy Tarreaude70ca52020-08-28 11:49:31 +0200293/*
294 * Local variables:
295 * c-indent-level: 8
296 * c-basic-offset: 8
297 * End:
298 */