diff --git a/debian/changelog b/debian/changelog index e1c5d02..d51c754 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +ipaddrcheck (1.4) unstable; urgency=medium + + * Do not disallow all-zero host part IPv6 addresses for host and interface address checks (Daniil Baturin). + + -- Daniil Baturin Fri, 05 Dec 2025 15:04:03 +0000 + ipaddrcheck (1.3) unstable; urgency=medium * New option --range-prefix-length to require ranges to be within a subnet (Daniil Baturin). diff --git a/src/ipaddrcheck_functions.c b/src/ipaddrcheck_functions.c index fb0e654..75d8eec 100644 --- a/src/ipaddrcheck_functions.c +++ b/src/ipaddrcheck_functions.c @@ -2,7 +2,7 @@ * ipaddrcheck_functions.c: IPv4/IPv6 validation functions for ipaddrcheck * * Copyright (C) 2013 Daniil Baturin - * Copyright (C) 2018-2024 VyOS maintainers and contributors + * Copyright (C) 2018-2025 VyOS maintainers and contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -325,29 +325,15 @@ int is_ipv6(CIDR *address) return(result); } -/* Is it a correct IPv6 host address? */ +/* Is it a correct IPv6 host address? + Everything except the unspecified address is. + */ int is_ipv6_host(CIDR *address) { int result; - /* We reuse the same logic that prevents IPv4 network addresses - from being assigned to interfaces (address == network_address), - but the reason is slightly differnt. - - As per https://www.rfc-editor.org/rfc/rfc4291 section 2.6.1, - >[Subnet-Router anycast address] is syntactically - >the same as a unicast address for an interface on the link with the - >interface identifier set to zero. - - So, the first address of the subnet must not be used for link addresses, - even if the semantic reason is different. - There's absolutely nothing wrong with assigning the last address, though, - since there's no broadcast in IPv6. - */ - if( (cidr_get_proto(address) == CIDR_IPV6) && - ((cidr_equals(address, cidr_addr_network(address)) < 0) || - (cidr_get_pflen(address) >= 127)) ) + (cidr_equals(address, cidr_from_str(IPV6_UNSPECIFIED)) != 0) ) { result = RESULT_SUCCESS; } diff --git a/src/ipaddrcheck_functions.h b/src/ipaddrcheck_functions.h index b717d74..bd131cf 100644 --- a/src/ipaddrcheck_functions.h +++ b/src/ipaddrcheck_functions.h @@ -2,7 +2,7 @@ * ipaddrcheck_functions.h: macros and prototypes for ipaddrcheck * * Copyright (C) 2013 Daniil Baturin - * Copyright (C) 2018-2024 VyOS maintainers and contributors + * Copyright (C) 2018-2025 VyOS maintainers and contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,6 +50,7 @@ #define IPV6_MULTICAST "ff00::/8" #define IPV6_LINKLOCAL "fe80::/64" #define IPV6_LOOPBACK "::1/128" +#define IPV6_UNSPECIFIED "::/0" #define NO_LOOPBACK 0 #define LOOPBACK_ALLOWED 1 diff --git a/tests/check_ipaddrcheck.c b/tests/check_ipaddrcheck.c index ebae1e1..c4be8b1 100644 --- a/tests/check_ipaddrcheck.c +++ b/tests/check_ipaddrcheck.c @@ -2,7 +2,7 @@ * check_ipaddrcheck.c: ipaddrcheck unit tests * * Copyright (C) 2013 Daniil Baturin - * Copyright (C) 2018-2024 VyOS maintainers and contributors + * Copyright (C) 2018-2025 VyOS maintainers and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 or later as @@ -289,11 +289,6 @@ START_TEST (test_is_ipv6_host) CIDR* good_address_cidr = cidr_from_str(good_address_str_cidr); ck_assert_int_eq(is_ipv6_host(good_address_cidr), RESULT_SUCCESS); cidr_free(good_address_cidr); - - char* bad_address_str = "2001:db8:f::/48"; - CIDR* bad_address = cidr_from_str(bad_address_str); - ck_assert_int_eq(is_ipv6_host(bad_address), RESULT_FAILURE); - cidr_free(bad_address); } END_TEST @@ -367,15 +362,20 @@ START_TEST (test_is_any_host) ck_assert_int_eq(is_any_host(good_address_v6), RESULT_SUCCESS); cidr_free(good_address_v6); + char* good_address_str_v6_all_zero = "2001:db8::/32"; + CIDR* good_address_v6_all_zero = cidr_from_str(good_address_str_v6_all_zero); + ck_assert_int_eq(is_any_host(good_address_v6_all_zero), RESULT_SUCCESS); + cidr_free(good_address_v6_all_zero); + char* bad_address_str_v4 = "192.0.2.0/24"; CIDR* bad_address_v4 = cidr_from_str(bad_address_str_v4); ck_assert_int_eq(is_any_host(bad_address_v4), RESULT_FAILURE); cidr_free(bad_address_v4); - char* bad_address_str_v6 = "2001:db8::/32"; - CIDR* bad_address_v6 = cidr_from_str(bad_address_str_v6); - ck_assert_int_eq(is_any_host(bad_address_v6), RESULT_FAILURE); - cidr_free(bad_address_v6); + char* bad_address_str_v6_unspec = "::/0"; + CIDR* bad_address_v6_unspec = cidr_from_str(bad_address_str_v6_unspec); + ck_assert_int_eq(is_any_host(bad_address_v6_unspec), RESULT_FAILURE); + cidr_free(bad_address_v6_unspec); } END_TEST diff --git a/tests/integration_tests.sh b/tests/integration_tests.sh index 2c2c72b..9c937df 100755 --- a/tests/integration_tests.sh +++ b/tests/integration_tests.sh @@ -3,7 +3,7 @@ # integration_tests.sh: ipaddrcheck integration tests # # Copyright (C) 2013 Daniil Baturin -# Copyright (C) 2018-2024 VyOS maintainers and contributors +# Copyright (C) 2018-2025 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -59,6 +59,28 @@ ipv4_range_negative=( 192.0.2.1-192.0.2.500 ) +ipv4_interface_address_positive=( + 192.0.2.1/24 + 192.168.1.0/23 +) + +ipv4_interface_address_negative=( + 192.0.2.0/24 # Network address + 192.0.2.255/24 # Broadcast + 224.0.0.1/24 # Multicast + 0.0.0.0/0 # Unspecified +) + +ipv6_interface_address_positive=( + 2001:db8::1/64 + 2001:db8::/64 +) + +ipv6_interface_address_negative=( + ff02::1:2 # Multicast + ::/0 # Unspecified +) + ipv6_range_positive=( 2001:db8::1-2001:db8::99 ) @@ -111,10 +133,11 @@ ipv4_host_negative=( ipv6_host_positive=( 2001:db8:1::1/64 2001:db8:1::1/127 + 2001:db8:1::/64 # All-zero IPv6 addresses are valid host addresses ) ipv6_host_negative=( - 2001:db8::/32 + ::/0 ) string="garbage" @@ -272,7 +295,31 @@ done # --is-ipv6-net # --is-ipv6-multicast # --is-ipv6-link-local + # --is-valid-intf-address +for address in \ + ${ipv4_interface_address_positive[*]} +do + assert_raises "$IPADDRCHECK --is-valid-intf-address $address" 0 +done + +for address in \ + ${ipv4_interface_address_negative[*]} +do + assert_raises "$IPADDRCHECK --is-valid-intf-address $address" 1 +done + +for address in \ + ${ipv6_interface_address_positive[*]} +do + assert_raises "$IPADDRCHECK --is-valid-intf-address $address" 0 +done + +for address in \ + ${ipv6_interface_address_negative[*]} +do + assert_raises "$IPADDRCHECK --is-valid-intf-address $address" 1 +done # --is-ipv4-range for range in \