Net-Patricia-1.23/000755 000765 000024 00000000000 15003371435 014206 5ustar00tobezstaff000000 000000 Net-Patricia-1.23/demo/000755 000765 000024 00000000000 15003371434 015131 5ustar00tobezstaff000000 000000 Net-Patricia-1.23/Patricia.xs000644 000765 000024 00000023760 15002650744 016330 0ustar00tobezstaff000000 000000 #ifdef __cplusplus extern "C" { #endif #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #ifdef __cplusplus } #endif /* memory for libpatricia must not be managed with redefined calloc() and * free() routines from XSUB.h. Otherwise, DESTROY crashes on Perl releases * such as Strawberry Perl that use their own memory management. */ #undef calloc #undef free #include #include #include "libpatricia/patricia.h" /* frozen stuff is frozen in network byte order */ struct frozen_node { int32_t l_index; int32_t r_index; int32_t d_index; uint16_t bitlen; /* bit 0x8000 indicates presence of prefix */ uint16_t family; uint8_t address[16]; } __attribute__((__packed__)); struct frozen_header { uint32_t magic; #define FROZEN_MAGIC 0x4E655061 /* NePa */ uint8_t major; #define FROZEN_MAJOR 0 uint8_t minor; #define FROZEN_MINOR 0 uint16_t maxbits; int32_t num_total_node; int32_t num_active_node; } __attribute__((__packed__)); struct frozen_patricia { struct frozen_header header; struct frozen_node node[1]; } __attribute__((__packed__)); #define Fill_Prefix(p,f,a,b,mb) \ do { \ if (b < 0 || b > mb) \ croak("invalid key"); \ memcpy(&p.add.sin, a, (mb+7)/8); \ p.family = f; \ p.bitlen = b; \ p.ref_count = 0; \ } while (0) static void deref_data(SV *data) { SvREFCNT_dec(data); data = NULL; } static size_t patricia_walk_inorder_perl(patricia_node_t *node, SV *coderef) { dSP; size_t n = 0; if (node->l) { n += patricia_walk_inorder_perl(node->l, coderef); } if (node->prefix) { if (NULL != coderef) { PUSHMARK(SP); XPUSHs(sv_mortalcopy((SV *)node->data)); PUTBACK; perl_call_sv(coderef, G_VOID|G_DISCARD); SPAGAIN; } n++; } if (node->r) { n += patricia_walk_inorder_perl(node->r, coderef); } return n; } typedef patricia_tree_t *Net__Patricia; typedef patricia_node_t *Net__PatriciaNode; MODULE = Net::Patricia PACKAGE = Net::Patricia PROTOTYPES: ENABLE Net::Patricia _new(size) int size CODE: RETVAL = New_Patricia(size); OUTPUT: RETVAL void _add(tree, family, addr, bits, data) Net::Patricia tree int family char * addr int bits SV * data PROTOTYPE: $$$$$ PREINIT: prefix_t prefix; Net__PatriciaNode node; PPCODE: Fill_Prefix(prefix, family, addr, bits, tree->maxbits); node = patricia_lookup(tree, &prefix); if (NULL != node) { /* { */ if (node->data) { deref_data(node->data); } node->data = newSVsv(data); /* } */ PUSHs(data); } else { XSRETURN_UNDEF; } void _match(tree, family, addr, bits) Net::Patricia tree int family char * addr int bits PROTOTYPE: $$$$ PREINIT: prefix_t prefix; Net__PatriciaNode node; PPCODE: Fill_Prefix(prefix, family, addr, bits, tree->maxbits); node = patricia_search_best(tree, &prefix); if (NULL != node) { XPUSHs((SV *)node->data); } else { XSRETURN_UNDEF; } void _exact(tree, family, addr, bits) Net::Patricia tree int family char * addr int bits PROTOTYPE: $$$$ PREINIT: prefix_t prefix; Net__PatriciaNode node; PPCODE: Fill_Prefix(prefix, family, addr, bits, tree->maxbits); node = patricia_search_exact(tree, &prefix); if (NULL != node) { XPUSHs((SV *)node->data); } else { XSRETURN_UNDEF; } void _remove(tree, family, addr, bits) Net::Patricia tree int family char * addr int bits PROTOTYPE: $$$$ PREINIT: prefix_t prefix; Net__PatriciaNode node; PPCODE: Fill_Prefix(prefix, family, addr, bits, tree->maxbits); node = patricia_search_exact(tree, &prefix); if (NULL != node) { XPUSHs(sv_mortalcopy((SV *)node->data)); deref_data(node->data); patricia_remove(tree, node); } else { XSRETURN_UNDEF; } size_t climb(tree, ...) Net::Patricia tree PREINIT: patricia_node_t *node = NULL; size_t n = 0; SV *func = NULL; CODE: if (2 == items) { func = ST(1); } else if (2 < items) { croak("Usage: Net::Patricia::climb(tree[,CODEREF])"); } PATRICIA_WALK (tree->head, node) { if (NULL != func) { PUSHMARK(SP); XPUSHs(sv_mortalcopy((SV *)node->data)); PUTBACK; perl_call_sv(func, G_VOID|G_DISCARD); SPAGAIN; } n++; } PATRICIA_WALK_END; RETVAL = n; OUTPUT: RETVAL size_t climb_inorder(tree, ...) Net::Patricia tree PREINIT: size_t n = 0; SV *func = NULL; CODE: func = NULL; if (2 == items) { func = ST(1); } else if (2 < items) { croak("Usage: Net::Patricia::climb_inorder(tree[,CODEREF])"); } if (NULL != tree->head) { n = patricia_walk_inorder_perl(tree->head, func); } RETVAL = n; OUTPUT: RETVAL void STORABLE_freeze(tree, cloning) Net::Patricia tree SV * cloning PREINIT: patricia_node_t *node = NULL; struct frozen_header frozen_header; struct frozen_node *frozen_nodes, *frozen_node; size_t n = 0, i = 0, nd = 0; SV *frozen_patricia; PPCODE: if (SvTRUE(cloning)) XSRETURN_UNDEF; /* I do not know enough of patricia.c to * decide whether inactive nodes can * be present in a tree, and whether such * nodes can be skipped while copying, * so we copy everything and do not use * num_active_node here. */ PATRICIA_WALK_ALL (tree->head, node) { n++; } PATRICIA_WALK_END; if (n > 2147483646) croak("Net::Patricia::STORABLE_freeze: too many nodes"); frozen_header.magic = htonl(FROZEN_MAGIC); frozen_header.major = FROZEN_MAJOR; frozen_header.minor = FROZEN_MINOR; frozen_header.maxbits = htons((uint16_t)tree->maxbits); frozen_header.num_total_node = htonl(n); frozen_header.num_active_node = htonl(tree->num_active_node); frozen_patricia = newSVpv((char *)&frozen_header, sizeof(frozen_header)); XPUSHs(frozen_patricia); frozen_nodes = calloc(n, sizeof(struct frozen_node)); /* We use user1 field to store the index of each node * in the frozen_node array; it is okay since Net::Patricia * is not using it for anything anywhere else */ PATRICIA_WALK_ALL (tree->head, node) { node->user1 = (void *)(IV)i; frozen_node = &frozen_nodes[i]; frozen_node->l_index = htonl(-1); frozen_node->r_index = htonl(-1); frozen_node->bitlen = node->bit; if (node->prefix) { frozen_node->bitlen |= 0x8000; frozen_node->family = htons(node->prefix->family); if (tree->maxbits == 32) memcpy(&frozen_node->address, &node->prefix->add, 4); else memcpy(&frozen_node->address, &node->prefix->add, 16); } frozen_node->bitlen = htons(frozen_node->bitlen); if (node->data) { frozen_node->d_index = htonl(nd); nd++; XPUSHs(sv_2mortal(newRV_inc((SV *)node->data))); } else { frozen_node->d_index = htonl(-1); } if (node->parent && node->parent->l == node) { frozen_nodes[(IV)node->parent->user1].l_index = htonl(i); } else if (node->parent && node->parent->r == node) { frozen_nodes[(IV)node->parent->user1].r_index = htonl(i); } i++; } PATRICIA_WALK_END; sv_catpvn(frozen_patricia, (char*)frozen_nodes, n*sizeof(struct frozen_node)); free(frozen_nodes); void STORABLE_thaw(tobj, cloning, serialized, ...) SV * tobj SV * cloning SV * serialized; PREINIT: struct frozen_patricia *frozen_patricia; struct frozen_node *frozen_node; struct _patricia_tree_t *tree; patricia_node_t *node = NULL, *child, **fixup; int n, n_calculated, i, d_index, l_index, r_index; STRLEN len; PPCODE: if (SvTRUE(cloning)) XSRETURN_UNDEF; tree = calloc(1, sizeof(*tree)); frozen_patricia = (struct frozen_patricia*)SvPV(serialized, len); if (ntohl(frozen_patricia->header.magic) != FROZEN_MAGIC) croak("Net::Patricia::STORABLE_thaw: magic mismatch"); if (frozen_patricia->header.major != FROZEN_MAJOR) croak("Net::Patricia::STORABLE_thaw: major mismatch"); if (frozen_patricia->header.minor != FROZEN_MINOR) croak("Net::Patricia::STORABLE_thaw: minor mismatch"); tree->maxbits = ntohs(frozen_patricia->header.maxbits); tree->num_active_node = ntohl(frozen_patricia->header.num_active_node); tree->head = NULL; n = ntohl(frozen_patricia->header.num_total_node); n_calculated = (len - sizeof(frozen_patricia->header)) / sizeof(struct frozen_node); if (n_calculated < n) croak("Net::Patricia::STORABLE_thaw: size mismatch"); fixup = calloc(n, sizeof(patricia_node_t *)); for (i = 0; i < n; i++) { node = calloc(1, sizeof(*node)); memset(node, 0, sizeof(*node)); frozen_node = &frozen_patricia->node[i]; node->bit = ntohs(frozen_node->bitlen) & ~0x8000; d_index = ntohl(frozen_node->d_index); if (d_index >= 0) node->data = newSVsv(SvRV(ST(3+d_index))); if (ntohs(frozen_node->bitlen) & 0x8000) { node->prefix = calloc(1, sizeof(*node->prefix)); node->prefix->bitlen = node->bit; node->prefix->family = ntohs(frozen_node->family); #ifndef HAVE_IPV6 if (tree->maxbits > 32) croak("Net::Patricia::STORABLE_thaw: IPv6 is not supported by Net::Patricia on this machine"); #endif if (tree->maxbits == 32) { memcpy(&node->prefix->add, &frozen_node->address, 4); } else memcpy(&node->prefix->add, &frozen_node->address, 16); node->prefix->ref_count = 1; } fixup[i] = node; } /* Fix pointers up. */ if (n) tree->head = fixup[0]; for (i = 0; i < n; i++) { frozen_node = &frozen_patricia->node[i]; node = fixup[i]; l_index = ntohl(frozen_node->l_index); if (l_index >= 0) { child = fixup[l_index]; child->parent = node; node->l = child; } r_index = ntohl(frozen_node->r_index); if (r_index >= 0) { child = fixup[r_index]; child->parent = node; node->r = child; } } free(fixup); sv_setiv((SV*)SvRV(tobj), PTR2IV(tree)); XSRETURN_EMPTY; void DESTROY(tree) Net::Patricia tree CODE: Destroy_Patricia(tree, (void_fn_t)deref_data); Net-Patricia-1.23/Changes000644 000765 000024 00000007571 15003371025 015506 0ustar00tobezstaff000000 000000 Revision history for Perl extension Net::Patricia This module was originally called Tree::PatriciaTrie::IPv4 TODO - add POD describing Graham Barr's API improvements 1.23 2025-04-27 11:00:00 CEST - do not link to the nsl library (CPAN RT 124088) - add test_dynamic and test_static targets (CPAN RT 117929) - test with 0.0.0.0/0 instead of 0/0 (CPAN RT 146613) - support compilation with GCC 15 - support Windows 1.22 2013-10-15 21:38:53 CEST - update FSF address (CPAN RT 88585) - add IPv6 examples to the synopsis - mention Storable support - no code changes 1.21 2013-09-4 12:47:00 CEST - MRT https://github.com/deepfield/MRT has changed license to one compatible with GPL, so libpatricia/ content is now also GPL-compatible - no code changes 1.20 2012-10-8 22:46:13 CEST - Add Storable hooks 1.019 2010-11-14 12:15 - Improve parameter checking 1.018 2010-10-23 14:02 - Remove inet_pton() - version Test::More requirement 1.017 2010-05-23 17:30 - miscellaneous bug fixes - add additional hybrid methods if Net::CIDR::Lite is present - corrections to PM_PREREQ 1.016 2010-02-14 11:16 - miscellaneous bug fixes - rewrote Makefile to simplify it 1.015 2009-01-25 19:07 - fixed bugs #14244, #20219 - added _integer methods to add/remove entries - add AF_INET6 support 1.014 2005-12-08 18:05 - fixed the "climb_inorder" item in the POD 1.013 2005-12-08 17:36 - fixed a perl stack handling bug in the previous release. 1.012 2005-12-07 14:59 - added climb_inorder method requested by George Michaelson. - included for u_* definitions on FreeBSD 5 as suggested by Brian McDonald. - tested for windows environment to include winsock - patched libpatricia to get u_* types on FreeBSD 5.x from Brian McDonald 1.011 2000-10-30 15:13 - applied api-patch from Graham Barr - added description of match_string to BUGS section of POD from John Payne 1.010 2000-10-30 09:51 - applied patch from Graham Barr which contained a memory leak fix and removed the unnecessary Exporter and AutoLoader stuff. 1.009 2000-10-23 12:45 - changed add_string and remove_string methods so that they croak if ascii2prefix doesn't grok the key string value. Previously, bad key values would cause perl to abort with this error: perl: patricia.c:645: patricia_lookup: Assertion `prefix' failed. which was reported by Freddy Frouin 1.008 2000-10-18 13:46 - change XS code to just store a pointer to user data in the Patricia Trie node, rather than a pointer to a perl reference to that user data as was done previously. - improved POD 1.007 2000-10-18 11:32 - renamed from Net::PatriciaTrie to Net::Patricia 1.006 2000-10-12 00:07 - bug fix to the climb method which previously would sometimes cause a Memory Fault when a CODEREF argument was supplied. - added demo sub-dir with some info about libpatricia C API 1.005 2000-10-04 14:18 - added climb method 1.004 2000-09-29 14:16 - added POD - added the ability to store user data in Patricia Trie nodes - added more match methods - added remove method - cleaned up copyright stuff in prep distribution 1.003 2000-09-28 19:24 - renamed from Tree::PatriciaTrie::IPv4 to Net::PatriciaTrie - suppressed error on "make test" of patricialib - prototyped some functions used by "PatriciaTrie.xs" to avoid complaints about making "pointer from integer without a cast" 1.002 2000-09-28 15:23 - last cut named Tree::PatriciaTrie::IPv4 1.001 2000-09-23 09:58 - this one works, tested in production use w/pre-release FlowScan-1.004 0.02 2000-09-23 02:21 - works(?) but leaks memory in "patricialib" 0.01 2000-09-22 23:00:11 - original version; created by h2xs 1.18 Net-Patricia-1.23/MANIFEST000644 000765 000024 00000000517 15003371435 015342 0ustar00tobezstaff000000 000000 Changes COPYING demo/demo.c libpatricia/copyright libpatricia/credits.txt libpatricia/Makefile.PL libpatricia/patricia.c libpatricia/patricia.h Makefile.PL MANIFEST This list of files META.yml Patricia.pm Patricia.xs README t/01everything.t typemap META.json Module JSON meta-data (added by MakeMaker) Net-Patricia-1.23/Patricia.pm000644 000765 000024 00000044254 15003371145 016307 0ustar00tobezstaff000000 000000 # Net::Patricia - Patricia Trie perl module for fast IP address lookups # Copyright (C) 2000-2005 Dave Plonka # Copyright (C) 2009 Dave Plonka & Philip Prindeville # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # Dave Plonka # Philip Prindeville # Anton Berezin # Andreas Vögele package Net::Patricia; use strict; use warnings; require 5.008; use version; use Carp; use vars qw($VERSION @ISA @EXPORT); use Socket qw(AF_INET AF_INET6); BEGIN { require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT = qw(AF_INET AF_INET6); } '$Revision: 1.23 $' =~ m/(\d+)\.(\d+)((_\d+)|)/ && ( $VERSION = "$1.$2$3"); bootstrap Net::Patricia $VERSION; sub new { my ($class, $type) = @_; $type ||= AF_INET; if ($type == AF_INET) { return bless _new(32), 'Net::Patricia::AF_INET'; } if ($type == AF_INET6) { return bless _new(128), 'Net::Patricia::AF_INET6'; } croak "new: unimplemented type"; } ## ## Compat functions ## sub _ip_bits { my ($self, $str) = @_; my $bits; if (ref ($self) eq 'Net::Patricia::AF_INET6') { $bits = ($str =~ s|/(\d+)$||) ? $1 : 128; } else { $bits = ($str =~ s|/(\d+)$||) ? $1 : 32; } ($str,$bits); } sub add_string { croak "add_string: wrong number of args" if (@_ < 2 || @_ > 3); my ($self,$str,$data) = @_; $data = $str unless @_ > 2; $self->add($self->_ip_bits($str),$data); } sub match_string { croak "match_string: wrong number of args" if (@_ != 2); my ($self,$str) = @_; $self->match($self->_ip_bits($str)) } sub match_exact_string { croak "match_exact_string: wrong number of args" if (@_ != 2); my ($self,$str) = @_; $self->exact($self->_ip_bits($str)) } sub match_exact_integer { shift->exact_integer(@_) } sub remove_string { croak "remove_string: wrong number of args" if (@_ != 2); my ($self,$str) = @_; $self->remove($self->_ip_bits($str)) } BEGIN { eval { my $class = 'Net::CIDR::Lite'; eval "require $class"; }; last if (@_); sub add_cidr { croak "add_cidr: wrong number of args" if (@_ != 3); my ($self, $range, $data) = @_; my $cidr = Net::CIDR::Lite->new(); $cidr->add_range($range); my @list = (); for ($cidr->list()) { push(@list, $_) if ($self->add_string($_, $data)); } @list; } sub remove_cidr { croak "remove_cidr: wrong number of args" if (@_ != 2); my ($self, $range) = @_; my $cidr = Net::CIDR::Lite->new(); $cidr->add_range($range); my @list = (); for ($cidr->list()) { push(@list, $_) if ($self->remove_string($_)); } @list; } } ## ## AF_INET ## package Net::Patricia::AF_INET; use Carp; use Socket qw(AF_INET inet_aton inet_ntoa); use vars qw(@ISA @EXPORT); BEGIN { require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader Net::Patricia); @EXPORT = qw(AF_INET); } sub add { croak "add: wrong number of args" if (@_ < 2 || @_ > 4); my ($self, $ip, $bits, $data) = @_; $data = (defined $bits ? "$ip/$bits" : $ip) if (@_ < 4); my $packed = inet_aton($ip); croak("invalid key") unless (defined $packed); $bits = 32 if (@_ < 3); $self->SUPER::_add(AF_INET, $packed, $bits, $data); } sub add_integer { croak "add_integer: wrong number of args" if (@_ < 2 || @_ > 4); my ($self, $num, $bits, $data) = @_; my $packed = pack("N", $num); my $ip = inet_ntoa($packed); croak("invalid address") unless (defined $ip); $data = (defined $bits ? "$ip/$bits" : $ip) if (@_ < 4); $bits = 32 if (@_ < 3); $self->SUPER::_add(AF_INET, $packed, $bits, $data); } sub match_integer { croak "match_integer: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $num, $bits) = @_; $bits = 32 if (@_ < 3); $self->SUPER::_match(AF_INET, pack("N",$num), $bits); } sub exact_integer { croak "exact_integer: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $num, $bits) = @_; $bits = 32 if (@_ < 3); $self->SUPER::_exact(AF_INET, pack("N",$num), $bits); } sub match { croak "match: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $ip, $bits) = @_; my $packed = inet_aton($ip); croak("invalid key") unless (defined $packed); $bits = 32 if (@_ < 3); $self->SUPER::_match(AF_INET, $packed, $bits); } sub exact { croak "exact: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $ip, $bits) = @_; my $packed = inet_aton($ip); croak("invalid key") unless (defined $packed); $bits = 32 if (@_ < 3); $self->SUPER::_exact(AF_INET, $packed, $bits); } sub remove { croak "remove: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $ip, $bits) = @_; my $packed = inet_aton($ip); croak("invalid key") unless (defined $packed); $bits = 32 if (@_ < 3); $self->SUPER::_remove(AF_INET, $packed, $bits); } sub remove_integer { croak "remote_integer: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $num, $bits) = @_; $bits = 32 if (@_ < 3); $self->SUPER::_remove(AF_INET, pack("N",$num), $bits); } ## ## AF_INET6 ## package Net::Patricia::AF_INET6; use Carp; use Socket qw(AF_INET6); use Socket6 qw(inet_pton inet_ntop); use vars qw(@ISA @EXPORT); BEGIN { require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader Net::Patricia); @EXPORT = qw(AF_INET6); } sub add { croak "add: wrong number of args" if (@_ < 2 || @_ > 4); my ($self, $ip, $bits, $data) = @_; $data = (defined $bits ? "$ip/$bits" : $ip) if (@_ < 3); my $packed = inet_pton(AF_INET6, $ip); croak("invalid key") unless (defined $packed); $bits = 128 if (@_ < 4); $self->SUPER::_add(AF_INET6, $packed, $bits, $data); } sub add_integer { croak "add_integer: wrong number of args" if (@_ < 2 || @_ > 4); my ($self, $num, $bits, $data) = @_; my $packed = pack("N", $num); my $ip = inet_ntop(AF_INET6, $packed); croak("invalid address") unless (defined $ip); $data = (defined $bits ? "$ip/$bits" : $ip) if (@_ < 3); $bits = 128 if (@_ < 4); $self->SUPER::_add(AF_INET6, $packed, $bits, $data); } sub match_integer { croak "match_integer: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $num, $bits) = @_; $bits = 128 if (@_ < 3); $self->SUPER::_match(AF_INET6, pack("N",$num), $bits); } sub exact_integer { croak "exact_integer: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $num, $bits) = @_; $bits = 128 if (@_ < 3); $self->SUPER::_exact(AF_INET6, pack("N",$num), $bits); } sub match { croak "match: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $ip, $bits) = @_; my $packed = inet_pton(AF_INET6, $ip); croak("invalid key") unless (defined $packed); $bits = 128 if (@_ < 3); $self->SUPER::_match(AF_INET6, $packed, $bits); } sub exact { croak "exact: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $ip, $bits) = @_; my $packed = inet_pton(AF_INET6, $ip); croak("invalid key") unless (defined $packed); $bits = 128 if (@_ < 3); $self->SUPER::_exact(AF_INET6, $packed, $bits); } sub remove { croak "remove: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $ip, $bits) = @_; my $packed = inet_pton(AF_INET6, $ip); croak("invalid key") unless (defined $packed); $bits = 128 if (@_ < 3); $self->SUPER::_remove(AF_INET6, $packed, $bits); } sub remove_integer { croak "remote_integer: wrong number of args" if (@_ < 2 || @_ > 3); my ($self, $num, $bits) = @_; $bits = 128 if (@_ < 3); $self->SUPER::_remove(AF_INET6, pack("N",$num), $bits); } 1; __END__ =head1 NAME Net::Patricia - Patricia Trie perl module for fast IP address lookups =head1 SYNOPSIS use Net::Patricia; my $pt = Net::Patricia->new; my $user_data; $pt->add_string('127.0.0.0/8', \$user_data); $pt->match_string('127.0.0.1'); $pt->match_exact_string('127.0.0.0'); $pt->match_integer(2130706433); # 127.0.0.1 $pt->match_exact_integer(2130706432, 8); # 127.0.0.0 $pt->remove_string('127.0.0.0/8'); $pt->climb(sub { print "climbing at node $_[0]\n" }); undef $pt; # automatically destroys the Patricia Trie # IPv6 support: $pt = Net::Patricia->new(AF_INET6); $pt->add_string('2001:db8::/32'); $pt->add_string('2001:db8:0:dead::/64'); $pt->add_string('2001:db8:0:beef::/64'); $pt->climb(sub { print "climbing at node $_[0]\n" }); print $pt->match_string('2001:db8:0:dead::1'), "\n"; # IPv4-mapped IPv6 addresses: $pt->add_string('::ffff:0:0/96'); for my $cidr (qw( 192.0.2.0/24 192.0.2.0/25 192.0.2.128/25 )) { my($ip, $len) = split(m|/|, $cidr); $pt->add_string("::ffff:$ip/" . (96+(defined($len)? $len : 32)), $cidr); } $pt->climb(sub { print "climbing at node $_[0]\n" }); print $pt->match_string("::ffff:" . "192.0.2.129"), "\n"; =head1 DESCRIPTION This module uses a Patricia Trie data structure to quickly perform IP address prefix matching for applications such as IP subnet, network or routing table lookups. The data structure is based on a radix tree using a radix of two, so sometimes you see patricia implementations called "radix" as well. The term "Trie" is derived from the word "retrieval" but is pronounced like "try". Patricia stands for "Practical Algorithm to Retrieve Information Coded as Alphanumeric", and was first suggested for routing table lookups by Van Jacobsen. Patricia Trie performance characteristics are well-known as it has been employed for routing table lookups within the BSD kernel since the 4.3 Reno release. The BSD radix code is thoroughly described in "TCP/IP Illustrated, Volume 2" by Wright and Stevens and in the paper ``A Tree-Based Packet Routing Table for Berkeley Unix'' by Keith Sklower. =head1 METHODS =over 4 =item B - create a new Net::Patricia object $pt = Net::Patricia->new; This is the class' constructor - it returns a C object upon success or undef on failure. The constructor takes an optional argument (of AF_INET or AF_INET6, defaulting to the former), and creates a tree with address and mask values of that type as keys. The C object will be destroyed automatically when there are no longer any references to it. =item B $pt->add_string(key_string[,user_data]); The first argument, key_string, is a network or subnet specification in canonical form, e.g. "10.0.0.0/8", where the number after the slash represents the number of bits in the netmask. If no mask width is specified, the longest possible mask is assumed, i.e. 32 bits for AF_INET addresses. The second argument, user_data, is optional. If supplied, it should be a SCALAR value (which may be a perl reference) specifying the user data that will be stored in the Patricia Trie node. Subsequently, this value will be returned by the match methods described below to indicate a successful search. Remember that perl references and objects are represented as SCALAR values and therefore the user data can be complicated data objects. If no second argument is passed, the key_string will be stored as the user data and therfore will likewise be returned by the match functions. On success, this method returns the user_data passed as the second argument or key_string if no user data was specified. It returns undef on failure. =item B $pt->match_string(key_string); This method searches the Patricia Trie to find a matching node, according to normal subnetting rules for the address and mask specified. The key_string argument is a network or subnet specification in canonical form, e.g. "10.0.0.0/8", where the number after the slash represents the number of bits in the netmask. If no mask width value is specified, the longest mask is assumed, i.e. 32 bits for AF_INET addresses. If a matching node is found in the Patricia Trie, this method returns the user data for the node. This method returns undef on failure. =item B $pt->match_exact_string(key_string); This method searches the Patricia Trie to find a matching node. Its semantics are exactly the same as those described for C except that the key must match a node exactly. I.e. it is not sufficient that the address and mask specified merely falls within the subnet specified by a particular node. =item B $pt->match_integer(integer[,mask_bits]); This method searches the Patricia Trie to find a matching node, according to normal subnetting rules for the address and mask specified. Its semantics are similar to those described for C except that the key is specified using an integer (i.e. SCALAR), such as that returned by perl's C function for values converted using the "N" (network-ordered long). Note that this argument is not a packed network-ordered long. Just to be completely clear, the integer argument should be a value of the sort produced by this code: use Socket; $integer = unpack("N", inet_aton("10.0.0.0")); =item B $pt->match_exact_integer(integer[,mask_bits]); This method searches the Patricia Trie to find a matching node. Its semantics are exactly the same as C except that the key must match a node exactly. I.e. it is not sufficient that the address and mask specified merely falls within the subnet specified by a particular node. =item B $pt->remove_string(key_string); This method removes the node which exactly matches the the address and mask specified from the Patricia Trie. If the matching node is found in the Patricia Trie, it is removed, and this method returns the user data for the node. This method returns undef on failure. =item B $pt->climb([CODEREF]); This method climbs the Patricia Trie, visiting each node as it does so. It performs a non-recursive, "preorder" traversal. The CODEREF argument is optional. It is a perl code reference used to specify a user-defined subroutine to be called when visiting each node. The node's user data will be passed as the sole argument to that subroutine. This method returns the number of nodes successfully visited while climbing the Trie. That is, without a CODEREF argument, it simply counts the number of nodes in the Patricia Trie. Note that currently the return value from your CODEREF subroutine is ignored. In the future the climb method may return the number of times your subroutine returned non-zero, as it is called once per node. So, if you are currently relying on the climb return value to accurately report a count of the number of nodes in the Patricia Trie, it would be prudent to have your subroutine return a non-zero value. This method is called climb() rather than walk() because climbing trees (and therfore tries) is a more popular pass-time than walking them. =item B $pt->climb_inorder([CODEREF]); This method climbs the Patricia Trie, visiting each node in order as it does so. That is, it performs an "inorder" traversal. The CODEREF argument is optional. It is a perl code reference used to specify a user-defined subroutine to be called when visiting each node. The node's user data will be passed as the sole argument to that subroutine. This method returns the number of nodes successfully visited while climbing the Trie. That is, without a CODEREF argument, it simply counts the number of nodes in the Patricia Trie. Note that currently the return value from your CODEREF subroutine is ignored. In the future the climb method may return the number of times your subroutine returned non-zero, as it is called once per node. So, if you are currently relying on the climb return value to accurately report a count of the number of nodes in the Patricia Trie, it would be prudent to have your subroutine return a non-zero value. This method is called climb() rather than walk() because climbing trees (and therfore tries) is a more popular pass-time than walking them. =back =head2 Serialization Net::Patricia trees, unlike many classes with XS-level data, can be frozen and thawed using Storable. =head1 BUGS When passing a CODEREF argument to the climb method, the return value from your CODEREF subroutine is currently ignored. In the future the climb method may return the number of times your subroutine returned non-zero, as it is called once per node. So, if you are currently relying on the climb return value to accurately report a count of the number of nodes in the Patricia Trie, it would be prudent to have your subroutine return a non-zero value. =head1 AUTHOR Dave Plonka Philip Prindeville Anton Berezin Andreas Vögele Copyright (C) 2000-2005 Dave Plonka. Copyright (C) 2009 Dave Plonka & Philip Prindeville. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This product includes software developed by the University of Michigan, Merit Network, Inc., and their contributors. See the copyright file in the patricialib sub-directory of the distribution for details. patricialib, the C library used by this perl extension, is an extracted version of MRT's patricia code from radix.[ch], which was worked on by Masaki Hirabaru and Craig Labovitz. For more info on MRT see: https://github.com/deepfield/MRT The MRT patricia code owes some heritage to GateD's radix code, which in turn owes something to the BSD kernel. =head1 SEE ALSO perl(1), L, L, L, L. Tree::Radix and Net::RoutingTable are modules by Daniel Hagerty written entirely in perl, unlike this module. At the time of this writing, they are works-in-progress but may be available at: https://www.linnaean.org/~hag/ =cut Net-Patricia-1.23/t/000755 000765 000024 00000000000 15003371434 014450 5ustar00tobezstaff000000 000000 Net-Patricia-1.23/README000644 000765 000024 00000003160 15003370755 015072 0ustar00tobezstaff000000 000000 README for Net-Patricia ----------------------- Net::Patricia - Patricia Trie perl module for fast IP address lookups DESCRIPTION ----------- This module uses a Patricia Trie data structure to quickly perform IP address prefix matching for applications such as IP subnet, network or routing table lookups. The data structure is based on a radix tree using a radix of two, so sometimes you see patricia implementations called "radix" as well. The term "Trie" is derived from the word "retrieval" but is pronounced like "try". Patricia stands for "Practical Algorithm to Retrieve Information Coded as Alphanumeric", and was first suggested for routing table lookups by Van Jacobsen. Patricia Trie performance characteristics are well-known as it has been employed for routing table lookups within the BSD kernel since the 4.3 Reno release. The BSD radix code is thoroughly described in "TCP/IP Illustrated, Volume 2" by Wright and Stevens and in the paper ``A Tree-Based Packet Routing Table for Berkeley Unix'' by Keith Sklower. Availability ------------ Please find the latest version of Net::Patricia on CPAN: https://metacpan.org/dist/Net-Patricia Installation ------------ This package extracts, builds, installs in the usual fashion, i.e.: $ gunzip -c .tar.gz |tar xf - $ cd $ perl Makefile.PL $ make $ make test # make install Requirements ------------ * perl version 5 -- plonka@doit.wisc.edu https://pages.cs.wisc.edu/~plonka/ ARS:N9HZF Madison, WI Net-Patricia-1.23/PaxHeader/typemap000644 000765 000024 00000000210 14475524047 017565 xustar00tobezstaff000000 000000 30 mtime=1693886503.336546063 57 LIBARCHIVE.xattr.com.apple.provenance=AQAA8is7Wn3W9Yo 49 SCHILY.xattr.com.apple.provenance=ò+;Z}ÖõŠ Net-Patricia-1.23/typemap000644 000765 000024 00000000202 14475524047 015615 0ustar00tobezstaff000000 000000 Net::Patricia T_PTROBJ Net::PatriciaNode T_PTROBJ patricia_tree_t * T_PTROBJ patricia_node_t * T_PTROBJ in_addr_t T_U_LONG Net-Patricia-1.23/PaxHeader/COPYING000644 000765 000024 00000000210 14475524047 017216 xustar00tobezstaff000000 000000 30 mtime=1693886503.334820509 57 LIBARCHIVE.xattr.com.apple.provenance=AQAA8is7Wn3W9Yo 49 SCHILY.xattr.com.apple.provenance=ò+;Z}ÖõŠ Net-Patricia-1.23/COPYING000644 000765 000024 00000043076 14475524047 015266 0ustar00tobezstaff000000 000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. Net-Patricia-1.23/META.yml000644 000765 000024 00000001504 15003371434 015456 0ustar00tobezstaff000000 000000 --- abstract: 'Patricia Trie for fast IP address lookups' author: - 'Dave Plonka ' build_requires: ExtUtils::MakeMaker: '0' Storable: '0' Test::More: '0.88' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' license: open_source meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Net-Patricia no_index: directory: - t - inc requires: Carp: '0' Exporter: '0' Net::CIDR::Lite: '0.20' Socket: '0' Socket6: '0' version: '0' resources: bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=Net-Patricia repository: git://github.com/tobez/Net-Patricia.git version: '1.23' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Net-Patricia-1.23/Makefile.PL000644 000765 000024 00000003535 15003370755 016172 0ustar00tobezstaff000000 000000 use strict; use warnings; use 5.008; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. my %WriteMakefileArgs = ( 'NAME' => 'Net::Patricia', 'VERSION_FROM' => 'Patricia.pm', # finds $VERSION 'ABSTRACT' => 'Patricia Trie for fast IP address lookups', 'AUTHOR' => 'Dave Plonka ', 'LICENSE' => 'gpl_2', # and BSD two-clause (see below) 'MYEXTLIB' => 'libpatricia/libpatricia$(LIB_EXT)', 'PREREQ_PM' => { 'Carp' => 0, 'Exporter' => 0, 'Net::CIDR::Lite' => '0.20', 'Socket' => 0, 'Socket6' => 0, 'version' => 0, }, 'TEST_REQUIRES' => { 'Storable' => 0, 'Test::More' => '0.88', }, META_MERGE => { 'meta-spec' => {version => 2}, license => ['freebsd'], resources => { bugtracker => { web => 'http://rt.cpan.org/Public/Dist/Display.html?Name=Net-Patricia', }, repository => { type => 'git', web => 'https://github.com/tobez/Net-Patricia', url => 'git://github.com/tobez/Net-Patricia.git', }, }, }, ); my %FallbackPrereqs = ( %{$WriteMakefileArgs{PREREQ_PM}}, %{$WriteMakefileArgs{TEST_REQUIRES}}, ); unless (eval { ExtUtils::MakeMaker->VERSION('6.63_03') }) { delete $WriteMakefileArgs{TEST_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } unless (eval { ExtUtils::MakeMaker->VERSION(6.46) }) { delete $WriteMakefileArgs{META_MERGE}; } WriteMakefile(%WriteMakefileArgs); sub MY::postamble { ' $(MYEXTLIB): libpatricia/Makefile cd libpatricia && $(MAKE) $(PASTHRU) '; } Net-Patricia-1.23/libpatricia/000755 000765 000024 00000000000 15003371434 016470 5ustar00tobezstaff000000 000000 Net-Patricia-1.23/META.json000644 000765 000024 00000003013 15003371435 015624 0ustar00tobezstaff000000 000000 { "abstract" : "Patricia Trie for fast IP address lookups", "author" : [ "Dave Plonka " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "gpl_2", "freebsd" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Net-Patricia", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Carp" : "0", "Exporter" : "0", "Net::CIDR::Lite" : "0.20", "Socket" : "0", "Socket6" : "0", "version" : "0" } }, "test" : { "requires" : { "Storable" : "0", "Test::More" : "0.88" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "http://rt.cpan.org/Public/Dist/Display.html?Name=Net-Patricia" }, "repository" : { "type" : "git", "url" : "git://github.com/tobez/Net-Patricia.git", "web" : "https://github.com/tobez/Net-Patricia" } }, "version" : "1.23", "x_serialization_backend" : "JSON::PP version 4.16" } Net-Patricia-1.23/libpatricia/patricia.c000644 000765 000024 00000060704 15002650744 020442 0ustar00tobezstaff000000 000000 /* * $Id: patricia.c,v 1.7 2005/12/07 20:46:41 dplonka Exp $ * Dave Plonka * * This product includes software developed by the University of Michigan, * Merit Network, Inc., and their contributors. * * This file had been called "radix.c" in the MRT sources. * * I renamed it to "patricia.c" since it's not an implementation of a general * radix trie. Also I pulled in various requirements from "prefix.c" and * "demo.c" so that it could be used as a standalone API. */ static char copyright[] = "This product includes software developed by the University of Michigan, Merit" "Network, Inc., and their contributors."; /* inet_ntop() and inet_pton() require at least Windows Vista. */ #ifdef _WIN32 # undef WINVER # undef _WIN32_WINNT # define WINVER 0x0600 # define _WIN32_WINNT 0x0600 #endif #include /* assert */ #include /* isdigit */ #include /* errno */ #include /* sin */ #include /* NULL */ #include /* sprintf, fprintf, stderr */ #include /* free, atol, calloc */ #include /* memcpy, strchr, strlen */ #include /* BSD: for inet_addr */ #include /* BSD, Linux: for inet_addr */ #include /* BSD, Linux, Solaris: for inet_addr */ #include "patricia.h" #define Delete free /* { from prefix.c */ /* prefix_tochar * convert prefix information to bytes */ u_char * prefix_tochar (prefix_t * prefix) { if (prefix == NULL) return (NULL); return ((u_char *) & prefix->add.sin); } int comp_with_mask (void *addr, void *dest, u_int mask) { if ( /* mask/8 == 0 || */ memcmp (addr, dest, mask / 8) == 0) { int n = mask / 8; int m = ((-1) << (8 - (mask % 8))); if (mask % 8 == 0 || (((u_char *)addr)[n] & m) == (((u_char *)dest)[n] & m)) return (1); } return (0); } /* this allows imcomplete prefix */ int my_inet_pton (int af, const char *src, void *dst) { if (af == AF_INET) { int i, c, val; u_char xp[sizeof(struct in_addr)] = {0, 0, 0, 0}; for (i = 0; ; i++) { c = *src++; if (!isdigit (c)) return (-1); val = 0; do { val = val * 10 + c - '0'; if (val > 255) return (0); c = *src++; } while (c && isdigit (c)); xp[i] = val; if (c == '\0') break; if (c != '.') return (0); if (i >= 3) return (0); } memcpy (dst, xp, sizeof(struct in_addr)); return (1); #ifdef HAVE_IPV6 } else if (af == AF_INET6) { return (inet_pton (af, src, dst)); #endif /* HAVE_IPV6 */ } else { #ifndef NT errno = EAFNOSUPPORT; #endif /* NT */ return -1; } } #define PATRICIA_MAX_THREADS 16 /* * convert prefix information to ascii string with length * thread safe and (almost) re-entrant implementation */ char * prefix_toa2x (prefix_t *prefix, char *buff, int with_len) { if (prefix == NULL) return ("(Null)"); assert (prefix->ref_count >= 0); if (buff == NULL) { struct buffer { char buffs[PATRICIA_MAX_THREADS][48+5]; u_int i; } *buffp; # if 0 THREAD_SPECIFIC_DATA (struct buffer, buffp, 1); # else { /* for scope only */ static struct buffer local_buff; buffp = &local_buff; } # endif if (buffp == NULL) { /* XXX should we report an error? */ return (NULL); } buff = buffp->buffs[buffp->i++%PATRICIA_MAX_THREADS]; } if (prefix->family == AF_INET) { u_char *a; assert (prefix->bitlen <= sizeof(struct in_addr) * 8); a = prefix_touchar (prefix); if (with_len) { sprintf (buff, "%d.%d.%d.%d/%d", a[0], a[1], a[2], a[3], prefix->bitlen); } else { sprintf (buff, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]); } return (buff); } #ifdef HAVE_IPV6 else if (prefix->family == AF_INET6) { char *r; r = (char *) inet_ntop (AF_INET6, &prefix->add.sin6, buff, 48 /* a guess value */ ); if (r && with_len) { assert (prefix->bitlen <= sizeof(struct in6_addr) * 8); sprintf (buff + strlen (buff), "/%d", prefix->bitlen); } return (buff); } #endif /* HAVE_IPV6 */ else return (NULL); } /* prefix_toa2 * convert prefix information to ascii string */ char * prefix_toa2 (prefix_t *prefix, char *buff) { return (prefix_toa2x (prefix, buff, 0)); } /* prefix_toa */ char * prefix_toa (prefix_t * prefix) { return (prefix_toa2 (prefix, (char *) NULL)); } prefix_t * New_Prefix2 (int family, void *dest, int bitlen, prefix_t *prefix) { int dynamic_allocated = 0; int default_bitlen = sizeof(struct in_addr) * 8; #ifdef HAVE_IPV6 if (family == AF_INET6) { default_bitlen = sizeof(struct in6_addr) * 8; if (prefix == NULL) { prefix = calloc(1, sizeof (prefix_t)); dynamic_allocated++; } memcpy (&prefix->add.sin6, dest, sizeof(struct in6_addr)); } else #endif /* HAVE_IPV6 */ if (family == AF_INET) { if (prefix == NULL) { #ifndef NT prefix = calloc(1, sizeof (prefix4_t)); #else //for some reason, compiler is getting //prefix4_t size incorrect on NT prefix = calloc(1, sizeof (prefix_t)); #endif /* NT */ dynamic_allocated++; } memcpy (&prefix->add.sin, dest, sizeof(struct in_addr)); } else { return (NULL); } prefix->bitlen = (bitlen >= 0)? bitlen: default_bitlen; prefix->family = family; prefix->ref_count = 0; if (dynamic_allocated) { prefix->ref_count++; } /* fprintf(stderr, "[C %s, %d]\n", prefix_toa (prefix), prefix->ref_count); */ return (prefix); } prefix_t * New_Prefix (int family, void *dest, int bitlen) { return (New_Prefix2 (family, dest, bitlen, NULL)); } /* ascii2prefix */ prefix_t * ascii2prefix (int family, char *string) { u_long bitlen, maxbitlen = 0; char *cp; struct in_addr sin; #ifdef HAVE_IPV6 struct in6_addr sin6; #endif /* HAVE_IPV6 */ int result; char save[MAXLINE]; if (string == NULL) return (NULL); /* easy way to handle both families */ if (family == 0) { family = AF_INET; #ifdef HAVE_IPV6 if (strchr (string, ':')) family = AF_INET6; #endif /* HAVE_IPV6 */ } if (family == AF_INET) { maxbitlen = sizeof(struct in_addr) * 8; } #ifdef HAVE_IPV6 else if (family == AF_INET6) { maxbitlen = sizeof(struct in6_addr) * 8; } #endif /* HAVE_IPV6 */ if ((cp = strchr (string, '/')) != NULL) { bitlen = atol (cp + 1); /* *cp = '\0'; */ /* copy the string to save. Avoid destroying the string */ assert (cp - string < MAXLINE); memcpy (save, string, cp - string); save[cp - string] = '\0'; string = save; if (bitlen < 0 || bitlen > maxbitlen) bitlen = maxbitlen; } else { bitlen = maxbitlen; } if (family == AF_INET) { if ((result = my_inet_pton (AF_INET, string, &sin)) <= 0) return (NULL); return (New_Prefix (AF_INET, &sin, bitlen)); } #ifdef HAVE_IPV6 else if (family == AF_INET6) { // Get rid of this with next IPv6 upgrade #if defined(NT) && !defined(HAVE_INET_NTOP) inet6_addr(string, &sin6); return (New_Prefix (AF_INET6, &sin6, bitlen)); #else if ((result = inet_pton (AF_INET6, string, &sin6)) <= 0) return (NULL); #endif /* NT */ return (New_Prefix (AF_INET6, &sin6, bitlen)); } #endif /* HAVE_IPV6 */ else return (NULL); } prefix_t * Ref_Prefix (prefix_t * prefix) { if (prefix == NULL) return (NULL); if (prefix->ref_count == 0) { /* make a copy in case of a static prefix */ return (New_Prefix2 (prefix->family, &prefix->add, prefix->bitlen, NULL)); } prefix->ref_count++; /* fprintf(stderr, "[A %s, %d]\n", prefix_toa (prefix), prefix->ref_count); */ return (prefix); } void Deref_Prefix (prefix_t * prefix) { if (prefix == NULL) return; /* for secure programming, raise an assert. no static prefix can call this */ assert (prefix->ref_count > 0); prefix->ref_count--; assert (prefix->ref_count >= 0); if (prefix->ref_count <= 0) { Delete (prefix); return; } } /* } */ /* #define PATRICIA_DEBUG 1 */ static int num_active_patricia = 0; /* these routines support continuous mask only */ patricia_tree_t * New_Patricia (int maxbits) { patricia_tree_t *patricia = calloc(1, sizeof *patricia); patricia->maxbits = maxbits; patricia->head = NULL; patricia->num_active_node = 0; assert (maxbits <= PATRICIA_MAXBITS); /* XXX */ num_active_patricia++; return (patricia); } /* * if func is supplied, it will be called as func(node->data) * before deleting the node */ void Clear_Patricia (patricia_tree_t *patricia, void_fn_t func) { assert (patricia); if (patricia->head) { patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; patricia_node_t **Xsp = Xstack; patricia_node_t *Xrn = patricia->head; while (Xrn) { patricia_node_t *l = Xrn->l; patricia_node_t *r = Xrn->r; if (Xrn->prefix) { Deref_Prefix (Xrn->prefix); if (Xrn->data && func) ((void (*)(void *)) func) (Xrn->data); } else { assert (Xrn->data == NULL); } Delete (Xrn); patricia->num_active_node--; if (l) { if (r) { *Xsp++ = r; } Xrn = l; } else if (r) { Xrn = r; } else if (Xsp != Xstack) { Xrn = *(--Xsp); } else { Xrn = NULL; } } } assert (patricia->num_active_node == 0); /* Delete (patricia); */ } void Destroy_Patricia (patricia_tree_t *patricia, void_fn_t func) { Clear_Patricia (patricia, func); Delete (patricia); num_active_patricia--; } /* * if func is supplied, it will be called as func(node->prefix, node->data) */ void patricia_process (patricia_tree_t *patricia, void_fn_t func) { patricia_node_t *node; assert (func); PATRICIA_WALK (patricia->head, node) { ((void (*)(prefix_t *, void *)) func) (node->prefix, node->data); } PATRICIA_WALK_END; } size_t patricia_walk_inorder(patricia_node_t *node, void_fn_t func) { size_t n = 0; assert(func); if (node->l) { n += patricia_walk_inorder(node->l, func); } if (node->prefix) { ((void (*)(prefix_t *, void *)) func) (node->prefix, node->data); n++; } if (node->r) { n += patricia_walk_inorder(node->r, func); } return n; } patricia_node_t * patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix) { patricia_node_t *node; u_char *addr; u_int bitlen; assert (patricia); assert (prefix); assert (prefix->bitlen <= patricia->maxbits); if (patricia->head == NULL) return (NULL); node = patricia->head; addr = prefix_touchar (prefix); bitlen = prefix->bitlen; while (node->bit < bitlen) { if (BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_exact: take right %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_exact: take right at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->r; } else { #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_exact: take left %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_exact: take left at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->l; } if (node == NULL) return (NULL); } #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_exact: stop at %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_exact: stop at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ if (node->bit > bitlen || node->prefix == NULL) return (NULL); assert (node->bit == bitlen); assert (node->bit == node->prefix->bitlen); if (comp_with_mask (prefix_tochar (node->prefix), prefix_tochar (prefix), bitlen)) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_search_exact: found %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ return (node); } return (NULL); } /* if inclusive != 0, "best" may be the given prefix itself */ patricia_node_t * patricia_search_best2 (patricia_tree_t *patricia, prefix_t *prefix, int inclusive) { patricia_node_t *node; patricia_node_t *stack[PATRICIA_MAXBITS + 1]; u_char *addr; u_int bitlen; int cnt = 0; assert (patricia); assert (prefix); assert (prefix->bitlen <= patricia->maxbits); if (patricia->head == NULL) return (NULL); node = patricia->head; addr = prefix_touchar (prefix); bitlen = prefix->bitlen; while (node->bit < bitlen) { if (node->prefix) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_search_best: push %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ stack[cnt++] = node; } if (BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_best: take right %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_best: take right at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->r; } else { #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_best: take left %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_best: take left at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->l; } if (node == NULL) break; } if (inclusive && node && node->prefix) stack[cnt++] = node; #ifdef PATRICIA_DEBUG if (node == NULL) fprintf (stderr, "patricia_search_best: stop at null\n"); else if (node->prefix) fprintf (stderr, "patricia_search_best: stop at %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_best: stop at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ if (cnt <= 0) return (NULL); while (--cnt >= 0) { node = stack[cnt]; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_search_best: pop %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ if (comp_with_mask (prefix_tochar (node->prefix), prefix_tochar (prefix), node->prefix->bitlen) && node->prefix->bitlen <= bitlen) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_search_best: found %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ return (node); } } return (NULL); } patricia_node_t * patricia_search_best (patricia_tree_t *patricia, prefix_t *prefix) { return (patricia_search_best2 (patricia, prefix, 1)); } patricia_node_t * patricia_lookup (patricia_tree_t *patricia, prefix_t *prefix) { patricia_node_t *node, *new_node, *parent, *glue; u_char *addr, *test_addr; u_int bitlen, check_bit, differ_bit; int i, j, r; assert (patricia); assert (prefix); assert (prefix->bitlen <= patricia->maxbits); if (patricia->head == NULL) { node = calloc(1, sizeof *node); node->bit = prefix->bitlen; node->prefix = Ref_Prefix (prefix); node->parent = NULL; node->l = node->r = NULL; node->data = NULL; patricia->head = node; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new_node #0 %s/%d (head)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ patricia->num_active_node++; return (node); } addr = prefix_touchar (prefix); bitlen = prefix->bitlen; node = patricia->head; while (node->bit < bitlen || node->prefix == NULL) { if (node->bit < patricia->maxbits && BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { if (node->r == NULL) break; #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_lookup: take right %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_lookup: take right at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->r; } else { if (node->l == NULL) break; #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_lookup: take left %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_lookup: take left at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->l; } assert (node); } assert (node->prefix); #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: stop at %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ test_addr = prefix_touchar (node->prefix); /* find the first bit different */ check_bit = (node->bit < bitlen)? node->bit: bitlen; differ_bit = 0; for (i = 0; i*8 < check_bit; i++) { if ((r = (addr[i] ^ test_addr[i])) == 0) { differ_bit = (i + 1) * 8; continue; } /* I know the better way, but for now */ for (j = 0; j < 8; j++) { if (BIT_TEST (r, (0x80 >> j))) break; } /* must be found */ assert (j < 8); differ_bit = i * 8 + j; break; } if (differ_bit > check_bit) differ_bit = check_bit; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: differ_bit %d\n", differ_bit); #endif /* PATRICIA_DEBUG */ parent = node->parent; while (parent && parent->bit >= differ_bit) { node = parent; parent = node->parent; #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_lookup: up to %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_lookup: up to %u\n", node->bit); #endif /* PATRICIA_DEBUG */ } if (differ_bit == bitlen && node->bit == bitlen) { if (node->prefix) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: found %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ return (node); } node->prefix = Ref_Prefix (prefix); #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new node #1 %s/%d (glue mod)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ assert (node->data == NULL); return (node); } new_node = calloc(1, sizeof *new_node); new_node->bit = prefix->bitlen; new_node->prefix = Ref_Prefix (prefix); new_node->parent = NULL; new_node->l = new_node->r = NULL; new_node->data = NULL; patricia->num_active_node++; if (node->bit == differ_bit) { new_node->parent = node; if (node->bit < patricia->maxbits && BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { assert (node->r == NULL); node->r = new_node; } else { assert (node->l == NULL); node->l = new_node; } #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new_node #2 %s/%d (child)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ return (new_node); } if (bitlen == differ_bit) { if (bitlen < patricia->maxbits && BIT_TEST (test_addr[bitlen >> 3], 0x80 >> (bitlen & 0x07))) { new_node->r = node; } else { new_node->l = node; } new_node->parent = node->parent; if (node->parent == NULL) { assert (patricia->head == node); patricia->head = new_node; } else if (node->parent->r == node) { node->parent->r = new_node; } else { node->parent->l = new_node; } node->parent = new_node; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new_node #3 %s/%d (parent)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ } else { glue = calloc(1, sizeof *glue); glue->bit = differ_bit; glue->prefix = NULL; glue->parent = node->parent; glue->data = NULL; patricia->num_active_node++; if (differ_bit < patricia->maxbits && BIT_TEST (addr[differ_bit >> 3], 0x80 >> (differ_bit & 0x07))) { glue->r = new_node; glue->l = node; } else { glue->r = node; glue->l = new_node; } new_node->parent = glue; if (node->parent == NULL) { assert (patricia->head == node); patricia->head = glue; } else if (node->parent->r == node) { node->parent->r = glue; } else { node->parent->l = glue; } node->parent = glue; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new_node #4 %s/%d (glue+node)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ } return (new_node); } void patricia_remove (patricia_tree_t *patricia, patricia_node_t *node) { patricia_node_t *parent, *child; assert (patricia); assert (node); if (node->r && node->l) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_remove: #0 %s/%d (r & l)\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ /* this might be a placeholder node -- have to check and make sure * there is a prefix aossciated with it ! */ if (node->prefix != NULL) Deref_Prefix (node->prefix); node->prefix = NULL; /* Also I needed to clear data pointer -- masaki */ node->data = NULL; return; } if (node->r == NULL && node->l == NULL) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_remove: #1 %s/%d (!r & !l)\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ parent = node->parent; Deref_Prefix (node->prefix); Delete (node); patricia->num_active_node--; if (parent == NULL) { assert (patricia->head == node); patricia->head = NULL; return; } if (parent->r == node) { parent->r = NULL; child = parent->l; } else { assert (parent->l == node); parent->l = NULL; child = parent->r; } if (parent->prefix) return; /* we need to remove parent too */ if (parent->parent == NULL) { assert (patricia->head == parent); patricia->head = child; } else if (parent->parent->r == parent) { parent->parent->r = child; } else { assert (parent->parent->l == parent); parent->parent->l = child; } child->parent = parent->parent; Delete (parent); patricia->num_active_node--; return; } #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_remove: #2 %s/%d (r ^ l)\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ if (node->r) { child = node->r; } else { assert (node->l); child = node->l; } parent = node->parent; child->parent = parent; Deref_Prefix (node->prefix); Delete (node); patricia->num_active_node--; if (parent == NULL) { assert (patricia->head == node); patricia->head = child; return; } if (parent->r == node) { parent->r = child; } else { assert (parent->l == node); parent->l = child; } } /* { from demo.c */ patricia_node_t * make_and_lookup (patricia_tree_t *tree, char *string) { prefix_t *prefix; patricia_node_t *node; prefix = ascii2prefix (AF_INET, string); printf ("make_and_lookup: %s/%d\n", prefix_toa (prefix), prefix->bitlen); node = patricia_lookup (tree, prefix); Deref_Prefix (prefix); return (node); } patricia_node_t * try_search_exact (patricia_tree_t *tree, char *string) { prefix_t *prefix; patricia_node_t *node; prefix = ascii2prefix (AF_INET, string); printf ("try_search_exact: %s/%d\n", prefix_toa (prefix), prefix->bitlen); if ((node = patricia_search_exact (tree, prefix)) == NULL) { printf ("try_search_exact: not found\n"); } else { printf ("try_search_exact: %s/%d found\n", prefix_toa (node->prefix), node->prefix->bitlen); } Deref_Prefix (prefix); return (node); } void lookup_then_remove (patricia_tree_t *tree, char *string) { patricia_node_t *node; if ((node = try_search_exact (tree, string))) patricia_remove (tree, node); } patricia_node_t * try_search_best (patricia_tree_t *tree, char *string) { prefix_t *prefix; patricia_node_t *node; prefix = ascii2prefix (AF_INET, string); printf ("try_search_best: %s/%d\n", prefix_toa (prefix), prefix->bitlen); if ((node = patricia_search_best (tree, prefix)) == NULL) printf ("try_search_best: not found\n"); else printf ("try_search_best: %s/%d found\n", prefix_toa (node->prefix), node->prefix->bitlen); Deref_Prefix (prefix); return (node); } /* } */ Net-Patricia-1.23/libpatricia/PaxHeader/credits.txt000644 000765 000024 00000000210 14475524047 022644 xustar00tobezstaff000000 000000 30 mtime=1693886503.336024284 57 LIBARCHIVE.xattr.com.apple.provenance=AQAA8is7Wn3W9Yo 49 SCHILY.xattr.com.apple.provenance=ò+;Z}ÖõŠ Net-Patricia-1.23/libpatricia/credits.txt000644 000765 000024 00000005435 14475524047 020711 0ustar00tobezstaff000000 000000 [newtool.gif] MRT Credits The Multi-Threaded Routing Toolkit _________________________________________________________________ MRT was developed by [1]Merit Network, Inc., under National Science Foundation grant NCR-9318902, "Experimentation with Routing Technology to be Used for Inter-Domain Routing in the Internet." Current MRT Staff * [2]Craig Labovitz * [3]Makaki Hirabaru * [4]Farnam Jahanian * Susan Hares * Susan R. Harris * Nathan Binkert * Gerald Winters Project Alumni * [5]Marc Unangst * John Scudder The BGP4+ extension was originally written by Francis Dupont . The public domain Struct C-library of linked list, hash table and memory allocation routines was developed by Jonathan Dekock . Susan Rebecca Harris provided help with the documentation. David Ward provided bug fixes and helpful suggestions. Some sections of code and architecture ideas were taken from the GateD routing daemon. The first port to Linux with IPv6 was done by Pedro Roque . Some interface routines to the Linux kernel were originally written by him. Alexey Kuznetsov made enhancements to 1.4.3a and fixed the Linux kernel intarface. Linux's netlink interface was written, referring to his code "iproute2". We would also like to thank our other colleagues in Japan, Portugal, the Netherlands, the UK, and the US for their many contributions to the MRT development effort. _________________________________________________________________ Cisco is a registered trademark of Cisco Systems Inc. _________________________________________________________________ Merit Network 4251 Plymouth Road Suite C Ann Arbor, MI 48105-2785 734-764-9430 info@merit.edu _________________________________________________________________ © 1999 Merit Network, Inc. [6]www@merit.edu References 1. http://www.merit.edu/ 2. http://www.merit.edu/~labovit 3. http://www.merit.edu/~masaki 4. http://www.eecs.umich.edu/~farnam 5. http://www.contrib.andrew.cmu.edu/~mju/ 6. mailto:www@merit.edu Net-Patricia-1.23/libpatricia/Makefile.PL000644 000765 000024 00000000772 15002650744 020453 0ustar00tobezstaff000000 000000 use ExtUtils::MakeMaker; $Verbose = 1; WriteMakefile( NAME => 'Net::Patricia::libpatricia', SKIP => [qw(all static static_lib dynamic dynamic_lib test)], clean => {'FILES' => 'libpatricia$(LIB_EXT)'}, ); sub MY::top_targets { ' all :: static test :: test_dynamic :: test_static :: static :: libpatricia$(LIB_EXT) libpatricia$(LIB_EXT): $(O_FILES) $(AR) cr libpatricia$(LIB_EXT) $(O_FILES) $(RANLIB) libpatricia$(LIB_EXT) '; } Net-Patricia-1.23/libpatricia/PaxHeader/copyright000644 000765 000024 00000000210 14475524047 022401 xustar00tobezstaff000000 000000 30 mtime=1693886503.335932612 57 LIBARCHIVE.xattr.com.apple.provenance=AQAA8is7Wn3W9Yo 49 SCHILY.xattr.com.apple.provenance=ò+;Z}ÖõŠ Net-Patricia-1.23/libpatricia/copyright000644 000765 000024 00000002345 14475524047 020443 0ustar00tobezstaff000000 000000 $Id: COPYRIGHT,v 1.1.1.1 2013/08/15 18:46:09 labovit Exp $ Copyright (c) 1999-2013 The Regents of the University of Michigan ("The Regents") and Merit Network, Inc. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.Net-Patricia-1.23/libpatricia/patricia.h000644 000765 000024 00000010607 15002650744 020444 0ustar00tobezstaff000000 000000 /* * $Id: patricia.h,v 1.6 2005/12/07 20:53:01 dplonka Exp $ * Dave Plonka * * This product includes software developed by the University of Michigan, * Merit Network, Inc., and their contributors. * * This file had been called "radix.h" in the MRT sources. * * I renamed it to "patricia.h" since it's not an implementation of a general * radix trie. Also, pulled in various requirements from "mrt.h" and added * some other things it could be used as a standalone API. */ #ifndef _PATRICIA_H #define _PATRICIA_H #define HAVE_IPV6 /* typedef unsigned int u_int; */ typedef void (*void_fn_t)(); /* { from defs.h */ #define prefix_touchar(prefix) ((u_char *)&(prefix)->add.sin) #define MAXLINE 1024 #define BIT_TEST(f, b) ((f) & (b)) /* } */ #define addroute make_and_lookup #include /* for u_* definitions (on FreeBSD 5) */ #include /* for EAFNOSUPPORT */ #ifndef EAFNOSUPPORT # define EAFNOSUPPORT WSAEAFNOSUPPORT #endif #ifdef _WIN32 # include # include #else # include /* for struct in_addr */ #endif #include /* for AF_INET */ /* { from mrt.h */ typedef struct _prefix4_t { u_short family; /* AF_INET | AF_INET6 */ u_short bitlen; /* same as mask? */ int ref_count; /* reference count */ struct in_addr sin; } prefix4_t; typedef struct _prefix_t { u_short family; /* AF_INET | AF_INET6 */ u_short bitlen; /* same as mask? */ int ref_count; /* reference count */ union { struct in_addr sin; #ifdef HAVE_IPV6 struct in6_addr sin6; #endif /* IPV6 */ } add; } prefix_t; /* } */ typedef struct _patricia_node_t { u_int bit; /* flag if this node used */ prefix_t *prefix; /* who we are in patricia tree */ struct _patricia_node_t *l, *r; /* left and right children */ struct _patricia_node_t *parent;/* may be used */ void *data; /* pointer to data */ void *user1; /* pointer to usr data (ex. route flap info) */ } patricia_node_t; typedef struct _patricia_tree_t { patricia_node_t *head; u_int maxbits; /* for IP, 32 bit addresses */ int num_active_node; /* for debug purpose */ } patricia_tree_t; patricia_node_t *patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix); patricia_node_t *patricia_search_best (patricia_tree_t *patricia, prefix_t *prefix); patricia_node_t * patricia_search_best2 (patricia_tree_t *patricia, prefix_t *prefix, int inclusive); patricia_node_t *patricia_lookup (patricia_tree_t *patricia, prefix_t *prefix); void patricia_remove (patricia_tree_t *patricia, patricia_node_t *node); patricia_tree_t *New_Patricia (int maxbits); void Clear_Patricia (patricia_tree_t *patricia, void_fn_t func); void Destroy_Patricia (patricia_tree_t *patricia, void_fn_t func); void patricia_process (patricia_tree_t *patricia, void_fn_t func); char *prefix_toa (prefix_t * prefix); /* { from demo.c */ prefix_t * ascii2prefix (int family, char *string); patricia_node_t * make_and_lookup (patricia_tree_t *tree, char *string); /* } */ #define PATRICIA_MAXBITS (sizeof(struct in6_addr) * 8) #define PATRICIA_NBIT(x) (0x80 >> ((x) & 0x7f)) #define PATRICIA_NBYTE(x) ((x) >> 3) #define PATRICIA_DATA_GET(node, type) (type *)((node)->data) #define PATRICIA_DATA_SET(node, value) ((node)->data = (void *)(value)) #define PATRICIA_WALK(Xhead, Xnode) \ do { \ patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; \ patricia_node_t **Xsp = Xstack; \ patricia_node_t *Xrn = (Xhead); \ while ((Xnode = Xrn)) { \ if (Xnode->prefix) #define PATRICIA_WALK_ALL(Xhead, Xnode) \ do { \ patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; \ patricia_node_t **Xsp = Xstack; \ patricia_node_t *Xrn = (Xhead); \ while ((Xnode = Xrn)) { \ if (1) #define PATRICIA_WALK_BREAK { \ if (Xsp != Xstack) { \ Xrn = *(--Xsp); \ } else { \ Xrn = (patricia_node_t *) 0; \ } \ continue; } #define PATRICIA_WALK_END \ if (Xrn->l) { \ if (Xrn->r) { \ *Xsp++ = Xrn->r; \ } \ Xrn = Xrn->l; \ } else if (Xrn->r) { \ Xrn = Xrn->r; \ } else if (Xsp != Xstack) { \ Xrn = *(--Xsp); \ } else { \ Xrn = (patricia_node_t *) 0; \ } \ } \ } while (0) #endif /* _PATRICIA_H */ Net-Patricia-1.23/t/01everything.t000644 000765 000024 00000010354 15003370755 017172 0ustar00tobezstaff000000 000000 #!/usr/bin/perl -w use Test::More; use strict qw(vars); use diagnostics; use Net::Patricia; use Storable; our $debug = 1; plan tests => 48; # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): my $t = Net::Patricia->new; isa_ok($t, 'Net::Patricia', 'creating base object'); ok($t->add_string('127.0.0.0/8'), 'adding 127.0.0.0/8'); my $thawed = Storable::thaw(Storable::nfreeze($t)); for my $o ({ name => "original", obj => $t }, { name => "thawed", obj => $thawed }) { is($o->{obj}->match_string("127.0.0.1"), "127.0.0.0/8", "$o->{name}: looking for 127.0.0.1"); is($o->{obj}->match_integer(2130706433), "127.0.0.0/8", "$o->{name}: looking for 2130706433"); ok(!$o->{obj}->match_string("10.0.0.1"), "$o->{name}: looking for 10.0.0.1"); ok(!$o->{obj}->match_integer(42), "$o->{name}: looking for 42"); } { my $ten = Thingy->new(10); my $twenty = Thingy->new(20); ok($t->add_string("10.0.0.0/8", $ten), "adding 10.0.0.0/8"); } diag "Destructor 10 should *not* have run yet (but 20 should have).\n" if $debug; foreach my $subnet (qw(10.42.42.0/31 10.42.42.0/26 10.42.42.0/24 10.42.42.0/32 10.42.69.0/24)) { ok($t->add_string($subnet), "adding $subnet"); } $thawed = Storable::thaw(Storable::nfreeze($t)); for my $o ({ name => "original", obj => $t }, { name => "thawed", obj => $thawed }) { my $str1 = $o->{obj}->match_string("10.42.42.0/24"); my $str2 = $o->{obj}->match_string("10.42.69.0/24"); isnt($str1, $str2, "$o->{name}: compare matches from 10.42.42.0/24 and 10.42.69.0/24"); is(${$o->{obj}->match_integer(168430090)}, 10, "$o->{name}: looking for 168430090"); ok($o->{obj}->match_string("10.0.0.1"), "$o->{name}: looking for 10.0.0.1"); ok(!$o->{obj}->match_exact_integer(167772160), "$o->{name}: looking for 167772160"); ok($o->{obj}->match_exact_integer(167772160, 8), "$o->{name}: looking for 167772160, 8"); is(${$o->{obj}->match_exact_string("10.0.0.0/8")}, 10, "$o->{name}: looking for 10.0.0.0/8"); } ok(!$t->remove_string("42.0.0.0/8"), "removing 42.0.0.0/8"); is(${$t->remove_string("10.0.0.0/8")}, 10, "removing 10.0.0.0/8"); $thawed = Storable::thaw(Storable::nfreeze($t)); diag "Destructor 10 should have just run (twice, once for the destroyed clone).\n" if $debug; for my $o ({ name => "original", obj => $t }, { name => "thawed", obj => $thawed }) { ok(!$o->{obj}->match_exact_integer(167772160, 8), "$o->{name}: looking for exact 167772160, 8"); # print "YOU SHOULD SEE A USAGE ERROR HERE:\n"; # $o->{obj}->match_exact_integer(167772160, 8, 10); is($o->{obj}->climb_inorder(sub { diag "$o->{name}: climbing at $_[0]\n" }), 6, "$o->{name}: climb inorder"); ok($o->{obj}->climb, "$o->{name}: climb"); } eval '$t->add_string("_")'; # invalid key like($@, qr/invalid/, 'adding "_"'); ok($t->add_string("0.0.0.0/0"), "add 0.0.0.0/0"); $thawed = Storable::thaw(Storable::nfreeze($t)); for my $o ({ name => "original", obj => $t }, { name => "thawed", obj => $thawed }) { ok($o->{obj}->match_string("10.0.0.1"), "$o->{name}: lookup 10.0.0.1"); } my @a = $t->add_cidr("211.200.0.0-211.205.255.255", "cidr block!"); is(@a, 2, "adding cidr block"); $thawed = Storable::thaw(Storable::nfreeze($t)); for my $o ({ name => "original", obj => $t }, { name => "thawed", obj => $thawed }) { is($o->{obj}->match_string("211.202.0.1"), "cidr block!", "$o->{name}: looking for 211.202.0.1"); } @a = $t->remove_cidr("211.200.0.0-211.205.255.255"); is(@a, 2, "removing cidr block"); undef $t; $t = Net::Patricia->new(AF_INET6); isa_ok($t, "Net::Patricia::AF_INET6", "constructing a Net::Patrica::AF_INET6"); ok($t->add_string("2001:220::/35", "hello, world"), "adding 2001:220::/35"); $thawed = Storable::thaw(Storable::nfreeze($t)); for my $o ({ name => "original", obj => $t }, { name => "thawed", obj => $thawed }) { is($o->{obj}->match_string("2001:220::/128"), "hello, world", "$o->{name}: looking for 2001:220::/128"); } undef $t; undef $thawed; done_testing(); package Thingy; use diagnostics; sub new { my $class = shift(@_); my $self = shift(@_); return bless \$self, $class; } sub DESTROY { my $self = shift(@_); print STDERR "$$self What a world, what a world...\n"; } Net-Patricia-1.23/demo/PaxHeader/demo.c000644 000765 000024 00000000207 14475524047 020205 xustar00tobezstaff000000 000000 29 mtime=1693886503.33567822 57 LIBARCHIVE.xattr.com.apple.provenance=AQAA8is7Wn3W9Yo 49 SCHILY.xattr.com.apple.provenance=ò+;Z}ÖõŠ Net-Patricia-1.23/demo/demo.c000644 000765 000024 00000003125 14475524047 016236 0ustar00tobezstaff000000 000000 /* * $Id: demo.c,v 1.4 2005/12/07 20:55:52 dplonka Exp $ * * This is based on "demo.c" provided with MRT-2.2.2a. */ #include "patricia.h" #include /* printf */ #include /* exit */ void func(prefix_t *prefix) { printf("node: %s/%d\n", prefix_toa(prefix), prefix->bitlen); } int main(void) { prefix_t *prefix; patricia_tree_t *tree; patricia_node_t *node; tree = New_Patricia(32); make_and_lookup(tree, "127.0.0.0/8"); try_search_best(tree, "127.0.0.1"); try_search_best(tree, "10.0.0.1"); make_and_lookup(tree, "10.42.42.0/24"); make_and_lookup(tree, "10.42.69.0/24"); make_and_lookup(tree, "10.0.0.0/8"); make_and_lookup(tree, "10.0.0.0/9"); try_search_best(tree, "10.42.42.0/24"); try_search_best(tree, "10.10.10.10"); try_search_best(tree, "10.10.10.1"); try_search_exact(tree, "10.0.0.0"); try_search_exact(tree, "10.0.0.0/8"); #if 0 PATRICIA_WALK(tree->head, node) { printf("node: %s/%d\n", prefix_toa(node->prefix), node->prefix->bitlen); } PATRICIA_WALK_END; #else printf("%u total nodes.\n", patricia_walk_inorder(tree->head, func)); #endif lookup_then_remove(tree, "42.0.0.0/8"); lookup_then_remove(tree, "10.0.0.0/8"); try_search_exact(tree, "10.0.0.0"); #if 0 PATRICIA_WALK(tree->head, node) { printf("node: %s/%d\n", prefix_toa(node->prefix), node->prefix->bitlen); } PATRICIA_WALK_END; #else printf("%u total nodes.\n", patricia_walk_inorder(tree->head, func)); #endif Destroy_Patricia(tree, (void *)0); exit(0); }