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

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package api
     5  
     6  import (
     7  	"encoding/base64"
     8  	"errors"
     9  	"fmt"
    10  	"net/netip"
    11  
    12  	"github.com/osrg/gobgp/v3/pkg/packet/bgp"
    13  
    14  	"github.com/cilium/cilium/api/v1/models"
    15  	restapi "github.com/cilium/cilium/api/v1/server/restapi/bgp"
    16  	"github.com/cilium/cilium/pkg/bgpv1/types"
    17  )
    18  
    19  const (
    20  	routePolicyTypeExport = "export"
    21  	routePolicyTypeImport = "import"
    22  
    23  	routePolicyActionNone   = "none"
    24  	routePolicyActionAccept = "accept"
    25  	routePolicyActionReject = "reject"
    26  )
    27  
    28  func ToAgentGetRoutesRequest(params restapi.GetBgpRoutesParams) (*types.GetRoutesRequest, error) {
    29  	ret := &types.GetRoutesRequest{}
    30  
    31  	if ret.TableType = types.ParseTableType(params.TableType); ret.TableType == types.TableTypeUnknown {
    32  		return nil, fmt.Errorf("unknown table type %s", params.TableType)
    33  	}
    34  
    35  	if ret.Family.Afi = types.ParseAfi(params.Afi); ret.Family.Afi == types.AfiUnknown {
    36  		return nil, fmt.Errorf("unknown AFI %s", params.Afi)
    37  	}
    38  
    39  	if ret.Family.Safi = types.ParseSafi(params.Safi); ret.Family.Safi == types.SafiUnknown {
    40  		return nil, fmt.Errorf("unknown SAFI %s", params.Safi)
    41  	}
    42  
    43  	if params.Neighbor != nil {
    44  		if ret.TableType == types.TableTypeLocRIB {
    45  			return nil, fmt.Errorf("neighbor is unnecessary for loc-rib table type")
    46  		}
    47  		addr, err := netip.ParseAddr(*params.Neighbor)
    48  		if err != nil {
    49  			return nil, fmt.Errorf("invalid neighbor address: %w", err)
    50  		}
    51  		ret.Neighbor = addr
    52  	} else {
    53  		if ret.TableType == types.TableTypeAdjRIBIn || ret.TableType == types.TableTypeAdjRIBOut {
    54  			return nil, fmt.Errorf("table %s requires neighbor parameter", params.TableType)
    55  		}
    56  	}
    57  
    58  	return ret, nil
    59  }
    60  
    61  func ToAPIFamily(f *types.Family) (*models.BgpFamily, error) {
    62  	return &models.BgpFamily{
    63  		Afi:  f.Afi.String(),
    64  		Safi: f.Safi.String(),
    65  	}, nil
    66  }
    67  
    68  func ToAgentFamily(m *models.BgpFamily) (*types.Family, error) {
    69  	f := &types.Family{}
    70  
    71  	if f.Afi = types.ParseAfi(m.Afi); f.Afi == types.AfiUnknown {
    72  		return nil, fmt.Errorf("unknown afi %s", m.Afi)
    73  	}
    74  
    75  	if f.Safi = types.ParseSafi(m.Safi); f.Safi == types.SafiUnknown {
    76  		return nil, fmt.Errorf("unknown safi %s", m.Safi)
    77  	}
    78  
    79  	return f, nil
    80  }
    81  
    82  func ToAPIPath(p *types.Path) (*models.BgpPath, error) {
    83  	ret := &models.BgpPath{}
    84  
    85  	ret.AgeNanoseconds = p.AgeNanoseconds
    86  	ret.Best = p.Best
    87  
    88  	// We need this Base64 encoding because OpenAPI 2.0 spec doesn't support Union
    89  	// type and we don't have any way to express the API response field which can
    90  	// be a multiple types. This is especially inconvenient for NLRI and Path
    91  	// Attributes. The workaround here is serialize NLRI or Path Attribute into
    92  	// BGP UPDATE messsage format and encode it with base64 to put them into text
    93  	// based protocol. So that we can still stick to the standard (theoretically
    94  	// people can use standard BGP decoder to decode this base64 field).
    95  	bin, err := p.NLRI.Serialize()
    96  	if err != nil {
    97  		return nil, fmt.Errorf("failed to serialize NLRI: %w", err)
    98  	}
    99  	ret.Nlri = &models.BgpNlri{Base64: base64.StdEncoding.EncodeToString(bin)}
   100  
   101  	if ret.Family, err = ToAPIFamily(&types.Family{
   102  		Afi:  types.Afi(p.NLRI.AFI()),
   103  		Safi: types.Safi(p.NLRI.SAFI()),
   104  	}); err != nil {
   105  		return nil, fmt.Errorf("failed to serialize address family: %w", err)
   106  	}
   107  
   108  	for _, pattr := range p.PathAttributes {
   109  		bin, err := pattr.Serialize()
   110  		if err != nil {
   111  			return nil, fmt.Errorf("failed to serialize Path Attribute: %w", err)
   112  		}
   113  
   114  		ret.PathAttributes = append(ret.PathAttributes, &models.BgpPathAttribute{
   115  			Base64: base64.StdEncoding.EncodeToString(bin),
   116  		})
   117  	}
   118  
   119  	return ret, nil
   120  }
   121  
   122  func ToAgentPath(m *models.BgpPath) (*types.Path, error) {
   123  	p := &types.Path{}
   124  
   125  	p.AgeNanoseconds = m.AgeNanoseconds
   126  	p.Best = m.Best
   127  
   128  	afi := types.ParseAfi(m.Family.Afi)
   129  	safi := types.ParseSafi(m.Family.Safi)
   130  
   131  	// Create empty NLRI structure. The underlying type will be set correctly by providing AFI/SAFI
   132  	nlri, err := bgp.NewPrefixFromRouteFamily(uint16(afi), uint8(safi))
   133  	if err != nil {
   134  		return nil, fmt.Errorf("failed to create native NLRI struct from AFI/SAFI: %w", err)
   135  	}
   136  
   137  	// Decode serialized NLRI
   138  	bin, err := base64.StdEncoding.DecodeString(m.Nlri.Base64)
   139  	if err != nil {
   140  		return nil, fmt.Errorf("failed to decode base64-encoded NLRI: %w", err)
   141  	}
   142  
   143  	if err := nlri.DecodeFromBytes(bin); err != nil {
   144  		return nil, fmt.Errorf("failed to decode NLRI: %w", err)
   145  	}
   146  
   147  	p.NLRI = nlri
   148  
   149  	// Decode path attributes
   150  	for _, pattr := range m.PathAttributes {
   151  		bin, err := base64.StdEncoding.DecodeString(pattr.Base64)
   152  		if err != nil {
   153  			return nil, fmt.Errorf("failed to decode base64-encoded Path Attribute: %w", err)
   154  		}
   155  
   156  		attr, err := bgp.GetPathAttribute(bin)
   157  		if err != nil {
   158  			return nil, fmt.Errorf("failed to retrieve serialized Path Attribute: %w", err)
   159  		}
   160  		err = attr.DecodeFromBytes(bin)
   161  		if err != nil {
   162  			return nil, fmt.Errorf("failed to decode serialized Path Attribute: %w", err)
   163  		}
   164  
   165  		p.PathAttributes = append(p.PathAttributes, attr)
   166  	}
   167  
   168  	return p, nil
   169  }
   170  
   171  func ToAPIPaths(ps []*types.Path) ([]*models.BgpPath, error) {
   172  	errs := []error{}
   173  	ret := []*models.BgpPath{}
   174  
   175  	for _, p := range ps {
   176  		path, err := ToAPIPath(p)
   177  		if err != nil {
   178  			errs = append(errs, err)
   179  			continue
   180  		}
   181  		ret = append(ret, path)
   182  	}
   183  
   184  	if len(errs) != 0 {
   185  		return nil, errors.Join(errs...)
   186  	}
   187  
   188  	return ret, nil
   189  }
   190  
   191  func ToAgentPaths(ms []*models.BgpPath) ([]*types.Path, error) {
   192  	errs := []error{}
   193  	ret := []*types.Path{}
   194  
   195  	for _, m := range ms {
   196  		path, err := ToAgentPath(m)
   197  		if err != nil {
   198  			errs = append(errs, err)
   199  			continue
   200  		}
   201  		ret = append(ret, path)
   202  	}
   203  
   204  	if len(errs) != 0 {
   205  		return nil, errors.Join(errs...)
   206  	}
   207  
   208  	return ret, nil
   209  }
   210  
   211  func ToAPIRoute(r *types.Route, routerASN int64, neighbor string) (*models.BgpRoute, error) {
   212  	paths, err := ToAPIPaths(r.Paths)
   213  	if err != nil {
   214  		return nil, fmt.Errorf("failed to serialize Paths: %w", err)
   215  	}
   216  	return &models.BgpRoute{
   217  		RouterAsn: routerASN,
   218  		Neighbor:  neighbor,
   219  		Prefix:    r.Prefix,
   220  		Paths:     paths,
   221  	}, nil
   222  }
   223  
   224  func ToAgentRoute(m *models.BgpRoute) (*types.Route, error) {
   225  	paths, err := ToAgentPaths(m.Paths)
   226  	if err != nil {
   227  		return nil, fmt.Errorf("failed to deserialize Paths: %w", err)
   228  	}
   229  	return &types.Route{
   230  		Prefix: m.Prefix,
   231  		Paths:  paths,
   232  	}, nil
   233  }
   234  
   235  func ToAPIRoutes(rs []*types.Route, routerASN int64, neighbor string) ([]*models.BgpRoute, error) {
   236  	errs := []error{}
   237  	ret := []*models.BgpRoute{}
   238  
   239  	for _, r := range rs {
   240  		route, err := ToAPIRoute(r, routerASN, neighbor)
   241  		if err != nil {
   242  			errs = append(errs, err)
   243  			continue
   244  		}
   245  		ret = append(ret, route)
   246  	}
   247  
   248  	if len(errs) != 0 {
   249  		return nil, errors.Join(errs...)
   250  	}
   251  
   252  	return ret, nil
   253  }
   254  
   255  func ToAgentRoutes(ms []*models.BgpRoute) ([]*types.Route, error) {
   256  	errs := []error{}
   257  	ret := []*types.Route{}
   258  
   259  	for _, m := range ms {
   260  		route, err := ToAgentRoute(m)
   261  		if err != nil {
   262  			errs = append(errs, err)
   263  			continue
   264  		}
   265  		ret = append(ret, route)
   266  	}
   267  
   268  	if len(errs) != 0 {
   269  		return nil, errors.Join(errs...)
   270  	}
   271  
   272  	return ret, nil
   273  }
   274  
   275  func ToAPIRoutePolicies(policies []*types.RoutePolicy, routerASN int64) []*models.BgpRoutePolicy {
   276  	ret := make([]*models.BgpRoutePolicy, 0, len(policies))
   277  
   278  	for _, p := range policies {
   279  		policy := ToAPIRoutePolicy(p, routerASN)
   280  		ret = append(ret, policy)
   281  	}
   282  	return ret
   283  }
   284  
   285  func ToAgentRoutePolicies(policies []*models.BgpRoutePolicy) ([]*types.RoutePolicy, error) {
   286  	var retErr error
   287  	ret := make([]*types.RoutePolicy, 0, len(policies))
   288  
   289  	for _, p := range policies {
   290  		policy, err := ToAgentRoutePolicy(p)
   291  		if err != nil {
   292  			retErr = errors.Join(retErr, err)
   293  			continue
   294  		}
   295  		ret = append(ret, policy)
   296  	}
   297  	return ret, retErr
   298  }
   299  
   300  func ToAPIRoutePolicy(policy *types.RoutePolicy, routerASN int64) *models.BgpRoutePolicy {
   301  	return &models.BgpRoutePolicy{
   302  		RouterAsn:  routerASN,
   303  		Name:       policy.Name,
   304  		Type:       ToApiRoutePolicyType(policy.Type),
   305  		Statements: ToAPIRoutePolicyStatements(policy.Statements),
   306  	}
   307  }
   308  
   309  func ToAgentRoutePolicy(policy *models.BgpRoutePolicy) (*types.RoutePolicy, error) {
   310  	stmts, err := ToAgentRoutePolicyStatements(policy.Statements)
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	return &types.RoutePolicy{
   315  		Name:       policy.Name,
   316  		Type:       ToAgentRoutePolicyType(policy.Type),
   317  		Statements: stmts,
   318  	}, nil
   319  }
   320  
   321  func ToAPIRoutePolicyStatements(statements []*types.RoutePolicyStatement) []*models.BgpRoutePolicyStatement {
   322  	ret := make([]*models.BgpRoutePolicyStatement, 0, len(statements))
   323  
   324  	for _, s := range statements {
   325  		ret = append(ret, ToAPIRoutePolicyStatement(s))
   326  	}
   327  	return ret
   328  }
   329  
   330  func ToAgentRoutePolicyStatements(statements []*models.BgpRoutePolicyStatement) ([]*types.RoutePolicyStatement, error) {
   331  	var retErr error
   332  	ret := make([]*types.RoutePolicyStatement, 0, len(statements))
   333  
   334  	for _, s := range statements {
   335  		stmt, err := ToAgentRoutePolicyStatement(s)
   336  		if err != nil {
   337  			retErr = errors.Join(retErr, err)
   338  			continue
   339  		}
   340  		ret = append(ret, stmt)
   341  	}
   342  	return ret, retErr
   343  }
   344  
   345  func ToAPIRoutePolicyStatement(s *types.RoutePolicyStatement) *models.BgpRoutePolicyStatement {
   346  	localPref := int64(-1)
   347  	if s.Actions.SetLocalPreference != nil {
   348  		localPref = *s.Actions.SetLocalPreference
   349  	}
   350  	ret := &models.BgpRoutePolicyStatement{
   351  		MatchNeighbors:      s.Conditions.MatchNeighbors,
   352  		MatchPrefixes:       ToApiMatchPrefixes(s.Conditions.MatchPrefixes),
   353  		RouteAction:         ToApiRoutePolicyAction(s.Actions.RouteAction),
   354  		AddCommunities:      s.Actions.AddCommunities,
   355  		AddLargeCommunities: s.Actions.AddLargeCommunities,
   356  		SetLocalPreference:  localPref,
   357  	}
   358  	return ret
   359  }
   360  
   361  func ToAgentRoutePolicyStatement(s *models.BgpRoutePolicyStatement) (*types.RoutePolicyStatement, error) {
   362  	var localPref *int64
   363  	if s.SetLocalPreference >= 0 {
   364  		localPref = &s.SetLocalPreference
   365  	}
   366  	prefixes, err := ToAgentMatchPrefixes(s.MatchPrefixes)
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  	ret := &types.RoutePolicyStatement{
   371  		Conditions: types.RoutePolicyConditions{
   372  			MatchNeighbors: s.MatchNeighbors,
   373  			MatchPrefixes:  prefixes,
   374  		},
   375  		Actions: types.RoutePolicyActions{
   376  			RouteAction:         ToAgentRoutePolicyAction(s.RouteAction),
   377  			AddCommunities:      s.AddCommunities,
   378  			AddLargeCommunities: s.AddLargeCommunities,
   379  			SetLocalPreference:  localPref,
   380  		},
   381  	}
   382  	return ret, nil
   383  }
   384  
   385  func ToApiMatchPrefixes(prefixes []*types.RoutePolicyPrefixMatch) []*models.BgpRoutePolicyPrefixMatch {
   386  	ret := make([]*models.BgpRoutePolicyPrefixMatch, 0, len(prefixes))
   387  
   388  	for _, p := range prefixes {
   389  		ret = append(ret, &models.BgpRoutePolicyPrefixMatch{
   390  			Cidr:         p.CIDR.String(),
   391  			PrefixLenMin: int64(p.PrefixLenMin),
   392  			PrefixLenMax: int64(p.PrefixLenMax),
   393  		})
   394  	}
   395  	return ret
   396  }
   397  
   398  func ToAgentMatchPrefixes(prefixes []*models.BgpRoutePolicyPrefixMatch) ([]*types.RoutePolicyPrefixMatch, error) {
   399  	var retErr error
   400  	ret := make([]*types.RoutePolicyPrefixMatch, 0, len(prefixes))
   401  
   402  	for _, p := range prefixes {
   403  		cidr, err := netip.ParsePrefix(p.Cidr)
   404  		if err != nil {
   405  			retErr = errors.Join(retErr, err)
   406  			continue
   407  		}
   408  		ret = append(ret, &types.RoutePolicyPrefixMatch{
   409  			CIDR:         cidr,
   410  			PrefixLenMin: int(p.PrefixLenMin),
   411  			PrefixLenMax: int(p.PrefixLenMax),
   412  		})
   413  	}
   414  	return ret, retErr
   415  }
   416  
   417  func ToApiRoutePolicyType(t types.RoutePolicyType) string {
   418  	if t == types.RoutePolicyTypeExport {
   419  		return routePolicyTypeExport
   420  	}
   421  	return routePolicyTypeImport
   422  }
   423  
   424  func ToAgentRoutePolicyType(t string) types.RoutePolicyType {
   425  	if t == routePolicyTypeExport {
   426  		return types.RoutePolicyTypeExport
   427  	}
   428  	return types.RoutePolicyTypeImport
   429  }
   430  
   431  func ToApiRoutePolicyAction(a types.RoutePolicyAction) string {
   432  	switch a {
   433  	case types.RoutePolicyActionNone:
   434  		return routePolicyActionNone
   435  	case types.RoutePolicyActionAccept:
   436  		return routePolicyActionAccept
   437  	case types.RoutePolicyActionReject:
   438  		return routePolicyActionReject
   439  	}
   440  	return routePolicyActionNone
   441  }
   442  
   443  func ToAgentRoutePolicyAction(a string) types.RoutePolicyAction {
   444  	switch a {
   445  	case routePolicyActionNone:
   446  		return types.RoutePolicyActionNone
   447  	case routePolicyActionAccept:
   448  		return types.RoutePolicyActionAccept
   449  	case routePolicyActionReject:
   450  		return types.RoutePolicyActionReject
   451  	}
   452  	return types.RoutePolicyActionNone
   453  }