Handle ICMP not permitted errors

This commit is contained in:
Quentin McGaw
2025-10-14 17:56:04 +00:00
parent 6c25ee53f1
commit 5428580b8f
5 changed files with 22 additions and 3 deletions

View File

@@ -5,10 +5,12 @@ import (
"errors"
"fmt"
"net"
"strings"
"time"
)
var (
ErrICMPNotPermitted = errors.New("ICMP not permitted")
ErrICMPDestinationUnreachable = errors.New("ICMP destination unreachable")
ErrICMPCommunicationAdministrativelyProhibited = errors.New("communication administratively prohibited")
ErrICMPBodyUnsupported = errors.New("ICMP body type is not supported")
@@ -16,6 +18,8 @@ var (
func wrapConnErr(err error, timedCtx context.Context, pingTimeout time.Duration) error { //nolint:revive
switch {
case strings.HasSuffix(err.Error(), "sendto: operation not permitted"):
err = fmt.Errorf("%w", ErrICMPNotPermitted)
case errors.Is(timedCtx.Err(), context.DeadlineExceeded):
err = fmt.Errorf("%w (timed out after %s)", net.ErrClosed, pingTimeout)
case timedCtx.Err() != nil:

View File

@@ -7,6 +7,7 @@ import (
"net"
"net/netip"
"runtime"
"strings"
"syscall"
"time"
@@ -36,6 +37,9 @@ func listenICMPv4(ctx context.Context) (conn net.PacketConn, err error) {
const listenAddress = ""
packetConn, err := listenConfig.ListenPacket(ctx, "ip4:icmp", listenAddress)
if err != nil {
if strings.HasSuffix(err.Error(), "socket: operation not permitted") {
err = fmt.Errorf("%w: you can try adding NET_RAW capability to resolve this", ErrICMPNotPermitted)
}
return nil, fmt.Errorf("listening for ICMP packets: %w", err)
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/netip"
"strings"
"time"
"golang.org/x/net/icmp"
@@ -21,6 +22,9 @@ func listenICMPv6(ctx context.Context) (conn net.PacketConn, err error) {
const listenAddress = ""
packetConn, err := listenConfig.ListenPacket(ctx, "ip6:ipv6-icmp", listenAddress)
if err != nil {
if strings.HasSuffix(err.Error(), "socket: operation not permitted") {
err = fmt.Errorf("%w: you can try adding NET_RAW capability to resolve this", ErrICMPNotPermitted)
}
return nil, fmt.Errorf("listening for ICMPv6 packets: %w", err)
}
return packetConn, nil

View File

@@ -7,6 +7,7 @@ import (
"math"
"net"
"net/netip"
"strings"
"time"
"golang.org/x/net/icmp"
@@ -87,6 +88,9 @@ func pmtudMultiSizes(ctx context.Context, ip netip.Addr,
conn, err = listenICMPv6(ctx)
}
if err != nil {
if strings.HasSuffix(err.Error(), "socket: operation not permitted") {
err = fmt.Errorf("%w: you can try adding NET_RAW capability to resolve this", ErrICMPNotPermitted)
}
return 0, fmt.Errorf("listening for ICMP packets: %w", err)
}
@@ -120,6 +124,9 @@ func pmtudMultiSizes(ctx context.Context, ip netip.Addr,
_, err = conn.WriteTo(encodedMessage, &net.IPAddr{IP: ip.AsSlice()})
if err != nil {
if strings.HasSuffix(err.Error(), "sendto: operation not permitted") {
err = fmt.Errorf("%w", ErrICMPNotPermitted)
}
return 0, fmt.Errorf("writing ICMP message: %w", err)
}
}

View File

@@ -120,10 +120,10 @@ func updateToMaxMTU(ctx context.Context, vpnInterface string,
vpnLinkMTU, err = pmtud.PathMTUDiscover(ctx, vpnGatewayIP, vpnLinkMTU, pingTimeout, logger)
switch {
case err == nil:
logger.Infof("Setting VPN interface %s MTU to maximum valid MTU %d", vpnInterface, vpnLinkMTU)
case errors.Is(err, pmtud.ErrMTUNotFound):
logger.Infof("setting VPN interface %s MTU to maximum valid MTU %d", vpnInterface, vpnLinkMTU)
case errors.Is(err, pmtud.ErrMTUNotFound) || errors.Is(err, pmtud.ErrICMPNotPermitted):
vpnLinkMTU = int(originalMTU)
logger.Infof("Reverting VPN interface %s MTU to %d (due to: %s)",
logger.Infof("reverting VPN interface %s MTU to %d (due to: %s)",
vpnInterface, originalMTU, err)
default:
return fmt.Errorf("path MTU discovering: %w", err)