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

noironetworks / aci-containers / 11911

01 May 2026 10:22AM UTC coverage: 63.014% (+0.01%) from 63.002%
11911

Pull #1724

travis-pro

web-flow
Merge 23c965613 into 360c19205
Pull Request #1724: [mmr-6.1.1] Fix missing return in handleRemIpContUpdate

0 of 1 new or added line in 1 file covered. (0.0%)

2 existing lines in 1 file now uncovered.

13265 of 21051 relevant lines covered (63.01%)

0.72 hits per line

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

78.71
/pkg/controller/network_policy.go
1
// Copyright 2017 Cisco Systems, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
// Handlers for network policy updates.  Generate ACI security groups
16
// based on Kubernetes network policies.
17

18
package controller
19

20
import (
21
        "bytes"
22
        "context"
23
        "fmt"
24
        "maps"
25
        "net"
26
        "os"
27
        "reflect"
28
        "slices"
29
        "sort"
30
        "strconv"
31
        "strings"
32

33
        "github.com/sirupsen/logrus"
34
        "github.com/yl2chen/cidranger"
35

36
        v1 "k8s.io/api/core/v1"
37
        v1net "k8s.io/api/networking/v1"
38
        "k8s.io/apimachinery/pkg/api/errors"
39
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
40
        "k8s.io/apimachinery/pkg/fields"
41
        "k8s.io/apimachinery/pkg/labels"
42
        "k8s.io/apimachinery/pkg/util/intstr"
43
        "k8s.io/client-go/kubernetes"
44
        "k8s.io/client-go/tools/cache"
45
        k8util "k8s.io/kubectl/pkg/util"
46

47
        "github.com/noironetworks/aci-containers/pkg/apicapi"
48
        hppv1 "github.com/noironetworks/aci-containers/pkg/hpp/apis/aci.hpp/v1"
49
        hppclset "github.com/noironetworks/aci-containers/pkg/hpp/clientset/versioned"
50
        "github.com/noironetworks/aci-containers/pkg/index"
51
        "github.com/noironetworks/aci-containers/pkg/ipam"
52
        "github.com/noironetworks/aci-containers/pkg/util"
53
        discovery "k8s.io/api/discovery/v1"
54
)
55

56
func (cont *AciController) initNetworkPolicyInformerFromClient(
57
        kubeClient kubernetes.Interface) {
×
58
        cont.initNetworkPolicyInformerBase(
×
59
                cache.NewListWatchFromClient(
×
60
                        kubeClient.NetworkingV1().RESTClient(), "networkpolicies",
×
61
                        metav1.NamespaceAll, fields.Everything()))
×
62
}
×
63

64
func (cont *AciController) initNetworkPolicyInformerBase(listWatch *cache.ListWatch) {
1✔
65
        cont.networkPolicyIndexer, cont.networkPolicyInformer =
1✔
66
                cache.NewIndexerInformer(
1✔
67
                        listWatch, &v1net.NetworkPolicy{}, 0,
1✔
68
                        cache.ResourceEventHandlerFuncs{
1✔
69
                                AddFunc: func(obj interface{}) {
2✔
70
                                        cont.networkPolicyAdded(obj)
1✔
71
                                },
1✔
72
                                UpdateFunc: func(oldobj interface{}, newobj interface{}) {
×
73
                                        cont.networkPolicyChanged(oldobj, newobj)
×
74
                                },
×
75
                                DeleteFunc: func(obj interface{}) {
1✔
76
                                        cont.networkPolicyDeleted(obj)
1✔
77
                                },
1✔
78
                        },
79
                        cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
80
                )
81
}
82

83
func (cont *AciController) peerPodSelector(np *v1net.NetworkPolicy,
84
        peers []v1net.NetworkPolicyPeer) []index.PodSelector {
1✔
85
        var ret []index.PodSelector
1✔
86
        for _, peer := range peers {
2✔
87
                podselector, err :=
1✔
88
                        metav1.LabelSelectorAsSelector(peer.PodSelector)
1✔
89
                if err != nil {
1✔
90
                        networkPolicyLogger(cont.log, np).
×
91
                                Error("Could not create selector: ", err)
×
92
                        continue
×
93
                }
94
                nsselector, err := metav1.
1✔
95
                        LabelSelectorAsSelector(peer.NamespaceSelector)
1✔
96
                if err != nil {
1✔
97
                        networkPolicyLogger(cont.log, np).
×
98
                                Error("Could not create selector: ", err)
×
99
                        continue
×
100
                }
101

102
                switch {
1✔
103
                case peer.PodSelector != nil && peer.NamespaceSelector != nil:
1✔
104
                        ret = append(ret, index.PodSelector{
1✔
105
                                NsSelector:  nsselector,
1✔
106
                                PodSelector: podselector,
1✔
107
                        })
1✔
108
                case peer.PodSelector != nil:
1✔
109
                        ret = append(ret, index.PodSelector{
1✔
110
                                Namespace:   &np.ObjectMeta.Namespace,
1✔
111
                                PodSelector: podselector,
1✔
112
                        })
1✔
113
                case peer.NamespaceSelector != nil:
1✔
114
                        ret = append(ret, index.PodSelector{
1✔
115
                                NsSelector:  nsselector,
1✔
116
                                PodSelector: labels.Everything(),
1✔
117
                        })
1✔
118
                }
119
        }
120
        return ret
1✔
121
}
122

123
func (cont *AciController) egressPodSelector(np *v1net.NetworkPolicy) []index.PodSelector {
1✔
124
        var ret []index.PodSelector
1✔
125

1✔
126
        for _, egress := range np.Spec.Egress {
2✔
127
                ret = append(ret, cont.peerPodSelector(np, egress.To)...)
1✔
128
        }
1✔
129

130
        return ret
1✔
131
}
132

133
func (cont *AciController) ingressPodSelector(np *v1net.NetworkPolicy) []index.PodSelector {
1✔
134
        var ret []index.PodSelector
1✔
135

1✔
136
        for _, ingress := range np.Spec.Ingress {
2✔
137
                ret = append(ret, cont.peerPodSelector(np, ingress.From)...)
1✔
138
        }
1✔
139

140
        return ret
1✔
141
}
142

143
func (cont *AciController) initNetPolPodIndex() {
1✔
144
        cont.netPolPods = index.NewPodSelectorIndex(
1✔
145
                cont.log,
1✔
146
                cont.podIndexer, cont.namespaceIndexer, cont.networkPolicyIndexer,
1✔
147
                cache.MetaNamespaceKeyFunc,
1✔
148
                func(obj interface{}) []index.PodSelector {
2✔
149
                        np := obj.(*v1net.NetworkPolicy)
1✔
150
                        return index.PodSelectorFromNsAndSelector(np.ObjectMeta.Namespace,
1✔
151
                                &np.Spec.PodSelector)
1✔
152
                },
1✔
153
        )
154
        cont.netPolPods.SetPodUpdateCallback(func(podkey string) {
2✔
155
                podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
156
                if exists && err == nil {
2✔
157
                        cont.queuePodUpdate(podobj.(*v1.Pod))
1✔
158
                }
1✔
159
        })
160

161
        cont.netPolIngressPods = index.NewPodSelectorIndex(
1✔
162
                cont.log,
1✔
163
                cont.podIndexer, cont.namespaceIndexer, cont.networkPolicyIndexer,
1✔
164
                cache.MetaNamespaceKeyFunc,
1✔
165
                func(obj interface{}) []index.PodSelector {
2✔
166
                        return cont.ingressPodSelector(obj.(*v1net.NetworkPolicy))
1✔
167
                },
1✔
168
        )
169
        cont.netPolEgressPods = index.NewPodSelectorIndex(
1✔
170
                cont.log,
1✔
171
                cont.podIndexer, cont.namespaceIndexer, cont.networkPolicyIndexer,
1✔
172
                cache.MetaNamespaceKeyFunc,
1✔
173
                func(obj interface{}) []index.PodSelector {
2✔
174
                        return cont.egressPodSelector(obj.(*v1net.NetworkPolicy))
1✔
175
                },
1✔
176
        )
177
        npupdate := func(npkey string) {
2✔
178
                npobj, exists, err := cont.networkPolicyIndexer.GetByKey(npkey)
1✔
179
                if exists && err == nil {
2✔
180
                        cont.queueNetPolUpdate(npobj.(*v1net.NetworkPolicy))
1✔
181
                }
1✔
182
        }
183
        nphash := func(pod *v1.Pod) string {
2✔
184
                return pod.Status.PodIP
1✔
185
        }
1✔
186

187
        remipupdate := func(pod *v1.Pod, deleted bool) {
2✔
188
                cont.queueRemoteIpConUpdate(pod, deleted)
1✔
189
        }
1✔
190

191
        cont.netPolIngressPods.SetObjUpdateCallback(npupdate)
1✔
192
        cont.netPolIngressPods.SetRemIpUpdateCallback(remipupdate)
1✔
193
        cont.netPolIngressPods.SetPodHashFunc(nphash)
1✔
194
        cont.netPolEgressPods.SetObjUpdateCallback(npupdate)
1✔
195
        cont.netPolEgressPods.SetRemIpUpdateCallback(remipupdate)
1✔
196
        cont.netPolEgressPods.SetPodHashFunc(nphash)
1✔
197
}
198

199
func (cont *AciController) staticNetPolObjs() apicapi.ApicSlice {
1✔
200
        hppIngress :=
1✔
201
                apicapi.NewHostprotPol(cont.config.AciPolicyTenant,
1✔
202
                        cont.aciNameForKey("np", "static-ingress"))
1✔
203
        {
2✔
204
                ingressSubj := apicapi.NewHostprotSubj(hppIngress.GetDn(), "ingress")
1✔
205
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
206
                        outbound := apicapi.NewHostprotRule(ingressSubj.GetDn(),
1✔
207
                                "allow-all-reflexive-v6")
1✔
208
                        outbound.SetAttr("direction", "ingress")
1✔
209
                        outbound.SetAttr("ethertype", "ipv6")
1✔
210
                        ingressSubj.AddChild(outbound)
1✔
211
                }
1✔
212
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
213
                        outbound := apicapi.NewHostprotRule(ingressSubj.GetDn(),
1✔
214
                                "allow-all-reflexive")
1✔
215
                        outbound.SetAttr("direction", "ingress")
1✔
216
                        outbound.SetAttr("ethertype", "ipv4")
1✔
217
                        ingressSubj.AddChild(outbound)
1✔
218
                }
1✔
219
                hppIngress.AddChild(ingressSubj)
1✔
220
        }
221

222
        hppEgress :=
1✔
223
                apicapi.NewHostprotPol(cont.config.AciPolicyTenant,
1✔
224
                        cont.aciNameForKey("np", "static-egress"))
1✔
225
        {
2✔
226
                egressSubj := apicapi.NewHostprotSubj(hppEgress.GetDn(), "egress")
1✔
227
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
228
                        outbound := apicapi.NewHostprotRule(egressSubj.GetDn(),
1✔
229
                                "allow-all-reflexive-v6")
1✔
230
                        outbound.SetAttr("direction", "egress")
1✔
231
                        outbound.SetAttr("ethertype", "ipv6")
1✔
232
                        egressSubj.AddChild(outbound)
1✔
233
                }
1✔
234
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
235
                        outbound := apicapi.NewHostprotRule(egressSubj.GetDn(),
1✔
236
                                "allow-all-reflexive")
1✔
237
                        outbound.SetAttr("direction", "egress")
1✔
238
                        outbound.SetAttr("ethertype", "ipv4")
1✔
239
                        egressSubj.AddChild(outbound)
1✔
240
                }
1✔
241
                hppEgress.AddChild(egressSubj)
1✔
242
        }
243

244
        hppDiscovery :=
1✔
245
                apicapi.NewHostprotPol(cont.config.AciPolicyTenant,
1✔
246
                        cont.aciNameForKey("np", "static-discovery"))
1✔
247
        {
2✔
248
                discSubj := apicapi.NewHostprotSubj(hppDiscovery.GetDn(), "discovery")
1✔
249
                discDn := discSubj.GetDn()
1✔
250
                {
2✔
251
                        arpin := apicapi.NewHostprotRule(discDn, "arp-ingress")
1✔
252
                        arpin.SetAttr("direction", "ingress")
1✔
253
                        arpin.SetAttr("ethertype", "arp")
1✔
254
                        arpin.SetAttr("connTrack", "normal")
1✔
255
                        discSubj.AddChild(arpin)
1✔
256
                }
1✔
257
                {
1✔
258
                        arpout := apicapi.NewHostprotRule(discDn, "arp-egress")
1✔
259
                        arpout.SetAttr("direction", "egress")
1✔
260
                        arpout.SetAttr("ethertype", "arp")
1✔
261
                        arpout.SetAttr("connTrack", "normal")
1✔
262
                        discSubj.AddChild(arpout)
1✔
263
                }
1✔
264
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
265
                        icmpin := apicapi.NewHostprotRule(discDn, "icmp-ingress")
1✔
266
                        icmpin.SetAttr("direction", "ingress")
1✔
267
                        icmpin.SetAttr("ethertype", "ipv4")
1✔
268
                        icmpin.SetAttr("protocol", "icmp")
1✔
269
                        icmpin.SetAttr("connTrack", "normal")
1✔
270
                        discSubj.AddChild(icmpin)
1✔
271
                }
1✔
272

273
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
274
                        icmpin := apicapi.NewHostprotRule(discDn, "icmpv6-ingress")
1✔
275
                        icmpin.SetAttr("direction", "ingress")
1✔
276
                        icmpin.SetAttr("ethertype", "ipv6")
1✔
277
                        icmpin.SetAttr("protocol", "icmpv6")
1✔
278
                        icmpin.SetAttr("connTrack", "normal")
1✔
279
                        discSubj.AddChild(icmpin)
1✔
280
                }
1✔
281
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
282
                        icmpout := apicapi.NewHostprotRule(discDn, "icmp-egress")
1✔
283
                        icmpout.SetAttr("direction", "egress")
1✔
284
                        icmpout.SetAttr("ethertype", "ipv4")
1✔
285
                        icmpout.SetAttr("protocol", "icmp")
1✔
286
                        icmpout.SetAttr("connTrack", "normal")
1✔
287
                        discSubj.AddChild(icmpout)
1✔
288
                }
1✔
289

290
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
291
                        icmpout := apicapi.NewHostprotRule(discDn, "icmpv6-egress")
1✔
292
                        icmpout.SetAttr("direction", "egress")
1✔
293
                        icmpout.SetAttr("ethertype", "ipv6")
1✔
294
                        icmpout.SetAttr("protocol", "icmpv6")
1✔
295
                        icmpout.SetAttr("connTrack", "normal")
1✔
296
                        discSubj.AddChild(icmpout)
1✔
297
                }
1✔
298

299
                hppDiscovery.AddChild(discSubj)
1✔
300
        }
301

302
        return apicapi.ApicSlice{hppIngress, hppEgress, hppDiscovery}
1✔
303
}
304

305
func (cont *AciController) getHppClient() (hppclset.Interface, bool) {
1✔
306
        env := cont.env.(*K8sEnvironment)
1✔
307
        hppcl := env.hppClient
1✔
308
        if hppcl == nil {
2✔
309
                cont.log.Error("hpp client not found")
1✔
310
                return nil, false
1✔
311
        }
1✔
312
        return hppcl, true
1✔
313
}
314

315
func (cont *AciController) validateHppCr(hpp *hppv1.HostprotPol) bool {
1✔
316
        allowedProtocols := map[string]bool{
1✔
317
                "tcp":         true,
1✔
318
                "udp":         true,
1✔
319
                "icmp":        true,
1✔
320
                "icmpv6":      true,
1✔
321
                "unspecified": true,
1✔
322
        }
1✔
323

1✔
324
        for _, subj := range hpp.Spec.HostprotSubj {
2✔
325
                for _, rule := range subj.HostprotRule {
2✔
326
                        if rule.Protocol != "" {
2✔
327
                                if !allowedProtocols[rule.Protocol] {
1✔
328
                                        cont.log.Error("unknown protocol value: ", rule.Protocol, ", hostprotPol CR: ", hpp)
×
329
                                        return false
×
330
                                }
×
331
                        }
332
                }
333
        }
334
        return true
1✔
335
}
336

337
func (cont *AciController) createHostprotPol(hpp *hppv1.HostprotPol, ns string) bool {
1✔
338
        if !cont.validateHppCr(hpp) {
1✔
339
                return false
×
340
        }
×
341
        hppcl, ok := cont.getHppClient()
1✔
342
        if !ok {
2✔
343
                return false
1✔
344
        }
1✔
345

346
        cont.log.Debug("Creating HPP CR: ", hpp)
1✔
347
        _, err := hppcl.AciV1().HostprotPols(ns).Create(context.TODO(), hpp, metav1.CreateOptions{})
1✔
348
        if err != nil {
1✔
349
                cont.log.Error("Error creating HPP CR: ", err)
×
350
                return false
×
351
        }
×
352

353
        return true
1✔
354
}
355

