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

kubeovn / kube-ovn / 24283852894

11 Apr 2026 01:50PM UTC coverage: 24.521% (-0.007%) from 24.528%
24283852894

push

github

web-flow
fix(underlay): wait for localnet patch port before IPv6 DAD (#6621)

* fix(underlay): wait for localnet patch port before IPv6 DAD

In underlay subnets, the kernel IPv6 DAD may run before the localnet
patch port (created asynchronously by ovn-controller) is ready. Without
L2 connectivity to the physical network, the DAD Neighbor Solicitation
packets are silently dropped, causing DAD to falsely succeed and miss
duplicate address conflicts.

Wait for the localnet patch port to appear in OVS after the pod port
is ready (ovn-installed=true) but before configuring the container NIC.
This ensures L2 connectivity is established when the kernel starts DAD.

The fix only applies to underlay subnets (has VLAN, non-logical gateway);
overlay subnets are not affected.

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

* fix(underlay): improve error message in waitForLocalnetPatchPort

Use "failed waiting" instead of "timeout waiting" since the error may
not always be a timeout (e.g. OVSDB connectivity issues).

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

---------

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

0 of 19 new or added lines in 2 files covered. (0.0%)

2 existing lines in 1 file now uncovered.

13798 of 56270 relevant lines covered (24.52%)

0.29 hits per line

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

0.0
/pkg/daemon/handler.go
1
package daemon
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "fmt"
7
        "net"
8
        "net/http"
9
        "strconv"
10
        "strings"
11
        "time"
12

13
        "github.com/emicklei/go-restful/v3"
14
        v1 "k8s.io/api/core/v1"
15
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
16
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
        "k8s.io/apimachinery/pkg/labels"
18
        "k8s.io/client-go/kubernetes"
19
        "k8s.io/klog/v2"
20
        kubevirtv1 "kubevirt.io/api/core/v1"
21

22
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
23
        clientset "github.com/kubeovn/kube-ovn/pkg/client/clientset/versioned"
24
        "github.com/kubeovn/kube-ovn/pkg/ovs"
25
        "github.com/kubeovn/kube-ovn/pkg/request"
26
        "github.com/kubeovn/kube-ovn/pkg/util"
27
)
28

29
const (
30
        gatewayCheckModeDisabled = iota
31
        gatewayCheckModePing
32
        gatewayCheckModeArping
33
        gatewayCheckModePingNotConcerned
34
        gatewayCheckModeArpingNotConcerned
35
)
36

37
type cniServerHandler struct {
38
        Config        *Configuration
39
        KubeClient    kubernetes.Interface
40
        KubeOvnClient clientset.Interface
41
        Controller    *Controller
42
}
43

44
func createCniServerHandler(config *Configuration, controller *Controller) *cniServerHandler {
×
45
        csh := &cniServerHandler{KubeClient: config.KubeClient, KubeOvnClient: config.KubeOvnClient, Config: config, Controller: controller}
×
46
        return csh
×
47
}
×
48

49
func (csh cniServerHandler) providerExists(provider string) (*kubeovnv1.Subnet, bool) {
×
50
        if util.IsOvnProvider(provider) {
×
51
                return nil, true
×
52
        }
×
53
        subnets, _ := csh.Controller.subnetsLister.List(labels.Everything())
×
54
        for _, subnet := range subnets {
×
55
                if subnet.Spec.Provider == provider {
×
56
                        return subnet.DeepCopy(), true
×
57
                }
×
58
        }
59
        return nil, false
×
60
}
61

62
func (csh cniServerHandler) handleAdd(req *restful.Request, resp *restful.Response) {
×
63
        podRequest := request.CniRequest{}
×
64
        if err := req.ReadEntity(&podRequest); err != nil {
×
65
                errMsg := fmt.Errorf("parse add request failed %w", err)
×
66
                klog.Error(errMsg)
×
67
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
68
                        klog.Errorf("failed to write response, %v", err)
×
69
                }
×
70
                return
×
71
        }
72
        klog.V(5).Infof("request body is %v", podRequest)
×
73
        podSubnet, exist := csh.providerExists(podRequest.Provider)
×
74
        if !exist {
×
75
                errMsg := fmt.Errorf("provider %s not bind to any subnet", podRequest.Provider)
×
76
                klog.Error(errMsg)
×
77
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
78
                        klog.Errorf("failed to write response, %v", err)
×
79
                }
