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

kubeovn / kube-ovn / 27389982525

12 Jun 2026 02:10AM UTC coverage: 26.342% (-0.002%) from 26.344%
27389982525

push

github

web-flow
fix: misc bug fixes from code audit (#6856)

* fix(controller): gc all stale reserved vips in one pass

gcVip returned nil right after releasing the first stale reserved VIP,
so the remaining stale VIPs in the list were never processed. Since gc
runs only once at controller startup, those VIPs kept their ip_reserved
label for the whole controller lifetime, blocking new pods from reusing
them via the ovn.kubernetes.io/vip annotation.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>

* fix(controller): use resolved IP CR name in update failure error

The update branch of createOrUpdateIPCR formatted the error with
ipCRName, which is empty for the node/u2o/mcast querier IP paths;
use the resolved ipName like the create branch does.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>

* fix(controller): avoid mutating conditions slice while iterating it

resyncProviderNetworkStatus called RemoveNodeConditions inside a range
over pn.Status.Conditions; slices.Delete shifts elements in the shared
backing array, so the condition adjacent to a removed one was skipped
and tail iterations read zeroed entries. Collect the stale node names
first, then remove them after the loop.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>

* fix(daemon): guard DefaultEncapIP access with node networks lock

loopEncapIPCheck wrote config.DefaultEncapIP without holding
nodeNetworksMutex while setEncapIPs reads it under RLock from the
updateNode worker goroutine, which is a data race. Add a locked setter
and use it there. Also move the networkName=="" early return in
GetEncapIPByNetwork under the RLock, which had the same unguarded read.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>

* fix(daemon): add missing path separator in ipsec CSR file path

ipsecReqPath was built without the separator, producing
/etc/ovs_ipsec_keysipsec-req.pem outside the keys directory, so
clearIPSecKeysDir could never clean the CSR up after rotation.

Signed-off-by: Mengxin Liu <liu... (continued)

3 of 27 new or added lines in 7 files covered. (11.11%)

2 existing lines in 2 files now uncovered.

15451 of 58655 relevant lines covered (26.34%)

0.31 hits per line

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

8.43
/pkg/daemon/controller_linux.go
1
package daemon
2

3
import (
4
        "errors"
5
        "fmt"
6
        "net"
7
        "os"
8
        "os/exec"
9
        "path/filepath"
10
        "reflect"
11
        "slices"
12
        "strings"
13
        "sync"
14
        "syscall"
15

16
        ovsutil "github.com/digitalocean/go-openvswitch/ovs"
17
        nadv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
18
        nadutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils"
19
        "github.com/kubeovn/felix/ipsets"
20
        "github.com/kubeovn/go-iptables/iptables"
21
        "github.com/vishvananda/netlink"
22
        "golang.org/x/sys/unix"
23
        v1 "k8s.io/api/core/v1"
24
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
25
        "k8s.io/apimachinery/pkg/labels"
26
        utilruntime "k8s.io/apimachinery/pkg/util/runtime"
27
        "k8s.io/client-go/tools/cache"
28
        "k8s.io/klog/v2"
29
        k8sipset "k8s.io/kubernetes/pkg/proxy/ipvs/ipset"
30
        k8siptables "k8s.io/kubernetes/pkg/util/iptables"
31

32
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
33
        "github.com/kubeovn/kube-ovn/pkg/ovs"
34
        "github.com/kubeovn/kube-ovn/pkg/util"
35
)
36

37
const (
38
        kernelModuleIPTables  = "ip_tables"
39
        kernelModuleIP6Tables = "ip6_tables"
40
)
41

42
// ControllerRuntime represents runtime specific controller members
43
type ControllerRuntime struct {
44
        iptables         map[string]*iptables.IPTables
45
        iptablesObsolete map[string]*iptables.IPTables
46
        k8siptables      map[string]k8siptables.Interface
47
        k8sipsets        k8sipset.Interface
48
        ipsets           map[string]*ipsets.IPSets
49
        gwCounters       map[string]*util.GwIPTablesCounters
50

51
        nmSyncer  *networkManagerSyncer
52
        ovsClient *ovsutil.Client
53

54
        flowCache      map[string]map[string][]string // key: bridgeName -> flowKey -> flow rules
55
        flowCacheMutex sync.RWMutex
56
        flowChan       chan struct{} // channel to trigger immediate flow sync
57
}
58

59
type LbServiceRules struct {
60
        IP          string
61
        Port        uint16
62
        Protocol    string
63
        BridgeName  string
64
        DstMac      string
65
        UnderlayNic string
66
        SubnetName  string
67
}
68

69
func evalCommandSymlinks(cmd string) (string, error) {
×
70
        path, err := exec.LookPath(cmd)
×
71
        if err != nil {
×
72
                return "", fmt.Errorf("failed to search for command %q: %w", cmd, err)
×
73
        }
×
74
        file, err := filepath.EvalSymlinks(path)
×
75
        if err != nil {
×
76
                return "", fmt.Errorf("failed to resolve symbolic links for file %q: %w", path, err)
×
77
        }
×
78

79
        return file, nil
×
80
}
81

82
func isLegacyIptablesMode() (bool, error) {
×
83
        path, err := evalCommandSymlinks("iptables")
×
84
        if err != nil {
×
85
                return false, err
×
86
        }
×
87
        pathLegacy, err := evalCommandSymlinks("iptables-legacy")
×
88
        if err != nil {
×
89
                return false, err
×
90
        }
×
91
        return path == pathLegacy, nil
×
92
}
93

94
func (c *Controller) initRuntime() error {
×
95
        ok, err := isLegacyIptablesMode()
×
96
        if err != nil {
×
97
                klog.Errorf("failed to check iptables mode: %v", err)
×
98
                return err
×
99
        }
×
100
        if !ok {
×
101
                // iptables works in nft mode, we should migrate iptables rules
×
102
                c.iptablesObsolete = make(map[string]*iptables.IPTables, 2)
×
103
        }
×
104

105
        c.iptables = make(map[string]*iptables.IPTables)
×
106
        c.ipsets = make(map[string]*ipsets.IPSets)
×
107
        c.gwCounters = make(map[string]*util.GwIPTablesCounters)
×
108
        c.k8siptables = make(map[string]k8siptables.Interface)
×
109
        c.k8sipsets = k8sipset.New()
×
110
        c.ovsClient = ovsutil.New()
×
111

×
112
        // Initialize OpenFlow flow cache (ovn-kubernetes style)
×
113
        c.flowCache = make(map[string]map[string][]string)
×
114
        c.flowChan = make(chan struct{}, 1)
×
115

×
116
        if c.protocol == kubeovnv1.ProtocolIPv4 || c.protocol == kubeovnv1.ProtocolDual {
×
117
                ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
×
118
                if err != nil {
×
119
                        klog.Error(err)
×
120
                        return err
×
121
                }
×
122
                c.iptables[kubeovnv1.ProtocolIPv4] = ipt
×
123
                if c.iptablesObsolete != nil {
×
124
                        ok, err := kernelModuleLoaded(kernelModuleIPTables)
×
125
                        if err != nil {
×
126
                                klog.Errorf("failed to check kernel module %s: %v", kernelModuleIPTables, err)
×
127
                        }
×
128
                        if ok {
×
129
                                if ipt, err = iptables.NewWithProtocolAndMode(iptables.ProtocolIPv4, "legacy"); err != nil {
×
130
                                        klog.Error(err)
×
131
                                        return err
×
132
                                }
×
133
                                c.iptablesObsolete[kubeovnv1.ProtocolIPv4] = ipt
×
134
                        }
135
                }
136
                c.ipsets[kubeovnv1.ProtocolIPv4] = ipsets.NewIPSets(ipsets.NewIPVersionConfig(ipsets.IPFamilyV4, IPSetPrefix, nil, nil))
×
137
                c.k8siptables[kubeovnv1.ProtocolIPv4] = k8siptables.New(k8siptables.ProtocolIPv4)
×
138
        }
139
        if c.protocol == kubeovnv1.ProtocolIPv6 || c.protocol == kubeovnv1.ProtocolDual {
×
140
                ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
×
141
                if err != nil {
×
142
                        klog.Error(err)
×
143
                        return err
×
144
                }
×
145
                c.iptables[kubeovnv1.ProtocolIPv6] = ipt
×
146
                if c.iptablesObsolete != nil {
×
147
                        ok, err := kernelModuleLoaded(kernelModuleIP6Tables)
×
148
                        if err != nil {
×
149
                                klog.Errorf("failed to check kernel module %s: %v", kernelModuleIP6Tables, err)
×
150
                        }
×
151
                        if ok {
×
152
                                if ipt, err = iptables.NewWithProtocolAndMode(iptables.ProtocolIPv6, "legacy"); err != nil {
×
153
                                        klog.Error(err)
×
154
                                        return err
×
155
                                }
×
156
                                c.iptablesObsolete[kubeovnv1.ProtocolIPv6] = ipt
×
157
                        }
158
                }
159
                c.ipsets[kubeovnv1.ProtocolIPv6] = ipsets.NewIPSets(ipsets.NewIPVersionConfig(ipsets.IPFamilyV6, IPSetPrefix, nil, nil))
×
160
                c.k8siptables[kubeovnv1.ProtocolIPv6] = k8siptables.New(k8siptables.ProtocolIPv6)
×
161
        }
162

163
        if err = ovs.ClearU2OFlows(c.ovsClient); err != nil {
×
164
                util.LogFatalAndExit(err, "failed to clear obsolete u2o flows")
×
165
        }
×
166

167
        c.nmSyncer = newNetworkManagerSyncer()
×
168
        c.nmSyncer.Run(c.transferAddrsAndRoutes)
×
169

×
170
        return nil
×
171
}
172

173
func (c *Controller) handleEnableExternalLBAddressChange(oldSubnet, newSubnet *kubeovnv1.Subnet) error {
×
174
        var subnetName string
×
175
        var action string
×
176

×
177
        switch {
×
178
        case oldSubnet != nil && newSubnet != nil:
×
179
                subnetName = oldSubnet.Name
×
180
                if oldSubnet.Spec.EnableExternalLBAddress != newSubnet.Spec.EnableExternalLBAddress {
×
181
                        klog.Infof("EnableExternalLBAddress changed for subnet %s", newSubnet.Name)
×
182
                        if newSubnet.Spec.EnableExternalLBAddress {
×
183
                                action = "add"
×
184
                        } else {
×
185
                                action = "remove"
×
186
                        }
×
187
                }
188
        case oldSubnet != nil:
×
189
                subnetName = oldSubnet.Name
×
190
                if oldSubnet.Spec.EnableExternalLBAddress {
×
191
                        klog.Infof("EnableExternalLBAddress removed for subnet %s", oldSubnet.Name)
×
192
                        action = "remove"
×
193
                }
×
194
        case newSubnet != nil:
×
195
                subnetName = newSubnet.Name
×
196
                if newSubnet.Spec.EnableExternalLBAddress {
×
197
                        klog.Infof("EnableExternalLBAddress added for subnet %s", newSubnet.Name)
×
198
                        action = "add"
×
199
                }
×
200
        }
201

202
        if action != "" {
×
203
                services, err := c.servicesLister.List(labels.Everything())
×
204
                if err != nil {
×
205
                        klog.Errorf("failed to list services: %v", err)
×
206
                        return err
×
207
                }
×
208

209
                for _, svc := range services {
×
210
                        if svc.Annotations[util.ServiceExternalIPFromSubnetAnnotation] == subnetName {
×
211
                                klog.Infof("Service %s/%s has external LB address pool annotation from subnet %s, action: %s", svc.Namespace, svc.Name, subnetName, action)
×
212
                                switch action {
×
213
                                case "add":
×
214
                                        c.serviceQueue.Add(&serviceEvent{newObj: svc})
×
215
                                case "remove":
×
216
                                        c.serviceQueue.Add(&serviceEvent{oldObj: svc})
×
217
                                }
218
                        }
219
                }
220
        }
221
        return nil
×
222
}
223

224
// handleU2OInterconnectionMACChange handles U2O interconnection MAC address changes.
225
// When U2O (Underlay to Overlay) interconnection is enabled, the svc local flow's destination
226
// MAC must point to the LRP (Logical Router Port) MAC. Otherwise, without U2O enabled (no LRP exists),
227
// the flow would hit the rules created by build_lswitch_dnat_mod_dl_dst_rules instead.
228
func (c *Controller) handleU2OInterconnectionMACChange(oldSubnet, newSubnet *kubeovnv1.Subnet) error {
×
229
        if oldSubnet == nil || newSubnet == nil {
×
230
                return nil
×
231
        }
×
232

233
        oldMAC := oldSubnet.Status.U2OInterconnectionMAC
×
234
        newMAC := newSubnet.Status.U2OInterconnectionMAC
×
235

×
236
        if oldMAC == newMAC {
×
237
                return nil
×
238
        }
×
239

240
        if newMAC == "" && oldMAC == "" {
×
241
                return nil
×
242
        }
×
243

244
        klog.Infof("U2OInterconnectionMAC changed for subnet %s: %s -> %s",
×
245
                oldSubnet.Name, oldMAC, newMAC)
×
246

×
247
        // Find all services using this subnet and re-sync them
×
248
        services, err := c.servicesLister.List(labels.Everything())
×
249
        if err != nil {
×
250
                klog.Errorf("failed to list services: %v", err)
×
251
                return err
×
252
        }
×
253

254
        for _, svc := range services {
×
255
                if svc.Annotations[util.ServiceExternalIPFromSubnetAnnotation] == oldSubnet.Name {
×
256
                        klog.Infof("Re-syncing service %s/%s due to U2OInterconnectionMAC change in subnet %s",
×
257
                                svc.Namespace, svc.Name, oldSubnet.Name)
×
258
                        c.serviceQueue.Add(&serviceEvent{newObj: svc})
×
259
                }
×
260
        }
261
        return nil
×
262
}
263

264
func (c *Controller) reconcileRouters(event *subnetEvent) error {
×
265
        subnets, err := c.subnetsLister.List(labels.Everything())
×
266
        if err != nil {
×
267
                klog.Errorf("failed to list subnets %v", err)
×
268
                return err
×
269
        }
×
270

271
        if event != nil {
×
272
                var ok bool
×
273
                var oldSubnet, newSubnet *kubeovnv1.Subnet
×
274
                if event.oldObj != nil {
×
275
                        if oldSubnet, ok = event.oldObj.(*kubeovnv1.Subnet); !ok {
×
276
                                klog.Errorf("expected old subnet in subnetEvent but got %#v", event.oldObj)
×
277
                                return nil
×
278
                        }
×
279
                }
280
                if event.newObj != nil {
×
281
                        if newSubnet, ok = event.newObj.(*kubeovnv1.Subnet); !ok {
×
282
                                klog.Errorf("expected new subnet in subnetEvent but got %#v", event.newObj)
×
283
                                return nil
×
284
                        }
×
285
                }
286

287
                if err = c.handleEnableExternalLBAddressChange(oldSubnet, newSubnet); err != nil {
×
288
                        klog.Errorf("failed to handle enable external lb address change: %v", err)
×
289
                        return err
×
290
                }
×
291

292
                if err = c.handleU2OInterconnectionMACChange(oldSubnet, newSubnet); err != nil {
×
293
                        klog.Errorf("failed to handle u2o interconnection mac change: %v", err)
×
294
                        return err
×
295
                }
×
296
                // handle policy routing
297
                rulesToAdd, rulesToDel, routesToAdd, routesToDel, err := c.diffPolicyRouting(oldSubnet, newSubnet)
×
298
                if err != nil {
×
299
                        klog.Errorf("failed to get policy routing difference: %v", err)
×
300
                        return err
×
301
                }
×
302
                // add new routes first
303
                for _, r := range routesToAdd {
×
304
                        if err = netlink.RouteReplace(&r); err != nil && !errors.Is(err, syscall.EEXIST) {
×
305
                                klog.Errorf("failed to replace route for subnet %s: %v", newSubnet.Name, err)
×
306
                                return err
×
307
                        }
×
308
                }
309
                // next, add new rules
310
                for _, r := range rulesToAdd {
×
311
                        if err = netlink.RuleAdd(&r); err != nil && !errors.Is(err, syscall.EEXIST) {
×
312
                                klog.Errorf("failed to add network rule for subnet %s: %v", newSubnet.Name, err)
×
313
                                return err
×
314
                        }
×
315
                }
316
                // then delete old network rules
317
                for _, r := range rulesToDel {
×
318
                        // loop to delete all matched rules
×
319
                        for {
×
320
                                if err = netlink.RuleDel(&r); err != nil {
×
321
                                        if !errors.Is(err, syscall.ENOENT) {
×
322
                                                klog.Errorf("failed to delete network rule for subnet %s: %v", oldSubnet.Name, err)
×
323
                                                return err
×
324
                                        }
×
325
                                        break
×
326
                                }
327
                        }
328
                }
329
                // last, delete old network routes
330
                for _, r := range routesToDel {
×
331
                        if err = netlink.RouteDel(&r); err != nil && !errors.Is(err, syscall.ENOENT) {
×
332
                                klog.Errorf("failed to delete route for subnet %s: %v", oldSubnet.Name, err)
×
333
                                return err
×
334
                        }
×
335
                }
336
        }
337

338
        node, err := c.nodesLister.Get(c.config.NodeName)
×
339
        if err != nil {
×
340
                klog.Errorf("failed to get node %s %v", c.config.NodeName, err)
×
341
                return err
×
342
        }
×
343
        nodeIPv4, nodeIPv6 := util.GetNodeInternalIP(*node)
×
344
        var joinIPv4, joinIPv6 string
×
345
        if len(node.Annotations) != 0 {
×
346
                joinIPv4, joinIPv6 = util.SplitStringIP(node.Annotations[util.IPAddressAnnotation])
×
347
        }
×
348

349
        joinCIDR := make([]string, 0, 2)
×
350
        cidrs := make([]string, 0, len(subnets)*2)
×
351
        for _, subnet := range subnets {
×
352
                // The route for overlay subnet cidr via ovn0 should not be deleted even though subnet.Status has changed to not ready
×
353
                if subnet.Spec.Vpc != c.config.ClusterRouter ||
×
354
                        (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway && (!subnet.Spec.U2OInterconnection || (subnet.Spec.EnableLb != nil && *subnet.Spec.EnableLb))) ||
×
355
                        !subnet.Status.IsValidated() {
×
356
                        continue
×
357
                }
358

359
                for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
360
                        if _, ipNet, err := net.ParseCIDR(cidrBlock); err != nil {
×
361
                                klog.Errorf("%s is not a valid cidr block", cidrBlock)
×
362
                        } else {
×
363
                                if nodeIPv4 != "" && util.CIDRContainIP(cidrBlock, nodeIPv4) {
×
364
                                        continue
×
365
                                }
366
                                if nodeIPv6 != "" && util.CIDRContainIP(cidrBlock, nodeIPv6) {
×
367
                                        continue
×
368
                                }
369
                                cidrs = append(cidrs, ipNet.String())
×
370
                                if subnet.Name == c.config.NodeSwitch {
×
371
                                        joinCIDR = append(joinCIDR, ipNet.String())
×
372
                                }
×
373
                        }
374
                }
375
        }
376

377
        gateway, ok := node.Annotations[util.GatewayAnnotation]
×
378
        if !ok {
×
379
                err = fmt.Errorf("gateway annotation for node %s does not exist", node.Name)
×
380
                klog.Error(err)
×
381
                return err
×
382
        }
×
383
        nic, err := netlink.LinkByName(util.NodeNic)
×
384
        if err != nil {
×
385
                klog.Errorf("failed to get nic %s", util.NodeNic)
×
386
                return fmt.Errorf("failed to get nic %s", util.NodeNic)
×
387
        }
×
388

389
        allRoutes, err := getNicExistRoutes(nil, gateway)
×
390
        if err != nil {
×
391
                klog.Error(err)
×
392
                return err
×
393
        }
×
394
        nodeNicRoutes, err := getNicExistRoutes(nic, gateway)
×
395
        if err != nil {
×
396
                klog.Error(err)
×
397
                return err
×
398
        }
×
399
        toAdd, toDel := routeDiff(nodeNicRoutes, allRoutes, cidrs, joinCIDR, joinIPv4, joinIPv6, gateway, net.ParseIP(nodeIPv4), net.ParseIP(nodeIPv6))
×
400
        for _, r := range toDel {
×
401
                if err = netlink.RouteDel(&netlink.Route{Dst: r.Dst}); err != nil {
×
402
                        klog.Errorf("failed to del route %v", err)
×
403
                }
×
404
        }
405

406
        for _, r := range toAdd {
×
407
                r.LinkIndex = nic.Attrs().Index
×
408
                if err = netlink.RouteReplace(&r); err != nil {
×
409
                        klog.Errorf("failed to replace route %v: %v", r, err)
×
410
                }
×
411
        }
412

413
        return nil
×
414
}
415

