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

kubeovn / kube-ovn / 18554291225

16 Oct 2025 07:53AM UTC coverage: 21.145% (+0.06%) from 21.087%
18554291225

push

github

web-flow
feat(netpol): authorize l3 protocols (#5745)

* feat(netpol): authorize l3 protocols

Signed-off-by: SkalaNetworks <contact@skala.network>

* feat(netpol): add annotation for different policy enforcements

Signed-off-by: SkalaNetworks <contact@skala.network>

* feat(netpols): add enforcement configuration and deployment config

Signed-off-by: SkalaNetworks <contact@skala.network>

* chore(netpol): add unit tests

Signed-off-by: SkalaNetworks <contact@skala.network>

* feat(netpol): only block tcp/udp/sctp in lax enforcement

Signed-off-by: SkalaNetworks <contact@skala.network>

---------

Signed-off-by: SkalaNetworks <contact@skala.network>

57 of 94 new or added lines in 3 files covered. (60.64%)

2 existing lines in 1 file now uncovered.

10729 of 50740 relevant lines covered (21.15%)

0.25 hits per line

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

79.62
/pkg/ovs/ovn-nb-acl.go
1
package ovs
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "slices"
8
        "strconv"
9
        "strings"
10

11
        "github.com/ovn-kubernetes/libovsdb/model"
12
        "github.com/ovn-kubernetes/libovsdb/ovsdb"
13
        netv1 "k8s.io/api/networking/v1"
14
        "k8s.io/apimachinery/pkg/util/intstr"
15
        "k8s.io/klog/v2"
16
        "k8s.io/utils/ptr"
17
        "k8s.io/utils/set"
18

19
        v1alpha1 "sigs.k8s.io/network-policy-api/apis/v1alpha1"
20

21
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
22
        ovsclient "github.com/kubeovn/kube-ovn/pkg/ovsdb/client"
23
        "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb"
24
        "github.com/kubeovn/kube-ovn/pkg/util"
25
)
26

27
type ACLErrorType int
28

29
const (
30
        ACLErrorNotFound ACLErrorType = iota
31
        ACLErrorDuplicated
32
        ACLErrorDatabase
33
)
34

35
type ACLError struct {
36
        Type ACLErrorType
37
        Msg  string
38
}
39

40
func (e *ACLError) Error() string {
1✔
41
        return e.Msg
1✔
42
}
1✔
43

44
func NewACLError(errType ACLErrorType, msg string) *ACLError {
1✔
45
        return &ACLError{Type: errType, Msg: msg}
1✔
46
}
1✔
47

48
func setACLName(acl *ovnnb.ACL, name string) {
1✔
49
        if len(name) > 63 {
1✔
50
                // ACL name length limit is 63
×
51
                name = name[:60] + "..."
×
52
        }
×
53
        acl.Name = ptr.To(name)
1✔
54
}
55

56
// UpdateDefaultBlockACLOps returns operations to update/create the default block ACL
57
func (c *OVNNbClient) UpdateDefaultBlockACLOps(npName, pgName, direction string, loggingEnabled, lax bool) ([]ovsdb.Operation, error) {
1✔
58
        portDirection := "outport"
1✔
59
        priority := util.IngressDefaultDrop
1✔
60

1✔
61
        if direction == ovnnb.ACLDirectionFromLport {
2✔
62
                portDirection = "inport"
1✔
63
                priority = util.EgressDefaultDrop
1✔
64
        }
1✔
65

66
        var match ACLMatch
1✔
67

1✔
68
        if lax {
2✔
69
                // This is the "lax" enforcement mode, we block only TCP/UDP/SCTP
1✔
70
                match = NewAndACLMatch(
1✔
71
                        NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
72
                        NewACLMatch("(tcp || udp || sctp)", "", "", ""),
1✔
73
                )
1✔
74
        } else {
2✔
75
                // This is the "standard" enforcement mode, we block everything IP related (IPv4/IPv6/ICMPv4/ICMPv6/...)
1✔
76
                match = NewAndACLMatch(
1✔
77
                        NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
78
                        NewACLMatch("ip", "", "", ""),
1✔
79
                )
1✔
80
        }
1✔
81

82
        options := func(acl *ovnnb.ACL) {
2✔
83
                setACLName(acl, npName)
1✔
84
                if loggingEnabled {
2✔
85
                        acl.Log = true
1✔
86
                        acl.Severity = ptr.To(ovnnb.ACLSeverityWarning)
1✔
87
                }
1✔
88

89
                if direction == ovnnb.ACLDirectionFromLport {
2✔
90
                        if acl.Options == nil {
2✔
91
                                acl.Options = make(map[string]string)
1✔
92
                        }
1✔
93
                        acl.Options["apply-after-lb"] = "true"
1✔
94
                }
95
        }
96

97
        defaultDropACL, err := c.newACLWithoutCheck(pgName, direction, priority, match.String(), ovnnb.ACLActionDrop, util.NetpolACLTier, options)
1✔
98
        if err != nil {
1✔
99
                klog.Error(err)
×
100
                return nil, fmt.Errorf("failed to create drop acl for port group %s: %w", pgName, err)
×
101
        }
×
102

103
        ops, err := c.CreateAclsOps(pgName, portGroupKey, defaultDropACL)
1✔
104
        if err != nil {
1✔
105
                klog.Error(err)
×
106
                return nil, fmt.Errorf("failed to create default drop acl ops for port group %s: %w", pgName, err)
×
107
        }
×
108

109
        return ops, nil
1✔
110
}
111

112
// UpdateDefaultBlockExceptionsACLOps updates the exceptions to the default block ACLs of a NetworkPolicy to allow DHCPv4/DHCPv6.
113
func (c *OVNNbClient) UpdateDefaultBlockExceptionsACLOps(npName, pgName, npNamespace, direction string) ([]ovsdb.Operation, error) {
1✔
114
        portDirection := "outport"
1✔
115
        dhcpv4UdpSrc, dhcpv4UdpDst := "67", "68"
1✔
116
        dhcpv6UdpSrc, dhcpv6UdpDst := "547", "546"
1✔
117

1✔
118
        if direction == ovnnb.ACLDirectionFromLport { // Egress rule
2✔
119
                portDirection = "inport"
1✔
120
                dhcpv4UdpSrc, dhcpv4UdpDst = dhcpv4UdpDst, dhcpv4UdpSrc
1✔
121
                dhcpv6UdpSrc, dhcpv6UdpDst = dhcpv6UdpDst, dhcpv6UdpSrc
1✔
122
        }
1✔
123

124
        acls := make([]*ovnnb.ACL, 0)
1✔
125

1✔
126
        newACL := func(match string) {
2✔
127
                options := func(acl *ovnnb.ACL) {
2✔
128
                        setACLName(acl, npName)
1✔
129
                }
1✔
130

131
                acl, err := c.newACL(pgName, direction, util.IngressAllowPriority, match, ovnnb.ACLActionAllowRelated, util.NetpolACLTier, options)
1✔
132
                if err != nil {
1✔
NEW
133
                        klog.Error(err)
×
NEW
134
                        klog.Errorf("failed to create new block exceptions acl for network policy %s/%s: %v", npNamespace, npName, err)
×
NEW
135
                        return
×
NEW
136
                }
×
137
                acls = append(acls, acl)
1✔
138
        }
139

140
        // Allow DHCPv6
141
        dhcpv6Match := NewAndACLMatch(
1✔
142
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
143
                NewACLMatch("udp.src", "==", dhcpv6UdpSrc, ""),
1✔
144
                NewACLMatch("udp.dst", "==", dhcpv6UdpDst, ""),
1✔
145
                NewACLMatch("ip6", "", "", ""),
1✔
146
        )
1✔
147
        newACL(dhcpv6Match.String())
1✔
148

1✔
149
        // Allow DHCPv4
1✔
150
        dhcpv4Match := NewAndACLMatch(
1✔
151
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
152
                NewACLMatch("udp.src", "==", dhcpv4UdpSrc, ""),
1✔
153
                NewACLMatch("udp.dst", "==", dhcpv4UdpDst, ""),
1✔
154
                NewACLMatch("ip4", "", "", ""),
1✔
155
        )
1✔
156
        newACL(dhcpv4Match.String())
1✔
157

1✔
158
        ops, err := c.CreateAclsOps(pgName, portGroupKey, acls...)
1✔
159
        if err != nil {
1✔
NEW
160
                klog.Error(err)
×
NEW
161
                return nil, fmt.Errorf("failed to create block exceptions acl for port group %s: %w", pgName, err)
×
NEW
162
        }
×
163
        return ops, nil
1✔
164
}
165

166
// UpdateIngressACLOps return operation that creates an ingress ACL
167
func (c *OVNNbClient) UpdateIngressACLOps(pgName, asIngressName, asExceptName, protocol, aclName string, npp []netv1.NetworkPolicyPort, logEnable bool, logACLActions []ovnnb.ACLAction, namedPortMap map[string]*util.NamedPortInfo) ([]ovsdb.Operation, error) {
1✔
168
        acls := make([]*ovnnb.ACL, 0)
1✔
169

1✔
170
        /* allow acl */
1✔
171
        matches := newNetworkPolicyACLMatch(pgName, asIngressName, asExceptName, protocol, ovnnb.ACLDirectionToLport, npp, namedPortMap)
1✔
172
        for _, m := range matches {
2✔
173
                options := func(acl *ovnnb.ACL) {
2✔
174
                        setACLName(acl, aclName)
1✔
175
                        if logEnable && slices.Contains(logACLActions, ovnnb.ACLActionAllow) {
1✔
176
                                acl.Log = true
×
177
                        }
×
178
                }
179

180
                allowACL, err := c.newACLWithoutCheck(pgName, ovnnb.ACLDirectionToLport, util.IngressAllowPriority, m, ovnnb.ACLActionAllowRelated, util.NetpolACLTier, options)
1✔
181
                if err != nil {
2✔
182
                        klog.Error(err)
1✔
183
                        return nil, fmt.Errorf("new allow ingress acl for port group %s: %w", pgName, err)
1✔
184
                }
1✔
185

186
                acls = append(acls, allowACL)
1✔
187
        }
188

189
        ops, err := c.CreateAclsOps(pgName, portGroupKey, acls...)
1✔
190
        if err != nil {
1✔
191
                klog.Error(err)
×
192
                return nil, fmt.Errorf("failed to create ingress acl for port group %s: %w", pgName, err)
×
193
        }
×
194

195
        return ops, nil
1✔
196
}
197

