github.com/cilium/cilium@v1.16.2/pkg/bgpv1/gobgp/conversions.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package gobgp
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"net/netip"
    10  
    11  	gobgp "github.com/osrg/gobgp/v3/api"
    12  	"github.com/osrg/gobgp/v3/pkg/apiutil"
    13  	"github.com/osrg/gobgp/v3/pkg/packet/bgp"
    14  	"google.golang.org/protobuf/types/known/timestamppb"
    15  
    16  	"github.com/cilium/cilium/pkg/bgpv1/types"
    17  	"github.com/cilium/cilium/pkg/time"
    18  )
    19  
    20  // ToGoBGPPath converts the Agent Path type to the GoBGP Path type
    21  func ToGoBGPPath(p *types.Path) (*gobgp.Path, error) {
    22  	nlri, err := apiutil.MarshalNLRI(p.NLRI)
    23  	if err != nil {
    24  		return nil, fmt.Errorf("failed to convert NLRI: %w", err)
    25  	}
    26  
    27  	pattrs, err := apiutil.MarshalPathAttributes(p.PathAttributes)
    28  	if err != nil {
    29  		return nil, fmt.Errorf("failed to convert PathAttribute: %w", err)
    30  	}
    31  
    32  	// ageTimestamp is Path's creation time stamp.
    33  	// It is calculated by subtraction of the AgeNanoseconds from the current timestamp.
    34  	ageTimestamp := timestamppb.New(time.Now().Add(time.Duration(-1 * p.AgeNanoseconds)))
    35  
    36  	family := toGoBGPFamily(p.Family)
    37  
    38  	// infer family from NLRI if not provided
    39  	if family.Afi == gobgp.Family_AFI_UNKNOWN {
    40  		family = &gobgp.Family{
    41  			Afi:  gobgp.Family_Afi(p.NLRI.AFI()),
    42  			Safi: gobgp.Family_Safi(p.NLRI.SAFI()),
    43  		}
    44  	}
    45  
    46  	return &gobgp.Path{
    47  		Nlri:   nlri,
    48  		Pattrs: pattrs,
    49  		Age:    ageTimestamp,
    50  		Best:   p.Best,
    51  		Family: family,
    52  		Uuid:   p.UUID,
    53  	}, nil
    54  }
    55  
    56  // ToAgentPath converts the GoBGP Path type to the Agent Path type
    57  func ToAgentPath(p *gobgp.Path) (*types.Path, error) {
    58  	family := bgp.AfiSafiToRouteFamily(uint16(p.Family.Afi), uint8(p.Family.Safi))
    59  
    60  	nlri, err := apiutil.UnmarshalNLRI(family, p.Nlri)
    61  	if err != nil {
    62  		return nil, fmt.Errorf("failed to convert Nlri: %w", err)
    63  	}
    64  
    65  	pattrs, err := apiutil.UnmarshalPathAttributes(p.Pattrs)
    66  	if err != nil {
    67  		return nil, fmt.Errorf("failed to convert Pattrs: %w", err)
    68  	}
    69  
    70  	// ageNano is time since the Path was created in nanoseconds.
    71  	// It is calculated by difference in time from age timestamp till now.
    72  	ageNano := int64(time.Since(p.Age.AsTime()))
    73  
    74  	return &types.Path{
    75  		NLRI:           nlri,
    76  		Family:         toAgentFamily(p.Family),
    77  		PathAttributes: pattrs,
    78  		AgeNanoseconds: ageNano,
    79  		Best:           p.Best,
    80  		UUID:           p.Uuid,
    81  	}, nil
    82  }
    83  
    84  // ToAgentPaths converts slice of the GoBGP Path type to slice of the Agent Path type
    85  func ToAgentPaths(paths []*gobgp.Path) ([]*types.Path, error) {
    86  	errs := []error{}
    87  	ps := []*types.Path{}
    88  
    89  	for _, path := range paths {
    90  		p, err := ToAgentPath(path)
    91  		if err != nil {
    92  			errs = append(errs, err)
    93  			continue
    94  		}
    95  		ps = append(ps, p)
    96  	}
    97  
    98  	if len(errs) != 0 {
    99  		return nil, errors.Join(errs...)
   100  	}
   101  
   102  	return ps, nil
   103  }
   104  
   105  func toGoBGPFamily(family types.Family) *gobgp.Family {
   106  	return &gobgp.Family{
   107  		Afi:  toGoBGPAfi(family.Afi),
   108  		Safi: toGoBGPSafi(family.Safi),
   109  	}
   110  }
   111  
   112  func toGoBGPAfi(a types.Afi) gobgp.Family_Afi {
   113  	switch a {
   114  	case types.AfiUnknown:
   115  		return gobgp.Family_AFI_UNKNOWN
   116  	case types.AfiIPv4:
   117  		return gobgp.Family_AFI_IP
   118  	case types.AfiIPv6:
   119  		return gobgp.Family_AFI_IP6
   120  	case types.AfiL2VPN:
   121  		return gobgp.Family_AFI_L2VPN
   122  	case types.AfiLS:
   123  		return gobgp.Family_AFI_LS
   124  	case types.AfiOpaque:
   125  		return gobgp.Family_AFI_OPAQUE
   126  	default:
   127  		return gobgp.Family_AFI_UNKNOWN
   128  	}
   129  }
   130  
   131  func toGoBGPSafi(s types.Safi) gobgp.Family_Safi {
   132  	switch s {
   133  	case types.SafiUnknown:
   134  		return gobgp.Family_SAFI_UNKNOWN
   135  	case types.SafiUnicast:
   136  		return gobgp.Family_SAFI_UNICAST
   137  	case types.SafiMulticast:
   138  		return gobgp.Family_SAFI_MULTICAST
   139  	case types.SafiMplsLabel:
   140  		return gobgp.Family_SAFI_MPLS_LABEL
   141  	case types.SafiEncapsulation:
   142  		return gobgp.Family_SAFI_ENCAPSULATION
   143  	case types.SafiVpls:
   144  		return gobgp.Family_SAFI_VPLS
   145  	case types.SafiEvpn:
   146  		return gobgp.Family_SAFI_EVPN
   147  	case types.SafiLs:
   148  		return gobgp.Family_SAFI_LS
   149  	case types.SafiSrPolicy:
   150  		return gobgp.Family_SAFI_SR_POLICY
   151  	case types.SafiMup:
   152  		return gobgp.Family_SAFI_MUP
   153  	case types.SafiMplsVpn:
   154  		return gobgp.Family_SAFI_MPLS_VPN
   155  	case types.SafiMplsVpnMulticast:
   156  		return gobgp.Family_SAFI_MPLS_VPN_MULTICAST
   157  	case types.SafiRouteTargetConstraints:
   158  		return gobgp.Family_SAFI_ROUTE_TARGET_CONSTRAINTS
   159  	case types.SafiFlowSpecUnicast:
   160  		return gobgp.Family_SAFI_FLOW_SPEC_UNICAST
   161  	case types.SafiFlowSpecVpn:
   162  		return gobgp.Family_SAFI_FLOW_SPEC_VPN
   163  	case types.SafiKeyValue:
   164  		return gobgp.Family_SAFI_KEY_VALUE
   165  	default:
   166  		return gobgp.Family_SAFI_UNKNOWN
   167  	}
   168  }
   169  
   170  func toGoBGPPolicy(apiPolicy *types.RoutePolicy) (*gobgp.Policy, []*gobgp.DefinedSet) {
   171  	var definedSets []*gobgp.DefinedSet
   172  
   173  	policy := &gobgp.Policy{
   174  		Name: apiPolicy.Name,
   175  	}
   176  	for i, stmt := range apiPolicy.Statements {
   177  		statement, dSets := toGoBGPPolicyStatement(stmt, policyStatementName(apiPolicy.Name, i))
   178  		policy.Statements = append(policy.Statements, statement)
   179  		definedSets = append(definedSets, dSets...)
   180  	}
   181  
   182  	return policy, definedSets
   183  }
   184  
   185  func toAgentPolicy(p *gobgp.Policy, definedSets map[string]*gobgp.DefinedSet, assignment *gobgp.PolicyAssignment) *types.RoutePolicy {
   186  	policy := &types.RoutePolicy{
   187  		Name: p.Name,
   188  		Type: toAgentPolicyType(assignment.Direction),
   189  	}
   190  	for _, s := range p.Statements {
   191  		policy.Statements = append(policy.Statements, toAgentPolicyStatement(s, definedSets))
   192  	}
   193  	return policy
   194  }
   195  
   196  func toGoBGPPolicyStatement(apiStatement *types.RoutePolicyStatement, name string) (*gobgp.Statement, []*gobgp.DefinedSet) {
   197  	var definedSets []*gobgp.DefinedSet
   198  
   199  	s := &gobgp.Statement{
   200  		Name:       name,
   201  		Conditions: &gobgp.Conditions{},
   202  		Actions: &gobgp.Actions{
   203  			RouteAction: toGoBGPRouteAction(apiStatement.Actions.RouteAction),
   204  		},
   205  	}
   206  
   207  	// defined set to match neighbor
   208  	if len(apiStatement.Conditions.MatchNeighbors) > 0 {
   209  		ds := &gobgp.DefinedSet{
   210  			DefinedType: gobgp.DefinedType_NEIGHBOR,
   211  			Name:        policyNeighborDefinedSetName(name),
   212  			List:        apiStatement.Conditions.MatchNeighbors,
   213  		}
   214  		s.Conditions.NeighborSet = &gobgp.MatchSet{
   215  			Type: gobgp.MatchSet_ANY, // any of the configured neighbors
   216  			Name: ds.Name,
   217  		}
   218  		definedSets = append(definedSets, ds)
   219  	}
   220  
   221  	// defined set to match prefixes
   222  	if len(apiStatement.Conditions.MatchPrefixes) > 0 {
   223  		ds := &gobgp.DefinedSet{
   224  			DefinedType: gobgp.DefinedType_PREFIX,
   225  			Name:        policyPrefixDefinedSetName(name),
   226  		}
   227  		for _, prefix := range apiStatement.Conditions.MatchPrefixes {
   228  			p := &gobgp.Prefix{
   229  				IpPrefix:      prefix.CIDR.String(),
   230  				MaskLengthMin: uint32(prefix.PrefixLenMin),
   231  				MaskLengthMax: uint32(prefix.PrefixLenMax),
   232  			}
   233  			ds.Prefixes = append(ds.Prefixes, p)
   234  		}
   235  		s.Conditions.PrefixSet = &gobgp.MatchSet{
   236  			Type: gobgp.MatchSet_ANY, // any of the configured prefixes
   237  			Name: ds.Name,
   238  		}
   239  		definedSets = append(definedSets, ds)
   240  	}
   241  
   242  	// community actions
   243  	if len(apiStatement.Actions.AddCommunities) > 0 {
   244  		s.Actions.Community = &gobgp.CommunityAction{
   245  			Type:        gobgp.CommunityAction_ADD,
   246  			Communities: apiStatement.Actions.AddCommunities,
   247  		}
   248  	}
   249  	if len(apiStatement.Actions.AddLargeCommunities) > 0 {
   250  		s.Actions.LargeCommunity = &gobgp.CommunityAction{
   251  			Type:        gobgp.CommunityAction_ADD,
   252  			Communities: apiStatement.Actions.AddLargeCommunities,
   253  		}
   254  	}
   255  
   256  	// local preference actions
   257  	if apiStatement.Actions.SetLocalPreference != nil {
   258  		// Local preference only makes sense for iBGP sessions. However, it can be applied
   259  		// unconditionally here - it would have no effect on eBGP peers matching this policy.
   260  		s.Actions.LocalPref = &gobgp.LocalPrefAction{
   261  			Value: uint32(*apiStatement.Actions.SetLocalPreference),
   262  		}
   263  	}
   264  	return s, definedSets
   265  }
   266  
   267  func toAgentPolicyStatement(s *gobgp.Statement, definedSets map[string]*gobgp.DefinedSet) *types.RoutePolicyStatement {
   268  	stmt := &types.RoutePolicyStatement{}
   269  
   270  	if s.Conditions != nil {
   271  		if s.Conditions.NeighborSet != nil && definedSets[s.Conditions.NeighborSet.Name] != nil {
   272  			stmt.Conditions.MatchNeighbors = definedSets[s.Conditions.NeighborSet.Name].List
   273  		}
   274  		if s.Conditions.PrefixSet != nil && definedSets[s.Conditions.PrefixSet.Name] != nil {
   275  			for _, pfx := range definedSets[s.Conditions.PrefixSet.Name].Prefixes {
   276  				cidr, err := netip.ParsePrefix(pfx.IpPrefix)
   277  				if err == nil {
   278  					stmt.Conditions.MatchPrefixes = append(stmt.Conditions.MatchPrefixes, &types.RoutePolicyPrefixMatch{
   279  						CIDR:         cidr,
   280  						PrefixLenMin: int(pfx.MaskLengthMin),
   281  						PrefixLenMax: int(pfx.MaskLengthMax),
   282  					})
   283  				}
   284  			}
   285  		}
   286  	}
   287  	if s.Actions != nil {
   288  		stmt.Actions.RouteAction = toAgentRouteAction(s.Actions.RouteAction)
   289  		if s.Actions.Community != nil {
   290  			stmt.Actions.AddCommunities = s.Actions.Community.Communities
   291  		}
   292  		if s.Actions.LargeCommunity != nil {
   293  			stmt.Actions.AddLargeCommunities = s.Actions.LargeCommunity.Communities
   294  		}
   295  		if s.Actions.LocalPref != nil {
   296  			localPref := int64(s.Actions.LocalPref.Value)
   297  			stmt.Actions.SetLocalPreference = &localPref
   298  		}
   299  	}
   300  	return stmt
   301  }
   302  
   303  func policyStatementName(policyName string, cnt int) string {
   304  	return fmt.Sprintf("%s-%d", policyName, cnt)
   305  }
   306  
   307  func policyNeighborDefinedSetName(policyStatementName string) string {
   308  	return fmt.Sprintf(policyStatementName + "-neighbor")
   309  }
   310  
   311  func policyPrefixDefinedSetName(policyStatementName string) string {
   312  	return fmt.Sprintf(policyStatementName + "-prefix")
   313  }
   314  
   315  func toGoBGPRouteAction(a types.RoutePolicyAction) gobgp.RouteAction {
   316  	switch a {
   317  	case types.RoutePolicyActionAccept:
   318  		return gobgp.RouteAction_ACCEPT
   319  	case types.RoutePolicyActionReject:
   320  		return gobgp.RouteAction_REJECT
   321  	}
   322  	return gobgp.RouteAction_NONE
   323  }
   324  
   325  func toAgentRouteAction(a gobgp.RouteAction) types.RoutePolicyAction {
   326  	switch a {
   327  	case gobgp.RouteAction_ACCEPT:
   328  		return types.RoutePolicyActionAccept
   329  	case gobgp.RouteAction_REJECT:
   330  		return types.RoutePolicyActionReject
   331  	}
   332  	return types.RoutePolicyActionNone
   333  }
   334  
   335  func toGoBGPPolicyDirection(policyType types.RoutePolicyType) gobgp.PolicyDirection {
   336  	switch policyType {
   337  	case types.RoutePolicyTypeExport:
   338  		return gobgp.PolicyDirection_EXPORT
   339  	case types.RoutePolicyTypeImport:
   340  		return gobgp.PolicyDirection_IMPORT
   341  	}
   342  	return gobgp.PolicyDirection_UNKNOWN
   343  }
   344  
   345  func toAgentPolicyType(d gobgp.PolicyDirection) types.RoutePolicyType {
   346  	if d == gobgp.PolicyDirection_IMPORT {
   347  		return types.RoutePolicyTypeImport
   348  	}
   349  	return types.RoutePolicyTypeExport
   350  }
   351  
   352  func toGoBGPSoftResetDirection(direction types.SoftResetDirection) gobgp.ResetPeerRequest_SoftResetDirection {
   353  	switch direction {
   354  	case types.SoftResetDirectionIn:
   355  		return gobgp.ResetPeerRequest_IN
   356  	case types.SoftResetDirectionOut:
   357  		return gobgp.ResetPeerRequest_OUT
   358  	}
   359  	return gobgp.ResetPeerRequest_BOTH
   360  }
   361  
   362  // toAgentSessionState translates gobgp session state to cilium bgp session state.
   363  func toAgentSessionState(s gobgp.PeerState_SessionState) types.SessionState {
   364  	switch s {
   365  	case gobgp.PeerState_UNKNOWN:
   366  		return types.SessionUnknown
   367  	case gobgp.PeerState_IDLE:
   368  		return types.SessionIdle
   369  	case gobgp.PeerState_CONNECT:
   370  		return types.SessionConnect
   371  	case gobgp.PeerState_ACTIVE:
   372  		return types.SessionActive
   373  	case gobgp.PeerState_OPENSENT:
   374  		return types.SessionOpenSent
   375  	case gobgp.PeerState_OPENCONFIRM:
   376  		return types.SessionOpenConfirm
   377  	case gobgp.PeerState_ESTABLISHED:
   378  		return types.SessionEstablished
   379  	default:
   380  		return types.SessionUnknown
   381  	}
   382  }
   383  
   384  func toAgentFamily(family *gobgp.Family) types.Family {
   385  	return types.Family{
   386  		Afi:  toAgentAfi(family.Afi),
   387  		Safi: toAgentSafi(family.Safi),
   388  	}
   389  }
   390  
   391  // toAgentAfi translates gobgp AFI to cilium bgp AFI.
   392  func toAgentAfi(a gobgp.Family_Afi) types.Afi {
   393  	switch a {
   394  	case gobgp.Family_AFI_UNKNOWN:
   395  		return types.AfiUnknown
   396  	case gobgp.Family_AFI_IP:
   397  		return types.AfiIPv4
   398  	case gobgp.Family_AFI_IP6:
   399  		return types.AfiIPv6
   400  	case gobgp.Family_AFI_L2VPN:
   401  		return types.AfiL2VPN
   402  	case gobgp.Family_AFI_LS:
   403  		return types.AfiLS
   404  	case gobgp.Family_AFI_OPAQUE:
   405  		return types.AfiOpaque
   406  	default:
   407  		return types.AfiUnknown
   408  	}
   409  }
   410  
   411  func toAgentSafi(s gobgp.Family_Safi) types.Safi {
   412  	switch s {
   413  	case gobgp.Family_SAFI_UNKNOWN:
   414  		return types.SafiUnknown
   415  	case gobgp.Family_SAFI_UNICAST:
   416  		return types.SafiUnicast
   417  	case gobgp.Family_SAFI_MULTICAST:
   418  		return types.SafiMulticast
   419  	case gobgp.Family_SAFI_MPLS_LABEL:
   420  		return types.SafiMplsLabel
   421  	case gobgp.Family_SAFI_ENCAPSULATION:
   422  		return types.SafiEncapsulation
   423  	case gobgp.Family_SAFI_VPLS:
   424  		return types.SafiVpls
   425  	case gobgp.Family_SAFI_EVPN:
   426  		return types.SafiEvpn
   427  	case gobgp.Family_SAFI_LS:
   428  		return types.SafiLs
   429  	case gobgp.Family_SAFI_SR_POLICY:
   430  		return types.SafiSrPolicy
   431  	case gobgp.Family_SAFI_MUP:
   432  		return types.SafiMup
   433  	case gobgp.Family_SAFI_MPLS_VPN:
   434  		return types.SafiMplsVpn
   435  	case gobgp.Family_SAFI_MPLS_VPN_MULTICAST:
   436  		return types.SafiMplsVpnMulticast
   437  	case gobgp.Family_SAFI_ROUTE_TARGET_CONSTRAINTS:
   438  		return types.SafiRouteTargetConstraints
   439  	case gobgp.Family_SAFI_FLOW_SPEC_UNICAST:
   440  		return types.SafiFlowSpecUnicast
   441  	case gobgp.Family_SAFI_FLOW_SPEC_VPN:
   442  		return types.SafiFlowSpecVpn
   443  	case gobgp.Family_SAFI_KEY_VALUE:
   444  		return types.SafiKeyValue
   445  	default:
   446  		return types.SafiUnknown
   447  	}
   448  }
   449  
   450  func toGoBGPTableType(t types.TableType) (gobgp.TableType, error) {
   451  	switch t {
   452  	case types.TableTypeLocRIB:
   453  		return gobgp.TableType_LOCAL, nil
   454  	case types.TableTypeAdjRIBIn:
   455  		return gobgp.TableType_ADJ_IN, nil
   456  	case types.TableTypeAdjRIBOut:
   457  		return gobgp.TableType_ADJ_OUT, nil
   458  	default:
   459  		return gobgp.TableType_LOCAL, fmt.Errorf("unknown table type %d", t)
   460  	}
   461  }