T-SQL的子网(或CIDR)IP控制

时间:2015-08-20 12:02:38

标签: tsql ip-address subnet cidr

我不知道如何准确解释,但是选择和比较查询IP子网存在问题。例如,有一个IP地址列表,我有另一个CIDR /子网掩码列表(X.X.X.0 / 24等)。如何通过T-SQL了解第一个列表中的每个IP地址是否在CIDR /子网掩码列表中?

例如:

IP:172.28.112.23 - >假

IP:172.28.111.33 - >真

IP列表输出:

IP List

子网输出:

enter image description here

3 个答案:

答案 0 :(得分:3)

您想要完全按计算机执行的操作来确定IP地址是否在子网中 - 即:

1)将网络地址,子网掩码和测试地址转换为二进制。

2)检查(网络地址和子网掩码)=(测试地址和子网掩码)
(&代表按位AND)
如果此比较为真,则测试地址在子网内

理解这一点的关键是要认识到IP地址(和子网掩码)只是32位数字 按位且在2个32位数之间创建一个新的32位数字,其中1位在比较的两个数字中均为1,否则为0。

EG:1010& 1100 = 1000,因为第一个数字在两个数字中都是1(在第一个数字的结果中产生1),但第二个第3和第4个数字不是(因此在第二个第3和第4个数字的结果中给出0)。

不幸的是,SQL Server不能按位进行2位二进制数,但它在十进制表示之间(即转换为BIGINT数据类型时)可以正常工作。

因此,我建议您创建一个首先将IP地址转换为BIGINT数据类型的函数

CREATE FUNCTION dbo.fnIPtoBigInt
(
    @Ipaddress NVARCHAR(15) -- should be in the form '123.123.123.123'
)
RETURNS BIGINT
AS
BEGIN
 DECLARE @part1 AS NVARCHAR(3) 
 DECLARE @part2 AS NVARCHAR(3) 
 DECLARE @part3 AS NVARCHAR(3)
 DECLARE @part4 AS NVARCHAR(3)

 SELECT @part1 = LEFT(@Ipaddress, CHARINDEX('.',@Ipaddress) - 1)
 SELECT @Ipaddress = SUBSTRING(@Ipaddress, LEN(@part1) + 2, 15)
 SELECT @part2 = LEFT(@Ipaddress, CHARINDEX('.',@Ipaddress) - 1)
 SELECT @Ipaddress = SUBSTRING(@Ipaddress, LEN(@part2) + 2, 15)
 SELECT @part3 = LEFT(@Ipaddress, CHARINDEX('.',@Ipaddress) - 1)
 SELECT @part4 = SUBSTRING(@Ipaddress, LEN(@part3) + 2, 15)

 DECLARE @ipAsBigInt AS BIGINT
 SELECT @ipAsBigInt =
    (16777216 * (CAST(@part1 AS BIGINT)))
    + (65536 * (CAST(@part2 AS BIGINT)))
    + (256 * (CAST(@part3 AS BIGINT)))
    + (CAST(@part4 AS BIGINT))

 RETURN @ipAsBigInt

END

GO

然后,您可以轻松实现一个函数来测试地址是否在子网中:

CREATE FUNCTION dbo.fnIsIpaddressInSubnet
(
    @networkAddress NVARCHAR(15), -- 'eg: '192.168.0.0'
    @subnetMask NVARCHAR(15), -- 'eg: '255.255.255.0' for '/24'
    @testAddress NVARCHAR(15) -- 'eg: '192.168.0.1'
)
RETURNS BIT AS
BEGIN
    RETURN CASE WHEN (dbo.fnIPtoBigInt(@networkAddress) & dbo.fnIPtoBigInt(@subnetMask)) 
        = (dbo.fnIPtoBigInt(@testAddress) & dbo.fnIPtoBigInt(@subnetMask)) 
    THEN 1 ELSE 0 END
END

