Skip to content

Instantly share code, notes, and snippets.

@tklauser
Created June 24, 2022 21:12
Show Gist options
  • Save tklauser/68e341befa2066b6fb5b59a0dda3669f to your computer and use it in GitHub Desktop.
Save tklauser/68e341befa2066b6fb5b59a0dda3669f to your computer and use it in GitHub Desktop.
net/netip <-> net conversion helpers
// Package netconv provides utilities to convert between types in packages net
// and netip.
package netconv
import (
"net"
"net/netip"
)
// PrefixToIPNet returns p as a *net.IPNet.
// TODO: special cases
func PrefixToIPNet(p netip.Prefix) *net.IPNet {
if !p.IsValid() {
return nil
}
addr := p.Masked().Addr()
return &net.IPNet{
IP: addr.AsSlice(),
Mask: net.CIDRMask(p.Bits(), addr.BitLen()),
}
}
// IPNetToPrefix return n as a netip.Prefix.
// TODO: special cases
func IPNetToPrefix(n *net.IPNet) netip.Prefix {
if n == nil {
return netip.Prefix{}
}
ones, bits := n.Mask.Size()
if bits != net.IPv4len*8 && bits != net.IPv6len*8 {
// invalid mask
return netip.Prefix{}
}
ip, ok := netip.AddrFromSlice(n.IP)
if !ok {
return netip.Prefix{}
}
return netip.PrefixFrom(ip, ones)
}
package netconv_test
import (
"net"
"net/netip"
"reflect"
"testing"
"github.com/tklauser/netconv"
)
func mustParseCIDR(s string) *net.IPNet {
_, net, err := net.ParseCIDR(s)
if err != nil {
panic(err)
}
return net
}
func TestPrefixToIPNet(t *testing.T) {
tests := []struct {
prefix netip.Prefix
net *net.IPNet
}{
{
prefix: netip.MustParsePrefix("0.0.0.0/24"),
net: &net.IPNet{
IP: net.IPv4(0, 0, 0, 0).To4(),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
},
{
prefix: netip.MustParsePrefix("0.0.0.0/0"),
net: mustParseCIDR("0.0.0.0/0"),
},
{
prefix: netip.MustParsePrefix("10.0.0.1/32"),
net: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1).To4(),
Mask: net.IPv4Mask(255, 255, 255, 255),
},
},
{
prefix: netip.MustParsePrefix("10.0.128.0/18"),
net: mustParseCIDR("10.0.129.1/18"),
},
{
prefix: netip.MustParsePrefix("::1/128"),
net: &net.IPNet{
IP: net.ParseIP("::1"),
Mask: net.CIDRMask(128, 128),
},
},
{
prefix: netip.MustParsePrefix("fd00::1/96"),
net: &net.IPNet{
IP: net.ParseIP("fd00::0"),
Mask: net.CIDRMask(96, 128),
},
},
{
prefix: netip.Prefix{},
net: nil,
},
}
for _, tc := range tests {
net := netconv.PrefixToIPNet(tc.prefix)
if !reflect.DeepEqual(net, tc.net) {
t.Errorf("PrefixToIPNet(%q)\n"+
" got %#v\n"+
" want %#v", tc.prefix, net, tc.net)
}
}
}
func TestIPNetToPrefix(t *testing.T) {
tests := []struct {
net *net.IPNet
prefix netip.Prefix
}{
{
net: mustParseCIDR("0.0.0.0/24"),
prefix: netip.MustParsePrefix("0.0.0.0/24"),
},
{
net: mustParseCIDR("0.0.0.0/0"),
prefix: netip.MustParsePrefix("0.0.0.0/0"),
},
{
net: mustParseCIDR("10.0.129.1/18"),
prefix: netip.MustParsePrefix("10.0.128.0/18"),
},
{
net: &net.IPNet{
IP: net.IPv4(0, 0, 0, 0).To4(),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
prefix: netip.MustParsePrefix("0.0.0.0/24"),
},
{
net: &net.IPNet{
IP: net.IPv4(0, 0, 0, 0),
Mask: net.IPv4Mask(255, 255, 255, 255),
},
prefix: netip.MustParsePrefix("0.0.0.0/32"),
},
{
// invalid IP
net: &net.IPNet{
IP: net.IP{1, 2},
Mask: net.IPv4Mask(255, 255, 255, 255),
},
prefix: netip.Prefix{},
},
{
// invalid mask
net: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
},
prefix: netip.Prefix{},
},
{
// non-contiguous mask
net: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 3, 255, 0),
},
prefix: netip.Prefix{},
},
{
net: nil,
prefix: netip.Prefix{},
},
}
for _, tc := range tests {
prefix := netconv.IPNetToPrefix(tc.net)
if !reflect.DeepEqual(prefix, tc.prefix) {
t.Errorf("IPNetToPrefix(%q)\n"+
" got %v\n"+
" want %v", tc.net, prefix, tc.prefix)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment