You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							170 lines
						
					
					
						
							6.4 KiB
						
					
					
				
			
		
		
	
	
							170 lines
						
					
					
						
							6.4 KiB
						
					
					
				| package NGCP::Panel::Utils::ContractLocations;
 | |
| use strict;
 | |
| use warnings;
 | |
| 
 | |
| #use Sipwise::Base;
 | |
| #use DBIx::Class::Exception;
 | |
| 
 | |
| use NetAddr::IP;
 | |
| use Data::Validate::IP qw(is_ipv4 is_ipv6);
 | |
| use NGCP::Panel::Utils::IntervalTree::Simple;
 | |
| 
 | |
| use constant _CHECK_BLOCK_OVERLAPS => 1;
 | |
| 
 | |
| sub check_network_update_item {
 | |
|     my ($c,$new_resource,$old_item,$err_code) = @_;
 | |
| 
 | |
|     return 1 unless $old_item;
 | |
|     
 | |
|     if (!defined $err_code || ref $err_code ne 'CODE') {
 | |
|         $err_code = sub { return 0; };
 | |
|     }
 | |
|     
 | |
|     return 1;
 | |
| 
 | |
| }
 | |
| 
 | |
| sub set_blocks_from_to {
 | |
|     my ($blocks,$err_code) = @_;
 | |
|     my $intersecter = (_CHECK_BLOCK_OVERLAPS ? NGCP::Panel::Utils::IntervalTree::Simple->new() : undef);
 | |
|     my $version;
 | |
|     if (!defined $err_code || ref $err_code ne 'CODE') {
 | |
|         $err_code = sub { return 0; };
 | |
|     }
 | |
|     if ((scalar @$blocks) == 0) {
 | |
|         return &{$err_code}('At least one block definition is required');
 | |
|     }
 | |
|     foreach my $block (@$blocks) {
 | |
|         if (my $err = _set_ip_net_from_to($block)) {
 | |
|             return 0 unless &{$err_code}($err);
 | |
|         } else {
 | |
|             if ((defined $version && $version == 4 && $block->{_version} != 4) ||
 | |
|                 (defined $version && $version == 6 && $block->{_version} != 6)) {
 | |
|                 return &{$err_code}('Ipv4 and ipv6 must not be mixed in block definitions');
 | |
|             } 
 | |
|             $version //= $block->{_version};
 | |
|             if (defined $intersecter) {
 | |
|                 my $to = $block->{_to}->copy->badd(1); #right open intervals
 | |
|                 my $overlaps_with = $intersecter->find($block->{_from},$to);
 | |
|                 if ((scalar @$overlaps_with) > 0) {
 | |
|                     return 0 unless &{$err_code}("Block '$block->{_label}' overlaps with block(s) '" . join("', '",@$overlaps_with) . "'");
 | |
|                 } else {
 | |
|                     $intersecter->insert($block->{_from},$to,$block->{_label});
 | |
|                 }
 | |
|             }
 | |
|             delete $block->{_from};
 | |
|             delete $block->{_to};
 | |
|             delete $block->{_version};
 | |
|             delete $block->{_label};
 | |
|         }
 | |
|     }
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| sub _set_ip_net_from_to {
 | |
|     my ($resource) = @_;
 | |
|     return "Invalid IP address '$resource->{ip}'" unless _validate_ip($resource->{ip});
 | |
|     my $net = NetAddr::IP->new($resource->{ip} . (defined $resource->{mask} ? '/' . $resource->{mask} : ''));
 | |
|     if (!defined $net || (defined $resource->{mask} && $net->masklen != $resource->{mask})) {
 | |
|       return "Invalid mask '$resource->{mask}'";
 | |
|     }
 | |
|     $resource->{_label} = $net->cidr;
 | |
|     #force scalar context:
 | |
|     my $from = $net->network->bigint; #first->bigint;
 | |
|     my $to = $net->broadcast->bigint; #last->bigint;
 | |
|     #if (NetAddr::IP::Util::hasbits($net->{mask} ^ _CIDR127)) { #other than point-to-point
 | |
|     #  $from->bsub(1); #include network addr
 | |
|     #  $to->badd(1); #include broadcast addr      
 | |
|     #}
 | |
|     ($resource->{_from},$resource->{_to},$resource->{_version}) = ($from,$to,$net->version);
 | |
|     #whatever format we want to save:
 | |
|     if ($resource->{_version} == 4) {
 | |
|         $resource->{_ipv4_net_from} = $from;
 | |
|         $resource->{_ipv4_net_to} = $to;
 | |
|         $resource->{_ipv6_net_from} = undef;
 | |
|         $resource->{_ipv6_net_to} = undef;        
 | |
|     } elsif ($resource->{_version} == 6) {   
 | |
|         $resource->{_ipv4_net_from} = undef;
 | |
|         $resource->{_ipv4_net_to} = undef;
 | |
|         $resource->{_ipv6_net_from} = $from;
 | |
|         $resource->{_ipv6_net_to} = $to;
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| 
 | |
| #deflate column values for search parameters doesn't work, so we need this sub
 | |
| #(http://search.cpan.org/~ribasushi/DBIx-Class-0.082820/lib/DBIx/Class/Manual/FAQ.pod#Searching)
 | |
| sub _ip_to_bytes {
 | |
|     my ($ip) = @_;
 | |
|     if (_validate_ip($ip) && (my $net = NetAddr::IP->new($ip))) {
 | |
|         my $bigint = $net->bigint; #force scalar context
 | |
|         return (_bigint_to_bytes($bigint,$net->version == 6 ? 16 : 4),$net->version);
 | |
|     }
 | |
|     return (undef,undef);
 | |
| }
 | |
| 
 | |
| sub prepare_query_param_value {
 | |
|     my ($q) = @_;
 | |
|     return _prepare_query_param_value($q);
 | |
| }
 | |
| 
 | |
| sub _prepare_query_param_value {
 | |
|     my ($q,$v) = @_;
 | |
|     my ($bytes,$version) = _ip_to_bytes($q);
 | |
|     if (defined $v) {
 | |
|         # really complex interaction
 | |
|         ## no critic (ProhibitExplicitReturnUndef)
 | |
|         return undef if (!defined $bytes || $v != $version);
 | |
|         return $bytes;
 | |
|     } else {
 | |
|         return {} unless defined $bytes;
 | |
|         return {
 | |
|             'voip_contract_location_blocks._ipv4_net_from' => { '<=', $bytes },
 | |
|             'voip_contract_location_blocks._ipv4_net_to'  => { '>=', $bytes },
 | |
|         } if $version == 4;
 | |
|         return {
 | |
|             'voip_contract_location_blocks._ipv6_net_from' => { '<=', $bytes },
 | |
|             'voip_contract_location_blocks._ipv6_net_to'  => { '>=', $bytes },
 | |
|         } if $version == 6;
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub _bigint_to_bytes {
 | |
|     my ($bigint,$size) = @_;
 | |
|     #print '>'.sprintf('%0' . 2 * $size . 's',substr($bigint->as_hex(),2)) . "\n";
 | |
|     return pack('C' x $size, map { hex($_) } (sprintf('%0' . 2 * $size . 's',substr($bigint->as_hex(),2)) =~ /(..)/g));
 | |
|     #print '>' . join('',map { sprintf('%02x',$_) } unpack('C' x $size, $data)) . "\n";
 | |
|     #return $data;
 | |
| }
 | |
| 
 | |
| sub _validate_ip {
 | |
|     my ($ip) = @_;
 | |
|     return (is_ipv4($ip) || is_ipv6($ip));  
 | |
| }
 | |
| 
 | |
| sub get_datatable_cols {
 | |
|     
 | |
|     my ($c) = @_;
 | |
|     my $grp_stmt = "group_concat(if(`voip_contract_location_blocks`.`mask` is null,`voip_contract_location_blocks`.`ip`,concat(`voip_contract_location_blocks`.`ip`,'/',`voip_contract_location_blocks`.`mask`)) separator ', ')";
 | |
|     my $grp_len = 30;
 | |
|     return (
 | |
|         { name => 'blocks_grp', accessor => "blocks_grp", search => 0, title => $c->loc('Network Blocks'), literal_sql =>
 | |
|           "if(length(".$grp_stmt.") > ".$grp_len.", concat(left(".$grp_stmt.", ".$grp_len."), '...'), ".$grp_stmt.")" },
 | |
|         { name => "voip_contract_location_blocks._ipv4_net_from", search_lower_column => 'ipv4', convert_code => sub {
 | |
|             return _prepare_query_param_value(shift,4);
 | |
|             } },
 | |
|         { name => "voip_contract_location_blocks._ipv4_net_to", search_upper_column => 'ipv4', convert_code => sub {
 | |
|             return _prepare_query_param_value(shift,4);
 | |
|             } },
 | |
|         { name => "voip_contract_location_blocks._ipv6_net_from", search_lower_column => 'ipv6', convert_code => sub {
 | |
|             return _prepare_query_param_value(shift,6);
 | |
|             } },
 | |
|         { name => "voip_contract_location_blocks._ipv6_net_to", search_upper_column => 'ipv6', convert_code => sub {
 | |
|             return _prepare_query_param_value(shift,6);
 | |
|             } },
 | |
|     );
 | |
| 
 | |
| }
 | |
| 
 | |
| 1;
 |