diff options
Diffstat (limited to 'table/message.go')
-rw-r--r-- | table/message.go | 365 |
1 files changed, 214 insertions, 151 deletions
diff --git a/table/message.go b/table/message.go index 9d82ce31..4f49e36f 100644 --- a/table/message.go +++ b/table/message.go @@ -17,9 +17,7 @@ package table import ( "bytes" - "hash/fnv" "reflect" - "sort" "github.com/osrg/gobgp/packet/bgp" log "github.com/sirupsen/logrus" @@ -258,175 +256,240 @@ func UpdatePathAggregator4ByteAs(msg *bgp.BGPUpdate) error { return nil } -func createUpdateMsgFromPath(path *Path, msg *bgp.BGPMessage) *bgp.BGPMessage { - family := path.GetRouteFamily() - v4 := true - if family != bgp.RF_IPv4_UC || !path.IsWithdraw && path.GetNexthop().To4() == nil { - v4 = false +type cage struct { + attrsBytes []byte + paths []*Path +} + +func newCage(b []byte, path *Path) *cage { + return &cage{ + attrsBytes: b, + paths: []*Path{path}, } +} - if v4 { - nlri := path.GetNlri().(*bgp.IPAddrPrefix) - if path.IsWithdraw { - if msg != nil { - u := msg.Body.(*bgp.BGPUpdate) - u.WithdrawnRoutes = append(u.WithdrawnRoutes, nlri) - return nil - } else { - return bgp.NewBGPUpdateMessage([]*bgp.IPAddrPrefix{nlri}, nil, nil) - } - } else { - if msg != nil { - u := msg.Body.(*bgp.BGPUpdate) - u.NLRI = append(u.NLRI, nlri) - } else { - return bgp.NewBGPUpdateMessage(nil, path.GetPathAttrs(), []*bgp.IPAddrPrefix{nlri}) - } - } - } else { - if path.IsWithdraw { - if msg != nil { - u := msg.Body.(*bgp.BGPUpdate) - for _, p := range u.PathAttributes { - if p.GetType() == bgp.BGP_ATTR_TYPE_MP_UNREACH_NLRI { - unreach := p.(*bgp.PathAttributeMpUnreachNLRI) - unreach.Value = append(unreach.Value, path.GetNlri()) - } - } - } else { - nlris := []bgp.AddrPrefixInterface{path.GetNlri()} - return bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeMpUnreachNLRI(nlris)}, nil) - } +type packerInterface interface { + add(*Path) + pack() []*bgp.BGPMessage +} + +type packer struct { + eof bool + family bgp.RouteFamily + total uint32 +} + +type packerMP struct { + packer + paths []*Path + withdrawals []*Path +} + +func (p *packerMP) add(path *Path) { + p.packer.total++ + + if path.IsEOR() { + p.packer.eof = true + return + } + + if path.IsWithdraw { + p.withdrawals = append(p.withdrawals, path) + return + } + + p.paths = append(p.paths, path) +} + +func createMPReachMessage(path *Path) *bgp.BGPMessage { + oattrs := path.GetPathAttrs() + attrs := make([]bgp.PathAttributeInterface, 0, len(oattrs)) + for _, a := range oattrs { + if a.GetType() == bgp.BGP_ATTR_TYPE_MP_REACH_NLRI { + attrs = append(attrs, bgp.NewPathAttributeMpReachNLRI(path.GetNexthop().String(), []bgp.AddrPrefixInterface{path.GetNlri()})) } else { - if msg != nil { - u := msg.Body.(*bgp.BGPUpdate) - for _, p := range u.PathAttributes { - if p.GetType() == bgp.BGP_ATTR_TYPE_MP_REACH_NLRI { - reach := p.(*bgp.PathAttributeMpReachNLRI) - reach.Value = append(reach.Value, path.GetNlri()) - } - } - } else { - attrs := make([]bgp.PathAttributeInterface, 0, 8) - for _, a := range path.GetPathAttrs() { - if a.GetType() == bgp.BGP_ATTR_TYPE_MP_REACH_NLRI { - attrs = append(attrs, bgp.NewPathAttributeMpReachNLRI(path.GetNexthop().String(), []bgp.AddrPrefixInterface{path.GetNlri()})) - } else { - attrs = append(attrs, a) - } - } - sort.Slice(attrs, func(i, j int) bool { - return attrs[i].GetType() < attrs[j].GetType() - }) - return bgp.NewBGPUpdateMessage(nil, attrs, nil) - } + attrs = append(attrs, a) } } - return nil + return bgp.NewBGPUpdateMessage(nil, attrs, nil) } -type bucket struct { - attrs []byte - paths []*Path +func (p *packerMP) pack() []*bgp.BGPMessage { + msgs := make([]*bgp.BGPMessage, 0, p.packer.total) + + for _, path := range p.withdrawals { + nlris := []bgp.AddrPrefixInterface{path.GetNlri()} + msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeMpUnreachNLRI(nlris)}, nil)) + } + + for _, path := range p.paths { + msgs = append(msgs, createMPReachMessage(path)) + } + + if p.eof { + msgs = append(msgs, bgp.NewEndOfRib(p.family)) + } + return msgs } -func CreateUpdateMsgFromPaths(pathList []*Path) []*bgp.BGPMessage { - var msgs []*bgp.BGPMessage - var eors []*bgp.BGPMessage +func newPackerMP(f bgp.RouteFamily) *packerMP { + return &packerMP{ + packer: packer{ + family: f, + }, + withdrawals: make([]*Path, 0), + paths: make([]*Path, 0), + } +} - pathByAttrs := make(map[uint32][]*bucket) - for _, path := range pathList { - if path == nil { - continue - } else if path.IsEOR() { - eors = append(eors, bgp.NewEndOfRib(path.GetRouteFamily())) - continue - } - y := func(p *Path) bool { - if p.GetRouteFamily() != bgp.RF_IPv4_UC { - return false - } - if p.IsWithdraw { - return false +type packerV4 struct { + packer + hashmap map[uint32][]*cage + mpPaths []*Path + withdrawals []*Path +} + +func (p *packerV4) add(path *Path) { + p.packer.total++ + + if path.IsEOR() { + p.packer.eof = true + return + } + + if path.IsWithdraw { + p.withdrawals = append(p.withdrawals, path) + return + } + + if path.GetNexthop().To4() == nil { + // RFC 5549 + p.mpPaths = append(p.mpPaths, path) + return + } + + key := path.GetHash() + attrsB := bytes.NewBuffer(make([]byte, 0)) + for _, v := range path.GetPathAttrs() { + b, _ := v.Serialize() + attrsB.Write(b) + } + + if cages, y := p.hashmap[key]; y { + added := false + for _, c := range cages { + if bytes.Compare(c.attrsBytes, attrsB.Bytes()) == 0 { + c.paths = append(c.paths, path) + added = true + break } - return true - }(path) - - if y { - key, attrs := func(p *Path) (uint32, []byte) { - h := fnv.New32() - total := bytes.NewBuffer(make([]byte, 0)) - for _, v := range p.GetPathAttrs() { - b, _ := v.Serialize() - total.Write(b) - } - h.Write(total.Bytes()) - return h.Sum32(), total.Bytes() - }(path) - - if bl, y := pathByAttrs[key]; y { - found := false - for _, b := range bl { - if bytes.Compare(b.attrs, attrs) == 0 { - b.paths = append(b.paths, path) - found = true - break - } - } - if found == false { - nb := &bucket{ - attrs: attrs, - paths: []*Path{path}, - } - pathByAttrs[key] = append(pathByAttrs[key], nb) - } - } else { - nb := &bucket{ - attrs: attrs, - paths: []*Path{path}, - } - pathByAttrs[key] = []*bucket{nb} + } + if !added { + p.hashmap[key] = append(p.hashmap[key], newCage(attrsB.Bytes(), path)) + } + } else { + p.hashmap[key] = []*cage{newCage(attrsB.Bytes(), path)} + } +} + +func (p *packerV4) pack() []*bgp.BGPMessage { + split := func(max int, paths []*Path) ([]*bgp.IPAddrPrefix, []*Path) { + nlris := make([]*bgp.IPAddrPrefix, 0, max) + i := 0 + if max > len(paths) { + max = len(paths) + } + for ; i < max; i++ { + nlris = append(nlris, paths[i].GetNlri().(*bgp.IPAddrPrefix)) + } + return nlris, paths[i:] + } + // Header + Update (WithdrawnRoutesLen + + // TotalPathAttributeLen + attributes + maxlen of NLRI). + // the max size of NLRI is 5bytes + // TODO: addpath needs 4 bytes per NLRI + maxNLRIs := func(attrsLen int) int { + return (bgp.BGP_MAX_MESSAGE_LENGTH - (19 + 2 + 2 + attrsLen)) / 5 + } + + loop := func(attrsLen int, paths []*Path, cb func([]*bgp.IPAddrPrefix)) { + max := maxNLRIs(attrsLen) + var nlris []*bgp.IPAddrPrefix + for { + nlris, paths = split(max, paths) + if len(nlris) == 0 { + break } - } else { - msg := createUpdateMsgFromPath(path, nil) - msgs = append(msgs, msg) + cb(nlris) } } - for _, bList := range pathByAttrs { - for _, b := range bList { - var msg *bgp.BGPMessage - for i, path := range b.paths { - if i == 0 { - msg = createUpdateMsgFromPath(path, nil) - msgs = append(msgs, msg) - } else { - msgLen := func(u *bgp.BGPUpdate) int { - attrsLen := 0 - for _, a := range u.PathAttributes { - attrsLen += a.Len() - } - // Header + Update (WithdrawnRoutesLen + - // TotalPathAttributeLen + attributes + maxlen of - // NLRI). Note that we try to add one NLRI. - return 19 + 2 + 2 + attrsLen + (len(u.NLRI)+1)*5 - }(msg.Body.(*bgp.BGPUpdate)) - - if msgLen+32 > bgp.BGP_MAX_MESSAGE_LENGTH { - // don't marge - msg = createUpdateMsgFromPath(path, nil) - msgs = append(msgs, msg) - } else { - createUpdateMsgFromPath(path, msg) - } - } + msgs := make([]*bgp.BGPMessage, 0, p.packer.total) + + loop(0, p.withdrawals, func(nlris []*bgp.IPAddrPrefix) { + msgs = append(msgs, bgp.NewBGPUpdateMessage(nlris, nil, nil)) + }) + + for _, cages := range p.hashmap { + for _, c := range cages { + paths := c.paths + + attrs := paths[0].GetPathAttrs() + attrsLen := 0 + for _, a := range attrs { + attrsLen += a.Len() } + + loop(attrsLen, paths, func(nlris []*bgp.IPAddrPrefix) { + msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, attrs, nlris)) + }) } } - for _, eor := range eors { - msgs = append(msgs, eor) + for _, path := range p.mpPaths { + msgs = append(msgs, createMPReachMessage(path)) + } + + if p.eof { + msgs = append(msgs, bgp.NewEndOfRib(p.family)) } + return msgs +} +func newPackerV4(f bgp.RouteFamily) *packerV4 { + return &packerV4{ + packer: packer{ + family: f, + }, + hashmap: make(map[uint32][]*cage), + withdrawals: make([]*Path, 0), + mpPaths: make([]*Path, 0), + } +} + +func newPacker(f bgp.RouteFamily) packerInterface { + switch f { + case bgp.RF_IPv4_UC: + return newPackerV4(bgp.RF_IPv4_UC) + default: + return newPackerMP(f) + } +} + +func CreateUpdateMsgFromPaths(pathList []*Path) []*bgp.BGPMessage { + msgs := make([]*bgp.BGPMessage, 0, len(pathList)) + + m := make(map[bgp.RouteFamily]packerInterface) + for _, path := range pathList { + f := path.GetRouteFamily() + if _, y := m[f]; !y { + m[f] = newPacker(f) + } + m[f].add(path) + } + + for _, p := range m { + msgs = append(msgs, p.pack()...) + } return msgs } |