198
// UpdateEgressACLOps return operation that creates an egress ACL
199
func (c *OVNNbClient) UpdateEgressACLOps(pgName, asEgressName, asExceptName, protocol, aclName string, npp []netv1.NetworkPolicyPort, logEnable bool, logACLActions []ovnnb.ACLAction, namedPortMap map[string]*util.NamedPortInfo) ([]ovsdb.Operation, error) {
1✔
200
        acls := make([]*ovnnb.ACL, 0)
1✔
201

1✔
202
        /* allow acl */
1✔
203
        matches := newNetworkPolicyACLMatch(pgName, asEgressName, asExceptName, protocol, ovnnb.ACLDirectionFromLport, npp, namedPortMap)
1✔
204
        for _, m := range matches {
2✔
205
                allowACL, err := c.newACLWithoutCheck(pgName, ovnnb.ACLDirectionFromLport, util.EgressAllowPriority, m, ovnnb.ACLActionAllowRelated, util.NetpolACLTier, func(acl *ovnnb.ACL) {
2✔
206
                        setACLName(acl, aclName)
1✔
207
                        if acl.Options == nil {
2✔
208
                                acl.Options = make(map[string]string)
1✔
209
                        }
1✔
210
                        acl.Options["apply-after-lb"] = "true"
1✔
211
                        if logEnable && slices.Contains(logACLActions, ovnnb.ACLActionAllow) {
1✔
212
                                acl.Log = true
×
213
                        }
×
214
                })
215
                if err != nil {
2✔
216
                        klog.Error(err)
1✔
217
                        return nil, fmt.Errorf("new allow egress acl for port group %s: %w", pgName, err)
1✔
218
                }
1✔
219

220
                acls = append(acls, allowACL)
1✔
221
        }
222

223
        ops, err := c.CreateAclsOps(pgName, portGroupKey, acls...)
1✔
224
        if err != nil {
1✔
225
                klog.Error(err)
×
226
                return nil, err
×
227
        }
×
228

229
        return ops, nil
1✔
230
}
231

232
// CreateGatewayACL create allow acl for subnet gateway
233
func (c *OVNNbClient) CreateGatewayACL(lsName, pgName, gateway, u2oInterconnectionIP string) error {
1✔
234
        acls := make([]*ovnnb.ACL, 0)
1✔
235

1✔
236
        var parentName, parentType string
1✔
237
        switch {
1✔
238
        case len(pgName) != 0:
1✔
239
                parentName, parentType = pgName, portGroupKey
1✔
240
        case len(lsName) != 0:
1✔
241
                parentName, parentType = lsName, LogicalSwitchKey
1✔
242
        default:
1✔
243
                return errors.New("one of port group name and logical switch name must be specified")
1✔
244
        }
245

246
        gateways := set.New(strings.Split(gateway, ",")...)
1✔
247
        if u2oInterconnectionIP != "" {
2✔
248
                gateways = gateways.Insert(strings.Split(u2oInterconnectionIP, ",")...)
1✔
249
        }
1✔
250

251
        options := func(acl *ovnnb.ACL) {
2✔
252
                if acl.Options == nil {
2✔
253
                        acl.Options = make(map[string]string)
1✔
254
                }
1✔
255
                acl.Options["apply-after-lb"] = "true"
1✔
256
        }
257
        v6Exists := false
1✔
258
        for gw := range gateways {
2✔
259
                protocol := util.CheckProtocol(gw)
1✔
260
                ipSuffix := "ip4"
1✔
261
                if protocol == kubeovnv1.ProtocolIPv6 {
2✔
262
                        ipSuffix = "ip6"
1✔
263
                        v6Exists = true
1✔
264
                }
1✔
265

266
                allowIngressACL, err := c.newACL(parentName, ovnnb.ACLDirectionToLport, util.IngressAllowPriority, fmt.Sprintf("%s.src == %s", ipSuffix, gw), ovnnb.ACLActionAllowStateless, util.NetpolACLTier)
1✔
267
                if err != nil {
1✔
268
                        klog.Error(err)
×
269
                        return fmt.Errorf("new allow ingress acl for %s: %w", parentName, err)
×
270
                }
×
271

272
                allowEgressACL, err := c.newACL(parentName, ovnnb.ACLDirectionFromLport, util.EgressAllowPriority, fmt.Sprintf("%s.dst == %s", ipSuffix, gw), ovnnb.ACLActionAllowStateless, util.NetpolACLTier, options)
1✔
273
                if err != nil {
1✔
274
                        klog.Error(err)
×
275
                        return fmt.Errorf("new allow egress acl for %s: %w", parentName, err)
×
276
                }
×
277

278
                acls = append(acls, allowIngressACL, allowEgressACL)
1✔
279
        }
280

281
        if v6Exists {
2✔
282
                ndACL, err := c.newACL(parentName, ovnnb.ACLDirectionFromLport, util.EgressAllowPriority, "nd || nd_ra || nd_rs", ovnnb.ACLActionAllowStateless, util.NetpolACLTier, options)
1✔
283
                if err != nil {
1✔
284
                        klog.Error(err)
×
285
                        return fmt.Errorf("new nd acl for %s: %w", parentName, err)
×
286
                }
×
287

288
                acls = append(acls, ndACL)
1✔
289
        }
290

291
        if err := c.CreateAcls(parentName, parentType, acls...); err != nil {
1✔
292
                klog.Error(err)
×
293
                return fmt.Errorf("add gateway acls to %s: %w", pgName, err)
×
294
        }
×
295

296
        return nil
1✔
297
}
298

299
// CreateNodeACL create allow acl for node join ip
300
func (c *OVNNbClient) CreateNodeACL(pgName, nodeIPStr, joinIPStr string) error {
1✔
301
        acls := make([]*ovnnb.ACL, 0)
1✔
302
        nodeIPs := strings.Split(nodeIPStr, ",")
1✔
303
        for _, nodeIP := range nodeIPs {
2✔
304
                protocol := util.CheckProtocol(nodeIP)
1✔
305
                ipSuffix := "ip4"
1✔
306
                if protocol == kubeovnv1.ProtocolIPv6 {
2✔
307
                        ipSuffix = "ip6"
1✔
308
                }
1✔
309
                pgAs := fmt.Sprintf("%s_%s", pgName, ipSuffix)
1✔
310

1✔
311
                allowIngressACL, err := c.newACL(pgName, ovnnb.ACLDirectionToLport, util.NodeAllowPriority, fmt.Sprintf("%s.src == %s && %s.dst == $%s", ipSuffix, nodeIP, ipSuffix, pgAs), ovnnb.ACLActionAllowRelated, util.NetpolACLTier)
1✔
312
                if err != nil {
2✔
313
                        klog.Error(err)
1✔
314
                        return fmt.Errorf("new allow ingress acl for port group %s: %w", pgName, err)
1✔
315
                }
1✔
316

317
                options := func(acl *ovnnb.ACL) {
2✔
318
                        if acl.Options == nil {
2✔
319
                                acl.Options = make(map[string]string)
1✔
320
                        }
1✔
321
                        acl.Options["apply-after-lb"] = "true"
1✔
322
                }
323

324
                allowEgressACL, err := c.newACL(pgName, ovnnb.ACLDirectionFromLport, util.NodeAllowPriority, fmt.Sprintf("%s.dst == %s && %s.src == $%s", ipSuffix, nodeIP, ipSuffix, pgAs), ovnnb.ACLActionAllowRelated, util.NetpolACLTier, options)
1✔
325
                if err != nil {
1✔
326
                        klog.Error(err)
×
327
                        return fmt.Errorf("new allow egress acl for port group %s: %w", pgName, err)
×
328
                }
×
329

330
                acls = append(acls, allowIngressACL, allowEgressACL)
1✔
331
        }
332

333
        for joinIP := range strings.SplitSeq(joinIPStr, ",") {
2✔
334
                if slices.Contains(nodeIPs, joinIP) {
2✔
335
                        continue
1✔
336
                }
337

338
                protocol := util.CheckProtocol(joinIP)
1✔
339
                ipSuffix := "ip4"
1✔
340
                if protocol == kubeovnv1.ProtocolIPv6 {
2✔
341
                        ipSuffix = "ip6"
1✔
342
                }
1✔
343

344
                pgAs := fmt.Sprintf("%s_%s", pgName, ipSuffix)
1✔
345

1✔
346
                if err := c.DeleteACL(pgName, portGroupKey, ovnnb.ACLDirectionToLport, util.NodeAllowPriority, fmt.Sprintf("%s.src == %s && %s.dst == $%s", ipSuffix, joinIP, ipSuffix, pgAs)); err != nil {
1✔
347
                        klog.Errorf("delete ingress acl from port group %s: %v", pgName, err)
×
348
                        return err
×
349
                }
×
350

351
                if err := c.DeleteACL(pgName, portGroupKey, ovnnb.ACLDirectionFromLport, util.NodeAllowPriority, fmt.Sprintf("%s.dst == %s && %s.src == $%s", ipSuffix, joinIP, ipSuffix, pgAs)); err != nil {
1✔
352
                        klog.Errorf("delete egress acl from port group %s: %v", pgName, err)
×
353
                        return err
×
354
                }
×
355
        }
356

357
        if err := c.CreateAcls(pgName, portGroupKey, acls...); err != nil {
1✔
358
                return fmt.Errorf("add node acls to port group %s: %w", pgName, err)
×
359
        }
×
360

361
        return nil
1✔
362
}
363

364
func (c *OVNNbClient) CreateSgDenyAllACL(sgName string) error {
1✔
365
        pgName := GetSgPortGroupName(sgName)
1✔
366

1✔
367
        ingressACL, err := c.newACL(pgName, ovnnb.ACLDirectionToLport, util.SecurityGroupDropPriority, fmt.Sprintf("outport == @%s && ip", pgName), ovnnb.ACLActionDrop, util.NetpolACLTier)
1✔
368
        if err != nil {
2✔
369
                klog.Error(err)
1✔
370
                return fmt.Errorf("new deny all ingress acl for security group %s: %w", sgName, err)
1✔
371
        }
1✔
372

373
        egressACL, err := c.newACL(pgName, ovnnb.ACLDirectionFromLport, util.SecurityGroupDropPriority, fmt.Sprintf("inport == @%s && ip", pgName), ovnnb.ACLActionDrop, util.NetpolACLTier)
1✔
374
        if err != nil {
1✔
375
                klog.Error(err)
×
376
                return fmt.Errorf("new deny all egress acl for security group %s: %w", sgName, err)
×
377
        }
×
378

379
        err = c.CreateAcls(pgName, portGroupKey, ingressACL, egressACL)
1✔
380
        if err != nil {
2✔
381
                klog.Error(err)
1✔
382
                return fmt.Errorf("add deny all acl to port group %s: %w", pgName, err)
1✔
383
        }
1✔
384

385
        return nil
1✔
386
}
387

388
// CreateSgACL create allow acl for security group
389
func (c *OVNNbClient) CreateSgBaseACL(sgName, direction string) error {
1✔
390
        pgName := GetSgPortGroupName(sgName)
1✔
391

1✔
392
        // ingress rule
1✔
393
        portDirection := "outport"
1✔
394
        dhcpv4UdpSrc, dhcpv4UdpDst := "67", "68"
1✔
395
        dhcpv6UdpSrc, dhcpv6UdpDst := "547", "546"
1✔
396
        icmpv6Type := "{130, 134, 135, 136}"
1✔
397
        // 130 group membership query
1✔
398
        // 133 router solicitation
1✔
399
        // 134 router advertisement
1✔
400
        // 135 neighbor solicitation
1✔
401
        // 136 neighbor advertisement
1✔
402

1✔
403
        if direction == ovnnb.ACLDirectionFromLport { // egress rule
2✔
404
                portDirection = "inport"
1✔
405
                dhcpv4UdpSrc, dhcpv4UdpDst = dhcpv4UdpDst, dhcpv4UdpSrc
1✔
406
                dhcpv6UdpSrc, dhcpv6UdpDst = dhcpv6UdpDst, dhcpv6UdpSrc
1✔
407
                icmpv6Type = "{130, 133, 135, 136}"
1✔
408
        }
1✔
409

410
        acls := make([]*ovnnb.ACL, 0)
1✔
411

1✔
412
        newACL := func(match string) {
2✔
413
                acl, err := c.newACL(pgName, direction, util.SecurityGroupBasePriority, match, ovnnb.ACLActionAllowRelated, util.NetpolACLTier)
1✔
414
                if err != nil {
2✔
415
                        klog.Error(err)
1✔
416
                        klog.Errorf("failed to create new base ingress acl for security group %s: %v", sgName, err)
1✔
417
                        return
1✔
418
                }
1✔
419
                acls = append(acls, acl)
1✔
420
        }
421

422
        // allow arp
423
        allArpMatch := NewAndACLMatch(
1✔
424
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
425
                NewACLMatch("arp", "", "", ""),
1✔
426
        )
1✔
427
        newACL(allArpMatch.String())
1✔
428

1✔
429
        // icmpv6
1✔
430
        icmpv6Match := NewAndACLMatch(
1✔
431
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
432
                NewACLMatch("icmp6.type", "==", icmpv6Type, ""),
1✔
433
                NewACLMatch("icmp6.code", "==", "0", ""),
1✔
434
                NewACLMatch("ip.ttl", "==", "255", ""),
1✔
435
        )
1✔
436
        newACL(icmpv6Match.String())
1✔
437

1✔
438
        // dhcpv4 offer
1✔
439
        dhcpv4Match := NewAndACLMatch(
1✔
440
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
441
                NewACLMatch("udp.src", "==", dhcpv4UdpSrc, ""),
1✔
442
                NewACLMatch("udp.dst", "==", dhcpv4UdpDst, ""),
1✔
443
                NewACLMatch("ip4", "", "", ""),
1✔
444
        )
1✔
445
        newACL(dhcpv4Match.String())
1✔
446

1✔
447
        // dhcpv6 offer
1✔
448
        dhcpv6Match := NewAndACLMatch(
1✔
449
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
450
                NewACLMatch("udp.src", "==", dhcpv6UdpSrc, ""),
1✔
451
                NewACLMatch("udp.dst", "==", dhcpv6UdpDst, ""),
1✔
452
                NewACLMatch("ip6", "", "", ""),
1✔
453
        )
1✔
454
        newACL(dhcpv6Match.String())
1✔
455

1✔
456
        // vrrp
1✔
457
        vrrpMatch := NewAndACLMatch(
1✔
458
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
459
                NewACLMatch("ip.proto", "==", "112", ""),
1✔
460
        )
1✔
461
        newACL(vrrpMatch.String())
1✔
462

1✔
463
        if err := c.CreateAcls(pgName, portGroupKey, acls...); err != nil {
1✔
464
                klog.Error(err)
×
465
                return fmt.Errorf("add ingress acls to port group %s: %w", pgName, err)
×
466
        }
×
467
        return nil
1✔
468
}
469

470
func (c *OVNNbClient) UpdateSgACL(sg *kubeovnv1.SecurityGroup, direction string) error {
1✔
471
        pgName := GetSgPortGroupName(sg.Name)
1✔
472

1✔
473
        // clear acl
1✔
474
        if err := c.DeleteAcls(pgName, portGroupKey, direction, nil); err != nil {
2✔
475
                klog.Error(err)
1✔
476
                return fmt.Errorf("delete direction '%s' acls from port group %s: %w", direction, pgName, err)
1✔
477
        }
1✔
478

479
        acls := make([]*ovnnb.ACL, 0, 2)
1✔
480

1✔
481
        // ingress rule
1✔
482
        srcOrDst, portDirection, sgRules := "src", "outport", sg.Spec.IngressRules
1✔
483
        if direction == ovnnb.ACLDirectionFromLport { // egress rule
2✔
484
                srcOrDst = "dst"
1✔
485
                portDirection = "inport"
1✔
486
                sgRules = sg.Spec.EgressRules
1✔
487
        }
1✔
488

489
        /* create port_group associated acl */
490
        if sg.Spec.AllowSameGroupTraffic {
2✔
491
                asName := GetSgV4AssociatedName(sg.Name)
1✔
492
                for _, ipSuffix := range []string{"ip4", "ip6"} {
2✔
493
                        if ipSuffix == "ip6" {
2✔
494
                                asName = GetSgV6AssociatedName(sg.Name)
1✔
495
                        }
1✔
496

497
                        match := NewAndACLMatch(
1✔
498
                                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
499
                                NewACLMatch(ipSuffix, "", "", ""),
1✔
500
                                NewACLMatch(ipSuffix+"."+srcOrDst, "==", "$"+asName, ""),
1✔
501
                        )
1✔
502
                        acl, err := c.newACL(pgName, direction, util.SecurityGroupAllowPriority, match.String(), ovnnb.ACLActionAllowRelated, util.NetpolACLTier)
1✔
503
                        if err != nil {
1✔
504
                                klog.Error(err)
×
505
                                return fmt.Errorf("new allow acl for security group %s: %w", sg.Name, err)
×
506
                        }
×
507

508
                        acls = append(acls, acl)
1✔
509
                }
510
        }
511

512
        /* create rule acl */
513
        for _, rule := range sgRules {
2✔
514
                acl, err := c.newSgRuleACL(sg.Name, direction, rule)
1✔
515
                if err != nil {
1✔
516
                        klog.Error(err)
×
517
                        return fmt.Errorf("new rule acl for security group %s: %w", sg.Name, err)
×
518
                }
×
519
                acls = append(acls, acl)
1✔
520
        }
521

522
        if err := c.CreateAcls(pgName, portGroupKey, acls...); err != nil {
1✔
523
                klog.Error(err)
×
524
                return fmt.Errorf("add acl to port group %s: %w", pgName, err)
×
525
        }
×
526

527
        return nil
1✔
528
}
529

530
func (c *OVNNbClient) UpdateLogicalSwitchACL(lsName, cidrBlock string, subnetAcls []kubeovnv1.ACL, allowEWTraffic bool) error {
1✔
531
        if len(subnetAcls) == 0 {
2✔
532
                if err := c.DeleteAcls(lsName, LogicalSwitchKey, "", map[string]string{"subnet": lsName}); err != nil {
1✔
533
                        klog.Error(err)
×
534
                        return fmt.Errorf("delete subnet acls from %s: %w", lsName, err)
×
535
                }
×
536
                return nil
1✔
537
        }
538

539
        acls := make([]*ovnnb.ACL, 0)
1✔
540

1✔
541
        options := func(acl *ovnnb.ACL) {
2✔
542
                if acl.ExternalIDs == nil {
1✔
543
                        acl.ExternalIDs = make(map[string]string)
×
544
                }
×
545
                acl.ExternalIDs["subnet"] = lsName
1✔
546
        }
547

548
        if allowEWTraffic {
2✔
549
                for cidr := range strings.SplitSeq(cidrBlock, ",") {
2✔
550
                        protocol := util.CheckProtocol(cidr)
1✔
551

1✔
552
                        ipSuffix := "ip4"
1✔
553
                        if protocol == kubeovnv1.ProtocolIPv6 {
2✔
554
                                ipSuffix = "ip6"
1✔
555
                        }
1✔
556

557
                        /* same subnet acl */
558
                        sameSubnetMatch := NewAndACLMatch(
1✔
559
                                NewACLMatch(ipSuffix+".src", "==", cidr, ""),
1✔
560
                                NewACLMatch(ipSuffix+".dst", "==", cidr, ""),
1✔
561
                        )
1✔
562

1✔
563
                        ingressSameSubnetACL, err := c.newACL(lsName, ovnnb.ACLDirectionToLport, util.AllowEWTrafficPriority, sameSubnetMatch.String(), ovnnb.ACLActionAllow, util.NetpolACLTier, options)
1✔
564
                        if err != nil {
2✔
565
                                klog.Error(err)
1✔
566
                                return fmt.Errorf("new same subnet ingress acl for logical switch %s: %w", lsName, err)
1✔
567
                        }
1✔
568
                        acls = append(acls, ingressSameSubnetACL)
1✔
569

1✔
570
                        egressSameSubnetACL, err := c.newACL(lsName, ovnnb.ACLDirectionFromLport, util.AllowEWTrafficPriority, sameSubnetMatch.String(), ovnnb.ACLActionAllow, util.NetpolACLTier, options)
1✔
571
                        if err != nil {
1✔
572
                                klog.Error(err)
×
573
                                return fmt.Errorf("new same subnet egress acl for logical switch %s: %w", lsName, err)
×
574
                        }
×
575
                        acls = append(acls, egressSameSubnetACL)
1✔
576
                }
577
        }
578

579
        /* recreate logical switch acl */
580
        for _, subnetACL := range subnetAcls {
2✔
581
                acl, err := c.newACL(lsName, subnetACL.Direction, strconv.Itoa(subnetACL.Priority), subnetACL.Match, subnetACL.Action, util.NetpolACLTier, options)
1✔
582
                if err != nil {
2✔
583
                        klog.Error(err)
1✔
584
                        return fmt.Errorf("new acl for logical switch %s: %w", lsName, err)
1✔
585
                }
1✔
586
                acls = append(acls, acl)
1✔
587
        }
588

589
        delOps, err := c.DeleteAclsOps(lsName, LogicalSwitchKey, "", map[string]string{"subnet": lsName})
1✔
590
        if err != nil {
1✔
591
                klog.Error(err)
×
592
                return err
×
593
        }
×
594

595
        addOps, err := c.CreateAclsOps(lsName, LogicalSwitchKey, acls...)
1✔
596
        if err != nil {
1✔
597
                klog.Error(err)
×
598
                return err
×
599
        }
×
600

601
        if err := c.Transact("acls-update", append(delOps, addOps...)); err != nil {
1✔
602
                klog.Error(err)
×
603
                return fmt.Errorf("update acls for logical switch %s: %w", lsName, err)
×
604
        }
×
605

606
        return nil
1✔
607
}
608

609
// UpdateACL update acl
610
func (c *OVNNbClient) UpdateACL(acl *ovnnb.ACL, fields ...any) error {
1✔
611
        if acl == nil {
2✔
612
                return errors.New("address_set is nil")
1✔
613
        }
1✔
614

615
        op, err := c.Where(acl).Update(acl, fields...)
1✔
616
        if err != nil {
1✔
617
                klog.Error(err)
×
618
                return fmt.Errorf("generate operations for updating acl with 'direction %s priority %d match %s': %w", acl.Direction, acl.Priority, acl.Match, err)
×
619
        }
×
620

621
        if err = c.Transact("acl-update", op); err != nil {
1✔
622
                klog.Error(err)
×
623
                return fmt.Errorf("update acl with 'direction %s priority %d match %s': %w", acl.Direction, acl.Priority, acl.Match, err)
×
624
        }
×
625

626
        return nil
1✔
627
}
628

629
// SetLogicalSwitchPrivate will drop all ingress traffic except allow subnets, same subnet and node subnet
630
func (c *OVNNbClient) SetLogicalSwitchPrivate(lsName, cidrBlock, nodeSwitchCIDR string, allowSubnets []string) error {
1✔
631
        // clear acls
1✔
632
        if err := c.DeleteAcls(lsName, LogicalSwitchKey, "", nil); err != nil {
2✔
633
                klog.Error(err)
1✔
634
                return fmt.Errorf("clear logical switch %s acls: %w", lsName, err)
1✔
635
        }
1✔
636

637
        acls := make([]*ovnnb.ACL, 0)
1✔
638

1✔
639
        /* default drop acl */
1✔
640
        allIPMatch := NewACLMatch("ip", "", "", "")
1✔
641

1✔
642
        options := func(acl *ovnnb.ACL) {
2✔
643
                setACLName(acl, lsName)
1✔
644
                acl.Log = true
1✔
645
                acl.Severity = ptr.To(ovnnb.ACLSeverityWarning)
1✔
646
        }
1✔
647

648
        defaultDropACL, err := c.newACL(lsName, ovnnb.ACLDirectionToLport, util.DefaultDropPriority, allIPMatch.String(), ovnnb.ACLActionDrop, util.NetpolACLTier, options)
1✔
649
        if err != nil {
1✔
650
                klog.Error(err)
×
651
                return fmt.Errorf("new default drop ingress acl for logical switch %s: %w", lsName, err)
×
652
        }
×
653

654
        acls = append(acls, defaultDropACL)
1✔
655

1✔
656
        nodeSubnetACLFunc := func(protocol, ipSuffix string) error {
2✔
657
                for nodeCidr := range strings.SplitSeq(nodeSwitchCIDR, ",") {
2✔
658
                        // skip different address family
1✔
659
                        if protocol != util.CheckProtocol(nodeCidr) {
2✔
660
                                continue
1✔
661
                        }
662

663
                        match := NewACLMatch(ipSuffix+".src", "==", nodeCidr, "")
1✔
664

1✔
665
                        acl, err := c.newACL(lsName, ovnnb.ACLDirectionToLport, util.NodeAllowPriority, match.String(), ovnnb.ACLActionAllowRelated, util.NetpolACLTier)
1✔
666
                        if err != nil {
1✔
667
                                klog.Error(err)
×
668
                                return fmt.Errorf("new node subnet ingress acl for logical switch %s: %w", lsName, err)
×
669
                        }
×
670

671
                        acls = append(acls, acl)
1✔
672
                }
673

674
                return nil
1✔
675
        }
676

677
        allowSubnetACLFunc := func(protocol, ipSuffix, cidr string) error {
2✔
678
                for _, allowSubnet := range allowSubnets {
2✔
679
                        subnet := strings.TrimSpace(allowSubnet)
1✔
680
                        // skip empty subnet
1✔
681
                        if len(subnet) == 0 {
1✔
682
                                continue
×
683
                        }
684

685
                        // skip different address family
686
                        if util.CheckProtocol(subnet) != protocol {
2✔
687
                                continue
1✔
688
                        }
689

690
                        match := NewOrACLMatch(
1✔
691
                                NewAndACLMatch(
1✔
692
                                        NewACLMatch(ipSuffix+".src", "==", cidr, ""),
1✔
693
                                        NewACLMatch(ipSuffix+".dst", "==", subnet, ""),
1✔
694
                                ),
1✔
695
                                NewAndACLMatch(
1✔
696
                                        NewACLMatch(ipSuffix+".src", "==", subnet, ""),
1✔
697
                                        NewACLMatch(ipSuffix+".dst", "==", cidr, ""),
1✔
698
                                ),
1✔
699
                        )
1✔
700

1✔
701
                        acl, err := c.newACL(lsName, ovnnb.ACLDirectionToLport, util.SubnetAllowPriority, match.String(), ovnnb.ACLActionAllowRelated, util.NetpolACLTier)
1✔
702
                        if err != nil {
1✔
703
                                klog.Error(err)
×
704
                                return fmt.Errorf("new allow subnet ingress acl for logical switch %s: %w", lsName, err)
×
705
                        }
×
706

707
                        acls = append(acls, acl)
1✔
708
                }
709
                return nil
1✔
710
        }
711

712
        for cidr := range strings.SplitSeq(cidrBlock, ",") {
2✔
713
                protocol := util.CheckProtocol(cidr)
1✔
714

1✔
715
                ipSuffix := "ip4"
1✔
716
                if protocol == kubeovnv1.ProtocolIPv6 {
2✔
717
                        ipSuffix = "ip6"
1✔
718
                }
1✔
719

720
                /* same subnet acl */
721
                sameSubnetMatch := NewAndACLMatch(
1✔
722
                        NewACLMatch(ipSuffix+".src", "==", cidr, ""),
1✔
723
                        NewACLMatch(ipSuffix+".dst", "==", cidr, ""),
1✔
724
                )
1✔
725

1✔
726
                sameSubnetACL, err := c.newACL(lsName, ovnnb.ACLDirectionToLport, util.SubnetAllowPriority, sameSubnetMatch.String(), ovnnb.ACLActionAllowRelated, util.NetpolACLTier)
1✔
727
                if err != nil {
1✔
728
                        klog.Error(err)
×
729
                        return fmt.Errorf("new same subnet ingress acl for logical switch %s: %w", lsName, err)
×
730
                }
×
731

732
                acls = append(acls, sameSubnetACL)
1✔
733

1✔
734
                // node subnet acl
1✔
735
                if err := nodeSubnetACLFunc(protocol, ipSuffix); err != nil {
1✔
736
                        klog.Error(err)
×
737
                        return err
×
738
                }
×
739

740
                // allow subnet acl
741
                if err := allowSubnetACLFunc(protocol, ipSuffix, cidr); err != nil {
1✔
742
                        klog.Error(err)
×
743
                        return err
×
744
                }
×
745
        }
746

747
        if err := c.CreateAcls(lsName, LogicalSwitchKey, acls...); err != nil {
1✔
748
                klog.Error(err)
×
749
                return fmt.Errorf("add ingress acls to logical switch %s: %w", lsName, err)
×
750
        }
×
751

752
        return nil
1✔
753
}
754

755
func (c *OVNNbClient) SetACLLog(pgName string, logEnable, isIngress bool) error {
1✔
756
        direction := ovnnb.ACLDirectionToLport
1✔
757
        portDirection := "outport"
1✔
758
        if !isIngress {
2✔
759
                direction = ovnnb.ACLDirectionFromLport
1✔
760
                portDirection = "inport"
1✔
761
        }
1✔
762

763
        // match all traffic to or from pgName
764
        allIPMatch := NewAndACLMatch(
1✔
765
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
766
                NewACLMatch("ip", "", "", ""),
1✔
767
        )
1✔
768

1✔
769
        acl, err := c.GetACL(pgName, direction, util.IngressDefaultDrop, allIPMatch.String(), true)
1✔
770
        if err != nil {
2✔
771
                klog.Error(err)
1✔
772
                return err
1✔
773
        }
1✔
774

775
        if acl == nil {
2✔
776
                return nil // skip if acl not found
1✔
777
        }
1✔
778

779
        if acl.Log == logEnable {
2✔
780
                return nil
1✔
781
        }
1✔
782
        acl.Log = logEnable
1✔
783

1✔
784
        err = c.UpdateACL(acl, &acl.Log)
1✔
785
        if err != nil {
1✔
786
                klog.Error(err)
×
787
                return fmt.Errorf("update acl: %w", err)
×
788
        }
×
789

790
        return nil
1✔
791
}
792

793
// CreateAcls create several acl once
794
// parentType is 'ls' or 'pg'
795
func (c *OVNNbClient) CreateAcls(parentName, parentType string, acls ...*ovnnb.ACL) error {
1✔
796
        ops, err := c.CreateAclsOps(parentName, parentType, acls...)
1✔
797
        if err != nil {
2✔
798
                klog.Error(err)
1✔
799
                return err
1✔
800
        }
1✔
801

802
        if err = c.Transact("acls-add", ops); err != nil {
1✔
803
                return fmt.Errorf("add acls to type %s %s: %w", parentType, parentName, err)
×
804
        }
×
805

806
        return nil
1✔
807
}
808

