blob: 3f4edcb1d42a248ecd5f4ddac87b255254772a70 [file] [log] [blame]
Willy Tarreau0d06df62020-08-28 15:10:11 +02001/*
2 * AF_INET/AF_INET6 socket management
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 <string.h>
14
15#include <sys/param.h>
16#include <sys/socket.h>
17#include <sys/types.h>
18
19#include <netinet/tcp.h>
20#include <netinet/in.h>
21
22#include <haproxy/api.h>
Willy Tarreau37bafdc2020-08-28 17:23:40 +020023#include <haproxy/global.h>
Willy Tarreau0d06df62020-08-28 15:10:11 +020024#include <haproxy/sock_inet.h>
25#include <haproxy/tools.h>
26
27
28/* PLEASE NOTE for function below:
29 * - sock_inet4_* is solely for AF_INET (IPv4)
30 * - sock_inet6_* is solely for AF_INET6 (IPv6)
31 * - sock_inet_* is for either
32 *
33 * The address family SHOULD always be checked. In some cases a function will
34 * be used in a situation where the address family is guaranteed (e.g. protocol
35 * definitions), so the test may be avoided. This special case must then be
36 * mentioned in the comment before the function definition.
37 */
38
Willy Tarreaud88e8c02020-08-28 16:06:01 +020039/* determine if the operating system uses IPV6_V6ONLY by default. 0=no, 1=yes.
40 * It also remains if IPv6 is not enabled/configured.
41 */
42int sock_inet6_v6only_default = 0;
Willy Tarreau0d06df62020-08-28 15:10:11 +020043
Willy Tarreaue5bdc512020-08-28 18:03:10 +020044/* Default TCPv4/TCPv6 MSS settings. -1=unknown. */
45int sock_inet_tcp_maxseg_default = -1;
46int sock_inet6_tcp_maxseg_default = -1;
47
Willy Tarreau0d06df62020-08-28 15:10:11 +020048/* Compares two AF_INET sockaddr addresses. Returns 0 if they match or non-zero
49 * if they do not match.
50 */
51int sock_inet4_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
52{
53 const struct sockaddr_in *a4 = (const struct sockaddr_in *)a;
54 const struct sockaddr_in *b4 = (const struct sockaddr_in *)b;
55
56 if (a->ss_family != b->ss_family)
57 return -1;
58
59 if (a->ss_family != AF_INET)
60 return -1;
61
62 if (a4->sin_port != b4->sin_port)
63 return -1;
64
65 return memcmp(&a4->sin_addr, &b4->sin_addr, sizeof(a4->sin_addr));
66}
67
68/* Compares two AF_INET6 sockaddr addresses. Returns 0 if they match or
69 * non-zero if they do not match.
70 */
71int sock_inet6_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
72{
73 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *)a;
74 const struct sockaddr_in6 *b6 = (const struct sockaddr_in6 *)b;
75
76 if (a->ss_family != b->ss_family)
77 return -1;
78
79 if (a->ss_family != AF_INET6)
80 return -1;
81
82 if (a6->sin6_port != b6->sin6_port)
83 return -1;
84
85 return memcmp(&a6->sin6_addr, &b6->sin6_addr, sizeof(a6->sin6_addr));
86}
Willy Tarreauc5a94c92020-08-28 15:19:45 +020087
88/*
89 * Retrieves the original destination address for the socket <fd> which must be
90 * of family AF_INET (not AF_INET6), with <dir> indicating if we're a listener
91 * (=0) or an initiator (!=0). In the case of a listener, if the original
92 * destination address was translated, the original address is retrieved. It
93 * returns 0 in case of success, -1 in case of error. The socket's source
94 * address is stored in <sa> for <salen> bytes.
95 */
96int sock_inet_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir)
97{
98 if (dir)
99 return getpeername(fd, sa, &salen);
100 else {
101 int ret = getsockname(fd, sa, &salen);
102
103 if (ret < 0)
104 return ret;
105
106#if defined(USE_TPROXY) && defined(SO_ORIGINAL_DST)
107 /* For TPROXY and Netfilter's NAT, we can retrieve the original
108 * IPv4 address before DNAT/REDIRECT. We must not do that with
109 * other families because v6-mapped IPv4 addresses are still
110 * reported as v4.
111 */
112 if (getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, sa, &salen) == 0)
113 return 0;
114#endif
115 return ret;
116 }
117}
Willy Tarreau25140cc2020-08-28 15:40:33 +0200118
119/* Returns true if the passed FD corresponds to a socket bound with LI_O_FOREIGN
120 * according to the various supported socket options. The socket's address family
121 * must be passed in <family>.
122 */
123int sock_inet_is_foreign(int fd, sa_family_t family)
124{
125 int val __maybe_unused;
126 socklen_t len __maybe_unused;
127
128 switch (family) {
129 case AF_INET:
130#if defined(IP_TRANSPARENT)
131 val = 0; len = sizeof(val);
132 if (getsockopt(fd, SOL_IP, IP_TRANSPARENT, &val, &len) == 0 && val)
133 return 1;
134#endif
135#if defined(IP_FREEBIND)
136 val = 0; len = sizeof(val);
137 if (getsockopt(fd, SOL_IP, IP_FREEBIND, &val, &len) == 0 && val)
138 return 1;
139#endif
140#if defined(IP_BINDANY)
141 val = 0; len = sizeof(val);
142 if (getsockopt(fd, IPPROTO_IP, IP_BINDANY, &val, &len) == 0 && val)
143 return 1;
144#endif
145#if defined(SO_BINDANY)
146 val = 0; len = sizeof(val);
147 if (getsockopt(fd, SOL_SOCKET, SO_BINDANY, &val, &len) == 0 && val)
148 return 1;
149#endif
150 break;
151
152 case AF_INET6:
153#if defined(IPV6_TRANSPARENT) && defined(SOL_IPV6)
154 val = 0; len = sizeof(val);
155 if (getsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &val, &len) == 0 && val)
156 return 1;
157#endif
158#if defined(IP_FREEBIND)
159 val = 0; len = sizeof(val);
160 if (getsockopt(fd, SOL_IP, IP_FREEBIND, &val, &len) == 0 && val)
161 return 1;
162#endif
163#if defined(IPV6_BINDANY)
164 val = 0; len = sizeof(val);
165 if (getsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &val, &len) == 0 && val)
166 return 1;
167#endif
168#if defined(SO_BINDANY)
169 val = 0; len = sizeof(val);
170 if (getsockopt(fd, SOL_SOCKET, SO_BINDANY, &val, &len) == 0 && val)
171 return 1;
172#endif
173 break;
174 }
175 return 0;
176}
Willy Tarreaud88e8c02020-08-28 16:06:01 +0200177
Willy Tarreau37bafdc2020-08-28 17:23:40 +0200178/* Attempt all known socket options to prepare an AF_INET4 socket to be bound
179 * to a foreign address. The socket must already exist and must not be bound.
180 * 1 is returned on success, 0 on failure. The caller must check the address
181 * family before calling this function.
182 */
183int sock_inet4_make_foreign(int fd)
184{
185 return
186#if defined(IP_TRANSPARENT)
187 setsockopt(fd, SOL_IP, IP_TRANSPARENT, &one, sizeof(one)) == 0 ||
188#endif
189#if defined(IP_FREEBIND)
190 setsockopt(fd, SOL_IP, IP_FREEBIND, &one, sizeof(one)) == 0 ||
191#endif
192#if defined(IP_BINDANY)
193 setsockopt(fd, IPPROTO_IP, IP_BINDANY, &one, sizeof(one)) == 0 ||
194#endif
195#if defined(SO_BINDANY)
196 setsockopt(fd, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) == 0 ||
197#endif
198 0;
199}
200
201/* Attempt all known socket options to prepare an AF_INET6 socket to be bound
202 * to a foreign address. The socket must already exist and must not be bound.
203 * 1 is returned on success, 0 on failure. The caller must check the address
204 * family before calling this function.
205 */
206int sock_inet6_make_foreign(int fd)
207{
208 return
209#if defined(IPV6_TRANSPARENT) && defined(SOL_IPV6)
210 setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &one, sizeof(one)) == 0 ||
211#endif
212#if defined(IP_FREEBIND)
213 setsockopt(fd, SOL_IP, IP_FREEBIND, &one, sizeof(one)) == 0 ||
214#endif
215#if defined(IPV6_BINDANY)
216 setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &one, sizeof(one)) == 0 ||
217#endif
218#if defined(SO_BINDANY)
219 setsockopt(fd, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) == 0 ||
220#endif
221 0;
222}
223
Willy Tarreaud88e8c02020-08-28 16:06:01 +0200224static void sock_inet_prepare()
225{
226 int fd, val;
227 socklen_t len;
228
Willy Tarreaue5bdc512020-08-28 18:03:10 +0200229 fd = socket(AF_INET, SOCK_STREAM, 0);
230 if (fd >= 0) {
231#ifdef TCP_MAXSEG
232 /* retrieve the OS' default mss for TCPv4 */
233 len = sizeof(val);
234 if (getsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &val, &len) == 0)
235 sock_inet_tcp_maxseg_default = val;
236#endif
237 close(fd);
238 }
239
Willy Tarreaud88e8c02020-08-28 16:06:01 +0200240 fd = socket(AF_INET6, SOCK_STREAM, 0);
241 if (fd >= 0) {
242#if defined(IPV6_V6ONLY)
243 /* retrieve the OS' bindv6only value */
244 len = sizeof(val);
245 if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &len) == 0 && val > 0)
246 sock_inet6_v6only_default = 1;
247#endif
Willy Tarreaue5bdc512020-08-28 18:03:10 +0200248
249#ifdef TCP_MAXSEG
250 /* retrieve the OS' default mss for TCPv6 */
251 len = sizeof(val);
252 if (getsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &val, &len) == 0)
253 sock_inet6_tcp_maxseg_default = val;
254#endif
Willy Tarreaud88e8c02020-08-28 16:06:01 +0200255 close(fd);
256 }
257}
258
259INITCALL0(STG_PREPARE, sock_inet_prepare);
Willy Tarreau37bafdc2020-08-28 17:23:40 +0200260
261
262REGISTER_BUILD_OPTS("Built with transparent proxy support using:"
263#if defined(IP_TRANSPARENT)
264 " IP_TRANSPARENT"
265#endif
266#if defined(IPV6_TRANSPARENT)
267 " IPV6_TRANSPARENT"
268#endif
269#if defined(IP_FREEBIND)
270 " IP_FREEBIND"
271#endif
272#if defined(IP_BINDANY)
273 " IP_BINDANY"
274#endif
275#if defined(IPV6_BINDANY)
276 " IPV6_BINDANY"
277#endif
278#if defined(SO_BINDANY)
279 " SO_BINDANY"
280#endif
281 "");