/** * Determines the intersection between an IP (with optional prefix) and a * CIDR block. * * The IP will be checked against the CIDR block given and will either be * inside or outside the CIDR completely, or partially. * * NOTE: The caller should explicitly check against the INTERSECT_* * constants because this method will return a value > 1 even for partial * matches. * * @param mixed $ip The IP/cidr to match * @param mixed $cidr The CIDR block to match within * @return integer Returns an INTERSECT_* constant * @throws \InvalidArgumentException if either $ip or $cidr is invalid */ public static function cidr_intersect($ip, $cidr) { // use fixed length HEX strings so we can easily do STRING comparisons // instead of using slower bccomp() math. list($lo, $hi) = array_map(function ($v) { return sprintf("%032s", IP::inet_ptoh($v)); }, CIDR::cidr_to_range($ip)); list($min, $max) = array_map(function ($v) { return sprintf("%032s", IP::inet_ptoh($v)); }, CIDR::cidr_to_range($cidr)); /** visualization of logic used below lo-hi = $ip to check min-max = $cidr block being checked against --- --- --- lo --- --- hi --- --- --- --- --- IP/prefix to check --- min --- --- max --- --- --- --- --- --- --- Partial "LOW" match --- --- --- --- --- min --- --- max --- --- --- Partial "HIGH" match --- --- --- --- min max --- --- --- --- --- --- No match "NO" --- --- --- --- --- --- --- --- min --- max --- No match "NO" min --- max --- --- --- --- --- --- --- --- --- No match "NO" --- --- min --- --- --- --- max --- --- --- --- Full match "YES" */ // IP is exact match or completely inside the CIDR block if ($lo >= $min and $hi <= $max) { return self::INTERSECT_YES; } // IP is completely outside the CIDR block if ($max < $lo or $min > $hi) { return self::INTERSECT_NO; } // @todo is it useful to return LOW/HIGH partial matches? // IP matches the lower end if ($max <= $hi and $min <= $lo) { return self::INTERSECT_LOW; } // IP matches the higher end if ($min >= $lo and $max >= $hi) { return self::INTERSECT_HIGH; } return self::INTERSECT_NO; }