416
func genLBServiceRules(service *v1.Service, bridgeName, underlayNic, dstMac, subnetName string) []LbServiceRules {
×
417
        var lbServiceRules []LbServiceRules
×
418
        for _, ingress := range service.Status.LoadBalancer.Ingress {
×
419
                for _, port := range service.Spec.Ports {
×
420
                        lbServiceRules = append(lbServiceRules, LbServiceRules{
×
421
                                IP:          ingress.IP,
×
422
                                Port:        uint16(port.Port), // #nosec G115
×
423
                                Protocol:    string(port.Protocol),
×
424
                                DstMac:      dstMac,
×
425
                                UnderlayNic: underlayNic,
×
426
                                BridgeName:  bridgeName,
×
427
                                SubnetName:  subnetName,
×
428
                        })
×
429
                }
×
430
        }
431
        return lbServiceRules
×
432
}
433

434
func (c *Controller) diffExternalLBServiceRules(oldService, newService *v1.Service, isSubnetExternalLBEnabled bool) (lbServiceRulesToAdd, lbServiceRulesToDel []LbServiceRules, err error) {
×
435
        var oldlbServiceRules, newlbServiceRules []LbServiceRules
×
436

×
437
        if oldService != nil && oldService.Annotations[util.ServiceExternalIPFromSubnetAnnotation] != "" {
×
438
                oldSubnetName := oldService.Annotations[util.ServiceExternalIPFromSubnetAnnotation]
×
439
                oldBridgeName, underlayNic, dstMac, err := c.getExtInfoBySubnet(oldSubnetName)
×
440
                if err != nil {
×
441
                        klog.Errorf("failed to get provider network by subnet %s: %v", oldSubnetName, err)
×
442
                        return nil, nil, err
×
443
                }
×
444

445
                oldlbServiceRules = genLBServiceRules(oldService, oldBridgeName, underlayNic, dstMac, oldSubnetName)
×
446
        }
447

448
        if isSubnetExternalLBEnabled && newService != nil && newService.Annotations[util.ServiceExternalIPFromSubnetAnnotation] != "" {
×
449
                newSubnetName := newService.Annotations[util.ServiceExternalIPFromSubnetAnnotation]
×
450
                newBridgeName, underlayNic, dstMac, err := c.getExtInfoBySubnet(newSubnetName)
×
451
                if err != nil {
×
452
                        klog.Errorf("failed to get provider network by subnet %s: %v", newSubnetName, err)
×
453
                        return nil, nil, err
×
454
                }
×
455
                newlbServiceRules = genLBServiceRules(newService, newBridgeName, underlayNic, dstMac, newSubnetName)
×
456
        }
457

458
        for _, oldRule := range oldlbServiceRules {
×
459
                found := slices.Contains(newlbServiceRules, oldRule)
×
460
                if !found {
×
461
                        lbServiceRulesToDel = append(lbServiceRulesToDel, oldRule)
×
462
                }
×
463
        }
464

465
        for _, newRule := range newlbServiceRules {
×
466
                found := slices.Contains(oldlbServiceRules, newRule)
×
467
                if !found {
×
468
                        lbServiceRulesToAdd = append(lbServiceRulesToAdd, newRule)
×
469
                }
×
470
        }
471

472
        return lbServiceRulesToAdd, lbServiceRulesToDel, nil
×
473
}
474

475
func (c *Controller) getExtInfoBySubnet(subnetName string) (string, string, string, error) {
×
476
        subnet, err := c.subnetsLister.Get(subnetName)
×
477
        if err != nil {
×
478
                klog.Errorf("failed to get subnet %s: %v", subnetName, err)
×
479
                return "", "", "", err
×
480
        }
×
481

482
        dstMac := subnet.Status.U2OInterconnectionMAC
×
483
        if dstMac == "" {
×
484
                dstMac = util.MasqueradeExternalLBAccessMac
×
485
                klog.Infof("Subnet %s has no U2OInterconnectionMAC, using default MAC %s", subnetName, dstMac)
×
486
        } else {
×
487
                klog.Infof("Using U2OInterconnectionMAC %s for subnet %s", dstMac, subnetName)
×
488
        }
×
489

490
        vlanName := subnet.Spec.Vlan
×
491
        if vlanName == "" {
×
492
                return "", "", "", errors.New("vlan not specified in subnet")
×
493
        }
×
494

495
        vlan, err := c.vlansLister.Get(vlanName)
×
496
        if err != nil {
×
497
                klog.Errorf("failed to get vlan %s: %v", vlanName, err)
×
498
                return "", "", "", err
×
499
        }
×
500

501
        providerNetworkName := vlan.Spec.Provider
×
502
        if providerNetworkName == "" {
×
503
                return "", "", "", errors.New("provider network not specified in vlan")
×
504
        }
×
505

506
        pn, err := c.providerNetworksLister.Get(providerNetworkName)
×
507
        if err != nil {
×
508
                klog.Errorf("failed to get provider network %s: %v", providerNetworkName, err)
×
509
                return "", "", "", err
×
510
        }
×
511

512
        underlayNic := pn.Spec.DefaultInterface
×
513
        for _, item := range pn.Spec.CustomInterfaces {
×
514
                if slices.Contains(item.Nodes, c.config.NodeName) {
×
515
                        underlayNic = item.Interface
×
516
                        break
×
517
                }
518
        }
519
        bridgeName := util.ExternalBridgeName(providerNetworkName)
×
520
        klog.Infof("Provider network: %s, Underlay NIC: %s, DstMac: %s", providerNetworkName, underlayNic, dstMac)
×
521
        return bridgeName, underlayNic, dstMac, nil
×
522
}
523

