Skip to content

Instantly share code, notes, and snippets.

@ammario
Last active April 20, 2024 12:51
Show Gist options
  • Star 73 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save ammario/649d4c0da650162efd404af23e25b86b to your computer and use it in GitHub Desktop.
Save ammario/649d4c0da650162efd404af23e25b86b to your computer and use it in GitHub Desktop.
Golang IP <-> int conversion
func ip2int(ip net.IP) uint32 {
if len(ip) == 16 {
return binary.BigEndian.Uint32(ip[12:16])
}
return binary.BigEndian.Uint32(ip)
}
func int2ip(nn uint32) net.IP {
ip := make(net.IP, 4)
binary.BigEndian.PutUint32(ip, nn)
return ip
}
@pidgy
Copy link

pidgy commented Jun 25, 2020

Yeah, i got it. I put it here for future adventurers of GO

func ipv6ToInt(IPv6Addr net.IP) *big.Int {
    IPv6Int := big.NewInt(0)
    IPv6Int.SetBytes(IPv6Addr)
    return IPv6Int
} 
func IntToIpv6(intipv6 *big.Int) net.IP {
    ip := intipv6.Bytes()
    var a net.IP = ip
    return a
}

Regards

For the second conversion, could you not just do:

func IntToIpv6(intipv6 *big.Int) net.IP {
   return net.IP(intipv6.Bytes())
}

or even

func IntToIpv6(intipv6 *big.Int) net.IP {
   return intipv6.Bytes()
}

@yagashiraevil
Copy link

In my Active Directory, the IP 173.208.122.131 converts to this -1378846077 when I use the above function ip2int it converts to this 2916121219
how can I convert these IP'sto Active Directory IP bytes

@shipa988
Copy link

shipa988 commented May 20, 2021

if you want convert ip address represents srting type (often case when you make web services), you have string ip <->int converting. The converting method above makes allocations and overhead in stdlib net\ip.go line 49,50
p := make(IP, IPv6len) copy(p, v4InV6Prefix)
You may copy some funcs from stdlib packet and a little modify they such as
https://play.golang.org/p/z_KMQrEsYqP
This func ParseIPv4(s string) int makes int from string without overhead in allocations

@zhlsunshine
Copy link

Yeah, i got it. I put it here for future adventurers of GO

func ipv6ToInt(IPv6Addr net.IP) *big.Int {
    IPv6Int := big.NewInt(0)
    IPv6Int.SetBytes(IPv6Addr)
    return IPv6Int
} 
func IntToIpv6(intipv6 *big.Int) net.IP {
    ip := intipv6.Bytes()
    var a net.IP = ip
    return a
}

Regards

Hi @srPuebla, are you using these 2 functions, especially for IntToIpv6 ? I think there is a bug for it, you can try the case:

  1. convert ::1 to bigInt by ipv6ToInt
  2. reverse the conversion by using IntToIpv6
    After above steps, you may find the problem. Besides, there should be a new solution to handle it.

@ecigar13
Copy link

ecigar13 commented Oct 4, 2022

@zhlsunshine The reason is the net.IP accepts an array of byte. When converting back from BigInt, it needs to fill the prefix with 0s up to the length of the IP or it can't recognize the IP. See this example: https://pkg.go.dev/net#IP
praserx has a solution for this: https://github.com/praserx/ipconv/blob/master/ipconv.go
Or just write a loop to determine the IP type (v4 or v6), then fill out the prefix slice.

@ecigar13
Copy link

ecigar13 commented Oct 6, 2022

@ammario Can you explain why you check len(ip) == 16? Wouldn't it return only a quarter of the ipv6 IP?

@ammario
Copy link
Author

ammario commented Oct 7, 2022

@ammario Can you explain why you check len(ip) == 16? Wouldn't it return only a quarter of the ipv6 IP?

I honestly can't remember why I did that. The behavior doesn't seem useful, it should probably panic instead. I updated the gist.

@wilrodriguez
Copy link

@ammario Can you explain why you check len(ip) == 16? Wouldn't it return only a quarter of the ipv6 IP?

I honestly can't remember why I did that. The behavior doesn't seem useful, it should probably panic instead. I updated the gist.

I'm surprised no one has mentioned it yet, but this latest change actually breaks the code. The length of both ipv4 and ipv6 addresses in net.IP representation is 16 bytes due the data structure being designed to accommodate both types of addresses. As proof of this point, see the following unit test:

func TestIPRepresentation(t *testing.T) {
	ipv4Str := "192.168.1.1"
	ipv6Str := "2001:0db8:85a3:0000:0000:8a2e:0370:7334"

	ipv4 := net.ParseIP(ipv4Str)
	ipv6 := net.ParseIP(ipv6Str)

	t.Logf("IPv4: %s, Length: %d bytes", ipv4, len(ipv4))
	t.Logf("IPv6: %s, Length: %d bytes", ipv6, len(ipv6))

	if len(ipv4) != 16 {
		t.Errorf("IPv4 length is not 16 bytes")
	}

	if len(ipv6) != 16 {
		t.Errorf("IPv6 length is not 16 bytes")
	}
}

This test results in the following output:

=== RUN   TestIPRepresentation
    ./networking_test.go:72: IPv4: 192.168.1.1, Length: 16 bytes
    ./networking_test.go:73: IPv6: 2001:db8:85a3::8a2e:370:7334, Length: 16 bytes
--- PASS: TestIPRepresentation (0.00s)
PASS
ok      gitlab.eitccorp.com/elicsar/elx/elx-manager/pkg/eitccommon      0.231s

I've come up with this alternative implementation that depends on net.IP's To4() method, which returns nil for anything that's not a valid v4 IP.

func IPv4toInt(ipv4 net.IP) (uint32, error) {
	ipv4Bytes := ipv4.To4()
	if ipv4Bytes == nil {
		return 0, errors.New("not a valid IPv4 address")
	}
	return binary.BigEndian.Uint32(ipv4Bytes), nil
}

Note that my implementation does return error, so, if you need a function that only returns a single value, you can always change to a penic like the original function, but I personally like returning an error better.

@ammario
Copy link
Author

ammario commented Aug 29, 2023

@wilrodriguez I didn't realize how popular this gist was and recklessly updated it. I reverted it back per your message. Thank you!

@barbiequeue
Copy link

Really helpful!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment