-
Notifications
You must be signed in to change notification settings - Fork 346
/
iptable_rule.go
165 lines (148 loc) · 5.64 KB
/
iptable_rule.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//go:build !windows
// +build !windows
// Copyright 2020 Antrea Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rules
import (
"bytes"
"fmt"
"k8s.io/klog/v2"
"antrea.io/antrea/pkg/agent/util/iptables"
)
// InitRules initializes rules based on the underlying implementation
func InitRules() PodPortRules {
// This can be extended based on the system capability.
return NewIPTableRules()
}
// NodePortLocalChain is the name of the chain in IPTABLES for Node Port Local
const NodePortLocalChain = "ANTREA-NODE-PORT-LOCAL"
// IPTableRules provides a client to perform IPTABLES operations
type iptablesRules struct {
name string
table *iptables.Client
}
// NewIPTableRules retruns a new instance of IPTableRules
func NewIPTableRules() *iptablesRules {
iptInstance, _ := iptables.New(true, false)
iptRule := iptablesRules{
name: "NPL",
table: iptInstance,
}
return &iptRule
}
// Init initializes IPTABLES rules for NPL. Currently it deletes existing rules to ensure that no stale entries are present.
func (ipt *iptablesRules) Init() error {
if err := ipt.initRules(); err != nil {
return fmt.Errorf("initialization of NPL iptables rules failed: %v", err)
}
return nil
}
// initRules creates the NPL chain and links it to the PREROUTING (for incoming
// traffic) and OUTPUT chain (for locally-generated traffic). All NPL DNAT rules
// will be added to this chain.
func (ipt *iptablesRules) initRules() error {
if err := ipt.table.EnsureChain(iptables.ProtocolIPv4, iptables.NATTable, NodePortLocalChain); err != nil {
return err
}
ruleSpec := []string{
"-p", "all", "-m", "addrtype", "--dst-type", "LOCAL", "-j", NodePortLocalChain,
}
if err := ipt.table.AppendRule(iptables.ProtocolIPv4, iptables.NATTable, iptables.PreRoutingChain, ruleSpec); err != nil {
return err
}
if err := ipt.table.AppendRule(iptables.ProtocolIPv4, iptables.NATTable, iptables.OutputChain, ruleSpec); err != nil {
return err
}
return nil
}
func buildRuleForPod(port int, podIP, protocol string) []string {
return []string{
"-p", protocol, "-m", protocol, "--dport", fmt.Sprint(port),
"-j", "DNAT", "--to-destination", podIP,
}
}
// AddRule appends a DNAT rule in NodePortLocalChain chain of NAT table.
func (ipt *iptablesRules) AddRule(nodePort int, podIP string, podPort int, protocol string) error {
podAddr := fmt.Sprintf("%s:%d", podIP, podPort)
rule := buildRuleForPod(nodePort, podAddr, protocol)
if err := ipt.table.AppendRule(iptables.ProtocolIPv4, iptables.NATTable, NodePortLocalChain, rule); err != nil {
return err
}
klog.InfoS("Successfully added DNAT rule", "podAddr", podAddr, "nodePort", nodePort, "protocol", protocol)
return nil
}
// AddAllRules constructs a list of iptables rules for the NPL chain and performs a
// iptables-restore on this chain. It uses --no-flush to keep the previous rules intact.
func (ipt *iptablesRules) AddAllRules(nplList []PodNodePort) error {
iptablesData := bytes.NewBuffer(nil)
writeLine(iptablesData, "*nat")
writeLine(iptablesData, iptables.MakeChainLine(NodePortLocalChain))
for _, nplData := range nplList {
for _, protocol := range nplData.Protocols {
destination := nplData.PodIP + ":" + fmt.Sprint(nplData.PodPort)
rule := buildRuleForPod(nplData.NodePort, destination, protocol)
writeLine(iptablesData, append([]string{"-A", NodePortLocalChain}, rule...)...)
}
}
writeLine(iptablesData, "COMMIT")
if err := ipt.table.Restore(iptablesData.Bytes(), false, false); err != nil {
return err
}
return nil
}
// DeleteRule deletes a specific NPL rule from NodePortLocalChain chain
func (ipt *iptablesRules) DeleteRule(nodePort int, podIP string, podPort int, protocol string) error {
podAddr := fmt.Sprintf("%s:%d", podIP, podPort)
rule := buildRuleForPod(nodePort, podAddr, protocol)
if err := ipt.table.DeleteRule(iptables.ProtocolIPv4, iptables.NATTable, NodePortLocalChain, rule); err != nil {
return err
}
klog.InfoS("Successfully deleted DNAT rule", "podAddr", podAddr, "nodePort", nodePort, "protocol", protocol)
return nil
}
// DeleteAllRules deletes all NPL rules programmed in the node
func (ipt *iptablesRules) DeleteAllRules() error {
exists, err := ipt.table.ChainExists(iptables.ProtocolIPv4, iptables.NATTable, NodePortLocalChain)
if err != nil {
return fmt.Errorf("failed to check if NodePortLocal chain exists in NAT table: %v", err)
}
if !exists {
return nil
}
ruleSpec := []string{
"-p", "all", "-m", "addrtype", "--dst-type", "LOCAL", "-j", NodePortLocalChain,
}
if err := ipt.table.DeleteRule(iptables.ProtocolIPv4, iptables.NATTable, iptables.PreRoutingChain, ruleSpec); err != nil {
return err
}
if err := ipt.table.DeleteRule(iptables.ProtocolIPv4, iptables.NATTable, iptables.OutputChain, ruleSpec); err != nil {
return err
}
if err := ipt.table.DeleteChain(iptables.ProtocolIPv4, iptables.NATTable, NodePortLocalChain); err != nil {
return err
}
return nil
}
// Join all words with spaces, terminate with newline and write to buf.
func writeLine(buf *bytes.Buffer, words ...string) {
// We avoid strings.Join for performance reasons.
for i := range words {
buf.WriteString(words[i])
if i < len(words)-1 {
buf.WriteByte(' ')
} else {
buf.WriteByte('\n')
}
}
}