From b0325ca5a235dd26800dac0f407be5b8d23b1430 Mon Sep 17 00:00:00 2001
From: Daniel Czerwonk <czerwonk@users.noreply.github.com>
Date: Sat, 2 Feb 2019 16:24:45 +0100
Subject: [PATCH] VRF structure (#171)

* added unique name for locRIBs

* added VRFs

* added error when no RIB configured in VRF

* vrf unique name ensurance

* fixed examples

* added missing test

* for convenience we provide a default RIB for v4 and v6 on each VRF

* removed debug output in main.go (api is coming)

* also removed debug output from fib example

* removed fib example
---
 .gitignore                                 |   1 +
 config/peer.go                             |   4 +-
 examples/bgp/main.go                       |  24 ++---
 examples/bgp/main_ipv4.go                  |  11 +--
 examples/bgp/main_ipv6.go                  |  10 +-
 examples/bmp/main_bmp.go                   |   4 +-
 examples/fib/main.go                       |  63 -------------
 examples/fib/main_ipv4.go                  |  49 ----------
 examples/fib/main_ipv6.go                  |  72 --------------
 go.mod                                     |   5 +-
 go.sum                                     |   4 +-
 protocols/bgp/server/bmp_router_test.go    |  68 ++++++-------
 protocols/bgp/server/fsm_test.go           |   7 +-
 protocols/bgp/server/peer.go               |  13 ++-
 protocols/bgp/server/update_sender_test.go |  10 +-
 routingtable/locRIB/loc_rib.go             |  11 ++-
 routingtable/locRIB/loc_rib_test.go        |   5 +-
 routingtable/vrf/vrf.go                    | 105 +++++++++++++++++++++
 routingtable/vrf/vrf_registry.go           |  32 +++++++
 routingtable/vrf/vrf_test.go               |  55 +++++++++++
 20 files changed, 280 insertions(+), 273 deletions(-)
 delete mode 100644 examples/fib/main.go
 delete mode 100644 examples/fib/main_ipv4.go
 delete mode 100644 examples/fib/main_ipv6.go
 create mode 100644 routingtable/vrf/vrf.go
 create mode 100644 routingtable/vrf/vrf_registry.go
 create mode 100644 routingtable/vrf/vrf_test.go

diff --git a/.gitignore b/.gitignore
index f135138c..0d491221 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ bio-rd
 examples/bgp
 examples/bmp
 examples/fib/fib
+examples/device/device
 
 # bazel directories
 /bazel-*
diff --git a/config/peer.go b/config/peer.go
index 1736ac66..a5e448bf 100644
--- a/config/peer.go
+++ b/config/peer.go
@@ -6,7 +6,7 @@ import (
 	bnet "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/routingtable"
 	"github.com/bio-routing/bio-rd/routingtable/filter"
-	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+	"github.com/bio-routing/bio-rd/routingtable/vrf"
 )
 
 // Peer defines the configuration for a BGP session
@@ -27,11 +27,11 @@ type Peer struct {
 	AdvertiseIPv4MultiProtocol bool
 	IPv4                       *AddressFamilyConfig
 	IPv6                       *AddressFamilyConfig
+	VRF                        *vrf.VRF
 }
 
 // AddressFamilyConfig represents all configuration parameters specific for an address family
 type AddressFamilyConfig struct {
-	RIB          *locRIB.LocRIB
 	ImportFilter *filter.Filter
 	ExportFilter *filter.Filter
 	AddPathSend  routingtable.ClientOptions
diff --git a/examples/bgp/main.go b/examples/bgp/main.go
index 11c2a689..e2e7d9d8 100644
--- a/examples/bgp/main.go
+++ b/examples/bgp/main.go
@@ -1,15 +1,12 @@
 package main
 
 import (
-	"fmt"
-	"time"
-
-	"github.com/sirupsen/logrus"
-
-	"github.com/bio-routing/bio-rd/protocols/bgp/server"
-	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+	"log"
 
 	bnet "github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/protocols/bgp/server"
+	"github.com/bio-routing/bio-rd/routingtable/vrf"
+	"github.com/sirupsen/logrus"
 )
 
 func strAddr(s string) uint32 {
@@ -20,16 +17,13 @@ func strAddr(s string) uint32 {
 func main() {
 	logrus.Printf("This is a BGP speaker\n")
 
-	rib := locRIB.New()
 	b := server.NewBgpServer()
-	startServer(b, rib)
+	v, err := vrf.New("master")
+	if err != nil {
+		log.Fatal(err)
+	}
 
-	go func() {
-		for {
-			fmt.Printf("LocRIB count: %d\n", rib.Count())
-			time.Sleep(time.Second * 10)
-		}
-	}()
+	startServer(b, v)
 
 	select {}
 }
diff --git a/examples/bgp/main_ipv4.go b/examples/bgp/main_ipv4.go
index c280c072..bd9fb5e1 100644
--- a/examples/bgp/main_ipv4.go
+++ b/examples/bgp/main_ipv4.go
@@ -6,18 +6,17 @@ import (
 	"net"
 	"time"
 
-	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+	"github.com/bio-routing/bio-rd/routingtable/vrf"
 
 	"github.com/bio-routing/bio-rd/config"
+	bnet "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/protocols/bgp/server"
 	"github.com/bio-routing/bio-rd/routingtable"
 	"github.com/bio-routing/bio-rd/routingtable/filter"
 	"github.com/sirupsen/logrus"
-
-	bnet "github.com/bio-routing/bio-rd/net"
 )
 
-func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
+func startServer(b server.BGPServer, v *vrf.VRF) {
 	err := b.Start(&config.Global{
 		Listen: true,
 		LocalAddressList: []net.IP{
@@ -41,7 +40,6 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		Passive:           true,
 		RouterID:          b.RouterID(),
 		IPv4: &config.AddressFamilyConfig{
-			RIB:          rib,
 			ImportFilter: filter.NewAcceptAllFilter(),
 			ExportFilter: filter.NewAcceptAllFilter(),
 			AddPathSend: routingtable.ClientOptions{
@@ -49,6 +47,7 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 			},
 		},
 		RouteServerClient: true,
+		VRF:               v,
 	})
 
 	b.AddPeer(config.Peer{
@@ -64,7 +63,6 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		RouterID:          b.RouterID(),
 		RouteServerClient: true,
 		IPv4: &config.AddressFamilyConfig{
-			RIB:          rib,
 			ImportFilter: filter.NewAcceptAllFilter(),
 			ExportFilter: filter.NewAcceptAllFilter(),
 			AddPathSend: routingtable.ClientOptions{
@@ -72,5 +70,6 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 			},
 			AddPathRecv: true,
 		},
+		VRF: v,
 	})
 }
diff --git a/examples/bgp/main_ipv6.go b/examples/bgp/main_ipv6.go
index f416133c..6dd1d694 100644
--- a/examples/bgp/main_ipv6.go
+++ b/examples/bgp/main_ipv6.go
@@ -7,16 +7,15 @@ import (
 	"time"
 
 	"github.com/bio-routing/bio-rd/config"
+	bnet "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/protocols/bgp/server"
 	"github.com/bio-routing/bio-rd/routingtable"
 	"github.com/bio-routing/bio-rd/routingtable/filter"
-	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+	"github.com/bio-routing/bio-rd/routingtable/vrf"
 	"github.com/sirupsen/logrus"
-
-	bnet "github.com/bio-routing/bio-rd/net"
 )
 
-func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
+func startServer(b server.BGPServer, v *vrf.VRF) {
 	err := b.Start(&config.Global{
 		Listen: true,
 		LocalAddressList: []net.IP{
@@ -39,13 +38,13 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		Passive:           true,
 		RouterID:          b.RouterID(),
 		IPv6: &config.AddressFamilyConfig{
-			RIB:          rib,
 			ImportFilter: filter.NewAcceptAllFilter(),
 			ExportFilter: filter.NewDrainFilter(),
 			AddPathSend: routingtable.ClientOptions{
 				BestOnly: true,
 			},
 		},
+		VRF: v,
 	})
 
 	b.AddPeer(config.Peer{
@@ -60,7 +59,6 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		Passive:           true,
 		RouterID:          b.RouterID(),
 		IPv6: &config.AddressFamilyConfig{
-			RIB:          rib,
 			ImportFilter: filter.NewDrainFilter(),
 			ExportFilter: filter.NewAcceptAllFilter(),
 			AddPathSend: routingtable.ClientOptions{
diff --git a/examples/bmp/main_bmp.go b/examples/bmp/main_bmp.go
index 63de954f..c973e6f5 100644
--- a/examples/bmp/main_bmp.go
+++ b/examples/bmp/main_bmp.go
@@ -13,8 +13,8 @@ import (
 func main() {
 	logrus.Printf("This is a BMP speaker\n")
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 	b := server.NewServer()
 	b.AddRouter(net.IP{10, 0, 255, 0}, 30119, rib4, rib6)
 
diff --git a/examples/fib/main.go b/examples/fib/main.go
deleted file mode 100644
index 677ab115..00000000
--- a/examples/fib/main.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package main
-
-import (
-	"net"
-	"os"
-	"time"
-
-	"github.com/bio-routing/bio-rd/config"
-	"github.com/bio-routing/bio-rd/protocols/bgp/server"
-	"github.com/bio-routing/bio-rd/protocols/fib"
-	"github.com/bio-routing/bio-rd/routingtable/locRIB"
-	log "github.com/sirupsen/logrus"
-
-	bnet "github.com/bio-routing/bio-rd/net"
-)
-
-func strAddr(s string) uint32 {
-	ret, _ := bnet.StrToAddr(s)
-	return ret
-}
-
-func main() {
-	log.SetLevel(log.DebugLevel)
-
-	f, err := os.OpenFile("/var/log/bio-rd.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
-	if err != nil {
-		log.Fatalf("error opening file: %v", err)
-	}
-	defer f.Close()
-
-	log.SetOutput(f)
-
-	log.Info("bio-routing started...\n")
-
-	cfg := &config.Global{
-		Listen: true,
-		LocalAddressList: []net.IP{
-			net.IPv4(169, 254, 0, 2),
-		},
-	}
-
-	rib := locRIB.New()
-	b := server.NewBgpServer()
-	startBGPServer(b, rib, cfg)
-
-	// FIB communication
-	n := fib.NewFIB(&config.Netlink{
-		HoldTime:       time.Second * 15,
-		UpdateInterval: time.Second * 15,
-		RoutingTable:   config.RtMain,
-	}, rib)
-	n.Start()
-
-	go func() {
-		for {
-			log.Debugf("LocRIB count: %d", rib.Count())
-			log.Debugf(rib.String())
-			time.Sleep(time.Second * 10)
-		}
-	}()
-
-	select {}
-}
diff --git a/examples/fib/main_ipv4.go b/examples/fib/main_ipv4.go
deleted file mode 100644
index 12bb67ae..00000000
--- a/examples/fib/main_ipv4.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package main
-
-import (
-	"time"
-
-	"github.com/bio-routing/bio-rd/routingtable"
-	"github.com/bio-routing/bio-rd/routingtable/locRIB"
-
-	"github.com/bio-routing/bio-rd/config"
-	"github.com/bio-routing/bio-rd/protocols/bgp/server"
-	"github.com/bio-routing/bio-rd/routingtable/filter"
-	log "github.com/sirupsen/logrus"
-
-	bnet "github.com/bio-routing/bio-rd/net"
-)
-
-func startBGPServer(b server.BGPServer, rib *locRIB.LocRIB, cfg *config.Global) {
-	err := b.Start(cfg)
-	if err != nil {
-		log.Fatalf("Unable to start BGP server: %v", err)
-	}
-
-	b.AddPeer(config.Peer{
-		AdminEnabled:      true,
-		LocalAS:           65200,
-		PeerAS:            65100,
-		PeerAddress:       bnet.IPv4FromOctets(169, 254, 0, 1),
-		LocalAddress:      bnet.IPv4FromOctets(169, 254, 0, 2),
-		ReconnectInterval: time.Second * 20,
-		HoldTime:          time.Second * 20,
-		KeepAlive:         time.Second * 20,
-		Passive:           false,
-		RouterID:          b.RouterID(),
-
-		//AddPathSend: routingtable.ClientOptions{
-		//	MaxPaths: 10,
-		//},
-		//RouteServerClient: true,
-		IPv4: &config.AddressFamilyConfig{
-			RIB:          rib,
-			ImportFilter: filter.NewAcceptAllFilter(),
-			ExportFilter: filter.NewAcceptAllFilter(),
-			AddPathSend: routingtable.ClientOptions{
-				MaxPaths: 10,
-			},
-			AddPathRecv: true,
-		},
-	})
-}
diff --git a/examples/fib/main_ipv6.go b/examples/fib/main_ipv6.go
deleted file mode 100644
index 3a1716e1..00000000
--- a/examples/fib/main_ipv6.go
+++ /dev/null
@@ -1,72 +0,0 @@
-// +build ipv6
-
-package main
-
-import (
-	"net"
-	"time"
-
-	"github.com/bio-routing/bio-rd/config"
-	"github.com/bio-routing/bio-rd/protocols/bgp/server"
-	"github.com/bio-routing/bio-rd/routingtable"
-	"github.com/bio-routing/bio-rd/routingtable/filter"
-	"github.com/bio-routing/bio-rd/routingtable/locRIB"
-	"github.com/sirupsen/logrus"
-
-	bnet "github.com/bio-routing/bio-rd/net"
-)
-
-func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
-
-	err := b.Start(&config.Global{
-		Listen: true,
-		LocalAddressList: []net.IP{
-			{0x20, 0x01, 0x6, 0x78, 0x1, 0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 0xca, 0xfe},
-		},
-	})
-	if err != nil {
-		logrus.Fatalf("Unable to start BGP server: %v", err)
-	}
-
-	b.AddPeer(config.Peer{
-		AdminEnabled:      true,
-		LocalAS:           65200,
-		PeerAS:            202739,
-		PeerAddress:       bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 1),
-		LocalAddress:      bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0xcafe),
-		ReconnectInterval: time.Second * 15,
-		HoldTime:          time.Second * 90,
-		KeepAlive:         time.Second * 30,
-		Passive:           true,
-		RouterID:          b.RouterID(),
-		IPv6: &config.AddressFamilyConfig{
-			RIB:          rib,
-			ImportFilter: filter.NewAcceptAllFilter(),
-			ExportFilter: filter.NewDrainFilter(),
-			AddPathSend: routingtable.ClientOptions{
-				BestOnly: true,
-			},
-		},
-	})
-
-	b.AddPeer(config.Peer{
-		AdminEnabled:      true,
-		LocalAS:           65200,
-		PeerAS:            65400,
-		PeerAddress:       bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0xcafe, 0, 0, 0, 5),
-		LocalAddress:      bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0xcafe),
-		ReconnectInterval: time.Second * 15,
-		HoldTime:          time.Second * 90,
-		KeepAlive:         time.Second * 30,
-		Passive:           true,
-		RouterID:          b.RouterID(),
-		IPv6: &config.AddressFamilyConfig{
-			RIB:          rib,
-			ImportFilter: filter.NewDrainFilter(),
-			ExportFilter: filter.NewAcceptAllFilter(),
-			AddPathSend: routingtable.ClientOptions{
-				BestOnly: true,
-			},
-		},
-	})
-}
diff --git a/go.mod b/go.mod
index 4a127d31..af9d8336 100644
--- a/go.mod
+++ b/go.mod
@@ -1,16 +1,15 @@
 module github.com/bio-routing/bio-rd
 
 require (
-	github.com/FMNSSun/libhash v0.0.0-20180614155432-dabbc4426b6d
 	github.com/bio-routing/tflow2 v0.0.0-20181230153523-2e308a4a3c3a
 	github.com/golang/protobuf v1.2.1-0.20181128192352-1d3f30b51784
 	github.com/pkg/errors v0.8.0
-	github.com/sirupsen/logrus v1.2.0
+	github.com/sirupsen/logrus v1.3.0
 	github.com/stretchr/testify v1.2.2
 	github.com/vishvananda/netlink v1.0.0
 	github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
 	golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect
-	golang.org/x/net v0.0.0-20181220203305-927f97764cc3
+	golang.org/x/net v0.0.0-20181220203305-927f97764cc3 // indirect
 	golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
 	golang.org/x/sys v0.0.0-20181228120256-c6cbdbf9e68a // indirect
 	google.golang.org/genproto v0.0.0-20181221175505-bd9b4fb69e2f // indirect
diff --git a/go.sum b/go.sum
index 88b6d834..e6bac905 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,4 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/FMNSSun/libhash v0.0.0-20180614155432-dabbc4426b6d h1:kLgFLWVyqlIMBCyE1f5e8pfLOoJ/RbFOlVbX27np+h4=
-github.com/FMNSSun/libhash v0.0.0-20180614155432-dabbc4426b6d/go.mod h1:o6y2qav+ovGETjay+RIj2kGI/o/LmjnuT/qyvmVPhCc=
 github.com/bio-routing/tflow2 v0.0.0-20181230153523-2e308a4a3c3a h1:CsHtkAummoG7yhc9+6NRBkoPcTzSSmTfiyzWx5NwFPw=
 github.com/bio-routing/tflow2 v0.0.0-20181230153523-2e308a4a3c3a/go.mod h1:tjzJ5IykdbWNs1FjmiJWsH6SRBl+aWgxO5I44DAegIw=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -23,6 +21,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
+github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
diff --git a/protocols/bgp/server/bmp_router_test.go b/protocols/bgp/server/bmp_router_test.go
index ace18461..aa17d74e 100644
--- a/protocols/bgp/server/bmp_router_test.go
+++ b/protocols/bgp/server/bmp_router_test.go
@@ -35,8 +35,8 @@ func TestBMPRouterServe(t *testing.T) {
 	for _, test := range tests {
 		addr := net.IP{10, 20, 30, 40}
 		port := uint16(123)
-		rib4 := locRIB.New()
-		rib6 := locRIB.New()
+		rib4 := locRIB.New("inet.0")
+		rib6 := locRIB.New("inet6.0")
 		conA, conB := net.Pipe()
 
 		r := newRouter(addr, port, rib4, rib6)
@@ -53,8 +53,8 @@ func TestBMPRouterServe(t *testing.T) {
 func TestStartStopBMP(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(123)
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	con := biotesting.NewMockConn()
 
@@ -260,8 +260,8 @@ func TestProcessPeerUpNotification(t *testing.T) {
 		{
 			name: "Regular BGP by RFC4271",
 			router: &router{
-				rib4:      locRIB.New(),
-				rib6:      locRIB.New(),
+				rib4:      locRIB.New("inet.0"),
+				rib6:      locRIB.New("inet6.0"),
 				neighbors: make(map[[16]byte]*neighbor),
 			},
 			pkt: &bmppkt.PeerUpNotification{
@@ -300,8 +300,8 @@ func TestProcessPeerUpNotification(t *testing.T) {
 			},
 			wantFail: false,
 			expected: &router{
-				rib4: locRIB.New(),
-				rib6: locRIB.New(),
+				rib4: locRIB.New("inet.0"),
+				rib6: locRIB.New("inet6.0"),
 				neighbors: map[[16]byte]*neighbor{
 					{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 255, 1}: {
 						localAS:     200,
@@ -631,16 +631,16 @@ func TestRegisterClients(t *testing.T) {
 	n := &neighbor{
 		fsm: &FSM{
 			ipv4Unicast: &fsmAddressFamily{
-				adjRIBIn: locRIB.New(),
+				adjRIBIn: locRIB.New("inet.0"),
 			},
 			ipv6Unicast: &fsmAddressFamily{
-				adjRIBIn: locRIB.New(),
+				adjRIBIn: locRIB.New("inet6.0"),
 			},
 		},
 	}
 
-	client4 := locRIB.New()
-	client6 := locRIB.New()
+	client4 := locRIB.New("inet.0")
+	client6 := locRIB.New("inet6.0")
 	ac4 := afiClient{
 		afi:    packet.IPv4AFI,
 		client: client4,
@@ -677,8 +677,8 @@ func TestIntegrationPeerUpRouteMonitor(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(12346)
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	r := newRouter(addr, port, rib4, rib6)
 	conA, conB := net.Pipe()
@@ -810,8 +810,8 @@ func TestIntegrationPeerUpRouteMonitorIPv6IPv4(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(12346)
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	r := newRouter(addr, port, rib4, rib6)
 	conA, conB := net.Pipe()
@@ -1020,8 +1020,8 @@ func TestIntegrationPeerUpRouteMonitorIPv4IPv6(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(12346)
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	r := newRouter(addr, port, rib4, rib6)
 	conA, conB := net.Pipe()
@@ -1231,8 +1231,8 @@ func TestIntegrationPeerUpRouteMonitorIPv6(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(12346)
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	r := newRouter(addr, port, rib4, rib6)
 	conA, conB := net.Pipe()
@@ -1383,8 +1383,8 @@ func TestIntegrationIncompleteBMPMsg(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(12346)
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	r := newRouter(addr, port, rib4, rib6)
 	con := biotesting.NewMockConn()
@@ -1461,8 +1461,8 @@ func TestBMPFullRunWithWithdraw(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(12346)
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	r := newRouter(addr, port, rib4, rib6)
 	con := biotesting.NewMockConn()
@@ -1630,8 +1630,8 @@ func TestBMPFullRunWithPeerDownNotification(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(12346)
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	r := newRouter(addr, port, rib4, rib6)
 	con := biotesting.NewMockConn()
@@ -1790,8 +1790,8 @@ func TestBMPFullRunWithTerminationMessage(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(12346)
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	r := newRouter(addr, port, rib4, rib6)
 	con := biotesting.NewMockConn()
@@ -1942,8 +1942,8 @@ func TestIntegrationPeerUpRouteMonitorIPv6WithClientAtEnd(t *testing.T) {
 	addr := net.IP{10, 20, 30, 40}
 	port := uint16(12346)
 
-	rib4 := locRIB.New()
-	rib6 := locRIB.New()
+	rib4 := locRIB.New("inet.0")
+	rib6 := locRIB.New("inet6.0")
 
 	r := newRouter(addr, port, rib4, rib6)
 	conA, conB := net.Pipe()
@@ -2087,7 +2087,7 @@ func TestIntegrationPeerUpRouteMonitorIPv6WithClientAtEnd(t *testing.T) {
 		t.Errorf("Unexpected IPv4 route count. Expected: 0 Got: %d", count)
 	}
 
-	client6 := locRIB.New()
+	client6 := locRIB.New("client6")
 	r.subscribeRIBs(client6, packet.IPv6AFI)
 
 	count = client6.RouteCount()
@@ -2702,13 +2702,13 @@ func TestIntegrationPeerUpRouteMonitorIPv6WithClientBeforeBMPPeer(t *testing.T)
 		addr := net.IP{10, 20, 30, 40}
 		port := uint16(12346)
 
-		rib4 := locRIB.New()
-		rib6 := locRIB.New()
+		rib4 := locRIB.New("inet.0")
+		rib6 := locRIB.New("inet6.0")
 
 		r := newRouter(addr, port, rib4, rib6)
 		conA, conB := net.Pipe()
 
-		client := locRIB.New()
+		client := locRIB.New("client")
 		r.subscribeRIBs(client, test.afi)
 		if test.doubleSubscribe {
 			r.subscribeRIBs(client, test.afi)
diff --git a/protocols/bgp/server/fsm_test.go b/protocols/bgp/server/fsm_test.go
index 5b410321..86a8fd78 100644
--- a/protocols/bgp/server/fsm_test.go
+++ b/protocols/bgp/server/fsm_test.go
@@ -5,12 +5,11 @@ import (
 	"testing"
 	"time"
 
+	bnet "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/routingtable/filter"
 	"github.com/bio-routing/bio-rd/routingtable/locRIB"
 	"github.com/stretchr/testify/assert"
-
-	bnet "github.com/bio-routing/bio-rd/net"
 )
 
 // TestFSM255UpdatesIPv4 emulates receiving 255 BGP updates and withdraws. Checks route counts.
@@ -19,7 +18,7 @@ func TestFSM255UpdatesIPv4(t *testing.T) {
 		addr:     bnet.IPv4FromOctets(169, 254, 100, 100),
 		routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(),
 		ipv4: &peerAddressFamily{
-			rib:          locRIB.New(),
+			rib:          locRIB.New("inet.0"),
 			importFilter: filter.NewAcceptAllFilter(),
 			exportFilter: filter.NewAcceptAllFilter(),
 		},
@@ -133,7 +132,7 @@ func TestFSM255UpdatesIPv6(t *testing.T) {
 		addr:     bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0xffff, 0, 0, 0, 1),
 		routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(),
 		ipv6: &peerAddressFamily{
-			rib:          locRIB.New(),
+			rib:          locRIB.New("inet6.0"),
 			importFilter: filter.NewAcceptAllFilter(),
 			exportFilter: filter.NewAcceptAllFilter(),
 		},
diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go
index 29001e70..c2db07d7 100644
--- a/protocols/bgp/server/peer.go
+++ b/protocols/bgp/server/peer.go
@@ -1,6 +1,7 @@
 package server
 
 import (
+	"fmt"
 	"sync"
 	"time"
 
@@ -136,12 +137,16 @@ func newPeer(c config.Peer, server *bgpServer) (*peer, error) {
 
 	if c.IPv4 != nil {
 		p.ipv4 = &peerAddressFamily{
-			rib:            c.IPv4.RIB,
+			rib:            c.VRF.IPv4UnicastRIB(),
 			importFilter:   filterOrDefault(c.IPv4.ImportFilter),
 			exportFilter:   filterOrDefault(c.IPv4.ExportFilter),
 			addPathReceive: c.IPv4.AddPathRecv,
 			addPathSend:    c.IPv4.AddPathSend,
 		}
+
+		if p.ipv4.rib == nil {
+			return nil, fmt.Errorf("No RIB for IPv4 unicast configured")
+		}
 	}
 
 	// If we are a route reflector and no ClusterID was set, use our RouterID
@@ -162,13 +167,17 @@ func newPeer(c config.Peer, server *bgpServer) (*peer, error) {
 
 	if c.IPv6 != nil {
 		p.ipv6 = &peerAddressFamily{
-			rib:            c.IPv6.RIB,
+			rib:            c.VRF.IPv6UnicastRIB(),
 			importFilter:   filterOrDefault(c.IPv6.ImportFilter),
 			exportFilter:   filterOrDefault(c.IPv6.ExportFilter),
 			addPathReceive: c.IPv6.AddPathRecv,
 			addPathSend:    c.IPv6.AddPathSend,
 		}
 		caps = append(caps, multiProtocolCapability(packet.IPv6AFI))
+
+		if p.ipv6.rib == nil {
+			return nil, fmt.Errorf("No RIB for IPv6 unicast configured")
+		}
 	}
 
 	p.optOpenParams = append(p.optOpenParams, packet.OptParam{
diff --git a/protocols/bgp/server/update_sender_test.go b/protocols/bgp/server/update_sender_test.go
index 3b0c0701..4e75183a 100644
--- a/protocols/bgp/server/update_sender_test.go
+++ b/protocols/bgp/server/update_sender_test.go
@@ -7,16 +7,14 @@ import (
 	"testing"
 	"time"
 
-	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
-	"github.com/bio-routing/bio-rd/routingtable"
-
-	"github.com/stretchr/testify/assert"
-
 	bnet "github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/route"
+	"github.com/bio-routing/bio-rd/routingtable"
 	"github.com/bio-routing/bio-rd/routingtable/filter"
 	"github.com/bio-routing/bio-rd/routingtable/locRIB"
 	btest "github.com/bio-routing/bio-rd/testing"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestSender(t *testing.T) {
@@ -881,7 +879,7 @@ func TestSender(t *testing.T) {
 			addr: bnet.IPv4FromOctets(169, 254, 100, 100),
 		})
 
-		rib := locRIB.New()
+		rib := locRIB.New("inet6.0")
 		if test.afi == packet.IPv6AFI {
 			fsmA.ipv6Unicast = newFSMAddressFamily(packet.IPv6AFI, packet.UnicastSAFI, &peerAddressFamily{
 				rib:          rib,
diff --git a/routingtable/locRIB/loc_rib.go b/routingtable/locRIB/loc_rib.go
index b1f975f6..fe4f2905 100644
--- a/routingtable/locRIB/loc_rib.go
+++ b/routingtable/locRIB/loc_rib.go
@@ -8,11 +8,12 @@ import (
 	"github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/route"
 	"github.com/bio-routing/bio-rd/routingtable"
-	"github.com/sirupsen/logrus"
+	log "github.com/sirupsen/logrus"
 )
 
 // LocRIB represents a routing information base
 type LocRIB struct {
+	name             string
 	clientManager    *routingtable.ClientManager
 	rt               *routingtable.RoutingTable
 	mu               sync.RWMutex
@@ -20,12 +21,14 @@ type LocRIB struct {
 }
 
 // New creates a new routing information base
-func New() *LocRIB {
+func New(name string) *LocRIB {
 	a := &LocRIB{
+		name:             name,
 		rt:               routingtable.NewRoutingTable(),
 		contributingASNs: routingtable.NewContributingASNs(),
 	}
 	a.clientManager = routingtable.NewClientManager(a)
+
 	return a
 }
 
@@ -64,7 +67,7 @@ func (a *LocRIB) RouteCount() int64 {
 func (a *LocRIB) AddPath(pfx net.Prefix, p *route.Path) error {
 	a.mu.Lock()
 	defer a.mu.Unlock()
-	logrus.WithFields(map[string]interface{}{
+	log.WithFields(map[string]interface{}{
 		"Prefix": pfx,
 		"Route":  p,
 	}).Debug("AddPath to locRIB")
@@ -94,7 +97,7 @@ func (a *LocRIB) RemovePath(pfx net.Prefix, p *route.Path) bool {
 	a.mu.Lock()
 	defer a.mu.Unlock()
 
-	logrus.WithFields(map[string]interface{}{
+	log.WithFields(map[string]interface{}{
 		"Prefix": pfx,
 		"Route":  p,
 	}).Debug("Remove from locRIB")
diff --git a/routingtable/locRIB/loc_rib_test.go b/routingtable/locRIB/loc_rib_test.go
index 6b3e24e0..b2038e95 100644
--- a/routingtable/locRIB/loc_rib_test.go
+++ b/routingtable/locRIB/loc_rib_test.go
@@ -5,7 +5,6 @@ import (
 
 	bnet "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/route"
-
 	"github.com/stretchr/testify/assert"
 )
 
@@ -75,7 +74,7 @@ func TestContainsPfxPath(t *testing.T) {
 		},
 	}
 	for i, tc := range testCases {
-		rib := New()
+		rib := New("inet.0")
 		for _, p := range tc.in {
 			err := rib.AddPath(p.pfx, p.path)
 			assert.Nil(t, err, "could not fill rib in testcase %v", i)
@@ -86,7 +85,7 @@ func TestContainsPfxPath(t *testing.T) {
 }
 
 func TestLocRIB_RemovePathUnknown(t *testing.T) {
-	rib := New()
+	rib := New("inet.0")
 	assert.True(t, rib.RemovePath(bnet.NewPfx(bnet.IPv4(1), 32),
 		&route.Path{
 			Type: route.StaticPathType,
diff --git a/routingtable/vrf/vrf.go b/routingtable/vrf/vrf.go
new file mode 100644
index 00000000..c016d864
--- /dev/null
+++ b/routingtable/vrf/vrf.go
@@ -0,0 +1,105 @@
+package vrf
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+)
+
+const (
+	afiIPv4     = 1
+	afiIPv6     = 2
+	safiUnicast = 1
+)
+
+type addressFamily struct {
+	afi  uint16
+	safi uint8
+}
+
+// VRF a list of RIBs for different address families building a routing instance
+type VRF struct {
+	name     string
+	ribs     map[addressFamily]*locRIB.LocRIB
+	mu       sync.Mutex
+	ribNames map[string]*locRIB.LocRIB
+}
+
+// New creates a new VRF
+func New(name string) (*VRF, error) {
+	v := newUntrackedVRF(name)
+	v.CreateIPv4UnicastLocRIB("inet.0")
+	v.CreateIPv6UnicastLocRIB("inet6.0")
+
+	err := globalRegistry.registerVRF(v)
+	if err != nil {
+		return nil, err
+	}
+
+	return v, nil
+}
+
+func newUntrackedVRF(name string) *VRF {
+	return &VRF{
+		name:     name,
+		ribs:     make(map[addressFamily]*locRIB.LocRIB),
+		ribNames: make(map[string]*locRIB.LocRIB),
+	}
+}
+
+// CreateLocRIB creates a local RIB with the given name
+func (v *VRF) createLocRIB(name string, family addressFamily) (*locRIB.LocRIB, error) {
+	v.mu.Lock()
+	defer v.mu.Unlock()
+
+	_, found := v.ribNames[name]
+	if found {
+		return nil, fmt.Errorf("a table with the name '%s' already exists in VRF '%s'", name, v.name)
+	}
+
+	rib := locRIB.New(name)
+	v.ribs[family] = rib
+	v.ribNames[name] = rib
+
+	return rib, nil
+}
+
+// CreateIPv4UnicastLocRIB creates a LocRIB for the IPv4 unicast address family
+func (v *VRF) CreateIPv4UnicastLocRIB(name string) (*locRIB.LocRIB, error) {
+	return v.createLocRIB(name, addressFamily{afi: afiIPv4, safi: safiUnicast})
+}
+
+// CreateIPv6UnicastLocRIB creates a LocRIB for the IPv6 unicast address family
+func (v *VRF) CreateIPv6UnicastLocRIB(name string) (*locRIB.LocRIB, error) {
+	return v.createLocRIB(name, addressFamily{afi: afiIPv6, safi: safiUnicast})
+}
+
+// IPv4UnicastRIB returns the local RIB for the IPv4 unicast address family
+func (v *VRF) IPv4UnicastRIB() *locRIB.LocRIB {
+	return v.ribForAddressFamily(addressFamily{afi: afiIPv4, safi: safiUnicast})
+}
+
+// IPv6UnicastRIB returns the local RIB for the IPv6 unicast address family
+func (v *VRF) IPv6UnicastRIB() *locRIB.LocRIB {
+	return v.ribForAddressFamily(addressFamily{afi: afiIPv6, safi: safiUnicast})
+}
+
+func (v *VRF) Name() string {
+	return v.name
+}
+
+func (v *VRF) ribForAddressFamily(family addressFamily) *locRIB.LocRIB {
+	v.mu.Lock()
+	defer v.mu.Unlock()
+
+	rib, _ := v.ribs[family]
+
+	return rib
+}
+
+// RIBByName returns the RIB for a given name. If there is no RIB with this name, found is false
+func (v *VRF) RIBByName(name string) (rib *locRIB.LocRIB, found bool) {
+	rib, found = v.ribNames[name]
+	return rib, found
+}
diff --git a/routingtable/vrf/vrf_registry.go b/routingtable/vrf/vrf_registry.go
new file mode 100644
index 00000000..5af298d4
--- /dev/null
+++ b/routingtable/vrf/vrf_registry.go
@@ -0,0 +1,32 @@
+package vrf
+
+import (
+	"fmt"
+	"sync"
+)
+
+var globalRegistry *vrfRegistry
+
+func init() {
+	globalRegistry = &vrfRegistry{
+		vrfs: make(map[string]*VRF),
+	}
+}
+
+type vrfRegistry struct {
+	vrfs map[string]*VRF
+	mu   sync.Mutex
+}
+
+func (r *vrfRegistry) registerVRF(v *VRF) error {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	_, found := r.vrfs[v.name]
+	if found {
+		return fmt.Errorf("a VRF with the name '%s' already exists", v.name)
+	}
+
+	r.vrfs[v.name] = v
+	return nil
+}
diff --git a/routingtable/vrf/vrf_test.go b/routingtable/vrf/vrf_test.go
new file mode 100644
index 00000000..3b707ccc
--- /dev/null
+++ b/routingtable/vrf/vrf_test.go
@@ -0,0 +1,55 @@
+package vrf
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewWithDuplicate(t *testing.T) {
+	_, err := New("master")
+	assert.Nil(t, err, "no error on first invocation")
+
+	_, err = New("master")
+	assert.NotNil(t, err, "ambigious VRF name")
+}
+
+func TestIPv4UnicastRIBWith(t *testing.T) {
+	v := newUntrackedVRF("master")
+	rib, err := v.CreateIPv4UnicastLocRIB("inet.0")
+
+	assert.Equal(t, rib, v.IPv4UnicastRIB())
+	assert.Nil(t, err, "error must be nil")
+}
+
+func TestIPv6UnicastRIB(t *testing.T) {
+	v := newUntrackedVRF("master")
+	rib, err := v.CreateIPv6UnicastLocRIB("inet6.0")
+
+	assert.Equal(t, rib, v.IPv6UnicastRIB())
+	assert.Nil(t, err, "error must be nil")
+}
+
+func TestCreateLocRIBTwice(t *testing.T) {
+	v := newUntrackedVRF("master")
+	_, err := v.CreateIPv6UnicastLocRIB("inet6.0")
+	assert.Nil(t, err, "error must be nil on first invokation")
+
+	_, err = v.CreateIPv6UnicastLocRIB("inet6.0")
+	assert.NotNil(t, err, "error must not be nil on second invokation")
+}
+
+func TestRIBByName(t *testing.T) {
+	v := newUntrackedVRF("master")
+	rib, _ := v.CreateIPv6UnicastLocRIB("inet6.0")
+	assert.NotNil(t, rib, "rib must not be nil after creation")
+
+	foundRIB, found := v.RIBByName("inet6.0")
+	assert.True(t, found)
+	assert.Exactly(t, rib, foundRIB)
+}
+
+func TestName(t *testing.T) {
+	v := newUntrackedVRF("foo")
+	assert.Equal(t, "foo", v.Name())
+}
-- 
GitLab