356
func (cont *AciController) updateHostprotPol(hpp *hppv1.HostprotPol, ns string) bool {
1✔
357
        if !cont.validateHppCr(hpp) {
1✔
358
                cont.deleteHostprotPol(hpp.Name, hpp.Namespace)
×
359
                return false
×
360
        }
×
361
        hppcl, ok := cont.getHppClient()
1✔
362
        if !ok {
2✔
363
                return false
1✔
364
        }
1✔
365

366
        cont.log.Debug("Updating HPP CR: ", hpp)
1✔
367
        _, err := hppcl.AciV1().HostprotPols(ns).Update(context.TODO(), hpp, metav1.UpdateOptions{})
1✔
368
        if err != nil {
1✔
369
                cont.log.Error("Error updating HPP CR: ", err)
×
370
                return false
×
371
        }
×
372

373
        return true
1✔
374
}
375

376
func (cont *AciController) deleteAllHostprotPol() error {
1✔
377
        sysNs := os.Getenv("SYSTEM_NAMESPACE")
1✔
378
        hppcl, ok := cont.getHppClient()
1✔
379
        if !ok {
1✔
380
                cont.log.Error("Failed to delete HostprotPol CRs")
×
381
                return fmt.Errorf("HppClient not initialized")
×
382
        }
×
383

384
        cont.log.Debug("Deleting all HostprotPol CRs")
1✔
385
        err := hppcl.AciV1().HostprotPols(sysNs).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{})
1✔
386
        if err != nil {
1✔
387
                cont.log.Error("Failed to delete HostprotPol CRs: ", err)
×
388
        }
×
389
        return err
1✔
390
}
391

392
func (cont *AciController) deleteHostprotPol(hppName string, ns string) bool {
1✔
393
        hppcl, ok := cont.getHppClient()
1✔
394
        if !ok {
2✔
395
                return false
1✔
396
        }
1✔
397

398
        cont.log.Debug("Deleting HPP CR: ", hppName)
1✔
399
        err := hppcl.AciV1().HostprotPols(ns).Delete(context.TODO(), hppName, metav1.DeleteOptions{})
1✔
400
        if err != nil {
1✔
401
                cont.log.Error("Error deleting HPP CR: ", err)
×
402
                return false
×
403
        }
×
404

405
        return true
1✔
406
}
407

408
func (cont *AciController) getHostprotPol(hppName string, ns string) (*hppv1.HostprotPol, error) {
1✔
409
        hppcl, ok := cont.getHppClient()
1✔
410
        if !ok {
2✔
411
                return nil, fmt.Errorf("hpp client not found")
1✔
412
        }
1✔
413

414
        hpp, err := hppcl.AciV1().HostprotPols(ns).Get(context.TODO(), hppName, metav1.GetOptions{})
1✔
415
        if err != nil {
2✔
416
                return nil, err
1✔
417
        }
1✔
418
        cont.log.Debug("HPP CR found: ", hpp)
1✔
419
        return hpp, nil
1✔
420
}
421

422
func (cont *AciController) getHostprotRemoteIpContainer(name, ns string) (*hppv1.HostprotRemoteIpContainer, error) {
1✔
423
        hppcl, ok := cont.getHppClient()
1✔
424
        if !ok {
2✔
425
                return nil, fmt.Errorf("hpp client not found")
1✔
426
        }
1✔
427

428
        hpp, err := hppcl.AciV1().HostprotRemoteIpContainers(ns).Get(context.TODO(), name, metav1.GetOptions{})
1✔
429
        if err != nil {
2✔
430
                cont.log.Error("Error getting HostprotRemoteIpContainers CR: ", err)
1✔
431
                return nil, err
1✔
432
        }
1✔
433
        cont.log.Debug("HostprotRemoteIpContainers CR found: ", hpp)
1✔
434
        return hpp, nil
1✔
435
}
436

437
func (cont *AciController) createHostprotRemoteIpContainer(hppIpCont *hppv1.HostprotRemoteIpContainer, ns string) bool {
1✔
438
        hppcl, ok := cont.getHppClient()
1✔
439
        if !ok {
2✔
440
                return false
1✔
441
        }
1✔
442

443
        cont.log.Debug("Creating HostprotRemoteIpContainer CR: ", hppIpCont)
1✔
444
        _, err := hppcl.AciV1().HostprotRemoteIpContainers(ns).Create(context.TODO(), hppIpCont, metav1.CreateOptions{})
1✔
445
        if err != nil {
1✔
446
                cont.log.Error("Error creating HostprotRemoteIpContainer CR: ", err)
×
447
                return false
×
448
        }
×
449

450
        return true
1✔
451
}
452

453
func (cont *AciController) updateHostprotRemoteIpContainer(hppIpCont *hppv1.HostprotRemoteIpContainer, ns string) bool {
1✔
454
        hppcl, ok := cont.getHppClient()
1✔
455
        if !ok {
2✔
456
                return false
1✔
457
        }
1✔
458

459
        cont.log.Debug("Updating HostprotRemoteIpContainer CR: ", hppIpCont)
1✔
460
        _, err := hppcl.AciV1().HostprotRemoteIpContainers(ns).Update(context.TODO(), hppIpCont, metav1.UpdateOptions{})
1✔
461
        if err != nil {
1✔
462
                cont.log.Error("Error updating HostprotRemoteIpContainer CR: ", err)
×
463
                return false
×
464
        }
×
465

466
        return true
1✔
467
}
468

469
func (cont *AciController) deleteAllHostprotRemoteIpContainers() error {
1✔
470
        sysNs := os.Getenv("SYSTEM_NAMESPACE")
1✔
471
        hppcl, ok := cont.getHppClient()
1✔
472
        if !ok {
1✔
473
                cont.log.Error("Failed to delete HostprotRemoteIpContainer CRs")
×
474
                return fmt.Errorf("HppClient not initialized")
×
475
        }
×
476

477
        cont.log.Debug("Deleting all HostprotRemoteIpContainer CRs")
1✔
478
        err := hppcl.AciV1().HostprotRemoteIpContainers(sysNs).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{})
1✔
479
        if err != nil {
1✔
480
                cont.log.Error("Failed to delete HostprotRemoteIpContainer CRs: ", err)
×
481
        }
×
482
        return err
1✔
483
}
484

485
func (cont *AciController) deleteHostprotRemoteIpContainer(hppIpContName string, ns string) bool {
1✔
486
        hppcl, ok := cont.getHppClient()
1✔
487
        if !ok {
2✔
488
                return false
1✔
489
        }
1✔
490

491
        cont.log.Debug("Deleting HostprotRemoteIpContainer CR: ", hppIpContName)
1✔
492
        err := hppcl.AciV1().HostprotRemoteIpContainers(ns).Delete(context.TODO(), hppIpContName, metav1.DeleteOptions{})
1✔
493
        if err != nil {
1✔
494
                cont.log.Error("Error deleting HostprotRemoteIpContainer CR: ", err)
×
495
                return false
×
496
        }
×
497

498
        return true
1✔
499
}
500

501
func (cont *AciController) listHostprotPol(ns string) (*hppv1.HostprotPolList, error) {
1✔
502
        hppcl, ok := cont.getHppClient()
1✔
503
        if !ok {
1✔
504
                return nil, fmt.Errorf("hpp client not found")
×
505
        }
×
506

507
        hpps, err := hppcl.AciV1().HostprotPols(ns).List(context.TODO(), metav1.ListOptions{})
1✔
508
        if err != nil {
2✔
509
                cont.log.Error("Error listing HPP CR: ", err)
1✔
510
                return nil, err
1✔
511
        }
1✔
512
        return hpps, nil
×
513
}
514

515
func (cont *AciController) listHostprotRemoteIpContainers(ns string) (*hppv1.HostprotRemoteIpContainerList, error) {
1✔
516
        hppcl, ok := cont.getHppClient()
1✔
517
        if !ok {
1✔
518
                return nil, fmt.Errorf("hpp client not found")
×
519
        }
×
520

521
        hpRemoteIpConts, err := hppcl.AciV1().HostprotRemoteIpContainers(ns).List(context.TODO(), metav1.ListOptions{})
1✔
522
        if err != nil {
2✔
523
                cont.log.Error("Error getting HostprotRemoteIpContainers CRs: ", err)
1✔
524
                return nil, err
1✔
525
        }
1✔
526
        return hpRemoteIpConts, nil
×
527
}
528

529
func (cont *AciController) createStaticNetPolCrs() bool {
1✔
530
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
531

1✔
532
        createPol := func(labelKey, subjName, direction string, rules []hppv1.HostprotRule) bool {
2✔
533
                hppName := strings.ReplaceAll(labelKey, "_", "-")
1✔
534
                if _, err := cont.getHostprotPol(hppName, ns); errors.IsNotFound(err) {
2✔
535
                        hpp := &hppv1.HostprotPol{
1✔
536
                                ObjectMeta: metav1.ObjectMeta{
1✔
537
                                        Name:      hppName,
1✔
538
                                        Namespace: ns,
1✔
539
                                },
1✔
540
                                Spec: hppv1.HostprotPolSpec{
1✔
541
                                        Name:            labelKey,
1✔
542
                                        NetworkPolicies: []string{labelKey},
1✔
543
                                        HostprotSubj: []hppv1.HostprotSubj{
1✔
544
                                                {
1✔
545
                                                        Name:         subjName,
1✔
546
                                                        HostprotRule: rules,
1✔
547
                                                },
1✔
548
                                        },
1✔
549
                                },
1✔
550
                        }
1✔
551
                        if !cont.createHostprotPol(hpp, ns) {
1✔
552
                                return false
×
553
                        }
×
554
                }
555
                return true
1✔
556
        }
557

558
        if !createPol(cont.aciNameForKey("np", "static-ingress"), "ingress", "ingress", cont.getHostprotRules("ingress")) {
1✔
559
                return false
×
560
        }
×
561
        if !createPol(cont.aciNameForKey("np", "static-egress"), "egress", "egress", cont.getHostprotRules("egress")) {
1✔
562
                return false
×
563
        }
×
564
        if !createPol(cont.aciNameForKey("np", "static-discovery"), "discovery", "discovery", cont.getDiscoveryRules()) {
1✔
565
                return false
×
566
        }
×
567

568
        return true
1✔
569
}
570

571
func (cont *AciController) getHostprotRules(direction string) []hppv1.HostprotRule {
1✔
572
        var rules []hppv1.HostprotRule
1✔
573
        outbound := hppv1.HostprotRule{
1✔
574
                ConnTrack: "reflexive",
1✔
575
                Protocol:  "unspecified",
1✔
576
                FromPort:  "unspecified",
1✔
577
                ToPort:    "unspecified",
1✔
578
                Direction: direction,
1✔
579
        }
1✔
580

1✔
581
        if !cont.configuredPodNetworkIps.V6.Empty() {
1✔
582
                outbound.Name = "allow-all-reflexive-v6"
×
583
                outbound.Ethertype = "ipv6"
×
584
                rules = append(rules, outbound)
×
585
        }
×
586
        if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
587
                outbound.Name = "allow-all-reflexive"
1✔
588
                outbound.Ethertype = "ipv4"
1✔
589
                rules = append(rules, outbound)
1✔
590
        }
1✔
591

592
        return rules
1✔
593
}
594

595
func (cont *AciController) getDiscoveryRules() []hppv1.HostprotRule {
1✔
596
        rules := []hppv1.HostprotRule{
1✔
597
                {
1✔
598
                        Name:      "arp-ingress",
1✔
599
                        Direction: "ingress",
1✔
600
                        Ethertype: "arp",
1✔
601
                        ConnTrack: "normal",
1✔
602
                },
1✔
603
                {
1✔
604
                        Name:      "arp-egress",
1✔
605
                        Direction: "egress",
1✔
606
                        Ethertype: "arp",
1✔
607
                        ConnTrack: "normal",
1✔
608
                },
1✔
609
        }
1✔
610

1✔
611
        if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
612
                rules = append(rules,
1✔
613
                        hppv1.HostprotRule{
1✔
614
                                Name:      "icmp-ingress",
1✔
615
                                Direction: "ingress",
1✔
616
                                Ethertype: "ipv4",
1✔
617
                                Protocol:  "icmp",
1✔
618
                                ConnTrack: "normal",
1✔
619
                        },
1✔
620
                        hppv1.HostprotRule{
1✔
621
                                Name:      "icmp-egress",
1✔
622
                                Direction: "egress",
1✔
623
                                Ethertype: "ipv4",
1✔
624
                                Protocol:  "icmp",
1✔
625
                                ConnTrack: "normal",
1✔
626
                        },
1✔
627
                )
1✔
628
        }
1✔
629

630
        if !cont.configuredPodNetworkIps.V6.Empty() {
1✔
631
                rules = append(rules,
×
632
                        hppv1.HostprotRule{
×
633
                                Name:      "icmpv6-ingress",
×
634
                                Direction: "ingress",
×
635
                                Ethertype: "ipv6",
×
636
                                Protocol:  "icmpv6",
×
637
                                ConnTrack: "normal",
×
638
                        },
×
639
                        hppv1.HostprotRule{
×
640
                                Name:      "icmpv6-egress",
×
641
                                Direction: "egress",
×
642
                                Ethertype: "ipv6",
×
643
                                Protocol:  "icmpv6",
×
644
                                ConnTrack: "normal",
×
645
                        },
×
646
                )
×
647
        }
×
648

649
        return rules
1✔
650
}
651

652
func (cont *AciController) cleanStaleHppCrs() {
1✔
653
        sysNs := os.Getenv("SYSTEM_NAMESPACE")
1✔
654
        npNames := make(map[string]struct{})
1✔
655

1✔
656
        namespaces, err := cont.listNamespaces()
1✔
657
        if err != nil {
1✔
658
                cont.log.Error("Error listing namespaces: ", err)
×
659
                return
×
660
        }
×
661

662
        for _, ns := range namespaces.Items {
1✔
663
                netpols, err := cont.listNetworkPolicies(ns.Name)
×
664
                if err != nil {
×
665
                        cont.log.Error("Error listing network policies in namespace ", ns.Name, ": ", err)
×
666
                        continue
×
667
                }
668
                for _, np := range netpols.Items {
×
669
                        nsName := np.ObjectMeta.Namespace + "/" + np.ObjectMeta.Name
×
670
                        npNames[nsName] = struct{}{}
×
671
                }
×
672
        }
673

674
        hpps, err := cont.listHostprotPol(sysNs)
1✔
675
        if err != nil {
2✔
676
                cont.log.Error("Error listing HostprotPols: ", err)
1✔
677
                return
1✔
678
        }
1✔
679

680
        for _, hpp := range hpps.Items {
×
681
                for _, npName := range hpp.Spec.NetworkPolicies {
×
682
                        if _, exists := npNames[npName]; !exists {
×
683
                                if !cont.deleteHostprotPol(hpp.ObjectMeta.Name, sysNs) {
×
684
                                        cont.log.Error("Error deleting stale HostprotPol: ", hpp.ObjectMeta.Name)
×
685
                                }
×
686
                        }
687
                }
688
        }
689
}
690

691
func (cont *AciController) cleanStaleHostprotRemoteIpContainers() {
1✔
692
        sysNs := os.Getenv("SYSTEM_NAMESPACE")
1✔
693
        nsNames := make(map[string]struct{})
1✔
694

1✔
695
        namespaces, err := cont.listNamespaces()
1✔
696
        if err != nil {
1✔
697
                cont.log.Error("Error listing namespaces: ", err)
×
698
                return
×
699
        }
×
700

701
        for _, ns := range namespaces.Items {
1✔
702
                nsNames[ns.Name] = struct{}{}
×
703
        }
×
704

705
        hpRemIpConts, err := cont.listHostprotRemoteIpContainers(sysNs)
1✔
706
        if err != nil {
2✔
707
                cont.log.Error("Error listing HostprotRemoteIpContainers: ", err)
1✔
708
                return
1✔
709
        }
1✔
710

711
        for _, hpRemIpCont := range hpRemIpConts.Items {
×
712
                if _, exists := nsNames[hpRemIpCont.ObjectMeta.Name]; !exists {
×
713
                        if !cont.deleteHostprotRemoteIpContainer(hpRemIpCont.ObjectMeta.Name, sysNs) {
×
714
                                cont.log.Error("Error deleting stale HostprotRemoteIpContainer: ", hpRemIpCont.ObjectMeta.Name)
×
715
                        }
×
716
                }
717
        }
718
}
719

