I think all the corner cases in IPv6 addresses are likely to make you crazy if you try to do the whole problem in an RE. You could try the is_ipv6 function here:
my $quad = "[0-9a-fA-F]{0,4}";
my $ipv6addr = "(?:$quad:){2,7}$quad";
sub is_ipv6 {
local $_ = $_[0];
return 0
unless m{^$ipv6addr(?:/(\d+))?$};
my $mask = defined($1) ? $1 : 128;
# in 5.10: my $mask = $1 // 128;
return 0
if /:::/
or /::.*::/
or not /::/ and 7 != tr/:/:/;
return ( 0 <= $mask and $mask <= 128 );
}
But you might be better off to just go ahead and try to convert the address with NetAddr::IP -> new6, and see if the result is true.
A quick and dirty test comparing the two approaches...
use warnings;
use strict;
use NetAddr::IP;
my $quad = "[0-9a-fA-F]{0,4}";
my $ipv6addr = "(?:$quad:){2,7}$quad";
sub is_ipv6 {
local $_ = $_[0];
return 0
unless m{^$ipv6addr(?:/(\d+))?$};
my $mask = defined($1) ? $1 : 128;
# in 5.10: my $mask = $1 // 128;
return 0
if /:::/
or /::.*::/
or not /::/ and 7 != tr/:/:/;
return ( 0 <= $mask and $mask <= 128 );
}
while (<DATA>) {
chomp;
printf "%-25s ", $_;
my $addr = NetAddr::IP -> new6 ($_);
$addr ||= "";
printf "%-25s ", $addr;
print is_ipv6($_) ? "yes\n" : "no\n";
}
__DATA__
::
::/0
::1/128
1::/64
:
:/96
2001:1::/64
2001:0:6:4003::/64
2001::12/128
2001::15/0
2001::15/-1
2001::15/129
20012::15/96
2001:1:::2/96
2001::1::2/96
2001::1:2::3/96
2001:2:3:4:5:6:7/48
2001:2:3:4::6:7/48
2001:2:3:4:5:6:7:8/64
2001:2:3:4:5:6:7:8:9/96
2001:2:3:4::6:7:8:9/96
prints the following:
:: 0:0:0:0:0:0:0:0/128 yes
::/0 0:0:0:0:0:0:0:0/0 yes
::1/128 0:0:0:0:0:0:0:1/128 yes
1::/64 1:0:0:0:0:0:0:0/64 yes
: no
:/96 no
2001:1::/64 2001:1:0:0:0:0:0:0/64 yes
2001:0:6:4003::/64 2001:0:6:4003:0:0:0:0/64 yes
2001::12/128 2001:0:0:0:0:0:0:12/128 yes
2001::15/0 2001:0:0:0:0:0:0:15/0 yes
2001::15/-1 no
2001::15/129 no
20012::15/96 no
2001:1:::2/96 no
2001::1::2/96 no
2001::1:2::3/96 no
2001:2:3:4:5:6:7/48 no
2001:2:3:4::6:7/48 2001:2:3:4:0:0:6:7/48 yes
2001:2:3:4:5:6:7:8/64 2001:2:3:4:5:6:7:8/64 yes
2001:2:3:4:5:6:7:8:9/96 no
2001:2:3:4::6:7:8:9/96 no