別の視点
こんにちは。それぞれ 8KB の長さの 2 つのデータ チャンクで 2 バイトにアクセスすることで、アドレスがブロックされているかどうかを確認できます。はい、私は本気です...説明するのに少し時間がかかるので、しばらくお待ちください。
法則
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 (liliput 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 を検出すると、同じ計算を実行してビットを調べ、設定されている場合はアドレスをブロックします。
さて、物語の教訓は何ですか?さて、このリリプティアン構造を使用できます。
練習
実世界のケース
512MB のファイルを割り当てることは適切な選択ではないため、「必要なときに使用する」というアプローチでリリプティアンのケースを現実の世界に適用できます。
次のようなエントリを持つ BLOCKS という名前のデータベース テーブルを考えてみてください。
IpHead(key): unsigned 16 bit integer,
Map : 8KB BLOB(fixed size),
EntryCount : unsigned 16 bit integer.
そして、BASE という名前の下の構造を持つエントリが 1 つだけの別のテーブル
Map : 8KB BLOB(fixed size).
ここで、受信アドレスが 56.28.10.2 であるとします。
スクリプトは BASE テーブルにアクセスし、マップを取得します。
上位の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)
マップのバイト 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:はい、可能ですが、データベース サーバーの BTREE 検索よりも BASE マップ検索の方が高速です。
Q2: BLOCKS テーブルの EntryCount フィールドは何のためにありますか?
A2:同じレコードのマップ フィールドでブロックされた IP アドレスの数です。したがって、IP のブロックを解除して EntryCount が 0 に達すると、その BLOCKS レコードは不要になります。これは消去でき、BASE マップの対応するビットは設定解除されます。
IMHOこのアプローチは非常に高速です。また、ブロブの割り当ては、レコードごとに 8K です。db サーバーはブロブを個別のファイルに保持するため、4K、8K、または 4K の倍数のページングを持つファイル システムは高速に反応します。
ブロックされたアドレスが分散している場合
これは、データベースの BLOCKS テーブルが不必要に大きくなる懸念事項です。
しかし、そのような場合の代替手段は、2097152 バイト = 2MB に相当する 16777216 ビット長の 256*256*256 ビット キューブを使用することです。
前の例では、Higher Ip 解決は次のとおりです。
(56 * 65536)+(28 * 256)+10
したがって、BASE は db テーブル レコードの代わりに 2MB のファイルになり、開かれ (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);
}
?>
これが役立つことを願っています。
詳細についてご質問がございましたら、喜んでお答えいたします。