slackhq/nebula · error

could not create netstack NIC

Error message

could not create netstack NIC: %v

What it means

service.New creates a channel-backed NIC in the gVisor netstack via CreateNIC(nicID, linkEP); a non-nil tcpip error aborts startup with this message. Without the NIC there is no link endpoint to route tunneled packets through.

Solutions

  1. Ensure service.New creates its own fresh ipstack and uses the constant nicID only once
  2. If reusing a stack, pick a unique nicID or call NIC removal/Disable first
  3. Pin/upgrade the gVisor dependency to match the CreateNIC API this code expects
  4. Log the wrapped tcpipProblem to identify the exact error (e.g. ErrDuplicateNIC)

Example fix

// before
s.ipstack.CreateNIC(nicID, linkEP) // nicID already registered on this stack
// after
s.ipstack.RemoveNIC(nicID) // or use a fresh stack per service.New
s.ipstack.CreateNIC(nicID, linkEP)
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure one NIC per stack:
// create a fresh ipstack in service.New; do not share across instances

Try / catch

svc, err := service.New(...)
if err != nil {
    if strings.Contains(err.Error(), "netstack NIC") {
        log.Fatalf("NIC creation failed (duplicate nicID or gVisor API change): %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: service.New is called and CreateNIC fails — typically duplicate nicID registration on the same stack, or a gVisor version where channel.New/endpoint registration semantics changed.

Common situations: Calling New twice on the same shared ipstack with the same nicID; custom netstack assembly code reusing nicID; gVisor API mismatch after dependency upgrade.

Related errors


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

Appendix: source

Thrown at service/service.go:84

	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,
	}
	if err := s.ipstack.AddProtocolAddress(nicID, pa, stack.AddressProperties{
		PEB:        stack.CanBePrimaryEndpoint, // zero value default
		ConfigType: stack.AddressConfigStatic,  // zero value default
	}); err != nil {

View on GitHub (pinned to dd8f660c0a)