524
func (c *Controller) reconcileServices(event *serviceEvent) error {
×
525
        if event == nil {
×
526
                return nil
×
527
        }
×
528
        var ok bool
×
529
        var oldService, newService *v1.Service
×
530
        if event.oldObj != nil {
×
531
                if oldService, ok = event.oldObj.(*v1.Service); !ok {
×
532
                        klog.Errorf("expected old service in serviceEvent but got %#v", event.oldObj)
×
533
                        return nil
×
534
                }
×
535
        }
536

537
        if event.newObj != nil {
×
538
                if newService, ok = event.newObj.(*v1.Service); !ok {
×
539
                        klog.Errorf("expected new service in serviceEvent but got %#v", event.newObj)
×
540
                        return nil
×
541
                }
×
542
        }
543

544
        // check is the lb service IP related subnet's EnableExternalLBAddress
545
        isSubnetExternalLBEnabled := false
×
546
        if newService != nil && newService.Annotations[util.ServiceExternalIPFromSubnetAnnotation] != "" {
×
547
                subnet, err := c.subnetsLister.Get(newService.Annotations[util.ServiceExternalIPFromSubnetAnnotation])
×
548
                if err != nil {
×
549
                        klog.Errorf("failed to get subnet %s: %v", newService.Annotations[util.ServiceExternalIPFromSubnetAnnotation], err)
×
550
                        return err
×
551
                }
×
552
                isSubnetExternalLBEnabled = subnet.Spec.EnableExternalLBAddress
×
553
        }
554

555
        lbServiceRulesToAdd, lbServiceRulesToDel, err := c.diffExternalLBServiceRules(oldService, newService, isSubnetExternalLBEnabled)
×
556
        if err != nil {
×
557
                klog.Errorf("failed to get ip port difference: %v", err)
×
558
                return err
×
559
        }
×
560

561
        if len(lbServiceRulesToAdd) > 0 {
×
562
                for _, rule := range lbServiceRulesToAdd {
×
563
                        klog.Infof("Adding LB service rule: %+v", rule)
×
564
                        if err := c.AddOrUpdateUnderlaySubnetSvcLocalFlowCache(rule.IP, rule.Port, rule.Protocol, rule.DstMac, rule.UnderlayNic, rule.BridgeName, rule.SubnetName); err != nil {
×
565
                                klog.Errorf("failed to update underlay subnet svc local openflow cache: %v", err)
×
566
                                return err
×
567
                        }
×
568
                }
569
        }
570

571
        if len(lbServiceRulesToDel) > 0 {
×
572
                for _, rule := range lbServiceRulesToDel {
×
573
                        klog.Infof("Delete LB service rule: %+v", rule)
×
574
                        c.deleteUnderlaySubnetSvcLocalFlowCache(rule.BridgeName, rule.IP, rule.Port, rule.Protocol)
×
575
                }
×
576
        }
577

578
        return nil
×
579
}
580

581
func getNicExistRoutes(nic netlink.Link, gateway string) ([]netlink.Route, error) {
×
582
        var routes, existRoutes []netlink.Route
×
583
        var err error
×
584
        for gw := range strings.SplitSeq(gateway, ",") {
×
585
                if util.CheckProtocol(gw) == kubeovnv1.ProtocolIPv4 {
×
586
                        routes, err = netlink.RouteList(nic, netlink.FAMILY_V4)
×
587
                } else {
×
588
                        routes, err = netlink.RouteList(nic, netlink.FAMILY_V6)
×
589
                }
×
590
                if err != nil {
×
591
                        return nil, err
×
592
                }
×
593
                existRoutes = append(existRoutes, routes...)
×
594
        }
595
        return existRoutes, nil
×
596
}
597

598
func routeDiff(nodeNicRoutes, allRoutes []netlink.Route, cidrs, joinCIDR []string, joinIPv4, joinIPv6, gateway string, srcIPv4, srcIPv6 net.IP) (toAdd, toDel []netlink.Route) {
×
599
        // joinIPv6 is not used for now
×
600
        _ = joinIPv6
×
601

×
602
        for _, route := range nodeNicRoutes {
×
603
                if route.Scope == netlink.SCOPE_LINK || route.Dst == nil || route.Dst.IP.IsLinkLocalUnicast() {
×
604
                        continue
×
605
                }
606

607
                found := slices.Contains(cidrs, route.Dst.String())
×
608
                if !found {
×
609
                        toDel = append(toDel, route)
×
610
                }
×
611
                conflict := false
×
612
                for _, ar := range allRoutes {
×
613
                        if ar.Dst != nil && ar.Dst.String() == route.Dst.String() && ar.LinkIndex != route.LinkIndex {
×
614
                                // route conflict
×
615
                                conflict = true
×
616
                                break
×
617
                        }
618
                }
619
                if conflict {
×
620
                        toDel = append(toDel, route)
×
621
                }
×
622
        }
623
        if len(toDel) > 0 {
×
624
                klog.Infof("routes to delete: %v", toDel)
×
625
        }
×
626

627
        ipv4, ipv6 := util.SplitStringIP(gateway)
×
628
        gwV4, gwV6 := net.ParseIP(ipv4), net.ParseIP(ipv6)
×
629
        for _, c := range cidrs {
×
630
                var src, gw net.IP
×
631
                switch util.CheckProtocol(c) {
×
632
                case kubeovnv1.ProtocolIPv4:
×
633
                        src, gw = srcIPv4, gwV4
×
634
                case kubeovnv1.ProtocolIPv6:
×
635
                        src, gw = srcIPv6, gwV6
×
636
                }
637

638
                found := false
×
639
                for _, ar := range allRoutes {
×
640
                        if ar.Dst != nil && ar.Dst.String() == c {
×
641
                                if slices.Contains(joinCIDR, c) {
×
642
                                        // Only compare Dst for join subnets
×
643
                                        found = true
×
644
                                        klog.V(3).Infof("[routeDiff] joinCIDR route already exists in allRoutes: %v", ar)
×
645
                                        break
×
646
                                } else if (ar.Src == nil && src == nil) || (ar.Src != nil && src != nil && ar.Src.Equal(src)) {
×
647
                                        // For non-join subnets, both Dst and Src must be the same
×
648
                                        found = true
×
649
                                        klog.V(3).Infof("[routeDiff] route already exists in allRoutes: %v", ar)
×
650
                                        break
×
651
                                }
652
                        }
653
                }
654
                if found {
×
655
                        continue
×
656
                }
657
                for _, r := range nodeNicRoutes {
×
658
                        if r.Dst == nil || r.Dst.String() != c {
×
659
                                continue
×
660
                        }
661
                        if (src == nil && r.Src == nil) || (src != nil && r.Src != nil && src.Equal(r.Src)) {
×
662
                                found = true
×
663
                                break
×
664
                        }
665
                }
666
                if !found {
×
667
                        var priority int
×
668
                        scope := netlink.SCOPE_UNIVERSE
×
669
                        proto := netlink.RouteProtocol(syscall.RTPROT_STATIC)
×
670
                        if slices.Contains(joinCIDR, c) {
×
671
                                if util.CheckProtocol(c) == kubeovnv1.ProtocolIPv4 {
×
672
                                        src = net.ParseIP(joinIPv4)
×
673
                                } else {
×
674
                                        src, priority = nil, 256
×
675
                                }
×
676
                                gw, scope = nil, netlink.SCOPE_LINK
×
677
                                proto = netlink.RouteProtocol(unix.RTPROT_KERNEL)
×
678
                        }
679
                        _, cidr, _ := net.ParseCIDR(c)
×
680
                        toAdd = append(toAdd, netlink.Route{
×
681
                                Dst:      cidr,
×
682
                                Src:      src,
×
683
                                Gw:       gw,
×
684
                                Protocol: proto,
×
685
                                Scope:    scope,
×
686
                                Priority: priority,
×
687
                        })
×
688
                }
689
        }
690
        if len(toAdd) > 0 {
×
691
                klog.Infof("routes to add: %v", toAdd)
×
692
        }
×
693
        return toAdd, toDel
×
694
}
695

