• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubeovn / kube-ovn / 25472384490

07 May 2026 02:20AM UTC coverage: 24.899% (+0.06%) from 24.835%
25472384490

push

github

web-flow
feat(servicecidr): support K8s multiple ServiceCIDR (KEP-1880) (#6690)

* feat(servicecidr): support Kubernetes multiple ServiceCIDR (KEP-1880)

Watch networking.k8s.io/v1 ServiceCIDR objects and merge them with the
--service-cluster-ip-range flag value into a single source of truth used
by every Service-CIDR consumer (U2O policy routes, vpc-lb init
containers, vpc-nat-gw routes, daemon ipset/iptables). The flag now
serves as a startup fallback that yields once the API observes any valid
entry, and re-engages if the API set ever empties — so old clusters
without the API behave exactly as before, while 1.33+ clusters converge
to the API-advertised set and pick up dynamic add/remove.

ServiceCIDR API discovery uses APIResourceExists with a 10s ticker
fallback (same shape as the NAD/KubeVirt scaffolds), so missing API on
older clusters is a no-op rather than an error. RBAC for
networking.k8s.io/servicecidrs is added to system:ovn and
system:kube-ovn-cni in install.sh and both Helm charts.

Notable behavior changes:
- vpc-lb deployment is now upserted with a ServiceCIDR hash annotation;
  changing the merged set rolls the pod via the existing Recreate
  strategy.
- U2O no-LB policy routes are pruned at the start of every reconcile
  (policy-only delete, port groups untouched) so shrinking the set no
  longer leaves stale OVN entries.
- Existing VPC NAT gateways are intentionally not re-enqueued on
  ServiceCIDR change; their routes only refresh when the pod is
  recreated by other means. Newly created NAT gateways pick up the
  current store via their own add path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>

* refactor(servicecidr): consolidate duplicates and use stdlib helpers

- Promote readyServiceCIDRs to util.ReadyServiceCIDRs so controller and
  daemon share one source of truth.
- Drop custom equalStringSlice in favour of slices.Equal.
- Extract vpcLbInitContainers helpe... (continued)

124 of 467 new or added lines in 12 files covered. (26.55%)

2 existing lines in 1 file now uncovered.

14198 of 57023 relevant lines covered (24.9%)

0.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

11.84
/pkg/controller/subnet.go
1
package controller
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "maps"
8
        "net"
9
        "reflect"
10
        "slices"
11
        "sort"
12
        "strings"
13

14
        "github.com/ovn-kubernetes/libovsdb/ovsdb"
15
        v1 "k8s.io/api/core/v1"
16
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
17
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
        "k8s.io/apimachinery/pkg/labels"
19
        "k8s.io/apimachinery/pkg/types"
20
        "k8s.io/client-go/tools/cache"
21
        "k8s.io/klog/v2"
22
        "sigs.k8s.io/controller-runtime/pkg/client"
23
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
24

25
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
26
        "github.com/kubeovn/kube-ovn/pkg/ipam"
27
        "github.com/kubeovn/kube-ovn/pkg/ovs"
28
        "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb"
29
        "github.com/kubeovn/kube-ovn/pkg/util"
30
)
31

32
func (c *Controller) enqueueAddSubnet(obj any) {
×
33
        key := cache.MetaObjectToName(obj.(*kubeovnv1.Subnet)).String()
×
34
        klog.V(3).Infof("enqueue add subnet %s", key)
×
35
        c.addOrUpdateSubnetQueue.Add(key)
×
36
}
×
37

38
func (c *Controller) enqueueDeleteSubnet(obj any) {
×
39
        var subnet *kubeovnv1.Subnet
×
40
        switch t := obj.(type) {
×
41
        case *kubeovnv1.Subnet:
×
42
                subnet = t
×
43
        case cache.DeletedFinalStateUnknown:
×
44
                s, ok := t.Obj.(*kubeovnv1.Subnet)
×
45
                if !ok {
×
46
                        klog.Warningf("unexpected object type: %T", t.Obj)
×
47
                        return
×
48
                }
×
49
                subnet = s
×
50
        default:
×
51
                klog.Warningf("unexpected type: %T", obj)
×
52
                return
×
53
        }
54

55
        klog.V(3).Infof("enqueue delete subnet %s", subnet.Name)
×
56
        c.deleteSubnetQueue.Add(subnet)
×
57
}
58

59
func readyToRemoveFinalizer(subnet *kubeovnv1.Subnet) bool {
1✔
60
        if subnet.DeletionTimestamp.IsZero() {
2✔
61
                return false
1✔
62
        }
1✔
63

64
        if subnet.Status.V4UsingIPs.EqualInt64(0) && subnet.Status.V6UsingIPs.EqualInt64(0) {
2✔
65
                return true
1✔
66
        }
1✔
67

68
        usingIPs := subnet.Status.V4UsingIPs.Add(subnet.Status.V6UsingIPs)
1✔
69

1✔
70
        if subnet.Status.U2OInterconnectionIP != "" {
2✔
71
                return usingIPs.EqualInt64(int64(len(strings.Split(subnet.Status.U2OInterconnectionIP, ","))))
1✔
72
        }
1✔
73

74
        return false
1✔
75
}
76

77
func (c *Controller) enqueueUpdateSubnet(oldObj, newObj any) {
×
78
        oldSubnet := oldObj.(*kubeovnv1.Subnet)
×
79
        newSubnet := newObj.(*kubeovnv1.Subnet)
×
80
        key := cache.MetaObjectToName(newSubnet).String()
×
81

×
82
        if readyToRemoveFinalizer(newSubnet) {
×
83
                klog.Infof("enqueue update subnet %s triggered by ready to remove finalizer", key)
×
84
                c.addOrUpdateSubnetQueue.Add(key)
×
85
                return
×
86
        }
×
87

88
        if !reflect.DeepEqual(oldSubnet.Spec, newSubnet.Spec) {
×
89
                klog.V(3).Infof("enqueue update subnet %s", key)
×
90

×
91
                if oldSubnet.Spec.U2OInterconnection != newSubnet.Spec.U2OInterconnection {
×
92
                        klog.Infof("enqueue update vpc %s to update always-lear-from-arp option triggered by u2o interconnection change of subnet %s", newSubnet.Spec.Vpc, key)
×
93
                        c.addOrUpdateVpcQueue.Add(newSubnet.Spec.Vpc)
×
94
                }
×
95

96
                if oldSubnet.Spec.GatewayType != newSubnet.Spec.GatewayType {
×
97
                        c.recorder.Eventf(newSubnet, v1.EventTypeNormal, "SubnetGatewayTypeChanged",
×
98
                                "subnet gateway type changes from %q to %q", oldSubnet.Spec.GatewayType, newSubnet.Spec.GatewayType)
×
99
                }
×
100

101
                if oldSubnet.Spec.GatewayNode != newSubnet.Spec.GatewayNode {
×
102
                        c.recorder.Eventf(newSubnet, v1.EventTypeNormal, "SubnetGatewayNodeChanged",
×
103
                                "gateway node changes from %q to %q", oldSubnet.Spec.GatewayNode, newSubnet.Spec.GatewayNode)
×
104
                }
×
105

106
                c.addOrUpdateSubnetQueue.Add(key)
×
107
        }
108
}
109

110
func (c *Controller) formatSubnet(subnet *kubeovnv1.Subnet) (*kubeovnv1.Subnet, error) {
1✔
111
        newSubnet := subnet.DeepCopy()
1✔
112
        if err := formatAddress(newSubnet); err != nil {
1✔
113
                klog.Error(err)
×
114
                return nil, err
×
115
        }
×
116

117
        if newSubnet.Spec.Provider == "" {
2✔
118
                newSubnet.Spec.Provider = util.OvnProvider
1✔
119
        }
1✔
120

121
        if newSubnet.Spec.Vpc == "" {
2✔
122
                if isOvnSubnet(newSubnet) {
2✔
123
                        newSubnet.Spec.Vpc = c.config.ClusterRouter
1✔
124
                }
1✔
125
        }
126

127
        if newSubnet.Spec.Vpc == c.config.ClusterRouter && newSubnet.Name != c.config.NodeSwitch {
2✔
128
                // Some format only needed in the default VPC
1✔
129
                if newSubnet.Spec.GatewayType == "" {
2✔
130
                        newSubnet.Spec.GatewayType = kubeovnv1.GWDistributedType
1✔
131
                }
1✔
132
                if newSubnet.Spec.Default && newSubnet.Name != c.config.DefaultLogicalSwitch {
1✔
133
                        newSubnet.Spec.Default = false
×
134
                }
×
135
        }
136

137
        if newSubnet.Spec.EnableLb == nil && newSubnet.Name != c.config.NodeSwitch {
2✔
138
                newSubnet.Spec.EnableLb = &c.config.EnableLb
1✔
139
        }
1✔
140
        // set join subnet Spec.EnableLb to nil
141
        if newSubnet.Spec.EnableLb != nil && newSubnet.Name == c.config.NodeSwitch {
1✔
142
                newSubnet.Spec.EnableLb = nil
×
143
        }
×
144

145
        if newSubnet.Spec.U2OInterconnectionIP != "" && !newSubnet.Spec.U2OInterconnection {
1✔
146
                newSubnet.Spec.U2OInterconnectionIP = ""
×
147
        }
×
148

149
        if newSubnet.Spec.Vlan == "" && newSubnet.Spec.U2OInterconnection {
1✔
150
                newSubnet.Spec.U2OInterconnection = false
×
151
        }
×
152

153
        changed := !reflect.DeepEqual(subnet, newSubnet)
1✔
154
        klog.Infof("format subnet %v, changed %v", subnet.Name, changed)
1✔
155
        if changed {
2✔
156
                ret, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Update(context.Background(), newSubnet, metav1.UpdateOptions{})
1✔
157
                if err != nil {
1✔
158
                        klog.Errorf("failed to update subnet %s, %v", subnet.Name, err)
×
159
                        return nil, err
×
160
                }
×
161
                return ret, nil
1✔
162
        }
163
        return subnet, nil
1✔
164
}
165

166
// errVlanNotReady is returned when the vlan has not been fully processed by
167
// the vlan handler yet. The caller should requeue silently without patching
168
// the subnet status as failed.
169
var errVlanNotReady = errors.New("vlan not ready")
170

171
func (c *Controller) validateSubnetVlan(subnet *kubeovnv1.Subnet) error {
1✔
172
        if subnet.Spec.Vlan == "" {
2✔
173
                return nil
1✔
174
        }
1✔
175

176
        vlan, err := c.vlansLister.Get(subnet.Spec.Vlan)
1✔
177
        if err != nil {
2✔
178
                err = fmt.Errorf("failed to get vlan %s: %w", subnet.Spec.Vlan, err)
1✔
179
                klog.Error(err)
1✔
180
                return err
1✔
181
        }
1✔
182

183
        if vlan.Status.Conflict {
2✔
184
                err = fmt.Errorf("subnet %s has invalid conflict vlan %s", subnet.Name, vlan.Name)
1✔
185
                klog.Error(err)
1✔
186
                return err
1✔
187
        }
1✔
188

189
        // Ensure the vlan has been fully processed by the vlan handler before
190
        // allowing the subnet to proceed. A newly created vlan has Conflict=false
191
        // (zero value) which is indistinguishable from a processed non-conflicting
192
        // vlan. Use pn.Status.Vlans as a "vlan ready" signal — only set after
193
        // the vlan handler completes successfully (including conflict check).
194
        // This prevents a race where the subnet allocates IPAM before the vlan's
195
        // conflict status is determined.
196
        if vlan.Spec.Provider == "" {
2✔
197
                return fmt.Errorf("vlan %s provider not yet set, deferring subnet %s: %w", vlan.Name, subnet.Name, errVlanNotReady)
1✔
198
        }
1✔
199
        pn, err := c.providerNetworksLister.Get(vlan.Spec.Provider)
1✔
200
        if err != nil {
1✔
201
                return fmt.Errorf("vlan %s not yet ready (provider network %s not found): %w", vlan.Name, vlan.Spec.Provider, errVlanNotReady)
×
202
        }
×
203
        if !slices.Contains(pn.Status.Vlans, vlan.Name) {
2✔
204
                return fmt.Errorf("vlan %s not yet processed by vlan handler, deferring subnet %s: %w", vlan.Name, subnet.Name, errVlanNotReady)
1✔
205
        }
1✔
206

207
        return nil
1✔
208
}
209

210
func formatAddress(subnet *kubeovnv1.Subnet) error {
1✔
211
        if err := formatCIDR(subnet); err != nil {
1✔
212
                klog.Error(err)
×
213
                return err
×
214
        }
×
215

216
        if err := formatGateway(subnet); err != nil {
1✔
217
                klog.Error(err)
×
218
                return err
×
219
        }
×
220

221
        formatExcludeIPs(subnet)
1✔
222

1✔
223
        subnet.Spec.Protocol = util.CheckProtocol(subnet.Spec.CIDRBlock)
1✔
224

1✔
225
        return nil
1✔
226
}
227

228
func formatCIDR(subnet *kubeovnv1.Subnet) error {
1✔
229
        var cidrBlocks []string
1✔
230

1✔
231
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
2✔
232
                _, ipNet, err := net.ParseCIDR(cidr)
1✔
233
                if err != nil {
1✔
234
                        klog.Error(err)
×
235
                        return fmt.Errorf("subnet %s cidr %s is invalid", subnet.Name, cidr)
×
236
                }
×
237
                cidrBlocks = append(cidrBlocks, ipNet.String())
1✔
238
        }
239
        subnet.Spec.CIDRBlock = strings.Join(cidrBlocks, ",")
1✔
240
        return nil
1✔
241
}
242

243
func formatGateway(subnet *kubeovnv1.Subnet) error {
1✔
244
        var (
1✔
245
                gw  string
1✔
246
                err error
1✔
247
        )
1✔
248

1✔
249
        switch {
1✔
250
        case subnet.Spec.Gateway == "":
1✔
251
                gw, err = util.GetGwByCidr(subnet.Spec.CIDRBlock)
1✔
252
        case subnet.Spec.Protocol == kubeovnv1.ProtocolDual && util.CheckProtocol(subnet.Spec.Gateway) != util.CheckProtocol(subnet.Spec.CIDRBlock):
×
253
                gw, err = util.AppendGwByCidr(subnet.Spec.Gateway, subnet.Spec.CIDRBlock)
×
254
        default:
1✔
255
                gw = subnet.Spec.Gateway
1✔
256
        }
257
        if err != nil {
1✔
258
                klog.Error(err)
×
259
                return err
×
260
        }
×
261
        subnet.Spec.Gateway = gw
1✔
262

1✔
263
        return nil
1✔
264
}
265

266
func formatExcludeIPs(subnet *kubeovnv1.Subnet) {
1✔
267
        var excludeIPs []string
1✔
268
        excludeIPs = append(excludeIPs, strings.Split(subnet.Spec.Gateway, ",")...)
1✔
269
        sort.Strings(excludeIPs)
1✔
270
        if len(subnet.Spec.ExcludeIps) == 0 {
2✔
271
                subnet.Spec.ExcludeIps = excludeIPs
1✔
272
        } else {
2✔
273
                formatExcludeIPRanges(subnet)
1✔
274
                for _, gw := range excludeIPs {
2✔
275
                        gwExists := false
1✔
276
                        for _, excludeIP := range subnet.Spec.ExcludeIps {
2✔
277
                                if util.ContainsIPs(excludeIP, gw) {
2✔
278
                                        gwExists = true
1✔
279
                                        break
1✔
280
                                }
281
                        }
282
                        if !gwExists {
1✔
283
                                subnet.Spec.ExcludeIps = append(subnet.Spec.ExcludeIps, gw)
×
284
                                sort.Strings(subnet.Spec.ExcludeIps)
×
285
                        }
×
286
                }
287
        }
288
}
289

290
func (c *Controller) syncSubnetFinalizer(cl client.Client) error {
×
291
        // migrate deprecated finalizer to new finalizer
×
292
        subnets := &kubeovnv1.SubnetList{}
×
293
        return migrateFinalizers(cl, subnets, func(i int) (client.Object, client.Object) {
×
294
                if i < 0 || i >= len(subnets.Items) {
×
295
                        return nil, nil
×
296
                }
×
297
                return subnets.Items[i].DeepCopy(), subnets.Items[i].DeepCopy()
×
298
        })
299
}
300

301
func (c *Controller) handleSubnetFinalizer(subnet *kubeovnv1.Subnet) (*kubeovnv1.Subnet, bool, error) {
×
302
        if subnet.DeletionTimestamp.IsZero() && !slices.Contains(subnet.GetFinalizers(), util.KubeOVNControllerFinalizer) {
×
303
                newSubnet := subnet.DeepCopy()
×
304
                controllerutil.RemoveFinalizer(newSubnet, util.DeprecatedFinalizerName)
×
305
                controllerutil.AddFinalizer(newSubnet, util.KubeOVNControllerFinalizer)
×
306
                patch, err := util.GenerateMergePatchPayload(subnet, newSubnet)
×
307
                if err != nil {
×
308
                        klog.Errorf("failed to generate patch payload for subnet '%s', %v", subnet.Name, err)
×
309
                        return newSubnet, false, err
×
310
                }
×
311
                patchSubnet, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name, types.MergePatchType, patch, metav1.PatchOptions{}, "")
×
312
                if err != nil {
×
313
                        klog.Errorf("failed to add finalizer to subnet %s, %v", subnet.Name, err)
×
314
                        return patchSubnet, false, err
×
315
                }
×
316

317
                return patchSubnet, false, nil
×
318
        }
319

320
        if readyToRemoveFinalizer(subnet) {
×
321
                newSubnet := subnet.DeepCopy()
×
322
                controllerutil.RemoveFinalizer(newSubnet, util.DeprecatedFinalizerName)
×
323
                controllerutil.RemoveFinalizer(newSubnet, util.KubeOVNControllerFinalizer)
×
324
                patch, err := util.GenerateMergePatchPayload(subnet, newSubnet)
×
325
                if err != nil {
×
326
                        klog.Errorf("failed to generate patch payload for subnet '%s', %v", subnet.Name, err)
×
327
                        return newSubnet, false, err
×
328
                }
×
329
                if _, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name,
×
330
                        types.MergePatchType, patch, metav1.PatchOptions{}, ""); err != nil {
×
331
                        klog.Errorf("failed to remove finalizer from subnet %s, %v", subnet.Name, err)
×
332
                        return newSubnet, false, err
×
333
                }
×
334
                return newSubnet, true, nil
×
335
        }
336
        return subnet, false, nil
×
337
}
338

