diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go index 8aabec7f7c729cd23d47ed6abe517742f2583708..a8df09179d16d6154a6df2e8b0bbfe3ea7abd481 100644 --- a/protocols/bgp/packet/bgp.go +++ b/protocols/bgp/packet/bgp.go @@ -133,22 +133,6 @@ type BGPNotification struct { ErrorSubcode uint8 } -type BGPUpdate struct { - WithdrawnRoutesLen uint16 - WithdrawnRoutes *NLRI - TotalPathAttrLen uint16 - PathAttributes *PathAttribute - NLRI *NLRI -} - -type BGPUpdateAddPath struct { - WithdrawnRoutesLen uint16 - WithdrawnRoutes *NLRIAddPath - TotalPathAttrLen uint16 - PathAttributes *PathAttribute - NLRI *NLRIAddPath -} - type PathAttribute struct { Length uint16 Optional bool @@ -160,19 +144,6 @@ type PathAttribute struct { Next *PathAttribute } -type NLRI struct { - IP uint32 - Pfxlen uint8 - Next *NLRI -} - -type NLRIAddPath struct { - PathIdentifier uint32 - IP uint32 - Pfxlen uint8 - Next *NLRIAddPath -} - type Aggretator struct { Addr uint32 ASN uint16 diff --git a/protocols/bgp/packet/encoder.go b/protocols/bgp/packet/encoder.go index ac9bc4483199b81f4e8ecc25c6b91e3ec8361b9c..122bca972219f246346b263f666554a13f035b04 100644 --- a/protocols/bgp/packet/encoder.go +++ b/protocols/bgp/packet/encoder.go @@ -2,7 +2,6 @@ package packet import ( "bytes" - "fmt" "github.com/taktv6/tflow2/convert" ) @@ -62,121 +61,3 @@ func serializeHeader(buf *bytes.Buffer, length uint16, typ uint8) { buf.Write(convert.Uint16Byte(length)) buf.WriteByte(typ) } - -func (b *BGPUpdateAddPath) SerializeUpdate(opt *Options) ([]byte, error) { - budget := MaxLen - MinLen - buf := bytes.NewBuffer(nil) - - withdrawBuf := bytes.NewBuffer(nil) - for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { - nlriLen := int(withdraw.serialize(withdrawBuf)) - budget -= nlriLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - pathAttributesBuf := bytes.NewBuffer(nil) - for pa := b.PathAttributes; pa != nil; pa = pa.Next { - paLen := int(pa.serialize(pathAttributesBuf, opt)) - budget -= paLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - nlriBuf := bytes.NewBuffer(nil) - for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { - nlriLen := int(nlri.serialize(nlriBuf)) - budget -= nlriLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - withdrawnRoutesLen := withdrawBuf.Len() - if withdrawnRoutesLen > 65535 { - return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen) - } - - totalPathAttributesLen := pathAttributesBuf.Len() - if totalPathAttributesLen > 65535 { - return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen) - } - - totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19 - if totalLength > 4096 { - return nil, fmt.Errorf("Update too long: %d bytes", totalLength) - } - - serializeHeader(buf, uint16(totalLength), UpdateMsg) - - buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen))) - buf.Write(withdrawBuf.Bytes()) - - buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen))) - buf.Write(pathAttributesBuf.Bytes()) - - buf.Write(nlriBuf.Bytes()) - - return buf.Bytes(), nil -} - -func (b *BGPUpdate) SerializeUpdate(opt *Options) ([]byte, error) { - budget := MaxLen - MinLen - buf := bytes.NewBuffer(nil) - - withdrawBuf := bytes.NewBuffer(nil) - for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { - nlriLen := int(withdraw.serialize(withdrawBuf)) - budget -= nlriLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - pathAttributesBuf := bytes.NewBuffer(nil) - for pa := b.PathAttributes; pa != nil; pa = pa.Next { - paLen := int(pa.serialize(pathAttributesBuf, opt)) - budget -= paLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - nlriBuf := bytes.NewBuffer(nil) - for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { - nlriLen := int(nlri.serialize(nlriBuf)) - budget -= nlriLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - withdrawnRoutesLen := withdrawBuf.Len() - if withdrawnRoutesLen > 65535 { - return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen) - } - - totalPathAttributesLen := pathAttributesBuf.Len() - if totalPathAttributesLen > 65535 { - return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen) - } - - totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19 - if totalLength > 4096 { - return nil, fmt.Errorf("Update too long: %d bytes", totalLength) - } - - serializeHeader(buf, uint16(totalLength), UpdateMsg) - - buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen))) - buf.Write(withdrawBuf.Bytes()) - - buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen))) - buf.Write(pathAttributesBuf.Bytes()) - - buf.Write(nlriBuf.Bytes()) - - return buf.Bytes(), nil -} diff --git a/protocols/bgp/packet/nlri.go b/protocols/bgp/packet/nlri.go index 6de92a6cd59ff167d87efbaf5d197fb46c00aaca..a9ed83b94946ea6415672970668e58fd5c99aa71 100644 --- a/protocols/bgp/packet/nlri.go +++ b/protocols/bgp/packet/nlri.go @@ -9,6 +9,13 @@ import ( "github.com/taktv6/tflow2/convert" ) +type NLRI struct { + PathIdentifier uint32 + IP uint32 + Pfxlen uint8 + Next *NLRI +} + func decodeNLRIs(buf *bytes.Buffer, length uint16) (*NLRI, error) { var ret *NLRI var eol *NLRI @@ -65,7 +72,7 @@ func (n *NLRI) serialize(buf *bytes.Buffer) uint8 { a := convert.Uint32Byte(n.IP) addr := [4]byte{a[0], a[1], a[2], a[3]} - nBytes := bytesInAddr(n.Pfxlen) + nBytes := BytesInAddr(n.Pfxlen) buf.WriteByte(n.Pfxlen) buf.Write(addr[:nBytes]) @@ -73,11 +80,11 @@ func (n *NLRI) serialize(buf *bytes.Buffer) uint8 { return nBytes + 1 } -func (n *NLRIAddPath) serialize(buf *bytes.Buffer) uint8 { +func (n *NLRI) serializeAddPath(buf *bytes.Buffer) uint8 { a := convert.Uint32Byte(n.IP) addr := [4]byte{a[0], a[1], a[2], a[3]} - nBytes := bytesInAddr(n.Pfxlen) + nBytes := BytesInAddr(n.Pfxlen) buf.Write(convert.Uint32Byte(n.PathIdentifier)) buf.WriteByte(n.Pfxlen) @@ -86,6 +93,7 @@ func (n *NLRIAddPath) serialize(buf *bytes.Buffer) uint8 { return nBytes + 1 } -func bytesInAddr(pfxlen uint8) uint8 { +// BytesInAddr gets the amount of bytes needed to encode an NLRI of prefix length pfxlen +func BytesInAddr(pfxlen uint8) uint8 { return uint8(math.Ceil(float64(pfxlen) / 8)) } diff --git a/protocols/bgp/packet/options.go b/protocols/bgp/packet/options.go index b2d35bc60cbe17490f94314c28da26de9a19e40a..612e68935c59108d84915dcd78d505bf5e42efb5 100644 --- a/protocols/bgp/packet/options.go +++ b/protocols/bgp/packet/options.go @@ -2,4 +2,5 @@ package packet type Options struct { Supports4OctetASN bool + AddPathRX bool } diff --git a/protocols/bgp/packet/update.go b/protocols/bgp/packet/update.go new file mode 100644 index 0000000000000000000000000000000000000000..001f2c1cdba2227d8bdb179789a9f0bebc03bb23 --- /dev/null +++ b/protocols/bgp/packet/update.go @@ -0,0 +1,145 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/taktv6/tflow2/convert" +) + +type BGPUpdate struct { + WithdrawnRoutesLen uint16 + WithdrawnRoutes *NLRI + TotalPathAttrLen uint16 + PathAttributes *PathAttribute + NLRI *NLRI +} + +func (b *BGPUpdate) SerializeUpdate(opt *Options) ([]byte, error) { + budget := MaxLen - MinLen + nlriLen := 0 + buf := bytes.NewBuffer(nil) + + withdrawBuf := bytes.NewBuffer(nil) + for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { + if opt.AddPathRX { + nlriLen = int(withdraw.serializeAddPath(withdrawBuf)) + } else { + nlriLen = int(withdraw.serialize(withdrawBuf)) + } + + budget -= nlriLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + pathAttributesBuf := bytes.NewBuffer(nil) + for pa := b.PathAttributes; pa != nil; pa = pa.Next { + paLen := int(pa.serialize(pathAttributesBuf, opt)) + budget -= paLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + nlriBuf := bytes.NewBuffer(nil) + for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { + if opt.AddPathRX { + nlriLen = int(nlri.serializeAddPath(withdrawBuf)) + } else { + nlriLen = int(nlri.serialize(withdrawBuf)) + } + + budget -= nlriLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + withdrawnRoutesLen := withdrawBuf.Len() + if withdrawnRoutesLen > 65535 { + return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen) + } + + totalPathAttributesLen := pathAttributesBuf.Len() + if totalPathAttributesLen > 65535 { + return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen) + } + + totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19 + if totalLength > 4096 { + return nil, fmt.Errorf("Update too long: %d bytes", totalLength) + } + + serializeHeader(buf, uint16(totalLength), UpdateMsg) + + buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen))) + buf.Write(withdrawBuf.Bytes()) + + buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen))) + buf.Write(pathAttributesBuf.Bytes()) + + buf.Write(nlriBuf.Bytes()) + + return buf.Bytes(), nil +} + +func (b *BGPUpdate) SerializeUpdateAddPath(opt *Options) ([]byte, error) { + budget := MaxLen - MinLen + buf := bytes.NewBuffer(nil) + + withdrawBuf := bytes.NewBuffer(nil) + for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { + nlriLen := int(withdraw.serialize(withdrawBuf)) + budget -= nlriLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + pathAttributesBuf := bytes.NewBuffer(nil) + for pa := b.PathAttributes; pa != nil; pa = pa.Next { + paLen := int(pa.serialize(pathAttributesBuf, opt)) + budget -= paLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + nlriBuf := bytes.NewBuffer(nil) + for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { + nlriLen := int(nlri.serialize(nlriBuf)) + budget -= nlriLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + withdrawnRoutesLen := withdrawBuf.Len() + if withdrawnRoutesLen > 65535 { + return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen) + } + + totalPathAttributesLen := pathAttributesBuf.Len() + if totalPathAttributesLen > 65535 { + return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen) + } + + totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19 + if totalLength > 4096 { + return nil, fmt.Errorf("Update too long: %d bytes", totalLength) + } + + serializeHeader(buf, uint16(totalLength), UpdateMsg) + + buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen))) + buf.Write(withdrawBuf.Bytes()) + + buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen))) + buf.Write(pathAttributesBuf.Bytes()) + + buf.Write(nlriBuf.Bytes()) + + return buf.Bytes(), nil +} diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index cf970b40483511c2ecafc1d36597f66117de06ef..94612c231adca5737f1c28537c428e8152808b0b 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -54,9 +54,6 @@ type FSM struct { msgRecvFailCh chan error stopMsgRecvCh chan struct{} - capAddPathSend bool - capAddPathRecv bool - options *packet.Options local net.IP @@ -65,7 +62,7 @@ type FSM struct { adjRIBIn routingtable.RouteTableClient adjRIBOut routingtable.RouteTableClient rib *locRIB.LocRIB - updateSender routingtable.RouteTableClient + updateSender *UpdateSender neighborID uint32 state state diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go index fdb6ea30a1463ad06e0cb44507205eed71d7c75b..3111055d9c0757f329c72acf27c66a1942cc34a4 100644 --- a/protocols/bgp/server/fsm_established.go +++ b/protocols/bgp/server/fsm_established.go @@ -77,20 +77,21 @@ func (s *establishedState) init() error { LocalASN: s.fsm.peer.localASN, RouteServerClient: s.fsm.peer.routeServerClient, LocalAddress: bnet.IPv4ToUint32(hostIP), - CapAddPathRX: s.fsm.capAddPathSend, + CapAddPathRX: s.fsm.options.AddPathRX, } s.fsm.adjRIBOut = adjRIBOut.New(n, s.fsm.peer.exportFilter) clientOptions := routingtable.ClientOptions{ BestOnly: true, } - if s.fsm.capAddPathSend { + if s.fsm.options.AddPathRX { s.fsm.updateSender = newUpdateSenderAddPath(s.fsm) clientOptions = s.fsm.peer.addPathSend } else { s.fsm.updateSender = newUpdateSender(s.fsm) } + s.fsm.updateSender.Start() s.fsm.adjRIBOut.Register(s.fsm.updateSender) s.fsm.rib.RegisterWithOptions(s.fsm.adjRIBOut, clientOptions) @@ -103,6 +104,7 @@ func (s *establishedState) uninit() { s.fsm.adjRIBIn.Unregister(s.fsm.rib) s.fsm.rib.Unregister(s.fsm.adjRIBOut) s.fsm.adjRIBOut.Unregister(s.fsm.updateSender) + s.fsm.updateSender.Destroy() s.fsm.adjRIBIn = nil s.fsm.adjRIBOut = nil diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go index 121ea883a1c4d2de0e9384e275c394608b8665b7..928ed450b7b2a4cb230acaca5db418af80923c11 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -183,18 +183,18 @@ func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapabi switch addPathCap.SendReceive { case packet.AddPathReceive: if !s.fsm.peer.addPathSend.BestOnly { - s.fsm.capAddPathSend = true + s.fsm.options.AddPathRX = true } case packet.AddPathSend: if s.fsm.peer.addPathRecv { - s.fsm.capAddPathRecv = true + s.fsm.options.AddPathRX = true } case packet.AddPathSendReceive: if !s.fsm.peer.addPathSend.BestOnly { - s.fsm.capAddPathSend = true + s.fsm.options.AddPathRX = true } if s.fsm.peer.addPathRecv { - s.fsm.capAddPathRecv = true + s.fsm.options.AddPathRX = true } } } diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go index 1fe4fdbaf05d6e483047712861874365a3af74a3..38070219394100e9006e660f75f2212b1a2b5668 100644 --- a/protocols/bgp/server/update_helper.go +++ b/protocols/bgp/server/update_helper.go @@ -65,11 +65,7 @@ func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) *pack return current } -type serializeAbleUpdate interface { - SerializeUpdate(opt *packet.Options) ([]byte, error) -} - -func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *packet.Options) error { +func serializeAndSendUpdate(out io.Writer, update *packet.BGPUpdate, opt *packet.Options) error { updateBytes, err := update.SerializeUpdate(opt) if err != nil { log.Errorf("Unable to serialize BGP Update: %v", err) diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index 23da78c532952a70bce63b8cc9570dba2d9e503e..3ac1e6e9349d5e31caf29b139088b20a235daacc 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -1,12 +1,12 @@ package server import ( - "fmt" - "strings" + "sync" + "time" log "github.com/sirupsen/logrus" - "github.com/bio-routing/bio-rd/net" + 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" @@ -15,59 +15,151 @@ import ( // UpdateSender converts table changes into BGP update messages type UpdateSender struct { routingtable.ClientManager - fsm *FSM - iBGP bool + fsm *FSM + iBGP bool + addPath bool + toSendMu sync.Mutex + toSend map[string]*pathPfxs + destroyCh chan struct{} +} + +type pathPfxs struct { + path *route.Path + pfxs []bnet.Prefix +} + +func newUpdateSenderAddPath(fsm *FSM) *UpdateSender { + u := newUpdateSender(fsm) + u.addPath = true + return u } func newUpdateSender(fsm *FSM) *UpdateSender { return &UpdateSender{ - fsm: fsm, - iBGP: fsm.peer.localASN == fsm.peer.peerASN, + fsm: fsm, + iBGP: fsm.peer.localASN == fsm.peer.peerASN, + destroyCh: make(chan struct{}), } } -// AddPath serializes a new path and sends out a BGP update message -func (u *UpdateSender) AddPath(pfx net.Prefix, p *route.Path) error { - pathAttrs, err := pathAttribues(p) - if err != nil { - log.Errorf("Unable to create BGP Update: %v", err) +// Start starts the update sender +func (u *UpdateSender) Start() { + go u.sender() +} + +// Destroy destroys everything (with greetings to Hatebreed) +func (u *UpdateSender) Destroy() { + u.destroyCh <- struct{}{} +} + +// AddPath adds path p for pfx to toSend queue +func (u *UpdateSender) AddPath(pfx bnet.Prefix, p *route.Path) error { + u.toSendMu.Lock() + + hash := p.BGPPath.ComputeHash() + if _, exists := u.toSend[hash]; exists { + u.toSend[hash].pfxs = append(u.toSend[hash].pfxs, pfx) + u.toSendMu.Unlock() return nil } - update := &packet.BGPUpdate{ - PathAttributes: pathAttrs, - NLRI: &packet.NLRI{ - IP: pfx.Addr(), - Pfxlen: pfx.Pfxlen(), + u.toSend[p.BGPPath.ComputeHash()] = &pathPfxs{ + path: p, + pfxs: []bnet.Prefix{ + pfx, }, } - return serializeAndSendUpdate(u.fsm.con, update, u.fsm.options) + u.toSendMu.Unlock() + return nil +} + +// sender serializes BGP update messages +func (u *UpdateSender) sender() { + ticker := time.NewTicker(time.Millisecond * 5) + var err error + var pathAttrs *packet.PathAttribute + var budget int + var nlri *packet.NLRI + + for { + select { + case <-u.destroyCh: + return + case <-ticker.C: + } + + u.toSendMu.Lock() + + for key, pathNLRIs := range u.toSend { + budget = packet.MaxLen - int(pathNLRIs.path.BGPPath.Length()) + pathAttrs, err = pathAttribues(pathNLRIs.path) + if err != nil { + log.Errorf("Unable to get path attributes: %v", err) + continue + } + + updatesPrefixes := make([][]bnet.Prefix, 1) + prefixes := make([]bnet.Prefix, 1) + for _, pfx := range pathNLRIs.pfxs { + budget -= int(packet.BytesInAddr(nlri.Pfxlen)) - 5 + if budget < 0 { + updatesPrefixes = append(updatesPrefixes, prefixes) + prefixes = make([]bnet.Prefix, 1) + } + + prefixes = append(prefixes, pfx) + } + + delete(u.toSend, key) + u.toSendMu.Unlock() + + u.sendUpdates(pathAttrs, updatesPrefixes, pathNLRIs.path.BGPPath.PathIdentifier) + u.toSendMu.Lock() + } + } +} + +func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefixes [][]bnet.Prefix, pathID uint32) { + var nlri *packet.NLRI + var err error + + for _, updatePrefix := range updatePrefixes { + update := &packet.BGPUpdate{ + PathAttributes: pathAttrs, + } + + for _, pfx := range updatePrefix { + nlri = &packet.NLRI{ + PathIdentifier: pathID, + IP: pfx.Addr(), + Pfxlen: pfx.Pfxlen(), + Next: update.NLRI, + } + update.NLRI = nlri + } + + err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options) + if err != nil { + log.Errorf("Failed to serialize and send: %v", err) + } + } } // RemovePath withdraws prefix `pfx` from a peer -func (u *UpdateSender) RemovePath(pfx net.Prefix, p *route.Path) bool { - err := withDrawPrefixes(u.fsm.con, u.fsm.options, pfx) +func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool { + err := withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) return err == nil } // UpdateNewClient does nothing func (u *UpdateSender) UpdateNewClient(client routingtable.RouteTableClient) error { - log.Warningf("BGP Update Sender: UpdateNewClient() not supported") + log.Warningf("BGP Update Sender: UpdateNewClient not implemented") return nil } -// RouteCount does nothing +// RouteCount returns the number of stored routes func (u *UpdateSender) RouteCount() int64 { - log.Warningf("BGP Update Sender: RouteCount() not supported") + log.Warningf("BGP Update Sender: RouteCount not implemented") return 0 } - -func asPathString(iBGP bool, localASN uint16, asPath string) string { - ret := "" - if iBGP { - ret = ret + fmt.Sprintf("%d ", localASN) - } - ret = ret + asPath - return strings.TrimRight(ret, " ") -} diff --git a/protocols/bgp/server/update_sender_add_path.go b/protocols/bgp/server/update_sender_add_path.go deleted file mode 100644 index 9d4820a3f3d7ed8a57ea821d3714f192053fe03e..0000000000000000000000000000000000000000 --- a/protocols/bgp/server/update_sender_add_path.go +++ /dev/null @@ -1,127 +0,0 @@ -package server - -import ( - "sync" - "time" - - log "github.com/sirupsen/logrus" - - 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" -) - -// UpdateSenderAddPath converts table changes into BGP update messages with add path -type UpdateSenderAddPath struct { - routingtable.ClientManager - fsm *FSM - iBGP bool - buffer chan *route.Route - toSendMu sync.Mutex - toSend map[string]struct { - path *route.Path - pfxs []bnet.Prefix - } -} - -func newUpdateSenderAddPath(fsm *FSM) *UpdateSenderAddPath { - return &UpdateSenderAddPath{ - fsm: fsm, - iBGP: fsm.peer.localASN == fsm.peer.peerASN, - buffer: make(chan *route.Route, 1000), - } -} - -func (u *UpdateSenderAddPath) AddPath(pfx bnet.Prefix, p *route.Path) error { - u.buffer <- route.NewRoute(pfx, p) - return nil -} - -// sender serializes BGP update messages -func (u *UpdateSenderAddPath) sender() { - ticker := time.NewTicker(time.Millisecond * 5) - var r *route.Route - var p *route.Path - var pfx bnet.Prefix - var err error - var pathAttrs *packet.PathAttribute - var update *packet.BGPUpdateAddPath - var lastNLRI *packet.NLRIAddPath - var budget int64 - - for { - <-ticker.C - u.toSendMu.Lock() - - for _, pathNLRIs := range u.toSend { - budget = packet.MaxLen - - pathAttrs, err = pathAttribues(pathNLRIs.path) - if err != nil { - log.Errorf("Unable to get path attributes: %v", err) - continue - } - - update = &packet.BGPUpdateAddPath{ - PathAttributes: pathAttrs, - /*NLRI: &packet.NLRIAddPath{ - PathIdentifier: p.BGPPath.PathIdentifier, - IP: pfx.Addr(), - Pfxlen: pfx.Pfxlen(), - },*/ - } - - for _, pfx := range pathNLRIs.pfxs { - nlri = &packet.NLRIAddPath{ - PathIdentifier: pathNLRIs.path.BGPPath.PathIdentifier, - IP: pfx.Addr(), - Pfxlen: pfx.Pfxlen(), - } - } - } - - u.toSendMu.Unlock() - - p = r.Paths()[0] - pfx = r.Prefix() - pathAttrs, err = pathAttribues(p) - - if err != nil { - log.Errorf("Unable to create BGP Update: %v", err) - continue - } - update = &packet.BGPUpdateAddPath{ - PathAttributes: pathAttrs, - NLRI: &packet.NLRIAddPath{ - PathIdentifier: p.BGPPath.PathIdentifier, - IP: pfx.Addr(), - Pfxlen: pfx.Pfxlen(), - }, - } - - err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options) - if err != nil { - log.Errorf("Failed to serialize and send: %v", err) - } - } - -} - -// RemovePath withdraws prefix `pfx` from a peer -func (u *UpdateSenderAddPath) RemovePath(pfx bnet.Prefix, p *route.Path) bool { - err := withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) - return err == nil -} - -// UpdateNewClient does nothing -func (u *UpdateSenderAddPath) UpdateNewClient(client routingtable.RouteTableClient) error { - log.Warningf("BGP Update Sender: UpdateNewClient not implemented") - return nil -} - -// RouteCount returns the number of stored routes -func (u *UpdateSenderAddPath) RouteCount() int64 { - log.Warningf("BGP Update Sender: RouteCount not implemented") - return 0 -} diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go index 089f9f2c0f7a0e25d1c5d1f2e9f292cdeec74a3c..a66fade667558a45b4a2f92d4a569516c57c7dd1 100644 --- a/protocols/bgp/server/withdraw.go +++ b/protocols/bgp/server/withdraw.go @@ -48,8 +48,8 @@ func withDrawPrefixesAddPath(out io.Writer, opt *packet.Options, pfx net.Prefix, if p.BGPPath == nil { return errors.New("got nil BGPPath") } - update := &packet.BGPUpdateAddPath{ - WithdrawnRoutes: &packet.NLRIAddPath{ + update := &packet.BGPUpdate{ + WithdrawnRoutes: &packet.NLRI{ PathIdentifier: p.BGPPath.PathIdentifier, IP: pfx.Addr(), Pfxlen: pfx.Pfxlen(), diff --git a/route/bgp_path.go b/route/bgp_path.go index 175d7bf4c8d45d1a24edc7d6bb0e98b0aafc16d8..31837161b2a5a0ba91cb9b302b9a3b8ef3c975a9 100644 --- a/route/bgp_path.go +++ b/route/bgp_path.go @@ -26,15 +26,27 @@ type BGPPath struct { UnknownAttributes *packet.PathAttribute } -// Legth get's the length of serialized path -func (b *BGPPath) Legth() uint16 { +// Length get's the length of serialized path +func (b *BGPPath) Length() uint16 { asPathLen := uint16(3) for _, segment := range b.ASPath { asPathLen++ asPathLen += uint16(4 * len(segment.ASNs)) } - return uint16(len(b.Communities))*7 + uint16(len(b.LargeCommunities))*15 + 4*7 + 4 + asPathLen + return uint16(len(b.Communities))*7 + uint16(len(b.LargeCommunities))*15 + 4*7 + 4 + asPathLen + b.unknownAttributesLength() +} + +// unknownAttributesLength calculates the length of unknown attributes +func (b *BGPPath) unknownAttributesLength() (length uint16) { + for a := b.UnknownAttributes; a != nil; a = a.Next { + length += a.Length + 3 + if a.ExtendedLength { + length++ + } + } + + return length } // ECMP determines if routes b and c are euqal in terms of ECMP