diff --git a/net/helper.go b/net/helper.go index 4f7319cddfd92ef975819b143e4c03488ec22fb8..4b82b2425fdc199023c675c904e92cd6bcce27b3 100644 --- a/net/helper.go +++ b/net/helper.go @@ -4,5 +4,6 @@ import "net" // IPv4ToUint32 converts an `net.IP` to an uint32 interpretation func IPv4ToUint32(ip net.IP) uint32 { + ip = ip.To4() return uint32(ip[3]) + uint32(ip[2])<<8 + uint32(ip[1])<<16 + uint32(ip[0])<<24 } diff --git a/net/helper_test.go b/net/helper_test.go index 7a267c3c9e26715bd9b14e3579265e2057aac8db..d5a038cbb6bd2683b50ba50c658fae797e46d39c 100644 --- a/net/helper_test.go +++ b/net/helper_test.go @@ -3,6 +3,8 @@ package net import ( "testing" + "net" + "github.com/stretchr/testify/assert" ) @@ -23,6 +25,10 @@ func TestIPv4ToUint32(t *testing.T) { input: []byte{172, 24, 5, 1}, expected: 2887255297, }, + { + input: net.ParseIP("172.24.5.1"), + expected: 2887255297, + }, } for _, test := range tests { diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index 7681a127f17d6e4229d3ea79af22c55665a31831..d4c472066583d2c0190b64ef60b98f54a61b981e 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -724,7 +724,9 @@ func (fsm *FSM) established() int { Address: tnet.IPv4ToUint32(fsm.remote), } - clientOptions := routingtable.ClientOptions{} + clientOptions := routingtable.ClientOptions{ + BestOnly: true, + } if fsm.capAddPathSend { fsm.updateSender = newUpdateSenderAddPath(fsm) fsm.adjRIBOut = adjRIBOutAddPath.New(n) diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index 0f2096aa9d5c5d89b14dc3da9fb33b9480c49113..a5d05887f026cf336e3792e405fa7c456eae6e74 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -65,8 +65,8 @@ func (u *UpdateSender) AddPath(pfx net.Prefix, p *route.Path) error { // RemovePath withdraws prefix `pfx` from a peer func (u *UpdateSender) RemovePath(pfx net.Prefix, p *route.Path) bool { - log.Warningf("BGP Update Sender: RemovePath not implemented") - return false + err := withDrawPrefixes(u.fsm.con, pfx) + return err == nil } // UpdateNewClient does nothing diff --git a/protocols/bgp/server/update_sender_add_path.go b/protocols/bgp/server/update_sender_add_path.go index f63f485e738f0540666bea9e624163ca422e72f8..e7b46577c594c5b5f84e115981ac560777899d11 100644 --- a/protocols/bgp/server/update_sender_add_path.go +++ b/protocols/bgp/server/update_sender_add_path.go @@ -66,8 +66,8 @@ func (u *UpdateSenderAddPath) AddPath(pfx net.Prefix, p *route.Path) error { // RemovePath withdraws prefix `pfx` from a peer func (u *UpdateSenderAddPath) RemovePath(pfx net.Prefix, p *route.Path) bool { - log.Warningf("BGP Update Sender: RemovePath not implemented") - return false + err := withDrawPrefixesAddPath(u.fsm.con, pfx, p) + return err == nil } // UpdateNewClient does nothing diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go new file mode 100644 index 0000000000000000000000000000000000000000..3769c63fa1dd5d9a59f200aadff4b945fae61d20 --- /dev/null +++ b/protocols/bgp/server/withdraw.go @@ -0,0 +1,68 @@ +package server + +import ( + "errors" + "io" + + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/route" +) + +// withDrawPrefixes generates a BGPUpdate message and write it to the given +// io.Writer. +func withDrawPrefixes(out io.Writer, prefixes ...net.Prefix) error { + if len(prefixes) < 1 { + return nil + } + var rootNLRI *packet.NLRI + var currentNLRI *packet.NLRI + for _, pfx := range prefixes { + if rootNLRI == nil { + rootNLRI = &packet.NLRI{ + IP: pfx.Addr(), + Pfxlen: pfx.Pfxlen(), + } + currentNLRI = rootNLRI + } else { + currentNLRI.Next = &packet.NLRI{ + IP: pfx.Addr(), + Pfxlen: pfx.Pfxlen(), + } + currentNLRI = currentNLRI.Next + } + } + update := &packet.BGPUpdate{ + WithdrawnRoutes: rootNLRI, + } + data, err := update.SerializeUpdate() + if err != nil { + return err + } + _, err = out.Write(data) + return err +} + +// withDrawPrefixesAddPath generates a BGPUpdateAddPath message and write it to the given +// io.Writer. +func withDrawPrefixesAddPath(out io.Writer, pfx net.Prefix, p *route.Path) error { + if p.Type != route.BGPPathType { + return errors.New("wrong path type, expected BGPPathType") + } + if p.BGPPath == nil { + return errors.New("got nil BGPPath") + } + update := &packet.BGPUpdateAddPath{ + WithdrawnRoutes: &packet.NLRIAddPath{ + PathIdentifier: p.BGPPath.PathIdentifier, + IP: pfx.Addr(), + Pfxlen: pfx.Pfxlen(), + }, + } + data, err := update.SerializeUpdate() + if err != nil { + return err + } + _, err = out.Write(data) + return err +} diff --git a/protocols/bgp/server/withdraw_test.go b/protocols/bgp/server/withdraw_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0df94f75259aa446af0d78d1a65aa9ea7a4c67b2 --- /dev/null +++ b/protocols/bgp/server/withdraw_test.go @@ -0,0 +1,115 @@ +package server + +import ( + "testing" + + "errors" + + "bytes" + + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/stretchr/testify/assert" +) + +func TestWithDrawPrefixes(t *testing.T) { + testcases := []struct { + Name string + Prefix []net.Prefix + Expected []byte + ExpectedError error + }{ + { + Name: "One withdraw", + Prefix: []net.Prefix{net.NewPfx(1413010532, 24)}, + Expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker + 0x00, 0x1b, // BGP Message Length + 0x02, // BGP Message Type == Update + 0x00, 0x04, // WithDraw Octet length + 0x18, // Prefix Length + 0x54, 0x38, 0xd4, // Prefix, + 0x00, 0x00, // Total Path Attribute Length + }, + ExpectedError: nil, + }, + { + Name: "two withdraws", + Prefix: []net.Prefix{net.NewPfx(1413010532, 24), net.NewPfx(1413010534, 25)}, + Expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker + 0x00, 0x20, // BGP Message Length + 0x02, // BGP Message Type == Update + 0x00, 0x09, // WithDraw Octet length + 0x18, // Prefix Length first + 0x54, 0x38, 0xd4, // Prefix, + 0x19, // Prefix Length second + 0x54, 0x38, 0xd4, 0x66, // Prefix, + 0x00, 0x00, // Total Path Attribute Length + }, + ExpectedError: nil, + }, + } + for _, tc := range testcases { + buf := bytes.NewBuffer([]byte{}) + err := withDrawPrefixes(buf, tc.Prefix...) + assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name) + assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name) + } +} + +func TestWithDrawPrefixesAddPath(t *testing.T) { + testcases := []struct { + Name string + Prefix net.Prefix + Path *route.Path + Expected []byte + ExpectedError error + }{ + { + Name: "Normal withdraw", + Prefix: net.NewPfx(1413010532, 24), + Path: &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + PathIdentifier: 1, + }, + }, + Expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker + 0x00, 0x1f, // BGP Message Length + 0x02, // BGP Message Type == Update + 0x00, 0x08, // WithDraw Octet length + 0x00, 0x00, 0x00, 0x01, // NLRI Path Identifier + 0x18, // Prefix Length + 0x54, 0x38, 0xd4, // Prefix, + 0x00, 0x00, // Total Path Attribute Length + }, + ExpectedError: nil, + }, + { + Name: "Non bgp withdraw", + Prefix: net.NewPfx(1413010532, 24), + Path: &route.Path{ + Type: route.StaticPathType, + }, + Expected: []byte{}, + ExpectedError: errors.New("wrong path type, expected BGPPathType"), + }, + { + Name: "Nil BGPPathType", + Prefix: net.NewPfx(1413010532, 24), + Path: &route.Path{ + Type: route.BGPPathType, + }, + Expected: []byte{}, + ExpectedError: errors.New("got nil BGPPath"), + }, + } + for _, tc := range testcases { + buf := bytes.NewBuffer([]byte{}) + err := withDrawPrefixesAddPath(buf, tc.Prefix, tc.Path) + assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name) + assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name) + } +} diff --git a/route/path.go b/route/path.go index 6cfaf993303e727ddad5484c23683dc893f1d2ff..7c0edb7e11443fcad6f2bb0024794c32036fb629 100644 --- a/route/path.go +++ b/route/path.go @@ -53,19 +53,7 @@ func (p *Path) Equal(q *Path) bool { if p == nil || q == nil { return false } - - if p.Type != q.Type { - return false - } - - switch p.Type { - case BGPPathType: - if *p.BGPPath != *q.BGPPath { - return false - } - } - - return true + return p.Compare(q) == 0 } // PathsDiff gets the list of elements contained by a but not b diff --git a/routingtable/client_manager.go b/routingtable/client_manager.go index ee0275c3f2358ae96b4e297b17b540e29139bf90..20d848b2c090a4c311cb4a81fc8c1c4697cf5891 100644 --- a/routingtable/client_manager.go +++ b/routingtable/client_manager.go @@ -63,6 +63,8 @@ func (c *ClientManager) RegisterWithOptions(client RouteTableClient, opt ClientO // Unregister unregisters a client func (c *ClientManager) Unregister(client RouteTableClient) { + c.mu.Lock() + defer c.mu.Unlock() if _, ok := c.clients[client]; !ok { return } @@ -71,6 +73,8 @@ func (c *ClientManager) Unregister(client RouteTableClient) { // Clients returns a list of registered clients func (c *ClientManager) Clients() []RouteTableClient { + c.mu.RLock() + defer c.mu.RUnlock() ret := make([]RouteTableClient, 0) for rtc := range c.clients { ret = append(ret, rtc) diff --git a/routingtable/locRIB/loc_rib.go b/routingtable/locRIB/loc_rib.go index b5c0b522ac4866cedba8de80213afa3564758905..584954a954e394bf7f859693b87ec827529a4b05 100644 --- a/routingtable/locRIB/loc_rib.go +++ b/routingtable/locRIB/loc_rib.go @@ -120,7 +120,7 @@ func (a *LocRIB) removePathsFromClients(oldRoute *route.Route, newRoute *route.R withdraw := route.PathsDiff(oldRoute.Paths()[0:oldPathsLimit], newRoute.Paths()[0:newPathsLimit]) for _, p := range withdraw { - client.RemovePath(newRoute.Prefix(), p) + client.RemovePath(oldRoute.Prefix(), p) } } } @@ -130,18 +130,18 @@ func (a *LocRIB) removePathsFromClients(oldRoute *route.Route, newRoute *route.R func (a *LocRIB) ContainsPfxPath(pfx net.Prefix, p *route.Path) bool { a.mu.RLock() defer a.mu.RUnlock() - + r := a.rt.Get(pfx) if r == nil { return false } - + for _, path := range r.Paths() { if path.Compare(p) == 0 { return true } } - + return false }