1

I have some internet traffic data to analyze. I need to analyze only those packets that are within a certain IP range. So, I need to write a if statement. I suppose I need a regular expression for the test condition. My knowledge of regexp is a little weak. Can someone tell me how would I construct a regular expression for that condition. An example range may be like

Group A 56.286.75.0/19 57.256.106.0/21 64.131.14.0/22 Group B 58.176.44.0/21 58.177.92.0/19 

The if statement would be like

if("IP in A" || "IP in B") { do something } else { do something else } 

so i would need to make the equivalent regexp for "IP in A" and "IP in B" conditions.

4
  • 3
    Why the hell do you want to use regex for checking something defined on bits level? Commented Dec 16, 2010 at 13:49
  • @x13n well thats just an initial thought and one of the reasons I posted here to get any better ideas. i didn't get what u mean by "defined on bits level" though. Commented Dec 16, 2010 at 14:03
  • Network mask (the /<number> part in each of your address) tells how many bits from the left in address should be compared to tell whether an address belongs to some subnet. It would be rather hard to write a regular expression for this. Commented Dec 16, 2010 at 14:10
  • @x13n oh ok got it now, i've got one suggestion that looks like a better way to test this so doing that. thanks. Commented Dec 16, 2010 at 14:14

4 Answers 4

8

I don't think that regexps provide much advantage for this problem.

Instead, use the Net::Netmask module. The "match" method should do what you want.

Sign up to request clarification or add additional context in comments.

Comments

1

I have to echo the disagreement with using a regex to check IP addresses...however, here is a way to pull IPs out of text:

qr{ (?<!\d) # No digit having come immediately before (?: [1-9] \d? # any one or two-digit number | 1 \d \d # OR any three-digit number starting with 1 | 2 (?: [0-4] \d # OR 200 - 249 | 5 [0-6] # OR 250 - 256 ) ) (?: \. # followed by a dot (?: [1-9] \d? # 1-256 reprise... | 1 \d \d | 2 (?: [0-4 \d | 5 [0-6] ) ) ){3} # that group exactly 3 times (?!\d) # no digit following immediately after }x ; 

But given that general pattern, we can construct an IP parser. But for the given "ranges", I wouldn't do anything less than the following:

A => qr{ (?<! \d ) (?: 56\.186\. 75 | 57\.256\.106 | 64\.131\. 14 ) \. (?: [1-9] \d? | 1 \d \d | 2 (?: [0-4] \d | 5 [0-6] ) ) (?! \d ) }x B => qr{ (?<! \d ) 58 \. (?: 176\.44 | 177\.92 ) \. (?: [1-9] \d? | 1 \d \d | 2 (?: [0-4] \d | 5 [0-6] ) ) (?! \d ) }x 

4 Comments

The prefix length (number after the '/') tells you how many bits make up the network part of the address which is the only part you should be matching. Here you're just matching the first 3 octets which assumes everything is a '/24'. en.wikipedia.org/wiki/CIDR_notation
Although I strongly advise using a netmask module that abstracts away the bits, for those bound and determined to use a regex (which just out of sheer cussedness I sometimes myself am :), this seems like a great place to use a “grammatical” regex, with a ((DEFINE)…) block and “regex subroutines”.
This re-invents the wheel - just use Regexp::Common::net.
@tchrist: Agree. @Ether: Whoops. I think it's just all my years of CPAN-less Perl environment that makes me code first.
1

I'm doing something like:

use NetAddr::IP; my @group_a = map NetAddr::IP->new($_), @group_a_masks; ... my $addr = NetAddr::IP->new( $ip_addr_in ); if ( grep $_->contains( $addr ), @group_a ) { print "group a"; } 

I chose NetAddr::IP over Net::Netmask for IPv6 support.

Comments

0

Martin is right, use Net::Netmask. If you really want to use a regex though...

$prefix = "192.168.1.0/25"; $ip1 = "192.168.1.1"; $ip2 = "192.168.1.129"; $prefix =~ s/([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)\/([0-9]+)/$mask=(2**32-1)<<(32-$5); $1<<24|$2<<16|$3<<8|$4/e; $ip1 =~ s/([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/$1<<24|$2<<16|$3<<8|$4/e; $ip2 =~ s/([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/$1<<24|$2<<16|$3<<8|$4/e; if (($prefix & $mask) == ($ip1 & $mask)) { print "ip1 matches\n"; } if (($prefix & $mask) == ($ip2 & $mask)) { print "ip2 matches\n"; } 

4 Comments

Whilst clever, it's code like that which gets Perl a bad reputation as being write-only mess akin to line noise :)
Agreed. Don't actually do this.
[0-9]+ is a poor character class to use for matching IP addresses, given that any given octet can be between 0 and 255 only. Something like [12]?[1-5]?[0-9]\. is better for matching one octet, but still not perfect. And this is why you shouldn't use regexes for matching IPs. :)
Yeah, there are all sorts of gotcahas that make using a good library the right answer. Let someone else figure out how to deal with stuff like this: ping 0300.0250.0xa.0xb

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.