339
func (c *Controller) validateVpcBySubnet(subnet *kubeovnv1.Subnet) (*kubeovnv1.Vpc, error) {
×
340
        vpc, err := c.vpcsLister.Get(subnet.Spec.Vpc)
×
341
        if err != nil {
×
342
                klog.Errorf("failed to get subnet's vpc '%s', %v", subnet.Spec.Vpc, err)
×
343
                return vpc, err
×
344
        }
×
345

346
        if !vpc.Status.Standby {
×
347
                err = fmt.Errorf("the vpc '%s' not standby yet", vpc.Name)
×
348
                klog.Error(err)
×
349
                return vpc, err
×
350
        }
×
351

352
        if !vpc.Status.Default {
×
353
                for _, ns := range subnet.Spec.Namespaces {
×
354
                        if !slices.Contains(vpc.Spec.Namespaces, ns) {
×
355
                                err = fmt.Errorf("namespace '%s' is out of range to custom vpc '%s'", ns, vpc.Name)
×
356
                                klog.Error(err)
×
357
                                return vpc, err
×
358
                        }
×
359
                }
360
        } else {
×
361
                vpcs, err := c.vpcsLister.List(labels.Everything())
×
362
                if err != nil {
×
363
                        klog.Errorf("failed to list vpc, %v", err)
×
364
                        return vpc, err
×
365
                }
×
366

367
                for _, vpc := range vpcs {
×
368
                        if subnet.Spec.Vpc != vpc.Name &&
×
369
                                !vpc.Status.Default && util.IsStringsOverlap(vpc.Spec.Namespaces, subnet.Spec.Namespaces) {
×
370
                                err = fmt.Errorf("namespaces %v are overlap with vpc '%s'", subnet.Spec.Namespaces, vpc.Name)
×
371
                                klog.Error(err)
×
372
                                return vpc, err
×
373
                        }
×
374
                }
375
        }
376
        return vpc, nil
×
377
}
378

379
func (c *Controller) checkSubnetConflict(subnet *kubeovnv1.Subnet) error {
1✔
380
        subnetList, err := c.subnetsLister.List(labels.Everything())
1✔
381
        if err != nil {
1✔
382
                klog.Errorf("failed to list subnets %v", err)
×
383
                return err
×
384
        }
×
385

386
        for _, sub := range subnetList {
2✔
387
                if sub.Spec.Vpc != subnet.Spec.Vpc || sub.Spec.Vlan != subnet.Spec.Vlan || sub.Name == subnet.Name {
2✔
388
                        continue
1✔
389
                }
390

391
                if util.CIDROverlap(sub.Spec.CIDRBlock, subnet.Spec.CIDRBlock) {
2✔
392
                        conflictErr := fmt.Errorf("subnet %s cidr %s is conflict with subnet %s cidr %s", subnet.Name, subnet.Spec.CIDRBlock, sub.Name, sub.Spec.CIDRBlock)
1✔
393
                        klog.Error(conflictErr)
1✔
394
                        if patchErr := c.patchSubnetStatus(subnet, "ValidateLogicalSwitchFailed", conflictErr.Error()); patchErr != nil {
1✔
395
                                klog.Error(patchErr)
×
396
                                return errors.Join(conflictErr, patchErr)
×
397
                        }
×
398
                        return conflictErr
1✔
399
                }
400

401
                if subnet.Spec.ExternalEgressGateway != "" && sub.Spec.ExternalEgressGateway != "" &&
1✔
402
                        subnet.Spec.PolicyRoutingTableID == sub.Spec.PolicyRoutingTableID {
2✔
403
                        conflictErr := fmt.Errorf("subnet %s policy routing table ID %d is conflict with subnet %s policy routing table ID %d", subnet.Name, subnet.Spec.PolicyRoutingTableID, sub.Name, sub.Spec.PolicyRoutingTableID)
1✔
404
                        klog.Error(conflictErr)
1✔
405
                        if patchErr := c.patchSubnetStatus(subnet, "ValidateLogicalSwitchFailed", conflictErr.Error()); patchErr != nil {
1✔
406
                                klog.Error(patchErr)
×
407
                                return errors.Join(conflictErr, patchErr)
×
408
                        }
×
409
                        return conflictErr
1✔
410
                }
411
        }
412

413
        if subnet.Spec.Vlan == "" && subnet.Spec.Vpc == c.config.ClusterRouter {
2✔
414
                nodes, err := c.nodesLister.List(labels.Everything())
1✔
415
                if err != nil {
1✔
416
                        klog.Errorf("failed to list nodes: %v", err)
×
417
                        return err
×
418
                }
×
419
                for _, node := range nodes {
2✔
420
                        for _, addr := range node.Status.Addresses {
2✔
421
                                if addr.Type == v1.NodeInternalIP && util.CIDRContainIP(subnet.Spec.CIDRBlock, addr.Address) {
2✔
422
                                        conflictErr := fmt.Errorf("subnet %s cidr %s conflict with node %s address %s", subnet.Name, subnet.Spec.CIDRBlock, node.Name, addr.Address)
1✔
423
                                        klog.Error(conflictErr)
1✔
424
                                        if patchErr := c.patchSubnetStatus(subnet, "ValidateLogicalSwitchFailed", conflictErr.Error()); patchErr != nil {
1✔
425
                                                klog.Error(patchErr)
×
426
                                                return errors.Join(conflictErr, patchErr)
×
427
                                        }
×
428
                                        return conflictErr
1✔
429
                                }
430
                        }
431
                }
432
        }
433
        return nil
1✔
434
}
435

436
// getSubnetMTU returns the effective MTU for DHCP options of the given subnet.
437
func (c *Controller) getSubnetMTU(subnet *kubeovnv1.Subnet) (int, error) {
×
438
        var mtu int
×
439
        if subnet.Spec.Mtu > 0 {
×
440
                mtu = int(subnet.Spec.Mtu)
×
441
        } else {
×
442
                mtu = util.DefaultMTU
×
443
                if subnet.Spec.Vlan == "" {
×
444
                        switch c.config.NetworkType {
×
445
                        case util.NetworkTypeVlan:
×
446
                                // default to geneve
×
447
                                fallthrough
×
448
                        case util.NetworkTypeGeneve:
×
449
                                mtu -= util.GeneveHeaderLength
×
450
                        case util.NetworkTypeVxlan:
×
451
                                mtu -= util.VxlanHeaderLength
×
452
                        case util.NetworkTypeStt:
×
453
                                mtu -= util.SttHeaderLength
×
454
                        default:
×
455
                                return 0, fmt.Errorf("invalid network type: %s", c.config.NetworkType)
×
456
                        }
457
                }
458
        }
459
        // Surface IPv6 MTU misconfiguration without rewriting the value: the path
460
        // MTU is set by the underlying link, so silently raising it would only
461
        // shift the failure to fragmentation/blackholing of IPv4 traffic. The
462
        // webhook rejects new subnets that violate the floor; this branch warns
463
        // for upgraded subnets and auto-computed values that can no longer carry
464
        // IPv6.
465
        if mtu < util.IPv6MinMTU {
×
466
                protocol := util.CheckProtocol(subnet.Spec.CIDRBlock)
×
467
                if protocol == kubeovnv1.ProtocolIPv6 || protocol == kubeovnv1.ProtocolDual {
×
468
                        klog.Warningf("subnet %s mtu %d is below the IPv6 minimum %d; IPv6 traffic will be dropped on pod interfaces", subnet.Name, mtu, util.IPv6MinMTU)
×
469
                }
×
470
        }
471
        return mtu, nil
×
472
}
473

474
func (c *Controller) updateSubnetDHCPOption(subnet *kubeovnv1.Subnet, needRouter bool) error {
×
475
        mtu, err := c.getSubnetMTU(subnet)
×
476
        if err != nil {
×
477
                return err
×
478
        }
×
479

480
        dhcpOptionsUUIDs, err := c.OVNNbClient.UpdateDHCPOptions(subnet, mtu)
×
481
        if err != nil {
×
482
                klog.Errorf("failed to update dhcp options for switch %s, %v", subnet.Name, err)
×
483
                return err
×
484
        }
×
485

486
        vpc, err := c.vpcsLister.Get(subnet.Spec.Vpc)
×
487
        if err != nil {
×
488
                klog.Errorf("failed to get subnet's vpc '%s', %v", subnet.Spec.Vpc, err)
×
489
                return err
×
490
        }
×
491

492
        if needRouter {
×
493
                lrpName := fmt.Sprintf("%s-%s", vpc.Status.Router, subnet.Name)
×
494
                if err := c.OVNNbClient.UpdateLogicalRouterPortRA(lrpName, subnet.Spec.IPv6RAConfigs, subnet.Spec.EnableIPv6RA); err != nil {
×
495
                        klog.Errorf("update ipv6 ra configs for logical router port %s, %v", lrpName, err)
×
496
                        return err
×
497
                }
×
498
        }
499

500
        if subnet.Status.DHCPv4OptionsUUID != dhcpOptionsUUIDs.DHCPv4OptionsUUID || subnet.Status.DHCPv6OptionsUUID != dhcpOptionsUUIDs.DHCPv6OptionsUUID {
×
501
                subnet.Status.DHCPv4OptionsUUID = dhcpOptionsUUIDs.DHCPv4OptionsUUID
×
502
                subnet.Status.DHCPv6OptionsUUID = dhcpOptionsUUIDs.DHCPv6OptionsUUID
×
503
                bytes, err := subnet.Status.Bytes()
×
504
                if err != nil {
×
505
                        klog.Error(err)
×
506
                        return err
×
507
                }
×
508
                if _, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
509
                        klog.Errorf("patch subnet %s dhcp options failed: %v", subnet.Name, err)
×
510
                        return err
×
511
                }
×
512
        }
513

514
        return nil
×
515
}
516

517
func (c *Controller) handleAddOrUpdateSubnet(key string) error {
1✔
518
        c.subnetKeyMutex.LockKey(key)
1✔
519
        defer func() { _ = c.subnetKeyMutex.UnlockKey(key) }()
2✔
520

521
        cachedSubnet, err := c.subnetsLister.Get(key)
1✔
522
        if err != nil {
1✔
523
                if k8serrors.IsNotFound(err) {
×
524
                        return nil
×
525
                }
×
526
                klog.Error(err)
×
527
                return err
×
528
        }
529
        klog.V(3).Infof("handle add or update subnet %s", cachedSubnet.Name)
1✔
530
        subnet, err := c.formatSubnet(cachedSubnet)
1✔
531
        if err != nil {
1✔
532
                err := fmt.Errorf("failed to format subnet %s, %w", key, err)
×
533
                klog.Error(err)
×
534
                return err
×
535
        }
×
536

537
        err = c.validateSubnetVlan(subnet)
1✔
538
        if err != nil {
2✔
539
                if errors.Is(err, errVlanNotReady) {
1✔
540
                        // vlan hasn't been processed yet, requeue silently without
×
541
                        // marking the subnet as failed
×
542
                        klog.Infof("deferring subnet %s: %v", key, err)
×
543
                        return err
×
544
                }
×
545
                err = fmt.Errorf("failed to validate vlan for subnet %s, %w", key, err)
1✔
546
                klog.Error(err)
1✔
547
                if patchErr := c.patchSubnetStatus(subnet, "ValidateSubnetVlanFailed", err.Error()); patchErr != nil {
1✔
548
                        klog.Error(patchErr)
×
549
                        return patchErr
×
550
                }
×
551
                return err
1✔
552
        }
553

554
        if err = util.ValidateSubnet(*subnet); err != nil {
×
555
                klog.Errorf("failed to validate subnet %s, %v", subnet.Name, err)
×
556
                if patchErr := c.patchSubnetStatus(subnet, "ValidateLogicalSwitchFailed", err.Error()); patchErr != nil {
×
557
                        klog.Error(patchErr)
×
558
                        return patchErr
×
559
                }
×
560
                return err
×
561
        }
562
        if err = c.patchSubnetStatus(subnet, "ValidateLogicalSwitchSuccess", ""); err != nil {
×
563
                klog.Error(err)
×
564
                return err
×
565
        }
×
566

567
        if err := c.ipam.AddOrUpdateSubnet(subnet.Name, subnet.Spec.CIDRBlock, subnet.Spec.Gateway, subnet.Spec.ExcludeIps); err != nil {
×
568
                klog.Error(err)
×
569
                return err
×
570
        }
×
571

572
        // availableIPStr valued from ipam, so leave update subnet.status after ipam process
573
        subnet, err = c.calcSubnetStatusIP(subnet)
×
574
        if err != nil {
×
575
                klog.Errorf("calculate subnet %s used ip failed, %v", cachedSubnet.Name, err)
×
576
                return err
×
577
        }
×
578

579
        subnet, deleted, err := c.handleSubnetFinalizer(subnet)
×
580
        if err != nil {
×
581
                klog.Errorf("handle subnet finalizer failed %v", err)
×
582
                return err
×
583
        }
×
584
        if deleted {
×
585
                return nil
×
586
        }
×
587

588
        if !isOvnSubnet(subnet) {
×
589
                // subnet provider is not ovn, and vpc is empty, should not reconcile
×
590
                if err = c.patchSubnetStatus(subnet, "SetNonOvnSubnetSuccess", ""); err != nil {
×
591
                        klog.Error(err)
×
592
                        return err
×
593
                }
×
594

595
                subnet.Status.EnsureStandardConditions()
×
596
                klog.Infof("non ovn subnet %s is ready", subnet.Name)
×
597
                return nil
×
598
        }
599

600
        // This validate should be processed after isOvnSubnet, since maybe there's no vpc for subnet not managed by kube-ovn
601
        vpc, err := c.validateVpcBySubnet(subnet)
×
602
        if err != nil {
×
603
                klog.Errorf("failed to get subnet's vpc '%s', %v", subnet.Spec.Vpc, err)
×
604
                return err
×
605
        }
×
606
        _, isMcastQuerierChanged, err := c.reconcileSubnetSpecialIPs(subnet)
×
607
        if err != nil {
×
608
                klog.Errorf("failed to reconcile subnet %s Custom IPs %v", subnet.Name, err)
×
609
                return err
×
610
        }
×
611

612
        needRouter := subnet.Spec.Vlan == "" || subnet.Spec.LogicalGateway ||
×
613
                (subnet.Status.U2OInterconnectionIP != "" && subnet.Spec.U2OInterconnection)
×
614
        // 1. overlay subnet, should add lrp, lrp ip is subnet gw
×
615
        // 2. underlay subnet use logical gw, should add lrp, lrp ip is subnet gw
×
616
        randomAllocateGW := !subnet.Spec.LogicalGateway && vpc.Spec.EnableExternal && subnet.Name == c.config.ExternalGatewaySwitch
×
617
        // 3. underlay subnet use physical gw, vpc has eip, lrp managed in vpc process, lrp ip is random allocation, not subnet gw
×
618

×
619
        gateway := subnet.Spec.Gateway
×
620
        var gatewayMAC string
×
621
        if subnet.Status.U2OInterconnectionIP != "" && subnet.Spec.U2OInterconnection {
×
622
                gateway = subnet.Status.U2OInterconnectionIP
×
623
                gatewayMAC = subnet.Status.U2OInterconnectionMAC
×
624
        }
×
625

626
        if err := c.clearOldU2OResource(subnet); err != nil {
×
627
                klog.Errorf("clear subnet %s old u2o resource failed: %v", subnet.Name, err)
×
628
                return err
×
629
        }
×
630

631
        // Lock VPC to prevent CIDR conflict between concurrent subnet creations in the same VPC
632
        if err := func() error {
×
633
                c.vpcKeyMutex.LockKey(subnet.Spec.Vpc)
×
634
                defer func() { _ = c.vpcKeyMutex.UnlockKey(subnet.Spec.Vpc) }()
×
635

636
                if err := c.checkSubnetConflict(subnet); err != nil {
×
637
                        klog.Errorf("failed to check subnet %s, %v", subnet.Name, err)
×
638
                        return err
×
639
                }
×
640
                // create or update logical switch
641
                if err := c.OVNNbClient.CreateLogicalSwitch(subnet.Name, vpc.Status.Router, subnet.Spec.CIDRBlock, gateway, gatewayMAC, needRouter, randomAllocateGW); err != nil {
×
642
                        klog.Errorf("create logical switch %s: %v", subnet.Name, err)
×
643
                        return err
×
644
                }
×
645
                return nil
×
646
        }(); err != nil {
×
647
                return err
×
648
        }
×
649

650
        // Record the gateway MAC in ipam if router port exists
651
        if needRouter {
×
652
                routerPortName := ovs.LogicalRouterPortName(vpc.Status.Router, subnet.Name)
×
653
                if lrp, err := c.OVNNbClient.GetLogicalRouterPort(routerPortName, true); err == nil && lrp != nil && lrp.MAC != "" {
×
654
                        if err := c.ipam.RecordGatewayMAC(subnet.Name, lrp.MAC); err != nil {
×
655
                                klog.Warningf("failed to record gateway MAC %s for subnet %s: %v", lrp.MAC, subnet.Name, err)
×
656
                        }
×
657
                } else {
×
658
                        klog.V(3).Infof("router port %s not found or has no MAC, skipping gateway MAC record", routerPortName)
×
659
                }
×
660
        }
661

662
        if isMcastQuerierChanged {
×
663
                if err := c.handleMcastQuerierChange(subnet); err != nil {
×
664
                        klog.Errorf("failed to handle mcast querier IP change for subnet %s: %v", subnet.Name, err)
×
665
                        return err
×
666
                }
×
667
        }
668

669
        subnet.Status.EnsureStandardConditions()
×
670

×
671
        if err := c.updateSubnetDHCPOption(subnet, needRouter); err != nil {
×
672
                klog.Errorf("failed to update subnet %s dhcpOptions: %v", subnet.Name, err)
×
673
                return err
×
674
        }
×
675

676
        if c.config.EnableLb && subnet.Name != c.config.NodeSwitch {
×
677
                lbs := []string{
×
678
                        vpc.Status.TCPLoadBalancer,
×
679
                        vpc.Status.TCPSessionLoadBalancer,
×
680
                        vpc.Status.UDPLoadBalancer,
×
681
                        vpc.Status.UDPSessionLoadBalancer,
×
682
                        vpc.Status.SctpLoadBalancer,
×
683
                        vpc.Status.SctpSessionLoadBalancer,
×
684
                }
×
685
                if subnet.Spec.EnableLb != nil && *subnet.Spec.EnableLb {
×
686
                        if lbErr := c.OVNNbClient.LogicalSwitchUpdateLoadBalancers(subnet.Name, ovsdb.MutateOperationInsert, lbs...); lbErr != nil {
×
687
                                klog.Error(lbErr)
×
688
                                if patchErr := c.patchSubnetStatus(subnet, "AddLbToLogicalSwitchFailed", lbErr.Error()); patchErr != nil {
×
689
                                        klog.Error(patchErr)
×
690
                                        return errors.Join(lbErr, patchErr)
×
691
                                }
×
692
                                return lbErr
×
693
                        }
694
                } else {
×
695
                        if err := c.OVNNbClient.LogicalSwitchUpdateLoadBalancers(subnet.Name, ovsdb.MutateOperationDelete, lbs...); err != nil {
×
696
                                klog.Errorf("remove load-balancer from subnet %s failed: %v", subnet.Name, err)
×
697
                                return err
×
698
                        }
×
699
                }
700
        }
701

702
        if err := c.reconcileSubnet(subnet); err != nil {
×
703
                klog.Errorf("reconcile subnet for %s failed, %v", subnet.Name, err)
×
704
                return err
×
705
        }
×
706

707
        subnet.Status.U2OInterconnectionVPC = ""
×
708
        if subnet.Spec.U2OInterconnection {
×
709
                subnet.Status.U2OInterconnectionVPC = vpc.Status.Router
×
710
        }
×
711

712
        if err = c.updateNatOutgoingPolicyRulesStatus(subnet); err != nil {
×
713
                klog.Errorf("failed to update NAT outgoing policy status for subnet %s: %v", subnet.Name, err)
×
714
                return err
×
715
        }
×
716

717
        if subnet.Spec.Private {
×
718
                if privErr := c.OVNNbClient.SetLogicalSwitchPrivate(subnet.Name, subnet.Spec.CIDRBlock, c.config.NodeSwitchCIDR, subnet.Spec.AllowSubnets); privErr != nil {
×
719
                        klog.Error(privErr)
×
720
                        if patchErr := c.patchSubnetStatus(subnet, "SetPrivateLogicalSwitchFailed", privErr.Error()); patchErr != nil {
×
721
                                klog.Error(patchErr)
×
722
                                return errors.Join(privErr, patchErr)
×
723
                        }
×
724
                        return privErr
×
725
                }
726

727
                if err = c.patchSubnetStatus(subnet, "SetPrivateLogicalSwitchSuccess", ""); err != nil {
×
728
                        klog.Error(err)
×
729
                        return err
×
730
                }
×
731
        } else {
×
732
                // clear acl when direction is ""
×
733
                if aclErr := c.OVNNbClient.DeleteAcls(subnet.Name, logicalSwitchKey, "", nil); aclErr != nil {
×
734
                        klog.Error(aclErr)
×
735
                        if patchErr := c.patchSubnetStatus(subnet, "ResetLogicalSwitchAclFailed", aclErr.Error()); patchErr != nil {
×
736
                                klog.Error(patchErr)
×
737
                                return errors.Join(aclErr, patchErr)
×
738
                        }
×
739
                        return aclErr
×
740
                }
741

742
                if err = c.patchSubnetStatus(subnet, "ResetLogicalSwitchAclSuccess", ""); err != nil {
×
743
                        klog.Error(err)
×
744
                        return err
×
745
                }
×
746
        }
747

748
        if aclErr := c.OVNNbClient.UpdateLogicalSwitchACL(subnet.Name, subnet.Spec.CIDRBlock, subnet.Spec.Acls, subnet.Spec.AllowEWTraffic); aclErr != nil {
×
749
                klog.Error(aclErr)
×
750
                if patchErr := c.patchSubnetStatus(subnet, "SetLogicalSwitchAclsFailed", aclErr.Error()); patchErr != nil {
×
751
                        klog.Error(patchErr)
×
752
                        return errors.Join(aclErr, patchErr)
×
753
                }
×
754
                return aclErr
×
755
        }
756

757
        c.updateVpcStatusQueue.Add(subnet.Spec.Vpc)
×
758

×
759
        ippools, err := c.ippoolLister.List(labels.Everything())
×
760
        if err != nil {
×
761
                klog.Errorf("failed to list ippools: %v", err)
×
762
                return err
×
763
        }
×
764

765
        for _, p := range ippools {
×
766
                if p.Spec.Subnet == subnet.Name {
×
767
                        c.addOrUpdateIPPoolQueue.Add(p.Name)
×
768
                }
×
769
        }
770

771
        return nil
×
772
}
773

774
func (c *Controller) handleDeleteLogicalSwitch(key string) (err error) {
×
775
        c.ipam.DeleteSubnet(key)
×
776

×
777
        exist, err := c.OVNNbClient.LogicalSwitchExists(key)
×
778
        if err != nil {
×
779
                klog.Errorf("check logical switch %s exist: %v", key, err)
×
780
                return err
×
781
        }
×
782

783
        // not found, skip
784
        if !exist {
×
785
                return nil
×
786
        }
×
787

788
        // clear acl when direction is ""
789
        if err = c.OVNNbClient.DeleteAcls(key, logicalSwitchKey, "", nil); err != nil {
×
790
                klog.Errorf("clear logical switch %s acls: %v", key, err)
×
791
                return err
×
792
        }
×
793

794
        if err = c.OVNNbClient.DeleteDHCPOptions(key, kubeovnv1.ProtocolDual); err != nil {
×
795
                klog.Errorf("failed to delete dhcp options of logical switch %s %v", key, err)
×
796
                return err
×
797
        }
×
798

799
        if err = c.OVNNbClient.DeleteLogicalSwitch(key); err != nil {
×
800
                klog.Errorf("delete logical switch %s: %v", key, err)
×
801
                return err
×
802
        }
×
803

804
        nss, err := c.namespacesLister.List(labels.Everything())
×
805
        if err != nil {
×
806
                klog.Errorf("failed to list namespaces, %v", err)
×
807
                return err
×
808
        }
×
809

810
        // re-annotate namespace
811
        for _, ns := range nss {
×
812
                annotations := ns.GetAnnotations()
×
813
                if annotations == nil {
×
814
                        continue
×
815
                }
816

817
                if slices.Contains(strings.Split(annotations[util.LogicalSwitchAnnotation], ","), key) {
×
818
                        c.enqueueAddNamespace(ns)
×
819
                }
×
820
        }
821

822
        return c.delLocalnet(key)
×
823
}
824

825
func (c *Controller) handleDeleteSubnet(subnet *kubeovnv1.Subnet) error {
×
826
        c.subnetKeyMutex.LockKey(subnet.Name)
×
827
        defer func() { _ = c.subnetKeyMutex.UnlockKey(subnet.Name) }()
×
828

829
        c.updateVpcStatusQueue.Add(subnet.Spec.Vpc)
×
830
        klog.Infof("delete u2o interconnection policy route for subnet %s", subnet.Name)
×
831
        if err := c.deletePolicyRouteForU2OInterconn(subnet); err != nil {
×
832
                klog.Errorf("failed to delete policy route for underlay to overlay subnet interconnection %s, %v", subnet.Name, err)
×
833
                return err
×
834
        }
×
835
        if subnet.Spec.Vpc != c.config.ClusterRouter {
×
836
                if err := c.deleteStaticRouteForU2OInterconn(subnet); err != nil {
×
837
                        klog.Errorf("failed to delete static route for underlay to overlay subnet interconnection %s, %v", subnet.Name, err)
×
838
                        return err
×
839
                }
×
840
        }
841

842
        u2oInterconnName := fmt.Sprintf(util.U2OInterconnName, subnet.Spec.Vpc, subnet.Name)
×
843
        if err := c.config.KubeOvnClient.KubeovnV1().IPs().Delete(context.Background(), u2oInterconnName, metav1.DeleteOptions{}); err != nil {
×
844
                if !k8serrors.IsNotFound(err) {
×
845
                        klog.Errorf("failed to delete ip %s, %v", u2oInterconnName, err)
×
846
                        return err
×
847
                }
×
848
        }
849

850
        if subnet.Spec.Vpc != c.config.ClusterRouter {
×
851
                if err := c.deleteCustomVPCPolicyRoutesForSubnet(subnet); err != nil {
×
852
                        klog.Errorf("failed to delete custom vpc routes subnet %s, %v", subnet.Name, err)
×
853
                        return err
×
854
                }
×
855
        }
856

857
        klog.Infof("delete policy route for %s subnet %s", subnet.Spec.GatewayType, subnet.Name)
×
858
        if err := c.deletePolicyRouteByGatewayType(subnet, subnet.Spec.GatewayType, true); err != nil {
×
859
                klog.Errorf("failed to delete policy route for overlay subnet %s, %v", subnet.Name, err)
×
860
                return err
×
861
        }
×
862

863
        err := c.handleDeleteLogicalSwitch(subnet.Name)
×
864
        if err != nil {
×
865
                klog.Errorf("failed to delete logical switch %s %v", subnet.Name, err)
×
866
                return err
×
867
        }
×
868

869
        var router string
×
870
        vpc, err := c.vpcsLister.Get(subnet.Spec.Vpc)
×
871
        if err != nil {
×
872
                if !k8serrors.IsNotFound(err) {
×
873
                        klog.Errorf("get vpc %s: %v", subnet.Spec.Vpc, err)
×
874
                        return err
×
875
                }
×
876
                router = c.config.ClusterRouter
×
877
        } else {
×
878
                router = vpc.Status.Router
×
879
        }
×
880

881
        lspName := fmt.Sprintf("%s-%s", subnet.Name, router)
×
882
        lrpName := fmt.Sprintf("%s-%s", router, subnet.Name)
×
883
        if err = c.OVNNbClient.RemoveLogicalPatchPort(lspName, lrpName); err != nil {
×
884
                klog.Errorf("delete router port %s and %s:%v", lspName, lrpName, err)
×
885
                return err
×
886
        }
×
887

888
        vlans, err := c.vlansLister.List(labels.Everything())
×
889
        if err != nil && !k8serrors.IsNotFound(err) {
×
890
                klog.Errorf("failed to list vlans: %v", err)
×
891
                return err
×
892
        }
×
893

894
        for _, vlan := range vlans {
×
895
                if err = c.updateVlanStatusForSubnetDeletion(vlan, subnet.Name); err != nil {
×
896
                        klog.Error(err)
×
897
                        return err
×
898
                }
×
899
        }
900

901
        return nil
×
902
}
903

904
func (c *Controller) updateVlanStatusForSubnetDeletion(vlan *kubeovnv1.Vlan, subnet string) error {
×
905
        if !slices.Contains(vlan.Status.Subnets, subnet) {
×
906
                return nil
×
907
        }
×
908

909
        newVlan := vlan.DeepCopy()
×
910
        newVlan.Status.Subnets = util.RemoveString(newVlan.Status.Subnets, subnet)
×
911
        _, err := c.config.KubeOvnClient.KubeovnV1().Vlans().UpdateStatus(context.Background(), newVlan, metav1.UpdateOptions{})
×
912
        if err != nil {
×
913
                klog.Errorf("failed to update status of vlan %s: %v", vlan.Name, err)
×
914
                return err
×
915
        }
×
916

917
        return nil
×
918
}
919

920
func (c *Controller) reconcileSubnet(subnet *kubeovnv1.Subnet) error {
×
921
        if err := c.reconcileNamespaces(subnet); err != nil {
×
922
                klog.Errorf("reconcile namespaces for subnet %s failed, %v", subnet.Name, err)
×
923
                return err
×
924
        }
×
925

926
        if err := c.reconcileRouteTableForSubnet(subnet); err != nil {
×
927
                klog.Errorf("reconcile route table for subnet %s failed, %v", subnet.Name, err)
×
928
                return err
×
929
        }
×
930

931
        if subnet.Spec.Vpc == c.config.ClusterRouter {
×
932
                if err := c.reconcileOvnDefaultVpcRoute(subnet); err != nil {
×
933
                        klog.Errorf("reconcile default vpc ovn route for subnet %s failed: %v", subnet.Name, err)
×
934
                        return err
×
935
                }
×
936
        } else if err := c.reconcileCustomVpcStaticRoute(subnet); err != nil {
×
937
                klog.Errorf("reconcile custom vpc ovn route for subnet %s failed: %v", subnet.Name, err)
×
938
                return err
×
939
        }
×
940

941
        if err := c.reconcileVlan(subnet); err != nil {
×
942
                klog.Errorf("reconcile vlan for subnet %s failed, %v", subnet.Name, err)
×
943
                return err
×
944
        }
×
945

946
        if err := c.reconcileVips(subnet); err != nil {
×
947
                klog.Errorf("reconcile vips for subnet %s failed, %v", subnet.Name, err)
×
948
                return err
×
949
        }
×
950
        return nil
×
951
}
952

953
func (c *Controller) reconcileVips(subnet *kubeovnv1.Subnet) error {
1✔
954
        /* get all virtual port belongs to this logical switch */
1✔
955
        lsps, err := c.OVNNbClient.ListLogicalSwitchPorts(true, map[string]string{logicalSwitchKey: subnet.Name}, func(lsp *ovnnb.LogicalSwitchPort) bool {
1✔
956
                return lsp.Type == "virtual"
×
957
        })
×
958
        if err != nil {
1✔
959
                klog.Errorf("failed to find virtual port for subnet %s: %v", subnet.Name, err)
×
960
                return err
×
961
        }
×
962

963
        /* filter all invalid virtual port */
964
        existVips := make(map[string]string) // key is vip, value is port name
1✔
965
        for _, lsp := range lsps {
2✔
966
                vip, ok := lsp.Options["virtual-ip"]
1✔
967
                if !ok {
1✔
968
                        continue // ignore vip which is empty
×
969
                }
970

971
                if net.ParseIP(vip) == nil {
1✔
972
                        continue // ignore invalid vip
×
973
                }
974

975
                existVips[vip] = lsp.Name
1✔
976
        }
977

978
        /* filter virtual port to be added and old virtual port to be deleted */
979
        var newVips []string
1✔
980
        for _, vip := range subnet.Spec.Vips {
2✔
981
                if _, ok := existVips[vip]; !ok {
2✔
982
                        // new virtual port to be added
1✔
983
                        newVips = append(newVips, vip)
1✔
984
                } else {
2✔
985
                        // delete old virtual port that do not need to be deleted
1✔
986
                        delete(existVips, vip)
1✔
987
                }
1✔
988
        }
989

990
        // delete old virtual ports
991
        for _, lspName := range existVips {
2✔
992
                if err = c.OVNNbClient.DeleteLogicalSwitchPort(lspName); err != nil {
1✔
993
                        klog.Errorf("delete virtual port %s lspName from logical switch %s: %v", lspName, subnet.Name, err)
×
994
                        return err
×
995
                }
×
996
        }
997

998
        // add new virtual port
999
        if err = c.OVNNbClient.CreateVirtualLogicalSwitchPorts(subnet.Name, newVips...); err != nil {
1✔
1000
                klog.Errorf("create virtual port with vips %v from logical switch %s: %v", newVips, subnet.Name, err)
×
1001
                return err
×
1002
        }
×
1003

1004
        c.syncVirtualPortsQueue.Add(subnet.Name)
1✔
1005
        return nil
1✔
1006
}
1007

1008
func (c *Controller) syncVirtualPort(key string) error {
1✔
1009
        subnet, err := c.subnetsLister.Get(key)
1✔
1010
        if err != nil {
1✔
1011
                if k8serrors.IsNotFound(err) {
×
1012
                        return nil
×
1013
                }
×
1014
                klog.Errorf("failed to get subnet %s, %v", key, err)
×
1015
                return err
×
1016
        }
1017
        if len(subnet.Spec.Vips) == 0 {
1✔
1018
                return nil
×
1019
        }
×
1020

1021
        externalIDs := map[string]string{
1✔
1022
                logicalSwitchKey: subnet.Name,
1✔
1023
                "attach-vips":    "true",
1✔
1024
        }
1✔
1025

1✔
1026
        lsps, err := c.OVNNbClient.ListNormalLogicalSwitchPorts(true, externalIDs)
1✔
1027
        if err != nil {
1✔
1028
                klog.Errorf("list logical switch %s ports: %v", subnet.Name, err)
×
1029
                return err
×
1030
        }
×
1031

1032
        for _, vip := range subnet.Spec.Vips {
2✔
1033
                if !util.CIDRContainIP(subnet.Spec.CIDRBlock, vip) {
1✔
1034
                        klog.Errorf("vip %s is out of range to subnet %s", vip, subnet.Name)
×
1035
                        continue
×
1036
                }
1037

1038
                var virtualParents []string
1✔
1039
                for _, lsp := range lsps {
2✔
1040
                        vips, ok := lsp.ExternalIDs["vips"]
1✔
1041
                        if !ok {
1✔
1042
                                continue // ignore vips which is empty
×
1043
                        }
1044

1045
                        if slices.Contains(strings.Split(vips, ","), vip) {
2✔
1046
                                virtualParents = append(virtualParents, lsp.Name)
1✔
1047
                        }
1✔
1048
                }
1049

1050
                // logical switch port has no valid vip
1051
                if len(virtualParents) == 0 {
2✔
1052
                        continue
1✔
1053
                }
1054

1055
                if err = c.OVNNbClient.SetLogicalSwitchPortVirtualParents(subnet.Name, strings.Join(virtualParents, ","), vip); err != nil {
1✔
1056
                        klog.Errorf("set vip %s virtual parents %v: %v", vip, virtualParents, err)
×
1057
                        return err
×
1058
                }
×
1059
        }
1060

1061
        return nil
1✔
1062
}
1063

1064
func (c *Controller) reconcileNamespaces(subnet *kubeovnv1.Subnet) error {
×
1065
        var (
×
1066
                namespaces []*v1.Namespace
×
1067
                err        error
×
1068
        )
×
1069

×
1070
        // 1. get all namespaces should be updated
×
1071
        expectNss, err := c.getNamespacesBySelector(subnet.Spec.NamespaceSelectors)
×
1072
        if err != nil {
×
1073
                klog.Errorf("failed to list namespaces by selector, %v", err)
×
1074
                return err
×
1075
        }
×
1076
        for _, ns := range subnet.Spec.Namespaces {
×
1077
                if !slices.Contains(expectNss, ns) {
×
1078
                        expectNss = append(expectNss, ns)
×
1079
                }
×
1080
        }
1081

1082
        // 2. update namespaces
1083
        for _, expectNs := range expectNss {
×
1084
                checkNs, err := c.namespacesLister.Get(expectNs)
×
1085
                if err != nil {
×
1086
                        if k8serrors.IsNotFound(err) {
×
1087
                                continue
×
1088
                        }
1089
                        klog.Error(err)
×
1090
                        return err
×
1091
                }
1092
                if checkNs.Annotations != nil && slices.Contains(strings.Split(checkNs.Annotations[util.LogicalSwitchAnnotation], ","), subnet.Name) {
×
1093
                        // when subnet cidr changed, the ns annotation with the subnet should be updated
×
1094
                        if !slices.Contains(strings.Split(checkNs.Annotations[util.CidrAnnotation], ";"), subnet.Spec.CIDRBlock) {
×
1095
                                c.addNamespaceQueue.Add(checkNs.Name)
×
1096
                        }
×
1097
                        continue
×
1098
                }
1099
                c.addNamespaceQueue.Add(expectNs)
×
1100
        }
1101

1102
        // 3. update unbind namespace annotation
1103
        if namespaces, err = c.namespacesLister.List(labels.Everything()); err != nil {
×
1104
                klog.Errorf("failed to list namespaces, %v", err)
×
1105
                return err
×
1106
        }
×
1107

1108
        for _, ns := range namespaces {
×
1109
                if ns.Annotations != nil && slices.Contains(strings.Split(ns.Annotations[util.LogicalSwitchAnnotation], ","), subnet.Name) {
×
1110
                        if slices.Contains(expectNss, ns.Name) {
×
1111
                                continue
×
1112
                        }
1113
                        // ns deleted from subnet.Spec.Namespaces or subnet delete namespaceSelectors which match the checked namespace
1114
                        c.addNamespaceQueue.Add(ns.Name)
×
1115
                }
1116
        }
1117

1118
        return nil
×
1119
}
1120

1121
func (c *Controller) getNamespacesBySelector(nsSelectors []metav1.LabelSelector) ([]string, error) {
×
1122
        var expectNss []string
×
1123
        for _, nsSelector := range nsSelectors {
×
1124
                matchSelector, err := metav1.LabelSelectorAsSelector(&nsSelector)
×
1125
                if err != nil {
×
1126
                        klog.Errorf("failed to convert label selector, %v", err)
×
1127
                        return expectNss, err
×
1128
                }
×
1129

1130
                nss, err := c.namespacesLister.List(matchSelector)
×
1131
                if err != nil {
×
1132
                        klog.Errorf("failed to list namespaces by selector, %v", err)
×
1133
                        return expectNss, err
×
1134
                }
×
1135
                for _, ns := range nss {
×
1136
                        expectNss = append(expectNss, ns.Name)
×
1137
                }
×
1138
        }
1139
        return expectNss, nil
×
1140
}
1141

1142
func (c *Controller) reconcileCustomVpcBfdStaticRoute(vpcName, subnetName string) error {
×
1143
        // vpc enable bfd and subnet enable ecmp
×
1144
        // use static ecmp route with bfd
×
1145
        ovnEips, err := c.ovnEipsLister.List(labels.SelectorFromSet(labels.Set{util.OvnEipTypeLabel: util.OvnEipTypeLSP}))
×
1146
        if err != nil {
×
1147
                klog.Errorf("failed to list node external ovn eip, %v", err)
×
1148
                return err
×
1149
        }
×
1150
        if len(ovnEips) < 2 {
×
1151
                err := fmt.Errorf("ecmp route with bfd for HA, which need two %s type eips at least, has %d", util.OvnEipTypeLSP, len(ovnEips))
×
1152
                klog.Error(err)
×
1153
                return err
×
1154
        }
×
1155

1156
        subnet, err := c.subnetsLister.Get(subnetName)
×
1157
        if err != nil {
×
1158
                klog.Errorf("failed to get subnet %s, %v", subnetName, err)
×
1159
                return err
×
1160
        }
×
1161
        cachedVpc, err := c.vpcsLister.Get(vpcName)
×
1162
        if err != nil {
×
1163
                if k8serrors.IsNotFound(err) {
×
1164
                        return nil
×
1165
                }
×
1166
                klog.Errorf("failed to get vpc %s, %v", vpcName, err)
×
1167
                return err
×
1168
        }
1169

1170
        var (
×
1171
                needUpdate, v4Exist bool
×
1172
                lrpEipName          string
×
1173
        )
×
1174

×
1175
        lrpEipName = fmt.Sprintf("%s-%s", vpcName, c.config.ExternalGatewaySwitch)
×
1176
        lrpEip, err := c.ovnEipsLister.Get(lrpEipName)
×
1177
        if err != nil {
×
1178
                err := fmt.Errorf("failed to get lrp eip %s, %w", lrpEipName, err)
×
1179
                klog.Error(err)
×
1180
                return err
×
1181
        }
×
1182
        if !lrpEip.Status.Ready || lrpEip.Status.V4Ip == "" {
×
1183
                err := fmt.Errorf("lrp eip %q not ready", lrpEipName)
×
1184
                klog.Error(err)
×
1185
                return err
×
1186
        }
×
1187
        vpc := cachedVpc.DeepCopy()
×
1188

×
1189
        for _, eip := range ovnEips {
×
1190
                if !eip.Status.Ready || eip.Status.V4Ip == "" {
×
1191
                        err := fmt.Errorf("ovn eip %q not ready", eip.Name)
×
1192
                        klog.Error(err)
×
1193
                        return err
×
1194
                }
×
1195
                bfd, err := c.OVNNbClient.CreateBFD(lrpEipName, eip.Status.V4Ip, c.config.BfdMinRx, c.config.BfdMinTx, c.config.BfdDetectMult, nil)
×
1196
                if err != nil {
×
1197
                        klog.Error(err)
×
1198
                        return err
×
1199
                }
×
1200
                // TODO:// support v6
1201
                v4Exist = false
×
1202
                for _, route := range vpc.Spec.StaticRoutes {
×
1203
                        if route.Policy == kubeovnv1.PolicySrc &&
×
1204
                                route.NextHopIP == eip.Status.V4Ip &&
×
1205
                                route.ECMPMode == util.StaticRouteBfdEcmp &&
×
1206
                                route.CIDR == subnet.Spec.CIDRBlock &&
×
1207
                                route.RouteTable == subnet.Spec.RouteTable {
×
1208
                                v4Exist = true
×
1209
                                break
×
1210
                        }
1211
                }
1212
                if !v4Exist {
×
1213
                        // add ecmp type static route with bfd
×
1214
                        route := &kubeovnv1.StaticRoute{
×
1215
                                Policy:     kubeovnv1.PolicySrc,
×
1216
                                CIDR:       subnet.Spec.CIDRBlock,
×
1217
                                NextHopIP:  eip.Status.V4Ip,
×
1218
                                ECMPMode:   util.StaticRouteBfdEcmp,
×
1219
                                BfdID:      bfd.UUID,
×
1220
                                RouteTable: subnet.Spec.RouteTable,
×
1221
                        }
×
1222
                        klog.Infof("add ecmp bfd static route %v", route)
×
1223
                        vpc.Spec.StaticRoutes = append(vpc.Spec.StaticRoutes, route)
×
1224
                        needUpdate = true
×
1225
                }
×
1226
        }
1227
        if needUpdate {
×
1228
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
1229
                        klog.Errorf("failed to update vpc spec static route %s, %v", vpc.Name, err)
×
1230
                        return err
×
1231
                }
×
1232
                if err = c.patchVpcBfdStatus(vpc.Name); err != nil {
×
1233
                        klog.Errorf("failed to patch vpc %s, %v", vpc.Name, err)
×
1234
                        return err
×
1235
                }
×
1236
        }
1237
        return nil
×
1238
}
1239

1240
func (c *Controller) reconcileCustomVpcDelNormalStaticRoute(vpcName string) error {
×
1241
        // normal static route is prior than ecmp bfd static route
×
1242
        // if use ecmp bfd static route, normal static route should not exist
×
1243
        defaultExternalSubnet, err := c.subnetsLister.Get(c.config.ExternalGatewaySwitch)
×
1244
        if err != nil {
×
1245
                klog.Errorf("failed to get default external switch subnet %s: %v", c.config.ExternalGatewaySwitch, err)
×
1246
                return err
×
1247
        }
×
1248
        gatewayV4, gatewayV6 := util.SplitStringIP(defaultExternalSubnet.Spec.Gateway)
×
1249
        needUpdate := false
×
1250
        cachedVpc, err := c.vpcsLister.Get(vpcName)
×
1251
        if err != nil {
×
1252
                if k8serrors.IsNotFound(err) {
×
1253
                        return nil
×
1254
                }
×
1255
                klog.Errorf("failed to get vpc %s, %v", vpcName, err)
×
1256
                return err
×
1257
        }
1258
        vpc := cachedVpc.DeepCopy()
×
1259
        routeTotal := len(vpc.Spec.StaticRoutes)
×
1260
        routes := make([]*kubeovnv1.StaticRoute, 0, routeTotal)
×
1261
        for _, route := range vpc.Spec.StaticRoutes {
×
1262
                if route.Policy == kubeovnv1.PolicyDst &&
×
1263
                        (route.NextHopIP == gatewayV4 || route.NextHopIP == gatewayV6) &&
×
1264
                        (route.CIDR == "0.0.0.0/0" || route.CIDR == "::/0") {
×
1265
                        klog.Infof("in order to use ecmp bfd route, need remove normal static route %v", route)
×
1266
                        needUpdate = true
×
1267
                } else {
×
1268
                        routes = append(routes, route)
×
1269
                }
×
1270
        }
1271

1272
        if needUpdate {
×
1273
                vpc.Spec.StaticRoutes = routes
×
1274
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
1275
                        klog.Errorf("failed to update vpc spec static route %s, %v", vpc.Name, err)
×
1276
                        return err
×
1277
                }
×
1278
        }
1279

1280
        if err = c.patchVpcBfdStatus(vpc.Name); err != nil {
×
1281
                klog.Errorf("failed to patch vpc %s, %v", vpc.Name, err)
×
1282
                return err
×
1283
        }
×
1284

1285
        return nil
×
1286
}
1287

1288
func (c *Controller) reconcileDistributedSubnetRouteInDefaultVpc(subnet *kubeovnv1.Subnet) error {
×
1289
        if subnet.Spec.GatewayNode != "" || subnet.Status.ActivateGateway != "" {
×
1290
                klog.Infof("delete old centralized policy route for subnet %s", subnet.Name)
×
1291
                if err := c.deletePolicyRouteForCentralizedSubnet(subnet); err != nil {
×
1292
                        klog.Errorf("failed to delete policy route for subnet %s, %v", subnet.Name, err)
×
1293
                        return err
×
1294
                }
×
1295

1296
                subnet.Spec.GatewayNode = ""
×
1297
                if _, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Update(context.Background(), subnet, metav1.UpdateOptions{}); err != nil {
×
1298
                        klog.Errorf("failed to remove gatewayNode or activateGateway from subnet %s, %v", subnet.Name, err)
×
1299
                        return err
×
1300
                }
×
1301
                subnet.Status.ActivateGateway = ""
×
1302
                if err := c.patchSubnetStatus(subnet, "ChangeToDistributedGw", ""); err != nil {
×
1303
                        klog.Error(err)
×
1304
                        return err
×
1305
                }
×
1306
        }
1307

1308
        nodes, err := c.nodesLister.List(labels.Everything())
×
1309
        if err != nil {
×
1310
                klog.Errorf("failed to list nodes: %v", err)
×
1311
                return err
×
1312
        }
×
1313
        for _, node := range nodes {
×
1314
                if err = c.createPortGroupForDistributedSubnet(node, subnet); err != nil {
×
1315
                        klog.Errorf("failed to create port group %v", err)
×
1316
                        return err
×
1317
                }
×
1318
                if node.Annotations[util.AllocatedAnnotation] != "true" {
×
1319
                        klog.Warningf("node %s has not been successfully initialized, skip adding policy route for subnet %s", node.Name, subnet.Name)
×
1320
                        continue
×
1321
                }
1322
                nodeIP, err := getNodeTunlIP(node)
×
1323
                if err != nil {
×
1324
                        klog.Errorf("failed to get node %s tunnel ip, %v", node.Name, err)
×
1325
                        return err
×
1326
                }
×
1327
                nextHop := getNextHopByTunnelIP(nodeIP)
×
1328
                v4IP, v6IP := util.SplitStringIP(nextHop)
×
1329
                if err = c.addPolicyRouteForDistributedSubnet(subnet, node.Name, v4IP, v6IP); err != nil {
×
1330
                        klog.Errorf("failed to add policy router for node %s and subnet %s: %v", node.Name, subnet.Name, err)
×
1331
                        return err
×
1332
                }
×
1333
        }
1334

1335
        portGroups, err := c.OVNNbClient.ListPortGroups(map[string]string{"subnet": subnet.Name, "node": "", networkPolicyKey: ""})
×
1336
        if err != nil {
×
1337
                klog.Errorf("failed to list port groups for subnet %s: %v", subnet.Name, err)
×
1338
                return err
×
1339
        }
×
1340

1341
        pods, err := c.podsLister.Pods(metav1.NamespaceAll).List(labels.Everything())
×
1342
        if err != nil {
×
1343
                klog.Errorf("failed to list pods %v", err)
×
1344
                return err
×
1345
        }
×
1346
        for _, pod := range pods {
×
1347
                if !isPodAlive(pod) || pod.Spec.NodeName == "" {
×
1348
                        continue
×
1349
                }
1350

1351
                podNets, err := c.getPodKubeovnNets(pod)
×
1352
                if err != nil {
×
1353
                        klog.Errorf("failed to get pod nets %v", err)
×
1354
                        continue
×
1355
                }
1356

1357
                podPorts := make([]string, 0, 1)
×
1358
                for _, podNet := range podNets {
×
1359
                        if !isOvnSubnet(podNet.Subnet) {
×
1360
                                continue
×
1361
                        }
1362

1363
                        if pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, podNet.ProviderName)] == "" || pod.Annotations[fmt.Sprintf(util.LogicalSwitchAnnotationTemplate, podNet.ProviderName)] != subnet.Name {
×
1364
                                continue
×
1365
                        }
1366

1367
                        podName := c.getNameByPod(pod)
×
1368
                        portName := ovs.PodNameToPortName(podName, pod.Namespace, podNet.ProviderName)
×
1369
                        podPorts = append(podPorts, portName)
×
1370
                }
1371

1372
                pgName := getOverlaySubnetsPortGroupName(subnet.Name, pod.Spec.NodeName)
×
1373
                portsToAdd := make([]string, 0, len(podPorts))
×
1374
                for _, port := range podPorts {
×
1375
                        exist, err := c.OVNNbClient.LogicalSwitchPortExists(port)
×
1376
                        if err != nil {
×
1377
                                klog.Error(err)
×
1378
                                return err
×
1379
                        }
×
1380

1381
                        if !exist {
×
1382
                                klog.Errorf("lsp does not exist for pod %v, please delete the pod and retry", port)
×
1383
                                continue
×
1384
                        }
1385

1386
                        portsToAdd = append(portsToAdd, port)
×
1387
                }
1388

1389
                // remove lsp from other port groups
1390
                // we need to do this because the pod, e.g. a sts/vm, can be rescheduled to another node
1391
                for _, pg := range portGroups {
×
1392
                        if pg.Name == pgName {
×
1393
                                continue
×
1394
                        }
1395
                        if err = c.OVNNbClient.PortGroupRemovePorts(pg.Name, podPorts...); err != nil {
×
1396
                                klog.Errorf("remove ports from port group %s: %v", pg.Name, err)
×
1397
                                return err
×
1398
                        }
×
1399
                }
1400
                // add ports to the port group
1401
                if err = c.OVNNbClient.PortGroupAddPorts(pgName, portsToAdd...); err != nil {
×
1402
                        klog.Errorf("add ports to port group %s: %v", pgName, err)
×
1403
                        return err
×
1404
                }
×
1405
        }
1406
        return nil
×
1407
}
1408

1409
func (c *Controller) reconcileDefaultCentralizedSubnetRouteInDefaultVpc(subnet *kubeovnv1.Subnet) error {
×
1410
        gatewayNodes, err := c.getGatewayNodes(subnet)
×
1411
        if err != nil {
×
1412
                klog.Errorf("failed to get gateway nodes for subnet %s: %v", subnet.Name, err)
×
1413
                return err
×
1414
        }
×
1415

1416
        // check if activateGateway still ready
1417
        if subnet.Status.ActivateGateway != "" && slices.Contains(gatewayNodes, subnet.Status.ActivateGateway) {
×
1418
                node, err := c.nodesLister.Get(subnet.Status.ActivateGateway)
×
1419
                if err == nil && nodeReady(node) {
×
1420
                        klog.Infof("subnet %s uses the old activate gw %s", subnet.Name, node.Name)
×
1421

×
1422
                        nodeTunlIPAddr, err := getNodeTunlIP(node)
×
1423
                        if err != nil {
×
1424
                                klog.Errorf("failed to get gatewayNode tunnel ip for subnet %s", subnet.Name)
×
1425
                                return err
×
1426
                        }
×
1427
                        nextHop := getNextHopByTunnelIP(nodeTunlIPAddr)
×
1428
                        if err = c.addPolicyRouteForCentralizedSubnet(subnet, subnet.Status.ActivateGateway, nil, strings.Split(nextHop, ",")); err != nil {
×
1429
                                klog.Errorf("failed to add active-backup policy route for centralized subnet %s: %v", subnet.Name, err)
×
1430
                                return err
×
1431
                        }
×
1432
                        return nil
×
1433
                }
1434
        }
1435

1436
        klog.Info("find a new activate node")
×
1437
        // need a new activate gateway
×
1438
        newActivateNode := ""
×
1439
        var nodeTunlIPAddr []net.IP
×
1440
        for _, gw := range gatewayNodes {
×
1441
                node, err := c.nodesLister.Get(gw)
×
1442
                if err == nil && nodeReady(node) {
×
1443
                        newActivateNode = node.Name
×
1444
                        nodeTunlIPAddr, err = getNodeTunlIP(node)
×
1445
                        if err != nil {
×
1446
                                klog.Error(err)
×
1447
                                return err
×
1448
                        }
×
1449
                        klog.Infof("subnet %s uses a new activate gw %s", subnet.Name, node.Name)
×
1450
                        break
×
1451
                }
1452
        }
1453
        if newActivateNode == "" {
×
1454
                klog.Warningf("all gateways of subnet %s are not ready", subnet.Name)
×
1455
                subnet.Status.ActivateGateway = newActivateNode
×
1456
                if err := c.patchSubnetStatus(subnet, "NoActiveGatewayFound", fmt.Sprintf("subnet %s gws are not ready", subnet.Name)); err != nil {
×
1457
                        klog.Error(err)
×
1458
                        return err
×
1459
                }
×
1460

1461
                return fmt.Errorf("subnet %s gws are not ready", subnet.Name)
×
1462
        }
1463

1464
        nextHop := getNextHopByTunnelIP(nodeTunlIPAddr)
×
1465
        klog.Infof("subnet %s configure new gateway node, nextHop %s", subnet.Name, nextHop)
×
1466
        if err := c.addPolicyRouteForCentralizedSubnet(subnet, newActivateNode, nil, strings.Split(nextHop, ",")); err != nil {
×
1467
                klog.Errorf("failed to add policy route for active-backup centralized subnet %s: %v", subnet.Name, err)
×
1468
                return err
×
1469
        }
×
1470
        subnet.Status.ActivateGateway = newActivateNode
×
1471
        if err := c.patchSubnetStatus(subnet, "ReconcileCentralizedGatewaySuccess", ""); err != nil {
×
1472
                klog.Error(err)
×
1473
                return err
×
1474
        }
×
1475

1476
        klog.Infof("delete old distributed policy route for subnet %s", subnet.Name)
×
1477
        if err := c.deletePolicyRouteByGatewayType(subnet, kubeovnv1.GWDistributedType, false); err != nil {
×
1478
                klog.Errorf("failed to delete policy route for overlay subnet %s, %v", subnet.Name, err)
×
1479
                return err
×
1480
        }
×
1481
        return nil
×
1482
}
1483

1484
func (c *Controller) reconcileEcmpCentralizedSubnetRouteInDefaultVpc(subnet *kubeovnv1.Subnet) error {
×
1485
        gatewayNodes, err := c.getGatewayNodes(subnet)
×
1486
        if err != nil {
×
1487
                klog.Errorf("failed to get gateway nodes for subnet %s: %v", subnet.Name, err)
×
1488
                return err
×
1489
        }
×
1490

1491
        nodeIPs := [2][]string{make([]string, 0, len(gatewayNodes)), make([]string, 0, len(gatewayNodes))}
×
1492
        nameIPMaps := [2]map[string]string{make(map[string]string, len(gatewayNodes)), make(map[string]string, len(gatewayNodes))}
×
1493

×
1494
        for _, gw := range gatewayNodes {
×
1495
                node, err := c.nodesLister.Get(gw)
×
1496
                if err != nil {
×
1497
                        klog.Errorf("failed to get gw node %s, %v", gw, err)
×
1498
                        continue
×
1499
                }
1500
                if !nodeReady(node) {
×
1501
                        klog.Errorf("gateway node %v is not ready", gw)
×
1502
                        continue
×
1503
                }
1504
                nexthopNodeIP := strings.TrimSpace(node.Annotations[util.IPAddressAnnotation])
×
1505
                if nexthopNodeIP == "" {
×
1506
                        klog.Errorf("gateway node %v has no ip annotation", node.Name)
×
1507
                        continue
×
1508
                }
1509
                nexthopV4, nexthopV6 := util.SplitStringIP(nexthopNodeIP)
×
1510
                if nexthopV4 != "" {
×
1511
                        nameIPMaps[0][node.Name] = nexthopV4
×
1512
                        nodeIPs[0] = append(nodeIPs[0], nexthopV4)
×
1513
                }
×
1514
                if nexthopV6 != "" {
×
1515
                        nameIPMaps[1][node.Name] = nexthopV6
×
1516
                        nodeIPs[1] = append(nodeIPs[1], nexthopV6)
×
1517
                }
×
1518
        }
1519

1520
        v4CIDR, v6CIDR := util.SplitStringIP(subnet.Spec.CIDRBlock)
×
1521
        cidrs := [2]string{v4CIDR, v6CIDR}
×
1522
        for i, cidr := range cidrs {
×
1523
                if len(nodeIPs[i]) == 0 || cidr == "" { // #nosec G602
×
1524
                        continue
×
1525
                }
1526
                klog.Infof("delete old distributed policy route for subnet %s", subnet.Name)
×
1527
                if err := c.deletePolicyRouteByGatewayType(subnet, kubeovnv1.GWDistributedType, false); err != nil {
×
1528
                        klog.Errorf("failed to delete policy route for overlay subnet %s, %v", subnet.Name, err)
×
1529
                        return err
×
1530
                }
×
1531
                klog.Infof("subnet %s configure ecmp policy route, nexthops %v", subnet.Name, nodeIPs[i])                     // #nosec G602
×
1532
                if err := c.updatePolicyRouteForCentralizedSubnet(subnet.Name, cidr, nodeIPs[i], nameIPMaps[i]); err != nil { // #nosec G602
×
1533
                        klog.Errorf("failed to add ecmp policy route for centralized subnet %s: %v", subnet.Name, err)
×
1534
                        return err
×
1535
                }
×
1536
        }
1537
        return nil
×
1538
}
1539

