inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/tcpip/transport/internal/network/endpoint.go (about)

     1  // Copyright 2021 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 network provides facilities to support tcpip.Endpoints that operate
    16  // at the network layer or above.
    17  package network
    18  
    19  import (
    20  	"fmt"
    21  	"sync/atomic"
    22  
    23  	"inet.af/netstack/sync"
    24  	"inet.af/netstack/tcpip"
    25  	"inet.af/netstack/tcpip/header"
    26  	"inet.af/netstack/tcpip/stack"
    27  	"inet.af/netstack/tcpip/transport"
    28  )
    29  
    30  // Endpoint is a datagram-based endpoint. It only supports sending datagrams to
    31  // a peer.
    32  //
    33  // +stateify savable
    34  type Endpoint struct {
    35  	// The following fields must only be set once then never changed.
    36  	stack      *stack.Stack `state:"manual"`
    37  	ops        *tcpip.SocketOptions
    38  	netProto   tcpip.NetworkProtocolNumber
    39  	transProto tcpip.TransportProtocolNumber
    40  
    41  	mu sync.RWMutex `state:"nosave"`
    42  	// +checklocks:mu
    43  	wasBound bool
    44  	// owner is the owner of transmitted packets.
    45  	//
    46  	// +checklocks:mu
    47  	owner tcpip.PacketOwner
    48  	// +checklocks:mu
    49  	writeShutdown bool
    50  	// +checklocks:mu
    51  	effectiveNetProto tcpip.NetworkProtocolNumber
    52  	// +checklocks:mu
    53  	connectedRoute *stack.Route `state:"manual"`
    54  	// +checklocks:mu
    55  	multicastMemberships map[multicastMembership]struct{}
    56  	// TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
    57  	// +checklocks:mu
    58  	ttl uint8
    59  	// TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
    60  	// +checklocks:mu
    61  	multicastTTL uint8
    62  	// TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
    63  	// +checklocks:mu
    64  	multicastAddr tcpip.Address
    65  	// TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
    66  	// +checklocks:mu
    67  	multicastNICID tcpip.NICID
    68  	// +checklocks:mu
    69  	ipv4TOS uint8
    70  	// +checklocks:mu
    71  	ipv6TClass uint8
    72  
    73  	// Lock ordering: mu > infoMu.
    74  	infoMu sync.RWMutex `state:"nosave"`
    75  	// info has a dedicated mutex so that we can avoid lock ordering violations
    76  	// when reading the endpoint's info. If we used mu, we need to guarantee
    77  	// that any lock taken while mu is held is not held when calling Info()
    78  	// which is not true as of writing (we hold mu while registering transport
    79  	// endpoints (taking the transport demuxer lock but we also hold the demuxer
    80  	// lock when delivering packets/errors to endpoints).
    81  	//
    82  	// Writes must be performed through setInfo.
    83  	//
    84  	// +checklocks:infoMu
    85  	info stack.TransportEndpointInfo
    86  
    87  	// state holds a transport.DatagramBasedEndpointState.
    88  	//
    89  	// state must be accessed with atomics so that we can avoid lock ordering
    90  	// violations when reading the state. If we used mu, we need to guarantee
    91  	// that any lock taken while mu is held is not held when calling State()
    92  	// which is not true as of writing (we hold mu while registering transport
    93  	// endpoints (taking the transport demuxer lock but we also hold the demuxer
    94  	// lock when delivering packets/errors to endpoints).
    95  	//
    96  	// Writes must be performed through setEndpointState.
    97  	//
    98  	// +checkatomics
    99  	state uint32
   100  }
   101  
   102  // +stateify savable
   103  type multicastMembership struct {
   104  	nicID         tcpip.NICID
   105  	multicastAddr tcpip.Address
   106  }
   107  
   108  // Init initializes the endpoint.
   109  func (e *Endpoint) Init(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, ops *tcpip.SocketOptions) {
   110  	e.mu.Lock()
   111  	memberships := e.multicastMemberships
   112  	e.mu.Unlock()
   113  	if memberships != nil {
   114  		panic(fmt.Sprintf("endpoint is already initialized; got e.multicastMemberships = %#v, want = nil", memberships))
   115  	}
   116  
   117  	switch netProto {
   118  	case header.IPv4ProtocolNumber, header.IPv6ProtocolNumber:
   119  	default:
   120  		panic(fmt.Sprintf("invalid protocol number = %d", netProto))
   121  	}
   122  
   123  	*e = Endpoint{
   124  		stack:      s,
   125  		ops:        ops,
   126  		netProto:   netProto,
   127  		transProto: transProto,
   128  
   129  		info: stack.TransportEndpointInfo{
   130  			NetProto:   netProto,
   131  			TransProto: transProto,
   132  		},
   133  		effectiveNetProto: netProto,
   134  		// Linux defaults to TTL=1.
   135  		multicastTTL:         1,
   136  		multicastMemberships: make(map[multicastMembership]struct{}),
   137  	}
   138  
   139  	e.mu.Lock()
   140  	defer e.mu.Unlock()
   141  	e.setEndpointState(transport.DatagramEndpointStateInitial)
   142  }
   143  
   144  // NetProto returns the network protocol the endpoint was initialized with.
   145  func (e *Endpoint) NetProto() tcpip.NetworkProtocolNumber {
   146  	return e.netProto
   147  }
   148  
   149  // setEndpointState sets the state of the endpoint.
   150  //
   151  // e.mu must be held to synchronize changes to state with the rest of the
   152  // endpoint.
   153  //
   154  // +checklocks:e.mu
   155  func (e *Endpoint) setEndpointState(state transport.DatagramEndpointState) {
   156  	atomic.StoreUint32(&e.state, uint32(state))
   157  }
   158  
   159  // State returns the state of the endpoint.
   160  func (e *Endpoint) State() transport.DatagramEndpointState {
   161  	return transport.DatagramEndpointState(atomic.LoadUint32(&e.state))
   162  }
   163  
   164  // Close cleans the endpoint's resources and leaves the endpoint in a closed
   165  // state.
   166  func (e *Endpoint) Close() {
   167  	e.mu.Lock()
   168  	defer e.mu.Unlock()
   169  
   170  	if e.State() == transport.DatagramEndpointStateClosed {
   171  		return
   172  	}
   173  
   174  	for mem := range e.multicastMemberships {
   175  		e.stack.LeaveGroup(e.netProto, mem.nicID, mem.multicastAddr)
   176  	}
   177  	e.multicastMemberships = nil
   178  
   179  	if e.connectedRoute != nil {
   180  		e.connectedRoute.Release()
   181  		e.connectedRoute = nil
   182  	}
   183  
   184  	e.setEndpointState(transport.DatagramEndpointStateClosed)
   185  }
   186  
   187  // SetOwner sets the owner of transmitted packets.
   188  func (e *Endpoint) SetOwner(owner tcpip.PacketOwner) {
   189  	e.mu.Lock()
   190  	defer e.mu.Unlock()
   191  	e.owner = owner
   192  }
   193  
   194  func calculateTTL(route *stack.Route, ttl uint8, multicastTTL uint8) uint8 {
   195  	if header.IsV4MulticastAddress(route.RemoteAddress()) || header.IsV6MulticastAddress(route.RemoteAddress()) {
   196  		return multicastTTL
   197  	}
   198  
   199  	if ttl == 0 {
   200  		return route.DefaultTTL()
   201  	}
   202  
   203  	return ttl
   204  }
   205  
   206  // WriteContext holds the context for a write.
   207  type WriteContext struct {
   208  	transProto tcpip.TransportProtocolNumber
   209  	route      *stack.Route
   210  	ttl        uint8
   211  	tos        uint8
   212  	owner      tcpip.PacketOwner
   213  }
   214  
   215  // Release releases held resources.
   216  func (c *WriteContext) Release() {
   217  	c.route.Release()
   218  	*c = WriteContext{}
   219  }
   220  
   221  // WritePacketInfo is the properties of a packet that may be written.
   222  type WritePacketInfo struct {
   223  	NetProto                    tcpip.NetworkProtocolNumber
   224  	LocalAddress, RemoteAddress tcpip.Address
   225  	MaxHeaderLength             uint16
   226  	RequiresTXTransportChecksum bool
   227  }
   228  
   229  // PacketInfo returns the properties of a packet that will be written.
   230  func (c *WriteContext) PacketInfo() WritePacketInfo {
   231  	return WritePacketInfo{
   232  		NetProto:                    c.route.NetProto(),
   233  		LocalAddress:                c.route.LocalAddress(),
   234  		RemoteAddress:               c.route.RemoteAddress(),
   235  		MaxHeaderLength:             c.route.MaxHeaderLength(),
   236  		RequiresTXTransportChecksum: c.route.RequiresTXTransportChecksum(),
   237  	}
   238  }
   239  
   240  // WritePacket attempts to write the packet.
   241  func (c *WriteContext) WritePacket(pkt *stack.PacketBuffer, headerIncluded bool) tcpip.Error {
   242  	pkt.Owner = c.owner
   243  
   244  	if headerIncluded {
   245  		return c.route.WriteHeaderIncludedPacket(pkt)
   246  	}
   247  
   248  	return c.route.WritePacket(stack.NetworkHeaderParams{
   249  		Protocol: c.transProto,
   250  		TTL:      c.ttl,
   251  		TOS:      c.tos,
   252  	}, pkt)
   253  }
   254  
   255  // AcquireContextForWrite acquires a WriteContext.
   256  func (e *Endpoint) AcquireContextForWrite(opts tcpip.WriteOptions) (WriteContext, tcpip.Error) {
   257  	e.mu.RLock()
   258  	defer e.mu.RUnlock()
   259  
   260  	// MSG_MORE is unimplemented. This also means that MSG_EOR is a no-op.
   261  	if opts.More {
   262  		return WriteContext{}, &tcpip.ErrInvalidOptionValue{}
   263  	}
   264  
   265  	if e.State() == transport.DatagramEndpointStateClosed {
   266  		return WriteContext{}, &tcpip.ErrInvalidEndpointState{}
   267  	}
   268  
   269  	if e.writeShutdown {
   270  		return WriteContext{}, &tcpip.ErrClosedForSend{}
   271  	}
   272  
   273  	route := e.connectedRoute
   274  	if opts.To == nil {
   275  		// If the user doesn't specify a destination, they should have
   276  		// connected to another address.
   277  		if e.State() != transport.DatagramEndpointStateConnected {
   278  			return WriteContext{}, &tcpip.ErrDestinationRequired{}
   279  		}
   280  
   281  		route.Acquire()
   282  	} else {
   283  		// Reject destination address if it goes through a different
   284  		// NIC than the endpoint was bound to.
   285  		nicID := opts.To.NIC
   286  		if nicID == 0 {
   287  			nicID = tcpip.NICID(e.ops.GetBindToDevice())
   288  		}
   289  		info := e.Info()
   290  		if info.BindNICID != 0 {
   291  			if nicID != 0 && nicID != info.BindNICID {
   292  				return WriteContext{}, &tcpip.ErrNoRoute{}
   293  			}
   294  
   295  			nicID = info.BindNICID
   296  		}
   297  		if nicID == 0 {
   298  			nicID = info.RegisterNICID
   299  		}
   300  
   301  		dst, netProto, err := e.checkV4Mapped(*opts.To)
   302  		if err != nil {
   303  			return WriteContext{}, err
   304  		}
   305  
   306  		route, _, err = e.connectRouteRLocked(nicID, dst, netProto)
   307  		if err != nil {
   308  			return WriteContext{}, err
   309  		}
   310  	}
   311  
   312  	if !e.ops.GetBroadcast() && route.IsOutboundBroadcast() {
   313  		route.Release()
   314  		return WriteContext{}, &tcpip.ErrBroadcastDisabled{}
   315  	}
   316  
   317  	var tos uint8
   318  	switch netProto := route.NetProto(); netProto {
   319  	case header.IPv4ProtocolNumber:
   320  		tos = e.ipv4TOS
   321  	case header.IPv6ProtocolNumber:
   322  		tos = e.ipv6TClass
   323  	default:
   324  		panic(fmt.Sprintf("invalid protocol number = %d", netProto))
   325  	}
   326  
   327  	return WriteContext{
   328  		transProto: e.transProto,
   329  		route:      route,
   330  		ttl:        calculateTTL(route, e.ttl, e.multicastTTL),
   331  		tos:        tos,
   332  		owner:      e.owner,
   333  	}, nil
   334  }
   335  
   336  // Disconnect disconnects the endpoint from its peer.
   337  func (e *Endpoint) Disconnect() {
   338  	e.mu.Lock()
   339  	defer e.mu.Unlock()
   340  
   341  	if e.State() != transport.DatagramEndpointStateConnected {
   342  		return
   343  	}
   344  
   345  	info := e.Info()
   346  	// Exclude ephemerally bound endpoints.
   347  	if e.wasBound {
   348  		info.ID = stack.TransportEndpointID{
   349  			LocalAddress: info.BindAddr,
   350  		}
   351  		e.setEndpointState(transport.DatagramEndpointStateBound)
   352  	} else {
   353  		info.ID = stack.TransportEndpointID{}
   354  		e.setEndpointState(transport.DatagramEndpointStateInitial)
   355  	}
   356  	e.setInfo(info)
   357  
   358  	e.connectedRoute.Release()
   359  	e.connectedRoute = nil
   360  }
   361  
   362  // connectRouteRLocked establishes a route to the specified interface or the
   363  // configured multicast interface if no interface is specified and the
   364  // specified address is a multicast address.
   365  //
   366  // +checklocksread:e.mu
   367  func (e *Endpoint) connectRouteRLocked(nicID tcpip.NICID, addr tcpip.FullAddress, netProto tcpip.NetworkProtocolNumber) (*stack.Route, tcpip.NICID, tcpip.Error) {
   368  	localAddr := e.Info().ID.LocalAddress
   369  	if e.isBroadcastOrMulticast(nicID, netProto, localAddr) {
   370  		// A packet can only originate from a unicast address (i.e., an interface).
   371  		localAddr = ""
   372  	}
   373  
   374  	if header.IsV4MulticastAddress(addr.Addr) || header.IsV6MulticastAddress(addr.Addr) {
   375  		if nicID == 0 {
   376  			nicID = e.multicastNICID
   377  		}
   378  		if localAddr == "" && nicID == 0 {
   379  			localAddr = e.multicastAddr
   380  		}
   381  	}
   382  
   383  	// Find a route to the desired destination.
   384  	r, err := e.stack.FindRoute(nicID, localAddr, addr.Addr, netProto, e.ops.GetMulticastLoop())
   385  	if err != nil {
   386  		return nil, 0, err
   387  	}
   388  	return r, nicID, nil
   389  }
   390  
   391  // Connect connects the endpoint to the address.
   392  func (e *Endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
   393  	return e.ConnectAndThen(addr, func(_ tcpip.NetworkProtocolNumber, _, _ stack.TransportEndpointID) tcpip.Error {
   394  		return nil
   395  	})
   396  }
   397  
   398  // ConnectAndThen connects the endpoint to the address and then calls the
   399  // provided function.
   400  //
   401  // If the function returns an error, the endpoint's state does not change. The
   402  // function will be called with the network protocol used to connect to the peer
   403  // and the source and destination addresses that will be used to send traffic to
   404  // the peer.
   405  func (e *Endpoint) ConnectAndThen(addr tcpip.FullAddress, f func(netProto tcpip.NetworkProtocolNumber, previousID, nextID stack.TransportEndpointID) tcpip.Error) tcpip.Error {
   406  	addr.Port = 0
   407  
   408  	e.mu.Lock()
   409  	defer e.mu.Unlock()
   410  
   411  	info := e.Info()
   412  	nicID := addr.NIC
   413  	switch e.State() {
   414  	case transport.DatagramEndpointStateInitial:
   415  	case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected:
   416  		if info.BindNICID == 0 {
   417  			break
   418  		}
   419  
   420  		if nicID != 0 && nicID != info.BindNICID {
   421  			return &tcpip.ErrInvalidEndpointState{}
   422  		}
   423  
   424  		nicID = info.BindNICID
   425  	default:
   426  		return &tcpip.ErrInvalidEndpointState{}
   427  	}
   428  
   429  	addr, netProto, err := e.checkV4Mapped(addr)
   430  	if err != nil {
   431  		return err
   432  	}
   433  
   434  	r, nicID, err := e.connectRouteRLocked(nicID, addr, netProto)
   435  	if err != nil {
   436  		return err
   437  	}
   438  
   439  	id := stack.TransportEndpointID{
   440  		LocalAddress:  info.ID.LocalAddress,
   441  		RemoteAddress: r.RemoteAddress(),
   442  	}
   443  	if e.State() == transport.DatagramEndpointStateInitial {
   444  		id.LocalAddress = r.LocalAddress()
   445  	}
   446  
   447  	if err := f(r.NetProto(), info.ID, id); err != nil {
   448  		return err
   449  	}
   450  
   451  	if e.connectedRoute != nil {
   452  		// If the endpoint was previously connected then release any previous route.
   453  		e.connectedRoute.Release()
   454  	}
   455  	e.connectedRoute = r
   456  	info.ID = id
   457  	info.RegisterNICID = nicID
   458  	e.setInfo(info)
   459  	e.effectiveNetProto = netProto
   460  	e.setEndpointState(transport.DatagramEndpointStateConnected)
   461  	return nil
   462  }
   463  
   464  // Shutdown shutsdown the endpoint.
   465  func (e *Endpoint) Shutdown() tcpip.Error {
   466  	e.mu.Lock()
   467  	defer e.mu.Unlock()
   468  
   469  	switch state := e.State(); state {
   470  	case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateClosed:
   471  		return &tcpip.ErrNotConnected{}
   472  	case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected:
   473  		e.writeShutdown = true
   474  		return nil
   475  	default:
   476  		panic(fmt.Sprintf("unhandled state = %s", state))
   477  	}
   478  }
   479  
   480  // checkV4MappedRLocked determines the effective network protocol and converts
   481  // addr to its canonical form.
   482  func (e *Endpoint) checkV4Mapped(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
   483  	info := e.Info()
   484  	unwrapped, netProto, err := info.AddrNetProtoLocked(addr, e.ops.GetV6Only())
   485  	if err != nil {
   486  		return tcpip.FullAddress{}, 0, err
   487  	}
   488  	return unwrapped, netProto, nil
   489  }
   490  
   491  func (e *Endpoint) isBroadcastOrMulticast(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, addr tcpip.Address) bool {
   492  	return addr == header.IPv4Broadcast || header.IsV4MulticastAddress(addr) || header.IsV6MulticastAddress(addr) || e.stack.IsSubnetBroadcast(nicID, netProto, addr)
   493  }
   494  
   495  // Bind binds the endpoint to the address.
   496  func (e *Endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
   497  	return e.BindAndThen(addr, func(tcpip.NetworkProtocolNumber, tcpip.Address) tcpip.Error {
   498  		return nil
   499  	})
   500  }
   501  
   502  // BindAndThen binds the endpoint to the address and then calls the provided
   503  // function.
   504  //
   505  // If the function returns an error, the endpoint's state does not change. The
   506  // function will be called with the bound network protocol and address.
   507  func (e *Endpoint) BindAndThen(addr tcpip.FullAddress, f func(tcpip.NetworkProtocolNumber, tcpip.Address) tcpip.Error) tcpip.Error {
   508  	addr.Port = 0
   509  
   510  	e.mu.Lock()
   511  	defer e.mu.Unlock()
   512  
   513  	// Don't allow binding once endpoint is not in the initial state
   514  	// anymore.
   515  	if e.State() != transport.DatagramEndpointStateInitial {
   516  		return &tcpip.ErrInvalidEndpointState{}
   517  	}
   518  
   519  	addr, netProto, err := e.checkV4Mapped(addr)
   520  	if err != nil {
   521  		return err
   522  	}
   523  
   524  	nicID := addr.NIC
   525  	if len(addr.Addr) != 0 && !e.isBroadcastOrMulticast(addr.NIC, netProto, addr.Addr) {
   526  		nicID = e.stack.CheckLocalAddress(nicID, netProto, addr.Addr)
   527  		if nicID == 0 {
   528  			return &tcpip.ErrBadLocalAddress{}
   529  		}
   530  	}
   531  
   532  	if err := f(netProto, addr.Addr); err != nil {
   533  		return err
   534  	}
   535  
   536  	e.wasBound = true
   537  
   538  	info := e.Info()
   539  	info.ID = stack.TransportEndpointID{
   540  		LocalAddress: addr.Addr,
   541  	}
   542  	info.BindNICID = addr.NIC
   543  	info.RegisterNICID = nicID
   544  	info.BindAddr = addr.Addr
   545  	e.setInfo(info)
   546  	e.effectiveNetProto = netProto
   547  	e.setEndpointState(transport.DatagramEndpointStateBound)
   548  	return nil
   549  }
   550  
   551  // WasBound returns true iff the endpoint was ever bound.
   552  func (e *Endpoint) WasBound() bool {
   553  	e.mu.RLock()
   554  	defer e.mu.RUnlock()
   555  	return e.wasBound
   556  }
   557  
   558  // GetLocalAddress returns the address that the endpoint is bound to.
   559  func (e *Endpoint) GetLocalAddress() tcpip.FullAddress {
   560  	e.mu.RLock()
   561  	defer e.mu.RUnlock()
   562  
   563  	info := e.Info()
   564  	addr := info.BindAddr
   565  	if e.State() == transport.DatagramEndpointStateConnected {
   566  		addr = e.connectedRoute.LocalAddress()
   567  	}
   568  
   569  	return tcpip.FullAddress{
   570  		NIC:  info.RegisterNICID,
   571  		Addr: addr,
   572  	}
   573  }
   574  
   575  // GetRemoteAddress returns the address that the endpoint is connected to.
   576  func (e *Endpoint) GetRemoteAddress() (tcpip.FullAddress, bool) {
   577  	e.mu.RLock()
   578  	defer e.mu.RUnlock()
   579  
   580  	if e.State() != transport.DatagramEndpointStateConnected {
   581  		return tcpip.FullAddress{}, false
   582  	}
   583  
   584  	return tcpip.FullAddress{
   585  		Addr: e.connectedRoute.RemoteAddress(),
   586  		NIC:  e.Info().RegisterNICID,
   587  	}, true
   588  }
   589  
   590  // SetSockOptInt sets the socket option.
   591  func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
   592  	switch opt {
   593  	case tcpip.MTUDiscoverOption:
   594  		// Return not supported if the value is not disabling path
   595  		// MTU discovery.
   596  		if v != tcpip.PMTUDiscoveryDont {
   597  			return &tcpip.ErrNotSupported{}
   598  		}
   599  
   600  	case tcpip.MulticastTTLOption:
   601  		e.mu.Lock()
   602  		e.multicastTTL = uint8(v)
   603  		e.mu.Unlock()
   604  
   605  	case tcpip.TTLOption:
   606  		e.mu.Lock()
   607  		e.ttl = uint8(v)
   608  		e.mu.Unlock()
   609  
   610  	case tcpip.IPv4TOSOption:
   611  		e.mu.Lock()
   612  		e.ipv4TOS = uint8(v)
   613  		e.mu.Unlock()
   614  
   615  	case tcpip.IPv6TrafficClassOption:
   616  		e.mu.Lock()
   617  		e.ipv6TClass = uint8(v)
   618  		e.mu.Unlock()
   619  	}
   620  
   621  	return nil
   622  }
   623  
   624  // GetSockOptInt returns the socket option.
   625  func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
   626  	switch opt {
   627  	case tcpip.MTUDiscoverOption:
   628  		// The only supported setting is path MTU discovery disabled.
   629  		return tcpip.PMTUDiscoveryDont, nil
   630  
   631  	case tcpip.MulticastTTLOption:
   632  		e.mu.Lock()
   633  		v := int(e.multicastTTL)
   634  		e.mu.Unlock()
   635  		return v, nil
   636  
   637  	case tcpip.TTLOption:
   638  		e.mu.Lock()
   639  		v := int(e.ttl)
   640  		e.mu.Unlock()
   641  		return v, nil
   642  
   643  	case tcpip.IPv4TOSOption:
   644  		e.mu.RLock()
   645  		v := int(e.ipv4TOS)
   646  		e.mu.RUnlock()
   647  		return v, nil
   648  
   649  	case tcpip.IPv6TrafficClassOption:
   650  		e.mu.RLock()
   651  		v := int(e.ipv6TClass)
   652  		e.mu.RUnlock()
   653  		return v, nil
   654  
   655  	default:
   656  		return -1, &tcpip.ErrUnknownProtocolOption{}
   657  	}
   658  }
   659  
   660  // SetSockOpt sets the socket option.
   661  func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
   662  	switch v := opt.(type) {
   663  	case *tcpip.MulticastInterfaceOption:
   664  		e.mu.Lock()
   665  		defer e.mu.Unlock()
   666  
   667  		fa := tcpip.FullAddress{Addr: v.InterfaceAddr}
   668  		fa, netProto, err := e.checkV4Mapped(fa)
   669  		if err != nil {
   670  			return err
   671  		}
   672  		nic := v.NIC
   673  		addr := fa.Addr
   674  
   675  		if nic == 0 && addr == "" {
   676  			e.multicastAddr = ""
   677  			e.multicastNICID = 0
   678  			break
   679  		}
   680  
   681  		if nic != 0 {
   682  			if !e.stack.CheckNIC(nic) {
   683  				return &tcpip.ErrBadLocalAddress{}
   684  			}
   685  		} else {
   686  			nic = e.stack.CheckLocalAddress(0, netProto, addr)
   687  			if nic == 0 {
   688  				return &tcpip.ErrBadLocalAddress{}
   689  			}
   690  		}
   691  
   692  		if info := e.Info(); info.BindNICID != 0 && info.BindNICID != nic {
   693  			return &tcpip.ErrInvalidEndpointState{}
   694  		}
   695  
   696  		e.multicastNICID = nic
   697  		e.multicastAddr = addr
   698  
   699  	case *tcpip.AddMembershipOption:
   700  		if !header.IsV4MulticastAddress(v.MulticastAddr) && !header.IsV6MulticastAddress(v.MulticastAddr) {
   701  			return &tcpip.ErrInvalidOptionValue{}
   702  		}
   703  
   704  		nicID := v.NIC
   705  
   706  		if v.InterfaceAddr.Unspecified() {
   707  			if nicID == 0 {
   708  				if r, err := e.stack.FindRoute(0, "", v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil {
   709  					nicID = r.NICID()
   710  					r.Release()
   711  				}
   712  			}
   713  		} else {
   714  			nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
   715  		}
   716  		if nicID == 0 {
   717  			return &tcpip.ErrUnknownDevice{}
   718  		}
   719  
   720  		memToInsert := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr}
   721  
   722  		e.mu.Lock()
   723  		defer e.mu.Unlock()
   724  
   725  		if _, ok := e.multicastMemberships[memToInsert]; ok {
   726  			return &tcpip.ErrPortInUse{}
   727  		}
   728  
   729  		if err := e.stack.JoinGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
   730  			return err
   731  		}
   732  
   733  		e.multicastMemberships[memToInsert] = struct{}{}
   734  
   735  	case *tcpip.RemoveMembershipOption:
   736  		if !header.IsV4MulticastAddress(v.MulticastAddr) && !header.IsV6MulticastAddress(v.MulticastAddr) {
   737  			return &tcpip.ErrInvalidOptionValue{}
   738  		}
   739  
   740  		nicID := v.NIC
   741  		if v.InterfaceAddr.Unspecified() {
   742  			if nicID == 0 {
   743  				if r, err := e.stack.FindRoute(0, "", v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil {
   744  					nicID = r.NICID()
   745  					r.Release()
   746  				}
   747  			}
   748  		} else {
   749  			nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
   750  		}
   751  		if nicID == 0 {
   752  			return &tcpip.ErrUnknownDevice{}
   753  		}
   754  
   755  		memToRemove := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr}
   756  
   757  		e.mu.Lock()
   758  		defer e.mu.Unlock()
   759  
   760  		if _, ok := e.multicastMemberships[memToRemove]; !ok {
   761  			return &tcpip.ErrBadLocalAddress{}
   762  		}
   763  
   764  		if err := e.stack.LeaveGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
   765  			return err
   766  		}
   767  
   768  		delete(e.multicastMemberships, memToRemove)
   769  
   770  	case *tcpip.SocketDetachFilterOption:
   771  		return nil
   772  	}
   773  	return nil
   774  }
   775  
   776  // GetSockOpt returns the socket option.
   777  func (e *Endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
   778  	switch o := opt.(type) {
   779  	case *tcpip.MulticastInterfaceOption:
   780  		e.mu.Lock()
   781  		*o = tcpip.MulticastInterfaceOption{
   782  			NIC:           e.multicastNICID,
   783  			InterfaceAddr: e.multicastAddr,
   784  		}
   785  		e.mu.Unlock()
   786  
   787  	default:
   788  		return &tcpip.ErrUnknownProtocolOption{}
   789  	}
   790  	return nil
   791  }
   792  
   793  // Info returns a copy of the endpoint info.
   794  func (e *Endpoint) Info() stack.TransportEndpointInfo {
   795  	e.infoMu.RLock()
   796  	defer e.infoMu.RUnlock()
   797  	return e.info
   798  }
   799  
   800  // setInfo sets the endpoint's info.
   801  //
   802  // e.mu must be held to synchronize changes to info with the rest of the
   803  // endpoint.
   804  //
   805  // +checklocks:e.mu
   806  func (e *Endpoint) setInfo(info stack.TransportEndpointInfo) {
   807  	e.infoMu.Lock()
   808  	defer e.infoMu.Unlock()
   809  	e.info = info
   810  }