【问题标题】:Proper way to find IPv6 in CIDR Range MySQL在 CIDR 范围 MySQL 中查找 IPv6 的正确方法
【发布时间】:2013-05-17 14:37:11
【问题描述】:

我有一个 MySQL 黑名单表,其中存储了两种类型之一的单个 IPv4、IPv6 或 CIDR 范围。

我的桌子看起来有点像这样:

 +-----------+-------------+
 | Name      | Type        |
 +-----------+-------------+
 | IpAddress | VARCHAR(46) |
 +-----------+-------------+
 | Mask      | INT(2)      |
 +-----------+-------------+

现在,我想检查给定的 IP 地址是否在已保存的 CIDR 范围之一中。 经过几天的研究,我已经完成了这一点,最后只是将以下解决方案移植到 MySQL:PHP5 calculate IPv6 range from cidr prefix?

因此,我将 IP + CIDR 掩码转换为该范围内的第一个和最后一个 IP,然后使用 INET6_ATON 将其转换为数字,并将其与 BETWEEN 运算符进行比较。

我的实现:

获取最后一个ip

FUNCTION (`Ip` VARCHAR(46), `Mask` INT(2) UNSIGNED) RETURNS varchar(39)
BEGIN
    DECLARE IpNumber VARBINARY(16);
    DECLARE Last VARCHAR(39) DEFAULT '';
    DECLARE FlexBits, Counter, Deci, NewByte INT UNSIGNED;
    DECLARE HexIp VARCHAR(32);

    SET IpNumber = INET6_ATON(Ip);
    SET HexIp    = HEX(IpNumber);
    SET FlexBits = 128 - Mask;
    SET Counter  = 32;

    WHILE (FlexBits > 0) DO
        SET Deci    = CONV(SUBSTR(HexIp, Counter, 1), 16, 10);
        SET NewByte = Deci | (POW(2, LEAST(4, FlexBits)) - 1);
        SET Last    = CONCAT(CONV(NewByte, 10, 16), Last);

        IF FlexBits >= 4 THEN SET FlexBits = FlexBits - 4;
        ELSE SET FlexBits = 0;
        END IF;

        SET Counter  = Counter - 1;
    END WHILE;

    SET Last = CONCAT(SUBSTR(HexIp, 1, Counter), Last);

    RETURN INET6_NTOA(UNHEX(Last));
END

获取第一个ip

FUNCTION (`Ip` VARCHAR(46), `Mask` INT(2) UNSIGNED, `WithMask` BOOLEAN) RETURNS varchar(39)
BEGIN
    DECLARE First VARCHAR (42) DEFAULT '';

    SET First = INET6_NTOA(UNHEX(RPAD(SUBSTR(HEX(INET6_ATON(Ip)), 1, Mask / 4), 32, 0)));

    IF (WithMask = 1) THEN
        SET First = CONCAT(First, '/', CAST(Mask AS CHAR));
    END IF;

    RETURN First;
END

这很好用!我只是认为使用一些聪明的位操作可以更有效地完成它。 我已经阅读了大量关于这个主题的问题,但我并没有真正找到具体的解决方案 任何正确方向的帮助将不胜感激!

注意:仅适用于 IPv6,我以正确的方式实现了 IPv4。

【问题讨论】:

  • 我知道这已经很老了,但是 MYSQL 改变了它执行 FUNCTIONS 的方式吗? 5.6 根本不会接受这个。

标签: mysql bit-manipulation ipv6


【解决方案1】:

我在一年前以同样的方式为 MySQL 实现了这个。所以这个想法很好。但是,您的代码中有一个小但相当重要的错误。

行:

SET Last = CONCAT(Last, CONV(NewByte, 10, 16));

应该是:

SET Last = CONCAT(CONV(NewByte, 10, 16), Last);

【讨论】:

  • 很好看,谢谢!这可能会在未来的某个地方引起真正的掌声时刻。尽管对于您放入的大多数 IP 而言,这并不重要。
【解决方案2】:

我在您的 Get last ip 功能中发现了一个错误。

而不是这个:

SET FlexBits = FlexBits - 4;

使用这个:

IF FlexBits >= 4 THEN SET FlexBits = FlexBits - 4;
ELSE SET FlexBits = 0;
END IF;

否则你会得到这样的错误信息:

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(FlexBits@4 - 4)'

当您尝试获取这些 IPv6 范围的最后一个 IP 时(其中掩码不能被 4 整除):

SELECT getLastIp("2001:200:1::", 47);
SELECT getLastIp("2001:200:0:8000", 49);
SELECT getLastIp("2001:470:1f06:2000::", 51);
SELECT getLastIp("2001:470:0:284:2::", 79);
SELECT getLastIp("2001:470:1f08:415::8:0", 109);
SELECT getLastIp("2001:550:0:1000::8275:8000", 113);
SELECT getLastIp("2001:550:0:1000::9a19:300", 123);
SELECT getLastIp("2001:550:0:1000::9a19:320", 126);

【讨论】:

    猜你喜欢
    • 2020-10-19
    • 2022-07-05
    • 2021-06-29
    • 2012-04-22
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-30
    相关资源
    最近更新 更多