×
80
                return
×
81
        }
82

83
        klog.Infof("add port request: %v", podRequest)
×
84
        if err := csh.validatePodRequest(&podRequest); err != nil {
×
85
                klog.Error(err)
×
86
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: err.Error()}); err != nil {
×
87
                        klog.Errorf("failed to write response, %v", err)
×
88
                }
×
89
                return
×
90
        }
91

92
        var gatewayCheckMode int
×
93
        var macAddr, ip, ipAddr, cidr, gw, subnet, ingress, egress, providerNetwork, ifName, nicType, podNicName, vmName, latency, limit, loss, jitter, u2oInterconnectionIP, oldPodName string
×
94
        var routes []request.Route
×
95
        var isDefaultRoute, noIPAM bool
×
96
        var pod *v1.Pod
×
97
        var err error
×
98
        for range 20 {
×
99
                if pod, err = csh.Controller.podsLister.Pods(podRequest.PodNamespace).Get(podRequest.PodName); err != nil {
×
100
                        errMsg := fmt.Errorf("get pod %s/%s failed %w", podRequest.PodNamespace, podRequest.PodName, err)
×
101
                        klog.Error(errMsg)
×
102
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
103
                                klog.Errorf("failed to write response, %v", err)
×
104
                        }
×
105
                        return
×
106
                }
107
                if pod.Annotations[fmt.Sprintf(util.AllocatedAnnotationTemplate, podRequest.Provider)] != "true" {
×
108
                        klog.Infof("wait address for pod %s/%s provider %s", podRequest.PodNamespace, podRequest.PodName, podRequest.Provider)
×
109
                        // wait controller assign an address
×
110
                        cniWaitAddressResult.WithLabelValues(nodeName).Inc()
×
111
                        time.Sleep(1 * time.Second)
×
112
                        continue
×
113
                }
114

115
                if err := util.ValidatePodNetwork(pod.Annotations); err != nil {
×
116
                        klog.Errorf("validate pod %s/%s failed, %v", podRequest.PodNamespace, podRequest.PodName, err)
×
117
                        // wait controller assign an address
×
118
                        cniWaitAddressResult.WithLabelValues(nodeName).Inc()
×
119
                        time.Sleep(1 * time.Second)
×
120
                        continue
×
121
                }
122
                ip = pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, podRequest.Provider)]
×
123
                cidr = pod.Annotations[fmt.Sprintf(util.CidrAnnotationTemplate, podRequest.Provider)]
×
124
                gw = pod.Annotations[fmt.Sprintf(util.GatewayAnnotationTemplate, podRequest.Provider)]
×
125
                subnet = pod.Annotations[fmt.Sprintf(util.LogicalSwitchAnnotationTemplate, podRequest.Provider)]
×
126
                ingress = pod.Annotations[fmt.Sprintf(util.IngressRateAnnotationTemplate, podRequest.Provider)]
×
127
                egress = pod.Annotations[fmt.Sprintf(util.EgressRateAnnotationTemplate, podRequest.Provider)]
×
128
                latency = pod.Annotations[fmt.Sprintf(util.NetemQosLatencyAnnotationTemplate, podRequest.Provider)]
×
129
                limit = pod.Annotations[fmt.Sprintf(util.NetemQosLimitAnnotationTemplate, podRequest.Provider)]
×
130
                loss = pod.Annotations[fmt.Sprintf(util.NetemQosLossAnnotationTemplate, podRequest.Provider)]
×
131
                jitter = pod.Annotations[fmt.Sprintf(util.NetemQosJitterAnnotationTemplate, podRequest.Provider)]
×
132
                providerNetwork = pod.Annotations[fmt.Sprintf(util.ProviderNetworkTemplate, podRequest.Provider)]
×
133
                vmName = pod.Annotations[fmt.Sprintf(util.VMAnnotationTemplate, podRequest.Provider)]
×
134
                ipAddr, noIPAM, err = util.GetIPAddrWithMaskForCNI(ip, cidr)
×
135
                if err != nil {
×
136
                        errMsg := fmt.Errorf("failed to get ip address with mask, %w", err)
×
137
                        klog.Error(errMsg)
×
138
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
139
                                klog.Errorf("failed to write response, %v", err)
×
140
                        }
×
141
                        return
×
142
                }
