]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: sock: implement sock_find_compatible_fd()
authorWilly Tarreau <w@1wt.eu>
Fri, 28 Aug 2020 14:49:41 +0000 (16:49 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 28 Aug 2020 16:51:36 +0000 (18:51 +0200)
This is essentially a merge from tcp_find_compatible_fd() and
uxst_find_compatible_fd() that relies on a listener's address and
compare function and still checks for other variations. For AF_INET6
it compares a few of the listener's bind options. A minor change for
UNIX sockets is that transparent mode, interface and namespace used
to be ignored when trying to pick a previous socket while now if they
are changed, the socket will not be reused. This could be refined but
it's still better this way as there is no more risk of using a
differently bound socket by accident.

Eventually we should not pass a listener there but a set of binding
parameters (address, interface, namespace etc...) which ultimately will
be grouped into a receiver. For now this still doesn't exist so let's
stick to the listener to break dependencies in the rest of the code.

include/haproxy/sock.h
src/proto_tcp.c
src/proto_uxst.c
src/sock.c

index b3690d717adbd68b59c8c76484989cc73addc1ee..e405bb30c4da83c38e7461c228dcbf356266c4d7 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <haproxy/api.h>
 #include <haproxy/connection-t.h>
+#include <haproxy/listener-t.h>
 #include <haproxy/sock-t.h>
 
 extern struct xfer_sock_list *xfer_sock_list;
@@ -34,6 +35,7 @@ extern struct xfer_sock_list *xfer_sock_list;
 int sock_create_server_socket(struct connection *conn);
 int sock_get_src(int fd, struct sockaddr *sa, socklen_t salen, int dir);
 int sock_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir);
+int sock_find_compatible_fd(const struct listener *l);
 
 #endif /* _HAPROXY_SOCK_H */
 
index 44b11e44a010e77e9341cb1fce2ede6c3cdbd758..4ae4a489f32ef882f62cdb421a35a97f832e0cd5 100644 (file)
@@ -570,65 +570,6 @@ int tcp_connect_server(struct connection *conn, int flags)
        return SF_ERR_NONE;  /* connection is OK */
 }
 
-#define LI_MANDATORY_FLAGS     (LI_O_FOREIGN | LI_O_V6ONLY)
-/* When binding the listeners, check if a socket has been sent to us by the
- * previous process that we could reuse, instead of creating a new one.
- */
-static int tcp_find_compatible_fd(struct listener *l)
-{
-       struct xfer_sock_list *xfer_sock = xfer_sock_list;
-       int options = l->options & (LI_MANDATORY_FLAGS | LI_O_V4V6);
-       int ret = -1;
-
-       /* 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 ((options & LI_O_V6ONLY) || (sock_inet6_v6only_default && !(options & LI_O_V4V6)))
-               options |= LI_O_V6ONLY;
-       else if ((options & LI_O_V4V6) || !sock_inet6_v6only_default)
-               options &= ~LI_O_V6ONLY;
-       options &= ~LI_O_V4V6;
-
-       while (xfer_sock) {
-               if (!l->proto->addrcmp(&xfer_sock->addr, &l->addr)) {
-                       if ((l->interface == NULL && xfer_sock->iface == NULL) ||
-                           (l->interface != NULL && xfer_sock->iface != NULL &&
-                            !strcmp(l->interface, xfer_sock->iface))) {
-                               if (options == (xfer_sock->options & LI_MANDATORY_FLAGS)) {
-                                       if ((xfer_sock->namespace == NULL &&
-                                           l->netns == NULL)
-#ifdef USE_NS
-                                           || (xfer_sock->namespace != NULL &&
-                                           l->netns != NULL &&
-                                           !strcmp(xfer_sock->namespace,
-                                           l->netns->node.key))
-#endif
-                                          ) {
-                                               break;
-                                       }
-
-                               }
-                       }
-               }
-               xfer_sock = xfer_sock->next;
-       }
-       if (xfer_sock != NULL) {
-               ret = xfer_sock->fd;
-               if (xfer_sock == xfer_sock_list)
-                       xfer_sock_list = xfer_sock->next;
-               if (xfer_sock->prev)
-                       xfer_sock->prev->next = xfer_sock->next;
-               if (xfer_sock->next)
-                       xfer_sock->next->prev = xfer_sock->prev;
-               free(xfer_sock->iface);
-               free(xfer_sock->namespace);
-               free(xfer_sock);
-       }
-       return ret;
-}
-#undef L1_MANDATORY_FLAGS
-
 /* This function tries to bind a TCPv4/v6 listener. It may return a warning or
  * an error message in <errmsg> if the message is at most <errlen> bytes long
  * (including '\0'). Note that <errmsg> may be NULL if <errlen> is also zero.
@@ -660,7 +601,7 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
        err = ERR_NONE;
 
        if (listener->fd == -1)
-               listener->fd = tcp_find_compatible_fd(listener);
+               listener->fd = sock_find_compatible_fd(listener);
 
        /* 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
index f0cf99988a5e3a19820457bb5aea51f132378e71..2c3e202de65758f8fe52fcfd425d5608187e8354 100644 (file)
@@ -83,36 +83,6 @@ INITCALL1(STG_REGISTER, protocol_register, &proto_unix);
  * 2) listener-oriented functions
  ********************************/
 
-
-static int uxst_find_compatible_fd(struct listener *l)
-{
-       struct xfer_sock_list *xfer_sock = xfer_sock_list;
-       int ret = -1;
-
-       while (xfer_sock) {
-               /*
-                * The bound socket's path as returned by getsockaddr
-                * will be the temporary name <sockname>.XXXXX.tmp,
-                * so we can't just compare the two names
-                */
-               if (!l->proto->addrcmp(&xfer_sock->addr, &l->addr))
-                               break;
-               xfer_sock = xfer_sock->next;
-       }
-       if (xfer_sock != NULL) {
-               ret = xfer_sock->fd;
-               if (xfer_sock == xfer_sock_list)
-                       xfer_sock_list = xfer_sock->next;
-               if (xfer_sock->prev)
-                       xfer_sock->prev->next = xfer_sock->next;
-               if (xfer_sock->next)
-                       xfer_sock->next->prev = xfer_sock->prev;
-               free(xfer_sock);
-       }
-       return ret;
-
-}
-
 /* This function creates a UNIX socket associated to the listener. It changes
  * the state from ASSIGNED to LISTEN. The socket is NOT enabled for polling.
  * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL. It
@@ -144,7 +114,8 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
                return ERR_NONE; /* already bound */
                
        if (listener->fd == -1)
-               listener->fd = uxst_find_compatible_fd(listener);
+               listener->fd = sock_find_compatible_fd(listener);
+
        path = ((struct sockaddr_un *)&listener->addr)->sun_path;
 
        maxpathlen = MIN(MAXPATHLEN, sizeof(addr.sun_path));
index 56a4a5cf2e13edbf2f2bc40e4e16e273bfe6481e..5bb6e13d5ba9e1eed7c5613bdaaa040c3242ef04 100644 (file)
@@ -27,6 +27,7 @@
 #include <haproxy/listener-t.h>
 #include <haproxy/namespace.h>
 #include <haproxy/sock.h>
+#include <haproxy/sock_inet.h>
 #include <haproxy/tools.h>
 
 /* the list of remaining sockets transferred from an older process */
@@ -80,6 +81,72 @@ int sock_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir)
                return getsockname(fd, sa, &salen);
 }
 
