Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

regex for classless IP subnets

by permanentE (Acolyte)
on Mar 21, 2003 at 23:27 UTC ( #245034=perlquestion: print w/ replies, xml ) Need Help??
permanentE has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to write a function that tells you if an IP address is in a classless subnet. First of all, has any one already written this code? If not, I'm having trouble with the regex's. Is there an easy way to match a range of numbers like 128-255? I've written it like this, taking advantage of the fact that 255 is the highest possible number:
if ($number =~ /12[89]/ ||$number =~ /1[3-9]\d/ ||$number =~ /2\d\d\/) + { $match = 1; }
There must be a better way.

For the more general subnetting problem I'm thinking I probably need to work in binary and doing the AND's and OR's etc. does anyone know of some example Perl code.

Comment on regex for classless IP subnets
Download Code
Re: regex for classless IP subnets
by zengargoyle (Deacon) on Mar 21, 2003 at 23:47 UTC

    Net::Netmask

    DESCRIPTION
    Net::Netmask parses and understands IPv4 CIDR blocks. It's built with an object-oriented interface. Nearly all functions are methods that operate on a Net::Netmask object. There are methods that provide the nearly all bits of information about a network block that you might want. There are also functions to put a network block into a table and then later lookup network blocks by IP address in that table. There are functions to turn a IP address range into a list of CIDR blocks. There are functions to turn a list of CIDR blocks into a list of IP addresses. There is a function for sorting by text IP address.
Re: regex for classless IP subnets
by traveler (Parson) on Mar 22, 2003 at 00:14 UTC
    Or you could do something like this:
    use strict; use Net::IP; my $net = new Net::IP("10.4.5.0/24"); my $host = new Net::IP("10.4.5.1"); print "yes\n" if ($net->overlaps($host) == $Net::IP::IP_B_IN_A_OVERLAP +);
    (The manual implies that qualifying IP_B_IN_A_OVERLAP is not necessary, but it seems to be).

    HTH, --traveler

Re: regex for classless IP subnets
by Kanji (Parson) on Mar 22, 2003 at 00:19 UTC

    Other than the proposed module solutions, why do you even need a regex?

    sub in_range { my( $number, $low, $high ) = @_; return 1 if $number >= $low && $number <= $high; return 0; } my $match = in_range( 68 => 128, 255 );

        --k.


Re: regex for classless IP subnets
by Vorlin (Sexton) on Mar 22, 2003 at 00:57 UTC
    Here's a relatively easy way I pounded out in about 2 minutes...
    $match = 0; $ip = $ARGV[0] ? $ARGV[0] : "129.10.10.120"; ($a,$b,$c,$d) = split '\.', $ip; $match = 1 if ($a > 127 && $a < 256); print "$match\n";
    Nothing glamorous, that's for sure, but it's an easy comparison.
Regex assertions in perl code -- Re: regex for classless IP subnets
by bart (Canon) on Mar 22, 2003 at 09:49 UTC
    Is there an easy way to match a range of numbers like 128-255?
    There is a way, which makes a lot of sense in theory, but of which implementation is rather difficult in Perl5. I have hopes that it will be a lot easier in Perl6. And that mechanism, is to use a regex assertion in Perl code.

    An assertion is a regex pattern that must match, but which doesn't eat any characters. Well known assertions are anchoring (/^/ and /$/), edge matching (/\b/) and lookahead and lookbehind (eh... see perlre).

    So how can you do assertion in Perl code? The following solution is based on work by dominus: using the experimental feature of embedding code in a regex, /(?{CODE})/, and use the result of that code as a condition in another experimental feature, conditional matches: /(?(condition)yes-pattern)/ or /(?(condition)yes-pattern|no-pattern)/. A third trick is to use a pattern that can never match: since // always matches, its negation (using negative lookahead), /(?!)/, will always fail.

    The combination, which acts like an assertion in Perl code, can look like

    (?(?{NOT-COND})(?!))
    or
    (?(?{COND})|(?!))
    "COND" and "NOT-COND" represent perl a perl expression that returns a boolean value. It's very ugly, I know. For this example:
    /\b(\d+)\b(?(?{not($1 >= 128 && $1 <= 255)})(?!))/
    or
    /\b(\d+)\b(?(?{$1 >= 128 && $1 <= 255})|(?!))/

    In case you're wondering about the /\b/ assertions: in case our assertion in code fails, it will not prevent backtracking. So without them, if the assertion fails for "350", it would backtrack and try again using "35", and succeed there. That is not what you want, here. Or, with just a trailing /\b/ as in /\d+\b/, it could still succeed for "50". That's why you need both.

Re: regex for classless IP subnets
by fokat (Deacon) on Mar 23, 2003 at 03:23 UTC

    Try out NetAddr::IP. It parses the input subnet specification. Then you can use the ->contains function to look for what you want. It also has a variety of other uses.

    Best regards

    -lem, but some call me fokat

Re: regex for classless IP subnets
by kal (Hermit) on Mar 24, 2003 at 19:57 UTC

    If you want to avoid the overhead of a full module, you could probably to it with two functions. First, something to convert IP addresses into integers, like:

    sub ip_2_int { my ($ip) = @_; $ip =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/ or die "$ip is an invalid address"; return ($1<<24)|($2<<16)|($3<<8)|$4; }

    Then, you just want to compare the network parts of the IP address with the network address. So, obviously you need to create the netmask (i.e., /16 is (2**16)-1, I think?), and then just compare the numbers, i.e.:

    if (($ipaddress|$netmask)==$network) { # then $ipaddress is in the range $network/$netmask defines }

    You could obviously wrap that in a common function, perhaps something like:

    sub isInNetwork { my ($cidr_str, $ipaddress_str) = @_; my ($network_str, $netmask_str) = split ('/', $cidr_str); my ($network, $ip) = (ip_2_int ($network_str), ip_2_int ($ipaddress_str)); return ($network==($ip|(2**$network-1)); } print "10.0.50.12 is in 10/8!\n" if isInNetwork ('10.0.0.0/8', '10.0.50.12');

    Or something (much more error checking would of course be required ;). That's off the bat, unfortunately I don't have access to a Perl right now :/ But, I'm pretty sure it's not supposed to be sufficiently complex to require a regex - remember, C programmers use IP addresses (socket.h), so it's can't be very difficult.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://245034]
Approved by robartes
Front-paged by Tomte
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (3)
As of 2014-09-19 23:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (151 votes), past polls