Jykke has asked for the wisdom of the Perl Monks concerning the following question:

What kind of structure would be the best for me when I need to store company and multiple ip address ranges per company? I'm thinking of TFTP server which can serve files based on incoming reguest source ip address. After get request received, I need to search my structure to find out which company is using that ip address and select the right source directory for that user. I have 1 config file per company including multiple ip ranges like 10.0.0.0;10.0.0.255,10.1.1.0;10.1.1.255,... All ip address ranges will be unigue, so there won't be overlapping. I can read all those config files at startup. Thanks in advance, Jykke.

Replies are listed 'Best First'.
Re: Best structure for range find?
by Masem (Monsignor) on Oct 30, 2001 at 01:54 UTC
    Use a hash; very fast lookups, and you can easily deal with cases in which the IP isn't in your structure. Since you will most likely not be covereing the entire IPv4 address space, you don't need to worry about memory storage concerns.
    # assume line is: # ip;ip;ip;.. datum # my %ip_hash; while ( my $line = <FILE> ) { my ( $ip, $datum ) = split /\s*/, $line; my @ip = split /\;/, $ip; foreach my $ipa ( @ip ) { $ip_hash{ $ipa } = $datum; } } # Later.. if ( exists $ip_hash{ $query_ip } ) { do_something_with_datum( $ip_hash{ $query_ip } ); } else { do_not_found_action( $query_ip ); }

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    "I can see my house from here!"
    It's not what you know, but knowing how to find it if you don't know that's important

Re: Best structure for range find?
by traveler (Parson) on Oct 30, 2001 at 04:05 UTC
    It would seem that Array::IntSpan::IP will do exactly what you want. Just put the company names in for the network names.

    HTH, --traveler

      Jiihaaa! Array::IntSpan::IP is just what I need! :). - Jykke -
Re: Best structure for range find?
by DrManhattan (Chaplain) on Oct 30, 2001 at 06:00 UTC
    I'm a big fan of Net::Netmask. I maintain a simple database of my company's netblocks and which city they belong in by building an array of Net::Netmask/city combinations. Assuming the netblocks/cities are stored in a pipe-delimited text file like so:
    12.34.56.78/16|City A 90.12.34.56/19|City B etc
    The code looks something like this:
    # Build an array. Each element of the array is a reference # to a two-element array consisting of a Net::Netmask object # and a city name my @blocks; while (<FH>) { my ($block, $city) = split /\|/; push @blocks, [Net::Netmask->new($block), $city]; }
    Then to check which city an ip address belongs in, I just do a sequential search:
    sub find_city { my $address = shift; foreach my $arrayref (@blocks) { # Extra variables assigned for clarity my $netblock = $arrayref->[0]; my $city = $arrayref->[1]; if ($netblock->match($address)) { return $city; } } }
    The code could be written faster by using a better search algorithm [1], but a sequential search of my company's 2100+ netblocks takes negligible time so I haven't seen fit to recode it.

    [1] We don't have anything larger than a /16, so hash buckets keyed on the first two octets could speed things up a bit. Something like this:

    $blocks{10}{0} = [ [Net::Netmask->new("10.0.0.0/24"), "City A"], [Net::Netmask->new("10.0.1.0/24"), "City B"] ]; $blocks{10}{1} = [ [Net::Netmask->new("10.1.0.0/24"), "City C"], [Net::Netmask->new("10.1.1.0/24"), "City D"] ];

    -Matt

      Answers are already given but I was driven (tye made me do it :) to add lookup functionality to Net::CIDR::Lite(0.07). There's two ways to look up, looking up a single ip address in a Net::CIDR object, and looking up many ip addresses in several labeled Net::CIDR::Lite objects (kind of like Array::IntSpan, but looking up everything at once is more efficient than looking up one thing at a time since my searches are sequential, i.e. O(n), except for an initial sort which is cached for subsequent searches, and Array::IntSpan is O(log n) (but for small 'n' it doesn't really matter, eh?)).

      Array::IntSpan has already been chosen as the 'favoured' answer by the OP (and I tend to agree for his particular needs), but if you ever need IPv6 support...try out Net::CIDR::Lite.