696
func getRulesToAdd(oldRules, newRules []netlink.Rule) []netlink.Rule {
×
697
        var toAdd []netlink.Rule
×
698

×
699
        for _, rule := range newRules {
×
700
                var found bool
×
701
                for _, r := range oldRules {
×
702
                        if r.Family == rule.Family && r.Priority == rule.Priority && r.Table == rule.Table && reflect.DeepEqual(r.Src, rule.Src) {
×
703
                                found = true
×
704
                                break
×
705
                        }
706
                }
707
                if !found {
×
708
                        toAdd = append(toAdd, rule)
×
709
                }
×
710
        }
711

712
        return toAdd
×
713
}
714

715
func getRoutesToAdd(oldRoutes, newRoutes []netlink.Route) []netlink.Route {
×
716
        var toAdd []netlink.Route
×
717

×
718
        for _, route := range newRoutes {
×
719
                var found bool
×
720
                for _, r := range oldRoutes {
×
721
                        if r.Equal(route) {
×
722
                                found = true
×
723
                                break
×
724
                        }
725
                }
726
                if !found {
×
727
                        toAdd = append(toAdd, route)
×
728
                }
×
729
        }
730

731
        return toAdd
×
732
}
733

734
func (c *Controller) diffPolicyRouting(oldSubnet, newSubnet *kubeovnv1.Subnet) (rulesToAdd, rulesToDel []netlink.Rule, routesToAdd, routesToDel []netlink.Route, err error) {
×
735
        oldRules, oldRoutes, err := c.getPolicyRouting(oldSubnet)
×
736
        if err != nil {
×
737
                klog.Error(err)
×
738
                return rulesToAdd, rulesToDel, routesToAdd, routesToDel, err
×
739
        }
×
740
        newRules, newRoutes, err := c.getPolicyRouting(newSubnet)
×
741
        if err != nil {
×
742
                klog.Error(err)
×
743
                return rulesToAdd, rulesToDel, routesToAdd, routesToDel, err
×
744
        }
×
745

746
        rulesToAdd = getRulesToAdd(oldRules, newRules)
×
747
        rulesToDel = getRulesToAdd(newRules, oldRules)
×
748
        routesToAdd = getRoutesToAdd(oldRoutes, newRoutes)
×
749
        routesToDel = getRoutesToAdd(newRoutes, oldRoutes)
×
750

×
751
        return rulesToAdd, rulesToDel, routesToAdd, routesToDel, err
×
752
}
753

754
func (c *Controller) getPolicyRouting(subnet *kubeovnv1.Subnet) ([]netlink.Rule, []netlink.Route, error) {
1✔
755
        if subnet == nil || subnet.Spec.ExternalEgressGateway == "" || subnet.Spec.Vpc != c.config.ClusterRouter {
2✔
756
                return nil, nil, nil
1✔
757
        }
1✔
758
        if subnet.Spec.GatewayType == kubeovnv1.GWCentralizedType {
2✔
759
                node, err := c.nodesLister.Get(c.config.NodeName)
1✔
760
                if err != nil {
1✔
761
                        klog.Errorf("failed to get node %s: %v", c.config.NodeName, err)
×
762
                        return nil, nil, err
×
763
                }
×
764
                isGatewayNode := util.GatewayContains(subnet.Spec.GatewayNode, c.config.NodeName) ||
1✔
765
                        (subnet.Spec.GatewayNode == "" && util.MatchLabelSelectors(subnet.Spec.GatewayNodeSelectors, node.Labels))
1✔
766
                if !isGatewayNode {
1✔
767
                        return nil, nil, nil
×
768
                }
×
769
        }
770

771
        protocols := make([]string, 1, 2)
1✔
772
        if protocol := util.CheckProtocol(subnet.Spec.ExternalEgressGateway); protocol == kubeovnv1.ProtocolDual {
2✔
773
                protocols[0] = kubeovnv1.ProtocolIPv4
1✔
774
                protocols = append(protocols, kubeovnv1.ProtocolIPv6)
1✔
775
        } else {
2✔
776
                protocols[0] = protocol
1✔
777
        }
1✔
778

779
        cidr := strings.Split(subnet.Spec.CIDRBlock, ",")
1✔
780
        egw := util.SplitTrimmed(subnet.Spec.ExternalEgressGateway, ",")
1✔
781
        if len(egw) == 0 {
1✔
782
                return nil, nil, nil
×
783
        }
×
784

785
        // rules
786
        var rules []netlink.Rule
1✔
787
        rule := netlink.NewRule()
1✔
788
        rule.Table = int(subnet.Spec.PolicyRoutingTableID)
1✔
789
        rule.Priority = int(subnet.Spec.PolicyRoutingPriority)
1✔
790
        if subnet.Spec.GatewayType == kubeovnv1.GWDistributedType {
2✔
791
                pods, err := c.podsLister.List(labels.Everything())
1✔
792
                if err != nil {
1✔
793
                        klog.Errorf("list pods failed, %+v", err)
×
794
                        return nil, nil, err
×
795
                }
×
796

797
                for _, pod := range pods {
2✔
798
                        if pod.Status.PodIP == "" ||
1✔
799
                                pod.Annotations[util.LogicalSwitchAnnotation] != subnet.Name {
2✔
800
                                continue
1✔
801
                        }
802

803
                        for i := range protocols {
2✔
804
                                rule.Family, _ = util.ProtocolToFamily(protocols[i])
1✔
805

1✔
806
                                var ip net.IP
1✔
807
                                var maskBits int
1✔
808
                                if len(pod.Status.PodIPs) == 2 && protocols[i] == kubeovnv1.ProtocolIPv6 {
2✔
809
                                        ip = net.ParseIP(pod.Status.PodIPs[1].IP)
1✔
810
                                        maskBits = 128
1✔
811
                                } else if util.CheckProtocol(pod.Status.PodIP) == protocols[i] {
3✔
812
                                        ip = net.ParseIP(pod.Status.PodIP)
1✔
813
                                        maskBits = 32
1✔
814
                                        if rule.Family == netlink.FAMILY_V6 {
2✔
815
                                                maskBits = 128
1✔
816
                                        }
1✔
817
                                }
818

819
                                if ip == nil {
2✔
820
                                        continue
1✔
821
                                }
822
                                rule.Src = &net.IPNet{IP: ip, Mask: net.CIDRMask(maskBits, maskBits)}
1✔
823
                                rules = append(rules, *rule)
1✔
824
                        }
825
                }
826
        } else {
1✔
827
                for i := range protocols {
2✔
828
                        rule.Family, _ = util.ProtocolToFamily(protocols[i])
1✔
829
                        if i >= len(cidr) {
2✔
830
                                continue
1✔
831
                        }
832
                        _, ipNet, err := net.ParseCIDR(cidr[i])
1✔
833
                        if err != nil {
1✔
834
                                klog.Errorf("failed to parse CIDR %q for subnet %s policy routing: %v", cidr[i], subnet.Name, err)
×
835
                                continue
×
836
                        }
837
                        rule.Src = ipNet
1✔
838
                        rules = append(rules, *rule)
1✔
839
                }
840
        }
841

842
        // routes
843
        var routes []netlink.Route
1✔
844
        for i := range protocols {
2✔
845
                routes = append(routes, netlink.Route{
1✔
846
                        Protocol: netlink.RouteProtocol(syscall.RTPROT_STATIC),
1✔
847
                        Table:    int(subnet.Spec.PolicyRoutingTableID),
1✔
848
                        Gw:       net.ParseIP(egw[i]),
1✔
849
                })
1✔
850
        }
1✔
851

852
        return rules, routes, nil
1✔
853
}
854

