2017-04-05 114 views
-1

ex.IPv6地址與CIDR: 2620:0:2d0:200 :: 7/32 輸出 開始範圍:2620:0:0:0: 0:0:0:0 結束範圍:2620:0:ffff:ffff:ffff:ffff:ffff:fffforacle PL/SQL如何計算IPv6的範圍ip cidr

如何使用PL/SQL進行計算?

+0

這對於IPv4工作HTTP:/ /stackoverflow.com/questions/20959302/get-ip-address-range-from-xxx-xxx-xx-0-16但我想找到範圍IPv6 –

+0

是的,這是我的軟件包的舊版本。下面你看到的版本也支持IPv6 –

回答

1

有一次,我寫了一個一般的PL/SQL程序包,你可以做這樣的轉換:

CREATE OR REPLACE PACKAGE BODY Misc_Util AS 

    NUMERIC_OVERFLOW EXCEPTION; 
    PRAGMA EXCEPTION_INIT(NUMERIC_OVERFLOW, -1426); 
    BASE_BIN CONSTANT PLS_INTEGER := 2; 
    BASE_OCT CONSTANT PLS_INTEGER := 8; 
    BASE_HEX CONSTANT PLS_INTEGER := 16; 

FUNCTION Dec2Base(DecN IN NUMBER, Base IN PLS_INTEGER DEFAULT BASE_HEX) RETURN VARCHAR2 DETERMINISTIC IS 

    HexString VARCHAR2(33); 
    n INTEGER; 
    BaseString VARCHAR2(129) := NULL; 
    DecNumber NUMBER := DecN; 

BEGIN 
    IF DecN IS NULL THEN 
     RETURN NULL; 
    ELSIF Base NOT IN (BASE_BIN, BASE_OCT, BASE_HEX) THEN 
     RAISE INVALID_NUMBER; 
    ELSIF DecN > 2**128 - 1 THEN 
     RAISE NUMERIC_OVERFLOW; 
    ELSIF base = BASE_HEX THEN 
     RETURN TO_CHAR(DecN, 'fmXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'); 
    ELSE    
     IF Base = BASE_BIN THEN 
      -- Since "MOD(DecNumber, Base)" returns wrong result for numbers > 2^127 I have to make it differently. 
      HexString := TO_CHAR(DecN, 'fmXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'); 
      FOR i IN 1..LENGTH(HexString) LOOP 
       n := TO_NUMBER(SUBSTR(HexString, -i, 1), 'X'); 
       BaseString := SIGN(BITAND(n, 8)) || SIGN(BITAND(n, 4)) || SIGN(BITAND(n, 2)) || SIGN(BITAND(n, 1)) || BaseString; 
      END LOOP; 
      RETURN COALESCE(REGEXP_REPLACE(BaseString, '^0+'), 0); 
     ELSE 
      HexString := ''; 
      LOOP 
       BaseString := SUBSTR(HexString, MOD(DecNumber, Base) + 1, 1) || BaseString; 
       DecNumber := TRUNC(DecNumber/Base); 
       EXIT WHEN DecNumber = 0; 
      END LOOP; 
      RETURN BaseString;   
     END IF;  
    END IF; 
END Dec2Base; 


FUNCTION Base2Dec(BaseString IN VARCHAR2, Base IN PLS_INTEGER DEFAULT BASE_HEX) RETURN NUMBER DETERMINISTIC IS 

    BaseNumber NUMBER := 0; 
    HexString CONSTANT CHAR(16) := 'ABCDEF'; 

