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

kubeovn / kube-ovn / 23181819432

17 Mar 2026 06:39AM UTC coverage: 23.439% (-0.02%) from 23.456%
23181819432

Pull #6462

github

zbb88888
feat: add --disable-svc-iptables-rule flag to skip service-related iptables rules

In non-primary CNI scenarios (Cilium, Calico with kube-proxy IPVS),
kube-ovn's service-related iptables rules (MARK, REJECT, SNAT, NodePort)
are redundant and can interfere with the primary CNI's service handling.

Add a new --disable-svc-iptables-rule flag that, when enabled:
- Skips MARK 0x4000 and MASQUERADE rules for pod-to-service traffic
- Skips REJECT rules for unmatched service traffic
- Skips SNAT rules for service-to-pod traffic
- Skips NodePort MARK rules (0x80000 and 0x4000)
- Skips service ACCEPT rules in filter INPUT/FORWARD chains
- Cleans up any previously installed service rules

Signed-off-by: zbb88888 <jmdxjsjgcxy@gmail.com>
Pull Request #6462: feat: add --disable-svc-iptables-rule flag to skip service-related iptables

0 of 44 new or added lines in 2 files covered. (0.0%)

196 existing lines in 2 files now uncovered.

12887 of 54981 relevant lines covered (23.44%)

0.27 hits per line

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

0.0
/pkg/daemon/gateway_linux.go
1
package daemon
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "net"
8
        "os"
9
        "slices"
10
        "sort"
11
        "strconv"
12
        "strings"
13
        "syscall"
14

15
        "github.com/kubeovn/felix/ipsets"
16
        "github.com/kubeovn/go-iptables/iptables"
17
        "github.com/scylladb/go-set/strset"
18
        "github.com/vishvananda/netlink"
19
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
20
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21
        "k8s.io/apimachinery/pkg/labels"
22
        "k8s.io/klog/v2"
23
        "k8s.io/utils/set"
24

25
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
26
        "github.com/kubeovn/kube-ovn/pkg/ovs"
27
        "github.com/kubeovn/kube-ovn/pkg/util"
28
)
29

30
const (
31
        ServiceSet                 = "services"
32
        SubnetSet                  = "subnets"
33
        SubnetNatSet               = "subnets-nat"
34
        SubnetDistributedGwSet     = "subnets-distributed-gw"
35
        LocalPodSet                = "local-pod-ip-nat"
36
        OtherNodeSet               = "other-node"
37
        IPSetPrefix                = "ovn"
38
        NatOutGoingPolicySubnetSet = "subnets-nat-policy"
39
        NatOutGoingPolicyRuleSet   = "natpr-"
40
)
41

42
const (
43
        NAT                        = util.NAT
44
        MANGLE                     = util.Mangle
45
        Prerouting                 = util.Prerouting
46
        Postrouting                = util.Postrouting
47
        Output                     = util.Output
48
        OvnPrerouting              = util.OvnPrerouting
49
        OvnPostrouting             = util.OvnPostrouting
50
        OvnOutput                  = util.OvnOutput
51
        OvnMasquerade              = util.OvnMasquerade
52
        OvnNatOutGoingPolicy       = util.OvnNatOutGoingPolicy
53
        OvnNatOutGoingPolicySubnet = util.OvnNatOutGoingPolicySubnet
54
)
55

56
const (
57
        OnOutGoingNatMark     = "0x90001/0x90001"
58
        OnOutGoingForwardMark = "0x90002/0x90002"
59
        TProxyOutputMark      = util.TProxyOutputMark
60
        TProxyOutputMask      = util.TProxyOutputMask
61
        TProxyPreroutingMark  = util.TProxyPreroutingMark
62
        TProxyPreroutingMask  = util.TProxyPreroutingMask
63
)
64

65
var (
66
        tProxyOutputMarkMask     = fmt.Sprintf("%#x/%#x", TProxyOutputMark, TProxyOutputMask)
67
        tProxyPreRoutingMarkMask = fmt.Sprintf("%#x/%#x", TProxyPreroutingMark, TProxyPreroutingMask)
68
)
69

70
type policyRouteMeta struct {
71
        family   int
72
        source   string
73
        gateway  string
74
        tableID  uint32
75
        priority uint32
76
}
77

78
func (c *Controller) setIPSet() error {
×
79
        protocols := make([]string, 2)
×
80
        if c.protocol == kubeovnv1.ProtocolDual {
×
81
                protocols[0] = kubeovnv1.ProtocolIPv4
×
82
                protocols[1] = kubeovnv1.ProtocolIPv6
×
83
        } else {
×
84
                protocols[0] = c.protocol
×
85
        }
×
86

87
        for _, protocol := range protocols {
×
88
                if c.ipsets[protocol] == nil {
×
89
                        continue
×
90
                }
91
                services := c.getServicesCIDR(protocol)
×
92
                subnets, _, err := c.getDefaultVpcSubnetsCIDR(protocol)
×
93
                if err != nil {
×
94
                        klog.Errorf("get subnets failed, %+v", err)
×
95
                        return err
×
96
                }
×
97
                subnetsNeedNat, err := c.getSubnetsNeedNAT(protocol)
×
98
                if err != nil {
×
99
                        klog.Errorf("get need nat subnets failed, %+v", err)
×
100
                        return err
×
101
                }
×
102
                subnetsDistributedGateway, err := c.getSubnetsDistributedGateway(protocol)
×
103
                if err != nil {
×
104
                        klog.Errorf("failed to get subnets with centralized gateway: %v", err)
×
105
                        return err
×
106
                }
×
107
                otherNode, err := c.getOtherNodes(protocol)
×
108
                if err != nil {
×
109
                        klog.Errorf("failed to get node, %+v", err)
×
110
                        return err
×
111
                }
×
112
                c.ipsets[protocol].AddOrReplaceIPSet(ipsets.IPSetMetadata{
×
113
                        MaxSize: 1048576,
×
114
                        SetID:   ServiceSet,
×
115
                        Type:    ipsets.IPSetTypeHashNet,
×
116
                }, services)
×
117
                c.ipsets[protocol].AddOrReplaceIPSet(ipsets.IPSetMetadata{
×
118
                        MaxSize: 1048576,
×
119
                        SetID:   SubnetSet,
×
120
                        Type:    ipsets.IPSetTypeHashNet,
×
121
                }, subnets)
×
122
                c.ipsets[protocol].AddOrReplaceIPSet(ipsets.IPSetMetadata{
×
123
                        MaxSize: 1048576,
×
124
                        SetID:   LocalPodSet,
×
125
                        Type:    ipsets.IPSetTypeHashIP,
×
126
                }, nil)
×
127
                c.ipsets[protocol].AddOrReplaceIPSet(ipsets.IPSetMetadata{
×
128
                        MaxSize: 1048576,
×
129
                        SetID:   SubnetNatSet,
×
130
                        Type:    ipsets.IPSetTypeHashNet,
×
131
                }, subnetsNeedNat)
×
132
                c.ipsets[protocol].AddOrReplaceIPSet(ipsets.IPSetMetadata{
×
133
                        MaxSize: 1048576,
×
134
                        SetID:   SubnetDistributedGwSet,
×
135
                        Type:    ipsets.IPSetTypeHashNet,
×
136
                }, subnetsDistributedGateway)
×
137
                c.ipsets[protocol].AddOrReplaceIPSet(ipsets.IPSetMetadata{
×
138
                        MaxSize: 1048576,
×
139
                        SetID:   OtherNodeSet,
×
140
                        Type:    ipsets.IPSetTypeHashNet,
×
141
                }, otherNode)
×
142
                c.reconcileNatOutGoingPolicyIPset(protocol)
×
143
                c.ipsets[protocol].ApplyUpdates()
×
144
        }
145
        return nil
×
146
}
147

148
func (c *Controller) gcIPSet() {
×
149
        protocols := make([]string, 2)
×
150
        if c.protocol == kubeovnv1.ProtocolDual {
×
151
                protocols[0] = kubeovnv1.ProtocolIPv4
×
152
                protocols[1] = kubeovnv1.ProtocolIPv6
×
153
        } else {
×
154
                protocols[0] = c.protocol
×
155
        }
×
156

157
        for _, protocol := range protocols {
×
158
                if c.ipsets[protocol] == nil {
×
159
                        continue
×
160
                }
161
                c.ipsets[protocol].ApplyDeletions()
×
162
        }
163
}
164

165
func (c *Controller) addNatOutGoingPolicyRuleIPset(rule kubeovnv1.NatOutgoingPolicyRuleStatus, protocol string) {
×
166
        if rule.Match.SrcIPs != "" {
×
167
                ipsetName := getNatOutGoingPolicyRuleIPSetName(rule.RuleID, "src", "", false)
×
168
                c.ipsets[protocol].AddOrReplaceIPSet(ipsets.IPSetMetadata{
×
169
                        MaxSize: 1048576,
×
170
                        SetID:   ipsetName,
×
171
                        Type:    ipsets.IPSetTypeHashNet,
×
172
                }, strings.Split(rule.Match.SrcIPs, ","))
×
173
        }
×
174

175
        if rule.Match.DstIPs != "" {
×
176
                ipsetName := getNatOutGoingPolicyRuleIPSetName(rule.RuleID, "dst", "", false)
×
177
                c.ipsets[protocol].AddOrReplaceIPSet(ipsets.IPSetMetadata{
×
178
                        MaxSize: 1048576,
×
179
                        SetID:   ipsetName,
×
180
                        Type:    ipsets.IPSetTypeHashNet,
×
181
                }, strings.Split(rule.Match.DstIPs, ","))
×
182
        }
×
183
}
184

185
func (c *Controller) removeNatOutGoingPolicyRuleIPset(protocol string, natPolicyRuleIDs *strset.Set) {
×
186
        sets, err := c.k8sipsets.ListSets()
×
187
        if err != nil {
×
188
                klog.Errorf("failed to list ipsets: %v", err)
×
189
                return
×
190
        }
×
191
        for _, set := range sets {
×
192
                if isNatOutGoingPolicyRuleIPSet(set) {
×
193
                        ruleID, _ := getNatOutGoingPolicyRuleIPSetItem(set)
×
194
                        if !natPolicyRuleIDs.Has(ruleID) {
×
195
                                c.ipsets[protocol].RemoveIPSet(formatIPsetUnPrefix(set))
×
196
                        }
×
197
                }
198
        }
199
}
200

201
func (c *Controller) reconcileNatOutGoingPolicyIPset(protocol string) {
×
202
        subnets, err := c.getSubnetsNatOutGoingPolicy(protocol)
×
203
        if err != nil {
×
204
                klog.Errorf("failed to get subnets with NAT outgoing policy rule: %v", err)
×
205
                return
×
206
        }
×
207

208
        subnetCidrs := make([]string, 0, len(subnets))
×
209
        natPolicyRuleIDs := strset.New()
×
210
        for _, subnet := range subnets {
×
211
                cidrBlock, err := getCidrByProtocol(subnet.Spec.CIDRBlock, protocol)
×
212
                if err != nil {
×
213
                        klog.Errorf("failed to get subnet %s CIDR block by protocol: %v", subnet.Name, err)
×
214
                        continue
×
215
                }
216
                if cidrBlock != "" {
×
217
                        subnetCidrs = append(subnetCidrs, cidrBlock)
×
218
                }
×
219
                for _, rule := range subnet.Status.NatOutgoingPolicyRules {
×
220
                        if rule.RuleID == "" {
×
221
                                klog.Errorf("unexpected empty ID for NAT outgoing rule %q of subnet %s", rule.NatOutgoingPolicyRule, subnet.Name)
×
222
                                continue
×
223
                        }
224
                        natPolicyRuleIDs.Add(rule.RuleID)
×
225
                        c.addNatOutGoingPolicyRuleIPset(rule, protocol)
×
226
                }
227
        }
228

229
        c.ipsets[protocol].AddOrReplaceIPSet(ipsets.IPSetMetadata{
×
230
                MaxSize: 1048576,
×
231
                SetID:   NatOutGoingPolicySubnetSet,
×
232
                Type:    ipsets.IPSetTypeHashNet,
×
233
        }, subnetCidrs)
×
234

×
235
        c.removeNatOutGoingPolicyRuleIPset(protocol, natPolicyRuleIDs)
×
236
}
237

