sql >> データベース >  >> RDS >> Oracle

oracle PL / SQLIPv6cidrの範囲IPを計算する方法

    このような変換を実行できる一般的なPL/SQLパッケージを作成したことがあります。 IPv4とIPv6の両方で機能します。

    CREATE OR REPLACE PACKAGE IP_Util AS
    
    /**
    * Convert an IP-Address into decimal value.
    * @param IP The IP-Address, e.g. '10.151.20.224' or '1080::8:800:200C:417A'. 
    * Supports also mixed notation like '0:0:0:0:0:FFFF:129.144.52.38'. CIDR value (e.g. '1080::8:800:200C:417A/80') is ignored.
    * @return The decimal equivalent
    */
    FUNCTION IP2Decimal(IP IN VARCHAR2) RETURN NUMBER DETERMINISTIC;
    
    /**
    * Convert an IP-Address into RWA value.
    * @param IP The IP-Address, e.g. '10.151.20.224' or '1080::8:800:200C:417A'. 
    * Supports also mixed notation like '0:0:0:0:0:FFFF:129.144.52.38'. CIDR value (e.g. '1080::8:800:200C:417A/80') is ignored.
    * @param ver IP version, either 4 or 6. If NULL then function determines the IP version.
    * @return The RAW equivalent
    */
    FUNCTION IP2RAW(IP IN VARCHAR2, ver IN INTEGER DEFAULT NULL) RETURN RAW DETERMINISTIC;
    
    /**
    * Convert an IP-Address from decimal value into IPv4 or IPv6 format.
    * @param ip Decimal IP-Address, 0..(2**32)-1 or 0..(2**128)-1
    * @param ver IP version, either 4 or 6 
    * @return The IP in IPv4 or IPv6 format
    */    
    FUNCTION Decimal2IP(ip IN NUMBER, ver IN INTEGER) RETURN VARCHAR2 DETERMINISTIC;    
    
    
    /**
    * Convert an IP-Address from RAW value into IPv4 or IPv6 format.
    * @param ip RAW value of IP-Address, 0..FFFFFFFF or 0..FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    * @param ver IP version, either 4 or 6 
    * @return The IP in IPv4 or IPv6 format
    */    
    FUNCTION RAW2IP(ip IN RAW, ver IN INTEGER) RETURN VARCHAR2 DETERMINISTIC;   
    
    /**
    * Returns SubnetMask of given IP-Subnet in CIDR notation.
    * @param Ip Subnet IP-Address with CIDR notation, e.g. '10.152.10.17/24' or '1080::8:800:200C:417A/60'
    * @return SubnetMask Subnet mask of IP-Subnet, e.g. '255.255.255.0' or 'ffff:ffff:ffff::'
    */
    FUNCTION SubnetMask(Ip IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC;
    
    /**
    * Returns Network prefix of given IP-Subnet. In IPv4 this address was called subnet address.
    * @param Ip IP-Address of subnet, e.g. '10.152.10.17' or '1080:0:100:8:800:200C:FFFF:417A'
    * @param SubnetMask Subnet mask of subnet, e.g. '255.255.0.0' or 'FFFF:FFFF:FFFF::'
    * @return Network prefix, i.e. the first address from subnet, for example '10.152.0.0' or '1080:0:100:8:800:200C:FFFF::'
    */
    FUNCTION NetworkPrefix(Ip IN VARCHAR2, SubnetMask IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC;
    
    /**
    * Returns Network prefix of given IP-Subnet. In IPv4 this address was called subnet address.
    * @param Ip IP-Subnet with CIDR notation, e.g. '10.152.10.17/24' or '1080:0:100:8:800:200C:FFFF:417A/60'
    * @return Network prefix, i.e. the first address from subnet, for example '10.152.0.0' or '1080:0:100:8:800:200C:FFFF::'
    */
    FUNCTION NetworkPrefix(Ip IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC;
    
    /**
    * Returns Broadcast address of given IP-Subnet. 
    * IPv6 does not provide Broadcast anymore. However, function supports IPv6 for internal purpose.
    * @param Ip IP-Address of subnet, e.g. '10.152.10.17' or '1080:0:100:8:800:200C:FFFF:417A'
    * @param SubnetMask Subnet mask of subnet, e.g. '255.255.0.0' or 'FFFF:FFFF:FFFF::'
    * @return Broadcast address, i.e. the last address from subnet, for example '10.152.10.255' or '1080:0:100:8:800:ffff:ffff:ffff'
    */
    FUNCTION BroadcastIp(Ip IN VARCHAR2, SubnetMask IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC;
    
    /**
    * Returns Broadcast address of given IP-Subnet.   
    * IPv6 does not provide Broadcast anymore. However, function supports IPv6 for internal purpose.
    * @param Ip IP-Subnet with CIDR notation, e.g. '10.152.10.17/24' or '1080:0:100:8:800:200C:FFFF:417A/60'
    * @return Broadcast address, i.e. the last address from subnet, for example '10.152.10.255' or '1080:0:100:8:800:ffff:ffff:ffff'
    */    
    FUNCTION BroadcastIp(Ip IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC;
    
    /**
    * Translate Subnet mask to CIDR.
    * @param SubnetMask Subnet mask of subnet, e.g. '255.255.0.0' or 'FFFF:FFFF:FFFF::'
    * @return CIDR value, e.g. 26
    */    
    FUNCTION SubnetMask2CIDR(SubnetMask VARCHAR2) RETURN INTEGER RESULT_CACHE DETERMINISTIC;
    
    /**
    * Translate CIDR to Subnet mask in IPv4 or IPv6 format.
    * @param CIDR Length of network prefix
    * @param ver IP version, either 4 or 6 
    * @return Subnet mask, e.g. '255.255.0.0' or 'FFFF:FFFF:FFFF::'
    */
    FUNCTION CIDR2SubnetMask(CIDR IN INTEGER, ver IN INTEGER) RETURN VARCHAR2 RESULT_CACHE DETERMINISTIC;
    
    /**
    * Returns full uncompressed IPv6 Address. Mainly used for internal purpose like conversion, storage, comparison, etc.
    * '::' is replaced by zero pads, leading '0' are inserted (if leadingZero = TRUE), converted to lower cases. 
    * @param Ip Compact IPv6-Address (with CIDR or without CIDR, e.g. 2620:0:2D0:A2A2::7)
    * @param leadingZero If TRUE then bit fields are padded with '0' in order to have always 4 characters 
    * @return The full IPv6 Address with 8 x 16 bits, e.g. '2620:0000:02d0:a2a2:0000:0000:0000:0007'
    */
    FUNCTION UncompressIpV6(Ip IN VARCHAR2, leadingZero IN BOOLEAN DEFAULT TRUE) RETURN VARCHAR2 DETERMINISTIC;
    
    /**
    * Makes an canonical IPv6 address according to RFC 5952, i.e. human readable.
    * @param IPv6 IPv6-Address (with or without '::', with or without leading '0')
    * @return Canonical IPv6 Address, e.g. 2620:0:2d0:200::7
    */
    FUNCTION Canonical_IPv6(IPv6 IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC;
    
    
    END IP_Util;
    /
    
    
    CREATE OR REPLACE PACKAGE BODY IP_Util AS
    
    
    NUMERIC_OVERFLOW EXCEPTION;
    PRAGMA EXCEPTION_INIT(NUMERIC_OVERFLOW, -1426);
    
    
    
    FUNCTION IP2Decimal(IP IN VARCHAR2) RETURN NUMBER DETERMINISTIC IS
        DecimalIp NUMBER; -- INTEGER does not cover (2**128)-1
    BEGIN
        
        IF REGEXP_LIKE(IP, ':') THEN
            -- IPv6 Address
            IF REGEXP_LIKE(IP, '\d+\.\d+\.\d+\.\d+') THEN
                -- Mixed notation, e.g.: 0:0:0:0:0:FFFF:129.144.52.38
                SELECT SUM(TO_NUMBER(REGEXP_SUBSTR(UncompressIpV6(IP), '[[:xdigit:]]+', 1, LEVEL), 'XXXX') * POWER(65536, 8-LEVEL))
                INTO DecimalIp
                FROM dual 
                CONNECT BY LEVEL <= 6;
    
                SELECT DecimalIp + SUM(REGEXP_SUBSTR(REGEXP_SUBSTR(UncompressIpV6(IP), '\d+\.\d+\.\d+\.\d+'), '\d+', 1, LEVEL) * POWER(256, 4-LEVEL))
                INTO DecimalIp
                FROM dual 
                CONNECT BY LEVEL <= 4;
                RETURN DecimalIp;       
            ELSE
                SELECT SUM(TO_NUMBER(REGEXP_SUBSTR(UncompressIpV6(IP), '[[:xdigit:]]+', 1, LEVEL), 'XXXX') * POWER(65536, 8-LEVEL))
                INTO DecimalIp
                FROM dual 
                CONNECT BY LEVEL <= 8;
                RETURN DecimalIp;
            END IF;
        ELSE
            -- IPv4 Address
            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 IP2RAW(IP IN VARCHAR2, ver IN INTEGER DEFAULT NULL) RETURN RAW DETERMINISTIC IS
    BEGIN
        IF ver IS NULL THEN
            IF REGEXP_LIKE(IP, ':') THEN
                RETURN IP2RAW(IP, 6);
            ELSE    
                RETURN IP2RAW(IP, 4);
            END IF;
        ELSE
            IF ver = 6 THEN
                RETURN HEXTORAW(LPAD(TO_CHAR(IP2Decimal(ip), 'fmXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'), 32, '0'));
            ELSIF ver = 4 THEN  
                RETURN HEXTORAW(LPAD(TO_CHAR(IP2Decimal(ip), 'fmXXXXXXXX'), 8, '0'));
            ELSE
                RAISE VALUE_ERROR;
            END IF;
        END IF;
    END IP2RAW;
    
    
    
    FUNCTION RAW2IP(ip IN RAW, ver IN INTEGER) RETURN VARCHAR2 DETERMINISTIC IS
        res VARCHAR2(45);
    BEGIN
        -- Range check "TO_NUMBER(ip, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') < 2**32, resp 2**128" not needed, because RAW values are usually not based on error-prone user input
        IF ver = 4 THEN
            -- Take only last 32 bit from RAW value with UTL_RAW.SUBSTR(ip, -4)
            SELECT LISTAGG(TO_NUMBER(SUBSTR(SUBSTR(LPAD(RAWTOHEX(UTL_RAW.SUBSTR(ip, -4)), 8, '0'), -8), 2*LEVEL-1, 2), 'XX'), '.') WITHIN GROUP (ORDER BY LEVEL)
            INTO res
            FROM DUAL
            CONNECT BY LEVEL <= 4;
            RETURN res;
        ELSIF ver = 6 THEN
            RETURN Canonical_IPv6(SUBSTR(REGEXP_REPLACE(LPAD(RAWTOHEX(ip), 32, '0'), '([[:xdigit:]]{4})', ':\1'), 2));
        ELSE
            RAISE VALUE_ERROR;
        END IF;
    END RAW2IP;
    
    
    
    FUNCTION Decimal2IP(ip IN NUMBER, ver IN INTEGER) RETURN VARCHAR2 DETERMINISTIC IS
        res VARCHAR2(45);
    BEGIN
        IF ip IS NULL THEN 
            RETURN NULL; 
        END IF;
    
        IF ver = 4 THEN
            IF ip > 2**32 - 1 THEN
                RAISE NUMERIC_OVERFLOW;
            END IF;
            
            SELECT LISTAGG(TO_NUMBER(SUBSTR(LPAD(TO_CHAR(ip, 'fmXXXXXXXX'), 8, '0'), 2*LEVEL-1, 2), 'XX'), '.') WITHIN GROUP (ORDER BY LEVEL)
            INTO res
            FROM dual
            CONNECT BY LEVEL <= 4;
            RETURN res;
        ELSIF ver = 6 THEN
            IF ip > 2**128 - 1 THEN
                RAISE NUMERIC_OVERFLOW;
            END IF;
            
            res := LPAD(TO_CHAR(ip, 'fmxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'),32, '0');
            RETURN Canonical_IPv6(SUBSTR(REGEXP_REPLACE(res, '([[:xdigit:]]{4})', ':\1'), 2));
        ELSE
            RAISE VALUE_ERROR;
        END IF;
        
    END Decimal2IP;
    
    
    
    FUNCTION SubnetMask(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 CIDR2SubnetMask(REGEXP_SUBSTR(Ip, '\d{1,3}$'), 6);
        ELSE
            RETURN CIDR2SubnetMask(REGEXP_SUBSTR(Ip, '\d{1,2}$'), 4);
        END IF; 
    END SubnetMask;
    
    
    
    
    FUNCTION NetworkPrefix(Ip IN VARCHAR2, SubnetMask IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS
    BEGIN
        IF REGEXP_LIKE(ip, ':') THEN
            RETURN RAW2IP(UTL_RAW.BIT_AND(Ip2RAW(Ip, 6), Ip2RAW(SubnetMask, 6)), 6);
        ELSE
            RETURN RAW2IP(UTL_RAW.BIT_AND(Ip2RAW(Ip, 4),Ip2RAW(SubnetMask, 4)), 4);
        END IF;
    END NetworkPrefix;
    
    
    FUNCTION NetworkPrefix(Ip IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS
    BEGIN   
        RETURN NetworkPrefix(REGEXP_REPLACE(Ip, '/\d{1,3}$'), SubnetMask(Ip));
    END NetworkPrefix;
    
    
    
    FUNCTION BroadcastIp(Ip IN VARCHAR2, SubnetMask IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS
        Subnet RAW(16);
        SubnetInv RAW(16);
    BEGIN   
        IF REGEXP_LIKE(ip, ':') THEN
            Subnet := UTL_RAW.BIT_AND(Ip2RAW(Ip, 6), Ip2RAW(SubnetMask, 6));
            SubnetInv := UTL_RAW.BIT_COMPLEMENT(Ip2RAW(SubnetMask, 6));     
            RETURN RAW2IP(UTL_RAW.BIT_OR(Subnet, SubnetInv), 6);
        ELSE    
            Subnet := UTL_RAW.BIT_AND(Ip2RAW(Ip, 4), Ip2RAW(SubnetMask, 4));
            SubnetInv := UTL_RAW.BIT_COMPLEMENT(Ip2RAW(SubnetMask, 4));     
            RETURN RAW2IP(UTL_RAW.BIT_OR(Subnet, SubnetInv), 4);
        END IF;
    END BroadcastIp;
    
    
    FUNCTION BroadcastIp(Ip IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS
    BEGIN
        RETURN BroadcastIp(REGEXP_REPLACE(Ip, '/\d{1,3}$'), SubnetMask(Ip));
    END BroadcastIp;
    
    
    
    FUNCTION SubnetMask2CIDR(SubnetMask VARCHAR2) RETURN INTEGER RESULT_CACHE DETERMINISTIC IS
        ip RAW(16);
        cidr INTEGER;
    BEGIN
        IF SubnetMask IS NULL THEN
            RETURN NULL;
        END IF; 
            
        IF REGEXP_LIKE(SubnetMask, ':') THEN
            ip := IP2RAW(SubnetMask, 6);
            cidr := 128-LOG(2, TO_NUMBER(UTL_RAW.BIT_COMPLEMENT(ip), 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')+1);
        ELSE
            ip := IP2RAW(SubnetMask, 4);
            cidr := 32-LOG(2, TO_NUMBER(UTL_RAW.BIT_COMPLEMENT(ip), 'XXXXXXXX')+1);
        END IF;
        RETURN cidr;
    
    END SubnetMask2CIDR;
    
    
    
    FUNCTION CIDR2SubnetMask(CIDR IN INTEGER, ver IN INTEGER) RETURN VARCHAR2 RESULT_CACHE DETERMINISTIC IS
    BEGIN
        IF CIDR IS NULL THEN
            RETURN NULL;
        END IF;
        
        IF ver = 4 THEN
            IF CIDR NOT BETWEEN 0 AND 32 THEN
                RAISE VALUE_ERROR;
            END IF;
            RETURN RAW2IP(UTL_RAW.BIT_COMPLEMENT(HEXTORAW(LPAD(TO_CHAR(2**(32-cidr)-1, 'fmXXXXXXXX'),8 , '0'))), 4);
        ELSIF ver = 6 THEN
            IF CIDR NOT BETWEEN 0 AND 128 THEN
                RAISE VALUE_ERROR;
            END IF; 
            RETURN RAW2IP(UTL_RAW.BIT_COMPLEMENT(HEXTORAW(LPAD(TO_CHAR(2**(128-cidr)-1, 'fmXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'),32 , '0'))), 6);     
        ELSE
            RAISE VALUE_ERROR;
        END IF;
    END CIDR2SubnetMask;
    
    
    
    
    FUNCTION UncompressIpV6(Ip IN VARCHAR2, leadingZero IN BOOLEAN DEFAULT TRUE) RETURN VARCHAR2 DETERMINISTIC IS
        IpFull VARCHAR2(50);
        len INTEGER := 7;
        TYPE VARCHAR_TABLE_TYPE IS TABLE OF VARCHAR2(10);
        BitFields VARCHAR_TABLE_TYPE;
        cidr VARCHAR2(5);
    BEGIN
        IF NOT REGEXP_LIKE(Ip, ':') THEN
            RETURN Ip;
        END IF;
        
        cidr := REGEXP_SUBSTR(Ip, '/\d{1,3}$');
        IpFull := REGEXP_REPLACE(Ip, '/\d{1,3}$');
        
        IF REGEXP_LIKE(IpFull, '::') THEN
            IpFull := REGEXP_REPLACE(REGEXP_REPLACE(IpFull, '^::', '0::'), '::$', '::0');
            IF REGEXP_LIKE(IpFull, ':\d+\.\d+\.\d+\.\d+') THEN
                -- Mixed notation, e.g.: 2002::FFFF:129.144.52.38
                len := 6;
            END IF;
            WHILE REGEXP_COUNT(IpFull, ':') <= len LOOP
                IpFull := REGEXP_REPLACE(IpFull, '::', ':0::');
            END LOOP;   
            IpFull := REGEXP_REPLACE(IpFull, '::', ':');
        END IF;
    
        IF NOT leadingZero THEN
            RETURN LOWER(IpFull||cidr);
        END IF;
        
        SELECT REGEXP_SUBSTR(IpFull, '[^:]+', 1, LEVEL) 
        BULK COLLECT INTO BitFields
        FROM dual
        CONNECT BY REGEXP_SUBSTR(IpFull, '[^:]+', 1, LEVEL) IS NOT NULL;
        
        IpFull := LPAD(BitFields(1), 4, '0');
        FOR i IN 2..BitFields.COUNT LOOP
            IF REGEXP_LIKE(BitFields(i), '\d+\.\d+\.\d+\.\d+') THEN
                IpFull := IpFull ||':'||BitFields(i);
            ELSE
                IpFull := IpFull ||':'||LPAD(BitFields(i), 4, '0');
            END IF;
        END LOOP;
        RETURN LOWER(IpFull)||cidr;
    
    END UncompressIpV6;
    
    
    
    FUNCTION Canonical_IPv6(IPv6 IN VARCHAR2) RETURN VARCHAR2 DETERMINISTIC IS
        res VARCHAR2(50);
        cidr VARCHAR2(5);
    BEGIN       
        IF NOT REGEXP_LIKE(IPv6, ':') THEN
            RETURN IPv6;
        ELSIF REGEXP_LIKE(IPv6, '::') THEN
            -- Do not shorten twice
            res := UncompressIpV6(IPv6, FALSE);
        ELSE
            -- RFC 5952 section-4.3
            res := LOWER(IPv6);
        END IF;
        
        -- Split CIDR if existing
        cidr := REGEXP_SUBSTR(res, '/\d{1,3}$');
        res := REGEXP_REPLACE(res, '/\d{1,3}$');
        
        -- remove leading '0', RFC 5952 section-4.1
        res := REGEXP_REPLACE(res, '(:|^)0+([[:xdigit:]]+)', '\1\2');
        
        WITH ip AS
            -- split IP into 16-bit fields
            (SELECT REGEXP_SUBSTR(res, '[^:]+', 1, LEVEL) AS val, LEVEL AS pos
            FROM DUAL
            CONNECT BY REGEXP_SUBSTR(res, '[^:]+', 1, LEVEL) IS NOT NULL),
        p AS 
            -- find consecutive (at least 2) 0 fields, RFC 5952 section-4.2.2
            (SELECT pos, len, match_num
            FROM ip
            MATCH_RECOGNIZE (
                ORDER BY pos
                MEASURES 
                    FINAL COUNT(*) AS len,
                    MATCH_NUMBER() AS match_num
                ALL ROWS PER MATCH
                PATTERN(zero{2,})
                DEFINE zero AS val = '0')
            ),      
        m AS 
            -- select longest run of consecutive 0 fields, RFC 5952 section-4.2.3
            (SELECT * FROM p WHERE len = (SELECT MAX(len) FROM p)),
        f AS 
            -- select first sequence of longest run of consecutive 0 fields, RFC 5952 section-4.2.3
            (SELECT * FROM m WHERE match_num = (SELECT MIN(match_num) FROM m))
        SELECT REGEXP_REPLACE(LISTAGG(NVL2(match_num, ':', val), ':') WITHIN GROUP (ORDER BY pos), ':{2,}', '::')
        INTO res
        FROM ip
            LEFT OUTER JOIN f USING (pos);
        RETURN res||cidr;
    
    END Canonical_IPv6;
    
    
    END IP_Util;
    /
    

    次に、たとえば次のように使用できます:

    SELECT 
       IP_Util.NetworkPrefix('2620:0:2d0:200::7/32'), 
       IP_Util.BroadcastIp('2620:0:2d0:200::7/32') 
    FROM dual;
    
    
    2620::    2620:0:ffff:ffff:ffff:ffff:ffff:ffff
    

    2620:0:0:0:0:0:0:0をご希望の場合 次に

    を使用します
    IP_Util.UncompressIpV6(IP_Util.NetworkPrefix('2620:0:2d0:200::7/32'), false)
    

    ただし、RFC 5952 2620::によると 推奨される形式です。

    このパッケージの使用方法の例をいくつか示します。

    -- Determine if (IPv4) Address is a Private IP:
    CREATE OR REPLACE FUNCTION IsPrivate_IP(ip IN VARCHAR2) RETURN NUMBER DETERMINISTIC IS
    BEGIN
        IF IP_Util.NetworkPrefix('10.0.0.0', '255.0.0.0') = IP_Util.NetworkPrefix(ip, '255.0.0.0') THEN
            RETURN 1;
        ELSIF IP_Util.NetworkPrefix('172.16.0.0', '255.240.0.0') = IP_Util.NetworkPrefix(ip, '255.240.0.0') THEN
            RETURN 1;
        ELSIF IP_Util.NetworkPrefix('192.168.0.0', '255.255.0.0') = IP_Util.NetworkPrefix(ip, '255.255.0.0') THEN
            RETURN 1;
        ELSE
            RETURN 0;
        END IF;
    
    END IsPrivate_IP;
    

    または、6to4および6RDネットワークプレフィックスを使用してIPv4をIPv6に、またはその逆に変換する、より複雑なもの:

    CREATE OR REPLACE FUNCTION NAT64(ip IN VARCHAR2, IpV6mask IN VARCHAR2 DEFAULT '::ffff:0:0') RETURN VARCHAR2 DETERMINISTIC IS
        shift INTEGER;
        cidr INTEGER;
        n NUMBER;
        a RAW(16);
        b RAW(16);
    BEGIN
        IF REGEXP_LIKE(ip, ':') THEN
            -- Translate from IPv6 to IPv4
            IF NOT REGEXP_LIKE(IpV6mask, '/\d+{1,3}$') THEN
                RETURN IP_Util.RAW2IP(UTL_RAW.BIT_AND(IP_Util.IP2Raw(ip), HEXTORAW('000000000000000000000000FFFFFFFF')), 4);
            ELSE
                shift := 128 - REGEXP_SUBSTR(IpV6mask, '\d+{1,3}$');
                IF shift < 32 THEN 
                    RAISE VALUE_ERROR;
                END IF;
                -- Generate mask for IPv4 address, e.g. '0000000000000000FFFFFFFF00000000'
                b := HEXTORAW(LPAD(TO_CHAR((2**shift-1) - (2**(shift-32)-1), 'fmXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'),32 , '0'));
                n := TO_NUMBER(UTL_RAW.BIT_AND(IP_Util.IP2Raw(ip), b), 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
                -- UTL_RAW.SUBSTR does not work because you can shift only Bytes, I need Bits
                RETURN IP_Util.Decimal2IP(TRUNC(n / 2**(shift-32)), 4);
            END IF;
        ELSE
            -- Translate from IPv4 to IPv6
            IF NOT REGEXP_LIKE(IpV6mask, '/\d+{1,3}$') THEN
                a := UTL_RAW.BIT_AND(IP_Util.IP2Raw(IpV6mask), UTL_RAW.BIT_COMPLEMENT(HEXTORAW(LPAD(TO_CHAR(2**32-1 , 'fmXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'),32 , '0'))));
                RETURN IP_Util.RAW2IP(UTL_RAW.BIT_OR(a, IP_Util.IP2RAW(ip, 6)), 6);
            ELSE
                cidr := REGEXP_SUBSTR(IpV6mask, '\d+{1,3}$');
                shift := 128 - 32 - cidr;
                IF shift < 0 THEN 
                    RAISE VALUE_ERROR;
                END IF;
    
                a := UTL_RAW.BIT_AND(IP_Util.IP2Raw(IpV6mask), UTL_RAW.BIT_COMPLEMENT(HEXTORAW(LPAD(TO_CHAR(2**(128-cidr)-1 , 'fmXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'),32 , '0'))));
                b := HEXTORAW(LPAD(TO_CHAR(2**shift * IP_Util.IP2Decimal(ip), 'fmXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'),32 , '0'));
                RETURN IP_Util.RAW2IP(UTL_RAW.BIT_OR(a, b), 6);
            END IF;     
        END IF;
    END NAT64;
    



    1. 一括収集の使用中にLIMITの値を設定する

    2. MySQL-挿入後に同じテーブルを更新するためのトリガー

    3. mysql / fuzzy検索のためのレーベンシュタイン距離の実装?

    4. SQLServerANYオペレーターの説明