[PATCH net-next] selftests: net: add IPv4 and IPv6 same scope address order check
From: Martin Jabůrek <hidden>
Date: 2026-09-01 08:22:31
Subsystem:
kernel selftest framework, networking [general], the rest · Maintainers:
Shuah Khan, Shuah Khan, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Introduce two new tests `ipv4_verify_addr_order` and `ipv6_verify_addr_order`, to check the ordering of a set of IP addresses after insertion. The implementations of these protocols are inconsistent in regard to address ordering. IPv4 addresses stay in the same order as inserted while IPv6 addresses appear in reverse order. This incosistency has prompted attempts to unify the ordering, so both protocols act the same (as IPv4). This however caused user-space regressions in certain applications, which relied on the order as it was prior to the change (particularly NetworkManager). The addition of these tests aims to consolidate the behaviour to prevent regressions in the future. The expected behaviour is the initial one, where each protocol acts differently. Tests were verified on a recent commit with the expected behaviour (61eb236c41c2) and a commit making both protocols act the same way (cb3de96eea66). Tests respectively pass and not pass as expected. Conversations detailing the decision process for creating these tests are linked below. Link: https://lore.kernel.org/netdev/20260521135310.GC977@cmadams.net/ (local) Link: https://lore.kernel.org/netdev/20260529112357.5079-1-fmancera@suse.de/ (local) Suggested-by: Fernando Fernandez Mancera <redacted> Signed-off-by: Martin Jabůrek <redacted> --- tools/testing/selftests/net/rtnetlink.py | 58 +++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/rtnetlink.py b/tools/testing/selftests/net/rtnetlink.py
index 5cc3ebdcf08d..1ee604e4e5f6 100755
--- a/tools/testing/selftests/net/rtnetlink.py
+++ b/tools/testing/selftests/net/rtnetlink.py@@ -314,11 +314,67 @@ def ipv6_route_del_reason_absent() -> None: "user deletion must not carry del-reason") +def ipv4_verify_addr_order() -> None: + """ + After inserting multiple same scope IPv4 addresses, their order + must be the same as the insertion order. + + See function ipv6_verify_addr_order in this file for further details. + """ + + DEV_NAME = "dummy_dev" + TEST_ADDRESSES = ["192.0.2.1", "192.0.2.2", "192.0.2.3"] + + with NetNS() as ns: + with NetNSEnter(str(ns)): + ip(f"link add name {DEV_NAME} type dummy", ns=str(ns)) + for addr in TEST_ADDRESSES: + ip(f"address add {addr}/24 dev {DEV_NAME}", ns=str(ns)) + ip(f"link set dev {DEV_NAME} up", ns=str(ns)) + + rtnl = RtnlAddrFamily() + addrs = rtnl.getaddr({"ifa-family": socket.AF_INET}, dump=True) + address_list = [addr["address"] for addr in addrs] + + ksft_eq(TEST_ADDRESSES, address_list, "Incorrect IPv4 address order") + + +def ipv6_verify_addr_order() -> None: + """ + After inserting multiple same scope IPv6 addresses, their order + must be the _reverse_ of the insertion order. + + While this behaviour is different from how IPv4 acts, + updating the IPv6 implementation to act the same way + has proved to cause user-space application regressions + (particularly in NetworkManager). This behaviour is being + tested for to consolidate it as being expected and correct. + """ + + DEV_NAME = "dummy_dev" + TEST_ADDRESSES = ["2001:db8::1", "2001:db8::2", "2001:db8::3"] + + with NetNS() as ns: + with NetNSEnter(str(ns)): + ip(f"link add name {DEV_NAME} type dummy", ns=str(ns)) + for addr in TEST_ADDRESSES: + ip(f"address add {addr}/32 dev {DEV_NAME}", ns=str(ns)) + ip(f"link set dev {DEV_NAME} up", ns=str(ns)) + + rtnl = RtnlAddrFamily() + addrs = rtnl.getaddr({"ifa-family": socket.AF_INET6}, dump=True) + address_list = [addr["address"] for addr in addrs] + + # We ignore the link-local address present by default. + ksft_eq(TEST_ADDRESSES[::-1], address_list[:3], "Incorrect IPv6 address order") + + def main() -> None: ksft_run([dump_mcaddr_check, dump_mcaddr6_check, ipv4_devconf_notify, ipv6_route_del_reason_expired, ipv6_route_del_reason_ra_withdrawn, - ipv6_route_del_reason_absent]) + ipv6_route_del_reason_absent, + ipv4_verify_addr_order, ipv6_verify_addr_order]) ksft_exit() if __name__ == "__main__":
--
2.55.0