为了让你更容易,你可能想要一个可以将'/ 24'转换为BigInt的功能。
'/ 24'是写入255.255.255.0的简写方式 - 即32位数字,前24位设置为1(其余8位设置为0)

CREATE FUNCTION dbo.fnSubnetBitstoBigInt
(
    @SubnetBits TINYINT -- max = 32
)
RETURNS BIGINT
AS
BEGIN

 DECLARE @multiplier AS BIGINT = 2147483648
 DECLARE @ipAsBigInt AS BIGINT = 0
 DECLARE @bitIndex TINYINT = 1
 WHILE @bitIndex <= @SubnetBits
 BEGIN
    SELECT @ipAsBigInt = @ipAsBigInt + @multiplier
    SELECT @multiplier = @multiplier / 2
    SELECT @bitIndex = @bitIndex + 1
 END

 RETURN @ipAsBigInt

END

GO

如果您创建以下附加功能,则转换变得轻松

CREATE FUNCTION dbo.fnIsIpaddressInSubnetShortHand
(
    @network NVARCHAR(18), -- 'eg: '192.168.0.0/24'
    @testAddress NVARCHAR(15) -- 'eg: '192.168.0.1'
)
RETURNS BIT AS
BEGIN
    DECLARE @networkAddress NVARCHAR(15)
    DECLARE @subnetBits TINYINT

    SELECT @networkAddress = LEFT(@network, CHARINDEX('/', @network) - 1)
    SELECT @subnetBits = CAST(SUBSTRING(@network, LEN(@networkAddress) + 2, 2) AS TINYINT)

    RETURN CASE WHEN (dbo.fnIPtoBigInt(@networkAddress) & dbo.fnSubnetBitstoBigInt(@subnetBits)) 
        = (dbo.fnIPtoBigInt(@testAddress) & dbo.fnSubnetBitstoBigInt(@subnetBits)) 
    THEN 1 ELSE 0 END
END

SELECT dbo.fnIsIpaddressInSubnetShorthand('192.168.2.0/24','192.168.3.91') -- returns 0
SELECT dbo.fnIsIpaddressInSubnetShorthand('192.168.2.0/24','192.168.2.91') -- returns 1

答案 1 :(得分:0)

这本身并不是答案,而是一种使 James S answer 中的函数更易于阅读且可能更高效的方法。

SQL Server 具有处理从数据库对象名称中获取部分的功能。这些名称由 4 个部分 [Server].[Database].[Schema].[Object] 组成。因此,以下允许您获取架构名称。索引从右边开始

SELECT PARSENAME('[myServer].[master].[sys].[objects]', 2)

没有什么可以说您不能将其用于 IP 地址。由于它是 SQL 操作方式的基础,我认为它已经过优化。

CREATE FUNCTION dbo.fnIPtoBigInt
(
    @Ipaddress NVARCHAR(15) -- should be in the form '123.123.123.123'
)
RETURNS BIGINT
AS
BEGIN
 DECLARE @ipAsBigInt AS BIGINT
 SELECT @ipAsBigInt =
    (16777216 * (CAST(PARSENAME(@Ipaddress, 4) AS BIGINT)))
    + (65536 * (CAST(PARSENAME(@Ipaddress, 3) AS BIGINT)))
    + (256 * (CAST(PARSENAME(@Ipaddress, 2) AS BIGINT)))
    + (CAST(PARSENAME(@Ipaddress, 1) AS BIGINT))

 RETURN @ipAsBigInt

END

GO

答案 2 :(得分:0)

完整的解决方案

CREATE
OR ALTER FUNCTION dbo.IPv4SubnetContainsIPAddress (
  @net AS VARCHAR(15),
  @mask AS VARCHAR(15),
  @ip AS VARCHAR(15)
) RETURNS tinyint AS BEGIN DECLARE @result AS tinyint IF LEN(@mask) <= 2
SELECT
  @mask = m
