diff --git a/main.go b/main.go index fbf3692f7b40265edd9b39c6d151809bd130b2e4..30aaa128fec913ee952a781a0a343f4635a71bf1 100644 --- a/main.go +++ b/main.go @@ -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) diff --git a/protocols/bgp/packet/nlri.go b/protocols/bgp/packet/nlri.go index b9ce4f2427c97c776d11cc257bb5ae2f3b5e6a1f..42c7a65e07af14b46719ef8630d42b79443184cd 100644 --- a/protocols/bgp/packet/nlri.go +++ b/protocols/bgp/packet/nlri.go @@ -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) diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go index be40ccf8ed2efbd0503bedc9d392ed3cd4cf118d..50c0cdb5e49d0ee0c3a4225e30ecfe9d8814c1f1 100644 --- a/protocols/bgp/packet/path_attributes.go +++ b/protocols/bgp/packet/path_attributes.go @@ -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 { diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index 052c40796d1b720fbba8c394dc3158a38725b369..247028cb85f8e9a350e929baa10b5a010e40731f 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -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 diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index 1d353675374d6f8f79fdce314357b92d1eb5013a..a65d957040feeb969c50c5b94ef08c31fc937c7d 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -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 diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index 02051838f9d3e496ff7e4dfe590786f3e7b193f1..4371d5d48641a6bb672a9d0e399c96225e9a9e50 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -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 } diff --git a/protocols/bgp/server/server.go b/protocols/bgp/server/server.go index 6bf46ac7634f684b5d060ea2e5ffd7417819e417..3603c164fe60508dbc0fa75b9c33588ec650742a 100644 --- a/protocols/bgp/server/server.go +++ b/protocols/bgp/server/server.go @@ -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 } diff --git a/rt/bgp.go b/rt/bgp.go index 7e71427dcf111932d199950eb3d93cedd77f296f..9fd3e56c0d92d8459a9375eb4020c8ef35ddc173 100644 --- a/rt/bgp.go +++ b/rt/bgp.go @@ -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 } diff --git a/rt/client_manager.go b/rt/client_manager.go new file mode 100644 index 0000000000000000000000000000000000000000..723b6e94da5e300f0dddd0d31e1ed487d77f1506 --- /dev/null +++ b/rt/client_manager.go @@ -0,0 +1,21 @@ +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) +} diff --git a/rt/filter.go b/rt/filter.go new file mode 100644 index 0000000000000000000000000000000000000000..f68a6d421451f850c3e65a7a2816a909608e5bf1 --- /dev/null +++ b/rt/filter.go @@ -0,0 +1,21 @@ +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) + } +} diff --git a/rt/route.go b/rt/route.go index f309d3c108241ab2e62d0c5536b6275b7a35ddf8..d271c914a99920a77515c64ddb04ac96a84893f4 100644 --- a/rt/route.go +++ b/rt/route.go @@ -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 { diff --git a/rt/route_test.go b/rt/route_test.go index 3bac4e87cf7907e57fce7af3ac8192370f048bec..7b381809d414d7c65f043dd403e6c5b5f6423825 100644 --- a/rt/route_test.go +++ b/rt/route_test.go @@ -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) + } +} diff --git a/rt/routing_table.go b/rt/routing_table.go index 6ce2785cdbc5bb6fdcbe2342b17e037ae2147180..9e03e03d8aaf9617983d138aabaf88235f4222aa 100644 --- a/rt/routing_table.go +++ b/rt/routing_table.go @@ -1,13 +1,25 @@ 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 } diff --git a/rt/routing_table_test.go b/rt/routing_table_test.go index 6a80752c9d9047bf94e7a2ff9de3e8c59ce6eaf6..093af87b8184fa90cc3fc20c926675574433c5b0 100644 --- a/rt/routing_table_test.go +++ b/rt/routing_table_test.go @@ -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) diff --git a/rt/static.go b/rt/static.go index 72720a6548427ce7d0a1d88fbd9039731f96698e..ae3e6bc35ea659c229f4ea8b960bc24a47f42275 100644 --- a/rt/static.go +++ b/rt/static.go @@ -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