github.com/osrg/gobgp/v3@v3.30.0/internal/pkg/table/message.go (about)

     1  // Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package table
    17  
    18  import (
    19  	"bytes"
    20  	"fmt"
    21  	"reflect"
    22  
    23  	"github.com/osrg/gobgp/v3/pkg/log"
    24  	"github.com/osrg/gobgp/v3/pkg/packet/bgp"
    25  )
    26  
    27  func UpdatePathAttrs2ByteAs(msg *bgp.BGPUpdate) error {
    28  	ps := msg.PathAttributes
    29  	msg.PathAttributes = make([]bgp.PathAttributeInterface, len(ps))
    30  	copy(msg.PathAttributes, ps)
    31  	var asAttr *bgp.PathAttributeAsPath
    32  	idx := 0
    33  	for i, attr := range msg.PathAttributes {
    34  		if a, ok := attr.(*bgp.PathAttributeAsPath); ok {
    35  			asAttr = a
    36  			idx = i
    37  			break
    38  		}
    39  	}
    40  
    41  	if asAttr == nil {
    42  		return nil
    43  	}
    44  
    45  	as4Params := make([]*bgp.As4PathParam, 0, len(asAttr.Value))
    46  	as2Params := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
    47  	mkAs4 := false
    48  	for _, param := range asAttr.Value {
    49  		segType := param.GetType()
    50  		asList := param.GetAS()
    51  		as2Path := make([]uint16, 0, len(asList))
    52  		for _, as := range asList {
    53  			if as > (1<<16)-1 {
    54  				mkAs4 = true
    55  				as2Path = append(as2Path, bgp.AS_TRANS)
    56  			} else {
    57  				as2Path = append(as2Path, uint16(as))
    58  			}
    59  		}
    60  		as2Params = append(as2Params, bgp.NewAsPathParam(segType, as2Path))
    61  
    62  		// RFC 6793 4.2.2 Generating Updates
    63  		//
    64  		// Whenever the AS path information contains the AS_CONFED_SEQUENCE or
    65  		// AS_CONFED_SET path segment, the NEW BGP speaker MUST exclude such
    66  		// path segments from the AS4_PATH attribute being constructed.
    67  		switch segType {
    68  		case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET:
    69  			// pass
    70  		default:
    71  			if as4param, ok := param.(*bgp.As4PathParam); ok {
    72  				as4Params = append(as4Params, as4param)
    73  			}
    74  		}
    75  	}
    76  	msg.PathAttributes[idx] = bgp.NewPathAttributeAsPath(as2Params)
    77  	if mkAs4 {
    78  		msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Path(as4Params))
    79  	}
    80  	return nil
    81  }
    82  
    83  func UpdatePathAttrs4ByteAs(logger log.Logger, msg *bgp.BGPUpdate) error {
    84  	var asAttr *bgp.PathAttributeAsPath
    85  	var as4Attr *bgp.PathAttributeAs4Path
    86  	asAttrPos := 0
    87  	as4AttrPos := 0
    88  	for i, attr := range msg.PathAttributes {
    89  		switch a := attr.(type) {
    90  		case *bgp.PathAttributeAsPath:
    91  			asAttr = a
    92  			for j, param := range asAttr.Value {
    93  				as2Param, ok := param.(*bgp.AsPathParam)
    94  				if ok {
    95  					asPath := make([]uint32, 0, len(as2Param.AS))
    96  					for _, as := range as2Param.AS {
    97  						asPath = append(asPath, uint32(as))
    98  					}
    99  					as4Param := bgp.NewAs4PathParam(as2Param.Type, asPath)
   100  					asAttr.Value[j] = as4Param
   101  				}
   102  			}
   103  			asAttrPos = i
   104  			msg.PathAttributes[i] = asAttr
   105  		case *bgp.PathAttributeAs4Path:
   106  			as4AttrPos = i
   107  			as4Attr = a
   108  		}
   109  	}
   110  
   111  	if as4Attr != nil {
   112  		msg.PathAttributes = append(msg.PathAttributes[:as4AttrPos], msg.PathAttributes[as4AttrPos+1:]...)
   113  	}
   114  
   115  	if asAttr == nil || as4Attr == nil {
   116  		return nil
   117  	}
   118  
   119  	asLen := 0
   120  	asConfedLen := 0
   121  	asParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
   122  	for _, param := range asAttr.Value {
   123  		asLen += param.ASLen()
   124  		switch param.GetType() {
   125  		case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET:
   126  			asConfedLen++
   127  		case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
   128  			asConfedLen += len(param.GetAS())
   129  		}
   130  		asParams = append(asParams, param)
   131  	}
   132  
   133  	as4Len := 0
   134  	var as4Params []bgp.AsPathParamInterface
   135  	if as4Attr != nil {
   136  		as4Params = make([]bgp.AsPathParamInterface, 0, len(as4Attr.Value))
   137  		for _, p := range as4Attr.Value {
   138  			// RFC 6793 6. Error Handling
   139  			//
   140  			// the path segment types AS_CONFED_SEQUENCE and AS_CONFED_SET [RFC5065]
   141  			// MUST NOT be carried in the AS4_PATH attribute of an UPDATE message.
   142  			// A NEW BGP speaker that receives these path segment types in the AS4_PATH
   143  			// attribute of an UPDATE message from an OLD BGP speaker MUST discard
   144  			// these path segments, adjust the relevant attribute fields accordingly,
   145  			// and continue processing the UPDATE message.
   146  			// This case SHOULD be logged locally for analysis.
   147  			switch p.Type {
   148  			case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET:
   149  				typ := "CONFED_SEQ"
   150  				if p.Type == bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET {
   151  					typ = "CONFED_SET"
   152  				}
   153  				logger.Warn(fmt.Sprintf("AS4_PATH contains %s segment %s. ignore", typ, p.String()),
   154  					log.Fields{
   155  						"Topic": "Table"})
   156  				continue
   157  			}
   158  			as4Len += p.ASLen()
   159  			as4Params = append(as4Params, p)
   160  		}
   161  	}
   162  
   163  	if asLen+asConfedLen < as4Len {
   164  		logger.Warn("AS4_PATH is longer than AS_PATH. ignore AS4_PATH",
   165  			log.Fields{
   166  				"Topic": "Table"})
   167  		return nil
   168  	}
   169  
   170  	keepNum := asLen + asConfedLen - as4Len
   171  
   172  	newParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
   173  	for _, param := range asParams {
   174  		if keepNum-param.ASLen() >= 0 {
   175  			newParams = append(newParams, param)
   176  			keepNum -= param.ASLen()
   177  		} else {
   178  			// only SEQ param reaches here
   179  			newParams = append(newParams, bgp.NewAs4PathParam(param.GetType(), param.GetAS()[:keepNum]))
   180  			keepNum = 0
   181  		}
   182  
   183  		if keepNum <= 0 {
   184  			break
   185  		}
   186  	}
   187  
   188  	for _, param := range as4Params {
   189  		lastParam := newParams[len(newParams)-1]
   190  		lastParamAS := lastParam.GetAS()
   191  		paramType := param.GetType()
   192  		paramAS := param.GetAS()
   193  		if paramType == lastParam.GetType() && paramType == bgp.BGP_ASPATH_ATTR_TYPE_SEQ {
   194  			if len(lastParamAS)+len(paramAS) > 255 {
   195  				newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS[:255-len(lastParamAS)]...))
   196  				newParams = append(newParams, bgp.NewAs4PathParam(paramType, paramAS[255-len(lastParamAS):]))
   197  			} else {
   198  				newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS...))
   199  			}
   200  		} else {
   201  			newParams = append(newParams, param)
   202  		}
   203  	}
   204  
   205  	newIntfParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
   206  	newIntfParams = append(newIntfParams, newParams...)
   207  
   208  	msg.PathAttributes[asAttrPos] = bgp.NewPathAttributeAsPath(newIntfParams)
   209  	return nil
   210  }
   211  
   212  func UpdatePathAggregator2ByteAs(msg *bgp.BGPUpdate) {
   213  	as := uint32(0)
   214  	var addr string
   215  	for i, attr := range msg.PathAttributes {
   216  		switch agg := attr.(type) {
   217  		case *bgp.PathAttributeAggregator:
   218  			addr = agg.Value.Address.String()
   219  			if agg.Value.AS > (1<<16)-1 {
   220  				as = agg.Value.AS
   221  				msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(bgp.AS_TRANS), addr)
   222  			} else {
   223  				msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(agg.Value.AS), addr)
   224  			}
   225  		}
   226  	}
   227  	if as != 0 {
   228  		msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Aggregator(as, addr))
   229  	}
   230  }
   231  
   232  func UpdatePathAggregator4ByteAs(msg *bgp.BGPUpdate) error {
   233  	var aggAttr *bgp.PathAttributeAggregator
   234  	var agg4Attr *bgp.PathAttributeAs4Aggregator
   235  	agg4AttrPos := 0
   236  	for i, attr := range msg.PathAttributes {
   237  		switch agg := attr.(type) {
   238  		case *bgp.PathAttributeAggregator:
   239  			attr := agg
   240  			if attr.Value.Askind == reflect.Uint16 {
   241  				aggAttr = attr
   242  				aggAttr.Value.Askind = reflect.Uint32
   243  			} else if attr.Value.Askind == reflect.Uint32 {
   244  				aggAttr = attr
   245  			}
   246  		case *bgp.PathAttributeAs4Aggregator:
   247  			agg4Attr = agg
   248  			agg4AttrPos = i
   249  		}
   250  	}
   251  	if aggAttr == nil && agg4Attr == nil {
   252  		return nil
   253  	}
   254  
   255  	if aggAttr == nil && agg4Attr != nil {
   256  		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")
   257  	}
   258  
   259  	if agg4Attr != nil {
   260  		msg.PathAttributes = append(msg.PathAttributes[:agg4AttrPos], msg.PathAttributes[agg4AttrPos+1:]...)
   261  		aggAttr.Value.AS = agg4Attr.Value.AS
   262  	}
   263  	return nil
   264  }
   265  
   266  type cage struct {
   267  	attrsBytes []byte
   268  	paths      []*Path
   269  }
   270  
   271  func newCage(b []byte, path *Path) *cage {
   272  	return &cage{
   273  		attrsBytes: b,
   274  		paths:      []*Path{path},
   275  	}
   276  }
   277  
   278  type packerInterface interface {
   279  	add(*Path)
   280  	pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage
   281  }
   282  
   283  type packer struct {
   284  	eof    bool
   285  	family bgp.RouteFamily
   286  	total  uint32
   287  }
   288  
   289  type packerMP struct {
   290  	packer
   291  	paths       []*Path
   292  	withdrawals []*Path
   293  }
   294  
   295  func (p *packerMP) add(path *Path) {
   296  	p.packer.total++
   297  
   298  	if path.IsEOR() {
   299  		p.packer.eof = true
   300  		return
   301  	}
   302  
   303  	if path.IsWithdraw {
   304  		p.withdrawals = append(p.withdrawals, path)
   305  		return
   306  	}
   307  
   308  	p.paths = append(p.paths, path)
   309  }
   310  
   311  func createMPReachMessage(path *Path) *bgp.BGPMessage {
   312  	oattrs := path.GetPathAttrs()
   313  	attrs := make([]bgp.PathAttributeInterface, 0, len(oattrs))
   314  	for _, a := range oattrs {
   315  		if a.GetType() == bgp.BGP_ATTR_TYPE_MP_REACH_NLRI {
   316  			attrs = append(attrs, bgp.NewPathAttributeMpReachNLRI(path.GetNexthop().String(), []bgp.AddrPrefixInterface{path.GetNlri()}))
   317  		} else {
   318  			attrs = append(attrs, a)
   319  		}
   320  	}
   321  	return bgp.NewBGPUpdateMessage(nil, attrs, nil)
   322  }
   323  
   324  func (p *packerMP) pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage {
   325  	msgs := make([]*bgp.BGPMessage, 0, p.packer.total)
   326  
   327  	for _, path := range p.withdrawals {
   328  		nlris := []bgp.AddrPrefixInterface{path.GetNlri()}
   329  		msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeMpUnreachNLRI(nlris)}, nil))
   330  	}
   331  
   332  	for _, path := range p.paths {
   333  		msgs = append(msgs, createMPReachMessage(path))
   334  	}
   335  
   336  	if p.eof {
   337  		msgs = append(msgs, bgp.NewEndOfRib(p.family))
   338  	}
   339  	return msgs
   340  }
   341  
   342  func newPackerMP(f bgp.RouteFamily) *packerMP {
   343  	return &packerMP{
   344  		packer: packer{
   345  			family: f,
   346  		},
   347  		withdrawals: make([]*Path, 0),
   348  		paths:       make([]*Path, 0),
   349  	}
   350  }
   351  
   352  type packerV4 struct {
   353  	packer
   354  	hashmap     map[uint32][]*cage
   355  	mpPaths     []*Path
   356  	withdrawals []*Path
   357  }
   358  
   359  func (p *packerV4) add(path *Path) {
   360  	p.packer.total++
   361  
   362  	if path.IsEOR() {
   363  		p.packer.eof = true
   364  		return
   365  	}
   366  
   367  	if path.IsWithdraw {
   368  		p.withdrawals = append(p.withdrawals, path)
   369  		return
   370  	}
   371  
   372  	if path.GetNexthop().To4() == nil {
   373  		// RFC 5549
   374  		p.mpPaths = append(p.mpPaths, path)
   375  		return
   376  	}
   377  
   378  	key := path.GetHash()
   379  	attrsB := bytes.NewBuffer(make([]byte, 0))
   380  	for _, v := range path.GetPathAttrs() {
   381  		b, _ := v.Serialize()
   382  		attrsB.Write(b)
   383  	}
   384  
   385  	if cages, y := p.hashmap[key]; y {
   386  		added := false
   387  		for _, c := range cages {
   388  			if bytes.Equal(c.attrsBytes, attrsB.Bytes()) {
   389  				c.paths = append(c.paths, path)
   390  				added = true
   391  				break
   392  			}
   393  		}
   394  		if !added {
   395  			p.hashmap[key] = append(p.hashmap[key], newCage(attrsB.Bytes(), path))
   396  		}
   397  	} else {
   398  		p.hashmap[key] = []*cage{newCage(attrsB.Bytes(), path)}
   399  	}
   400  }
   401  
   402  func (p *packerV4) pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage {
   403  	split := func(max int, paths []*Path) ([]*bgp.IPAddrPrefix, []*Path) {
   404  		if max > len(paths) {
   405  			max = len(paths)
   406  		}
   407  		nlris := make([]*bgp.IPAddrPrefix, 0, max)
   408  		i := 0
   409  		for ; i < max; i++ {
   410  			nlris = append(nlris, paths[i].GetNlri().(*bgp.IPAddrPrefix))
   411  		}
   412  		return nlris, paths[i:]
   413  	}
   414  	addpathNLRILen := 0
   415  	if bgp.IsAddPathEnabled(false, p.packer.family, options) {
   416  		addpathNLRILen = 4
   417  	}
   418  	// Header + Update (WithdrawnRoutesLen +
   419  	// TotalPathAttributeLen + attributes + maxlen of NLRI).
   420  	// the max size of NLRI is 5bytes (plus 4bytes with addpath enabled)
   421  	maxNLRIs := func(attrsLen int) int {
   422  		return (bgp.BGP_MAX_MESSAGE_LENGTH - (19 + 2 + 2 + attrsLen)) / (5 + addpathNLRILen)
   423  	}
   424  
   425  	loop := func(attrsLen int, paths []*Path, cb func([]*bgp.IPAddrPrefix)) {
   426  		max := maxNLRIs(attrsLen)
   427  		var nlris []*bgp.IPAddrPrefix
   428  		for {
   429  			nlris, paths = split(max, paths)
   430  			if len(nlris) == 0 {
   431  				break
   432  			}
   433  			cb(nlris)
   434  		}
   435  	}
   436  
   437  	msgs := make([]*bgp.BGPMessage, 0, p.packer.total)
   438  
   439  	loop(0, p.withdrawals, func(nlris []*bgp.IPAddrPrefix) {
   440  		msgs = append(msgs, bgp.NewBGPUpdateMessage(nlris, nil, nil))
   441  	})
   442  
   443  	for _, cages := range p.hashmap {
   444  		for _, c := range cages {
   445  			paths := c.paths
   446  
   447  			attrs := paths[0].GetPathAttrs()
   448  			// we can apply a fix here when gobgp receives from MP peer
   449  			// and propagtes to non-MP peer
   450  			// we should make sure that next-hop exists in pathattrs
   451  			// while we build the update message
   452  			// we do not want to modify the `path` though
   453  			if paths[0].getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP) == nil {
   454  				attrs = append(attrs, bgp.NewPathAttributeNextHop(paths[0].GetNexthop().String()))
   455  			}
   456  			// if we have ever reach here
   457  			// there is no point keeping MP_REACH_NLRI in the announcement
   458  			attrs_without_mp := make([]bgp.PathAttributeInterface, 0, len(attrs))
   459  			for _, attr := range attrs {
   460  				if attr.GetType() != bgp.BGP_ATTR_TYPE_MP_REACH_NLRI {
   461  					attrs_without_mp = append(attrs_without_mp, attr)
   462  				}
   463  			}
   464  			attrsLen := 0
   465  			for _, a := range attrs_without_mp {
   466  				attrsLen += a.Len()
   467  			}
   468  
   469  			loop(attrsLen, paths, func(nlris []*bgp.IPAddrPrefix) {
   470  				msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, attrs_without_mp, nlris))
   471  			})
   472  		}
   473  	}
   474  
   475  	for _, path := range p.mpPaths {
   476  		msgs = append(msgs, createMPReachMessage(path))
   477  	}
   478  
   479  	if p.eof {
   480  		msgs = append(msgs, bgp.NewEndOfRib(p.family))
   481  	}
   482  	return msgs
   483  }
   484  
   485  func newPackerV4(f bgp.RouteFamily) *packerV4 {
   486  	return &packerV4{
   487  		packer: packer{
   488  			family: f,
   489  		},
   490  		hashmap:     make(map[uint32][]*cage),
   491  		withdrawals: make([]*Path, 0),
   492  		mpPaths:     make([]*Path, 0),
   493  	}
   494  }
   495  
   496  func newPacker(f bgp.RouteFamily) packerInterface {
   497  	switch f {
   498  	case bgp.RF_IPv4_UC:
   499  		return newPackerV4(bgp.RF_IPv4_UC)
   500  	default:
   501  		return newPackerMP(f)
   502  	}
   503  }
   504  
   505  func CreateUpdateMsgFromPaths(pathList []*Path, options ...*bgp.MarshallingOption) []*bgp.BGPMessage {
   506  	msgs := make([]*bgp.BGPMessage, 0, len(pathList))
   507  
   508  	m := make(map[bgp.RouteFamily]packerInterface)
   509  	for _, path := range pathList {
   510  		f := path.GetRouteFamily()
   511  		if _, y := m[f]; !y {
   512  			m[f] = newPacker(f)
   513  		}
   514  		m[f].add(path)
   515  	}
   516  
   517  	for _, p := range m {
   518  		msgs = append(msgs, p.pack(options...)...)
   519  	}
   520  	return msgs
   521  }