X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fsvc_IP_Mixin.pm;h=ce9218c104349810f3b70f7ed6355f4876407dcb;hb=2ee7f0c27233d800254d5244fc5913d881b48800;hp=abafe86adea2406fec39d3af821281a6ac8fb0be;hpb=fee90e01f8fa5ee58153b558b3a2129982bee1cb;p=freeside.git diff --git a/FS/FS/svc_IP_Mixin.pm b/FS/FS/svc_IP_Mixin.pm index abafe86ad..ce9218c10 100644 --- a/FS/FS/svc_IP_Mixin.pm +++ b/FS/FS/svc_IP_Mixin.pm @@ -3,7 +3,8 @@ use base 'FS::IP_Mixin'; use strict; use NEXT; -use FS::Record qw(qsearchs qsearch); +use Carp qw(croak carp); +use FS::Record qw(qsearchs qsearch dbh); use FS::Conf; use FS::router; use FS::part_svc_router; @@ -90,6 +91,9 @@ sub svc_ip_check { } sub _used_addresses { + + # Returns all addresses in use. Does not filter with $block. ref:f197bdbaa1 + my ($class, $block, $exclude) = @_; my $ip_field = $class->table_info->{'ip_field'} or return (); @@ -107,6 +111,69 @@ sub _used_addresses { }); } +sub _used_addresses_in_block { + my ($class, $block) = @_; + + croak "_used_addresses_in_block() requires an FS::addr_block parameter" + unless ref $block && $block->isa('FS::addr_block'); + + my $ip_field = $class->table_info->{'ip_field'}; + if ( !$ip_field ) { + carp "_used_addresses_in_block() skipped, no ip_field"; + return; + } + + my $block_na = $block->NetAddr; + + my $octets; + if ($block->ip_netmask >= 24) { + $octets = 3; + } elsif ($block->ip_netmask >= 16) { + $octets = 2; + } elsif ($block->ip_netmask >= 8) { + $octets = 1; + } + + # e.g. + # SELECT ip_addr + # FROM svc_broadband + # WHERE ip_addr != '' + # AND ip_addr != '0e0' + # AND ip_addr LIKE '10.0.2.%'; + # + # For /24, /16 and /8 this approach is fast, even when svc_broadband table + # contains 650,000+ ip records. For other allocations, this approach is + # not speedy, but usable. + # + # Note: A use case like this would could greatly benefit from a qsearch() + # parameter to bypass FS::Record objects creation and just + # return hashrefs from DBI. 200,000 hashrefs are many seconds faster + # than 200,000 FS::Record objects + my %qsearch = ( + table => $class->table, + select => $ip_field, + hashref => { $ip_field => { op => '!=', value => '' }}, + extra_sql => " AND $ip_field != '0e0' ", + ); + if ( $octets ) { + my $block_str = join('.', (split(/\D/, $block_na->first))[0..$octets-1]); + $qsearch{extra_sql} .= " AND $ip_field LIKE ".dbh->quote("${block_str}.%"); + } + + if ( $block->ip_netmask % 8 ) { + # Some addresses returned by qsearch may be outside the network block, + # so each ip address is tested to be in the block before it's returned. + return + grep { $block_na->contains( NetAddr::IP->new( $_ ) ) } + map { $_->$ip_field } + qsearch( \%qsearch ); + } + + return + map { $_->$ip_field } + qsearch( \%qsearch ); +} + sub _is_used { my ($class, $addr, $exclude) = @_; my $ip_field = $class->table_info->{'ip_field'} @@ -186,7 +253,9 @@ means "Framed-Route" if there's an attached router. sub radius_reply { my $self = shift; + my %reply = (); + if ( my $block = $self->attached_block ) { # block routed over dynamic IP: "192.168.100.0/29 0.0.0.0 1" # or @@ -196,21 +265,9 @@ sub radius_reply { ($self->ip_addr || '0.0.0.0') . ' 1'; } - if ( $self->router_routernum && FS::Conf->new->exists('radius-canopy') ) { - - my @addr_block = - qsearch('addr_block', { routernum => $self->router_routernum } ); - if ( @addr_block ) { - - #? - warn "Multiple address blocks attached to this service's router; using first" - if scalar(@addr_block) > 1; - - $reply{'Motorola-Canopy-Gateway'} = $addr_block[0]->ip_gateway + $reply{'Motorola-Canopy-Gateway'} = $self->addr_block->ip_gateway + if FS::Conf->new->exists('radius-canopy') && $self->addr_block; - } - - } %reply; }