Skip to content

Commit 840f8b5

Browse files
committed
Merge branch 'github-pull-6'
ipv6_ip_matches takes two arguments, an IPv6 address and a prefix with length, and checks that this address is, in fact, in this prefix. We also add a hopefully complete enough set of tests. It has analogous semantics to ipv4_ip_subnet; the name is different because the word "subnet" does not really fit to universally describe an address range with common leading bits; many prefixes can be used to number a single subnet, a prefix might not correspond to a particular subnet at all (esp. with DHCP Prefix Delegation), etc. There's a short note on that in the doc page. Signed-off-by: Alexey Gladkov <legion@kernel.org>
2 parents ab2e6d1 + d6bdd3b commit 840f8b5

File tree

3 files changed

+253
-3
lines changed

3 files changed

+253
-3
lines changed

docs/shell-ip-address.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ shell-ip-address(3)
22

33
# NAME
44

5-
ipv4_ip_subnet, ipv4_mask2prefix, ipv4_prefix2mask, ipv4_ptonx, ipv6_addr_type, ipv6_ptonx, valid_ipv4 - functions to manipulate IP addresses
5+
ipv4_ip_subnet, ipv4_mask2prefix, ipv4_prefix2mask, ipv4_ptonx, ipv6_addr_type, ipv6_ip_matches, ipv6_ptonx, valid_ipv4 - functions to manipulate IP addresses
66

77
# SYNOPSIS
88

@@ -13,12 +13,13 @@ ipv4_ip_subnet, ipv4_mask2prefix, ipv4_prefix2mask, ipv4_ptonx, ipv6_addr_type,
1313
- ipv4_ptonx ipaddr
1414

1515
- ipv6_addr_type ipaddr
16+
- ipv6_ip_matches ipaddr prefix
1617
- ipv6_ptonx ipaddr
1718

1819
# DESCRIPTION
1920

2021
## ipv4_ip_subnet
21-
Function checks that IP address is in subnet.
22+
Function checks that IPv4 address is in subnet.
2223

