Skip to content

Commit 77c92b3

Browse files
authored
[NPM] feat: use iptables nft for linux (#1937)
* [NPM] feat: use iptables nft for linux * adding the capability to detect nft * fixing an issue * fixing same issue * cleaning up old state * fixing UTs * correcting tests * fixing windows lint
1 parent 41f451a commit 77c92b3

File tree

8 files changed

+209
-27
lines changed

8 files changed

+209
-27
lines changed

npm/pkg/dataplane/dataplane_linux.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dataplane
22

33
import (
44
"github.com/Azure/azure-container-networking/npm/pkg/dataplane/policies"
5+
"github.com/Azure/azure-container-networking/npm/util"
56
npmerrors "github.com/Azure/azure-container-networking/npm/util/errors"
67
)
78

@@ -20,6 +21,8 @@ func (dp *DataPlane) updatePod(pod *updateNPMPod) error {
2021
}
2122

2223
func (dp *DataPlane) bootupDataPlane() error {
24+
util.DetectIptablesVersion(dp.ioShim)
25+
2326
// It is important to keep order to clean-up ACLs before ipsets. Otherwise we won't be able to delete ipsets referenced by ACLs
2427
if err := dp.policyMgr.Bootup(nil); err != nil {
2528
return npmerrors.ErrorWrapper(npmerrors.BootupDataplane, false, "failed to reset policy dataplane", err)

npm/pkg/dataplane/dataplane_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ func TestUpdatePodCache(t *testing.T) {
393393
}
394394

395395
func getBootupTestCalls() []testutils.TestCmd {
396-
return append(policies.GetBootupTestCalls(), ipsets.GetResetTestCalls()...)
396+
return append(policies.GetBootupTestCalls(true), ipsets.GetResetTestCalls()...)
397397
}
398398

399399
func getAddPolicyTestCallsForDP(networkPolicy *policies.NPMNetworkPolicy) []testutils.TestCmd {

npm/pkg/dataplane/policies/chain-management_linux.go

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -147,23 +147,24 @@ func isBaseChain(chain string) bool {
147147
}
148148

149149
/*
150-
Called once at startup.
151-
Like the rest of PolicyManager, minimizes the number of OS calls by consolidating all possible actions into one iptables-restore call.
152-
153-
1. Delete the deprecated jump from FORWARD to AZURE-NPM chain (if it exists).
154-
2. Cleanup old NPM chains, and configure base chains and their rules.
155-
1. Do the following via iptables-restore --noflush:
156-
- flush all deprecated chains
157-
- flush old v2 policy chains
158-
- create/flush the base chains
159-
- add rules for the base chains, except for AZURE-NPM (so that PolicyManager will be deactivated)
160-
2. In the background:
161-
- delete all deprecated chains
162-
- delete old v2 policy chains
163-
3. Add/reposition the jump from FORWARD chain to AZURE-NPM chain.
164-
165-
TODO: could use one grep call instead of separate calls for getting jump line nums and for getting deprecated chains and old v2 policy chains
166-
- would use a grep pattern like so: <line num...AZURE-NPM>|<Chain AZURE-NPM>
150+
Called once at startup.
151+
Like the rest of PolicyManager, minimizes the number of OS calls by consolidating all possible actions into one iptables-restore call.
152+
153+
1. Delete the deprecated jump from FORWARD to AZURE-NPM chain (if it exists).
154+
2. Cleanup old NPM chains, and configure base chains and their rules.
155+
1. Do the following via iptables-restore --noflush:
156+
- flush all deprecated chains
157+
- flush old v2 policy chains
158+
- create/flush the base chains
159+
- add rules for the base chains, except for AZURE-NPM (so that PolicyManager will be deactivated)
160+
2. In the background:
161+
- delete all deprecated chains
162+
- delete old v2 policy chains
163+
164+
3. Add/reposition the jump from FORWARD chain to AZURE-NPM chain.
165+
166+
TODO: could use one grep call instead of separate calls for getting jump line nums and for getting deprecated chains and old v2 policy chains
167+
- would use a grep pattern like so: <line num...AZURE-NPM>|<Chain AZURE-NPM>
167168
*/
168169
func (pMgr *PolicyManager) bootup(_ []string) error {
169170
klog.Infof("booting up iptables Azure chains")
@@ -173,6 +174,79 @@ func (pMgr *PolicyManager) bootup(_ []string) error {
173174
pMgr.reconcileManager.forceLock()
174175
defer pMgr.reconcileManager.forceUnlock()
175176

177+
if strings.Contains(util.Iptables, "nft") {
178+
util.Iptables = util.IptablesLegacy
179+
util.IptablesSave = util.IptablesSaveLegacy
180+
util.IptablesRestore = util.IptablesRestoreLegacy
181+
182+
// 0. delete the deprecated jump to deprecated AZURE-NPM in legacy iptables
183+
deprecatedErrCode, deprecatedErr := pMgr.ignoreErrorsAndRunIPTablesCommand(removeDeprecatedJumpIgnoredErrors, util.IptablesDeletionFlag, deprecatedJumpFromForwardToAzureChainArgs...)
184+
if deprecatedErrCode == 0 {
185+
klog.Infof("deleted deprecated jump rule from FORWARD chain to AZURE-NPM chain")
186+
} else if deprecatedErr != nil {
187+
metrics.SendErrorLogAndMetric(util.IptmID,
188+
"failed to delete deprecated jump rule from FORWARD chain to AZURE-NPM chain for unexpected reason with exit code %d and error: %s",
189+
deprecatedErrCode, deprecatedErr.Error())
190+
}
191+
192+
// 0. delete the deprecated jump to current AZURE-NPM in legacy iptables
193+
deprecatedErrCode, deprecatedErr = pMgr.ignoreErrorsAndRunIPTablesCommand(removeDeprecatedJumpIgnoredErrors, util.IptablesDeletionFlag, jumpFromForwardToAzureChainArgs...)
194+
if deprecatedErrCode == 0 {
195+
klog.Infof("deleted deprecated jump rule from FORWARD chain to AZURE-NPM chain")
196+
} else if deprecatedErr != nil {
197+
metrics.SendErrorLogAndMetric(util.IptmID,
198+
"failed to delete deprecated jump rule from FORWARD chain to AZURE-NPM chain for unexpected reason with exit code %d and error: %s",
199+
deprecatedErrCode, deprecatedErr.Error())
200+
}
201+
202+
// clean up current chains in legacy iptables
203+
currentChains, err := ioutil.AllCurrentAzureChains(pMgr.ioShim.Exec, util.IptablesDefaultWaitTime)
204+
if err != nil {
205+
return npmerrors.SimpleErrorWrapper("failed to get current chains for bootup", err)
206+
}
207+
208+
// We have only one chance to clean existing legacy iptables chains.
209+
// So flush all the chains and then destroy them
210+
var aggregateError error
211+
for chain := range currentChains {
212+
errCode, err := pMgr.runIPTablesCommand(util.IptablesFlushFlag, chain)
213+
if err != nil && errCode != doesNotExistErrorCode {
214+
// add to staleChains if it's not one of the iptablesAzureChains
215+
pMgr.staleChains.add(chain)
216+
currentErrString := fmt.Sprintf("failed to flush chain %s with err [%v]", chain, err)
217+
if aggregateError == nil {
218+
aggregateError = npmerrors.SimpleError(currentErrString)
219+
} else {
220+
aggregateError = npmerrors.SimpleErrorWrapper(fmt.Sprintf("%s and had previous error", currentErrString), aggregateError)
221+
}
222+
}
223+
}
224+
225+
for chain := range currentChains {
226+
errCode, err := pMgr.runIPTablesCommand(util.IptablesDestroyFlag, chain)
227+
if err != nil && errCode != doesNotExistErrorCode {
228+
// add to staleChains if it's not one of the iptablesAzureChains
229+
pMgr.staleChains.add(chain)
230+
currentErrString := fmt.Sprintf("failed to delete chain %s with err [%v]", chain, err)
231+
if aggregateError == nil {
232+
aggregateError = npmerrors.SimpleError(currentErrString)
233+
} else {
234+
aggregateError = npmerrors.SimpleErrorWrapper(fmt.Sprintf("%s and had previous error", currentErrString), aggregateError)
235+
}
236+
}
237+
}
238+
239+
if aggregateError != nil {
240+
metrics.SendErrorLogAndMetric(util.IptmID,
241+
"failed to flush and delete stale chain in legacy iptables with error: %s",
242+
aggregateError.Error())
243+
}
244+
245+
util.Iptables = util.IptablesNft
246+
util.IptablesSave = util.IptablesSaveNft
247+
util.IptablesRestore = util.IptablesRestoreNft
248+
}
249+
176250
// 1. delete the deprecated jump to AZURE-NPM
177251
deprecatedErrCode, deprecatedErr := pMgr.ignoreErrorsAndRunIPTablesCommand(removeDeprecatedJumpIgnoredErrors, util.IptablesDeletionFlag, deprecatedJumpFromForwardToAzureChainArgs...)
178252
if deprecatedErrCode == 0 {

npm/pkg/dataplane/policies/chain-management_linux_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ func TestBootupLinux(t *testing.T) {
365365
// but the fake command exit codes and stdouts are in line with having no NPM prior
366366
{
367367
name: "success (no NPM prior)",
368-
calls: GetBootupTestCalls(),
368+
calls: GetBootupTestCalls(false),
369369
wantErr: false,
370370
},
371371
{

npm/pkg/dataplane/policies/policymanager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func (p promVals) testPrometheusMetrics(t *testing.T) {
101101
// see chain-management_linux_test.go for testing when an error occurs
102102
func TestBootup(t *testing.T) {
103103
metrics.ReinitializeAll()
104-
calls := GetBootupTestCalls()
104+
calls := GetBootupTestCalls(false)
105105
ioshim := common.NewMockIOShim(calls)
106106
defer ioshim.VerifyCalls(t, calls)
107107
pMgr := NewPolicyManager(ioshim, ipsetConfig)

npm/pkg/dataplane/policies/testutils_linux.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,22 @@ func GetRemovePolicyFailureTestCalls(policy *NPMNetworkPolicy) []testutils.TestC
5050
return append(calls, fakeIPTablesRestoreFailureCommand)
5151
}
5252

53-
func GetBootupTestCalls() []testutils.TestCmd {
54-
return []testutils.TestCmd{
53+
func GetBootupTestCalls(addDetectCalls bool) []testutils.TestCmd {
54+
detectIptable := []testutils.TestCmd{
55+
{Cmd: []string{"iptables-nft-save", "-t", "mangle"}, Stdout: ""}, //nolint // AZURE-NPM chain didn't exist
56+
{Cmd: []string{"iptables-save", "-t", "mangle"}, Stdout: `# Generated by iptables-save v1.8.7 on Wed May 3 01:35:24 2023
57+
*mangle
58+
:PREROUTING ACCEPT [0:0]
59+
:INPUT ACCEPT [0:0]
60+
:FORWARD ACCEPT [0:0]
61+
:OUTPUT ACCEPT [0:0]
62+
:POSTROUTING ACCEPT [0:0]
63+
:KUBE-IPTABLES-HINT - [0:0]
64+
:KUBE-KUBELET-CANARY - [0:0]
65+
:KUBE-PROXY-CANARY - [0:0]
66+
COMMIT`}, //nolint // AZURE-NPM chain didn't exist
67+
}
68+
bootUp := []testutils.TestCmd{
5569
{Cmd: []string{"iptables", "-w", "60", "-D", "FORWARD", "-j", "AZURE-NPM"}, ExitCode: 2}, //nolint // AZURE-NPM chain didn't exist
5670
{Cmd: listAllCommandStrings, PipedToCommand: true},
5771
{
@@ -63,6 +77,11 @@ func GetBootupTestCalls() []testutils.TestCmd {
6377
{Cmd: []string{"grep", "AZURE-NPM"}, ExitCode: 1},
6478
{Cmd: []string{"iptables", "-w", "60", "-I", "FORWARD", "-j", "AZURE-NPM", "-m", "conntrack", "--ctstate", "NEW"}},
6579
}
80+
81+
if addDetectCalls {
82+
return append(detectIptable, bootUp...)
83+
}
84+
return bootUp
6685
}
6786

6887
func getFakeDeleteJumpCommand(chainName, jumpRule string) testutils.TestCmd {

npm/pkg/dataplane/policies/testutils_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ func GetRemovePolicyTestCalls(_ *NPMNetworkPolicy) []testutils.TestCmd {
1010
return []testutils.TestCmd{}
1111
}
1212

13-
func GetBootupTestCalls() []testutils.TestCmd {
13+
func GetBootupTestCalls(_ bool) []testutils.TestCmd {
1414
return []testutils.TestCmd{}
1515
}

npm/util/const.go

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
// MIT License
33
package util
44

5+
import (
6+
"bytes"
7+
"fmt"
8+
"strings"
9+
10+
"github.com/Azure/azure-container-networking/common"
11+
)
12+
513
// kubernetes related constants.
614
const (
715
KubeSystemFlag string = "kube-system"
@@ -19,15 +27,25 @@ const (
1927
k8sMinorVerForNewPolicyDef string = "11"
2028
)
2129

30+
var (
31+
Iptables = IptablesLegacy
32+
Ip6tables = Ip6tablesLegacy //nolint (avoid warning to capitalize this p)
33+
IptablesSave = IptablesSaveLegacy
34+
IptablesRestore = IptablesRestoreLegacy
35+
)
36+
2237
// iptables related constants.
2338
const (
2439
PlaceAzureChainAfterKubeServices = false
2540
PlaceAzureChainFirst = true
2641

27-
Iptables string = "iptables"
28-
Ip6tables string = "ip6tables" //nolint (avoid warning to capitalize this p)
29-
IptablesSave string = "iptables-save"
30-
IptablesRestore string = "iptables-restore"
42+
IptablesNft string = "iptables-nft"
43+
Ip6tablesLegacy string = "ip6tables" //nolint (avoid warning to capitalize this p)
44+
IptablesSaveNft string = "iptables-nft-save"
45+
IptablesRestoreNft string = "iptables-nft-restore"
46+
IptablesLegacy string = "iptables"
47+
IptablesSaveLegacy string = "iptables-save"
48+
IptablesRestoreLegacy string = "iptables-restore"
3149
IptablesRestoreNoFlushFlag string = "--noflush"
3250
IptablesRestoreTableFlag string = "-T"
3351
IptablesRestoreCommit string = "COMMIT"
@@ -253,3 +271,71 @@ const (
253271
DaemonDataplaneID // for v2
254272
FanOutServerID // for v2
255273
)
274+
275+
func DetectIptablesVersion(ioShim *common.IOShim) {
276+
cmd := ioShim.Exec.Command(IptablesSaveNft, "-t", "mangle")
277+
278+
output, err := cmd.CombinedOutput()
279+
if err != nil {
280+
fmt.Printf("Error running iptables-nft-save: %s", err)
281+
return
282+
}
283+
284+
if strings.Contains(string(output), "KUBE-IPTABLES-HINT") || strings.Contains(string(output), "KUBE-KUBELET-CANARY") {
285+
Iptables = IptablesNft
286+
IptablesSave = IptablesSaveNft
287+
IptablesRestore = IptablesRestoreNft
288+
} else {
289+
lCmd := ioShim.Exec.Command(IptablesSaveLegacy, "-t", "mangle")
290+
291+
loutput, err := lCmd.CombinedOutput()
292+
if err != nil {
293+
fmt.Printf("Error running iptables-legacy-save: %s", err)
294+
return
295+
}
296+
297+
if strings.Contains(string(loutput), "KUBE-IPTABLES-HINT") || strings.Contains(string(loutput), "KUBE-KUBELET-CANARY") {
298+
Iptables = IptablesLegacy
299+
IptablesSave = IptablesSaveLegacy
300+
IptablesRestore = IptablesRestoreLegacy
301+
} else {
302+
lsavecmd := ioShim.Exec.Command(IptablesSaveNft)
303+
lsaveoutput, err := lsavecmd.CombinedOutput()
304+
if err != nil {
305+
fmt.Printf("Error running iptables-nft-save: %s", err)
306+
return
307+
}
308+
309+
lcount := countLines(lsaveoutput)
310+
311+
savecmd := ioShim.Exec.Command(IptablesSaveLegacy)
312+
saveoutput, err := savecmd.CombinedOutput()
313+
if err != nil {
314+
fmt.Printf("Error running iptables-legacy-save: %s", err)
315+
return
316+
}
317+
318+
count := countLines(saveoutput)
319+
320+
if lcount > count {
321+
Iptables = IptablesLegacy
322+
IptablesSave = IptablesSaveLegacy
323+
IptablesRestore = IptablesRestoreLegacy
324+
} else {
325+
Iptables = IptablesNft
326+
IptablesSave = IptablesSaveNft
327+
IptablesRestore = IptablesRestoreNft
328+
}
329+
}
330+
}
331+
}
332+
333+
func countLines(output []byte) int {
334+
count := 0
335+
for _, x := range bytes.Split(output, []byte("\n")) {
336+
if len(x) >= 1 && x[0] == '-' {
337+
count++
338+
}
339+
}
340+
return count
341+
}

0 commit comments

Comments
 (0)