diff --git a/apps/bmp-streamer/bmp-streamer b/apps/bmp-streamer/bmp-streamer
new file mode 100755
index 0000000000000000000000000000000000000000..a8055af5df7a82b55b29c734507bf4898d3ad20c
Binary files /dev/null and b/apps/bmp-streamer/bmp-streamer differ
diff --git a/apps/bmp-streamer/client/client b/apps/bmp-streamer/client/client
new file mode 100755
index 0000000000000000000000000000000000000000..f98ec0b593f9eb6ef933d0b43f7041485cc513f4
Binary files /dev/null and b/apps/bmp-streamer/client/client differ
diff --git a/apps/bmp-streamer/client/main.go b/apps/bmp-streamer/client/main.go
index 5ca04182c610400746c426d8fb360000d54f937f..05fdadc5ed9ad7d41d4a0e43aa8b2f3da3862462 100644
--- a/apps/bmp-streamer/client/main.go
+++ b/apps/bmp-streamer/client/main.go
@@ -5,8 +5,9 @@ import (
 	"fmt"
 	"os"
 
-	pb "github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpsrvapi"
+	pb "github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpstreamer"
 	"github.com/bio-routing/bio-rd/net"
+	netapi "github.com/bio-routing/bio-rd/net/api"
 	log "github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
 )
@@ -29,10 +30,10 @@ func main() {
 
 	c := pb.NewRIBServiceClient(conn)
 	streamClient, err := c.AdjRIBInStream(context.Background(), &pb.AdjRIBInStreamRequest{
-		Router: &pb.IP{
-			Higher:    rtr.Higher(),
-			Lower:     rtr.Lower(),
-			IPVersion: uint32(rtr.Version()),
+		Router: &netapi.IP{
+			Higher:   rtr.Higher(),
+			Lower:    rtr.Lower(),
+			IsLegacy: true,
 		},
 	})
 
diff --git a/apps/bmp-streamer/main.go b/apps/bmp-streamer/main.go
index 825b3bd3c8f32e09c57886a07a22e5df68c2d843..91051ed0ec490eb86b0107486501e8eddfe3ab14 100644
--- a/apps/bmp-streamer/main.go
+++ b/apps/bmp-streamer/main.go
@@ -9,7 +9,7 @@ import (
 	"github.com/bio-routing/bio-rd/lib/grpchelper"
 	"github.com/bio-routing/bio-rd/protocols/bgp/server"
 
-	pb "github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpsrvapi"
+	pb "github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpstreamer"
 
 	"github.com/grpc-ecosystem/go-grpc-prometheus"
 	log "github.com/sirupsen/logrus"
diff --git a/apps/bmp-streamer/pkg/apiserver/server.go b/apps/bmp-streamer/pkg/apiserver/server.go
index 5deb1649549499b1ad8c19a08a534d44ce9bf814..4b173f7d50e2cf2cc94d8cf840993aec2bde9f39 100644
--- a/apps/bmp-streamer/pkg/apiserver/server.go
+++ b/apps/bmp-streamer/pkg/apiserver/server.go
@@ -3,11 +3,10 @@ package apiserver
 import (
 	"fmt"
 
-	pb "github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpsrvapi"
+	pb "github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpstreamer"
 	net "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/protocols/bgp/server"
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
 	"github.com/bio-routing/bio-rd/route"
 	"github.com/bio-routing/bio-rd/routingtable"
 )
@@ -26,72 +25,8 @@ func New(bmpServer *server.BMPServer) *APIServer {
 
 func (u update) toRIBUpdate() *pb.RIBUpdate {
 	toSend := &pb.RIBUpdate{
-		Peer: &pb.IP{},
-		Route: &pb.Route{
-			Pfx: &pb.Prefix{
-				Address: &pb.IP{},
-			},
-			Path: &pb.Path{
-				Type: route.BGPPathType,
-				BGPPath: &pb.BGPPath{
-					NextHop: &pb.IP{},
-					Source:  &pb.IP{},
-				},
-			},
-		},
-	}
-
-	toSend.Advertisement = u.advertisement
-	toSend.Peer.Lower = u.path.BGPPath.Source.Lower()
-	toSend.Peer.Higher = u.path.BGPPath.Source.Higher()
-	toSend.Peer.IPVersion = uint32(u.path.BGPPath.Source.IPVersion())
-	toSend.Route.Pfx.Pfxlen = uint32(u.prefix.Pfxlen())
-	toSend.Route.Pfx.Address.Lower = u.prefix.Addr().Lower()
-	toSend.Route.Pfx.Address.Higher = u.prefix.Addr().Higher()
-	toSend.Route.Pfx.Address.IPVersion = uint32(u.prefix.Addr().IPVersion())
-	toSend.Route.Path.BGPPath.NextHop.Lower = u.path.BGPPath.NextHop.Lower()
-	toSend.Route.Path.BGPPath.NextHop.Higher = u.path.BGPPath.NextHop.Higher()
-	toSend.Route.Path.BGPPath.NextHop.IPVersion = uint32(u.path.BGPPath.NextHop.IPVersion())
-	toSend.Route.Path.BGPPath.Source.Lower = u.path.BGPPath.Source.Lower()
-	toSend.Route.Path.BGPPath.Source.Higher = u.path.BGPPath.Source.Higher()
-	toSend.Route.Path.BGPPath.Source.IPVersion = uint32(u.path.BGPPath.Source.IPVersion())
-	toSend.Route.Path.BGPPath.EBGP = u.path.BGPPath.EBGP
-	toSend.Route.Path.BGPPath.BGPIdentifier = u.path.BGPPath.BGPIdentifier
-	toSend.Route.Path.BGPPath.ClusterList = u.path.BGPPath.ClusterList
-	toSend.Route.Path.BGPPath.Communities = u.path.BGPPath.Communities
-	toSend.Route.Path.BGPPath.LocalPref = u.path.BGPPath.LocalPref
-	toSend.Route.Path.BGPPath.MED = u.path.BGPPath.MED
-	toSend.Route.Path.BGPPath.PathIdentifier = u.path.BGPPath.PathIdentifier
-	toSend.Route.Path.BGPPath.Origin = uint32(u.path.BGPPath.Origin)
-
-	toSend.Route.Path.BGPPath.LargeCommunities = make([]*pb.LargeCommunity, len(u.path.BGPPath.LargeCommunities))
-	for i, com := range u.path.BGPPath.LargeCommunities {
-		toSend.Route.Path.BGPPath.LargeCommunities[i] = &pb.LargeCommunity{
-			GlobalAdministrator: com.GlobalAdministrator,
-			DataPart1:           com.DataPart1,
-			DataPart2:           com.DataPart2,
-		}
-	}
-
-	toSend.Route.Path.BGPPath.ASPath = make([]*pb.ASPathSegment, len(u.path.BGPPath.ASPath))
-	for i, pathSegment := range u.path.BGPPath.ASPath {
-		newSegment := &pb.ASPathSegment{
-			ASSequence: pathSegment.Type == types.ASSequence,
-			ASNs:       make([]uint32, len(pathSegment.ASNs)),
-		}
-		copy(newSegment.ASNs, pathSegment.ASNs)
-		toSend.Route.Path.BGPPath.ASPath[i] = newSegment
-	}
-
-	toSend.Route.Path.BGPPath.UnknownAttributes = make([]*pb.UnknownAttribute, len(u.path.BGPPath.UnknownAttributes))
-	for i, attr := range u.path.BGPPath.UnknownAttributes {
-		toSend.Route.Path.BGPPath.UnknownAttributes[i] = &pb.UnknownAttribute{
-			Optional:   attr.Optional,
-			Transitive: attr.Transitive,
-			Partial:    attr.Partial,
-			TypeCode:   uint32(attr.TypeCode),
-			Value:      attr.Value,
-		}
+		Peer:  u.route.Paths()[0].BGPPath.Source.ToProto(),
+		Route: u.route.ToProto(),
 	}
 
 	return toSend
@@ -103,12 +38,10 @@ func (a *APIServer) AdjRIBInStream(req *pb.AdjRIBInStreamRequest, stream pb.RIBS
 	r6 := newRIBClient()
 
 	addr := net.IP{}
-	if req.Router.IPVersion == 4 {
+	if req.Router.IsLegacy {
 		addr = net.IPv4(uint32(req.Router.Lower))
-	} else if req.Router.IPVersion == 6 {
-		addr = net.IPv6(req.Router.Higher, req.Router.Lower)
 	} else {
-		return fmt.Errorf("Unknown IP version: %d", req.Router.IPVersion)
+		addr = net.IPv6(req.Router.Higher, req.Router.Lower)
 	}
 
 	ret := make(chan error)
@@ -147,8 +80,7 @@ func (a *APIServer) AdjRIBInStream(req *pb.AdjRIBInStreamRequest, stream pb.RIBS
 
 type update struct {
 	advertisement bool
-	prefix        net.Prefix
-	path          *route.Path
+	route         *route.Route
 }
 
 type ribClient struct {
@@ -164,8 +96,7 @@ func newRIBClient() *ribClient {
 func (r *ribClient) AddPath(pfx net.Prefix, path *route.Path) error {
 	r.ch <- update{
 		advertisement: true,
-		prefix:        pfx,
-		path:          path,
+		route:         route.NewRoute(pfx, path),
 	}
 
 	return nil
@@ -174,8 +105,7 @@ func (r *ribClient) AddPath(pfx net.Prefix, path *route.Path) error {
 func (r *ribClient) RemovePath(pfx net.Prefix, path *route.Path) bool {
 	r.ch <- update{
 		advertisement: false,
-		prefix:        pfx,
-		path:          path,
+		route:         route.NewRoute(pfx, path),
 	}
 
 	return false
diff --git a/apps/bmp-streamer/pkg/bmpsrvapi/api.pb.go b/apps/bmp-streamer/pkg/bmpsrvapi/api.pb.go
deleted file mode 100644
index af865faa2b9f06a2dd328e505374ae857d5b0c11..0000000000000000000000000000000000000000
--- a/apps/bmp-streamer/pkg/bmpsrvapi/api.pb.go
+++ /dev/null
@@ -1,586 +0,0 @@
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// source: api.proto
-
-/*
-Package bmpsrvapi is a generated protocol buffer package.
-
-It is generated from these files:
-	api.proto
-
-It has these top-level messages:
-	AdjRIBInStreamRequest
-	RIBUpdate
-	Route
-	Prefix
-	IP
-	Path
-	BGPPath
-	ASPathSegment
-	LargeCommunity
-	UnknownAttribute
-*/
-package bmpsrvapi
-
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
-
-import (
-	context "golang.org/x/net/context"
-	grpc "google.golang.org/grpc"
-)
-
-// 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 AdjRIBInStreamRequest struct {
-	Router *IP `protobuf:"bytes,1,opt,name=router" json:"router,omitempty"`
-}
-
-func (m *AdjRIBInStreamRequest) Reset()                    { *m = AdjRIBInStreamRequest{} }
-func (m *AdjRIBInStreamRequest) String() string            { return proto.CompactTextString(m) }
-func (*AdjRIBInStreamRequest) ProtoMessage()               {}
-func (*AdjRIBInStreamRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
-
-func (m *AdjRIBInStreamRequest) GetRouter() *IP {
-	if m != nil {
-		return m.Router
-	}
-	return nil
-}
-
-type RIBUpdate struct {
-	Peer          *IP    `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"`
-	Advertisement bool   `protobuf:"varint,2,opt,name=advertisement" json:"advertisement,omitempty"`
-	Route         *Route `protobuf:"bytes,3,opt,name=route" json:"route,omitempty"`
-}
-
-func (m *RIBUpdate) Reset()                    { *m = RIBUpdate{} }
-func (m *RIBUpdate) String() string            { return proto.CompactTextString(m) }
-func (*RIBUpdate) ProtoMessage()               {}
-func (*RIBUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
-
-func (m *RIBUpdate) GetPeer() *IP {
-	if m != nil {
-		return m.Peer
-	}
-	return nil
-}
-
-func (m *RIBUpdate) GetAdvertisement() bool {
-	if m != nil {
-		return m.Advertisement
-	}
-	return false
-}
-
-func (m *RIBUpdate) GetRoute() *Route {
-	if m != nil {
-		return m.Route
-	}
-	return nil
-}
-
-type Route struct {
-	Pfx  *Prefix `protobuf:"bytes,1,opt,name=pfx" json:"pfx,omitempty"`
-	Path *Path   `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
-}
-
-func (m *Route) Reset()                    { *m = Route{} }
-func (m *Route) String() string            { return proto.CompactTextString(m) }
-func (*Route) ProtoMessage()               {}
-func (*Route) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
-
-func (m *Route) GetPfx() *Prefix {
-	if m != nil {
-		return m.Pfx
-	}
-	return nil
-}
-
-func (m *Route) GetPath() *Path {
-	if m != nil {
-		return m.Path
-	}
-	return nil
-}
-
-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{3} }
-
-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"`
-	IPVersion uint32 `protobuf:"varint,3,opt,name=IP_version,json=IPVersion" json:"IP_version,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{4} }
-
-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) GetIPVersion() uint32 {
-	if m != nil {
-		return m.IPVersion
-	}
-	return 0
-}
-
-type Path struct {
-	Type    uint32   `protobuf:"varint,1,opt,name=type" json:"type,omitempty"`
-	BGPPath *BGPPath `protobuf:"bytes,2,opt,name=BGP_path,json=BGPPath" json:"BGP_path,omitempty"`
-}
-
-func (m *Path) Reset()                    { *m = Path{} }
-func (m *Path) String() string            { return proto.CompactTextString(m) }
-func (*Path) ProtoMessage()               {}
-func (*Path) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
-
-func (m *Path) GetType() uint32 {
-	if m != nil {
-		return m.Type
-	}
-	return 0
-}
-
-func (m *Path) GetBGPPath() *BGPPath {
-	if m != nil {
-		return m.BGPPath
-	}
-	return nil
-}
-
-type BGPPath struct {
-	PathIdentifier    uint32              `protobuf:"varint,1,opt,name=path_identifier,json=pathIdentifier" json:"path_identifier,omitempty"`
-	NextHop           *IP                 `protobuf:"bytes,2,opt,name=next_hop,json=nextHop" json:"next_hop,omitempty"`
-	LocalPref         uint32              `protobuf:"varint,3,opt,name=local_pref,json=localPref" json:"local_pref,omitempty"`
-	ASPath            []*ASPathSegment    `protobuf:"bytes,4,rep,name=AS_path,json=ASPath" json:"AS_path,omitempty"`
-	Origin            uint32              `protobuf:"varint,5,opt,name=origin" json:"origin,omitempty"`
-	MED               uint32              `protobuf:"varint,6,opt,name=MED" json:"MED,omitempty"`
-	EBGP              bool                `protobuf:"varint,7,opt,name=EBGP" json:"EBGP,omitempty"`
-	BGPIdentifier     uint32              `protobuf:"varint,8,opt,name=BGP_identifier,json=BGPIdentifier" json:"BGP_identifier,omitempty"`
-	Source            *IP                 `protobuf:"bytes,9,opt,name=source" json:"source,omitempty"`
-	Communities       []uint32            `protobuf:"varint,10,rep,packed,name=communities" json:"communities,omitempty"`
-	LargeCommunities  []*LargeCommunity   `protobuf:"bytes,11,rep,name=large_communities,json=largeCommunities" json:"large_communities,omitempty"`
-	OriginatorId      uint32              `protobuf:"varint,12,opt,name=originator_id,json=originatorId" json:"originator_id,omitempty"`
-	ClusterList       []uint32            `protobuf:"varint,13,rep,packed,name=cluster_list,json=clusterList" json:"cluster_list,omitempty"`
-	UnknownAttributes []*UnknownAttribute `protobuf:"bytes,14,rep,name=unknown_attributes,json=unknownAttributes" json:"unknown_attributes,omitempty"`
-}
-
-func (m *BGPPath) Reset()                    { *m = BGPPath{} }
-func (m *BGPPath) String() string            { return proto.CompactTextString(m) }
-func (*BGPPath) ProtoMessage()               {}
-func (*BGPPath) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
-
-func (m *BGPPath) GetPathIdentifier() uint32 {
-	if m != nil {
-		return m.PathIdentifier
-	}
-	return 0
-}
-
-func (m *BGPPath) GetNextHop() *IP {
-	if m != nil {
-		return m.NextHop
-	}
-	return nil
-}
-
-func (m *BGPPath) GetLocalPref() uint32 {
-	if m != nil {
-		return m.LocalPref
-	}
-	return 0
-}
-
-func (m *BGPPath) GetASPath() []*ASPathSegment {
-	if m != nil {
-		return m.ASPath
-	}
-	return nil
-}
-
-func (m *BGPPath) GetOrigin() uint32 {
-	if m != nil {
-		return m.Origin
-	}
-	return 0
-}
-
-func (m *BGPPath) GetMED() uint32 {
-	if m != nil {
-		return m.MED
-	}
-	return 0
-}
-
-func (m *BGPPath) GetEBGP() bool {
-	if m != nil {
-		return m.EBGP
-	}
-	return false
-}
-
-func (m *BGPPath) GetBGPIdentifier() uint32 {
-	if m != nil {
-		return m.BGPIdentifier
-	}
-	return 0
-}
-
-func (m *BGPPath) GetSource() *IP {
-	if m != nil {
-		return m.Source
-	}
-	return nil
-}
-
-func (m *BGPPath) GetCommunities() []uint32 {
-	if m != nil {
-		return m.Communities
-	}
-	return nil
-}
-
-func (m *BGPPath) GetLargeCommunities() []*LargeCommunity {
-	if m != nil {
-		return m.LargeCommunities
-	}
-	return nil
-}
-
-func (m *BGPPath) GetOriginatorId() uint32 {
-	if m != nil {
-		return m.OriginatorId
-	}
-	return 0
-}
-
-func (m *BGPPath) GetClusterList() []uint32 {
-	if m != nil {
-		return m.ClusterList
-	}
-	return nil
-}
-
-func (m *BGPPath) GetUnknownAttributes() []*UnknownAttribute {
-	if m != nil {
-		return m.UnknownAttributes
-	}
-	return nil
-}
-
-type ASPathSegment struct {
-	ASSequence bool     `protobuf:"varint,1,opt,name=AS_sequence,json=ASSequence" json:"AS_sequence,omitempty"`
-	ASNs       []uint32 `protobuf:"varint,2,rep,packed,name=ASNs" json:"ASNs,omitempty"`
-}
-
-func (m *ASPathSegment) Reset()                    { *m = ASPathSegment{} }
-func (m *ASPathSegment) String() string            { return proto.CompactTextString(m) }
-func (*ASPathSegment) ProtoMessage()               {}
-func (*ASPathSegment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
-
-func (m *ASPathSegment) GetASSequence() bool {
-	if m != nil {
-		return m.ASSequence
-	}
-	return false
-}
-
-func (m *ASPathSegment) GetASNs() []uint32 {
-	if m != nil {
-		return m.ASNs
-	}
-	return nil
-}
-
-type LargeCommunity struct {
-	GlobalAdministrator uint32 `protobuf:"varint,1,opt,name=global_administrator,json=globalAdministrator" json:"global_administrator,omitempty"`
-	DataPart1           uint32 `protobuf:"varint,2,opt,name=data_part1,json=dataPart1" json:"data_part1,omitempty"`
-	DataPart2           uint32 `protobuf:"varint,3,opt,name=data_part2,json=dataPart2" json:"data_part2,omitempty"`
-}
-
-func (m *LargeCommunity) Reset()                    { *m = LargeCommunity{} }
-func (m *LargeCommunity) String() string            { return proto.CompactTextString(m) }
-func (*LargeCommunity) ProtoMessage()               {}
-func (*LargeCommunity) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
-
-func (m *LargeCommunity) GetGlobalAdministrator() uint32 {
-	if m != nil {
-		return m.GlobalAdministrator
-	}
-	return 0
-}
-
-func (m *LargeCommunity) GetDataPart1() uint32 {
-	if m != nil {
-		return m.DataPart1
-	}
-	return 0
-}
-
-func (m *LargeCommunity) GetDataPart2() uint32 {
-	if m != nil {
-		return m.DataPart2
-	}
-	return 0
-}
-
-type UnknownAttribute struct {
-	Optional   bool   `protobuf:"varint,1,opt,name=optional" json:"optional,omitempty"`
-	Transitive bool   `protobuf:"varint,2,opt,name=transitive" json:"transitive,omitempty"`
-	Partial    bool   `protobuf:"varint,3,opt,name=partial" json:"partial,omitempty"`
-	TypeCode   uint32 `protobuf:"varint,4,opt,name=type_code,json=typeCode" json:"type_code,omitempty"`
-	Value      []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
-}
-
-func (m *UnknownAttribute) Reset()                    { *m = UnknownAttribute{} }
-func (m *UnknownAttribute) String() string            { return proto.CompactTextString(m) }
-func (*UnknownAttribute) ProtoMessage()               {}
-func (*UnknownAttribute) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
-
-func (m *UnknownAttribute) GetOptional() bool {
-	if m != nil {
-		return m.Optional
-	}
-	return false
-}
-
-func (m *UnknownAttribute) GetTransitive() bool {
-	if m != nil {
-		return m.Transitive
-	}
-	return false
-}
-
-func (m *UnknownAttribute) GetPartial() bool {
-	if m != nil {
-		return m.Partial
-	}
-	return false
-}
-
-func (m *UnknownAttribute) GetTypeCode() uint32 {
-	if m != nil {
-		return m.TypeCode
-	}
-	return 0
-}
-
-func (m *UnknownAttribute) GetValue() []byte {
-	if m != nil {
-		return m.Value
-	}
-	return nil
-}
-
-func init() {
-	proto.RegisterType((*AdjRIBInStreamRequest)(nil), "bmpsrvapi.AdjRIBInStreamRequest")
-	proto.RegisterType((*RIBUpdate)(nil), "bmpsrvapi.RIBUpdate")
-	proto.RegisterType((*Route)(nil), "bmpsrvapi.Route")
-	proto.RegisterType((*Prefix)(nil), "bmpsrvapi.Prefix")
-	proto.RegisterType((*IP)(nil), "bmpsrvapi.IP")
-	proto.RegisterType((*Path)(nil), "bmpsrvapi.Path")
-	proto.RegisterType((*BGPPath)(nil), "bmpsrvapi.BGPPath")
-	proto.RegisterType((*ASPathSegment)(nil), "bmpsrvapi.ASPathSegment")
-	proto.RegisterType((*LargeCommunity)(nil), "bmpsrvapi.LargeCommunity")
-	proto.RegisterType((*UnknownAttribute)(nil), "bmpsrvapi.UnknownAttribute")
-}
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConn
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-const _ = grpc.SupportPackageIsVersion4
-
-// Client API for RIBService service
-
-type RIBServiceClient interface {
-	AdjRIBInStream(ctx context.Context, in *AdjRIBInStreamRequest, opts ...grpc.CallOption) (RIBService_AdjRIBInStreamClient, error)
-}
-
-type rIBServiceClient struct {
-	cc *grpc.ClientConn
-}
-
-func NewRIBServiceClient(cc *grpc.ClientConn) RIBServiceClient {
-	return &rIBServiceClient{cc}
-}
-
-func (c *rIBServiceClient) AdjRIBInStream(ctx context.Context, in *AdjRIBInStreamRequest, opts ...grpc.CallOption) (RIBService_AdjRIBInStreamClient, error) {
-	stream, err := grpc.NewClientStream(ctx, &_RIBService_serviceDesc.Streams[0], c.cc, "/bmpsrvapi.RIBService/adjRIBInStream", opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &rIBServiceAdjRIBInStreamClient{stream}
-	if err := x.ClientStream.SendMsg(in); err != nil {
-		return nil, err
-	}
-	if err := x.ClientStream.CloseSend(); err != nil {
-		return nil, err
-	}
-	return x, nil
-}
-
-type RIBService_AdjRIBInStreamClient interface {
-	Recv() (*RIBUpdate, error)
-	grpc.ClientStream
-}
-
-type rIBServiceAdjRIBInStreamClient struct {
-	grpc.ClientStream
-}
-
-func (x *rIBServiceAdjRIBInStreamClient) Recv() (*RIBUpdate, error) {
-	m := new(RIBUpdate)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-// Server API for RIBService service
-
-type RIBServiceServer interface {
-	AdjRIBInStream(*AdjRIBInStreamRequest, RIBService_AdjRIBInStreamServer) error
-}
-
-func RegisterRIBServiceServer(s *grpc.Server, srv RIBServiceServer) {
-	s.RegisterService(&_RIBService_serviceDesc, srv)
-}
-
-func _RIBService_AdjRIBInStream_Handler(srv interface{}, stream grpc.ServerStream) error {
-	m := new(AdjRIBInStreamRequest)
-	if err := stream.RecvMsg(m); err != nil {
-		return err
-	}
-	return srv.(RIBServiceServer).AdjRIBInStream(m, &rIBServiceAdjRIBInStreamServer{stream})
-}
-
-type RIBService_AdjRIBInStreamServer interface {
-	Send(*RIBUpdate) error
-	grpc.ServerStream
-}
-
-type rIBServiceAdjRIBInStreamServer struct {
-	grpc.ServerStream
-}
-
-func (x *rIBServiceAdjRIBInStreamServer) Send(m *RIBUpdate) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-var _RIBService_serviceDesc = grpc.ServiceDesc{
-	ServiceName: "bmpsrvapi.RIBService",
-	HandlerType: (*RIBServiceServer)(nil),
-	Methods:     []grpc.MethodDesc{},
-	Streams: []grpc.StreamDesc{
-		{
-			StreamName:    "adjRIBInStream",
-			Handler:       _RIBService_AdjRIBInStream_Handler,
-			ServerStreams: true,
-		},
-	},
-	Metadata: "api.proto",
-}
-
-func init() { proto.RegisterFile("api.proto", fileDescriptor0) }
-
-var fileDescriptor0 = []byte{
-	// 800 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x55, 0x5f, 0x6f, 0xe3, 0x44,
-	0x10, 0x57, 0x1a, 0x37, 0x7f, 0x26, 0x75, 0xae, 0x1d, 0x0a, 0x32, 0x77, 0x02, 0x72, 0x2e, 0xe5,
-	0xf2, 0x42, 0x45, 0xc3, 0x3b, 0x52, 0x72, 0x77, 0x14, 0x9f, 0x0e, 0xe4, 0x5b, 0xeb, 0x10, 0x6f,
-	0xd6, 0x36, 0xde, 0x24, 0x0b, 0x8e, 0xd7, 0xec, 0xae, 0x73, 0xa9, 0xc4, 0x0b, 0x9f, 0x83, 0x2f,
-	0xc2, 0xc7, 0x43, 0xbb, 0xb6, 0xcb, 0xa6, 0xea, 0xbd, 0xcd, 0xfc, 0x7e, 0x33, 0xb3, 0xf3, 0x9b,
-	0x8c, 0x27, 0x30, 0xa4, 0x25, 0xbf, 0x2a, 0xa5, 0xd0, 0x02, 0x87, 0xb7, 0xdb, 0x52, 0xc9, 0x1d,
-	0x2d, 0x79, 0xf8, 0x03, 0x7c, 0x3a, 0xcf, 0x7e, 0x27, 0xd1, 0x22, 0x2a, 0x12, 0x2d, 0x19, 0xdd,
-	0x12, 0xf6, 0x67, 0xc5, 0x94, 0xc6, 0x4b, 0xe8, 0x49, 0x51, 0x69, 0x26, 0x83, 0xce, 0xa4, 0x33,
-	0x1d, 0xcd, 0xfc, 0xab, 0xfb, 0xa4, 0xab, 0x28, 0x26, 0x0d, 0x19, 0xfe, 0x05, 0x43, 0x12, 0x2d,
-	0xde, 0x97, 0x19, 0xd5, 0x0c, 0x9f, 0x83, 0x57, 0xb2, 0x8f, 0x65, 0x58, 0x0a, 0xbf, 0x06, 0x9f,
-	0x66, 0x3b, 0x26, 0x35, 0x57, 0x6c, 0xcb, 0x0a, 0x1d, 0x1c, 0x4d, 0x3a, 0xd3, 0x01, 0x39, 0x04,
-	0xf1, 0x1b, 0x38, 0xb6, 0xf5, 0x83, 0xae, 0xad, 0x74, 0xea, 0x54, 0x22, 0x06, 0x27, 0x35, 0x1d,
-	0xbe, 0x83, 0x63, 0xeb, 0xe3, 0x05, 0x74, 0xcb, 0xd5, 0xbe, 0x79, 0xf8, 0xcc, 0x09, 0x8f, 0x25,
-	0x5b, 0xf1, 0x3d, 0x31, 0x2c, 0x5e, 0x80, 0x57, 0x52, 0xbd, 0xb1, 0x4f, 0x8e, 0x66, 0x4f, 0xdc,
-	0x28, 0xaa, 0x37, 0xc4, 0x92, 0x61, 0x04, 0xbd, 0x3a, 0x07, 0x5f, 0x40, 0x9f, 0x66, 0x99, 0x64,
-	0x4a, 0x3d, 0x2e, 0xa8, 0x65, 0xf1, 0x33, 0xe8, 0x95, 0xab, 0x7d, 0xce, 0x0a, 0x5b, 0xd9, 0x27,
-	0x8d, 0x17, 0xbe, 0x83, 0xa3, 0x28, 0x36, 0xec, 0x86, 0xaf, 0x37, 0xcd, 0x58, 0x3c, 0xd2, 0x78,
-	0x78, 0x0e, 0xc7, 0xb9, 0xf8, 0xc0, 0xa4, 0x4d, 0xf2, 0x48, 0xed, 0xe0, 0x17, 0x00, 0x51, 0x9c,
-	0xee, 0x98, 0x54, 0x5c, 0x14, 0x56, 0xbe, 0x4f, 0x86, 0x51, 0xfc, 0x6b, 0x0d, 0x84, 0x11, 0x78,
-	0xa6, 0x57, 0x44, 0xf0, 0xf4, 0x5d, 0xc9, 0x6c, 0x49, 0x9f, 0x58, 0x1b, 0xbf, 0x85, 0xc1, 0xe2,
-	0x26, 0x4e, 0x1d, 0x89, 0xe8, 0x34, 0xbc, 0xb8, 0x89, 0xad, 0xca, 0x7e, 0x63, 0x84, 0xff, 0x7a,
-	0xd0, 0xda, 0xf8, 0x02, 0x9e, 0x98, 0xb4, 0x94, 0x67, 0xac, 0xd0, 0x7c, 0xc5, 0x9b, 0x66, 0x7d,
-	0x32, 0x36, 0x70, 0x74, 0x8f, 0xe2, 0x14, 0x06, 0x05, 0xdb, 0xeb, 0x74, 0x23, 0xca, 0xe6, 0x8d,
-	0x87, 0x43, 0x31, 0xf4, 0x4f, 0xa2, 0x34, 0x42, 0x72, 0xb1, 0xa4, 0x79, 0x5a, 0x4a, 0xb6, 0x6a,
-	0x85, 0x58, 0xc4, 0x8c, 0x17, 0xaf, 0xa1, 0x3f, 0x4f, 0xea, 0x5e, 0xbd, 0x49, 0x77, 0x3a, 0x9a,
-	0x05, 0x4e, 0x9d, 0x79, 0x62, 0xba, 0x4a, 0xd8, 0xda, 0x2c, 0x03, 0xe9, 0xd5, 0xae, 0x19, 0xa4,
-	0x90, 0x7c, 0xcd, 0x8b, 0xe0, 0xb8, 0x1e, 0x73, 0xed, 0xe1, 0x29, 0x74, 0x7f, 0x7e, 0xfd, 0x2a,
-	0xe8, 0x59, 0xd0, 0x98, 0x66, 0x3a, 0xaf, 0x17, 0x37, 0x71, 0xd0, 0xb7, 0xbb, 0x65, 0x6d, 0xbc,
-	0x84, 0xb1, 0x99, 0x8e, 0xa3, 0x70, 0x60, 0x13, 0xfc, 0xc5, 0x4d, 0xec, 0x08, 0xbc, 0x84, 0x9e,
-	0x12, 0x95, 0x5c, 0xb2, 0x60, 0xf8, 0xe8, 0xda, 0xd7, 0x24, 0x4e, 0x60, 0xb4, 0x14, 0xdb, 0x6d,
-	0x55, 0x70, 0xcd, 0x99, 0x0a, 0x60, 0xd2, 0x9d, 0xfa, 0xc4, 0x85, 0xf0, 0x47, 0x38, 0xcb, 0xa9,
-	0x5c, 0xb3, 0xd4, 0x8d, 0x1b, 0x59, 0xa9, 0x9f, 0x3b, 0x35, 0xdf, 0x9a, 0x98, 0x97, 0x4d, 0xc8,
-	0x1d, 0x39, 0xcd, 0x5d, 0xdf, 0xd4, 0xb9, 0x00, 0xbf, 0xd6, 0x49, 0xb5, 0x90, 0x29, 0xcf, 0x82,
-	0x13, 0xdb, 0xf6, 0xc9, 0xff, 0x60, 0x94, 0xe1, 0x73, 0x38, 0x59, 0xe6, 0x95, 0xd2, 0x4c, 0xa6,
-	0x39, 0x57, 0x3a, 0xf0, 0x9b, 0x7e, 0x6a, 0xec, 0x2d, 0x57, 0x1a, 0xdf, 0x00, 0x56, 0xc5, 0x1f,
-	0x85, 0xf8, 0x50, 0xa4, 0x54, 0x6b, 0xc9, 0x6f, 0x2b, 0xcd, 0x54, 0x30, 0xb6, 0x0d, 0x3d, 0x73,
-	0x1a, 0x7a, 0x5f, 0x07, 0xcd, 0xdb, 0x18, 0x72, 0x56, 0x3d, 0x40, 0x54, 0xf8, 0x0a, 0xfc, 0x83,
-	0x9f, 0x08, 0xbf, 0x82, 0xd1, 0x3c, 0x49, 0x95, 0x39, 0x1d, 0xc5, 0xb2, 0xde, 0xca, 0x01, 0x81,
-	0x79, 0x92, 0x34, 0x88, 0xf9, 0x45, 0xe6, 0xc9, 0x2f, 0x2a, 0x38, 0xb2, 0x8d, 0x59, 0x3b, 0xfc,
-	0xbb, 0x03, 0xe3, 0x43, 0xf9, 0x78, 0x0d, 0xe7, 0xeb, 0x5c, 0xdc, 0xd2, 0x3c, 0xa5, 0xd9, 0x96,
-	0x17, 0x5c, 0x69, 0x69, 0x14, 0x36, 0xcb, 0xf8, 0x49, 0xcd, 0xcd, 0x5d, 0xca, 0xec, 0x59, 0x46,
-	0x35, 0x4d, 0x4b, 0x2a, 0xf5, 0x75, 0xf3, 0x01, 0x0e, 0x0d, 0x12, 0x1b, 0xe0, 0x80, 0x9e, 0xb5,
-	0x6b, 0xd8, 0xd2, 0xb3, 0xf0, 0x9f, 0x0e, 0x9c, 0x3e, 0x54, 0x8c, 0x4f, 0x61, 0x20, 0x4a, 0xcd,
-	0x45, 0x41, 0xf3, 0x46, 0xca, 0xbd, 0x8f, 0x5f, 0x02, 0x68, 0x49, 0x0b, 0xc5, 0x35, 0xdf, 0xb1,
-	0xe6, 0x78, 0x39, 0x08, 0x06, 0xd0, 0x37, 0x4f, 0x71, 0x9a, 0xdb, 0xc7, 0x06, 0xa4, 0x75, 0xf1,
-	0x19, 0x0c, 0xcd, 0x67, 0x9a, 0x2e, 0x45, 0xc6, 0x02, 0xcf, 0x36, 0x32, 0x30, 0xc0, 0x4b, 0x91,
-	0x31, 0x73, 0x0c, 0x76, 0x34, 0xaf, 0x98, 0x5d, 0xed, 0x13, 0x52, 0x3b, 0xb3, 0xdf, 0x00, 0x48,
-	0xb4, 0x48, 0x98, 0xdc, 0xf1, 0x25, 0xc3, 0x37, 0x30, 0xa6, 0x07, 0xa7, 0x1a, 0x27, 0xee, 0x37,
-	0xf3, 0xd8, 0x15, 0x7f, 0x7a, 0xee, 0x5e, 0xce, 0xf6, 0x4e, 0x7f, 0xd7, 0xb9, 0xed, 0xd9, 0x3f,
-	0x82, 0xef, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xe8, 0xea, 0xf0, 0x86, 0x15, 0x06, 0x00, 0x00,
-}
diff --git a/apps/bmp-streamer/pkg/bmpsrvapi/BUILD.bazel b/apps/bmp-streamer/pkg/bmpstreamer/BUILD.bazel
similarity index 100%
rename from apps/bmp-streamer/pkg/bmpsrvapi/BUILD.bazel
rename to apps/bmp-streamer/pkg/bmpstreamer/BUILD.bazel
diff --git a/apps/bmp-streamer/pkg/bmpstreamer/bmp.pb.go b/apps/bmp-streamer/pkg/bmpstreamer/bmp.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..4cb68101b2b3d6aa29dc397306d2f1005a396cd5
--- /dev/null
+++ b/apps/bmp-streamer/pkg/bmpstreamer/bmp.pb.go
@@ -0,0 +1,213 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpstreamer/bmp.proto
+
+/*
+Package bmpstreamer is a generated protocol buffer package.
+
+It is generated from these files:
+	github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpstreamer/bmp.proto
+
+It has these top-level messages:
+	AdjRIBInStreamRequest
+	RIBUpdate
+*/
+package bmpstreamer
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import bio_net "github.com/bio-routing/bio-rd/net/api"
+import bio_route "github.com/bio-routing/bio-rd/route/api"
+
+import (
+	context "golang.org/x/net/context"
+	grpc "google.golang.org/grpc"
+)
+
+// 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 AdjRIBInStreamRequest struct {
+	Router *bio_net.IP `protobuf:"bytes,1,opt,name=router" json:"router,omitempty"`
+}
+
+func (m *AdjRIBInStreamRequest) Reset()                    { *m = AdjRIBInStreamRequest{} }
+func (m *AdjRIBInStreamRequest) String() string            { return proto.CompactTextString(m) }
+func (*AdjRIBInStreamRequest) ProtoMessage()               {}
+func (*AdjRIBInStreamRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (m *AdjRIBInStreamRequest) GetRouter() *bio_net.IP {
+	if m != nil {
+		return m.Router
+	}
+	return nil
+}
+
+type RIBUpdate struct {
+	Peer          *bio_net.IP      `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"`
+	Advertisement bool             `protobuf:"varint,2,opt,name=advertisement" json:"advertisement,omitempty"`
+	Route         *bio_route.Route `protobuf:"bytes,3,opt,name=route" json:"route,omitempty"`
+}
+
+func (m *RIBUpdate) Reset()                    { *m = RIBUpdate{} }
+func (m *RIBUpdate) String() string            { return proto.CompactTextString(m) }
+func (*RIBUpdate) ProtoMessage()               {}
+func (*RIBUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *RIBUpdate) GetPeer() *bio_net.IP {
+	if m != nil {
+		return m.Peer
+	}
+	return nil
+}
+
+func (m *RIBUpdate) GetAdvertisement() bool {
+	if m != nil {
+		return m.Advertisement
+	}
+	return false
+}
+
+func (m *RIBUpdate) GetRoute() *bio_route.Route {
+	if m != nil {
+		return m.Route
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*AdjRIBInStreamRequest)(nil), "bmpstreamer.AdjRIBInStreamRequest")
+	proto.RegisterType((*RIBUpdate)(nil), "bmpstreamer.RIBUpdate")
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// Client API for RIBService service
+
+type RIBServiceClient interface {
+	AdjRIBInStream(ctx context.Context, in *AdjRIBInStreamRequest, opts ...grpc.CallOption) (RIBService_AdjRIBInStreamClient, error)
+}
+
+type rIBServiceClient struct {
+	cc *grpc.ClientConn
+}
+
+func NewRIBServiceClient(cc *grpc.ClientConn) RIBServiceClient {
+	return &rIBServiceClient{cc}
+}
+
+func (c *rIBServiceClient) AdjRIBInStream(ctx context.Context, in *AdjRIBInStreamRequest, opts ...grpc.CallOption) (RIBService_AdjRIBInStreamClient, error) {
+	stream, err := grpc.NewClientStream(ctx, &_RIBService_serviceDesc.Streams[0], c.cc, "/bmpstreamer.RIBService/adjRIBInStream", opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &rIBServiceAdjRIBInStreamClient{stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+type RIBService_AdjRIBInStreamClient interface {
+	Recv() (*RIBUpdate, error)
+	grpc.ClientStream
+}
+
+type rIBServiceAdjRIBInStreamClient struct {
+	grpc.ClientStream
+}
+
+func (x *rIBServiceAdjRIBInStreamClient) Recv() (*RIBUpdate, error) {
+	m := new(RIBUpdate)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// Server API for RIBService service
+
+type RIBServiceServer interface {
+	AdjRIBInStream(*AdjRIBInStreamRequest, RIBService_AdjRIBInStreamServer) error
+}
+
+func RegisterRIBServiceServer(s *grpc.Server, srv RIBServiceServer) {
+	s.RegisterService(&_RIBService_serviceDesc, srv)
+}
+
+func _RIBService_AdjRIBInStream_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(AdjRIBInStreamRequest)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(RIBServiceServer).AdjRIBInStream(m, &rIBServiceAdjRIBInStreamServer{stream})
+}
+
+type RIBService_AdjRIBInStreamServer interface {
+	Send(*RIBUpdate) error
+	grpc.ServerStream
+}
+
+type rIBServiceAdjRIBInStreamServer struct {
+	grpc.ServerStream
+}
+
+func (x *rIBServiceAdjRIBInStreamServer) Send(m *RIBUpdate) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+var _RIBService_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "bmpstreamer.RIBService",
+	HandlerType: (*RIBServiceServer)(nil),
+	Methods:     []grpc.MethodDesc{},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "adjRIBInStream",
+			Handler:       _RIBService_AdjRIBInStream_Handler,
+			ServerStreams: true,
+		},
+	},
+	Metadata: "github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpstreamer/bmp.proto",
+}
+
+func init() {
+	proto.RegisterFile("github.com/bio-routing/bio-rd/apps/bmp-streamer/pkg/bmpstreamer/bmp.proto", fileDescriptor0)
+}
+
+var fileDescriptor0 = []byte{
+	// 270 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x50, 0xcd, 0x4b, 0xc3, 0x30,
+	0x14, 0xa7, 0x7e, 0x0c, 0x7d, 0x45, 0x91, 0x80, 0x52, 0x7a, 0x71, 0x54, 0x91, 0x5d, 0x96, 0xc8,
+	0x76, 0xf5, 0x62, 0x6f, 0xb9, 0x88, 0x64, 0x78, 0xf3, 0x92, 0xac, 0x8f, 0x1a, 0xa5, 0x4d, 0x4c,
+	0xd3, 0x1d, 0xfc, 0xeb, 0xa5, 0xc9, 0x18, 0x2b, 0x8c, 0x5d, 0xda, 0xf7, 0xde, 0xef, 0xe3, 0xe5,
+	0xf7, 0x80, 0xd7, 0xda, 0x7f, 0xf5, 0x8a, 0xae, 0x4d, 0xc3, 0x94, 0x36, 0x73, 0x67, 0x7a, 0xaf,
+	0xdb, 0x3a, 0xd6, 0x15, 0x93, 0xd6, 0x76, 0x4c, 0x35, 0x76, 0xde, 0x79, 0x87, 0xb2, 0x41, 0xc7,
+	0xec, 0x4f, 0x3d, 0x0c, 0x76, 0xbd, 0x6a, 0x2c, 0xb5, 0xce, 0x78, 0x43, 0xd2, 0xbd, 0x71, 0xce,
+	0x8e, 0xfb, 0xb6, 0xe8, 0x99, 0xb4, 0x7a, 0xf8, 0x47, 0x75, 0xbe, 0x3c, 0x2e, 0x18, 0x5a, 0x0c,
+	0x92, 0x50, 0x45, 0x51, 0xf1, 0x02, 0xb7, 0xaf, 0xd5, 0xb7, 0xe0, 0x25, 0x6f, 0x57, 0x61, 0xb3,
+	0xc0, 0xdf, 0x1e, 0x3b, 0x4f, 0x1e, 0x60, 0x12, 0x78, 0x2e, 0x4b, 0xa6, 0xc9, 0x2c, 0x5d, 0xa4,
+	0x54, 0x69, 0x43, 0x87, 0x6d, 0xfc, 0x5d, 0x6c, 0xa1, 0xe2, 0x0f, 0x2e, 0x05, 0x2f, 0x3f, 0x6c,
+	0x25, 0x3d, 0x92, 0x7b, 0x38, 0xb3, 0x78, 0x98, 0x1f, 0x00, 0xf2, 0x08, 0x57, 0xb2, 0xda, 0xa0,
+	0xf3, 0xba, 0xc3, 0x06, 0x5b, 0x9f, 0x9d, 0x4c, 0x93, 0xd9, 0x85, 0x18, 0x0f, 0xc9, 0x13, 0x9c,
+	0x07, 0xf7, 0xec, 0x34, 0xf8, 0xdc, 0x04, 0x9f, 0xf8, 0x64, 0x31, 0x7c, 0x45, 0x84, 0x17, 0x9f,
+	0x00, 0x82, 0x97, 0x2b, 0x74, 0x1b, 0xbd, 0x46, 0xf2, 0x06, 0xd7, 0x72, 0x94, 0x83, 0x14, 0x74,
+	0xef, 0x9a, 0xf4, 0x60, 0xc8, 0xfc, 0x6e, 0xc4, 0xd9, 0x45, 0x79, 0x4e, 0xd4, 0x24, 0x9c, 0x67,
+	0xf9, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x4f, 0x84, 0x96, 0x8a, 0xde, 0x01, 0x00, 0x00,
+}
diff --git a/apps/bmp-streamer/pkg/bmpstreamer/bmp.proto b/apps/bmp-streamer/pkg/bmpstreamer/bmp.proto
new file mode 100644
index 0000000000000000000000000000000000000000..f86455ee04e8746c7eaab4cba9d6f30b81762d91
--- /dev/null
+++ b/apps/bmp-streamer/pkg/bmpstreamer/bmp.proto
@@ -0,0 +1,21 @@
+syntax = "proto3";
+
+package bmpstreamer;
+
+import "github.com/bio-routing/bio-rd/net/api/net.proto";
+import "github.com/bio-routing/bio-rd/route/api/route.proto";
+
+service RIBService {
+    rpc adjRIBInStream(AdjRIBInStreamRequest) returns (stream RIBUpdate);
+}
+
+message AdjRIBInStreamRequest {
+    bio.net.IP router = 1;
+}
+
+message RIBUpdate {
+    bio.net.IP peer = 1;
+    bool advertisement = 2;
+    bio.route.Route route = 3;
+}
+
diff --git a/net/api/net.proto b/net/api/net.proto
index f7abc0a18f819bf79128a2414becec5a3dfa070c..9253389210be8b159900aa46b68bdf08752e6ee5 100644
--- a/net/api/net.proto
+++ b/net/api/net.proto
@@ -1,6 +1,6 @@
 syntax = "proto3";
 
-package api;
+package bio.net;
 
 message Prefix {
     IP address = 1;
diff --git a/net/ip.go b/net/ip.go
index ed3a0d4ff4ecee90d6369264f76ff7d4279a8c75..dbf17fe3d44f5f4d0c7868995d0fff36e3e7260f 100644
--- a/net/ip.go
+++ b/net/ip.go
@@ -47,21 +47,6 @@ func (ip IP) IsLegacy() bool {
 	return 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
-}
-
-// IPVersion gets the version of the IP address
-func (ip IP) IPVersion() uint8 {
-	return ip.ipVersion
-}
-
 // IPv4 returns a new `IP` representing an IPv4 address
 func IPv4(val uint32) IP {
 	return IP{
diff --git a/net/prefix.go b/net/prefix.go
index f5e1e08c8dbe7aebdf527b57935513a645cef7cb..22158dc027cb51bd270acb6c24207dfc81f36121 100644
--- a/net/prefix.go
+++ b/net/prefix.go
@@ -24,8 +24,8 @@ func NewPrefixFromProtoPrefix(pfx api.Prefix) Prefix {
 }
 
 // ToProto converts prefix to proto prefix
-func (pfx Prefix) ToProto() api.Prefix {
-	return api.Prefix{
+func (pfx Prefix) ToProto() *api.Prefix {
+	return &api.Prefix{
 		Address: pfx.addr.ToProto(),
 		Pfxlen:  uint32(pfx.pfxlen),
 	}
diff --git a/route/api/route.pb.go b/route/api/route.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..0aff4db4fedae53378b18c49295ad764e4867c18
--- /dev/null
+++ b/route/api/route.pb.go
@@ -0,0 +1,392 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: github.com/bio-routing/bio-rd/route/api/route.proto
+
+/*
+Package bio_route is a generated protocol buffer package.
+
+It is generated from these files:
+	github.com/bio-routing/bio-rd/route/api/route.proto
+
+It has these top-level messages:
+	Route
+	Path
+	StaticPath
+	BGPPath
+	ASPathSegment
+	LargeCommunity
+	UnknownAttribute
+*/
+package bio_route
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import bio_net "github.com/bio-routing/bio-rd/net/api"
+
+// 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 Route struct {
+	Pfx   *bio_net.Prefix `protobuf:"bytes,1,opt,name=pfx" json:"pfx,omitempty"`
+	Paths []*Path         `protobuf:"bytes,2,rep,name=paths" json:"paths,omitempty"`
+}
+
+func (m *Route) Reset()                    { *m = Route{} }
+func (m *Route) String() string            { return proto.CompactTextString(m) }
+func (*Route) ProtoMessage()               {}
+func (*Route) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (m *Route) GetPfx() *bio_net.Prefix {
+	if m != nil {
+		return m.Pfx
+	}
+	return nil
+}
+
+func (m *Route) GetPaths() []*Path {
+	if m != nil {
+		return m.Paths
+	}
+	return nil
+}
+
+type Path struct {
+	Type       uint32      `protobuf:"varint,1,opt,name=type" json:"type,omitempty"`
+	BGPPath    *BGPPath    `protobuf:"bytes,2,opt,name=BGP_path,json=BGPPath" json:"BGP_path,omitempty"`
+	StaticPath *StaticPath `protobuf:"bytes,3,opt,name=static_path,json=staticPath" json:"static_path,omitempty"`
+}
+
+func (m *Path) Reset()                    { *m = Path{} }
+func (m *Path) String() string            { return proto.CompactTextString(m) }
+func (*Path) ProtoMessage()               {}
+func (*Path) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *Path) GetType() uint32 {
+	if m != nil {
+		return m.Type
+	}
+	return 0
+}
+
+func (m *Path) GetBGPPath() *BGPPath {
+	if m != nil {
+		return m.BGPPath
+	}
+	return nil
+}
+
+func (m *Path) GetStaticPath() *StaticPath {
+	if m != nil {
+		return m.StaticPath
+	}
+	return nil
+}
+
+type StaticPath struct {
+	NextHop *bio_net.IP `protobuf:"bytes,1,opt,name=next_hop,json=nextHop" json:"next_hop,omitempty"`
+}
+
+func (m *StaticPath) Reset()                    { *m = StaticPath{} }
+func (m *StaticPath) String() string            { return proto.CompactTextString(m) }
+func (*StaticPath) ProtoMessage()               {}
+func (*StaticPath) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+func (m *StaticPath) GetNextHop() *bio_net.IP {
+	if m != nil {
+		return m.NextHop
+	}
+	return nil
+}
+
+type BGPPath struct {
+	PathIdentifier    uint32              `protobuf:"varint,1,opt,name=path_identifier,json=pathIdentifier" json:"path_identifier,omitempty"`
+	NextHop           *bio_net.IP         `protobuf:"bytes,2,opt,name=next_hop,json=nextHop" json:"next_hop,omitempty"`
+	LocalPref         uint32              `protobuf:"varint,3,opt,name=local_pref,json=localPref" json:"local_pref,omitempty"`
+	ASPath            []*ASPathSegment    `protobuf:"bytes,4,rep,name=AS_path,json=ASPath" json:"AS_path,omitempty"`
+	Origin            uint32              `protobuf:"varint,5,opt,name=origin" json:"origin,omitempty"`
+	MED               uint32              `protobuf:"varint,6,opt,name=MED" json:"MED,omitempty"`
+	EBGP              bool                `protobuf:"varint,7,opt,name=EBGP" json:"EBGP,omitempty"`
+	BGPIdentifier     uint32              `protobuf:"varint,8,opt,name=BGP_identifier,json=BGPIdentifier" json:"BGP_identifier,omitempty"`
+	Source            *bio_net.IP         `protobuf:"bytes,9,opt,name=source" json:"source,omitempty"`
+	Communities       []uint32            `protobuf:"varint,10,rep,packed,name=communities" json:"communities,omitempty"`
+	LargeCommunities  []*LargeCommunity   `protobuf:"bytes,11,rep,name=large_communities,json=largeCommunities" json:"large_communities,omitempty"`
+	OriginatorId      uint32              `protobuf:"varint,12,opt,name=originator_id,json=originatorId" json:"originator_id,omitempty"`
+	ClusterList       []uint32            `protobuf:"varint,13,rep,packed,name=cluster_list,json=clusterList" json:"cluster_list,omitempty"`
+	UnknownAttributes []*UnknownAttribute `protobuf:"bytes,14,rep,name=unknown_attributes,json=unknownAttributes" json:"unknown_attributes,omitempty"`
+}
+
+func (m *BGPPath) Reset()                    { *m = BGPPath{} }
+func (m *BGPPath) String() string            { return proto.CompactTextString(m) }
+func (*BGPPath) ProtoMessage()               {}
+func (*BGPPath) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+
+func (m *BGPPath) GetPathIdentifier() uint32 {
+	if m != nil {
+		return m.PathIdentifier
+	}
+	return 0
+}
+
+func (m *BGPPath) GetNextHop() *bio_net.IP {
+	if m != nil {
+		return m.NextHop
+	}
+	return nil
+}
+
+func (m *BGPPath) GetLocalPref() uint32 {
+	if m != nil {
+		return m.LocalPref
+	}
+	return 0
+}
+
+func (m *BGPPath) GetASPath() []*ASPathSegment {
+	if m != nil {
+		return m.ASPath
+	}
+	return nil
+}
+
+func (m *BGPPath) GetOrigin() uint32 {
+	if m != nil {
+		return m.Origin
+	}
+	return 0
+}
+
+func (m *BGPPath) GetMED() uint32 {
+	if m != nil {
+		return m.MED
+	}
+	return 0
+}
+
+func (m *BGPPath) GetEBGP() bool {
+	if m != nil {
+		return m.EBGP
+	}
+	return false
+}
+
+func (m *BGPPath) GetBGPIdentifier() uint32 {
+	if m != nil {
+		return m.BGPIdentifier
+	}
+	return 0
+}
+
+func (m *BGPPath) GetSource() *bio_net.IP {
+	if m != nil {
+		return m.Source
+	}
+	return nil
+}
+
+func (m *BGPPath) GetCommunities() []uint32 {
+	if m != nil {
+		return m.Communities
+	}
+	return nil
+}
+
+func (m *BGPPath) GetLargeCommunities() []*LargeCommunity {
+	if m != nil {
+		return m.LargeCommunities
+	}
+	return nil
+}
+
+func (m *BGPPath) GetOriginatorId() uint32 {
+	if m != nil {
+		return m.OriginatorId
+	}
+	return 0
+}
+
+func (m *BGPPath) GetClusterList() []uint32 {
+	if m != nil {
+		return m.ClusterList
+	}
+	return nil
+}
+
+func (m *BGPPath) GetUnknownAttributes() []*UnknownAttribute {
+	if m != nil {
+		return m.UnknownAttributes
+	}
+	return nil
+}
+
+type ASPathSegment struct {
+	ASSequence bool     `protobuf:"varint,1,opt,name=AS_sequence,json=ASSequence" json:"AS_sequence,omitempty"`
+	ASNs       []uint32 `protobuf:"varint,2,rep,packed,name=ASNs" json:"ASNs,omitempty"`
+}
+
+func (m *ASPathSegment) Reset()                    { *m = ASPathSegment{} }
+func (m *ASPathSegment) String() string            { return proto.CompactTextString(m) }
+func (*ASPathSegment) ProtoMessage()               {}
+func (*ASPathSegment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+
+func (m *ASPathSegment) GetASSequence() bool {
+	if m != nil {
+		return m.ASSequence
+	}
+	return false
+}
+
+func (m *ASPathSegment) GetASNs() []uint32 {
+	if m != nil {
+		return m.ASNs
+	}
+	return nil
+}
+
+type LargeCommunity struct {
+	GlobalAdministrator uint32 `protobuf:"varint,1,opt,name=global_administrator,json=globalAdministrator" json:"global_administrator,omitempty"`
+	DataPart1           uint32 `protobuf:"varint,2,opt,name=data_part1,json=dataPart1" json:"data_part1,omitempty"`
+	DataPart2           uint32 `protobuf:"varint,3,opt,name=data_part2,json=dataPart2" json:"data_part2,omitempty"`
+}
+
+func (m *LargeCommunity) Reset()                    { *m = LargeCommunity{} }
+func (m *LargeCommunity) String() string            { return proto.CompactTextString(m) }
+func (*LargeCommunity) ProtoMessage()               {}
+func (*LargeCommunity) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
+
+func (m *LargeCommunity) GetGlobalAdministrator() uint32 {
+	if m != nil {
+		return m.GlobalAdministrator
+	}
+	return 0
+}
+
+func (m *LargeCommunity) GetDataPart1() uint32 {
+	if m != nil {
+		return m.DataPart1
+	}
+	return 0
+}
+
+func (m *LargeCommunity) GetDataPart2() uint32 {
+	if m != nil {
+		return m.DataPart2
+	}
+	return 0
+}
+
+type UnknownAttribute struct {
+	Optional   bool   `protobuf:"varint,1,opt,name=optional" json:"optional,omitempty"`
+	Transitive bool   `protobuf:"varint,2,opt,name=transitive" json:"transitive,omitempty"`
+	Partial    bool   `protobuf:"varint,3,opt,name=partial" json:"partial,omitempty"`
+	TypeCode   uint32 `protobuf:"varint,4,opt,name=type_code,json=typeCode" json:"type_code,omitempty"`
+	Value      []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (m *UnknownAttribute) Reset()                    { *m = UnknownAttribute{} }
+func (m *UnknownAttribute) String() string            { return proto.CompactTextString(m) }
+func (*UnknownAttribute) ProtoMessage()               {}
+func (*UnknownAttribute) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
+
+func (m *UnknownAttribute) GetOptional() bool {
+	if m != nil {
+		return m.Optional
+	}
+	return false
+}
+
+func (m *UnknownAttribute) GetTransitive() bool {
+	if m != nil {
+		return m.Transitive
+	}
+	return false
+}
+
+func (m *UnknownAttribute) GetPartial() bool {
+	if m != nil {
+		return m.Partial
+	}
+	return false
+}
+
+func (m *UnknownAttribute) GetTypeCode() uint32 {
+	if m != nil {
+		return m.TypeCode
+	}
+	return 0
+}
+
+func (m *UnknownAttribute) GetValue() []byte {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Route)(nil), "bio.route.Route")
+	proto.RegisterType((*Path)(nil), "bio.route.Path")
+	proto.RegisterType((*StaticPath)(nil), "bio.route.StaticPath")
+	proto.RegisterType((*BGPPath)(nil), "bio.route.BGPPath")
+	proto.RegisterType((*ASPathSegment)(nil), "bio.route.ASPathSegment")
+	proto.RegisterType((*LargeCommunity)(nil), "bio.route.LargeCommunity")
+	proto.RegisterType((*UnknownAttribute)(nil), "bio.route.UnknownAttribute")
+}
+
+func init() {
+	proto.RegisterFile("github.com/bio-routing/bio-rd/route/api/route.proto", fileDescriptor0)
+}
+
+var fileDescriptor0 = []byte{
+	// 682 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x54, 0x5d, 0x4f, 0xdb, 0x3c,
+	0x14, 0x56, 0xe9, 0x57, 0x7a, 0xda, 0x14, 0xf0, 0xcb, 0xfb, 0x2a, 0x2f, 0x68, 0x5b, 0x29, 0x62,
+	0xe3, 0x86, 0x56, 0xc0, 0xb4, 0xfb, 0x16, 0x58, 0xc7, 0xc4, 0xa6, 0xcc, 0xd1, 0xae, 0x23, 0x37,
+	0x71, 0x5b, 0x6b, 0xa9, 0x9d, 0x39, 0x0e, 0x83, 0xcb, 0xfd, 0x8e, 0xfd, 0x8d, 0xfd, 0xc0, 0xc9,
+	0x76, 0x5a, 0x52, 0xa6, 0xed, 0xee, 0x9c, 0xe7, 0x39, 0x5f, 0x4f, 0xce, 0x89, 0xe1, 0x62, 0xce,
+	0xd4, 0x22, 0x9f, 0x0e, 0x22, 0xb1, 0x1c, 0x4e, 0x99, 0x38, 0x95, 0x22, 0x57, 0x8c, 0xcf, 0xad,
+	0x1d, 0x0f, 0xb5, 0x4b, 0x87, 0x24, 0x65, 0xd6, 0x1a, 0xa4, 0x52, 0x28, 0x81, 0x5a, 0x53, 0x26,
+	0x06, 0x06, 0xd8, 0x1f, 0xfe, 0x3d, 0x9f, 0x53, 0x65, 0xb2, 0x39, 0x55, 0x36, 0xb7, 0xff, 0x09,
+	0xea, 0x58, 0x67, 0xa2, 0x43, 0xa8, 0xa6, 0xb3, 0x7b, 0xaf, 0xd2, 0xab, 0x9c, 0xb4, 0xcf, 0xb7,
+	0x07, 0xba, 0xa4, 0x8e, 0xf2, 0x25, 0x9d, 0xb1, 0x7b, 0xac, 0x39, 0x74, 0x0c, 0xf5, 0x94, 0xa8,
+	0x45, 0xe6, 0x6d, 0xf5, 0xaa, 0xeb, 0x20, 0x3b, 0x88, 0x4f, 0xd4, 0x02, 0x5b, 0xb6, 0xff, 0xbd,
+	0x02, 0x35, 0xed, 0x23, 0x04, 0x35, 0xf5, 0x90, 0x52, 0x53, 0xd3, 0xc5, 0xc6, 0x46, 0xa7, 0xe0,
+	0x8c, 0x27, 0x7e, 0xa8, 0x23, 0xbd, 0x2d, 0xd3, 0x0b, 0x95, 0xca, 0x8c, 0x27, 0xbe, 0xa9, 0xd4,
+	0x2c, 0x0c, 0xf4, 0x06, 0xda, 0x99, 0x22, 0x8a, 0x45, 0x36, 0xa3, 0x6a, 0x32, 0xfe, 0x2d, 0x65,
+	0x04, 0x86, 0x35, 0x49, 0x90, 0xad, 0xed, 0xfe, 0x6b, 0x80, 0x47, 0x06, 0xbd, 0x04, 0x87, 0xd3,
+	0x7b, 0x15, 0x2e, 0x44, 0x5a, 0x08, 0x6c, 0xaf, 0x05, 0xde, 0xf8, 0xb8, 0xa9, 0xc9, 0x77, 0x22,
+	0xed, 0xff, 0xac, 0xc1, 0xba, 0xf3, 0x2b, 0xd8, 0xd6, 0x2d, 0x43, 0x16, 0x53, 0xae, 0xd8, 0x8c,
+	0x51, 0x59, 0xe8, 0xe8, 0x6a, 0xf8, 0x66, 0x8d, 0x6e, 0x14, 0xdf, 0xfa, 0x73, 0x71, 0xf4, 0x0c,
+	0x20, 0x11, 0x11, 0x49, 0xc2, 0x54, 0xd2, 0x99, 0x51, 0xe2, 0xe2, 0x96, 0x41, 0xf4, 0x37, 0x46,
+	0x67, 0xd0, 0x1c, 0x05, 0x56, 0x65, 0xcd, 0x7c, 0x5e, 0xaf, 0xa4, 0x72, 0x14, 0xe8, 0x99, 0x02,
+	0x3a, 0x5f, 0x52, 0xae, 0x70, 0xc3, 0xba, 0xe8, 0x3f, 0x68, 0x08, 0xc9, 0xe6, 0x8c, 0x7b, 0x75,
+	0x53, 0xad, 0xf0, 0xd0, 0x0e, 0x54, 0x3f, 0x5c, 0x5f, 0x79, 0x0d, 0x03, 0x6a, 0x53, 0x6f, 0xe2,
+	0x7a, 0x3c, 0xf1, 0xbd, 0x66, 0xaf, 0x72, 0xe2, 0x60, 0x63, 0xa3, 0x63, 0xe8, 0xea, 0x4d, 0x94,
+	0xf4, 0x39, 0x26, 0xc1, 0x1d, 0x4f, 0xfc, 0x92, 0xbc, 0x23, 0x68, 0x64, 0x22, 0x97, 0x11, 0xf5,
+	0x5a, 0xbf, 0x8b, 0x2b, 0x28, 0xd4, 0x83, 0x76, 0x24, 0x96, 0xcb, 0x9c, 0x33, 0xc5, 0x68, 0xe6,
+	0x41, 0xaf, 0x7a, 0xe2, 0xe2, 0x32, 0x84, 0xde, 0xc2, 0x6e, 0x42, 0xe4, 0x9c, 0x86, 0xe5, 0xb8,
+	0xb6, 0x11, 0xfa, 0x7f, 0x49, 0xe8, 0xad, 0x8e, 0xb9, 0x2c, 0x42, 0x1e, 0xf0, 0x4e, 0x52, 0xf6,
+	0x75, 0x9d, 0x23, 0x70, 0xad, 0x4a, 0xa2, 0x84, 0x0c, 0x59, 0xec, 0x75, 0xcc, 0xd0, 0x9d, 0x47,
+	0xf0, 0x26, 0x46, 0x87, 0xd0, 0x89, 0x92, 0x3c, 0x53, 0x54, 0x86, 0x09, 0xcb, 0x94, 0xe7, 0x16,
+	0xf3, 0x58, 0xec, 0x96, 0x65, 0x0a, 0xbd, 0x07, 0x94, 0xf3, 0x2f, 0x5c, 0x7c, 0xe3, 0x21, 0x51,
+	0x4a, 0xb2, 0x69, 0xae, 0x68, 0xe6, 0x75, 0xcd, 0x40, 0x07, 0xa5, 0x81, 0x3e, 0xdb, 0xa0, 0xd1,
+	0x2a, 0x06, 0xef, 0xe6, 0x4f, 0x90, 0xac, 0x7f, 0x05, 0xee, 0xc6, 0x82, 0xd0, 0x0b, 0x68, 0x8f,
+	0x82, 0x30, 0xa3, 0x5f, 0x73, 0xca, 0x23, 0x7b, 0xff, 0x0e, 0x86, 0x51, 0x10, 0x14, 0x88, 0xde,
+	0xc7, 0x28, 0xf8, 0x68, 0x7f, 0x24, 0x17, 0x1b, 0x5b, 0xff, 0x36, 0xdd, 0x4d, 0xf9, 0xe8, 0x0c,
+	0xf6, 0xe6, 0x89, 0x98, 0x92, 0x24, 0x24, 0xf1, 0x92, 0x71, 0x96, 0x29, 0xa9, 0x15, 0x16, 0x87,
+	0xf8, 0x8f, 0xe5, 0x46, 0x65, 0x4a, 0x5f, 0x59, 0x4c, 0x14, 0x09, 0x53, 0x22, 0xd5, 0x99, 0xb9,
+	0x47, 0x17, 0xb7, 0x34, 0xe2, 0x6b, 0x60, 0x83, 0x3e, 0x5f, 0x1d, 0xe1, 0x8a, 0x3e, 0xef, 0xff,
+	0xa8, 0xc0, 0xce, 0x53, 0xc5, 0x68, 0x1f, 0x1c, 0x91, 0x2a, 0x26, 0x38, 0x49, 0x0a, 0x29, 0x6b,
+	0x1f, 0x3d, 0x07, 0x50, 0x92, 0xf0, 0x8c, 0x29, 0x76, 0x47, 0x4d, 0x3b, 0x07, 0x97, 0x10, 0xe4,
+	0x41, 0x53, 0xb7, 0x62, 0x24, 0x31, 0xcd, 0x1c, 0xbc, 0x72, 0xd1, 0x01, 0xb4, 0xf4, 0x83, 0x10,
+	0x46, 0x22, 0xa6, 0x5e, 0xcd, 0x0c, 0xe2, 0x68, 0xe0, 0x52, 0xc4, 0x14, 0xed, 0x41, 0xfd, 0x8e,
+	0x24, 0x39, 0x35, 0x87, 0xdd, 0xc1, 0xd6, 0x99, 0x36, 0xcc, 0x93, 0x75, 0xf1, 0x2b, 0x00, 0x00,
+	0xff, 0xff, 0xe9, 0x5f, 0x53, 0x36, 0x25, 0x05, 0x00, 0x00,
+}
diff --git a/apps/bmp-streamer/pkg/bmpsrvapi/api.proto b/route/api/route.proto
similarity index 64%
rename from apps/bmp-streamer/pkg/bmpsrvapi/api.proto
rename to route/api/route.proto
index 1401d3ff35e5f4c456150b78ff1c74e525299728..eff70408ac421e0d47be93e4bbb1c4f92b2b8fac 100644
--- a/apps/bmp-streamer/pkg/bmpsrvapi/api.proto
+++ b/route/api/route.proto
@@ -1,52 +1,34 @@
 syntax = "proto3";
 
-package bmpsrvapi;
+package bio.route;
 
-service RIBService {
-    rpc adjRIBInStream(AdjRIBInStreamRequest) returns (stream RIBUpdate);
-}
-
-message AdjRIBInStreamRequest {
-    IP router = 1;
-}
-
-message RIBUpdate {
-    IP peer = 1;
-    bool advertisement = 2;
-    Route route = 3;
-}
+import "github.com/bio-routing/bio-rd/net/api/net.proto";
 
 message Route {
-    Prefix pfx = 1;
-    Path path = 2;
-}
-
-message Prefix {
-    IP address = 1;
-    uint32 pfxlen = 2;
-}
-
-message IP {
-    uint64 higher = 1;
-    uint64 lower = 2;
-    uint32 IP_version = 3;
+    bio.net.Prefix pfx = 1;
+    repeated Path paths = 2;
 }
 
 message Path {
     uint32 type = 1;
     BGPPath BGP_path = 2;
+    StaticPath static_path = 3;
+}
+
+message StaticPath {
+    bio.net.IP next_hop = 1;
 }
 
 message BGPPath {
     uint32 path_identifier = 1;
-    IP next_hop = 2;
+    bio.net.IP next_hop = 2;
     uint32 local_pref = 3;
     repeated ASPathSegment AS_path = 4;
     uint32 origin = 5;
     uint32 MED = 6;
     bool EBGP = 7;
     uint32 BGP_identifier = 8;
-    IP source = 9;
+    bio.net.IP source = 9;
     repeated uint32 communities = 10;
     repeated LargeCommunity large_communities = 11;
     uint32 originator_id = 12;
diff --git a/route/bgp_path.go b/route/bgp_path.go
index 78c6d2a327bebd62eb5ed1cc4145b56ad0d50398..c72cc6158e8de9bac6fcaa8d08f3088e5b3b085a 100644
--- a/route/bgp_path.go
+++ b/route/bgp_path.go
@@ -9,6 +9,9 @@ import (
 
 	bnet "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/protocols/bgp/types"
+
+	netapi "github.com/bio-routing/bio-rd/net/api"
+	api "github.com/bio-routing/bio-rd/route/api"
 )
 
 // BGPPath represents a set of BGP path attributes
@@ -32,6 +35,64 @@ type BGPPath struct {
 	ClusterList       []uint32
 }
 
+// ToProto converts BGPPath to api.BGPPath
+func (b *BGPPath) ToProto() *api.BGPPath {
+	if b == nil {
+		return nil
+	}
+
+	ret := &api.BGPPath{
+		NextHop: &netapi.IP{},
+		Source:  &netapi.IP{},
+	}
+	ret.NextHop.Lower = b.NextHop.Lower()
+	ret.NextHop.Higher = b.NextHop.Higher()
+	ret.NextHop.IsLegacy = b.NextHop.IsLegacy()
+	ret.Source.Lower = b.Source.Lower()
+	ret.Source.Higher = b.Source.Higher()
+	ret.Source.IsLegacy = b.Source.IsLegacy()
+	ret.EBGP = b.EBGP
+	ret.BGPIdentifier = b.BGPIdentifier
+	ret.ClusterList = b.ClusterList
+	ret.Communities = b.Communities
+	ret.LocalPref = b.LocalPref
+	ret.MED = b.MED
+	ret.PathIdentifier = b.PathIdentifier
+	ret.Origin = uint32(b.Origin)
+
+	ret.LargeCommunities = make([]*api.LargeCommunity, len(b.LargeCommunities))
+	for i, com := range b.LargeCommunities {
+		ret.LargeCommunities[i] = &api.LargeCommunity{
+			GlobalAdministrator: com.GlobalAdministrator,
+			DataPart1:           com.DataPart1,
+			DataPart2:           com.DataPart2,
+		}
+	}
+
+	ret.ASPath = make([]*api.ASPathSegment, len(b.ASPath))
+	for i, pathSegment := range b.ASPath {
+		newSegment := &api.ASPathSegment{
+			ASSequence: pathSegment.Type == types.ASSequence,
+			ASNs:       make([]uint32, len(pathSegment.ASNs)),
+		}
+		copy(newSegment.ASNs, pathSegment.ASNs)
+		ret.ASPath[i] = newSegment
+	}
+
+	ret.UnknownAttributes = make([]*api.UnknownAttribute, len(b.UnknownAttributes))
+	for i, attr := range b.UnknownAttributes {
+		ret.UnknownAttributes[i] = &api.UnknownAttribute{
+			Optional:   attr.Optional,
+			Transitive: attr.Transitive,
+			Partial:    attr.Partial,
+			TypeCode:   uint32(attr.TypeCode),
+			Value:      attr.Value,
+		}
+	}
+
+	return ret
+}
+
 // Length get's the length of serialized path
 func (b *BGPPath) Length() uint16 {
 	asPathLen := uint16(3)
diff --git a/route/bgp_path_test.go b/route/bgp_path_test.go
index 9ea931153ea5778061e852e34af7666204e97b0b..e4eb882388924819f906411b6835522dfa31f700 100644
--- a/route/bgp_path_test.go
+++ b/route/bgp_path_test.go
@@ -3,8 +3,12 @@ package route
 import (
 	"testing"
 
+	"github.com/bio-routing/bio-rd/net"
+	netapi "github.com/bio-routing/bio-rd/net/api"
 	"github.com/bio-routing/bio-rd/protocols/bgp/types"
 	"github.com/stretchr/testify/assert"
+
+	pb "github.com/bio-routing/bio-rd/route/api"
 )
 
 func TestCommunitiesString(t *testing.T) {
@@ -133,3 +137,95 @@ func TestLength(t *testing.T) {
 		}
 	}
 }
+
+func TestBGPPathToProto(t *testing.T) {
+	tests := []struct {
+		name     string
+		p        *BGPPath
+		expected *pb.BGPPath
+	}{
+		{
+			name: "Basics advert.",
+			p: &BGPPath{
+				PathIdentifier: 10,
+				NextHop:        net.IPv4(210),
+				LocalPref:      20,
+				ASPath: types.ASPath{
+					{
+						Type: types.ASSequence,
+						ASNs: []uint32{100, 200, 300},
+					},
+				},
+				Origin:        1,
+				MED:           1000,
+				EBGP:          true,
+				BGPIdentifier: 1337,
+				Source:        net.IPv4(220),
+				Communities:   []uint32{10000, 20000},
+				LargeCommunities: []types.LargeCommunity{
+					{
+						GlobalAdministrator: 1,
+						DataPart1:           2,
+						DataPart2:           3,
+					},
+				},
+				UnknownAttributes: []types.UnknownPathAttribute{
+					{
+						Optional:   true,
+						Transitive: true,
+						Partial:    true,
+						TypeCode:   222,
+						Value:      []byte{1, 1, 1, 1},
+					},
+				},
+				OriginatorID: 5,
+				ClusterList:  []uint32{3, 4, 5},
+			},
+			expected: &pb.BGPPath{
+				PathIdentifier: 10,
+				NextHop: &netapi.IP{
+					Lower:    210,
+					IsLegacy: true,
+				},
+				LocalPref: 20,
+				ASPath: []*pb.ASPathSegment{
+					{
+						ASSequence: true,
+						ASNs:       []uint32{100, 200, 300},
+					},
+				},
+				Origin:        1,
+				MED:           1000,
+				EBGP:          true,
+				BGPIdentifier: 1337,
+				Source: &netapi.IP{
+					Lower:    220,
+					IsLegacy: true,
+				},
+				Communities: []uint32{10000, 20000},
+				LargeCommunities: []*pb.LargeCommunity{
+					{
+						GlobalAdministrator: 1,
+						DataPart1:           2,
+						DataPart2:           3,
+					},
+				},
+				ClusterList: []uint32{3, 4, 5},
+				UnknownAttributes: []*pb.UnknownAttribute{
+					{
+						Optional:   true,
+						Transitive: true,
+						Partial:    true,
+						TypeCode:   222,
+						Value:      []byte{1, 1, 1, 1},
+					},
+				},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		res := test.p.ToProto()
+		assert.Equal(t, test.expected, res, test.name)
+	}
+}
diff --git a/route/path.go b/route/path.go
index 0f7d8372a9f04e93eed1ec24b9f14407f36fb6fd..98a2c53282a35627fbc5b40852aaaaaf7ee75f64 100644
--- a/route/path.go
+++ b/route/path.go
@@ -2,14 +2,26 @@ package route
 
 import (
 	"fmt"
+
+	api "github.com/bio-routing/bio-rd/route/api"
 )
 
+// Path represents a network path
 type Path struct {
 	Type       uint8
 	StaticPath *StaticPath
 	BGPPath    *BGPPath
 }
 
+// ToProto converts Path to Proto Path
+func (p *Path) ToProto() *api.Path {
+	return &api.Path{
+		Type:       uint32(p.Type),
+		BGPPath:    p.BGPPath.ToProto(),
+		StaticPath: p.StaticPath.ToProto(),
+	}
+}
+
 // Select returns negative if p < q, 0 if paths are equal, positive if p > q
 func (p *Path) Select(q *Path) int8 {
 	switch {
diff --git a/route/route.go b/route/route.go
index f263f70bcd7573b381299759d4ca24ba5440f5fe..189627da0e81ac59f466fc9b27c64957fe9f0518 100644
--- a/route/route.go
+++ b/route/route.go
@@ -6,6 +6,7 @@ import (
 	"sync"
 
 	"github.com/bio-routing/bio-rd/net"
+	api "github.com/bio-routing/bio-rd/route/api"
 )
 
 // StaticPathType indicats a path is a static path
@@ -60,6 +61,20 @@ func NewRouteAddPath(pfx net.Prefix, p []*Path) *Route {
 	return r
 }
 
+// ToProto converts a route into proto route
+func (r *Route) ToProto() *api.Route {
+	ret := &api.Route{
+		Pfx:   r.pfx.ToProto(),
+		Paths: make([]*api.Path, len(r.paths)),
+	}
+
+	for i := 0; i < len(r.paths); i++ {
+		ret.Paths[i] = r.paths[i].ToProto()
+	}
+
+	return ret
+}
+
 // Copy returns a copy of route r
 func (r *Route) Copy() *Route {
 	if r == nil {
diff --git a/route/static.go b/route/static.go
index df282d2217eca7a4457a65f995cc2427cd9789e9..3a4bbc202368782fcdb19ce8c3133c8ee8fd30d0 100644
--- a/route/static.go
+++ b/route/static.go
@@ -1,12 +1,26 @@
 package route
 
-import bnet "github.com/bio-routing/bio-rd/net"
+import (
+	bnet "github.com/bio-routing/bio-rd/net"
+	api "github.com/bio-routing/bio-rd/route/api"
+)
 
 // StaticPath represents a static path of a route
 type StaticPath struct {
 	NextHop bnet.IP
 }
 
+// ToProto converts a static path to proto StaticPath
+func (s *StaticPath) ToProto() *api.StaticPath {
+	if s == nil {
+		return nil
+	}
+
+	return &api.StaticPath{
+		NextHop: s.NextHop.ToProto(),
+	}
+}
+
 func (r *Route) staticPathSelection() {
 	if len(r.paths) == 0 {
 		return
diff --git a/route/static_test.go b/route/static_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7964e3289358bbfab8e094d556d254426affdb12
--- /dev/null
+++ b/route/static_test.go
@@ -0,0 +1,36 @@
+package route
+
+import (
+	"testing"
+
+	bnet "github.com/bio-routing/bio-rd/net"
+	apinet "github.com/bio-routing/bio-rd/net/api"
+	api "github.com/bio-routing/bio-rd/route/api"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestStaticToProto(t *testing.T) {
+	tests := []struct {
+		name     string
+		s        *StaticPath
+		expected *api.StaticPath
+	}{
+		{
+			name: "Some static path",
+			s: &StaticPath{
+				NextHop: bnet.IPv4(123),
+			},
+			expected: &api.StaticPath{
+				NextHop: &apinet.IP{
+					IsLegacy: true,
+					Lower:    123,
+				},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		res := test.s.ToProto()
+		assert.Equal(t, test.expected, res, test.name)
+	}
+}