FROM
  (
    VALUES
      (0, '0.0.0.0'),
      (1, '128.0.0.0'),
      (2, '192.0.0.0'),
      (3, '224.0.0.0'),
      (4, '240.0.0.0'),
      (5, '248.0.0.0'),
      (6, '252.0.0.0'),
      (7, '254.0.0.0'),
      (8, '255.0.0.0'),
      (9, '255.128.0.0'),
      (10, '255.192.0.0'),
      (11, '255.224.0.0'),
      (12, '255.240.0.0'),
      (13, '255.248.0.0'),
      (14, '255.252.0.0'),
      (15, '255.254.0.0'),
      (16, '255.255.0.0'),
      (17, '255.255.128.0'),
      (18, '255.255.192.0'),
      (19, '255.255.224.0'),
      (20, '255.255.240.0'),
      (21, '255.255.248.0'),
      (22, '255.255.252.0'),
      (23, '255.255.254.0'),
      (24, '255.255.255.0'),
      (25, '255.255.255.128'),
      (26, '255.255.255.192'),
      (27, '255.255.255.224'),
      (28, '255.255.255.240'),
      (29, '255.255.255.248'),
      (30, '255.255.255.252'),
      (31, '255.255.255.254'),
      (32, '255.255.255.255')
  ) AS o (i, m)
WHERE
  i = @mask
SELECT
  @result = IIF(Count(*) = 4, 1, 0)
FROM
  (
    SELECT
      *,
      IIF(
        o_ip BETWEEN o_subnet
        AND o_broadcast,
        1,
        0
      ) AS eq
    FROM
      (
        SELECT
          *,
          o_net & o_mask AS o_subnet,
          o_net | (255 - o_mask) AS o_broadcast
        FROM
          (
            SELECT
              o_net,
              o_mask,
              o_ip
            FROM
              (
                VALUES
                  (1, CAST(PARSENAME(@net, 4) AS INTEGER)),
                  (2, CAST(PARSENAME(@net, 3) AS INTEGER)),
                  (3, CAST(PARSENAME(@net, 2) AS INTEGER)),
                  (4, CAST(PARSENAME(@net, 1) AS INTEGER))
              ) AS c1 (i, o_net)
              LEFT JOIN (
                SELECT
                  i,
                  o_mask
                FROM
                  (
                    VALUES
                      (1, CAST(PARSENAME(@mask, 4) AS INTEGER)),
                      (2, CAST(PARSENAME(@mask, 3) AS INTEGER)),
                      (3, CAST(PARSENAME(@mask, 2) AS INTEGER)),
                      (4, CAST(PARSENAME(@mask, 1) AS INTEGER))
                  ) AS c2 (i, o_mask)
              ) AS c2 ON c1.i = c2.i
              LEFT JOIN (
                SELECT
                  i,
                  o_ip
                FROM
                  (
                    VALUES
                      (1, CAST(PARSENAME(@ip, 4) AS INTEGER)),
                      (2, CAST(PARSENAME(@ip, 3) AS INTEGER)),
                      (3, CAST(PARSENAME(@ip, 2) AS INTEGER)),
                      (4, CAST(PARSENAME(@ip, 1) AS INTEGER))
                  ) AS c3 (i, o_ip)
              ) AS c3 ON c1.i = c3.i
          ) AS t
      ) AS t
  ) AS t
WHERE
  eq = 1 RETURN @result END
GO
SELECT
  dbo.IPv4SubnetContainsIPAddress('192.168.64.0', '255.255.224.0', '192.168.40.1') -- returns 0
SELECT
  dbo.IPv4SubnetContainsIPAddress('192.168.64.0', '19', '192.168.40.1') -- returns 0
SELECT
  dbo.IPv4SubnetContainsIPAddress('192.168.64.0', '255.255.192.0', '192.168.80.1') -- returns 1
SELECT
  dbo.IPv4SubnetContainsIPAddress('192.168.64.0', '18', '192.168.80.1') -- returns 1