mirror of
https://github.com/cloudnativelabs/kube-router.git
synced 2025-10-17 21:01:06 +02:00
503 lines
13 KiB
Go
503 lines
13 KiB
Go
// 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
|
|
}
|