Project

General

Profile

« Previous | Next » 

Revision 3490b8dd

Added by Phil Davis over 10 years ago

Check for overlapping subnets when saving interface addresses

This checks if a static IP address entered for an interface has a subnet
that overlaps with any other configured subnet. e.g.:
LAN is IPv4 10.10.12.1/24
Then try to set OPT1 to 10.10.13.1/23 - it overlaps with LAN because
"/23" covers the "12" and "13" together.
In the input errors message, display to the user the other interfaces
and subnets that overlap/conflict. Then the user has some idea what it
is that conflicts and can easily go looking in the right place for the
problem.
Do the same thing for IPv6 address/CIDR.

Note: I have not enhanced any of the checks for conflicts with static
routes - there could be cases where a user has a static route like
10.0.0.0/8 pointing to some internal router that has the rest of
10.0.0.0/8 behind it, but the user has some direct-attached subnet
inside that - e.g. 10.1.2.0/24 - the routing table should cope with
this, delivering directly to 10.1.2.0/24 and routing for the rest of
10.0.0.0/8. So we cannot invalidate all overlaps with static routes.

I think this validation will not invalidate any exotic-but-valid use
cases. I can't think of when the interface subnets on 2 interfaces can
overlap and still be a valid/useful configuration.

This should stop people setting up dumb mixes of LAN/OPT1/OPT2... with
random addresses and CIDR prefix that overlap each other.

View differences:

etc/inc/pfsense-utils.inc
2709 2709
 * INPUTS
2710 2710
 *   IP Address to check.
2711 2711
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2712
 *   check_localip - if true then also check for matches with PPTP and LT2P addresses
2713
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2714
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2715
 *     If check_subnets is true and cidrprefix is specified, 
2716
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2712 2717
 * RESULT
2713
 *   returns true if the IP Address is
2714
 *   configured and present on this device.
2718
 *   returns true if the IP Address is configured and present on this device or overlaps a configured subnet.
2715 2719
*/
2716
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false) {
2720
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2721
	if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) {
2722
		return true;
2723
	}
2724
	return false;
2725
}
2726

  
2727
/****f* pfsense-utils/where_is_ipaddr_configured
2728
 * NAME
2729
 *   where_is_ipaddr_configured
2730
 * INPUTS
2731
 *   IP Address to check.
2732
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2733
 *   check_localip - if true then also check for matches with PPTP and LT2P addresses
2734
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2735
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2736
 *     If check_subnets is true and cidrprefix is specified, 
2737
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2738
 * RESULT
2739
 *   Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check.
2740
 *   If there are no matches then an empty array is returned.
2741
*/
2742
function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2717 2743
	global $config;
2718 2744

  
2745
	$where_configured = array();
2746

  
2719 2747
	$pos = strpos($ignore_if, '_virtualip');
2720 2748
	if ($pos !== false) {
2721 2749
		$ignore_vip_id = substr($ignore_if, $pos+10);
......
2728 2756
	$isipv6 = is_ipaddrv6($ipaddr);
2729 2757

  
2730 2758
	if ($check_subnets) {
2759
		$cidrprefix = intval($cidrprefix);
2760
		if ($isipv6) {
2761
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
2762
				$cidrprefix = 128;
2763
			}
2764
		} else {
2765
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
2766
				$cidrprefix = 32;
2767
			}
2768
		}
2731 2769
		$iflist = get_configured_interface_list();
2732 2770
		foreach ($iflist as $if => $ifname) {
2733 2771
			if ($ignore_if == $if) {
2734 2772
				continue;
2735 2773
			}
2736 2774

  
2737
			if ($isipv6 === true) {
2738
				$bitmask = get_interface_subnetv6($if);
2739
				$subnet = gen_subnetv6(get_interface_ipv6($if), $bitmask);
2775
			if ($isipv6) {
2776
				$if_ipv6 = get_interface_ipv6($if);
2777
				$if_snbitsv6 = get_interface_subnetv6($if);
2778
				if ($if_ipv6 && $if_snbitsv6 && check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
2779
					$where_entry = array();
2780
					$where_entry['if'] = $if;
2781
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
2782
					$where_configured[] = $where_entry;
2783
				}
2740 2784
			} else {
2741
				$bitmask = get_interface_subnet($if);
2742
				$subnet = gen_subnet(get_interface_ip($if), $bitmask);
2743
			}
2744

  
2745
			if (ip_in_subnet($ipaddr, $subnet . '/' . $bitmask)) {
2746
				return true;
2785
				$if_ipv4 = get_interface_ip($if);
2786
				$if_snbitsv4 = get_interface_subnet($if);
2787
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
2788
					$where_entry = array();
2789
					$where_entry['if'] = $if;
2790
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
2791
					$where_configured[] = $where_entry;
2792
				}
2747 2793
			}
2748 2794
		}
2749 2795
	} else {
2750
		if ($isipv6 === true) {
2796
		if ($isipv6) {
2751 2797
			$interface_list_ips = get_configured_ipv6_addresses();
2752 2798
		} else {
2753 2799
			$interface_list_ips = get_configured_ip_addresses();
......
2758 2804
				continue;
2759 2805
			}
2760 2806
			if (strcasecmp($ipaddr, $ilips) == 0) {
2761
				return true;
2807
				$where_entry = array();
2808
				$where_entry['if'] = $if;
2809
				$where_entry['ip_or_subnet'] = $ilips;
2810
				$where_configured[] = $where_entry;
2762 2811
			}
2763 2812
		}
2764 2813
	}
......
2770 2819
			continue;
2771 2820
		}
2772 2821
		if (strcasecmp($ipaddr, $vip['ipaddr']) == 0) {
2773
			return true;
2822
			$where_entry = array();
2823
			$where_entry['if'] = $vip['if'];
2824
			$where_entry['ip_or_subnet'] = $vip['ipaddr'];
2825
			$where_configured[] = $where_entry;
2774 2826
		}
2775 2827
	}
2776 2828

  
2777 2829
	if ($check_localip) {
2778 2830
		if (is_array($config['pptpd']) && !empty($config['pptpd']['localip']) && (strcasecmp($ipaddr, $config['pptpd']['localip']) == 0)) {
2779
			return true;
2831
			$where_entry = array();
2832
			$where_entry['if'] = 'pptp';
2833
			$where_entry['ip_or_subnet'] = $config['pptpd']['localip'];
2834
			$where_configured[] = $where_entry;
2780 2835
		}
2781 2836

  
2782 2837
		if (!is_array($config['l2tp']) && !empty($config['l2tp']['localip']) && (strcasecmp($ipaddr, $config['l2tp']['localip']) == 0)) {
2783
			return true;
2838
			$where_entry = array();
2839
			$where_entry['if'] = 'l2tp';
2840
			$where_entry['ip_or_subnet'] = $config['l2tp']['localip'];
2841
			$where_configured[] = $where_entry;
2784 2842
		}
2785 2843
	}
2786 2844

  
2787
	return false;
2845
	return $where_configured;
2788 2846
}
2789 2847

  
2790 2848
/****f* pfsense-utils/pfSense_handle_custom_code

Also available in: Unified diff