From 27742146fd7acd9f31c25c3e7b4f2aecbd9cf46d Mon Sep 17 00:00:00 2001
From: Jakub Wartak <jakub.wartak@enterprisedb.com>
Date: Thu, 4 Sep 2025 11:39:54 +0200
Subject: [PATCH v3] Add MPTCP protocol support to server and libpq on Linux.

This adds new listen_mptcp configuration option and also exposes new
environment variable PGMPTCP, which can be enabled to request
MultiPathed TCP connections.

Author: Jakub Wartak <jakub.wartak@enterprisedb.com>
Discussion: https://postgr.es/m/CAKZiRmy6j9PBzDHZwdgwHavwKDzv5GWtRSWOTj6-jv6SCOZ%3DYA%40mail.gmail.com
---
 doc/src/sgml/libpq.sgml                       | 26 +++++++++++++++++++
 src/backend/commands/variable.c               | 13 ++++++++++
 src/backend/libpq/pqcomm.c                    | 11 +++++++-
 src/backend/postmaster/postmaster.c           |  3 +++
 src/backend/utils/misc/guc_parameters.dat     |  7 +++++
 src/backend/utils/misc/postgresql.conf.sample |  2 ++
 src/include/postmaster/postmaster.h           |  1 +
 src/include/utils/guc_hooks.h                 |  1 +
 src/interfaces/libpq/fe-connect.c             | 17 +++++++++++-
 src/interfaces/libpq/libpq-int.h              |  1 +
 10 files changed, 80 insertions(+), 2 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 123e7f03902..3dec81930fe 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2602,6 +2602,22 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-mptcp" xreflabel="mptcp">
+      <term><literal>MPTCP</literal><indexterm><primary>MultiPath TCP</primary></indexterm></term>
+      <listitem>
+       <para>
+        Controls whether client-side MPTCP protocol is used. The default
+        value is 0, meaning off, but you can change this to 1, meaning on.
+        This parameter is ignored for connections made via a Unix-domain socket.
+       </para>
+
+       <para>
+        MPTCP protocol is only supported on Linux and allows connection aggregation
+        (multiplexing) over mulitple network paths, provided that remote also
+        supports MPTCP.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </sect2>
@@ -9097,6 +9113,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGMPTCP</envar></primary>
+      </indexterm>
+      <envar>PGMPTCP</envar> behaves the same as the <xref
+      linkend="libpq-connect-mptcp"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 8afd252fc8c..ec0a61fdd6b 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -1230,6 +1230,19 @@ check_bonjour(bool *newval, void **extra, GucSource source)
 	return true;
 }
 
+bool
+check_listen_mptcp(bool *newval, void **extra, GucSource source)
+{
+#ifndef IPPROTO_MPTCP
+	if (*newval)
+	{
+		GUC_check_errmsg("setting the MPTCP listening socket is not supported on this platform");
+		return false;
+	}
+#endif
+	return true;
+}
+
 bool
 check_default_with_oids(bool *newval, void **extra, GucSource source)
 {
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 3704d121003..153c455a727 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -485,6 +485,8 @@ ListenServerPort(int family, const char *hostName, unsigned short portNumber,
 
 	for (addr = addrs; addr; addr = addr->ai_next)
 	{
+		int			ipprotocol = 0;
+
 		if (family != AF_UNIX && addr->ai_family == AF_UNIX)
 		{
 			/*
@@ -536,7 +538,14 @@ ListenServerPort(int family, const char *hostName, unsigned short portNumber,
 			addrDesc = addrBuf;
 		}
 
-		if ((fd = socket(addr->ai_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
+		/*
+		 * enable MPTCP only on IP and IPv6 sockets and not for UNIX domain
+		 * sockets
+		 */
+		if (addr->ai_family != AF_UNIX)
+			ipprotocol = ListenMPTCP ? IPPROTO_MPTCP : 0;
+
+		if ((fd = socket(addr->ai_family, SOCK_STREAM, ipprotocol)) == PGINVALID_SOCKET)
 		{
 			ereport(LOG,
 					(errcode_for_socket_access(),
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 90c7c4528e8..f97419e0325 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -209,6 +209,9 @@ char	   *Unix_socket_directories;
 /* The TCP listen address(es) */
 char	   *ListenAddresses;
 
+/* Whether to use MPTCP */
+bool		ListenMPTCP;
+
 /*
  * SuperuserReservedConnections is the number of backends reserved for
  * superuser use, and ReservedConnections is the number of backends reserved
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 3c5e16ad1e7..7b1ab280401 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1599,6 +1599,13 @@
   boot_val => '"localhost"',
 },
 
+{ name => 'listen_mptcp', type => 'bool', context => 'PGC_POSTMASTER', group => 'CONN_AUTH_SETTINGS',
+  short_desc => 'Whether to enable MPTCP on the listening socket',
+  variable => 'ListenMPTCP',
+  boot_val => 'false',
+  check_hook => 'check_listen_mptcp',
+},
+
 { name => 'lo_compat_privileges', type => 'bool', context => 'PGC_SUSET', group => 'COMPAT_OPTIONS_PREVIOUS',
   short_desc => 'Enables backward compatibility mode for privilege checks on large objects.',
   long_desc => 'Skips privilege checks when reading or modifying large objects, for compatibility with PostgreSQL releases prior to 9.0.',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index e759f06b50f..6def7f7bd5e 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -63,6 +63,8 @@
                                         # comma-separated list of addresses;
                                         # defaults to 'localhost'; use '*' for all
                                         # (change requires restart)
+#listen_mptcp = off                     # whether to enable Multipathing TCP or not
+                                        # (change requires restart)
 #port = 5432                            # (change requires restart)
 #max_connections = 100                  # (change requires restart)
 #reserved_connections = 0               # (change requires restart)
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 716b4c912b3..a183fefd2d8 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -60,6 +60,7 @@ extern PGDLLIMPORT int Unix_socket_permissions;
 extern PGDLLIMPORT char *Unix_socket_group;
 extern PGDLLIMPORT char *Unix_socket_directories;
 extern PGDLLIMPORT char *ListenAddresses;
+extern PGDLLIMPORT bool ListenMPTCP;
 extern PGDLLIMPORT bool ClientAuthInProgress;
 extern PGDLLIMPORT int PreAuthDelay;
 extern PGDLLIMPORT int AuthenticationTimeout;
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 6a76f8d5ed6..b35fcf8f2be 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -66,6 +66,7 @@ extern bool check_huge_page_size(int *newval, void **extra, GucSource source);
 extern void assign_io_method(int newval, void *extra);
 extern bool check_io_max_concurrency(int *newval, void **extra, GucSource source);
 extern const char *show_in_hot_standby(void);
+extern bool check_listen_mptcp(bool *newval, void **extra, GucSource source);
 extern bool check_locale_messages(char **newval, void **extra, GucSource source);
 extern void assign_locale_messages(const char *newval, void *extra);
 extern bool check_locale_monetary(char **newval, void **extra, GucSource source);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index ee398f13998..53d19a9bb95 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -421,6 +421,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"SSL-Key-Log-File", "D", 64,
 	offsetof(struct pg_conn, sslkeylogfile)},
 
+	{"mptcp", "PGMPTCP", "0", NULL,
+		"MPTCP-Protocol", "", 1,
+	offsetof(struct pg_conn, mptcp)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
@@ -3253,6 +3257,7 @@ keep_going:						/* We will come back to here until there is
 					char		host_addr[NI_MAXHOST];
 					int			sock_type;
 					AddrInfo   *addr_cur;
+					int			ip_protocol = 0;
 
 					/*
 					 * Advance to next possible host, if we've tried all of
@@ -3338,7 +3343,17 @@ keep_going:						/* We will come back to here until there is
 					 */
 					sock_type |= SOCK_NONBLOCK;
 #endif
-					conn->sock = socket(addr_cur->family, sock_type, 0);
+
+					/*
+					 * enable MPTCP only on IP and IPv6 sockets and not for
+					 * UNIX domain sockets
+					 */
+					if (addr_cur->family != AF_UNIX && conn->mptcp && conn->mptcp[0] == '1')
+					{
+						fprintf(stderr, "enabling MPTCP client\n");
+						ip_protocol = IPPROTO_MPTCP;
+					}
+					conn->sock = socket(addr_cur->family, sock_type, ip_protocol);
 					if (conn->sock == PGINVALID_SOCKET)
 					{
 						int			errorno = SOCK_ERRNO;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index a737d1db457..9bbe59f384d 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -432,6 +432,7 @@ struct pg_conn
 	char	   *scram_client_key;	/* base64-encoded SCRAM client key */
 	char	   *scram_server_key;	/* base64-encoded SCRAM server key */
 	char	   *sslkeylogfile;	/* where should the client write ssl keylogs */
+	char	   *mptcp;			/* use MPTCP ? */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
-- 
2.43.0

