github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconcilerv2/pod_cidr.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package reconcilerv2 5 6 import ( 7 "context" 8 "fmt" 9 "net/netip" 10 11 "github.com/cilium/hive/cell" 12 "github.com/sirupsen/logrus" 13 14 "github.com/cilium/cilium/pkg/bgpv1/manager/instance" 15 "github.com/cilium/cilium/pkg/bgpv1/types" 16 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" 17 "github.com/cilium/cilium/pkg/option" 18 ) 19 20 type PodCIDRReconcilerOut struct { 21 cell.Out 22 23 Reconciler ConfigReconciler `group:"bgp-config-reconciler-v2"` 24 } 25 26 type PodCIDRReconcilerIn struct { 27 cell.In 28 29 Logger logrus.FieldLogger 30 PeerAdvert *CiliumPeerAdvertisement 31 DaemonConfig *option.DaemonConfig 32 } 33 34 type PodCIDRReconciler struct { 35 logger logrus.FieldLogger 36 peerAdvert *CiliumPeerAdvertisement 37 } 38 39 // PodCIDRReconcilerMetadata is a map of advertisements per family, key is family type 40 type PodCIDRReconcilerMetadata struct { 41 AFPaths AFPathsMap 42 RoutePolicies RoutePolicyMap 43 } 44 45 func NewPodCIDRReconciler(params PodCIDRReconcilerIn) PodCIDRReconcilerOut { 46 // Don't provide the reconciler if the IPAM mode is not supported 47 if !types.CanAdvertisePodCIDR(params.DaemonConfig.IPAMMode()) { 48 params.Logger.Info("Unsupported IPAM mode, disabling PodCIDR advertisements.") 49 return PodCIDRReconcilerOut{} 50 } 51 return PodCIDRReconcilerOut{ 52 Reconciler: &PodCIDRReconciler{ 53 logger: params.Logger.WithField(types.ReconcilerLogField, "PodCIDR"), 54 peerAdvert: params.PeerAdvert, 55 }, 56 } 57 } 58 59 func (r *PodCIDRReconciler) Name() string { 60 return "PodCIDR" 61 } 62 63 func (r *PodCIDRReconciler) Priority() int { 64 return 30 65 } 66 67 func (r *PodCIDRReconciler) Init(_ *instance.BGPInstance) error { 68 return nil 69 } 70 71 func (r *PodCIDRReconciler) Cleanup(_ *instance.BGPInstance) {} 72 73 func (r *PodCIDRReconciler) Reconcile(ctx context.Context, p ReconcileParams) error { 74 if p.DesiredConfig == nil { 75 return fmt.Errorf("BUG: PodCIDR reconciler called with nil CiliumBGPNodeConfig") 76 } 77 78 if p.CiliumNode == nil { 79 return fmt.Errorf("BUG: PodCIDR reconciler called with nil CiliumNode") 80 } 81 82 // get pod CIDR prefixes 83 var podCIDRPrefixes []netip.Prefix 84 for _, cidr := range p.CiliumNode.Spec.IPAM.PodCIDRs { 85 prefix, err := netip.ParsePrefix(cidr) 86 if err != nil { 87 return fmt.Errorf("failed to parse prefix %s: %w", cidr, err) 88 } 89 podCIDRPrefixes = append(podCIDRPrefixes, prefix) 90 } 91 92 // get per peer per family pod cidr advertisements 93 desiredPeerAdverts, err := r.peerAdvert.GetConfiguredAdvertisements(p.DesiredConfig, v2alpha1.BGPPodCIDRAdvert) 94 if err != nil { 95 return err 96 } 97 98 err = r.reconcileRoutePolicies(ctx, p, desiredPeerAdverts, podCIDRPrefixes) 99 if err != nil { 100 return err 101 } 102 103 return r.reconcilePaths(ctx, p, desiredPeerAdverts, podCIDRPrefixes) 104 } 105 106 func (r *PodCIDRReconciler) reconcilePaths(ctx context.Context, p ReconcileParams, desiredPeerAdverts PeerAdvertisements, podPrefixes []netip.Prefix) error { 107 metadata := r.getMetadata(p.BGPInstance) 108 109 // get desired paths per address family 110 desiredFamilyAdverts, err := r.getDesiredPathsPerFamily(desiredPeerAdverts, podPrefixes) 111 if err != nil { 112 return err 113 } 114 115 // reconcile family advertisements 116 updatedAFPaths, err := ReconcileAFPaths(&ReconcileAFPathsParams{ 117 Logger: r.logger.WithField(types.InstanceLogField, p.DesiredConfig.Name), 118 Ctx: ctx, 119 Router: p.BGPInstance.Router, 120 DesiredPaths: desiredFamilyAdverts, 121 CurrentPaths: metadata.AFPaths, 122 }) 123 124 metadata.AFPaths = updatedAFPaths 125 r.setMetadata(p.BGPInstance, metadata) 126 return err 127 } 128 129 func (r *PodCIDRReconciler) reconcileRoutePolicies(ctx context.Context, p ReconcileParams, desiredPeerAdverts PeerAdvertisements, podPrefixes []netip.Prefix) error { 130 metadata := r.getMetadata(p.BGPInstance) 131 132 // get desired policies 133 desiredRoutePolicies, err := r.getDesiredRoutePolicies(p, desiredPeerAdverts, podPrefixes) 134 if err != nil { 135 return err 136 } 137 138 // reconcile route policies 139 updatedPolicies, err := ReconcileRoutePolicies(&ReconcileRoutePoliciesParams{ 140 Logger: r.logger.WithField(types.InstanceLogField, p.DesiredConfig.Name), 141 Ctx: ctx, 142 Router: p.BGPInstance.Router, 143 DesiredPolicies: desiredRoutePolicies, 144 CurrentPolicies: r.getMetadata(p.BGPInstance).RoutePolicies, 145 }) 146 147 metadata.RoutePolicies = updatedPolicies 148 r.setMetadata(p.BGPInstance, metadata) 149 return err 150 } 151 152 // getDesiredPathsPerFamily returns a map of desired paths per address family. 153 // Note: This returns prefixes per address family. Global routing table will contain prefix per family not per neighbor. 154 // Per neighbor advertisement will be controlled by BGP Policy. 155 func (r *PodCIDRReconciler) getDesiredPathsPerFamily(desiredPeerAdverts PeerAdvertisements, desiredPrefixes []netip.Prefix) (AFPathsMap, error) { 156 // Calculate desired paths per address family, collapsing per-peer advertisements into per-family advertisements. 157 desiredFamilyAdverts := make(AFPathsMap) 158 for _, peerFamilyAdverts := range desiredPeerAdverts { 159 for family, familyAdverts := range peerFamilyAdverts { 160 agentFamily := types.ToAgentFamily(family) 161 pathsPerFamily, exists := desiredFamilyAdverts[agentFamily] 162 if !exists { 163 pathsPerFamily = make(PathMap) 164 desiredFamilyAdverts[agentFamily] = pathsPerFamily 165 } 166 167 // there are some advertisements which have pod CIDR advert enabled. 168 // we need to add podCIDR prefixes to the desiredFamilyAdverts. 169 if len(familyAdverts) != 0 { 170 for _, prefix := range desiredPrefixes { 171 path := types.NewPathForPrefix(prefix) 172 path.Family = agentFamily 173 174 // we only add path corresponding to the family of the prefix. 175 if agentFamily.Afi == types.AfiIPv4 && prefix.Addr().Is4() { 176 pathsPerFamily[path.NLRI.String()] = path 177 } 178 if agentFamily.Afi == types.AfiIPv6 && prefix.Addr().Is6() { 179 pathsPerFamily[path.NLRI.String()] = path 180 } 181 } 182 } 183 } 184 } 185 return desiredFamilyAdverts, nil 186 } 187 188 func (r *PodCIDRReconciler) getDesiredRoutePolicies(p ReconcileParams, desiredPeerAdverts PeerAdvertisements, desiredPrefixes []netip.Prefix) (RoutePolicyMap, error) { 189 desiredPolicies := make(RoutePolicyMap) 190 191 for peer, afAdverts := range desiredPeerAdverts { 192 peerAddr, err := GetPeerAddressFromConfig(p.DesiredConfig, peer) 193 if err != nil { 194 return nil, err 195 } 196 197 for family, adverts := range afAdverts { 198 fam := types.ToAgentFamily(family) 199 200 for _, advert := range adverts { 201 var v4Prefixes, v6Prefixes types.PolicyPrefixMatchList 202 for _, prefix := range desiredPrefixes { 203 match := &types.RoutePolicyPrefixMatch{CIDR: prefix, PrefixLenMin: prefix.Bits(), PrefixLenMax: prefix.Bits()} 204 205 if fam.Afi == types.AfiIPv4 && prefix.Addr().Is4() { 206 v4Prefixes = append(v4Prefixes, match) 207 } 208 209 if fam.Afi == types.AfiIPv6 && prefix.Addr().Is6() { 210 v6Prefixes = append(v6Prefixes, match) 211 } 212 } 213 214 if len(v6Prefixes) > 0 || len(v4Prefixes) > 0 { 215 name := PolicyName(peer, fam.Afi.String(), advert.AdvertisementType, "") 216 policy, err := CreatePolicy(name, peerAddr, v4Prefixes, v6Prefixes, advert) 217 if err != nil { 218 return nil, err 219 } 220 desiredPolicies[name] = policy 221 } 222 } 223 } 224 } 225 226 return desiredPolicies, nil 227 } 228 229 func (r *PodCIDRReconciler) getMetadata(i *instance.BGPInstance) PodCIDRReconcilerMetadata { 230 if _, found := i.Metadata[r.Name()]; !found { 231 i.Metadata[r.Name()] = PodCIDRReconcilerMetadata{ 232 AFPaths: make(AFPathsMap), 233 RoutePolicies: make(RoutePolicyMap), 234 } 235 } 236 return i.Metadata[r.Name()].(PodCIDRReconcilerMetadata) 237 } 238 239 func (r *PodCIDRReconciler) setMetadata(i *instance.BGPInstance, metadata PodCIDRReconcilerMetadata) { 240 i.Metadata[r.Name()] = metadata 241 }