5

我需要存储一个带有通配符的 IP 地址,例如 10.0.%.1。然后,我希望从我的查询中找到 IP 地址在允许范围 (10.0.0-255.1) 内的任何人。

你们能帮帮我吗?我已经尝试存储 10.0.%.1 但无法使用 LIKE 查询找到它(我什至不知道这是否是这样做的方法 - 可能不是)。

提前致谢。

4

3 回答 3

3

将 IP 地址转换为 32 位整数,并为地址和掩码创建一列。您可以使用INET_NTOA()INET_ATON()功能。您的 10.0.%.1 范围可以表示为网络 10.0.0.1,掩码为 255.255.0.255。要查看给定 IP 地址是否与此网络匹配,请将掩码应用于 IP 地址,并将其与网络地址进行比较。如果它们匹配,则地址在网络中。例如,如果您想测试 10.0.4.1,您可以使用按位与将掩码应用于它:

10.0.4.1 & 255.255.0.255 = 10.0.0.1

这与您的网络地址匹配,因此该 IP 在网络中。

您可以像这样将此网络存储在您的表中(ip 和掩码是整数):

CREATE TABLE networks (id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, ip INT, mask INT);

INSERT INTO networks (ip, mask)
VALUES (INET_ATON('10.0.0.1'), INET_ATON('255.255.0.255'));

您可以查看给定 IP (10.0.23.1) 是否匹配,如下所示:

SELECT * FROM networks
WHERE ip & mask = INET_ATON('10.0.23.1') & mask

但是,如果您希望它们可读,这会将 IP 选择为整数:

SELECT id, INET_NTOA(ip) AS ipstring, INET_NTOA(mask) AS maskstring
FROM networks
WHERE ip & mask = INET_ATON('10.0.23.1') & mask

这允许您测试单个给定 IP 地址是否与网络匹配。如果要测试一系列 IP,则必须将掩码应用于该范围内的每个 IP,并按上述方式测试每个 IP。您可以在单个查询中完成,只需列出您正在测试的每个 IP 地址。

于 2012-10-01T12:11:19.640 回答
2

您如何将 IP 地址保存为数据库中的 4 个整数,如下所示:

IPpart1 IPpart2 IPpart3 IPpart4
-------------------------------
127     0       0       1

并使它们可以为空。(NULL 将成为我们的通配符)

然后,您可以像这样对数据库进行查询:

SELECT * FROM iptable WHERE (IPpart1 IS NULL OR IPpart1 = '$firstpart') AND (IPpart2 IS NULL OR IPpart2 = '$secondpart') AND (IPpart3 IS NULL OR IPpart3 = '$thirdpart') AND (IPpart4 IS NULL OR IPpart4 = '$fourthpart')

这有点冗长,它可以完成工作。

甚至应该可以从普通 IP 字段构造具有该结构的临时表,但是如果您有很多请求,那将是很多开销。

于 2012-10-01T12:08:25.330 回答
0

我仔细查看了 MySQL 文档页面,并在那里找到了符合您需求的帖子:

SELECT '10.0.0.1' AS IP0,
( SUBSTRING_INDEX( '10.0.0.1', '.', 1 ) * 16777216 +
SUBSTRING_INDEX(SUBSTRING_INDEX( '10.0.0.1', '.', 2 ),'.',-1) * 65536 +
SUBSTRING_INDEX(SUBSTRING_INDEX( '10.0.0.1', '.', -2 ),'.',1) * 256 +
SUBSTRING_INDEX( '10.0.0.1', '.', -1 )
),
'10.0.255.1' AS IP1,
    ( SUBSTRING_INDEX( '10.0.255.1', '.', 1 ) * 16777216 +
    SUBSTRING_INDEX(SUBSTRING_INDEX( '10.0.255.1', '.', 2 ),'.',-1) * 65536 +
    SUBSTRING_INDEX(SUBSTRING_INDEX( '10.0.255.1', '.', -2 ),'.',1) * 256 +
    SUBSTRING_INDEX( '10.0.255.1', '.', -1 )
) AS IP2Num1

返回每个 IP 的数值:

Ip:  |  10.0.0.1  | 10.0.255.1 |
--------------------------------
Num: | 167772161  | 167837441  |

换句话说:

SELECT foo 
FROM bar.foobar
WHERE ( SUBSTRING_INDEX( ipField, '.', 1 ) * 16777216 +
    SUBSTRING_INDEX(SUBSTRING_INDEX( ipField, '.', 2 ),'.',-1) * 65536 +
    SUBSTRING_INDEX(SUBSTRING_INDEX( ipField, '.', -2 ),'.',1) * 256 +
    SUBSTRING_INDEX( ipField, '.', -1 )
) BETWEEN 167772161 AND 167837441;

好吧,你可以这样做:

WHERE ipField LIKE '10.0.%.1'
AND (CAST
       (REPLACE(SUBSTRING_INDEX(ipField,'.',3),
                CONCAT(SUBSTRING_INDEX(ipField ,'.',2),'.')
                ,'')
     AS UNSIGNED) BETWEEN 0 AND 255) 

我使用以下查询进行了测试:

SELECT IF ('10.0.12.1' LIKE '10.0.%.1'
       AND (CAST
             (REPLACE( SUBSTRING_INDEX('10.0.12.1','.',3),
                 CONCAT(SUBSTRING_INDEX('10.0.12.1','.',2),'.')
              ,'')
            AS UNSIGNED) BETWEEN 0 AND 255),
      TRUE,
      FALSE);

它返回了1,但是,老实说,这有点小题大做,不是吗?为什么不简单:

WHERE ipField REGEXP '10\\.0\\.[0-9]{1,3}\\.1'

你可以稍微调整一下这个表达式,但基本上,在我看来,这似乎是查询这些 IP 地址的最简单方法。
有关 MySQL 字符串函数的更多信息(编写起来几乎总是很痛苦),请参阅此处的文档

于 2012-10-01T12:19:05.633 回答