143
                oldPodName = podRequest.PodName
×
144
                if s := pod.Annotations[fmt.Sprintf(util.RoutesAnnotationTemplate, podRequest.Provider)]; s != "" {
×
145
                        if err = json.Unmarshal([]byte(s), &routes); err != nil {
×
146
                                errMsg := fmt.Errorf("invalid routes for pod %s/%s: %w", pod.Namespace, pod.Name, err)
×
147
                                klog.Error(errMsg)
×
148
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
149
                                        klog.Errorf("failed to write response: %v", err)
×
150
                                }
×
151
                                return
×
152
                        }
153
                }
154
                if ifName = podRequest.IfName; ifName == "" {
×
155
                        ifName = "eth0"
×
156
                }
×
157

158
                // To support KubeVirt hotplug dpdk nic, forbidden set the volume name
159
                if podRequest.VhostUserSocketConsumption == util.ConsumptionKubevirt {
×
160
                        podRequest.VhostUserSocketVolumeName = util.VhostUserSocketVolumeName
×
161
                }
×
162

163
                switch {
×
164
                case podRequest.DeviceID != "":
×
165
                        nicType = util.OffloadType
×
166
                case podRequest.VhostUserSocketVolumeName != "":
×
167
                        nicType = util.DpdkType
×
168
                        if err = createShortSharedDir(pod, podRequest.VhostUserSocketVolumeName, podRequest.VhostUserSocketConsumption, csh.Config.KubeletDir); err != nil {
×
169
                                klog.Error(err.Error())
×
170
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
171
                                        klog.Errorf("failed to write response: %v", err)
×
172
                                }
×
173
                                return
×
174
                        }
175
                default:
×
176
                        nicType = pod.Annotations[fmt.Sprintf(util.PodNicAnnotationTemplate, podRequest.Provider)]
×
177
                }
178

179
                switch pod.Annotations[fmt.Sprintf(util.DefaultRouteAnnotationTemplate, podRequest.Provider)] {
×
180
                case "true":
×
181
                        isDefaultRoute = true
×
182
                case "false":
×
183
                        isDefaultRoute = false
×
184
                default:
×
185
                        isDefaultRoute = ifName == "eth0"
×
186
                }
187

188
                if isDefaultRoute && pod.Annotations[fmt.Sprintf(util.RoutedAnnotationTemplate, podRequest.Provider)] != "true" && util.IsOvnProvider(podRequest.Provider) {
×
189
                        klog.Infof("wait route ready for pod %s/%s provider %s", podRequest.PodNamespace, podRequest.PodName, podRequest.Provider)
×
190
                        cniWaitRouteResult.WithLabelValues(nodeName).Inc()
×
191
                        time.Sleep(1 * time.Second)
×
192
                        continue
×
193
                }
194

195
                if vmName != "" {
×
196
                        podRequest.PodName = vmName
×
197
                }
×
198

199
                break
×
200
        }
201

202
        if pod.Annotations[fmt.Sprintf(util.AllocatedAnnotationTemplate, podRequest.Provider)] != "true" {
×
203
                err := fmt.Errorf("no address allocated to pod %s/%s provider %s, please see kube-ovn-controller logs to find errors", pod.Namespace, pod.Name, podRequest.Provider)
×
204
                klog.Error(err)
×
205
                if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
206
                        klog.Errorf("failed to write response, %v", err)
×
207
                }
×
208
                return
×
209
        }
210

211
        if subnet == "" && podSubnet != nil {
×
212
                subnet = podSubnet.Name
×
213
        }
×
214
        if !noIPAM {
×
215
                if err := csh.UpdateIPCR(podRequest, subnet, ip); err != nil {
×
216
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
217
                                klog.Errorf("failed to write response, %v", err)
×
218
                        }
×
219
                        return
×
220
                }
221
        }
222

223
        if isDefaultRoute && pod.Annotations[fmt.Sprintf(util.RoutedAnnotationTemplate, podRequest.Provider)] != "true" && util.IsOvnProvider(podRequest.Provider) {
×
224
                err := fmt.Errorf("route is not ready for pod %s/%s provider %s, please see kube-ovn-controller logs to find errors", pod.Namespace, pod.Name, podRequest.Provider)
×
225
                klog.Error(err)
×
226
                if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
227
                        klog.Errorf("failed to write response, %v", err)
×
228
                }
×
229
                return
×
230
        }
231

232
        var mtu int
×
233
        routes = append(podRequest.Routes, routes...)
×
234
        if strings.HasSuffix(podRequest.Provider, util.OvnProvider) && subnet != "" {
×
235
                podSubnet, err := csh.Controller.subnetsLister.Get(subnet)
×
236
                if err != nil {
×
237
                        errMsg := fmt.Errorf("failed to get subnet %s: %w", subnet, err)
×
238
                        klog.Error(errMsg)
×
239
                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
240
                                klog.Errorf("failed to write response: %v", err)
×
241
                        }
×
242
                        return
×
243
                }
244

245
                if podSubnet.Status.U2OInterconnectionIP == "" && podSubnet.Spec.U2OInterconnection {
×
246
                        errMsg := fmt.Errorf("failed to generate u2o ip on subnet %s", podSubnet.Name)
×
247
                        klog.Error(errMsg)
×
248
                        return
×
249
                }
×
250

251
                if podSubnet.Status.U2OInterconnectionIP != "" && podSubnet.Spec.U2OInterconnection {
×
252
                        u2oInterconnectionIP = podSubnet.Status.U2OInterconnectionIP
×
253
                }
×
254

255
                var vmMigration bool
×
256
                subnetHasVlan := podSubnet.Spec.Vlan != ""
×
257
                // skip ping check gateway for pods during live migration
×
258
                if pod.Annotations[kubevirtv1.MigrationJobNameAnnotation] == "" {
×
259
                        if subnetHasVlan && !podSubnet.Spec.LogicalGateway {
×
260
                                if podSubnet.Spec.DisableGatewayCheck {
×
261
                                        gatewayCheckMode = gatewayCheckModeArpingNotConcerned
×
262
                                } else {
×
263
                                        gatewayCheckMode = gatewayCheckModeArping
×
264
                                }
×
265
                        } else {
×
266
                                if podSubnet.Spec.DisableGatewayCheck {
×
267
                                        gatewayCheckMode = gatewayCheckModePingNotConcerned
×
268
                                } else {
×
269
                                        gatewayCheckMode = gatewayCheckModePing
×
270
                                }
×
271
                        }
272
                } else {
×
273
                        vmMigration = true
×
274
                }
×
275
                if pod.Annotations[fmt.Sprintf(util.ActivationStrategyTemplate, podRequest.Provider)] != "" {
×
276
                        gatewayCheckMode = gatewayCheckModeDisabled
×
277
                }
×
278

279
                if podSubnet.Spec.Mtu > 0 {
×
280
                        mtu = int(podSubnet.Spec.Mtu)
×
281
                } else {
×
282
                        if providerNetwork != "" && !podSubnet.Spec.LogicalGateway && !podSubnet.Spec.U2OInterconnection {
×
283
                                node, err := csh.Controller.nodesLister.Get(csh.Config.NodeName)
×
284
                                if err != nil {
×
285
                                        errMsg := fmt.Errorf("failed to get node %s: %w", csh.Config.NodeName, err)
×
286
                                        klog.Error(errMsg)
×
287
                                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
288
                                                klog.Errorf("failed to write response: %v", err)
×
289
                                        }
×
290
                                        return
×
291
                                }
292
                                mtuStr := node.Labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, providerNetwork)]
×
293
                                if mtuStr != "" {
×
294
                                        if mtu, err = strconv.Atoi(mtuStr); err != nil || mtu <= 0 {
×
295
                                                errMsg := fmt.Errorf("failed to parse provider network MTU %s: %w", mtuStr, err)
×
296
                                                klog.Error(errMsg)
×
297
                                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
298
                                                        klog.Errorf("failed to write response: %v", err)
×
299
                                                }
×
300
                                                return
×
301
                                        }
302
                                }
303
                        } else {
×
304
                                mtu = csh.Config.MTU
×
305
                        }
×
306
                }
307

308
                macAddr = pod.Annotations[fmt.Sprintf(util.MacAddressAnnotationTemplate, podRequest.Provider)]
×
309
                klog.Infof("create container interface %s mac %s, ip %s, cidr %s, gw %s, custom routes %v", ifName, macAddr, ipAddr, cidr, gw, routes)
×
310
                podNicName = ifName
×
311

×
312
                var encapIP string
×
313
                if podSubnet.Spec.NodeNetwork != "" {
×
314
                        encapIP, err = csh.Config.GetEncapIPByNetwork(podSubnet.Spec.NodeNetwork)
×
315
                        if err != nil {
×
316
                                errMsg := fmt.Errorf("failed to get encap IP for node network %s: %w", podSubnet.Spec.NodeNetwork, err)
×
317
                                klog.Error(errMsg)
×
318
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
319
                                        klog.Errorf("failed to write response: %v", err)
×
320
                                }
×
321
                                return
×
322
                        }
323
                }
324

NEW
325
                var localnetSubnet string
×
NEW
326
                if subnetHasVlan && !podSubnet.Spec.LogicalGateway {
×
NEW
327
                        localnetSubnet = subnet
×
NEW
328
                }
×
329

330
                switch nicType {
×
331
                case util.DpdkType:
×
332
                        err = csh.configureDpdkNic(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider, podRequest.NetNs, podRequest.ContainerID, ifName, macAddr, mtu, ipAddr, gw, ingress, egress, getShortSharedDir(pod.UID, podRequest.VhostUserSocketVolumeName), podRequest.VhostUserSocketName, podRequest.VhostUserSocketConsumption)
×
333
                        routes = nil
×
334
                default:
×
NEW
335
                        routes, err = csh.configureNic(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider, podRequest.NetNs, podRequest.ContainerID, podRequest.VfDriver, ifName, macAddr, mtu, ipAddr, gw, isDefaultRoute, vmMigration, routes, podRequest.DNS.Nameservers, podRequest.DNS.Search, ingress, egress, podRequest.DeviceID, latency, limit, loss, jitter, gatewayCheckMode, u2oInterconnectionIP, oldPodName, encapIP, localnetSubnet)
×
336
                }
337
                if err != nil {
×
338
                        errMsg := fmt.Errorf("configure nic %s for pod %s/%s failed: %w", ifName, podRequest.PodName, podRequest.PodNamespace, err)
×
339
                        klog.Error(errMsg)
×
340
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
341
                                klog.Errorf("failed to write response, %v", err)
×
342
                        }
×
343
                        return
×
344
                }
345

346
                ifaceID := ovs.PodNameToPortName(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider)
×
347
                if err = ovs.ConfigInterfaceMirror(csh.Config.EnableMirror, pod.Annotations[fmt.Sprintf(util.MirrorControlAnnotationTemplate, podRequest.Provider)], ifaceID); err != nil {
×
348
                        klog.Errorf("failed mirror to mirror0, %v", err)
×
349
                        return
×
350
                }
×
351

352
                if err = csh.Controller.addEgressConfig(podSubnet, ip); err != nil {
×
353
                        errMsg := fmt.Errorf("failed to add egress configuration: %w", err)
×
354
                        klog.Error(errMsg)
×
355
                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
356
                                klog.Errorf("failed to write response, %v", err)
×
357
                        }
×
358
                        return
×
359
                }
360
        } else if len(routes) != 0 {
×
361
                hasDefaultRoute := make(map[string]bool, 2)
×
362
                for _, r := range routes {
×
363
                        if r.Destination == "" {
×
364
                                hasDefaultRoute[util.CheckProtocol(r.Gateway)] = true
×
365
                                continue
×
366
                        }
367
                        if _, cidr, err := net.ParseCIDR(r.Destination); err == nil {
×
368
                                if ones, _ := cidr.Mask.Size(); ones == 0 {
×
369
                                        hasDefaultRoute[util.CheckProtocol(r.Gateway)] = true
×
370
                                }
×
371
                        }
372
                }
373
                if len(hasDefaultRoute) != 0 {
×
374
                        // remove existing default route so other CNI plugins, such as macvlan, can add the new default route correctly
×
375
                        if err = csh.removeDefaultRoute(podRequest.NetNs, hasDefaultRoute[kubeovnv1.ProtocolIPv4], hasDefaultRoute[kubeovnv1.ProtocolIPv6]); err != nil {
×
376
                                errMsg := fmt.Errorf("failed to remove existing default route for interface %s of pod %s/%s: %w", podRequest.IfName, podRequest.PodNamespace, podRequest.PodName, err)
×
377
                                klog.Error(errMsg)
×
378
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
379
                                        klog.Errorf("failed to write response: %v", err)
×
380
                                }
×
381
                                return
×
382
                        }
383
                }
