blob: 5bb6e13d5ba9e1eed7c5613bdaaa040c3242ef04 [file] [log] [blame]
Willy Tarreau18b7df72020-08-28 12:07:22 +02001/*
2 * Generic code for native (BSD-compatible) sockets
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 <haproxy/api.h>
26#include <haproxy/connection.h>
Willy Tarreau063d47d2020-08-28 16:29:53 +020027#include <haproxy/listener-t.h>
Willy Tarreau18b7df72020-08-28 12:07:22 +020028#include <haproxy/namespace.h>
29#include <haproxy/sock.h>
Willy Tarreau2d34a712020-08-28 16:49:41 +020030#include <haproxy/sock_inet.h>
Willy Tarreau18b7df72020-08-28 12:07:22 +020031#include <haproxy/tools.h>
32
Willy Tarreau063d47d2020-08-28 16:29:53 +020033/* the list of remaining sockets transferred from an older process */
34struct xfer_sock_list *xfer_sock_list = NULL;
Willy Tarreau18b7df72020-08-28 12:07:22 +020035
36/* Create a socket to connect to the server in conn->dst (which MUST be valid),
37 * using the configured namespace if needed, or the one passed by the proxy
38 * protocol if required to do so. It ultimately calls socket() or socketat()
39 * and returns the FD or error code.
40 */
41int sock_create_server_socket(struct connection *conn)
42{
43 const struct netns_entry *ns = NULL;
44
45#ifdef USE_NS
46 if (objt_server(conn->target)) {
47 if (__objt_server(conn->target)->flags & SRV_F_USE_NS_FROM_PP)
48 ns = conn->proxy_netns;
49 else
50 ns = __objt_server(conn->target)->netns;
51 }
52#endif
53 return my_socketat(ns, conn->dst->ss_family, SOCK_STREAM, 0);
54}
55
56/*
57 * Retrieves the source address for the socket <fd>, with <dir> indicating
58 * if we're a listener (=0) or an initiator (!=0). It returns 0 in case of
59 * success, -1 in case of error. The socket's source address is stored in
60 * <sa> for <salen> bytes.
61 */
62int sock_get_src(int fd, struct sockaddr *sa, socklen_t salen, int dir)
63{
64 if (dir)
65 return getsockname(fd, sa, &salen);
66 else
67 return getpeername(fd, sa, &salen);
68}
69
70/*
71 * Retrieves the original destination address for the socket <fd>, with <dir>
72 * indicating if we're a listener (=0) or an initiator (!=0). It returns 0 in
73 * case of success, -1 in case of error. The socket's source address is stored
74 * in <sa> for <salen> bytes.
75 */
76int sock_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir)
77{
78 if (dir)
79 return getpeername(fd, sa, &salen);
80 else
81 return getsockname(fd, sa, &salen);
82}
83
Willy Tarreau2d34a712020-08-28 16:49:41 +020084/* When binding the listeners, check if a socket has been sent to us by the
85 * previous process that we could reuse, instead of creating a new one. Note
86 * that some address family-specific options are checked on the listener and
87 * on the socket. Typically for AF_INET and AF_INET6, we check for transparent
88 * mode, and for AF_INET6 we also check for "v4v6" or "v6only". The reused
89 * socket is automatically removed from the list so that it's not proposed
90 * anymore.
91 */
92int sock_find_compatible_fd(const struct listener *l)
93{
94 struct xfer_sock_list *xfer_sock = xfer_sock_list;
95 int options = l->options & (LI_O_FOREIGN | LI_O_V6ONLY | LI_O_V4V6);
96 int if_namelen = 0;
97 int ns_namelen = 0;
98 int ret = -1;
99
100 if (!l->proto->addrcmp)
101 return -1;
102
103 if (l->addr.ss_family == AF_INET6) {
104 /* Prepare to match the v6only option against what we really want. Note
105 * that sadly the two options are not exclusive to each other and that
106 * v6only is stronger than v4v6.
107 */
108 if ((options & LI_O_V6ONLY) || (sock_inet6_v6only_default && !(options & LI_O_V4V6)))
109 options |= LI_O_V6ONLY;
110 else if ((options & LI_O_V4V6) || !sock_inet6_v6only_default)
111 options &= ~LI_O_V6ONLY;
112 }
113 options &= ~LI_O_V4V6;
114
115 if (l->interface)
116 if_namelen = strlen(l->interface);
117#ifdef USE_NS
118 if (l->netns)
119 ns_namelen = l->netns->name_len;
120#endif
121
122 while (xfer_sock) {
123 if (((options ^ xfer_sock->options) & (LI_O_FOREIGN | LI_O_V6ONLY)) == 0 &&
124 (if_namelen == xfer_sock->if_namelen) &&
125 (ns_namelen == xfer_sock->ns_namelen) &&
126 (!if_namelen || strcmp(l->interface, xfer_sock->iface) == 0) &&
127#ifdef USE_NS
128 (!ns_namelen || strcmp(l->netns->node.key, xfer_sock->namespace) == 0) &&
129#endif
130 l->proto->addrcmp(&xfer_sock->addr, &l->addr) == 0)
131 break;
132 xfer_sock = xfer_sock->next;
133 }
134
135 if (xfer_sock != NULL) {
136 ret = xfer_sock->fd;
137 if (xfer_sock == xfer_sock_list)
138 xfer_sock_list = xfer_sock->next;
139 if (xfer_sock->prev)
140 xfer_sock->prev->next = xfer_sock->next;
141 if (xfer_sock->next)
142 xfer_sock->next->prev = xfer_sock->prev;
143 free(xfer_sock->iface);
144 free(xfer_sock->namespace);
145 free(xfer_sock);
146 }
147 return ret;
148}
149
Willy Tarreau18b7df72020-08-28 12:07:22 +0200150/*
151 * Local variables:
152 * c-indent-level: 8
153 * c-basic-offset: 8
154 * End:
155 */