github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/socket/netstack/stack.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package netstack
    16  
    17  import (
    18  	"fmt"
    19  	"time"
    20  
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/log"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/refs"
    25  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/inet"
    26  	"github.com/nicocha30/gvisor-ligolo/pkg/syserr"
    27  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip"
    28  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/header"
    29  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/network/ipv4"
    30  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/network/ipv6"
    31  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/stack"
    32  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/transport/tcp"
    33  )
    34  
    35  // Stack implements inet.Stack for netstack/tcpip/stack.Stack.
    36  //
    37  // +stateify savable
    38  type Stack struct {
    39  	Stack *stack.Stack `state:"manual"`
    40  }
    41  
    42  // Destroy implements inet.Stack.Destroy.
    43  func (s *Stack) Destroy() {
    44  	s.Stack.Close()
    45  	refs.CleanupSync.Add(1)
    46  	go func() {
    47  		s.Stack.Wait()
    48  		refs.CleanupSync.Done()
    49  	}()
    50  }
    51  
    52  // SupportsIPv6 implements Stack.SupportsIPv6.
    53  func (s *Stack) SupportsIPv6() bool {
    54  	return s.Stack.CheckNetworkProtocol(ipv6.ProtocolNumber)
    55  }
    56  
    57  // Converts Netstack's ARPHardwareType to equivalent linux constants.
    58  func toLinuxARPHardwareType(t header.ARPHardwareType) uint16 {
    59  	switch t {
    60  	case header.ARPHardwareNone:
    61  		return linux.ARPHRD_NONE
    62  	case header.ARPHardwareLoopback:
    63  		return linux.ARPHRD_LOOPBACK
    64  	case header.ARPHardwareEther:
    65  		return linux.ARPHRD_ETHER
    66  	default:
    67  		panic(fmt.Sprintf("unknown ARPHRD type: %d", t))
    68  	}
    69  }
    70  
    71  // Interfaces implements inet.Stack.Interfaces.
    72  func (s *Stack) Interfaces() map[int32]inet.Interface {
    73  	is := make(map[int32]inet.Interface)
    74  	for id, ni := range s.Stack.NICInfo() {
    75  		is[int32(id)] = inet.Interface{
    76  			Name:       ni.Name,
    77  			Addr:       []byte(ni.LinkAddress),
    78  			Flags:      uint32(nicStateFlagsToLinux(ni.Flags)),
    79  			DeviceType: toLinuxARPHardwareType(ni.ARPHardwareType),
    80  			MTU:        ni.MTU,
    81  		}
    82  	}
    83  	return is
    84  }
    85  
    86  // RemoveInterface implements inet.Stack.RemoveInterface.
    87  func (s *Stack) RemoveInterface(idx int32) error {
    88  	nic := tcpip.NICID(idx)
    89  
    90  	nicInfo, ok := s.Stack.NICInfo()[nic]
    91  	if !ok {
    92  		return syserr.ErrUnknownNICID.ToError()
    93  	}
    94  
    95  	// Don't allow removing the loopback interface.
    96  	if nicInfo.Flags.Loopback {
    97  		return syserr.ErrNotSupported.ToError()
    98  	}
    99  
   100  	return syserr.TranslateNetstackError(s.Stack.RemoveNIC(nic)).ToError()
   101  }
   102  
   103  // InterfaceAddrs implements inet.Stack.InterfaceAddrs.
   104  func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr {
   105  	nicAddrs := make(map[int32][]inet.InterfaceAddr)
   106  	for id, ni := range s.Stack.NICInfo() {
   107  		var addrs []inet.InterfaceAddr
   108  		for _, a := range ni.ProtocolAddresses {
   109  			var family uint8
   110  			switch a.Protocol {
   111  			case ipv4.ProtocolNumber:
   112  				family = linux.AF_INET
   113  			case ipv6.ProtocolNumber:
   114  				family = linux.AF_INET6
   115  			default:
   116  				log.Warningf("Unknown network protocol in %+v", a)
   117  				continue
   118  			}
   119  
   120  			addrCopy := a.AddressWithPrefix.Address
   121  			addrs = append(addrs, inet.InterfaceAddr{
   122  				Family:    family,
   123  				PrefixLen: uint8(a.AddressWithPrefix.PrefixLen),
   124  				Addr:      addrCopy.AsSlice(),
   125  				// TODO(b/68878065): Other fields.
   126  			})
   127  		}
   128  		nicAddrs[int32(id)] = addrs
   129  	}
   130  	return nicAddrs
   131  }
   132  
   133  // convertAddr converts an InterfaceAddr to a ProtocolAddress.
   134  func convertAddr(addr inet.InterfaceAddr) (tcpip.ProtocolAddress, error) {
   135  	var (
   136  		protocol        tcpip.NetworkProtocolNumber
   137  		address         tcpip.Address
   138  		protocolAddress tcpip.ProtocolAddress
   139  	)
   140  	switch addr.Family {
   141  	case linux.AF_INET:
   142  		if len(addr.Addr) != header.IPv4AddressSize {
   143  			return protocolAddress, linuxerr.EINVAL
   144  		}
   145  		if addr.PrefixLen > header.IPv4AddressSize*8 {
   146  			return protocolAddress, linuxerr.EINVAL
   147  		}
   148  		protocol = ipv4.ProtocolNumber
   149  		address = tcpip.AddrFrom4Slice(addr.Addr)
   150  	case linux.AF_INET6:
   151  		if len(addr.Addr) != header.IPv6AddressSize {
   152  			return protocolAddress, linuxerr.EINVAL
   153  		}
   154  		if addr.PrefixLen > header.IPv6AddressSize*8 {
   155  			return protocolAddress, linuxerr.EINVAL
   156  		}
   157  		protocol = ipv6.ProtocolNumber
   158  		address = tcpip.AddrFrom16Slice(addr.Addr)
   159  	default:
   160  		return protocolAddress, linuxerr.ENOTSUP
   161  	}
   162  
   163  	protocolAddress = tcpip.ProtocolAddress{
   164  		Protocol: protocol,
   165  		AddressWithPrefix: tcpip.AddressWithPrefix{
   166  			Address:   address,
   167  			PrefixLen: int(addr.PrefixLen),
   168  		},
   169  	}
   170  	return protocolAddress, nil
   171  }
   172  
   173  // AddInterfaceAddr implements inet.Stack.AddInterfaceAddr.
   174  func (s *Stack) AddInterfaceAddr(idx int32, addr inet.InterfaceAddr) error {
   175  	protocolAddress, err := convertAddr(addr)
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	// Attach address to interface.
   181  	nicID := tcpip.NICID(idx)
   182  	if err := s.Stack.AddProtocolAddress(nicID, protocolAddress, stack.AddressProperties{}); err != nil {
   183  		return syserr.TranslateNetstackError(err).ToError()
   184  	}
   185  
   186  	// Add route for local network if it doesn't exist already.
   187  	localRoute := tcpip.Route{
   188  		Destination: protocolAddress.AddressWithPrefix.Subnet(),
   189  		Gateway:     tcpip.Address{}, // No gateway for local network.
   190  		NIC:         nicID,
   191  	}
   192  
   193  	for _, rt := range s.Stack.GetRouteTable() {
   194  		if rt.Equal(localRoute) {
   195  			return nil
   196  		}
   197  	}
   198  
   199  	// Local route does not exist yet. Add it.
   200  	s.Stack.AddRoute(localRoute)
   201  
   202  	return nil
   203  }
   204  
   205  // RemoveInterfaceAddr implements inet.Stack.RemoveInterfaceAddr.
   206  func (s *Stack) RemoveInterfaceAddr(idx int32, addr inet.InterfaceAddr) error {
   207  	protocolAddress, err := convertAddr(addr)
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	// Remove addresses matching the address and prefix.
   213  	nicID := tcpip.NICID(idx)
   214  	if err := s.Stack.RemoveAddress(nicID, protocolAddress.AddressWithPrefix.Address); err != nil {
   215  		return syserr.TranslateNetstackError(err).ToError()
   216  	}
   217  
   218  	// Remove the corresponding local network route if it exists.
   219  	localRoute := tcpip.Route{
   220  		Destination: protocolAddress.AddressWithPrefix.Subnet(),
   221  		Gateway:     tcpip.Address{}, // No gateway for local network.
   222  		NIC:         nicID,
   223  	}
   224  	s.Stack.RemoveRoutes(func(rt tcpip.Route) bool {
   225  		return rt.Equal(localRoute)
   226  	})
   227  
   228  	return nil
   229  }
   230  
   231  // TCPReceiveBufferSize implements inet.Stack.TCPReceiveBufferSize.
   232  func (s *Stack) TCPReceiveBufferSize() (inet.TCPBufferSize, error) {
   233  	var rs tcpip.TCPReceiveBufferSizeRangeOption
   234  	err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &rs)
   235  	return inet.TCPBufferSize{
   236  		Min:     rs.Min,
   237  		Default: rs.Default,
   238  		Max:     rs.Max,
   239  	}, syserr.TranslateNetstackError(err).ToError()
   240  }
   241  
   242  // SetTCPReceiveBufferSize implements inet.Stack.SetTCPReceiveBufferSize.
   243  func (s *Stack) SetTCPReceiveBufferSize(size inet.TCPBufferSize) error {
   244  	rs := tcpip.TCPReceiveBufferSizeRangeOption{
   245  		Min:     size.Min,
   246  		Default: size.Default,
   247  		Max:     size.Max,
   248  	}
   249  	return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, &rs)).ToError()
   250  }
   251  
   252  // TCPSendBufferSize implements inet.Stack.TCPSendBufferSize.
   253  func (s *Stack) TCPSendBufferSize() (inet.TCPBufferSize, error) {
   254  	var ss tcpip.TCPSendBufferSizeRangeOption
   255  	err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &ss)
   256  	return inet.TCPBufferSize{
   257  		Min:     ss.Min,
   258  		Default: ss.Default,
   259  		Max:     ss.Max,
   260  	}, syserr.TranslateNetstackError(err).ToError()
   261  }
   262  
   263  // SetTCPSendBufferSize implements inet.Stack.SetTCPSendBufferSize.
   264  func (s *Stack) SetTCPSendBufferSize(size inet.TCPBufferSize) error {
   265  	ss := tcpip.TCPSendBufferSizeRangeOption{
   266  		Min:     size.Min,
   267  		Default: size.Default,
   268  		Max:     size.Max,
   269  	}
   270  	return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, &ss)).ToError()
   271  }
   272  
   273  // TCPSACKEnabled implements inet.Stack.TCPSACKEnabled.
   274  func (s *Stack) TCPSACKEnabled() (bool, error) {
   275  	var sack tcpip.TCPSACKEnabled
   276  	err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &sack)
   277  	return bool(sack), syserr.TranslateNetstackError(err).ToError()
   278  }
   279  
   280  // SetTCPSACKEnabled implements inet.Stack.SetTCPSACKEnabled.
   281  func (s *Stack) SetTCPSACKEnabled(enabled bool) error {
   282  	opt := tcpip.TCPSACKEnabled(enabled)
   283  	return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, &opt)).ToError()
   284  }
   285  
   286  // TCPRecovery implements inet.Stack.TCPRecovery.
   287  func (s *Stack) TCPRecovery() (inet.TCPLossRecovery, error) {
   288  	var recovery tcpip.TCPRecovery
   289  	if err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &recovery); err != nil {
   290  		return 0, syserr.TranslateNetstackError(err).ToError()
   291  	}
   292  	return inet.TCPLossRecovery(recovery), nil
   293  }
   294  
   295  // SetTCPRecovery implements inet.Stack.SetTCPRecovery.
   296  func (s *Stack) SetTCPRecovery(recovery inet.TCPLossRecovery) error {
   297  	opt := tcpip.TCPRecovery(recovery)
   298  	return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, &opt)).ToError()
   299  }
   300  
   301  // Statistics implements inet.Stack.Statistics.
   302  func (s *Stack) Statistics(stat any, arg string) error {
   303  	switch stats := stat.(type) {
   304  	case *inet.StatDev:
   305  		for _, ni := range s.Stack.NICInfo() {
   306  			if ni.Name != arg {
   307  				continue
   308  			}
   309  			// TODO(gvisor.dev/issue/2103) Support stubbed stats.
   310  			*stats = inet.StatDev{
   311  				// Receive section.
   312  				ni.Stats.Rx.Bytes.Value(),   // bytes.
   313  				ni.Stats.Rx.Packets.Value(), // packets.
   314  				0,                           // errs.
   315  				0,                           // drop.
   316  				0,                           // fifo.
   317  				0,                           // frame.
   318  				0,                           // compressed.
   319  				0,                           // multicast.
   320  				// Transmit section.
   321  				ni.Stats.Tx.Bytes.Value(),   // bytes.
   322  				ni.Stats.Tx.Packets.Value(), // packets.
   323  				0,                           // errs.
   324  				0,                           // drop.
   325  				0,                           // fifo.
   326  				0,                           // colls.
   327  				0,                           // carrier.
   328  				0,                           // compressed.
   329  			}
   330  			break
   331  		}
   332  	case *inet.StatSNMPIP:
   333  		ip := Metrics.IP
   334  		// TODO(gvisor.dev/issue/969) Support stubbed stats.
   335  		*stats = inet.StatSNMPIP{
   336  			0,                          // Ip/Forwarding.
   337  			0,                          // Ip/DefaultTTL.
   338  			ip.PacketsReceived.Value(), // InReceives.
   339  			0,                          // Ip/InHdrErrors.
   340  			ip.InvalidDestinationAddressesReceived.Value(), // InAddrErrors.
   341  			0,                               // Ip/ForwDatagrams.
   342  			0,                               // Ip/InUnknownProtos.
   343  			0,                               // Ip/InDiscards.
   344  			ip.PacketsDelivered.Value(),     // InDelivers.
   345  			ip.PacketsSent.Value(),          // OutRequests.
   346  			ip.OutgoingPacketErrors.Value(), // OutDiscards.
   347  			0,                               // Ip/OutNoRoutes.
   348  			0,                               // Support Ip/ReasmTimeout.
   349  			0,                               // Support Ip/ReasmReqds.
   350  			0,                               // Support Ip/ReasmOKs.
   351  			0,                               // Support Ip/ReasmFails.
   352  			0,                               // Support Ip/FragOKs.
   353  			0,                               // Support Ip/FragFails.
   354  			0,                               // Support Ip/FragCreates.
   355  		}
   356  	case *inet.StatSNMPICMP:
   357  		in := Metrics.ICMP.V4.PacketsReceived.ICMPv4PacketStats
   358  		out := Metrics.ICMP.V4.PacketsSent.ICMPv4PacketStats
   359  		// TODO(gvisor.dev/issue/969) Support stubbed stats.
   360  		*stats = inet.StatSNMPICMP{
   361  			0, // Icmp/InMsgs.
   362  			Metrics.ICMP.V4.PacketsSent.Dropped.Value(), // InErrors.
   363  			0,                         // Icmp/InCsumErrors.
   364  			in.DstUnreachable.Value(), // InDestUnreachs.
   365  			in.TimeExceeded.Value(),   // InTimeExcds.
   366  			in.ParamProblem.Value(),   // InParmProbs.
   367  			in.SrcQuench.Value(),      // InSrcQuenchs.
   368  			in.Redirect.Value(),       // InRedirects.
   369  			in.EchoRequest.Value(),    // InEchos.
   370  			in.EchoReply.Value(),      // InEchoReps.
   371  			in.Timestamp.Value(),      // InTimestamps.
   372  			in.TimestampReply.Value(), // InTimestampReps.
   373  			in.InfoRequest.Value(),    // InAddrMasks.
   374  			in.InfoReply.Value(),      // InAddrMaskReps.
   375  			0,                         // Icmp/OutMsgs.
   376  			Metrics.ICMP.V4.PacketsReceived.Invalid.Value(), // OutErrors.
   377  			out.DstUnreachable.Value(),                      // OutDestUnreachs.
   378  			out.TimeExceeded.Value(),                        // OutTimeExcds.
   379  			out.ParamProblem.Value(),                        // OutParmProbs.
   380  			out.SrcQuench.Value(),                           // OutSrcQuenchs.
   381  			out.Redirect.Value(),                            // OutRedirects.
   382  			out.EchoRequest.Value(),                         // OutEchos.
   383  			out.EchoReply.Value(),                           // OutEchoReps.
   384  			out.Timestamp.Value(),                           // OutTimestamps.
   385  			out.TimestampReply.Value(),                      // OutTimestampReps.
   386  			out.InfoRequest.Value(),                         // OutAddrMasks.
   387  			out.InfoReply.Value(),                           // OutAddrMaskReps.
   388  		}
   389  	case *inet.StatSNMPTCP:
   390  		tcp := Metrics.TCP
   391  		// RFC 2012 (updates 1213):  SNMPv2-MIB-TCP.
   392  		*stats = inet.StatSNMPTCP{
   393  			1,                                     // RtoAlgorithm.
   394  			200,                                   // RtoMin.
   395  			120000,                                // RtoMax.
   396  			(1<<64 - 1),                           // MaxConn.
   397  			tcp.ActiveConnectionOpenings.Value(),  // ActiveOpens.
   398  			tcp.PassiveConnectionOpenings.Value(), // PassiveOpens.
   399  			tcp.FailedConnectionAttempts.Value(),  // AttemptFails.
   400  			tcp.EstablishedResets.Value(),         // EstabResets.
   401  			tcp.CurrentEstablished.Value(),        // CurrEstab.
   402  			tcp.ValidSegmentsReceived.Value(),     // InSegs.
   403  			tcp.SegmentsSent.Value(),              // OutSegs.
   404  			tcp.Retransmits.Value(),               // RetransSegs.
   405  			tcp.InvalidSegmentsReceived.Value(),   // InErrs.
   406  			tcp.ResetsSent.Value(),                // OutRsts.
   407  			tcp.ChecksumErrors.Value(),            // InCsumErrors.
   408  		}
   409  	case *inet.StatSNMPUDP:
   410  		udp := Metrics.UDP
   411  		// TODO(gvisor.dev/issue/969) Support stubbed stats.
   412  		*stats = inet.StatSNMPUDP{
   413  			udp.PacketsReceived.Value(),     // InDatagrams.
   414  			udp.UnknownPortErrors.Value(),   // NoPorts.
   415  			0,                               // Udp/InErrors.
   416  			udp.PacketsSent.Value(),         // OutDatagrams.
   417  			udp.ReceiveBufferErrors.Value(), // RcvbufErrors.
   418  			0,                               // Udp/SndbufErrors.
   419  			udp.ChecksumErrors.Value(),      // Udp/InCsumErrors.
   420  			0,                               // Udp/IgnoredMulti.
   421  		}
   422  	default:
   423  		return syserr.ErrEndpointOperation.ToError()
   424  	}
   425  	return nil
   426  }
   427  
   428  // RouteTable implements inet.Stack.RouteTable.
   429  func (s *Stack) RouteTable() []inet.Route {
   430  	var routeTable []inet.Route
   431  
   432  	for _, rt := range s.Stack.GetRouteTable() {
   433  		var family uint8
   434  		switch rt.Destination.ID().BitLen() {
   435  		case header.IPv4AddressSizeBits:
   436  			family = linux.AF_INET
   437  		case header.IPv6AddressSizeBits:
   438  			family = linux.AF_INET6
   439  		default:
   440  			log.Warningf("Unknown network protocol in route %+v", rt)
   441  			continue
   442  		}
   443  
   444  		dstAddr := rt.Destination.ID()
   445  		routeTable = append(routeTable, inet.Route{
   446  			Family: family,
   447  			DstLen: uint8(rt.Destination.Prefix()), // The CIDR prefix for the destination.
   448  
   449  			// Always return unspecified protocol since we have no notion of
   450  			// protocol for routes.
   451  			Protocol: linux.RTPROT_UNSPEC,
   452  			// Set statically to LINK scope for now.
   453  			//
   454  			// TODO(gvisor.dev/issue/595): Set scope for routes.
   455  			Scope: linux.RT_SCOPE_LINK,
   456  			Type:  linux.RTN_UNICAST,
   457  
   458  			DstAddr:         dstAddr.AsSlice(),
   459  			OutputInterface: int32(rt.NIC),
   460  			GatewayAddr:     rt.Gateway.AsSlice(),
   461  		})
   462  	}
   463  
   464  	return routeTable
   465  }
   466  
   467  // IPTables returns the stack's iptables.
   468  func (s *Stack) IPTables() (*stack.IPTables, error) {
   469  	return s.Stack.IPTables(), nil
   470  }
   471  
   472  // Pause implements inet.Stack.Pause.
   473  func (s *Stack) Pause() {
   474  	s.Stack.Pause()
   475  }
   476  
   477  // Resume implements inet.Stack.Resume.
   478  func (s *Stack) Resume() {
   479  	s.Stack.Resume()
   480  }
   481  
   482  // RegisteredEndpoints implements inet.Stack.RegisteredEndpoints.
   483  func (s *Stack) RegisteredEndpoints() []stack.TransportEndpoint {
   484  	return s.Stack.RegisteredEndpoints()
   485  }
   486  
   487  // CleanupEndpoints implements inet.Stack.CleanupEndpoints.
   488  func (s *Stack) CleanupEndpoints() []stack.TransportEndpoint {
   489  	return s.Stack.CleanupEndpoints()
   490  }
   491  
   492  // RestoreCleanupEndpoints implements inet.Stack.RestoreCleanupEndpoints.
   493  func (s *Stack) RestoreCleanupEndpoints(es []stack.TransportEndpoint) {
   494  	s.Stack.RestoreCleanupEndpoints(es)
   495  }
   496  
   497  // SetForwarding implements inet.Stack.SetForwarding.
   498  func (s *Stack) SetForwarding(protocol tcpip.NetworkProtocolNumber, enable bool) error {
   499  	if err := s.Stack.SetForwardingDefaultAndAllNICs(protocol, enable); err != nil {
   500  		return fmt.Errorf("SetForwardingDefaultAndAllNICs(%d, %t): %s", protocol, enable, err)
   501  	}
   502  	return nil
   503  }
   504  
   505  // PortRange implements inet.Stack.PortRange.
   506  func (s *Stack) PortRange() (uint16, uint16) {
   507  	return s.Stack.PortRange()
   508  }
   509  
   510  // SetPortRange implements inet.Stack.SetPortRange.
   511  func (s *Stack) SetPortRange(start uint16, end uint16) error {
   512  	return syserr.TranslateNetstackError(s.Stack.SetPortRange(start, end)).ToError()
   513  }
   514  
   515  // GROTimeout implements inet.Stack.GROTimeout.
   516  func (s *Stack) GROTimeout(nicID int32) (time.Duration, error) {
   517  	timeout, err := s.Stack.GROTimeout(tcpip.NICID(nicID))
   518  	return timeout, syserr.TranslateNetstackError(err).ToError()
   519  }
   520  
   521  // SetGROTimeout implements inet.Stack.SetGROTimeout.
   522  func (s *Stack) SetGROTimeout(nicID int32, timeout time.Duration) error {
   523  	return syserr.TranslateNetstackError(s.Stack.SetGROTimeout(tcpip.NICID(nicID), timeout)).ToError()
   524  }