Project

General

Profile

« Previous | Next » 

Revision e8d5be8e

Added by Stilez y over 9 years ago

Fix logic for subnet overlap check + canonical for IPv6

The subnet overlap functions came up as a concern while fixing redmine 5702.
Specifically -
The "canonical" function check_subnets_overlap() doesn't handle IPv6 (util.inc has standardised on v4/v6/agnostic versions, but this doesn't fit). Fixed by adding transparent detection of v4/v6 and a specific IPv4-only version
The IPv6 version is wrong (if sub1 contains sub2 then neither of sub1's endpoints will be detected as "inrange" of sub2 and result will be incorrect: this logic error has been fixed recently in other code too)
Bad data isn't detected - this still isn't detected for compatibility and is tagged "FIXME" instead to look at in future. Reason - not to break anything, at present always returns "overlap = true/false", not "true/false/invalid input".
because CIDR overlap implies containment, the IPv4 version uses a very efficient logic, namely calculates largest size subnet and checks this is same for both. Adopting this for both, and simplifying, makes these functions far "neater"
The old v4 version allowed for non-numeric $bits which doesn't make sense and I've omitted. Cannot think of a single situation where we would provide empty or bad data when we actually mean a /32 single IP.

Solution in this commit - a canonical "overlap" test (IPv4/IPv6 agnostic), IPv4/v6 "overlap" versions that actually do the work, in each case using the same logic as the old v4 (identify largest bit size and test if subnets created are valid and identical), and tag lack of "bad data" detection as fixme for now, returning FALSE instead to avoid breaking anything until fixed. Should be transparent from outside.

View differences:

src/etc/inc/util.inc
847 847
	return $result;
848 848
}
849 849

  
850
/* find out whether two subnets overlap */
850
/* find out whether two IPv4/IPv6 CIDR subnets overlap.
851
   Note: CIDR overlap implies one is identical or included so largest sn will be the same */
851 852
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
852

  
853
	if (!is_numeric($bits1)) {
854
		$bits1 = 32;
855
	}
856
	if (!is_numeric($bits2)) {
857
		$bits2 = 32;
858
	}
859

  
860
	if ($bits1 < $bits2) {
861
		$relbits = $bits1;
853
	if (is_ipaddrv4($ipaddr)) {
854
		return check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2);
862 855
	} else {
863
		$relbits = $bits2;
856
		return check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2);
864 857
	}
865

  
866
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
867
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
868

  
869
	return ($sn1 == $sn2);
870 858
}
871 859

  
872
/* find out whether two IPv6 subnets overlap */
873
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
874
	$sub1_min = gen_subnetv6($subnet1, $bits1);
875
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
876
	$sub2_min = gen_subnetv6($subnet2, $bits2);
877
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
860
/* find out whether two IPv4 CIDR subnets overlap.
861
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
862
function check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2) {
863
	$largest_sn = max($bits1, $bits2);
864
	$subnetv4_start1 = gen_subnetv4($subnet1, $largest_sn);
865
	$subnetv4_start2 = gen_subnetv4($subnet1, $largest_sn);
866
	
867
	if($subnetv4_start1 == '' || $subnetv4_start2 == '') {
868
		// One or both args is not a valid IPv4 subnet
869
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
870
		return false;
871
	}
872
	return ($subnetv4_start1 == $subnetv4_start2);
873
}
878 874

  
879
	return (is_inrange_v6($sub1_min, $sub2_min, $sub2_max) || is_inrange_v6($sub1_max, $sub2_min, $sub2_max) || is_inrange_v6($sub2_min, $sub1_min, $sub1_max));
875
/* find out whether two IPv6 CIDR subnets overlap.
876
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
877
function check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2) {
878
	$largest_sn = max($bits1, $bits2);
879
	$subnetv4_start1 = gen_subnetv6($subnet1, $largest_sn);
880
	$subnetv4_start2 = gen_subnetv6($subnet1, $largest_sn);
881
	
882
	if($subnetv6_start1 == '' || $subnetv6_start2 == '') {
883
		// One or both args is not a valid IPv6 subnet
884
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
885
		return false;
886
	}
887
	return ($subnetv6_start1 == $subnetv6_start2);
880 888
}
881 889

  
882 890
/* return true if $addr is in $subnet, false if not */

Also available in: Unified diff