【问题标题】:Find whether a given IP exists in CIDR or not查找给定 IP 是否存在于 CIDR 中
【发布时间】:2012-05-01 21:12:17
【问题描述】:

我正在使用 $_SERVER['REMOTE_ADDR'] 获取尝试访问我的网站的用户的 IP 地址。我的数据库中有不同的盒子,每个盒子都有不同的 IDS 和不同的 CIDR 条目。我想检查特定框的用户 IP 是否存在于任何 CIDR 中。 如果存在,则允许他进入,否则不允许。 假设用户 IP 是

127.0.0.1

假设框 12 的 CIDR 条目是

192.168.1.20/27
192.168.0.10/32

CIDR 条目存储在数据库的列中

如果他的 IP 在此范围内,则应允许他进入该站点,否则不允许。 我想根据每个 CIDR 条目检查他的 ip。

有什么想法吗?

谢谢

【问题讨论】:

标签: php codeigniter


【解决方案1】:

好的,试试这 5 个简单的步骤...

1. 将您的 CIDR 存储到数组中(从数据库中读取它们;猜你知道如何获取它)

$cidrs = array(
  '192.168.1.20/27', 
  '192.168.0.10/32'
  );

2.获取用户IP(远程地址)

$user_ip = $_SERVER['REMOTE_ADDR'];

3.添加此功能

function IPvsCIDR($user_ip, $cidr) {
  $parts = explode('/', $cidr);
  $ipc = explode('.', $parts[0]);
  foreach ($ipc as &$v)
    $v = str_pad(decbin($v), 8, '0', STR_PAD_LEFT);
  $ipc = substr(join('', $ipc), 0, $parts[1]);
  $ipu = explode('.', $user_ip);
  foreach ($ipu as &$v)
    $v = str_pad(decbin($v), 8, '0', STR_PAD_LEFT);
  $ipu = substr(join('', $ipu), 0, $parts[1]);
  return $ipu == $ipc;
  }

4. 将用户的 IP 地址与 $cidrs

进行比较
$validaddr = false;
foreach ($cidrs as $addr)
  if (IPvsCIDR($user_ip, $addr)) {
    $validaddr = true;
    break;
    } 

5.决定如何处理用户

if ($validaddr) {
  echo "CORRECT IP ADDRESS";
  }
else {
  echo "INCORRECT IP ADDRESS";
  }

就是这样!

这个函数是如何工作的。它将 CIDR 地址部分 (x.x.x.x) 转换为二进制字符串并取前 N 位数字。然后它对用户的 IP 执行相同的工作并检查值是否匹配。

示例 2(函数完成工作)

function testUserIP($user_ip, $cidrs) {
  $ipu = explode('.', $user_ip);
  foreach ($ipu as &$v)
    $v = str_pad(decbin($v), 8, '0', STR_PAD_LEFT);
  $ipu = join('', $ipu);
  $res = false;
  foreach ($cidrs as $cidr) {
    $parts = explode('/', $cidr);
    $ipc = explode('.', $parts[0]);
    foreach ($ipc as &$v) $v = str_pad(decbin($v), 8, '0', STR_PAD_LEFT);
    $ipc = substr(join('', $ipc), 0, $parts[1]);
    $ipux = substr($ipu, 0, $parts[1]);
    $res = ($ipc === $ipux);
    if ($res) break;
    }
  return $res;
  }

用法:

$user_ip = $_SERVER['REMOTE_ADDR'];
$cidrs = array('192.168.1.20/27', '192.168.0.10/32'); 
if (testUserIP($user_ip, $cidrs)) {
  // user ip is ok
  }
else {
  // access denied
  }

【讨论】:

  • 我正在从数据库中获取 CIDR,我想根据用户的 IP 检查所有 CIDR
  • 更新您的问题并提供一些数据库示例(结构+数据)
  • 谢谢你的帮助你能告诉我你的foreach循环在哪里结束吗?
  • 我猜你说的是第一个例子foreach... if...。它在if 行结束的同一位置结束。对于单行命令,您不需要括号 { },并且 foreach 后面只有 if 行。所以我用foreach() if() {...}代替foreach() { if() {...} }
  • IPvsCIDR 函数中的 foreach 循环怎么样,它们也不需要任何括号?
【解决方案2】:

这个功能我已经用了一段时间了:

<?php

/**
 * Returns TRUE if given IPv4 address belongs to given network, FALSE otherwhise
 *
 * @param string $str_ip IP address in '127.0.0.1' format
 * @param string $str_range Network and mask as '127.0.0.0/8', '127.0.0.0/255.0.0.0' or '127.0.0.1'
 * @return bool
 *
 * @version v2011-08-30
 */
function ip_belongs_to_network($str_ip, $str_range){
    // Extract mask
    list($str_network, $str_mask) = array_pad(explode('/', $str_range), 2, NULL);
    if( is_null($str_mask) ){
        // No mask specified: range is a single IP address
        $mask = 0xFFFFFFFF;
    }elseif( (int)$str_mask==$str_mask ){
        // Mask is an integer: it's a bit count
        $mask = 0xFFFFFFFF << (32 - (int)$str_mask);
    }else{
        // Mask is in x.x.x.x format
        $mask = ip2long($str_mask);
    }

    $ip = ip2long($str_ip);
    $network = ip2long($str_network);
    $lower = $network & $mask;
    $upper = $network | (~$mask & 0xFFFFFFFF);

    return $ip>=$lower && $ip<=$upper;
}

更新:

在我的例子中,实际上所有的 CIDR 条目都在数据库中。我怎样才能 检查具有所有 tose CIDR 条目的 IP,foreach 循环是否可以工作?

这取决于您有多少条目。一个 PHP 循环适用于几个范围,但拥有 50 个范围意味着启动 50 个 SQL 查询!在这种情况下,您可能应该切换到数据库方法。例如,您可以将范围存储在两列(较低的 IP 地址和较高的 IP 地址)中。这样,您需要做的就是:

$sql = 'SELECT COUNT(1) AS belongs_to_at_least_one_range
    FROM ranges
    WHERE :remote_address BETWEEN lower_address AND upper_address
    LIMIT 1';
$params = array(
    'remote_address' => ip2long($_SERVER['REMOTE_ADDR']),
);

(如果需要,也可以获取所有匹配项。)

你能解释一下这行代码list($str_network, $str_mask) = array_pad(explode('/', $str_range), 2, NULL);

explode('/', $str_range)
// Split the string at `/` characters

array_pad(explode('/', $str_range), 2, NULL);
// Fill with NULLs if we have less than 2 items

list($str_network, $str_mask) =
// Store first part in `$str_network` and second part in `$str_mask`

【讨论】:

  • 您的代码有点复杂....实际上,在我的情况下,所有 CIDR 条目都在数据库中。如何检查具有所有 tose CIDR 条目的 IP,foreach 循环是否可以工作?你能解释一下这行代码 list($str_network, $str_mask) = array_pad(explode('/', $str_range), 2, NULL);
  • 是的,我在创作时需要匹配所有可能的格式(不仅是 CIDR)。我会在一分钟内更新。
  • 你在这个函数中返回了什么?
  • 如果给定的 IPv4 地址属于给定的网络,则返回 TRUE,否则返回 FALSE
猜你喜欢
  • 2019-02-24
  • 2017-01-14
  • 2019-11-09
  • 2016-12-27
  • 1970-01-01
  • 2010-12-28
  • 2019-10-26
  • 1970-01-01
相关资源
最近更新 更多