809
func (c *OVNNbClient) CreateBareACL(parentName, direction, priority, match, action string) error {
1✔
810
        acl, err := c.newACL(parentName, direction, priority, match, action, util.NetpolACLTier)
1✔
811
        if err != nil {
2✔
812
                klog.Error(err)
1✔
813
                return fmt.Errorf("new acl direction %s priority %s match %s action %s: %w", direction, priority, match, action, err)
1✔
814
        }
1✔
815

816
        op, err := c.Create(acl)
1✔
817
        if err != nil {
1✔
818
                klog.Error(err)
×
819
                return fmt.Errorf("generate operations for creating acl direction %s priority %s match %s action %s: %w", direction, priority, match, action, err)
×
820
        }
×
821

822
        if err = c.Transact("acl-create", op); err != nil {
2✔
823
                klog.Error(err)
1✔
824
                return fmt.Errorf("create acl direction %s priority %s match %s action %s: %w", direction, priority, match, action, err)
1✔
825
        }
1✔
826

827
        return nil
1✔
828
}
829

830
// DeleteAcls delete several acl once,
831
// delete to-lport and from-lport direction acl when direction is empty, otherwise one-way
832
// parentType is 'ls' or 'pg'
833
func (c *OVNNbClient) DeleteAcls(parentName, parentType, direction string, externalIDs map[string]string) error {
1✔
834
        ops, err := c.DeleteAclsOps(parentName, parentType, direction, externalIDs)
1✔
835
        if err != nil {
2✔
836
                klog.Error(err)
1✔
837
                return err
1✔
838
        }
1✔
839

840
        if err = c.Transact("acls-del", ops); err != nil {
1✔
841
                klog.Error(err)
×
842
                return fmt.Errorf("del acls from type %s %s: %w", parentType, parentName, err)
×
843
        }
×
844

845
        return nil
1✔
846
}
847

848
func (c *OVNNbClient) DeleteACL(parentName, parentType, direction, priority, match string) error {
1✔
849
        acl, err := c.GetACL(parentName, direction, priority, match, true)
1✔
850
        if err != nil {
2✔
851
                klog.Error(err)
1✔
852
                return err
1✔
853
        }
1✔
854

855
        if acl == nil {
2✔
856
                return nil // skip if acl not exist
1✔
857
        }
1✔
858

859
        // the acls column has a strong reference to the ACL table, so there is no need to delete the ACL
860
        var removeACLOp []ovsdb.Operation
1✔
861
        if parentType == portGroupKey { // remove acl from port group
2✔
862
                removeACLOp, err = c.portGroupUpdateACLOp(parentName, []string{acl.UUID}, ovsdb.MutateOperationDelete)
1✔
863
                if err != nil {
1✔
864
                        klog.Error(err)
×
865
                        return fmt.Errorf("generate operations for deleting acl from port group %s: %w", parentName, err)
×
866
                }
×
867
        } else { // remove acl from logical switch
1✔
868
                removeACLOp, err = c.logicalSwitchUpdateACLOp(parentName, []string{acl.UUID}, ovsdb.MutateOperationDelete)
1✔
869
                if err != nil {
1✔
870
                        klog.Error(err)
×
871
                        return fmt.Errorf("generate operations for deleting acl from logical switch %s: %w", parentName, err)
×
872
                }
×
873
        }
874

875
        if err = c.Transact("acls-del", removeACLOp); err != nil {
1✔
876
                klog.Error(err)
×
877
                return fmt.Errorf("del acls from type %s %s: %w", parentType, parentName, err)
×
878
        }
×
879

880
        return nil
1✔
881
}
882

883
// GetACL get acl by direction, priority and match,
884
// be consistent with ovn-nbctl which direction, priority and match determine one acl in port group or logical switch
885
func (c *OVNNbClient) GetACL(parent, direction, priority, match string, ignoreNotFound bool) (*ovnnb.ACL, error) {
1✔
886
        // this is necessary because may exist same direction, priority and match acl in different port group or logical switch
1✔
887
        if len(parent) == 0 {
2✔
888
                return nil, errors.New("the port group name or logical switch name is required")
1✔
889
        }
1✔
890

891
        ctx, cancel := context.WithTimeout(context.Background(), c.Timeout)
1✔
892
        defer cancel()
1✔
893

1✔
894
        intPriority, _ := strconv.Atoi(priority)
1✔
895

1✔
896
        aclList := make([]ovnnb.ACL, 0)
1✔
897
        if err := c.ovsDbClient.WhereCache(func(acl *ovnnb.ACL) bool {
2✔
898
                return len(acl.ExternalIDs) != 0 && acl.ExternalIDs[aclParentKey] == parent && acl.Direction == direction && acl.Priority == intPriority && acl.Match == match
1✔
899
        }).List(ctx, &aclList); err != nil {
1✔
900
                klog.Error(err)
×
901
                return nil, NewACLError(ACLErrorDatabase, fmt.Sprintf("get acl with 'parent %s direction %s priority %s match %s': %v", parent, direction, priority, match, err))
×
902
        }
×
903

904
        // not found
905
        if len(aclList) == 0 {
2✔
906
                if ignoreNotFound {
2✔
907
                        return nil, nil
1✔
908
                }
1✔
909
                return nil, NewACLError(ACLErrorNotFound, fmt.Sprintf("not found acl with 'parent %s direction %s priority %s match %s'", parent, direction, priority, match))
1✔
910
        }
911

912
        if len(aclList) > 1 {
2✔
913
                return nil, NewACLError(ACLErrorDuplicated, fmt.Sprintf("more than one acl with same 'parent %s direction %s priority %s match %s'", parent, direction, priority, match))
1✔
914
        }
1✔
915

916
        // #nosec G602
917
        return &aclList[0], nil
1✔
918
}
919

920
// ListAcls list acls which match the given externalIDs,
921
// result should include all to-lport and from-lport acls when direction is empty,
922
// result should include all acls when externalIDs is empty,
923
// result should include all acls which externalIDs[key] is not empty when externalIDs[key] is ""
924
// TODO: maybe add other filter conditions(priority or match)
925
func (c *OVNNbClient) ListAcls(direction string, externalIDs map[string]string) ([]ovnnb.ACL, error) {
1✔
926
        ctx, cancel := context.WithTimeout(context.Background(), c.Timeout)
1✔
927
        defer cancel()
1✔
928

1✔
929
        aclList := make([]ovnnb.ACL, 0)
1✔
930

1✔
931
        if err := c.WhereCache(aclFilter(direction, externalIDs)).List(ctx, &aclList); err != nil {
1✔
932
                klog.Error(err)
×
933
                return nil, fmt.Errorf("list acls: %w", err)
×
934
        }
×
935

936
        return aclList, nil
1✔
937
}
938

939
func (c *OVNNbClient) ACLExists(parent, direction, priority, match string) (bool, error) {
1✔
940
        acl, err := c.GetACL(parent, direction, priority, match, true)
1✔
941
        if err != nil {
1✔
942
                var aclErr *ACLError
×
943
                if errors.As(err, &aclErr) && aclErr.Type == ACLErrorDuplicated {
×
944
                        return true, nil
×
945
                }
×
946
                return false, err
×
947
        }
948
        return acl != nil, nil
1✔
949
}
950

951
// newACL return acl with basic information
952
func (c *OVNNbClient) newACL(parent, direction, priority, match, action string, tier int, options ...func(acl *ovnnb.ACL)) (*ovnnb.ACL, error) {
1✔
953
        if len(parent) == 0 {
2✔
954
                return nil, errors.New("the port group name or logical switch name is required")
1✔
955
        }
1✔
956

957
        if len(direction) == 0 || len(priority) == 0 || len(match) == 0 || len(action) == 0 {
2✔
958
                return nil, fmt.Errorf("acl 'direction %s' and 'priority %s' and 'match %s' and 'action %s' is required", direction, priority, match, action)
1✔
959
        }
1✔
960

961
        exists, err := c.ACLExists(parent, direction, priority, match)
1✔
962
        if err != nil {
1✔
963
                klog.Error(err)
×
964
                return nil, fmt.Errorf("get parent %s acl: %w", parent, err)
×
965
        }
×
966

967
        // found, ignore
968
        if exists {
2✔
969
                return nil, nil
1✔
970
        }
1✔
971

972
        intPriority, _ := strconv.Atoi(priority)
1✔
973

1✔
974
        acl := &ovnnb.ACL{
1✔
975
                UUID:      ovsclient.NamedUUID(),
1✔
976
                Action:    action,
1✔
977
                Direction: direction,
1✔
978
                Match:     match,
1✔
979
                Priority:  intPriority,
1✔
980
                ExternalIDs: map[string]string{
1✔
981
                        aclParentKey: parent,
1✔
982
                },
1✔
983
                Tier: tier,
1✔
984
        }
1✔
985

1✔
986
        for _, option := range options {
2✔
987
                option(acl)
1✔
988
        }
1✔
989

990
        return acl, nil
1✔
991
}
992

993
// newACLWithoutCheck return acl with basic information without check acl exists,
994
// this would cause duplicated acl, so don't use this function to create acl normally,
995
// but maybe used for updating network policy acl
996
func (c *OVNNbClient) newACLWithoutCheck(parent, direction, priority, match, action string, tier int, options ...func(acl *ovnnb.ACL)) (*ovnnb.ACL, error) {
1✔
997
        if len(parent) == 0 {
2✔
998
                return nil, errors.New("the port group name or logical switch name is required")
1✔
999
        }
1✔
1000

1001
        if len(direction) == 0 || len(priority) == 0 || len(match) == 0 || len(action) == 0 {
2✔
1002
                return nil, fmt.Errorf("acl 'direction %s' and 'priority %s' and 'match %s' and 'action %s' is required", direction, priority, match, action)
1✔
1003
        }
1✔
1004

1005
        intPriority, _ := strconv.Atoi(priority)
1✔
1006

1✔
1007
        acl := &ovnnb.ACL{
1✔
1008
                UUID:      ovsclient.NamedUUID(),
1✔
1009
                Action:    action,
1✔
1010
                Direction: direction,
1✔
1011
                Match:     match,
1✔
1012
                Priority:  intPriority,
1✔
1013
                ExternalIDs: map[string]string{
1✔
1014
                        aclParentKey: parent,
1✔
1015
                },
1✔
1016
                Tier: tier,
1✔
1017
        }
1✔
1018

1✔
1019
        for _, option := range options {
2✔
1020
                option(acl)
1✔
1021
        }
1✔
1022

1023
        return acl, nil
1✔
1024
}
1025