384
        }
385

386
        v4IP, v6IP := util.SplitStringIP(ip)
×
387
        v4CIDR, v6CIDR := util.SplitStringIP(cidr)
×
388
        v4GW, v6GW := util.SplitStringIP(gw)
×
389

×
390
        var ips []request.IPConfig
×
391
        if v4IP != "" {
×
392
                cfg := request.IPConfig{Protocol: kubeovnv1.ProtocolIPv4, IP: v4IP, CIDR: v4CIDR}
×
393
                if isDefaultRoute {
×
394
                        cfg.Gateway = v4GW
×
395
                }
×
396
                ips = append(ips, cfg)
×
397
        }
398
        if v6IP != "" {
×
399
                cfg := request.IPConfig{Protocol: kubeovnv1.ProtocolIPv6, IP: v6IP, CIDR: v6CIDR}
×
400
                if isDefaultRoute {
×
401
                        cfg.Gateway = v6GW
×
402
                }
×
403
                ips = append(ips, cfg)
×
404
        }
405

406
        response := &request.CniResponse{
×
407
                IPs:        ips,
×
408
                MacAddress: macAddr,
×
409
                PodNicName: podNicName,
×
410
                Routes:     routes,
×
411
                Mtu:        mtu,
×
412
        }
×
413
        if err := resp.WriteHeaderAndEntity(http.StatusOK, response); err != nil {
×
414
                klog.Errorf("failed to write response, %v", err)
×
415
        }
×
416
}
417

418
func (csh cniServerHandler) UpdateIPCR(podRequest request.CniRequest, subnet, ip string) error {
×
419
        ipCRName := ovs.PodNameToPortName(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider)
×
420
        for range 20 {
×
421
                ipCR, err := csh.KubeOvnClient.KubeovnV1().IPs().Get(context.Background(), ipCRName, metav1.GetOptions{})
×
422
                if err != nil {
×
423
                        err = fmt.Errorf("failed to get ip crd for %s, %w", ip, err)
×
424
                        // maybe create a backup pod with previous annotations
×
425
                        klog.Error(err)
×
426
                } else if ipCR.Spec.NodeName != csh.Config.NodeName {
×
427
                        ipCR := ipCR.DeepCopy()
×
428
                        if ipCR.Labels == nil {
×
429
                                ipCR.Labels = map[string]string{}
×
430
                        }
×
431
                        ipCR.Spec.NodeName = csh.Config.NodeName
×
432
                        ipCR.Spec.AttachIPs = []string{}
×
433
                        ipCR.Labels[subnet] = ""
×
434
                        ipCR.Labels[util.NodeNameLabel] = csh.Config.NodeName
×
435
                        ipCR.Spec.AttachSubnets = []string{}
×
436
                        ipCR.Spec.AttachMacs = []string{}
×
437
                        if _, err := csh.KubeOvnClient.KubeovnV1().IPs().Update(context.Background(), ipCR, metav1.UpdateOptions{}); err != nil {
×
438
                                err = fmt.Errorf("failed to update ip crd for %s, %w", ip, err)
×
439
                                klog.Error(err)
×
440
                        } else {
×
441
                                return nil
×
442
                        }
×
443
                }
444
                if err != nil {
×
445
                        klog.Warningf("wait pod ip %s to be ready", ipCRName)
×
446
                        time.Sleep(1 * time.Second)
×
447
                } else {
×
448
                        return nil
×
449
                }
×
450
        }
451
        // update ip spec node is not that necessary, so we just log the error
452
        return nil
×
453
}
454

455
func (csh cniServerHandler) handleDel(req *restful.Request, resp *restful.Response) {
×
456
        var podRequest request.CniRequest
×
457
        if err := req.ReadEntity(&podRequest); err != nil {
×
458
                errMsg := fmt.Errorf("parse del request failed %w", err)
×
459
                klog.Error(errMsg)
×
460
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
461
                        klog.Errorf("failed to write response, %v", err)
×
462
                }
×
463
                return
×
464
        }
465

466
        // Try to get the Pod, but if it fails due to not being found, log a warning and continue.
467
        pod, err := csh.Controller.podsLister.Pods(podRequest.PodNamespace).Get(podRequest.PodName)
