On Thu, Aug 13, 2026 at 9:59 PM Greg Sabino Mullane <[email protected]> wrote: > > I like the idea a lot. Have not tested yet, but the patch looks okay as a > POC. I'm wondering if rather than a run-time check for IPPROTO_MCP in > src/backend/libpq/pqcomm.c, we shouldn't check right away as soon as it is > enabled via a guc check - see check_bonjour as a good example.
Hi Greg, thanks for taking a look. I've attached v3 that added check_listen_mptcp() (GUC check) that enhanced the situation just as You have indicated. -J.
From 27742146fd7acd9f31c25c3e7b4f2aecbd9cf46d Mon Sep 17 00:00:00 2001 From: Jakub Wartak <[email protected]> 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 <[email protected]> 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
