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 7 commits into
base: main
Choose a base branch
from
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,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a comment as to why this is optional? Also, maybe log this as a warn so that user doesn't go looking for this information in the capture.

},
},
},
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.
anubhabMajumdar marked this conversation as resolved.
Show resolved Hide resolved
nftIptablesModeAvaiable := true
legacyIptablesModeAvaiable := true
legacySaveOut, err := exec.Command("iptables-legacy-save").CombinedOutput()
if err != nil && strings.Contains(err.Error(), "command not found") {
anubhabMajumdar marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Contributor

Choose a reason for hiding this comment

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

I want to highlight this again - we are returning nftIptablesMode even if we know that is not available. This assumes the behavior of the caller and shouldn't be done. We should return error if no mode is available and let the caller handle the error.

}
Loading