我正在尝试编写一个函数,它将在CIDR格式的IP上输出一些地址信息(在代码下输出):
22
这输出以下内容:
create function dbo.ConvertIpToInt (@Ip as varchar(15))
returns bigint
as
begin
return (convert(bigint, parsename(@Ip, 1)) +
convert(bigint, parsename(@Ip, 2)) * 256 +
convert(bigint, parsename(@Ip, 3)) * 256 * 256 +
convert(bigint, parsename(@Ip, 4)) * 256 * 256 * 256)
end
go
create function dbo.ConvertIntToIp (@Int bigint)
returns varchar(15)
as
begin
declare
@IpHex varchar(8)
,@IpDotted varchar(15)
select
@IpHex = substring(convert(varchar(30), master.dbo.fn_varbintohexstr(@Int)), 11, 8)
select
@IpDotted = convert(varchar(3), convert(int, (convert(varbinary, substring(@IpHex, 1, 2), 2)))) + '.' +
convert(varchar(3), convert(int, (convert(varbinary, substring(@IpHex, 3, 2), 2)))) + '.' +
convert(varchar(3), convert(int, (convert(varbinary, substring(@IpHex, 5, 2), 2)))) + '.' +
convert(varchar(3), convert(int, (convert(varbinary, substring(@IpHex, 7, 2), 2))))
return @IpDotted
end
go
create function dbo.GetCidrIpRange (@CidrIp varchar(15))
returns @result table
(
CidrIp varchar(15) not null,
Mask int not null,
LowRange varchar(15) not null,
LowIp varchar(15) not null,
HighRange varchar(15) not null,
HighIp varchar(15) not null,
AddressQty bigint not null
)
as
begin
declare @Base bigint = cast(4294967295 as bigint)
declare @Mask int = cast(substring(@CidrIp, patindex('%/%' , @CidrIP) + 1, 2) as int)
declare @Power bigint = Power(2.0, 32.0 - @Mask) - 1
declare @LowRange bigint = dbo.ConvertIpToInt(left(@CidrIp, patindex('%/%' , @CidrIp) - 1)) & (@Base ^ @Power)
declare @HighRange bigint = @LowRange + @Power
insert @result
select
CidrIp = @CidrIp
, Mask = @Mask
, LowRange = @LowRange
, LowIp = dbo.ConvertIntToIp(@LowRange)
, HighRange = @HighRange
, HighIp = dbo.ConvertIntToIp(@HighRange)
, AddressQty = convert(bigint, power(2.0, (32.0 - @Mask)))
return
end
go
select * from dbo.GetCidrIpRange('195.65.254.11/2');
我一直在浏览SO和Google几个小时,我相信CidrIp Mask LowRange LowIp HighRange HighIp AddressQty
--------------------------------------------------------------------------------------
195.65.254.11/2 2 3221225472 192.0.0.0 4294967295 255.255.255.255 1073741824
和ConvertIpToInt
是正确的。
但是,我期待以下输出:
ConvertIntToIp
有人可以指出我代码中的错误在哪里吗?我一直盯着自己失明,我没有看到它(或者我误解了如何做到这一点)。
答案 0 :(得分:1)
根据http://www.ipaddressguide.com/cidr和http://jodies.de/ipcalc?host=195.65.254.11&mask1=2&mask2=,您的计算是正确的。这两个网站之间唯一的不同意见是 jodies.de/ipcalc 页面会删除该范围内的最低和最高(广播)IP地址。
我使用195.65.254.11/2
和195.65.254.11/24
进行了测试。为了使代码正常工作,我需要将dbo.GetCidrIpRang
上的输入参数规范更改为VARCHAR(20)
(如comment on the question中的@Damien_The_Unbeliever所述。)
关于表现的两个注释:
对于ConvertIpToInt
和ConvertIntToIp
标量UDF,您最好分别使用 INET_AddressToNumber 和 INET_NumberToAddress 函数,包含在SQL# SQLCLR库的免费版本中(我写过,但是嘿,免费:)。这个建议的原因是,与T-SQL UDF不同, deterministic SQLCLR UDF(以及这两者)不会阻止并行计划。
如果您不想使用SQLCLR路线,那么您至少应该将ConvertIntToIp
功能保持为纯数学。没有理由进行所有这些转换和子串。
CREATE FUNCTION dbo.IPNumberToAddress(@IPNumber BIGINT)
RETURNS VARCHAR(15)
WITH SCHEMABINDING
AS
BEGIN
DECLARE @Oct1 BIGINT,
@Oct2 INT,
@Oct3 INT;
SET @Oct1 = @IPNumber / (256 * 256 * 256);
SET @IPNumber -= (@Oct1 * (256 * 256 * 256));
SET @Oct2 = @IPNumber / (256 * 256);
SET @IPNumber -= (@Oct2 * (256 * 256));
SET @Oct3 = @IPNumber / 256;
SET @IPNumber -= (@Oct3 * 256);
RETURN CONCAT(@Oct1, '.', @Oct2, '.', @Oct3, '.', @IPNumber);
END;
GO
然后:
SELECT dbo.IPNumberToAddress(3275881995);
-- 195.65.254.11
对于GetCidrIpRange
TVF,您最好将其转换为内联TVF。您可以通过以下方式通过CTE完成多步计算(您只需要将其清理一点/完成它):
WITH cte1 AS
(
SELECT 2 AS [Mask] -- replace with real formula
), cte2 AS
(
SELECT 999 AS [Base], -- replace with real formula
POWER(2.0, 32.0 - cte1.[Mask]) - 1 AS [Power],
cte1.[Mask]
FROM cte1
), cte3 AS
(
SELECT SQL#.INET_AddressToNumber(left(@CidrIp, PATINDEX('%/%' , @CidrIp) - 1))
& (cte2.[Base] ^ cte2.[Power]) AS [LowRange],
cte2.[Power],
cte2.[Mask]
FROM cte2
)
SELECT @CidrIp AS [CidrIp],
cte3.[Mask],
cte3.[LowRange],
SQL#.INET_NumberToAddress(cte3.[LowRange]) AS [LowIp],
(cte3.[LowRange] + cte3.[Power]) AS [HighRange],
SQL#.INET_NumberToAddress(cte3.[LowRange] + cte3.[Power]) AS [HighIp],
CONVERT(BIGINT, POWER(2.0, (32.0 - cte3.[Mask]))) AS [AddressQty]
FROM cte3 c;