From 2e6b3d00fa99445409be73f43ff3b0ca0f8f803c 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 v2] 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/libpq/pqcomm.c                    | 20 +++++++++++++-
 src/backend/postmaster/postmaster.c           |  3 +++
 src/backend/utils/misc/guc_parameters.dat     |  6 +++++
 src/backend/utils/misc/postgresql.conf.sample |  2 ++
 src/include/postmaster/postmaster.h           |  1 +
 src/interfaces/libpq/fe-connect.c             | 17 +++++++++++-
 src/interfaces/libpq/libpq-int.h              |  1 +
 8 files changed, 74 insertions(+), 2 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 7d3c3bb66d8..ef7655ae5cb 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>
@@ -9212,6 +9228,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/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index aaae7214f13..dc4f5c7f4ac 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -439,6 +439,15 @@ ListenServerPort(int family, const char *hostName, unsigned short portNumber,
 	int			one = 1;
 #endif
 
+#ifndef IPPROTO_MPTCP
+	if (ListenMPTCP)
+	{
+		ereport(WARNING,
+				(errmsg("setting the MPTCP listening socket is not supported on this platform")));
+		return STATUS_ERROR;
+	}
+#endif
+
 	/* Initialize hint structure */
 	MemSet(&hint, 0, sizeof(hint));
 	hint.ai_family = family;
@@ -488,6 +497,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)
 		{
 			/*
@@ -539,7 +550,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 adb72361ce0..b8bb6da0a76 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1599,6 +1599,12 @@
   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',
+},
+
 { 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 7958653077b..d52e71d700a 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/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 17c2288e9bc..63d9f49f89b 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}
@@ -3255,6 +3259,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
@@ -3340,7 +3345,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 3f921207a14..c9eac1c9290 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

