From nobody Wed Oct 1 21:24:25 2025 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4ccScB2QSwz69RZp; Wed, 01 Oct 2025 21:24:26 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4ccSc96yWCz3Dk3; Wed, 01 Oct 2025 21:24:25 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1759353866; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=EfDfSDTVFmfjZ24rCP20LjOvOsxg5hlcDH/UfLHn7GM=; b=dc9bmLNut/dR1Sb4Jts3KEVLvdnVyb5cp6evTprPIZkEG5NQGC2FG6N2JBsusCEzaVDbbK 1Dcpuyq4koBsk+x2Zh8LAXZY8fR05geIeyrJZDke9fv8m/RL0JkTpM+ZUZbTZhRSIzGCIV Qon1gzKZEakEL33arjWxYM/MIlp8NHyMIIIqx4T/7KG0pmjRvt7v4I0fYdHgbLMfCEHm4V 60DrL8RRmbTaLJnawuJQ5iujOQBqVOXEMRqRgM0Fa6U2Bl8yLPlXWjBG785cCbVRyRQhzG 0TAnU2IfejfMCcyxduntMr3JRB/iaq1oCzeEjIpJVw04PUtyfFvndH/mKgVxGw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1759353866; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=EfDfSDTVFmfjZ24rCP20LjOvOsxg5hlcDH/UfLHn7GM=; b=aJxiDlxr3ahEXUe1XrlHH0Omza1IlMau2xw3p/OtIu47bscAuxslguQT6C/nkP7o5rCrkE wrk4KghPSbF5zKJAl4Eo4nO1mxY1NL2GLPaAQM4NszY4JkhFroBupCatoMC6RSqo/jw7yL kiYu8M+BRdDCwl7QiQ97nIQ1BLHOoAJschBgbfXb+9TKSWl/o3zuh6y2H9q+PF+98Fr2b9 W78hMR/bZHRKI2zsi8zsrDH4YzU+SzU3O89vLJLOoHaSPEU2eLk9Yb5zG7zrIGUN9mLmkw EdzuzErzDHlqzlChhF1WKUm85cAUGTj6lHGuMzanqKdxES4dgJJWYovmX0El1g== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1759353866; a=rsa-sha256; cv=none; b=ctK4c87OntIhpiKR54SfQ8ANMRNAMIYy26Lpb2i59ymGPIl1Owe4DW6RGjlfRsBS5Wgkxi BBnlvm8b87HZ2IHM2huA45sPSm5FYeXyn2x4A55ky+wZP9ciOcfxPzIRXXYNmmjhaEImZ8 RAcDPQxPjClByCZnbWqFC4/fccOFJtLGGjSW6YXAopL8Usm6DgTibknEQNxqMUxirUcdFT VqCgA9sqixYfYMDTOq2ehleCoTgxBa2yGCpZhra0VhoV5B3aE9BcUoRU2Cs7S5XkJleZyY BBg3KCbCisLAN8gcOAMxCYLbr1iiDZnPtSNHnn/yyWelENiO60IY6aSIhghBQA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4ccSc95rgzzgGg; Wed, 01 Oct 2025 21:24:25 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 591LOP83064030; Wed, 1 Oct 2025 21:24:25 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 591LOPtb064027; Wed, 1 Oct 2025 21:24:25 GMT (envelope-from git) Date: Wed, 1 Oct 2025 21:24:25 GMT Message-Id: <202510012124.591LOPtb064027@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Gleb Smirnoff Subject: git: d0c8a0b3e6fc - main - tests/netinet: extend IPv4 multicast testing framework List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: glebius X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: d0c8a0b3e6fc5372428eb4fddcd4f8c65b5b0ee0 Auto-Submitted: auto-generated The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=d0c8a0b3e6fc5372428eb4fddcd4f8c65b5b0ee0 commit d0c8a0b3e6fc5372428eb4fddcd4f8c65b5b0ee0 Author: Gleb Smirnoff AuthorDate: 2025-09-29 23:23:28 +0000 Commit: Gleb Smirnoff CommitDate: 2025-10-01 19:41:52 +0000 tests/netinet: extend IPv4 multicast testing framework Provide a helper program multicast-receive.c that joins arbitrary group on arbitrary specified interface and receives a single datagram and prints it out. In multicast.sh provide a template with two jails connected by two interface pairs, so that we can check sends/receives in presence of multiple interfaces. Compose a few basic tests to check legacy and new IP_ADD_MEMBERSHIP and MCAST_JOIN_GROUP. --- tests/sys/netinet/Makefile | 2 +- tests/sys/netinet/multicast-receive.c | 130 ++++++++++++++++++++++++++++++++++ tests/sys/netinet/multicast.sh | 125 +++++++++++++++++++++++++++----- 3 files changed, 239 insertions(+), 18 deletions(-) diff --git a/tests/sys/netinet/Makefile b/tests/sys/netinet/Makefile index 1880709112c8..b742342beecb 100644 --- a/tests/sys/netinet/Makefile +++ b/tests/sys/netinet/Makefile @@ -48,7 +48,7 @@ TEST_METADATA.forward+= required_programs="python" \ TEST_METADATA.output+= required_programs="python" TEST_METADATA.redirect+= required_programs="python" -PROGS= udp_dontroute tcp_user_cookie multicast-send +PROGS= udp_dontroute tcp_user_cookie multicast-send multicast-receive ${PACKAGE}FILES+= redirect.py diff --git a/tests/sys/netinet/multicast-receive.c b/tests/sys/netinet/multicast-receive.c new file mode 100644 index 000000000000..81d0f10f5cfe --- /dev/null +++ b/tests/sys/netinet/multicast-receive.c @@ -0,0 +1,130 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Gleb Smirnoff + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static in_port_t +atop(const char *c) +{ + unsigned long ul; + + errno = 0; + if ((ul = strtol(c, NULL, 10)) < 1 || ul > IPPORT_MAX || errno != 0) + err(1, "can't parse %s", c); + + return ((in_port_t)ul); +} + +int +main(int argc, char *argv[]) +{ + char buf[IP_MAXPACKET + 1]; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in), + }; + socklen_t slen = sizeof(struct sockaddr_in); + struct in_addr maddr, ifaddr; + ssize_t len; + int s, ifindex; + bool index; + + if (argc < 4) +usage: + errx(1, "Usage: %s (ip_mreq|ip_mreqn|group_req) " + "IPv4-group port interface", argv[0]); + + if (inet_pton(AF_INET, argv[2], &maddr) != 1) + err(1, "inet_pton(%s) failed", argv[2]); + sin.sin_port = htons(atop(argv[3])); + if (inet_pton(AF_INET, argv[4], &ifaddr) == 1) + index = false; + else if ((ifindex = if_nametoindex(argv[4])) > 0) + index = true; + else if (strcmp(argv[4], "0") == 0) { + ifindex = 0; + index = true; + } else + err(1, "if_nametoindex(%s) failed", argv[4]); + + assert((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0); + assert(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0); + + if (strcmp(argv[1], "ip_mreq") == 0) { + if (index) + errx(1, "ip_mreq doesn't accept index"); + struct ip_mreq mreq = { + .imr_multiaddr = maddr, + .imr_interface = ifaddr, + }; + assert(setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) == 0); + } else if (strcmp(argv[1], "ip_mreqn") == 0) { + /* + * ip_mreqn shall be used with index, but for testing + * purposes accept address too. + */ + struct ip_mreqn mreqn = { + .imr_multiaddr = maddr, + .imr_address = index ? (struct in_addr){ 0 } : ifaddr, + .imr_ifindex = index ? ifindex : 0, + }; + assert(setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, + sizeof(mreqn)) == 0); + } else if (strcmp(argv[1], "group_req") == 0) { + if (!index) + errx(1, "group_req expects index"); + struct group_req greq = { .gr_interface = ifindex }; + struct sockaddr_in *gsa = (struct sockaddr_in *)&greq.gr_group; + + gsa->sin_family = AF_INET; + gsa->sin_len = sizeof(struct sockaddr_in); + gsa->sin_addr = maddr; + assert(setsockopt(s, IPPROTO_IP, MCAST_JOIN_GROUP, &greq, + sizeof(greq)) == 0); + } else + goto usage; + + assert((len = recvfrom(s, buf, sizeof(buf) - 1, 0, + (struct sockaddr *)&sin, &slen)) > 0); + buf[len] = '\0'; + printf("%s:%u %s\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), buf); + + return (0); +} diff --git a/tests/sys/netinet/multicast.sh b/tests/sys/netinet/multicast.sh old mode 100644 new mode 100755 index 1826e1e7c4ee..2ce6be68e44d --- a/tests/sys/netinet/multicast.sh +++ b/tests/sys/netinet/multicast.sh @@ -26,37 +26,128 @@ . $(atf_get_srcdir)/../common/vnet.subr -# See regression fixed in baad45c9c12028964acd0b58096f3aaa0fb22859 -atf_test_case "IP_MULTICAST_IF" "cleanup" -IP_MULTICAST_IF_head() +# Set up two jails, mjail1 and mjail2, connected with two interface pairs +multicast_vnet_init() { - atf_set descr \ - 'sendto() for IP_MULTICAST_IF socket does not do routing lookup' + + vnet_init + epair1=$(vnet_mkepair) + epair2=$(vnet_mkepair) + vnet_mkjail mjail1 ${epair1}a ${epair2}a + jexec mjail1 ifconfig ${epair1}a up + jexec mjail1 ifconfig ${epair1}a 192.0.2.1/24 + jexec mjail1 ifconfig ${epair2}a up + jexec mjail1 ifconfig ${epair2}a 192.0.3.1/24 + vnet_mkjail mjail2 ${epair1}b ${epair2}b + jexec mjail2 ifconfig ${epair1}b up + jexec mjail2 ifconfig ${epair1}b 192.0.2.2/24 + jexec mjail2 ifconfig ${epair2}b up + jexec mjail2 ifconfig ${epair2}b 192.0.3.2/24 +} + +atf_test_case "IP_ADD_MEMBERSHIP_ip_mreq" "cleanup" +IP_ADD_MEMBERSHIP_ip_mreq_head() +{ + atf_set descr 'IP_ADD_MEMBERSHIP / IP_MULTICAST_IF with ip_mreq' atf_set require.user root +} +IP_ADD_MEMBERSHIP_ip_mreq_body() +{ + multicast_vnet_init + + # join group on interface with IP address 192.0.2.2 + jexec mjail2 $(atf_get_srcdir)/multicast-receive \ + ip_mreq 233.252.0.1 6676 192.0.2.2 > out & pid=$! + atf_check -s exit:0 -o empty \ + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 192.0.2.1 hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" + atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out + # join group on interface with IP address 192.0.3.2 + jexec mjail2 $(atf_get_srcdir)/multicast-receive \ + ip_mreq 233.252.0.1 6676 192.0.3.2 > out & pid=$! + atf_check -s exit:0 -o empty \ + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 192.0.3.1 hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" + atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out +} +IP_ADD_MEMBERSHIP_ip_mreq_cleanup() +{ + rm out + vnet_cleanup } -IP_MULTICAST_IF_body() +atf_test_case "IP_ADD_MEMBERSHIP_ip_mreqn" "cleanup" +IP_ADD_MEMBERSHIP_ip_mreqn_head() +{ + atf_set descr 'IP_ADD_MEMBERSHIP / IP_MULTICAST_IF with ip_mreqn' + atf_set require.user root +} +IP_ADD_MEMBERSHIP_ip_mreqn_body() { - local epair mjail + multicast_vnet_init - vnet_init - # The test doesn't use our half of epair - epair=$(vnet_mkepair) - vnet_mkjail mjail ${epair}a - jexec mjail ifconfig ${epair}a up - jexec mjail ifconfig ${epair}a 192.0.2.1/24 + # join group on interface epair2 + jexec mjail2 $(atf_get_srcdir)/multicast-receive \ + ip_mreqn 233.252.0.1 6676 ${epair1}b > out & pid=$! + atf_check -s exit:0 -o empty \ + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 ${epair1}a hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" + atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out + + # join group on interface epair2 + jexec mjail2 $(atf_get_srcdir)/multicast-receive \ + ip_mreqn 233.252.0.1 6676 ${epair2}b > out & pid=$! atf_check -s exit:0 -o empty \ - jexec mjail $(atf_get_srcdir)/multicast-send \ - 0.0.0.0 0 233.252.0.1 6676 192.0.2.1 hello + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 ${epair2}a hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" +} +IP_ADD_MEMBERSHIP_ip_mreqn_cleanup() +{ + rm out + vnet_cleanup } -IP_MULTICAST_IF_cleanup() +atf_test_case "MCAST_JOIN_GROUP" "cleanup" +MCAST_JOIN_GROUP_head() +{ + atf_set descr 'MCAST_JOIN_GROUP' + atf_set require.user root +} +MCAST_JOIN_GROUP_body() +{ + multicast_vnet_init + + # join group on interface epair2 + jexec mjail2 $(atf_get_srcdir)/multicast-receive \ + group_req 233.252.0.1 6676 ${epair1}b > out & pid=$! + atf_check -s exit:0 -o empty \ + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 ${epair1}a hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" + atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out + + # join group on interface epair2 + jexec mjail2 $(atf_get_srcdir)/multicast-receive \ + group_req 233.252.0.1 6676 ${epair2}b > out & pid=$! + atf_check -s exit:0 -o empty \ + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 ${epair2}a hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" +} +MCAST_JOIN_GROUP_cleanup() { + rm out vnet_cleanup } atf_init_test_cases() { - atf_add_test_case "IP_MULTICAST_IF" + atf_add_test_case "IP_ADD_MEMBERSHIP_ip_mreq" + atf_add_test_case "IP_ADD_MEMBERSHIP_ip_mreqn" + atf_add_test_case "MCAST_JOIN_GROUP" }