3 changed files with 258 additions and 0 deletions
			
			
		@ -0,0 +1,77 @@
					 | 
				
			||||
package rings | 
				
			||||
 | 
				
			||||
import "net/netip" | 
				
			||||
 | 
				
			||||
// AddrFromU32 converts a 32bit value into an IPv4
 | 
				
			||||
// address.
 | 
				
			||||
func AddrFromU32(v uint32) netip.Addr { | 
				
			||||
	return AddrFrom4( | 
				
			||||
		uint(v>>24), | 
				
			||||
		uint(v>>16), | 
				
			||||
		uint(v>>8), | 
				
			||||
		uint(v), | 
				
			||||
	) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// AddrFrom4 assembles an IPv4 address for 4 numbers.
 | 
				
			||||
// each number is truncated to 8-bits.
 | 
				
			||||
func AddrFrom4(a, b, c, d uint) netip.Addr { | 
				
			||||
	return netip.AddrFrom4([4]byte{ | 
				
			||||
		byte(a & 0xff), | 
				
			||||
		byte(b & 0xff), | 
				
			||||
		byte(c & 0xff), | 
				
			||||
		byte(d & 0xff), | 
				
			||||
	}) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// AddrToU32 converts a valid IPv4 address into it's
 | 
				
			||||
// 32bit numeric representation.
 | 
				
			||||
func AddrToU32(addr netip.Addr) (v uint32, ok bool) { | 
				
			||||
	if addr.IsValid() { | 
				
			||||
		if addr.Is4() || addr.Is4In6() { | 
				
			||||
			a4 := addr.As4() | 
				
			||||
 | 
				
			||||
			v = uint32(a4[0])<<24 + | 
				
			||||
				uint32(a4[1])<<16 + | 
				
			||||
				uint32(a4[2])<<8 + | 
				
			||||
				uint32(a4[3]) | 
				
			||||
			return v, true | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return 0, false | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// PrefixToRange returns the beginning and end of a
 | 
				
			||||
// [netip.Prefix] (aka CIDR or subnet).
 | 
				
			||||
func PrefixToRange(subnet netip.Prefix) (from, to netip.Addr, ok bool) { | 
				
			||||
	var u uint32 | 
				
			||||
 | 
				
			||||
	addr := subnet.Addr() | 
				
			||||
	if u, ok = AddrToU32(addr); ok { | 
				
			||||
		bits := subnet.Bits() | 
				
			||||
 | 
				
			||||
		switch { | 
				
			||||
		case bits > 32, bits < 0: | 
				
			||||
			// bad
 | 
				
			||||
		case bits == 32: | 
				
			||||
			// single
 | 
				
			||||
			from, to, ok = addr, addr, true | 
				
			||||
		default: | 
				
			||||
			// subnet
 | 
				
			||||
			shift := 32 - bits | 
				
			||||
 | 
				
			||||
			m1 := uint32((1 << shift) - 1) | 
				
			||||
			m0 := uint32(0xffffffff) & ^m1 | 
				
			||||
 | 
				
			||||
			u0 := u & m0 | 
				
			||||
			u1 := u0 + m1 | 
				
			||||
 | 
				
			||||
			ok = true | 
				
			||||
			from = AddrFromU32(u0) | 
				
			||||
			to = AddrFromU32(u1) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return from, to, ok | 
				
			||||
} | 
				
			||||
@ -0,0 +1,178 @@
					 | 
				
			||||
package rings | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"fmt" | 
				
			||||
	"net/netip" | 
				
			||||
	"testing" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func TestAddrFrom4(t *testing.T) { | 
				
			||||
	cases := []struct { | 
				
			||||
		v [4]uint | 
				
			||||
		s string | 
				
			||||
	}{ | 
				
			||||
		{[4]uint{0, 0, 0, 0}, "0.0.0.0"}, | 
				
			||||
		{[4]uint{127, 0, 0, 1}, "127.0.0.1"}, | 
				
			||||
		{[4]uint{4096 + 127, 0, 0, 1}, "127.0.0.1"}, | 
				
			||||
		{[4]uint{257, 258, 259, 260}, "1.2.3.4"}, | 
				
			||||
		{[4]uint{255, 255, 255, 255}, "255.255.255.255"}, | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	for i, tc := range cases { | 
				
			||||
		fn := fmt.Sprintf("%v.%v.%v.%v", tc.v[0], tc.v[1], tc.v[2], tc.v[3]) | 
				
			||||
		addr := AddrFrom4(tc.v[0], tc.v[1], tc.v[2], tc.v[3]) | 
				
			||||
		s := addr.String() | 
				
			||||
 | 
				
			||||
		if s == tc.s { | 
				
			||||
			t.Logf("[%v/%v]: %s → %s", i, len(cases), fn, s) | 
				
			||||
		} else { | 
				
			||||
			t.Errorf("ERROR: [%v/%v]: %s → %s (expected %s)", i, len(cases), fn, s, tc.s) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func TestAddrU32Invalid(t *testing.T) { | 
				
			||||
	cases := []netip.Addr{ | 
				
			||||
		{}, | 
				
			||||
		netip.IPv6Unspecified(), | 
				
			||||
		netip.IPv6Loopback(), | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	for i, tc := range cases { | 
				
			||||
		v, ok := AddrToU32(tc) | 
				
			||||
		switch { | 
				
			||||
		case !ok && v == 0: | 
				
			||||
			t.Logf("[%v/%v]: %s → %v %v", i, len(cases), tc, 0, false) | 
				
			||||
		default: | 
				
			||||
			t.Errorf("ERROR: [%v/%v]: %s → %v %v (expected %v %v)", i, len(cases), | 
				
			||||
				tc, v, ok, 0, false) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func TestAddrU32Valid(t *testing.T) { | 
				
			||||
	cases := []netip.Addr{ | 
				
			||||
		netip.IPv4Unspecified(), | 
				
			||||
		AddrFrom4(0, 0, 0, 0), | 
				
			||||
		AddrFrom4(1, 2, 3, 4), | 
				
			||||
		AddrFrom4(10, 20, 30, 40), | 
				
			||||
		AddrFrom4(127, 0, 0, 1), | 
				
			||||
		AddrFrom4(255, 255, 255, 255), | 
				
			||||
		MustParseAddr("::ffff:1.2.3.4"), | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	for i, tc := range cases { | 
				
			||||
		u32, ok := AddrToU32(tc) | 
				
			||||
		if !ok { | 
				
			||||
			t.Errorf("ERROR: [%v/%v]: %s → %v %v", i, len(cases), tc, u32, ok) | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		addr := AddrFromU32(u32) | 
				
			||||
		if tc.Is4In6() { | 
				
			||||
			ok = addr.Compare(tc.Unmap()) == 0 | 
				
			||||
		} else { | 
				
			||||
			ok = addr.Compare(tc) == 0 | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		if ok { | 
				
			||||
			t.Logf("[%v/%v]: %s → %v → %s", i, len(cases), tc, u32, addr) | 
				
			||||
		} else { | 
				
			||||
			t.Errorf("ERROR: [%v/%v]: %s → %v → %s", i, len(cases), tc, u32, addr) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func MustParseAddr(s string) netip.Addr { | 
				
			||||
	addr, err := netip.ParseAddr(s) | 
				
			||||
	if err != nil { | 
				
			||||
		panic(err) | 
				
			||||
	} | 
				
			||||
	return addr | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func MustParsePrefix(s string) netip.Prefix { | 
				
			||||
	subnet, err := netip.ParsePrefix(s) | 
				
			||||
	if err != nil { | 
				
			||||
		panic(err) | 
				
			||||
	} | 
				
			||||
	return subnet | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func TestPrefixToRangeValid(t *testing.T) { | 
				
			||||
	cases := []struct { | 
				
			||||
		subnet netip.Prefix | 
				
			||||
		from   netip.Addr | 
				
			||||
		to     netip.Addr | 
				
			||||
	}{ | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("127.0.0.1/32"), | 
				
			||||
			MustParseAddr("127.0.0.1"), | 
				
			||||
			MustParseAddr("127.0.0.1"), | 
				
			||||
		}, | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("127.0.0.1/24"), | 
				
			||||
			MustParseAddr("127.0.0.0"), | 
				
			||||
			MustParseAddr("127.0.0.255"), | 
				
			||||
		}, | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("127.0.1.2/16"), | 
				
			||||
			MustParseAddr("127.0.0.0"), | 
				
			||||
			MustParseAddr("127.0.255.255"), | 
				
			||||
		}, | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("127.1.2.3/8"), | 
				
			||||
			MustParseAddr("127.0.0.0"), | 
				
			||||
			MustParseAddr("127.255.255.255"), | 
				
			||||
		}, | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("10.20.30.40/12"), | 
				
			||||
			MustParseAddr("10.16.0.0"), | 
				
			||||
			MustParseAddr("10.31.255.255"), | 
				
			||||
		}, | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("10.20.30.40/20"), | 
				
			||||
			MustParseAddr("10.20.16.0"), | 
				
			||||
			MustParseAddr("10.20.31.255"), | 
				
			||||
		}, | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("10.0.0.0/12"), | 
				
			||||
			MustParseAddr("10.0.0.0"), | 
				
			||||
			MustParseAddr("10.15.255.255"), | 
				
			||||
		}, | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("10.16.0.0/12"), | 
				
			||||
			MustParseAddr("10.16.0.0"), | 
				
			||||
			MustParseAddr("10.31.255.255"), | 
				
			||||
		}, | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("10.32.0.0/12"), | 
				
			||||
			MustParseAddr("10.32.0.0"), | 
				
			||||
			MustParseAddr("10.47.255.255"), | 
				
			||||
		}, | 
				
			||||
		{ | 
				
			||||
			MustParsePrefix("10.48.0.0/12"), | 
				
			||||
			MustParseAddr("10.48.0.0"), | 
				
			||||
			MustParseAddr("10.63.255.255"), | 
				
			||||
		}, | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	for i, tc := range cases { | 
				
			||||
		from, to, ok := PrefixToRange(tc.subnet) | 
				
			||||
		if ok && from.IsValid() && to.IsValid() && | 
				
			||||
			from.Compare(tc.from) == 0 && | 
				
			||||
			to.Compare(tc.to) == 0 { | 
				
			||||
			//
 | 
				
			||||
			t.Logf("[%v/%v]: %s → %s - %s", | 
				
			||||
				i, len(cases), | 
				
			||||
				tc.subnet, | 
				
			||||
				from, to) | 
				
			||||
		} else { | 
				
			||||
			t.Errorf("ERROR: [%v/%v]: %q → %s - %s %v (expected %s - %s %v)", | 
				
			||||
				i, len(cases), | 
				
			||||
				tc.subnet, | 
				
			||||
				from, to, ok, | 
				
			||||
				tc.from, tc.to, true) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue