blob: db1a8c00e4c024540ee00cb76b7fa9effa617d0d [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
38
39/* Compares two AF_INET sockaddr addresses. Returns 0 if they match or non-zero
40 * if they do not match.
41 */
42int sock_inet4_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
43{
44 const struct sockaddr_in *a4 = (const struct sockaddr_in *)a;
45 const struct sockaddr_in *b4 = (const struct sockaddr_in *)b;
46
47 if (a->ss_family != b->ss_family)
48 return -1;
49
50 if (a->ss_family != AF_INET)
51 return -1;
52
53 if (a4->sin_port != b4->sin_port)
54 return -1;
55
56 return memcmp(&a4->sin_addr, &b4->sin_addr, sizeof(a4->sin_addr));
57}
58
59/* Compares two AF_INET6 sockaddr addresses. Returns 0 if they match or
60 * non-zero if they do not match.
61 */
62int sock_inet6_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
63{
64 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *)a;
65 const struct sockaddr_in6 *b6 = (const struct sockaddr_in6 *)b;
66
67 if (a->ss_family != b->ss_family)
68 return -1;
69
70 if (a->ss_family != AF_INET6)
71 return -1;
72
73 if (a6->sin6_port != b6->sin6_port)
74 return -1;
75
76 return memcmp(&a6->sin6_addr, &b6->sin6_addr, sizeof(a6->sin6_addr));
77}
Willy Tarreauc5a94c92020-08-28 15:19:45 +020078
79/*
80 * Retrieves the original destination address for the socket <fd> which must be
81 * of family AF_INET (not AF_INET6), with <dir> indicating if we're a listener
82 * (=0) or an initiator (!=0). In the case of a listener, if the original
83 * destination address was translated, the original address is retrieved. It
84 * returns 0 in case of success, -1 in case of error. The socket's source
85 * address is stored in <sa> for <salen> bytes.
86 */
87int sock_inet_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir)
88{
89 if (dir)
90 return getpeername(fd, sa, &salen);
91 else {
92 int ret = getsockname(fd, sa, &salen);
93
94 if (ret < 0)
95 return ret;
96
97#if defined(USE_TPROXY) && defined(SO_ORIGINAL_DST)
98 /* For TPROXY and Netfilter's NAT, we can retrieve the original
99 * IPv4 address before DNAT/REDIRECT. We must not do that with
100 * other families because v6-mapped IPv4 addresses are still
101 * reported as v4.
102 */
103 if (getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, sa, &salen) == 0)
104 return 0;
105#endif
106 return ret;
107 }
108}
Willy Tarreau25140cc2020-08-28 15:40:33 +0200109
110/* Returns true if the passed FD corresponds to a socket bound with LI_O_FOREIGN
111 * according to the various supported socket options. The socket's address family
112 * must be passed in <family>.
113 */
114int sock_inet_is_foreign(int fd, sa_family_t family)
115{
116 int val __maybe_unused;
117 socklen_t len __maybe_unused;
118
119 switch (family) {
120 case AF_INET:
121#if defined(IP_TRANSPARENT)
122 val = 0; len = sizeof(val);
123 if (getsockopt(fd, SOL_IP, IP_TRANSPARENT, &val, &len) == 0 && val)
124 return 1;
125#endif
126#if defined(IP_FREEBIND)
127 val = 0; len = sizeof(val);
128 if (getsockopt(fd, SOL_IP, IP_FREEBIND, &val, &len) == 0 && val)
129 return 1;
130#endif
131#if defined(IP_BINDANY)
132 val = 0; len = sizeof(val);
133 if (getsockopt(fd, IPPROTO_IP, IP_BINDANY, &val, &len) == 0 && val)
134 return 1;
135#endif
136#if defined(SO_BINDANY)
137 val = 0; len = sizeof(val);
138 if (getsockopt(fd, SOL_SOCKET, SO_BINDANY, &val, &len) == 0 && val)
139 return 1;
140#endif
141 break;
142
143 case AF_INET6:
144#if defined(IPV6_TRANSPARENT) && defined(SOL_IPV6)
145 val = 0; len = sizeof(val);
146 if (getsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &val, &len) == 0 && val)
147 return 1;
148#endif
149#if defined(IP_FREEBIND)
150 val = 0; len = sizeof(val);
151 if (getsockopt(fd, SOL_IP, IP_FREEBIND, &val, &len) == 0 && val)
152 return 1;
153#endif
154#if defined(IPV6_BINDANY)
155 val = 0; len = sizeof(val);
156 if (getsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &val, &len) == 0 && val)
157 return 1;
158#endif
159#if defined(SO_BINDANY)
160 val = 0; len = sizeof(val);
161 if (getsockopt(fd, SOL_SOCKET, SO_BINDANY, &val, &len) == 0 && val)
162 return 1;
163#endif
164 break;
165 }
166 return 0;
167}