Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages


home | help
Net::IP::Match::RegexpUser Contributed Perl DocumentaNet::IP::Match::Regexp(3)

       Net::IP::Match::Regexp -	Efficiently match IP addresses against ranges

       Copyright 2005-2006 Clotho Advanced Media, Inc.,	<>

       Copyright 2007-2008 Chris Dolan,	<>

       This library is free software; you can redistribute it and/or modify it
       under the same terms as Perl itself.

	   use Net::IP::Match::Regexp qw( create_iprange_regexp	match_ip );

	   my $regexp =	create_iprange_regexp(
	      qw(	)
	   if (match_ip('', $regexp)) {

       This module allows you to check an IP address against one or more IP
       ranges.	It employs Perl's highly optimized regular expression engine
       to do the hard work, so it is very fast.	 It is optimized for speed by
       doing the match against a regexp	which implicitly checks	the broadest
       IP ranges first.	 An advantage is that the regexp can be	computed and
       stored in advance (in source code, in a database	table, etc) and
       reused, saving much time	if the IP ranges don't change too often.  The
       match can optionally report a value (e.g. a network name) instead of
       just a boolean, which makes module useful for mapping IP	ranges to
       names or	codes or anything else.

       This module does	not yet	support	IPv6 addresses,	although that feature
       should not be hard to implement as long as the regexps start with a 4
       vs. 6 flag.  Patches welcome.  :-)

       This module only	accepts	IP ranges in "a.b.c.d/x" (aka CIDR) notation.
       To work around that limitation, I recommend Net::CIDR::Lite to
       conveniently convert collections	of IP address ranges into CIDR format.

       This module makes no effort to validate the IP addresses	or ranges
       passed as arguments.  If	you pass address ranges	like "1000.0.0.0/300",
       you will	probably get weird regexps out.

       create_iprange_regexp($iprange |	$hashref | $arrayref, ...)
	   This	function digests IP ranges into	a regular expression that can
	   subsequently	be used	to efficiently test single IP addresses.  It
	   returns a regular expression	string that can	be passed to

	   The simple way to use this is to pass a list	of IP ranges as
	   "aaa.bbb.ccc.ddd/n".	 When used this	way, the return	value of the
	   match_ip() function will be simply 1	or "undef".

	   The more complex way	is to pass a hash reference of IP range	=>
	   return value	pairs.	When used this way, the	return value of	the
	   match_ip() function will be the specified return value or "undef"
	   for no match.

	   For example:

	       my $re1 = create_iprange_regexp('', '');
	       print match_ip('',	$re1); # prints	'1'

	       my $re2 = create_iprange_regexp({'' => '',
						'' => 'localhost'});
	       print match_ip('',	$re2); # prints	''

	   Be aware that the value string will be wrapped in single quotes in
	   the regexp.	Therefore, you must double-escape any single quotes in
	   that	value.	For example:

	       create_iprange_regexp({'' => 'O\\'Reilly publishing'});

	   Note	that the scalar	and hash styles	can be mixed (a	rarely used
	   feature).  These two	examples are equivalent:

				     {'' => ''},
				     {'' => 'LAN'});

	       create_iprange_regexp({'' =>	1,
				      '' => '',
				      '' => 1,
				      '' => 'LAN'});

	   If any of the IP ranges are overlapping, the	broadest one is	used.
	   If they are equivalent, then	the first one passed is	used.  If you
	   have	some data that might be	ambiguous, you pass an arrayref
	   instead of a	hashref, but it's better to clean up your data
	   instead!  For example:

	       my $re =	create_iprange_regexp(['' => 'zero', '' => 'one']);
	       print match_ip('', $re));	  # prints 'zero', since both match

	   WARNING: This function does no checking for validity	of IP ranges.
	   It happily accepts "1000.0.0.0/-38" and makes a garbage regexp.
	   Hopefully a future version will validate the	ranges,	perhaps	via
	   Net::CIDR or	Net::IP.

       create_iprange_regexp_depthfirst($iprange | $hashref | $arrayref, ...)
	   Returns a regexp in matches the most	specific IP range instead of
	   the broadest	range.	Example:

	       my $re =	create_iprange_regexp_depthfirst({'' => 'LAN',
							  ''	=> 'router'});
	       match_ip('', $re);

	   returns 'router' instead of 'LAN'.

       match_ip($ipaddr, $regexp)
	   Given a single IP address as	a string of the	form "aaa.bbb.ccc.ddd"
	   and a regular expression string (typically the output of
	   create_iprange_regexp()), this function returns a specified value
	   (typically 1) if the	IP is in one of	the ranges, or "undef" if no
	   ranges match.

	   See create_ipranges_regexp()	for more details about the return
	   value of this function.

	   WARNING: This function does no checking for validity	of the IP

       There are several other CPAN modules that perform a similar function.
       This one	is comparable to or faster than	the other ones that I've found
       and tried.  Here	is a synopsis of those others:

       Optimized for speed by taking a "source filter" approach.  That is, it
       modifies	your source code at run	time, kind of like a C preprocessor.
       A huge limitation is that the IP	ranges must be hard-coded into your

       (Also released as Net::IP::CMatch)

       Optimized for speed by doing the	match in C instead of in Perl.	This
       module loses efficiency,	however, because the IP	ranges must be re-
       parsed every invocation.

       Uses Net::IP::Match::XS to implement a range-to-name map.

       I ran a series of test on a Mac G5 with Perl 5.8.6 to compare this
       module to Net::IP::Match::XS.  The tests	are intended to	be a realistic
       net filter case:	100,000	random IP addresses tested against a number of
       semi-random IP ranges.  Times are in seconds.

	   Networks: 1,	IPs: 100000
	   Test	name		  | Setup time | Run time | Total time
	   Net::IP::Match::XS	  |    0.000   |  0.415	  |    0.415
	   Net::IP::Match::Regexp |    0.001   |  1.141	  |    1.141

	   Networks: 10, IPs: 100000
	   Test	name		  | Setup time | Run time | Total time
	   Net::IP::Match::XS	  |    0.000   |  0.613	  |    0.613
	   Net::IP::Match::Regexp |    0.003   |  1.312	  |    1.316

	   Networks: 100, IPs: 100000
	   Test	name		  | Setup time | Run time | Total time
	   Net::IP::Match::XS	  |    0.000   |  2.621	  |    2.622
	   Net::IP::Match::Regexp |    0.024   |  1.381	  |    1.405

	   Networks: 1000, IPs:	100000
	   Test	name		  | Setup time | Run time | Total time
	   Net::IP::Match::XS	  |    0.003   | 20.910	  |   20.912
	   Net::IP::Match::Regexp |    0.203   |  1.514	  |    1.717

       This test indicates that	::Regexp is faster than	::XS when you have
       more than about 50 IP ranges to test.  The relative run time does not
       vary significantly with the number of singe IP to match,	but with a
       small number of IP addresses to match, the setup	time begins to
       dominate, so ::Regexp loses in that scenario.

       To reproduce the	above benchmarks, run the following command in the
       distribution directory:

	  perl benchmark/ -s -n 1,10,100,1000 -i 100000

       The speed of this module	comes from the short-circuit nature of regular
       expressions.  The setup function	turns all of the IP ranges into	binary
       strings,	and mixes them into a regexp with "|" choices between ones and
       zeros.  This regexp can then be passed to the match function.  When an
       unambiguous match is found, the regexp sets a variable (via the regexp
       $LAST_REGEXP_CODE_RESULT, aka $^R, feature) and terminates.  That
       variable	becomes	the return value for the match,	typically a true

       Here's an example of the	regexp for a single range, that	of the subnet:

	   print create_iprange_regexp('')'
	   # ^41101000111111001101000110(?{'1'})

       If we add another range,	say a NAT LAN, we get:

	   print create_iprange_regexp('', '')'
	   # ^4110(?>0000010101000(?{'1'})|1000111111001101000110(?{'1'}))

       Note that for a 192.168.x.x address, the	regexp checks at most the
       first 16	bits (1100000010101000)	whereas	for a 209.249.163.x address,
       it goes out to 25 bits (1101000111111001101000110).  The	cool part is
       that for	an IP address that starts in the lower half (say
       only needs to check the first bit (0) to	see that the regexp won't

       If mapped return	values are specified for the ranges, they get embedded
       into the	regexp like so:

	   print create_iprange_regexp({'' => '',
					'' => 'localhost'})'
	   # ^4110(?>0000010101000(?{'localhost'})|1000111111001101000110(?{''}))

       This could be implemented in C to be even faster.  In C,	it would
       probably	be better to use a binary tree instead of a regexp.  However,
       a goal of this module is	to create a serializable representation	of the
       range data, and the regexp is perfect for that.	So, I'll probably
       never do	a C version.

       Because this module relies on the "(?{ code })" feature of regexps, it
       won't work on old Perl versions.	 I've successfully tested this module
       on Perl 5.6.0, 5.8.1 and	5.8.6.	In theory, the code regexp feature
       should work in 5.005, but I've used "our" and the like, so it won't
       work there.  I don't have a 5.005 to test anyway...

       Update from Oct 2008: I no longer keep a	5.6.0 around, so I rely	on to tell me when this breaks for older Perls.

       This module has 100% code coverage in its regression tests, as reported
       by Devel::Cover via "perl Build testcover".

       This module passes Perl Best Practices guidelines, as enforced by
       Perl::Critic v1.093.

       Chris Dolan

       I originally developed this module at Clotho Advanced Media Inc.	 Now I
       maintain	it in my spare time.

ACKNOWLEDGMENTS inspired and subsidized development of	the original
       version of this module.

       Chris Snyder contributed	a valuable and well-written bug	report about
       handling	missing	mask values.

       Michael Aronsen of suggested the depth-first matching

perl v5.32.1			  2008-10-04	     Net::IP::Match::Regexp(3)


Want to link to this manual page? Use this URL:

home | help