// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // implied. // See the License for the specific language governing permissions and // limitations under the License. package table import ( "bytes" "reflect" "github.com/osrg/gobgp/packet/bgp" log "github.com/sirupsen/logrus" ) func UpdatePathAttrs2ByteAs(msg *bgp.BGPUpdate) error { ps := msg.PathAttributes msg.PathAttributes = make([]bgp.PathAttributeInterface, len(ps)) copy(msg.PathAttributes, ps) var asAttr *bgp.PathAttributeAsPath idx := 0 for i, attr := range msg.PathAttributes { if a, ok := attr.(*bgp.PathAttributeAsPath); ok { asAttr = a idx = i break } } if asAttr == nil { return nil } as4Params := make([]*bgp.As4PathParam, 0, len(asAttr.Value)) as2Params := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value)) mkAs4 := false for _, param := range asAttr.Value { segType := param.GetType() asList := param.GetAS() as2Path := make([]uint16, 0, len(asList)) for _, as := range asList { if as > (1<<16)-1 { mkAs4 = true as2Path = append(as2Path, bgp.AS_TRANS) } else { as2Path = append(as2Path, uint16(as)) } } as2Params = append(as2Params, bgp.NewAsPathParam(segType, as2Path)) // RFC 6793 4.2.2 Generating Updates // // Whenever the AS path information contains the AS_CONFED_SEQUENCE or // AS_CONFED_SET path segment, the NEW BGP speaker MUST exclude such // path segments from the AS4_PATH attribute being constructed. switch segType { case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET: // pass default: if as4param, ok := param.(*bgp.As4PathParam); ok { as4Params = append(as4Params, as4param) } } } msg.PathAttributes[idx] = bgp.NewPathAttributeAsPath(as2Params) if mkAs4 { msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Path(as4Params)) } return nil } func UpdatePathAttrs4ByteAs(msg *bgp.BGPUpdate) error { var asAttr *bgp.PathAttributeAsPath var as4Attr *bgp.PathAttributeAs4Path asAttrPos := 0 as4AttrPos := 0 for i, attr := range msg.PathAttributes { switch attr.(type) { case *bgp.PathAttributeAsPath: asAttr = attr.(*bgp.PathAttributeAsPath) for j, param := range asAttr.Value { as2Param, ok := param.(*bgp.AsPathParam) if ok { asPath := make([]uint32, 0, len(as2Param.AS)) for _, as := range as2Param.AS { asPath = append(asPath, uint32(as)) } as4Param := bgp.NewAs4PathParam(as2Param.Type, asPath) asAttr.Value[j] = as4Param } } asAttrPos = i msg.PathAttributes[i] = asAttr case *bgp.PathAttributeAs4Path: as4AttrPos = i as4Attr = attr.(*bgp.PathAttributeAs4Path) } } if as4Attr != nil { msg.PathAttributes = append(msg.PathAttributes[:as4AttrPos], msg.PathAttributes[as4AttrPos+1:]...) } if asAttr == nil || as4Attr == nil { return nil } asLen := 0 asConfedLen := 0 asParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value)) for _, param := range asAttr.Value { asLen += param.ASLen() switch param.GetType() { case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET: asConfedLen++ case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: asConfedLen += len(param.GetAS()) } asParams = append(asParams, param) } as4Len := 0 as4Params := make([]bgp.AsPathParamInterface, 0, len(as4Attr.Value)) if as4Attr != nil { for _, p := range as4Attr.Value { // RFC 6793 6. Error Handling // // the path segment types AS_CONFED_SEQUENCE and AS_CONFED_SET [RFC5065] // MUST NOT be carried in the AS4_PATH attribute of an UPDATE message. // A NEW BGP speaker that receives these path segment types in the AS4_PATH // attribute of an UPDATE message from an OLD BGP speaker MUST discard // these path segments, adjust the relevant attribute fields accordingly, // and continue processing the UPDATE message. // This case SHOULD be logged locally for analysis. switch p.Type { case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET: typ := "CONFED_SEQ" if p.Type == bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET { typ = "CONFED_SET" } log.WithFields(log.Fields{ "Topic": "Table", }).Warnf("AS4_PATH contains %s segment %s. ignore", typ, p.String()) continue } as4Len += p.ASLen() as4Params = append(as4Params, p) } } if asLen+asConfedLen < as4Len { log.WithFields(log.Fields{ "Topic": "Table", }).Warn("AS4_PATH is longer than AS_PATH. ignore AS4_PATH") return nil } keepNum := asLen + asConfedLen - as4Len newParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value)) for _, param := range asParams { if keepNum-param.ASLen() >= 0 { newParams = append(newParams, param) keepNum -= param.ASLen() } else { // only SEQ param reaches here newParams = append(newParams, bgp.NewAs4PathParam(param.GetType(), param.GetAS()[:keepNum])) keepNum = 0 } if keepNum <= 0 { break } } for _, param := range as4Params { lastParam := newParams[len(newParams)-1] lastParamAS := lastParam.GetAS() paramType := param.GetType() paramAS := param.GetAS() if paramType == lastParam.GetType() && paramType == bgp.BGP_ASPATH_ATTR_TYPE_SEQ { if len(lastParamAS)+len(paramAS) > 255 { newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS[:255-len(lastParamAS)]...)) newParams = append(newParams, bgp.NewAs4PathParam(paramType, paramAS[255-len(lastParamAS):])) } else { newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS...)) } } else { newParams = append(newParams, param) } } newIntfParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value)) newIntfParams = append(newIntfParams, newParams...) msg.PathAttributes[asAttrPos] = bgp.NewPathAttributeAsPath(newIntfParams) return nil } func UpdatePathAggregator2ByteAs(msg *bgp.BGPUpdate) { as := uint32(0) var addr string for i, attr := range msg.PathAttributes { switch attr.(type) { case *bgp.PathAttributeAggregator: agg := attr.(*bgp.PathAttributeAggregator) addr = agg.Value.Address.String() if agg.Value.AS > (1<<16)-1 { as = agg.Value.AS msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(bgp.AS_TRANS), addr) } else { msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(agg.Value.AS), addr) } } } if as != 0 { msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Aggregator(as, addr)) } } func UpdatePathAggregator4ByteAs(msg *bgp.BGPUpdate) error { var aggAttr *bgp.PathAttributeAggregator var agg4Attr *bgp.PathAttributeAs4Aggregator agg4AttrPos := 0 for i, attr := range msg.PathAttributes { switch attr.(type) { case *bgp.PathAttributeAggregator: attr := attr.(*bgp.PathAttributeAggregator) if attr.Value.Askind == reflect.Uint16 { aggAttr = attr aggAttr.Value.Askind = reflect.Uint32 } case *bgp.PathAttributeAs4Aggregator: agg4Attr = attr.(*bgp.PathAttributeAs4Aggregator) agg4AttrPos = i } } if aggAttr == nil && agg4Attr == nil { return nil } if aggAttr == nil && agg4Attr != nil { return bgp.NewMessageError(bgp.BGP_ERROR_UPDATE_MESSAGE_ERROR, bgp.BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "AS4 AGGREGATOR attribute exists, but AGGREGATOR doesn't") } if agg4Attr != nil { msg.PathAttributes = append(msg.PathAttributes[:agg4AttrPos], msg.PathAttributes[agg4AttrPos+1:]...) aggAttr.Value.AS = agg4Attr.Value.AS } return nil } type cage struct { attrsBytes []byte paths []*Path } func newCage(b []byte, path *Path) *cage { return &cage{ attrsBytes: b, paths: []*Path{path}, } } type packerInterface interface { add(*Path) pack(options ...*bgp.MarshallingOption) []*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 { attrs = append(attrs, a) } } return bgp.NewBGPUpdateMessage(nil, attrs, nil) } func (p *packerMP) pack(options ...*bgp.MarshallingOption) []*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 newPackerMP(f bgp.RouteFamily) *packerMP { return &packerMP{ packer: packer{ family: f, }, withdrawals: make([]*Path, 0), paths: make([]*Path, 0), } } 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.Equal(c.attrsBytes, attrsB.Bytes()) { c.paths = append(c.paths, path) added = true break } } 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(options ...*bgp.MarshallingOption) []*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:] } addpathNLRILen := 0 if bgp.IsAddPathEnabled(false, p.packer.family, options) { addpathNLRILen = 4 } // Header + Update (WithdrawnRoutesLen + // TotalPathAttributeLen + attributes + maxlen of NLRI). // the max size of NLRI is 5bytes (plus 4bytes with addpath enabled) maxNLRIs := func(attrsLen int) int { return (bgp.BGP_MAX_MESSAGE_LENGTH - (19 + 2 + 2 + attrsLen)) / (5 + addpathNLRILen) } 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 } cb(nlris) } } 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 _, 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, options ...*bgp.MarshallingOption) []*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(options...)...) } return msgs }