我有一个存储为无符号整数IP地址(ipNumeric),并与子网的表B(subnetNumeric)的表答:检查IP是子网
INET_NTOA(ipNumeric) = 192.168.0.1
INET_NTOA(subnetNumeric) = 192.168.0.0
我想检查这个IP是子网的成员。
的子网A,B和C类
这是可能在合理的时间做在MySQL上飞还是应该的子网范围预计算?
我有一个存储为无符号整数IP地址(ipNumeric),并与子网的表B(subnetNumeric)的表答:检查IP是子网
INET_NTOA(ipNumeric) = 192.168.0.1
INET_NTOA(subnetNumeric) = 192.168.0.0
我想检查这个IP是子网的成员。
的子网A,B和C类
这是可能在合理的时间做在MySQL上飞还是应该的子网范围预计算?
当然,这是可行的。我们的想法是,我们通过将最高有效位设置为1来计算子网掩码,这与子网类所规定的一样多。对于C类,这将是
SELECT -1 << 8;
然后,AND子网掩码与您拥有的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");
首先创建表,以支持测试。
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 |
+------------------------+----------------------------+
这是我们使用的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")
我有当地址被存储为字符串的解决方案。所以如果你想做出算法的那部分,你可以在这里使用我的解决方案。
这有点棘手,特别是对于IPv6。在IPv6中,可以有选择地使用压缩段,如1 :: 1,这相当于1:0:0:0:0:0:0:1,这是棘手的主要原因。
The IPAddress Java library将产生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();
}
Ouptut:
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)))
谢谢,但是这依赖于知道子网类? – matiasf 2012-04-03 22:09:50
@matiasf:是的。看看更新。 – Jon 2012-04-03 22:27:41
很好的答案 - 但IPv6呢?这甚至有可能吗? – TSG 2016-10-18 02:11:02