238
func (c *Controller) setPolicyRouting() error {
×
239
        protocols := make([]string, 2)
×
240
        if c.protocol == kubeovnv1.ProtocolDual {
×
241
                protocols[0] = kubeovnv1.ProtocolIPv4
×
242
                protocols[1] = kubeovnv1.ProtocolIPv6
×
243
        } else {
×
244
                protocols[0] = c.protocol
×
245
        }
×
246

247
        for _, protocol := range protocols {
×
248
                if c.ipsets[protocol] == nil {
×
249
                        continue
×
250
                }
251

252
                localPodIPs, err := c.getLocalPodIPsNeedPR(protocol)
×
253
                if err != nil {
×
254
                        klog.Errorf("failed to get local pod ips failed: %+v", err)
×
255
                        return err
×
256
                }
×
257
                subnetsNeedPR, err := c.getSubnetsNeedPR(protocol)
×
258
                if err != nil {
×
259
                        klog.Errorf("failed to get subnets that need policy routing: %+v", err)
×
260
                        return err
×
261
                }
×
262

263
                family, err := util.ProtocolToFamily(protocol)
×
264
                if err != nil {
×
265
                        klog.Error(err)
×
266
                        return err
×
267
                }
×
268

269
                for meta, ips := range localPodIPs {
×
270
                        if err = c.addPolicyRouting(family, meta.gateway, meta.priority, meta.tableID, ips...); err != nil {
×
271
                                klog.Errorf("failed to add policy routing for local pods: %+v", err)
×
272
                                return err
×
273
                        }
×
274
                }
275
                for meta, cidr := range subnetsNeedPR {
×
276
                        if err = c.addPolicyRouting(family, meta.gateway, meta.priority, meta.tableID, cidr); err != nil {
×
277
                                klog.Errorf("failed to add policy routing for subnet: %+v", err)
×
278
                                return err
×
279
                        }
×
280
                }
281
        }
282

283
        return nil
×
284
}
285

286
func (c *Controller) addPodPolicyRouting(podProtocol, externalEgressGateway string, priority, tableID uint32, ips []string) error {
×
287
        egw := strings.Split(externalEgressGateway, ",")
×
288
        prMetas := make([]policyRouteMeta, 0, 2)
×
289
        if len(egw) == 1 {
×
290
                family, _ := util.ProtocolToFamily(util.CheckProtocol(egw[0]))
×
291
                if family == netlink.FAMILY_V4 || podProtocol != kubeovnv1.ProtocolDual {
×
292
                        prMetas = append(prMetas, policyRouteMeta{family: family, source: ips[0], gateway: egw[0]})
×
293
                } else {
×
294
                        prMetas = append(prMetas, policyRouteMeta{family: family, source: ips[1], gateway: egw[0]})
×
295
                }
×
296
        } else {
×
297
                prMetas = append(prMetas, policyRouteMeta{family: netlink.FAMILY_V4, source: ips[0], gateway: egw[0]})
×
298
                prMetas = append(prMetas, policyRouteMeta{family: netlink.FAMILY_V6, source: ips[1], gateway: egw[1]})
×
299
        }
×
300

301
        for _, meta := range prMetas {
×
302
                if err := c.addPolicyRouting(meta.family, meta.gateway, priority, tableID, meta.source); err != nil {
×
303
                        klog.Errorf("failed to add policy routing for pod: %+v", err)
×
304
                        return err
×
305
                }
×
306
        }
307

308
        return nil
×
309
}
310

311
func (c *Controller) deletePodPolicyRouting(podProtocol, externalEgressGateway string, priority, tableID uint32, ips []string) error {
×
312
        egw := strings.Split(externalEgressGateway, ",")
×
313
        prMetas := make([]policyRouteMeta, 0, 2)
×
314
        if len(egw) == 1 {
×
315
                family, _ := util.ProtocolToFamily(util.CheckProtocol(egw[0]))
×
316
                if family == netlink.FAMILY_V4 || podProtocol != kubeovnv1.ProtocolDual {
×
317
                        prMetas = append(prMetas, policyRouteMeta{family: family, source: ips[0], gateway: egw[0]})
×
318
                } else {
×
319
                        prMetas = append(prMetas, policyRouteMeta{family: family, source: ips[1], gateway: egw[0]})
×
320
                }
×
321
        } else {
×
322
                prMetas = append(prMetas, policyRouteMeta{family: netlink.FAMILY_V4, source: ips[0], gateway: egw[0]})
×
323
                prMetas = append(prMetas, policyRouteMeta{family: netlink.FAMILY_V6, source: ips[1], gateway: egw[1]})
×
324
        }
×
325

326
        for _, meta := range prMetas {
×
327
                if err := c.deletePolicyRouting(meta.family, meta.gateway, priority, tableID, meta.source); err != nil {
×
328
                        klog.Errorf("failed to delete policy routing for pod: %+v", err)
×
329
                        return err
×
330
                }
×
331
        }
332

333
        return nil
×
334
}
335

336
func (c *Controller) addPolicyRouting(family int, gateway string, priority, tableID uint32, ips ...string) error {
×
337
        route := &netlink.Route{
×
338
                Protocol: netlink.RouteProtocol(syscall.RTPROT_STATIC),
×
339
                Gw:       net.ParseIP(gateway),
×
340
                Table:    int(tableID),
×
341
        }
×
342
        if err := netlink.RouteReplace(route); err != nil && !errors.Is(err, syscall.EEXIST) {
×
343
                err = fmt.Errorf("failed to replace route in table %d: %w", tableID, err)
×
344
                klog.Error(err)
×
345
                return err
×
346
        }
×
347

348
        maskBits := 32
×
349
        if family == netlink.FAMILY_V6 {
×
350
                maskBits = 128
×
351
        }
×
352

353
        rule := netlink.NewRule()
×
354
        rule.Family = family
×
355
        rule.Table = int(tableID)
×
356
        rule.Priority = int(priority)
×
357
        mask := net.CIDRMask(maskBits, maskBits)
×
358

×
359
        for _, ip := range ips {
×
360
                if strings.ContainsRune(ip, '/') {
×
361
                        var err error
×
362
                        if rule.Src, err = netlink.ParseIPNet(ip); err != nil {
×
363
                                klog.Errorf("unexpected CIDR: %s", ip)
×
364
                                err = fmt.Errorf("failed to add route in table %d: %w", tableID, err)
×
365
                                klog.Error(err)
×
366
                                return err
×
367
                        }
×
368
                } else {
×
369
                        rule.Src = &net.IPNet{IP: net.ParseIP(ip), Mask: mask}
×
370
                }
×
371

372
                if err := netlink.RuleAdd(rule); err != nil && !errors.Is(err, syscall.EEXIST) {
×
373
                        err = fmt.Errorf("failed to add network rule: %w", err)
×
374
                        klog.Error(err)
×
375
                        return err
×
376
                }
×
377
        }
378

379
        return nil
×
380
}
381

382
func (c *Controller) deletePolicyRouting(family int, _ string, priority, tableID uint32, ips ...string) error {
×
383
        maskBits := 32
×
384
        if family == netlink.FAMILY_V6 {
×
385
                maskBits = 128
×
386
        }
×
387

388
        rule := netlink.NewRule()
×
389
        rule.Family = family
×
390
        rule.Table = int(tableID)
×
391
        rule.Priority = int(priority)
×
392
        mask := net.CIDRMask(maskBits, maskBits)
×
393

×
394
        for _, ip := range ips {
×
395
                if strings.ContainsRune(ip, '/') {
×
396
                        var err error
×
397
                        if rule.Src, err = netlink.ParseIPNet(ip); err != nil {
×
398
                                klog.Errorf("unexpected CIDR: %s", ip)
×
399
                                err = fmt.Errorf("failed to delete route in table %d: %w", tableID, err)
×
400
                                klog.Error(err)
×
401
                                return err
×
402
                        }
×
403
                } else {
×
404
                        rule.Src = &net.IPNet{IP: net.ParseIP(ip), Mask: mask}
×
405
                }
×
406

407
                if err := netlink.RuleDel(rule); err != nil && !errors.Is(err, syscall.ENOENT) {
×
408
                        err = fmt.Errorf("failed to delete network rule: %w", err)
×
409
                        klog.Error(err)
×
410
                        return err
×
411
                }
×
412
        }
413

414
        // routes may be used by other Pods so delete rules only
415
        return nil
×
416
}
417

418
// findRulePositions locates all occurrences of a rule in the specified table and chain,
419
// searching from lowest to highest priority (bottom to top).
420
// Returns all matching position indices, or an empty slice if none found.
421
func findRulePositions(ipt *iptables.IPTables, rule util.IPTableRule) ([]int, error) {
×
422
        rules, err := ipt.List(rule.Table, rule.Chain)
×
423
        if err != nil {
×
424
                return nil, fmt.Errorf("failed to list iptables rules: %w", err)
×
425
        }
×
426

427
        positions := make([]int, 0)
×
428

×
429
        // Start from the end of the list (lowest priority) and go up to 1 (but skip index 0)
×
430
        for i := len(rules) - 1; i >= 1; i-- {
×
431
                ruleSpec := util.DoubleQuotedFields(rules[i])
×
432
                if len(ruleSpec) < 3 {
×
433
                        continue
×
434
                }
435

436
                if slices.Equal(ruleSpec[2:], rule.Rule) {
×
437
                        positions = append(positions, i)
×
438
                }
×
439
        }
440

441
        return positions, nil
×
442
}
443

444
// ensureNatPreroutingRulePosition ensures that the nat prerouting rule has a higher priority than the kube-proxy rule.
445
func ensureNatPreroutingRulePosition(ipt *iptables.IPTables, rule util.IPTableRule) error {
×
446
        kubeProxyRule := util.IPTableRule{
×
447
                Table: "nat",
×
448
                Chain: "PREROUTING",
×
449
                Rule:  []string{"-m", "comment", "--comment", "kubernetes service portals", "-j", "KUBE-SERVICES"},
×
450
        }
×
451
        kubeProxyPosList, err := findRulePositions(ipt, kubeProxyRule)
×
452
        if err != nil {
×
453
                return fmt.Errorf("failed to find kube-proxy rule position: %w", err)
×
454
        }
×
455

456
        insertPosition := 1
×
457
        if len(kubeProxyPosList) > 0 {
×
458
                insertPosition = kubeProxyPosList[len(kubeProxyPosList)-1]
×
459
        }
×
460

461
        // Check if the rule already exists at a higher priority than the kube-proxy rule
462
        existingNatPreroutingPosList, err := findRulePositions(ipt, rule)
×
463
        if err != nil {
×
464
                return fmt.Errorf("failed to find nat prerouting rule position: %w", err)
×
465
        }
×
466
        if len(existingNatPreroutingPosList) > 0 {
×
467
                pos := existingNatPreroutingPosList[len(existingNatPreroutingPosList)-1]
×
468
                if pos <= insertPosition {
×
469
                        klog.V(5).Infof("nat prerouting rule already exists at position %d", pos)
×
470
                        return nil
×
471
                }
×
472
        }
473

474
        klog.Infof("inserting nat prerouting rule %q at position %d", rule.Rule, insertPosition)
×
475
        return ipt.Insert(rule.Table, rule.Chain, insertPosition, rule.Rule...)
×
476
}
477

478
// ensureNatPreroutingRuleNoDuplicate ensures that there are no duplicate nat prerouting rules.
479
func ensureNatPreroutingRuleNoDuplicate(ipt *iptables.IPTables, rule util.IPTableRule) error {
×
480
        existingNatPreroutingPosList, err := findRulePositions(ipt, rule)
×
481
        if err != nil {
×
482
                return fmt.Errorf("failed to find nat prerouting rule position: %w", err)
×
483
        }
×
484
        if len(existingNatPreroutingPosList) == 0 {
×
485
                klog.Warningf("nat prerouting rule not found, skipping duplicate check")
×
486
                return nil
×
487
        }
×
488

489
        // Delete all but the top priority (highest) rule
490
        // NOTE: There's a race condition here as iptables rules could be modified by other processes
491
        // between our findRulePositions call and the Delete operations below. Since iptables lacks
492
        // a transaction mechanism, rule positions might change, potentially causing us to delete
493
        // incorrect rules. This is an accepted limitation of the current implementation.
494
        for _, pos := range existingNatPreroutingPosList[:len(existingNatPreroutingPosList)-1] {
×
495
                klog.Infof("deleting duplicate nat prerouting rule at position %d", pos)
×
496
                if err := ipt.Delete(rule.Table, rule.Chain, strconv.Itoa(pos)); err != nil {
×
497
                        return fmt.Errorf("failed to delete duplicate nat prerouting rule: %w", err)
×
498
                }
×
499
        }
500

501
        return nil
×
502
}
503

504
// ensureNatPreroutingRule ensures that the nat prerouting rule is in the right position and no duplicate exists.
505
func ensureNatPreroutingRule(ipt *iptables.IPTables, rule util.IPTableRule) error {
×
506
        klog.V(3).Infof("ensure nat prerouting rule %q", rule.Rule)
×
507

×
508
        if err := ensureNatPreroutingRulePosition(ipt, rule); err != nil {
×
509
                return fmt.Errorf("failed to ensure nat prerouting rule position: %w", err)
×
510
        }
×
511

512
        if err := ensureNatPreroutingRuleNoDuplicate(ipt, rule); err != nil {
×
513
                return fmt.Errorf("failed to ensure nat prerouting rule no duplicate: %w", err)
×
514
        }
×
515

516
        return nil
×
517
}
518