×
468
        if err != nil && !k8serrors.IsNotFound(err) {
×
469
                errMsg := fmt.Errorf("failed to retrieve Pod %s/%s: %w", podRequest.PodNamespace, podRequest.PodName, err)
×
470
                klog.Error(errMsg)
×
471
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
472
                        klog.Errorf("failed to write response, %v", err)
×
473
                }
×
474
                return
×
475
        }
476

477
        if podRequest.NetNs == "" {
×
478
                klog.Infof("skip del port request: %v", podRequest)
×
479
                resp.WriteHeader(http.StatusNoContent)
×
480
                return
×
481
        }
×
482

483
        klog.Infof("del port request: %v", podRequest)
×
484
        if err := csh.validatePodRequest(&podRequest); err != nil {
×
485
                klog.Error(err)
×
486
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: err.Error()}); err != nil {
×
487
                        klog.Errorf("failed to write response, %v", err)
×
488
                }
×
489
                return
×
490
        }
491

492
        var nicType string
×
493
        var vmName string
×
494

×
495
        // If the Pod was found, process its annotations and labels.
×
496
        if pod != nil {
×
497
                if pod.Annotations != nil && (util.IsOvnProvider(podRequest.Provider) || podRequest.CniType == util.CniTypeName) {
×
498
                        subnet := pod.Annotations[fmt.Sprintf(util.LogicalSwitchAnnotationTemplate, podRequest.Provider)]
×
499
                        if subnet != "" {
×
500
                                ip := pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, podRequest.Provider)]
×
501
                                if err = csh.Controller.removeEgressConfig(subnet, ip); err != nil {
×
502
                                        errMsg := fmt.Errorf("failed to remove egress configuration: %w", err)
×
503
                                        klog.Error(errMsg)
×
504
                                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
505
                                                klog.Errorf("failed to write response, %v", err)
×
506
                                        }
×
507
                                        return
×
508
                                }
509
                        }
510

511
                        switch {
×
512
                        case podRequest.DeviceID != "":
×
513
                                nicType = util.OffloadType
×
514
                        case podRequest.VhostUserSocketVolumeName != "":
×
515
                                nicType = util.DpdkType
×
516
                                if err = removeShortSharedDir(pod, podRequest.VhostUserSocketVolumeName, podRequest.VhostUserSocketConsumption); err != nil {
×
517
                                        klog.Error(err.Error())
×
518
                                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
519
                                                klog.Errorf("failed to write response: %v", err)
×
520
                                        }
×
521
                                        return
×
522
                                }
523
                        default:
×
524
                                nicType = pod.Annotations[fmt.Sprintf(util.PodNicAnnotationTemplate, podRequest.Provider)]
×
525
                        }
526

527
                        vmName = pod.Annotations[fmt.Sprintf(util.VMAnnotationTemplate, podRequest.Provider)]
×
528
                        if vmName != "" {
×
529
                                podRequest.PodName = vmName
×
530
                        }
×
531
                }
532
        } else {
×
533
                // If the Pod is not found, assign a default value.
×
534
                klog.Warningf("Pod %s not found, proceeding with NIC deletion using ContainerID and NetNs", podRequest.PodName)
×
535
                switch {
×
536
                case podRequest.DeviceID != "":
×
537
                        nicType = util.OffloadType
×
538
                case podRequest.VhostUserSocketVolumeName != "":
×
539
                        nicType = util.DpdkType
×
540
                default:
×
541
                        nicType = util.VethType
×
542
                }
543
        }
544

545
        // To support KubeVirt hotplug dpdk nic, forbidden set the volume name
546
        if podRequest.VhostUserSocketConsumption == util.ConsumptionKubevirt {
×
547
                podRequest.VhostUserSocketVolumeName = util.VhostUserSocketVolumeName
×
548
        }
×
549

550
        // Proceed to delete the NIC regardless of whether the Pod was found or not.
551
        err = csh.deleteNic(podRequest.PodName, podRequest.PodNamespace, podRequest.ContainerID, podRequest.NetNs, podRequest.DeviceID, podRequest.IfName, nicType)
×
552
        if err != nil {
×
553
                errMsg := fmt.Errorf("del nic failed %w", err)
×
554
                klog.Error(errMsg)
×
555
                if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
556
                        klog.Errorf("failed to write response, %v", err)
×
557
                }
×
558
                return
×
559
        }
560
        resp.WriteHeader(http.StatusNoContent)
×
561
}
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