github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconciler/advertisements.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package reconciler
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"github.com/sirupsen/logrus"
    11  
    12  	"github.com/cilium/cilium/pkg/bgpv1/manager/instance"
    13  	"github.com/cilium/cilium/pkg/bgpv1/types"
    14  	v2alpha1api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
    15  )
    16  
    17  type advertisementsReconcilerParams struct {
    18  	ctx       context.Context
    19  	name      string
    20  	component string
    21  	enabled   bool
    22  
    23  	sc   *instance.ServerWithConfig
    24  	newc *v2alpha1api.CiliumBGPVirtualRouter
    25  
    26  	currentAdvertisements []*types.Path
    27  	toAdvertise           []*types.Path
    28  }
    29  
    30  // exportAdvertisementsReconciler reconciles the state of the BGP advertisements
    31  // with the provided toAdvertise list and returns a list of the advertisements
    32  // currently being announced.
    33  func exportAdvertisementsReconciler(params *advertisementsReconcilerParams) ([]*types.Path, error) {
    34  	var (
    35  		l = log.WithFields(
    36  			logrus.Fields{
    37  				"component": params.component,
    38  			},
    39  		)
    40  		// holds advertisements which must be advertised
    41  		toAdvertise []*types.Path
    42  		// holds advertisements which must remain in place
    43  		toKeep []*types.Path
    44  		// holds advertisements which must be removed
    45  		toWithdraw []*types.Path
    46  		// the result of advertising toAdvertise.
    47  		newAdverts []*types.Path
    48  	)
    49  
    50  	l.Debugf("Begin reconciling %s advertisements for virtual router with local ASN %v", params.name, params.newc.LocalASN)
    51  
    52  	// if advertisement is turned off withdraw any previously advertised
    53  	// cidrs and early return nil.
    54  	if !params.enabled {
    55  		l.Debugf("%s advertisements disabled for virtual router with local ASN %v", params.name, params.newc.LocalASN)
    56  
    57  		for _, advrt := range params.currentAdvertisements {
    58  			l.Debugf("Withdrawing %s advertisement %v for local ASN %v", params.name, advrt.NLRI, params.newc.LocalASN)
    59  			if err := params.sc.Server.WithdrawPath(params.ctx, types.PathRequest{Path: advrt}); err != nil {
    60  				return nil, err
    61  			}
    62  		}
    63  
    64  		return nil, nil
    65  	}
    66  
    67  	// an aset member which book keeps which universe it exists in
    68  	type member struct {
    69  		a     bool
    70  		b     bool
    71  		advrt *types.Path
    72  	}
    73  
    74  	aset := map[string]*member{}
    75  
    76  	// populate the advrts that must be present, universe a
    77  	for _, path := range params.toAdvertise {
    78  		var (
    79  			m  *member
    80  			ok bool
    81  		)
    82  
    83  		key := path.NLRI.String()
    84  		if m, ok = aset[key]; !ok {
    85  			aset[key] = &member{
    86  				a:     true,
    87  				advrt: path,
    88  			}
    89  			continue
    90  		}
    91  		m.a = true
    92  	}
    93  
    94  	// populate the advrts that are current advertised
    95  	for _, path := range params.currentAdvertisements {
    96  		var (
    97  			m  *member
    98  			ok bool
    99  		)
   100  		key := path.NLRI.String()
   101  		if m, ok = aset[key]; !ok {
   102  			aset[key] = &member{
   103  				b:     true,
   104  				advrt: path,
   105  			}
   106  			continue
   107  		}
   108  		m.b = true
   109  	}
   110  
   111  	for _, m := range aset {
   112  		// present in configured cidrs (set a) but not in advertised cidrs
   113  		// (set b)
   114  		if m.a && !m.b {
   115  			toAdvertise = append(toAdvertise, m.advrt)
   116  		}
   117  		// present in advertised cidrs (set b) but no in configured cidrs
   118  		// (set b)
   119  		if m.b && !m.a {
   120  			toWithdraw = append(toWithdraw, m.advrt)
   121  		}
   122  		// present in both configured (set a) and advertised (set b) add this to
   123  		// cidrs to leave advertised.
   124  		if m.b && m.a {
   125  			toKeep = append(toKeep, m.advrt)
   126  		}
   127  	}
   128  
   129  	if len(toAdvertise) == 0 && len(toWithdraw) == 0 {
   130  		l.Debugf("No reconciliation necessary")
   131  		return append([]*types.Path{}, params.currentAdvertisements...), nil
   132  	}
   133  
   134  	// create new adverts
   135  	for _, advrt := range toAdvertise {
   136  		l.Debugf("Advertising %s %v for policy with local ASN: %v", params.name, advrt.NLRI, params.newc.LocalASN)
   137  		advrtResp, err := params.sc.Server.AdvertisePath(params.ctx, types.PathRequest{Path: advrt})
   138  		if err != nil {
   139  			return nil, fmt.Errorf("failed to advertise %s prefix %v: %w", params.name, advrt.NLRI, err)
   140  		}
   141  		newAdverts = append(newAdverts, advrtResp.Path)
   142  	}
   143  
   144  	// withdraw uneeded adverts
   145  	for _, advrt := range toWithdraw {
   146  		l.Debugf("Withdrawing %s %v for policy with local ASN: %v", params.name, advrt.NLRI, params.newc.LocalASN)
   147  		if err := params.sc.Server.WithdrawPath(params.ctx, types.PathRequest{Path: advrt}); err != nil {
   148  			return nil, err
   149  		}
   150  	}
   151  
   152  	// concat our toKeep and newAdverts slices to store the latest
   153  	// reconciliation and return it
   154  	return append(toKeep, newAdverts...), nil
   155  }