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

noironetworks / aci-containers / 11576

05 Feb 2026 01:34PM UTC coverage: 62.852% (+0.04%) from 62.814%
11576

push

travis-pro

web-flow
Merge pull request #1665 from noironetworks/fix-repeated-netpol-file-creation

Fix repeated netpol file recreation due to map ordering in CR updates

49 of 62 new or added lines in 1 file covered. (79.03%)

5 existing lines in 3 files now uncovered.

13463 of 21420 relevant lines covered (62.85%)

0.72 hits per line

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

77.49
/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] {
2✔
929
                        continue
1✔
930
                }
931
                cont.updateIpIndexEntry(index, subStr, key, false)
1✔
932
        }
933
        for subStr := range newSubnets {
2✔
934
                if oldSubnets[subStr] {
2✔
935
                        continue
1✔
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 {
×
1395
                                        var portMap map[int]bool
×
1396
                                        if direction == "egress" {
×
1397
                                                portMap = cont.getPortNums(&ports[j])
×
1398
                                        } else {
×
1399
                                                // TODO need to handle empty Pod Selector
×
1400
                                                if reflect.DeepEqual(np.Spec.PodSelector, metav1.LabelSelector{}) {
×
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)
×
1406
                                                portMap = cont.getPortNumsFromPortName(podKeys, ports[j].Port.String())
×
1407
                                        }
1408
                                        if len(portMap) == 0 {
×
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 {
×
1414
                                                pr := portRange{fromPort: strconv.Itoa(portnum)}
×
1415
                                                portRanges = append(portRanges, pr)
×
1416
                                        }
×
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 checkEndpoints(subnetIndex cidranger.Ranger,
1486
        addresses []v1.EndpointAddress) bool {
1✔
1487
        for _, addr := range addresses {
2✔
1488
                ip := net.ParseIP(addr.IP)
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

1498
        return true
1✔
1499
}
1500
func checkEndpointslices(subnetIndex cidranger.Ranger,
1501
        addresses []string) bool {
1✔
1502
        for _, addr := range addresses {
2✔
1503
                ip := net.ParseIP(addr)
1✔
1504
                if ip == nil {
1✔
1505
                        return false
×
1506
                }
×
1507
                contains, err := subnetIndex.Contains(ip)
1✔
1508
                if err != nil || !contains {
2✔
1509
                        return false
1✔
1510
                }
1✔
1511
        }
1512
        return true
1✔
1513
}
1514

1515
type portRemoteSubnet struct {
1516
        port           *v1net.NetworkPolicyPort
1517
        subnetMap      map[string]bool
1518
        hasNamedTarget bool
1519
}
1520

1521
func updatePortRemoteSubnets(portRemoteSubs map[string]*portRemoteSubnet,
1522
        portkey string, port *v1net.NetworkPolicyPort, subnetMap map[string]bool,
1523
        hasNamedTarget bool) {
1✔
1524
        if prs, ok := portRemoteSubs[portkey]; ok {
1✔
1525
                for s := range subnetMap {
×
1526
                        prs.subnetMap[s] = true
×
1527
                }
×
1528
                prs.hasNamedTarget = hasNamedTarget || prs.hasNamedTarget
×
1529
        } else {
1✔
1530
                portRemoteSubs[portkey] = &portRemoteSubnet{
1✔
1531
                        port:           port,
1✔
1532
                        subnetMap:      subnetMap,
1✔
1533
                        hasNamedTarget: hasNamedTarget,
1✔
1534
                }
1✔
1535
        }
1✔
1536
}
1537

1538
func portServiceAugmentKey(proto, port string) string {
1✔
1539
        return proto + "-" + port
1✔
1540
}
1✔
1541

1542
type portServiceAugment struct {
1543
        proto string
1544
        port  string
1545
        ipMap map[string]bool
1546
}
1547

1548
func updateServiceAugment(portAugments map[string]*portServiceAugment, proto, port, ip string) {
1✔
1549
        key := portServiceAugmentKey(proto, port)
1✔
1550
        if psa, ok := portAugments[key]; ok {
1✔
1551
                psa.ipMap[ip] = true
×
1552
        } else {
1✔
1553
                portAugments[key] = &portServiceAugment{
1✔
1554
                        proto: proto,
1✔
1555
                        port:  port,
1✔
1556
                        ipMap: map[string]bool{ip: true},
1✔
1557
                }
1✔
1558
        }
1✔
1559
}
1560

1561
func updateServiceAugmentForService(portAugments map[string]*portServiceAugment,
1562
        proto, port string, service *v1.Service) {
1✔
1563
        if service.Spec.ClusterIP != "" {
2✔
1564
                updateServiceAugment(portAugments,
1✔
1565
                        proto, port, service.Spec.ClusterIP)
1✔
1566
        }
1✔
1567
        for _, ig := range service.Status.LoadBalancer.Ingress {
1✔
1568
                if ig.IP == "" {
×
1569
                        continue
×
1570
                }
1571
                updateServiceAugment(portAugments,
×
1572
                        proto, port, ig.IP)
×
1573
        }
1574
}
1575

1576
// build service augment by matching peers against the endpoints ip
1577
// index
1578
func (cont *AciController) getServiceAugmentBySubnet(
1579
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
1580
        logger *logrus.Entry) {
1✔
1581
        matchedServices := make(map[string]bool)
1✔
1582
        subnetIndex := cidranger.NewPCTrieRanger()
1✔
1583

1✔
1584
        // find candidate service endpoints objects that include
1✔
1585
        // endpoints selected by the egress rule
1✔
1586
        cont.indexMutex.Lock()
1✔
1587
        for sub := range prs.subnetMap {
2✔
1588
                cidr := parseCIDR(sub)
1✔
1589
                if cidr == nil {
1✔
1590
                        continue
×
1591
                }
1592
                subnetIndex.Insert(cidranger.NewBasicRangerEntry(*cidr))
1✔
1593

1✔
1594
                entries, err := cont.endpointsIpIndex.CoveredNetworks(*cidr)
1✔
1595
                if err != nil {
1✔
1596
                        logger.Error("endpointsIpIndex corrupted: ", err)
×
1597
                        continue
×
1598
                }
1599
                for _, entry := range entries {
2✔
1600
                        e := entry.(*ipIndexEntry)
1✔
1601
                        for servicekey := range e.keys {
2✔
1602
                                matchedServices[servicekey] = true
1✔
1603
                        }
1✔
1604
                }
1605
        }
1606
        cont.indexMutex.Unlock()
1✔
1607

1✔
1608
        // if all endpoints are selected by egress rule, allow egress
1✔
1609
        // to the service cluster IP as well as to the endpoints
1✔
1610
        // themselves
1✔
1611
        for servicekey := range matchedServices {
2✔
1612
                serviceobj, _, err := cont.serviceIndexer.GetByKey(servicekey)
1✔
1613
                if err != nil {
1✔
1614
                        logger.Error("Could not lookup service for "+
×
1615
                                servicekey+": ", err.Error())
×
1616
                        continue
×
1617
                }
1618
                if serviceobj == nil {
1✔
1619
                        continue
×
1620
                }
1621
                service := serviceobj.(*v1.Service)
1✔
1622
                cont.serviceEndPoints.SetNpServiceAugmentForService(servicekey, service,
1✔
1623
                        prs, portAugments, subnetIndex, logger)
1✔
1624
        }
1625
}
1626

1627
// build service augment by matching against services with a given
1628
// target port
1629
func (cont *AciController) getServiceAugmentByPort(
1630
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
1631
        logger *logrus.Entry) {
1✔
1632
        // nil port means it matches against all ports.  If we're here, it
1✔
1633
        // means this is a rule that matches all ports with all
1✔
1634
        // destinations, so there's no need to augment anything.
1✔
1635
        if prs.port == nil ||
1✔
1636
                prs.port.Port == nil {
2✔
1637
                return
1✔
1638
        }
1✔
1639

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

1✔
1733
                        for _, svcPort := range service.Spec.Ports {
2✔
1734
                                if svcPort.Protocol != *prs.port.Protocol {
1✔
1735
                                        continue
×
1736
                                }
1737
                                match := false
1✔
1738
                                if indexEntry, ok := cont.namedPortServiceIndex[servicekey]; ok {
1✔
1739
                                        if svcPortIdxEntry, ok := (*indexEntry)[svcPort.Name]; ok && len(svcPortIdxEntry.resolvedPorts) == 1 {
×
1740
                                                intKey, error := strconv.Atoi(key)
×
1741
                                                if error == nil && svcPortIdxEntry.resolvedPorts[intKey] {
×
1742
                                                        match = true
×
1743
                                                }
×
1744
                                        }
1745
                                }
1746
                                if !match && svcPort.TargetPort.String() != key {
1✔
1747
                                        continue
×
1748
                                }
1749
                                proto := portProto(&svcPort.Protocol)
1✔
1750
                                port := strconv.Itoa(int(svcPort.Port))
1✔
1751

1✔
1752
                                updateServiceAugmentForService(portAugments,
1✔
1753
                                        proto, port, service)
1✔
1754

1✔
1755
                                logger.WithFields(logrus.Fields{
1✔
1756
                                        "proto":   proto,
1✔
1757
                                        "port":    port,
1✔
1758
                                        "service": servicekey,
1✔
1759
                                }).Debug("Allowing egress for service by port")
1✔
1760
                        }
1761
                }
1762
        }
1763
}
1764

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

1855
func isAllowAllForAllNamespaces(peers []v1net.NetworkPolicyPeer) bool {
1✔
1856
        addPodSubnetAsRemIp := false
1✔
1857
        if peers != nil && len(peers) > 0 {
2✔
1858
                var emptyPodSel, emptyNsSel bool
1✔
1859
                emptyPodSel = true
1✔
1860
                for _, peer := range peers {
2✔
1861
                        // namespaceSelector: {}
1✔
1862
                        if peer.NamespaceSelector != nil && peer.NamespaceSelector.MatchLabels == nil && peer.NamespaceSelector.MatchExpressions == nil {
1✔
1863
                                emptyNsSel = true
×
1864
                        }
×
1865
                        // podSelector has some fields
1866
                        if peer.PodSelector != nil && (peer.PodSelector.MatchLabels != nil || peer.PodSelector.MatchExpressions != nil) {
2✔
1867
                                emptyPodSel = false
1✔
1868
                        }
1✔
1869
                }
1870
                if emptyNsSel && emptyPodSel {
1✔
1871
                        addPodSubnetAsRemIp = true
×
1872
                }
×
1873
        }
1874
        return addPodSubnetAsRemIp
1✔
1875
}
1876

1877
func (cont *AciController) handleRemIpContUpdate(ns string) bool {
1✔
1878
        cont.hppMutex.Lock()
1✔
1879
        defer cont.hppMutex.Unlock()
1✔
1880

1✔
1881
        sysNs := os.Getenv("SYSTEM_NAMESPACE")
1✔
1882
        aobj, err := cont.getHostprotRemoteIpContainer(ns, sysNs)
1✔
1883
        isUpdate := err == nil
1✔
1884

1✔
1885
        if err != nil && !errors.IsNotFound(err) {
1✔
1886
                cont.log.Error("Error getting HostprotRemoteIpContainers CR: ", err)
×
1887
                return true
×
1888
        }
×
1889

1890
        var existingSpec hppv1.HostprotRemoteIpContainerSpec
1✔
1891
        if !isUpdate {
2✔
1892
                aobj = &hppv1.HostprotRemoteIpContainer{
1✔
1893
                        ObjectMeta: metav1.ObjectMeta{
1✔
1894
                                Name:      ns,
1✔
1895
                                Namespace: sysNs,
1✔
1896
                        },
1✔
1897
                        Spec: hppv1.HostprotRemoteIpContainerSpec{
1✔
1898
                                Name:             ns,
1✔
1899
                                HostprotRemoteIp: []hppv1.HostprotRemoteIp{},
1✔
1900
                        },
1✔
1901
                }
1✔
1902
        } else {
1✔
1903
                cont.log.Debug("HostprotRemoteIpContainers CR already exists: ", aobj)
×
NEW
1904
                existingSpec = aobj.Spec
×
UNCOV
1905
        }
×
1906

1907
        remIpCont, exists := cont.nsRemoteIpCont[ns]
1✔
1908
        if !exists {
2✔
1909
                if isUpdate {
1✔
1910
                        if !cont.deleteHostprotRemoteIpContainer(ns, sysNs) {
×
1911
                                return true
×
1912
                        }
×
1913
                } else {
1✔
1914
                        cont.log.Error("Couldn't find the ns in nsRemoteIpCont cache: ", ns)
1✔
1915
                        return false
1✔
1916
                }
1✔
NEW
1917
                return false
×
1918
        }
1919

1920
        aobj.Spec.HostprotRemoteIp = buildHostprotRemoteIpList(remIpCont)
1✔
1921

1✔
1922
        if isUpdate {
1✔
NEW
1923
                // Skip update if spec hasn't changed
×
NEW
1924
                if reflect.DeepEqual(existingSpec, aobj.Spec) {
×
NEW
1925
                        cont.log.Debug("HostprotRemoteIpContainer CR unchanged, skipping update: ", ns)
×
NEW
1926
                        return false
×
NEW
1927
                }
×
1928
                if !cont.updateHostprotRemoteIpContainer(aobj, sysNs) {
×
1929
                        return true
×
1930
                }
×
1931
        } else {
1✔
1932
                if !cont.createHostprotRemoteIpContainer(aobj, sysNs) {
1✔
1933
                        return true
×
1934
                }
×
1935
        }
1936

1937
        return false
1✔
1938
}
1939

1940
func buildHostprotRemoteIpList(remIpConts map[string]remoteIpCont) []hppv1.HostprotRemoteIp {
1✔
1941
        hostprotRemoteIpList := []hppv1.HostprotRemoteIp{}
1✔
1942

1✔
1943
        // Sort pod names for deterministic ordering
1✔
1944
        podNames := make([]string, 0, len(remIpConts))
1✔
1945
        for podName := range remIpConts {
2✔
1946
                podNames = append(podNames, podName)
1✔
1947
        }
1✔
1948
        sort.Strings(podNames)
1✔
1949

1✔
1950
        for _, podName := range podNames {
2✔
1951
                remIpCont := remIpConts[podName]
1✔
1952
                // Sort IPs for deterministic ordering
1✔
1953
                ips := make([]string, 0, len(remIpCont))
1✔
1954
                for ip := range remIpCont {
2✔
1955
                        ips = append(ips, ip)
1✔
1956
                }
1✔
1957
                sort.Strings(ips)
1✔
1958

1✔
1959
                for _, ip := range ips {
2✔
1960
                        labels := remIpCont[ip]
1✔
1961
                        remIpObj := hppv1.HostprotRemoteIp{
1✔
1962
                                Addr: ip,
1✔
1963
                        }
1✔
1964
                        // Sort label keys for deterministic ordering
1✔
1965
                        labelKeys := make([]string, 0, len(labels))
1✔
1966
                        for key := range labels {
2✔
1967
                                labelKeys = append(labelKeys, key)
1✔
1968
                        }
1✔
1969
                        sort.Strings(labelKeys)
1✔
1970

1✔
1971
                        for _, key := range labelKeys {
2✔
1972
                                remIpObj.HppEpLabel = append(remIpObj.HppEpLabel, hppv1.HppEpLabel{
1✔
1973
                                        Key:   key,
1✔
1974
                                        Value: labels[key],
1✔
1975
                                })
1✔
1976
                        }
1✔
1977
                        hostprotRemoteIpList = append(hostprotRemoteIpList, remIpObj)
1✔
1978
                }
1979
        }
1980

1981
        return hostprotRemoteIpList
1✔
1982
}
1983

1984
func (cont *AciController) deleteHppCr(np *v1net.NetworkPolicy) bool {
1✔
1985
        key, err := cache.MetaNamespaceKeyFunc(np)
1✔
1986
        logger := networkPolicyLogger(cont.log, np)
1✔
1987
        if err != nil {
1✔
1988
                logger.Error("Could not create network policy key: ", err)
×
1989
                return false
×
1990
        }
×
1991
        hash, err := util.CreateHashFromNetPol(np)
1✔
1992
        if err != nil {
1✔
1993
                logger.Error("Could not create hash from network policy: ", err)
×
1994
                return false
×
1995
        }
×
1996
        labelKey := cont.aciNameForKey("np", hash)
1✔
1997
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
1998
        hppName := strings.ReplaceAll(labelKey, "_", "-")
1✔
1999
        hpp, _ := cont.getHostprotPol(hppName, ns)
1✔
2000
        if hpp == nil {
2✔
2001
                logger.Error("Could not find hostprotPol: ", hppName)
1✔
2002
                return false
1✔
2003
        }
1✔
2004
        netPols := hpp.Spec.NetworkPolicies
1✔
2005
        newNetPols := make([]string, 0)
1✔
2006
        for _, npName := range netPols {
2✔
2007
                if npName != key {
2✔
2008
                        newNetPols = append(newNetPols, npName)
1✔
2009
                }
1✔
2010
        }
2011

2012
        hpp.Spec.NetworkPolicies = newNetPols
1✔
2013

1✔
2014
        if len(newNetPols) > 0 {
2✔
2015
                return cont.updateHostprotPol(hpp, ns)
1✔
2016
        } else {
2✔
2017
                return cont.deleteHostprotPol(hppName, ns)
1✔
2018
        }
1✔
2019
}
2020

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

1✔
2025
        aobj, err := cont.getHostprotRemoteIpContainer(name, ns)
1✔
2026
        isUpdate := err == nil
1✔
2027

1✔
2028
        if err != nil && !errors.IsNotFound(err) {
1✔
2029
                cont.log.Error("Error getting HostprotRemoteIpContainers CR: ", err)
×
2030
                return
×
2031
        }
×
2032

2033
        var existingSpec hppv1.HostprotRemoteIpContainerSpec
1✔
2034
        if !isUpdate {
2✔
2035
                aobj = &hppv1.HostprotRemoteIpContainer{
1✔
2036
                        ObjectMeta: metav1.ObjectMeta{
1✔
2037
                                Name:      name,
1✔
2038
                                Namespace: ns,
1✔
2039
                        },
1✔
2040
                        Spec: hppv1.HostprotRemoteIpContainerSpec{
1✔
2041
                                Name:             name,
1✔
2042
                                HostprotRemoteIp: []hppv1.HostprotRemoteIp{},
1✔
2043
                        },
1✔
2044
                }
1✔
2045
        } else {
2✔
2046
                cont.log.Debug("HostprotRemoteIpContainers CR already exists: ", aobj)
1✔
2047
                existingSpec = aobj.Spec
1✔
2048
        }
1✔
2049

2050
        existingIps := make(map[string]bool)
1✔
2051
        for _, ip := range aobj.Spec.HostprotRemoteIp {
2✔
2052
                existingIps[ip.Addr] = true
1✔
2053
        }
1✔
2054

2055
        // Sort IPs for deterministic ordering
2056
        sortedIps := make([]string, 0, len(nodeIps))
1✔
2057
        for ip := range nodeIps {
2✔
2058
                sortedIps = append(sortedIps, ip)
1✔
2059
        }
1✔
2060
        sort.Strings(sortedIps)
1✔
2061

1✔
2062
        for _, ip := range sortedIps {
2✔
2063
                if !existingIps[ip] {
2✔
2064
                        aobj.Spec.HostprotRemoteIp = append(aobj.Spec.HostprotRemoteIp, hppv1.HostprotRemoteIp{Addr: ip})
1✔
2065
                }
1✔
2066
        }
2067

2068
        if isUpdate {
2✔
2069
                // Skip update if spec hasn't changed
1✔
2070
                if reflect.DeepEqual(existingSpec, aobj.Spec) {
1✔
NEW
2071
                        cont.log.Debug("HostprotRemoteIpContainer CR unchanged, skipping update: ", name)
×
NEW
2072
                        return
×
NEW
2073
                }
×
2074
                cont.updateHostprotRemoteIpContainer(aobj, ns)
1✔
2075
        } else {
1✔
2076
                cont.createHostprotRemoteIpContainer(aobj, ns)
1✔
2077
        }
1✔
2078
}
2079

2080
func (cont *AciController) deleteNodeIpsHostprotRemoteIpContainer(nodeIps map[string]bool) {
1✔
2081
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2082
        name := "nodeips"
1✔
2083

1✔
2084
        aobj, _ := cont.getHostprotRemoteIpContainer(name, ns)
1✔
2085
        if aobj == nil {
1✔
2086
                return
×
2087
        }
×
2088

2089
        newHostprotRemoteIps := aobj.Spec.HostprotRemoteIp[:0]
1✔
2090
        for _, hostprotRemoteIp := range aobj.Spec.HostprotRemoteIp {
2✔
2091
                if len(nodeIps) > 0 && !nodeIps[hostprotRemoteIp.Addr] {
2✔
2092
                        newHostprotRemoteIps = append(newHostprotRemoteIps, hostprotRemoteIp)
1✔
2093
                }
1✔
2094
        }
2095

2096
        aobj.Spec.HostprotRemoteIp = newHostprotRemoteIps
1✔
2097

1✔
2098
        if len(newHostprotRemoteIps) > 0 {
2✔
2099
                cont.updateHostprotRemoteIpContainer(aobj, ns)
1✔
2100
        } else {
2✔
2101
                cont.deleteHostprotRemoteIpContainer(name, ns)
1✔
2102
        }
1✔
2103
}
2104

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

1✔
2108
        aobj, err := cont.getHostprotRemoteIpContainer(name, ns)
1✔
2109
        isUpdate := err == nil
1✔
2110

1✔
2111
        if err != nil && !errors.IsNotFound(err) {
1✔
2112
                cont.log.Error("Error getting HostprotRemoteIpContainers CR: ", err)
×
2113
                return
×
2114
        }
×
2115

2116
        var existingSpec hppv1.HostprotRemoteIpContainerSpec
1✔
2117
        if !isUpdate {
2✔
2118
                aobj = &hppv1.HostprotRemoteIpContainer{
1✔
2119
                        ObjectMeta: metav1.ObjectMeta{
1✔
2120
                                Name:      name,
1✔
2121
                                Namespace: ns,
1✔
2122
                        },
1✔
2123
                        Spec: hppv1.HostprotRemoteIpContainerSpec{
1✔
2124
                                Name:             name,
1✔
2125
                                HostprotRemoteIp: []hppv1.HostprotRemoteIp{},
1✔
2126
                        },
1✔
2127
                }
1✔
2128
        } else {
2✔
2129
                cont.log.Debug("HostprotRemoteIpContainers CR already exists: ", aobj)
1✔
2130
                existingSpec = aobj.Spec
1✔
2131
        }
1✔
2132

2133
        // Sort IPs for deterministic ordering
2134
        sortedIps := make([]string, 0, len(nodeIps))
1✔
2135
        for ip := range nodeIps {
2✔
2136
                sortedIps = append(sortedIps, ip)
1✔
2137
        }
1✔
2138
        sort.Strings(sortedIps)
1✔
2139

1✔
2140
        aobj.Spec.HostprotRemoteIp = make([]hppv1.HostprotRemoteIp, 0, len(nodeIps))
1✔
2141
        for _, ip := range sortedIps {
2✔
2142
                aobj.Spec.HostprotRemoteIp = append(aobj.Spec.HostprotRemoteIp, hppv1.HostprotRemoteIp{Addr: ip})
1✔
2143
        }
1✔
2144

2145
        if isUpdate {
2✔
2146
                // Skip update if spec hasn't changed
1✔
2147
                if reflect.DeepEqual(existingSpec, aobj.Spec) {
1✔
NEW
2148
                        cont.log.Debug("HostprotRemoteIpContainer CR unchanged, skipping update: ", name)
×
NEW
2149
                        return
×
NEW
2150
                }
×
2151
                cont.updateHostprotRemoteIpContainer(aobj, ns)
1✔
2152
        } else {
1✔
2153
                cont.createHostprotRemoteIpContainer(aobj, ns)
1✔
2154
        }
1✔
2155
}
2156

2157
func (cont *AciController) deleteNodeHostprotRemoteIpContainer(name string) {
1✔
2158
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2159

1✔
2160
        if _, err := cont.getHostprotRemoteIpContainer(name, ns); err == nil {
2✔
2161
                cont.deleteHostprotRemoteIpContainer(name, ns)
1✔
2162
        }
1✔
2163
}
2164

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

1✔
2169
        hpp, err := cont.getHostprotPol(hppName, ns)
1✔
2170
        isUpdate := hpp != nil && err == nil
1✔
2171

1✔
2172
        if err != nil && !errors.IsNotFound(err) {
1✔
2173
                cont.log.Error("Error getting HPP CR: ", err)
×
2174
                return
×
2175
        }
×
2176

2177
        if !isUpdate {
1✔
2178
                hpp = &hppv1.HostprotPol{
×
2179
                        ObjectMeta: metav1.ObjectMeta{
×
2180
                                Name:      hppName,
×
2181
                                Namespace: ns,
×
2182
                        },
×
2183
                        Spec: hppv1.HostprotPolSpec{
×
2184
                                Name:            name,
×
2185
                                NetworkPolicies: []string{name},
×
2186
                                HostprotSubj:    []hppv1.HostprotSubj{},
×
2187
                        },
×
2188
                }
×
2189
        } else {
1✔
2190
                cont.log.Debug("HPP CR already exists: ", hpp)
1✔
2191
                hpp.Spec.HostprotSubj = []hppv1.HostprotSubj{}
1✔
2192
        }
1✔
2193

2194
        if len(nodeIps) > 0 {
2✔
2195
                cont.updateNodeHostprotRemoteIpContainer(nodeName, nodeIps)
1✔
2196
                cont.updateNodeIpsHostprotRemoteIpContainer(nodeIps)
1✔
2197

1✔
2198
                hostprotSubj := hppv1.HostprotSubj{
1✔
2199
                        Name: "local-node",
1✔
2200
                        HostprotRule: []hppv1.HostprotRule{
1✔
2201
                                {
1✔
2202
                                        Name:                "allow-all-egress",
1✔
2203
                                        Direction:           "egress",
1✔
2204
                                        Ethertype:           "ipv4",
1✔
2205
                                        ConnTrack:           "normal",
1✔
2206
                                        RsRemoteIpContainer: []string{nodeName},
1✔
2207
                                },
1✔
2208
                                {
1✔
2209
                                        Name:                "allow-all-ingress",
1✔
2210
                                        Direction:           "ingress",
1✔
2211
                                        Ethertype:           "ipv4",
1✔
2212
                                        ConnTrack:           "normal",
1✔
2213
                                        RsRemoteIpContainer: []string{nodeName},
1✔
2214
                                },
1✔
2215
                        },
1✔
2216
                }
1✔
2217

1✔
2218
                hpp.Spec.HostprotSubj = append(hpp.Spec.HostprotSubj, hostprotSubj)
1✔
2219
        } else {
2✔
2220
                cont.deleteNodeHostprotRemoteIpContainer(nodeName)
1✔
2221
                cont.deleteNodeIpsHostprotRemoteIpContainer(nodeIps)
1✔
2222
        }
1✔
2223

2224
        if isUpdate {
2✔
2225
                cont.updateHostprotPol(hpp, ns)
1✔
2226
        } else {
1✔
2227
                cont.createHostprotPol(hpp, ns)
×
2228
        }
×
2229
}
2230

2231
func (cont *AciController) handleNetPolUpdate(np *v1net.NetworkPolicy) bool {
1✔
2232
        if cont.isCNOEnabled() {
1✔
2233
                return false
×
2234
        }
×
2235
        key, err := cache.MetaNamespaceKeyFunc(np)
1✔
2236
        logger := networkPolicyLogger(cont.log, np)
1✔
2237
        if err != nil {
1✔
2238
                logger.Error("Could not create network policy key: ", err)
×
2239
                return false
×
2240
        }
×
2241

2242
        peerPodKeys := cont.netPolIngressPods.GetPodForObj(key)
1✔
2243
        peerPodKeys =
1✔
2244
                append(peerPodKeys, cont.netPolEgressPods.GetPodForObj(key)...)
1✔
2245
        var peerPods []*v1.Pod
1✔
2246
        peerNs := make(map[string]*v1.Namespace)
1✔
2247
        for _, podkey := range peerPodKeys {
2✔
2248
                podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
2249
                if exists && err == nil {
2✔
2250
                        pod := podobj.(*v1.Pod)
1✔
2251
                        if _, nsok := peerNs[pod.ObjectMeta.Namespace]; !nsok {
2✔
2252
                                nsobj, exists, err :=
1✔
2253
                                        cont.namespaceIndexer.GetByKey(pod.ObjectMeta.Namespace)
1✔
2254
                                if !exists || err != nil {
1✔
2255
                                        continue
×
2256
                                }
2257
                                peerNs[pod.ObjectMeta.Namespace] = nsobj.(*v1.Namespace)
1✔
2258
                        }
2259
                        peerPods = append(peerPods, pod)
1✔
2260
                }
2261
        }
2262
        ptypeset := make(map[v1net.PolicyType]bool)
1✔
2263
        for _, t := range np.Spec.PolicyTypes {
2✔
2264
                ptypeset[t] = true
1✔
2265
        }
1✔
2266
        var labelKey string
1✔
2267

1✔
2268
        if !cont.config.EnableHppDirect {
2✔
2269
                if cont.config.HppOptimization {
2✔
2270
                        hash, err := util.CreateHashFromNetPol(np)
1✔
2271
                        if err != nil {
1✔
2272
                                logger.Error("Could not create hash from network policy: ", err)
×
2273
                                return false
×
2274
                        }
×
2275
                        labelKey = cont.aciNameForKey("np", hash)
1✔
2276
                } else {
1✔
2277
                        labelKey = cont.aciNameForKey("np", key)
1✔
2278
                }
1✔
2279
                hpp := apicapi.NewHostprotPol(cont.config.AciPolicyTenant, labelKey)
1✔
2280
                // Generate ingress policies
1✔
2281
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeIngress] {
2✔
2282
                        subjIngress :=
1✔
2283
                                apicapi.NewHostprotSubj(hpp.GetDn(), "networkpolicy-ingress")
1✔
2284

1✔
2285
                        for i, ingress := range np.Spec.Ingress {
2✔
2286
                                addPodSubnetAsRemIp := isAllowAllForAllNamespaces(ingress.From)
1✔
2287
                                remoteSubnets, _, _, _, _ := cont.getPeerRemoteSubnets(ingress.From,
1✔
2288
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2289
                                cont.buildNetPolSubjRules(strconv.Itoa(i), subjIngress,
1✔
2290
                                        "ingress", ingress.From, remoteSubnets, ingress.Ports, logger, key, np, addPodSubnetAsRemIp)
1✔
2291
                        }
1✔
2292
                        hpp.AddChild(subjIngress)
1✔
2293
                }
2294
                // Generate egress policies
2295
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeEgress] {
2✔
2296
                        subjEgress :=
1✔
2297
                                apicapi.NewHostprotSubj(hpp.GetDn(), "networkpolicy-egress")
1✔
2298

1✔
2299
                        portRemoteSubs := make(map[string]*portRemoteSubnet)
1✔
2300

1✔
2301
                        for i, egress := range np.Spec.Egress {
2✔
2302
                                addPodSubnetAsRemIp := isAllowAllForAllNamespaces(egress.To)
1✔
2303
                                remoteSubnets, _, _, subnetMap, _ := cont.getPeerRemoteSubnets(egress.To,
1✔
2304
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2305
                                cont.buildNetPolSubjRules(strconv.Itoa(i), subjEgress,
1✔
2306
                                        "egress", egress.To, remoteSubnets, egress.Ports, logger, key, np, addPodSubnetAsRemIp)
1✔
2307

1✔
2308
                                // creating a rule to egress to all on a given port needs
1✔
2309
                                // to enable access to any service IPs/ports that have
1✔
2310
                                // that port as their target port.
1✔
2311
                                if len(egress.To) == 0 {
2✔
2312
                                        subnetMap = map[string]bool{
1✔
2313
                                                "0.0.0.0/0": true,
1✔
2314
                                        }
1✔
2315
                                }
1✔
2316
                                for idx := range egress.Ports {
2✔
2317
                                        port := egress.Ports[idx]
1✔
2318
                                        portkey := portKey(&port)
1✔
2319
                                        updatePortRemoteSubnets(portRemoteSubs, portkey, &port, subnetMap,
1✔
2320
                                                port.Port != nil && port.Port.Type == intstr.Int)
1✔
2321
                                }
1✔
2322
                                if len(egress.Ports) == 0 {
2✔
2323
                                        updatePortRemoteSubnets(portRemoteSubs, "", nil, subnetMap,
1✔
2324
                                                false)
1✔
2325
                                }
1✔
2326
                        }
2327
                        cont.buildServiceAugment(subjEgress, nil, portRemoteSubs, logger)
1✔
2328
                        hpp.AddChild(subjEgress)
1✔
2329
                }
2330
                if cont.config.HppOptimization {
2✔
2331
                        cont.addToHppCache(labelKey, key, apicapi.ApicSlice{hpp}, &hppv1.HostprotPol{})
1✔
2332
                }
1✔
2333
                cont.apicConn.WriteApicObjects(labelKey, apicapi.ApicSlice{hpp})
1✔
2334
        } else {
1✔
2335
                hash, err := util.CreateHashFromNetPol(np)
1✔
2336
                if err != nil {
1✔
2337
                        logger.Error("Could not create hash from network policy: ", err)
×
2338
                        return false
×
2339
                }
×
2340
                labelKey = cont.aciNameForKey("np", hash)
1✔
2341
                ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2342
                hppName := strings.ReplaceAll(labelKey, "_", "-")
1✔
2343
                hpp, err := cont.getHostprotPol(hppName, ns)
1✔
2344
                isUpdate := err == nil
1✔
2345

1✔
2346
                if err != nil && !errors.IsNotFound(err) {
1✔
2347
                        logger.Error("Error getting HPP CR: ", err)
×
2348
                        return false
×
2349
                }
×
2350

2351
                if isUpdate {
1✔
2352
                        logger.Debug("HPP CR already exists: ", hpp)
×
2353
                        if !slices.Contains(hpp.Spec.NetworkPolicies, key) {
×
2354
                                hpp.Spec.NetworkPolicies = append(hpp.Spec.NetworkPolicies, key)
×
2355
                        }
×
2356
                        hpp.Spec.HostprotSubj = nil
×
2357
                } else {
1✔
2358
                        hpp = &hppv1.HostprotPol{
1✔
2359
                                ObjectMeta: metav1.ObjectMeta{
1✔
2360
                                        Name:      hppName,
1✔
2361
                                        Namespace: ns,
1✔
2362
                                },
1✔
2363
                                Spec: hppv1.HostprotPolSpec{
1✔
2364
                                        Name:            labelKey,
1✔
2365
                                        NetworkPolicies: []string{key},
1✔
2366
                                        HostprotSubj:    nil,
1✔
2367
                                },
1✔
2368
                        }
1✔
2369
                }
1✔
2370

2371
                // Generate ingress policies
2372
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeIngress] {
2✔
2373
                        subjIngress := &hppv1.HostprotSubj{
1✔
2374
                                Name:         "networkpolicy-ingress",
1✔
2375
                                HostprotRule: []hppv1.HostprotRule{},
1✔
2376
                        }
1✔
2377

1✔
2378
                        for i, ingress := range np.Spec.Ingress {
2✔
2379
                                remoteSubnets, peerNsList, peerremote, _, peerIpBlock := cont.getPeerRemoteSubnets(ingress.From,
1✔
2380
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2381
                                if isAllowAllForAllNamespaces(ingress.From) {
1✔
2382
                                        peerNsList = append(peerNsList, "nodeips")
×
2383
                                }
×
2384
                                if !(len(ingress.From) > 0 && len(remoteSubnets) == 0) {
2✔
2385
                                        cont.buildLocalNetPolSubjRules(strconv.Itoa(i), subjIngress,
1✔
2386
                                                "ingress", peerNsList, peerremote.podSelectors, ingress.Ports,
1✔
2387
                                                logger, key, np, peerIpBlock)
1✔
2388
                                }
1✔
2389
                        }
2390
                        hpp.Spec.HostprotSubj = append(hpp.Spec.HostprotSubj, *subjIngress)
1✔
2391
                }
2392

2393
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeEgress] {
2✔
2394
                        subjEgress := &hppv1.HostprotSubj{
1✔
2395
                                Name:         "networkpolicy-egress",
1✔
2396
                                HostprotRule: []hppv1.HostprotRule{},
1✔
2397
                        }
1✔
2398

1✔
2399
                        portRemoteSubs := make(map[string]*portRemoteSubnet)
1✔
2400

1✔
2401
                        for i, egress := range np.Spec.Egress {
2✔
2402
                                remoteSubnets, peerNsList, peerremote, subnetMap, peerIpBlock := cont.getPeerRemoteSubnets(egress.To,
1✔
2403
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2404
                                if isAllowAllForAllNamespaces(egress.To) {
1✔
2405
                                        peerNsList = append(peerNsList, "nodeips")
×
2406
                                }
×
2407
                                if !(len(egress.To) > 0 && len(remoteSubnets) == 0) {
2✔
2408
                                        cont.buildLocalNetPolSubjRules(strconv.Itoa(i), subjEgress,
1✔
2409
                                                "egress", peerNsList, peerremote.podSelectors, egress.Ports, logger, key, np, peerIpBlock)
1✔
2410
                                }
1✔
2411

2412
                                if len(egress.To) == 0 {
2✔
2413
                                        subnetMap = map[string]bool{"0.0.0.0/0": true}
1✔
2414
                                }
1✔
2415
                                for idx := range egress.Ports {
2✔
2416
                                        port := egress.Ports[idx]
1✔
2417
                                        portkey := portKey(&port)
1✔
2418
                                        updatePortRemoteSubnets(portRemoteSubs, portkey, &port, subnetMap,
1✔
2419
                                                port.Port != nil && port.Port.Type == intstr.Int)
1✔
2420
                                }
1✔
2421
                                if len(egress.Ports) == 0 {
1✔
2422
                                        updatePortRemoteSubnets(portRemoteSubs, "", nil, subnetMap,
×
2423
                                                false)
×
2424
                                }
×
2425
                        }
2426
                        cont.buildServiceAugment(nil, subjEgress, portRemoteSubs, logger)
1✔
2427
                        hpp.Spec.HostprotSubj = append(hpp.Spec.HostprotSubj, *subjEgress)
1✔
2428
                }
2429

2430
                cont.addToHppCache(labelKey, key, apicapi.ApicSlice{}, hpp)
1✔
2431

1✔
2432
                if isUpdate {
1✔
2433
                        cont.updateHostprotPol(hpp, ns)
×
2434
                } else {
1✔
2435
                        cont.createHostprotPol(hpp, ns)
1✔
2436
                }
1✔
2437
        }
2438
        return false
1✔
2439
}
2440

2441
func (cont *AciController) updateNsRemoteIpCont(pod *v1.Pod, deleted bool) bool {
1✔
2442
        podips := ipsForPod(pod)
1✔
2443
        podns := pod.ObjectMeta.Namespace
1✔
2444
        podname := pod.ObjectMeta.Name
1✔
2445
        podlabels := pod.ObjectMeta.Labels
1✔
2446
        remipconts, ok := cont.nsRemoteIpCont[podns]
1✔
2447

1✔
2448
        if deleted {
2✔
2449
                if !ok {
2✔
2450
                        return true
1✔
2451
                }
1✔
2452

2453
                present := false
1✔
2454
                if remipcont, remipcontok := remipconts[podname]; remipcontok {
1✔
2455
                        for _, ip := range podips {
×
2456
                                if _, ipok := remipcont[ip]; ipok {
×
2457
                                        delete(remipcont, ip)
×
2458
                                        present = true
×
2459
                                }
×
2460
                        }
2461
                        if len(remipcont) < 1 {
×
2462
                                delete(remipconts, podname)
×
2463
                        }
×
2464
                }
2465

2466
                if len(remipconts) < 1 {
1✔
2467
                        delete(cont.nsRemoteIpCont, podns)
×
2468
                        cont.apicConn.ClearApicObjects(cont.aciNameForKey("hostprot-ns-", podns))
×
2469
                        return false
×
2470
                }
×
2471

2472
                if !present {
2✔
2473
                        return false
1✔
2474
                }
1✔
2475
        } else {
1✔
2476
                if !ok {
2✔
2477
                        remipconts = make(remoteIpConts)
1✔
2478
                        cont.nsRemoteIpCont[podns] = remipconts
1✔
2479
                }
1✔
2480

2481
                remipcont, remipcontok := remipconts[podname]
1✔
2482
                if !remipcontok {
2✔
2483
                        remipcont = make(remoteIpCont)
1✔
2484
                }
1✔
2485
                for _, ip := range podips {
2✔
2486
                        remipcont[ip] = podlabels
1✔
2487
                }
1✔
2488
                remipconts[podname] = remipcont
1✔
2489
        }
2490

2491
        return true
1✔
2492
}
2493

