diff --git a/pkg/rings/cidr.go b/pkg/rings/cidr.go new file mode 100644 index 0000000..36e821d --- /dev/null +++ b/pkg/rings/cidr.go @@ -0,0 +1,54 @@ +package rings + +import "net/netip" + +func PrefixToRange(subnet netip.Prefix) (from, to netip.Addr) { + if u, ok := addrToU32(subnet.Addr()); ok { + shift := 32 - subnet.Bits() + + m1 := uint32((1 << shift) - 1) + m0 := uint32(0xffffffff) & ^m1 + + u0 := u & m0 + u1 := u0 + m1 + + from = u32ToAddr(u0) + to = u32ToAddr(u1) + } + + return from, to +} + +func u32ToAddr(v uint32) netip.Addr { + return addrFrom4( + uint(v>>24)&0xff, + uint(v>>16)&0xff, + uint(v>>8)&0xff, + uint(v)&0xff, + ) +} + +func addrToU32(addr netip.Addr) (uint32, bool) { + switch { + case !addr.IsValid(): + return 0, false + case 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 + default: + return 0, false + } +} + +func addrFrom4(a, b, c, d uint) netip.Addr { + return netip.AddrFrom4([4]byte{ + byte(a), + byte(b), + byte(c), + byte(d), + }) +} diff --git a/pkg/rings/errors.go b/pkg/rings/errors.go new file mode 100644 index 0000000..e66f79b --- /dev/null +++ b/pkg/rings/errors.go @@ -0,0 +1,11 @@ +package rings + +import ( + "syscall" + + "darvaza.org/core" +) + +func ErrOutOfRange[T ~int](value T, field string) error { + return core.Wrap(syscall.EINVAL, "%v: %s out of range", value, field) +} diff --git a/pkg/rings/rings.go b/pkg/rings/rings.go new file mode 100644 index 0000000..ad39f19 --- /dev/null +++ b/pkg/rings/rings.go @@ -0,0 +1,118 @@ +// Package rings implements ... +package rings + +import ( + "net/netip" +) + +const ( + RegionBits = 4 + ZoneBits = 4 + NodeBits = 12 + NodeZeroBits = 8 + + RegionMax = (1 << RegionBits) - 1 + ZoneMax = (1 << ZoneBits) - 1 + NodeMax = (1 << NodeBits) - 1 + NodeZeroMax = (1 << NodeZeroBits) - 1 + + RingZeroBits = 20 + RingOneBits = 20 + RingTwoBits = 20 + RingThreeBits = 12 +) + +type ( + RegionID int + ZoneID int + NodeID int +) + +func (n RegionID) Valid() bool { return n > 0 && n <= RegionMax } +func (n ZoneID) Valid() bool { return n > 0 && n <= ZoneMax } +func (n NodeID) Valid() bool { return n > 0 && n <= NodeMax } +func (n NodeID) ValidZero() bool { return n > 0 && n <= NodeZeroMax } + +// 10.0.(region_id << 4 + zone_id).(node_id)/20 for ring 0 +func RingZeroAddress(region RegionID, zone ZoneID, node NodeID) (addr netip.Addr, err error) { + switch { + case !region.Valid(): + err = ErrOutOfRange(region, "region") + case !zone.Valid(): + err = ErrOutOfRange(zone, "zone") + case !node.ValidZero(): + err = ErrOutOfRange(node, "node") + default: + addr = addrFrom4(10, 0, uint(region)<<4+uint(zone), uint(node)) + } + + return addr, err +} + +func RingZeroPrefix(region RegionID, zone ZoneID) (cidr netip.Prefix, err error) { + switch { + case !region.Valid(): + err = ErrOutOfRange(region, "region") + case !zone.Valid(): + err = ErrOutOfRange(zone, "zone") + default: + addr := addrFrom4(10, 0, uint(region)<<4+uint(zone), 0) + cidr = netip.PrefixFrom(addr, RingZeroBits) + } + + return cidr, err +} + +// 10.(region_id).(zone_id << 4).(node_id)/20 for ring 1 +func RingOneAddress(region RegionID, zone ZoneID, node NodeID) (addr netip.Addr, err error) { + switch { + case !region.Valid(): + err = ErrOutOfRange(region, "region") + case !zone.Valid(): + err = ErrOutOfRange(zone, "zone") + case !node.Valid(): + err = ErrOutOfRange(node, "node") + default: + n1 := uint(node) / 8 + n2 := uint(node) % 8 + addr = addrFrom4(10, uint(region), uint(zone)<<4+n1, n2) + } + return addr, err +} + +func RingOnePrefix(region RegionID, zone ZoneID) (cidr netip.Prefix, err error) { + switch { + case !region.Valid(): + err = ErrOutOfRange(region, "region") + case !zone.Valid(): + err = ErrOutOfRange(zone, "zone") + default: + addr := addrFrom4(10, uint(region), uint(zone)<<4, 0) + cidr = netip.PrefixFrom(addr, RingOneBits) + } + return cidr, err +} + +// 10.(region_id).0.0/20 for ring 2 +func RingTwoPrefix(region RegionID) (cidr netip.Prefix, err error) { + switch { + case !region.Valid(): + err = ErrOutOfRange(region, "region") + default: + addr := addrFrom4(10, uint(region), 0, 0) + cidr = netip.PrefixFrom(addr, RingTwoBits) + } + return cidr, err +} + +// 10.(region_id << 4).0.0/12 for ring 3 +func RingThreePrefix(region RegionID) (cidr netip.Prefix, err error) { + switch { + case !region.Valid(): + err = ErrOutOfRange(region, "region") + default: + addr := addrFrom4(10, uint(region)<<4, 0, 0) + cidr = netip.PrefixFrom(addr, RingThreeBits) + } + return cidr, err +}