diff --git a/routingtable/client_interface.go b/routingtable/client_interface.go
new file mode 100644
index 0000000000000000000000000000000000000000..648bf40ae29738a7c9f85bf068183d2317d66120
--- /dev/null
+++ b/routingtable/client_interface.go
@@ -0,0 +1,12 @@
+package routingtable
+
+import (
+	"github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/route"
+)
+
+// RouteTableClient is the interface that every type of RIB must implement
+type RouteTableClient interface {
+	AddPath(*net.Prefix, *route.Path) error
+	RemovePath(*net.Prefix, *route.Path) error
+}
diff --git a/routingtable/client_manager.go b/routingtable/client_manager.go
new file mode 100644
index 0000000000000000000000000000000000000000..08e2587aa0d9f50bcf59054765754cfe84166af0
--- /dev/null
+++ b/routingtable/client_manager.go
@@ -0,0 +1,21 @@
+package routingtable
+
+type ClientManager struct {
+	clients      map[RouteTableClient]struct{} // Ensures a client registers at most once
+	routingTable *RoutingTable
+}
+
+func (c *ClientManager) Register(client RouteTableClient) {
+	if c.clients == nil {
+		c.clients = make(map[RouteTableClient]struct{}, 0)
+	}
+	c.clients[client] = struct{}{}
+	//c.routingTable.updateNewClient(client)
+}
+
+func (c *ClientManager) Unregister(client RouteTableClient) {
+	if _, ok := c.clients[client]; !ok {
+		return
+	}
+	delete(c.clients, client)
+}
diff --git a/routingtable/rib_interface.go b/routingtable/rib_interface.go
new file mode 100644
index 0000000000000000000000000000000000000000..45afeb2605a13a3ca65bfb885950d0a3b2fc8692
--- /dev/null
+++ b/routingtable/rib_interface.go
@@ -0,0 +1,11 @@
+package routingtable
+
+import (
+	"github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/route"
+)
+
+type RIB interface {
+	AddPath(*net.Prefix, *route.Path)
+	RemovePath(*net.Prefix, *route.Path)
+}
diff --git a/routingtable/table.go b/routingtable/table.go
new file mode 100644
index 0000000000000000000000000000000000000000..6b8bcb6b39200b0710aa3de0899ce12c248ed846
--- /dev/null
+++ b/routingtable/table.go
@@ -0,0 +1,92 @@
+package routingtable
+
+import (
+	"sync"
+
+	"github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/route"
+)
+
+// RoutingTable is a binary trie that stores prefixes and their paths
+type RoutingTable struct {
+	root *node
+	mu   sync.RWMutex
+}
+
+// NewRoutingTable creates a new routing table
+func NewRoutingTable() *RoutingTable {
+	return &RoutingTable{}
+}
+
+// AddPath adds a path to the routing table
+func (rt *RoutingTable) AddPath(pfx net.Prefix, p *route.Path) error {
+	rt.mu.Lock()
+	defer rt.mu.Unlock()
+
+	if rt.root == nil {
+		rt.root = newNode(pfx, p, pfx.Pfxlen(), false)
+		return nil
+	}
+
+	rt.root = rt.root.addPath(pfx, p)
+	return nil
+}
+
+// RemovePath removes a path from the trie
+func (rt *RoutingTable) RemovePath(pfx net.Prefix, p *route.Path) error {
+	rt.mu.Lock()
+	defer rt.mu.Unlock()
+
+	rt.root.removePath(pfx, p)
+	return nil
+}
+
+// LPM performs a longest prefix match for pfx on lpm
+func (rt *RoutingTable) LPM(pfx net.Prefix) (res []*route.Route) {
+	rt.mu.RLock()
+	defer rt.mu.RUnlock()
+
+	if rt.root == nil {
+		return nil
+	}
+
+	rt.root.lpm(pfx, &res)
+	return res
+}
+
+// Get get's the route for pfx from the LPM
+func (rt *RoutingTable) Get(pfx net.Prefix) *route.Route {
+	rt.mu.RLock()
+	defer rt.mu.RUnlock()
+
+	if rt.root == nil {
+		return nil
+	}
+
+	res := rt.root.get(pfx)
+	if res == nil {
+		return nil
+	}
+	return res.route
+}
+
+// GetLonger get's prefix pfx and all it's more specifics from the LPM
+func (rt *RoutingTable) GetLonger(pfx net.Prefix) (res []*route.Route) {
+	rt.mu.RLock()
+	defer rt.mu.RUnlock()
+
+	if rt.root == nil {
+		return []*route.Route{}
+	}
+
+	return rt.root.get(pfx).dumpPfxs(res)
+}
+
+// Dump dumps all routes in table rt into a slice
+func (rt *RoutingTable) Dump() []*route.Route {
+	rt.mu.RLock()
+	defer rt.mu.RUnlock()
+
+	res := make([]*route.Route, 0)
+	return rt.root.dump(res)
+}
diff --git a/routingtable/table_test.go b/routingtable/table_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a2eb10f76f93046fe71e98e59998ff52e15283b5
--- /dev/null
+++ b/routingtable/table_test.go
@@ -0,0 +1,451 @@
+package routingtable
+
+import (
+	"testing"
+
+	"github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/route"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAddPath(t *testing.T) {
+	tests := []struct {
+		name     string
+		routes   []*route.Route
+		expected *node
+	}{
+		{
+			name: "Insert first node",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+			},
+			expected: &node{
+				route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				skip:  8,
+			},
+		},
+		{
+			name: "Insert duplicate node",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+			},
+			expected: &node{
+				route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				skip:  8,
+			},
+		},
+		{
+			name: "Insert triangle",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), nil),
+			},
+			expected: &node{
+				route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				skip:  8,
+				l: &node{
+					route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), nil),
+				},
+				h: &node{
+					route: route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), nil),
+				},
+			},
+		},
+		{
+			name: "Insert disjunct prefixes",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+			},
+			expected: &node{
+				route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 7), nil),
+				skip:  7,
+				dummy: true,
+				l: &node{
+					route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				},
+				h: &node{
+					route: route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+					skip:  16,
+				},
+			},
+		},
+		{
+			name: "Insert disjunct prefixes plus one child low",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+			},
+			expected: &node{
+				route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 7), nil),
+				skip:  7,
+				dummy: true,
+				l: &node{
+					route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+					l: &node{
+						skip:  1,
+						route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+						l: &node{
+							skip:  1,
+							route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+						},
+					},
+				},
+				h: &node{
+					route: route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+					skip:  16,
+				},
+			},
+		},
+		{
+			name: "Insert disjunct prefixes plus one child high",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.128"), 25), nil),
+			},
+			expected: &node{
+				route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 7), nil),
+				skip:  7,
+				dummy: true,
+				l: &node{
+					route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+					l: &node{
+						skip:  1,
+						route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+						l: &node{
+							skip:  1,
+							route: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+						},
+					},
+				},
+				h: &node{
+					route: route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+					skip:  16,
+					h: &node{
+						route: route.NewRoute(net.NewPfx(strAddr("11.100.123.128"), 25), nil),
+					},
+				},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		rt := NewRoutingTable()
+		for _, route := range test.routes {
+			rt.AddPath(route.Prefix(), nil)
+		}
+
+		assert.Equal(t, test.expected, rt.root)
+	}
+}
+
+func TestGet(t *testing.T) {
+	tests := []struct {
+		name     string
+		routes   []*route.Route
+		needle   net.Prefix
+		expected *route.Route
+	}{
+		{
+			name: "Test 1: Search pfx and dump route + more specifics",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+			},
+			needle:   net.NewPfx(strAddr("10.0.0.0"), 8),
+			expected: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+		},
+		{
+			name: "Test 2: Search pfx and don't dump more specifics",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+			},
+			needle:   net.NewPfx(strAddr("10.0.0.0"), 8),
+			expected: route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+		},
+		{
+			name:     "Test 3: Empty table",
+			routes:   []*route.Route{},
+			needle:   net.NewPfx(strAddr("10.0.0.0"), 32),
+			expected: nil,
+		},
+		{
+			name: "Test 4: Get Dummy",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+			},
+			needle:   net.NewPfx(strAddr("10.0.0.0"), 7),
+			expected: nil,
+		},
+		{
+			name: "Test 5",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+			},
+			needle:   net.NewPfx(strAddr("11.100.123.0"), 24),
+			expected: route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+		},
+		{
+			name: "Test 4: Get nonexistent #1",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+			},
+			needle:   net.NewPfx(strAddr("10.0.0.0"), 10),
+			expected: nil,
+		},
+		{
+			name: "Test 4: Get nonexistent #2",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+			},
+			needle:   net.NewPfx(strAddr("10.0.0.0"), 10),
+			expected: nil,
+		},
+	}
+
+	for _, test := range tests {
+		rt := NewRoutingTable()
+		for _, route := range test.routes {
+			rt.AddPath(route.Prefix(), nil)
+		}
+		p := rt.Get(test.needle)
+
+		if p == nil {
+			if test.expected != nil {
+				t.Errorf("Unexpected nil result for test %q", test.name)
+			}
+			continue
+		}
+
+		assert.Equal(t, test.expected, p)
+	}
+}
+func TestGetLonger(t *testing.T) {
+	tests := []struct {
+		name     string
+		routes   []*route.Route
+		needle   net.Prefix
+		expected []*route.Route
+	}{
+		{
+			name: "Test 1: Search pfx and dump route + more specifics",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+			},
+			needle: net.NewPfx(strAddr("10.0.0.0"), 8),
+			expected: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+			},
+		},
+	}
+
+	for _, test := range tests {
+		rt := NewRoutingTable()
+		for _, route := range test.routes {
+			rt.AddPath(route.Prefix(), nil)
+		}
+		p := rt.GetLonger(test.needle)
+
+		if p == nil {
+			if test.expected != nil {
+				t.Errorf("Unexpected nil result for test %q", test.name)
+			}
+			continue
+		}
+
+		assert.Equal(t, test.expected, p)
+	}
+}
+func TestLPM(t *testing.T) {
+	tests := []struct {
+		name     string
+		routes   []*route.Route
+		needle   net.Prefix
+		expected []*route.Route
+	}{
+		{
+			name:     "LPM for non-existent route",
+			routes:   []*route.Route{},
+			needle:   net.NewPfx(strAddr("10.0.0.0"), 32),
+			expected: nil,
+		},
+		{
+			name: "Positive LPM test",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+			},
+			needle: net.NewPfx(167772160, 32), // 10.0.0.0/32
+			expected: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+			},
+		},
+		{
+			name: "Exact match",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("11.100.123.0"), 24), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+			},
+			needle: net.NewPfx(strAddr("10.0.0.0"), 10),
+			expected: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), nil),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 10), nil),
+			},
+		},
+	}
+
+	for _, test := range tests {
+		rt := NewRoutingTable()
+		for _, route := range test.routes {
+			rt.AddPath(route.Prefix(), nil)
+		}
+		assert.Equal(t, test.expected, rt.LPM(test.needle))
+	}
+}
+
+func TestRemovePath(t *testing.T) {
+	tests := []struct {
+		name       string
+		routes     []*route.Route
+		removePfx  net.Prefix
+		removePath *route.Path
+		expected   []*route.Route
+	}{
+		{
+			name: "Remove a path that is the only one for a prefix",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
+					Type:    route.BGPPathType,
+					BGPPath: &route.BGPPath{},
+				}),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), &route.Path{
+					Type:    route.BGPPathType,
+					BGPPath: &route.BGPPath{},
+				}),
+				route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), &route.Path{
+					Type:    route.BGPPathType,
+					BGPPath: &route.BGPPath{},
+				}),
+			},
+			removePfx: net.NewPfx(strAddr("10.0.0.0"), 8),
+			removePath: &route.Path{
+				Type:    route.BGPPathType,
+				BGPPath: &route.BGPPath{},
+			},
+			expected: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), &route.Path{
+					Type:    route.BGPPathType,
+					BGPPath: &route.BGPPath{},
+				}),
+				route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), &route.Path{
+					Type:    route.BGPPathType,
+					BGPPath: &route.BGPPath{},
+				}),
+			},
+		},
+		{
+			name: "Remove a path that is one of two for a prefix",
+			routes: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
+					Type: route.BGPPathType,
+					BGPPath: &route.BGPPath{
+						LocalPref: 1000,
+					},
+				}),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
+					Type: route.BGPPathType,
+					BGPPath: &route.BGPPath{
+						LocalPref: 2000,
+					},
+				}),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), &route.Path{
+					Type:    route.BGPPathType,
+					BGPPath: &route.BGPPath{},
+				}),
+				route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), &route.Path{
+					Type:    route.BGPPathType,
+					BGPPath: &route.BGPPath{},
+				}),
+			},
+			removePfx: net.NewPfx(strAddr("10.0.0.0"), 8),
+			removePath: &route.Path{
+				Type: route.BGPPathType,
+				BGPPath: &route.BGPPath{
+					LocalPref: 1000,
+				},
+			},
+			expected: []*route.Route{
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{
+					Type: route.BGPPathType,
+					BGPPath: &route.BGPPath{
+						LocalPref: 2000,
+					},
+				}),
+				route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 9), &route.Path{
+					Type:    route.BGPPathType,
+					BGPPath: &route.BGPPath{},
+				}),
+				route.NewRoute(net.NewPfx(strAddr("10.128.0.0"), 9), &route.Path{
+					Type:    route.BGPPathType,
+					BGPPath: &route.BGPPath{},
+				}),
+			},
+		},
+	}
+
+	for _, test := range tests {
+		rt := NewRoutingTable()
+		for _, route := range test.routes {
+			for _, p := range route.Paths() {
+				rt.AddPath(route.Prefix(), p)
+			}
+		}
+
+		rt.RemovePath(test.removePfx, test.removePath)
+
+		rtExpected := NewRoutingTable()
+		for _, route := range test.expected {
+			for _, p := range route.Paths() {
+				rtExpected.AddPath(route.Prefix(), p)
+			}
+		}
+
+		assert.Equal(t, rtExpected.Dump(), rt.Dump())
+	}
+}
+
+func strAddr(s string) uint32 {
+	ret, _ := net.StrToAddr(s)
+	return ret
+}
diff --git a/routingtable/trie.go b/routingtable/trie.go
new file mode 100644
index 0000000000000000000000000000000000000000..5fdc4b8cb73357740e777fd6606ffcea129972ba
--- /dev/null
+++ b/routingtable/trie.go
@@ -0,0 +1,229 @@
+package routingtable
+
+import (
+	"github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/route"
+)
+
+// node is a node in the compressed trie that is used to implement a routing table
+type node struct {
+	skip  uint8
+	dummy bool
+	route *route.Route
+	l     *node
+	h     *node
+}
+
+func getBitUint32(x uint32, pos uint8) bool {
+	return ((x) & (1 << (32 - pos))) != 0
+}
+
+func newNode(pfx net.Prefix, path *route.Path, skip uint8, dummy bool) *node {
+	n := &node{
+		route: route.NewRoute(pfx, path),
+		skip:  skip,
+		dummy: dummy,
+	}
+	return n
+}
+
+func (n *node) removePath(pfx net.Prefix, p *route.Path) {
+	if n == nil {
+		return
+	}
+
+	if n.route.Prefix() == pfx {
+		if n.dummy {
+			return
+		}
+
+		n.route.RemovePath(p)
+		if len(n.route.Paths()) == 0 {
+			// FIXME: Can this node actually be removed from the trie entirely?
+			n.dummy = true
+		}
+
+		return
+	}
+
+	b := getBitUint32(pfx.Addr(), n.route.Pfxlen()+1)
+	if !b {
+		n.l.removePath(pfx, p)
+		return
+	}
+	n.h.removePath(pfx, p)
+	return
+}
+
+func (n *node) lpm(needle net.Prefix, res *[]*route.Route) {
+	if n == nil {
+		return
+	}
+
+	currentPfx := n.route.Prefix()
+	if currentPfx == needle && !n.dummy {
+		*res = append(*res, n.route)
+		return
+	}
+
+	if !currentPfx.Contains(needle) {
+		return
+	}
+
+	if !n.dummy {
+		*res = append(*res, n.route)
+	}
+	n.l.lpm(needle, res)
+	n.h.lpm(needle, res)
+}
+
+func (n *node) dumpPfxs(res []*route.Route) []*route.Route {
+	if n == nil {
+		return nil
+	}
+
+	if !n.dummy {
+		res = append(res, n.route)
+	}
+
+	if n.l != nil {
+		res = n.l.dumpPfxs(res)
+	}
+
+	if n.h != nil {
+		res = n.h.dumpPfxs(res)
+	}
+
+	return res
+}
+
+func (n *node) get(pfx net.Prefix) *node {
+	if n == nil {
+		return nil
+	}
+
+	if n.route.Prefix() == pfx {
+		if n.dummy {
+			return nil
+		}
+		return n
+	}
+
+	if n.route.Pfxlen() > pfx.Pfxlen() {
+		return nil
+	}
+
+	b := getBitUint32(pfx.Addr(), n.route.Pfxlen()+1)
+	if !b {
+		return n.l.get(pfx)
+	}
+	return n.h.get(pfx)
+}
+
+func (n *node) addPath(pfx net.Prefix, p *route.Path) *node {
+	currentPfx := n.route.Prefix()
+	if currentPfx == pfx {
+		n.route.AddPath(p)
+		n.dummy = false
+		return n
+	}
+
+	// is pfx NOT a subnet of this node?
+	if !currentPfx.Contains(pfx) {
+		if pfx.Contains(currentPfx) {
+			return n.insertBefore(pfx, p, n.route.Pfxlen()-n.skip-1)
+		}
+
+		return n.newSuperNode(pfx, p)
+	}
+
+	// pfx is a subnet of this node
+	b := getBitUint32(pfx.Addr(), n.route.Pfxlen()+1)
+	if !b {
+		return n.insertLow(pfx, p, currentPfx.Pfxlen())
+	}
+	return n.insertHigh(pfx, p, n.route.Pfxlen())
+}
+
+func (n *node) insertLow(pfx net.Prefix, p *route.Path, parentPfxLen uint8) *node {
+	if n.l == nil {
+		n.l = newNode(pfx, p, pfx.Pfxlen()-parentPfxLen-1, false)
+		return n
+	}
+	n.l = n.l.addPath(pfx, p)
+	return n
+}
+
+func (n *node) insertHigh(pfx net.Prefix, p *route.Path, parentPfxLen uint8) *node {
+	if n.h == nil {
+		n.h = newNode(pfx, p, pfx.Pfxlen()-parentPfxLen-1, false)
+		return n
+	}
+	n.h = n.h.addPath(pfx, p)
+	return n
+}
+
+func (n *node) newSuperNode(pfx net.Prefix, p *route.Path) *node {
+	superNet := pfx.GetSupernet(n.route.Prefix())
+
+	pfxLenDiff := n.route.Pfxlen() - superNet.Pfxlen()
+	skip := n.skip - pfxLenDiff
+
+	pseudoNode := newNode(superNet, nil, skip, true)
+	pseudoNode.insertChildren(n, pfx, p)
+	return pseudoNode
+}
+
+func (n *node) insertChildren(old *node, newPfx net.Prefix, newPath *route.Path) {
+	// Place the old node
+	b := getBitUint32(old.route.Prefix().Addr(), n.route.Pfxlen()+1)
+	if !b {
+		n.l = old
+		n.l.skip = old.route.Pfxlen() - n.route.Pfxlen() - 1
+	} else {
+		n.h = old
+		n.h.skip = old.route.Pfxlen() - n.route.Pfxlen() - 1
+	}
+
+	// Place the new Prefix
+	newNode := newNode(newPfx, newPath, newPfx.Pfxlen()-n.route.Pfxlen()-1, false)
+	b = getBitUint32(newPfx.Addr(), n.route.Pfxlen()+1)
+	if !b {
+		n.l = newNode
+	} else {
+		n.h = newNode
+	}
+}
+
+func (n *node) insertBefore(pfx net.Prefix, p *route.Path, parentPfxLen uint8) *node {
+	tmp := n
+
+	pfxLenDiff := n.route.Pfxlen() - pfx.Pfxlen()
+	skip := n.skip - pfxLenDiff
+	new := newNode(pfx, p, skip, false)
+
+	b := getBitUint32(pfx.Addr(), parentPfxLen)
+	if !b {
+		new.l = tmp
+		new.l.skip = tmp.route.Pfxlen() - pfx.Pfxlen() - 1
+	} else {
+		new.h = tmp
+		new.h.skip = tmp.route.Pfxlen() - pfx.Pfxlen() - 1
+	}
+
+	return new
+}
+
+func (n *node) dump(res []*route.Route) []*route.Route {
+	if n == nil {
+		return res
+	}
+
+	if !n.dummy {
+		res = append(res, n.route)
+	}
+
+	res = n.l.dump(res)
+	res = n.h.dump(res)
+	return res
+}
diff --git a/routingtable/trie_test.go b/routingtable/trie_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7bbf46e11dcc2a19e3af36a012e81b65be326cad
--- /dev/null
+++ b/routingtable/trie_test.go
@@ -0,0 +1,34 @@
+package routingtable
+
+import (
+	"testing"
+)
+
+func TestGetBitUint32(t *testing.T) {
+	tests := []struct {
+		name     string
+		input    uint32
+		offset   uint8
+		expected bool
+	}{
+		{
+			name:     "test 1",
+			input:    167772160, // 10.0.0.0
+			offset:   8,
+			expected: false,
+		},
+		{
+			name:     "test 2",
+			input:    184549376, // 11.0.0.0
+			offset:   8,
+			expected: true,
+		},
+	}
+
+	for _, test := range tests {
+		b := getBitUint32(test.input, test.offset)
+		if b != test.expected {
+			t.Errorf("%s: Unexpected failure: Bit %d of %d is %v. Expected %v", test.name, test.offset, test.input, b, test.expected)
+		}
+	}
+}