From 4671f8510cc2d975241799d77c766673e8a1660f Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 31 Mar 2025 22:38:42 +0100 Subject: [PATCH] ext/sockets: UDP_SEGMENT support. UDP segmentation offload is an optimisation attempt by sending multiple large enough datagrams over UDP which reduces syscalls as by default, they have to be broke down in small UDP packets, it is better if the hardware supports it, other handed down to the software implementation. close GH-18213 --- NEWS | 2 ++ UPGRADING | 1 + ext/sockets/config.m4 | 2 +- ext/sockets/sockets.c | 19 ++++++++++++++ ext/sockets/sockets.stub.php | 8 ++++++ ext/sockets/sockets_arginfo.h | 5 +++- .../tests/socket_cmsg_udp_segment.phpt | 26 +++++++++++++++++++ 7 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 ext/sockets/tests/socket_cmsg_udp_segment.phpt diff --git a/NEWS b/NEWS index d0a1d30fc9e..be2005a98f0 100644 --- a/NEWS +++ b/NEWS @@ -208,6 +208,8 @@ PHP NEWS (David Carlier) . Added IP_BINDANY for a socket to bind to any address. (David Carlier) . Added SO_BUSY_POOL to reduce packets poll latency. (David Carlier) + - Added UDP_SEGMENT support to optimise multiple large datagrams over UDP + if the kernel and hardware supports it. (David Carlier) - Sodium: . Fix overall theorical overflows on zend_string buffer allocations. diff --git a/UPGRADING b/UPGRADING index 67b6dcb8e75..400187a40f2 100644 --- a/UPGRADING +++ b/UPGRADING @@ -440,6 +440,7 @@ PHP 8.5 UPGRADE NOTES . AF_PACKET (Linux only). . IP_BINDANY (FreeBSD/NetBSD/OpenBSD only). . SO_BUSY_POLL (Linux only). + . UDP_SEGMENT (Linux only). ======================================== 11. Changes to INI File Handling diff --git a/ext/sockets/config.m4 b/ext/sockets/config.m4 index 37f927a7818..f637c32b3b6 100644 --- a/ext/sockets/config.m4 +++ b/ext/sockets/config.m4 @@ -5,7 +5,7 @@ PHP_ARG_ENABLE([sockets], if test "$PHP_SOCKETS" != "no"; then AC_CHECK_FUNCS([hstrerror if_nametoindex if_indextoname sockatmark]) - AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h]) + AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h linux/udp.h]) AC_DEFINE([HAVE_SOCKETS], [1], [Define to 1 if the PHP extension 'sockets' is available.]) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 2d8b55c6b97..e3d5dc6c296 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -70,6 +70,9 @@ # if defined(HAVE_LINUX_IF_ETHER_H) # include # endif +# if defined(HAVE_LINUX_UDP_H) +# include +# endif #endif #include @@ -2301,6 +2304,22 @@ PHP_FUNCTION(socket_set_option) } #endif +#if defined(UDP_SEGMENT) + case UDP_SEGMENT: { + ov = zval_get_long(arg4); + + // UDP segmentation offload maximum size or 0 to disable it + if (ov < 0 || ov > USHRT_MAX) { + zend_argument_value_error(4, "must be of between 0 and %u", USHRT_MAX); + RETURN_FALSE; + } + + optlen = sizeof(ov); + opt_ptr = &ov; + break; + } +#endif + default: default_case: ov = zval_get_long(arg4); diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index d647f46b80d..d94352e3628 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -2022,6 +2022,14 @@ const ETH_P_LOOP = UNKNOWN; const ETH_P_ALL = UNKNOWN; #endif +#ifdef UDP_SEGMENT +/** + * @var int + * @cvalue UDP_SEGMENT + */ +const UDP_SEGMENT = UNKNOWN; +#endif + /** * @strict-properties * @not-serializable diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 45714fb2851..78f2212a518 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 42d486d2666d23569e70860e2b1ef203161792b3 */ + * Stub hash: 0ff66adfea41b603b9d58f1f0a82a950f4034cc4 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -1108,6 +1108,9 @@ static void register_sockets_symbols(int module_number) #if defined(ETH_P_ALL) REGISTER_LONG_CONSTANT("ETH_P_ALL", ETH_P_ALL, CONST_PERSISTENT); #endif +#if defined(UDP_SEGMENT) + REGISTER_LONG_CONSTANT("UDP_SEGMENT", UDP_SEGMENT, CONST_PERSISTENT); +#endif } static zend_class_entry *register_class_Socket(void) diff --git a/ext/sockets/tests/socket_cmsg_udp_segment.phpt b/ext/sockets/tests/socket_cmsg_udp_segment.phpt new file mode 100644 index 00000000000..679b246ea9d --- /dev/null +++ b/ext/sockets/tests/socket_cmsg_udp_segment.phpt @@ -0,0 +1,26 @@ +--TEST-- +UDP_SEGMENT setsockopt(), can't really test is as the kernel support might not be enabled. +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} +try { + socket_setopt($src, SOL_UDP, UDP_SEGMENT, 65536); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +socket_setopt(): Argument #4 ($value) must be of between 0 and 65535 +socket_setopt(): Argument #4 ($value) must be of between 0 and 65535