720
func (cont *AciController) initStaticNetPolObjs() {
1✔
721
        if cont.config.EnableHppDirect {
2✔
722
                cont.cleanStaleHostprotRemoteIpContainers()
1✔
723
                cont.cleanStaleHppCrs()
1✔
724

1✔
725
                if !cont.createStaticNetPolCrs() {
1✔
726
                        cont.log.Error("Error creating static HPP CRs")
×
727
                }
×
728
                return
1✔
729
        } else {
1✔
730
                cont.deleteAllHostprotPol()
1✔
731
                cont.deleteAllHostprotRemoteIpContainers()
1✔
732
        }
1✔
733

734
        cont.apicConn.WriteApicObjects(cont.config.AciPrefix+"_np_static", cont.staticNetPolObjs())
1✔
735
}
736

737
func networkPolicyLogger(log *logrus.Logger,
738
        np *v1net.NetworkPolicy) *logrus.Entry {
1✔
739
        return log.WithFields(logrus.Fields{
1✔
740
                "namespace": np.ObjectMeta.Namespace,
1✔
741
                "name":      np.ObjectMeta.Name,
1✔
742
        })
1✔
743
}
1✔
744

745
func (cont *AciController) queueNetPolUpdateByKey(key string) {
1✔
746
        cont.netPolQueue.Add(key)
1✔
747
}
1✔
748

749
func (cont *AciController) queueRemoteIpConUpdate(pod *v1.Pod, deleted bool) {
1✔
750
        cont.hppMutex.Lock()
1✔
751
        update := cont.updateNsRemoteIpCont(pod, deleted)
1✔
752
        if update {
2✔
753
                podns := pod.ObjectMeta.Namespace
1✔
754
                cont.remIpContQueue.Add(podns)
1✔
755
        }
1✔
756
        cont.hppMutex.Unlock()
1✔
757
}
758

759
func (cont *AciController) queueNetPolUpdate(netpol *v1net.NetworkPolicy) {
1✔
760
        key, err := cache.MetaNamespaceKeyFunc(netpol)
1✔
761
        if err != nil {
1✔
762
                networkPolicyLogger(cont.log, netpol).
×
763
                        Error("Could not create network policy key: ", err)
×
764
                return
×
765
        }
×
766
        cont.netPolQueue.Add(key)
1✔
767
}
768

769
func (cont *AciController) peerMatchesPod(npNs string,
770
        peer *v1net.NetworkPolicyPeer, pod *v1.Pod, podNs *v1.Namespace) bool {
1✔
771
        if peer.PodSelector != nil && npNs == pod.ObjectMeta.Namespace {
2✔
772
                selector, err :=
1✔
773
                        metav1.LabelSelectorAsSelector(peer.PodSelector)
1✔
774
                if err != nil {
1✔
775
                        cont.log.Error("Could not parse pod selector: ", err)
×
776
                } else {
1✔
777
                        return selector.Matches(labels.Set(pod.ObjectMeta.Labels))
1✔
778
                }
1✔
779
        }
780
        if peer.NamespaceSelector != nil {
2✔
781
                selector, err :=
1✔
782
                        metav1.LabelSelectorAsSelector(peer.NamespaceSelector)
1✔
783
                if err != nil {
1✔
784
                        cont.log.Error("Could not parse namespace selector: ", err)
×
785
                } else {
1✔
786
                        match := selector.Matches(labels.Set(podNs.ObjectMeta.Labels))
1✔
787
                        if match && peer.PodSelector != nil {
2✔
788
                                podSelector, err :=
1✔
789
                                        metav1.LabelSelectorAsSelector(peer.PodSelector)
1✔
790
                                if err != nil {
1✔
791
                                        cont.log.Error("Could not parse pod selector: ", err)
×
792
                                } else {
1✔
793
                                        return podSelector.Matches(labels.Set(pod.ObjectMeta.Labels))
1✔
794
                                }
1✔
795
                        }
796
                        return match
1✔
797
                }
798
        }
799
        return false
×
800
}
801

802
func ipsForPod(pod *v1.Pod) []string {
1✔
803
        var ips []string
1✔
804
        podIPsField := reflect.ValueOf(pod.Status).FieldByName("PodIPs")
1✔
805
        if podIPsField.IsValid() {
2✔
806
                if len(pod.Status.PodIPs) > 0 {
1✔
807
                        for _, ip := range pod.Status.PodIPs {
×
808
                                ips = append(ips, ip.IP)
×
809
                        }
×
810
                        return ips
×
811
                }
812
        }
813
        if pod.Status.PodIP != "" {
2✔
814
                return []string{pod.Status.PodIP}
1✔
815
        }
1✔
816
        return nil
1✔
817
}
818

819
func ipBlockToSubnets(ipblock *v1net.IPBlock) ([]string, error) {
1✔
820
        _, nw, err := net.ParseCIDR(ipblock.CIDR)
1✔
821
        if err != nil {
1✔
822
                return nil, err
×
823
        }
×
824
        ips := ipam.New()
1✔
825
        ips.AddSubnet(nw)
1✔
826
        for _, except := range ipblock.Except {
2✔
827
                _, nw, err = net.ParseCIDR(except)
1✔
828
                if err != nil {
1✔
829
                        return nil, err
×
830
                }
×
831
                ips.RemoveSubnet(nw)
1✔
832
        }
833
        var subnets []string
1✔
834
        for _, r := range ips.FreeList {
2✔
835
                ipnets := ipam.Range2Cidr(r.Start, r.End)
1✔
836
                for _, n := range ipnets {
2✔
837
                        subnets = append(subnets, n.String())
1✔
838
                }
1✔
839
        }
840
        return subnets, nil
1✔
841
}
842

843
func parseCIDR(sub string) *net.IPNet {
1✔
844
        _, netw, err := net.ParseCIDR(sub)
1✔
845
        if err == nil {
2✔
846
                return netw
1✔
847
        }
1✔
848
        ip := net.ParseIP(sub)
1✔
849
        if ip == nil {
1✔
850
                return nil
×
851
        }
×
852
        var mask net.IPMask
1✔
853
        if ip.To4() != nil {
2✔
854
                mask = net.CIDRMask(32, 32)
1✔
855
        } else if ip.To16() != nil {
3✔
856
                mask = net.CIDRMask(128, 128)
1✔
857
        } else {
1✔
858
                return nil
×
859
        }
×
860
        return &net.IPNet{
1✔
861
                IP:   ip,
1✔
862
                Mask: mask,
1✔
863
        }
1✔
864
}
865

866
func netEqual(a, b net.IPNet) bool {
1✔
867
        return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
1✔
868
}
1✔
869

870
func (cont *AciController) updateIpIndexEntry(index cidranger.Ranger,
871
        subnetStr string, key string, add bool) bool {
1✔
872
        cidr := parseCIDR(subnetStr)
1✔
873
        if cidr == nil {
1✔
874
                cont.log.WithFields(logrus.Fields{
×
875
                        "subnet": subnetStr,
×
876
                        "netpol": key,
×
877
                }).Warning("Invalid subnet or IP")
×
878
                return false
×
879
        }
×
880

881
        entries, err := index.CoveredNetworks(*cidr)
1✔
882
        if err != nil {
1✔
883
                cont.log.Error("Corrupted subnet index: ", err)
×
884
                return false
×
885
        }
×
886
        if add {
2✔
887
                for _, entryObj := range entries {
2✔
888
                        if netEqual(entryObj.Network(), *cidr) {
2✔
889
                                entry := entryObj.(*ipIndexEntry)
1✔
890
                                existing := entry.keys[key]
1✔
891
                                entry.keys[key] = true
1✔
892
                                return !existing
1✔
893
                        }
1✔
894
                }
895

896
                entry := &ipIndexEntry{
1✔
897
                        ipNet: *cidr,
1✔
898
                        keys: map[string]bool{
1✔
899
                                key: true,
1✔
900
                        },
1✔
901
                }
1✔
902
                index.Insert(entry)
1✔
903
                return true
1✔
904
        } else {
1✔
905
                var existing bool
1✔
906
                for _, entryObj := range entries {
2✔
907
                        entry := entryObj.(*ipIndexEntry)
1✔
908
                        if entry.keys[key] {
2✔
909
                                existing = true
1✔
910
                                delete(entry.keys, key)
1✔
911
                        }
1✔
912
                        if len(entry.keys) == 0 {
2✔
913
                                index.Remove(entry.Network())
1✔
914
                        }
1✔
915
                }
916
                return existing
1✔
917
        }
918
}
919

920
type portRange struct {
921
        fromPort string
922
        toPort   string
923
}
924

925
func (cont *AciController) updateIpIndex(index cidranger.Ranger,
926
        oldSubnets map[string]bool, newSubnets map[string]bool, key string) {
1✔
927
        for subStr := range oldSubnets {
2✔
928
                if newSubnets[subStr] {
1✔
929
                        continue
×
930
                }
931
                cont.updateIpIndexEntry(index, subStr, key, false)
1✔
932
        }
933
        for subStr := range newSubnets {
2✔
934
                if oldSubnets[subStr] {
1✔
935
                        continue
×
936
                }
937
                cont.updateIpIndexEntry(index, subStr, key, true)
1✔
938
        }
939
}
940

941
func (cont *AciController) updateTargetPortIndex(service bool, key string,
942
        oldPorts map[string]targetPort, newPorts map[string]targetPort) {
1✔
943
        for portkey := range oldPorts {
2✔
944
                if _, ok := newPorts[portkey]; ok {
1✔
945
                        continue
×
946
                }
947

948
                entry, ok := cont.targetPortIndex[portkey]
1✔
949
                if !ok {
1✔
950
                        continue
×
951
                }
952

953
                if service {
1✔
954
                        delete(entry.serviceKeys, key)
×
955
                } else {
1✔
956
                        delete(entry.networkPolicyKeys, key)
1✔
957
                }
1✔
958
                if len(entry.serviceKeys) == 0 && len(entry.networkPolicyKeys) == 0 {
2✔
959
                        delete(cont.targetPortIndex, portkey)
1✔
960
                }
1✔
961
        }
962
        for portkey, port := range newPorts {
2✔
963
                if _, ok := oldPorts[portkey]; ok {
1✔
964
                        continue
×
965
                }
966
                entry := cont.targetPortIndex[portkey]
1✔
967
                if entry == nil {
2✔
968
                        entry = &portIndexEntry{
1✔
969
                                port:              port,
1✔
970
                                serviceKeys:       make(map[string]bool),
1✔
971
                                networkPolicyKeys: make(map[string]bool),
1✔
972
                        }
1✔
973
                        cont.targetPortIndex[portkey] = entry
1✔
974
                } else {
2✔
975
                        for p := range port.ports {
2✔
976
                                entry.port.ports[p] = true
1✔
977
                        }
1✔
978
                }
979

980
                if service {
2✔
981
                        entry.serviceKeys[key] = true
1✔
982
                } else {
2✔
983
                        entry.networkPolicyKeys[key] = true
1✔
984
                }
1✔
985
        }
986
}
987

988
func (cont *AciController) getPortNumsFromPortName(podKeys []string, portName string) map[int]bool {
1✔
989
        ports := make(map[int]bool)
1✔
990
        for _, podkey := range podKeys {
2✔
991
                podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
992
                if exists && err == nil {
2✔
993
                        pod := podobj.(*v1.Pod)
1✔
994
                        port, err := k8util.LookupContainerPortNumberByName(*pod, portName)
1✔
995
                        if err != nil {
1✔
996
                                continue
×
997
                        }
998
                        ports[int(port)] = true
1✔
999
                }
1000
        }
1001
        if len(ports) == 0 {
2✔
1002
                cont.log.Infof("No matching portnumbers for portname %s: ", portName)
1✔
1003
        }
1✔
1004
        cont.log.Debug("PortName: ", portName, "Mapping port numbers: ", ports)
1✔
1005
        return ports
1✔
1006
}
1007

1008
// get a map of target ports for egress rules that have no "To" clause
1009
func (cont *AciController) getNetPolTargetPorts(np *v1net.NetworkPolicy) map[string]targetPort {
1✔
1010
        ports := make(map[string]targetPort)
1✔
1011
        for _, egress := range np.Spec.Egress {
2✔
1012
                if len(egress.To) != 0 && !isNamedPortPresenInNp(np) {
2✔
1013
                        continue
1✔
1014
                }
1015
                for _, port := range egress.Ports {
2✔
1016
                        if port.Port == nil {
1✔
1017
                                continue
×
1018
                        }
1019
                        proto := v1.ProtocolTCP
1✔
1020
                        if port.Protocol != nil {
2✔
1021
                                proto = *port.Protocol
1✔
1022
                        }
1✔
1023
                        npKey, _ := cache.MetaNamespaceKeyFunc(np)
1✔
1024
                        var key string
1✔
1025
                        portnums := make(map[int]bool)
1✔
1026
                        if port.Port.Type == intstr.Int {
2✔
1027
                                key = portProto(&proto) + "-num-" + port.Port.String()
1✔
1028
                                portnums[port.Port.IntValue()] = true
1✔
1029
                        } else {
2✔
1030
                                if len(egress.To) != 0 {
2✔
1031
                                        // TODO optimize this code instead going through all matching pods every time
1✔
1032
                                        podKeys := cont.netPolEgressPods.GetPodForObj(npKey)
1✔
1033
                                        portnums = cont.getPortNumsFromPortName(podKeys, port.Port.String())
1✔
1034
                                } else {
2✔
1035
                                        ctrNmpEntry, ok := cont.ctrPortNameCache[port.Port.String()]
1✔
1036
                                        if ok {
2✔
1037
                                                for key := range ctrNmpEntry.ctrNmpToPods {
2✔
1038
                                                        val := strings.Split(key, "-")
1✔
1039
                                                        if len(val) != 2 {
1✔
1040
                                                                continue
×
1041
                                                        }
1042
                                                        if val[0] == portProto(&proto) {
2✔
1043
                                                                port, _ := strconv.Atoi(val[1])
1✔
1044
                                                                portnums[port] = true
1✔
1045
                                                        }
1✔
1046
                                                }
1047
                                        }
1048
                                }
1049
                                if len(portnums) == 0 {
2✔
1050
                                        continue
1✔
1051
                                }
1052
                                key = portProto(&proto) + "-name-" + port.Port.String()
1✔
1053
                        }
1054
                        ports[key] = targetPort{
1✔
1055
                                proto: proto,
1✔
1056
                                ports: portnums,
1✔
1057
                        }
1✔
1058
                }
1059
        }
1060
        return ports
1✔
1061
}
1062

1063
type peerRemoteInfo struct {
1064
        remotePods   []*v1.Pod
1065
        podSelectors []*metav1.LabelSelector
1066
}
1067

1068
func (cont *AciController) getPeerRemoteSubnets(peers []v1net.NetworkPolicyPeer,
1069
        namespace string, peerPods []*v1.Pod, peerNs map[string]*v1.Namespace,
1070
        logger *logrus.Entry) ([]string, []string, peerRemoteInfo, map[string]bool, []string) {
1✔
1071
        var remoteSubnets []string
1✔
1072
        var peerremote peerRemoteInfo
1✔
1073
        subnetMap := make(map[string]bool)
1✔
1074
        var peerNsList []string
1✔
1075
        var ipBlockSubs []string
1✔
1076
        if len(peers) > 0 {
2✔
1077
                // only applies to matching pods
1✔
1078
                for _, pod := range peerPods {
2✔
1079
                        for peerIx, peer := range peers {
2✔
1080
                                if ns, ok := peerNs[pod.ObjectMeta.Namespace]; ok &&
1✔
1081
                                        cont.peerMatchesPod(namespace,
1✔
1082
                                                &peers[peerIx], pod, ns) {
2✔
1083
                                        podIps := ipsForPod(pod)
1✔
1084
                                        for _, ip := range podIps {
2✔
1085
                                                if _, exists := subnetMap[ip]; !exists {
2✔
1086
                                                        subnetMap[ip] = true
1✔
1087
                                                        if cont.config.EnableHppDirect {
2✔
1088
                                                                peerremote.remotePods = append(peerremote.remotePods, pod)
1✔
1089
                                                                if !slices.Contains(peerNsList, pod.ObjectMeta.Namespace) {
2✔
1090
                                                                        peerNsList = append(peerNsList, pod.ObjectMeta.Namespace)
1✔
1091
                                                                }
1✔
1092
                                                        }
1093
                                                        remoteSubnets = append(remoteSubnets, ip)
1✔
1094
                                                }
1095
                                        }
1096
                                }
1097
                                if cont.config.EnableHppDirect && peer.PodSelector != nil {
2✔
1098
                                        if !cont.isPodSelectorPresent(peerremote.podSelectors, peer.PodSelector) {
2✔
1099
                                                peerremote.podSelectors = append(peerremote.podSelectors, peer.PodSelector)
1✔
1100
                                        }
1✔
1101
                                }
1102
                        }
1103
                }
1104

1105
                for _, peer := range peers {
2✔
1106
                        if peer.IPBlock == nil {
2✔
1107
                                continue
1✔
1108
                        }
1109
                        subs, err := ipBlockToSubnets(peer.IPBlock)
1✔
1110
                        if err != nil {
1✔
1111
                                logger.Warning("Invalid IPBlock in network policy rule: ", err)
×
1112
                        } else {
1✔
1113
                                for _, subnet := range subs {
2✔
1114
                                        subnetMap[subnet] = true
1✔
1115
                                }
1✔
1116
                                remoteSubnets = append(remoteSubnets, subs...)
1✔
1117
                                ipBlockSubs = append(ipBlockSubs, subs...)
1✔
1118
                        }
1119
                }
1120
        }
1121
        sort.Strings(remoteSubnets)
1✔
1122
        return remoteSubnets, peerNsList, peerremote, subnetMap, ipBlockSubs
1✔
1123
}
1124

