blob: 29666801bea8d31a737b163b50a1fe4c450c1b25 [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>
23#include <haproxy/sock_inet.h>
24#include <haproxy/tools.h>
25
26
27/* PLEASE NOTE for function below:
28 * - sock_inet4_* is solely for AF_INET (IPv4)
29 * - sock_inet6_* is solely for AF_INET6 (IPv6)
30 * - sock_inet_* is for either
31 *
32 * The address family SHOULD always be checked. In some cases a function will
33 * be used in a situation where the address family is guaranteed (e.g. protocol
34 * definitions), so the test may be avoided. This special case must then be
35 * mentioned in the comment before the function definition.
36 */
37
Willy Tarreaud88e8c02020-08-28 16:06:01 +020038/* determine if the operating system uses IPV6_V6ONLY by default. 0=no, 1=yes.
39 * It also remains if IPv6 is not enabled/configured.
40 */
41int sock_inet6_v6only_default = 0;
Willy Tarreau0d06df62020-08-28 15:10:11 +020042
Willy Tarreaue5bdc512020-08-28 18:03:10 +020043/* Default TCPv4/TCPv6 MSS settings. -1=unknown. */
44int sock_inet_tcp_maxseg_default = -1;
45int sock_inet6_tcp_maxseg_default = -1;
46
Willy Tarreau0d06df62020-08-28 15:10:11 +020047/* Compares two AF_INET sockaddr addresses. Returns 0 if they match or non-zero
48 * if they do not match.
49 */
50int sock_inet4_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
51{
52 const struct sockaddr_in *a4 = (const struct sockaddr_in *)a;
53 const struct sockaddr_in *b4 = (const struct sockaddr_in *)b;
54
55 if (a->ss_family != b->ss_family)
56 return -1;
57
58 if (a->ss_family != AF_INET)
59 return -1;
60
61 if (a4->sin_port != b4->sin_port)
62 return -1;
63
64 return memcmp(&a4->sin_addr, &b4->sin_addr, sizeof(a4->sin_addr));
65}
66
67/* Compares two AF_INET6 sockaddr addresses. Returns 0 if they match or
68 * non-zero if they do not match.
69 */
70int sock_inet6_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
71{
72 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *)a;
73 const struct sockaddr_in6 *b6 = (const struct sockaddr_in6 *)b;
74
75 if (a->ss_family != b->ss_family)
76 return -1;
77
78 if (a->ss_family != AF_INET6)
79 return -1;
80
81 if (a6->sin6_port != b6->sin6_port)
82 return -1;
83
84 return memcmp(&a6->sin6_addr, &b6->sin6_addr, sizeof(a6->sin6_addr));
85}
Willy Tarreauc5a94c92020-08-28 15:19:45 +020086
87/*
88 * Retrieves the original destination address for the socket <fd> which must be
89 * of family AF_INET (not AF_INET6), with <dir> indicating if we're a listener
90 * (=0) or an initiator (!=0). In the case of a listener, if the original
91 * destination address was translated, the original address is retrieved. It
92 * returns 0 in case of success, -1 in case of error. The socket's source
93 * address is stored in <sa> for <salen> bytes.
94 */
95int sock_inet_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir)
96{
97 if (dir)
98 return getpeername(fd, sa, &salen);
99 else {
100 int ret = getsockname(fd, sa, &salen);
101
102 if (ret < 0)
103 return ret;
104
105#if defined(USE_TPROXY) && defined(SO_ORIGINAL_DST)
106 /* For TPROXY and Netfilter's NAT, we can retrieve the original
107 * IPv4 address before DNAT/REDIRECT. We must not do that with
108 * other families because v6-mapped IPv4 addresses are still
109 * reported as v4.
110 */
111 if (getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, sa, &salen) == 0)
112 return 0;
113#endif
114 return ret;
115 }
116}
Willy Tarreau25140cc2020-08-28 15:40:33 +0200117
118/* Returns true if the passed FD corresponds to a socket bound with LI_O_FOREIGN
119 * according to the various supported socket options. The socket's address family
120 * must be passed in <family>.
121 */
122int sock_inet_is_foreign(int fd, sa_family_t family)
123{
124 int val __maybe_unused;
125 socklen_t len __maybe_unused;
126
127 switch (family) {
128 case AF_INET:
129#if defined(IP_TRANSPARENT)
130 val = 0; len = sizeof(val);
131 if (getsockopt(fd, SOL_IP, IP_TRANSPARENT, &val, &len) == 0 && val)
132 return 1;
133#endif
134#if defined(IP_FREEBIND)
135 val = 0; len = sizeof(val);
136 if (getsockopt(fd, SOL_IP, IP_FREEBIND, &val, &len) == 0 && val)
137 return 1;
138#endif
139#if defined(IP_BINDANY)
140 val = 0; len = sizeof(val);
141 if (getsockopt(fd, IPPROTO_IP, IP_BINDANY, &val, &len) == 0 && val)
142 return 1;
143#endif
144#if defined(SO_BINDANY)
145 val = 0; len = sizeof(val);
146 if (getsockopt(fd, SOL_SOCKET, SO_BINDANY, &val, &len) == 0 && val)
147 return 1;
148#endif
149 break;
150
151 case AF_INET6:
152#if defined(IPV6_TRANSPARENT) && defined(SOL_IPV6)
153 val = 0; len = sizeof(val);
154 if (getsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &val, &len) == 0 && val)
155 return 1;
156#endif
157#if defined(IP_FREEBIND)
158 val = 0; len = sizeof(val);
159 if (getsockopt(fd, SOL_IP, IP_FREEBIND, &val, &len) == 0 && val)
160 return 1;
161#endif
162#if defined(IPV6_BINDANY)
163 val = 0; len = sizeof(val);
164 if (getsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &val, &len) == 0 && val)
165 return 1;
166#endif
167#if defined(SO_BINDANY)
168 val = 0; len = sizeof(val);
169 if (getsockopt(fd, SOL_SOCKET, SO_BINDANY, &val, &len) == 0 && val)
170 return 1;
171#endif
172 break;
173 }
174 return 0;
175}
Willy Tarreaud88e8c02020-08-28 16:06:01 +0200176
177static void sock_inet_prepare()
178{
179 int fd, val;
180 socklen_t len;
181
Willy Tarreaue5bdc512020-08-28 18:03:10 +0200182 fd = socket(AF_INET, SOCK_STREAM, 0);
183 if (fd >= 0) {
184#ifdef TCP_MAXSEG
185 /* retrieve the OS' default mss for TCPv4 */
186 len = sizeof(val);
187 if (getsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &val, &len) == 0)
188 sock_inet_tcp_maxseg_default = val;
189#endif
190 close(fd);
191 }
192
Willy Tarreaud88e8c02020-08-28 16:06:01 +0200193 fd = socket(AF_INET6, SOCK_STREAM, 0);
194 if (fd >= 0) {
195#if defined(IPV6_V6ONLY)
196 /* retrieve the OS' bindv6only value */
197 len = sizeof(val);
198 if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &len) == 0 && val > 0)
199 sock_inet6_v6only_default = 1;
200#endif
Willy Tarreaue5bdc512020-08-28 18:03:10 +0200201
202#ifdef TCP_MAXSEG
203 /* retrieve the OS' default mss for TCPv6 */
204 len = sizeof(val);
205 if (getsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &val, &len) == 0)
206 sock_inet6_tcp_maxseg_default = val;
207#endif
Willy Tarreaud88e8c02020-08-28 16:06:01 +0200208 close(fd);
209 }
210}
211
212INITCALL0(STG_PREPARE, sock_inet_prepare);