1026
// createSgRuleACL create security group rule acl
1027
func (c *OVNNbClient) newSgRuleACL(sgName, direction string, rule kubeovnv1.SecurityGroupRule) (*ovnnb.ACL, error) {
1✔
1028
        ipSuffix := "ip4"
1✔
1029
        if rule.IPVersion == "ipv6" {
2✔
1030
                ipSuffix = "ip6"
1✔
1031
        }
1✔
1032

1033
        pgName := GetSgPortGroupName(sgName)
1✔
1034

1✔
1035
        // ingress rule
1✔
1036
        srcOrDst, portDirection := "src", "outport"
1✔
1037
        if direction == ovnnb.ACLDirectionFromLport { // egress rule
2✔
1038
                srcOrDst = "dst"
1✔
1039
                portDirection = "inport"
1✔
1040
        }
1✔
1041

1042
        ipKey := ipSuffix + "." + srcOrDst
1✔
1043

1✔
1044
        /* match all traffic to or from pgName */
1✔
1045
        allIPMatch := NewAndACLMatch(
1✔
1046
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
1047
                NewACLMatch(ipSuffix, "", "", ""),
1✔
1048
        )
1✔
1049

1✔
1050
        /* allow allowed ip traffic */
1✔
1051
        // type address
1✔
1052
        allowedIPMatch := NewAndACLMatch(
1✔
1053
                allIPMatch,
1✔
1054
                NewACLMatch(ipKey, "==", rule.RemoteAddress, ""),
1✔
1055
        )
1✔
1056

1✔
1057
        // type securityGroup
1✔
1058
        remotePgName := GetSgV4AssociatedName(rule.RemoteSecurityGroup)
1✔
1059
        if rule.IPVersion == "ipv6" {
2✔
1060
                remotePgName = GetSgV6AssociatedName(rule.RemoteSecurityGroup)
1✔
1061
        }
1✔
1062
        if rule.RemoteType == kubeovnv1.SgRemoteTypeSg {
2✔
1063
                allowedIPMatch = NewAndACLMatch(
1✔
1064
                        allIPMatch,
1✔
1065
                        NewACLMatch(ipKey, "==", "$"+remotePgName, ""),
1✔
1066
                )
1✔
1067
        }
1✔
1068

1069
        /* allow layer 4 traffic */
1070
        // allow all layer 4 traffic
1071
        match := allowedIPMatch
1✔
1072

1✔
1073
        switch rule.Protocol {
1✔
1074
        case kubeovnv1.SgProtocolICMP:
1✔
1075
                match = NewAndACLMatch(
1✔
1076
                        allowedIPMatch,
1✔
1077
                        NewACLMatch("icmp4", "", "", ""),
1✔
1078
                )
1✔
1079
                if ipSuffix == "ip6" {
2✔
1080
                        match = NewAndACLMatch(
1✔
1081
                                allowedIPMatch,
1✔
1082
                                NewACLMatch("icmp6", "", "", ""),
1✔
1083
                        )
1✔
1084
                }
1✔
1085
        case kubeovnv1.SgProtocolTCP, kubeovnv1.SgProtocolUDP:
1✔
1086
                match = NewAndACLMatch(
1✔
1087
                        allowedIPMatch,
1✔
1088
                        NewACLMatch(string(rule.Protocol)+".dst", "<=", strconv.Itoa(rule.PortRangeMin), strconv.Itoa(rule.PortRangeMax)),
1✔
1089
                )
1✔
1090
        }
1091

1092
        action := ovnnb.ACLActionDrop
1✔
1093
        if rule.Policy == kubeovnv1.SgPolicyAllow {
2✔
1094
                action = ovnnb.ACLActionAllowRelated
1✔
1095
        }
1✔
1096

1097
        highestPriority, _ := strconv.Atoi(util.SecurityGroupHighestPriority)
1✔
1098

1✔
1099
        acl, err := c.newACL(pgName, direction, strconv.Itoa(highestPriority-rule.Priority), match.String(), action, util.NetpolACLTier)
1✔
1100
        if err != nil {
1✔
1101
                klog.Error(err)
×
1102
                return nil, fmt.Errorf("new security group acl for port group %s: %w", pgName, err)
×
1103
        }
×
1104

1105
        return acl, nil
1✔
1106
}
1107

1108
func newNetworkPolicyACLMatch(pgName, asAllowName, asExceptName, protocol, direction string, npp []netv1.NetworkPolicyPort, namedPortMap map[string]*util.NamedPortInfo) []string {
1✔
1109
        ipSuffix := "ip4"
1✔
1110
        if protocol == kubeovnv1.ProtocolIPv6 {
2✔
1111
                ipSuffix = "ip6"
1✔
1112
        }
1✔
1113

1114
        // ingress rule
1115
        srcOrDst, portDirection := "src", "outport"
1✔
1116
        if direction == ovnnb.ACLDirectionFromLport { // egress rule
2✔
1117
                srcOrDst = "dst"
1✔
1118
                portDirection = "inport"
1✔
1119
        }
1✔
1120

1121
        ipKey := ipSuffix + "." + srcOrDst
1✔
1122

1✔
1123
        // match all traffic to or from pgName
1✔
1124
        allIPMatch := NewAndACLMatch(
1✔
1125
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
1126
                NewACLMatch("ip", "", "", ""),
1✔
1127
        )
1✔
1128

1✔
1129
        allowedIPMatch := NewAndACLMatch(
1✔
1130
                allIPMatch,
1✔
1131
                NewACLMatch(ipKey, "==", "$"+asAllowName, ""),
1✔
1132
                NewACLMatch(ipKey, "!=", "$"+asExceptName, ""),
1✔
1133
        )
1✔
1134

1✔
1135
        matches := make([]string, 0)
1✔
1136

1✔
1137
        // allow allowed ip traffic but except
1✔
1138
        if len(npp) == 0 {
2✔
1139
                return []string{allowedIPMatch.String()}
1✔
1140
        }
1✔
1141

1142
        for _, port := range npp {
2✔
1143
                protocol := strings.ToLower(string(*port.Protocol))
1✔
1144

1✔
1145
                // allow all tcp or udp traffic
1✔
1146
                if port.Port == nil {
2✔
1147
                        allLayer4Match := NewAndACLMatch(
1✔
1148
                                allowedIPMatch,
1✔
1149
                                NewACLMatch(protocol, "", "", ""),
1✔
1150
                        )
1✔
1151

1✔
1152
                        matches = append(matches, allLayer4Match.String())
1✔
1153
                        continue
1✔
1154
                }
1155

1156
                // allow one tcp or udp port traffic
1157
                if port.EndPort == nil {
2✔
1158
                        tcpKey := protocol + ".dst"
1✔
1159

1✔
1160
                        var portID int32
1✔
1161
                        if port.Port.Type == intstr.Int {
2✔
1162
                                portID = port.Port.IntVal
1✔
1163
                        } else if namedPortMap != nil {
3✔
1164
                                _, ok := namedPortMap[port.Port.StrVal]
1✔
1165
                                if !ok {
2✔
1166
                                        // for cyclonus network policy test case 'should allow ingress access on one named port'
1✔
1167
                                        // this case expect all-deny if no named port defined
1✔
1168
                                        klog.Errorf("no named port with name %s found", port.Port.StrVal)
1✔
1169
                                } else {
2✔
1170
                                        portID = namedPortMap[port.Port.StrVal].PortID
1✔
1171
                                }
1✔
1172
                        }
1173

1174
                        oneTCPMatch := NewAndACLMatch(
1✔
1175
                                allowedIPMatch,
1✔
1176
                                NewACLMatch(tcpKey, "==", strconv.Itoa(int(portID)), ""),
1✔
1177
                        )
1✔
1178

1✔
1179
                        matches = append(matches, oneTCPMatch.String())
1✔
1180

1✔
1181
                        continue
1✔
1182
                }
1183

1184
                // allow several tcp or udp port traffic
1185
                tcpKey := protocol + ".dst"
1✔
1186
                severalTCPMatch := NewAndACLMatch(
1✔
1187
                        allowedIPMatch,
1✔
1188
                        NewACLMatch(tcpKey, "<=", strconv.Itoa(int(port.Port.IntVal)), strconv.Itoa(int(*port.EndPort))),
1✔
1189
                )
1✔
1190
                matches = append(matches, severalTCPMatch.String())
1✔
1191
        }
1192

1193
        return matches
1✔
1194
}
1195

1196
// aclFilter filter acls which match the given externalIDs,
1197
// result should include all to-lport and from-lport acls when direction is empty,
1198
// result should include all acls when externalIDs is empty,
1199
// result should include all acls which externalIDs[key] is not empty when externalIDs[key] is ""
1200
// TODO: maybe add other filter conditions(priority or match)
1201
func aclFilter(direction string, externalIDs map[string]string) func(acl *ovnnb.ACL) bool {
1✔
1202
        return func(acl *ovnnb.ACL) bool {
2✔
1203
                if len(acl.ExternalIDs) < len(externalIDs) {
2✔
1204
                        return false
1✔
1205
                }
1✔
1206

1207
                if len(acl.ExternalIDs) != 0 {
2✔
1208
                        for k, v := range externalIDs {
2✔
1209
                                // if only key exist but not value in externalIDs, we should include this lsp,
1✔
1210
                                // it's equal to shell command `ovn-nbctl --columns=xx find acl external_ids:key!=\"\"`
1✔
1211
                                if len(v) == 0 {
2✔
1212
                                        if len(acl.ExternalIDs[k]) == 0 {
1✔
1213
                                                return false
×
1214
                                        }
×
1215
                                } else {
1✔
1216
                                        if acl.ExternalIDs[k] != v {
2✔
1217
                                                return false
1✔
1218
                                        }
1✔
1219
                                }
1220
                        }
1221
                }
1222

1223
                if len(direction) != 0 && acl.Direction != direction {
2✔
1224
                        return false
1✔
1225
                }
1✔
1226

1227
                return true
1✔
1228
        }
1229
}
1230

