【问题标题】:Is there way to match IP with IP+CIDR straight from SELECT query?有没有办法直接从 SELECT 查询中将 IP 与 IP+CIDR​​ 匹配?
【发布时间】:2010-10-10 09:25:23
【问题描述】:

类似

SELECT COUNT(*) AS c FROM BANS WHERE typeid=6 AND (SELECT ipaddr,cidr FROM BANS) MATCH AGAINST 'this_ip';

因此,您不必先从数据库中获取所有记录,然后逐个匹配它们。

如果 c > 0 则匹配。

BANS 表:

id int auto incr PK
typeid TINYINT (1=hostname, 4=ipv4, 6=ipv6)
ipaddr BINARY(128)
cidr INT
host VARCHAR(255)

数据库:MySQL 5

查询时知道 IP 和 IPv 类型(4 或 6)。

IP 是例如 ::1 的二进制格式

BANNED IP 例如 ::1/64

【问题讨论】:

  • raspi,我知道这个问题很老,但是......你的 cidr 列是网络掩码中的一位数,所以对于 ipv6,它总是 64,对于 ipv4,它是一个介于0和32?虽然我想 0 会禁止所有地址... :)

标签: mysql ip mask bitmask cidr


【解决方案1】:

请记住,IP 不是文本地址,而是数字 ID。我有类似的情况(我们正在做geo-ip查找),如果您将所有IP地址存储为整数(例如,我的IP地址是192.115.22.33所以它存储为3228767777),那么您可以查找IP使用右移运算符很容易。

所有这些类型的查找的缺点是您无法从索引中受益,并且每次查找时都必须进行全表扫描。上述方案可以通过存储CIDR网络的网络IP地址(范围的开始)和广播地址(范围的结束)来改进,例如存储192.168.1.0/24可以存储两个列:

network     broadcast
3232235776, 3232236031 

然后你就可以简单地匹配它

SELECT count(*) FROM bans WHERE 3232235876 >= network AND 3232235876 <= broadcast

这将使您可以将 CIDR 网络存储在数据库中,并利用快速数字索引将它们与 IP 地址快速有效地匹配。

以下讨论的注意事项

MySQL 5.0 包含一个名为“index merge intersect”的范围查询优化,它允许加速此类查询(并避免全表扫描),只要:

  • 有一个多列索引按顺序与查询中的列完全匹配。所以 - 对于上面的查询示例,索引需要是 (network, broadcast)
  • 可以从索引中检索所有数据。这适用于 COUNT(*),但不适用于 SELECT * ... LIMIT 1

MySQL 5.6 包含一个称为 MRR 的优化,它也可以加快全行检索,但这超出了此答案的范围。

【讨论】:

  • 不幸的是,MySQL 无法将两个索引组合在一起。它当然会尝试在网络或广播上使用索引,但由于 IP 地址分布均匀,在这种情况下全表扫描会更有效率。
  • 这个神话有点过​​时了 :-) 从 MySQL 5.0 开始,服务器可以合并多个索引 (dev.mysql.com/doc/refman/5.1/en/index-merge-optimization.html)。无论如何,我看不出全表扫描比使用索引更好,即使它只有 1 个索引。
  • 如果您的过滤器返回超过大约 10% 或行,则全表扫描更好。试试看:)
  • 而且你这里不需要 COUNT(*) , SELECT ... LIMIT 1 足以禁止 :)
  • 而 MySQL 不能将两个范围条件与 AND 子句结合起来:dev.mysql.com/doc/refman/5.1/en/index-merge-intersection.html
【解决方案2】:

对于IPv4,您可以使用:

SET @length = 4;

SELECT  INET_NTOA(ipaddr), INET_NTOA(searchaddr), INET_NTOA(mask)
FROM  (
  SELECT
        (1 << (@length * 8)) - 1 & ~((1 << (@length * 8 - cidr)) - 1) AS mask,
        CAST(CONV(SUBSTR(HEX(ipaddr), 1, @length * 2), 16, 10) AS DECIMAL(20)) AS ipaddr,
        CAST(CONV(SUBSTR(HEX(@myaddr), 1, @length * 2), 16, 10) AS DECIMAL(20)) AS searchaddr
  FROM  ip
) ipo
WHERE ipaddr & mask = searchaddr & mask

【讨论】:

  • 谢谢。我很确定我不需要表格来执行 CIDR,但可以像这样动态计算/创建掩码。我相信掩码可以简化为 ~((1
【解决方案3】:

IPv4 地址、网络地址和网络掩码都是 UINT32 数字,并以人类可读的形式表示为“点四边形”。当检查地址是否在给定的网络空间(网络/网络掩码)中时,内核中的路由表代码执行非常快速的逐位与比较。这里的技巧是将点分四组 IP 地址、网络地址和网络掩码存储为 UINT32 表中,然后执行相同的 32 位逐位与匹配。例如

SET @test_addr = inet_aton('1.2.3.4');
SET @network_one = inet_aton('1.2.3.0');
SET @network_two = inet_aton('4.5.6.0');
SET @network_netmask = inet_aton('255.255.255.0');

SELECT (@test_addr & @network_netmask) = @network_one AS IS_MATCHED;
+------------+
| IS_MATCHED |
+------------+
|          1 |
+------------+

SELECT (@test_addr & @network_netmask) = @network_two AS IS_NOT_MATCHED;
+----------------+
| IS_NOT_MATCHED |
+----------------+
|              0 |
+----------------+

【讨论】:

    【解决方案4】:

    将 IP 地址范围生成为整数

    如果您的数据库不支持花哨的按位运算,您可以使用基于整数的简化方法。

    以下示例使用的是 PostgreSQL:

    select (cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 1) as bigint) * (256 * 256 * 256) +
            cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 2) as bigint) * (256 * 256      ) +
            cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 3) as bigint) * (256            ) +
            cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 4) as bigint)) 
            as network,
    
           (cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 1) as bigint) * (256 * 256 * 256) +
            cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 2) as bigint) * (256 * 256      ) +
            cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 3) as bigint) * (256            ) +
            cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 4) as bigint)) + cast(
              pow(256, (32 - cast(split_part('4.0.0.0/8', '/', 2) as bigint)) / 8) - 1 as bigint
            ) as broadcast;
    

    【讨论】:

      【解决方案5】:

      嗯。您可以建立一个 cidr 掩码表,加入它,然后将 ip 与(MySQL 中的&amp;)与带有禁止块 ipaddress 的掩码进行比较。那会做你想要的吗?

      如果您不想构建掩码表,则可以将掩码计算为 -1 &lt;&lt; (x-cidr)x = 6432 取决于。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-12
        • 2022-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多