855
func (c *Controller) handleUpdatePod(key string) error {
×
856
        namespace, name, err := cache.SplitMetaNamespaceKey(key)
×
857
        if err != nil {
×
858
                utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key))
×
859
                return nil
×
860
        }
×
861
        klog.Infof("handle qos update for pod %s/%s", namespace, name)
×
862

×
863
        pod, err := c.podsLister.Pods(namespace).Get(name)
×
864
        if err != nil {
×
865
                if k8serrors.IsNotFound(err) {
×
866
                        return nil
×
867
                }
×
868
                klog.Error(err)
×
869
                return err
×
870
        }
871

872
        if err := util.ValidatePodNetwork(pod.Annotations); err != nil {
×
873
                klog.Errorf("validate pod %s/%s failed, %v", namespace, name, err)
×
874
                c.recorder.Eventf(pod, v1.EventTypeWarning, "ValidatePodNetworkFailed", "%s", err.Error())
×
875
                return err
×
876
        }
×
877

878
        podName := pod.Name
×
879
        if pod.Annotations[fmt.Sprintf(util.VMAnnotationTemplate, util.OvnProvider)] != "" {
×
880
                podName = pod.Annotations[fmt.Sprintf(util.VMAnnotationTemplate, util.OvnProvider)]
×
881
        }
×
882

883
        // set default nic bandwidth
884
        //  ovsIngress and ovsEgress are derived from the pod's egress and ingress rate annotations respectively, their roles are reversed from the OVS interface perspective.
885
        ifaceID := ovs.PodNameToPortName(podName, pod.Namespace, util.OvnProvider)
×
886
        ovsIngress := pod.Annotations[util.EgressRateAnnotation]
×
887
        ovsEgress := pod.Annotations[util.IngressRateAnnotation]
×
888
        ovsIngressBurst := pod.Annotations[util.EgressBurstAnnotation]
×
889
        ovsEgressBurst := pod.Annotations[util.IngressBurstAnnotation]
×
890
        err = ovs.SetInterfaceBandwidth(podName, pod.Namespace, ifaceID, ovsIngress, ovsEgress, ovsIngressBurst, ovsEgressBurst)
×
891
        if err != nil {
×
892
                klog.Error(err)
×
893
                return err
×
894
        }
×
895
        err = ovs.ConfigInterfaceMirror(c.config.EnableMirror, pod.Annotations[util.MirrorControlAnnotation], ifaceID)
×
896
        if err != nil {
×
897
                klog.Error(err)
×
898
                return err
×
899
        }
×
900
        // set linux-netem qos
901
        err = ovs.SetNetemQos(podName, pod.Namespace, ifaceID, pod.Annotations[util.NetemQosLatencyAnnotation], pod.Annotations[util.NetemQosLimitAnnotation], pod.Annotations[util.NetemQosLossAnnotation], pod.Annotations[util.NetemQosJitterAnnotation])
×
902
        if err != nil {
×
903
                klog.Error(err)
×
904
                return err
×
905
        }
×
906

907
        // set multus-nic bandwidth
908
        attachNets, err := nadutils.ParsePodNetworkAnnotation(pod)
×
909
        if err != nil {
×
910
                if _, ok := err.(*nadv1.NoK8sNetworkError); ok {
×
911
                        return nil
×
912
                }
×
913
                klog.Error(err)
×
914
                return err
×
915
        }
