Files
Memoh/internal/containerd/network.go
T
BBQ 7730096696 fix(containerd): restore CNI MASQUERADE after container restart (#167)
cni.Remove() failure on stale iptables state blocked the retry
cni.Setup(), leaving bot containers without SNAT/MASQUERADE.

- Ignore cni.Remove() error so retry Setup always runs
- Add global MASQUERADE rule in entrypoints as belt-and-suspenders

Closes #161
2026-03-03 16:00:46 +08:00

115 lines
2.9 KiB
Go

package containerd
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/containerd/containerd/v2/client"
gocni "github.com/containerd/go-cni"
)
func setupCNINetwork(ctx context.Context, task client.Task, containerID string, CNIBinDir string, CNIConfDir string) error {
if task == nil {
return ErrInvalidArgument
}
if containerID == "" {
containerID = task.ID()
}
if containerID == "" {
return ErrInvalidArgument
}
pid := task.Pid()
if pid == 0 {
return fmt.Errorf("task pid not available for %s", containerID)
}
if _, err := os.Stat(CNIConfDir); err != nil {
return fmt.Errorf("cni config dir missing: %s: %w", CNIConfDir, err)
}
if _, err := os.Stat(CNIBinDir); err != nil {
return fmt.Errorf("cni bin dir missing: %s: %w", CNIBinDir, err)
}
netnsPath := filepath.Join("/proc", fmt.Sprint(pid), "ns", "net")
if _, err := os.Stat(netnsPath); err != nil {
return fmt.Errorf("netns not found: %s: %w", netnsPath, err)
}
cni, err := gocni.New(
gocni.WithPluginDir([]string{CNIBinDir}),
gocni.WithPluginConfDir(CNIConfDir),
)
if err != nil {
return err
}
if err := cni.Load(gocni.WithLoNetwork, gocni.WithDefaultConf); err != nil {
return err
}
_, err = cni.Setup(ctx, containerID, netnsPath)
if err != nil {
if !isDuplicateAllocationError(err) {
return err
}
// Stale IPAM allocation (e.g. after container restart with persisted
// /var/lib/cni). Remove may fail if the previous iptables/veth state
// is already gone; ignore the error so the retry Setup still runs.
_ = cni.Remove(ctx, containerID, netnsPath)
_, err = cni.Setup(ctx, containerID, netnsPath)
if err != nil {
return err
}
}
return nil
}
func removeCNINetwork(ctx context.Context, task client.Task, containerID string, CNIBinDir string, CNIConfDir string) error {
if task == nil {
return ErrInvalidArgument
}
if containerID == "" {
containerID = task.ID()
}
if containerID == "" {
return ErrInvalidArgument
}
pid := task.Pid()
if pid == 0 {
return fmt.Errorf("task pid not available for %s", containerID)
}
if _, err := os.Stat(CNIConfDir); err != nil {
return fmt.Errorf("cni config dir missing: %s: %w", CNIConfDir, err)
}
if _, err := os.Stat(CNIBinDir); err != nil {
return fmt.Errorf("cni bin dir missing: %s: %w", CNIBinDir, err)
}
netnsPath := filepath.Join("/proc", fmt.Sprint(pid), "ns", "net")
if _, err := os.Stat(netnsPath); err != nil {
return fmt.Errorf("netns not found: %s: %w", netnsPath, err)
}
cni, err := gocni.New(
gocni.WithPluginDir([]string{CNIBinDir}),
gocni.WithPluginConfDir(CNIConfDir),
)
if err != nil {
return err
}
if err := cni.Load(gocni.WithLoNetwork, gocni.WithDefaultConf); err != nil {
return err
}
return cni.Remove(ctx, containerID, netnsPath)
}
func isDuplicateAllocationError(err error) bool {
if err == nil {
return false
}
return strings.Contains(err.Error(), "duplicate allocation")
}