diff --git a/net/BUILD.bazel b/net/BUILD.bazel index 5bec44a8090a9cc26cf4d44f35240b5bf9bb4be9..dad0b392ae4874d7f9dc5e8329371784243ae313 100644 --- a/net/BUILD.bazel +++ b/net/BUILD.bazel @@ -8,6 +8,7 @@ go_library( ], importpath = "github.com/bio-routing/bio-rd/net", visibility = ["//visibility:public"], + deps = ["//net/api:go_default_library"], ) go_test( @@ -17,5 +18,8 @@ go_test( "prefix_test.go", ], embed = [":go_default_library"], - deps = ["//vendor/github.com/stretchr/testify/assert:go_default_library"], + deps = [ + "//net/api:go_default_library", + "//vendor/github.com/stretchr/testify/assert:go_default_library", + ], ) diff --git a/net/api/BUILD.bazel b/net/api/BUILD.bazel new file mode 100644 index 0000000000000000000000000000000000000000..720db1c0ca8d6b7e4e68a99dd6a4a23a80a6556b --- /dev/null +++ b/net/api/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") + +proto_library( + name = "api_proto", + srcs = ["net.proto"], + visibility = ["//visibility:public"], +) + +go_proto_library( + name = "api_go_proto", + importpath = "github.com/bio-routing/bio-rd/net/api", + proto = ":api_proto", + visibility = ["//visibility:public"], +) + +go_library( + name = "go_default_library", + embed = [":api_go_proto"], + importpath = "github.com/bio-routing/bio-rd/net/api", + visibility = ["//visibility:public"], +) diff --git a/net/api/net.pb.go b/net/api/net.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..6c617e3044b22031991d61809481ae3a42efd359 --- /dev/null +++ b/net/api/net.pb.go @@ -0,0 +1,109 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: github.com/bio-routing/bio-rd/net/api/net.proto + +/* +Package api is a generated protocol buffer package. + +It is generated from these files: + github.com/bio-routing/bio-rd/net/api/net.proto + +It has these top-level messages: + Prefix + IP +*/ +package api + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Prefix struct { + Address *IP `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` + Pfxlen uint32 `protobuf:"varint,2,opt,name=pfxlen" json:"pfxlen,omitempty"` +} + +func (m *Prefix) Reset() { *m = Prefix{} } +func (m *Prefix) String() string { return proto.CompactTextString(m) } +func (*Prefix) ProtoMessage() {} +func (*Prefix) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Prefix) GetAddress() *IP { + if m != nil { + return m.Address + } + return nil +} + +func (m *Prefix) GetPfxlen() uint32 { + if m != nil { + return m.Pfxlen + } + return 0 +} + +type IP struct { + Higher uint64 `protobuf:"varint,1,opt,name=higher" json:"higher,omitempty"` + Lower uint64 `protobuf:"varint,2,opt,name=lower" json:"lower,omitempty"` + IsLegacy bool `protobuf:"varint,3,opt,name=is_legacy,json=isLegacy" json:"is_legacy,omitempty"` +} + +func (m *IP) Reset() { *m = IP{} } +func (m *IP) String() string { return proto.CompactTextString(m) } +func (*IP) ProtoMessage() {} +func (*IP) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *IP) GetHigher() uint64 { + if m != nil { + return m.Higher + } + return 0 +} + +func (m *IP) GetLower() uint64 { + if m != nil { + return m.Lower + } + return 0 +} + +func (m *IP) GetIsLegacy() bool { + if m != nil { + return m.IsLegacy + } + return false +} + +func init() { + proto.RegisterType((*Prefix)(nil), "api.Prefix") + proto.RegisterType((*IP)(nil), "api.IP") +} + +func init() { proto.RegisterFile("github.com/bio-routing/bio-rd/net/api/net.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 193 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x34, 0xce, 0xc1, 0x6a, 0x84, 0x30, + 0x10, 0x06, 0x60, 0xa2, 0x56, 0x6d, 0x4a, 0x2f, 0xa1, 0x14, 0xa1, 0x17, 0xeb, 0xc9, 0x4b, 0x13, + 0x68, 0x1f, 0xa1, 0x27, 0xa1, 0x50, 0xc9, 0x0b, 0x94, 0xa8, 0x31, 0x0e, 0x58, 0x13, 0x92, 0xc8, + 0xba, 0x6f, 0xbf, 0x18, 0xdd, 0xd3, 0xcc, 0x37, 0xc3, 0x0f, 0x3f, 0x66, 0x0a, 0xfc, 0xb4, 0x76, + 0xb4, 0xd7, 0xff, 0xac, 0x03, 0xfd, 0x61, 0xf5, 0xea, 0x61, 0x51, 0xc7, 0x3e, 0xb0, 0x45, 0x7a, + 0x26, 0x0c, 0xec, 0x93, 0x1a, 0xab, 0xbd, 0x26, 0xb1, 0x30, 0x50, 0x7d, 0xe3, 0xb4, 0xb5, 0x72, + 0x84, 0x8d, 0xbc, 0xe3, 0x4c, 0x0c, 0x83, 0x95, 0xce, 0x15, 0xa8, 0x44, 0xf5, 0xd3, 0x67, 0x46, + 0x85, 0x01, 0xda, 0xb4, 0xfc, 0x7e, 0x27, 0xaf, 0x38, 0x35, 0xe3, 0x36, 0xcb, 0xa5, 0x88, 0x4a, + 0x54, 0x3f, 0xf3, 0x53, 0xd5, 0x2f, 0x8e, 0x9a, 0x76, 0xff, 0x4e, 0xa0, 0x26, 0x69, 0x43, 0x3e, + 0xe1, 0xa7, 0xc8, 0x0b, 0x7e, 0x98, 0xf5, 0x45, 0xda, 0x10, 0x4a, 0xf8, 0x01, 0xf2, 0x86, 0x1f, + 0xc1, 0xfd, 0xcd, 0x52, 0x89, 0xfe, 0x5a, 0xc4, 0x25, 0xaa, 0x73, 0x9e, 0x83, 0xfb, 0x09, 0xee, + 0xd2, 0xd0, 0xf0, 0xeb, 0x16, 0x00, 0x00, 0xff, 0xff, 0x19, 0xac, 0x48, 0x5d, 0xd4, 0x00, 0x00, + 0x00, +} diff --git a/net/api/net.proto b/net/api/net.proto new file mode 100644 index 0000000000000000000000000000000000000000..f7abc0a18f819bf79128a2414becec5a3dfa070c --- /dev/null +++ b/net/api/net.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package api; + +message Prefix { + IP address = 1; + uint32 pfxlen = 2; +} + +message IP { + uint64 higher = 1; + uint64 lower = 2; + bool is_legacy = 3; +} \ No newline at end of file diff --git a/net/ip.go b/net/ip.go index 5298375429b952ffc2e2488da4720d14b7b899ab..ed3a0d4ff4ecee90d6369264f76ff7d4279a8c75 100644 --- a/net/ip.go +++ b/net/ip.go @@ -3,13 +3,48 @@ 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 } // Lower gets the lower half of the IP address @@ -30,8 +65,8 @@ func (ip IP) IPVersion() uint8 { // 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, } } @@ -43,9 +78,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, } } @@ -119,7 +154,7 @@ func (ip IP) Compare(other IP) int { } func (ip IP) String() string { - if ip.ipVersion == 6 { + if !ip.isLegacy { return ip.stringIPv6() } @@ -146,7 +181,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() } @@ -165,12 +200,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 } @@ -210,11 +245,11 @@ func (ip IP) ToNetIP() net.IP { // BitAtPosition returns the bit at position pos func (ip IP) BitAtPosition(pos uint8) bool { - if ip.ipVersion == 6 { - return ip.bitAtPositionIPv6(pos) + if ip.isLegacy { + return ip.bitAtPositionIPv4(pos) } - return ip.bitAtPositionIPv4(pos) + return ip.bitAtPositionIPv6(pos) } func (ip IP) bitAtPositionIPv4(pos uint8) bool { diff --git a/net/ip_test.go b/net/ip_test.go index a8b006ac41fce091abe1970f5bc94b274e958ce4..53738b09d44663c4596ede2dec7aac0998cfff28 100644 --- a/net/ip_test.go +++ b/net/ip_test.go @@ -5,6 +5,7 @@ import ( "net" "testing" + "github.com/bio-routing/bio-rd/net/api" "github.com/stretchr/testify/assert" ) @@ -50,26 +51,103 @@ func TestIPVersion(t *testing.T) { tests := []struct { name string ip IP - expected uint8 + expected bool }{ { name: "Test", - ip: IP{ipVersion: 4}, - expected: 4, + ip: IP{isLegacy: true}, + expected: true, }, { name: "Test", - ip: IP{ipVersion: 6}, - expected: 6, + ip: IP{}, + expected: false, + }, + } + + for _, test := range tests { + res := test.ip.IsLegacy() + assert.Equal(t, test.expected, res, test.name) + } +} +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.IPVersion() + 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 @@ -217,27 +295,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, }, }, } @@ -268,9 +346,8 @@ func TestIPv6FromBlocks(t *testing.T) { 0xcafe, }, expected: IP{ - higher: 2306131596687708724, - lower: 6230974922281175806, - ipVersion: 6, + higher: 2306131596687708724, + lower: 6230974922281175806, }, }, } @@ -301,18 +378,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 { diff --git a/protocols/bgp/server/update_sender_test.go b/protocols/bgp/server/update_sender_test.go index 516a2d59f181e9831095b8cdcc36c588c64cdc28..d30f9b8f27312c9a23188af5d5fd482999b58757 100644 --- a/protocols/bgp/server/update_sender_test.go +++ b/protocols/bgp/server/update_sender_test.go @@ -38,6 +38,7 @@ func TestSender(t *testing.T) { Type: 2, BGPPath: &route.BGPPath{ LocalPref: 100, + NextHop: bnet.IPv4(0), }, }, pfxs: []bnet.Prefix{ @@ -52,6 +53,7 @@ func TestSender(t *testing.T) { Type: 2, BGPPath: &route.BGPPath{ LocalPref: 200, + NextHop: bnet.IPv4(0), }, }, pfxs: []bnet.Prefix{ @@ -101,6 +103,7 @@ func TestSender(t *testing.T) { Type: 2, BGPPath: &route.BGPPath{ LocalPref: 100, + NextHop: bnet.IPv4(0), }, }, pfxs: []bnet.Prefix{ @@ -115,6 +118,7 @@ func TestSender(t *testing.T) { Type: 2, BGPPath: &route.BGPPath{ LocalPref: 200, + NextHop: bnet.IPv4(0), }, }, pfxs: []bnet.Prefix{ @@ -180,6 +184,7 @@ func TestSender(t *testing.T) { Type: 2, BGPPath: &route.BGPPath{ LocalPref: 100, + NextHop: bnet.IPv4(0), }, }, },