1540
func (c *Controller) reconcileOvnDefaultVpcRoute(subnet *kubeovnv1.Subnet) error {
×
1541
        if subnet.Name == c.config.NodeSwitch {
×
1542
                if err := c.addCommonRoutesForSubnet(subnet); err != nil {
×
1543
                        klog.Error(err)
×
1544
                        return err
×
1545
                }
×
1546
                return nil
×
1547
        }
1548

1549
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway {
×
1550
                // physical switch provide gw for this underlay subnet
×
1551
                pods, err := c.podsLister.Pods(metav1.NamespaceAll).List(labels.Everything())
×
1552
                if err != nil {
×
1553
                        klog.Errorf("failed to list pods %v", err)
×
1554
                        return err
×
1555
                }
×
1556
                for _, pod := range pods {
×
1557
                        if pod.Annotations[util.LogicalSwitchAnnotation] == subnet.Name && pod.Annotations[util.IPAddressAnnotation] != "" {
×
1558
                                if err := c.deleteStaticRoute(
×
1559
                                        pod.Annotations[util.IPAddressAnnotation], c.config.ClusterRouter, subnet.Spec.RouteTable); err != nil {
×
1560
                                        klog.Errorf("failed to delete static route %v", err)
×
1561
                                        return err
×
1562
                                }
×
1563
                        }
1564
                }
1565

1566
                if !subnet.Spec.LogicalGateway && subnet.Name != c.config.ExternalGatewaySwitch && !subnet.Spec.U2OInterconnection {
×
1567
                        lspName := fmt.Sprintf("%s-%s", subnet.Name, c.config.ClusterRouter)
×
1568
                        klog.Infof("delete logical switch port %s", lspName)
×
1569
                        if err := c.OVNNbClient.DeleteLogicalSwitchPort(lspName); err != nil {
×
1570
                                klog.Errorf("failed to delete lsp %s-%s, %v", subnet.Name, c.config.ClusterRouter, err)
×
1571
                                return err
×
1572
                        }
×
1573
                        lrpName := fmt.Sprintf("%s-%s", c.config.ClusterRouter, subnet.Name)
×
1574
                        klog.Infof("delete logical router port %s", lrpName)
×
1575
                        if err := c.OVNNbClient.DeleteLogicalRouterPort(lrpName); err != nil {
×
1576
                                klog.Errorf("failed to delete lrp %s: %v", lrpName, err)
×
1577
                                return err
×
1578
                        }
×
1579
                }
1580

1581
                if subnet.Spec.U2OInterconnection && subnet.Status.U2OInterconnectionIP != "" {
×
1582
                        if err := c.addPolicyRouteForU2OInterconn(subnet); err != nil {
×
1583
                                klog.Errorf("failed to add policy route for underlay to overlay subnet interconnection %s %v", subnet.Name, err)
×
1584
                                return err
×
1585
                        }
×
1586
                } else {
×
1587
                        if err := c.deletePolicyRouteForU2OInterconn(subnet); err != nil {
×
1588
                                klog.Errorf("failed to delete policy route for underlay to overlay subnet interconnection %s, %v", subnet.Name, err)
×
1589
                                return err
×
1590
                        }
×
1591
                }
1592

1593
                if (!c.config.EnableLb || (subnet.Spec.EnableLb == nil || !*subnet.Spec.EnableLb)) &&
×
1594
                        subnet.Spec.U2OInterconnection && subnet.Status.U2OInterconnectionIP != "" {
×
1595
                        if err := c.addPolicyRouteForU2ONoLoadBalancer(subnet); err != nil {
×
1596
                                klog.Errorf("failed to add policy route for underlay to overlay subnet interconnection without enabling loadbalancer %s %v", subnet.Name, err)
×
1597
                                return err
×
1598
                        }
×
1599
                } else {
×
1600
                        if err := c.deletePolicyRouteForU2ONoLoadBalancer(subnet); err != nil {
×
1601
                                klog.Errorf("failed to delete policy route for underlay to overlay subnet interconnection without enabling loadbalancer %s, %v", subnet.Name, err)
×
1602
                                return err
×
1603
                        }
×
1604
                }
1605
        } else {
×
1606
                // It's difficult to update policy route when subnet cidr is changed, add check for cidr changed situation
×
1607
                if err := c.reconcilePolicyRouteForCidrChangedSubnet(subnet, true); err != nil {
×
1608
                        klog.Error(err)
×
1609
                        return err
×
1610
                }
×
1611

1612
                if err := c.addCommonRoutesForSubnet(subnet); err != nil {
×
1613
                        klog.Error(err)
×
1614
                        return err
×
1615
                }
×
1616

1617
                // distributed subnet, only add distributed policy route
1618
                if subnet.Spec.GatewayType == kubeovnv1.GWDistributedType {
×
1619
                        if err := c.reconcileDistributedSubnetRouteInDefaultVpc(subnet); err != nil {
×
1620
                                klog.Error(err)
×
1621
                                return err
×
1622
                        }
×
1623
                } else {
×
1624
                        // centralized subnet
×
1625
                        if subnet.Spec.GatewayNode == "" && len(subnet.Spec.GatewayNodeSelectors) == 0 {
×
1626
                                subnet.Status.NotReady("NoReadyGateway", "")
×
1627
                                if err := c.patchSubnetStatus(subnet, "NoReadyGateway", ""); err != nil {
×
1628
                                        klog.Error(err)
×
1629
                                        return err
×
1630
                                }
×
1631
                                err := fmt.Errorf("subnet %s Spec.GatewayNode or Spec.GatewayNodeSelectors must be specified for centralized gateway type", subnet.Name)
×
1632
                                klog.Error(err)
×
1633
                                return err
×
1634
                        }
1635

1636
                        gwNodeExists := c.checkSubnetGwNodesExist(subnet)
×
1637
                        if !gwNodeExists {
×
1638
                                klog.Errorf("failed to init centralized gateway for subnet %s, no gateway node exists", subnet.Name)
×
1639
                                return errors.New("failed to add ecmp policy route, no gateway node exists")
×
1640
                        }
×
1641

1642
                        if err := c.reconcilePolicyRouteForCidrChangedSubnet(subnet, false); err != nil {
×
1643
                                klog.Error(err)
×
1644
                                return err
×
1645
                        }
×
1646

1647
                        if subnet.Spec.EnableEcmp {
×
1648
                                if err := c.reconcileEcmpCentralizedSubnetRouteInDefaultVpc(subnet); err != nil {
×
1649
                                        klog.Error(err)
×
1650
                                        return err
×
1651
                                }
×
1652
                        } else {
×
1653
                                if err := c.reconcileDefaultCentralizedSubnetRouteInDefaultVpc(subnet); err != nil {
×
1654
                                        klog.Error(err)
×
1655
                                        return err
×
1656
                                }
×
1657
                        }
1658
                }
1659
        }
1660
        return nil
×
1661
}
1662

1663
func (c *Controller) reconcileCustomVpcStaticRoute(subnet *kubeovnv1.Subnet) error {
×
1664
        // in custom vpc, subnet gw type is unmeaning
×
1665
        // 1. vpc out to public network through vpc nat gw pod, the static route is auto managed by admin user
×
1666
        // 2. vpc out to public network through ovn nat lrp, whose nexthop rely on bfd ecmp, the vpc spec bfd static route is auto managed here
×
1667
        // 3. vpc out to public network through ovn nat lrp, without bfd ecmp, the vpc spec static route is auto managed here
×
1668

×
1669
        vpc, err := c.vpcsLister.Get(subnet.Spec.Vpc)
×
1670
        if err != nil {
×
1671
                if k8serrors.IsNotFound(err) {
×
1672
                        return nil
×
1673
                }
×
1674
                klog.Errorf("failed to get vpc %s, %v", subnet.Spec.Vpc, err)
×
1675
                return err
×
1676
        }
1677

1678
        if vpc.Spec.EnableExternal && vpc.Spec.EnableBfd && subnet.Spec.EnableEcmp {
×
1679
                klog.Infof("add bfd and external static ecmp route for vpc %s, subnet %s", vpc.Name, subnet.Name)
×
1680
                // handle vpc static route
×
1681
                // use static ecmp route with bfd
×
1682
                // bfd ecmp static route depend on subnet cidr
×
1683
                if err := c.reconcileCustomVpcBfdStaticRoute(vpc.Name, subnet.Name); err != nil {
×
1684
                        klog.Errorf("failed to reconcile vpc %q bfd static route", vpc.Name)
×
1685
                        return err
×
1686
                }
×
1687
        }
1688

1689
        if subnet.Spec.Vlan == "" || subnet.Spec.LogicalGateway || subnet.Spec.U2OInterconnection {
×
1690
                if err = c.addCustomVPCStaticRouteForSubnet(subnet); err != nil {
×
1691
                        klog.Errorf("failed to add static route for underlay to overlay subnet interconnection %s %v", subnet.Name, err)
×
1692
                        return err
×
1693
                }
×
1694
                if err = c.addCommonRoutesForSubnet(subnet); err != nil {
×
1695
                        klog.Error(err)
×
1696
                        return err
×
1697
                }
×
1698
        }
1699

1700
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway && subnet.Spec.U2OInterconnection && subnet.Status.U2OInterconnectionIP != "" {
×
1701
                if err := c.addPolicyRouteForU2OInterconn(subnet); err != nil {
×
1702
                        klog.Errorf("failed to add policy route for underlay to overlay subnet interconnection %s %v", subnet.Name, err)
×
1703
                        return err
×
1704
                }
×
1705
        }
1706

1707
        return nil
×
1708
}
1709

1710
func (c *Controller) deleteStaticRoute(ip, router, routeTable string) error {
×
1711
        for ipStr := range strings.SplitSeq(ip, ",") {
×
1712
                if err := c.deleteStaticRouteFromVpc(
×
1713
                        router,
×
1714
                        routeTable,
×
1715
                        ipStr,
×
1716
                        "",
×
1717
                        kubeovnv1.PolicyDst,
×
1718
                ); err != nil {
×
1719
                        klog.Errorf("failed to delete static route %s, %v", ipStr, err)
×
1720
                        return err
×
1721
                }
×
1722
        }
1723

1724
        return nil
×
1725
}
1726

1727
func (c *Controller) reconcileVlan(subnet *kubeovnv1.Subnet) error {
×
1728
        if subnet.Spec.Vlan == "" {
×
1729
                return nil
×
1730
        }
×
1731
        klog.Infof("reconcile vlan %v", subnet.Spec.Vlan)
×
1732

×
1733
        vlan, err := c.vlansLister.Get(subnet.Spec.Vlan)
×
1734
        if err != nil {
×
1735
                klog.Errorf("failed to get vlan %s: %v", subnet.Spec.Vlan, err)
×
1736
                return err
×
1737
        }
×
1738
        if vlan.Status.Conflict {
×
1739
                err = fmt.Errorf("subnet %s has invalid conflict vlan %s", subnet.Name, vlan.Name)
×
1740
                klog.Error(err)
×
1741
                return err
×
1742
        }
×
1743

1744
        localnetPort := ovs.GetLocalnetName(subnet.Name)
×
1745
        if err := c.OVNNbClient.CreateLocalnetLogicalSwitchPort(subnet.Name, localnetPort, vlan.Spec.Provider, subnet.Spec.CIDRBlock, vlan.Spec.ID); err != nil {
×
1746
                klog.Errorf("create localnet port for subnet %s: %v", subnet.Name, err)
×
1747
                return err
×
1748
        }
×
1749

1750
        if !slices.Contains(vlan.Status.Subnets, subnet.Name) {
×
1751
                newVlan := vlan.DeepCopy()
×
1752
                newVlan.Status.Subnets = append(newVlan.Status.Subnets, subnet.Name)
×
1753
                _, err = c.config.KubeOvnClient.KubeovnV1().Vlans().UpdateStatus(context.Background(), newVlan, metav1.UpdateOptions{})
×
1754
                if err != nil {
×
1755
                        klog.Errorf("failed to update status of vlan %s: %v", vlan.Name, err)
×
1756
                        return err
×
1757
                }
×
1758
        }
1759

1760
        return nil
×
1761
}
1762

1763
func (c *Controller) reconcileSubnetSpecialIPs(subnet *kubeovnv1.Subnet) (bool, bool, error) {
×
1764
        isU2OIPChanged := false
×
1765
        isMcastQuerierIPChanged := false
×
1766
        var err error
×
1767

×
1768
        // reconcile u2o IP
×
1769
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway {
×
1770
                u2oInterconnName := fmt.Sprintf(util.U2OInterconnName, subnet.Spec.Vpc, subnet.Name)
×
1771
                u2oInterconnLrpName := fmt.Sprintf("%s-%s", subnet.Spec.Vpc, subnet.Name)
×
1772
                var v4ip, v6ip string
×
1773
                if subnet.Spec.U2OInterconnection {
×
1774
                        v4ip, v6ip, _, err = c.acquireU2OIP(subnet, u2oInterconnName, u2oInterconnLrpName)
×
1775
                        if err != nil {
×
1776
                                return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1777
                        }
×
1778

1779
                        if v4ip != "" || v6ip != "" {
×
1780
                                isU2OIPChanged = true
×
1781
                        }
×
1782
                } else if subnet.Status.U2OInterconnectionIP != "" {
×
1783
                        err = c.releaseU2OIP(subnet, u2oInterconnName)
×
1784
                        if err != nil {
×
1785
                                return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1786
                        }
×
1787
                        isU2OIPChanged = true
×
1788
                }
1789

1790
                if isU2OIPChanged {
×
1791
                        klog.Infof("reconcile underlay subnet %s to overlay interconnection with U2OInterconnection %v U2OInterconnectionIP %s",
×
1792
                                subnet.Name, subnet.Spec.U2OInterconnection, subnet.Status.U2OInterconnectionIP)
×
1793
                }
×
1794
        }
1795

1796
        // reconcile mcast querier IP
1797
        if subnet.Spec.EnableMulticastSnoop {
×
1798
                isMcastQuerierIPChanged, err = c.acquireMcastQuerierIP(subnet)
×
1799
                if err != nil {
×
1800
                        return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1801
                }
×
1802
        } else {
×
1803
                isMcastQuerierIPChanged, err = c.releaseMcastQuerierIP(subnet)
×
1804
                if err != nil {
×
1805
                        return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1806
                }
×
1807
        }
1808

1809
        // calculate subnet status
1810
        if isU2OIPChanged || isMcastQuerierIPChanged {
×
1811
                if _, err := c.calcSubnetStatusIP(subnet); err != nil {
×
1812
                        klog.Error(err)
×
1813
                        return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1814
                }
×
1815
        }
1816

1817
        return isU2OIPChanged, isMcastQuerierIPChanged, nil
×
1818
}
1819

1820
func (c *Controller) acquireU2OIP(subnet *kubeovnv1.Subnet, u2oInterconnName, u2oInterconnLrpName string) (string, string, string, error) {
×
1821
        var v4ip, v6ip, mac string
×
1822
        var err error
×
1823
        if subnet.Spec.U2OInterconnectionIP == "" && (subnet.Status.U2OInterconnectionIP == "" || subnet.Status.U2OInterconnectionMAC == "") {
×
1824
                v4ip, v6ip, mac, err = c.acquireIPAddress(subnet.Name, u2oInterconnName, u2oInterconnLrpName)
×
1825
                if err != nil {
×
1826
                        klog.Errorf("failed to acquire underlay to overlay interconnection ip address for subnet %s, %v", subnet.Name, err)
×
1827
                        return "", "", "", err
×
1828
                }
×
1829
        } else if subnet.Spec.U2OInterconnectionIP != "" && subnet.Status.U2OInterconnectionIP != subnet.Spec.U2OInterconnectionIP {
×
1830
                if subnet.Status.U2OInterconnectionIP != "" {
×
1831
                        klog.Infof("release underlay to overlay interconnection ip address %s for subnet %s", subnet.Status.U2OInterconnectionIP, subnet.Name)
×
1832
                        c.ipam.ReleaseAddressByPod(u2oInterconnName, subnet.Name)
×
1833
                }
×
1834
                v4ip, v6ip, mac, err = c.acquireStaticIPAddress(subnet.Name, u2oInterconnName, u2oInterconnLrpName, subnet.Spec.U2OInterconnectionIP, nil)
×
1835
                if err != nil {
×
1836
                        klog.Errorf("failed to acquire static underlay to overlay interconnection ip address for subnet %s, %v", subnet.Name, err)
×
1837
                        return "", "", "", err
×
1838
                }
×
1839
        }
1840
        if v4ip != "" || v6ip != "" {
×
1841
                switch subnet.Spec.Protocol {
×
1842
                case kubeovnv1.ProtocolIPv4:
×
1843
                        subnet.Status.U2OInterconnectionIP = v4ip
×
1844
                case kubeovnv1.ProtocolIPv6:
×
1845
                        subnet.Status.U2OInterconnectionIP = v6ip
×
1846
                case kubeovnv1.ProtocolDual:
×
1847
                        subnet.Status.U2OInterconnectionIP = fmt.Sprintf("%s,%s", v4ip, v6ip)
×
1848
                }
1849
                err = c.createOrUpdateIPCR("", u2oInterconnName, subnet.Status.U2OInterconnectionIP, mac, subnet.Name, metav1.NamespaceDefault, "", "")
×
1850
                if err != nil {
×
1851
                        klog.Errorf("failed to create or update IPs of %s : %v", u2oInterconnLrpName, err)
×
1852
                        return "", "", "", err
×
1853
                }
×
1854
                subnet.Status.U2OInterconnectionMAC = mac
×
1855
        }
1856
        return v4ip, v6ip, mac, nil
×
1857
}
1858

1859
func (c *Controller) releaseU2OIP(subnet *kubeovnv1.Subnet, u2oInterconnName string) error {
×
1860
        klog.Infof("release underlay to overlay interconnection ip address %s for subnet %s", subnet.Status.U2OInterconnectionIP, subnet.Name)
×
1861
        c.ipam.ReleaseAddressByPod(u2oInterconnName, subnet.Name)
×
1862
        subnet.Status.U2OInterconnectionIP = ""
×
1863
        subnet.Status.U2OInterconnectionMAC = ""
×
1864
        subnet.Status.U2OInterconnectionVPC = ""
×
1865

×
1866
        err := c.config.KubeOvnClient.KubeovnV1().IPs().Delete(context.Background(), u2oInterconnName, metav1.DeleteOptions{})
×
1867
        if err != nil && !k8serrors.IsNotFound(err) {
×
1868
                klog.Errorf("failed to delete ip %s, %v", u2oInterconnName, err)
×
1869
                return err
×
1870
        }
×
1871

1872
        return nil
×
1873
}
1874

