首页 文章

在CIDR范围MySQL中找到IPv6的正确方法

提问于
浏览
3

我有一个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运算符进行比较 .

我的实施:

Get last 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

Get first 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

这很好用!我只是想到使用一些聪明的位操作可以更有效地完成它 . 我已经阅读了很多关于这个主题的问题,但我并没有真正找到具体的解决方案任何正确方向的帮助都会非常感激!

NOTE :只有IPv6,我才能以正确的方式实现IPv4 .

2 回答

  • 1

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

    这条线:

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

    应该是:

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

  • 1

    我在 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);
    

相关问题