One often works with IP addresses in packed form which allows for a very efficient regex match:
sub is_private {
my ($packed_ip) = @_;
return $packed_ip =~ m{
^
(?: \x0A # 10.0.0.0/8
| \xAC[\x10-\x1F] # 172.16.0.0/12
| \xC0\xA8 # 192.168.0.0/16
)
}x;
}
Test:
use strict;
use warnings;
use Socket qw( inet_aton );
sub is_private {
my ($packed_ip) = @_;
return $packed_ip =~ m{
^
(?: \x0A # 10.0.0.0/8
| \xAC[\x10-\x1F] # 172.16.0.0/12
| \xC0\xA8 # 192.168.0.0/16
)
}x;
}
my $result = '';
for (qw(
9.255.255.255 10.0.0.0 10.255.255.255 11.0.0.0
172.15.255.255 172.16.0.0 172.31.255.255 172.32.0.0
192.167.255.255 192.168.0.0 192.168.255.255 192.169.0.0
)) {
my $packed_ip = inet_aton($_);
$result .= is_private($packed_ip) ? 1 : 0;
}
print("got: $result\n");
print("expect: ", "0110"x3, "\n");
got: 011001100110
expect: 011001100110
Update: Alternatives:
sub is_private {
my ($packed_ip) = @_;
return ($packed_ip & "\xFF\x00\x00\x00") eq "\x0A\x00\x00\x00"
|| ($packed_ip & "\xFF\xF0\x00\x00") eq "\xAC\x10\x00\x00"
|| ($packed_ip & "\xFF\xFF\x00\x00") eq "\xC0\xA8\x00\x00";
}
sub is_private {
my $nummy_ip = unpack('N', shift);
return ($nummy_ip & 0xFF000000) == 0x0A000000 # 10.0.0.0/8
|| ($nummy_ip & 0xFFF00000) == 0xAC100000 # 172.16.0.0/12
|| ($nummy_ip & 0xFFFF0000) == 0xC0A80000; # 192.168.0.0/16
}
use Inline CPP => <<'__EOI__';
// Assumes the arg had UTF8=0
IV is_private(const char* packed_ip) {
// XXX Alignment issues
const U16& hi = *(const U16*)packed_ip;
// The irrelevant branch will be optimised away.
if (*(const U16*)("\x12\x34") == 0x1234) {
// Little endian
if ( hi == 0xC0A8
|| (hi & 0xFFF0) == 0xAC10
|| (hi & 0xFF00) == 0x0A00
)
return 1;
} else {
// Little endian
if ( hi == 0xA8C0
|| (hi & 0xF0FF) == 0x10AC
|| (hi & 0x00FF) == 0x000A
)
return 1;
}
return 0;
}
__EOI__