1875
func (c *Controller) acquireMcastQuerierIP(subnet *kubeovnv1.Subnet) (bool, error) {
×
1876
        isMcastQuerierChanged := false
×
1877
        mcastQuerierLspName := fmt.Sprintf(util.McastQuerierName, subnet.Name)
×
1878
        var v4ip, v6ip, mac string
×
1879
        var err error
×
1880

×
1881
        if subnet.Status.McastQuerierIP == "" || subnet.Status.McastQuerierMAC == "" {
×
1882
                v4ip, v6ip, mac, err = c.acquireIPAddress(subnet.Name, mcastQuerierLspName, mcastQuerierLspName)
×
1883
                if err != nil {
×
1884
                        klog.Errorf("failed to acquire mcast querier ip address for subnet %s, %v", subnet.Name, err)
×
1885
                        return isMcastQuerierChanged, err
×
1886
                }
×
1887
        }
1888

1889
        if v4ip != "" || v6ip != "" {
×
1890
                switch subnet.Spec.Protocol {
×
1891
                case kubeovnv1.ProtocolIPv4:
×
1892
                        subnet.Status.McastQuerierIP = v4ip
×
1893
                case kubeovnv1.ProtocolIPv6:
×
1894
                        subnet.Status.McastQuerierIP = v6ip
×
1895
                case kubeovnv1.ProtocolDual:
×
1896
                        subnet.Status.McastQuerierIP = fmt.Sprintf("%s,%s", v4ip, v6ip)
×
1897
                }
1898

1899
                err := c.createOrUpdateIPCR("", mcastQuerierLspName, subnet.Status.McastQuerierIP, mac, subnet.Name, metav1.NamespaceDefault, "", "")
×
1900
                if err != nil {
×
1901
                        klog.Errorf("failed to create or update IPs of %s : %v", mcastQuerierLspName, err)
×
1902
                        return isMcastQuerierChanged, err
×
1903
                }
×
1904

1905
                subnet.Status.McastQuerierMAC = mac
×
1906
                klog.Infof("reconcile subnet %s mcast querier IP %s mac %s",
×
1907
                        subnet.Name, subnet.Status.McastQuerierIP, subnet.Status.McastQuerierMAC)
×
1908
                isMcastQuerierChanged = true
×
1909
        }
1910

1911
        return isMcastQuerierChanged, nil
×
1912
}
1913

1914
func (c *Controller) releaseMcastQuerierIP(subnet *kubeovnv1.Subnet) (bool, error) {
×
1915
        isMcastQuerierChanged := false
×
1916
        if subnet.Status.McastQuerierIP != "" {
×
1917
                mcastQuerierLspName := fmt.Sprintf(util.McastQuerierName, subnet.Name)
×
1918
                klog.Infof("release mcast querier ip address %s for subnet %s", subnet.Status.McastQuerierIP, subnet.Name)
×
1919
                c.ipam.ReleaseAddressByPod(mcastQuerierLspName, subnet.Name)
×
1920
                subnet.Status.McastQuerierIP = ""
×
1921
                subnet.Status.McastQuerierMAC = ""
×
1922

×
1923
                if err := c.config.KubeOvnClient.KubeovnV1().IPs().Delete(context.Background(), mcastQuerierLspName, metav1.DeleteOptions{}); err != nil {
×
1924
                        if !k8serrors.IsNotFound(err) {
×
1925
                                klog.Errorf("failed to delete ip %s, %v", mcastQuerierLspName, err)
×
1926
                                return isMcastQuerierChanged, err
×
1927
                        }
×
1928
                }
1929
                isMcastQuerierChanged = true
×
1930
                klog.Infof("reconcile subnet %s mcast querier IP %s mac %s",
×
1931
                        subnet.Name, subnet.Status.McastQuerierIP, subnet.Status.McastQuerierMAC)
×
1932
        }
1933
        return isMcastQuerierChanged, nil
×
1934
}
1935

1936
func isOvnSubnet(subnet *kubeovnv1.Subnet) bool {
1✔
1937
        return subnet != nil && util.IsOvnProvider(subnet.Spec.Provider)
1✔
1938
}
1✔
1939

1940
func formatExcludeIPRanges(subnet *kubeovnv1.Subnet) {
1✔
1941
        var excludeIPs []string
1✔
1942
        mapIPs := make(map[string]*ipam.IPRange, len(subnet.Spec.ExcludeIps))
1✔
1943
        for _, excludeIP := range subnet.Spec.ExcludeIps {
2✔
1944
                if _, ok := mapIPs[excludeIP]; !ok {
2✔
1945
                        ips := strings.Split(excludeIP, "..")
1✔
1946
                        start, _ := ipam.NewIP(ips[0])
1✔
1947
                        end := start
1✔
1948
                        if len(ips) != 1 {
1✔
1949
                                end, _ = ipam.NewIP(ips[1])
×
1950
                        }
×
1951
                        mapIPs[excludeIP] = ipam.NewIPRange(start, end)
1✔
1952
                }
1953
        }
1954
        newMap := filterRepeatIPRange(mapIPs)
1✔
1955
        for _, v := range newMap {
2✔
1956
                if v.Start().Equal(v.End()) {
2✔
1957
                        excludeIPs = append(excludeIPs, v.Start().String())
1✔
1958
                } else {
1✔
1959
                        excludeIPs = append(excludeIPs, v.Start().String()+".."+v.End().String())
×
1960
                }
×
1961
        }
1962
        sort.Strings(excludeIPs)
1✔
1963
        if !slices.Equal(subnet.Spec.ExcludeIps, excludeIPs) {
1✔
1964
                klog.V(3).Infof("excludeips before format is %v, after format is %v", subnet.Spec.ExcludeIps, excludeIPs)
×
1965
                subnet.Spec.ExcludeIps = excludeIPs
×
1966
        }
×
1967
}
1968

1969
func filterRepeatIPRange(mapIPs map[string]*ipam.IPRange) map[string]*ipam.IPRange {
1✔
1970
        for ka, a := range mapIPs {
2✔
1971
                for kb, b := range mapIPs {
2✔
1972
                        if ka == kb && a == b {
2✔
1973
                                continue
1✔
1974
                        }
1975

1976
                        if b.End().LessThan(a.Start()) || b.Start().GreaterThan(a.End()) {
2✔
1977
                                continue
1✔
1978
                        }
1979

1980
                        if (a.Start().Equal(b.Start()) || a.Start().GreaterThan(b.Start())) &&
×
1981
                                (a.End().Equal(b.End()) || a.End().LessThan(b.End())) {
×
1982
                                delete(mapIPs, ka)
×
1983
                                continue
×
1984
                        }
1985

1986
                        if (a.Start().Equal(b.Start()) || a.Start().GreaterThan(b.Start())) &&
×
1987
                                a.End().GreaterThan(b.End()) {
×
1988
                                delete(mapIPs, ka)
×
1989
                                mapIPs[kb] = ipam.NewIPRange(b.Start(), a.End())
×
1990
                                continue
×
1991
                        }
1992

1993
                        if (a.End().Equal(b.End()) || a.End().LessThan(b.End())) &&
×
1994
                                a.Start().LessThan(b.Start()) {
×
1995
                                delete(mapIPs, ka)
×
1996
                                mapIPs[kb] = ipam.NewIPRange(a.Start(), b.End())
×
1997
                                continue
×
1998
                        }
1999

2000
                        // a contains b
2001
                        mapIPs[kb] = a
×
2002
                        delete(mapIPs, ka)
×
2003
                }
2004
        }
2005
        return mapIPs
1✔
2006
}
2007

2008
func (c *Controller) checkGwNodeExists(gatewayNode string) bool {
×
2009
        found := false
×
2010
        for gwName := range strings.SplitSeq(gatewayNode, ",") {
×
2011
                // the format of gatewayNode can be like 'kube-ovn-worker:172.18.0.2, kube-ovn-control-plane:172.18.0.3', which consists of node name and designative egress ip
×
2012
                if strings.Contains(gwName, ":") {
×
2013
                        gwName = strings.TrimSpace(strings.Split(gwName, ":")[0])
×
2014
                } else {
×
2015
                        gwName = strings.TrimSpace(gwName)
×
2016
                }
×
2017

2018
                gwNode, err := c.nodesLister.Get(gwName)
×
2019
                if err != nil {
×
2020
                        if k8serrors.IsNotFound(err) {
×
2021
                                klog.Errorf("gw node %s does not exist, %v", gwName, err)
×
2022
                                continue
×
2023
                        }
2024
                }
2025
                if gwNode != nil {
×
2026
                        found = true
×
2027
                        break
×
2028
                }
2029
        }
2030
        return found
×
2031
}
2032

2033
func (c *Controller) getGatewayNodes(subnet *kubeovnv1.Subnet) ([]string, error) {
×
2034
        if subnet.Spec.GatewayNode != "" {
×
2035
                var nodes []string
×
2036
                for gw := range strings.SplitSeq(subnet.Spec.GatewayNode, ",") {
×
2037
                        if strings.Contains(gw, ":") {
×
2038
                                gw = strings.TrimSpace(strings.Split(gw, ":")[0])
×
2039
                        } else {
×
2040
                                gw = strings.TrimSpace(gw)
×
2041
                        }
×
2042
                        if gw != "" {
×
2043
                                nodes = append(nodes, gw)
×
2044
                        }
×
2045
                }
2046
                return nodes, nil
×
2047
        }
2048

2049
        if len(subnet.Spec.GatewayNodeSelectors) > 0 {
×
2050
                return c.getNodesBySelectors(subnet.Spec.GatewayNodeSelectors)
×
2051
        }
×
2052

2053
        return nil, nil
×
2054
}
2055

2056
func (c *Controller) getNodesBySelectors(selectors []metav1.LabelSelector) ([]string, error) {
×
2057
        nodeSet := make(map[string]struct{})
×
2058
        for _, selector := range selectors {
×
2059
                labelSelector, err := metav1.LabelSelectorAsSelector(&selector)
×
2060
                if err != nil {
×
2061
                        klog.Errorf("failed to convert label selector: %v", err)
×
2062
                        continue
×
2063
                }
2064
                nodes, err := c.nodesLister.List(labelSelector)
×
2065
                if err != nil {
×
2066
                        return nil, fmt.Errorf("failed to list nodes with selector %s: %w", labelSelector.String(), err)
×
2067
                }
×
2068
                for _, node := range nodes {
×
2069
                        nodeSet[node.Name] = struct{}{}
×
2070
                }
×
2071
        }
2072

2073
        matchedNodes := make([]string, 0, len(nodeSet))
×
2074
        for name := range nodeSet {
×
2075
                matchedNodes = append(matchedNodes, name)
×
2076
        }
×
2077
        return matchedNodes, nil
×
2078
}
2079

2080
func (c *Controller) checkSubnetGwNodesExist(subnet *kubeovnv1.Subnet) bool {
×
2081
        if subnet.Spec.GatewayNode != "" {
×
2082
                return c.checkGwNodeExists(subnet.Spec.GatewayNode)
×
2083
        }
×
2084

2085
        if len(subnet.Spec.GatewayNodeSelectors) > 0 {
×
2086
                nodes, err := c.getNodesBySelectors(subnet.Spec.GatewayNodeSelectors)
×
2087
                if err != nil {
×
2088
                        klog.Errorf("failed to get nodes by selectors: %v", err)
×
2089
                        return false
×
2090
                }
×
2091
                return len(nodes) > 0
×
2092
        }
2093

2094
        return false
×
2095
}
2096

2097
func getIPSuffix(protocol string) string {
×
2098
        if protocol == kubeovnv1.ProtocolIPv6 {
×
2099
                return "ip6"
×
2100
        }
×
2101
        return "ip4"
×
2102
}
2103

2104
func buildPolicyRouteExternalIDs(subnetName string, extraIDs map[string]string) map[string]string {
×
2105
        externalIDs := map[string]string{
×
2106
                "vendor": util.CniTypeName,
×
2107
                "subnet": subnetName,
×
2108
        }
×
2109
        maps.Copy(externalIDs, extraIDs)
×
2110
        return externalIDs
×
2111
}
×
2112

2113
func (c *Controller) logicalRouterExists(vpcName string) bool {
×
2114
        lr, err := c.OVNNbClient.GetLogicalRouter(vpcName, true)
×
2115
        if err == nil && lr == nil {
×
2116
                klog.Infof("logical router %s already deleted", vpcName)
×
2117
                return false
×
2118
        }
×
2119
        return true
×
2120
}
2121

2122
func (c *Controller) addCommonRoutesForSubnet(subnet *kubeovnv1.Subnet) error {
×
2123
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2124
                if cidr == "" {
×
2125
                        continue
×
2126
                }
2127

2128
                var gateway string
×
2129
                protocol := util.CheckProtocol(cidr)
×
2130
                for gw := range strings.SplitSeq(subnet.Spec.Gateway, ",") {
×
2131
                        if util.CheckProtocol(gw) == protocol {
×
2132
                                gateway = gw
×
2133
                                break
×
2134
                        }
2135
                }
2136
                if gateway == "" {
×
2137
                        return fmt.Errorf("failed to get gateway of CIDR %s", cidr)
×
2138
                }
×
2139

2140
                ipSuffix := getIPSuffix(protocol)
×
2141
                match := fmt.Sprintf("%s.dst == %s", ipSuffix, cidr)
×
2142
                action := kubeovnv1.PolicyRouteActionAllow
×
2143
                externalIDs := buildPolicyRouteExternalIDs(subnet.Name, nil)
×
2144

×
2145
                klog.Infof("add common policy route for router: %s, match %s, action %s, externalID %v", subnet.Spec.Vpc, match, action, externalIDs)
×
2146
                if err := c.addPolicyRouteToVpc(
×
2147
                        subnet.Spec.Vpc,
×
2148
                        &kubeovnv1.PolicyRoute{
×
2149
                                Priority: util.SubnetRouterPolicyPriority,
×
2150
                                Match:    match,
×
2151
                                Action:   action,
×
2152
                        },
×
2153
                        externalIDs,
×
2154
                ); err != nil {
×
2155
                        klog.Errorf("failed to add logical router policy for CIDR %s of subnet %s: %v", cidr, subnet.Name, err)
×
2156
                        return err
×
2157
                }
×
2158
        }
2159
        return nil
×
2160
}
2161

2162
func getOverlaySubnetsPortGroupName(subnetName, nodeName string) string {
×
2163
        return strings.ReplaceAll(fmt.Sprintf("%s.%s", subnetName, nodeName), "-", ".")
×
2164
}
×
2165

2166
func (c *Controller) createPortGroupForDistributedSubnet(node *v1.Node, subnet *kubeovnv1.Subnet) error {
×
2167
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway {
×
2168
                return nil
×
2169
        }
×
2170
        if subnet.Spec.Vpc != c.config.ClusterRouter || subnet.Name == c.config.NodeSwitch {
×
2171
                return nil
×
2172
        }
×
2173

2174
        pgName := getOverlaySubnetsPortGroupName(subnet.Name, node.Name)
×
2175
        externalIDs := map[string]string{
×
2176
                "subnet":         subnet.Name,
×
2177
                "node":           node.Name,
×
2178
                "vendor":         util.CniTypeName,
×
2179
                networkPolicyKey: subnet.Name + "/" + node.Name,
×
2180
        }
×
2181
        if err := c.OVNNbClient.CreatePortGroup(pgName, externalIDs); err != nil {
×
2182
                klog.Errorf("create port group for subnet %s and node %s: %v", subnet.Name, node.Name, err)
×
2183
                return err
×
2184
        }
×
2185

2186
        return nil
×
2187
}
2188

2189
func (c *Controller) updatePolicyRouteForCentralizedSubnet(subnetName, cidr string, nextHops []string, nameIPMap map[string]string) error {
×
2190
        ipSuffix := getIPSuffix(util.CheckProtocol(cidr))
×
2191
        match := fmt.Sprintf("%s.src == %s", ipSuffix, cidr)
×
2192
        action := kubeovnv1.PolicyRouteActionReroute
×
2193
        externalIDs := buildPolicyRouteExternalIDs(subnetName, nameIPMap)
×
2194

×
2195
        klog.Infof("add policy route for router: %s, match %s, action %s, nexthops %v, externalID %s", c.config.ClusterRouter, match, action, nextHops, externalIDs)
×
2196
        if err := c.addPolicyRouteToVpc(
×
2197
                c.config.ClusterRouter,
×
2198
                &kubeovnv1.PolicyRoute{
×
2199
                        Priority:  util.GatewayRouterPolicyPriority,
×
2200
                        Match:     match,
×
2201
                        Action:    action,
×
2202
                        NextHopIP: strings.Join(nextHops, ","),
×
2203
                },
×
2204
                externalIDs,
×
2205
        ); err != nil {
×
2206
                klog.Errorf("failed to add policy route for centralized subnet %s: %v", subnetName, err)
×
2207
                return err
×
2208
        }
×
2209
        return nil
×
2210
}
2211

2212
func (c *Controller) addPolicyRouteForCentralizedSubnet(subnet *kubeovnv1.Subnet, nodeName string, ipNameMap map[string]string, nodeIPs []string) error {
×
2213
        for _, nodeIP := range nodeIPs {
×
2214
                // node v4ip v6ip
×
2215
                for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2216
                        if util.CheckProtocol(cidrBlock) != util.CheckProtocol(nodeIP) {
×
2217
                                continue
×
2218
                        }
2219
                        // Check for repeat policy route is processed in AddPolicyRoute
2220

2221
                        var nextHops []string
×
2222
                        nameIPMap := map[string]string{}
×
2223
                        nextHops = append(nextHops, nodeIP)
×
2224
                        tmpName := nodeName
×
2225
                        if nodeName == "" {
×
2226
                                tmpName = ipNameMap[nodeIP]
×
2227
                        }
×
2228
                        nameIPMap[tmpName] = nodeIP
×
2229
                        if err := c.updatePolicyRouteForCentralizedSubnet(subnet.Name, cidrBlock, nextHops, nameIPMap); err != nil {
×
2230
                                klog.Error(err)
×
2231
                                return err
×
2232
                        }
×
2233
                }
2234
        }
2235
        return nil
×
2236
}
2237

2238
func (c *Controller) deletePolicyRouteForCentralizedSubnet(subnet *kubeovnv1.Subnet) error {
×
2239
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2240
                ipSuffix := getIPSuffix(util.CheckProtocol(cidr))
×
2241
                match := fmt.Sprintf("%s.src == %s", ipSuffix, cidr)
×
2242
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", c.config.ClusterRouter, util.GatewayRouterPolicyPriority, match)
×
2243
                if err := c.deletePolicyRouteFromVpc(c.config.ClusterRouter, util.GatewayRouterPolicyPriority, match); err != nil {
×
2244
                        klog.Errorf("failed to delete policy route for centralized subnet %s: %v", subnet.Name, err)
×
2245
                        return err
×
2246
                }
×
2247
        }
2248
        return nil
×
2249
}
2250

2251
func (c *Controller) addPolicyRouteForDistributedSubnet(subnet *kubeovnv1.Subnet, nodeName, nodeIPv4, nodeIPv6 string) error {
×
2252
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway {
×
2253
                return nil
×
2254
        }
×
2255
        if subnet.Spec.Vpc != c.config.ClusterRouter || subnet.Name == c.config.NodeSwitch {
×
2256
                return nil
×
2257
        }
×
2258

2259
        pgName := getOverlaySubnetsPortGroupName(subnet.Name, nodeName)
×
2260
        for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2261
                ipSuffix := getIPSuffix(util.CheckProtocol(cidrBlock))
×
2262
                nodeIP := nodeIPv4
×
2263
                if ipSuffix == "ip6" {
×
2264
                        nodeIP = nodeIPv6
×
2265
                }
×
2266
                if nodeIP == "" {
×
2267
                        continue
×
2268
                }
2269

2270
                pgAs := fmt.Sprintf("%s_%s", pgName, ipSuffix)
×
2271
                match := fmt.Sprintf("%s.src == $%s", ipSuffix, pgAs)
×
2272
                action := kubeovnv1.PolicyRouteActionReroute
×
2273
                externalIDs := buildPolicyRouteExternalIDs(subnet.Name, map[string]string{"node": nodeName})
×
2274

×
2275
                klog.Infof("add policy route for router: %s, match %s, action %s, externalID %v", c.config.ClusterRouter, match, action, externalIDs)
×
2276
                if err := c.addPolicyRouteToVpc(
×
2277
                        c.config.ClusterRouter,
×
2278
                        &kubeovnv1.PolicyRoute{
×
2279
                                Priority:  util.GatewayRouterPolicyPriority,
×
2280
                                Match:     match,
×
2281
                                Action:    action,
×
2282
                                NextHopIP: nodeIP,
×
2283
                        },
×
2284
                        externalIDs,
×
2285
                ); err != nil {
×
2286
                        klog.Errorf("failed to add logical router policy for port-group address-set %s: %v", pgAs, err)
×
2287
                        return err
×
2288
                }
×
2289
        }
2290
        return nil
×
2291
}
2292

2293
func (c *Controller) deletePolicyRouteForDistributedSubnet(subnet *kubeovnv1.Subnet, nodeName string) error {
×
2294
        pgName := getOverlaySubnetsPortGroupName(subnet.Name, nodeName)
×
2295
        for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2296
                ipSuffix := getIPSuffix(util.CheckProtocol(cidrBlock))
×
2297
                pgAs := fmt.Sprintf("%s_%s", pgName, ipSuffix)
×
2298
                match := fmt.Sprintf("%s.src == $%s", ipSuffix, pgAs)
×
2299
                klog.Infof("delete policy route for router: %s, priority: %d, match: %q", c.config.ClusterRouter, util.GatewayRouterPolicyPriority, match)
×
2300
                if err := c.deletePolicyRouteFromVpc(c.config.ClusterRouter, util.GatewayRouterPolicyPriority, match); err != nil {
×
2301
                        klog.Errorf("failed to delete policy route for subnet %s: %v", subnet.Name, err)
×
2302
                        return err
×
2303
                }
×
2304
        }
2305
        return nil
×
2306
}
2307

2308
func (c *Controller) deletePolicyRouteByGatewayType(subnet *kubeovnv1.Subnet, gatewayType string, isDelete bool) error {
×
2309
        if (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway) || subnet.Spec.Vpc != c.config.ClusterRouter {
×
2310
                return nil
×
2311
        }
×
2312

2313
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2314
                if cidr == "" || !isDelete {
×
2315
                        continue
×
2316
                }
2317

2318
                ipSuffix := getIPSuffix(util.CheckProtocol(cidr))
×
2319
                match := fmt.Sprintf("%s.dst == %s", ipSuffix, cidr)
×
2320
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", c.config.ClusterRouter, util.SubnetRouterPolicyPriority, match)
×
2321
                if err := c.deletePolicyRouteFromVpc(c.config.ClusterRouter, util.SubnetRouterPolicyPriority, match); err != nil {
×
2322
                        klog.Errorf("failed to delete logical router policy for CIDR %s of subnet %s: %v", cidr, subnet.Name, err)
×
2323
                        return err
×
2324
                }
×
2325
        }
2326
        if subnet.Name == c.config.NodeSwitch {
×
2327
                return nil
×
2328
        }
×
2329

2330
        if gatewayType == kubeovnv1.GWDistributedType {
×
2331
                nodes, err := c.nodesLister.List(labels.Everything())
×
2332
                if err != nil {
×
2333
                        klog.Errorf("list nodes: %v", err)
×
2334
                        return err
×
2335
                }
×
2336
                for _, node := range nodes {
×
2337
                        pgName := getOverlaySubnetsPortGroupName(subnet.Name, node.Name)
×
2338
                        if err = c.OVNNbClient.DeletePortGroup(pgName); err != nil {
×
2339
                                klog.Errorf("delete port group for subnet %s and node %s: %v", subnet.Name, node.Name, err)
×
2340
                                return err
×
2341
                        }
×
2342

2343
                        if err = c.deletePolicyRouteForDistributedSubnet(subnet, node.Name); err != nil {
×
2344
                                klog.Errorf("delete policy route for subnet %s and node %s: %v", subnet.Name, node.Name, err)
×
2345
                                return err
×
2346
                        }
×
2347
                }
2348
        }
2349

2350
        if gatewayType == kubeovnv1.GWCentralizedType {
×
2351
                klog.Infof("delete policy route for centralized subnet %s", subnet.Name)
×
2352
                if err := c.deletePolicyRouteForCentralizedSubnet(subnet); err != nil {
×
2353
                        klog.Errorf("delete policy route for subnet %s: %v", subnet.Name, err)
×
2354
                        return err
×
2355
                }
×
2356
        }
2357

2358
        return nil
×
2359
}
2360

2361
func (c *Controller) addPolicyRouteForU2OInterconn(subnet *kubeovnv1.Subnet) error {
×
2362
        v4Gw, v6Gw := util.SplitStringIP(subnet.Spec.Gateway)
×
2363

×
2364
        externalIDs := buildPolicyRouteExternalIDs(subnet.Name, map[string]string{"isU2ORoutePolicy": "true"})
×
2365

×
2366
        nodes, err := c.nodesLister.List(labels.Everything())
×
2367
        if err != nil {
×
2368
                klog.Errorf("failed to list nodes: %v", err)
×
2369
                return err
×
2370
        }
×
2371

2372
        var nodesIPv4, nodesIPv6 []string
×
2373
        for _, node := range nodes {
×
2374
                nodeIPv4, nodeIPv6 := util.GetNodeInternalIP(*node)
×
2375

×
2376
                if nodeIPv4 != "" {
×
2377
                        nodesIPv4 = append(nodesIPv4, nodeIPv4)
×
2378
                }
×
2379
                if nodeIPv6 != "" {
×
2380
                        nodesIPv6 = append(nodesIPv6, nodeIPv6)
×
2381
                }
×
2382
        }
2383

2384
        u2oExcludeIP4Ag := strings.ReplaceAll(fmt.Sprintf(util.U2OExcludeIPAg, subnet.Name, "ip4"), "-", ".")
×
2385
        u2oExcludeIP6Ag := strings.ReplaceAll(fmt.Sprintf(util.U2OExcludeIPAg, subnet.Name, "ip6"), "-", ".")
×
2386

×
2387
        if err := c.OVNNbClient.CreateAddressSet(u2oExcludeIP4Ag, externalIDs); err != nil {
×
2388
                klog.Errorf("create address set %s: %v", u2oExcludeIP4Ag, err)
×
2389
                return err
×
2390
        }
×
2391

2392
        if err := c.OVNNbClient.CreateAddressSet(u2oExcludeIP6Ag, externalIDs); err != nil {
×
2393
                klog.Errorf("create address set %s: %v", u2oExcludeIP6Ag, err)
×
2394
                return err
×
2395
        }
×
2396

2397
        if len(nodesIPv4) > 0 {
×
2398
                if err := c.OVNNbClient.AddressSetUpdateAddress(u2oExcludeIP4Ag, nodesIPv4...); err != nil {
×
2399
                        klog.Errorf("set v4 address set %s with address %v: %v", u2oExcludeIP4Ag, nodesIPv4, err)
×
2400
                        return err
×
2401
                }
×
2402
        }
2403

2404
        if len(nodesIPv6) > 0 {
×
2405
                if err := c.OVNNbClient.AddressSetUpdateAddress(u2oExcludeIP6Ag, nodesIPv6...); err != nil {
×
2406
                        klog.Errorf("set v6 address set %s with address %v: %v", u2oExcludeIP6Ag, nodesIPv6, err)
×
2407
                        return err
×
2408
                }
×
2409
        }
2410

2411
        for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2412
                ipSuffix := getIPSuffix(util.CheckProtocol(cidrBlock))
×
2413
                nextHop := v4Gw
×
2414
                U2OexcludeIPAs := u2oExcludeIP4Ag
×
2415
                if ipSuffix == "ip6" {
×
2416
                        nextHop = v6Gw
×
2417
                        U2OexcludeIPAs = u2oExcludeIP6Ag
×
2418
                }
×
2419

2420
                match1 := fmt.Sprintf("%s.dst == %s", ipSuffix, cidrBlock)
×
2421
                match2 := fmt.Sprintf("%s.dst == $%s && %s.src == %s", ipSuffix, U2OexcludeIPAs, ipSuffix, cidrBlock)
×
2422
                match3 := fmt.Sprintf("%s.src == %s", ipSuffix, cidrBlock)
×
2423

×
2424
                /*
×
2425
                        policy1:
×
2426
                        priority 29400 match: "ip4.dst == underlay subnet cidr"                         action: allow
×
2427

×
2428
                        policy2:
×
2429
                        priority 31000 match: "ip4.dst == node ips && ip4.src == underlay subnet cidr"  action: reroute physical gw
×
2430

×
2431
                        policy3:
×
2432
                        priority 29000 match: "ip4.src == underlay subnet cidr"                         action: reroute physical gw
×
2433

×
2434
                        comment:
×
2435
                        policy1 and policy2 allow overlay pod access underlay but when overlay pod access node ip, it should go join subnet,
×
2436
                        policy3: underlay pod first access u2o interconnection lrp and then reroute to physical gw
×
2437
                */
×
2438
                action := kubeovnv1.PolicyRouteActionAllow
×
2439
                if subnet.Spec.Vpc == c.config.ClusterRouter {
×
2440
                        klog.Infof("add u2o interconnection policy for router: %s, match %s, action %s", subnet.Spec.Vpc, match1, action)
×
2441
                        if err := c.addPolicyRouteToVpc(
×
2442
                                subnet.Spec.Vpc,
×
2443
                                &kubeovnv1.PolicyRoute{
×
2444
                                        Priority: util.U2OSubnetPolicyPriority,
×
2445
                                        Match:    match1,
×
2446
                                        Action:   action,
×
2447
                                },
×
2448
                                externalIDs,
×
2449
                        ); err != nil {
×
2450
                                klog.Errorf("failed to add u2o interconnection policy1 for subnet %s %v", subnet.Name, err)
×
2451
                                return err
×
2452
                        }
×
2453

2454
                        action = kubeovnv1.PolicyRouteActionReroute
×
2455
                        klog.Infof("add u2o interconnection policy for router: %s, match %s, action %s", subnet.Spec.Vpc, match2, action)
×
2456
                        if err := c.addPolicyRouteToVpc(
×
2457
                                subnet.Spec.Vpc,
×
2458
                                &kubeovnv1.PolicyRoute{
×
2459
                                        Priority:  util.SubnetRouterPolicyPriority,
×
2460
                                        Match:     match2,
×
2461
                                        Action:    action,
×
2462
                                        NextHopIP: nextHop,
×
2463
                                },
×
2464
                                externalIDs,
×
2465
                        ); err != nil {
×
2466
                                klog.Errorf("failed to add u2o interconnection policy2 for subnet %s %v", subnet.Name, err)
×
2467
                                return err
×
2468
                        }
×
2469
                }
2470

2471
                action = kubeovnv1.PolicyRouteActionReroute
×
2472
                klog.Infof("add u2o interconnection policy for router: %s, match %s, action %s, nexthop %s", subnet.Spec.Vpc, match3, action, nextHop)
×
2473
                if err := c.addPolicyRouteToVpc(
×
2474
                        subnet.Spec.Vpc,
×
2475
                        &kubeovnv1.PolicyRoute{
×
2476
                                Priority:  util.GatewayRouterPolicyPriority,
×
2477
                                Match:     match3,
×
2478
                                Action:    action,
×
2479
                                NextHopIP: nextHop,
×
2480
                        },
×
2481
                        externalIDs,
×
2482
                ); err != nil {
×
2483
                        klog.Errorf("failed to add u2o interconnection policy3 for subnet %s %v", subnet.Name, err)
×
2484
                        return err
×
2485
                }
×
2486
        }
2487
        return nil
×
2488
}
2489

2490
func (c *Controller) deletePolicyRouteForU2OInterconn(subnet *kubeovnv1.Subnet) error {
×
2491
        if !c.logicalRouterExists(subnet.Spec.Vpc) {
×
2492
                return nil
×
2493
        }
×
2494
        policies, err := c.OVNNbClient.ListLogicalRouterPolicies(subnet.Spec.Vpc, -1, map[string]string{
×
2495
                "isU2ORoutePolicy": "true",
×
2496
                "vendor":           util.CniTypeName,
×
2497
                "subnet":           subnet.Name,
×
2498
        }, true)
×
2499
        if err != nil {
×
2500
                klog.Errorf("failed to list logical router policies: %v", err)
×
2501
                return err
×
2502
        }
×
2503
        if len(policies) == 0 {
×
2504
                return nil
×
2505
        }
×
2506

2507
        lr := subnet.Status.U2OInterconnectionVPC
×
2508
        if lr == "" {
×
2509
                // old version field U2OInterconnectionVPC may be "" and then use subnet.Spec.Vpc
×
2510
                lr = subnet.Spec.Vpc
×
2511
        }
×
2512

2513
        for _, policy := range policies {
×
2514
                klog.Infof("delete u2o interconnection policy for router %s with match %s priority %d", lr, policy.Match, policy.Priority)
×
2515
                if err = c.OVNNbClient.DeleteLogicalRouterPolicyByUUID(lr, policy.UUID); err != nil {
×
2516
                        klog.Errorf("failed to delete u2o interconnection policy for subnet %s: %v", subnet.Name, err)
×
2517
                        return err
×
2518
                }
×
2519
        }
2520

2521
        u2oExcludeIP4Ag := strings.ReplaceAll(fmt.Sprintf(util.U2OExcludeIPAg, subnet.Name, "ip4"), "-", ".")
×
2522
        u2oExcludeIP6Ag := strings.ReplaceAll(fmt.Sprintf(util.U2OExcludeIPAg, subnet.Name, "ip6"), "-", ".")
×
2523

×
2524
        if err := c.OVNNbClient.DeleteAddressSet(u2oExcludeIP4Ag); err != nil {
×
2525
                klog.Errorf("delete address set %s: %v", u2oExcludeIP4Ag, err)
×
2526
                return err
×
2527
        }
×
2528

2529
        if err := c.OVNNbClient.DeleteAddressSet(u2oExcludeIP6Ag); err != nil {
×
2530
                klog.Errorf("delete address set %s: %v", u2oExcludeIP6Ag, err)
×
2531
                return err
×
2532
        }
×
2533

2534
        return nil
×
2535
}
2536

2537
func (c *Controller) addCustomVPCStaticRouteForSubnet(subnet *kubeovnv1.Subnet) error {
×
2538
        if subnet.Spec.Vpc == "" {
×
2539
                return nil
×
2540
        }
×
2541

2542
        v4Gw, v6Gw := util.SplitStringIP(subnet.Spec.Gateway)
×
2543
        v4Cidr, v6Cidr := util.SplitStringIP(subnet.Spec.CIDRBlock)
×
2544

×
2545
        if v4Gw != "" && v4Cidr != "" {
×
2546
                if err := c.addStaticRouteToVpc(
×
2547
                        subnet.Spec.Vpc,
×
2548
                        &kubeovnv1.StaticRoute{
×
2549
                                Policy:    kubeovnv1.PolicySrc,
×
2550
                                CIDR:      v4Cidr,
×
2551
                                NextHopIP: v4Gw,
×
2552
                        },
×
2553
                ); err != nil {
×
2554
                        klog.Errorf("failed to add static route, %v", err)
×
2555
                        return err
×
2556
                }
×
2557
        }
2558

2559
        if v6Gw != "" && v6Cidr != "" {
×
2560
                if err := c.addStaticRouteToVpc(
×
2561
                        subnet.Spec.Vpc,
×
2562
                        &kubeovnv1.StaticRoute{
×
2563
                                Policy:    kubeovnv1.PolicySrc,
×
2564
                                CIDR:      v6Cidr,
×
2565
                                NextHopIP: v6Gw,
×
2566
                        },
×
2567
                ); err != nil {
×
2568
                        klog.Errorf("failed to add static route, %v", err)
×
2569
                        return err
×
2570
                }
×
2571
        }
2572
        return nil
×
2573
}
2574

2575
func (c *Controller) deleteStaticRouteForU2OInterconn(subnet *kubeovnv1.Subnet) error {
×
2576
        if subnet.Spec.Vpc == "" {
×
2577
                return nil
×
2578
        }
×
2579

2580
        v4Gw, v6Gw := util.SplitStringIP(subnet.Spec.Gateway)
×
2581
        v4Cidr, v6Cidr := util.SplitStringIP(subnet.Spec.CIDRBlock)
×
2582
        if v4Gw != "" && v4Cidr != "" {
×
2583
                if err := c.deleteStaticRouteFromVpc(
×
2584
                        subnet.Spec.Vpc,
×
2585
                        subnet.Spec.RouteTable,
×
2586
                        v4Cidr,
×
2587
                        v4Gw,
×
2588
                        kubeovnv1.PolicySrc,
×
2589
                ); err != nil {
×
2590
                        klog.Errorf("failed to add static route, %v", err)
×
2591
                        return err
×
2592
                }
×
2593
        }
2594

2595
        if v6Gw != "" && v6Cidr != "" {
×
2596
                if err := c.deleteStaticRouteFromVpc(
×
2597
                        subnet.Spec.Vpc,
×
2598
                        subnet.Spec.RouteTable,
×
2599
                        v6Cidr,
×
2600
                        v6Gw,
×
2601
                        kubeovnv1.PolicySrc,
×
2602
                ); err != nil {
×
2603
                        klog.Errorf("failed to delete static route, %v", err)
×
2604
                        return err
×
2605
                }
×
2606
        }
2607
        return nil
×
2608
}
2609

2610
func (c *Controller) reconcileRouteTableForSubnet(subnet *kubeovnv1.Subnet) error {
×
2611
        if subnet.Spec.Vlan != "" && !subnet.Spec.U2OInterconnection {
×
2612
                return nil
×
2613
        }
×
2614

2615
        routerPortName := ovs.LogicalRouterPortName(subnet.Spec.Vpc, subnet.Name)
×
2616
        lrp, err := c.OVNNbClient.GetLogicalRouterPort(routerPortName, false)
×
2617
        if err != nil {
×
2618
                klog.Error(err)
×
2619
                return err
×
2620
        }
×
2621

2622
        rtb := lrp.Options["route_table"]
×
2623

×
2624
        // no need to update
×
2625
        if rtb == subnet.Spec.RouteTable {
×
2626
                return nil
×
2627
        }
×
2628

2629
        klog.Infof("reconcile route table %q for subnet %s", subnet.Spec.RouteTable, subnet.Name)
×
2630
        opt := map[string]string{"route_table": subnet.Spec.RouteTable}
×
2631
        if err = c.OVNNbClient.UpdateLogicalRouterPortOptions(routerPortName, opt); err != nil {
×
2632
                klog.Errorf("failed to set route table of logical router port %s to %s: %v", routerPortName, subnet.Spec.RouteTable, err)
×
2633
                return err
×
2634
        }
×
2635

2636
        return nil
×
2637
}
2638

2639
func (c *Controller) deleteCustomVPCPolicyRoutesForSubnet(subnet *kubeovnv1.Subnet) error {
×
2640
        if !c.logicalRouterExists(subnet.Spec.Vpc) {
×
2641
                return nil
×
2642
        }
×
2643
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2644
                ipSuffix := getIPSuffix(util.CheckProtocol(cidr))
×
2645
                match := fmt.Sprintf("%s.dst == %s", ipSuffix, cidr)
×
2646
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", subnet.Spec.Vpc, util.SubnetRouterPolicyPriority, match)
×
2647
                if err := c.deletePolicyRouteFromVpc(subnet.Spec.Vpc, util.SubnetRouterPolicyPriority, match); err != nil {
×
2648
                        klog.Errorf("failed to delete logical router policy for CIDR %s of subnet %s: %v", cidr, subnet.Name, err)
×
2649
                        return err
×
2650
                }
×
2651
        }
2652
        return nil
×
2653
}
2654

2655
func (c *Controller) clearOldU2OResource(subnet *kubeovnv1.Subnet) error {
×
2656
        if subnet.Status.U2OInterconnectionVPC != "" &&
×
2657
                (!subnet.Spec.U2OInterconnection || (subnet.Spec.U2OInterconnection && subnet.Status.U2OInterconnectionVPC != subnet.Spec.Vpc)) {
×
2658
                // remove old u2o lsp and lrp first
×
2659
                lspName := fmt.Sprintf("%s-%s", subnet.Name, subnet.Status.U2OInterconnectionVPC)
×
2660
                lrpName := fmt.Sprintf("%s-%s", subnet.Status.U2OInterconnectionVPC, subnet.Name)
×
2661
                klog.Infof("clean subnet %s old u2o resource with lsp %s lrp %s", subnet.Name, lspName, lrpName)
×
2662
                if err := c.OVNNbClient.DeleteLogicalSwitchPort(lspName); err != nil {
×
2663
                        klog.Errorf("failed to delete u2o logical switch port %s: %v", lspName, err)
×
2664
                        return err
×
2665
                }
×
2666

2667
                if err := c.OVNNbClient.DeleteLogicalRouterPort(lrpName); err != nil {
×
2668
                        klog.Errorf("failed to delete u2o logical router port %s: %v", lrpName, err)
×
2669
                        return err
×
2670
                }
×
2671

2672
                if err := c.deletePolicyRouteForU2OInterconn(subnet); err != nil {
×
2673
                        klog.Errorf("failed to delete u2o policy route for u2o connection %s: %v", subnet.Name, err)
×
2674
                        return err
×
2675
                }
×
2676

2677
                if subnet.Status.U2OInterconnectionVPC != c.config.ClusterRouter {
×
2678
                        if err := c.deleteStaticRouteForU2OInterconn(subnet); err != nil {
×
2679
                                klog.Errorf("failed to delete u2o static route for u2o connection %s: %v", subnet.Name, err)
×
2680
                                return err
×
2681
                        }
×
2682
                }
2683
        }
2684
        return nil
×
2685
}
2686

2687
func (c *Controller) reconcilePolicyRouteForCidrChangedSubnet(subnet *kubeovnv1.Subnet, isCommonRoute bool) error {
×
2688
        var match string
×
2689
        var priority int
×
2690

×
2691
        if isCommonRoute {
×
2692
                priority = util.SubnetRouterPolicyPriority
×
2693
        } else {
×
2694
                priority = util.GatewayRouterPolicyPriority
×
2695
        }
×
2696

2697
        policies, err := c.OVNNbClient.ListLogicalRouterPolicies(subnet.Spec.Vpc, priority, map[string]string{
×
2698
                "vendor": util.CniTypeName,
×
2699
                "subnet": subnet.Name,
×
2700
        }, true)
×
2701
        if err != nil {
×
2702
                klog.Errorf("failed to list logical router policies: %v", err)
×
2703
                return err
×
2704
        }
×
2705
        if len(policies) == 0 {
×
2706
                return nil
×
2707
        }
×
2708

2709
        for _, policy := range policies {
×
2710
                policyProtocol := kubeovnv1.ProtocolIPv4
×
2711
                if strings.Contains(policy.Match, "ip6") {
×
2712
                        policyProtocol = kubeovnv1.ProtocolIPv6
×
2713
                }
×
2714

2715
                for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2716
                        if cidr == "" {
×
2717
                                continue
×
2718
                        }
2719
                        if policyProtocol != util.CheckProtocol(cidr) {
×
2720
                                continue
×
2721
                        }
2722

2723
                        ipSuffix := getIPSuffix(util.CheckProtocol(cidr))
×
2724

×
2725
                        if isCommonRoute {
×
2726
                                match = fmt.Sprintf("%s.dst == %s", ipSuffix, cidr)
×
2727
                        } else {
×
2728
                                if subnet.Spec.GatewayType == kubeovnv1.GWCentralizedType {
×
2729
                                        match = fmt.Sprintf("%s.src == %s", ipSuffix, cidr)
×
2730
                                } else {
×
2731
                                        continue
×
2732
                                }
2733
                        }
2734

2735
                        if policy.Match != match {
×
2736
                                klog.Infof("delete old policy route for subnet %s with match %s priority %d, new match %v", subnet.Name, policy.Match, policy.Priority, match)
×
2737
                                if err = c.OVNNbClient.DeleteLogicalRouterPolicyByUUID(subnet.Spec.Vpc, policy.UUID); err != nil {
×
2738
                                        klog.Errorf("failed to delete policy route for subnet %s: %v", subnet.Name, err)
×
2739
                                        return err
×
2740
                                }
×
2741
                        }
2742
                }
2743
        }
2744
        return nil
×
2745
}
2746

2747
func (c *Controller) addPolicyRouteForU2ONoLoadBalancer(subnet *kubeovnv1.Subnet) error {
×
2748
        nodes, err := c.nodesLister.List(labels.Everything())
×
2749
        if err != nil {
×
2750
                klog.Errorf("failed to list nodes: %v", err)
×
2751
                return err
×
2752
        }
×
2753
        // Drop any policies belonging to a previous Service CIDR set so that
2754
        // shrinking the merged set (e.g. ServiceCIDR object deleted) does not
2755
        // leave stale OVN entries behind. Port groups are reused, not touched.
NEW
2756
        if c.logicalRouterExists(subnet.Spec.Vpc) {
×
NEW
2757
                if err := c.OVNNbClient.DeleteLogicalRouterPolicies(subnet.Spec.Vpc, -1, map[string]string{
×
NEW
2758
                        "isU2ONoLBRoutePolicy": "true",
×
NEW
2759
                        "vendor":               util.CniTypeName,
×
NEW
2760
                        "subnet":               subnet.Name,
×
NEW
2761
                }); err != nil {
×
NEW
2762
                        klog.Errorf("failed to clean stale u2o no-lb policies for subnet %s: %v", subnet.Name, err)
×
NEW
2763
                        return err
×
NEW
2764
                }
×
2765
        }
NEW
2766
        v4Svcs := c.serviceCIDRStore.V4CIDRs()
×
NEW
2767
        v6Svcs := c.serviceCIDRStore.V6CIDRs()
×
2768
        for _, node := range nodes {
×
2769
                pgName := getOverlaySubnetsPortGroupName(subnet.Name, node.Name)
×
2770
                if err := c.OVNNbClient.CreatePortGroup(pgName, map[string]string{logicalRouterKey: subnet.Spec.Vpc, logicalSwitchKey: subnet.Name, u2oKey: "true"}); err != nil {
×
2771
                        klog.Errorf("failed to create u2o port group for subnet %s and node %s: %v", subnet.Name, node.Name, err)
×
2772
                        return err
×
2773
                }
×
2774
                key := util.NodeLspName(node.Name)
×
2775
                ip, err := c.ipsLister.Get(key)
×
2776
                if err != nil {
×
2777
                        if k8serrors.IsNotFound(err) {
×
2778
                                continue
×
2779
                        }
2780
                        klog.Error(err)
×
2781
                        return err
×
2782
                }
2783
                for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2784
                        ipSuffix := getIPSuffix(util.CheckProtocol(cidrBlock))
×
2785
                        nodeIP := ip.Spec.V4IPAddress
×
NEW
2786
                        svcCIDRs := v4Svcs
×
2787
                        if ipSuffix == "ip6" {
×
2788
                                nodeIP = ip.Spec.V6IPAddress
×
NEW
2789
                                svcCIDRs = v6Svcs
×
2790
                        }
×
NEW
2791
                        if nodeIP == "" || len(svcCIDRs) == 0 {
×
2792
                                continue
×
2793
                        }
2794

2795
                        pgAs := fmt.Sprintf("%s_%s", pgName, ipSuffix)
×
2796
                        action := kubeovnv1.PolicyRouteActionReroute
×
NEW
2797
                        for _, svcCIDR := range svcCIDRs {
×
NEW
2798
                                match := fmt.Sprintf("%s.src == $%s && %s.dst == %s", ipSuffix, pgAs, ipSuffix, svcCIDR)
×
NEW
2799
                                externalIDs := buildPolicyRouteExternalIDs(subnet.Name, map[string]string{
×
NEW
2800
                                        "isU2ORoutePolicy":     "true",
×
NEW
2801
                                        "isU2ONoLBRoutePolicy": "true",
×
NEW
2802
                                        "node":                 node.Name,
×
NEW
2803
                                        "svcCidr":              svcCIDR,
×
NEW
2804
                                })
×
NEW
2805

×
NEW
2806
                                klog.Infof("add u2o interconnection policy without enabling loadbalancer for router: %s, match %s, action %s, nexthop %s", subnet.Spec.Vpc, match, action, nodeIP)
×
NEW
2807
                                if err := c.addPolicyRouteToVpc(
×
NEW
2808
                                        c.config.ClusterRouter,
×
NEW
2809
                                        &kubeovnv1.PolicyRoute{
×
NEW
2810
                                                Priority:  util.U2OSubnetPolicyPriority,
×
NEW
2811
                                                Match:     match,
×
NEW
2812
                                                Action:    action,
×
NEW
2813
                                                NextHopIP: nodeIP,
×
NEW
2814
                                        },
×
NEW
2815
                                        externalIDs,
×
NEW
2816
                                ); err != nil {
×
NEW
2817
                                        klog.Errorf("failed to add logical router policy for port-group address-set %s: %v", pgAs, err)
×
NEW
2818
                                        return err
×
NEW
2819
                                }
×
2820
                        }
2821
                }
2822
        }
2823
        lsps, err := c.OVNNbClient.ListNormalLogicalSwitchPorts(true, map[string]string{logicalSwitchKey: subnet.Name})
×
2824
        if err != nil {
×
2825
                klog.Errorf("failed to list normal lsps for subnet %s: %v", subnet.Name, err)
×
2826
                return err
×
2827
        }
×
2828
        for _, lsp := range lsps {
×
2829
                ip, err := c.ipsLister.Get(lsp.Name)
×
2830
                if err != nil {
×
2831
                        if k8serrors.IsNotFound(err) {
×
2832
                                continue
×
2833
                        }
2834
                        klog.Error(err)
×
2835
                        return err
×
2836
                }
2837
                pgName := getOverlaySubnetsPortGroupName(subnet.Name, ip.Spec.NodeName)
×
2838
                if err = c.OVNNbClient.PortGroupAddPorts(pgName, lsp.Name); err != nil {
×
2839
                        klog.Errorf("failed to add port to u2o port group %s: %v", pgName, err)
×
2840
                        return err
×
2841
                }
×
2842
        }
2843
        return nil
×
2844
}
2845

2846
func (c *Controller) deletePolicyRouteForU2ONoLoadBalancer(subnet *kubeovnv1.Subnet) error {
×
2847
        if !c.logicalRouterExists(subnet.Spec.Vpc) {
×
2848
                return nil
×
2849
        }
×
2850
        policies, err := c.OVNNbClient.ListLogicalRouterPolicies(subnet.Spec.Vpc, -1, map[string]string{
×
2851
                "isU2ONoLBRoutePolicy": "true",
×
2852
                "vendor":               util.CniTypeName,
×
2853
                "subnet":               subnet.Name,
×
2854
        }, true)
×
2855
        if err != nil {
×
2856
                klog.Errorf("failed to list logical router policies: %v", err)
×
2857
                return err
×
2858
        }
×
2859

2860
        lr := subnet.Status.U2OInterconnectionVPC
×
2861
        if lr == "" {
×
2862
                // old version field U2OInterconnectionVPC may be "" and then use subnet.Spec.Vpc
×
2863
                lr = subnet.Spec.Vpc
×
2864
        }
×
2865

2866
        for _, policy := range policies {
×
2867
                klog.Infof("delete u2o interconnection policy without enabling loadbalancer for router %s with match %s priority %d", lr, policy.Match, policy.Priority)
×
2868
                if err = c.OVNNbClient.DeleteLogicalRouterPolicyByUUID(lr, policy.UUID); err != nil {
×
2869
                        klog.Errorf("failed to delete u2o interconnection policy for subnet %s: %v", subnet.Name, err)
×
2870
                        return err
×
2871
                }
×
2872
        }
2873

2874
        pgs, err := c.OVNNbClient.ListPortGroups(map[string]string{logicalRouterKey: subnet.Spec.Vpc, logicalSwitchKey: subnet.Name, u2oKey: "true"})
×
2875
        if err != nil {
×
2876
                klog.Errorf("failed to list u2o port groups with u2oKey is true for subnet %s: %v", subnet.Name, err)
×
2877
                return err
×
2878
        }
×
2879
        for _, pg := range pgs {
×
2880
                klog.Infof("delete u2o port group %s for subnet %s", pg.Name, subnet.Name)
×
2881
                if err = c.OVNNbClient.DeletePortGroup(pg.Name); err != nil {
×
2882
                        klog.Errorf("failed to delete u2o port group for subnet %s: %v", subnet.Name, err)
×
2883
                        return err
×
2884
                }
×
2885
        }
2886
        return nil
×
2887
}
2888

2889
func (c *Controller) findSubnetByNetworkAttachmentDefinition(ns, name string, subnets []*kubeovnv1.Subnet) (*kubeovnv1.Subnet, error) {
×
2890
        nad, err := c.netAttachLister.NetworkAttachmentDefinitions(ns).Get(name)
×
2891
        if err != nil {
×
2892
                klog.Errorf("failed to get net-attach-def %s/%s: %v", ns, name, err)
×
2893
                return nil, err
×
2894
        }
×
2895
        netCfg, err := loadNetConf([]byte(nad.Spec.Config))
×
2896
        if err != nil {
×
2897
                klog.Errorf("failed to parse config of net-attach-def %s/%s: %v", ns, name, err)
×
2898
                return nil, err
×
2899
        }
×
2900

2901
        var provider string
×
2902
        if netCfg.Conf.Type == util.CniTypeName {
×
2903
                provider = fmt.Sprintf("%s.%s.%s", name, ns, util.OvnProvider)
×
2904
        } else {
×
2905
                provider = fmt.Sprintf("%s.%s", name, ns)
×
2906
        }
×
2907
        var subnet *kubeovnv1.Subnet
×
2908
        for _, s := range subnets {
×
2909
                if s.Spec.Provider == provider {
×
2910
                        subnet = s.DeepCopy()
×
2911
                        break
×
2912
                }
2913
        }
2914
        if subnet == nil {
×
2915
                err = fmt.Errorf("failed to get subnet for net-attach-def %s/%s", ns, name)
×
2916
                klog.Error(err)
×
2917
                return nil, err
×
2918
        }
×
2919

2920
        return subnet, nil
×
2921
}
2922

2923
func (c *Controller) handleMcastQuerierChange(subnet *kubeovnv1.Subnet) error {
1✔
2924
        if subnet.Spec.EnableMulticastSnoop {
2✔
2925
                multicastSnoopFlag := map[string]string{
1✔
2926
                        "mcast_snoop":   "true",
1✔
2927
                        "mcast_querier": "true",
1✔
2928
                        "mcast_ip4_src": subnet.Status.McastQuerierIP,
1✔
2929
                        "mcast_eth_src": subnet.Status.McastQuerierMAC,
1✔
2930
                }
1✔
2931
                mcastQuerierLspName := fmt.Sprintf(util.McastQuerierName, subnet.Name)
1✔
2932
                if err := c.OVNNbClient.CreateLogicalSwitchPort(subnet.Name, mcastQuerierLspName, subnet.Status.McastQuerierIP, subnet.Status.McastQuerierMAC, mcastQuerierLspName, metav1.NamespaceDefault, false, "", "", false, nil, ""); err != nil {
2✔
2933
                        err = fmt.Errorf("failed to create mcast querier lsp %s: %w", mcastQuerierLspName, err)
1✔
2934
                        klog.Error(err)
1✔
2935
                        return err
1✔
2936
                }
1✔
2937

2938
                if err := c.OVNNbClient.LogicalSwitchUpdateOtherConfig(subnet.Name, ovsdb.MutateOperationInsert, multicastSnoopFlag); err != nil {
2✔
2939
                        klog.Errorf("enable logical switch multicast snoop %s: %v", subnet.Name, err)
1✔
2940
                        return err
1✔
2941
                }
1✔
2942
        } else {
1✔
2943
                lss, err := c.OVNNbClient.ListLogicalSwitch(false, func(ls *ovnnb.LogicalSwitch) bool {
1✔
2944
                        return ls.Name == subnet.Name
×
2945
                })
×
2946
                if err != nil {
2✔
2947
                        klog.Errorf("failed to list logical switch %s: %v", subnet.Name, err)
1✔
2948
                        return err
1✔
2949
                }
1✔
2950
                if len(lss) == 0 {
2✔
2951
                        klog.Warningf("logical switch %s not found, skipping multicast snoop cleanup", subnet.Name)
1✔
2952
                        return nil
1✔
2953
                }
1✔
2954

2955
                multicastSnoopFlag := map[string]string{
1✔
2956
                        "mcast_snoop":   lss[0].OtherConfig["mcast_snoop"],
1✔
2957
                        "mcast_querier": lss[0].OtherConfig["mcast_querier"],
1✔
2958
                        "mcast_ip4_src": lss[0].OtherConfig["mcast_ip4_src"],
1✔
2959
                        "mcast_eth_src": lss[0].OtherConfig["mcast_eth_src"],
1✔
2960
                }
1✔
2961
                mcastQuerierLspName := fmt.Sprintf(util.McastQuerierName, subnet.Name)
1✔
2962
                if err := c.OVNNbClient.LogicalSwitchUpdateOtherConfig(subnet.Name, ovsdb.MutateOperationDelete, multicastSnoopFlag); err != nil {
2✔
2963
                        klog.Errorf("disable logical switch multicast snoop %s: %v", subnet.Name, err)
1✔
2964
                        return err
1✔
2965
                }
1✔
2966

2967
                if err := c.OVNNbClient.DeleteLogicalSwitchPort(mcastQuerierLspName); err != nil {
2✔
2968
                        err = fmt.Errorf("failed to delete mcast querier lsp %s: %w", mcastQuerierLspName, err)
1✔
2969
                        klog.Error(err)
1✔
2970
                        return err
1✔
2971
                }
1✔
2972
        }
2973
        return nil
1✔
2974
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc