Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(capture): ignore known copy failure and fix iptables issue #903

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 48 additions & 17 deletions pkg/capture/provider/network_capture_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,15 +188,16 @@ func (ncp *NetworkCaptureProvider) CaptureNetworkPacket(filter string, duration,
}

type command struct {
name string
args []string
description string
name string
args []string
description string
ignoreFailure bool
}

func (ncp *NetworkCaptureProvider) CollectMetadata() error {
ncp.l.Info("Start to collect network metadata")

iptablesMode := obtainIptablesMode()
iptablesMode := obtainIptablesMode(ncp.l)
ncp.l.Info(fmt.Sprintf("Iptables mode %s is used", iptablesMode))
iptablesSaveCmdName := fmt.Sprintf("iptables-%s-save", iptablesMode)
iptablesCmdName := fmt.Sprintf("iptables-%s", iptablesMode)
Expand Down Expand Up @@ -282,9 +283,10 @@ func (ncp *NetworkCaptureProvider) CollectMetadata() error {
description: "networking stats",
},
{
name: "cp",
args: []string{"-r", "/proc/sys/net", filepath.Join(ncp.TmpCaptureDir, "proc-sys-net")},
description: "kernel networking configuration",
name: "cp",
args: []string{"-r", "/proc/sys/net", filepath.Join(ncp.TmpCaptureDir, "proc-sys-net")},
description: "kernel networking configuration",
ignoreFailure: true,
},
},
},
Expand Down Expand Up @@ -320,14 +322,15 @@ func (ncp *NetworkCaptureProvider) CollectMetadata() error {
}

// Write command stdout and stderr to output file
for _, cmd := range cmds {
for _, command := range metadata.commands {
cmd := exec.Command(command.name, command.args...)
if _, err := outfile.WriteString(fmt.Sprintf("%s\n\n", cmd.String())); err != nil {
ncp.l.Error("Failed to write string to file", zap.String("file", outfile.Name()), zap.Error(err))
}

cmd.Stdout = outfile
cmd.Stderr = outfile
if err := cmd.Run(); err != nil {
if err := cmd.Run(); err != nil && !command.ignoreFailure {
// Don't return for error to continue capturing following network metadata.
ncp.l.Error("Failed to execute command", zap.String("command", cmd.String()), zap.Error(err))
// Log the error in output file because this error does not stop capture job pod from finishing,
Expand All @@ -342,7 +345,7 @@ func (ncp *NetworkCaptureProvider) CollectMetadata() error {
cmd := exec.Command(command.name, command.args...)
// Errors will when copying kernel networking configuration for not all files under /proc/sys/net are
// readable, like '/proc/sys/net/ipv4/route/flush', which doesn't implement the read function.
if output, err := cmd.CombinedOutput(); err != nil {
if output, err := cmd.CombinedOutput(); err != nil && !command.ignoreFailure {
// Don't return for error to continue capturing following network metadata.
ncp.l.Error("Failed to execute command", zap.String("command", cmd.String()), zap.String("output", string(output)), zap.Error(err))
}
Expand All @@ -361,16 +364,44 @@ func (ncp *NetworkCaptureProvider) Cleanup() error {
return nil
}

func obtainIptablesMode() string {
type iptablesMode string

const (
legacyIptablesMode iptablesMode = "legacy"
nftIptablesMode iptablesMode = "nft"
)

func obtainIptablesMode(l *log.ZapLogger) iptablesMode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should return unhandled errors along with mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ideas in my mind is the failed iptables rules does not break the whole capture.

// Since iptables v1.8, nf_tables are introduced as an improvement of legacy iptables, but provides the same user
// interface as legacy iptables through iptables-nft command.
// based on: https://github.com/kubernetes-sigs/iptables-wrappers/blob/97b01f43a8e8db07840fc4b95e833a37c0d36b12/iptables-wrapper-installer.sh
legacySaveOut, _ := exec.Command("iptables-legacy-save").CombinedOutput()

// when both iptables modes available, we choose the one with more rules.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the thinking behind this? Can you not list all the rules using either one of the modes? Given one is named legacy, shouldn't nft be default?

Copy link
Contributor Author

@mainred mainred Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When both modes available, we should use the one list more rules, and the one list less rules normally just return empty rules.

I am referring to https://github.com/kubernetes-sigs/iptables-wrappers/blob/97b01f43a8e8db07840fc4b95e833a37c0d36b12/iptables-wrapper-installer.sh, which is also mentioned in L377

nftIptablesModeAvaiable := true
legacyIptablesModeAvaiable := true
legacySaveOut, err := exec.Command("iptables-legacy-save").CombinedOutput()
if err != nil && strings.Contains(err.Error(), "command not found") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about errors with running the command? How should we handle them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I treat all command errors as unavailability of that mode and log the error for us to investigate.

timraymond marked this conversation as resolved.
Show resolved Hide resolved
legacyIptablesModeAvaiable = false
}

legacySaveLineNum := len(strings.Split(string(legacySaveOut), "\n"))
nftSaveOut, _ := exec.Command("iptables-nft-save").CombinedOutput()
nftSaveLineNum := len(strings.Split(string(nftSaveOut), "\n"))
if legacySaveLineNum > nftSaveLineNum {
return "legacy"
nftSaveOut, err := exec.Command("iptables-nft-save").CombinedOutput()
if err != nil && strings.Contains(err.Error(), "command not found") {
nftIptablesModeAvaiable = false
}

if nftIptablesModeAvaiable && legacyIptablesModeAvaiable {
nftSaveLineNum := len(strings.Split(string(nftSaveOut), "\n"))
if legacySaveLineNum > nftSaveLineNum {
return legacyIptablesMode
}
return "nft"
mainred marked this conversation as resolved.
Show resolved Hide resolved
}

// when only one iptables mode available, we choose the available one when the other one is not available.
// when both iptables modes are not available, we choose the nft mode and let the iptables command fail.
if !nftIptablesModeAvaiable {
return legacyIptablesMode
}
return "nft"
return nftIptablesMode
}
Loading