另一个视角
你好。您可以通过访问每个 8KB 长的两个数据块中的两个字节来检查地址是否被阻止。是的,我是认真的......请耐心等待,因为它需要一点时间来解释它。
理论
IP地址是一个地址,实际上是一个4字节的数字。
问题是,如果我们让它解决位位置怎么办?
答案:好吧,我们会有
2^32 = 4 Giga Bits
寻址空间,这将需要
4Gb/8 = 512 Mega Bytes
的分配。哎哟! 不过不用担心,我们不会阻止 ipverse 中的所有内容,512MB 有点夸张。
这可以为我们打开解决方案的道路。
小人国案
想象一个小人国世界,它只存在从 0 到 65535 的 IP 地址。所以地址就像 0.1 或 42.42 到 255.255。
现在这个世界之王想要封锁几个L-IP(小人国IP)地址。
首先,他构建了一个 256 * 256 位长的虚拟 2D 位图,占用:
64 K Bits = 8 K Bytes.
他决定封锁他讨厌的那个讨厌的“革命”网站,因为他是国王,例如地址是 56.28。
Address = (56 * 256) + 28 = 14364.(bit position in whole map)
Byte in map = floor(14364 / 8) = 1795.
Bit position= 14364 % 8 = 4.(modulus)
他打开地图文件,访问第 1795 个字节并设置第 4 位(通过 | 16),然后将其写回以将站点标记为被阻止。
当他的脚本看到 56.28 时,它会执行相同的计算并查看该位,如果已设置,则阻止该地址。
现在这个故事的寓意是什么?好吧,我们可以使用这种小人国结构。
实践
真实案例
我们可以通过“在需要时使用它”的方法将 Lilliputian 案例应用于现实世界,因为分配 512MB 文件不是一个好的选择。
想象一个名为 BLOCKS 的数据库表,其条目如下:
IpHead(key): unsigned 16 bit integer,
Map : 8KB BLOB(fixed size),
EntryCount : unsigned 16 bit integer.
另一个表只有一个条目,其结构如下,名为 BASE
Map : 8KB BLOB(fixed size).
现在假设您有一个传入地址 56.28.10.2
脚本访问 BASE 表并获取 Map。
它查找高阶IP 编号 56.28:
Address = (56 * 256) + 28 = 14364.(bit position in whole map)
Byte in map = floor(14364 / 8) = 1795.
Bit position= 14364 % 8 = 4.(modulus)
查看 Map 中的字节 1795 位 4。
如果未设置该位,则不需要进一步操作,这意味着 56.28.0.0 - 56.28.255.255 范围内没有阻止的 IP 地址。
如果设置了位,则脚本访问 BLOCKS 表。
高阶 IP 编号为 56.28,即 14364,因此脚本查询索引 IpHead = 14364 的 BLOCKS 表。获取记录。该记录应该存在,因为它是在 BASE 标记的。
脚本计算低阶IP 地址
Address = (10 * 256) + 2 = 2562.(bit position in whole map)
Byte in map = floor(2562 / 8) = 320.
Bit position= 2562 % 8 = 2.(modulus)
然后它通过查看字段 Map 的字节 320 的位 2 来检查地址是否被阻塞。
任务完成!
Q1:我们为什么要使用BASE,我们可以直接用14364查询BLOCKS。
A1:是的,我们可以,但是 BASE map 查找会比 BTREE 搜索任何数据库服务器更快。
Q2: BLOCKS 表中的 EntryCount 字段是干什么用的?
A2:是在同一记录的map字段中被阻塞的ip地址的数量。因此,如果我们解除阻塞 ip 并且 EntryCount 达到 0,那么 BLOCKS 记录就变得不必要了。它可以被擦除,并且 BASE map 上的相应位将被取消设置。
恕我直言,这种方法将闪电般快速。同样对于 blob 分配是每条记录 8K。由于 db 服务器将 blob 保存在单独的文件中,因此具有 4K、8K 或多个 4K 分页的文件系统会做出快速反应。
如果被阻止的地址过于分散
嗯,这是一个问题,这将使数据库 BLOCKS 表不必要地增长。
但对于这种情况,替代方法是使用 256*256*256 位立方体,它长 16777216 位,等于 2097152 字节 = 2MB。
对于我们之前的示例,更高的 IP 解析是:
(56 * 65536)+(28 * 256)+10
所以 BASE 将变成一个 2MB 的文件,而不是一个 db 表记录,它将被打开(fopen 等)并且位将通过查找(如 fseek,从不读取整个文件内容,不必要)然后访问具有以下结构的 BLOCKS 表:
IpHead(key): unsigned 32 bit integer, (only 24 bit is used)
Map : 32 unsigned 8 bit integers(char maybe),(256 bit fixed)
EntryCount : unsigned 8 bit integer.
这里是bitplane-bitplane(8K 8K)版本块检查的php示例代码:
旁注:这个脚本可以通过消除几个调用等来进一步优化。但是这样写是为了让它易于理解。
<?
define('BLOCK_ON_ERROR', true); // WARNING if true errors block everyone
$shost = 'hosturl';
$suser = 'username';
$spass = 'password';
$sdbip = 'database';
$slink = null;
$slink = mysqli_connect($shost, $suser, $spass, $sdbip);
if (! $slink) {
$blocked = BLOCK_ON_ERROR;
} else {
$blocked = isBlocked();
mysqli_close($slink); // clean, tidy...
}
if ($blocked) {
// do what ever you want when blocked
} else {
// do what ever you want when not blocked
}
exit(0);
function getUserIp() {
$st = array(
'HTTP_CLIENT_IP',
'REMOTE_ADDR',
'HTTP_X_FORWARDED_FOR'
);
foreach ( $st as $v )
if (! empty($_SERVER[$v]))
return ($_SERVER[$v]);
return ("");
}
function ipToArray($ip) {
$ip = explode('.', $ip);
foreach ( $ip as $k => $v )
$ip[$k] = intval($v);
return ($ip);
}
function calculateBitPos($IpH, $IpL) {
$BitAdr = ($IpH * 256) + $IpL;
$BytAdr = floor($BitAdr / 8);
$BitOfs = $BitAdr % 8;
$BitMask = 1;
$BitMask = $BitMask << $BitOfs;
return (array(
'bytePos' => $BytAdr,
'bitMask' => $BitMask
));
}
function getBaseMap($link) {
$q = 'SELECT * FROM BASE WHERE id = 0';
$r = mysqli_query($link, $q);
if (! $r)
return (null);
$m = mysqli_fetch_assoc($r);
mysqli_free_result($r);
return ($m['map']);
}
function getBlocksMap($link, $IpHead) {
$q = "SELECT * FROM BLOCKS WHERE IpHead = $IpHead";
$r = mysqli_query($link, $q);
if (! $r)
return (null);
$m = mysqli_fetch_assoc($r);
mysqli_free_result($r);
return ($m['map']);
}
function isBlocked() {
global $slink;
$ip = getUserIp();
if($ip == "")
return (BLOCK_ON_ERROR);
$ip = ipToArray($ip);
// here you can embed preliminary checks like ip[0] = 10 exit(0)
// for unblocking or blocking address range 10 or 192 or 127 etc....
// Look at base table base record.
// map is a php string, which in fact is a good byte array
$map = getBaseMap($slink);
if (! $map)
return (BLOCK_ON_ERROR);
$p = calculateBitPos($ip[0], $ip[1]);
$c = ord($map[$p['bytePos']]);
if (($c & $p['bitMask']) == 0)
return (false); // No address blocked
// Look at blocks table related record
$map = getBlocksMap($slink, $p[0]);
if (! $map)
return (BLOCK_ON_ERROR);
$p = calculateBitPos($ip[2], $ip[3]);
$c = ord($map[$p['bytePos']]);
return (($c & $p['bitMask']) != 0);
}
?>
我希望这有帮助。
如果您对详细信息有任何疑问,我将很乐意为您解答。