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 {