519
func (c *Controller) createIptablesRule(ipt *iptables.IPTables, rule util.IPTableRule) error {
×
520
        exists, err := ipt.Exists(rule.Table, rule.Chain, rule.Rule...)
×
521
        if err != nil {
×
522
                klog.Errorf("failed to check iptables rule existence: %v", err)
×
523
                return err
×
524
        }
×
525

526
        s := strings.Join(rule.Rule, " ")
×
527
        if exists {
×
528
                if rule.Table == NAT && rule.Chain == Prerouting {
×
529
                        return ensureNatPreroutingRule(ipt, rule)
×
530
                }
×
531
                return nil
×
532
        }
533

534
        klog.Infof("creating iptables rule in table %s chain %s at position %d: %q", rule.Table, rule.Chain, 1, s)
×
535
        if err = ipt.Insert(rule.Table, rule.Chain, 1, rule.Rule...); err != nil {
×
536
                klog.Errorf(`failed to insert iptables rule "%s": %v`, s, err)
×
537
                return err
×
538
        }
×
539

540
        return nil
×
541
}
542

543
func (c *Controller) updateIptablesChain(ipt *iptables.IPTables, table, chain, parent string, rules []util.IPTableRule) error {
×
544
        ok, err := ipt.ChainExists(table, chain)
×
545
        if err != nil {
×
546
                klog.Errorf("failed to check existence of iptables chain %s in table %s: %v", chain, table, err)
×
547
                return err
×
548
        }
×
549
        if !ok {
×
550
                if err = ipt.NewChain(table, chain); err != nil {
×
551
                        klog.Errorf("failed to create iptables chain %s in table %s: %v", chain, table, err)
×
552
                        return err
×
553
                }
×
554
                klog.Infof("created iptables chain %s in table %s", chain, table)
×
555
        }
556
        if parent != "" {
×
557
                comment := fmt.Sprintf("kube-ovn %s rules", strings.ToLower(parent))
×
558
                rule := util.IPTableRule{
×
559
                        Table: table,
×
560
                        Chain: parent,
×
561
                        Rule:  []string{"-m", "comment", "--comment", comment, "-j", chain},
×
562
                }
×
563
                if err = c.createIptablesRule(ipt, rule); err != nil {
×
564
                        klog.Errorf("failed to create iptables rule: %v", err)
×
565
                        return err
×
566
                }
×
567
        }
568

569
        // list existing rules
570
        ruleList, err := ipt.List(table, chain)
×
571
        if err != nil {
×
572
                klog.Errorf("failed to list iptables rules in chain %s/%s: %v", table, chain, err)
×
573
                return err
×
574
        }
×
575

576
        // filter the heading default chain policy: -N OVN-POSTROUTING
577
        ruleList = ruleList[1:]
×
578

×
579
        // trim prefix: "-A OVN-POSTROUTING "
×
580
        prefixLen := 4 + len(chain)
×
581
        existingRules := make([][]string, 0, len(ruleList))
×
582
        for _, r := range ruleList {
×
583
                existingRules = append(existingRules, util.DoubleQuotedFields(r[prefixLen:]))
×
584
        }
×
585

586
        var added int
×
587
        for i, rule := range rules {
×
588
                if i-added < len(existingRules) && slices.Equal(existingRules[i-added], rule.Rule) {
×
589
                        klog.V(5).Infof("iptables rule %v already exists", rule.Rule)
×
590
                        continue
×
591
                }
592
                klog.Infof("creating iptables rule in table %s chain %s at position %d: %q", table, chain, i+1, strings.Join(rule.Rule, " "))
×
593
                if err = ipt.Insert(table, chain, i+1, rule.Rule...); err != nil {
×
594
                        klog.Errorf(`failed to insert iptables rule %v: %v`, rule.Rule, err)
×
595
                        return err
×
596
                }
×
597
                added++
×
598
        }
599
        for i := len(existingRules) - 1; i >= len(rules)-added; i-- {
×
600
                if err = ipt.Delete(table, chain, strconv.Itoa(i+added+1)); err != nil {
×
601
                        klog.Errorf(`failed to delete iptables rule %v: %v`, existingRules[i], err)
×
602
                        return err
×
603
                }
×
604
                klog.Infof("deleted iptables rule in table %s chain %s: %q", table, chain, strings.Join(existingRules[i], " "))
×
605
        }
606

607
        return nil
×
608
}
609

610
func (c *Controller) setIptables() error {
×
611
        klog.V(3).Infoln("start to set up iptables")
×
612
        node, err := c.nodesLister.Get(c.config.NodeName)
×
613
        if err != nil {
×
614
                klog.Errorf("failed to get node %s, %v", c.config.NodeName, err)
×
615
                return err
×
616
        }
×
617

618
        nodeIPv4, nodeIPv6 := util.GetNodeInternalIP(*node)
×
619
        nodeIPs := map[string]string{
×
620
                kubeovnv1.ProtocolIPv4: nodeIPv4,
×
621
                kubeovnv1.ProtocolIPv6: nodeIPv6,
×
622
        }
×
623

×
624
        centralGwNatIPs, err := c.getEgressNatIPByNode(c.config.NodeName)
×
625
        if err != nil {
×
626
                klog.Errorf("failed to get centralized subnets nat ips on node %s, %v", c.config.NodeName, err)
×
627
                return err
×
628
        }
×
629
        klog.V(3).Infof("centralized subnets nat ips %v", centralGwNatIPs)
×
630

×
631
        var (
×
632
                v4Rules = []util.IPTableRule{
×
633
                        // mark packets from pod to service
×
634
                        {Table: NAT, Chain: OvnPrerouting, Rule: strings.Fields(`-i ` + util.NodeNic + ` -m set --match-set ovn40subnets src -m set --match-set ovn40services dst -j MARK --set-xmark 0x4000/0x4000`)},
×
635
                        // nat packets marked by kube-proxy or kube-ovn
×
636
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m mark --mark 0x4000/0x4000 -j ` + OvnMasquerade)},
×
637
                        // nat service traffic
×
638
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m set --match-set ovn40subnets src -m set --match-set ovn40subnets dst -j ` + OvnMasquerade)},
×
639
                        // do not nat node port service traffic with external traffic policy set to local
×
640
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m mark --mark 0x80000/0x80000 -m set --match-set ovn40subnets-distributed-gw dst -j RETURN`)},
×
641
                        // nat node port service traffic with external traffic policy set to local for subnets with centralized gateway
×
642
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m mark --mark 0x80000/0x80000 -j ` + OvnMasquerade)},
×
643
                        // do not nat reply packets in direct routing
×
644
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-p tcp -m tcp --tcp-flags SYN NONE -m conntrack --ctstate NEW -j RETURN`)},
×
645
                        // do not nat route traffic
×
646
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m set ! --match-set ovn40subnets src -m set ! --match-set ovn40other-node src -m set --match-set ovn40subnets-nat dst -j RETURN`)},
×
647
                        // nat outgoing policy rules
×
648
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields("-m set --match-set ovn40subnets-nat-policy src -m set ! --match-set ovn40subnets dst -j " + OvnNatOutGoingPolicy)},
×
649
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(fmt.Sprintf(`-m mark --mark %s -j %s`, OnOutGoingNatMark, OvnMasquerade))},
×
650
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(fmt.Sprintf(`-m mark --mark %s -j RETURN`, OnOutGoingForwardMark))},
×
651
                        // default nat outgoing rules
×
652
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m set --match-set ovn40subnets-nat src -m set ! --match-set ovn40subnets dst -j ` + OvnMasquerade)},
×
653
                        // clear mark
×
654
                        {Table: NAT, Chain: OvnMasquerade, Rule: strings.Fields(`-j MARK --set-xmark 0x0/0xffffffff`)},
×
655
                        // do masquerade
×
656
                        {Table: NAT, Chain: OvnMasquerade, Rule: strings.Fields(`-j MASQUERADE`)},
×
657
                        // Input Accept
×
658
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn40subnets src -j ACCEPT`)},
×
659
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn40subnets dst -j ACCEPT`)},
×
660
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn40services src -j ACCEPT`)},
×
661
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn40services dst -j ACCEPT`)},
×
662
                        // Forward Accept
×
663
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn40subnets src -j ACCEPT`)},
×
664
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn40subnets dst -j ACCEPT`)},
×
665
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn40services src -j ACCEPT`)},
×
666
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn40services dst -j ACCEPT`)},
×
667
                        // Output unmark to bypass kernel nat checksum issue https://github.com/flannel-io/flannel/issues/1279
×
668
                        {Table: "filter", Chain: "OUTPUT", Rule: strings.Fields(`-p udp -m udp --dport 6081 -j MARK --set-xmark 0x0`)},
×
669
                        {Table: "filter", Chain: "OUTPUT", Rule: strings.Fields(`-p udp -m udp --dport 4789 -j MARK --set-xmark 0x0`)},
×
670
                        // Drop invalid rst
×
671
                        {Table: MANGLE, Chain: OvnPostrouting, Rule: strings.Fields(`-p tcp -m set --match-set ovn40subnets src -m tcp --tcp-flags RST RST -m state --state INVALID -j DROP`)},
×
672
                }
×
673
                v6Rules = []util.IPTableRule{
×
674
                        // mark packets from pod to service
×
675
                        {Table: NAT, Chain: OvnPrerouting, Rule: strings.Fields(`-i ` + util.NodeNic + ` -m set --match-set ovn60subnets src -m set --match-set ovn60services dst -j MARK --set-xmark 0x4000/0x4000`)},
×
676
                        // nat packets marked by kube-proxy or kube-ovn
×
677
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m mark --mark 0x4000/0x4000 -j ` + OvnMasquerade)},
×
678
                        // nat service traffic
×
679
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m set --match-set ovn60subnets src -m set --match-set ovn60subnets dst -j ` + OvnMasquerade)},
×
680
                        // do not nat node port service traffic with external traffic policy set to local
×
681
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m mark --mark 0x80000/0x80000 -m set --match-set ovn60subnets-distributed-gw dst -j RETURN`)},
×
682
                        // nat node port service traffic with external traffic policy set to local for subnets with centralized gateway
×
683
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m mark --mark 0x80000/0x80000 -j ` + OvnMasquerade)},
×
684
                        // do not nat reply packets in direct routing
×
685
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-p tcp -m tcp --tcp-flags SYN NONE -m conntrack --ctstate NEW -j RETURN`)},
×
686
                        // do not nat route traffic
