slackhq/nebula · error

could not enable TCP SACK

Error message

could not enable TCP SACK: %v

What it means

In netstack New (service/service.go), after creating the gVisor ipstack, TCP SACK is enabled via SetTransportProtocolOption; any tcpip error is fatal and reported as this message. SACK is required for the tunneled TCP stack to behave well, so the library fails fast.

Solutions

  1. Ensure tcp.NewProtocol is included in TransportProtocols when creating the stack
  2. Update or pin the gVisor dependency to a version compatible with this code
  3. Rebuild with the unmodified stack configuration from service/service.go
  4. Capture the wrapped tcpipErr (%v) to identify the exact tcpip error code

Example fix

// before
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6}
// after
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure protocols registered before New:
stacks := []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6}

Try / catch

svc, err := service.New(...)
if err != nil {
    if strings.Contains(err.Error(), "TCP SACK") {
        log.Fatalf("netstack TCP setup failed (check gVisor deps/protocol registration): %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: service.New is called (via doService, run, or newSimpleService) and the gVisor netstack rejects the TCPSACKEnabled option — practically only when the TCP protocol was not registered on the stack or a gVisor version/API incompatibility exists.

Common situations: Custom builds where tcp.NewProtocol was dropped from TransportProtocols; vendored gVisor fork with different option semantics; upstream gVisor API change breaking SetTransportProtocolOption.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/8b4ba36057a90da1. Report an issue: GitHub.

Appendix: source

Thrown at service/service.go:80

		}
	}()

	ctx := control.Context()
	eg, ctx := errgroup.WithContext(ctx)
	s := Service{
		eg:      eg,
		control: control,
	}
	s.mu.listeners = map[uint16]*tcpListener{}

	s.ipstack = stack.New(stack.Options{
		NetworkProtocols:   []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
		TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6},
	})
	sackEnabledOpt := tcpip.TCPSACKEnabled(true) // TCP SACK is disabled by default
	tcpipErr := s.ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &sackEnabledOpt)
	if tcpipErr != nil {
		return nil, fmt.Errorf("could not enable TCP SACK: %v", tcpipErr)
	}
	linkEP := channel.New( /*size*/ 512 /*mtu*/, 1280, "")
	if tcpipProblem := s.ipstack.CreateNIC(nicID, linkEP); tcpipProblem != nil {
		return nil, fmt.Errorf("could not create netstack NIC: %v", tcpipProblem)
	}
	ipv4Subnet, _ := tcpip.NewSubnet(tcpip.AddrFrom4([4]byte{0x00, 0x00, 0x00, 0x00}), tcpip.MaskFrom(strings.Repeat("\x00", 4)))
	s.ipstack.SetRouteTable([]tcpip.Route{
		{
			Destination: ipv4Subnet,
			NIC:         nicID,
		},
	})

	ipNet := device.Networks()
	pa := tcpip.ProtocolAddress{
		AddressWithPrefix: tcpip.AddrFromSlice(ipNet[0].Addr().AsSlice()).WithPrefix(),
		Protocol:          ipv4.ProtocolNumber,
	}

View on GitHub (pinned to dd8f660c0a)