NAME

Ereshkigal::IP - Exportable IP validation and normalization helper shared by the Ereshkigal modules.

VERSION

Version 0.0.1

SYNOPSIS

use Ereshkigal::IP qw( normalize_ip normalize_cidr );

my $ip = normalize_ip('2001:0DB8:0000:0000:0000:0000:0000:0001');
# $ip is now '2001:db8::1'

if ( !defined( normalize_ip($raw_ip) ) ) {
    die( '"' . $raw_ip . '" does not appear to be an IPv4 or IPv6 IP' );
}

my $cidr = normalize_cidr('1.2.3.4/24');
# $cidr is now '1.2.3.0/24' with the host bits masked off

DESCRIPTION

This holds the normalize_ip and normalize_cidr subs used for validating IPs and CIDR ranges and reducing them to a single canonical string form, so variant spellings of the same IP, most notably IPv6 long form vs short form as well as case, cannot be mistaken for differing IPs. normalize_cidr additionally masks the host bits off, so 1.2.3.4/24 and 1.2.3.0/24 are one range rather than two. Anything unparseable comes back as undef, letting garbage be bounced at the point of entry instead of being passed along for something further down to bounce.

EXPORTS

Nothing is exported by default. "normalize_ip" and "normalize_cidr" are available via @EXPORT_OK.

FUNCTIONS

normalize_ip

Returns the canonical string form of the passed IP. If it does not validate as either an IPv4 or IPv6 IP, undef is returned. undef and refs also return undef.

my $ip = normalize_ip($raw_ip);

Validation is done via Regexp::IPv4 and Regexp::IPv6, the same as Net::Firewall::BlockerHelper uses, so anything accepted here is also acceptable to the backends. On top of that IPv4 with leading zero octets, such as 010.0.0.1, which the regex permits, is explicitly refused rather than the octal vs decimal ambiguity being guessed at.

Only IPv6 IPs the regex has already validated are handed to inet_pton and inet_ntop, which are used purely for reducing them to the canonical form. IPv4 IPs that validate are already in canonical form and are returned as is.

normalize_cidr

Returns the canonical string form of the passed IPv4 or IPv6 CIDR range. If it does not validate, undef is returned. undef and refs also return undef.

my $cidr = normalize_cidr($raw_cidr);

A CIDR is an address, validated the same way "normalize_ip" validates it, followed by / and a prefix length that is a non-negative integer within the range valid for its family, 0 to 32 for IPv4 and 0 to 128 for IPv6. A bare IP with no prefix is refused, as are prefixes with leading zeros, since those are not canonical. The prefix range matches what Net::Firewall::BlockerHelper accepts, so anything accepted here is also acceptable to the backends.

The host bits below the prefix are masked off so the network address is returned, meaning 1.2.3.4/24 and 1.2.3.0/24 both reduce to 1.2.3.0/24 and variant spellings of the same range cannot be mistaken for differing ranges. The address portion is canonicalized the same as "normalize_ip", so IPv6 long form and case variants reduce to the same short form.