diff options
author | Willy Tarreau <w@1wt.eu> | 2020-09-01 16:12:50 +0200 |
---|---|---|
committer | Willy Tarreau <w@1wt.eu> | 2020-09-02 18:53:56 +0200 |
commit | 400fbfa21c3a406c5c1930783145eb3c19a775ec (patch) | |
tree | 26e58c1e6cdf7e25d4d4d5ee0c57c34fa5e228c3 | |
parent | 1625fa0ed93947c65f8f92e76556c1ea9efb73b5 (diff) | |
download | haproxy-400fbfa21c3a406c5c1930783145eb3c19a775ec.tar.gz |
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.
-rw-r--r-- | src/proto_tcp.c | 136 |
1 files changed, 15 insertions, 121 deletions
diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 02a458349..9e8e5a403 100644 --- a/src/proto_tcp.c +++ b/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) { - __label__ tcp_return, tcp_close_return; int fd, err; - int ext, ready; + int ready; socklen_t ready_len; - const char *msg = NULL; + char *msg = NULL; /* ensure we never return garbage */ if (errlen) @@ -569,62 +568,16 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) if (listener->state != LI_ASSIGNED) return ERR_NONE; /* already bound */ - err = ERR_NONE; - - if (listener->rx.options & RX_O_BOUND) - goto bound; - - if (listener->options & LI_O_FOREIGN) - listener->rx.options |= RX_O_FOREIGN; - - if (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->options & LI_O_V6ONLY) || - (sock_inet6_v6only_default && !(listener->options & LI_O_V4V6))) - listener->rx.options |= RX_O_V6ONLY; + err = sock_inet_bind_receiver(&listener->rx, + listener->rx.proto->accept, listener, + listener->bind_conf->bind_thread, &msg); + if (err != ERR_NONE) { + snprintf(errmsg, errlen, "%s", msg); + free(msg); msg = NULL; + return err; } - 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; - ext = (fd >= 0); - - if (!ext) { - fd = my_socketat(listener->rx.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) setsockopt(fd, SOL_SOCKET, SO_LINGER, &nolinger, sizeof(struct linger)); @@ -640,41 +593,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->options & LI_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.interface) { - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, - listener->rx.interface, strlen(listener->rx.interface) + 1) == -1) { - msg = "cannot bind listener to device"; - err |= ERR_WARN; - } - } -#endif #if defined(TCP_MAXSEG) if (listener->maxseg > 0) { if (setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, @@ -682,7 +600,8 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) msg = "cannot set MSS"; 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 defaultmss; socklen_t len = sizeof(tmpmaxseg); @@ -749,27 +668,12 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) } } #endif -#if defined(IPV6_V6ONLY) - if (!ext && listener->options & LI_O_V6ONLY) - setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - else if (!ext && listener->options & LI_O_V4V6) - 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.options |= RX_O_BOUND; - - bound: ready = 0; ready_len = sizeof(ready); if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &ready, &ready_len) == -1) 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) { err |= ERR_RETRYABLE | ERR_ALERT; msg = "cannot listen to socket"; @@ -784,17 +688,11 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) #endif /* the socket is ready */ - listener->rx.fd = fd; listener->state = LI_LISTEN; + return err; - fd_insert(fd, listener, listener->rx.proto->accept, - thread_mask(listener->bind_conf->bind_thread) & all_threads_mask); - - /* for now, all regularly bound TCP listeners are exportable */ - if (!(listener->rx.options & RX_O_INHERITED)) - fdtab[fd].exported = 1; - - tcp_return: + tcp_close_return: + close(fd); if (msg && errlen) { char pn[INET6_ADDRSTRLEN]; @@ -802,10 +700,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)); } return err; - - tcp_close_return: - close(fd); - goto tcp_return; } /* Add <listener> to the list of tcpv4 listeners, on port <port>. The |