2494
func (cont *AciController) addToHppCache(labelKey, key string, hpp apicapi.ApicSlice, hppcr *hppv1.HostprotPol) {
1✔
2495
        cont.indexMutex.Lock()
1✔
2496
        hppRef, ok := cont.hppRef[labelKey]
1✔
2497
        if ok {
2✔
2498
                var found bool
1✔
2499
                for _, npkey := range hppRef.Npkeys {
2✔
2500
                        if npkey == key {
2✔
2501
                                found = true
1✔
2502
                                break
1✔
2503
                        }
2504
                }
2505
                if !found {
1✔
2506
                        hppRef.RefCount++
×
2507
                        hppRef.Npkeys = append(hppRef.Npkeys, key)
×
2508
                }
×
2509
                hppRef.HppObj = hpp
1✔
2510
                hppRef.HppCr = *hppcr
1✔
2511
                cont.hppRef[labelKey] = hppRef
1✔
2512
        } else {
1✔
2513
                var newHppRef hppReference
1✔
2514
                newHppRef.RefCount++
1✔
2515
                newHppRef.HppObj = hpp
1✔
2516
                newHppRef.HppCr = *hppcr
1✔
2517
                newHppRef.Npkeys = append(newHppRef.Npkeys, key)
1✔
2518
                cont.hppRef[labelKey] = newHppRef
1✔
2519
        }
1✔
2520
        cont.indexMutex.Unlock()
1✔
2521
}
2522