2324
Example:
2425
```
@@ -133,6 +134,23 @@ done
133134
The intended purpose of *ipv6_addr_type* is to prevent misuse of special-use addresses.
134135
For example, since link-local unicast addresses only make sense within a link and are incomplete without a scope identifier, it generally makes no sense to specify them in DNS AAAA records.
135136

137+
## ipv6_ip_matches
138+
Checks that an IPv6 address is in a prefix (IOW, belongs in a range with common leading bits).
139+
140+
Example:
141+
```
142+
ipv6_ip_matches 3fff:e:b:1::2 3fff:e:b:1::/64; echo res=$?
143+
res=0
144+
145+
ipv6_ip_matches 3fff:e:b:3::2 3fff:e:b:1::/64; echo res=$?
146+
res=1
147+
148+
ipv6_ip_matches 3fff:e:b:3::2 3fff:e:b::/48; echo res=$?
149+
res=0
150+
```
151+
152+
Remember that an IPv6 prefix (netmask of leading 1 bits), unlike an IPv4 prefix, does not necessarily have routing significance; it might or might not be reachable "on-link" in any subnet.
153+
136154
## ipv6_ptonx
137155
This function interprets the given option value as an IPv6 address similarly to inet_pton(3), and outputs each octet in network byte order as 16 adjacent 2-digit hexadecimal numbers.
138156
This form is useful to perform bitwise operations on the address.

shell-ip-address

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ __len2mask_32()
108108
printf '%s' "$mask"
109109
}
110110

111-
### Checks that IP address is in subnet
111+
### Checks that IPv4 address is in subnet
112112
### Usage example:
113113
### ipv4_ip_subnet 172.16.1.2 172.16.1.0/24; echo res=$?
114114
### res=0
@@ -345,4 +345,56 @@ ipv6_addr_type()
345345
return 0
346346
}
347347

348+
### Checks that IPv6 address is in a prefix.
349+
### Usage example:
350+
### ipv6_ip_matches 3fff:e:b:1::2 3fff:e:b:1::/64; echo res=$?
351+
### res=0
352+
###
353+
### ipv6_ip_matches 3fff:e:b:3::2 3fff:e:b:1::/64; echo res=$?
354+
### res=1
355+
###
356+
### ipv6_ip_matches 3fff:e:b:3::2 3fff:e:b::/48; echo res=$?
357+
### res=0
358+
ipv6_ip_matches()
359+
{
360+
local addr pref preflen
361+
addr="${1-}"; shift
362+
pref="${1-}"; shift
363+
preflen="${pref##*/}"
364+
365+
[ -n "$preflen" ] ||
366+
return 2
367+
[ "$preflen" = 0 ] || shell_var_is_number "$preflen" ||
368+
return 2
369+
[ "$preflen" -le 128 ] ||
370+
return 2
371+
372+
local hex_addr hex_pref hex_mask
373+
374+
hex_addr="$(ipv6_ptonx "$addr")" &&
375+
hex_pref="$(ipv6_ptonx "${pref%%/*}")" ||
376+
return 2
377+
378+
local i_addr i_pref w_addr w_pref
379+
while [ "$preflen" -gt 32 ]; do
380+
i_addr="${hex_addr#????????}"
381+
i_pref="${hex_pref#????????}"
382+
w_addr="${hex_addr%$i_addr}"
383+
w_pref="${hex_pref%$i_pref}"
384+
[ "$w_pref" = "$w_addr" ] ||
385+
return 1
386+
387+
hex_addr="$i_addr"
388+
hex_pref="$i_pref"
389+
preflen="$(($preflen - 32))"
390+
done
391+
i_addr="${hex_addr#????????}"
392+
i_pref="${hex_pref#????????}"
393+
w_addr="${hex_addr%$i_addr}"
394+
w_pref="${hex_pref%$i_pref}"
395+
hex_mask="$(__len2mask_32 "$preflen")"
396+
[ "$((0x$w_pref & $hex_mask))" -eq "$((0x$w_addr & $hex_mask))" ] ||
397+
return 1
398+
}
399+
348400
fi #__included_shell_ip_address

tests/ip_address

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,3 +890,183 @@ ip_address_test6t21() { # UnitTest
890890
assertFalse "malformed address: $repr" $rc
891891
assertNull "empty output" "$(ipv6_addr_type "$repr")"
892892
}
893+
894+
ip_address_test_match600() { # UnitTest
895+
. ../shell-ip-address
896+
897+
local rc=0
898+
local addr=2001:7::23:443
899+
local prefix=::/0
900+
ipv6_ip_matches "$addr" "$prefix" || rc=1
901+
assertTrue "matches 'default route': $addr" $rc
902+
}
903+
904+
ip_address_test_match602() { # UnitTest
905+
. ../shell-ip-address
906+
907+
local rc=0
908+
local addr=::ffff:c000:2
909+
local prefix=::/64
910+
ipv6_ip_matches "$addr" "$prefix" || rc=1
911+
assertTrue "enough common bits: $addr -> $prefix" $rc
912+
}
913+
914+
ip_address_test_match603() { # UnitTest
915+
. ../shell-ip-address
916+
917+
local rc=0
918+
local addr=::ffff:c000:2
919+
local prefix=::ffff:0:0/96
920+
ipv6_ip_matches "$addr" "$prefix" || rc=1
921+
assertTrue "$addr -> $prefix" $rc
922+
}
923+
924+
ip_address_test_match604() { # UnitTest
925+
. ../shell-ip-address
926+
927+
local rc=0
928+
local addr=2001:db8:7:560a::c000:204
929+
local prefix=2001:db8:7:5608::/61
930+
ipv6_ip_matches "$addr" "$prefix" || rc=1
931+
assertTrue "non-4bit-aligned prefix: $addr -> $prefix" $rc
932+
}
933+
934+
ip_address_test_match605() { # UnitTest
935+
. ../shell-ip-address
936+
937+
local rc=0
938+
local addr=2001:db8:7:560a::c000:204
939+
local prefix=2001:db8:7:5600::/60
940+
ipv6_ip_matches "$addr" "$prefix" || rc=1
941+
assertTrue "non-32bit-aligned prefix: $addr -> $prefix" $rc
942+
}
943+
944+
ip_address_test_match606() { # UnitTest
945+
. ../shell-ip-address
946+
947+
local rc=0
948+
local addr=2001:db8:7:560a::c000:204
949+
local prefix=2001:db8:7:5000::/52
950+
ipv6_ip_matches "$addr" "$prefix" || rc=1
951+
assertTrue "non-32bit-aligned prefix: $addr -> $prefix" $rc
952+
}
953+
954+
ip_address_test_match607() { # UnitTest
955+
. ../shell-ip-address
956+
957+
local rc=0
958+
local addr=2001:db8:7:500a::c000:205
959+
local prefix=2001:db8:7:500a::c000:204/127
960+
ipv6_ip_matches "$addr" "$prefix" || rc=1
961+
assertTrue "127-bit prefix: $addr -> $prefix" $rc
962+
}
963+
964+
ip_address_test_match608() { # UnitTest
965+
. ../shell-ip-address
966+
967+
local rc=0
968+
local addr=2001:db8:7:500a::c000:204
969+
local prefix=2001:db8:7:500a::c000:204/128
970+
ipv6_ip_matches "$addr" "$prefix" || rc=1
971+
assertTrue "128-bit prefix: $addr -> $prefix" $rc
972+
}
973+
974+
ip_address_test_match616() { # UnitTest
975+
. ../shell-ip-address
976+
977+
local rc=0
978+
local addr=3fff:f:e:4::c
979+
local prefix=3fff:f:e:4::/64
980+
ipv6_ip_matches "$addr" "$prefix" || rc=1
981+
assertTrue "$addr -> $prefix" $rc
982+
}
983+
984+
ip_address_test_match617() { # UnitTest
985+
. ../shell-ip-address
986+
987+
local rc=0
988+
local addr=3fff:f:e:4::c
989+
local prefix=3fff:f:e:4::/48
990+
ipv6_ip_matches "$addr" "$prefix" || rc=1
991+
assertTrue "$addr -> $prefix" $rc
992+
}
993+
994+
ip_address_test_match620() { # UnitTest
995+
. ../shell-ip-address
996+
997+
local rc=0
998+
local addr=::ffff:c000:2
999+
local prefix=::/96
1000+
ipv6_ip_matches "$addr" "$prefix" || rc=1
1001+
assertFalse "common bits too short: $addr -> $prefix" $rc
1002+
}
1003+
1004+
ip_address_test_match621() { # UnitTest
1005+
. ../shell-ip-address
1006+
1007+
local rc=0
1008+
local addr=2001::ffff:c000:2
1009+
local prefix=::ffff:0:0/96
1010+
ipv6_ip_matches "$addr" "$prefix" || rc=1
1011+
assertFalse "common low bits: $addr -> $prefix" $rc
1012+
}
1013+
1014+
ip_address_test_match624() { # UnitTest
1015+
. ../shell-ip-address
1016+
1017+
local rc=0
1018+
local addr=2001:db8:7:560a::c000:204
1019+
local prefix=2001:db8::/60
1020+
ipv6_ip_matches "$addr" "$prefix" || rc=1
1021+
assertFalse "! $addr -> $prefix" $rc
1022+
}
1023+
1024+
ip_address_test_match636() { # UnitTest
1025+
. ../shell-ip-address
1026+
1027+
local rc=0
1028+
local addr=3fff:f:e:4::c
1029+
local prefix=3fff:f:e:1::/64
1030+
ipv6_ip_matches "$addr" "$prefix" || rc=1
1031+
assertFalse "! $addr -> $prefix" $rc
1032+
}
1033+
1034+
ip_address_test_match640() { # UnitTest
1035+
. ../shell-ip-address
1036+
1037+
local rc=0
1038+
local addr=xxx
1039+
local prefix=2002::/16
1040+
ipv6_ip_matches "$addr" "$prefix" || rc=$?
1041+
assertEquals "garbage addr: $addr" 2 $rc
1042+
}
1043+
1044+
ip_address_test_match641() { # UnitTest
1045+
. ../shell-ip-address
1046+
1047+
local rc=0
1048+
local addr=2001:db8:7:e8::c0
1049+
local prefix=yyy/16
1050+
ipv6_ip_matches "$addr" "$prefix" || rc=$?
1051+
assertEquals "garbage prefix: $prefix" 2 $rc
1052+
}
1053+
1054+
ip_address_test_match642() { # UnitTest
1055+
. ../shell-ip-address
1056+
1057+
local rc=0
1058+
local addr=2001:db8:7:e8::c0
1059+
local prefix=3fff:1::/zzz
1060+
ipv6_ip_matches "$addr" "$prefix" || rc=$?
1061+
assertEquals "garbage prefix length: $prefix" 2 $rc
1062+
}
1063+
1064+
ip_address_test_match643() { # UnitTest
1065+
. ../shell-ip-address
1066+
1067+
local rc=0
1068+
local addr=2001:db8:7:e8::c0
1069+
local prefix=3fff:1::/100500
1070+
ipv6_ip_matches "$addr" "$prefix" || rc=$?
1071+
assertEquals "garbage prefix length: $prefix" 2 $rc
1072+
}

0 commit comments

Comments
 (0)