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

noironetworks / aci-containers / 11884

30 Apr 2026 07:04AM UTC coverage: 63.102% (-0.2%) from 63.267%
11884

push

travis-pro

web-flow
Merge pull request #1715 from noironetworks/backport-remove-endpoint-functions

[mmr-6.1.1] Remove stale Endpoints API code

13312 of 21096 relevant lines covered (63.1%)

0.72 hits per line

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

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

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

18
package controller
19

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

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

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

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

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

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

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

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

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

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

130
        return ret
1✔
131
}
132

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

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

140
        return ret
1✔
141
}
142

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

353
        return true
1✔
354
}
355

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

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

373
        return true
1✔
374
}
375

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

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

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

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

405
        return true
1✔
406
}
407

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

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

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

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

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

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

450
        return true
1✔
451
}
452

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

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

466
        return true
1✔
467
}
468

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

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

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

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

498
        return true
1✔
499
}
500

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

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

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

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

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

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

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

568
        return true
1✔
569
}
570

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

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

592
        return rules
1✔
593
}
594

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

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

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

649
        return rules
1✔
650
}
651

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1905
        aobj.Spec.HostprotRemoteIp = buildHostprotRemoteIpList(remIpCont)
1✔
1906

1✔
1907
        if isUpdate {
1✔
1908
                // Skip update if spec hasn't changed
×
1909
                if reflect.DeepEqual(existingSpec, aobj.Spec) {
×
1910
                        cont.log.Debug("HostprotRemoteIpContainer CR unchanged, skipping update: ", ns)
×
1911
                        return false
×
1912
                }
×
1913
                if !cont.updateHostprotRemoteIpContainer(aobj, sysNs) {
×
1914
                        return true
×
1915
                }
×
1916
        } else {
1✔
1917
                if !cont.createHostprotRemoteIpContainer(aobj, sysNs) {
1✔
1918
                        return true
×
1919
                }
×
1920
        }
1921

1922
        return false
1✔
1923
}
1924

1925
func buildHostprotRemoteIpList(remIpConts map[string]remoteIpCont) []hppv1.HostprotRemoteIp {
1✔
1926
        hostprotRemoteIpList := []hppv1.HostprotRemoteIp{}
1✔
1927

1✔
1928
        // Sort pod names for deterministic ordering
1✔
1929
        podNames := make([]string, 0, len(remIpConts))
1✔
1930
        for podName := range remIpConts {
2✔
1931
                podNames = append(podNames, podName)
1✔
1932
        }
1✔
1933
        sort.Strings(podNames)
1✔
1934

1✔
1935
        for _, podName := range podNames {
2✔
1936
                remIpCont := remIpConts[podName]
1✔
1937
                // Sort IPs for deterministic ordering
1✔
1938
                ips := make([]string, 0, len(remIpCont))
1✔
1939
                for ip := range remIpCont {
2✔
1940
                        ips = append(ips, ip)
1✔
1941
                }
1✔
1942
                sort.Strings(ips)
1✔
1943

1✔
1944
                for _, ip := range ips {
2✔
1945
                        labels := remIpCont[ip]
1✔
1946
                        remIpObj := hppv1.HostprotRemoteIp{
1✔
1947
                                Addr: ip,
1✔
1948
                        }
1✔
1949
                        // Sort label keys for deterministic ordering
1✔
1950
                        labelKeys := make([]string, 0, len(labels))
1✔
1951
                        for key := range labels {
2✔
1952
                                labelKeys = append(labelKeys, key)
1✔
1953
                        }
1✔
1954
                        sort.Strings(labelKeys)
1✔
1955

1✔
1956
                        for _, key := range labelKeys {
2✔
1957
                                remIpObj.HppEpLabel = append(remIpObj.HppEpLabel, hppv1.HppEpLabel{
1✔
1958
                                        Key:   key,
1✔
1959
                                        Value: labels[key],
1✔
1960
                                })
1✔
1961
                        }
1✔
1962
                        hostprotRemoteIpList = append(hostprotRemoteIpList, remIpObj)
1✔
1963
                }
1964
        }
1965

1966
        return hostprotRemoteIpList
1✔
1967
}
1968

1969
func (cont *AciController) deleteHppCr(np *v1net.NetworkPolicy) bool {
1✔
1970
        key, err := cache.MetaNamespaceKeyFunc(np)
1✔
1971
        logger := networkPolicyLogger(cont.log, np)
1✔
1972
        if err != nil {
1✔
1973
                logger.Error("Could not create network policy key: ", err)
×
1974
                return false
×
1975
        }
×
1976
        hash, err := util.CreateHashFromNetPol(np)
1✔
1977
        if err != nil {
1✔
1978
                logger.Error("Could not create hash from network policy: ", err)
×
1979
                return false
×
1980
        }
×
1981
        labelKey := cont.aciNameForKey("np", hash)
1✔
1982
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
1983
        hppName := strings.ReplaceAll(labelKey, "_", "-")
1✔
1984
        hpp, _ := cont.getHostprotPol(hppName, ns)
1✔
1985
        if hpp == nil {
2✔
1986
                logger.Error("Could not find hostprotPol: ", hppName)
1✔
1987
                return false
1✔
1988
        }
1✔
1989
        netPols := hpp.Spec.NetworkPolicies
1✔
1990
        newNetPols := make([]string, 0)
1✔
1991
        for _, npName := range netPols {
2✔
1992
                if npName != key {
2✔
1993
                        newNetPols = append(newNetPols, npName)
1✔
1994
                }
1✔
1995
        }
1996

1997
        hpp.Spec.NetworkPolicies = newNetPols
1✔
1998

1✔
1999
        if len(newNetPols) > 0 {
2✔
2000
                return cont.updateHostprotPol(hpp, ns)
1✔
2001
        } else {
2✔
2002
                return cont.deleteHostprotPol(hppName, ns)
1✔
2003
        }
1✔
2004
}
2005

2006
func (cont *AciController) updateNodeIpsHostprotRemoteIpContainer(nodeIps map[string]bool) {
1✔
2007
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2008
        name := "nodeips"
1✔
2009

1✔
2010
        aobj, err := cont.getHostprotRemoteIpContainer(name, ns)
1✔
2011
        isUpdate := err == nil
1✔
2012

1✔
2013
        if err != nil && !errors.IsNotFound(err) {
1✔
2014
                cont.log.Error("Error getting HostprotRemoteIpContainers CR: ", err)
×
2015
                return
×
2016
        }
×
2017

2018
        var existingSpec hppv1.HostprotRemoteIpContainerSpec
1✔
2019
        if !isUpdate {
2✔
2020
                aobj = &hppv1.HostprotRemoteIpContainer{
1✔
2021
                        ObjectMeta: metav1.ObjectMeta{
1✔
2022
                                Name:      name,
1✔
2023
                                Namespace: ns,
1✔
2024
                        },
1✔
2025
                        Spec: hppv1.HostprotRemoteIpContainerSpec{
1✔
2026
                                Name:             name,
1✔
2027
                                HostprotRemoteIp: []hppv1.HostprotRemoteIp{},
1✔
2028
                        },
1✔
2029
                }
1✔
2030
        } else {
2✔
2031
                cont.log.Debug("HostprotRemoteIpContainers CR already exists: ", aobj)
1✔
2032
                existingSpec = aobj.Spec
1✔
2033
        }
1✔
2034

2035
        existingIps := make(map[string]bool)
1✔
2036
        for _, ip := range aobj.Spec.HostprotRemoteIp {
2✔
2037
                existingIps[ip.Addr] = true
1✔
2038
        }
1✔
2039

2040
        // Sort IPs for deterministic ordering
2041
        sortedIps := make([]string, 0, len(nodeIps))
1✔
2042
        for ip := range nodeIps {
2✔
2043
                sortedIps = append(sortedIps, ip)
1✔
2044
        }
1✔
2045
        sort.Strings(sortedIps)
1✔
2046

1✔
2047
        for _, ip := range sortedIps {
2✔
2048
                if !existingIps[ip] {
2✔
2049
                        aobj.Spec.HostprotRemoteIp = append(aobj.Spec.HostprotRemoteIp, hppv1.HostprotRemoteIp{Addr: ip})
1✔
2050
                }
1✔
2051
        }
2052

2053
        if isUpdate {
2✔
2054
                // Skip update if spec hasn't changed
1✔
2055
                if reflect.DeepEqual(existingSpec, aobj.Spec) {
1✔
2056
                        cont.log.Debug("HostprotRemoteIpContainer CR unchanged, skipping update: ", name)
×
2057
                        return
×
2058
                }
×
2059
                cont.updateHostprotRemoteIpContainer(aobj, ns)
1✔
2060
        } else {
1✔
2061
                cont.createHostprotRemoteIpContainer(aobj, ns)
1✔
2062
        }
1✔
2063
}
2064

2065
func (cont *AciController) deleteNodeIpsHostprotRemoteIpContainer(nodeIps map[string]bool) {
1✔
2066
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2067
        name := "nodeips"
1✔
2068

1✔
2069
        aobj, _ := cont.getHostprotRemoteIpContainer(name, ns)
1✔
2070
        if aobj == nil {
1✔
2071
                return
×
2072
        }
×
2073

2074
        newHostprotRemoteIps := aobj.Spec.HostprotRemoteIp[:0]
1✔
2075
        for _, hostprotRemoteIp := range aobj.Spec.HostprotRemoteIp {
2✔
2076
                if len(nodeIps) > 0 && !nodeIps[hostprotRemoteIp.Addr] {
2✔
2077
                        newHostprotRemoteIps = append(newHostprotRemoteIps, hostprotRemoteIp)
1✔
2078
                }
1✔
2079
        }
2080

2081
        aobj.Spec.HostprotRemoteIp = newHostprotRemoteIps
1✔
2082

1✔
2083
        if len(newHostprotRemoteIps) > 0 {
2✔
2084
                cont.updateHostprotRemoteIpContainer(aobj, ns)
1✔
2085
        } else {
2✔
2086
                cont.deleteHostprotRemoteIpContainer(name, ns)
1✔
2087
        }
1✔
2088
}
2089

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

1✔
2093
        aobj, err := cont.getHostprotRemoteIpContainer(name, ns)
1✔
2094
        isUpdate := err == nil
1✔
2095

1✔
2096
        if err != nil && !errors.IsNotFound(err) {
1✔
2097
                cont.log.Error("Error getting HostprotRemoteIpContainers CR: ", err)
×
2098
                return
×
2099
        }
×
2100

2101
        var existingSpec hppv1.HostprotRemoteIpContainerSpec
1✔
2102
        if !isUpdate {
2✔
2103
                aobj = &hppv1.HostprotRemoteIpContainer{
1✔
2104
                        ObjectMeta: metav1.ObjectMeta{
1✔
2105
                                Name:      name,
1✔
2106
                                Namespace: ns,
1✔
2107
                        },
1✔
2108
                        Spec: hppv1.HostprotRemoteIpContainerSpec{
1✔
2109
                                Name:             name,
1✔
2110
                                HostprotRemoteIp: []hppv1.HostprotRemoteIp{},
1✔
2111
                        },
1✔
2112
                }
1✔
2113
        } else {
2✔
2114
                cont.log.Debug("HostprotRemoteIpContainers CR already exists: ", aobj)
1✔
2115
                existingSpec = aobj.Spec
1✔
2116
        }
1✔
2117

2118
        // Sort IPs for deterministic ordering
2119
        sortedIps := make([]string, 0, len(nodeIps))
1✔
2120
        for ip := range nodeIps {
2✔
2121
                sortedIps = append(sortedIps, ip)
1✔
2122
        }
1✔
2123
        sort.Strings(sortedIps)
1✔
2124

1✔
2125
        aobj.Spec.HostprotRemoteIp = make([]hppv1.HostprotRemoteIp, 0, len(nodeIps))
1✔
2126
        for _, ip := range sortedIps {
2✔
2127
                aobj.Spec.HostprotRemoteIp = append(aobj.Spec.HostprotRemoteIp, hppv1.HostprotRemoteIp{Addr: ip})
1✔
2128
        }
1✔
2129

2130
        if isUpdate {
2✔
2131
                // Skip update if spec hasn't changed
1✔
2132
                if reflect.DeepEqual(existingSpec, aobj.Spec) {
1✔
2133
                        cont.log.Debug("HostprotRemoteIpContainer CR unchanged, skipping update: ", name)
×
2134
                        return
×
2135
                }
×
2136
                cont.updateHostprotRemoteIpContainer(aobj, ns)
1✔
2137
        } else {
1✔
2138
                cont.createHostprotRemoteIpContainer(aobj, ns)
1✔
2139
        }
1✔
2140
}
2141

2142
func (cont *AciController) deleteNodeHostprotRemoteIpContainer(name string) {
1✔
2143
        ns := os.Getenv("SYSTEM_NAMESPACE")
1✔
2144

1✔
2145
        if _, err := cont.getHostprotRemoteIpContainer(name, ns); err == nil {
2✔
2146
                cont.deleteHostprotRemoteIpContainer(name, ns)
1✔
2147
        }
1✔
2148
}
2149

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

1✔
2154
        hpp, err := cont.getHostprotPol(hppName, ns)
1✔
2155
        isUpdate := hpp != nil && err == nil
1✔
2156

1✔
2157
        if err != nil && !errors.IsNotFound(err) {
1✔
2158
                cont.log.Error("Error getting HPP CR: ", err)
×
2159
                return
×
2160
        }
×
2161

2162
        if !isUpdate {
1✔
2163
                hpp = &hppv1.HostprotPol{
×
2164
                        ObjectMeta: metav1.ObjectMeta{
×
2165
                                Name:      hppName,
×
2166
                                Namespace: ns,
×
2167
                        },
×
2168
                        Spec: hppv1.HostprotPolSpec{
×
2169
                                Name:            name,
×
2170
                                NetworkPolicies: []string{name},
×
2171
                                HostprotSubj:    []hppv1.HostprotSubj{},
×
2172
                        },
×
2173
                }
×
2174
        } else {
1✔
2175
                cont.log.Debug("HPP CR already exists: ", hpp)
1✔
2176
                hpp.Spec.HostprotSubj = []hppv1.HostprotSubj{}
1✔
2177
        }
1✔
2178

2179
        if len(nodeIps) > 0 {
2✔
2180
                cont.updateNodeHostprotRemoteIpContainer(nodeName, nodeIps)
1✔
2181
                cont.updateNodeIpsHostprotRemoteIpContainer(nodeIps)
1✔
2182

1✔
2183
                hostprotSubj := hppv1.HostprotSubj{
1✔
2184
                        Name: "local-node",
1✔
2185
                        HostprotRule: []hppv1.HostprotRule{
1✔
2186
                                {
1✔
2187
                                        Name:                "allow-all-egress",
1✔
2188
                                        Direction:           "egress",
1✔
2189
                                        Ethertype:           "ipv4",
1✔
2190
                                        ConnTrack:           "normal",
1✔
2191
                                        RsRemoteIpContainer: []string{nodeName},
1✔
2192
                                },
1✔
2193
                                {
1✔
2194
                                        Name:                "allow-all-ingress",
1✔
2195
                                        Direction:           "ingress",
1✔
2196
                                        Ethertype:           "ipv4",
1✔
2197
                                        ConnTrack:           "normal",
1✔
2198
                                        RsRemoteIpContainer: []string{nodeName},
1✔
2199
                                },
1✔
2200
                        },
1✔
2201
                }
1✔
2202

1✔
2203
                hpp.Spec.HostprotSubj = append(hpp.Spec.HostprotSubj, hostprotSubj)
1✔
2204
        } else {
2✔
2205
                cont.deleteNodeHostprotRemoteIpContainer(nodeName)
1✔
2206
                cont.deleteNodeIpsHostprotRemoteIpContainer(nodeIps)
1✔
2207
        }
1✔
2208

2209
        if isUpdate {
2✔
2210
                cont.updateHostprotPol(hpp, ns)
1✔
2211
        } else {
1✔
2212
                cont.createHostprotPol(hpp, ns)
×
2213
        }
×
2214
}
2215

2216
func (cont *AciController) handleNetPolUpdate(np *v1net.NetworkPolicy) bool {
1✔
2217
        if cont.isCNOEnabled() {
1✔
2218
                return false
×
2219
        }
×
2220
        key, err := cache.MetaNamespaceKeyFunc(np)
1✔
2221
        logger := networkPolicyLogger(cont.log, np)
1✔
2222
        if err != nil {
1✔
2223
                logger.Error("Could not create network policy key: ", err)
×
2224
                return false
×
2225
        }
×
2226

2227
        peerPodKeys := cont.netPolIngressPods.GetPodForObj(key)
1✔
2228
        peerPodKeys =
1✔
2229
                append(peerPodKeys, cont.netPolEgressPods.GetPodForObj(key)...)
1✔
2230
        var peerPods []*v1.Pod
1✔
2231
        peerNs := make(map[string]*v1.Namespace)
1✔
2232
        for _, podkey := range peerPodKeys {
2✔
2233
                podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
2234
                if exists && err == nil {
2✔
2235
                        pod := podobj.(*v1.Pod)
1✔
2236
                        if _, nsok := peerNs[pod.ObjectMeta.Namespace]; !nsok {
2✔
2237
                                nsobj, exists, err :=
1✔
2238
                                        cont.namespaceIndexer.GetByKey(pod.ObjectMeta.Namespace)
1✔
2239
                                if !exists || err != nil {
1✔
2240
                                        continue
×
2241
                                }
2242
                                peerNs[pod.ObjectMeta.Namespace] = nsobj.(*v1.Namespace)
1✔
2243
                        }
2244
                        peerPods = append(peerPods, pod)
1✔
2245
                }
2246
        }
2247
        ptypeset := make(map[v1net.PolicyType]bool)
1✔
2248
        for _, t := range np.Spec.PolicyTypes {
2✔
2249
                ptypeset[t] = true
1✔
2250
        }
1✔
2251
        var labelKey string
1✔
2252

1✔
2253
        if !cont.config.EnableHppDirect {
2✔
2254
                if cont.config.HppOptimization {
2✔
2255
                        hash, err := util.CreateHashFromNetPol(np)
1✔
2256
                        if err != nil {
1✔
2257
                                logger.Error("Could not create hash from network policy: ", err)
×
2258
                                return false
×
2259
                        }
×
2260
                        labelKey = cont.aciNameForKey("np", hash)
1✔
2261
                } else {
1✔
2262
                        labelKey = cont.aciNameForKey("np", key)
1✔
2263
                }
1✔
2264
                hpp := apicapi.NewHostprotPol(cont.config.AciPolicyTenant, labelKey)
1✔
2265
                // Generate ingress policies
1✔
2266
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeIngress] {
2✔
2267
                        subjIngress :=
1✔
2268
                                apicapi.NewHostprotSubj(hpp.GetDn(), "networkpolicy-ingress")
1✔
2269

1✔
2270
                        for i, ingress := range np.Spec.Ingress {
2✔
2271
                                addPodSubnetAsRemIp := isAllowAllForAllNamespaces(ingress.From)
1✔
2272
                                remoteSubnets, _, _, _, _ := cont.getPeerRemoteSubnets(ingress.From,
1✔
2273
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2274
                                cont.buildNetPolSubjRules(strconv.Itoa(i), subjIngress,
1✔
2275
                                        "ingress", ingress.From, remoteSubnets, ingress.Ports, logger, key, np, addPodSubnetAsRemIp)
1✔
2276
                        }
1✔
2277
                        hpp.AddChild(subjIngress)
1✔
2278
                }
2279
                // Generate egress policies
2280
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeEgress] {
2✔
2281
                        subjEgress :=
1✔
2282
                                apicapi.NewHostprotSubj(hpp.GetDn(), "networkpolicy-egress")
1✔
2283

1✔
2284
                        portRemoteSubs := make(map[string]*portRemoteSubnet)
1✔
2285

1✔
2286
                        for i, egress := range np.Spec.Egress {
2✔
2287
                                addPodSubnetAsRemIp := isAllowAllForAllNamespaces(egress.To)
1✔
2288
                                remoteSubnets, _, _, subnetMap, _ := cont.getPeerRemoteSubnets(egress.To,
1✔
2289
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2290
                                cont.buildNetPolSubjRules(strconv.Itoa(i), subjEgress,
1✔
2291
                                        "egress", egress.To, remoteSubnets, egress.Ports, logger, key, np, addPodSubnetAsRemIp)
1✔
2292

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

1✔
2331
                if err != nil && !errors.IsNotFound(err) {
1✔
2332
                        logger.Error("Error getting HPP CR: ", err)
×
2333
                        return false
×
2334
                }
×
2335

2336
                if isUpdate {
1✔
2337
                        logger.Debug("HPP CR already exists: ", hpp)
×
2338
                        if !slices.Contains(hpp.Spec.NetworkPolicies, key) {
×
2339
                                hpp.Spec.NetworkPolicies = append(hpp.Spec.NetworkPolicies, key)
×
2340
                        }
×
2341
                        hpp.Spec.HostprotSubj = nil
×
2342
                } else {
1✔
2343
                        hpp = &hppv1.HostprotPol{
1✔
2344
                                ObjectMeta: metav1.ObjectMeta{
1✔
2345
                                        Name:      hppName,
1✔
2346
                                        Namespace: ns,
1✔
2347
                                },
1✔
2348
                                Spec: hppv1.HostprotPolSpec{
1✔
2349
                                        Name:            labelKey,
1✔
2350
                                        NetworkPolicies: []string{key},
1✔
2351
                                        HostprotSubj:    nil,
1✔
2352
                                },
1✔
2353
                        }
1✔
2354
                }
1✔
2355

2356
                // Generate ingress policies
2357
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeIngress] {
2✔
2358
                        subjIngress := &hppv1.HostprotSubj{
1✔
2359
                                Name:         "networkpolicy-ingress",
1✔
2360
                                HostprotRule: []hppv1.HostprotRule{},
1✔
2361
                        }
1✔
2362

1✔
2363
                        for i, ingress := range np.Spec.Ingress {
2✔
2364
                                remoteSubnets, peerNsList, peerremote, _, peerIpBlock := cont.getPeerRemoteSubnets(ingress.From,
1✔
2365
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2366
                                if isAllowAllForAllNamespaces(ingress.From) {
1✔
2367
                                        peerNsList = append(peerNsList, "nodeips")
×
2368
                                }
×
2369
                                if !(len(ingress.From) > 0 && len(remoteSubnets) == 0) {
2✔
2370
                                        cont.buildLocalNetPolSubjRules(strconv.Itoa(i), subjIngress,
1✔
2371
                                                "ingress", peerNsList, peerremote.podSelectors, ingress.Ports,
1✔
2372
                                                logger, key, np, peerIpBlock)
1✔
2373
                                }
1✔
2374
                        }
2375
                        hpp.Spec.HostprotSubj = append(hpp.Spec.HostprotSubj, *subjIngress)
1✔
2376
                }
2377

2378
                if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeEgress] {
2✔
2379
                        subjEgress := &hppv1.HostprotSubj{
1✔
2380
                                Name:         "networkpolicy-egress",
1✔
2381
                                HostprotRule: []hppv1.HostprotRule{},
1✔
2382
                        }
1✔
2383

1✔
2384
                        portRemoteSubs := make(map[string]*portRemoteSubnet)
1✔
2385

1✔
2386
                        for i, egress := range np.Spec.Egress {
2✔
2387
                                remoteSubnets, peerNsList, peerremote, subnetMap, peerIpBlock := cont.getPeerRemoteSubnets(egress.To,
1✔
2388
                                        np.Namespace, peerPods, peerNs, logger)
1✔
2389
                                if isAllowAllForAllNamespaces(egress.To) {
1✔
2390
                                        peerNsList = append(peerNsList, "nodeips")
×
2391
                                }
×
2392
                                if !(len(egress.To) > 0 && len(remoteSubnets) == 0) {
2✔
2393
                                        cont.buildLocalNetPolSubjRules(strconv.Itoa(i), subjEgress,
1✔
2394
                                                "egress", peerNsList, peerremote.podSelectors, egress.Ports, logger, key, np, peerIpBlock)
1✔
2395
                                }
1✔
2396

2397
                                if len(egress.To) == 0 {
2✔
2398
                                        subnetMap = map[string]bool{"0.0.0.0/0": true}
1✔
2399
                                }
1✔
2400
                                for idx := range egress.Ports {
2✔
2401
                                        port := egress.Ports[idx]
1✔
2402
                                        portkey := portKey(&port)
1✔
2403
                                        updatePortRemoteSubnets(portRemoteSubs, portkey, &port, subnetMap,
1✔
2404
                                                port.Port != nil && port.Port.Type == intstr.Int)
1✔
2405
                                }
1✔
2406
                                if len(egress.Ports) == 0 {
1✔
2407
                                        updatePortRemoteSubnets(portRemoteSubs, "", nil, subnetMap,
×
2408
                                                false)
×
2409
                                }
×
2410
                        }
2411
                        cont.buildServiceAugment(nil, subjEgress, portRemoteSubs, logger)
1✔
2412
                        hpp.Spec.HostprotSubj = append(hpp.Spec.HostprotSubj, *subjEgress)
1✔
2413
                }
2414

2415
                cont.addToHppCache(labelKey, key, apicapi.ApicSlice{}, hpp)
1✔
2416

1✔
2417
                if isUpdate {
1✔
2418
                        cont.updateHostprotPol(hpp, ns)
×
2419
                } else {
1✔
2420
                        cont.createHostprotPol(hpp, ns)
1✔
2421
                }
1✔
2422
        }
2423
        return false
1✔
2424
}
2425

2426
func (cont *AciController) updateNsRemoteIpCont(pod *v1.Pod, deleted bool) bool {
1✔
2427
        podips := ipsForPod(pod)
1✔
2428
        podns := pod.ObjectMeta.Namespace
1✔
2429
        podname := pod.ObjectMeta.Name
1✔
2430
        podlabels := pod.ObjectMeta.Labels
1✔
2431
        remipconts, ok := cont.nsRemoteIpCont[podns]
1✔
2432

1✔
2433
        if deleted {
2✔
2434
                if !ok {
2✔
2435
                        return true
1✔
2436
                }
1✔
2437

2438
                present := false
1✔
2439
                if remipcont, remipcontok := remipconts[podname]; remipcontok {
1✔
2440
                        for _, ip := range podips {
×
2441
                                if _, ipok := remipcont[ip]; ipok {
×
2442
                                        delete(remipcont, ip)
×
2443
                                        present = true
×
2444
                                }
×
2445
                        }
2446
                        if len(remipcont) < 1 {
×
2447
                                delete(remipconts, podname)
×
2448
                        }
×
2449
                }
2450

2451
                if len(remipconts) < 1 {
1✔
2452
                        delete(cont.nsRemoteIpCont, podns)
×
2453
                        cont.apicConn.ClearApicObjects(cont.aciNameForKey("hostprot-ns-", podns))
×
2454
                        return false
×
2455
                }
×
2456

2457
                if !present {
2✔
2458
                        return false
1✔
2459
                }
1✔
2460
        } else {
1✔
2461
                if !ok {
2✔
2462
                        remipconts = make(remoteIpConts)
1✔
2463
                        cont.nsRemoteIpCont[podns] = remipconts
1✔
2464
                }
1✔
2465

2466
                remipcont, remipcontok := remipconts[podname]
1✔
2467
                if !remipcontok {
2✔
2468
                        remipcont = make(remoteIpCont)
1✔
2469
                }
1✔
2470
                for _, ip := range podips {
2✔
2471
                        remipcont[ip] = podlabels
1✔
2472
                }
1✔
2473
                remipconts[podname] = remipcont
1✔
2474
        }
2475

2476
        return true
1✔
2477
}
2478

2479
func (cont *AciController) addToHppCache(labelKey, key string, hpp apicapi.ApicSlice, hppcr *hppv1.HostprotPol) {
1✔
2480
        cont.indexMutex.Lock()
1✔
2481
        hppRef, ok := cont.hppRef[labelKey]
1✔
2482
        if ok {
2✔
2483
                var found bool
1✔
2484
                for _, npkey := range hppRef.Npkeys {
2✔
2485
                        if npkey == key {
2✔
2486
                                found = true
1✔
2487
                                break
1✔
2488
                        }
2489
                }
2490
                if !found {
1✔
2491
                        hppRef.RefCount++
×
2492
                        hppRef.Npkeys = append(hppRef.Npkeys, key)
×
2493
                }
×
2494
                hppRef.HppObj = hpp
1✔
2495
                hppRef.HppCr = *hppcr
1✔
2496
                cont.hppRef[labelKey] = hppRef
1✔
2497
        } else {
1✔
2498
                var newHppRef hppReference
1✔
2499
                newHppRef.RefCount++
1✔
2500
                newHppRef.HppObj = hpp
1✔
2501
                newHppRef.HppCr = *hppcr
1✔
2502
                newHppRef.Npkeys = append(newHppRef.Npkeys, key)
1✔
2503
                cont.hppRef[labelKey] = newHppRef
1✔
2504
        }
1✔
2505
        cont.indexMutex.Unlock()
1✔
2506
}
2507

2508
func (cont *AciController) removeFromHppCache(np *v1net.NetworkPolicy, key string) (string, bool) {
1✔
2509
        var labelKey string
1✔
2510
        var noRef bool
1✔
2511
        hash, err := util.CreateHashFromNetPol(np)
1✔
2512
        if err != nil {
1✔
2513
                cont.log.Error("Could not create hash from network policy: ", err)
×
2514
                cont.log.Error("Failed to remove np from hpp cache")
×
2515
                return labelKey, noRef
×
2516
        }
×
2517
        labelKey = cont.aciNameForKey("np", hash)
1✔
2518
        cont.indexMutex.Lock()
1✔
2519
        hppRef, ok := cont.hppRef[labelKey]
1✔
2520
        if ok {
2✔
2521
                for i, npkey := range hppRef.Npkeys {
2✔
2522
                        if npkey == key {
2✔
2523
                                hppRef.Npkeys = append(hppRef.Npkeys[:i], hppRef.Npkeys[i+1:]...)
1✔
2524
                                hppRef.RefCount--
1✔
2525
                                break
1✔
2526
                        }
2527
                }
2528
                if hppRef.RefCount > 0 {
1✔
2529
                        cont.hppRef[labelKey] = hppRef
×
2530
                } else {
1✔
2531
                        delete(cont.hppRef, labelKey)
1✔
2532
                        noRef = true
1✔
2533
                }
1✔
2534
        }
2535
        cont.indexMutex.Unlock()
1✔
2536
        return labelKey, noRef
1✔
2537
}
2538

2539
func getNetworkPolicyEgressIpBlocks(np *v1net.NetworkPolicy) map[string]bool {
1✔
2540
        subnets := make(map[string]bool)
1✔
2541
        for _, egress := range np.Spec.Egress {
2✔
2542
                for _, to := range egress.To {
2✔
2543
                        if to.IPBlock != nil && to.IPBlock.CIDR != "" {
2✔
2544
                                subnets[to.IPBlock.CIDR] = true
1✔
2545
                        }
1✔
2546
                }
2547
        }
2548
        return subnets
1✔
2549
}
2550

2551
func (cont *AciController) networkPolicyAdded(obj interface{}) {
1✔
2552
        np := obj.(*v1net.NetworkPolicy)
1✔
2553
        npkey, err := cache.MetaNamespaceKeyFunc(np)
1✔
2554
        if err != nil {
1✔
2555
                networkPolicyLogger(cont.log, np).
×
2556
                        Error("Could not create network policy key: ", err)
×
2557
                return
×
2558
        }
×
2559
        if cont.isCNOEnabled() {
1✔
2560
                return
×
2561
        }
×
2562
        cont.netPolPods.UpdateSelectorObj(obj)
1✔
2563
        cont.netPolIngressPods.UpdateSelectorObj(obj)
1✔
2564
        cont.netPolEgressPods.UpdateSelectorObj(obj)
1✔
2565
        cont.indexMutex.Lock()
1✔
2566
        subnets := getNetworkPolicyEgressIpBlocks(np)
1✔
2567
        cont.updateIpIndex(cont.netPolSubnetIndex, nil, subnets, npkey)
1✔
2568

1✔
2569
        ports := cont.getNetPolTargetPorts(np)
1✔
2570
        cont.updateTargetPortIndex(false, npkey, nil, ports)
1✔
2571
        if isNamedPortPresenInNp(np) {
2✔
2572
                cont.nmPortNp[npkey] = true
1✔
2573
        }
1✔
2574
        cont.indexMutex.Unlock()
1✔
2575
        cont.queueNetPolUpdateByKey(npkey)
1✔
2576
}
2577

2578
func (cont *AciController) networkPolicyChanged(oldobj interface{},
2579
        newobj interface{}) {
×
2580
        oldnp := oldobj.(*v1net.NetworkPolicy)
×
2581
        newnp := newobj.(*v1net.NetworkPolicy)
×
2582
        npkey, err := cache.MetaNamespaceKeyFunc(newnp)
×
2583
        if err != nil {
×
2584
                networkPolicyLogger(cont.log, newnp).
×
2585
                        Error("Could not create network policy key: ", err)
×
2586
                return
×
2587
        }
×
2588

2589
        if cont.config.HppOptimization || cont.config.EnableHppDirect {
×
2590
                if !reflect.DeepEqual(oldnp.Spec, newnp.Spec) {
×
2591
                        cont.removeFromHppCache(oldnp, npkey)
×
2592
                }
×
2593
        }
2594

2595
        cont.indexMutex.Lock()
×
2596
        oldSubnets := getNetworkPolicyEgressIpBlocks(oldnp)
×
2597
        newSubnets := getNetworkPolicyEgressIpBlocks(newnp)
×
2598
        cont.updateIpIndex(cont.netPolSubnetIndex, oldSubnets, newSubnets, npkey)
×
2599

×
2600
        oldPorts := cont.getNetPolTargetPorts(oldnp)
×
2601
        newPorts := cont.getNetPolTargetPorts(newnp)
×
2602
        cont.updateTargetPortIndex(false, npkey, oldPorts, newPorts)
×
2603
        cont.indexMutex.Unlock()
×
2604

×
2605
        if !reflect.DeepEqual(oldnp.Spec.PodSelector, newnp.Spec.PodSelector) {
×
2606
                cont.netPolPods.UpdateSelectorObjNoCallback(newobj)
×
2607
        }
×
2608
        if !reflect.DeepEqual(oldnp.Spec.PolicyTypes, newnp.Spec.PolicyTypes) {
×
2609
                peerPodKeys := cont.netPolPods.GetPodForObj(npkey)
×
2610
                for _, podkey := range peerPodKeys {
×
2611
                        cont.podQueue.Add(podkey)
×
2612
                }
×
2613
        }
2614
        var queue bool
×
2615
        if !reflect.DeepEqual(oldnp.Spec.Ingress, newnp.Spec.Ingress) {
×
2616
                cont.netPolIngressPods.UpdateSelectorObjNoCallback(newobj)
×
2617
                queue = true
×
2618
        }
×
2619
        if !reflect.DeepEqual(oldnp.Spec.Egress, newnp.Spec.Egress) {
×
2620
                cont.netPolEgressPods.UpdateSelectorObjNoCallback(newobj)
×
2621
                queue = true
×
2622
        }
×
2623
        if cont.config.EnableHppDirect && !reflect.DeepEqual(oldnp.Spec, newnp.Spec) {
×
2624
                cont.deleteHppCr(oldnp)
×
2625
                queue = true
×
2626
        }
×
2627
        if queue {
×
2628
                cont.queueNetPolUpdateByKey(npkey)
×
2629
        }
×
2630
}
2631

2632
func (cont *AciController) networkPolicyDeleted(obj interface{}) {
1✔
2633
        np, isNetworkpolicy := obj.(*v1net.NetworkPolicy)
1✔
2634
        if !isNetworkpolicy {
1✔
2635
                deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
×
2636
                if !ok {
×
2637
                        networkPolicyLogger(cont.log, np).
×
2638
                                Error("Received unexpected object: ", obj)
×
2639
                        return
×
2640
                }
×
2641
                np, ok = deletedState.Obj.(*v1net.NetworkPolicy)
×
2642
                if !ok {
×
2643
                        networkPolicyLogger(cont.log, np).
×
2644
                                Error("DeletedFinalStateUnknown contained non-Networkpolicy object: ", deletedState.Obj)
×
2645
                        return
×
2646
                }
×
2647
        }
2648
        npkey, err := cache.MetaNamespaceKeyFunc(np)
1✔
2649
        if err != nil {
1✔
2650
                networkPolicyLogger(cont.log, np).
×
2651
                        Error("Could not create network policy key: ", err)
×
2652
                return
×
2653
        }
×
2654

2655
        var labelKey string
1✔
2656
        var noHppRef bool
1✔
2657
        if cont.config.HppOptimization || cont.config.EnableHppDirect {
1✔
2658
                labelKey, noHppRef = cont.removeFromHppCache(np, npkey)
×
2659
        } else {
1✔
2660
                labelKey = cont.aciNameForKey("np", npkey)
1✔
2661
                noHppRef = true
1✔
2662
        }
1✔
2663

2664
        cont.indexMutex.Lock()
1✔
2665
        subnets := getNetworkPolicyEgressIpBlocks(np)
1✔
2666
        cont.updateIpIndex(cont.netPolSubnetIndex, subnets, nil, npkey)
1✔
2667

1✔
2668
        ports := cont.getNetPolTargetPorts(np)
1✔
2669
        cont.updateTargetPortIndex(false, npkey, ports, nil)
1✔
2670
        if isNamedPortPresenInNp(np) {
2✔
2671
                delete(cont.nmPortNp, npkey)
1✔
2672
        }
1✔
2673
        cont.indexMutex.Unlock()
1✔
2674

1✔
2675
        cont.netPolPods.DeleteSelectorObj(obj)
1✔
2676
        cont.netPolIngressPods.DeleteSelectorObj(obj)
1✔
2677
        cont.netPolEgressPods.DeleteSelectorObj(obj)
1✔
2678
        if noHppRef && labelKey != "" {
2✔
2679
                cont.apicConn.ClearApicObjects(labelKey)
1✔
2680
        }
1✔
2681
        if cont.config.EnableHppDirect {
1✔
2682
                cont.deleteHppCr(np)
×
2683
        }
×
2684
}
2685

2686
func (seps *serviceEndpointSlice) SetNpServiceAugmentForService(servicekey string, service *v1.Service,
2687
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
2688
        subnetIndex cidranger.Ranger, logger *logrus.Entry) {
1✔
2689
        cont := seps.cont
1✔
2690
        npTargetPortsMap := cont.getPortNums(prs.port)
1✔
2691

1✔
2692
        // Helper function to check if a numeric port matches the NetworkPolicy port spec
1✔
2693
        checkNumericPortMatchesNetpol := func(port int) bool {
2✔
2694
                if prs.port.EndPort != nil {
2✔
2695
                        // Port range matching: port must be within [Port, EndPort]
1✔
2696
                        return port >= prs.port.Port.IntValue() && port <= int(*prs.port.EndPort)
1✔
2697
                }
1✔
2698
                // Single port matching: check if port is in the target ports map
2699
                return npTargetPortsMap[port]
1✔
2700
        }
2701

2702
        label := map[string]string{discovery.LabelServiceName: service.ObjectMeta.Name}
1✔
2703
        selector := labels.SelectorFromSet(label)
1✔
2704

1✔
2705
        endpointSliceList, err := cont.endpointSliceIndexer.ByIndex("namespace", service.ObjectMeta.Namespace)
1✔
2706
        if err != nil {
1✔
2707
                logger.Error("Could not list endpoint slices: ", err)
×
2708
                return
×
2709
        }
×
2710
        for _, svcPort := range service.Spec.Ports {
2✔
2711
                incomplete := false
1✔
2712
                hasValidatedSlice := false
1✔
2713
                if prs.port != nil &&
1✔
2714
                        (svcPort.Protocol != *prs.port.Protocol) {
1✔
2715
                        // egress rule does not match service target port
×
2716
                        continue
×
2717
                }
2718
                // Match any port if no port is specified in the np
2719
                portMatched := prs.port == nil || prs.port.Port == nil
1✔
2720

1✔
2721
                if !portMatched {
2✔
2722
                        if svcPort.TargetPort.Type == intstr.String {
2✔
2723
                                if prs.port.Port.Type == intstr.String {
2✔
2724
                                        if prs.port.Port.String() != svcPort.TargetPort.String() {
1✔
2725
                                                continue
×
2726
                                        }
2727
                                        portMatched = true
1✔
2728
                                }
2729
                        } else {
1✔
2730
                                if !checkNumericPortMatchesNetpol(svcPort.TargetPort.IntValue()) {
2✔
2731
                                        continue
1✔
2732
                                }
2733
                                portMatched = true
1✔
2734
                        }
2735
                }
2736

2737
                for _, endpointSliceobj := range endpointSliceList {
2✔
2738
                        endpointSlices := endpointSliceobj.(*discovery.EndpointSlice)
1✔
2739
                        if !selector.Matches(labels.Set(endpointSlices.Labels)) {
2✔
2740
                                continue
1✔
2741
                        }
2742

2743
                        var foundEpPort *discovery.EndpointPort
1✔
2744
                        for ix := range endpointSlices.Ports {
2✔
2745
                                if endpointSlices.Ports[ix].Name != nil && *endpointSlices.Ports[ix].Name == svcPort.Name ||
1✔
2746
                                        (len(service.Spec.Ports) == 1 &&
1✔
2747
                                                endpointSlices.Ports[ix].Name != nil && *endpointSlices.Ports[ix].Name == "") {
2✔
2748
                                        foundEpPort = &endpointSlices.Ports[ix]
1✔
2749
                                        cont.log.Debug("Found EpPort: ", foundEpPort)
1✔
2750
                                        break
1✔
2751
                                }
2752
                        }
2753

2754
                        if foundEpPort == nil {
1✔
2755
                                continue
×
2756
                        }
2757
                        if !portMatched && (foundEpPort.Port == nil || !checkNumericPortMatchesNetpol(int(*foundEpPort.Port))) {
1✔
2758
                                incomplete = true
×
2759
                                break
×
2760
                        }
2761
                        // @FIXME for non ready address
2762
                        for _, endpoint := range endpointSlices.Endpoints {
2✔
2763
                                incomplete = incomplete || !checkEndpointslices(subnetIndex, endpoint.Addresses)
1✔
2764
                        }
1✔
2765
                        if incomplete {
2✔
2766
                                break
1✔
2767
                        }
2768
                        hasValidatedSlice = true
1✔
2769
                }
2770
                if !incomplete && hasValidatedSlice {
2✔
2771
                        proto := portProto(&svcPort.Protocol)
1✔
2772
                        port := strconv.Itoa(int(svcPort.Port))
1✔
2773
                        cont.log.Debug("updateServiceAugmentForService: ", service)
1✔
2774
                        updateServiceAugmentForService(portAugments,
1✔
2775
                                proto, port, service)
1✔
2776
                        logger.WithFields(logrus.Fields{
1✔
2777
                                "proto":   proto,
1✔
2778
                                "port":    port,
1✔
2779
                                "service": servicekey,
1✔
2780
                        }).Debug("Allowing egress for service by subnet match")
1✔
2781
                }
1✔
2782
        }
2783
}
2784

2785
func isNamedPortPresenInNp(np *v1net.NetworkPolicy) bool {
1✔
2786
        for _, egress := range np.Spec.Egress {
2✔
2787
                for _, p := range egress.Ports {
2✔
2788
                        if p.Port.Type == intstr.String {
2✔
2789
                                return true
1✔
2790
                        }
1✔
2791
                }
2792
        }
2793
        return false
1✔
2794
}
2795

2796
func (cont *AciController) checkPodNmpMatchesNp(npkey, podkey string) bool {
1✔
2797
        podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
2798
        if err != nil {
1✔
2799
                return false
×
2800
        }
×
2801
        if !exists || podobj == nil {
1✔
2802
                return false
×
2803
        }
×
2804
        pod := podobj.(*v1.Pod)
1✔
2805
        npobj, npexists, nperr := cont.networkPolicyIndexer.GetByKey(npkey)
1✔
2806
        if npexists && nperr == nil && npobj != nil {
2✔
2807
                np := npobj.(*v1net.NetworkPolicy)
1✔
2808
                for _, egress := range np.Spec.Egress {
2✔
2809
                        for _, p := range egress.Ports {
2✔
2810
                                if p.Port.Type == intstr.String {
2✔
2811
                                        _, err := k8util.LookupContainerPortNumberByName(*pod, p.Port.String())
1✔
2812
                                        if err == nil {
2✔
2813
                                                return true
1✔
2814
                                        }
1✔
2815
                                }
2816
                        }
2817
                }
2818
        }
2819
        return false
1✔
2820
}
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