1125
func (cont *AciController) ipInPodSubnet(ip net.IP) bool {
×
1126
        for _, podsubnet := range cont.config.PodSubnet {
×
1127
                _, subnet, err := net.ParseCIDR(podsubnet)
×
1128
                if err == nil && subnet != nil {
×
1129
                        if subnet.Contains(ip) {
×
1130
                                return true
×
1131
                        }
×
1132
                }
1133
        }
1134
        return false
×
1135
}
1136

1137
func (cont *AciController) buildNetPolSubjRule(subj apicapi.ApicObject, ruleName,
1138
        direction, ethertype, proto, port string, endPort string, remoteSubnets []string,
1139
        addPodSubnetAsRemIp bool) {
1✔
1140
        rule := apicapi.NewHostprotRule(subj.GetDn(), ruleName)
1✔
1141
        rule.SetAttr("direction", direction)
1✔
1142
        rule.SetAttr("ethertype", ethertype)
1✔
1143
        if proto != "" {
2✔
1144
                rule.SetAttr("protocol", proto)
1✔
1145
        }
1✔
1146

1147
        if addPodSubnetAsRemIp {
1✔
1148
                for _, podsubnet := range cont.config.PodSubnet {
×
1149
                        _, subnet, err := net.ParseCIDR(podsubnet)
×
1150
                        if err == nil && subnet != nil {
×
1151
                                if (ethertype == "ipv4" && subnet.IP.To4() != nil) || (ethertype == "ipv6" && subnet.IP.To4() == nil) {
×
1152
                                        rule.AddChild(apicapi.NewHostprotRemoteIp(rule.GetDn(), podsubnet))
×
1153
                                }
×
1154
                        }
1155
                }
1156
        }
1157
        for _, subnetStr := range remoteSubnets {
2✔
1158
                _, subnet, err := net.ParseCIDR(subnetStr)
1✔
1159
                if err == nil && subnet != nil {
2✔
1160
                        // subnetStr is a valid CIDR notation, check its IP version and add the subnet to the rule
1✔
1161
                        if (ethertype == "ipv4" && subnet.IP.To4() != nil) || (ethertype == "ipv6" && subnet.IP.To4() == nil) {
2✔
1162
                                rule.AddChild(apicapi.NewHostprotRemoteIp(rule.GetDn(), subnetStr))
1✔
1163
                        }
1✔
1164
                } else if ip := net.ParseIP(subnetStr); ip != nil {
2✔
1165
                        if addPodSubnetAsRemIp && cont.ipInPodSubnet(ip) {
1✔
1166
                                continue
×
1167
                        }
1168
                        if ethertype == "ipv6" && (ip.To16() != nil && ip.To4() == nil) || ethertype == "ipv4" && ip.To4() != nil {
2✔
1169
                                rule.AddChild(apicapi.NewHostprotRemoteIp(rule.GetDn(), subnetStr))
1✔
1170
                        }
1✔
1171
                }
1172
        }
1173
        if port != "" {
2✔
1174
                rule.SetAttr("fromPort", port)
1✔
1175
                if endPort != "" {
2✔
1176
                        rule.SetAttr("toPort", endPort)
1✔
1177
                }
1✔
1178
        }
1179

1180
        subj.AddChild(rule)
1✔
1181
}
1182

1183
func (cont *AciController) isPodSelectorPresent(podSelectors []*metav1.LabelSelector,
1184
        podSelector *metav1.LabelSelector) bool {
1✔
1185

1✔
1186
        present := false
1✔
1187
        for _, selector := range podSelectors {
1✔
1188
                if reflect.DeepEqual(selector, podSelector) {
×
1189
                        present = true
×
1190
                        break
×
1191
                }
1192
        }
1193
        return present
1✔
1194
}
1195

1196
func (cont *AciController) buildLocalNetPolSubjRule(subj *hppv1.HostprotSubj, ruleName,
1197
        direction, ethertype, proto, port, endPort string, remoteNs []string,
1198
        podSelectors []*metav1.LabelSelector, remoteSubnets []string) {
1✔
1199
        rule := hppv1.HostprotRule{
1✔
1200
                ConnTrack: "reflexive",
1✔
1201
                Direction: "ingress",
1✔
1202
                Ethertype: "undefined",
1✔
1203
                Protocol:  "unspecified",
1✔
1204
                FromPort:  "unspecified",
1✔
1205
                ToPort:    "unspecified",
1✔
1206
        }
1✔
1207
        rule.Direction = direction
1✔
1208
        rule.Ethertype = ethertype
1✔
1209
        if proto != "" {
2✔
1210
                rule.Protocol = proto
1✔
1211
        }
1✔
1212
        rule.Name = ruleName
1✔
1213

1✔
1214
        rule.RsRemoteIpContainer = remoteNs
1✔
1215
        var remoteSubnetsCidr []hppv1.HostprotRemoteIp
1✔
1216
        for _, subnetStr := range remoteSubnets {
2✔
1217
                _, subnet, err := net.ParseCIDR(subnetStr)
1✔
1218
                if err == nil && subnet != nil {
2✔
1219
                        if (ethertype == "ipv4" && subnet.IP.To4() != nil) || (ethertype == "ipv6" && subnet.IP.To4() == nil) {
2✔
1220
                                remIpObj := hppv1.HostprotRemoteIp{
1✔
1221
                                        Addr: subnetStr,
1✔
1222
                                }
1✔
1223
                                remoteSubnetsCidr = append(remoteSubnetsCidr, remIpObj)
1✔
1224
                        }
1✔
1225
                }
1226
        }
1227
        if len(remoteSubnetsCidr) > 0 {
2✔
1228
                rule.HostprotRemoteIp = remoteSubnetsCidr
1✔
1229
        }
1✔
1230

1231
        var filterContainers []hppv1.HostprotFilterContainer
1✔
1232
        for _, podSelector := range podSelectors {
2✔
1233
                filterContainer := hppv1.HostprotFilterContainer{}
1✔
1234
                for key, val := range podSelector.MatchLabels {
2✔
1235
                        filter := hppv1.HostprotFilter{
1✔
1236
                                Key: key,
1✔
1237
                        }
1✔
1238
                        filter.Values = append(filter.Values, val)
1✔
1239
                        filter.Operator = "Equals"
1✔
1240
                        filterContainer.HostprotFilter = append(filterContainer.HostprotFilter, filter)
1✔
1241
                }
1✔
1242
                for _, expressions := range podSelector.MatchExpressions {
2✔
1243
                        filter := hppv1.HostprotFilter{
1✔
1244
                                Key:      expressions.Key,
1✔
1245
                                Values:   expressions.Values,
1✔
1246
                                Operator: string(expressions.Operator),
1✔
1247
                        }
1✔
1248
                        filterContainer.HostprotFilter = append(filterContainer.HostprotFilter, filter)
1✔
1249
                }
1✔
1250
                filterContainers = append(filterContainers, filterContainer)
1✔
1251
        }
1252

1253
        if len(filterContainers) > 0 {
2✔
1254
                rule.HostprotFilterContainer = filterContainers
1✔
1255
        }
1✔
1256

1257
        if port != "" {
2✔
1258
                rule.FromPort = port
1✔
1259
                if endPort != "" {
1✔
1260
                        rule.ToPort = endPort
×
1261
                }
×
1262
        }
1263

1264
        cont.log.Debug(direction)
1✔
1265
        if len(remoteSubnets) != 0 && direction == "egress" {
2✔
1266
                cont.log.Debug("HostprotServiceRemoteIps")
1✔
1267
                rule.HostprotServiceRemoteIps = remoteSubnets
1✔
1268
        }
1✔
1269

1270
        subj.HostprotRule = append(subj.HostprotRule, rule)
1✔
1271
}
1272

1273
func (cont *AciController) buildNetPolSubjRules(ruleName string,
1274
        subj apicapi.ApicObject, direction string, peers []v1net.NetworkPolicyPeer,
1275
        remoteSubnets []string, ports []v1net.NetworkPolicyPort,
1276
        logger *logrus.Entry, npKey string, np *v1net.NetworkPolicy,
1277
        addPodSubnetAsRemIp bool) {
1✔
1278
        if len(peers) > 0 && len(remoteSubnets) == 0 {
2✔
1279
                // nonempty From matches no pods or IPBlocks; don't
1✔
1280
                // create the rule
1✔
1281
                return
1✔
1282
        }
1✔
1283
        if len(ports) == 0 {
2✔
1284
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
1285
                        prefix := fmt.Sprintf("%s-ipv4", ruleName)
1✔
1286
                        policyRuleName := util.AciNameForKey(prefix, "", np.Name)
1✔
1287
                        cont.buildNetPolSubjRule(subj, policyRuleName, direction,
1✔
1288
                                "ipv4", "", "", "", remoteSubnets, addPodSubnetAsRemIp)
1✔
1289
                }
1✔
1290
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
1291
                        prefix := fmt.Sprintf("%s-ipv6", ruleName)
1✔
1292
                        policyRuleName := util.AciNameForKey(prefix, "", np.Name)
1✔
1293
                        cont.buildNetPolSubjRule(subj, policyRuleName, direction,
1✔
1294
                                "ipv6", "", "", "", remoteSubnets, addPodSubnetAsRemIp)
1✔
1295
                }
1✔
1296
        } else {
1✔
1297
                ruleCounter := 0
1✔
1298
                for j := range ports {
2✔
1299
                        proto := portProto(ports[j].Protocol)
1✔
1300
                        var portRanges []portRange
1✔
1301

1✔
1302
                        if ports[j].Port != nil {
2✔
1303
                                if ports[j].Port.Type == intstr.Int {
2✔
1304
                                        pr := portRange{fromPort: ports[j].Port.String()}
1✔
1305
                                        if ports[j].EndPort != nil {
2✔
1306
                                                pr.toPort = strconv.Itoa(int(*ports[j].EndPort))
1✔
1307
                                        }
1✔
1308
                                        portRanges = append(portRanges, pr)
1✔
1309
                                } else {
1✔
1310
                                        var portMap map[int]bool
1✔
1311
                                        if direction == "egress" {
2✔
1312
                                                portMap = cont.getPortNums(&ports[j])
1✔
1313
                                        } else {
2✔
1314
                                                // TODO need to handle empty Pod Selector
1✔
1315
                                                if reflect.DeepEqual(np.Spec.PodSelector, metav1.LabelSelector{}) {
1✔
1316
                                                        logger.Warning("Empty PodSelector for NamedPort is not supported in ingress direction "+
×
1317
                                                                "port in network policy: ", ports[j].Port.String())
×
1318
                                                        continue
×
1319
                                                }
1320
                                                podKeys := cont.netPolPods.GetPodForObj(npKey)
1✔
1321
                                                portMap = cont.getPortNumsFromPortName(podKeys, ports[j].Port.String())
1✔
1322
                                        }
1323
                                        if len(portMap) == 0 {
2✔
1324
                                                logger.Warning("There is no matching ports in ingress/egress direction "+
1✔
1325
                                                        "port in network policy: ", ports[j].Port.String())
1✔
1326
                                                continue
1✔
1327
                                        }
1328
                                        for portnum := range portMap {
2✔
1329
                                                pr := portRange{fromPort: strconv.Itoa(portnum)}
1✔
1330
                                                portRanges = append(portRanges, pr)
1✔
1331
                                        }
1✔
1332
                                }
1333
                        }
1334
                        for _, pr := range portRanges {
2✔
1335
                                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
1336
                                        prefix := fmt.Sprintf("%s_%d-ipv4", ruleName, ruleCounter)
1✔
1337
                                        policyRuleName := util.AciNameForKey(prefix, "", np.Name)
1✔
1338
                                        cont.buildNetPolSubjRule(subj, policyRuleName, direction,
1✔
1339
                                                "ipv4", proto, pr.fromPort, pr.toPort, remoteSubnets, addPodSubnetAsRemIp)
1✔
1340
                                }
1✔
1341
                                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
1342
                                        prefix := fmt.Sprintf("%s_%d-ipv6", ruleName, ruleCounter)
1✔
1343
                                        policyRuleName := util.AciNameForKey(prefix, "", np.Name)
1✔
1344
                                        cont.buildNetPolSubjRule(subj, policyRuleName, direction,
1✔
1345
                                                "ipv6", proto, pr.fromPort, pr.toPort, remoteSubnets, addPodSubnetAsRemIp)
1✔
1346
                                }
1✔
1347
                                ruleCounter++
1✔
1348
                        }
1349
                        if len(portRanges) == 0 && proto != "" {
2✔
1350
                                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
1351
                                        prefix := fmt.Sprintf("%s_%d-ipv4", ruleName, ruleCounter)
1✔
1352
                                        policyRuleName := util.AciNameForKey(prefix, "", np.Name)
1✔
1353
                                        cont.buildNetPolSubjRule(subj, policyRuleName, direction,
1✔
1354
                                                "ipv4", proto, "", "", remoteSubnets, addPodSubnetAsRemIp)
1✔
1355
                                }
1✔
1356
                                if !cont.configuredPodNetworkIps.V6.Empty() {
1✔
1357
                                        prefix := fmt.Sprintf("%s_%d-ipv6", ruleName, ruleCounter)
×
1358
                                        policyRuleName := util.AciNameForKey(prefix, "", np.Name)
×
1359
                                        cont.buildNetPolSubjRule(subj, policyRuleName, direction,
×
1360
                                                "ipv6", proto, "", "", remoteSubnets, addPodSubnetAsRemIp)
×
1361
                                }
×
1362
                                ruleCounter++
1✔
1363
                        }
1364
                }
1365
        }
1366
}
1367

1368
func (cont *AciController) buildLocalNetPolSubjRules(ruleName string,
1369
        subj *hppv1.HostprotSubj, direction string, peerNs []string,
1370
        podSelector []*metav1.LabelSelector, ports []v1net.NetworkPolicyPort,
1371
        logger *logrus.Entry, npKey string, np *v1net.NetworkPolicy, peerIpBlock []string) {
1✔
1372
        if len(ports) == 0 {
2✔
1373
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
1374
                        cont.buildLocalNetPolSubjRule(subj, ruleName+"-ipv4", direction,
1✔
1375
                                "ipv4", "", "", "", peerNs, podSelector, peerIpBlock)
1✔
1376
                }
1✔
1377
                if !cont.configuredPodNetworkIps.V6.Empty() {
1✔
1378
                        cont.buildLocalNetPolSubjRule(subj, ruleName+"-ipv6", direction,
×
1379
                                "ipv6", "", "", "", peerNs, podSelector, peerIpBlock)
×
1380
                }
×
1381
        } else {
1✔
1382
                ruleCounter := 0
1✔
1383
                for j := range ports {
2✔
1384
                        proto := portProto(ports[j].Protocol)
1✔
1385
                        var portRanges []portRange
1✔
1386

1✔
1387
                        if ports[j].Port != nil {
2✔
1388
                                if ports[j].Port.Type == intstr.Int {
2✔
1389
                                        pr := portRange{fromPort: ports[j].Port.String()}
1✔
1390
                                        if ports[j].EndPort != nil {
1✔
1391
                                                pr.toPort = strconv.Itoa(int(*ports[j].EndPort))
×
1392
                                        }
×
1393
                                        portRanges = append(portRanges, pr)
1✔
1394
                                } else {
1✔
1395
                                        var portMap map[int]bool
1✔
1396
                                        if direction == "egress" {
2✔
1397
                                                portMap = cont.getPortNums(&ports[j])
1✔
1398
                                        } else {
2✔
1399
                                                // TODO need to handle empty Pod Selector
1✔
1400
                                                if reflect.DeepEqual(np.Spec.PodSelector, metav1.LabelSelector{}) {
1✔
1401
                                                        logger.Warning("Empty PodSelector for NamedPort is not supported in ingress direction "+
×
1402
                                                                "port in network policy: ", ports[j].Port.String())
×
1403
                                                        continue
×
1404
                                                }
1405
                                                podKeys := cont.netPolPods.GetPodForObj(npKey)
1✔
1406
                                                portMap = cont.getPortNumsFromPortName(podKeys, ports[j].Port.String())
1✔
1407
                                        }
1408
                                        if len(portMap) == 0 {
1✔
1409
                                                logger.Warning("There is no matching ports in ingress/egress direction "+
×
1410
                                                        "port in network policy: ", ports[j].Port.String())
×
1411
                                                continue
×
1412
                                        }
1413
                                        for portnum := range portMap {
2✔
1414
                                                pr := portRange{fromPort: strconv.Itoa(portnum)}
1✔
1415
                                                portRanges = append(portRanges, pr)
1✔
1416
                                        }
1✔
1417
                                }
1418
                        }
1419
                        for _, pr := range portRanges {
2✔
1420
                                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
1421
                                        prefix := fmt.Sprintf("%s_%d-ipv4", ruleName, ruleCounter)
1✔
1422
                                        cont.buildLocalNetPolSubjRule(subj, prefix, direction,
1✔
1423
                                                "ipv4", proto, pr.fromPort, pr.toPort, peerNs, podSelector, peerIpBlock)
1✔
1424
                                }
1✔
1425
                                if !cont.configuredPodNetworkIps.V6.Empty() {
1✔
1426
                                        prefix := fmt.Sprintf("%s_%d-ipv6", ruleName, ruleCounter)
×
1427
                                        cont.buildLocalNetPolSubjRule(subj, prefix, direction,
×
1428
                                                "ipv6", proto, pr.fromPort, pr.toPort, peerNs, podSelector, peerIpBlock)
×
1429
                                }
×
1430
                                ruleCounter++
1✔
1431
                        }
1432
                        if len(portRanges) == 0 && proto != "" {
1✔
1433
                                if !cont.configuredPodNetworkIps.V4.Empty() {
×
1434
                                        prefix := fmt.Sprintf("%s_%d-ipv4", ruleName, ruleCounter)
×
1435
                                        cont.buildLocalNetPolSubjRule(subj, prefix, direction,
×
1436
                                                "ipv4", proto, "", "", peerNs, podSelector, peerIpBlock)
×
1437
                                }
×
1438
                                if !cont.configuredPodNetworkIps.V6.Empty() {
×
1439
                                        prefix := fmt.Sprintf("%s_%d-ipv6", ruleName, ruleCounter)
×
1440
                                        cont.buildLocalNetPolSubjRule(subj, prefix, direction,
×
1441
                                                "ipv6", proto, "", "", peerNs, podSelector, peerIpBlock)
×
1442
                                }
×
1443
                                ruleCounter++
×
1444
                        }
1445
                }