×
687
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m set ! --match-set ovn60subnets src -m set ! --match-set ovn60other-node src -m set --match-set ovn60subnets-nat dst -j RETURN`)},
×
688
                        // nat outgoing policy rules
×
689
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields("-m set --match-set ovn60subnets-nat-policy src -m set ! --match-set ovn60subnets dst -j " + OvnNatOutGoingPolicy)},
×
690
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(fmt.Sprintf(`-m mark --mark %s -j %s`, OnOutGoingNatMark, OvnMasquerade))},
×
691
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(fmt.Sprintf(`-m mark --mark %s -j RETURN`, OnOutGoingForwardMark))},
×
692
                        {Table: NAT, Chain: OvnPostrouting, Rule: strings.Fields(`-m set --match-set ovn60subnets-nat src -m set ! --match-set ovn60subnets dst -j ` + OvnMasquerade)},
×
693
                        // clear mark
×
694
                        {Table: NAT, Chain: OvnMasquerade, Rule: strings.Fields(`-j MARK --set-xmark 0x0/0xffffffff`)},
×
695
                        // do masquerade
×
696
                        {Table: NAT, Chain: OvnMasquerade, Rule: strings.Fields(`-j MASQUERADE`)},
×
697
                        // Input Accept
×
698
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn60subnets src -j ACCEPT`)},
×
699
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn60subnets dst -j ACCEPT`)},
×
700
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn60services src -j ACCEPT`)},
×
701
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn60services dst -j ACCEPT`)},
×
702
                        // Forward Accept
×
703
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn60subnets src -j ACCEPT`)},
×
704
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn60subnets dst -j ACCEPT`)},
×
705
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn60services src -j ACCEPT`)},
×
706
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn60services dst -j ACCEPT`)},
×
707
                        // Output unmark to bypass kernel nat checksum issue https://github.com/flannel-io/flannel/issues/1279
×
708
                        {Table: "filter", Chain: "OUTPUT", Rule: strings.Fields(`-p udp -m udp --dport 6081 -j MARK --set-xmark 0x0`)},
×
709
                        {Table: "filter", Chain: "OUTPUT", Rule: strings.Fields(`-p udp -m udp --dport 4789 -j MARK --set-xmark 0x0`)},
×
710
                        // Drop invalid rst
×
711
                        {Table: MANGLE, Chain: OvnPostrouting, Rule: strings.Fields(`-p tcp -m set --match-set ovn60subnets src -m tcp --tcp-flags RST RST -m state --state INVALID -j DROP`)},
×
712
                }
×
713
        )
×
714
        protocols := make([]string, 0, 2)
×
715
        if c.protocol == kubeovnv1.ProtocolDual {
×
716
                protocols = append(protocols, kubeovnv1.ProtocolIPv4, kubeovnv1.ProtocolIPv6)
×
717
        } else {
×
718
                protocols = append(protocols, c.protocol)
×
719
        }
×
720

721
        for _, protocol := range protocols {
×
722
                ipt := c.iptables[protocol]
×
723
                if ipt == nil {
×
724
                        continue
×
725
                }
726

727
                var kubeProxyIpsetProtocol, matchset, svcMatchset, nodeMatchSet string
×
UNCOV
728
                var obsoleteRules, iptablesRules []util.IPTableRule
×
729
                if protocol == kubeovnv1.ProtocolIPv4 {
×
730
                        iptablesRules = v4Rules
×
731
                        matchset, svcMatchset, nodeMatchSet = "ovn40subnets", "ovn40services", "ovn40"+OtherNodeSet
×
732
                } else {
×
UNCOV
733
                        iptablesRules = v6Rules
×
UNCOV
734
                        kubeProxyIpsetProtocol, matchset, svcMatchset, nodeMatchSet = "6-", "ovn60subnets", "ovn60services", "ovn60"+OtherNodeSet
×
735
                }
×
736

737
                ipset := fmt.Sprintf("KUBE-%sCLUSTER-IP", kubeProxyIpsetProtocol)
×
738
                ipsetExists, err := c.ipsetExists(ipset)
×
739
                if err != nil {
×
740
                        klog.Errorf("failed to check existence of ipset %s: %v", ipset, err)
×
741
                        return err
×
742
                }
×
NEW
743
                if ipsetExists && !c.config.DisableSvcIptablesRule {
×
UNCOV
744
                        iptablesRules[0].Rule = strings.Fields(fmt.Sprintf(`-i %s -m set --match-set %s src -m set --match-set %s dst,dst -j MARK --set-xmark 0x4000/0x4000`, util.NodeNic, matchset, ipset))
×
745
                        rejectRule := strings.Fields(fmt.Sprintf(`-p tcp -m mark ! --mark 0x4000/0x4000 -m set --match-set %s dst -m conntrack --ctstate NEW -j REJECT`, svcMatchset))
×
746
                        iptablesRules = append(iptablesRules,
×
747
                                util.IPTableRule{Table: "filter", Chain: "INPUT", Rule: rejectRule},
×
748
                                util.IPTableRule{Table: "filter", Chain: "OUTPUT", Rule: rejectRule},
×
749
                        )
×
NEW
750
                        // TODO: remove obsoleteRejectRule cleanup after all clusters have upgraded past v1.12
×
NEW
751
                        obsoleteRejectRule := strings.Fields(fmt.Sprintf(`-m mark ! --mark 0x4000/0x4000 -m set --match-set %s dst -m conntrack --ctstate NEW -j REJECT`, svcMatchset))
×
752
                        obsoleteRejectRules := []util.IPTableRule{
×
753
                                {Table: "filter", Chain: "INPUT", Rule: obsoleteRejectRule},
×
754
                                {Table: "filter", Chain: "OUTPUT", Rule: obsoleteRejectRule},
×
755
                        }
×
756
                        for _, rule := range obsoleteRejectRules {
×
757
                                if err = deleteIptablesRule(ipt, rule); err != nil {
×
758
                                        klog.Errorf("failed to delete obsolete iptables rule %v: %v", rule, err)
×
759
                                        return err
×
760
                                }
×
761
                        }
762
                }
763

764
                if nodeIP := nodeIPs[protocol]; nodeIP != "" {
×
765
                        obsoleteRules = []util.IPTableRule{
×
766
                                {Table: NAT, Chain: Postrouting, Rule: strings.Fields(fmt.Sprintf(`! -s %s -m set --match-set %s dst -j MASQUERADE`, nodeIP, matchset))},
×
767
                                {Table: NAT, Chain: Postrouting, Rule: strings.Fields(fmt.Sprintf(`! -s %s -m mark --mark 0x4000/0x4000 -j MASQUERADE`, nodeIP))},
×
UNCOV
768
                                {Table: NAT, Chain: Postrouting, Rule: strings.Fields(fmt.Sprintf(`! -s %s -m set ! --match-set %s src -m set --match-set %s dst -j MASQUERADE`, nodeIP, matchset, matchset))},
×
UNCOV
769
                        }
×
NEW
770
                }
×
771

NEW
772
                if c.config.DisableSvcIptablesRule {
×
NEW
773
                        // Filter out service-related rules from iptablesRules and clean up
×
NEW
774
                        // any existing service rules in standard chains.
×
NEW
775
                        // Rules in custom chains (OvnPrerouting, OvnPostrouting) are automatically
×
NEW
776
                        // reconciled by updateIptablesChain.
×
NEW
777

×
NEW
778
                        // Clean up reject rules for unmatched service traffic
×
NEW
779
                        rejectRule := strings.Fields(fmt.Sprintf(`-p tcp -m mark ! --mark 0x4000/0x4000 -m set --match-set %s dst -m conntrack --ctstate NEW -j REJECT`, svcMatchset))
×
NEW
780
                        obsoleteRejectRule := strings.Fields(fmt.Sprintf(`-m mark ! --mark 0x4000/0x4000 -m set --match-set %s dst -m conntrack --ctstate NEW -j REJECT`, svcMatchset))
×
NEW
781
                        rejectRulesToClean := []util.IPTableRule{
×
NEW
782
                                {Table: "filter", Chain: "INPUT", Rule: rejectRule},
×
NEW
783
                                {Table: "filter", Chain: "OUTPUT", Rule: rejectRule},
×
NEW
784
                                {Table: "filter", Chain: "INPUT", Rule: obsoleteRejectRule},
×
NEW
785
                                {Table: "filter", Chain: "OUTPUT", Rule: obsoleteRejectRule},
×
NEW
786
                        }
×
NEW
787
                        for _, rule := range rejectRulesToClean {
×
NEW
788
                                if err = deleteIptablesRule(ipt, rule); err != nil {
×
NEW
789
                                        klog.Errorf("failed to delete service reject iptables rule %v: %v", rule, err)
×
NEW
790
                                        return err
×
NEW
791
                                }
×
792
                        }
793

NEW
794
                        var filteredRules []util.IPTableRule
×
NEW
795
                        for _, rule := range iptablesRules {
×
NEW
796
                                ruleStr := strings.Join(rule.Rule, " ")
×
NEW
797
                                // Skip MARK 0x4000 rule in OvnPrerouting (pod → service marking)
×
NEW
798
                                if rule.Table == NAT && rule.Chain == OvnPrerouting && strings.Contains(ruleStr, "0x4000/0x4000") {
×
NEW
UNCOV
799
                                        continue
×
800
                                }
801
                                // Skip 0x4000 MASQUERADE rule in OvnPostrouting
NEW
802
                                if rule.Table == NAT && rule.Chain == OvnPostrouting && strings.Contains(ruleStr, "0x4000/0x4000") && strings.Contains(ruleStr, OvnMasquerade) {
×
NEW
803
                                        continue
×
804
                                }
805
                                // Clean up and skip service ACCEPT rules in filter chains
NEW
806
                                if rule.Table == "filter" && strings.Contains(ruleStr, svcMatchset) {
×
NEW
UNCOV
807
                                        if err = deleteIptablesRule(ipt, rule); err != nil {
×
NEW
UNCOV
808
                                                klog.Errorf("failed to delete service iptables rule %v: %v", rule, err)
×
NEW
UNCOV
809
                                                return err
×
NEW
810
                                        }
×
NEW
811
                                        continue
×
812
                                }
NEW
813
                                filteredRules = append(filteredRules, rule)
×
814
                        }
NEW
UNCOV
815
                        iptablesRules = filteredRules
×
NEW
816
                } else if nodeIP := nodeIPs[protocol]; nodeIP != "" {
×
817
                        rules := make([]util.IPTableRule, len(iptablesRules)+1)
×
818
                        copy(rules, iptablesRules[:1])
×
819
                        copy(rules[2:], iptablesRules[1:])
×
820
                        rules[1] = util.IPTableRule{
×
821
                                Table: NAT,
×
822
                                Chain: OvnPostrouting,
×
823
                                Rule:  strings.Fields(fmt.Sprintf(`-m set --match-set %s src -m set --match-set %s dst -m mark --mark 0x4000/0x4000 -j SNAT --to-source %s`, svcMatchset, matchset, nodeIP)),
×
UNCOV
824
                        }
×
825
                        iptablesRules = rules
×
826

×
827
                        for _, p := range [...]string{"tcp", "udp"} {
×
828
                                ipset := fmt.Sprintf("KUBE-%sNODE-PORT-LOCAL-%s", kubeProxyIpsetProtocol, strings.ToUpper(p))
×
829
                                ipsetExists, err := c.ipsetExists(ipset)
×
UNCOV
830
                                if err != nil {
×
831
                                        klog.Errorf("failed to check existence of ipset %s: %v", ipset, err)
×
832
                                        return err
×
833
                                }
×
834
                                if !ipsetExists {
×
UNCOV
835
                                        klog.V(5).Infof("ipset %s does not exist", ipset)
×
836
                                        continue
×
837
                                }
838
                                rule := fmt.Sprintf("-p %s -m addrtype --dst-type LOCAL -m set --match-set %s dst -j MARK --set-xmark 0x80000/0x80000", p, ipset)
×
839
                                rule2 := fmt.Sprintf("-p %s -m set --match-set %s src -m set --match-set %s dst -j MARK --set-xmark 0x4000/0x4000", p, nodeMatchSet, ipset)
×
UNCOV
840
                                obsoleteRules = append(obsoleteRules, util.IPTableRule{Table: NAT, Chain: Prerouting, Rule: strings.Fields(rule)})
×
841
                                iptablesRules = append(iptablesRules,
×
842
                                        util.IPTableRule{Table: NAT, Chain: OvnPrerouting, Rule: strings.Fields(rule)},
×
843
                                        util.IPTableRule{Table: NAT, Chain: OvnPrerouting, Rule: strings.Fields(rule2)},
×
UNCOV
844
                                )
×
845
                        }
846
                }
847

UNCOV
848
                _, subnetCidrs, err := c.getDefaultVpcSubnetsCIDR(protocol)
×
UNCOV
849
                if err != nil {
×
850
                        klog.Errorf("get subnets failed, %+v", err)
×
851
                        return err
×
852
                }
×
853

UNCOV
854
                subnetNames := set.New[string]()
×
UNCOV
855
                for name, subnetCidr := range subnetCidrs {
×
856
                        subnetNames.Insert(name)
×
857
                        iptablesRules = append(iptablesRules,
×
858
                                util.IPTableRule{Table: "filter", Chain: "FORWARD", Rule: strings.Fields(fmt.Sprintf(`-m comment --comment %s,%s -s %s`, util.OvnSubnetGatewayIptables, name, subnetCidr))},
×
859
                                util.IPTableRule{Table: "filter", Chain: "FORWARD", Rule: strings.Fields(fmt.Sprintf(`-m comment --comment %s,%s -d %s`, util.OvnSubnetGatewayIptables, name, subnetCidr))},
×
860
                        )
×
861
                }
×
862

UNCOV
863
                rules, err := ipt.List("filter", "FORWARD")
×
864
                if err != nil {
×
865
                        klog.Errorf(`failed to list iptables rule table "filter" chain "FORWARD" with err %v `, err)
×
866
                        return err
×
867
                }
×
868

869
                pattern := fmt.Sprintf(`-m comment --comment "%s,`, util.OvnSubnetGatewayIptables)
×
870
                for _, rule := range rules {
×
871
                        if !strings.Contains(rule, pattern) {
×
872
                                continue
×
873
                        }
UNCOV
874
                        fields := util.DoubleQuotedFields(rule)
×
875
                        // -A FORWARD -d 10.16.0.0/16 -m comment --comment "ovn-subnet-gateway,ovn-default"
×
876
                        if len(fields) != 8 || fields[6] != "--comment" {
×
877
                                continue
×
878
                        }
UNCOV
879
                        commentFields := strings.Split(fields[7], ",")
×
UNCOV
880
                        if len(commentFields) != 2 {
×
UNCOV
881
                                continue
×
882
                        }
883
                        if subnetNames.Has(commentFields[1]) {
×
884
                                continue
×
885
                        }
886

887
                        // use fields[2:] to skip prefix "-A FORWARD"
888
                        if err = deleteIptablesRule(ipt, util.IPTableRule{Table: "filter", Chain: "FORWARD", Rule: fields[2:]}); err != nil {
×
889
                                klog.Error(err)
×
890
                                return err
×
891
                        }
×
892
                }
