我有一个 MySQL 黑名单表,其中存储了两种类型之一的单个 IPv4、IPv6 或 CIDR 范围。
我的桌子看起来有点像这样:
+-----------+-------------+
| Name | Type |
+-----------+-------------+
| IpAddress | VARCHAR(46) |
+-----------+-------------+
| Mask | INT(2) |
+-----------+-------------+
现在,我想检查给定的 IP 地址是否在已保存的 CIDR 范围之一中。经过几天研究大量页面后,我已经完成了这项工作,最后将以下解决方案移植到 MySQL:PHP5 从 cidr 前缀计算 IPv6 范围?
因此,我将 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。