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

noironetworks / aci-containers / 11917

04 May 2026 06:57AM UTC coverage: 63.383% (+0.4%) from 63.028%
11917

Pull #1706

travis-pro

jeffinkottaram
Fix per-service SNAT IP assignment for no-SnatIp policies

Two bugs are fixed:

1. Namespace-level no-SnatIp SNAT policies with multiple LoadBalancer
   services incorrectly assigned all LB external IPs to all pods.
   The controller now tags each global info entry with the originating
   service key. The hostagent filters SNAT UIDs per-pod by checking
   service endpoint membership, ensuring each pod only egresses with
   the external IP of the service it belongs to.

2. After hostagent pod restarts, namespace-level no-SnatIp policies
   lost their service SNAT entries because pod events did not evaluate
   service membership for the namespace-scoped policy path. The
   namespace-scoped branch now checks for matching services when
   processing pod events, consistent with the label-scoped path.

Additionally, two  inconsistencies are corrected:

1. The deployment path for no-SnatIp policies incorrectly matched
deployment metadata labels against service pod selectors. Since
services select pods (not deployments), and pod template changes
produce new pod events handled separately, this check was both
inaccurate and redundant. It is removed.

2. Explicit-SnatIp policies matching services now consistently apply the
specified SNAT IP to the service's backend pods across all policy
evaluation paths
Pull Request #1706: Fix per-service SNAT IP assignment for no-SnatIp policies

57 of 87 new or added lines in 2 files covered. (65.52%)

1119 existing lines in 9 files now uncovered.

13628 of 21501 relevant lines covered (63.38%)

0.72 hits per line

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

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

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

18
package controller
19

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

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

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

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

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

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

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

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

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

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

130
        return ret
1✔
131
}
132

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

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

140
        return ret
1✔
141
}
142

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

353
        return true
1✔
354
}
355

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

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

373
        return true
1✔
374
}
375

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

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

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

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

405
        return true
1✔
406
}
407

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

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

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

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

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

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

450
        return true
1✔
451
}
452

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

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

466
        return true
1✔
467
}
468

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

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

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

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

498
        return true
1✔
499
}
500

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

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

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

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

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

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

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

568
        return true
1✔
569
}
570

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

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

592
        return rules
1✔
593
}
594

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

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

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

649
        return rules
1✔
650
}
651

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1✔
1387
                        if ports[j].Port != nil {
2✔
1388
                                if ports[j].Port.Type == intstr.Int {
2✔
1389
                                        pr := portRange{fromPort: ports[j].Port.String()}
1✔
1390
                                        if ports[j].EndPort != nil {
1✔
1391
                                                pr.toPort = strconv.Itoa(int(*ports[j].EndPort))
×
1392
                                        }
×
1393
                                        portRanges = append(portRanges, pr)
1✔
1394
                                } else {
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 checkEndpoints(subnetIndex cidranger.Ranger,
1486
        addresses []v1.EndpointAddress) bool {
1✔
1487
        for _, addr := range addresses {
2✔
1488
                ip := net.ParseIP(addr.IP)
1✔
1489
                if ip == nil {
1✔
1490
                        return false
×
1491
                }
×
1492
                contains, err := subnetIndex.Contains(ip)
1✔
1493
                if err != nil || !contains {
2✔
1494
                        return false
1✔
1495
                }
1✔
1496
        }
1497

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1937
        return false
1✔
1938
}
1939

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

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

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

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

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

1981
        return hostprotRemoteIpList
1✔
1982
}
1983

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

2012
        hpp.Spec.NetworkPolicies = newNetPols
1✔
2013

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

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

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

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

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

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

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

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

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

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

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

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

2096
        aobj.Spec.HostprotRemoteIp = newHostprotRemoteIps
1✔
2097

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2491
        return true
1✔
2492
}
2493

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2870
func (cont *AciController) checkPodNmpMatchesNp(npkey, podkey string) bool {
1✔
2871
        podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
2872
        if err != nil {
1✔
UNCOV
2873
                return false
×
UNCOV
2874
        }
×
2875
        if !exists || podobj == nil {
1✔
UNCOV
2876
                return false
×
UNCOV
2877
        }
×
2878
        pod := podobj.(*v1.Pod)
1✔
2879
        npobj, npexists, nperr := cont.networkPolicyIndexer.GetByKey(npkey)
1✔
2880
        if npexists && nperr == nil && npobj != nil {
2✔
2881
                np := npobj.(*v1net.NetworkPolicy)
1✔
2882
                for _, egress := range np.Spec.Egress {
2✔
2883
                        for _, p := range egress.Ports {
2✔
2884
                                if p.Port.Type == intstr.String {
2✔
2885
                                        _, err := k8util.LookupContainerPortNumberByName(*pod, p.Port.String())
1✔
2886
                                        if err == nil {
2✔
2887
                                                return true
1✔
2888
                                        }
1✔
2889
                                }
2890
                        }
2891
                }
2892
        }
2893
        return false
1✔
2894
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc