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),
 						},
 					},
 				},