//go:build linux package ims import ( "context" "errors" "fmt" "os/exec" "strings" "sync" ) type linuxIPSecInstaller struct { ipCommand string } func defaultIPSecInstaller() IPSecSAInstaller { return linuxIPSecInstaller{ipCommand: "ip"} } type linuxIPSecHandle struct { mu sync.Mutex ipCommand string config IPSecSAConfig closed bool } func (installer linuxIPSecInstaller) Install(ctx context.Context, config IPSecSAConfig) (IPSecSAHandle, error) { command := installer.ipCommand if command == "" { command = "ip" } if _, err := exec.LookPath(command); err != nil { return nil, errors.New("ims: Linux iproute2 is required for ipsec-3gpp") } install, err := buildXFRMInstallPlan(config) if err != nil { return nil, err } handle := &linuxIPSecHandle{ ipCommand: command, config: cloneIPSecSAConfig(config), } for _, operation := range install { if err := runIPCommand(ctx, command, operation); err != nil { _ = handle.cleanup(context.Background()) zeroBytes(handle.config.EncryptionKey) zeroBytes(handle.config.IntegrityKey) return nil, fmt.Errorf("%w: %v", ErrIPSecInstall, err) } } zeroBytes(handle.config.EncryptionKey) zeroBytes(handle.config.IntegrityKey) return handle, nil } func (handle *linuxIPSecHandle) Close(ctx context.Context) error { handle.mu.Lock() defer handle.mu.Unlock() if handle.closed { return nil } handle.closed = true return handle.cleanup(ctx) } func (handle *linuxIPSecHandle) cleanup(ctx context.Context) error { var cleanupErrors []error for _, operation := range buildXFRMCleanupPlan(handle.config) { if err := runIPCommand(ctx, handle.ipCommand, operation); err != nil { cleanupErrors = append(cleanupErrors, err) } } return errors.Join(cleanupErrors...) } func runIPCommand(ctx context.Context, command string, operation xfrmOperation) error { output, err := exec.CommandContext(ctx, command, operation.arguments...).CombinedOutput() if err == nil { return nil } message := strings.TrimSpace(string(output)) if message == "" { message = err.Error() } // Operation descriptions contain no SPI keys or subscriber identity. return fmt.Errorf("%s: %s", operation.description, message) }