mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-29 01:31:19 +02:00
MEDIUM: tcp: make use of sock_inet_bind_receiver()
This removes all the AF_INET-specific code from tcp_bind_listener() and now simply relies on sock_inet_bind_listener() to do the same job. The function was now roughly cut in half and its error path significantly simplified.
This commit is contained in:
parent
d69ce1ffbc
commit
af9a7f5bb0
131
src/proto_tcp.c
131
src/proto_tcp.c
@ -556,11 +556,10 @@ int tcp_connect_server(struct connection *conn, int flags)
|
|||||||
*/
|
*/
|
||||||
int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
|
int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
|
||||||
{
|
{
|
||||||
__label__ tcp_return, tcp_close_return;
|
|
||||||
int fd, err;
|
int fd, err;
|
||||||
int ext, ready;
|
int ready;
|
||||||
socklen_t ready_len;
|
socklen_t ready_len;
|
||||||
const char *msg = NULL;
|
char *msg = NULL;
|
||||||
|
|
||||||
/* ensure we never return garbage */
|
/* ensure we never return garbage */
|
||||||
if (errlen)
|
if (errlen)
|
||||||
@ -569,49 +568,14 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
|
|||||||
if (listener->state != LI_ASSIGNED)
|
if (listener->state != LI_ASSIGNED)
|
||||||
return ERR_NONE; /* already bound */
|
return ERR_NONE; /* already bound */
|
||||||
|
|
||||||
err = ERR_NONE;
|
err = sock_inet_bind_receiver(&listener->rx, listener->rx.proto->accept, &msg);
|
||||||
|
if (err != ERR_NONE) {
|
||||||
|
snprintf(errmsg, errlen, "%s", msg);
|
||||||
|
free(msg); msg = NULL;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
if (listener->rx.flags & RX_F_BOUND)
|
|
||||||
goto bound;
|
|
||||||
|
|
||||||
if (listener->rx.fd == -1)
|
|
||||||
listener->rx.fd = sock_find_compatible_fd(&listener->rx);
|
|
||||||
|
|
||||||
/* if the listener already has an fd assigned, then we were offered the
|
|
||||||
* fd by an external process (most likely the parent), and we don't want
|
|
||||||
* to create a new socket. However we still want to set a few flags on
|
|
||||||
* the socket.
|
|
||||||
*/
|
|
||||||
fd = listener->rx.fd;
|
fd = listener->rx.fd;
|
||||||
ext = (fd >= 0);
|
|
||||||
|
|
||||||
if (!ext) {
|
|
||||||
fd = my_socketat(listener->rx.settings->netns, listener->rx.addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
|
|
||||||
if (fd == -1) {
|
|
||||||
err |= ERR_RETRYABLE | ERR_ALERT;
|
|
||||||
msg = "cannot create listening socket";
|
|
||||||
goto tcp_return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd >= global.maxsock) {
|
|
||||||
err |= ERR_FATAL | ERR_ABORT | ERR_ALERT;
|
|
||||||
msg = "not enough free sockets (raise '-n' parameter)";
|
|
||||||
goto tcp_close_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
|
|
||||||
err |= ERR_FATAL | ERR_ALERT;
|
|
||||||
msg = "cannot make socket non-blocking";
|
|
||||||
goto tcp_close_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ext && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
|
|
||||||
/* not fatal but should be reported */
|
|
||||||
msg = "cannot do so_reuseaddr";
|
|
||||||
err |= ERR_ALERT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener->options & LI_O_NOLINGER)
|
if (listener->options & LI_O_NOLINGER)
|
||||||
setsockopt(fd, SOL_SOCKET, SO_LINGER, &nolinger, sizeof(struct linger));
|
setsockopt(fd, SOL_SOCKET, SO_LINGER, &nolinger, sizeof(struct linger));
|
||||||
@ -627,42 +591,6 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SO_REUSEPORT
|
|
||||||
/* OpenBSD and Linux 3.9 support this. As it's present in old libc versions of
|
|
||||||
* Linux, it might return an error that we will silently ignore.
|
|
||||||
*/
|
|
||||||
if (!ext && (global.tune.options & GTUNE_USE_REUSEPORT))
|
|
||||||
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!ext && (listener->rx.settings->options & RX_O_FOREIGN)) {
|
|
||||||
switch (listener->rx.addr.ss_family) {
|
|
||||||
case AF_INET:
|
|
||||||
if (!sock_inet4_make_foreign(fd)) {
|
|
||||||
msg = "cannot make listening socket transparent";
|
|
||||||
err |= ERR_ALERT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
if (!sock_inet6_make_foreign(fd)) {
|
|
||||||
msg = "cannot make listening socket transparent";
|
|
||||||
err |= ERR_ALERT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SO_BINDTODEVICE
|
|
||||||
/* Note: this might fail if not CAP_NET_RAW */
|
|
||||||
if (!ext && listener->rx.settings->interface) {
|
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
|
|
||||||
listener->rx.settings->interface,
|
|
||||||
strlen(listener->rx.settings->interface) + 1) == -1) {
|
|
||||||
msg = "cannot bind listener to device";
|
|
||||||
err |= ERR_WARN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if defined(TCP_MAXSEG)
|
#if defined(TCP_MAXSEG)
|
||||||
if (listener->maxseg > 0) {
|
if (listener->maxseg > 0) {
|
||||||
if (setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG,
|
if (setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG,
|
||||||
@ -670,7 +598,8 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
|
|||||||
msg = "cannot set MSS";
|
msg = "cannot set MSS";
|
||||||
err |= ERR_WARN;
|
err |= ERR_WARN;
|
||||||
}
|
}
|
||||||
} else if (ext) {
|
} else {
|
||||||
|
/* we may want to try to restore the default MSS if the socket was inherited */
|
||||||
int tmpmaxseg = -1;
|
int tmpmaxseg = -1;
|
||||||
int defaultmss;
|
int defaultmss;
|
||||||
socklen_t len = sizeof(tmpmaxseg);
|
socklen_t len = sizeof(tmpmaxseg);
|
||||||
@ -737,34 +666,12 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if defined(IPV6_V6ONLY)
|
|
||||||
if (!ext && listener->rx.addr.ss_family == AF_INET6) {
|
|
||||||
/* Prepare to match the v6only option against what we really want. Note
|
|
||||||
* that sadly the two options are not exclusive to each other and that
|
|
||||||
* v6only is stronger than v4v6.
|
|
||||||
*/
|
|
||||||
if ((listener->rx.settings->options & RX_O_V6ONLY) ||
|
|
||||||
(sock_inet6_v6only_default && !(listener->rx.settings->options & RX_O_V4V6)))
|
|
||||||
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
|
|
||||||
else
|
|
||||||
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!ext && bind(fd, (struct sockaddr *)&listener->rx.addr, listener->rx.proto->sock_addrlen) == -1) {
|
|
||||||
err |= ERR_RETRYABLE | ERR_ALERT;
|
|
||||||
msg = "cannot bind socket";
|
|
||||||
goto tcp_close_return;
|
|
||||||
}
|
|
||||||
listener->rx.flags |= RX_F_BOUND;
|
|
||||||
|
|
||||||
bound:
|
|
||||||
ready = 0;
|
ready = 0;
|
||||||
ready_len = sizeof(ready);
|
ready_len = sizeof(ready);
|
||||||
if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &ready, &ready_len) == -1)
|
if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &ready, &ready_len) == -1)
|
||||||
ready = 0;
|
ready = 0;
|
||||||
|
|
||||||
if (!(ext && ready) && /* only listen if not already done by external process */
|
if (!ready && /* only listen if not already done by external process */
|
||||||
listen(fd, listener_backlog(listener)) == -1) {
|
listen(fd, listener_backlog(listener)) == -1) {
|
||||||
err |= ERR_RETRYABLE | ERR_ALERT;
|
err |= ERR_RETRYABLE | ERR_ALERT;
|
||||||
msg = "cannot listen to socket";
|
msg = "cannot listen to socket";
|
||||||
@ -779,17 +686,11 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* the socket is ready */
|
/* the socket is ready */
|
||||||
listener->rx.fd = fd;
|
|
||||||
listener->state = LI_LISTEN;
|
listener->state = LI_LISTEN;
|
||||||
|
return err;
|
||||||
|
|
||||||
fd_insert(fd, listener, listener->rx.proto->accept,
|
tcp_close_return:
|
||||||
thread_mask(listener->rx.settings->bind_thread) & all_threads_mask);
|
close(fd);
|
||||||
|
|
||||||
/* for now, all regularly bound TCP listeners are exportable */
|
|
||||||
if (!(listener->rx.flags & RX_F_INHERITED))
|
|
||||||
fdtab[fd].exported = 1;
|
|
||||||
|
|
||||||
tcp_return:
|
|
||||||
if (msg && errlen) {
|
if (msg && errlen) {
|
||||||
char pn[INET6_ADDRSTRLEN];
|
char pn[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
@ -797,10 +698,6 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
|
|||||||
snprintf(errmsg, errlen, "%s [%s:%d]", msg, pn, get_host_port(&listener->rx.addr));
|
snprintf(errmsg, errlen, "%s [%s:%d]", msg, pn, get_host_port(&listener->rx.addr));
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
tcp_close_return:
|
|
||||||
close(fd);
|
|
||||||
goto tcp_return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add <listener> to the list of tcpv4 listeners, on port <port>. The
|
/* Add <listener> to the list of tcpv4 listeners, on port <port>. The
|
||||||
|
Loading…
x
Reference in New Issue
Block a user