893

894
                var natPreroutingRules, natPostroutingRules, ovnMasqueradeRules, manglePostroutingRules []util.IPTableRule
×
895
                for _, rule := range iptablesRules {
×
896
                        if rule.Table == NAT {
×
UNCOV
897
                                if c.k8siptables[protocol].HasRandomFully() &&
×
UNCOV
898
                                        (rule.Rule[len(rule.Rule)-1] == "MASQUERADE" || slices.Contains(rule.Rule, "SNAT")) {
×
899
                                        rule.Rule = append(rule.Rule, "--random-fully")
×
900
                                }
×
901

902
                                switch rule.Chain {
×
903
                                case OvnPrerouting:
×
904
                                        natPreroutingRules = append(natPreroutingRules, rule)
×
905
                                        continue
×
906
                                case OvnPostrouting:
×
907
                                        natPostroutingRules = append(natPostroutingRules, rule)
×
UNCOV
908
                                        continue
×
UNCOV
909
                                case OvnMasquerade:
×
910
                                        ovnMasqueradeRules = append(ovnMasqueradeRules, rule)
×
911
                                        continue
×
912
                                }
913
                        } else if rule.Table == MANGLE {
×
UNCOV
914
                                if rule.Chain == OvnPostrouting {
×
915
                                        manglePostroutingRules = append(manglePostroutingRules, rule)
×
916
                                        continue
×
917
                                }
918
                        }
919

920
                        if err = c.createIptablesRule(ipt, rule); err != nil {
×
921
                                klog.Errorf(`failed to create iptables rule "%s": %v`, strings.Join(rule.Rule, " "), err)
×
922
                                return err
×
923
                        }
×
924
                }
925

926
                var randomFully string
×
927
                if c.k8siptables[protocol].HasRandomFully() {
×
928
                        randomFully = "--random-fully"
×
929
                }
×
930

931
                // add iptables rule for nat gw with designative ip in centralized subnet
UNCOV
932
                for cidr, ip := range centralGwNatIPs {
×
933
                        if util.CheckProtocol(cidr) != protocol {
×
934
                                continue
×
935
                        }
936

UNCOV
937
                        s := fmt.Sprintf("-s %s -m set ! --match-set %s dst -j SNAT --to-source %s %s", cidr, matchset, ip, randomFully)
×
938
                        rule := util.IPTableRule{
×
939
                                Table: NAT,
×
940
                                Chain: OvnPostrouting,
×
941
                                Rule:  util.DoubleQuotedFields(s),
×
UNCOV
942
                        }
×
943
                        // insert the rule before the one for nat outgoing
×
UNCOV
944
                        n := len(natPostroutingRules)
×
UNCOV
945
                        natPostroutingRules = append(natPostroutingRules[:n-1], rule, natPostroutingRules[n-1])
×
946
                }
947

948
                if err = c.reconcileNatOutgoingPolicyIptablesChain(protocol); err != nil {
×
949
                        klog.Error(err)
×
UNCOV
950
                        return err
×
951
                }
×
952

953
                if err = c.reconcileTProxyIPTableRules(protocol); err != nil {
×
954
                        klog.Error(err)
×
955
                        return err
×
956
                }
×
957

958
                if err = c.updateIptablesChain(ipt, NAT, OvnPrerouting, Prerouting, natPreroutingRules); err != nil {
×
959
                        klog.Errorf("failed to update chain %s/%s: %v", NAT, OvnPrerouting, err)
×
960
                        return err
×
961
                }
×
962
                if err = c.updateIptablesChain(ipt, NAT, OvnMasquerade, "", ovnMasqueradeRules); err != nil {
×
963
                        klog.Errorf("failed to update chain %s/%s: %v", NAT, OvnMasquerade, err)
×
964
                        return err
×
965
                }
×
966
                if err = c.updateIptablesChain(ipt, NAT, OvnPostrouting, Postrouting, natPostroutingRules); err != nil {
×
967
                        klog.Errorf("failed to update chain %s/%s: %v", NAT, OvnPostrouting, err)
×
968
                        return err
×
969
                }
×
970

971
                if err = c.updateIptablesChain(ipt, MANGLE, OvnPostrouting, Postrouting, manglePostroutingRules); err != nil {
×
972
                        klog.Errorf("failed to update chain %s/%s: %v", MANGLE, OvnPostrouting, err)
×
973
                        return err
×
974
                }
×
975

976
                if err = c.cleanObsoleteIptablesRules(protocol, obsoleteRules); err != nil {
×
977
                        klog.Errorf("failed to clean legacy iptables rules: %v", err)
×
978
                        return err
×
979
                }
×
980
        }
981
        return nil
×
982
}
983

984
func (c *Controller) reconcileTProxyIPTableRules(protocol string) error {
×
985
        if !c.config.EnableTProxy {
×
986
                return nil
×
987
        }
×
988

989
        ipt := c.iptables[protocol]
×
990
        tproxyPreRoutingRules := make([]util.IPTableRule, 0)
×
991
        tproxyOutputRules := make([]util.IPTableRule, 0)
×
992

×
993
        pods, err := c.getTProxyConditionPod(true)
×
994
        if err != nil {
×
995
                klog.Error(err)
×
996
                return err
×
997
        }
×
998

999
        for _, pod := range pods {
×
1000
                var podIP string
×
1001
                for _, ip := range pod.Status.PodIPs {
×
1002
                        if util.CheckProtocol(ip.IP) == protocol {
×
1003
                                podIP = ip.IP
×
1004
                                break
×
1005
                        }
1006
                }
1007

1008
                if podIP == "" {
×
1009
                        continue
×
1010
                }
1011

1012
                ports := getProbePorts(pod)
×
1013
                if ports.Len() == 0 {
×
1014
                        continue
×
1015
                }
1016

1017
                for _, probePort := range ports.SortedList() {
×
1018
                        hostIP := c.config.NodeIPv4
×
1019
                        prefixLen := 32
×
1020
                        if protocol == kubeovnv1.ProtocolIPv6 {
×
1021
                                prefixLen = 128
×
1022
                                hostIP = c.config.NodeIPv6
×
1023
                        }
×
1024

1025
                        tproxyOutputRules = append(tproxyOutputRules, util.IPTableRule{Table: MANGLE, Chain: OvnOutput, Rule: strings.Fields(fmt.Sprintf(`-d %s/%d -p tcp -m tcp --dport %d -j MARK --set-xmark %s`, podIP, prefixLen, probePort, tProxyOutputMarkMask))})
×
1026
                        tproxyPreRoutingRules = append(tproxyPreRoutingRules, util.IPTableRule{Table: MANGLE, Chain: OvnPrerouting, Rule: strings.Fields(fmt.Sprintf(`-d %s/%d -p tcp -m tcp --dport %d -j TPROXY --on-port %d --on-ip %s --tproxy-mark %s`, podIP, prefixLen, probePort, util.TProxyListenPort, hostIP, tProxyPreRoutingMarkMask))})
×
1027
                }
1028
        }
1029

1030
        if err := c.updateIptablesChain(ipt, MANGLE, OvnPrerouting, Prerouting, tproxyPreRoutingRules); err != nil {
×
1031
                klog.Errorf("failed to update chain %s with rules %v: %v", OvnPrerouting, tproxyPreRoutingRules, err)
×
1032
                return err
×
1033
        }
×
1034

1035
        if err := c.updateIptablesChain(ipt, MANGLE, OvnOutput, Output, tproxyOutputRules); err != nil {
×
1036
                klog.Errorf("failed to update chain %s with rules %v: %v", OvnOutput, tproxyOutputRules, err)
×
1037
                return err
×
1038
        }
×
1039
        return nil
×
1040
}
1041

1042
func (c *Controller) cleanTProxyIPTableRules(protocol string) {
×
1043
        ipt := c.iptables[protocol]
×
1044
        if ipt == nil {
×
1045
                return
×
1046
        }
×
1047
        for _, chain := range [2]string{OvnPrerouting, OvnOutput} {
×
1048
                if err := ipt.ClearChain(MANGLE, chain); err != nil {
×
1049
                        klog.Errorf("failed to clear iptables chain %v in table %v, %+v", chain, MANGLE, err)
×
1050
                        return
×
1051
                }
×
1052
        }
1053
}
1054

1055
func (c *Controller) reconcileNatOutgoingPolicyIptablesChain(protocol string) error {
×
1056
        ipt := c.iptables[protocol]
×
1057

×
1058
        natPolicySubnetIptables, natPolicyRuleIptablesMap, gcNatPolicySubnetChains, err := c.generateNatOutgoingPolicyChainRules(protocol)
×
1059
        if err != nil {
×
1060
                klog.Errorf(`failed to get nat policy post routing rules with err %v `, err)
×
1061
                return err
×
1062
        }
×
1063

1064
        for chainName, natPolicyRuleIptableRules := range natPolicyRuleIptablesMap {
×
1065
                if err = c.updateIptablesChain(ipt, NAT, chainName, "", natPolicyRuleIptableRules); err != nil {
×
1066
                        klog.Errorf("failed to update chain %s with rules %v: %v", chainName, natPolicyRuleIptableRules, err)
×
1067
                        return err
×
1068
                }
×
1069
        }
1070

1071
        if err = c.updateIptablesChain(ipt, NAT, OvnNatOutGoingPolicy, "", natPolicySubnetIptables); err != nil {
×
1072
                klog.Errorf("failed to update chain %s: %v", OvnNatOutGoingPolicy, err)
×
1073
                return err
×
1074
        }
×
1075

1076
        for _, gcNatPolicySubnetChain := range gcNatPolicySubnetChains {
×
1077
                if err = ipt.ClearAndDeleteChain(NAT, gcNatPolicySubnetChain); err != nil {
×
1078
                        klog.Errorf("failed to delete iptables chain %q in table %s: %v", gcNatPolicySubnetChain, NAT, err)
×
1079
                        return err
×
1080
                }
×
1081
                klog.Infof("deleted iptables chain %s in table %s", gcNatPolicySubnetChain, NAT)
×
1082
        }
1083
        return nil
×
1084
}
1085

