选择IP范围内的CIDR

时间:2017-08-13 00:28:03

标签: mysql select cidr

所以我有一个类似45.76.255.14的IP,我有一个CIDR行存储为单个varchar的表,怎么会 我选择该IP地址范围内的CIDR。例如45.76.255.14/31

理论上:选择IP范围内的CIDR

2 个答案:

答案 0 :(得分:1)

在这个问题的帮助下:MySQL query to convert CIDR into IP range

以下是适用于我的解决方案:

SELECT
    `cidr`
FROM
    cidr_list
WHERE
    INET_ATON('IP') BETWEEN(
        INET_ATON(SUBSTRING_INDEX(`cidr`, '/', 1)) & 0xffffffff ^(
            (
                0x1 <<(
                    32 - SUBSTRING_INDEX(`cidr`, '/', -1)
                )
            ) -1
        )
    ) AND(
        INET_ATON(SUBSTRING_INDEX(`cidr`, '/', 1)) |(
            (
                0x100000000 >> SUBSTRING_INDEX(`cidr`, '/', -1)
            ) -1
        )
    )

答案 1 :(得分:1)

VARCHAR中以点分四位表示法存储IP地址不是存储它们的最佳方式,因为dotted-quad是32位无符号整数的人类友好表示,它不适合数据库索引。但有时它基本上更方便,而且在小范围内,查询需要表扫描的事实通常不是问题。

MySQL存储函数是将相对复杂的逻辑封装在可以在查询中引用的简单函数的好方法,可能会导致更易于理解的查询并减少复制/粘贴错误。

所以,这是我写的一个名为find_ip4_in_cidr4()的存储函数。它的工作方式与内置函数FIND_IN_SET()有些相似 - 你给它一个值并给它一个“set”(CIDR规范),它返回一个值来表示该值是否在集合中。 / p>

首先,说明行动中的功能:

如果地址在块内,则返回前缀长度。为什么要返回前缀长度?非零整数是“真”,所以我们可以返回1,但是如果要对匹配结果进行排序以找到多个匹配前缀中的最短或最长,则可以ORDER BY返回值功能。

mysql> SELECT find_ip4_in_cidr4('203.0.113.123','203.0.113.0/24');
+-----------------------------------------------------+
| find_ip4_in_cidr4('203.0.113.123','203.0.113.0/24') |
+-----------------------------------------------------+
|                                                  24 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT find_ip4_in_cidr4('192.168.100.1','192.168.0.0/16');
+-----------------------------------------------------+
| find_ip4_in_cidr4('192.168.100.1','192.168.0.0/16') |
+-----------------------------------------------------+
|                                                  16 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

不在街区?返回0(false)。

mysql> SELECT find_ip4_in_cidr4('192.168.100.1','203.0.113.0/24');
+-----------------------------------------------------+
| find_ip4_in_cidr4('192.168.100.1','203.0.113.0/24') |
+-----------------------------------------------------+
|                                                   0 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT find_ip4_in_cidr4('192.168.100.1','192.168.0.0/24');
+-----------------------------------------------------+
| find_ip4_in_cidr4('192.168.100.1','192.168.0.0/24') |
+-----------------------------------------------------+
|                                                   0 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

全零地址有一个特殊情况,我们返回-1(仍为“true”,但保留排序顺序):

mysql> SELECT find_ip4_in_cidr4('192.168.100.1','0.0.0.0/0');
+------------------------------------------------+
| find_ip4_in_cidr4('192.168.100.1','0.0.0.0/0') |
+------------------------------------------------+
|                                             -1 |
+------------------------------------------------+
1 row in set (0.00 sec)

无意义的参数返回null:

mysql> SELECT find_ip4_in_cidr4('234.467.891.0','192.168.0.0/24');
+-----------------------------------------------------+
| find_ip4_in_cidr4('234.467.891.0','192.168.0.0/24') |
+-----------------------------------------------------+
|                                                NULL |
+-----------------------------------------------------+
1 row in set (0.00 sec)

现在,代码:

DELIMITER $$

DROP FUNCTION IF EXISTS `find_ip4_in_cidr4` $$
CREATE DEFINER=`mezzell`@`%` FUNCTION `find_ip4_in_cidr4`(
  _address VARCHAR(15), 
  _block VARCHAR(18)
) RETURNS TINYINT
DETERMINISTIC /* for a given input, this function always returns the same output */
CONTAINS SQL /* the function does not read from or write to tables */
BEGIN

-- given an IPv4 address and a cidr spec,
-- return -1 for a valid address inside 0.0.0.0/0
-- return prefix length if the address is within the block,
-- return 0 if the address is outside the block,
-- otherwise return null

DECLARE _ip_aton INT UNSIGNED DEFAULT INET_ATON(_address);
DECLARE _cidr_aton INT UNSIGNED DEFAULT INET_ATON(SUBSTRING_INDEX(_block,'/',1));
DECLARE _prefix TINYINT UNSIGNED DEFAULT SUBSTRING_INDEX(_block,'/',-1);
DECLARE _bitmask INT UNSIGNED DEFAULT (0xFFFFFFFF << (32 - _prefix)) & 0xFFFFFFFF;

RETURN CASE /* the first match, not "best" match is used in a CASE expression */
  WHEN _ip_aton IS NULL OR _cidr_aton IS NULL OR /* sanity checks */
       _prefix  IS NULL OR _bitmask IS NULL OR
       _prefix NOT BETWEEN 0 AND 32 OR
       (_prefix = 0 AND _cidr_aton != 0) THEN NULL
  WHEN _cidr_aton = 0 AND _bitmask = 0 THEN -1
  WHEN _ip_aton & _bitmask = _cidr_aton & _bitmask THEN _prefix /* here's the only actual test needed */
  ELSE 0 END;

END $$
DELIMITER ;

一个不是特定于存储函数的问题,而是适用于大多数RDBMS平台上的大多数函数的问题是当一列用作WHERE中函数的参数时,服务器不能“看起来”向后“通过函数使用索引来优化查询。