]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: sock: add a function to check for SO_REUSEPORT support at runtime
authorWilly Tarreau <w@1wt.eu>
Sat, 22 Apr 2023 16:25:09 +0000 (18:25 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 23 Apr 2023 07:46:15 +0000 (09:46 +0200)
The new function _sock_supports_reuseport() will be used to check if a
protocol type supports SO_REUSEPORT or not. This will be useful to verify
that shards can really work.

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

index 49dffa918b0f5007dd9c4578bec365b916816b80..60e81ec6d4ceee051ef7c547fd27ff655414d0ca 100644 (file)
@@ -49,6 +49,7 @@ int sock_conn_check(struct connection *conn);
 int sock_drain(struct connection *conn);
 int sock_check_events(struct connection *conn, int event_type);
 void sock_ignore_events(struct connection *conn, int event_type);
+int _sock_supports_reuseport(const struct proto_fam *fam, int type, int protocol);
 
 
 #endif /* _HAPROXY_SOCK_H */
index 3bc92fbfcf7ce7c06264e109e1c5ceeaa7e65604..fd9278383fb5f9a6a53ffcb98e918a1e56a696c1 100644 (file)
@@ -995,6 +995,65 @@ void sock_ignore_events(struct connection *conn, int event_type)
                fd_stop_send(conn->handle.fd);
 }
 
+/* Live check to see if a socket type supports SO_REUSEPORT for the specified
+ * family and socket() settings. Returns non-zero on success, 0 on failure. Use
+ * protocol_supports_flag() instead, which checks cached flags.
+ */
+int _sock_supports_reuseport(const struct proto_fam *fam, int type, int protocol)
+{
+       int ret = 0;
+#ifdef SO_REUSEPORT
+       struct sockaddr_storage ss;
+       socklen_t sl = sizeof(ss);
+       int fd1, fd2;
+
+       /* for the check, we'll need two sockets */
+       fd1 = fd2 = -1;
+
+       /* ignore custom sockets */
+       if (!fam || fam->sock_domain >= AF_MAX)
+               goto leave;
+
+       fd1 = socket(fam->sock_domain, type, protocol);
+       if (fd1 < 0)
+               goto leave;
+
+       if (setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
+               goto leave;
+
+       /* bind to any address assigned by the kernel, we'll then try to do it twice */
+       memset(&ss, 0, sizeof(ss));
+       ss.ss_family = fam->sock_family;
+       if (bind(fd1, (struct sockaddr *)&ss, fam->sock_addrlen) < 0)
+               goto leave;
+
+       if (getsockname(fd1, (struct sockaddr *)&ss, &sl) < 0)
+               goto leave;
+
+       fd2 = socket(fam->sock_domain, type, protocol);
+       if (fd2 < 0)
+               goto leave;
+
+       if (setsockopt(fd2, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
+               goto leave;
+
+       if (bind(fd2, (struct sockaddr *)&ss, sl) < 0)
+               goto leave;
+
+       /* OK we could bind twice to the same address:port, REUSEPORT
+        * is supported for this protocol.
+        */
+       ret = 1;
+
+ leave:
+       if (fd2 >= 0)
+               close(fd2);
+       if (fd1 >= 0)
+               close(fd1);
+#endif
+       return ret;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8