1086
func (c *Controller) generateNatOutgoingPolicyChainRules(protocol string) ([]util.IPTableRule, map[string][]util.IPTableRule, []string, error) {
×
1087
        natPolicySubnetIptables := make([]util.IPTableRule, 0)
×
1088
        natPolicyRuleIptablesMap := make(map[string][]util.IPTableRule)
×
1089
        natPolicySubnetUIDs := strset.New()
×
1090
        gcNatPolicySubnetChains := make([]string, 0)
×
1091
        subnetNames := make([]string, 0)
×
1092
        subnetMap := make(map[string]*kubeovnv1.Subnet)
×
1093

×
1094
        subnets, err := c.getSubnetsNatOutGoingPolicy(protocol)
×
1095
        if err != nil {
×
1096
                klog.Errorf("failed to get subnets with NAT outgoing policy rule: %v", err)
×
1097
                return nil, nil, nil, err
×
1098
        }
×
1099

1100
        for _, subnet := range subnets {
×
1101
                subnetNames = append(subnetNames, subnet.Name)
×
1102
                subnetMap[subnet.Name] = subnet
×
1103
        }
×
1104

1105
        // To ensure the iptable rule order
1106
        sort.Strings(subnetNames)
×
1107

×
1108
        getMatchProtocol := func(ips string) string {
×
1109
                ip := strings.Split(ips, ",")[0]
×
1110
                return util.CheckProtocol(ip)
×
1111
        }
×
1112

1113
        for _, subnetName := range subnetNames {
×
1114
                subnet := subnetMap[subnetName]
×
1115
                var natPolicyRuleIptables []util.IPTableRule
×
1116
                natPolicySubnetUIDs.Add(util.GetTruncatedUID(string(subnet.GetUID())))
×
1117
                cidrBlock, err := getCidrByProtocol(subnet.Spec.CIDRBlock, protocol)
×
1118
                if err != nil {
×
1119
                        klog.Errorf("failed to get subnet %s cidr block with protocol: %v", subnet.Name, err)
×
1120
                        continue
×
1121
                }
UNCOV
1122
                if cidrBlock == "" {
×
1123
                        continue
×
1124
                }
1125

1126
                ovnNatPolicySubnetChainName := OvnNatOutGoingPolicySubnet + util.GetTruncatedUID(string(subnet.GetUID()))
×
1127
                natPolicySubnetIptables = append(natPolicySubnetIptables, util.IPTableRule{Table: NAT, Chain: OvnNatOutGoingPolicy, Rule: strings.Fields(fmt.Sprintf(`-s %s -m comment --comment natPolicySubnet-%s -j %s`, cidrBlock, subnet.Name, ovnNatPolicySubnetChainName))})
×
1128
                for _, rule := range subnet.Status.NatOutgoingPolicyRules {
×
UNCOV
1129
                        var markCode string
×
UNCOV
1130
                        switch rule.Action {
×
UNCOV
1131
                        case util.NatPolicyRuleActionNat:
×
1132
                                markCode = OnOutGoingNatMark
×
1133
                        case util.NatPolicyRuleActionForward:
×
UNCOV
1134
                                markCode = OnOutGoingForwardMark
×
1135
                        }
1136

1137
                        if rule.RuleID == "" {
×
1138
                                continue
×
1139
                        }
1140

1141
                        if rule.Match.SrcIPs != "" && getMatchProtocol(rule.Match.SrcIPs) != protocol {
×
1142
                                continue
×
1143
                        }
1144

1145
                        if rule.Match.DstIPs != "" && getMatchProtocol(rule.Match.DstIPs) != protocol {
×
1146
                                continue
×
1147
                        }
1148

1149
                        srcMatch := getNatOutGoingPolicyRuleIPSetName(rule.RuleID, "src", protocol, true)
×
1150
                        dstMatch := getNatOutGoingPolicyRuleIPSetName(rule.RuleID, "dst", protocol, true)
×
UNCOV
1151

×
UNCOV
1152
                        var ovnNatoutGoingPolicyRule util.IPTableRule
×
UNCOV
1153

×
1154
                        switch {
×
1155
                        case rule.Match.DstIPs != "" && rule.Match.SrcIPs != "":
×
1156
                                ovnNatoutGoingPolicyRule = util.IPTableRule{Table: NAT, Chain: ovnNatPolicySubnetChainName, Rule: strings.Fields(fmt.Sprintf(`-m set --match-set %s src -m set --match-set %s dst -j MARK --set-xmark %s`, srcMatch, dstMatch, markCode))}
×
1157
                        case rule.Match.SrcIPs != "":
×
UNCOV
1158
                                protocol = getMatchProtocol(rule.Match.SrcIPs)
×
1159
                                ovnNatoutGoingPolicyRule = util.IPTableRule{Table: NAT, Chain: ovnNatPolicySubnetChainName, Rule: strings.Fields(fmt.Sprintf(`-m set --match-set %s src -j MARK --set-xmark %s`, srcMatch, markCode))}
×
1160
                        case rule.Match.DstIPs != "":
×
1161
                                protocol = getMatchProtocol(rule.Match.DstIPs)
×
1162
                                ovnNatoutGoingPolicyRule = util.IPTableRule{Table: NAT, Chain: ovnNatPolicySubnetChainName, Rule: strings.Fields(fmt.Sprintf(`-m set --match-set %s dst -j MARK --set-xmark %s`, dstMatch, markCode))}
×
1163
                        default:
×
UNCOV
1164
                                continue
×
1165
                        }
1166
                        natPolicyRuleIptables = append(natPolicyRuleIptables, ovnNatoutGoingPolicyRule)
×
1167
                }
1168
                natPolicyRuleIptablesMap[ovnNatPolicySubnetChainName] = natPolicyRuleIptables
×
1169
        }
1170

1171
        existNatChains, err := c.iptables[protocol].ListChains(NAT)
×
1172
        if err != nil {
×
1173
                klog.Errorf("list chains in table nat failed")
×
1174
                return nil, nil, nil, err
×
1175
        }
×
1176

UNCOV
1177
        for _, existNatChain := range existNatChains {
×
UNCOV
1178
                if strings.HasPrefix(existNatChain, OvnNatOutGoingPolicySubnet) &&
×
1179
                        !natPolicySubnetUIDs.Has(getNatPolicySubnetChainUID(existNatChain)) {
×
1180
                        gcNatPolicySubnetChains = append(gcNatPolicySubnetChains, existNatChain)
×
1181
                }
×
1182
        }
1183

1184
        return natPolicySubnetIptables, natPolicyRuleIptablesMap, gcNatPolicySubnetChains, nil
×
1185
}
1186

UNCOV
1187
func deleteIptablesRule(ipt *iptables.IPTables, rule util.IPTableRule) error {
×
1188
        if rule.Pos != "" {
×
1189
                klog.Infof("delete iptables rule by pos %s: %v", rule.Pos, rule)
×
1190
                if err := ipt.Delete(rule.Table, rule.Chain, rule.Pos); err != nil {
×
1191
                        klog.Errorf("failed to delete iptables %s rule %q: %v", rule.Chain, strings.Join(rule.Rule, " "), err)
×
1192
                        return err
×
UNCOV
1193
                }
×
UNCOV
1194
                return nil
×
1195
        }
1196
        exists, err := ipt.Exists(rule.Table, rule.Chain, rule.Rule...)
×
1197
        if err == nil && exists {
×
1198
                klog.Infof("delete iptables rule: %v", rule)
×
UNCOV
1199
                err = ipt.Delete(rule.Table, rule.Chain, rule.Rule...)
×
1200
        }
×
1201
        if err != nil {
×
1202
                klog.Errorf("failed to delete iptables rule %q: %v", strings.Join(rule.Rule, " "), err)
×
1203
                return err
×
1204
        }
×
1205
        return nil
×
1206
}
1207

UNCOV
1208
func clearObsoleteIptablesChain(ipt *iptables.IPTables, table, chain, parent string) error {
×
UNCOV
1209
        exists, err := ipt.ChainExists(table, chain)
×
1210
        if err != nil {
×
1211
                klog.Error(err)
×
1212
                return err
×
1213
        }
×
1214
        if !exists {
×
1215
                return nil
×
1216
        }
×
1217

1218
        rule := fmt.Sprintf(`-m comment --comment "kube-ovn %s rules" -j %s`, strings.ToLower(parent), chain)
×
1219
        if err = deleteIptablesRule(ipt, util.IPTableRule{Table: table, Chain: parent, Rule: util.DoubleQuotedFields(rule)}); err != nil {
×
1220
                klog.Error(err)
×
1221
                return err
×
1222
        }
×
UNCOV
1223
        if err = ipt.ClearAndDeleteChain(table, chain); err != nil {
×
1224
                klog.Errorf("failed to delete iptables chain %q in table %s: %v", chain, table, err)
×
1225
                return err
×
1226
        }
×
1227
        return nil
×
1228
}
1229

1230
func (c *Controller) cleanObsoleteIptablesRules(protocol string, rules []util.IPTableRule) error {
×
1231
        if c.iptablesObsolete == nil || c.iptablesObsolete[protocol] == nil {
×
1232
                return nil
×
1233
        }
×
1234

1235
        var (
×
UNCOV
1236
                v4ObsoleteRules = []util.IPTableRule{
×
1237
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m mark --mark 0x40000/0x40000 -j MASQUERADE`)},
×
1238
                        {Table: "mangle", Chain: Prerouting, Rule: strings.Fields(`-i ` + util.NodeNic + ` -m set --match-set ovn40subnets src -m set --match-set ovn40services dst -j MARK --set-xmark 0x40000/0x40000`)},
×
1239
                        // legacy rules
×
1240
                        // nat packets marked by kube-proxy or kube-ovn
×
1241
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m mark --mark 0x4000/0x4000 -j MASQUERADE`)},
×
1242
                        // nat service traffic
×
1243
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m set --match-set ovn40subnets src -m set --match-set ovn40subnets dst -j MASQUERADE`)},
×
1244
                        // do not nat node port service traffic with external traffic policy set to local
×
UNCOV
1245
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m mark --mark 0x80000/0x80000 -m set --match-set ovn40subnets-distributed-gw dst -j RETURN`)},
×
1246
                        // nat node port service traffic with external traffic policy set to local for subnets with centralized gateway
×
1247
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m mark --mark 0x80000/0x80000 -j MASQUERADE`)},
×
UNCOV
1248
                        // do not nat reply packets in direct routing
×
UNCOV
1249
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-p tcp --tcp-flags SYN NONE -m conntrack --ctstate NEW -j RETURN`)},
×
1250
                        // do not nat route traffic
×
1251
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m set ! --match-set ovn40subnets src -m set ! --match-set ovn40other-node src -m set --match-set ovn40subnets-nat dst -j RETURN`)},
×
1252
                        // nat outgoing
×
1253
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m set --match-set ovn40subnets-nat src -m set ! --match-set ovn40subnets dst -j MASQUERADE`)},
×
1254
                        // mark packets from pod to service
×
1255
                        {Table: "mangle", Chain: Prerouting, Rule: strings.Fields(`-i ` + util.NodeNic + ` -m set --match-set ovn40subnets src -m set --match-set ovn40services dst -j MARK --set-xmark 0x4000/0x4000`)},
×
1256
                        // Input Accept
×
1257
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn40subnets src -j ACCEPT`)},
×
1258
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn40subnets dst -j ACCEPT`)},
×
UNCOV
1259
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn40services src -j ACCEPT`)},
×
UNCOV
1260
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn40services dst -j ACCEPT`)},
×
1261
                        // Forward Accept
×
1262
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn40subnets src -j ACCEPT`)},
×
UNCOV
1263
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn40subnets dst -j ACCEPT`)},
×
UNCOV
1264
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn40services src -j ACCEPT`)},
×
1265
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn40services dst -j ACCEPT`)},
×
1266
                        // Output unmark to bypass kernel nat checksum issue https://github.com/flannel-io/flannel/issues/1279
×
UNCOV
1267
                        {Table: "filter", Chain: "OUTPUT", Rule: strings.Fields(`-p udp -m udp --dport 6081 -j MARK --set-xmark 0x0`)},
×
UNCOV
1268
                        {Table: "filter", Chain: "OUTPUT", Rule: strings.Fields(`-p udp -m udp --dport 4789 -j MARK --set-xmark 0x0`)},
×
1269
                }
×
1270
                v6ObsoleteRules = []util.IPTableRule{
×
UNCOV
1271
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m mark --mark 0x40000/0x40000 -j MASQUERADE`)},
×
UNCOV
1272
                        {Table: "mangle", Chain: Prerouting, Rule: strings.Fields(`-i ` + util.NodeNic + ` -m set --match-set ovn60subnets src -m set --match-set ovn60services dst -j MARK --set-xmark 0x40000/0x40000`)},
×
1273
                        // legacy rules
×
1274
                        // nat packets marked by kube-proxy or kube-ovn
×
1275
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m mark --mark 0x4000/0x4000 -j MASQUERADE`)},
×
1276
                        // nat service traffic
×
1277
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m set --match-set ovn60subnets src -m set --match-set ovn60subnets dst -j MASQUERADE`)},
×
1278
                        // do not nat node port service traffic with external traffic policy set to local
×
1279
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m mark --mark 0x80000/0x80000 -m set --match-set ovn60subnets-distributed-gw dst -j RETURN`)},
×
1280
                        // nat node port service traffic with external traffic policy set to local for subnets with centralized gateway
×
1281
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m mark --mark 0x80000/0x80000 -j MASQUERADE`)},
×
1282
                        // do not nat reply packets in direct routing
×
1283
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-p tcp --tcp-flags SYN NONE -m conntrack --ctstate NEW -j RETURN`)},
×
1284
                        // do not nat route traffic
×
1285
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m set ! --match-set ovn60subnets src -m set ! --match-set ovn60other-node src -m set --match-set ovn60subnets-nat dst -j RETURN`)},
×
1286
                        // nat outgoing
×
1287
                        {Table: NAT, Chain: Postrouting, Rule: strings.Fields(`-m set --match-set ovn60subnets-nat src -m set ! --match-set ovn60subnets dst -j MASQUERADE`)},
×
1288
                        // mark packets from pod to service
×
UNCOV
1289
                        {Table: "mangle", Chain: Prerouting, Rule: strings.Fields(`-i ` + util.NodeNic + ` -m set --match-set ovn60subnets src -m set --match-set ovn60services dst -j MARK --set-xmark 0x4000/0x4000`)},
×
1290
                        // Input Accept
×
UNCOV
1291
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn60subnets src -j ACCEPT`)},
×
1292
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn60subnets dst -j ACCEPT`)},
×
UNCOV
1293
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn60services src -j ACCEPT`)},
×
UNCOV
1294
                        {Table: "filter", Chain: "INPUT", Rule: strings.Fields(`-m set --match-set ovn60services dst -j ACCEPT`)},
×
1295
                        // Forward Accept
×
1296
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn60subnets src -j ACCEPT`)},
×
1297
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn60subnets dst -j ACCEPT`)},
×
1298
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn60services src -j ACCEPT`)},
×
1299
                        {Table: "filter", Chain: "FORWARD", Rule: strings.Fields(`-m set --match-set ovn60services dst -j ACCEPT`)},
×
UNCOV
1300
                        // Output unmark to bypass kernel nat checksum issue https://github.com/flannel-io/flannel/issues/1279
×
1301
                        {Table: "filter", Chain: "OUTPUT", Rule: strings.Fields(`-p udp -m udp --dport 6081 -j MARK --set-xmark 0x0`)},
×
1302
                        {Table: "filter", Chain: "OUTPUT", Rule: strings.Fields(`-p udp -m udp --dport 4789 -j MARK --set-xmark 0x0`)},
×
1303
                }
×
1304
        )
×
1305

×
UNCOV
1306
        var obsoleteRules []util.IPTableRule
×
UNCOV
1307
        if protocol == kubeovnv1.ProtocolIPv4 {
×
1308
                obsoleteRules = v4ObsoleteRules
×
UNCOV
1309
        } else {
×
UNCOV
1310
                obsoleteRules = v6ObsoleteRules
×
1311
        }
×
1312

1313
        ipt := c.iptablesObsolete[protocol]
×
1314
        for _, rule := range obsoleteRules {
×
1315
                if err := deleteIptablesRule(ipt, rule); err != nil {
×
1316
                        klog.Error(err)
×
1317
                        return err
×
1318
                }
×
1319
        }
1320
        for _, rule := range rules {
×
1321
                if err := deleteIptablesRule(ipt, rule); err != nil {
×
1322
                        klog.Error(err)
×
1323
                        return err
×
1324
                }
×
1325
        }
1326

1327
        forwardRules, err := ipt.List("filter", "FORWARD")
×
1328
        if err != nil {
×
1329
                klog.Errorf(`failed to list legacy iptables rule in "FORWARD" chain "filter" table: %v`, err)
×
1330
                return err
×
1331
        }
×
1332
        prefix := util.OvnSubnetGatewayIptables + ","
×
1333
        for _, rule := range forwardRules {
×
1334
                fields := util.DoubleQuotedFields(rule)
×
1335
                for _, f := range fields {
×
1336
                        if strings.HasPrefix(f, prefix) {
×
1337
                                if err = ipt.Delete("filter", "FORWARD", fields...); err != nil {
×
1338
                                        klog.Errorf("failed to delete legacy iptables rules %q: %v", rule, err)
×
1339
                                }
×
1340
                        }
1341
                }
1342
        }
1343

1344
        // delete unused iptables rule when nat gw with designative ip has been changed in centralized subnet
1345
        if err = c.deleteObsoleteSnatRules(ipt, NAT, Postrouting); err != nil {
×
1346
                klog.Errorf("failed to delete legacy iptables rule for SNAT: %v", err)
×
1347
                return err
×
1348
        }
×
1349

UNCOV
1350
        if err = clearObsoleteIptablesChain(ipt, NAT, OvnPrerouting, Prerouting); err != nil {
×
UNCOV
1351
                klog.Error(err)
×
1352
                return err
×
1353
        }
×
1354
        if err = clearObsoleteIptablesChain(ipt, NAT, OvnPostrouting, Postrouting); err != nil {
×
1355
                klog.Error(err)
×
1356
                return err
×
1357
        }
×
1358

1359
        delete(c.iptablesObsolete, protocol)
×
1360
        if len(c.iptablesObsolete) == 0 {
×
UNCOV
1361
                c.iptablesObsolete = nil
×
1362
        }
×
1363
        return nil
×
1364
}
1365

1366
func (c *Controller) setOvnSubnetGatewayMetric() {
×
1367
        nodeName := os.Getenv(util.EnvNodeName)
×
1368
        for proto, iptables := range c.iptables {
×
1369
                rules, err := iptables.ListWithCounters("filter", "FORWARD")
×
1370
                if err != nil {
×
1371
                        klog.Errorf("get proto %s iptables failed with err %v", proto, err)
×
UNCOV
1372
                        continue
×
1373
                }
1374

1375
                for _, rule := range rules {
×
1376
                        if !strings.Contains(rule, util.OvnSubnetGatewayIptables) {
×
1377
                                continue
×
1378
                        }
1379

1380
                        items := util.DoubleQuotedFields(rule)
×
1381
                        if len(items) != 11 {
×
1382
                                continue
×
1383
                        }
1384

1385
                        comments := strings.Split(items[7], ",")
×
1386
                        if len(comments) != 2 || comments[0] != util.OvnSubnetGatewayIptables {
×
1387
                                continue
×
1388
                        }
1389
                        subnetName := comments[1]
×
1390

×
1391
                        var direction string
×
1392
                        switch items[2] {
×
1393
                        case "-s":
×
1394
                                direction = "egress"
×
1395
                        case "-d":
×
1396
                                direction = "ingress"
×
1397
                        default:
×
1398
                                continue
×
1399
                        }
1400

1401
                        cidr := items[3]
×
1402
                        proto := util.CheckProtocol(cidr)
×
1403
                        if proto == "" {
×
1404
                                klog.Errorf("failed to get protocol from cidr %q", cidr)
×
1405
                                continue
×
1406
                        }
1407

1408
                        currentPackets, err := strconv.Atoi(items[9])
×
1409
                        if err != nil {
×
1410
                                klog.Errorf("failed to parse packets %q: %v", items[9], err)
×
1411
                                continue
×
1412
                        }
1413
                        currentPacketBytes, err := strconv.Atoi(items[10])
×
1414
                        if err != nil {
×
1415
                                klog.Errorf("failed to parse packet bytes %q: %v", items[10], err)
×
1416
                                continue
×
1417
                        }
1418

1419
                        key := strings.Join([]string{subnetName, direction, proto}, "/")
×
1420
                        if c.gwCounters[key] == nil {
×
1421
                                c.gwCounters[key] = new(util.GwIPTablesCounters)
×
1422
                        }
×
1423
                        lastPackets, lastPacketBytes := c.gwCounters[key].Packets, c.gwCounters[key].PacketBytes
×
1424
                        c.gwCounters[key].Packets, c.gwCounters[key].PacketBytes = currentPackets, currentPacketBytes
×
1425

×
1426
                        if lastPackets == 0 && lastPacketBytes == 0 {
×
1427
                                // the gwCounters may just initialize don't cal the diff values,
×
1428
                                // it may loss packets to calculate during a metric period
×
1429
                                continue
×
1430
                        }
1431
                        if currentPackets < lastPackets || currentPacketBytes < lastPacketBytes {
×
1432
                                // if currentPacketBytes < lastPacketBytes, the reason is that iptables rule is reset ,
×
1433
                                // it may loss packets to calculate during a metric period
×
1434
                                continue
×
1435
                        }
1436

1437
                        diffPackets := currentPackets - lastPackets
×
1438
                        diffPacketBytes := currentPacketBytes - lastPacketBytes
×
1439
                        klog.V(3).Infof(`nodeName %s key %s cidr %s direction %s proto %s has diffPackets %d diffPacketBytes %d currentPackets %d currentPacketBytes %d lastPackets %d lastPacketBytes %d`,
×
1440
                                nodeName, key, cidr, direction, proto, diffPackets, diffPacketBytes, currentPackets, currentPacketBytes, lastPackets, lastPacketBytes)
×
1441
                        metricOvnSubnetGatewayPackets.WithLabelValues(nodeName, key, cidr, direction, proto).Add(float64(diffPackets))
×
1442
                        metricOvnSubnetGatewayPacketBytes.WithLabelValues(nodeName, key, cidr, direction, proto).Add(float64(diffPacketBytes))
×
1443
                }
1444
        }
1445
}
1446

1447
func (c *Controller) addEgressConfig(subnet *kubeovnv1.Subnet, ip string) error {
×
1448
        if (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway) ||
×
1449
                subnet.Spec.GatewayType != kubeovnv1.GWDistributedType ||
×
1450
                subnet.Spec.Vpc != c.config.ClusterRouter {
×
1451
                return nil
×
1452
        }
×
1453

1454
        if !subnet.Spec.NatOutgoing && subnet.Spec.ExternalEgressGateway != "" {
×
1455
                podIPs := strings.Split(ip, ",")
×
UNCOV
1456
                protocol := util.CheckProtocol(ip)
×
1457
                return c.addPodPolicyRouting(protocol, subnet.Spec.ExternalEgressGateway, subnet.Spec.PolicyRoutingPriority, subnet.Spec.PolicyRoutingTableID, podIPs)
×
1458
        }
×
1459

1460
        return nil
×
1461
}
1462

UNCOV
1463
func (c *Controller) removeEgressConfig(subnet, ip string) error {
×
1464
        if subnet == "" || ip == "" {
×
1465
                return nil
×
1466
        }
×
1467

1468
        podSubnet, err := c.subnetsLister.Get(subnet)
×
UNCOV
1469
        if k8serrors.IsNotFound(err) {
×
UNCOV
1470
                return nil
×
1471
        } else if err != nil {
×
1472
                klog.Errorf("failed to get subnet %s: %+v", subnet, err)
×
1473
                return err
×
1474
        }
×
1475

1476
        if (podSubnet.Spec.Vlan != "" && !podSubnet.Spec.LogicalGateway) ||
×
1477
                podSubnet.Spec.GatewayType != kubeovnv1.GWDistributedType ||
×
1478
                podSubnet.Spec.Vpc != c.config.ClusterRouter {
×
1479
                return nil
×
1480
        }
×
1481

1482
        if !podSubnet.Spec.NatOutgoing && podSubnet.Spec.ExternalEgressGateway != "" {
×
1483
                podIPs := strings.Split(ip, ",")
×
UNCOV
1484
                protocol := util.CheckProtocol(ip)
×
UNCOV
1485
                return c.deletePodPolicyRouting(protocol, podSubnet.Spec.ExternalEgressGateway, podSubnet.Spec.PolicyRoutingPriority, podSubnet.Spec.PolicyRoutingTableID, podIPs)
×
UNCOV
1486
        }
×
1487

UNCOV
1488
        return nil
×
1489
}
1490

1491
func (c *Controller) setExGateway() error {
×
1492
        node, err := c.nodesLister.Get(c.config.NodeName)
×
UNCOV
1493
        if err != nil {
×
1494
                klog.Errorf("failed to get node, %v", err)
×
1495
                return err
×
1496
        }
×
1497
        var isUserspaceDP bool
×
1498
        isUserspaceDP, err = ovs.IsUserspaceDataPath()
×
1499
        if err != nil {
×
1500
                klog.Error(err)
×
1501
                return err
×
UNCOV
1502
        }
×
1503
        enable := node.Labels[util.ExGatewayLabel]
×
1504
        externalBridge := util.ExternalBridgeName(c.config.ExternalGatewaySwitch)
×
1505
        if enable == "true" {
×
1506
                cm, err := c.config.KubeClient.CoreV1().ConfigMaps(c.config.ExternalGatewayConfigNS).Get(context.Background(), util.ExternalGatewayConfig, metav1.GetOptions{})
×
1507
                if err != nil {
×
UNCOV
1508
                        klog.Errorf("failed to get ovn-external-gw-config, %v", err)
×
UNCOV
1509
                        return err
×
1510
                }
×
1511

1512
                linkName, exist := cm.Data["external-gw-nic"]
×
1513
                if !exist || len(linkName) == 0 {
×
1514
                        err = errors.New("external-gw-nic not configured in ovn-external-gw-config")
×
1515
                        klog.Error(err)
×
1516
                        return err
×
UNCOV
1517
                }
×
1518

1519
                if !isUserspaceDP {
×
1520
                        link, err := netlink.LinkByName(linkName)
×
1521
                        if err != nil {
×
UNCOV
1522
                                klog.Errorf("failed to get nic %s, %v", linkName, err)
×
UNCOV
1523
                                return err
×
1524
                        }
×
1525
                        if err := netlink.LinkSetUp(link); err != nil {
×
1526
                                klog.Errorf("failed to set gateway nic %s up, %v", linkName, err)
×
UNCOV
1527
                                return err
×
UNCOV
1528
                        }
×
1529
                }
1530

1531
                externalBrReady := false
×
UNCOV
1532
                // if external nic already attached into another bridge
×
1533
                if existBr, err := ovs.Exec("port-to-br", linkName); err == nil {
×
1534
                        if existBr == externalBridge {
×
1535
                                externalBrReady = true
×
1536
                        } else {
×
1537
                                klog.Infof("external bridge should change from %s to %s, delete external bridge %s", existBr, externalBridge, existBr)
×
1538
                                if _, err := ovs.Exec(ovs.IfExists, "del-br", existBr); err != nil {
×
1539
                                        err = fmt.Errorf("failed to del external br %s, %w", existBr, err)
×
1540
                                        klog.Error(err)
×
1541
                                        return err
×
1542
                                }
×
1543
                        }
1544
                }
1545

1546
                if !externalBrReady {
×
1547
                        klog.Infof("create external bridge %s and add nic %s", externalBridge, linkName)
×
1548
                        if _, err := ovs.Exec(
×
1549
                                ovs.MayExist, "add-br", externalBridge, "--",
×
UNCOV
1550
                                ovs.MayExist, "add-port", externalBridge, linkName,
×
UNCOV
1551
                        ); err != nil {
×
1552
                                err = fmt.Errorf("failed to enable external gateway, %w", err)
×
1553
                                klog.Error(err)
×
1554
                        }
×
1555
                }
UNCOV
1556
                if err = addOvnMapping("ovn-bridge-mappings", c.config.ExternalGatewaySwitch, externalBridge, true); err != nil {
×
1557
                        klog.Error(err)
×
1558
                        return err
×
1559
                }
×
1560
        } else {
×
UNCOV
1561
                brExists, err := ovs.BridgeExists(externalBridge)
×
UNCOV
1562
                if err != nil {
×
1563
                        return fmt.Errorf("failed to check OVS bridge existence: %w", err)
×
1564
                }
×
1565
                if !brExists {
×
1566
                        return nil
×
1567
                }
×
1568

1569
                providerNetworks, err := c.providerNetworksLister.List(labels.Everything())
×
1570
                if err != nil && !k8serrors.IsNotFound(err) {
×
1571
                        klog.Errorf("failed to list provider networks: %v", err)
×
1572
                        return err
×
1573
                }
×
1574

1575
                for _, pn := range providerNetworks {
×
1576
                        // if external nic already attached into another bridge
×
1577
                        if existBr, err := ovs.Exec("port-to-br", pn.Spec.DefaultInterface); err == nil {
×
1578
                                if existBr == externalBridge {
×
UNCOV
1579
                                        // delete switch after related provider network not exist
×
UNCOV
1580
                                        return nil
×
1581
                                }
×
1582
                        }
1583
                }
1584

1585
                keepExternalSubnet := false
×
1586
                externalSubnet, err := c.subnetsLister.Get(c.config.ExternalGatewaySwitch)
×
UNCOV
1587
                if err != nil {
×
UNCOV
1588
                        if !k8serrors.IsNotFound(err) {
×
UNCOV
1589
                                klog.Errorf("failed to get subnet %s, %v", c.config.ExternalGatewaySwitch, err)
×
UNCOV
1590
                                return err
×
1591
                        }
×
1592
                } else {
×
1593
                        if externalSubnet.Spec.Vlan != "" {
×
1594
                                keepExternalSubnet = true
×
1595
                        }
×
1596
                }
1597

1598
                if !isUserspaceDP && !keepExternalSubnet {
×
1599
                        klog.Infof("delete external bridge %s", externalBridge)
×
1600
                        if _, err := ovs.Exec(
×
1601
                                ovs.IfExists, "del-br", externalBridge); err != nil {
×
1602
                                err = fmt.Errorf("failed to disable external gateway, %w", err)
×
UNCOV
1603
                                klog.Error(err)
×
1604
                                return err
×
UNCOV
1605
                        }
×
1606
                }
1607
        }
1608
        return nil
×
1609
}
1610

UNCOV
1611
func (c *Controller) getLocalPodIPsNeedPR(protocol string) (map[policyRouteMeta][]string, error) {
×
1612
        allPods, err := c.podsLister.List(labels.Everything())
×
1613
        if err != nil {
×
1614
                klog.Errorf("failed to list pods: %+v", err)
×
1615
                return nil, err
×
1616
        }
×
1617

1618
        localPodIPs := make(map[policyRouteMeta][]string)
×
UNCOV
1619
        for _, pod := range allPods {
×
1620
                if !pod.DeletionTimestamp.IsZero() ||
×
1621
                        pod.Annotations[util.LogicalSwitchAnnotation] == "" ||
×
1622
                        pod.Annotations[util.IPAddressAnnotation] == "" {
×
1623
                        continue
×
1624
                }
1625

1626
                subnet, err := c.subnetsLister.Get(pod.Annotations[util.LogicalSwitchAnnotation])
×
1627
                if err != nil {
×
1628
                        klog.Errorf("failed to get subnet %s: %+v", pod.Annotations[util.LogicalSwitchAnnotation], err)
×
1629
                        continue
×
1630
                }
1631

1632
                if subnet.Spec.ExternalEgressGateway == "" ||
×
UNCOV
1633
                        subnet.Spec.Vpc != c.config.ClusterRouter ||
×
UNCOV
1634
                        subnet.Spec.GatewayType != kubeovnv1.GWDistributedType {
×
1635
                        continue
×
1636
                }
1637
                if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway {
×
1638
                        continue
×
1639
                }
1640

1641
                ips := make([]string, 0, 2)
×
1642
                if len(pod.Status.PodIPs) != 0 {
×
1643
                        if len(pod.Status.PodIPs) == 2 && protocol == kubeovnv1.ProtocolIPv6 {
×
1644
                                ips = append(ips, pod.Status.PodIPs[1].IP)
×
1645
                        } else if util.CheckProtocol(pod.Status.PodIP) == protocol {
×
1646
                                ips = append(ips, pod.Status.PodIP)
×
1647
                        }
×
1648
                } else {
×
1649
                        ipv4, ipv6 := util.SplitStringIP(pod.Annotations[util.IPAddressAnnotation])
×
1650
                        if ipv4 != "" && protocol == kubeovnv1.ProtocolIPv4 {
×
1651
                                ips = append(ips, ipv4)
×
1652
                        }
×
1653
                        if ipv6 != "" && protocol == kubeovnv1.ProtocolIPv6 {
×
1654
                                ips = append(ips, ipv6)
×
UNCOV
1655
                        }
×
1656
                }
1657

1658
                if len(ips) != 0 {
×
1659
                        meta := policyRouteMeta{
×
1660
                                priority: subnet.Spec.PolicyRoutingPriority,
×
1661
                                tableID:  subnet.Spec.PolicyRoutingTableID,
×
UNCOV
1662
                        }
×
1663

×
1664
                        egw := strings.Split(subnet.Spec.ExternalEgressGateway, ",")
×
1665
                        if util.CheckProtocol(egw[0]) == protocol {
×
1666
                                meta.gateway = egw[0]
×
1667
                                if util.CheckProtocol(ips[0]) == protocol {
×
1668
                                        localPodIPs[meta] = append(localPodIPs[meta], ips[0])
×
1669
                                } else {
×
1670
                                        localPodIPs[meta] = append(localPodIPs[meta], ips[1])
×
1671
                                }
×
1672
                        } else if len(egw) == 2 && len(ips) == 2 {
×
UNCOV
1673
                                meta.gateway = egw[1]
×
UNCOV
1674
                                localPodIPs[meta] = append(localPodIPs[meta], ips[1])
×
1675
                        }
×
1676
                }
1677
        }
1678

1679
        return localPodIPs, nil
×
1680
}
1681

1682
func (c *Controller) getSubnetsNeedPR(protocol string) (map[policyRouteMeta]string, error) {
×
1683
        subnetsNeedPR := make(map[policyRouteMeta]string)
×
1684
        subnets, err := c.subnetsLister.List(labels.Everything())
×
1685
        if err != nil {
×
1686
                klog.Errorf("failed to list subnets: %v", err)
×
UNCOV
1687
                return nil, err
×
UNCOV
1688
        }
×
1689

1690
        node, err := c.nodesLister.Get(c.config.NodeName)
×
1691
        if err != nil {
×
1692
                klog.Errorf("failed to get node %s: %v", c.config.NodeName, err)
×
1693
                return nil, err
×
1694
        }
×
1695

1696
        for _, subnet := range subnets {
×
1697
                if !subnet.DeletionTimestamp.IsZero() ||
×
1698
                        subnet.Spec.ExternalEgressGateway == "" ||
×
UNCOV
1699
                        (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway) ||
×
1700
                        subnet.Spec.GatewayType != kubeovnv1.GWCentralizedType ||
×
1701
                        subnet.Spec.Vpc != c.config.ClusterRouter ||
×
1702
                        (subnet.Spec.Protocol != kubeovnv1.ProtocolDual && subnet.Spec.Protocol != protocol) {
×
1703
                        continue
×
1704
                }
1705

1706
                isGatewayNode := util.GatewayContains(subnet.Spec.GatewayNode, c.config.NodeName) ||
×
1707
                        (subnet.Spec.GatewayNode == "" && util.MatchLabelSelectors(subnet.Spec.GatewayNodeSelectors, node.Labels))
×
1708
                if !isGatewayNode {
×
1709
                        continue
×
1710
                }
1711

UNCOV
1712
                meta := policyRouteMeta{
×
1713
                        priority: subnet.Spec.PolicyRoutingPriority,
×
1714
                        tableID:  subnet.Spec.PolicyRoutingTableID,
×
1715
                }
×
1716
                egw := strings.Split(subnet.Spec.ExternalEgressGateway, ",")
×
1717
                if util.CheckProtocol(subnet.Spec.CIDRBlock) == kubeovnv1.ProtocolDual && protocol == kubeovnv1.ProtocolIPv6 {
×
UNCOV
1718
                        if len(egw) == 2 {
×
1719
                                meta.gateway = egw[1]
×
1720
                        } else if util.CheckProtocol(egw[0]) == protocol {
×
1721
                                meta.gateway = egw[0]
×
1722
                        }
×
1723
                } else {
×
1724
                        meta.gateway = egw[0]
×
1725
                }
×
UNCOV
1726
                if meta.gateway != "" {
×
UNCOV
1727
                        cidrBlock, err := getCidrByProtocol(subnet.Spec.CIDRBlock, protocol)
×
UNCOV
1728
                        if err == nil && cidrBlock != "" {
×
1729
                                subnetsNeedPR[meta] = cidrBlock
×
1730
                        }
×
1731
                }
1732
        }
1733

1734
        return subnetsNeedPR, nil
×
1735
}
1736

1737
func (c *Controller) deleteObsoleteSnatRules(ipt *iptables.IPTables, table, chain string) error {
×
1738
        rules, err := ipt.List(table, chain)
×
1739
        if err != nil {
×
UNCOV
1740
                klog.Errorf("failed to list iptables rules in table %v chain %v, %+v", table, chain, err)
×
UNCOV
1741
                return err
×
1742
        }
×
1743

1744
        for _, rule := range rules {
×
1745
                if !strings.Contains(rule, "--to-source") {
×
1746
                        continue
×
1747
                }
1748

1749
                // "-A POSTROUTING -s 100.168.10.0/24 -m set ! --match-set ovn40subnets dst -j SNAT --to-source 172.17.0.3"
UNCOV
1750
                rule := rule[4+len(chain):]
×
UNCOV
1751
                spec := util.DoubleQuotedFields(rule)
×
1752
                if err = ipt.Delete(table, chain, spec...); err != nil {
×
UNCOV
1753
                        klog.Errorf(`failed to delete iptables rule "%s": %v`, rule, err)
×
UNCOV
1754
                        return err
×
1755
                }
×
1756
        }
1757

1758
        return nil
×
1759
}
1760

UNCOV
1761
func (c *Controller) ipsetExists(name string) (bool, error) {
×
1762
        sets, err := c.k8sipsets.ListSets()
×
1763
        if err != nil {
×
1764
                return false, fmt.Errorf("failed to list ipset names: %w", err)
×
1765
        }
×
1766

1767
        return slices.Contains(sets, name), nil
×
1768
}
1769

1770
func getNatOutGoingPolicyRuleIPSetName(ruleID, srcOrDst, protocol string, hasPrefix bool) string {
×
1771
        prefix := ""
×
1772

×
1773
        if hasPrefix {
×
UNCOV
1774
                prefix = "ovn40"
×
UNCOV
1775
                if protocol == kubeovnv1.ProtocolIPv6 {
×
1776
                        prefix = "ovn60"
×
1777
                }
×
1778
        }
1779

UNCOV
1780
        return prefix + NatOutGoingPolicyRuleSet + fmt.Sprintf("%s-%s", ruleID, srcOrDst)
×
1781
}
1782

UNCOV
1783
func isNatOutGoingPolicyRuleIPSet(ipsetName string) bool {
×
UNCOV
1784
        return strings.HasPrefix(ipsetName, "ovn40"+NatOutGoingPolicyRuleSet) ||
×
1785
                strings.HasPrefix(ipsetName, "ovn60"+NatOutGoingPolicyRuleSet)
×
1786
}
×
1787

1788
func getNatOutGoingPolicyRuleIPSetItem(ipsetName string) (string, string) {
×
1789
        items := strings.Split(ipsetName[len("ovn40")+len(NatOutGoingPolicyRuleSet):], "-")
×
1790
        ruleID := items[0]
×
1791
        srcOrDst := items[1]
×
1792
        return ruleID, srcOrDst
×
1793
}
×
1794

1795
func getNatPolicySubnetChainUID(chainName string) string {
×
1796
        return chainName[len(OvnNatOutGoingPolicySubnet):]
×
1797
}
×
1798

1799
func formatIPsetUnPrefix(ipsetName string) string {
×
UNCOV
1800
        return ipsetName[len("ovn40"):]
×
UNCOV
1801
}
×
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