From nobody Mon May 5 18:20:58 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 4ZrqbH5534z5tcYK; Mon, 05 May 2025 18:20:59 +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 "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4ZrqbH12JPz3Xsb; Mon, 05 May 2025 18:20:59 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1746469259; 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=B2/6zWaZYuXUpVXz5pNIW8hGb0XEKtTzabWFtjjtZgo=; b=YgegoVi+Hz9vBCuJ6LNFfj8n6ikHiilrDIHZeps2sSi31PLl3qEJDv+WFT0B8JOt3aupQH 1aII9F1VYREc6VXxpwVeSTebkRTzi1jJlPIaI4xHP6QPH/fpFPphQNIg09+/y+uuUDF1K+ wqqUxnR4XS1Gn4dbUbxQv2GVqEwdpwUiYE9TRvIXm5KduKVeHCujG4U7VLg37K9jl5qIVj 3kn+5OLOEOO0vqpad5YE0dQbywEaGdXgw6hcH9gCr3Rwmpou0/7kHR50Tr8UMwH0MAChLd NE/BuFc0j1nJ/oXG5/cunaC3zCn0+sv/zDnf4FoiGnnWrL3dtz0gHOOdaZf2XQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1746469259; 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=B2/6zWaZYuXUpVXz5pNIW8hGb0XEKtTzabWFtjjtZgo=; b=tvBsdbfTcy8DBVLrNxAqDM0IqmrWta1AzgT97Tkxwd1cszRYS7PccCwkG4CyS7jsv3RurJ j3wYQ/g27TswGAa6MOvtdUKAfdffxJyS8vTr/FyuPPtfWq+hikcfwhtG99GIaXlQPoEWgv PaxwjxhI2huUA8LyWr/se+bJ8pMWyViH4Q+naj9vYvyqUaRzsL1yxhXide1C4Ibl2PDcwW efqBga6nG9gHb/i3LPXWiBJUwjxJpY3GLWYgGYIbsagwA40zb8RWtlI+pFWq/FKIo03MUD I7nq6BeRByKxmJa96bm2KmD4sN9nqR1oyXhJeQ0iR+qAyC02pCBCTgcHT9aolw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1746469259; a=rsa-sha256; cv=none; b=v6URSGfb4MxVHHn1RbIKYo6z/RKNGo1k824z0BM7LvzVJvunE4veI6/6proMFQzXmWsgEP VSGqQAo/MCzMDloCg+2wAIStRILtezpDq4Cv7XrMgd6d/CFAVb+Dl1IOU32jWZ6PjoQvFn Jr2aAEhUCz0+RHjs9XKrahQ+246RJDQd7/3Yroweh65hoRUJAjX3tTG+QcOaFEqXBUGdOp bhOjTk7KYvZeuj2wdhDZUr8QQcUIA7i6k/ix5Kpj+ZnF6UZY+INFRqgSwY48y0LpKFP5QI kLEbT95dYKfamMkh+woGTu9WTBlhDgZ8GC585o2yZebRdDbBu+nL9Kx1ZfglSA== 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 4ZrqbH0X31z8vT; Mon, 05 May 2025 18:20:59 +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 545IKwVA008219; Mon, 5 May 2025 18:20:58 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 545IKw2J008216; Mon, 5 May 2025 18:20:58 GMT (envelope-from git) Date: Mon, 5 May 2025 18:20:58 GMT Message-Id: <202505051820.545IKw2J008216@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Lexi Winter Subject: git: 757e973fb211 - main - libc tests: add tests for link_addr(3) and link_ntoa(3) 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: ivy X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 757e973fb2112ea442aa8990d991f406d407b6f7 Auto-Submitted: auto-generated The branch main has been updated by ivy: URL: https://cgit.FreeBSD.org/src/commit/?id=757e973fb2112ea442aa8990d991f406d407b6f7 commit 757e973fb2112ea442aa8990d991f406d407b6f7 Author: Lexi Winter AuthorDate: 2025-05-05 17:25:03 +0000 Commit: Lexi Winter CommitDate: 2025-05-05 18:20:02 +0000 libc tests: add tests for link_addr(3) and link_ntoa(3) for now, since link_addr() has no way to indicate an error, these are only positive tests which check the outcome of valid inputs. Reviewed by: ngie, des, adrian Approved by: des (mentor) Differential Revision: https://reviews.freebsd.org/D50062 --- lib/libc/tests/net/Makefile | 3 + lib/libc/tests/net/link_addr_test.cc | 269 +++++++++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+) diff --git a/lib/libc/tests/net/Makefile b/lib/libc/tests/net/Makefile index 016f0cd82ceb..24cff61e8d24 100644 --- a/lib/libc/tests/net/Makefile +++ b/lib/libc/tests/net/Makefile @@ -3,6 +3,9 @@ PACKAGE= tests ATF_TESTS_C+= ether_test ATF_TESTS_C+= eui64_aton_test ATF_TESTS_C+= eui64_ntoa_test +ATF_TESTS_CXX+= link_addr_test + +CXXSTD.link_addr_test= c++20 CFLAGS+= -I${.CURDIR} diff --git a/lib/libc/tests/net/link_addr_test.cc b/lib/libc/tests/net/link_addr_test.cc new file mode 100644 index 000000000000..009493e7e5aa --- /dev/null +++ b/lib/libc/tests/net/link_addr_test.cc @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2025 Lexi Winter + * + * SPDX-License-Identifier: ISC + */ + +/* + * Tests for link_addr() and link_ntoa(). + * + * link_addr converts a string representing an (optionally null) interface name + * followed by an Ethernet address into a sockaddr_dl. The expected format is + * "[ifname]:lladdr". This means if ifname is not specified, the leading colon + * is still required. + * + * link_ntoa does the inverse of link_addr, returning a string representation + * of the address. + * + * Note that the output format of link_ntoa is not valid input for link_addr + * since the leading colon may be omitted. This is by design. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std::literals; + +/* + * Define operator== and operator<< for ether_addr so we can use them in + * ATF_EXPECT_EQ expressions. + */ + +bool +operator==(ether_addr a, ether_addr b) +{ + return (std::ranges::equal(a.octet, b.octet)); +} + +std::ostream & +operator<<(std::ostream &s, ether_addr a) +{ + for (unsigned i = 0; i < ETHER_ADDR_LEN; ++i) { + if (i > 0) + s << ":"; + + s << std::format("{:02x}", static_cast(a.octet[i])); + } + + return (s); +} + +/* + * Create a sockaddr_dl from a string using link_addr(), and ensure the + * returned struct looks valid. + */ +sockaddr_dl +make_linkaddr(const std::string &addr) +{ + auto sdl = sockaddr_dl{}; + + sdl.sdl_len = sizeof(sdl); + ::link_addr(addr.c_str(), &sdl); + + ATF_REQUIRE_EQ(AF_LINK, static_cast(sdl.sdl_family)); + ATF_REQUIRE_EQ(true, sdl.sdl_len > 0); + ATF_REQUIRE_EQ(true, sdl.sdl_nlen >= 0); + + return (sdl); +} + +/* + * Return the data stored in a sockaddr_dl as a span. + */ +std::span +data(const sockaddr_dl &sdl) +{ + // sdl_len is the entire structure, but we only want the length of the + // data area. + auto dlen = sdl.sdl_len - offsetof(sockaddr_dl, sdl_data); + return {&sdl.sdl_data[0], dlen}; +} + +/* + * Return the interface name stored in a sockaddr_dl as a string. + */ +std::string_view +ifname(const sockaddr_dl &sdl) +{ + auto name = data(sdl).subspan(0, sdl.sdl_nlen); + return {name.begin(), name.end()}; +} + +/* + * Return the Ethernet address stored in a sockaddr_dl as an ether_addr. + */ +ether_addr +addr(const sockaddr_dl &sdl) +{ + ether_addr ret{}; + ATF_REQUIRE_EQ(ETHER_ADDR_LEN, sdl.sdl_alen); + std::ranges::copy(data(sdl).subspan(sdl.sdl_nlen, ETHER_ADDR_LEN), + &ret.octet[0]); + return (ret); +} + +/* + * Return the link address stored in a sockaddr_dl as a span of octets. + */ +std::span +lladdr(const sockaddr_dl &sdl) +{ + auto data = reinterpret_cast(LLADDR(&sdl)); + return {data, data + sdl.sdl_alen}; +} + + +/* + * Some sample addresses we use for testing. Include at least one address for + * each format we want to support. + */ + +struct test_address { + std::string input; /* value passed to link_addr */ + std::string ntoa; /* expected return from link_ntoa */ + ether_addr addr{}; /* expected return from link_addr */ +}; + +std::vector test_addresses{ + // No delimiter + {"001122334455"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // Colon delimiter + {"00:11:22:33:44:55"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // Dash delimiter + {"00-11-22-33-44-55"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // Period delimiter (link_ntoa format) + {"00.11.22.33.44.55"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // Period delimiter (Cisco format) + {"0011.2233.4455"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // An addresses without leading zeroes. + {"0:1:02:30:4:55"s, "0.1.2.30.4.55", + ether_addr{0x00, 0x01, 0x02, 0x30, 0x04, 0x55}}, + + // An address with some uppercase letters. + {"AA:B:cC:Dd:e0:1f"s, "aa.b.cc.dd.e0.1f", + ether_addr{0xaa, 0x0b, 0xcc, 0xdd, 0xe0, 0x1f}}, + + // Addresses composed only of letters, to make sure they're not + // confused with an interface name. + + {"aabbccddeeff"s, "aa.bb.cc.dd.ee.ff", + ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}, + + {"aa:bb:cc:dd:ee:ff"s, "aa.bb.cc.dd.ee.ff", + ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}, +}; + +/* + * Test without an interface name. + */ +ATF_TEST_CASE_WITHOUT_HEAD(basic) +ATF_TEST_CASE_BODY(basic) +{ + for (const auto &ta : test_addresses) { + // This does basic tests on the returned value. + auto sdl = make_linkaddr(":" + ta.input); + + // Check the ifname and address. + ATF_REQUIRE_EQ(""s, ifname(sdl)); + ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast(sdl.sdl_alen)); + ATF_REQUIRE_EQ(ta.addr, addr(sdl)); + + // Check link_ntoa returns the expected value. + auto ntoa = std::string(::link_ntoa(&sdl)); + ATF_REQUIRE_EQ(ta.ntoa, ntoa); + } + +} + +/* + * Test with an interface name. + */ +ATF_TEST_CASE_WITHOUT_HEAD(ifname) +ATF_TEST_CASE_BODY(ifname) +{ + for (const auto &ta : test_addresses) { + auto sdl = make_linkaddr("ix0:" + ta.input); + + ATF_REQUIRE_EQ("ix0", ifname(sdl)); + ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast(sdl.sdl_alen)); + ATF_REQUIRE_EQ(ta.addr, addr(sdl)); + + auto ntoa = std::string(::link_ntoa(&sdl)); + ATF_REQUIRE_EQ("ix0:" + ta.ntoa, ntoa); + } + +} + +/* + * Test some non-Ethernet addresses. + */ +ATF_TEST_CASE_WITHOUT_HEAD(nonether) +ATF_TEST_CASE_BODY(nonether) +{ + sockaddr_dl sdl; + + /* A short address */ + sdl = make_linkaddr(":1:23:cc"); + ATF_REQUIRE_EQ("", ifname(sdl)); + ATF_REQUIRE_EQ("1.23.cc"s, ::link_ntoa(&sdl)); + ATF_REQUIRE_EQ(3, sdl.sdl_alen); + ATF_REQUIRE_EQ(true, + std::ranges::equal(std::vector{0x01u, 0x23u, 0xccu}, lladdr(sdl))); + + /* A long address */ + sdl = make_linkaddr(":1:23:cc:a:b:c:d:e:f"); + ATF_REQUIRE_EQ("", ifname(sdl)); + ATF_REQUIRE_EQ("1.23.cc.a.b.c.d.e.f"s, ::link_ntoa(&sdl)); + ATF_REQUIRE_EQ(9, sdl.sdl_alen); + ATF_REQUIRE_EQ(true, std::ranges::equal( + std::vector{0x01u, 0x23u, 0xccu, 0xau, 0xbu, 0xcu, 0xdu, 0xeu, 0xfu}, + lladdr(sdl))); +} + +/* + * Test an extremely long address which would overflow link_ntoa's internal + * buffer. It should handle this by truncating the output. + * (Test for SA-16:37.libc / CVE-2016-6559.) + */ +ATF_TEST_CASE_WITHOUT_HEAD(overlong) +ATF_TEST_CASE_BODY(overlong) +{ + auto sdl = make_linkaddr( + ":01.02.03.04.05.06.07.08.09.0a.0b.0c.0d.0e.0f." + "11.12.13.14.15.16.17.18.19.1a.1b.1c.1d.1e.1f." + "22.22.23.24.25.26.27.28.29.2a.2b.2c.2d.2e.2f"); + + ATF_REQUIRE_EQ( + "1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.11.12.13.14.15.16.17.18.19.1a.1b."s, + ::link_ntoa(&sdl)); +} + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, basic); + ATF_ADD_TEST_CASE(tcs, ifname); + ATF_ADD_TEST_CASE(tcs, nonether); + ATF_ADD_TEST_CASE(tcs, overlong); +}