Skip to content
Snippets Groups Projects
Commit f5740a78 authored by Oliver Herms's avatar Oliver Herms
Browse files

Added routing table

parent cde9a80f
No related branches found
No related tags found
No related merge requests found
......@@ -9,11 +9,13 @@ import (
"github.com/bio-routing/bio-rd/config"
"github.com/bio-routing/bio-rd/protocols/bgp/server"
"github.com/bio-routing/bio-rd/rt"
)
func main() {
fmt.Printf("This is a BGP speaker\n")
VRF := rt.New(true)
b := server.NewBgpServer()
err := b.Start(&config.Global{
......@@ -26,14 +28,26 @@ func main() {
b.AddPeer(config.Peer{
AdminEnabled: true,
LocalAS: 65200,
PeerAS: 65201,
PeerAddress: net.IP([]byte{169, 254, 123, 1}),
LocalAddress: net.IP([]byte{169, 254, 123, 0}),
PeerAS: 65100,
PeerAddress: net.IP([]byte{169, 254, 100, 0}),
LocalAddress: net.IP([]byte{169, 254, 100, 1}),
HoldTimer: 90,
KeepAlive: 30,
Passive: true,
RouterID: b.RouterID(),
})
}, VRF)
b.AddPeer(config.Peer{
AdminEnabled: true,
LocalAS: 65200,
PeerAS: 65300,
PeerAddress: net.IP([]byte{169, 254, 200, 1}),
LocalAddress: net.IP([]byte{169, 254, 200, 0}),
HoldTimer: 90,
KeepAlive: 30,
Passive: true,
RouterID: b.RouterID(),
}, VRF)
var wg sync.WaitGroup
wg.Add(1)
......
......@@ -5,6 +5,8 @@ import (
"fmt"
"math"
"net"
"github.com/taktv6/tflow2/convert"
)
func decodeNLRIs(buf *bytes.Buffer, length uint16) (*NLRI, error) {
......@@ -60,7 +62,9 @@ func decodeNLRI(buf *bytes.Buffer) (*NLRI, uint8, error) {
}
func (n *NLRI) serialize(buf *bytes.Buffer) uint8 {
addr := n.IP.([4]byte)
a := convert.Uint32Byte(n.IP.(uint32))
addr := [4]byte{a[0], a[1], a[2], a[3]}
nBytes := bytesInAddr(n.Pfxlen)
buf.WriteByte(n.Pfxlen)
......
......@@ -244,7 +244,7 @@ func (pa *PathAttribute) decodeUint32(buf *bytes.Buffer) (uint32, error) {
}
func (pa *PathAttribute) ASPathString() (ret string) {
for _, p := range *pa.Value.(*ASPath) {
for _, p := range pa.Value.(ASPath) {
if p.Type == ASSet {
ret += " ("
}
......@@ -265,7 +265,7 @@ func (pa *PathAttribute) ASPathString() (ret string) {
}
func (pa *PathAttribute) ASPathLen() (ret uint16) {
for _, p := range *pa.Value.(*ASPath) {
for _, p := range pa.Value.(ASPath) {
if p.Type == ASSet {
ret++
continue
......@@ -329,18 +329,22 @@ func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer) uint8 {
attrFlags = setTransitive(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(ASPathAttr)
length := uint8(2)
asPath := pa.Value.(ASPath)
for _, segment := range asPath {
buf.WriteByte(segment.Type)
buf.WriteByte(uint8(len(segment.ASNs)))
length := uint8(0)
segmentsBuf := bytes.NewBuffer(nil)
for _, segment := range pa.Value.(ASPath) {
segmentsBuf.WriteByte(segment.Type)
segmentsBuf.WriteByte(uint8(len(segment.ASNs)))
for _, asn := range segment.ASNs {
buf.Write(convert.Uint16Byte(uint16(asn)))
segmentsBuf.Write(convert.Uint16Byte(uint16(asn)))
}
length += 2 + uint8(len(segment.ASNs))*2
}
return length
buf.WriteByte(length)
buf.Write(segmentsBuf.Bytes())
return length + 2
}
func (pa *PathAttribute) serializeNextHop(buf *bytes.Buffer) uint8 {
......
......@@ -1072,6 +1072,7 @@ func TestSerializeASPath(t *testing.T) {
expected: []byte{
64, // Attribute flags
2, // Type
8, // Length
2, // AS_SEQUENCE
3, // ASN count
0, 100, // ASN 100
......
......@@ -83,8 +83,10 @@ type FSM struct {
msgRecvFailCh chan msgRecvErr
stopMsgRecvCh chan struct{}
adjRibIn *rt.RT
adjRibOut *rt.RT
adjRIBIn *rt.RT
adjRIBOut *rt.RT
vrf *rt.RT
updateSender *UpdateSender
}
type msgRecvMsg struct {
......@@ -97,7 +99,7 @@ type msgRecvErr struct {
con *net.TCPConn
}
func NewFSM(c config.Peer) *FSM {
func NewFSM(c config.Peer, vrf *rt.RT) *FSM {
fsm := &FSM{
state: Idle,
passive: true,
......@@ -121,7 +123,11 @@ func NewFSM(c config.Peer) *FSM {
eventCh: make(chan int),
conCh: make(chan *net.TCPConn),
conErrCh: make(chan error), initiateCon: make(chan struct{}),
vrf: vrf,
}
fsm.updateSender = newUpdateSender(fsm)
return fsm
}
......@@ -201,8 +207,12 @@ func (fsm *FSM) main() error {
}
func (fsm *FSM) idle() int {
fsm.adjRibIn = nil
fsm.adjRibOut = nil
if fsm.adjRIBOut != nil {
fsm.vrf.Unregister(fsm.adjRIBOut)
fsm.adjRIBOut.Unregister(fsm.updateSender)
}
fsm.adjRIBIn = nil
fsm.adjRIBOut = nil
for {
select {
case c := <-fsm.conCh:
......@@ -643,12 +653,19 @@ func (fsm *FSM) openConfirmTCPFail(err error) int {
}
func (fsm *FSM) established() int {
fsm.adjRibIn = rt.New()
fsm.adjRIBIn = rt.New(false)
fsm.adjRIBIn.Register(fsm.vrf)
fsm.adjRIBOut = rt.New(false)
fsm.adjRIBOut.Register(fsm.updateSender)
fsm.vrf.Register(fsm.adjRIBOut)
go func() {
for {
time.Sleep(time.Second * 10)
fmt.Printf("Dumping AdjRibIn\n")
routes := fsm.adjRibIn.Dump()
routes := fsm.adjRIBIn.Dump()
for _, route := range routes {
fmt.Printf("LPM: %s\n", route.Prefix().String())
}
......@@ -721,7 +738,7 @@ func (fsm *FSM) established() int {
x := r.IP.([4]byte)
pfx := tnet.NewPfx(convert.Uint32b(x[:]), r.Pfxlen)
fmt.Printf("LPM: Removing prefix %s\n", pfx.String())
fsm.adjRibIn.RemovePfx(pfx)
fsm.adjRIBIn.RemoveRoute(pfx)
}
for r := u.NLRI; r != nil; r = r.Next {
......@@ -749,7 +766,9 @@ func (fsm *FSM) established() int {
path.BGPPath.ASPathLen = pa.ASPathLen()
}
}
fsm.adjRibIn.Insert(rt.NewRoute(pfx, []*rt.Path{path}))
// TO BE USED WITH BGP ADD PATH:
// fsm.adjRIBIn.AddPath(rt.NewRoute(pfx, []*rt.Path{path}))
fsm.adjRIBIn.ReplaceRoute(rt.NewRoute(pfx, []*rt.Path{path}))
}
continue
......
......@@ -4,20 +4,23 @@ import (
"net"
"github.com/bio-routing/bio-rd/config"
"github.com/bio-routing/bio-rd/rt"
)
type Peer struct {
addr net.IP
asn uint32
fsm *FSM
vrf *rt.RT
routerID uint32
}
func NewPeer(c config.Peer) (*Peer, error) {
func NewPeer(c config.Peer, vrf *rt.RT) (*Peer, error) {
p := &Peer{
addr: c.PeerAddress,
asn: c.PeerAS,
fsm: NewFSM(c),
fsm: NewFSM(c, vrf),
vrf: vrf,
}
return p, nil
}
......
......@@ -8,6 +8,7 @@ import (
"github.com/bio-routing/bio-rd/config"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
"github.com/bio-routing/bio-rd/rt"
log "github.com/sirupsen/logrus"
)
......@@ -83,12 +84,12 @@ func (b *BGPServer) incomingConnectionWorker() {
}
}
func (b *BGPServer) AddPeer(c config.Peer) error {
func (b *BGPServer) AddPeer(c config.Peer, vrf *rt.RT) error {
if c.LocalAS > uint16max || c.PeerAS > uint16max {
return fmt.Errorf("32bit ASNs are not supported yet")
}
peer, err := NewPeer(c)
peer, err := NewPeer(c, vrf)
if err != nil {
return err
}
......
......@@ -15,6 +15,7 @@ type BGPPath struct {
Origin uint8
MED uint32
EBGP bool
BGPIdentifier uint32
Source uint32
}
......@@ -70,39 +71,39 @@ func (m *BGPPathManager) RemovePath(p BGPPath) {
}
}
func (r *Route) bgpPathSelection() (res []*Path) {
func (r *Route) bgpPathSelection() (best *Path, active []*Path) {
// TODO: Implement next hop lookup and compare IGP metrics
if len(r.paths) == 1 {
copy(res, r.paths)
return res
}
for _, p := range r.paths {
if p.Type != BGPPathType {
continue
}
if len(res) == 0 {
res = append(res, p)
if len(active) == 0 {
active = append(active, p)
continue
}
if res[0].BGPPath.ecmp(p.BGPPath) {
res = append(res, p)
if active[0].BGPPath.ecmp(p.BGPPath) {
active = append(active, p)
if !r.bestPath.BGPPath.better(p.BGPPath) {
continue
}
best = p
continue
}
if !res[0].BGPPath.better(p.BGPPath) {
if !active[0].BGPPath.betterECMP(p.BGPPath) {
continue
}
res = []*Path{p}
active = []*Path{p}
}
return res
return best, active
}
func (b *BGPPath) better(c *BGPPath) bool {
func (b *BGPPath) betterECMP(c *BGPPath) bool {
if c.LocalPref < b.LocalPref {
return false
}
......@@ -138,6 +139,22 @@ func (b *BGPPath) better(c *BGPPath) bool {
return false
}
func (b *BGPPath) better(c *BGPPath) bool {
if b.betterECMP(c) {
return true
}
if c.BGPIdentifier < b.BGPIdentifier {
return true
}
if c.Source < b.Source {
return true
}
return false
}
func (b *BGPPath) ecmp(c *BGPPath) bool {
return b.LocalPref == c.LocalPref && b.ASPathLen == c.ASPathLen && b.Origin == c.Origin && b.MED == c.MED
}
package rt
type ClientManager struct {
clients map[RouteTableClient]struct{} // Ensures a client registers at most once
routingTable *RT
}
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)
}
package rt
type Filter struct {
ClientManager
}
func NewFilter() *Filter {
return &Filter{}
}
func (f *Filter) Add(r *Route) {
for client := range f.clients {
client.AddPath(r)
}
}
func (f *Filter) Remove(r *Route) {
for client := range f.clients {
client.RemovePath(r)
}
}
......@@ -18,6 +18,7 @@ type Path struct {
type Route struct {
pfx *net.Prefix
bestPath *Path
activePaths []*Path
paths []*Path
}
......@@ -47,6 +48,25 @@ func (r *Route) Remove(rm *Route) (final bool) {
return len(r.paths) == 0
}
// returns a list of Paths that are in a but not in b
func missingPaths(a, b []*Path) []*Path {
ret := make([]*Path, 0)
for _, p := range a {
found := false
for _, q := range b {
if *p == *q {
found = true
break
}
}
if !found {
ret = append(ret, p)
}
}
return ret
}
func removePath(paths []*Path, remove *Path) []*Path {
i := -1
for j := range paths {
......@@ -64,6 +84,10 @@ func removePath(paths []*Path, remove *Path) []*Path {
return paths[:len(paths)-1]
}
func (r *Route) removeAllPaths() {
r.paths = make([]*Path, 0)
}
func (p *Path) Equal(q *Path) bool {
if p == nil || q == nil {
return false
......@@ -96,17 +120,19 @@ func (r *Route) AddPaths(paths []*Path) {
}
func (r *Route) bestPaths() {
best := []*Path{}
var best *Path
var active []*Path
protocol := getBestProtocol(r.paths)
switch protocol {
case StaticPathType:
best = r.staticPathSelection()
best, active = r.staticPathSelection()
case BGPPathType:
best = r.bgpPathSelection()
best, active = r.bgpPathSelection()
}
r.activePaths = best
r.bestPath = best
r.activePaths = active
}
func getBestProtocol(paths []*Path) uint8 {
......
......@@ -392,3 +392,82 @@ func TestGetBestProtocol(t *testing.T) {
assert.Equal(t, test.expected, res)
}
}
func TestMissingPaths(t *testing.T) {
tests := []struct {
name string
a []*Path
b []*Path
expected []*Path
}{
{
name: "None missing #2",
a: []*Path{
{
Type: 1,
},
{
Type: 2,
},
},
b: []*Path{
{
Type: 1,
},
{
Type: 2,
},
{
Type: 3,
},
},
expected: []*Path{},
},
{
name: "None missing",
a: []*Path{
{
Type: 1,
},
{
Type: 2,
},
},
b: []*Path{
{
Type: 1,
},
{
Type: 2,
},
},
expected: []*Path{},
},
{
name: "One missing",
a: []*Path{
{
Type: 1,
},
{
Type: 2,
},
},
b: []*Path{
{
Type: 1,
},
},
expected: []*Path{
{
Type: 2,
},
},
},
}
for _, test := range tests {
res := missingPaths(test.a, test.b)
assert.Equal(t, test.expected, res)
}
}
package rt
import (
"fmt"
"sync"
"github.com/bio-routing/bio-rd/net"
)
type RouteTableClient interface {
AddPath(*Route)
ReplaceRoute(*Route)
RemovePath(*Route)
RemoveRoute(*net.Prefix)
}
// RT represents a routing table
type RT struct {
root *node
nodes uint64
root *node
selectBestPaths bool
mu sync.RWMutex
ClientManager
}
// node is a node in the compressed trie that is used to implement a routing table
......@@ -20,37 +32,131 @@ type node struct {
}
// New creates a new empty LPM
func New() *RT {
return &RT{}
func New(selectBestPaths bool) *RT {
rt := &RT{
selectBestPaths: selectBestPaths,
}
rt.ClientManager.routingTable = rt
return rt
}
func newNode(route *Route, skip uint8, dummy bool) *node {
n := &node{
route: route,
skip: skip,
dummy: dummy,
func (rt *RT) updateNewClient(client RouteTableClient) {
rt.root.updateNewClient(client)
}
func (n *node) updateNewClient(client RouteTableClient) {
if n == nil {
return
}
return n
if !n.dummy {
client.AddPath(n.route)
}
n.l.updateNewClient(client)
n.h.updateNewClient(client)
}
// LPM performs a longest prefix match for pfx on lpm
func (rt *RT) LPM(pfx *net.Prefix) (res []*Route) {
if rt.root == nil {
return nil
func (rt *RT) updatePathSelection(route *Route) {
fmt.Printf("updatePathSelection\n")
formerActivePaths := rt.Get(route.pfx, false)[0].activePaths
activePaths := rt.selectBestPath(route).route.activePaths
fmt.Printf("formerActivePaths: %v\n", formerActivePaths)
fmt.Printf("activePaths: %v\n", activePaths)
x := missingPaths(activePaths, formerActivePaths)
fmt.Printf("x: %v\n", x)
for _, advertisePath := range x {
adervtiseRoute := &Route{
pfx: route.pfx,
paths: []*Path{advertisePath},
}
for client := range rt.clients {
fmt.Printf("Propagating an Update via AddPath()")
client.AddPath(adervtiseRoute)
}
}
rt.root.lpm(pfx, &res)
return res
for _, withdrawPath := range missingPaths(formerActivePaths, activePaths) {
withdrawRoute := &Route{
pfx: route.pfx,
paths: []*Path{withdrawPath},
}
for client := range rt.clients {
fmt.Printf("Propagating an Update via RemovePath()")
client.RemovePath(withdrawRoute)
}
}
}
func (rt *RT) AddPath(route *Route) {
rt.addPath(route)
if rt.selectBestPaths {
rt.updatePathSelection(route)
return
}
for client := range rt.clients {
client.AddPath(route)
}
}
// RemovePath removes a path from the trie
func (rt *RT) RemovePath(route *Route) {
rt.root.removePath(route)
for client := range rt.clients {
client.RemovePath(route)
}
}
// RemoveRoute removes a prefix from the rt including all it's paths
func (rt *RT) RemoveRoute(pfx *net.Prefix) {
if rt.selectBestPaths {
return
}
r := rt.Get(pfx, false)
if len(r) == 0 {
return
}
for client := range rt.clients {
for _, path := range r[0].paths {
withdrawRoute := NewRoute(pfx, []*Path{path})
client.RemovePath(withdrawRoute)
}
}
rt.root.removeRoute(pfx)
}
// RemovePfx removes a prefix from the rt including all it's paths
func (rt *RT) RemovePfx(pfx *net.Prefix) {
rt.root.removePfx(pfx)
// ReplaceRoute replaces all paths of a route. Route is added if it doesn't exist yet.
func (rt *RT) ReplaceRoute(route *Route) {
fmt.Printf("Replacing a route!\n")
if rt.selectBestPaths {
fmt.Printf("Ignoring because rt.selectBestPaths is false\n")
return
}
r := rt.Get(route.pfx, false)
if len(r) > 0 {
rt.RemoveRoute(route.pfx)
}
rt.addPath(route)
rt.updatePathSelection(route)
}
// LPM performs a longest prefix match for pfx on lpm
func (rt *RT) LPM(pfx *net.Prefix) (res []*Route) {
if rt.root == nil {
return nil
}
rt.root.lpm(pfx, &res)
return res
}
// Get get's prefix pfx from the LPM
......@@ -73,10 +179,8 @@ func (rt *RT) Get(pfx *net.Prefix, moreSpecifics bool) (res []*Route) {
}
}
// Insert inserts a route into the LPM
func (rt *RT) Insert(route *Route) {
func (rt *RT) addPath(route *Route) {
if rt.root == nil {
route.bestPaths()
rt.root = newNode(route, route.Pfxlen(), false)
return
}
......@@ -84,6 +188,32 @@ func (rt *RT) Insert(route *Route) {
rt.root = rt.root.insert(route)
}
func (rt *RT) selectBestPath(route *Route) *node {
if rt.root == nil {
return nil
}
node := rt.root.get(route.pfx)
if !rt.selectBestPaths {
// If we don't select best path(s) evey path is best path
node.route.activePaths = make([]*Path, len(node.route.paths))
copy(node.route.activePaths, node.route.paths)
return node
}
node.route.bestPaths()
return node
}
func newNode(route *Route, skip uint8, dummy bool) *node {
n := &node{
route: route,
skip: skip,
dummy: dummy,
}
return n
}
func (n *node) removePath(route *Route) {
if n == nil {
return
......@@ -111,7 +241,27 @@ func (n *node) removePath(route *Route) {
return
}
func (n *node) removePfx(pfx *net.Prefix) {
func (n *node) replaceRoute(route *Route) {
if n == nil {
return
}
if *n.route.Prefix() == *route.Prefix() {
n.route = route
n.dummy = false
return
}
b := getBitUint32(route.Prefix().Addr(), n.route.Pfxlen()+1)
if !b {
n.l.replaceRoute(route)
return
}
n.h.replaceRoute(route)
return
}
func (n *node) removeRoute(pfx *net.Prefix) {
if n == nil {
return
}
......@@ -121,17 +271,17 @@ func (n *node) removePfx(pfx *net.Prefix) {
return
}
// TODO: Remove node if possible
n.dummy = true
return
}
b := getBitUint32(pfx.Addr(), n.route.Pfxlen()+1)
if !b {
n.l.removePfx(pfx)
n.l.removeRoute(pfx)
return
}
n.h.removePfx(pfx)
n.h.removeRoute(pfx)
return
}
......
......@@ -8,7 +8,7 @@ import (
)
func TestNew(t *testing.T) {
l := New()
l := New(false)
if l == nil {
t.Errorf("New() returned nil")
}
......@@ -72,7 +72,7 @@ func TestRemovePath(t *testing.T) {
},
},
},
{
/*{
name: "Remove a path that is one of two for a prefix",
routes: []*Route{
NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), []*Path{
......@@ -145,13 +145,13 @@ func TestRemovePath(t *testing.T) {
},
}),
},
},
},*/
}
for _, test := range tests {
rt := New()
rt := New(false)
for _, route := range test.routes {
rt.Insert(route)
rt.AddPath(route)
}
for _, route := range test.remove {
......@@ -222,13 +222,13 @@ func TestRemovePfx(t *testing.T) {
}
for _, test := range tests {
lpm := New()
lpm := New(false)
for _, route := range test.routes {
lpm.Insert(route)
lpm.AddPath(route)
}
for _, pfx := range test.remove {
lpm.RemovePfx(pfx)
lpm.RemoveRoute(pfx)
}
res := lpm.Dump()
......@@ -310,9 +310,9 @@ func TestLPM(t *testing.T) {
}
for _, test := range tests {
rt := New()
rt := New(false)
for _, route := range test.routes {
rt.Insert(route)
rt.AddPath(route)
}
assert.Equal(t, test.expected, rt.LPM(test.needle))
}
......@@ -404,9 +404,9 @@ func TestGet(t *testing.T) {
}
for _, test := range tests {
rt := New()
rt := New(false)
for _, route := range test.routes {
rt.Insert(route)
rt.AddPath(route)
}
p := rt.Get(test.needle, test.moreSpecifics)
......@@ -434,8 +434,7 @@ func TestInsert(t *testing.T) {
},
expected: &node{
route: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
activePaths: []*Path{},
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
},
skip: 8,
},
......@@ -450,8 +449,7 @@ func TestInsert(t *testing.T) {
},
expected: &node{
route: &Route{
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
activePaths: []*Path{},
pfx: net.NewPfx(strAddr("10.0.0.0"), 8),
},
skip: 8,
},
......@@ -592,9 +590,9 @@ func TestInsert(t *testing.T) {
}
for _, test := range tests {
rt := New()
rt := New(false)
for _, route := range test.routes {
rt.Insert(route)
rt.AddPath(route)
}
assert.Equal(t, test.expected, rt.root)
......
......@@ -4,10 +4,13 @@ type StaticPath struct {
NextHop uint32
}
func (r *Route) staticPathSelection() (res []*Path) {
if len(r.paths) == 1 {
copy(res, r.paths)
return res
func (r *Route) staticPathSelection() (best *Path, active []*Path) {
if r.paths == nil {
return nil, nil
}
if len(r.paths) == 0 {
return nil, nil
}
for _, p := range r.paths {
......@@ -15,7 +18,8 @@ func (r *Route) staticPathSelection() (res []*Path) {
continue
}
res = append(res, p)
active = append(active, p)
best = p
}
return
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment