summaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/table/path.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/pkg/table/path.go')
-rw-r--r--internal/pkg/table/path.go1179
1 files changed, 1179 insertions, 0 deletions
diff --git a/internal/pkg/table/path.go b/internal/pkg/table/path.go
new file mode 100644
index 00000000..14bbe6ae
--- /dev/null
+++ b/internal/pkg/table/path.go
@@ -0,0 +1,1179 @@
+// 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"
+ "encoding/json"
+ "fmt"
+ "math"
+ "net"
+ "sort"
+ "time"
+
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ log "github.com/sirupsen/logrus"
+)
+
+const (
+ DEFAULT_LOCAL_PREF = 100
+)
+
+type Bitmap struct {
+ bitmap []uint64
+}
+
+func (b *Bitmap) Flag(i uint) {
+ b.bitmap[i/64] |= 1 << uint(i%64)
+}
+
+func (b *Bitmap) Unflag(i uint) {
+ b.bitmap[i/64] &^= 1 << uint(i%64)
+}
+
+func (b *Bitmap) GetFlag(i uint) bool {
+ return b.bitmap[i/64]&(1<<uint(i%64)) > 0
+}
+
+func (b *Bitmap) FindandSetZeroBit() (uint, error) {
+ for i := 0; i < len(b.bitmap); i++ {
+ if b.bitmap[i] == math.MaxUint64 {
+ continue
+ }
+ // replace this with TrailingZero64() when gobgp drops go 1.8 support.
+ for j := 0; j < 64; j++ {
+ v := ^b.bitmap[i]
+ if v&(1<<uint64(j)) > 0 {
+ r := i*64 + j
+ b.Flag(uint(r))
+ return uint(r), nil
+ }
+ }
+ }
+ return 0, fmt.Errorf("no space")
+}
+
+func (b *Bitmap) Expand() {
+ old := b.bitmap
+ new := make([]uint64, len(old)+1)
+ for i := 0; i < len(old); i++ {
+ new[i] = old[i]
+ }
+ b.bitmap = new
+}
+
+func NewBitmap(size int) *Bitmap {
+ b := &Bitmap{}
+ if size != 0 {
+ b.bitmap = make([]uint64, (size+64-1)/64)
+ }
+ return b
+}
+
+type originInfo struct {
+ nlri bgp.AddrPrefixInterface
+ source *PeerInfo
+ timestamp int64
+ validation *Validation
+ noImplicitWithdraw bool
+ isFromExternal bool
+ eor bool
+ stale bool
+}
+
+type RpkiValidationReasonType string
+
+const (
+ RPKI_VALIDATION_REASON_TYPE_NONE RpkiValidationReasonType = "none"
+ RPKI_VALIDATION_REASON_TYPE_AS RpkiValidationReasonType = "as"
+ RPKI_VALIDATION_REASON_TYPE_LENGTH RpkiValidationReasonType = "length"
+)
+
+var RpkiValidationReasonTypeToIntMap = map[RpkiValidationReasonType]int{
+ RPKI_VALIDATION_REASON_TYPE_NONE: 0,
+ RPKI_VALIDATION_REASON_TYPE_AS: 1,
+ RPKI_VALIDATION_REASON_TYPE_LENGTH: 2,
+}
+
+func (v RpkiValidationReasonType) ToInt() int {
+ i, ok := RpkiValidationReasonTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToRpkiValidationReasonTypeMap = map[int]RpkiValidationReasonType{
+ 0: RPKI_VALIDATION_REASON_TYPE_NONE,
+ 1: RPKI_VALIDATION_REASON_TYPE_AS,
+ 2: RPKI_VALIDATION_REASON_TYPE_LENGTH,
+}
+
+type Validation struct {
+ Status config.RpkiValidationResultType
+ Reason RpkiValidationReasonType
+ Matched []*ROA
+ UnmatchedAs []*ROA
+ UnmatchedLength []*ROA
+}
+
+type Path struct {
+ info *originInfo
+ parent *Path
+ pathAttrs []bgp.PathAttributeInterface
+ dels []bgp.BGPAttrType
+ attrsHash uint32
+ aslooped bool
+ reason BestPathReason
+
+ // For BGP Nexthop Tracking, this field shows if nexthop is invalidated by IGP.
+ IsNexthopInvalid bool
+ IsWithdraw bool
+}
+
+func NewPath(source *PeerInfo, nlri bgp.AddrPrefixInterface, isWithdraw bool, pattrs []bgp.PathAttributeInterface, timestamp time.Time, noImplicitWithdraw bool) *Path {
+ if !isWithdraw && pattrs == nil {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": nlri.String(),
+ }).Error("Need to provide path attributes for non-withdrawn path.")
+ return nil
+ }
+
+ return &Path{
+ info: &originInfo{
+ nlri: nlri,
+ source: source,
+ timestamp: timestamp.Unix(),
+ noImplicitWithdraw: noImplicitWithdraw,
+ },
+ IsWithdraw: isWithdraw,
+ pathAttrs: pattrs,
+ }
+}
+
+func NewEOR(family bgp.RouteFamily) *Path {
+ afi, safi := bgp.RouteFamilyToAfiSafi(family)
+ nlri, _ := bgp.NewPrefixFromRouteFamily(afi, safi)
+ return &Path{
+ info: &originInfo{
+ nlri: nlri,
+ eor: true,
+ },
+ }
+}
+
+func (path *Path) IsEOR() bool {
+ if path.info != nil && path.info.eor {
+ return true
+ }
+ return false
+}
+
+func cloneAsPath(asAttr *bgp.PathAttributeAsPath) *bgp.PathAttributeAsPath {
+ newASparams := make([]bgp.AsPathParamInterface, len(asAttr.Value))
+ for i, param := range asAttr.Value {
+ asList := param.GetAS()
+ as := make([]uint32, len(asList))
+ copy(as, asList)
+ newASparams[i] = bgp.NewAs4PathParam(param.GetType(), as)
+ }
+ return bgp.NewPathAttributeAsPath(newASparams)
+}
+
+func UpdatePathAttrs(global *config.Global, peer *config.Neighbor, info *PeerInfo, original *Path) *Path {
+ if peer.RouteServer.Config.RouteServerClient {
+ return original
+ }
+ path := original.Clone(original.IsWithdraw)
+
+ for _, a := range path.GetPathAttrs() {
+ if _, y := bgp.PathAttrFlags[a.GetType()]; !y {
+ if a.GetFlags()&bgp.BGP_ATTR_FLAG_TRANSITIVE == 0 {
+ path.delPathAttr(a.GetType())
+ }
+ } else {
+ switch a.GetType() {
+ case bgp.BGP_ATTR_TYPE_CLUSTER_LIST, bgp.BGP_ATTR_TYPE_ORIGINATOR_ID:
+ if !(peer.State.PeerType == config.PEER_TYPE_INTERNAL && peer.RouteReflector.Config.RouteReflectorClient) {
+ // send these attributes to only rr clients
+ path.delPathAttr(a.GetType())
+ }
+ }
+ }
+ }
+
+ localAddress := info.LocalAddress
+ nexthop := path.GetNexthop()
+ if peer.State.PeerType == config.PEER_TYPE_EXTERNAL {
+ // NEXTHOP handling
+ if !path.IsLocal() || nexthop.IsUnspecified() {
+ path.SetNexthop(localAddress)
+ }
+
+ // remove-private-as handling
+ path.RemovePrivateAS(peer.Config.LocalAs, peer.State.RemovePrivateAs)
+
+ // AS_PATH handling
+ confed := peer.IsConfederationMember(global)
+ path.PrependAsn(peer.Config.LocalAs, 1, confed)
+ if !confed {
+ path.removeConfedAs()
+ }
+
+ // MED Handling
+ if med := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC); med != nil && !path.IsLocal() {
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ }
+
+ } else if peer.State.PeerType == config.PEER_TYPE_INTERNAL {
+ // NEXTHOP handling for iBGP
+ // if the path generated locally set local address as nexthop.
+ // if not, don't modify it.
+ // TODO: NEXT-HOP-SELF support
+ if path.IsLocal() && nexthop.IsUnspecified() {
+ path.SetNexthop(localAddress)
+ }
+
+ // AS_PATH handling for iBGP
+ // if the path has AS_PATH path attribute, don't modify it.
+ // if not, attach *empty* AS_PATH path attribute.
+ if nh := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH); nh == nil {
+ path.PrependAsn(0, 0, false)
+ }
+
+ // For iBGP peers we are required to send local-pref attribute
+ // for connected or local prefixes.
+ // We set default local-pref 100.
+ if pref := path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF); pref == nil {
+ path.setPathAttr(bgp.NewPathAttributeLocalPref(DEFAULT_LOCAL_PREF))
+ }
+
+ // RFC4456: BGP Route Reflection
+ // 8. Avoiding Routing Information Loops
+ info := path.GetSource()
+ if peer.RouteReflector.Config.RouteReflectorClient {
+ // This attribute will carry the BGP Identifier of the originator of the route in the local AS.
+ // A BGP speaker SHOULD NOT create an ORIGINATOR_ID attribute if one already exists.
+ //
+ // RFC4684 3.2 Intra-AS VPN Route Distribution
+ // When advertising RT membership NLRI to a route-reflector client,
+ // the Originator attribute shall be set to the router-id of the
+ // advertiser, and the Next-hop attribute shall be set of the local
+ // address for that session.
+ if path.GetRouteFamily() == bgp.RF_RTC_UC {
+ path.SetNexthop(localAddress)
+ path.setPathAttr(bgp.NewPathAttributeOriginatorId(info.LocalID.String()))
+ } else if path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGINATOR_ID) == nil {
+ if path.IsLocal() {
+ path.setPathAttr(bgp.NewPathAttributeOriginatorId(global.Config.RouterId))
+ } else {
+ path.setPathAttr(bgp.NewPathAttributeOriginatorId(info.ID.String()))
+ }
+ }
+ // When an RR reflects a route, it MUST prepend the local CLUSTER_ID to the CLUSTER_LIST.
+ // If the CLUSTER_LIST is empty, it MUST create a new one.
+ clusterID := string(peer.RouteReflector.State.RouteReflectorClusterId)
+ if p := path.getPathAttr(bgp.BGP_ATTR_TYPE_CLUSTER_LIST); p == nil {
+ path.setPathAttr(bgp.NewPathAttributeClusterList([]string{clusterID}))
+ } else {
+ clusterList := p.(*bgp.PathAttributeClusterList)
+ newClusterList := make([]string, 0, len(clusterList.Value))
+ for _, ip := range clusterList.Value {
+ newClusterList = append(newClusterList, ip.String())
+ }
+ path.setPathAttr(bgp.NewPathAttributeClusterList(append([]string{clusterID}, newClusterList...)))
+ }
+ }
+
+ } else {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ "Key": peer.State.NeighborAddress,
+ }).Warnf("invalid peer type: %d", peer.State.PeerType)
+ }
+ return path
+}
+
+func (path *Path) GetTimestamp() time.Time {
+ return time.Unix(path.OriginInfo().timestamp, 0)
+}
+
+func (path *Path) setTimestamp(t time.Time) {
+ path.OriginInfo().timestamp = t.Unix()
+}
+
+func (path *Path) IsLocal() bool {
+ return path.GetSource().Address == nil
+}
+
+func (path *Path) IsIBGP() bool {
+ return path.GetSource().AS == path.GetSource().LocalAS
+}
+
+// create new PathAttributes
+func (path *Path) Clone(isWithdraw bool) *Path {
+ return &Path{
+ parent: path,
+ IsWithdraw: isWithdraw,
+ IsNexthopInvalid: path.IsNexthopInvalid,
+ attrsHash: path.attrsHash,
+ }
+}
+
+func (path *Path) root() *Path {
+ p := path
+ for p.parent != nil {
+ p = p.parent
+ }
+ return p
+}
+
+func (path *Path) OriginInfo() *originInfo {
+ return path.root().info
+}
+
+func (path *Path) NoImplicitWithdraw() bool {
+ return path.OriginInfo().noImplicitWithdraw
+}
+
+func (path *Path) Validation() *Validation {
+ return path.OriginInfo().validation
+}
+
+func (path *Path) ValidationStatus() config.RpkiValidationResultType {
+ if v := path.OriginInfo().validation; v != nil {
+ return v.Status
+ } else {
+ return config.RPKI_VALIDATION_RESULT_TYPE_NONE
+ }
+}
+
+func (path *Path) SetValidation(v *Validation) {
+ path.OriginInfo().validation = v
+}
+
+func (path *Path) IsFromExternal() bool {
+ return path.OriginInfo().isFromExternal
+}
+
+func (path *Path) SetIsFromExternal(y bool) {
+ path.OriginInfo().isFromExternal = y
+}
+
+func (path *Path) GetRouteFamily() bgp.RouteFamily {
+ return bgp.AfiSafiToRouteFamily(path.OriginInfo().nlri.AFI(), path.OriginInfo().nlri.SAFI())
+}
+
+func (path *Path) SetSource(source *PeerInfo) {
+ path.OriginInfo().source = source
+}
+func (path *Path) GetSource() *PeerInfo {
+ return path.OriginInfo().source
+}
+
+func (path *Path) MarkStale(s bool) {
+ path.OriginInfo().stale = s
+}
+
+func (path *Path) IsStale() bool {
+ return path.OriginInfo().stale
+}
+
+func (path *Path) IsAsLooped() bool {
+ return path.aslooped
+}
+
+func (path *Path) SetAsLooped(y bool) {
+ path.aslooped = y
+}
+
+func (path *Path) IsLLGRStale() bool {
+ for _, c := range path.GetCommunities() {
+ if c == uint32(bgp.COMMUNITY_LLGR_STALE) {
+ return true
+ }
+ }
+ return false
+}
+
+func (path *Path) GetSourceAs() uint32 {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ if attr != nil {
+ asPathParam := attr.(*bgp.PathAttributeAsPath).Value
+ if len(asPathParam) == 0 {
+ return 0
+ }
+ asList := asPathParam[len(asPathParam)-1].GetAS()
+ if len(asList) == 0 {
+ return 0
+ }
+ return asList[len(asList)-1]
+ }
+ return 0
+}
+
+func (path *Path) GetNexthop() net.IP {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ if attr != nil {
+ return attr.(*bgp.PathAttributeNextHop).Value
+ }
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ if attr != nil {
+ return attr.(*bgp.PathAttributeMpReachNLRI).Nexthop
+ }
+ return net.IP{}
+}
+
+func (path *Path) SetNexthop(nexthop net.IP) {
+ if path.GetRouteFamily() == bgp.RF_IPv4_UC && nexthop.To4() == nil {
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ mpreach := bgp.NewPathAttributeMpReachNLRI(nexthop.String(), []bgp.AddrPrefixInterface{path.GetNlri()})
+ path.setPathAttr(mpreach)
+ return
+ }
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ if attr != nil {
+ path.setPathAttr(bgp.NewPathAttributeNextHop(nexthop.String()))
+ }
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ if attr != nil {
+ oldNlri := attr.(*bgp.PathAttributeMpReachNLRI)
+ path.setPathAttr(bgp.NewPathAttributeMpReachNLRI(nexthop.String(), oldNlri.Value))
+ }
+}
+
+func (path *Path) GetNlri() bgp.AddrPrefixInterface {
+ return path.OriginInfo().nlri
+}
+
+type PathAttrs []bgp.PathAttributeInterface
+
+func (a PathAttrs) Len() int {
+ return len(a)
+}
+
+func (a PathAttrs) Swap(i, j int) {
+ a[i], a[j] = a[j], a[i]
+}
+
+func (a PathAttrs) Less(i, j int) bool {
+ return a[i].GetType() < a[j].GetType()
+}
+
+func (path *Path) GetPathAttrs() []bgp.PathAttributeInterface {
+ deleted := NewBitmap(math.MaxUint8)
+ modified := make(map[uint]bgp.PathAttributeInterface)
+ p := path
+ for {
+ for _, t := range p.dels {
+ deleted.Flag(uint(t))
+ }
+ if p.parent == nil {
+ list := PathAttrs(make([]bgp.PathAttributeInterface, 0, len(p.pathAttrs)))
+ // we assume that the original pathAttrs are
+ // in order, that is, other bgp speakers send
+ // attributes in order.
+ for _, a := range p.pathAttrs {
+ typ := uint(a.GetType())
+ if m, ok := modified[typ]; ok {
+ list = append(list, m)
+ delete(modified, typ)
+ } else if !deleted.GetFlag(typ) {
+ list = append(list, a)
+ }
+ }
+ if len(modified) > 0 {
+ // Huh, some attributes were newly
+ // added. So we need to sort...
+ for _, m := range modified {
+ list = append(list, m)
+ }
+ sort.Sort(list)
+ }
+ return list
+ } else {
+ for _, a := range p.pathAttrs {
+ typ := uint(a.GetType())
+ if _, ok := modified[typ]; !deleted.GetFlag(typ) && !ok {
+ modified[typ] = a
+ }
+ }
+ }
+ p = p.parent
+ }
+}
+
+func (path *Path) getPathAttr(typ bgp.BGPAttrType) bgp.PathAttributeInterface {
+ p := path
+ for {
+ for _, t := range p.dels {
+ if t == typ {
+ return nil
+ }
+ }
+ for _, a := range p.pathAttrs {
+ if a.GetType() == typ {
+ return a
+ }
+ }
+ if p.parent == nil {
+ return nil
+ }
+ p = p.parent
+ }
+}
+
+func (path *Path) setPathAttr(a bgp.PathAttributeInterface) {
+ if len(path.pathAttrs) == 0 {
+ path.pathAttrs = []bgp.PathAttributeInterface{a}
+ } else {
+ for i, b := range path.pathAttrs {
+ if a.GetType() == b.GetType() {
+ path.pathAttrs[i] = a
+ return
+ }
+ }
+ path.pathAttrs = append(path.pathAttrs, a)
+ }
+}
+
+func (path *Path) delPathAttr(typ bgp.BGPAttrType) {
+ if len(path.dels) == 0 {
+ path.dels = []bgp.BGPAttrType{typ}
+ } else {
+ path.dels = append(path.dels, typ)
+ }
+}
+
+// return Path's string representation
+func (path *Path) String() string {
+ s := bytes.NewBuffer(make([]byte, 0, 64))
+ if path.IsEOR() {
+ s.WriteString(fmt.Sprintf("{ %s EOR | src: %s }", path.GetRouteFamily(), path.GetSource()))
+ return s.String()
+ }
+ s.WriteString(fmt.Sprintf("{ %s | ", path.getPrefix()))
+ s.WriteString(fmt.Sprintf("src: %s", path.GetSource()))
+ s.WriteString(fmt.Sprintf(", nh: %s", path.GetNexthop()))
+ if path.IsNexthopInvalid {
+ s.WriteString(" (not reachable)")
+ }
+ if path.IsWithdraw {
+ s.WriteString(", withdraw")
+ }
+ s.WriteString(" }")
+ return s.String()
+}
+
+func (path *Path) getPrefix() string {
+ return path.GetNlri().String()
+}
+
+func (path *Path) GetAsPath() *bgp.PathAttributeAsPath {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ if attr != nil {
+ return attr.(*bgp.PathAttributeAsPath)
+ }
+ return nil
+}
+
+// GetAsPathLen returns the number of AS_PATH
+func (path *Path) GetAsPathLen() int {
+
+ var length int = 0
+ if aspath := path.GetAsPath(); aspath != nil {
+ for _, as := range aspath.Value {
+ length += as.ASLen()
+ }
+ }
+ return length
+}
+
+func (path *Path) GetAsString() string {
+ s := bytes.NewBuffer(make([]byte, 0, 64))
+ if aspath := path.GetAsPath(); aspath != nil {
+ return bgp.AsPathString(aspath)
+ }
+ return s.String()
+}
+
+func (path *Path) GetAsList() []uint32 {
+ return path.getAsListOfSpecificType(true, true)
+
+}
+
+func (path *Path) GetAsSeqList() []uint32 {
+ return path.getAsListOfSpecificType(true, false)
+
+}
+
+func (path *Path) getAsListOfSpecificType(getAsSeq, getAsSet bool) []uint32 {
+ asList := []uint32{}
+ if aspath := path.GetAsPath(); aspath != nil {
+ for _, param := range aspath.Value {
+ segType := param.GetType()
+ if getAsSeq && segType == bgp.BGP_ASPATH_ATTR_TYPE_SEQ {
+ asList = append(asList, param.GetAS()...)
+ continue
+ }
+ if getAsSet && segType == bgp.BGP_ASPATH_ATTR_TYPE_SET {
+ asList = append(asList, param.GetAS()...)
+ } else {
+ asList = append(asList, 0)
+ }
+ }
+ }
+ return asList
+}
+
+func (path *Path) GetLabelString() string {
+ return bgp.LabelString(path.GetNlri())
+}
+
+// PrependAsn prepends AS number.
+// This function updates the AS_PATH attribute as follows.
+// (If the peer is in the confederation member AS,
+// replace AS_SEQUENCE in the following sentence with AS_CONFED_SEQUENCE.)
+// 1) if the first path segment of the AS_PATH is of type
+// AS_SEQUENCE, the local system prepends the specified AS num as
+// the last element of the sequence (put it in the left-most
+// position with respect to the position of octets in the
+// protocol message) the specified number of times.
+// If the act of prepending will cause an overflow in the AS_PATH
+// segment (i.e., more than 255 ASes),
+// it SHOULD prepend a new segment of type AS_SEQUENCE
+// and prepend its own AS number to this new segment.
+//
+// 2) if the first path segment of the AS_PATH is of other than type
+// AS_SEQUENCE, the local system prepends a new path segment of type
+// AS_SEQUENCE to the AS_PATH, including the specified AS number in
+// that segment.
+//
+// 3) if the AS_PATH is empty, the local system creates a path
+// segment of type AS_SEQUENCE, places the specified AS number
+// into that segment, and places that segment into the AS_PATH.
+func (path *Path) PrependAsn(asn uint32, repeat uint8, confed bool) {
+ var segType uint8
+ if confed {
+ segType = bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ
+ } else {
+ segType = bgp.BGP_ASPATH_ATTR_TYPE_SEQ
+ }
+
+ original := path.GetAsPath()
+
+ asns := make([]uint32, repeat)
+ for i := range asns {
+ asns[i] = asn
+ }
+
+ var asPath *bgp.PathAttributeAsPath
+ if original == nil {
+ asPath = bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{})
+ } else {
+ asPath = cloneAsPath(original)
+ }
+
+ if len(asPath.Value) > 0 {
+ param := asPath.Value[0]
+ asList := param.GetAS()
+ if param.GetType() == segType {
+ if int(repeat)+len(asList) > 255 {
+ repeat = uint8(255 - len(asList))
+ }
+ newAsList := append(asns[:int(repeat)], asList...)
+ asPath.Value[0] = bgp.NewAs4PathParam(segType, newAsList)
+ asns = asns[int(repeat):]
+ }
+ }
+
+ if len(asns) > 0 {
+ p := bgp.NewAs4PathParam(segType, asns)
+ asPath.Value = append([]bgp.AsPathParamInterface{p}, asPath.Value...)
+ }
+ path.setPathAttr(asPath)
+}
+
+func isPrivateAS(as uint32) bool {
+ return (64512 <= as && as <= 65534) || (4200000000 <= as && as <= 4294967294)
+}
+
+func (path *Path) RemovePrivateAS(localAS uint32, option config.RemovePrivateAsOption) {
+ original := path.GetAsPath()
+ if original == nil {
+ return
+ }
+ switch option {
+ case config.REMOVE_PRIVATE_AS_OPTION_ALL, config.REMOVE_PRIVATE_AS_OPTION_REPLACE:
+ newASParams := make([]bgp.AsPathParamInterface, 0, len(original.Value))
+ for _, param := range original.Value {
+ asList := param.GetAS()
+ newASParam := make([]uint32, 0, len(asList))
+ for _, as := range asList {
+ if isPrivateAS(as) {
+ if option == config.REMOVE_PRIVATE_AS_OPTION_REPLACE {
+ newASParam = append(newASParam, localAS)
+ }
+ } else {
+ newASParam = append(newASParam, as)
+ }
+ }
+ if len(newASParam) > 0 {
+ newASParams = append(newASParams, bgp.NewAs4PathParam(param.GetType(), newASParam))
+ }
+ }
+ path.setPathAttr(bgp.NewPathAttributeAsPath(newASParams))
+ }
+}
+
+func (path *Path) removeConfedAs() {
+ original := path.GetAsPath()
+ if original == nil {
+ return
+ }
+ newAsParams := make([]bgp.AsPathParamInterface, 0, len(original.Value))
+ for _, param := range original.Value {
+ switch param.GetType() {
+ case bgp.BGP_ASPATH_ATTR_TYPE_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_SET:
+ newAsParams = append(newAsParams, param)
+ }
+ }
+ path.setPathAttr(bgp.NewPathAttributeAsPath(newAsParams))
+}
+
+func (path *Path) ReplaceAS(localAS, peerAS uint32) *Path {
+ original := path.GetAsPath()
+ if original == nil {
+ return path
+ }
+ newASParams := make([]bgp.AsPathParamInterface, 0, len(original.Value))
+ changed := false
+ for _, param := range original.Value {
+ segType := param.GetType()
+ asList := param.GetAS()
+ newASParam := make([]uint32, 0, len(asList))
+ for _, as := range asList {
+ if as == peerAS {
+ as = localAS
+ changed = true
+ }
+ newASParam = append(newASParam, as)
+ }
+ newASParams = append(newASParams, bgp.NewAs4PathParam(segType, newASParam))
+ }
+ if changed {
+ path = path.Clone(path.IsWithdraw)
+ path.setPathAttr(bgp.NewPathAttributeAsPath(newASParams))
+ }
+ return path
+}
+
+func (path *Path) GetCommunities() []uint32 {
+ communityList := []uint32{}
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES); attr != nil {
+ communities := attr.(*bgp.PathAttributeCommunities)
+ communityList = append(communityList, communities.Value...)
+ }
+ return communityList
+}
+
+// SetCommunities adds or replaces communities with new ones.
+// If the length of communities is 0 and doReplace is true, it clears communities.
+func (path *Path) SetCommunities(communities []uint32, doReplace bool) {
+
+ if len(communities) == 0 && doReplace {
+ // clear communities
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ return
+ }
+
+ newList := make([]uint32, 0)
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ if attr != nil {
+ c := attr.(*bgp.PathAttributeCommunities)
+ if doReplace {
+ newList = append(newList, communities...)
+ } else {
+ newList = append(newList, c.Value...)
+ newList = append(newList, communities...)
+ }
+ } else {
+ newList = append(newList, communities...)
+ }
+ path.setPathAttr(bgp.NewPathAttributeCommunities(newList))
+
+}
+
+// RemoveCommunities removes specific communities.
+// If the length of communities is 0, it does nothing.
+// If all communities are removed, it removes Communities path attribute itself.
+func (path *Path) RemoveCommunities(communities []uint32) int {
+
+ if len(communities) == 0 {
+ // do nothing
+ return 0
+ }
+
+ find := func(val uint32) bool {
+ for _, com := range communities {
+ if com == val {
+ return true
+ }
+ }
+ return false
+ }
+
+ count := 0
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ if attr != nil {
+ newList := make([]uint32, 0)
+ c := attr.(*bgp.PathAttributeCommunities)
+
+ for _, value := range c.Value {
+ if find(value) {
+ count += 1
+ } else {
+ newList = append(newList, value)
+ }
+ }
+
+ if len(newList) != 0 {
+ path.setPathAttr(bgp.NewPathAttributeCommunities(newList))
+ } else {
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ }
+ }
+ return count
+}
+
+func (path *Path) GetExtCommunities() []bgp.ExtendedCommunityInterface {
+ eCommunityList := make([]bgp.ExtendedCommunityInterface, 0)
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES); attr != nil {
+ eCommunities := attr.(*bgp.PathAttributeExtendedCommunities).Value
+ eCommunityList = append(eCommunityList, eCommunities...)
+ }
+ return eCommunityList
+}
+
+func (path *Path) SetExtCommunities(exts []bgp.ExtendedCommunityInterface, doReplace bool) {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
+ if attr != nil {
+ l := attr.(*bgp.PathAttributeExtendedCommunities).Value
+ if doReplace {
+ l = exts
+ } else {
+ l = append(l, exts...)
+ }
+ path.setPathAttr(bgp.NewPathAttributeExtendedCommunities(l))
+ } else {
+ path.setPathAttr(bgp.NewPathAttributeExtendedCommunities(exts))
+ }
+}
+
+func (path *Path) GetLargeCommunities() []*bgp.LargeCommunity {
+ if a := path.getPathAttr(bgp.BGP_ATTR_TYPE_LARGE_COMMUNITY); a != nil {
+ v := a.(*bgp.PathAttributeLargeCommunities).Values
+ ret := make([]*bgp.LargeCommunity, 0, len(v))
+ ret = append(ret, v...)
+ return ret
+ }
+ return nil
+}
+
+func (path *Path) SetLargeCommunities(cs []*bgp.LargeCommunity, doReplace bool) {
+ a := path.getPathAttr(bgp.BGP_ATTR_TYPE_LARGE_COMMUNITY)
+ if a == nil || doReplace {
+ path.setPathAttr(bgp.NewPathAttributeLargeCommunities(cs))
+ } else {
+ l := a.(*bgp.PathAttributeLargeCommunities).Values
+ path.setPathAttr(bgp.NewPathAttributeLargeCommunities(append(l, cs...)))
+ }
+}
+
+func (path *Path) GetMed() (uint32, error) {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ if attr == nil {
+ return 0, fmt.Errorf("no med path attr")
+ }
+ return attr.(*bgp.PathAttributeMultiExitDisc).Value, nil
+}
+
+// SetMed replace, add or subtraction med with new ones.
+func (path *Path) SetMed(med int64, doReplace bool) error {
+ parseMed := func(orgMed uint32, med int64, doReplace bool) (*bgp.PathAttributeMultiExitDisc, error) {
+ if doReplace {
+ return bgp.NewPathAttributeMultiExitDisc(uint32(med)), nil
+ }
+
+ medVal := int64(orgMed) + med
+ if medVal < 0 {
+ return nil, fmt.Errorf("med value invalid. it's underflow threshold: %v", medVal)
+ } else if medVal > int64(math.MaxUint32) {
+ return nil, fmt.Errorf("med value invalid. it's overflow threshold: %v", medVal)
+ }
+
+ return bgp.NewPathAttributeMultiExitDisc(uint32(int64(orgMed) + med)), nil
+ }
+
+ m := uint32(0)
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC); attr != nil {
+ m = attr.(*bgp.PathAttributeMultiExitDisc).Value
+ }
+ newMed, err := parseMed(m, med, doReplace)
+ if err != nil {
+ return err
+ }
+ path.setPathAttr(newMed)
+ return nil
+}
+
+func (path *Path) RemoveLocalPref() {
+ if path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF) != nil {
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF)
+ }
+}
+
+func (path *Path) GetOriginatorID() net.IP {
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGINATOR_ID); attr != nil {
+ return attr.(*bgp.PathAttributeOriginatorId).Value
+ }
+ return nil
+}
+
+func (path *Path) GetClusterList() []net.IP {
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_CLUSTER_LIST); attr != nil {
+ return attr.(*bgp.PathAttributeClusterList).Value
+ }
+ return nil
+}
+
+func (path *Path) GetOrigin() (uint8, error) {
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN); attr != nil {
+ return attr.(*bgp.PathAttributeOrigin).Value, nil
+ }
+ return 0, fmt.Errorf("no origin path attr")
+}
+
+func (path *Path) GetLocalPref() (uint32, error) {
+ lp := uint32(DEFAULT_LOCAL_PREF)
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF)
+ if attr != nil {
+ lp = attr.(*bgp.PathAttributeLocalPref).Value
+ }
+ return lp, nil
+}
+
+func (lhs *Path) Equal(rhs *Path) bool {
+ if rhs == nil {
+ return false
+ }
+
+ if lhs.GetSource() != rhs.GetSource() {
+ return false
+ }
+
+ pattrs := func(arg []bgp.PathAttributeInterface) []byte {
+ ret := make([]byte, 0)
+ for _, a := range arg {
+ aa, _ := a.Serialize()
+ ret = append(ret, aa...)
+ }
+ return ret
+ }
+ return bytes.Equal(pattrs(lhs.GetPathAttrs()), pattrs(rhs.GetPathAttrs()))
+}
+
+func (path *Path) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Nlri bgp.AddrPrefixInterface `json:"nlri"`
+ PathAttrs []bgp.PathAttributeInterface `json:"attrs"`
+ Age int64 `json:"age"`
+ Withdrawal bool `json:"withdrawal,omitempty"`
+ Validation string `json:"validation,omitempty"`
+ SourceID net.IP `json:"source-id,omitempty"`
+ NeighborIP net.IP `json:"neighbor-ip,omitempty"`
+ Stale bool `json:"stale,omitempty"`
+ UUID string `json:"uuid,omitempty"`
+ ID uint32 `json:"id,omitempty"`
+ }{
+ Nlri: path.GetNlri(),
+ PathAttrs: path.GetPathAttrs(),
+ Age: path.GetTimestamp().Unix(),
+ Withdrawal: path.IsWithdraw,
+ Validation: string(path.ValidationStatus()),
+ SourceID: path.GetSource().ID,
+ NeighborIP: path.GetSource().Address,
+ Stale: path.IsStale(),
+ ID: path.GetNlri().PathIdentifier(),
+ })
+}
+
+func (lhs *Path) Compare(rhs *Path) int {
+ if lhs.IsLocal() && !rhs.IsLocal() {
+ return 1
+ } else if !lhs.IsLocal() && rhs.IsLocal() {
+ return -1
+ }
+
+ if !lhs.IsIBGP() && rhs.IsIBGP() {
+ return 1
+ } else if lhs.IsIBGP() && !rhs.IsIBGP() {
+ return -1
+ }
+
+ lp1, _ := lhs.GetLocalPref()
+ lp2, _ := rhs.GetLocalPref()
+ if lp1 != lp2 {
+ return int(lp1 - lp2)
+ }
+
+ l1 := lhs.GetAsPathLen()
+ l2 := rhs.GetAsPathLen()
+ if l1 != l2 {
+ return int(l2 - l1)
+ }
+
+ o1, _ := lhs.GetOrigin()
+ o2, _ := rhs.GetOrigin()
+ if o1 != o2 {
+ return int(o2 - o1)
+ }
+
+ m1, _ := lhs.GetMed()
+ m2, _ := rhs.GetMed()
+ return int(m2 - m1)
+}
+
+func (v *Vrf) ToGlobalPath(path *Path) error {
+ nlri := path.GetNlri()
+ switch rf := path.GetRouteFamily(); rf {
+ case bgp.RF_IPv4_UC:
+ n := nlri.(*bgp.IPAddrPrefix)
+ pathIdentifier := path.GetNlri().PathIdentifier()
+ path.OriginInfo().nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), v.Rd)
+ path.GetNlri().SetPathIdentifier(pathIdentifier)
+ case bgp.RF_IPv6_UC:
+ n := nlri.(*bgp.IPv6AddrPrefix)
+ pathIdentifier := path.GetNlri().PathIdentifier()
+ path.OriginInfo().nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), v.Rd)
+ path.GetNlri().SetPathIdentifier(pathIdentifier)
+ case bgp.RF_EVPN:
+ n := nlri.(*bgp.EVPNNLRI)
+ switch n.RouteType {
+ case bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT:
+ n.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute).RD = v.Rd
+ case bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG:
+ n.RouteTypeData.(*bgp.EVPNMulticastEthernetTagRoute).RD = v.Rd
+ }
+ default:
+ return fmt.Errorf("unsupported route family for vrf: %s", rf)
+ }
+ path.SetExtCommunities(v.ExportRt, false)
+ return nil
+}
+
+func (p *Path) ToGlobal(vrf *Vrf) *Path {
+ nlri := p.GetNlri()
+ nh := p.GetNexthop()
+ pathId := nlri.PathIdentifier()
+ switch rf := p.GetRouteFamily(); rf {
+ case bgp.RF_IPv4_UC:
+ n := nlri.(*bgp.IPAddrPrefix)
+ nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), vrf.Rd)
+ nlri.SetPathIdentifier(pathId)
+ case bgp.RF_IPv6_UC:
+ n := nlri.(*bgp.IPv6AddrPrefix)
+ nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), vrf.Rd)
+ nlri.SetPathIdentifier(pathId)
+ case bgp.RF_EVPN:
+ n := nlri.(*bgp.EVPNNLRI)
+ switch n.RouteType {
+ case bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT:
+ old := n.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute)
+ new := &bgp.EVPNMacIPAdvertisementRoute{
+ RD: vrf.Rd,
+ ESI: old.ESI,
+ ETag: old.ETag,
+ MacAddressLength: old.MacAddressLength,
+ MacAddress: old.MacAddress,
+ IPAddressLength: old.IPAddressLength,
+ IPAddress: old.IPAddress,
+ Labels: old.Labels,
+ }
+ nlri = bgp.NewEVPNNLRI(n.RouteType, new)
+ case bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG:
+ old := n.RouteTypeData.(*bgp.EVPNMulticastEthernetTagRoute)
+ new := &bgp.EVPNMulticastEthernetTagRoute{
+ RD: vrf.Rd,
+ ETag: old.ETag,
+ IPAddressLength: old.IPAddressLength,
+ IPAddress: old.IPAddress,
+ }
+ nlri = bgp.NewEVPNNLRI(n.RouteType, new)
+ }
+ default:
+ return p
+ }
+ path := NewPath(p.OriginInfo().source, nlri, p.IsWithdraw, p.GetPathAttrs(), p.GetTimestamp(), false)
+ path.SetExtCommunities(vrf.ExportRt, false)
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ path.setPathAttr(bgp.NewPathAttributeMpReachNLRI(nh.String(), []bgp.AddrPrefixInterface{nlri}))
+ path.IsNexthopInvalid = p.IsNexthopInvalid
+ return path
+}
+
+func (p *Path) ToLocal() *Path {
+ nlri := p.GetNlri()
+ f := p.GetRouteFamily()
+ pathId := nlri.PathLocalIdentifier()
+ switch f {
+ case bgp.RF_IPv4_VPN:
+ n := nlri.(*bgp.LabeledVPNIPAddrPrefix)
+ _, c, _ := net.ParseCIDR(n.IPPrefix())
+ ones, _ := c.Mask.Size()
+ nlri = bgp.NewIPAddrPrefix(uint8(ones), c.IP.String())
+ nlri.SetPathLocalIdentifier(pathId)
+ case bgp.RF_IPv6_VPN:
+ n := nlri.(*bgp.LabeledVPNIPv6AddrPrefix)
+ _, c, _ := net.ParseCIDR(n.IPPrefix())
+ ones, _ := c.Mask.Size()
+ nlri = bgp.NewIPv6AddrPrefix(uint8(ones), c.IP.String())
+ nlri.SetPathLocalIdentifier(pathId)
+ default:
+ return p
+ }
+ path := NewPath(p.OriginInfo().source, nlri, p.IsWithdraw, p.GetPathAttrs(), p.GetTimestamp(), false)
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
+
+ if f == bgp.RF_IPv4_VPN {
+ nh := path.GetNexthop()
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ path.setPathAttr(bgp.NewPathAttributeNextHop(nh.String()))
+ }
+ path.IsNexthopInvalid = p.IsNexthopInvalid
+ return path
+}
+
+func (p *Path) SetHash(v uint32) {
+ p.attrsHash = v
+}
+
+func (p *Path) GetHash() uint32 {
+ return p.attrsHash
+}