2012-04-03 54 views
5

我有一个存储为无符号整数IP地址(ipNumeric),并与子网的表B(subnetNumeric)的表答:检查IP是子网

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

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

的子网A,B和C类

这是可能在合理的时间做在MySQL上飞还是应该的子网范围预计算?

回答

11

当然,这是可行的。我们的想法是,我们通过将最高有效位设置为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/16X.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"); 
+0

谢谢,但是这依赖于知道子网类? – matiasf 2012-04-03 22:09:50

+0

@matiasf:是的。看看更新。 – Jon 2012-04-03 22:27:41

+0

很好的答案 - 但IPv6呢?这甚至有可能吗? – TSG 2016-10-18 02:11:02

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     | 
+------------------------+----------------------------+ 
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") 
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)))