1446
        }
1447
}
1448

1449
func (cont *AciController) getPortNums(port *v1net.NetworkPolicyPort) map[int]bool {
1✔
1450
        portkey := portKey(port)
1✔
1451
        cont.indexMutex.Lock()
1✔
1452
        defer cont.indexMutex.Unlock()
1✔
1453
        cont.log.Debug("PortKey1: ", portkey)
1✔
1454
        entry := cont.targetPortIndex[portkey]
1✔
1455
        if entry == nil || len(entry.port.ports) == 0 {
2✔
1456
                return map[int]bool{}
1✔
1457
        }
1✔
1458
        return maps.Clone(entry.port.ports)
1✔
1459
}
1460
func portProto(protocol *v1.Protocol) string {
1✔
1461
        proto := "tcp"
1✔
1462
        if protocol != nil && *protocol == v1.ProtocolUDP {
2✔
1463
                proto = "udp"
1✔
1464
        } else if protocol != nil && *protocol == v1.ProtocolSCTP {
3✔
1465
                proto = "sctp"
1✔
1466
        }
1✔
1467
        return proto
1✔
1468
}
1469

1470
func portKey(p *v1net.NetworkPolicyPort) string {
1✔
1471
        portType := ""
1✔
1472
        port := ""
1✔
1473
        if p != nil && p.Port != nil {
2✔
1474
                if p.Port.Type == intstr.Int {
2✔
1475
                        portType = "num"
1✔
1476
                } else {
2✔
1477
                        portType = "name"
1✔
1478
                }
1✔
1479
                port = p.Port.String()
1✔
1480
                return portProto(p.Protocol) + "-" + portType + "-" + port
1✔
1481
        }
1482
        return ""
1✔
1483
}
1484

1485
func checkEndpointslices(subnetIndex cidranger.Ranger,
1486
        addresses []string) bool {
1✔
1487
        for _, addr := range addresses {
2✔
1488
                ip := net.ParseIP(addr)
1✔
1489
                if ip == nil {
1✔
1490
                        return false
×
1491
                }
×
1492
                contains, err := subnetIndex.Contains(ip)
1✔
1493
                if err != nil || !contains {
2✔
1494
                        return false
1✔
1495
                }
1✔
1496
        }
1497
        return true
1✔
1498
}
1499

1500
type portRemoteSubnet struct {
1501
        port           *v1net.NetworkPolicyPort
1502
        subnetMap      map[string]bool
1503
        hasNamedTarget bool
1504
}
1505

1506
func updatePortRemoteSubnets(portRemoteSubs map[string]*portRemoteSubnet,
1507
        portkey string, port *v1net.NetworkPolicyPort, subnetMap map[string]bool,
1508
        hasNamedTarget bool) {
1✔
1509
        if prs, ok := portRemoteSubs[portkey]; ok {
1✔
1510
                for s := range subnetMap {
×
1511
                        prs.subnetMap[s] = true
×
1512
                }
×
1513
                prs.hasNamedTarget = hasNamedTarget || prs.hasNamedTarget
×
1514
        } else {
1✔
1515
                portRemoteSubs[portkey] = &portRemoteSubnet{
1✔
1516
                        port:           port,
1✔
1517
                        subnetMap:      subnetMap,
1✔
1518
                        hasNamedTarget: hasNamedTarget,
1✔
1519
                }
1✔
1520
        }
1✔
1521
}
1522

1523
func portServiceAugmentKey(proto, port string) string {
1✔
1524
        return proto + "-" + port
1✔
1525
}
1✔
1526

1527
type portServiceAugment struct {
1528
        proto string
1529
        port  string
1530
        ipMap map[string]bool
1531
}
1532

1533
func updateServiceAugment(portAugments map[string]*portServiceAugment, proto, port, ip string) {
1✔
1534
        key := portServiceAugmentKey(proto, port)
1✔
1535
        if psa, ok := portAugments[key]; ok {
2✔
1536
                psa.ipMap[ip] = true
1✔
1537
        } else {
2✔
1538
                portAugments[key] = &portServiceAugment{
1✔
1539
                        proto: proto,
1✔
1540
                        port:  port,
1✔
1541
                        ipMap: map[string]bool{ip: true},
1✔
1542
                }
1✔
1543
        }
1✔
1544
}
1545

1546
func updateServiceAugmentForService(portAugments map[string]*portServiceAugment,
1547
        proto, port string, service *v1.Service) {
1✔
1548
        if service.Spec.ClusterIP != "" {
2✔
1549
                updateServiceAugment(portAugments,
1✔
1550
                        proto, port, service.Spec.ClusterIP)
1✔
1551
        }
1✔
1552
        for _, ig := range service.Status.LoadBalancer.Ingress {
1✔
1553
                if ig.IP == "" {
×
1554
                        continue
×
1555
                }
1556
                updateServiceAugment(portAugments,
×
1557
                        proto, port, ig.IP)
×
1558
        }
1559
}
1560

1561
// build service augment by matching peers against the endpoints ip
1562
// index
1563
func (cont *AciController) getServiceAugmentBySubnet(
1564
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
1565
        logger *logrus.Entry) {
1✔
1566
        matchedServices := make(map[string]bool)
1✔
1567
        subnetIndex := cidranger.NewPCTrieRanger()
1✔
1568

1✔
1569
        // find candidate service endpoints objects that include
1✔
1570
        // endpoints selected by the egress rule
1✔
1571
        cont.indexMutex.Lock()
1✔
1572
        for sub := range prs.subnetMap {
2✔
1573
                cidr := parseCIDR(sub)
1✔
1574
                if cidr == nil {
1✔
1575
                        continue
×
1576
                }
1577
                subnetIndex.Insert(cidranger.NewBasicRangerEntry(*cidr))
1✔
1578

1✔
1579
                entries, err := cont.endpointsIpIndex.CoveredNetworks(*cidr)
1✔
1580
                if err != nil {
1✔
1581
                        logger.Error("endpointsIpIndex corrupted: ", err)
×
1582
                        continue
×
1583
                }
1584
                for _, entry := range entries {
2✔
1585
                        e := entry.(*ipIndexEntry)
1✔
1586
                        for servicekey := range e.keys {
2✔
1587
                                matchedServices[servicekey] = true
1✔
1588
                        }
1✔
1589
                }
1590
        }
1591
        cont.indexMutex.Unlock()
1✔
1592

1✔
1593
        // if all endpoints are selected by egress rule, allow egress
1✔
1594
        // to the service cluster IP as well as to the endpoints
1✔
1595
        // themselves
1✔
1596
        for servicekey := range matchedServices {
2✔
1597
                serviceobj, _, err := cont.serviceIndexer.GetByKey(servicekey)
1✔
1598
                if err != nil {
1✔
1599
                        logger.Error("Could not lookup service for "+
×
1600
                                servicekey+": ", err.Error())
×
1601
                        continue
×
1602
                }
1603
                if serviceobj == nil {
1✔
1604
                        continue
×
1605
                }
1606
                service := serviceobj.(*v1.Service)
1✔
1607
                cont.serviceEndPoints.SetNpServiceAugmentForService(servicekey, service,
1✔
1608
                        prs, portAugments, subnetIndex, logger)
1✔
1609
        }
1610
}
1611

1612
// build service augment by matching against services with a given
1613
// target port
1614
func (cont *AciController) getServiceAugmentByPort(
1615
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
1616
        logger *logrus.Entry) {
1✔
1617
        // nil port means it matches against all ports.  If we're here, it
1✔
1618
        // means this is a rule that matches all ports with all
1✔
1619
        // destinations, so there's no need to augment anything.
1✔
1620
        if prs.port == nil ||
1✔
1621
                prs.port.Port == nil {
2✔
1622
                return
1✔
1623
        }
1✔
1624

1625
        portkey := portKey(prs.port)
1✔
1626
        cont.indexMutex.Lock()
1✔
1627
        defer cont.indexMutex.Unlock()
1✔
1628
        entries := make(map[string]*portIndexEntry)
1✔
1629
        entry := cont.targetPortIndex[portkey]
1✔
1630
        if entry == nil {
2✔
1631
                return
1✔
1632
        }
1✔
1633
        if prs.port.Port.Type == intstr.String {
2✔
1634
                for port := range entry.port.ports {
2✔
1635
                        portstring := strconv.Itoa(port)
1✔
1636
                        key := portProto(prs.port.Protocol) + "-" + "num" + "-" + portstring
1✔
1637
                        portEntry := cont.targetPortIndex[key]
1✔
1638
                        if portEntry != nil {
2✔
1639
                                entries[portstring] = portEntry
1✔
1640
                        }
1✔
1641
                }
1642
        } else if prs.port.EndPort != nil {
1✔
1643
                startPort := prs.port.Port.IntValue()
×
1644
                endPort := int(*prs.port.EndPort)
×
1645
                rangeSize := endPort - startPort + 1
×
1646
                proto := portProto(prs.port.Protocol)
×
1647
                if rangeSize < len(cont.targetPortIndex) {
×
1648
                        for port := startPort; port <= endPort; port++ {
×
1649
                                portstring := strconv.Itoa(port)
×
1650
                                key := proto + "-num-" + portstring
×
1651
                                portEntry := cont.targetPortIndex[key]
×
1652
                                if portEntry != nil {
×
1653
                                        entries[portstring] = portEntry
×
1654
                                }
×
1655
                        }
1656
                } else {
×
1657
                        protoPrefix := proto + "-num-"
×
1658
                        for portkey, portEntry := range cont.targetPortIndex {
×
1659
                                if !strings.HasPrefix(portkey, protoPrefix) {
×
1660
                                        continue
×
1661
                                }
1662
                                portNumStr := strings.TrimPrefix(portkey, protoPrefix)
×
1663
                                portNum, err := strconv.Atoi(portNumStr)
×
1664
                                if err != nil {
×
1665
                                        continue
×
1666
                                }
1667
                                if portNum >= startPort && portNum <= endPort {
×
1668
                                        portstring := strconv.Itoa(portNum)
×
1669
                                        entries[portstring] = portEntry
×
1670
                                }
×
1671
                        }
1672
                }
1673
                // Look through services with named target ports as well
1674
                for serviceKey, namedSvcEntry := range cont.namedPortServiceIndex {
×
1675
                        for _, svcPortEntry := range *namedSvcEntry {
×
1676
                                // named ports that resolve to a single port number are already handled above while processing the -num- ports in targetPortIndex
×
1677
                                if len(svcPortEntry.resolvedPorts) <= 1 {
×
1678
                                        continue
×
1679
                                }
1680
                                // Check if ALL resolved ports are within the range (all-or-nothing semantics)
1681
                                allInRange := true
×
1682
                                for resolvedPort := range svcPortEntry.resolvedPorts {
×
1683
                                        if resolvedPort < startPort || resolvedPort > endPort {
×
1684
                                                allInRange = false
×
1685
                                                break
×
1686
                                        }
1687
                                }
1688
                                if allInRange {
×
1689
                                        portstring := svcPortEntry.targetPortName
×
1690
                                        if _, ok := entries[portstring]; !ok {
×
1691
                                                entries[portstring] = &portIndexEntry{
×
1692
                                                        serviceKeys: map[string]bool{serviceKey: true},
×
1693
                                                }
×
1694
                                        } else {
×
1695
                                                entries[portstring].serviceKeys[serviceKey] = true
×
1696
                                        }
×
1697
                                }
1698
                        }
1699
                }
1700
        }
1701
        if len(entry.port.ports) > 0 {
2✔
1702
                portString := prs.port.Port.String()
1✔
1703
                entries[portString] = entry
1✔
1704
        }
1✔
1705
        for key, portentry := range entries {
2✔
1706
                for servicekey := range portentry.serviceKeys {
2✔
1707
                        serviceobj, _, err := cont.serviceIndexer.GetByKey(servicekey)
1✔
1708
                        if err != nil {
1✔
1709
                                logger.Error("Could not lookup service for "+
×
1710
                                        servicekey+": ", err.Error())
×
1711
                                continue
×
1712
                        }
1713
                        if serviceobj == nil {
1✔
1714
                                continue
×
1715
                        }
1716
                        service := serviceobj.(*v1.Service)
1✔
1717

1✔
1718
                        for _, svcPort := range service.Spec.Ports {
2✔
1719
                                if svcPort.Protocol != *prs.port.Protocol {
1✔
1720
                                        continue
×
1721
                                }
1722
                                match := false
1✔
1723
                                if indexEntry, ok := cont.namedPortServiceIndex[servicekey]; ok {
2✔
1724
                                        if svcPortIdxEntry, ok := (*indexEntry)[svcPort.Name]; ok && len(svcPortIdxEntry.resolvedPorts) == 1 {
2✔
1725
                                                intKey, error := strconv.Atoi(key)
1✔
1726
                                                if error == nil && svcPortIdxEntry.resolvedPorts[intKey] {
2✔
1727
                                                        match = true
1✔
1728
                                                }
1✔
1729
                                        }
1730
                                }
1731
                                if !match && svcPort.TargetPort.String() != key {
2✔
1732
                                        continue
1✔
1733
                                }
1734
                                proto := portProto(&svcPort.Protocol)
1✔
1735
                                port := strconv.Itoa(int(svcPort.Port))
1✔
1736

1✔
1737
                                updateServiceAugmentForService(portAugments,
1✔
1738
                                        proto, port, service)
1✔
1739

1✔
1740
                                logger.WithFields(logrus.Fields{
1✔
1741
                                        "proto":   proto,
1✔
1742
                                        "port":    port,
1✔
1743
                                        "service": servicekey,
1✔
1744
                                }).Debug("Allowing egress for service by port")
1✔
1745
                        }
1746
                }
1747
        }
1748
}
1749