916
        for _, multiNet := range attachNets {
×
917
                provider := fmt.Sprintf("%s.%s.%s", multiNet.Name, multiNet.Namespace, util.OvnProvider)
×
918
                if pod.Annotations[fmt.Sprintf(util.VMAnnotationTemplate, provider)] != "" {
×
919
                        podName = pod.Annotations[fmt.Sprintf(util.VMAnnotationTemplate, provider)]
×
920
                }
×
921
                if pod.Annotations[fmt.Sprintf(util.AllocatedAnnotationTemplate, provider)] == "true" {
×
922
                        ifaceID = ovs.PodNameToPortName(podName, pod.Namespace, provider)
×
923

×
924
                        err = ovs.SetInterfaceBandwidth(podName, pod.Namespace, ifaceID,
×
925
                                pod.Annotations[fmt.Sprintf(util.EgressRateAnnotationTemplate, provider)],
×
926
                                pod.Annotations[fmt.Sprintf(util.IngressRateAnnotationTemplate, provider)],
×
927
                                pod.Annotations[fmt.Sprintf(util.EgressBurstAnnotationTemplate, provider)],
×
928
                                pod.Annotations[fmt.Sprintf(util.IngressBurstAnnotationTemplate, provider)])
×
929
                        if err != nil {
×
930
                                klog.Error(err)
×
931
                                return err
×
932
                        }
×
933
                        err = ovs.ConfigInterfaceMirror(c.config.EnableMirror, pod.Annotations[fmt.Sprintf(util.MirrorControlAnnotationTemplate, provider)], ifaceID)
×
934
                        if err != nil {
×
935
                                klog.Error(err)
×
936
                                return err
×
937
                        }
×
938
                        err = ovs.SetNetemQos(podName, pod.Namespace, ifaceID, pod.Annotations[fmt.Sprintf(util.NetemQosLatencyAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLimitAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLossAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosJitterAnnotationTemplate, provider)])
×
939
                        if err != nil {
×
940
                                klog.Error(err)
×
941
                                return err
×
942
                        }
×
943
                }
944
        }
945

946
        return nil
×
947
}
948

949
func (c *Controller) loopEncapIPCheck() {
×
950
        node, err := c.nodesLister.Get(c.config.NodeName)
×
951
        if err != nil {
×
952
                klog.Errorf("failed to get node %s %v", c.config.NodeName, err)
×
953
                return
×
954
        }
×
955

956
        if nodeTunnelName := node.GetAnnotations()[util.TunnelInterfaceAnnotation]; nodeTunnelName != "" {
×
957
                iface, err := findInterface(nodeTunnelName)
×
958
                if err != nil {
×
959
                        klog.Errorf("failed to find iface %s, %v", nodeTunnelName, err)
×
960
                        return
×
961
                }
×
962
                if iface.Flags&net.FlagUp == 0 {
×
963
                        klog.Errorf("iface %v is down", nodeTunnelName)
×
964
                        return
×
965
                }
×
966
                addrs, err := iface.Addrs()
×
967
                if err != nil {
×
968
                        klog.Errorf("failed to get iface addr. %v", err)
×
969
                        return
×
970
                }
×
971
                if len(addrs) == 0 {
×
972
                        klog.Errorf("iface %s has no ip address", nodeTunnelName)
×
973
                        return
×
974
                }
×
975
                if iface.Name != c.config.tunnelIface {
×
976
                        klog.Infof("use %s as tunnel interface", iface.Name)
×
977
                        c.config.tunnelIface = iface.Name
×
978
                }
×
979

980
                // if assigned iface in node annotation is down or with no ip, the error msg should be printed periodically
981
                if c.config.Iface == nodeTunnelName {
×
982
                        klog.V(3).Infof("node tunnel interface %s not changed", nodeTunnelName)
×
983
                        return
×
984
                }
×
985

986
                var encapIP string
×
987
                for _, addr := range addrs {
×
988
                        ipStr, _, _ := strings.Cut(addr.String(), "/")
×
989
                        if ip := net.ParseIP(ipStr); ip == nil || ip.IsLinkLocalUnicast() || ip.IsLoopback() {
×
990
                                continue
×
991
                        }
992
                        encapIP = ipStr
×
993
                        break
×
994
                }
995
                if encapIP == "" {
×
996
                        klog.Errorf("iface %s has no valid IP address", nodeTunnelName)
×
997
                        return
×
998
                }
×
999

1000
                c.config.Iface = nodeTunnelName
×
1001
                klog.Infof("Update node tunnel interface %v", nodeTunnelName)
×
NEW
1002
                c.config.SetDefaultEncapIP(encapIP)
×
1003
                if err = c.config.setEncapIPs(); err != nil {
×
NEW
1004
                        klog.Errorf("failed to set encap ip %s for iface %s", encapIP, nodeTunnelName)
×
1005
                        return
×
1006
                }
×
1007
        }
1008
}
1009

1010
func (c *Controller) ovnMetricsUpdate() {
×
1011
        c.setOvnSubnetGatewayMetric()
×
1012

×
1013
        resetSysParaMetrics()
×
1014
        c.setIPLocalPortRangeMetric()
×
1015
        c.setCheckSumErrMetric()
×
1016
        c.setDNSSearchMetric()
×
1017
        c.setTCPTwRecycleMetric()
×
1018
        c.setTCPMtuProbingMetric()
×
1019
        c.setConntrackTCPLiberalMetric()
×
1020
        c.setBridgeNfCallIptablesMetric()
×
1021
        c.setIPv6RouteMaxsizeMetric()
×
1022
        c.setTCPMemMetric()
×
1023
}
×
1024

1025
func resetSysParaMetrics() {
×
1026
        metricIPLocalPortRange.Reset()
×
1027
        metricCheckSumErr.Reset()
×
1028
        metricDNSSearch.Reset()
×
1029
        metricTCPTwRecycle.Reset()
×
1030
        metricTCPMtuProbing.Reset()
×
1031
        metricConntrackTCPLiberal.Reset()
×
1032
        metricBridgeNfCallIptables.Reset()
×
1033
        metricTCPMem.Reset()
×
1034
        metricIPv6RouteMaxsize.Reset()
×
1035
}
×
1036

1037
func rotateLog() {
×
1038
        output, err := exec.Command("logrotate", "/etc/logrotate.d/openvswitch").CombinedOutput()
×
1039
        if err != nil {
×
1040
                klog.Errorf("failed to rotate openvswitch log %q", output)
×
1041
        }
×
1042
        output, err = exec.Command("logrotate", "/etc/logrotate.d/ovn").CombinedOutput()
×
1043
        if err != nil {
×
1044
                klog.Errorf("failed to rotate ovn log %q", output)
×
1045
        }
×
1046
        output, err = exec.Command("logrotate", "/etc/logrotate.d/kubeovn").CombinedOutput()
×
1047
        if err != nil {
×
1048
                klog.Errorf("failed to rotate kube-ovn log %q", output)
×
1049
        }
×
1050
}
1051

1052
func kernelModuleLoaded(module string) (bool, error) {
×
1053
        data, err := os.ReadFile("/proc/modules")
×
1054
        if err != nil {
×
1055
                klog.Errorf("failed to read /proc/modules: %v", err)
×
1056
                return false, err
×
1057
        }
×
1058

1059
        for line := range strings.SplitSeq(string(data), "\n") {
×
1060
                if fields := strings.Fields(line); len(fields) != 0 && fields[0] == module {
×
1061
                        return true, nil
×
1062
                }
×
1063
        }
1064

1065
        return false, nil
×
1066
}
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