summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@vyos.io>2024-01-04 13:38:37 -0500
committerDaniil Baturin <daniil@vyos.io>2024-01-04 13:38:37 -0500
commit23e0eda853a9bfa42a2fa0d50b31eea874a01a9c (patch)
treeba5e579ed670d0275fea996b0284b3a74da78f28
parent346aedbcd2257512208195bb165cc857e2907922 (diff)
downloadipaddrcheck-23e0eda853a9bfa42a2fa0d50b31eea874a01a9c.tar.gz
ipaddrcheck-23e0eda853a9bfa42a2fa0d50b31eea874a01a9c.zip
Add --range-prefix-length option
to require the range boundaries to lie within the same subnet of a given size
-rw-r--r--src/Makefile.am2
-rw-r--r--src/ipaddrcheck.c49
-rw-r--r--src/ipaddrcheck_functions.c65
-rw-r--r--src/ipaddrcheck_functions.h4
-rw-r--r--src/ipaddrcheck_functions.obin45080 -> 55552 bytes
-rw-r--r--tests/check_ipaddrcheck.c26
-rwxr-xr-xtests/integration_tests.sh6
7 files changed, 131 insertions, 21 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 6098ae5..1c43bae 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,4 +1,4 @@
-AM_CFLAGS = --pedantic -Wall -Werror -std=c99 -O2
+AM_CFLAGS = --pedantic -Wall -Werror -Wno-error=format-overflow= -std=c99 -O2
AM_LDFLAGS =
ipaddrcheck_SOURCES = ipaddrcheck.c ipaddrcheck_functions.c
diff --git a/src/ipaddrcheck.c b/src/ipaddrcheck.c
index 230c6d6..248c248 100644
--- a/src/ipaddrcheck.c
+++ b/src/ipaddrcheck.c
@@ -88,6 +88,7 @@ static const struct option options[] =
{ "is-any-net", no_argument, NULL, 'E' },
{ "is-ipv4-range", no_argument, NULL, 'F' },
{ "is-ipv6-range", no_argument, NULL, 'G' },
+ { "range-prefix-length", required_argument, NULL, 'H' },
{ "version", no_argument, NULL, 'z' },
{ "help", no_argument, NULL, '?' },
{ "verbose", no_argument, NULL, 'V' },
@@ -109,6 +110,7 @@ int main(int argc, char* argv[])
int optc; /* Option character for getopt call */
int allow_loopback = NO_LOOPBACK; /* Allow IPv4 loopback in --is-valid-intf-address */
+ int range_prefix_length = 0;
int no_action = 0; /* Indicates the option modifies program behaviour
but doesn't have its own action */
@@ -124,9 +126,9 @@ int main(int argc, char* argv[])
const char* program_name = argv[0]; /* Program name for use in messages */
- /* Parse options, convert to action codes, store in array */
+ /* Parse options, convert to action codes, store in an array. */
- /* Try to allocate memory for the actions array, abort if fail */
+ /* Try to allocate memory for the actions array, abort if the attempt fails. */
actions = (int*)calloc(argc, sizeof(int));
if( errno == ENOMEM )
{
@@ -134,7 +136,7 @@ int main(int argc, char* argv[])
return(RESULT_INT_ERROR);
}
- while( (optc = getopt_long(argc, argv, "acdefghijklmnoprstuzABCDEFGV?", options, &option_index)) != -1 )
+ while( (optc = getopt_long(argc, argv, "acdefghijklmnoprstuzABCDEFGHV?", options, &option_index)) != -1 )
{
switch(optc)
{
@@ -219,6 +221,26 @@ int main(int argc, char* argv[])
case 'G':
ipv6_range_check = 1;
break;
+ case 'H':
+ errno = 0;
+ char* endptr = "";
+ /* Reminder to the reader on the quirks of strtol:
+ * errno != 0 --- internal parse error
+ * endptr == optarg --- no digits found in the string
+ * *endptr != '\0' --- extra characters after the last digit
+ */
+ range_prefix_length = (int)strtol(optarg, &endptr, 10);
+ if( (errno != 0) || (endptr == optarg) || (*endptr != '\0') )
+ {
+ fprintf(stderr, "Error: \"%s\" is not a valid prefix length\n", optarg);
+ return(RESULT_INT_ERROR);
+ }
+ if( (range_prefix_length < 0) || (range_prefix_length > 128) )
+ {
+ fprintf(stderr, "Error: \"%s\" is not a valid prefix length\n", optarg);
+ return(RESULT_INT_ERROR);
+ }
+ break;
case 'V':
verbose = 1;
break;
@@ -268,8 +290,13 @@ int main(int argc, char* argv[])
/* If the argument is a range, use special functions that can handle it. */
if( ipv4_range_check )
{
- int result = is_ipv4_range(address_str, verbose);
+ if( range_prefix_length > 32 )
+ {
+ fprintf(stderr, "Error: prefix length cannot exceed 32 for IPv4!\n");
+ return(RESULT_INT_ERROR);
+ }
+ int result = is_ipv4_range(address_str, range_prefix_length, verbose);
if( result == RESULT_SUCCESS )
{
return(EXIT_SUCCESS);
@@ -282,8 +309,13 @@ int main(int argc, char* argv[])
if( ipv6_range_check )
{
- int result = is_ipv6_range(address_str, verbose);
+ if( range_prefix_length > 128 )
+ {
+ fprintf(stderr, "Error: prefix length cannot exceed 32 for IPv4!\n");
+ return(RESULT_INT_ERROR);
+ }
+ int result = is_ipv6_range(address_str, range_prefix_length, verbose);
if( result == RESULT_SUCCESS )
{
return(EXIT_SUCCESS);
@@ -633,8 +665,11 @@ Address checking options:\n\
--is-ipv6-range Check if STRING is a valid IPv6 address range\n\
\n\
Behavior options:\n\
- --allow-loopback When used with --is-valid-intf-address,\n\
- makes IPv4 loopback addresses pass the check\n\
+ --allow-loopback When used with --is-valid-intf-address,\n\
+ makes IPv4 loopback addresses pass the check\n\
+ --range-prefix-length <INT> When used with --is-ipv4-range or --is-ipv6-range,\n\
+ requires the range boundaries to lie within\n\
+ a prefix of given length\n\
\n\
Other options:\n\
--version Print version information and exit \n\
diff --git a/src/ipaddrcheck_functions.c b/src/ipaddrcheck_functions.c
index e3e2c86..2b3cc22 100644
--- a/src/ipaddrcheck_functions.c
+++ b/src/ipaddrcheck_functions.c
@@ -460,7 +460,7 @@ int is_any_net(CIDR *address)
* This function is patently unsafe,
* whether it's safe to do what it does should be determined by its callers.
*/
-void split_range(char* range_str, char* left, char*right)
+void split_range(char* range_str, char* left, char* right)
{
char* ptr = left;
int length = strlen(range_str);
@@ -487,6 +487,9 @@ void split_range(char* range_str, char* left, char*right)
return;
}
+/* in6_addr fields are byte arrays, so we cannot compare them as numbers
+ * and needs custom comparison logic
+ */
int compare_ipv6(struct in6_addr *left, struct in6_addr *right)
{
int i = 0;
@@ -501,7 +504,7 @@ int compare_ipv6(struct in6_addr *left, struct in6_addr *right)
}
/* Is it a valid IPv4 address range? */
-int is_ipv4_range(char* range_str, int verbose)
+int is_ipv4_range(char* range_str, int prefix_length, int verbose)
{
int result = RESULT_SUCCESS;
@@ -553,7 +556,33 @@ int is_ipv4_range(char* range_str, int verbose)
if( left_in_addr->s_addr <= right_in_addr->s_addr )
{
- result = RESULT_SUCCESS;
+ /* If non-zero prefix_length is given,
+ check if the right address is within the network of the first one. */
+ if( prefix_length > 0 )
+ {
+ char left_pref_str[19];
+
+ /* XXX: Prefix length size is checked elsewhere, so it can't be more than 2 characters (32)
+ and overflow cannot occur.
+ */
+ sprintf(left_pref_str, "%s/%u", left, prefix_length);
+ CIDR* left_addr_with_pref = cidr_from_str(left_pref_str);
+ CIDR* left_net = cidr_addr_network(left_addr_with_pref);
+ if( cidr_contains(left_net, right_addr) == 0 )
+ {
+ result = RESULT_SUCCESS;
+ }
+ else
+ {
+ result = RESULT_FAILURE;
+ }
+ cidr_free(left_addr_with_pref);
+ cidr_free(left_net);
+ }
+ else
+ {
+ result = RESULT_SUCCESS;
+ }
}
else
{
@@ -573,7 +602,7 @@ int is_ipv4_range(char* range_str, int verbose)
}
/* Is it a valid IPv6 address range? */
-int is_ipv6_range(char* range_str, int verbose)
+int is_ipv6_range(char* range_str, int prefix_length, int verbose)
{
int result = RESULT_SUCCESS;
@@ -625,7 +654,33 @@ int is_ipv6_range(char* range_str, int verbose)
if( compare_ipv6(left_in6_addr, right_in6_addr) <= 0 )
{
- result = RESULT_SUCCESS;
+ /* If non-zero prefix_length is given,
+ check if the right address is within the network of the first one. */
+ if( prefix_length > 0 )
+ {
+ char left_pref_str[44];
+
+ /* XXX: Prefix length size is checked elsewhere, so it can't be more than 3 characters (128)
+ and overflow cannot occur.
+ */
+ sprintf(left_pref_str, "%s/%u", left, prefix_length);
+ CIDR* left_addr_with_pref = cidr_from_str(left_pref_str);
+ CIDR* left_net = cidr_addr_network(left_addr_with_pref);
+ if( cidr_contains(left_net, right_addr) == 0 )
+ {
+ result = RESULT_SUCCESS;
+ }
+ else
+ {
+ result = RESULT_FAILURE;
+ }
+ cidr_free(left_addr_with_pref);
+ cidr_free(left_net);
+ }
+ else
+ {
+ result = RESULT_SUCCESS;
+ }
}
else
{
diff --git a/src/ipaddrcheck_functions.h b/src/ipaddrcheck_functions.h
index c0d8603..9b5e55f 100644
--- a/src/ipaddrcheck_functions.h
+++ b/src/ipaddrcheck_functions.h
@@ -76,7 +76,7 @@ int is_ipv6_link_local(CIDR *address);
int is_valid_intf_address(CIDR *address, char* address_str, int allow_loopback);
int is_any_host(CIDR *address);
int is_any_net(CIDR *address);
-int is_ipv4_range(char* range_str, int verbose);
-int is_ipv6_range(char* range_str, int verbose);
+int is_ipv4_range(char* range_str, int prefix_length, int verbose);
+int is_ipv6_range(char* range_str, int prefix_length, int verbose);
#endif /* IPADDRCHECK_FUNCTIONS_H */
diff --git a/src/ipaddrcheck_functions.o b/src/ipaddrcheck_functions.o
index e8e714d..a689dac 100644
--- a/src/ipaddrcheck_functions.o
+++ b/src/ipaddrcheck_functions.o
Binary files differ
diff --git a/tests/check_ipaddrcheck.c b/tests/check_ipaddrcheck.c
index 3b260c2..ebae1e1 100644
--- a/tests/check_ipaddrcheck.c
+++ b/tests/check_ipaddrcheck.c
@@ -405,17 +405,31 @@ END_TEST
START_TEST (test_is_ipv4_range)
{
- ck_assert_int_eq(is_ipv4_range("192.0.2.0-192.0.2.10", 0), RESULT_SUCCESS);
- ck_assert_int_eq(is_ipv4_range("192.0.2.-", 0), RESULT_FAILURE);
- ck_assert_int_eq(is_ipv4_range("192.0.2.99-192.0.2.11", 0), RESULT_FAILURE);
+ ck_assert_int_eq(is_ipv4_range("192.0.2.0-192.0.2.10", 0, 1), RESULT_SUCCESS);
+ ck_assert_int_eq(is_ipv4_range("192.0.2.-", 0, 1), RESULT_FAILURE);
+ ck_assert_int_eq(is_ipv4_range("192.0.2.99-192.0.2.11", 0, 1), RESULT_FAILURE);
+}
+END_TEST
+
+START_TEST (test_is_ipv4_range_prefix)
+{
+ ck_assert_int_eq(is_ipv4_range("192.0.2.0-192.0.2.10", 24, 1), RESULT_SUCCESS);
+ ck_assert_int_eq(is_ipv4_range("10.0.1.1-10.0.2.1", 24, 1), RESULT_FAILURE);
}
END_TEST
START_TEST (test_is_ipv6_range)
{
- ck_assert_int_eq(is_ipv6_range("2001:db8::1-2001:db8::20", 0), RESULT_SUCCESS);
- ck_assert_int_eq(is_ipv6_range("2001:-", 0), RESULT_FAILURE);
- ck_assert_int_eq(is_ipv6_range("2001:db8::99-2001:db8:1", 0), RESULT_FAILURE);
+ ck_assert_int_eq(is_ipv6_range("2001:db8::1-2001:db8::20", 0, 1), RESULT_SUCCESS);
+ ck_assert_int_eq(is_ipv6_range("2001:-", 0, 1), RESULT_FAILURE);
+ ck_assert_int_eq(is_ipv6_range("2001:db8::99-2001:db8:1", 0, 1), RESULT_FAILURE);
+}
+END_TEST
+
+START_TEST (test_is_ipv6_range_prefix)
+{
+ ck_assert_int_eq(is_ipv6_range("2001:db8::1-2001:db8::20", 64, 1), RESULT_SUCCESS);
+ ck_assert_int_eq(is_ipv6_range("2001:db8:aaaa::1-2001:db8:bbbb::1", 64, 1), RESULT_FAILURE);
}
END_TEST
diff --git a/tests/integration_tests.sh b/tests/integration_tests.sh
index 8264960..3a1e4ea 100755
--- a/tests/integration_tests.sh
+++ b/tests/integration_tests.sh
@@ -284,6 +284,9 @@ do
assert_raises "$IPADDRCHECK --is-ipv4-range $range" 1
done
+assert_raises "$IPADDRCHECK --range-prefix-length 24 --is-ipv4-range 10.0.0.1-10.0.0.10" 0
+assert_raises "$IPADDRCHECK --range-prefix-length 29 --is-ipv4-range 10.0.0.1-10.0.0.10" 1
+
# --is-ipv6-range
for range in \
${ipv6_range_positive[*]}
@@ -297,4 +300,7 @@ do
assert_raises "$IPADDRCHECK --is-ipv6-range $range" 1
done
+assert_raises "$IPADDRCHECK --range-prefix-length 64 --is-ipv6-range 2001:db8::1-2001:db8::100" 0
+assert_raises "$IPADDRCHECK --range-prefix-length 64 --is-ipv6-range 2001:db8:aaaa::1-2001:db8:bbbb::1" 1
+
assert_end ipaddrcheck_integration