1750
// The egress NetworkPolicy API were designed with the iptables
1751
// implementation in mind and don't contemplate that the layer 4 load
1752
// balancer could happen separately from the policy.  In particular,
1753
// it expects load balancer operations to be applied before the policy
1754
// is applied in both directions, so network policies would apply only
1755
// to pods and not to service IPs. This presents a problem for egress
1756
// policies on ACI since the security groups are applied before load
1757
// balancer operations when egressing, and after when ingressing.
1758
//
1759
// To solve this problem, we use some indexes to discover situations
1760
// when an egress policy covers all the endpoints associated with a
1761
// particular service, and automatically add a rule that allows egress
1762
// to the corresponding service cluster IP and ports.
1763
//
1764
// Note that this differs slightly from the behavior you'd see if you
1765
// applied the load balancer rule first: If the egress policy allows
1766
// access to a subset of the allowed IPs you'd see random failures
1767
// depending on which destination is chosen, while with this approach
1768
// it's all or nothing.  This should not impact any correctly-written
1769
// network policies.
1770
//
1771
// To do this, we work first from the set of pods and subnets matches
1772
// by the egress policy.  We use this to find using the
1773
// endpointsIpIndex all services that contain at least one of the
1774
// matched pods or subnets.  For each of these candidate services, we
1775
// find service ports for which _all_ referenced endpoints are allowed
1776
// by the egress policy.  Note that a service will have the service
1777
// port and the target port; the NetworkPolicy (confusingly) refers to
1778
// the target port.
1779
//
1780
// Once confirmed matches are found, we augment the egress policy with
1781
// extra rules to allow egress to the service IPs and service ports.
1782
//
1783
// As a special case, for rules that match everything, we also have a
1784
// backup index that works through ports which should allow more
1785
// efficient matching when allowing egress to all.
1786
func (cont *AciController) buildServiceAugment(subj apicapi.ApicObject,
1787
        localsubj *hppv1.HostprotSubj,
1788
        portRemoteSubs map[string]*portRemoteSubnet, logger *logrus.Entry) {
1✔
1789
        portAugments := make(map[string]*portServiceAugment)
1✔
1790
        for _, prs := range portRemoteSubs {
2✔
1791
                // TODO ipv6
1✔
1792
                if prs.subnetMap["0.0.0.0/0"] {
2✔
1793
                        cont.getServiceAugmentByPort(prs, portAugments, logger)
1✔
1794
                } else {
2✔
1795
                        cont.getServiceAugmentBySubnet(prs, portAugments, logger)
1✔
1796
                }
1✔
1797
        }
1798
        for _, augment := range portAugments {
2✔
1799
                var remoteIpsv4 []string
1✔
1800
                var remoteIpsv6 []string
1✔
1801
                for ipstr := range augment.ipMap {
2✔
1802
                        ip := net.ParseIP(ipstr)
1✔
1803
                        if ip == nil {
1✔
1804
                                continue
×
1805
                        } else if ip.To4() != nil {
2✔
1806
                                remoteIpsv4 = append(remoteIpsv4, ipstr)
1✔
1807
                        } else if ip.To16() != nil {
3✔
1808
                                remoteIpsv6 = append(remoteIpsv6, ipstr)
1✔
1809
                        }
1✔
1810
                }
1811
                cont.log.Debug("Service Augment: ", augment)
1✔
1812
                if !cont.config.EnableHppDirect && subj != nil {
2✔
1813
                        if len(remoteIpsv4) > 0 {
2✔
1814
                                serviceName := fmt.Sprintf("service_%s_%s-ipv4", augment.proto, augment.port)
1✔
1815
                                cont.buildNetPolSubjRule(subj,
1✔
1816
                                        serviceName,
1✔
1817
                                        "egress", "ipv4", augment.proto, augment.port, "", remoteIpsv4, false)
1✔
1818
                        }
1✔
1819
                        if len(remoteIpsv6) > 0 {
2✔
1820
                                serviceName := fmt.Sprintf("service_%s_%s-ipv6", augment.proto, augment.port)
1✔
1821
                                cont.buildNetPolSubjRule(subj,
1✔
1822
                                        serviceName,
1✔
1823
                                        "egress", "ipv6", augment.proto, augment.port, "", remoteIpsv6, false)
1✔
1824
                        }
1✔
1825
                } else if cont.config.EnableHppDirect && localsubj != nil {
2✔
1826
                        if len(remoteIpsv4) > 0 {
2✔
1827
                                cont.buildLocalNetPolSubjRule(localsubj,
1✔
1828
                                        "service_"+augment.proto+"_"+augment.port,
1✔
1829
                                        "egress", "ipv4", augment.proto, augment.port, "", nil, nil, remoteIpsv4)
1✔
1830
                        }
1✔
1831
                        if len(remoteIpsv6) > 0 {
1✔
1832
                                cont.buildLocalNetPolSubjRule(localsubj,
×
1833
                                        "service_"+augment.proto+"_"+augment.port,
×
1834
                                        "egress", "ipv6", augment.proto, augment.port, "", nil, nil, remoteIpsv6)
×
1835
                        }
×
1836
                }
1837
        }
1838
}
1839

1840
func isAllowAllForAllNamespaces(peers []v1net.NetworkPolicyPeer) bool {
1✔
1841
        addPodSubnetAsRemIp := false
1✔
1842
        if peers != nil && len(peers) > 0 {
2✔
1843
                var emptyPodSel, emptyNsSel bool
1✔
1844
                emptyPodSel = true
1✔
1845
                for _, peer := range peers {
2✔
1846
                        // namespaceSelector: {}
1✔
1847
                        if peer.NamespaceSelector != nil && peer.NamespaceSelector.MatchLabels == nil && peer.NamespaceSelector.MatchExpressions == nil {
1✔
1848
                                emptyNsSel = true
×
1849
                        }
×
1850
                        // podSelector has some fields
1851
                        if peer.PodSelector != nil && (peer.PodSelector.MatchLabels != nil || peer.PodSelector.MatchExpressions != nil) {
2✔
1852
                                emptyPodSel = false
1✔
1853
                        }
1✔
1854
                }
1855
                if emptyNsSel && emptyPodSel {
1✔
1856
                        addPodSubnetAsRemIp = true
×
1857
                }
×
1858
        }
1859
        return addPodSubnetAsRemIp
1✔
1860
}
1861

1862
func (cont *AciController) handleRemIpContUpdate(ns string) bool {
1✔
1863
        cont.hppMutex.Lock()
1✔
1864
        defer cont.hppMutex.Unlock()
1✔
1865

1✔
1866
        sysNs := os.Getenv("SYSTEM_NAMESPACE")
1✔
1867
        aobj, err := cont.getHostprotRemoteIpContainer(ns, sysNs)
1✔
1868
        isUpdate := err == nil
1✔
1869

1✔
1870
        if err != nil && !errors.IsNotFound(err) {
1✔
1871
                cont.log.Error("Error getting HostprotRemoteIpContainers CR: ", err)
×
1872
                return true
×
1873
        }
×
1874

1875
        if !isUpdate {
2✔
1876
                aobj = &hppv1.HostprotRemoteIpContainer{
1✔
1877
                        ObjectMeta: metav1.ObjectMeta{
1✔
1878
                                Name:      ns,
1✔
1879
                                Namespace: sysNs,
1✔
1880
                        },
1✔
1881
                        Spec: hppv1.HostprotRemoteIpContainerSpec{
1✔
1882
                                Name:             ns,
1✔
1883
                                HostprotRemoteIp: []hppv1.HostprotRemoteIp{},
1✔
1884
                        },
1✔
1885
                }
1✔
1886
        } else {
1✔
1887
                cont.log.Debug("HostprotRemoteIpContainers CR already exists: ", aobj)
×
1888
        }
×
1889

1890
        remIpCont, exists := cont.nsRemoteIpCont[ns]
1✔
1891
        if !exists {
2✔
1892
                if isUpdate {
1✔
1893
                        if !cont.deleteHostprotRemoteIpContainer(ns, sysNs) {
×
1894
                                return true
×
1895
                        }
×
1896
                } else {
1✔
1897
                        cont.log.Error("Couldn't find the ns in nsRemoteIpCont cache: ", ns)
1✔
1898
                        return false
1✔
1899
                }
1✔
NEW
1900
                return false
×
1901
        }
1902

1903
        aobj.Spec.HostprotRemoteIp = buildHostprotRemoteIpList(remIpCont)
1✔
1904

1✔
1905
        if isUpdate {
1✔
1906
                if !cont.updateHostprotRemoteIpContainer(aobj, sysNs) {
×
1907
                        return true
×
1908
                }
×
1909
        } else {
1✔
1910
                if !cont.createHostprotRemoteIpContainer(aobj, sysNs) {
1✔
1911
                        return true
×
1912
                }
×
1913
        }
1914

1915
        return false
1✔
1916
}
1917

1918
func buildHostprotRemoteIpList(remIpConts map[string]remoteIpCont) []hppv1.HostprotRemoteIp {
1✔
1919
        hostprotRemoteIpList := []hppv1.HostprotRemoteIp{}
1✔
1920

1✔
1921
        for _, remIpCont := range remIpConts {
2✔
1922
                for ip, labels := range remIpCont {
2✔
1923
                        remIpObj := hppv1.HostprotRemoteIp{
1✔
1924
                                Addr: ip,
1✔
1925
                        }
1✔
1926
                        for key, val := range labels {
2✔
1927
                                remIpObj.HppEpLabel = append(remIpObj.HppEpLabel, hppv1.HppEpLabel{
1✔
1928
                                        Key:   key,
1✔
1929
                                        Value: val,
1✔
1930
                                })
1✔
1931
                        }
1✔
1932
                        hostprotRemoteIpList = append(hostprotRemoteIpList, remIpObj)
1✔
1933
                }
1934
        }
1935

1936
        return hostprotRemoteIpList
1✔
1937
}
1938

1939
func (cont *AciController) deleteHppCr(np *v1net.NetworkPolicy) bool {
1✔
1940
        key, err := cache.MetaNamespaceKeyFunc(np)
1✔
1941
        logger := networkPolicyLogger(cont.log, np)
1✔
1942
        if err != nil {
1✔
1943
                logger.Error("Could not create network policy key: ", err)
×
1944
                return false
×
1945
        }
×
1946
        hash, err := util.CreateHashFromNetPol(np)
1✔
1947
        if err != nil {
1✔
1948
                logger.Error("Could not create hash from network policy: ", err)
×
1949
                return false
×
1950
        }
×
1951
        labelKey := cont.aciNameForKey("np", hash)
1✔
1952
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
1953
        hppName := strings.ReplaceAll(labelKey, "_", "-")
1✔
1954
        hpp, _ := cont.getHostprotPol(hppName, ns)
1✔
1955
        if hpp == nil {
2✔
1956
                logger.Error("Could not find hostprotPol: ", hppName)
1✔
1957
                return false
1✔
1958
        }
1✔
1959
        netPols := hpp.Spec.NetworkPolicies
1✔
1960
        newNetPols := make([]string, 0)
1✔
1961
        for _, npName := range netPols {
2✔
1962
                if npName != key {
2✔
1963
                        newNetPols = append(newNetPols, npName)
1✔
1964
                }
1✔
1965
        }
1966

1967
        hpp.Spec.NetworkPolicies = newNetPols
1✔
1968

1✔
1969
        if len(newNetPols) > 0 {
2✔
1970
                return cont.updateHostprotPol(hpp, ns)
1✔
1971
        } else {
2✔
1972
                return cont.deleteHostprotPol(hppName, ns)
1✔
1973
        }
1✔
1974
}
1975

1976
func (cont *AciController) updateNodeIpsHostprotRemoteIpContainer(nodeIps map[string]bool) {
1✔
1977
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
1978
        name := "nodeips"
1✔
1979

1✔
1980
        aobj, err := cont.getHostprotRemoteIpContainer(name, ns)
1✔
1981
        isUpdate := err == nil
1✔
1982

1✔
1983
        if err != nil && !errors.IsNotFound(err) {
1✔
1984
                cont.log.Error("Error getting HostprotRemoteIpContainers CR: ", err)
×
1985
                return
×
1986
        }
×
1987

1988
        if !isUpdate {
2✔
1989
                aobj = &hppv1.HostprotRemoteIpContainer{
1✔
1990
                        ObjectMeta: metav1.ObjectMeta{
1✔
1991
                                Name:      name,
1✔
1992
                                Namespace: ns,
1✔
1993
                        },
1✔
1994
                        Spec: hppv1.HostprotRemoteIpContainerSpec{
1✔
1995
                                Name:             name,
1✔
1996
                                HostprotRemoteIp: []hppv1.HostprotRemoteIp{},
1✔
1997
                        },
1✔
1998
                }
1✔
1999
        } else {
2✔
2000
                cont.log.Debug("HostprotRemoteIpContainers CR already exists: ", aobj)
1✔
2001
        }
1✔
2002

2003
        existingIps := make(map[string]bool)
1✔
2004
        for _, ip := range aobj.Spec.HostprotRemoteIp {
2✔
2005
                existingIps[ip.Addr] = true
1✔
2006
        }
1✔
2007

2008
        for ip := range nodeIps {
2✔
2009
                if !existingIps[ip] {
2✔
2010
                        aobj.Spec.HostprotRemoteIp = append(aobj.Spec.HostprotRemoteIp, hppv1.HostprotRemoteIp{Addr: ip})
1✔
2011
                }
1✔
2012
        }
2013

2014
        if isUpdate {
2✔
2015
                cont.updateHostprotRemoteIpContainer(aobj, ns)
1✔
2016
        } else {
2✔
2017
                cont.createHostprotRemoteIpContainer(aobj, ns)
1✔
2018
        }
1✔
2019
}
2020

2021
func (cont *AciController) deleteNodeIpsHostprotRemoteIpContainer(nodeIps map[string]bool) {
1✔
2022
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2023
        name := "nodeips"
1✔
2024

1✔
2025
        aobj, _ := cont.getHostprotRemoteIpContainer(name, ns)
1✔
2026
        if aobj == nil {
1✔
2027
                return
×
2028
        }
×
2029

2030
        newHostprotRemoteIps := aobj.Spec.HostprotRemoteIp[:0]
1✔
2031
        for _, hostprotRemoteIp := range aobj.Spec.HostprotRemoteIp {
2✔
2032
                if len(nodeIps) > 0 && !nodeIps[hostprotRemoteIp.Addr] {
2✔
2033
                        newHostprotRemoteIps = append(newHostprotRemoteIps, hostprotRemoteIp)
1✔
2034
                }
1✔
2035
        }
2036

2037
        aobj.Spec.HostprotRemoteIp = newHostprotRemoteIps
1✔
2038

1✔
2039
        if len(newHostprotRemoteIps) > 0 {
2✔
2040
                cont.updateHostprotRemoteIpContainer(aobj, ns)
1✔
2041
        } else {
2✔
2042
                cont.deleteHostprotRemoteIpContainer(name, ns)
1✔
2043
        }
1✔
2044
}
2045

2046
func (cont *AciController) updateNodeHostprotRemoteIpContainer(name string, nodeIps map[string]bool) {
1✔
2047
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2048

1✔
2049
        aobj, err := cont.getHostprotRemoteIpContainer(name, ns)
1✔
2050
        isUpdate := err == nil
1✔
2051

1✔
2052
        if err != nil && !errors.IsNotFound(err) {
1✔
2053
                cont.log.Error("Error getting HostprotRemoteIpContainers CR: ", err)
×
2054
                return
×
2055
        }
×
2056

2057
        if !isUpdate {
2✔
2058
                aobj = &hppv1.HostprotRemoteIpContainer{
1✔
2059
                        ObjectMeta: metav1.ObjectMeta{
1✔
2060
                                Name:      name,
1✔
2061
                                Namespace: ns,
1✔
2062
                        },
1✔
2063
                        Spec: hppv1.HostprotRemoteIpContainerSpec{
1✔
2064
                                Name:             name,
1✔
2065
                                HostprotRemoteIp: []hppv1.HostprotRemoteIp{},
1✔
2066
                        },
1✔
2067
                }
1✔
2068
        } else {
2✔
2069
                cont.log.Debug("HostprotRemoteIpContainers CR already exists: ", aobj)
1✔
2070
        }
1✔
2071

2072
        aobj.Spec.HostprotRemoteIp = make([]hppv1.HostprotRemoteIp, 0, len(nodeIps))
1✔
2073
        for ip := range nodeIps {
2✔
2074
                aobj.Spec.HostprotRemoteIp = append(aobj.Spec.HostprotRemoteIp, hppv1.HostprotRemoteIp{Addr: ip})
1✔
2075
        }
1✔
2076

2077
        if isUpdate {
2✔
2078
                cont.updateHostprotRemoteIpContainer(aobj, ns)
1✔
2079
        } else {
2✔
2080
                cont.createHostprotRemoteIpContainer(aobj, ns)
1✔
2081
        }
1✔
2082
}
2083

2084
func (cont *AciController) deleteNodeHostprotRemoteIpContainer(name string) {
1✔
2085
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2086

1✔
2087
        if _, err := cont.getHostprotRemoteIpContainer(name, ns); err == nil {
2✔
2088
                cont.deleteHostprotRemoteIpContainer(name, ns)
1✔
2089
        }
1✔
2090
}
2091

2092
func (cont *AciController) createNodeHostProtPol(name, nodeName string, nodeIps map[string]bool) {
1✔
2093
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2094
        hppName := strings.ReplaceAll(name, "_", "-")
1✔
2095

1✔
2096
        hpp, err := cont.getHostprotPol(hppName, ns)
1✔
2097
        isUpdate := hpp != nil && err == nil
1✔
2098

1✔
2099
        if err != nil && !errors.IsNotFound(err) {
1✔
2100
                cont.log.Error("Error getting HPP CR: ", err)
×
2101
                return
×
2102
        }
×
2103

2104
        if !isUpdate {
1✔
2105
                hpp = &hppv1.HostprotPol{
×
2106
                        ObjectMeta: metav1.ObjectMeta{
×
2107
                                Name:      hppName,
×
2108
                                Namespace: ns,
×
2109
                        },
×
2110
                        Spec: hppv1.HostprotPolSpec{
×
2111
                                Name:            name,
×
2112
                                NetworkPolicies: []string{name},
×
2113
                                HostprotSubj:    []hppv1.HostprotSubj{},
×
2114
                        },
×
2115
                }
×
2116
        } else {
1✔
2117
                cont.log.Debug("HPP CR already exists: ", hpp)
1✔
2118
                hpp.Spec.HostprotSubj = []hppv1.HostprotSubj{}
1✔
2119
        }
1✔
2120

2121
        if len(nodeIps) > 0 {
2✔
2122
                cont.updateNodeHostprotRemoteIpContainer(nodeName, nodeIps)
1✔
2123
                cont.updateNodeIpsHostprotRemoteIpContainer(nodeIps)
1✔
2124

1✔
2125
                hostprotSubj := hppv1.HostprotSubj{
1✔
2126
                        Name: "local-node",
1✔
2127
                        HostprotRule: []hppv1.HostprotRule{
1✔
2128
                                {
1✔
2129
                                        Name:                "allow-all-egress",
1✔
2130
                                        Direction:           "egress",
1✔
2131
                                        Ethertype:           "ipv4",
1✔
2132
                                        ConnTrack:           "normal",
1✔
2133
                                        RsRemoteIpContainer: []string{nodeName},
1✔
2134
                                },
1✔
2135
                                {
1✔
2136
                                        Name:                "allow-all-ingress",
1✔
2137
                                        Direction:           "ingress",
1✔
2138
                                        Ethertype:           "ipv4",
1✔
2139
                                        ConnTrack:           "normal",
1✔
2140
                                        RsRemoteIpContainer: []string{nodeName},
1✔
2141
                                },
1✔
2142
                        },
1✔
2143
                }
1✔
2144

1✔
2145
                hpp.Spec.HostprotSubj = append(hpp.Spec.HostprotSubj, hostprotSubj)
1✔
2146
        } else {
2✔
2147
                cont.deleteNodeHostprotRemoteIpContainer(nodeName)
1✔
2148
                cont.deleteNodeIpsHostprotRemoteIpContainer(nodeIps)
1✔
2149
        }
1✔
2150

2151
        if isUpdate {
2✔
2152
                cont.updateHostprotPol(hpp, ns)
1✔
2153
        } else {
1✔
2154
                cont.createHostprotPol(hpp, ns)
×
2155
        }
×
2156
}
2157

2158
func (cont *AciController) handleNetPolUpdate(np *v1net.NetworkPolicy) bool {
1✔
2159
        if cont.isCNOEnabled() {
1✔
2160
                return false
×
2161
        }
×
2162
        key, err := cache.MetaNamespaceKeyFunc(np)
1✔
2163
        logger := networkPolicyLogger(cont.log, np)
1✔
2164
        if err != nil {
1✔
2165
                logger.Error("Could not create network policy key: ", err)
×
2166
                return false
×
2167
        }
×
2168

2169
        peerPodKeys := cont.netPolIngressPods.GetPodForObj(key)
1✔
2170
        peerPodKeys =
1✔
2171
                append(peerPodKeys, cont.netPolEgressPods.GetPodForObj(key)...)
1✔
2172
        var peerPods []*v1.Pod
1✔
2173
        peerNs := make(map[string]*v1.Namespace)
1✔
2174
        for _, podkey := range peerPodKeys {
2✔
2175
                podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
2176
                if exists && err == nil {
2✔
2177
                        pod := podobj.(*v1.Pod)
1✔
2178
                        if _, nsok := peerNs[pod.ObjectMeta.Namespace]; !nsok {
2✔
2179
                                nsobj, exists, err :=
1✔
2180
                                        cont.namespaceIndexer.GetByKey(pod.ObjectMeta.Namespace)
1✔
2181
                                if !exists || err != nil {
1✔
2182
                                        continue
×
2183
                                }
2184
                                peerNs[pod.ObjectMeta.Namespace] = nsobj.(*v1.Namespace)
1✔
2185
                        }
2186
                        peerPods = append(peerPods, pod)
1✔
2187
                }
2188
        }
2189
        ptypeset := make(map[v1net.PolicyType]bool)
1✔
2190
        for _, t := range np.Spec.PolicyTypes {
2✔
2191
                ptypeset[t] = true
1✔
2192
        }
1✔
2193
        var labelKey string
1✔
2194

1✔
2195
        if !cont.config.EnableHppDirect {
2✔
2196
                if cont.config.HppOptimization {
2✔
2197
                        hash, err := util.CreateHashFromNetPol(np)
1✔
2198
                        if err != nil {
1✔
2199
                                logger.Error("Could not create hash from network policy: ", err)
×
2200
                                return false
×
2201
                        }
×
2202
                        labelKey = cont.aciNameForKey("np", hash)
1✔
2203
                } else {
1✔
2204
                        labelKey = cont.aciNameForKey("np", key)
1✔
2205
                }
1✔
2206
                hpp := apicapi.NewHostprotPol(cont.config.AciPolicyTenant, labelKey)
1✔
2207
                // Generate ingress policies
1✔
2208
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeIngress] {
2✔
2209
                        subjIngress :=
1✔
2210
                                apicapi.NewHostprotSubj(hpp.GetDn(), "networkpolicy-ingress")
1✔
2211

1✔
2212
                        for i, ingress := range np.Spec.Ingress {
2✔
2213
                                addPodSubnetAsRemIp := isAllowAllForAllNamespaces(ingress.From)
1✔
2214
                                remoteSubnets, _, _, _, _ := cont.getPeerRemoteSubnets(ingress.From,
1✔
2215
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2216
                                cont.buildNetPolSubjRules(strconv.Itoa(i), subjIngress,
1✔
2217
                                        "ingress", ingress.From, remoteSubnets, ingress.Ports, logger, key, np, addPodSubnetAsRemIp)
1✔
2218
                        }
1✔
2219
                        hpp.AddChild(subjIngress)
1✔
2220
                }
2221
                // Generate egress policies
2222
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeEgress] {
2✔
2223
                        subjEgress :=
1✔
2224
                                apicapi.NewHostprotSubj(hpp.GetDn(), "networkpolicy-egress")
1✔
2225

1✔
2226
                        portRemoteSubs := make(map[string]*portRemoteSubnet)
1✔
2227

1✔
2228
                        for i, egress := range np.Spec.Egress {
2✔
2229
                                addPodSubnetAsRemIp := isAllowAllForAllNamespaces(egress.To)
1✔
2230
                                remoteSubnets, _, _, subnetMap, _ := cont.getPeerRemoteSubnets(egress.To,
1✔
2231
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2232
                                cont.buildNetPolSubjRules(strconv.Itoa(i), subjEgress,
1✔
2233
                                        "egress", egress.To, remoteSubnets, egress.Ports, logger, key, np, addPodSubnetAsRemIp)
1✔
2234

1✔
2235
                                // creating a rule to egress to all on a given port needs
1✔
2236
                                // to enable access to any service IPs/ports that have
1✔
2237
                                // that port as their target port.
1✔
2238
                                if len(egress.To) == 0 {
2✔
2239
                                        subnetMap = map[string]bool{
1✔
2240
                                                "0.0.0.0/0": true,
1✔
2241
                                        }
1✔
2242
                                }
1✔
2243
                                for idx := range egress.Ports {
2✔
2244
                                        port := egress.Ports[idx]
1✔
2245
                                        portkey := portKey(&port)
1✔
2246
                                        updatePortRemoteSubnets(portRemoteSubs, portkey, &port, subnetMap,
1✔
2247
                                                port.Port != nil && port.Port.Type == intstr.Int)
1✔
2248
                                }
1✔
2249
                                if len(egress.Ports) == 0 {
2✔
2250
                                        updatePortRemoteSubnets(portRemoteSubs, "", nil, subnetMap,
1✔
2251
                                                false)
1✔
2252
                                }
1✔
2253
                        }
2254
                        cont.buildServiceAugment(subjEgress, nil, portRemoteSubs, logger)
1✔
2255
                        hpp.AddChild(subjEgress)
1✔
2256
                }
2257
                if cont.config.HppOptimization {
2✔
2258
                        cont.addToHppCache(labelKey, key, apicapi.ApicSlice{hpp}, &hppv1.HostprotPol{})
1✔
2259
                }
1✔
2260
                cont.apicConn.WriteApicObjects(labelKey, apicapi.ApicSlice{hpp})
1✔
2261
        } else {
1✔
2262
                hash, err := util.CreateHashFromNetPol(np)
1✔
2263
                if err != nil {
1✔
2264
                        logger.Error("Could not create hash from network policy: ", err)
×
2265
                        return false
×
2266
                }
×
2267
                labelKey = cont.aciNameForKey("np", hash)
1✔
2268
                ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2269
                hppName := strings.ReplaceAll(labelKey, "_", "-")
1✔
2270
                hpp, err := cont.getHostprotPol(hppName, ns)
1✔
2271
                isUpdate := err == nil
1✔
2272

1✔
2273
                if err != nil && !errors.IsNotFound(err) {
1✔
2274
                        logger.Error("Error getting HPP CR: ", err)
×
2275
                        return false
×
2276
                }
×
2277

2278
                if isUpdate {
1✔
2279
                        logger.Debug("HPP CR already exists: ", hpp)
×
2280
                        if !slices.Contains(hpp.Spec.NetworkPolicies, key) {
×
2281
                                hpp.Spec.NetworkPolicies = append(hpp.Spec.NetworkPolicies, key)
×
2282
                        }
×
2283
                        hpp.Spec.HostprotSubj = nil
×
2284
                } else {
1✔
2285
                        hpp = &hppv1.HostprotPol{
1✔
2286
                                ObjectMeta: metav1.ObjectMeta{
1✔
2287
                                        Name:      hppName,
1✔
2288
                                        Namespace: ns,
1✔
2289
                                },
1✔
2290
                                Spec: hppv1.HostprotPolSpec{
1✔
2291
                                        Name:            labelKey,
1✔
2292
                                        NetworkPolicies: []string{key},
1✔
2293
                                        HostprotSubj:    nil,
1✔
2294
                                },
1✔
2295
                        }
1✔
2296
                }
1✔
2297

2298
                // Generate ingress policies
2299
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeIngress] {
2✔
2300
                        subjIngress := &hppv1.HostprotSubj{
1✔
2301
                                Name:         "networkpolicy-ingress",
1✔
2302
                                HostprotRule: []hppv1.HostprotRule{},
1✔
2303
                        }
1✔
2304

1✔
2305
                        for i, ingress := range np.Spec.Ingress {
2✔
2306
                                remoteSubnets, peerNsList, peerremote, _, peerIpBlock := cont.getPeerRemoteSubnets(ingress.From,
1✔
2307
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2308
                                if isAllowAllForAllNamespaces(ingress.From) {
1✔
2309
                                        peerNsList = append(peerNsList, "nodeips")
×
2310
                                }
×
2311
                                if !(len(ingress.From) > 0 && len(remoteSubnets) == 0) {
2✔
2312
                                        cont.buildLocalNetPolSubjRules(strconv.Itoa(i), subjIngress,
1✔
2313
                                                "ingress", peerNsList, peerremote.podSelectors, ingress.Ports,
1✔
2314
                                                logger, key, np, peerIpBlock)
1✔
2315
                                }
1✔
2316
                        }
2317
                        hpp.Spec.HostprotSubj = append(hpp.Spec.HostprotSubj, *subjIngress)
1✔
2318
                }
2319

2320
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeEgress] {
2✔
2321
                        subjEgress := &hppv1.HostprotSubj{
1✔
2322
                                Name:         "networkpolicy-egress",
1✔
2323
                                HostprotRule: []hppv1.HostprotRule{},
1✔
2324
                        }
1✔
2325

1✔
2326
                        portRemoteSubs := make(map[string]*portRemoteSubnet)
1✔
2327

1✔
2328
                        for i, egress := range np.Spec.Egress {
2✔
2329
                                remoteSubnets, peerNsList, peerremote, subnetMap, peerIpBlock := cont.getPeerRemoteSubnets(egress.To,
1✔
2330
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2331
                                if isAllowAllForAllNamespaces(egress.To) {
1✔
2332
                                        peerNsList = append(peerNsList, "nodeips")
×
2333
                                }
×
2334
                                if !(len(egress.To) > 0 && len(remoteSubnets) == 0) {
2✔
2335
                                        cont.buildLocalNetPolSubjRules(strconv.Itoa(i), subjEgress,
1✔
2336
                                                "egress", peerNsList, peerremote.podSelectors, egress.Ports, logger, key, np, peerIpBlock)
1✔
2337
                                }
1✔
2338

2339
                                if len(egress.To) == 0 {
2✔
2340
                                        subnetMap = map[string]bool{"0.0.0.0/0": true}
1✔
2341
                                }
1✔
2342
                                for idx := range egress.Ports {
2✔
2343
                                        port := egress.Ports[idx]
1✔
2344
                                        portkey := portKey(&port)
1✔
2345
                                        updatePortRemoteSubnets(portRemoteSubs, portkey, &port, subnetMap,
1✔
2346
                                                port.Port != nil && port.Port.Type == intstr.Int)
1✔
2347
                                }
1✔
2348
                                if len(egress.Ports) == 0 {
1✔
2349
                                        updatePortRemoteSubnets(portRemoteSubs, "", nil, subnetMap,
×
2350
                                                false)
×
2351
                                }
×
2352
                        }
2353
                        cont.buildServiceAugment(nil, subjEgress, portRemoteSubs, logger)
1✔
2354
                        hpp.Spec.HostprotSubj = append(hpp.Spec.HostprotSubj, *subjEgress)
1✔
2355
                }
2356

2357
                cont.addToHppCache(labelKey, key, apicapi.ApicSlice{}, hpp)
1✔
2358

1✔
2359
                if isUpdate {
1✔
2360
                        cont.updateHostprotPol(hpp, ns)
×
2361
                } else {
1✔
2362
                        cont.createHostprotPol(hpp, ns)
1✔
2363
                }
1✔
2364
        }
2365
        return false
1✔
2366
}
2367

2368
func (cont *AciController) updateNsRemoteIpCont(pod *v1.Pod, deleted bool) bool {
1✔
2369
        podips := ipsForPod(pod)
1✔
2370
        podns := pod.ObjectMeta.Namespace
1✔
2371
        podname := pod.ObjectMeta.Name
1✔
2372
        podlabels := pod.ObjectMeta.Labels
1✔
2373
        remipconts, ok := cont.nsRemoteIpCont[podns]
1✔
2374

1✔
2375
        if deleted {
2✔
2376
                if !ok {
2✔
2377
                        return true
1✔
2378
                }
1✔
2379

2380
                present := false
1✔
2381
                if remipcont, remipcontok := remipconts[podname]; remipcontok {
1✔
2382
                        for _, ip := range podips {
×
2383
                                if _, ipok := remipcont[ip]; ipok {
×
2384
                                        delete(remipcont, ip)
×
2385
                                        present = true
×
2386
                                }
×
2387
                        }
2388
                        if len(remipcont) < 1 {
×
2389
                                delete(remipconts, podname)
×
2390
                        }
×
2391
                }
2392

2393
                if len(remipconts) < 1 {
1✔
2394
                        delete(cont.nsRemoteIpCont, podns)
×
2395
                        cont.apicConn.ClearApicObjects(cont.aciNameForKey("hostprot-ns-", podns))
×
2396
                        return false
×
2397
                }
×
2398

2399
                if !present {
2✔
2400
                        return false
1✔
2401
                }
1✔
2402
        } else {
1✔
2403
                if !ok {
2✔
2404
                        remipconts = make(remoteIpConts)
1✔
2405
                        cont.nsRemoteIpCont[podns] = remipconts
1✔
2406
                }
1✔
2407

2408
                remipcont, remipcontok := remipconts[podname]
1✔
2409
                if !remipcontok {
2✔
2410
                        remipcont = make(remoteIpCont)
1✔
2411
                }
1✔
2412
                for _, ip := range podips {
2✔
2413
                        remipcont[ip] = podlabels
1✔
2414
                }
1✔
2415
                remipconts[podname] = remipcont
1✔
2416
        }
2417

2418
        return true
1✔
2419
}
2420

2421
func (cont *AciController) addToHppCache(labelKey, key string, hpp apicapi.ApicSlice, hppcr *hppv1.HostprotPol) {
1✔
2422
        cont.indexMutex.Lock()
1✔
2423
        hppRef, ok := cont.hppRef[labelKey]
1✔
2424
        if ok {
2✔
2425
                var found bool
1✔
2426
                for _, npkey := range hppRef.Npkeys {
2✔
2427
                        if npkey == key {
2✔
2428
                                found = true
1✔
2429
                                break
1✔
2430
                        }
2431
                }
2432
                if !found {
1✔
2433
                        hppRef.RefCount++
×
2434
                        hppRef.Npkeys = append(hppRef.Npkeys, key)
×
2435
                }
×
2436
                hppRef.HppObj = hpp
1✔
2437
                hppRef.HppCr = *hppcr
1✔
2438
                cont.hppRef[labelKey] = hppRef
1✔
2439
        } else {
1✔
2440
                var newHppRef hppReference
1✔
2441
                newHppRef.RefCount++
1✔
2442
                newHppRef.HppObj = hpp
1✔
2443
                newHppRef.HppCr = *hppcr
1✔
2444
                newHppRef.Npkeys = append(newHppRef.Npkeys, key)
1✔
2445
                cont.hppRef[labelKey] = newHppRef
1✔
2446
        }
1✔
2447
        cont.indexMutex.Unlock()
1✔
2448
}
2449

2450
func (cont *AciController) removeFromHppCache(np *v1net.NetworkPolicy, key string) (string, bool) {
1✔
2451
        var labelKey string
1✔
2452
        var noRef bool
1✔
2453
        hash, err := util.CreateHashFromNetPol(np)
1✔
2454
        if err != nil {
1✔
2455
                cont.log.Error("Could not create hash from network policy: ", err)
×
2456
                cont.log.Error("Failed to remove np from hpp cache")
×
2457
                return labelKey, noRef
×
2458
        }
×
2459
        labelKey = cont.aciNameForKey("np", hash)
1✔
2460
        cont.indexMutex.Lock()
1✔
2461
        hppRef, ok := cont.hppRef[labelKey]
1✔
2462
        if ok {
2✔
2463
                for i, npkey := range hppRef.Npkeys {
2✔
2464
                        if npkey == key {
2✔
2465
                                hppRef.Npkeys = append(hppRef.Npkeys[:i], hppRef.Npkeys[i+1:]...)
1✔
2466
                                hppRef.RefCount--
1✔
2467
                                break
1✔
2468
                        }
2469
                }
2470
                if hppRef.RefCount > 0 {
1✔
2471
                        cont.hppRef[labelKey] = hppRef
×
2472
                } else {
1✔
2473
                        delete(cont.hppRef, labelKey)
1✔
2474
                        noRef = true
1✔
2475
                }
1✔
2476
        }
2477
        cont.indexMutex.Unlock()
1✔
2478
        return labelKey, noRef
1✔
2479
}
2480

2481
func getNetworkPolicyEgressIpBlocks(np *v1net.NetworkPolicy) map[string]bool {
1✔
2482
        subnets := make(map[string]bool)
1✔
2483
        for _, egress := range np.Spec.Egress {
2✔
2484
                for _, to := range egress.To {
2✔
2485
                        if to.IPBlock != nil && to.IPBlock.CIDR != "" {
2✔
2486
                                subnets[to.IPBlock.CIDR] = true
1✔
2487
                        }
1✔
2488
                }
2489
        }
2490
        return subnets
1✔
2491
}
2492

2493
func (cont *AciController) networkPolicyAdded(obj interface{}) {
1✔
2494
        np := obj.(*v1net.NetworkPolicy)
1✔
2495
        npkey, err := cache.MetaNamespaceKeyFunc(np)
1✔
2496
        if err != nil {
1✔
2497
                networkPolicyLogger(cont.log, np).
×
2498
                        Error("Could not create network policy key: ", err)
×
2499
                return
×
2500
        }
×
2501
        if cont.isCNOEnabled() {
1✔
2502
                return
×
2503
        }
×
2504
        cont.netPolPods.UpdateSelectorObj(obj)
1✔
2505
        cont.netPolIngressPods.UpdateSelectorObj(obj)
1✔
2506
        cont.netPolEgressPods.UpdateSelectorObj(obj)
1✔
2507
        cont.indexMutex.Lock()
1✔
2508
        subnets := getNetworkPolicyEgressIpBlocks(np)
1✔
2509
        cont.updateIpIndex(cont.netPolSubnetIndex, nil, subnets, npkey)
1✔
2510

1✔
2511
        ports := cont.getNetPolTargetPorts(np)
1✔
2512
        cont.updateTargetPortIndex(false, npkey, nil, ports)
1✔
2513
        if isNamedPortPresenInNp(np) {
2✔
2514
                cont.nmPortNp[npkey] = true
1✔
2515
        }
1✔
2516
        cont.indexMutex.Unlock()
1✔
2517
        cont.queueNetPolUpdateByKey(npkey)
1✔
2518
}
2519

2520
func (cont *AciController) networkPolicyChanged(oldobj interface{},
2521
        newobj interface{}) {
×
2522
        oldnp := oldobj.(*v1net.NetworkPolicy)
×
2523
        newnp := newobj.(*v1net.NetworkPolicy)
×
2524
        npkey, err := cache.MetaNamespaceKeyFunc(newnp)
×
2525
        if err != nil {
×
2526
                networkPolicyLogger(cont.log, newnp).
×
2527
                        Error("Could not create network policy key: ", err)
×
2528
                return
×
2529
        }
×
2530

2531
        if cont.config.HppOptimization || cont.config.EnableHppDirect {
×
2532
                if !reflect.DeepEqual(oldnp.Spec, newnp.Spec) {
×
2533
                        cont.removeFromHppCache(oldnp, npkey)
×
2534
                }
×
2535
        }
2536

2537
        cont.indexMutex.Lock()
×
2538
        oldSubnets := getNetworkPolicyEgressIpBlocks(oldnp)
×
2539
        newSubnets := getNetworkPolicyEgressIpBlocks(newnp)
×
2540
        cont.updateIpIndex(cont.netPolSubnetIndex, oldSubnets, newSubnets, npkey)
×
2541

×
2542
        oldPorts := cont.getNetPolTargetPorts(oldnp)
×
2543
        newPorts := cont.getNetPolTargetPorts(newnp)
×
2544
        cont.updateTargetPortIndex(false, npkey, oldPorts, newPorts)
×
2545
        cont.indexMutex.Unlock()
×
2546

×
2547
        if !reflect.DeepEqual(oldnp.Spec.PodSelector, newnp.Spec.PodSelector) {
×
2548
                cont.netPolPods.UpdateSelectorObjNoCallback(newobj)
×
2549
        }
×
2550
        if !reflect.DeepEqual(oldnp.Spec.PolicyTypes, newnp.Spec.PolicyTypes) {
×
2551
                peerPodKeys := cont.netPolPods.GetPodForObj(npkey)
×
2552
                for _, podkey := range peerPodKeys {
×
2553
                        cont.podQueue.Add(podkey)
×
2554
                }
×
2555
        }
2556
        var queue bool
×
2557
        if !reflect.DeepEqual(oldnp.Spec.Ingress, newnp.Spec.Ingress) {
×
2558
                cont.netPolIngressPods.UpdateSelectorObjNoCallback(newobj)
×
2559
                queue = true
×
2560
        }
×
2561
        if !reflect.DeepEqual(oldnp.Spec.Egress, newnp.Spec.Egress) {
×
2562
                cont.netPolEgressPods.UpdateSelectorObjNoCallback(newobj)
×
2563
                queue = true
×
2564
        }
×
2565
        if cont.config.EnableHppDirect && !reflect.DeepEqual(oldnp.Spec, newnp.Spec) {
×
2566
                cont.deleteHppCr(oldnp)
×
2567
                queue = true
×
2568
        }
×
2569
        if queue {
×
2570
                cont.queueNetPolUpdateByKey(npkey)
×
2571
        }
×
2572
}
2573

2574
func (cont *AciController) networkPolicyDeleted(obj interface{}) {
1✔
2575
        np, isNetworkpolicy := obj.(*v1net.NetworkPolicy)
1✔
2576
        if !isNetworkpolicy {
1✔
2577
                deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
×
2578
                if !ok {
×
2579
                        networkPolicyLogger(cont.log, np).
×
2580
                                Error("Received unexpected object: ", obj)
×
2581
                        return
×
2582
                }
×
2583
                np, ok = deletedState.Obj.(*v1net.NetworkPolicy)
×
2584
                if !ok {
×
2585
                        networkPolicyLogger(cont.log, np).
×
2586
                                Error("DeletedFinalStateUnknown contained non-Networkpolicy object: ", deletedState.Obj)
×
2587
                        return
×
2588
                }
×
2589
        }
2590
        npkey, err := cache.MetaNamespaceKeyFunc(np)
1✔
2591
        if err != nil {
1✔
2592
                networkPolicyLogger(cont.log, np).
×
2593
                        Error("Could not create network policy key: ", err)
×
2594
                return
×
2595
        }
×
2596

2597
        var labelKey string
1✔
2598
        var noHppRef bool
1✔
2599
        if cont.config.HppOptimization || cont.config.EnableHppDirect {
1✔
2600
                labelKey, noHppRef = cont.removeFromHppCache(np, npkey)
×
2601
        } else {
1✔
2602
                labelKey = cont.aciNameForKey("np", npkey)
1✔
2603
                noHppRef = true
1✔
2604
        }
1✔
2605

2606
        cont.indexMutex.Lock()
1✔
2607
        subnets := getNetworkPolicyEgressIpBlocks(np)
1✔
2608
        cont.updateIpIndex(cont.netPolSubnetIndex, subnets, nil, npkey)
1✔
2609

1✔
2610
        ports := cont.getNetPolTargetPorts(np)
1✔
2611
        cont.updateTargetPortIndex(false, npkey, ports, nil)
1✔
2612
        if isNamedPortPresenInNp(np) {
2✔
2613
                delete(cont.nmPortNp, npkey)
1✔
2614
        }
1✔
2615
        cont.indexMutex.Unlock()
1✔
2616

1✔
2617
        cont.netPolPods.DeleteSelectorObj(obj)
1✔
2618
        cont.netPolIngressPods.DeleteSelectorObj(obj)
1✔
2619
        cont.netPolEgressPods.DeleteSelectorObj(obj)
1✔
2620
        if noHppRef && labelKey != "" {
2✔
2621
                cont.apicConn.ClearApicObjects(labelKey)
1✔
2622
        }
1✔
2623
        if cont.config.EnableHppDirect {
1✔
2624
                cont.deleteHppCr(np)
×
2625
        }
×
2626
}
2627

2628
func (seps *serviceEndpointSlice) SetNpServiceAugmentForService(servicekey string, service *v1.Service,
2629
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
2630
        subnetIndex cidranger.Ranger, logger *logrus.Entry) {
1✔
2631
        cont := seps.cont
1✔
2632
        npTargetPortsMap := cont.getPortNums(prs.port)
1✔
2633

1✔
2634
        // Helper function to check if a numeric port matches the NetworkPolicy port spec
1✔
2635
        checkNumericPortMatchesNetpol := func(port int) bool {
2✔
2636
                if prs.port.EndPort != nil {
2✔
2637
                        // Port range matching: port must be within [Port, EndPort]
1✔
2638
                        return port >= prs.port.Port.IntValue() && port <= int(*prs.port.EndPort)
1✔
2639
                }
1✔
2640
                // Single port matching: check if port is in the target ports map
2641
                return npTargetPortsMap[port]
1✔
2642
        }
2643

2644
        label := map[string]string{discovery.LabelServiceName: service.ObjectMeta.Name}
1✔
2645
        selector := labels.SelectorFromSet(label)
1✔
2646

1✔
2647
        endpointSliceList, err := cont.endpointSliceIndexer.ByIndex("namespace", service.ObjectMeta.Namespace)
1✔
2648
        if err != nil {
1✔
2649
                logger.Error("Could not list endpoint slices: ", err)
×
2650
                return
×
2651
        }
×
2652
        for _, svcPort := range service.Spec.Ports {
2✔
2653
                incomplete := false
1✔
2654
                hasValidatedSlice := false
1✔
2655
                if prs.port != nil &&
1✔
2656
                        (svcPort.Protocol != *prs.port.Protocol) {
1✔
2657
                        // egress rule does not match service target port
×
2658
                        continue
×
2659
                }
2660
                // Match any port if no port is specified in the np
2661
                portMatched := prs.port == nil || prs.port.Port == nil
1✔
2662

1✔
2663
                if !portMatched {
2✔
2664
                        if svcPort.TargetPort.Type == intstr.String {
2✔
2665
                                if prs.port.Port.Type == intstr.String {
2✔
2666
                                        if prs.port.Port.String() != svcPort.TargetPort.String() {
1✔
2667
                                                continue
×
2668
                                        }
2669
                                        portMatched = true
1✔
2670
                                }
2671
                        } else {
1✔
2672
                                if !checkNumericPortMatchesNetpol(svcPort.TargetPort.IntValue()) {
2✔
2673
                                        continue
1✔
2674
                                }
2675
                                portMatched = true
1✔
2676
                        }
2677
                }
2678

2679
                for _, endpointSliceobj := range endpointSliceList {
2✔
2680
                        endpointSlices := endpointSliceobj.(*discovery.EndpointSlice)
1✔
2681
                        if !selector.Matches(labels.Set(endpointSlices.Labels)) {
2✔
2682
                                continue
1✔
2683
                        }
2684

2685
                        var foundEpPort *discovery.EndpointPort
1✔
2686
                        for ix := range endpointSlices.Ports {
2✔
2687
                                if endpointSlices.Ports[ix].Name != nil && *endpointSlices.Ports[ix].Name == svcPort.Name ||
1✔
2688
                                        (len(service.Spec.Ports) == 1 &&
1✔
2689
                                                endpointSlices.Ports[ix].Name != nil && *endpointSlices.Ports[ix].Name == "") {
2✔
2690
                                        foundEpPort = &endpointSlices.Ports[ix]
1✔
2691
                                        cont.log.Debug("Found EpPort: ", foundEpPort)
1✔
2692
                                        break
1✔
2693
                                }
2694
                        }
2695

2696
                        if foundEpPort == nil {
1✔
2697
                                continue
×
2698
                        }
2699
                        if !portMatched && (foundEpPort.Port == nil || !checkNumericPortMatchesNetpol(int(*foundEpPort.Port))) {
1✔
2700
                                incomplete = true
×
2701
                                break
×
2702
                        }
2703
                        // @FIXME for non ready address
2704
                        for _, endpoint := range endpointSlices.Endpoints {
2✔
2705
                                incomplete = incomplete || !checkEndpointslices(subnetIndex, endpoint.Addresses)
1✔
2706
                        }
1✔
2707
                        if incomplete {
2✔
2708
                                break
1✔
2709
                        }
2710
                        hasValidatedSlice = true
1✔
2711
                }
2712
                if !incomplete && hasValidatedSlice {
2✔
2713
                        proto := portProto(&svcPort.Protocol)
1✔
2714
                        port := strconv.Itoa(int(svcPort.Port))
1✔
2715
                        cont.log.Debug("updateServiceAugmentForService: ", service)
1✔
2716
                        updateServiceAugmentForService(portAugments,
1✔
2717
                                proto, port, service)
1✔
2718
                        logger.WithFields(logrus.Fields{
1✔
2719
                                "proto":   proto,
1✔
2720
                                "port":    port,
1✔
2721
                                "service": servicekey,
1✔
2722
                        }).Debug("Allowing egress for service by subnet match")
1✔
2723
                }
1✔
2724
        }
2725
}
2726

2727
func isNamedPortPresenInNp(np *v1net.NetworkPolicy) bool {
1✔
2728
        for _, egress := range np.Spec.Egress {
2✔
2729
                for _, p := range egress.Ports {
2✔
2730
                        if p.Port.Type == intstr.String {
2✔
2731
                                return true
1✔
2732
                        }
1✔
2733
                }
2734
        }
2735
        return false
1✔
2736
}
2737

2738
func (cont *AciController) checkPodNmpMatchesNp(npkey, podkey string) bool {
1✔
2739
        podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
2740
        if err != nil {
1✔
2741
                return false
×
2742
        }
×
2743
        if !exists || podobj == nil {
1✔
UNCOV
2744
                return false
×
UNCOV
2745
        }
×
2746
        pod := podobj.(*v1.Pod)
1✔
2747
        npobj, npexists, nperr := cont.networkPolicyIndexer.GetByKey(npkey)
1✔
2748
        if npexists && nperr == nil && npobj != nil {
2✔
2749
                np := npobj.(*v1net.NetworkPolicy)
1✔
2750
                for _, egress := range np.Spec.Egress {
2✔
2751
                        for _, p := range egress.Ports {
2✔
2752
                                if p.Port.Type == intstr.String {
2✔
2753
                                        _, err := k8util.LookupContainerPortNumberByName(*pod, p.Port.String())
1✔
2754
                                        if err == nil {
2✔
2755
                                                return true
1✔
2756
                                        }
1✔
2757
                                }
2758
                        }
2759
                }
2760
        }
2761
        return false
1✔
2762
}
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