1231
// CreateAcls return operations which create several acl once
1232
// parentType is 'ls' or 'pg'
1233
func (c *OVNNbClient) CreateAclsOps(parentName, parentType string, acls ...*ovnnb.ACL) ([]ovsdb.Operation, error) {
1✔
1234
        if parentType != portGroupKey && parentType != LogicalSwitchKey {
2✔
1235
                return nil, fmt.Errorf("acl parent type must be '%s' or '%s'", portGroupKey, LogicalSwitchKey)
1✔
1236
        }
1✔
1237

1238
        if len(acls) == 0 {
2✔
1239
                return nil, nil
1✔
1240
        }
1✔
1241

1242
        models := make([]model.Model, 0, len(acls))
1✔
1243
        aclUUIDs := make([]string, 0, len(acls))
1✔
1244
        for _, acl := range acls {
2✔
1245
                if acl != nil {
2✔
1246
                        models = append(models, model.Model(acl))
1✔
1247
                        aclUUIDs = append(aclUUIDs, acl.UUID)
1✔
1248
                }
1✔
1249
        }
1250

1251
        createAclsOp, err := c.Create(models...)
1✔
1252
        if err != nil {
1✔
1253
                klog.Error(err)
×
1254
                return nil, fmt.Errorf("generate operations for creating acls: %w", err)
×
1255
        }
×
1256

1257
        var aclAddOp []ovsdb.Operation
1✔
1258
        if parentType == portGroupKey { // acl attach to port group
2✔
1259
                aclAddOp, err = c.portGroupUpdateACLOp(parentName, aclUUIDs, ovsdb.MutateOperationInsert)
1✔
1260
                if err != nil {
2✔
1261
                        klog.Error(err)
1✔
1262
                        return nil, fmt.Errorf("generate operations for adding acls to port group %s: %w", parentName, err)
1✔
1263
                }
1✔
1264
        } else { // acl attach to logical switch
1✔
1265
                aclAddOp, err = c.logicalSwitchUpdateACLOp(parentName, aclUUIDs, ovsdb.MutateOperationInsert)
1✔
1266
                if err != nil {
2✔
1267
                        klog.Error(err)
1✔
1268
                        return nil, fmt.Errorf("generate operations for adding acls to logical switch %s: %w", parentName, err)
1✔
1269
                }
1✔
1270
        }
1271

1272
        ops := make([]ovsdb.Operation, 0, len(createAclsOp)+len(aclAddOp))
1✔
1273
        ops = append(ops, createAclsOp...)
1✔
1274
        ops = append(ops, aclAddOp...)
1✔
1275

1✔
1276
        return ops, nil
1✔
1277
}
1278

1279
// DeleteAcls return operation which delete several acl once,
1280
// delete to-lport and from-lport direction acl when direction is empty, otherwise one-way
1281
// parentType is 'ls' or 'pg'
1282
func (c *OVNNbClient) DeleteAclsOps(parentName, parentType, direction string, externalIDs map[string]string) ([]ovsdb.Operation, error) {
1✔
1283
        if parentName == "" {
2✔
1284
                return nil, errors.New("the port group name or logical switch name is required")
1✔
1285
        }
1✔
1286

1287
        if externalIDs == nil {
2✔
1288
                externalIDs = make(map[string]string)
1✔
1289
        }
1✔
1290

1291
        externalIDs[aclParentKey] = parentName
1✔
1292

1✔
1293
        /* delete acls from port group or logical switch */
1✔
1294
        acls, err := c.ListAcls(direction, externalIDs)
1✔
1295
        if err != nil {
1✔
1296
                klog.Error(err)
×
1297
                return nil, fmt.Errorf("list type %s %s acls: %w", parentType, parentName, err)
×
1298
        }
×
1299

1300
        aclUUIDs := make([]string, 0, len(acls))
1✔
1301
        for _, acl := range acls {
2✔
1302
                aclUUIDs = append(aclUUIDs, acl.UUID)
1✔
1303
        }
1✔
1304

1305
        // the acls column has a strong reference to the ACL table, so there is no need to delete the ACL
1306
        var removeACLOp []ovsdb.Operation
1✔
1307
        if parentType == portGroupKey { // remove acl from port group
2✔
1308
                removeACLOp, err = c.portGroupUpdateACLOp(parentName, aclUUIDs, ovsdb.MutateOperationDelete)
1✔
1309
                if err != nil {
1✔
1310
                        klog.Error(err)
×
1311
                        return nil, fmt.Errorf("generate operations for deleting acls from port group %s: %w", parentName, err)
×
1312
                }
×
1313
        } else { // remove acl from logical switch
1✔
1314
                removeACLOp, err = c.logicalSwitchUpdateACLOp(parentName, aclUUIDs, ovsdb.MutateOperationDelete)
1✔
1315
                if err != nil {
1✔
1316
                        klog.Error(err)
×
1317
                        return nil, fmt.Errorf("generate operations for deleting acls from logical switch %s: %w", parentName, err)
×
1318
                }
×
1319
        }
1320

1321
        return removeACLOp, nil
1✔
1322
}
1323

1324
// sgRuleNoACL check if security group rule has acl
1325
func (c *OVNNbClient) sgRuleNoACL(sgName, direction string, rule kubeovnv1.SecurityGroupRule) (bool, error) {
1✔
1326
        ipSuffix := "ip4"
1✔
1327
        if rule.IPVersion == "ipv6" {
2✔
1328
                ipSuffix = "ip6"
1✔
1329
        }
1✔
1330

1331
        pgName := GetSgPortGroupName(sgName)
1✔
1332

1✔
1333
        // ingress rule
1✔
1334
        srcOrDst, portDirection := "src", "outport"
1✔
1335
        if direction == ovnnb.ACLDirectionFromLport { // egress rule
2✔
1336
                srcOrDst = "dst"
1✔
1337
                portDirection = "inport"
1✔
1338
        }
1✔
1339

1340
        ipKey := ipSuffix + "." + srcOrDst
1✔
1341

1✔
1342
        /* match all traffic to or from pgName */
1✔
1343
        allIPMatch := NewAndACLMatch(
1✔
1344
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
1345
                NewACLMatch(ipSuffix, "", "", ""),
1✔
1346
        )
1✔
1347

1✔
1348
        /* allow allowed ip traffic */
1✔
1349
        // type address
1✔
1350
        allowedIPMatch := NewAndACLMatch(
1✔
1351
                allIPMatch,
1✔
1352
                NewACLMatch(ipKey, "==", rule.RemoteAddress, ""),
1✔
1353
        )
1✔
1354

1✔
1355
        // type securityGroup
1✔
1356
        remotePgName := GetSgV4AssociatedName(rule.RemoteSecurityGroup)
1✔
1357
        if rule.IPVersion == "ipv6" {
2✔
1358
                remotePgName = GetSgV6AssociatedName(rule.RemoteSecurityGroup)
1✔
1359
        }
1✔
1360
        if rule.RemoteType == kubeovnv1.SgRemoteTypeSg {
2✔
1361
                allowedIPMatch = NewAndACLMatch(
1✔
1362
                        allIPMatch,
1✔
1363
                        NewACLMatch(ipKey, "==", "$"+remotePgName, ""),
1✔
1364
                )
1✔
1365
        }
1✔
1366

1367
        /* allow layer 4 traffic */
1368
        // allow all layer 4 traffic
1369
        match := allowedIPMatch
1✔
1370

1✔
1371
        switch rule.Protocol {
1✔
1372
        case kubeovnv1.SgProtocolICMP:
1✔
1373
                match = NewAndACLMatch(
1✔
1374
                        allowedIPMatch,
1✔
1375
                        NewACLMatch("icmp4", "", "", ""),
1✔
1376
                )
1✔
1377
                if ipSuffix == "ip6" {
1✔
1378
                        match = NewAndACLMatch(
×
1379
                                allowedIPMatch,
×
1380
                                NewACLMatch("icmp6", "", "", ""),
×
1381
                        )
×
1382
                }
×
1383
        case kubeovnv1.SgProtocolTCP, kubeovnv1.SgProtocolUDP:
1✔
1384
                match = NewAndACLMatch(
1✔
1385
                        allowedIPMatch,
1✔
1386
                        NewACLMatch(string(rule.Protocol)+".dst", "<=", strconv.Itoa(rule.PortRangeMin), strconv.Itoa(rule.PortRangeMax)),
1✔
1387
                )
1✔
1388
        }
1389

1390
        securityGroupHighestPriority, _ := strconv.Atoi(util.SecurityGroupHighestPriority)
1✔
1391
        priority := securityGroupHighestPriority - rule.Priority
1✔
1392
        exists, err := c.ACLExists(pgName, direction, strconv.Itoa(priority), match.String())
1✔
1393
        if err != nil {
1✔
1394
                err = fmt.Errorf("failed to check acl rule for security group %s: %w", sgName, err)
×
1395
                klog.Error(err)
×
1396
                return false, err
×
1397
        }
×
1398

1399
        // sg rule no acl, need to sync
1400
        if !exists {
2✔
1401
                return true, nil
1✔
1402
        }
1✔
1403
        return false, nil
1✔
1404
}
1405

1406
// SGLostACL check if security group lost an acl
1407
func (c *OVNNbClient) SGLostACL(sg *kubeovnv1.SecurityGroup) (bool, error) {
1✔
1408
        ingressRules := sg.Spec.IngressRules
1✔
1409
        for _, rule := range ingressRules {
2✔
1410
                no, err := c.sgRuleNoACL(sg.Name, ovnnb.ACLDirectionToLport, rule)
1✔
1411
                if err != nil {
1✔
1412
                        klog.Error(err)
×
1413
                        return false, err
×
1414
                }
×
1415
                if no {
2✔
1416
                        klog.Infof("security group %s lost ingress rule: %v", sg.Name, rule)
1✔
1417
                        return true, nil
1✔
1418
                }
1✔
1419
        }
1420
        egressRules := sg.Spec.EgressRules
1✔
1421
        for _, rule := range egressRules {
2✔
1422
                no, err := c.sgRuleNoACL(sg.Name, ovnnb.ACLDirectionFromLport, rule)
1✔
1423
                if err != nil {
1✔
1424
                        klog.Error(err)
×
1425
                        return false, err
×
1426
                }
×
1427
                if no {
2✔
1428
                        klog.Infof("security group %s lost egress rule: %v", sg.Name, rule)
1✔
1429
                        return true, nil
1✔
1430
                }
1✔
1431
        }
1432
        return false, nil
1✔
1433
}
1434

1435
// UpdateAnpRuleACLOps return operation that creates an ingress/egress ACL
1436
func (c *OVNNbClient) UpdateAnpRuleACLOps(pgName, asName, protocol, aclName string, priority int, aclAction ovnnb.ACLAction, logACLActions []ovnnb.ACLAction, rulePorts []v1alpha1.AdminNetworkPolicyPort, isIngress, isBanp bool) ([]ovsdb.Operation, error) {
1✔
1437
        acls := make([]*ovnnb.ACL, 0, 10)
1✔
1438

1✔
1439
        options := func(acl *ovnnb.ACL) {
2✔
1440
                setACLName(acl, aclName)
1✔
1441

1✔
1442
                if acl.ExternalIDs == nil {
1✔
1443
                        acl.ExternalIDs = make(map[string]string)
×
1444
                }
×
1445
                acl.ExternalIDs[aclParentKey] = pgName
1✔
1446

1✔
1447
                if acl.Options == nil {
2✔
1448
                        acl.Options = make(map[string]string)
1✔
1449
                }
1✔
1450
                acl.Options["apply-after-lb"] = "true"
1✔
1451

1✔
1452
                if slices.Contains(logACLActions, aclAction) {
2✔
1453
                        acl.Log = true
1✔
1454
                        if aclAction == ovnnb.ACLActionDrop {
2✔
1455
                                acl.Severity = ptr.To(ovnnb.ACLSeverityWarning)
1✔
1456
                        }
1✔
1457
                }
1458
        }
1459

1460
        var direction ovnnb.ACLDirection
1✔
1461
        if isIngress {
2✔
1462
                direction = ovnnb.ACLDirectionToLport
1✔
1463
        } else {
2✔
1464
                direction = ovnnb.ACLDirectionFromLport
1✔
1465
        }
1✔
1466

1467
        var tier int
1✔
1468
        if isBanp {
2✔
1469
                tier = util.BanpACLTier
1✔
1470
        } else {
2✔
1471
                tier = util.AnpACLTier
1✔
1472
        }
1✔
1473

1474
        matches := newAnpACLMatch(pgName, asName, protocol, direction, rulePorts)
1✔
1475
        for _, m := range matches {
2✔
1476
                strPriority := strconv.Itoa(priority)
1✔
1477
                setACL, err := c.newACLWithoutCheck(pgName, direction, strPriority, m, aclAction, tier, options)
1✔
1478
                if err != nil {
1✔
1479
                        klog.Error(err)
×
1480
                        return nil, fmt.Errorf("new ingress acl for port group %s: %w", pgName, err)
×
1481
                }
×
1482

1483
                acls = append(acls, setACL)
1✔
1484
        }
1485

1486
        ops, err := c.CreateAclsOps(pgName, portGroupKey, acls...)
1✔
1487
        if err != nil {
1✔
1488
                klog.Error(err)
×
1489
                return nil, err
×
1490
        }
×
1491

1492
        return ops, nil
1✔
1493
}
1494

1495
func newAnpACLMatch(pgName, asName, protocol, direction string, rulePorts []v1alpha1.AdminNetworkPolicyPort) []string {
1✔
1496
        ipSuffix := "ip4"
1✔
1497
        if protocol == kubeovnv1.ProtocolIPv6 {
2✔
1498
                ipSuffix = "ip6"
1✔
1499
        }
1✔
1500

1501
        // ingress rule
1502
        srcOrDst, portDirection := "src", "outport"
1✔
1503
        if direction == ovnnb.ACLDirectionFromLport { // egress rule
2✔
1504
                srcOrDst = "dst"
1✔
1505
                portDirection = "inport"
1✔
1506
        }
1✔
1507

1508
        ipKey := ipSuffix + "." + srcOrDst
1✔
1509

1✔
1510
        // match all traffic to or from pgName
1✔
1511
        allIPMatch := NewAndACLMatch(
1✔
1512
                NewACLMatch(portDirection, "==", "@"+pgName, ""),
1✔
1513
                NewACLMatch("ip", "", "", ""),
1✔
1514
        )
1✔
1515

1✔
1516
        selectIPMatch := NewAndACLMatch(
1✔
1517
                allIPMatch,
1✔
1518
                NewACLMatch(ipKey, "==", "$"+asName, ""),
1✔
1519
        )
1✔
1520
        if len(rulePorts) == 0 {
2✔
1521
                return []string{selectIPMatch.String()}
1✔
1522
        }
1✔
1523

1524
        matches := make([]string, 0, 10)
1✔
1525
        for _, port := range rulePorts {
2✔
1526
                // Exactly one field must be set.
1✔
1527
                // Do not support NamedPort now
1✔
1528
                switch {
1✔
1529
                case port.PortNumber != nil:
1✔
1530
                        protocol := strings.ToLower(string(port.PortNumber.Protocol))
1✔
1531
                        protocolKey := protocol + ".dst"
1✔
1532

1✔
1533
                        oneMatch := NewAndACLMatch(
1✔
1534
                                selectIPMatch,
1✔
1535
                                NewACLMatch(protocolKey, "==", strconv.Itoa(int(port.PortNumber.Port)), ""),
1✔
1536
                        )
1✔
1537
                        matches = append(matches, oneMatch.String())
1✔
1538
                case port.PortRange != nil:
1✔
1539
                        protocol := strings.ToLower(string(port.PortRange.Protocol))
1✔
1540
                        protocolKey := protocol + ".dst"
1✔
1541

1✔
1542
                        severalMatch := NewAndACLMatch(
1✔
1543
                                selectIPMatch,
1✔
1544
                                NewACLMatch(protocolKey, "<=", strconv.Itoa(int(port.PortRange.Start)), strconv.Itoa(int(port.PortRange.End))),
1✔
1545
                        )
1✔
1546
                        matches = append(matches, severalMatch.String())
1✔
1547
                default:
×
1548
                        klog.Errorf("failed to check port for anp ingress rule, pg %s, as %s", pgName, asName)
×
1549
                }
1550
        }
1551
        return matches
1✔
1552
}
1553

1554
func (c *OVNNbClient) MigrateACLTier() error {
×
1555
        ctx, cancel := context.WithTimeout(context.Background(), c.Timeout)
×
1556
        defer cancel()
×
1557

×
1558
        var aclList []ovnnb.ACL
×
1559
        if err := c.ovsDbClient.WhereCache(func(acl *ovnnb.ACL) bool { return acl.Tier == 0 }).List(ctx, &aclList); err != nil {
×
1560
                err = fmt.Errorf("failed to list acls with tier 0: %w", err)
×
1561
                klog.Error(err)
×
1562
                return err
×
1563
        }
×
1564

1565
        ops := make([]ovsdb.Operation, 0, len(aclList))
×
1566
        for _, acl := range aclList {
×
1567
                acl.Tier = util.NetpolACLTier
×
1568
                op, err := c.Where(&acl).Update(&acl, &acl.Tier)
×
1569
                if err != nil {
×
1570
                        klog.Error(err)
×
1571
                        return fmt.Errorf("failed to generate operations for updating acl %s tier: %w", acl.UUID, err)
×
1572
                }
×
1573
                ops = append(ops, op...)
×
1574
        }
1575
        if len(ops) == 0 {
×
1576
                return nil
×
1577
        }
×
1578

1579
        if err := c.Transact("acl-migrate-tier", ops); err != nil {
×
1580
                klog.Error(err)
×
1581
                return fmt.Errorf("failed to migrate acl tier: %w", err)
×
1582
        }
×
1583

1584
        return nil
×
1585
}
1586

1587
func (c *OVNNbClient) CleanNoParentKeyAcls() error {
×
1588
        ctx, cancel := context.WithTimeout(context.Background(), c.Timeout)
×
1589
        defer cancel()
×
1590

×
1591
        var aclList []ovnnb.ACL
×
1592
        if err := c.ovsDbClient.WhereCache(func(acl *ovnnb.ACL) bool {
×
1593
                _, ok := acl.ExternalIDs[aclParentKey]
×
1594
                return !ok
×
1595
        }).List(ctx, &aclList); err != nil {
×
1596
                err = fmt.Errorf("failed to list acls without parent: %w", err)
×
1597
                klog.Error(err)
×
1598
                return err
×
1599
        }
×
1600

1601
        ops := make([]ovsdb.Operation, 0, len(aclList))
×
1602
        for _, acl := range aclList {
×
1603
                var portGroups []ovnnb.PortGroup
×
1604
                if err := c.ovsDbClient.WhereCache(func(pg *ovnnb.PortGroup) bool {
×
1605
                        return slices.Contains(pg.ACLs, acl.UUID)
×
1606
                }).List(ctx, &portGroups); err == nil {
×
1607
                        for _, pg := range portGroups {
×
1608
                                op, err := c.portGroupUpdateACLOp(pg.Name, []string{acl.UUID}, ovsdb.MutateOperationDelete)
×
1609
                                if err == nil {
×
1610
                                        ops = append(ops, op...)
×
1611
                                }
×
1612
                        }
1613
                }
1614
                var logicalSwitches []ovnnb.LogicalSwitch
×
1615
                if err := c.ovsDbClient.WhereCache(func(ls *ovnnb.LogicalSwitch) bool {
×
1616
                        return slices.Contains(ls.ACLs, acl.UUID)
×
1617
                }).List(ctx, &logicalSwitches); err == nil {
×
1618
                        for _, ls := range logicalSwitches {
×
1619
                                op, err := c.logicalSwitchUpdateACLOp(ls.Name, []string{acl.UUID}, ovsdb.MutateOperationDelete)
×
1620
                                if err == nil {
×
1621
                                        ops = append(ops, op...)
×
1622
                                }
×
1623
                        }
1624
                }
1625
                delOp, err := c.Where(&acl).Delete()
×
1626
                if err == nil {
×
1627
                        ops = append(ops, delOp...)
×
1628
                }
×
1629
        }
1630
        if len(ops) == 0 {
×
1631
                return nil
×
1632
        }
×
1633

1634
        if err := c.Transact("acl-clean-no-parent", ops); err != nil {
×
1635
                klog.Error(err)
×
1636
                return fmt.Errorf("failed to clean acls without parent: %w", err)
×
1637
        }
×
1638

1639
        return nil
×
1640
}
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

© 2025 Coveralls, Inc