diff --git a/net/ip.go b/net/ip.go index 44b84d192402d5e309ea9e394ee5237664cecae2..83d86c3bf86878835af26f8f06f0015125154696 100644 --- a/net/ip.go +++ b/net/ip.go @@ -3,20 +3,55 @@ package net import ( "fmt" "net" + + "github.com/bio-routing/bio-rd/net/api" ) // IP represents an IPv4 or IPv6 address type IP struct { - higher uint64 - lower uint64 - ipVersion uint8 + higher uint64 + lower uint64 + isLegacy bool +} + +// IPFromProtoIP creates an IP address from a proto IP +func IPFromProtoIP(addr api.IP) IP { + return IP{ + higher: addr.Higher, + lower: addr.Lower, + isLegacy: addr.IsLegacy, + } +} + +// ToProto converts an IP to a proto IP +func (ip IP) ToProto() *api.IP { + return &api.IP{ + Lower: ip.lower, + Higher: ip.higher, + IsLegacy: ip.isLegacy, + } +} + +// Lower gets the lower half of the IP address +func (ip IP) Lower() uint64 { + return ip.lower +} + +// Higher gets the higher half of the IP address +func (ip IP) Higher() uint64 { + return ip.higher +} + +// IsLegacy returns true for IPv6, else false +func (ip IP) IsLegacy() bool { + return ip.isLegacy } // IPv4 returns a new `IP` representing an IPv4 address func IPv4(val uint32) IP { return IP{ - lower: uint64(val), - ipVersion: 4, + lower: uint64(val), + isLegacy: true, } } @@ -28,9 +63,9 @@ func IPv4FromOctets(o1, o2, o3, o4 uint8) IP { // IPv6 returns a new `IP` representing an IPv6 address func IPv6(higher, lower uint64) IP { return IP{ - higher: higher, - lower: lower, - ipVersion: 6, + higher: higher, + lower: lower, + isLegacy: false, } } @@ -59,7 +94,7 @@ func IPFromBytes(b []byte) (IP, error) { uint16(b[14])<<8+uint16(b[15])), nil } - return IP{}, fmt.Errorf("byte slice has an invalid legth. Expected either 4 (IPv4) or 16 (IPv6) bytes but got: %d", len(b)) + return IP{}, fmt.Errorf("byte slice has an invalid length. Expected either 4 (IPv4) or 16 (IPv6) bytes but got: %d", len(b)) } // IPFromString returns an IP address for a given string @@ -104,7 +139,7 @@ func (ip IP) Compare(other IP) int { } func (ip IP) String() string { - if ip.ipVersion == 6 { + if !ip.isLegacy { return ip.stringIPv6() } @@ -131,7 +166,7 @@ func (ip IP) stringIPv4() string { // Bytes returns the byte representation of an IP address func (ip IP) Bytes() []byte { - if ip.ipVersion == 6 { + if !ip.isLegacy { return ip.bytesIPv6() } @@ -150,12 +185,12 @@ func (ip IP) bytesIPv4() []byte { // IsIPv4 returns if the `IP` is of address family IPv4 func (ip IP) IsIPv4() bool { - return ip.ipVersion == 4 + return ip.isLegacy } // SizeBytes returns the number of bytes required to represent the `IP` func (ip IP) SizeBytes() uint8 { - if ip.ipVersion == 4 { + if ip.isLegacy { return 4 } @@ -195,7 +230,7 @@ func (ip IP) ToNetIP() net.IP { // BitAtPosition returns the bit at position pos func (ip IP) BitAtPosition(pos uint8) bool { - if ip.ipVersion == 6 { + if !ip.isLegacy { return ip.bitAtPositionIPv6(pos) } diff --git a/net/ip_test.go b/net/ip_test.go index 961730c01844a3c13c569de97eb7caed0be0567e..12449316b9d2665c517bd7dfa5130ebba9e3dd04 100644 --- a/net/ip_test.go +++ b/net/ip_test.go @@ -5,9 +5,88 @@ import ( "net" "testing" + "github.com/bio-routing/bio-rd/net/api" "github.com/stretchr/testify/assert" ) +func TestIPToProto(t *testing.T) { + tests := []struct { + name string + ip IP + expected *api.IP + }{ + { + name: "IPv4", + ip: IP{ + lower: 255, + isLegacy: true, + }, + expected: &api.IP{ + Lower: 255, + IsLegacy: true, + }, + }, + { + name: "IPv6", + ip: IP{ + higher: 1000, + lower: 255, + isLegacy: false, + }, + expected: &api.IP{ + Higher: 1000, + Lower: 255, + IsLegacy: false, + }, + }, + } + + for _, test := range tests { + res := test.ip.ToProto() + assert.Equal(t, test.expected, res, test.name) + } +} + +func TestIPFromProtoIP(t *testing.T) { + tests := []struct { + name string + proto api.IP + expected IP + }{ + { + name: "Test IPv4", + proto: api.IP{ + Lower: 100, + Higher: 0, + IsLegacy: true, + }, + expected: IP{ + lower: 100, + higher: 0, + isLegacy: true, + }, + }, + { + name: "Test IPv6", + proto: api.IP{ + Lower: 100, + Higher: 200, + IsLegacy: false, + }, + expected: IP{ + lower: 100, + higher: 200, + isLegacy: false, + }, + }, + } + + for _, test := range tests { + res := IPFromProtoIP(test.proto) + assert.Equal(t, test.expected, res, test.name) + } +} + func TestCompare(t *testing.T) { tests := []struct { name string @@ -155,27 +234,27 @@ func TestIPv4FromOctets(t *testing.T) { name: "172.217.16.195", octets: []uint8{172, 217, 16, 195}, expected: IP{ - higher: 0, - lower: 2899906755, - ipVersion: 4, + higher: 0, + lower: 2899906755, + isLegacy: true, }, }, { name: "0.0.0.0", octets: []uint8{0, 0, 0, 0}, expected: IP{ - higher: 0, - lower: 0, - ipVersion: 4, + higher: 0, + lower: 0, + isLegacy: true, }, }, { name: "255.255.255.255", octets: []uint8{255, 255, 255, 255}, expected: IP{ - higher: 0, - lower: math.MaxUint32, - ipVersion: 4, + higher: 0, + lower: math.MaxUint32, + isLegacy: true, }, }, } @@ -206,9 +285,8 @@ func TestIPv6FromBlocks(t *testing.T) { 0xcafe, }, expected: IP{ - higher: 2306131596687708724, - lower: 6230974922281175806, - ipVersion: 6, + higher: 2306131596687708724, + lower: 6230974922281175806, }, }, } @@ -239,18 +317,17 @@ func TestIPFromBytes(t *testing.T) { name: "IPV4: 172.217.16.195", bytes: []byte{172, 217, 16, 195}, expected: IP{ - higher: 0, - lower: 2899906755, - ipVersion: 4, + higher: 0, + lower: 2899906755, + isLegacy: true, }, }, { name: "IPV6: IPv6 2001:678:1E0:1234:5678:DEAD:BEEF:CAFE", bytes: []byte{0x20, 0x01, 0x06, 0x78, 0x01, 0xE0, 0x12, 0x34, 0x56, 0x78, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE}, expected: IP{ - higher: 2306131596687708724, - lower: 6230974922281175806, - ipVersion: 6, + higher: 2306131596687708724, + lower: 6230974922281175806, }, }, { diff --git a/net/prefix.go b/net/prefix.go index 5abd32f141f8ae5cfb9c107d2bc5a7ce98bddf4e..f5e1e08c8dbe7aebdf527b57935513a645cef7cb 100644 --- a/net/prefix.go +++ b/net/prefix.go @@ -5,6 +5,8 @@ import ( "math" "strconv" "strings" + + "github.com/bio-routing/bio-rd/net/api" ) // Prefix represents an IPv4 prefix @@ -13,6 +15,22 @@ type Prefix struct { pfxlen uint8 } +// NewPrefixFromProtoPrefix creates a Prefix from a proto Prefix +func NewPrefixFromProtoPrefix(pfx api.Prefix) Prefix { + return Prefix{ + addr: IPFromProtoIP(*pfx.Address), + pfxlen: uint8(pfx.Pfxlen), + } +} + +// ToProto converts prefix to proto prefix +func (pfx Prefix) ToProto() api.Prefix { + return api.Prefix{ + Address: pfx.addr.ToProto(), + Pfxlen: uint32(pfx.pfxlen), + } +} + // NewPfx creates a new Prefix func NewPfx(addr IP, pfxlen uint8) Prefix { return Prefix{ @@ -66,7 +84,7 @@ func (pfx Prefix) Contains(x Prefix) bool { return false } - if pfx.addr.ipVersion == 4 { + if pfx.addr.isLegacy { return pfx.containsIPv4(x) } @@ -99,7 +117,7 @@ func (pfx Prefix) Equal(x Prefix) bool { // GetSupernet gets the next common supernet of pfx and x func (pfx Prefix) GetSupernet(x Prefix) Prefix { - if pfx.addr.ipVersion == 4 { + if pfx.addr.isLegacy { return pfx.supernetIPv4(x) } diff --git a/net/prefix_test.go b/net/prefix_test.go index ccb05a7d3d753d61d6389d9ba6363da7562317f2..62151e1dec162006e2fb3c6b7606116dcfaf5bd7 100644 --- a/net/prefix_test.go +++ b/net/prefix_test.go @@ -3,9 +3,112 @@ package net import ( "testing" + "github.com/bio-routing/bio-rd/net/api" "github.com/stretchr/testify/assert" ) +func TestPrefixToProto(t *testing.T) { + tests := []struct { + name string + pfx Prefix + expected api.Prefix + }{ + { + name: "IPv4", + pfx: Prefix{ + addr: IP{ + lower: 200, + isLegacy: true, + }, + pfxlen: 24, + }, + expected: api.Prefix{ + Address: &api.IP{ + Lower: 200, + IsLegacy: true, + }, + Pfxlen: 24, + }, + }, + { + name: "IPv6", + pfx: Prefix{ + addr: IP{ + higher: 100, + lower: 200, + isLegacy: false, + }, + pfxlen: 64, + }, + expected: api.Prefix{ + Address: &api.IP{ + Higher: 100, + Lower: 200, + IsLegacy: false, + }, + Pfxlen: 64, + }, + }, + } + + for _, test := range tests { + res := test.pfx.ToProto() + assert.Equal(t, test.expected, res, test.name) + } +} + +func TestNewPrefixFromProtoPrefix(t *testing.T) { + tests := []struct { + name string + proto api.Prefix + expected Prefix + }{ + { + name: "IPv4", + proto: api.Prefix{ + Address: &api.IP{ + Higher: 0, + Lower: 2000, + IsLegacy: true, + }, + Pfxlen: 24, + }, + expected: Prefix{ + addr: IP{ + higher: 0, + lower: 2000, + isLegacy: true, + }, + pfxlen: 24, + }, + }, + { + name: "IPv6", + proto: api.Prefix{ + Address: &api.IP{ + Higher: 1000, + Lower: 2000, + IsLegacy: false, + }, + Pfxlen: 64, + }, + expected: Prefix{ + addr: IP{ + higher: 1000, + lower: 2000, + isLegacy: false, + }, + pfxlen: 64, + }, + }, + } + + for _, test := range tests { + res := NewPrefixFromProtoPrefix(test.proto) + assert.Equal(t, test.expected, res, test.name) + } +} + func TestNewPfx(t *testing.T) { p := NewPfx(IPv4(123), 11) if p.addr != IPv4(123) || p.pfxlen != 11 {