+/* When binding the listeners, check if a socket has been sent to us by the
+ * previous process that we could reuse, instead of creating a new one. Note
+ * that some address family-specific options are checked on the listener and
+ * on the socket. Typically for AF_INET and AF_INET6, we check for transparent
+ * mode, and for AF_INET6 we also check for "v4v6" or "v6only". The reused
+ * socket is automatically removed from the list so that it's not proposed
+ * anymore.
+ */
+int sock_find_compatible_fd(const struct listener *l)
+{
+       struct xfer_sock_list *xfer_sock = xfer_sock_list;
+       int options = l->options & (LI_O_FOREIGN | LI_O_V6ONLY | LI_O_V4V6);
+       int if_namelen = 0;
+       int ns_namelen = 0;
+       int ret = -1;
+
+       if (!l->proto->addrcmp)
+               return -1;
+
+       if (l->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 ((options & LI_O_V6ONLY) || (sock_inet6_v6only_default && !(options & LI_O_V4V6)))
+                       options |= LI_O_V6ONLY;
+               else if ((options & LI_O_V4V6) || !sock_inet6_v6only_default)
+                       options &= ~LI_O_V6ONLY;
+       }
+       options &= ~LI_O_V4V6;
+
+       if (l->interface)
+               if_namelen = strlen(l->interface);
+#ifdef USE_NS
+       if (l->netns)
+               ns_namelen = l->netns->name_len;
+#endif
+
+       while (xfer_sock) {
+               if (((options ^ xfer_sock->options) & (LI_O_FOREIGN | LI_O_V6ONLY)) == 0 &&
+                   (if_namelen == xfer_sock->if_namelen) &&
+                   (ns_namelen == xfer_sock->ns_namelen) &&
+                   (!if_namelen || strcmp(l->interface, xfer_sock->iface) == 0) &&
+#ifdef USE_NS
+                   (!ns_namelen || strcmp(l->netns->node.key, xfer_sock->namespace) == 0) &&
+#endif
+                   l->proto->addrcmp(&xfer_sock->addr, &l->addr) == 0)
+                       break;
+               xfer_sock = xfer_sock->next;
+       }
+
+       if (xfer_sock != NULL) {
+               ret = xfer_sock->fd;
+               if (xfer_sock == xfer_sock_list)
+                       xfer_sock_list = xfer_sock->next;
+               if (xfer_sock->prev)
+                       xfer_sock->prev->next = xfer_sock->next;
+               if (xfer_sock->next)
+                       xfer_sock->next->prev = xfer_sock->prev;
+               free(xfer_sock->iface);
+               free(xfer_sock->namespace);
+               free(xfer_sock);
+       }
+       return ret;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8