2523
func (cont *AciController) removeFromHppCache(np *v1net.NetworkPolicy, key string) (string, bool) {
1✔
2524
        var labelKey string
1✔
2525
        var noRef bool
1✔
2526
        hash, err := util.CreateHashFromNetPol(np)
1✔
2527
        if err != nil {
1✔
2528
                cont.log.Error("Could not create hash from network policy: ", err)
×
2529
                cont.log.Error("Failed to remove np from hpp cache")
×
2530
                return labelKey, noRef
×
2531
        }
×
2532
        labelKey = cont.aciNameForKey("np", hash)
1✔
2533
        cont.indexMutex.Lock()
1✔
2534
        hppRef, ok := cont.hppRef[labelKey]
1✔
2535
        if ok {
2✔
2536
                for i, npkey := range hppRef.Npkeys {
2✔
2537
                        if npkey == key {
2✔
2538
                                hppRef.Npkeys = append(hppRef.Npkeys[:i], hppRef.Npkeys[i+1:]...)
1✔
2539
                                hppRef.RefCount--
1✔
2540
                                break
1✔
2541
                        }
2542
                }
2543
                if hppRef.RefCount > 0 {
1✔
2544
                        cont.hppRef[labelKey] = hppRef
×
2545
                } else {
1✔
2546
                        delete(cont.hppRef, labelKey)
1✔
2547
                        noRef = true
1✔
2548
                }
1✔
2549
        }
2550
        cont.indexMutex.Unlock()
1✔
2551
        return labelKey, noRef
1✔
2552
}
2553

2554
func getNetworkPolicyEgressIpBlocks(np *v1net.NetworkPolicy) map[string]bool {
1✔
2555
        subnets := make(map[string]bool)
1✔
2556
        for _, egress := range np.Spec.Egress {
2✔
2557
                for _, to := range egress.To {
2✔
2558
                        if to.IPBlock != nil && to.IPBlock.CIDR != "" {
2✔
2559
                                subnets[to.IPBlock.CIDR] = true
1✔
2560
                        }
1✔
2561
                }
2562
        }
2563
        return subnets
1✔
2564
}
2565

2566
func (cont *AciController) networkPolicyAdded(obj interface{}) {
1✔
2567
        np := obj.(*v1net.NetworkPolicy)
1✔
2568
        npkey, err := cache.MetaNamespaceKeyFunc(np)
1✔
2569
        if err != nil {
1✔
2570
                networkPolicyLogger(cont.log, np).
×
2571
                        Error("Could not create network policy key: ", err)
×
2572
                return
×
2573
        }
×
2574
        if cont.isCNOEnabled() {
1✔
2575
                return
×
2576
        }
×
2577
        cont.netPolPods.UpdateSelectorObj(obj)
1✔
2578
        cont.netPolIngressPods.UpdateSelectorObj(obj)
1✔
2579
        cont.netPolEgressPods.UpdateSelectorObj(obj)
1✔
2580
        cont.indexMutex.Lock()
1✔
2581
        subnets := getNetworkPolicyEgressIpBlocks(np)
1✔
2582
        cont.updateIpIndex(cont.netPolSubnetIndex, nil, subnets, npkey)
1✔
2583

1✔
2584
        ports := cont.getNetPolTargetPorts(np)
1✔
2585
        cont.updateTargetPortIndex(false, npkey, nil, ports)
1✔
2586
        if isNamedPortPresenInNp(np) {
2✔
2587
                cont.nmPortNp[npkey] = true
1✔
2588
        }
1✔
2589
        cont.indexMutex.Unlock()
1✔
2590
        cont.queueNetPolUpdateByKey(npkey)
1✔
2591
}
2592

2593
func (cont *AciController) networkPolicyChanged(oldobj interface{},
2594
        newobj interface{}) {
×
2595
        oldnp := oldobj.(*v1net.NetworkPolicy)
×
2596
        newnp := newobj.(*v1net.NetworkPolicy)
×
2597
        npkey, err := cache.MetaNamespaceKeyFunc(newnp)
×
2598
        if err != nil {
×
2599
                networkPolicyLogger(cont.log, newnp).
×
2600
                        Error("Could not create network policy key: ", err)
×
2601
                return
×
2602
        }
×
2603

2604
        if cont.config.HppOptimization || cont.config.EnableHppDirect {
×
2605
                if !reflect.DeepEqual(oldnp.Spec, newnp.Spec) {
×
2606
                        cont.removeFromHppCache(oldnp, npkey)
×
2607
                }
×
2608
        }
2609

2610
        cont.indexMutex.Lock()
×
2611
        oldSubnets := getNetworkPolicyEgressIpBlocks(oldnp)
×
2612
        newSubnets := getNetworkPolicyEgressIpBlocks(newnp)
×
2613
        cont.updateIpIndex(cont.netPolSubnetIndex, oldSubnets, newSubnets, npkey)
×
2614

×
2615
        oldPorts := cont.getNetPolTargetPorts(oldnp)
×
2616
        newPorts := cont.getNetPolTargetPorts(newnp)
×
2617
        cont.updateTargetPortIndex(false, npkey, oldPorts, newPorts)
×
2618
        cont.indexMutex.Unlock()
×
2619

×
2620
        if !reflect.DeepEqual(oldnp.Spec.PodSelector, newnp.Spec.PodSelector) {
×
2621
                cont.netPolPods.UpdateSelectorObjNoCallback(newobj)
×
2622
        }
×
2623
        if !reflect.DeepEqual(oldnp.Spec.PolicyTypes, newnp.Spec.PolicyTypes) {
×
2624
                peerPodKeys := cont.netPolPods.GetPodForObj(npkey)
×
2625
                for _, podkey := range peerPodKeys {
×
2626
                        cont.podQueue.Add(podkey)
×
2627
                }
×
2628
        }
2629
        var queue bool
×
2630
        if !reflect.DeepEqual(oldnp.Spec.Ingress, newnp.Spec.Ingress) {
×
2631
                cont.netPolIngressPods.UpdateSelectorObjNoCallback(newobj)
×
2632
                queue = true
×
2633
        }
×
2634
        if !reflect.DeepEqual(oldnp.Spec.Egress, newnp.Spec.Egress) {
×
2635
                cont.netPolEgressPods.UpdateSelectorObjNoCallback(newobj)
×
2636
                queue = true
×
2637
        }
×
2638
        if cont.config.EnableHppDirect && !reflect.DeepEqual(oldnp.Spec, newnp.Spec) {
×
2639
                cont.deleteHppCr(oldnp)
×
2640
                queue = true
×
2641
        }
×
2642
        if queue {
×
2643
                cont.queueNetPolUpdateByKey(npkey)
×
2644
        }
×
2645
}
2646

2647
func (cont *AciController) networkPolicyDeleted(obj interface{}) {
1✔
2648
        np, isNetworkpolicy := obj.(*v1net.NetworkPolicy)
1✔
2649
        if !isNetworkpolicy {
1✔
2650
                deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
×
2651
                if !ok {
×
2652
                        networkPolicyLogger(cont.log, np).
×
2653
                                Error("Received unexpected object: ", obj)
×
2654
                        return
×
2655
                }
×
2656
                np, ok = deletedState.Obj.(*v1net.NetworkPolicy)
×
2657
                if !ok {
×
2658
                        networkPolicyLogger(cont.log, np).
×
2659
                                Error("DeletedFinalStateUnknown contained non-Networkpolicy object: ", deletedState.Obj)
×
2660
                        return
×
2661
                }
×
2662
        }
2663
        npkey, err := cache.MetaNamespaceKeyFunc(np)
1✔
2664
        if err != nil {
1✔
2665
                networkPolicyLogger(cont.log, np).
×
2666
                        Error("Could not create network policy key: ", err)
×
2667
                return
×
2668
        }
×
2669

2670
        var labelKey string
1✔
2671
        var noHppRef bool
1✔
2672
        if cont.config.HppOptimization || cont.config.EnableHppDirect {
1✔
2673
                labelKey, noHppRef = cont.removeFromHppCache(np, npkey)
×
2674
        } else {
1✔
2675
                labelKey = cont.aciNameForKey("np", npkey)
1✔
2676
                noHppRef = true
1✔
2677
        }
1✔
2678

2679
        cont.indexMutex.Lock()
1✔
2680
        subnets := getNetworkPolicyEgressIpBlocks(np)
1✔
2681
        cont.updateIpIndex(cont.netPolSubnetIndex, subnets, nil, npkey)
1✔
2682

1✔
2683
        ports := cont.getNetPolTargetPorts(np)
1✔
2684
        cont.updateTargetPortIndex(false, npkey, ports, nil)
1✔
2685
        if isNamedPortPresenInNp(np) {
2✔
2686
                delete(cont.nmPortNp, npkey)
1✔
2687
        }
1✔
2688
        cont.indexMutex.Unlock()
1✔
2689

1✔
2690
        cont.netPolPods.DeleteSelectorObj(obj)
1✔
2691
        cont.netPolIngressPods.DeleteSelectorObj(obj)
1✔
2692
        cont.netPolEgressPods.DeleteSelectorObj(obj)
1✔
2693
        if noHppRef && labelKey != "" {
2✔
2694
                cont.apicConn.ClearApicObjects(labelKey)
1✔
2695
        }
1✔
2696
        if cont.config.EnableHppDirect {
1✔
2697
                cont.deleteHppCr(np)
×
2698
        }
×
2699
}
2700

2701
func (sep *serviceEndpoint) SetNpServiceAugmentForService(servicekey string, service *v1.Service, prs *portRemoteSubnet,
2702
        portAugments map[string]*portServiceAugment, subnetIndex cidranger.Ranger, logger *logrus.Entry) {
1✔
2703
        cont := sep.cont
1✔
2704
        endpointsobj, _, err := cont.endpointsIndexer.GetByKey(servicekey)
1✔
2705
        if err != nil {
1✔
2706
                logger.Error("Could not lookup endpoints for "+
×
2707
                        servicekey+": ", err.Error())
×
2708
                return
×
2709
        }
×
2710
        if endpointsobj == nil {
1✔
2711
                return
×
2712
        }
×
2713
        endpoints := endpointsobj.(*v1.Endpoints)
1✔
2714
        npTargetPortsMap := cont.getPortNums(prs.port)
1✔
2715
        for _, svcPort := range service.Spec.Ports {
2✔
2716
                _, ok := npTargetPortsMap[svcPort.TargetPort.IntValue()]
1✔
2717
                if prs.port != nil &&
1✔
2718
                        (svcPort.Protocol != *prs.port.Protocol || !ok) {
2✔
2719
                        // egress rule does not match service target port
1✔
2720
                        continue
1✔
2721
                }
2722
                for _, subset := range endpoints.Subsets {
2✔
2723
                        var foundEpPort *v1.EndpointPort
1✔
2724
                        for ix := range subset.Ports {
2✔
2725
                                if subset.Ports[ix].Name == svcPort.Name ||
1✔
2726
                                        (len(service.Spec.Ports) == 1 &&
1✔
2727
                                                subset.Ports[ix].Name == "") {
2✔
2728
                                        foundEpPort = &subset.Ports[ix]
1✔
2729
                                        break
1✔
2730
                                }
2731
                        }
2732
                        if foundEpPort == nil {
1✔
2733
                                continue
×
2734
                        }
2735

2736
                        incomplete := false
1✔
2737
                        incomplete = incomplete ||
1✔
2738
                                !checkEndpoints(subnetIndex, subset.Addresses)
1✔
2739
                        incomplete = incomplete || !checkEndpoints(subnetIndex,
1✔
2740
                                subset.NotReadyAddresses)
1✔
2741

1✔
2742
                        if incomplete {
2✔
2743
                                continue
1✔
2744
                        }
2745

2746
                        proto := portProto(&foundEpPort.Protocol)
1✔
2747
                        port := strconv.Itoa(int(svcPort.Port))
1✔
2748
                        updateServiceAugmentForService(portAugments,
1✔
2749
                                proto, port, service)
1✔
2750

1✔
2751
                        logger.WithFields(logrus.Fields{
1✔
2752
                                "proto":   proto,
1✔
2753
                                "port":    port,
1✔
2754
                                "service": servicekey,
1✔
2755
                        }).Debug("Allowing egress for service by subnet match")
1✔
2756
                }
2757
        }
2758
}
2759

2760
func (seps *serviceEndpointSlice) SetNpServiceAugmentForService(servicekey string, service *v1.Service,
2761
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
2762
        subnetIndex cidranger.Ranger, logger *logrus.Entry) {
1✔
2763
        cont := seps.cont
1✔
2764
        npTargetPortsMap := cont.getPortNums(prs.port)
1✔
2765

1✔
2766
        // Helper function to check if a numeric port matches the NetworkPolicy port spec
1✔
2767
        checkNumericPortMatchesNetpol := func(port int) bool {
2✔
2768
                if prs.port.EndPort != nil {
1✔
2769
                        // Port range matching: port must be within [Port, EndPort]
×
2770
                        return port >= prs.port.Port.IntValue() && port <= int(*prs.port.EndPort)
×
2771
                }
×
2772
                // Single port matching: check if port is in the target ports map
2773
                return npTargetPortsMap[port]
1✔
2774
        }
2775

2776
        label := map[string]string{discovery.LabelServiceName: service.ObjectMeta.Name}
1✔
2777
        selector := labels.SelectorFromSet(label)
1✔
2778

1✔
2779
        endpointSliceList, err := cont.endpointSliceIndexer.ByIndex("namespace", service.ObjectMeta.Namespace)
1✔
2780
        if err != nil {
1✔
2781
                logger.Error("Could not list endpoint slices: ", err)
×
2782
                return
×
2783
        }
×
2784
        for _, svcPort := range service.Spec.Ports {
2✔
2785
                incomplete := false
1✔
2786
                hasValidatedSlice := false
1✔
2787
                if prs.port != nil &&
1✔
2788
                        (svcPort.Protocol != *prs.port.Protocol) {
1✔
2789
                        // egress rule does not match service target port
×
2790
                        continue
×
2791
                }
2792
                // Match any port if no port is specified in the np
2793
                portMatched := prs.port == nil || prs.port.Port == nil
1✔
2794

1✔
2795
                if !portMatched {
2✔
2796
                        if svcPort.TargetPort.Type == intstr.String {
1✔
2797
                                if prs.port.Port.Type == intstr.String {
×
2798
                                        if prs.port.Port.String() != svcPort.TargetPort.String() {
×
2799
                                                continue
×
2800
                                        }
2801
                                        portMatched = true
×
2802
                                }
2803
                        } else {
1✔
2804
                                if !checkNumericPortMatchesNetpol(svcPort.TargetPort.IntValue()) {
1✔
2805
                                        continue
×
2806
                                }
2807
                                portMatched = true
1✔
2808
                        }
2809
                }
2810

2811
                for _, endpointSliceobj := range endpointSliceList {
2✔
2812
                        endpointSlices := endpointSliceobj.(*discovery.EndpointSlice)
1✔
2813
                        if !selector.Matches(labels.Set(endpointSlices.Labels)) {
2✔
2814
                                continue
1✔
2815
                        }
2816

2817
                        var foundEpPort *discovery.EndpointPort
1✔
2818
                        for ix := range endpointSlices.Ports {
2✔
2819
                                if endpointSlices.Ports[ix].Name != nil && *endpointSlices.Ports[ix].Name == svcPort.Name ||
1✔
2820
                                        (len(service.Spec.Ports) == 1 &&
1✔
2821
                                                endpointSlices.Ports[ix].Name != nil && *endpointSlices.Ports[ix].Name == "") {
2✔
2822
                                        foundEpPort = &endpointSlices.Ports[ix]
1✔
2823
                                        cont.log.Debug("Found EpPort: ", foundEpPort)
1✔
2824
                                        break
1✔
2825
                                }
2826
                        }
2827

2828
                        if foundEpPort == nil {
1✔
2829
                                continue
×
2830
                        }
2831
                        if !portMatched && (foundEpPort.Port == nil || !checkNumericPortMatchesNetpol(int(*foundEpPort.Port))) {
1✔
2832
                                incomplete = true
×
2833
                                break
×
2834
                        }
2835
                        // @FIXME for non ready address
2836
                        for _, endpoint := range endpointSlices.Endpoints {
2✔
2837
                                incomplete = incomplete || !checkEndpointslices(subnetIndex, endpoint.Addresses)
1✔
2838
                        }
1✔
2839
                        if incomplete {
2✔
2840
                                break
1✔
2841
                        }
2842
                        hasValidatedSlice = true
1✔
2843
                }
2844
                if !incomplete && hasValidatedSlice {
2✔
2845
                        proto := portProto(&svcPort.Protocol)
1✔
2846
                        port := strconv.Itoa(int(svcPort.Port))
1✔
2847
                        cont.log.Debug("updateServiceAugmentForService: ", service)
1✔
2848
                        updateServiceAugmentForService(portAugments,
1✔
2849
                                proto, port, service)
1✔
2850
                        logger.WithFields(logrus.Fields{
1✔
2851
                                "proto":   proto,
1✔
2852
                                "port":    port,
1✔
2853
                                "service": servicekey,
1✔
2854
                        }).Debug("Allowing egress for service by subnet match")
1✔
2855
                }
1✔
2856
        }
2857
}
2858

2859
func isNamedPortPresenInNp(np *v1net.NetworkPolicy) bool {
1✔
2860
        for _, egress := range np.Spec.Egress {
2✔
2861
                for _, p := range egress.Ports {
2✔
2862
                        if p.Port.Type == intstr.String {
2✔
2863
                                return true
1✔
2864
                        }
1✔
2865
                }
2866
        }
2867
        return false
1✔
2868
}
2869

2870
func (cont *AciController) checkPodNmpMatchesNp(npkey, podkey string) bool {
1✔
2871
        podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
2872
        if err != nil {
1✔
2873
                return false
×
2874
        }
×
2875
        if !exists || podobj == nil {
2✔
2876
                return false
1✔
2877
        }
1✔
2878
        pod := podobj.(*v1.Pod)
1✔
2879
        npobj, npexists, nperr := cont.networkPolicyIndexer.GetByKey(npkey)
1✔
2880
        if npexists && nperr == nil && npobj != nil {
2✔
2881
                np := npobj.(*v1net.NetworkPolicy)
1✔
2882
                for _, egress := range np.Spec.Egress {
2✔
2883
                        for _, p := range egress.Ports {
2✔
2884
                                if p.Port.Type == intstr.String {
2✔
2885
                                        _, err := k8util.LookupContainerPortNumberByName(*pod, p.Port.String())
1✔
2886
                                        if err == nil {
2✔
2887
                                                return true
1✔
2888
                                        }
1✔
2889
                                }
2890
                        }
2891
                }
2892
        }
2893
        return false
1✔
2894
}
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