Newer
Older
"sync"
"time"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable"
bnet "github.com/bio-routing/bio-rd/net"
log "github.com/sirupsen/logrus"
fsm *FSM
iBGP bool
rrClient bool
toSendMu sync.Mutex
toSend map[string]*pathPfxs
destroyCh chan struct{}
}
type pathPfxs struct {
path *route.Path
pfxs []bnet.Prefix
}
func newUpdateSender(fsm *FSM, afi uint16, safi uint8) *UpdateSender {
f := fsm.addressFamily(afi, safi)
fsm: fsm,
iBGP: fsm.peer.localASN == fsm.peer.peerASN,
rrClient: fsm.peer.routeReflectorClient,
destroyCh: make(chan struct{}),
toSend: make(map[string]*pathPfxs),
options: &packet.EncodeOptions{
Use32BitASN: fsm.supports4OctetASN,
UseAddPath: f.addPathRX,
},
// Start starts the update sender
func (u *UpdateSender) Start(aggrTime time.Duration) {
go u.sender(aggrTime)
}
// 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()
u.toSend[p.BGPPath.ComputeHash()] = &pathPfxs{
path: p,
pfxs: []bnet.Prefix{
pfx,
u.toSendMu.Unlock()
return nil
}
// sender serializes BGP update messages
func (u *UpdateSender) sender(aggrTime time.Duration) {
ticker := time.NewTicker(aggrTime)
var err error
var pathAttrs *packet.PathAttribute
var budget int
for {
select {
case <-u.destroyCh:
return
case <-ticker.C:
}
u.toSendMu.Lock()
for key, pathNLRIs := range u.toSend {
budget = packet.MaxLen - packet.HeaderLen - packet.MinUpdateLen - int(pathNLRIs.path.BGPPath.Length()) - overhead
Maximilian Wilhelm
committed
pathAttrs, err = packet.PathAttributes(pathNLRIs.path, u.iBGP, u.rrClient)
if err != nil {
log.Errorf("Unable to get path attributes: %v", err)
continue
}
updatesPrefixes := make([][]bnet.Prefix, 0, 1)
prefixes := make([]bnet.Prefix, 0, 1)
for _, pfx := range pathNLRIs.pfxs {
if budget < 0 {
updatesPrefixes = append(updatesPrefixes, prefixes)
budget = packet.MaxLen - int(pathNLRIs.path.BGPPath.Length()) - overhead
}
prefixes = append(prefixes, pfx)
}
if len(prefixes) > 0 {
updatesPrefixes = append(updatesPrefixes, prefixes)
}
delete(u.toSend, key)
u.toSendMu.Unlock()
u.sendUpdates(pathAttrs, updatesPrefixes, pathNLRIs.path.BGPPath.PathIdentifier)
u.toSendMu.Lock()
}
}
}
Daniel Czerwonk
committed
if !u.fsm.supportsMultiProtocol {
addrLen := packet.IPv4AFI
if u.afi == packet.IPv6AFI {
addrLen = packet.IPv6Len
}
// since we are replacing the next hop attribute IPv4Len has to be subtracted, we also add another byte for extended length
return packet.AFILen + packet.SAFILen + 1 + addrLen - packet.IPv4Len + 1
func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefixes [][]bnet.Prefix, pathID uint32) {
var err error
for _, prefixes := range updatePrefixes {
update := u.updateMessageForPrefixes(prefixes, pathAttrs, pathID)
if update == nil {
log.Errorf("Failed to create update: Neighbor does not support multi protocol.")
return
}
err = serializeAndSendUpdate(u.fsm.con, update, u.options)
if err != nil {
log.Errorf("Failed to serialize and send: %v", err)
}
func (u *UpdateSender) updateMessageForPrefixes(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
if u.afi == packet.IPv4AFI && u.safi == packet.UnicastSAFI {
return u.bgpUpdate(pfxs, pa, pathID)
}
Daniel Czerwonk
committed
if u.fsm.supportsMultiProtocol {
return u.bgpUpdateMultiProtocol(pfxs, pa, pathID)
}
}
func (u *UpdateSender) bgpUpdate(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
update := &packet.BGPUpdate{
PathAttributes: pa,
}
var nlri *packet.NLRI
for _, pfx := range pfxs {
nlri = &packet.NLRI{
PathIdentifier: pathID,
IP: pfx.Addr().ToUint32(),
Pfxlen: pfx.Pfxlen(),
Next: update.NLRI,
}
return update
}
func (u *UpdateSender) bgpUpdateMultiProtocol(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
pa, nextHop := u.copyAttributesWithoutNextHop(pa)
attrs := &packet.PathAttribute{
TypeCode: packet.MultiProtocolReachNLRICode,
Value: packet.MultiProtocolReachNLRI{
},
}
attrs.Next = pa
return &packet.BGPUpdate{
PathAttributes: attrs,
}
}
func (u *UpdateSender) copyAttributesWithoutNextHop(pa *packet.PathAttribute) (attrs *packet.PathAttribute, nextHop bnet.IP) {
var curCopy, lastCopy *packet.PathAttribute
if cur.TypeCode == packet.NextHopAttr {
nextHop = cur.Value.(bnet.IP)
} else {
curCopy = cur.Copy()
if lastCopy == nil {
attrs = curCopy
} else {
lastCopy.Next = curCopy
}
lastCopy = curCopy
}
}
func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool {
if err != nil {
log.Errorf("Unable to withdraw prefix: %v", err)
return false
}
return true
func (u *UpdateSender) withdrawPrefix(pfx bnet.Prefix, p *route.Path) error {
Daniel Czerwonk
committed
if u.fsm.supportsMultiProtocol {
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
return u.withDrawPrefixesMultiProtocol(u.fsm.con, pfx, p)
}
return u.withDrawPrefixesAddPath(u.fsm.con, pfx, p)
}
// withDrawPrefixes generates a BGPUpdate message and write it to the given
// io.Writer.
func (u *UpdateSender) withDrawPrefixes(out io.Writer, prefixes ...bnet.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().ToUint32(),
Pfxlen: pfx.Pfxlen(),
}
currentNLRI = rootNLRI
} else {
currentNLRI.Next = &packet.NLRI{
IP: pfx.Addr().ToUint32(),
Pfxlen: pfx.Pfxlen(),
}
currentNLRI = currentNLRI.Next
}
}
update := &packet.BGPUpdate{
WithdrawnRoutes: rootNLRI,
}
return serializeAndSendUpdate(out, update, u.options)
}
// withDrawPrefixesAddPath generates a BGPUpdateAddPath message and write it to the given
// io.Writer.
func (u *UpdateSender) withDrawPrefixesAddPath(out io.Writer, pfx bnet.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.BGPUpdate{
WithdrawnRoutes: &packet.NLRI{
PathIdentifier: p.BGPPath.PathIdentifier,
IP: pfx.Addr().ToUint32(),
Pfxlen: pfx.Pfxlen(),
},
}
return serializeAndSendUpdate(out, update, u.options)
}
func (u *UpdateSender) withDrawPrefixesMultiProtocol(out io.Writer, pfx bnet.Prefix, p *route.Path) error {
pathID := uint32(0)
if p.BGPPath != nil {
pathID = p.BGPPath.PathIdentifier
}
update := &packet.BGPUpdate{
PathAttributes: &packet.PathAttribute{
TypeCode: packet.MultiProtocolUnreachNLRICode,
Value: packet.MultiProtocolUnreachNLRI{
AFI: u.afi,
SAFI: u.safi,
Prefixes: []bnet.Prefix{pfx},
PathID: pathID,
},
},
return serializeAndSendUpdate(out, update, u.options)
func (u *UpdateSender) UpdateNewClient(client routingtable.RouteTableClient) error {
log.Warningf("BGP Update Sender: UpdateNewClient not implemented")
// RouteCount returns the number of stored routes
func (u *UpdateSender) RouteCount() int64 {
log.Warningf("BGP Update Sender: RouteCount not implemented")
return 0
}