BEGIN 
    IF BaseString IS NULL THEN 
     RETURN NULL; 
    ELSIF Base NOT IN (BASE_BIN, BASE_OCT, BASE_HEX) THEN 
     RAISE INVALID_NUMBER; 
    ELSIF Base = BASE_HEX THEN 
     RETURN TO_NUMBER(BaseString, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'); 
    ELSE 
     FOR i IN 1..LENGTH(BaseString) LOOP 
      BaseNumber := BaseNumber * Base + INSTR(HexString, UPPER(SUBSTR(BaseString, i, 1))) - 1; 
     END LOOP; 
     RETURN BaseNumber; 
    END IF; 
END Base2Dec; 


FUNCTION UncompressIpV6(Ip IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS 
    IpFull VARCHAR2(40); 
BEGIN 
    IF REGEXP_LIKE(Ip, '::') THEN 
     IpFull := Ip; 
     FOR i IN LENGTH(REGEXP_REPLACE(REGEXP_REPLACE(Ip, '/\d{1,3}$'), '[[:xdigit:]]+', NULL))..7 LOOP 
      IpFull := REGEXP_REPLACE(IpFull, '::', ':0::'); 
     END LOOP; 
     RETURN REGEXP_REPLACE(IpFull, '::', ':'); 
    ELSE 
     RETURN Ip; 
    END IF; 
END UncompressIpV6; 


FUNCTION Ip2Decimal(IP IN VARCHAR2) RETURN NUMBER DETERMINISTIC IS 
    DecimalIp NUMBER := 0; 
BEGIN 

    IF REGEXP_LIKE(IP, ':') THEN 
     SELECT SUM(Base2Dec(REGEXP_SUBSTR(UncompressIpV6(IP), '[[:xdigit:]]+', 1, LEVEL), BASE_HEX) * POWER(65536, 8-LEVEL)) 
     INTO DecimalIp 
     FROM dual 
     CONNECT BY LEVEL <= 8; 
     RETURN DecimalIp;  
    ELSE 
     SELECT SUM(REGEXP_SUBSTR(IP, '\d+', 1, LEVEL) * POWER(256, 4-LEVEL)) 
     INTO DecimalIp 
     FROM dual 
     CONNECT BY LEVEL <= 4; 
     RETURN DecimalIp; 
    END IF; 

END Ip2Decimal; 

FUNCTION Decimal2Ip(IpDecimal IN NUMBER) RETURN VARCHAR2 DETERMINISTIC IS 

    IP VARCHAR2(16); 
    Octet INTEGER; 
    v_IpDecimal INTEGER := IpDecimal; 

BEGIN 
    IF IpDecimal IS NULL THEN 
     RETURN NULL; 
    END IF;  
    IF IpDecimal > 2**32 - 1 THEN 
     RAISE NUMERIC_OVERFLOW; 
    END IF;  
    FOR i IN 1..4 LOOP 
     Octet := TRUNC(v_IpDecimal/256**(4-i)); 
     v_IpDecimal := v_IpDecimal - Octet * 256**(4-i); 
     IP := IP ||'.'||Octet; 
    END LOOP; 
    RETURN SUBSTR(IP, 2); 

END Decimal2Ip; 

FUNCTION Decimal2IPv6(IpDecimal IN NUMBER) RETURN VARCHAR2 DETERMINISTIC IS 

    IP VARCHAR2(40); 
    Octet INTEGER; 
    v_IpDecimal NUMBER := IpDecimal; 

BEGIN 
    IF IpDecimal IS NULL THEN 
     RETURN NULL; 
    END IF;  
    IF IpDecimal > 2**128 - 1 THEN 
     RAISE NUMERIC_OVERFLOW; 
    END IF;  
    FOR i IN 1..8 LOOP 
     Octet := TRUNC(v_IpDecimal/65536**(8-i)); 
     v_IpDecimal := v_IpDecimal - Octet * 65536**(8-i); 
     IP := IP ||':'||Dec2Base(Octet, BASE_HEX); 
    END LOOP; 
    RETURN LOWER(SUBSTR(IP, 2)); 

END Decimal2IPv6; 

FUNCTION GetSubnetMask(Ip IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS 
BEGIN 
    IF Ip IS NULL OR NOT REGEXP_LIKE(Ip, '/\d{1,3}$') THEN 
     RETURN NULL; 
    END IF; 
    IF REGEXP_LIKE(Ip, ':') THEN 
     RETURN CIDR2SubnetMaskV6(REGEXP_SUBSTR(Ip, '\d{1,3}$')); 
    ELSE 
     RETURN CIDR2SubnetMask(REGEXP_SUBSTR(Ip, '\d{1,2}$')); 
    END IF; 
END GetSubnetMask; 

FUNCTION GetSubnetIp(Ip IN VARCHAR2, SubnetMask IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS 
    Subnet VARCHAR2(40); 
    BroadcastIp VARCHAR2(40); 
BEGIN 
    GetIpSubnet(Ip, SubnetMask, Subnet, BroadcastIp); 
    RETURN Subnet; 
END GetSubnetIp; 

FUNCTION GetBroadcastIp(Ip IN VARCHAR2, SubnetMask IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS 
    Subnet VARCHAR2(40); 
    BroadcastIp VARCHAR2(40); 
BEGIN 
    GetIpSubnet(Ip, SubnetMask, Subnet, BroadcastIp); 
    RETURN BroadcastIp; 
END GetBroadcastIp; 

FUNCTION GetSubnetIp(Ip IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS 
    Subnet VARCHAR2(40); 
    BroadcastIp VARCHAR2(40); 
    SubnetMask VARCHAR2(40); 
BEGIN 
    SubnetMask := GetSubnetMask(Ip); 
    GetIpSubnet(REGEXP_REPLACE(Ip,'/\d{1,3}$'), SubnetMask, Subnet, BroadcastIp); 
    RETURN Subnet; 
END GetSubnetIp; 

FUNCTION GetBroadcastIp(Ip IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS 
    Subnet VARCHAR2(40); 
    BroadcastIp VARCHAR2(40); 
    SubnetMask VARCHAR2(40); 
BEGIN 
    SubnetMask := GetSubnetMask(Ip); 
    GetIpSubnet(REGEXP_REPLACE(Ip,'/\d{1,3}$'), SubnetMask, Subnet, BroadcastIp); 
    RETURN BroadcastIp; 
END GetBroadcastIp; 

PROCEDURE GetIpSubnet(Ip IN VARCHAR2, SubnetMask IN VARCHAR2, Subnet OUT VARCHAR2, BroadcastIp OUT VARCHAR2) IS 
    CIDR INTEGER; 
    SubnetDec NUMBER; 
BEGIN 

    IF SubnetMask IS NULL OR Ip IS NULL THEN 
     RETURN; 
    END IF; 

    IF REGEXP_LIKE(Ip, ':') THEN 
     FOR i IN 1..8 LOOP 
      Subnet := Subnet ||':'||Dec2Base(BITAND(Base2Dec(REGEXP_SUBSTR(UncompressIpV6(Ip), '[[:xdigit:]]+', 1, i), BASE_HEX), Base2Dec(REGEXP_SUBSTR(SubnetMask, '[[:xdigit:]]+', 1, i), BASE_HEX)), BASE_HEX);  
     END LOOP; 
     Subnet := SUBSTR(Subnet, 2); 
    ELSE 
     FOR i IN 1..4 LOOP 
      Subnet := Subnet ||'.'||BITAND(REGEXP_SUBSTR(Ip, '\d+', 1, i), REGEXP_SUBSTR(SubnetMask, '\d+', 1, i)); 
     END LOOP; 
     Subnet := SUBSTR(Subnet, 2);  
    END IF; 

    CIDR := SubnetMask2CIDR(SubnetMask); 
    SubnetDec := Ip2Decimal(Subnet); 
    IF REGEXP_LIKE(Ip, ':') THEN 
     FOR i IN CIDR..127 LOOP 
      SubnetDec := SubnetDec + 2**(127-i); 
     END LOOP; 
     BroadcastIp := Decimal2IpV6(SubnetDec); 
    ELSE 
     FOR i IN CIDR..31 LOOP 
      SubnetDec := SubnetDec + 2**(31-i); 
     END LOOP; 
     BroadcastIp := Decimal2Ip(SubnetDec); 
    END IF; 

END GetIpSubnet; 

FUNCTION SubnetMask2CIDR(SubnetMask VARCHAR2) RETURN INTEGER DETERMINISTIC IS 
    IpBin VARCHAR(129); 
BEGIN 
    IF SubnetMask IS NULL THEN 
     RETURN NULL; 
    END IF; 

    IpBin := Dec2Base(Ip2Decimal(SubnetMask), BASE_BIN); 
    IF REGEXP_LIKE(IpBin, '^1+0+$') THEN 
     RETURN REGEXP_INSTR(IpBin, '0')-1; 
    ELSIF REGEXP_LIKE(IpBin, '^1{32}$') AND REGEXP_LIKE(SubnetMask, '\.') THEN 
     RETURN 32; 
    ELSIF REGEXP_LIKE(IpBin, '^1{128}$') AND REGEXP_LIKE(SubnetMask, ':') THEN 
     RETURN 128; 
    ELSE 
     RAISE VALUE_ERROR; 
    END IF; 

END SubnetMask2CIDR; 

FUNCTION CIDR2SubnetMask(CIDR IN INTEGER) RETURN VARCHAR2 DETERMINISTIC IS 
BEGIN 
    IF CIDR IS NULL THEN 
     RETURN NULL; 
    END IF; 
    IF CIDR NOT BETWEEN 1 AND 32 THEN 
     RAISE VALUE_ERROR; 
    END IF; 
    RETURN Decimal2Ip(Base2Dec(RPAD(LPAD('0', CIDR+1, '1'), 32, '0'), BASE_BIN)); 
END CIDR2SubnetMask; 

FUNCTION CIDR2SubnetMaskV6(CIDR IN INTEGER) RETURN VARCHAR2 DETERMINISTIC IS 
BEGIN 
    IF CIDR IS NULL THEN 
     RETURN NULL; 
    END IF; 
    IF CIDR NOT BETWEEN 1 AND 128 THEN 
     RAISE VALUE_ERROR; 
    END IF; 
    RETURN Decimal2IpV6(Base2Dec(RPAD(LPAD('0', CIDR+1, '1'), 128, '0'), BASE_BIN));  
END CIDR2SubnetMaskV6; 

Misc_Util; 
/

然後你可以使用它,例如像這樣:

SELECT 
    Misc_Util.GetSubnetIp('2620:0:2d0:200::7/32'), 
    Misc_Util.GetBroadcastIp('2620:0:2d0:200::7/32') 
FROM dual; 


2620:0:0:0:0:0:0:0 2620:0:ffff:ffff:ffff:ffff:ffff:ffff 
+0

非常感謝你,我會試試看。 –

+0

It works。謝謝你 –

+0

然後請接受答案 –