简体   繁体   English

如何在MySQL中使用INET_ATON进行通配符搜索IP地址?

[英]How to do wildcard search for IP addresses using INET_ATON in MySQL?

I found this method to store IP addresses in MySQL database as integer using INET_ATON: https://stackoverflow.com/a/5133610/4491952 我发现此方法使用INET_ATON将IP地址存储在MySQL数据库中为整数: https ://stackoverflow.com/a/5133610/4491952

Since IPv4 addresses are 4 byte long, you could use an INT ( UNSIGNED ) that has exactly 4 bytes: 由于IPv4地址的长度为4个字节,因此可以使用一个正好为4个字节的INTUNSIGNED

`ipv4` INT UNSIGNED

And INET_ATON and INET_NTOA to convert them: 然后使用INET_ATONINET_NTOA进行转换:

INSERT INTO `table` (`ipv4`) VALUES (INET_ATON("127.0.0.1"));
SELECT INET_NTOA(`ipv4`) FROM `table`;

For IPv6 addresses you could use a BINARY instead: 对于IPv6地址,您可以改用BINARY

`ipv6` BINARY(16)

And use PHP's inet_pton and inet_ntop for conversion: 并使用PHP的inet_ptoninet_ntop进行转换:

'INSERT INTO `table` (`ipv6`) VALUES ("'.mysqli_real_escape_string(inet_pton('2001:4860:a005::68')).'")'
'SELECT `ipv6` FROM `table`'
$ipv6 = inet_pton($row['ipv6']);

But how can I do a wildcard search, for example 192.168.%, using INET_ATON and PHP's ip2long function? 但是,如何使用INET_ATON和PHP的ip2long函数进行通配符搜索,例如192.168。%?

Wildcard search operates on strings and, since it can't normally benefit from indexes, it tends to be extremely slow. 通配符搜索对字符串进行操作,并且由于通常无法从索引中受益,因此通配符搜索的速度往往非常慢。

If you store IP addresses in a normalised representation aimed at machines (vs the human-readable dot-notation) you can treat them as if they were numbers, use many standard operators and make good use of indexes. 如果以针对机器的标准化表示形式存储IP地址(相对于人类可读的点符号),则可以将它们视为数字,使用许多标准运算符并充分利用索引。 An example: 一个例子:

SELECT *
FROM foo
WHERE dot_notation LIKE '192.168.%';

... can be rewritten as: ...可以改写为:

SELECT *
FROM foo
WHERE as_integer BETWEEN INET_ATON('192.168.0.0') AND INET_ATON('192.168.255.255');

Even these INET_ATON() instances are for mere readability, you could just enter the resulting integer. 即使这些INET_ATON()实例仅出于可读性,您也可以输入结果整数。 If you use PHP it's trivial because you can outsource it to PHP: 如果您使用PHP,那么这很简单,因为您可以将其外包给PHP:

$sql = 'SELECT *
    FROM foo
    WHERE as_integer BETWEEN ? AND ?';
$params = [
   // Not sure whether you still need the sprintf('%u') trick in 64-bit PHP
   ip2long('192.168.0.0'), ip2long('192.168.255.255')
];

I cannot test it right now but I understand this should work with IPv6 as well. 我现在无法对其进行测试,但我知道这也适用于IPv6。

One neat trick MySQL offers is bit shifting. MySQL提供的一项巧妙技巧是位移。 You can use it to see if an ip is contained within an address block written in cidr notation. 您可以使用它查看以cidr表示的地址块中是否包含ip。 You can use this method treating your addresses as XXXX/16 cidr block. 您可以使用此方法将您的地址视为XXXX / 16 cidr块。

set @cidr_block:='10.20.30.40/16';

select inet_ntoa(inet_aton(substring_index(@cidr_block,'/',1))>>(32-substring_index(@cidr_block,'/',-1))<<(32-substring_index(@cidr_block,'/',-1))) as first_ip,
                 inet_aton(substring_index(@cidr_block,'/',1))>>(32-substring_index(@cidr_block,'/',-1))<<(32-substring_index(@cidr_block,'/',-1))  as first_ip_num,
        inet_ntoa((((inet_aton(substring_index(@cidr_block,'/',1))>>(32-substring_index(@cidr_block,'/',-1)))+1)<<(32-substring_index(@cidr_block,'/',-1)))-1) as last_ip,
                 (((inet_aton(substring_index(@cidr_block,'/',1))>>(32-substring_index(@cidr_block,'/',-1)))+1)<<(32-substring_index(@cidr_block,'/',-1)))-1  as last_ip_num
;
+-----------+--------------+---------------+-------------+
| first_ip  | first_ip_num | last_ip       | last_ip_num |
+-----------+--------------+---------------+-------------+
| 10.20.0.0 |    169082880 | 10.20.255.255 |   169148415 |
+-----------+--------------+---------------+-------------+
1 row in set (0.00 sec)

Shortcut to seeing if an ip is in the address block - simply sift both cidr address and ip to see if they are the same. 查看IP地址是否在地址块中的快捷方式-只需筛查cidr地址和ip以查看它们是否相同。 Of course, this will be a table scan if applied to stored values. 当然,如果应用于存储的值,这将是表扫描。

select inet_aton('127.0.0.1')>>16 = inet_aton('127.0.10.20')>>16 as `1 = true`;
+----------+
| 1 = true |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

select inet_aton('127.0.0.1')>>16 = inet_aton('127.10.10.20')>>16 as `0 =  false`;
 +-----------+
 | 0 = false |
 +-----------+
 |         0 |
 +-----------+
 1 row in set (0.00 sec)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM