7

我有一个存储为无符号整数的 IP 地址(ipNumeric)的表 A 和一个带有子网(subnetNumeric)的表 B:

INET_NTOA(ipNumeric) = 192.168.0.1
INET_NTOA(subnetNumeric) = 192.168.0.0

我想检查这个 IP 是否是子网的成员。

子网是 A、B 和 C 类。

这是否可以在 MySQL 中在合理的时间内即时完成,或者是否应该预先计算子网范围?

4

4 回答 4

14

当然,这是可行的。这个想法是,我们通过将最高有效位设置为 1 来计算子网掩码,与子网类规定的一样多。对于 C 类,那将是

SELECT -1 << 8;

然后,将子网掩码与您拥有的 IP 地址相结合;如果 IP 在子网内,则结果应等于子网地址——标准网络内容。所以我们最终得到:

SELECT (-1 << 8) & INET_ATON("192.168.0.1") = INET_ATON("192.168.0.0");

更新:是的,有必要知道网络类子网掩码(这是等效信息)。X.Y.0.0考虑一下如果您没有此信息,您将如何处理子网所在的情况。这是X.Y.0.0/16还是X.Y.0.0/8第三个八位字节恰好是0?没办法知道。

如果您确实知道子网掩码,则查询可以写为

SELECT (-1 << (33 - INSTR(BIN(INET_ATON("255.255.255.0")), "0"))) &
       INET_ATON("192.168.0.1") = INET_ATON("192.168.0.0");
于 2012-04-03T22:00:31.400 回答
1

这是我们使用的 MySQL 函数。

DELIMITER $$
DROP FUNCTION IF EXISTS `ip_in_subnet_mask`$$
CREATE DEFINER=`root`@`%` FUNCTION `ip_in_subnet_mask`(ip VARCHAR(20), subnet VARCHAR(20), netmask VARCHAR(20)) RETURNS TINYINT(1)
    DETERMINISTIC
BEGIN
    RETURN (INET_ATON(ip) & INET_ATON(netmask)) = INET_ATON(subnet);
END$$
DELIMITER ;

例如,查找 t.ip 在 192.168.0.x 范围内的所有行,其中 0<=x<=255

 SELECT *
 FROM t
 WHERE
    ip_in_subnet_mask(t.ip, "192.168.0.0", "255.255.255.0")
于 2015-04-27T09:45:15.203 回答
1

首先创建表来支持测试。

CREATE TABLE a (ipNumeric INT UNSIGNED);
INSERT INTO a (ipNumeric) VALUES(INET_ATON("172.16.40.101"));
INSERT INTO a (ipNumeric) VALUES(INET_ATON("192.168.1.12"));
INSERT INTO a (ipNumeric) VALUES(INET_ATON("10.1.5.51"));
INSERT INTO a (ipNumeric) VALUES(INET_ATON("10.7.1.78"));      
CREATE TABLE b (subnetNumeric INT UNSIGNED);
INSERT INTO b (subnetNumeric) VALUES (INET_ATON("192.168.0.0"));
INSERT INTO b (subnetNumeric) VALUES (INET_ATON("10.1.0.0"));
INSERT INTO b (subnetNumeric) VALUES (INET_ATON("172.16.0.0"));

现在根据地址和掩码 = 子网进行选择查找表之间的匹配项。这里我们假设 B 类子网 (255.255.0.0)。无需移位,因为我们可以使用 INET_ATON() 函数。

SELECT INET_NTOA(a.ipNumeric),INET_NTOA(b.subnetNumeric) FROM a,b 
       WHERE (a.ipNumeric & INET_ATON("255.255.0.0")) = b.subnetNumeric;

+------------------------+----------------------------+
| INET_NTOA(a.ipNumeric) | INET_NTOA(b.subnetNumeric) |
+------------------------+----------------------------+
| 192.168.1.12           | 192.168.0.0                |
| 10.1.5.51              | 10.1.0.0                   |
| 172.16.40.101          | 172.16.0.0                 |
+------------------------+----------------------------+
于 2012-04-03T22:36:28.000 回答
0

当地址存储为字符串时,我有一个解决方案。因此,如果您希望将那部分作为您的算法,您可以在此处使用我的解决方案。

这有点棘手,尤其是对于 IPv6。在 IPv6 中,可以选择压缩段,例如 1::1,相当于 1:0:0:0:0:0:0:1,这是它很棘手的主要原因。

IPAddress Java 库将生成 mysql SQL 来搜索给定子网中的地址。该链接更详细地描述了这个问题。免责声明:我是项目经理。

基本算法是取子网地址的网络部分,然后取该部分的每个变体(例如上面的两个字符串是完整地址 1::1 的变体),然后计算段分隔符的数量,然后执行正在匹配的地址上的 mysql 子字符串,但也计算正在匹配的地址中的总分隔符。

这是示例代码:

public static void main(String[] args) throws IPAddressStringException {
    System.out.println(getSearchSQL("columnX", "1.2.3.4/16"));
    System.out.println(getSearchSQL("columnX", "1:2:3:4:5:6:7:8/64"));
    System.out.println(getSearchSQL("columnX", "1::8/64"));
}

static String getSearchSQL(String expression, String ipAddressStr) throws IPAddressStringException {
    IPAddressString ipAddressString = new IPAddressString(ipAddressStr);
    IPAddress ipAddress = ipAddressString.toAddress();
    IPAddressSection networkPrefix = ipAddress.getNetworkSection(ipAddress.getNetworkPrefixLength(), false);
    StringBuilder sql = new StringBuilder("Select rows from table where ");
    networkPrefix.getStartsWithSQLClause(sql, expression);
    return sql.toString();
}

输出:

Select rows from table where (substring_index(columnX,'.',2) = '1.2')
Select rows from table where (substring_index(columnX,':',4) = '1:2:3:4')
Select rows from table where ((substring_index(columnX,':',4) = '1:0:0:0') OR ((substring_index(columnX,':',2) = '1:') AND (LENGTH (columnX) - LENGTH(REPLACE(columnX, ':', '')) <= 5)))
于 2016-10-19T19:41:05.473 回答