Php convert ipv6 to number

前端 未结 7 878
醉话见心
醉话见心 2021-01-31 20:53

In Ipv4 we can use ip2long to convert it to number,

How to convert ipv6 compressed to number in PHP?

I tried inet_pton and it\'s not working.

<
相关标签:
7条回答
  • 2021-01-31 21:06

    $ip_2 is not a valid IPv6 address. You need "::" somewhere in there, to indicate the zero omission point.

    If you have it as one of

    $ip_2='2001::11ff:ffff:f';
    $ip_2='2001:11ff::ffff:f';
    $ip_2='2001:11ff:ffff::f';
    

    then inet_pton() should work fine.

    As already hinted, PHP doesn't have a 128 integer type, so the best you can get is a numeric string out of the binary string inet_pton() gives you... yes, that's what it is, and that's why it looks weird. If you look at the bits of that strings, you'll see they're exactly what you'd expect.

    Here's how to expand the binary string into a numeric string (argument "0" was missing from str_pad() originally):

    /**
     * @param string $ip A human readable IPv4 or IPv6 address.
     * @return string Decimal number, written out as a string due to limits on the size of int and float.
     */
    function ipv6_numeric($ip) {
        $binNum = '';
        foreach (unpack('C*', inet_pton($ip)) as $byte) {
            $binNum .= str_pad(decbin($byte), 8, "0", STR_PAD_LEFT);
        }
        return base_convert(ltrim($binNum, '0'), 2, 10);
    }
    
    0 讨论(0)
  • 2021-01-31 21:15

    Use:

    $ip  = 'fe80:0:0:0:202:b3ff:fe1e:8329';
    $dec = ip2long_v6($ip);
    $ip2 = long2ip_v6($dec);
    
    // $ip  = fe80:0:0:0:202:b3ff:fe1e:8329
    // $dec = 338288524927261089654163772891438416681
    // $ip2 = fe80::202:b3ff:fe1e:8329
    

    Functions:

    With enabled GMP or BCMATH extension.

    function ip2long_v6($ip) {
        $ip_n = inet_pton($ip);
        $bin = '';
        for ($bit = strlen($ip_n) - 1; $bit >= 0; $bit--) {
            $bin = sprintf('%08b', ord($ip_n[$bit])) . $bin;
        }
    
        if (function_exists('gmp_init')) {
            return gmp_strval(gmp_init($bin, 2), 10);
        } elseif (function_exists('bcadd')) {
            $dec = '0';
            for ($i = 0; $i < strlen($bin); $i++) {
                $dec = bcmul($dec, '2', 0);
                $dec = bcadd($dec, $bin[$i], 0);
            }
            return $dec;
        } else {
            trigger_error('GMP or BCMATH extension not installed!', E_USER_ERROR);
        }
    }
    
    function long2ip_v6($dec) {
        if (function_exists('gmp_init')) {
            $bin = gmp_strval(gmp_init($dec, 10), 2);
        } elseif (function_exists('bcadd')) {
            $bin = '';
            do {
                $bin = bcmod($dec, '2') . $bin;
                $dec = bcdiv($dec, '2', 0);
            } while (bccomp($dec, '0'));
        } else {
            trigger_error('GMP or BCMATH extension not installed!', E_USER_ERROR);
        }
    
        $bin = str_pad($bin, 128, '0', STR_PAD_LEFT);
        $ip = array();
        for ($bit = 0; $bit <= 7; $bit++) {
            $bin_part = substr($bin, $bit * 16, 16);
            $ip[] = dechex(bindec($bin_part));
        }
        $ip = implode(':', $ip);
        return inet_ntop(inet_pton($ip));
    }
    

    demo

    0 讨论(0)
  • 2021-01-31 21:17

    Try this

    function inet6_to_int64($addr)
    {
        /* Expand the address if necessary */
        if (strlen($addr) != 39) {
            $addr = inet6_expand($addr);
            if ($addr == false) return false;
        } // if
        $addr = str_replace(':', '', $addr);
        $p1 = '0x' . substr($addr, 0, 16);
        $p2 = '0x' . substr($addr, 16);
        $p1 = gmp_init($p1);
        $p2 = gmp_init($p2);
        $result = array(gmp_strval($p1), gmp_strval($p2));
        return $result;
    } // inet6_to_int64()
    

    For more functions or details please visit http://www.soucy.org/project/inet6/

    0 讨论(0)
  • 2021-01-31 21:23

    A version of the answer by boen_robot which avoids the overflow issue, using BC Math if available.

    function ipv62numeric($ip)
    {
        $str = '';
        foreach (unpack('C*', inet_pton($ip)) as $byte) {
            $str .= str_pad(decbin($byte), 8, '0', STR_PAD_LEFT);
        }
        $str = ltrim($str, '0');
        if (function_exists('bcadd')) {
            $numeric = 0;
            for ($i = 0; $i < strlen($str); $i++) {
                $right  = base_convert($str[$i], 2, 10);
                $numeric = bcadd(bcmul($numeric, 2), $right);
            }
            $str = $numeric;
        } else {
            $str = base_convert($str, 2, 10);
        }
    
        return $str;
    }
    

    Example:

    echo ipv62numeric('2001:11ff:ffff::f');
    

    Will return "42540853245347499564798372846648688655" as a string, which is the correct answer.

    0 讨论(0)
  • 2021-01-31 21:24

    Here's two functions that convert hex to decimal and decimal to hex. This only works with IPv6 hex and IPv6 integer representations (so, use ip2long() and long2ip() for IPv4 numbers). Theoretically, one could convert an IPv4 dotted notation number to hex values and use these, but that would probably be overkill.

    This will:

    • Convert all complete IPv6 numbers to a stringified long int (up to 39 characters, left padded for sorting if the flag is set to true.
    • Convert a stringified "long" back to a hexidecimal IPv6 representation, left padding as necessary to the full 32 bit hex string. Colons are optionally placed, right to left, if the appropriate flag is set to true.

    These can be modified to handle virtually any length hex value or virtually any length integer, and placement of colons and padding can be adjusted accordingly.

    HEX to DECIMAL

        function bchexdec($hex,$padl)
        // Input: A hexadecimal number as a String.
        // Output: The equivalent decimal number as a String.
        // - if padl==true then pad left to fill 39 characters for string sort
    
        {
            if (strlen($hex) != 39) 
            {
                $hex = inet6_expand($hex);
                if ($hex == false) return false;
            }
    
            $hex=str_replace(":","",$hex);
            $dec = 0;
            $len = strlen($hex);
            for ($i = 1; $i <= $len; $i++) 
            {
                $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
            }
    
            if ($padl==true)
            {
                $dec=str_pad($dec,39,"0",STR_PAD_LEFT);
            }
            return $dec;
    
        }
    

    DECIMAL to HEX

        function bcdechex($dec,$colon) 
        // Input: A decimal as a String.
        // Output: The equivalent hex value.
        // - if $colon==true then add colons.   
        {
            $hex = '';
    
            // RFC 5952, A Recommendation for IPv6 Address Text Representation, Section 4.3 Lowercase specifies letters should be lowercase. Though that generally doesn't matter, use lowercase
            //   to conform with the RFC for those rare systems that do so. 
            do 
            {    
                $last = bcmod($dec, 16);
                $hex = dechex($last).$hex;
                $dec = bcdiv(bcsub($dec, $last), 16);
            } while($dec>0);
            $hex=str_pad($hex,32,"0",STR_PAD_LEFT);
            // Add colons if $colon==true
            if ($colon==true)
            {
                $hex=strrev($hex);
                $chunks=str_split($hex,4);
                $hex=implode(":", $chunks);
                $hex=strrev($hex);
            }
    
            return $hex;
        }
    

    This is based off of ideas and examples found in a variety of places, as well as my own needs for easily sortable and easily storable IPv6 addresses.

    0 讨论(0)
  • 2021-01-31 21:26

    Note that all answers will result in incorrect results for big IP addresses or are going through a highly complicated process to receive the actual numbers. Retrieving the actual integer value from an IPv6 address requires two things:

    1. IPv6 support
    2. GMP extension (--with-gmp)

    With both prerequisites in place conversion is as simple as:

    $ip = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff';
    $int = gmp_import(inet_pton($ip));
    
    echo $int; // 340282366920938463463374607431768211455
    

    The binary numeric packed in_addr representation that is returned by inet_pton is already an integer and can directly be imported to GMP, as illustrated above. There is no need for special conversions or anything.

    Note that the other way around is equally simple:

    $int = '340282366920938463463374607431768211455';
    $ip = inet_ntop(str_pad(gmp_export($int), 16, "\0", STR_PAD_LEFT));
    
    echo $ip; // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
    

    Hence building the two desired functions is as simple as:

    function ipv6_to_integer($ip) {
        return (string) gmp_import(inet_pton($ip));
    }
    
    function ipv6_from_integer($integer) {
        return inet_ntop(str_pad(gmp_export($integer), 16, "\0", STR_PAD_LEFT));
    }
    
    0 讨论(0)
提交回复
热议问题