func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
	netProto, err := e.checkV4Mapped(&addr, false)
	if err != nil {
		return err
	}
	if addr.Port == 0 {
		return tcpip.ErrInvalidEndpointState
	}
	e.mu.Lock()
	defer e.mu.Unlock()
	nicID := addr.NIC
	var localPort uint16
	switch e.state {
	case StateInitial:
	case StateBound, StateConnected:
		localPort = e.ID.LocalPort
		if e.BindNICID == 0 {
			break
		}
		if nicID != 0 && nicID != e.BindNICID {
			return tcpip.ErrInvalidEndpointState
		}
		nicID = e.BindNICID
	default:
		return tcpip.ErrInvalidEndpointState
	}
	r, nicID, err := e.connectRoute(nicID, addr, netProto)
	if err != nil {
		return err
	}
	defer r.Release()
	id := stack.TransportEndpointID{
		LocalAddress:  e.ID.LocalAddress,
		LocalPort:     localPort,
		RemotePort:    addr.Port,
		RemoteAddress: r.RemoteAddress,
	}
	if e.state == StateInitial {
		id.LocalAddress = r.LocalAddress
	}
	netProtos := []tcpip.NetworkProtocolNumber{netProto}
	if netProto == header.IPv6ProtocolNumber && !e.v6only {
		netProtos = []tcpip.NetworkProtocolNumber{
			header.IPv4ProtocolNumber,
			header.IPv6ProtocolNumber,
		}
	}
	id, err = e.registerWithStack(nicID, netProtos, id)
	if err != nil {
		return err
	}
	if e.ID.LocalPort != 0 {
		e.stack.UnregisterTransportEndpoint(e.RegisterNICID, e.effectiveNetProtos, ProtocolNumber, e.ID, e, e.bindToDevice)
	}
	e.ID = id
	e.route = r.Clone()
	e.dstPort = addr.Port
	e.RegisterNICID = nicID
	e.effectiveNetProtos = netProtos
	e.state = StateConnected
	e.rcvMu.Lock()
	e.rcvReady = true
	e.rcvMu.Unlock()
	return nil
}
Connect connects the endpoint to its peer. Specifying a NIC is optional.
package udp
import (
	"sync"
	"github.com/google/netstack/tcpip"
	"github.com/google/netstack/tcpip/buffer"
	"github.com/google/netstack/tcpip/header"
	"github.com/google/netstack/tcpip/iptables"
	"github.com/google/netstack/tcpip/stack"
	"github.com/google/netstack/waiter"
)
type udpPacket struct {
	udpPacketEntry
	senderAddress tcpip.FullAddress
	data          buffer.VectorisedView
	timestamp     int64
}
type EndpointState uint32
const (
	StateInitial EndpointState = iota
	StateBound
	StateConnected
	StateClosed
)
func (s EndpointState) String() string {
	switch s {
	case StateInitial:
		return "INITIAL"
	case StateBound:
		return "BOUND"
	case StateConnected:
		return "CONNECTING"
	case StateClosed:
		return "CLOSED"
	default:
		return "UNKNOWN"
	}
}
type endpoint struct {
	stack.TransportEndpointInfo
	stack       *stack.Stack
	waiterQueue *waiter.Queue
	uniqueID    uint64
	rcvMu         sync.Mutex
	rcvReady      bool
	rcvList       udpPacketList
	rcvBufSizeMax int
	rcvBufSize    int
	rcvClosed     bool
	mu             sync.RWMutex
	sndBufSize     int
	state          EndpointState
	route          stack.Route
	dstPort        uint16
	v6only         bool
	ttl            uint8
	multicastTTL   uint8
	multicastAddr  tcpip.Address
	multicastNICID tcpip.NICID
	multicastLoop  bool
	reusePort      bool
	bindToDevice   tcpip.NICID
	broadcast      bool
	sendTOS uint8
	shutdownFlags tcpip.ShutdownFlags
	multicastMemberships []multicastMembership
	effectiveNetProtos []tcpip.NetworkProtocolNumber
	stats tcpip.TransportEndpointStats
}
type multicastMembership struct {
	nicID         tcpip.NICID
	multicastAddr tcpip.Address
}
func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) *endpoint {
	return &endpoint{
		stack: s,
		TransportEndpointInfo: stack.TransportEndpointInfo{
			NetProto:   netProto,
			TransProto: header.UDPProtocolNumber,
		},
		waiterQueue: waiterQueue,
		multicastTTL:  1,
		multicastLoop: true,
		rcvBufSizeMax: 32 * 1024,
		sndBufSize:    32 * 1024,
		state:         StateInitial,
		uniqueID:      s.UniqueID(),
	}
}
func (e *endpoint) UniqueID() uint64 {
	return e.uniqueID
}
func (e *endpoint) Close() {
	e.mu.Lock()
	e.shutdownFlags = tcpip.ShutdownRead | tcpip.ShutdownWrite
	switch e.state {
	case StateBound, StateConnected:
		e.stack.UnregisterTransportEndpoint(e.RegisterNICID, e.effectiveNetProtos, ProtocolNumber, e.ID, e, e.bindToDevice)
		e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.bindToDevice)
	}
	for _, mem := range e.multicastMemberships {
		e.stack.LeaveGroup(e.NetProto, mem.nicID, mem.multicastAddr)
	}
	e.multicastMemberships = nil
	e.rcvMu.Lock()
	e.rcvClosed = true
	e.rcvBufSize = 0
	for !e.rcvList.Empty() {
		p := e.rcvList.Front()
		e.rcvList.Remove(p)
	}
	e.rcvMu.Unlock()
	e.route.Release()
	e.state = StateClosed
	e.mu.Unlock()
	e.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.EventIn | waiter.EventOut)
}
func (e *endpoint) ModerateRecvBuf(copied int) {}
func (e *endpoint) IPTables() (iptables.IPTables, error) {
	return e.stack.IPTables(), nil
}
func (e *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMessages, *tcpip.Error) {
	e.rcvMu.Lock()
	if e.rcvList.Empty() {
		err := tcpip.ErrWouldBlock
		if e.rcvClosed {
			e.stats.ReadErrors.ReadClosed.Increment()
			err = tcpip.ErrClosedForReceive
		}
		e.rcvMu.Unlock()
		return buffer.View{}, tcpip.ControlMessages{}, err
	}
	p := e.rcvList.Front()
	e.rcvList.Remove(p)
	e.rcvBufSize -= p.data.Size()
	e.rcvMu.Unlock()
	if addr != nil {
		*addr = p.senderAddress
	}
	return p.data.ToView(), tcpip.ControlMessages{HasTimestamp: true, Timestamp: p.timestamp}, nil
}
func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err *tcpip.Error) {
	switch e.state {
	case StateInitial:
	case StateConnected:
		return false, nil
	case StateBound:
		if to == nil {
			return false, tcpip.ErrDestinationRequired
		}
		return false, nil
	default:
		return false, tcpip.ErrInvalidEndpointState
	}
	e.mu.RUnlock()
	defer e.mu.RLock()
	e.mu.Lock()
	defer e.mu.Unlock()
	if e.state != StateInitial {
		return true, nil
	}
	if err := e.bindLocked(tcpip.FullAddress{}); err != nil {
		return false, err
	}
	return true, nil
}
func (e *endpoint) connectRoute(nicID tcpip.NICID, addr tcpip.FullAddress, netProto tcpip.NetworkProtocolNumber) (stack.Route, tcpip.NICID, *tcpip.Error) {
	localAddr := e.ID.LocalAddress
	if isBroadcastOrMulticast(localAddr) {
		localAddr = ""
	}
	if header.IsV4MulticastAddress(addr.Addr) || header.IsV6MulticastAddress(addr.Addr) {
		if nicID == 0 {
			nicID = e.multicastNICID
		}
		if localAddr == "" && nicID == 0 {
			localAddr = e.multicastAddr
		}
	}
	r, err := e.stack.FindRoute(nicID, localAddr, addr.Addr, netProto, e.multicastLoop)
	if err != nil {
		return stack.Route{}, 0, err
	}
	return r, nicID, nil
}
func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-chan struct{}, *tcpip.Error) {
	n, ch, err := e.write(p, opts)
	switch err {
	case nil:
		e.stats.PacketsSent.Increment()
	case tcpip.ErrMessageTooLong, tcpip.ErrInvalidOptionValue:
		e.stats.WriteErrors.InvalidArgs.Increment()
	case tcpip.ErrClosedForSend:
		e.stats.WriteErrors.WriteClosed.Increment()
	case tcpip.ErrInvalidEndpointState:
		e.stats.WriteErrors.InvalidEndpointState.Increment()
	case tcpip.ErrNoLinkAddress:
		e.stats.SendErrors.NoLinkAddr.Increment()
	case tcpip.ErrNoRoute, tcpip.ErrBroadcastDisabled, tcpip.ErrNetworkUnreachable:
		e.stats.SendErrors.NoRoute.Increment()
	default:
		e.stats.SendErrors.SendToNetworkFailed.Increment()
	}
	return n, ch, err
}
func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-chan struct{}, *tcpip.Error) {
	if opts.More {
		return 0, nil, tcpip.ErrInvalidOptionValue
	}
	to := opts.To
	e.mu.RLock()
	defer e.mu.RUnlock()
	if e.shutdownFlags&tcpip.ShutdownWrite != 0 {
		return 0, nil, tcpip.ErrClosedForSend
	}
	for {
		retry, err := e.prepareForWrite(to)
		if err != nil {
			return 0, nil, err
		}
		if !retry {
			break
		}
	}
	var route *stack.Route
	var dstPort uint16
	if to == nil {
		route = &e.route
		dstPort = e.dstPort
		if route.IsResolutionRequired() {
			e.mu.RUnlock()
			defer e.mu.RLock()
			e.mu.Lock()
			defer e.mu.Unlock()
			if e.state != StateConnected {
				return 0, nil, tcpip.ErrInvalidEndpointState
			}
		}
	} else {
		nicID := to.NIC
		if e.BindNICID != 0 {
			if nicID != 0 && nicID != e.BindNICID {
				return 0, nil, tcpip.ErrNoRoute
			}
			nicID = e.BindNICID
		}
		if to.Addr == header.IPv4Broadcast && !e.broadcast {
			return 0, nil, tcpip.ErrBroadcastDisabled
		}
		netProto, err := e.checkV4Mapped(to, false)
		if err != nil {
			return 0, nil, err
		}
		r, _, err := e.connectRoute(nicID, *to, netProto)
		if err != nil {
			return 0, nil, err
		}
		defer r.Release()
		route = &r
		dstPort = to.Port
	}
	if route.IsResolutionRequired() {
		if ch, err := route.Resolve(nil); err != nil {
			if err == tcpip.ErrWouldBlock {
				return 0, ch, tcpip.ErrNoLinkAddress
			}
			return 0, nil, err
		}
	}
	v, err := p.FullPayload()
	if err != nil {
		return 0, nil, err
	}
	if len(v) > header.UDPMaximumPacketSize {
		return 0, nil, tcpip.ErrMessageTooLong
	}
	ttl := e.ttl
	useDefaultTTL := ttl == 0
	if header.IsV4MulticastAddress(route.RemoteAddress) || header.IsV6MulticastAddress(route.RemoteAddress) {
		ttl = e.multicastTTL
		useDefaultTTL = false
	}
	if err := sendUDP(route, buffer.View(v).ToVectorisedView(), e.ID.LocalPort, dstPort, ttl, useDefaultTTL, e.sendTOS); err != nil {
		return 0, nil, err
	}
	return int64(len(v)), nil, nil
}
func (e *endpoint) Peek([][]byte) (int64, tcpip.ControlMessages, *tcpip.Error) {
	return 0, tcpip.ControlMessages{}, nil
}
func (e *endpoint) SetSockOptInt(opt tcpip.SockOpt, v int) *tcpip.Error {
	return nil
}
func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
	switch v := opt.(type) {
	case tcpip.V6OnlyOption:
		if e.NetProto != header.IPv6ProtocolNumber {
			return tcpip.ErrInvalidEndpointState
		}
		e.mu.Lock()
		defer e.mu.Unlock()
		if e.state != StateInitial {
			return tcpip.ErrInvalidEndpointState
		}
		e.v6only = v != 0
	case tcpip.TTLOption:
		e.mu.Lock()
		e.ttl = uint8(v)
		e.mu.Unlock()
	case tcpip.MulticastTTLOption:
		e.mu.Lock()
		e.multicastTTL = uint8(v)
		e.mu.Unlock()
	case tcpip.MulticastInterfaceOption:
		e.mu.Lock()
		defer e.mu.Unlock()
		fa := tcpip.FullAddress{Addr: v.InterfaceAddr}
		netProto, err := e.checkV4Mapped(&fa, false)
		if err != nil {
			return err
		}
		nic := v.NIC
		addr := fa.Addr
		if nic == 0 && addr == "" {
			e.multicastAddr = ""
			e.multicastNICID = 0
			break
		}
		if nic != 0 {
			if !e.stack.CheckNIC(nic) {
				return tcpip.ErrBadLocalAddress
			}
		} else {
			nic = e.stack.CheckLocalAddress(0, netProto, addr)
			if nic == 0 {
				return tcpip.ErrBadLocalAddress
			}
		}
		if e.BindNICID != 0 && e.BindNICID != nic {
			return tcpip.ErrInvalidEndpointState
		}
		e.multicastNICID = nic
		e.multicastAddr = addr
	case tcpip.AddMembershipOption:
		if !header.IsV4MulticastAddress(v.MulticastAddr) && !header.IsV6MulticastAddress(v.MulticastAddr) {
			return tcpip.ErrInvalidOptionValue
		}
		nicID := v.NIC
		allZeros := header.IPv4Any
		if len(v.InterfaceAddr) == 0 || v.InterfaceAddr == allZeros {
			if nicID == 0 {
				r, err := e.stack.FindRoute(0, "", v.MulticastAddr, header.IPv4ProtocolNumber, false  )
				if err == nil {
					nicID = r.NICID()
					r.Release()
				}
			}
		} else {
			nicID = e.stack.CheckLocalAddress(nicID, e.NetProto, v.InterfaceAddr)
		}
		if nicID == 0 {
			return tcpip.ErrUnknownDevice
		}
		memToInsert := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr}
		e.mu.Lock()
		defer e.mu.Unlock()
		for _, mem := range e.multicastMemberships {
			if mem == memToInsert {
				return tcpip.ErrPortInUse
			}
		}
		if err := e.stack.JoinGroup(e.NetProto, nicID, v.MulticastAddr); err != nil {
			return err
		}
		e.multicastMemberships = append(e.multicastMemberships, memToInsert)
	case tcpip.RemoveMembershipOption:
		if !header.IsV4MulticastAddress(v.MulticastAddr) && !header.IsV6MulticastAddress(v.MulticastAddr) {
			return tcpip.ErrInvalidOptionValue
		}
		nicID := v.NIC
		if v.InterfaceAddr == header.IPv4Any {
			if nicID == 0 {
				r, err := e.stack.FindRoute(0, "", v.MulticastAddr, header.IPv4ProtocolNumber, false  )
				if err == nil {
					nicID = r.NICID()
					r.Release()
				}
			}
		} else {
			nicID = e.stack.CheckLocalAddress(nicID, e.NetProto, v.InterfaceAddr)
		}
		if nicID == 0 {
			return tcpip.ErrUnknownDevice
		}
		memToRemove := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr}
		memToRemoveIndex := -1
		e.mu.Lock()
		defer e.mu.Unlock()
		for i, mem := range e.multicastMemberships {
			if mem == memToRemove {
				memToRemoveIndex = i
				break
			}
		}
		if memToRemoveIndex == -1 {
			return tcpip.ErrBadLocalAddress
		}
		if err := e.stack.LeaveGroup(e.NetProto, nicID, v.MulticastAddr); err != nil {
			return err
		}
		e.multicastMemberships[memToRemoveIndex] = e.multicastMemberships[len(e.multicastMemberships)-1]
		e.multicastMemberships = e.multicastMemberships[:len(e.multicastMemberships)-1]
	case tcpip.MulticastLoopOption:
		e.mu.Lock()
		e.multicastLoop = bool(v)
		e.mu.Unlock()
	case tcpip.ReusePortOption:
		e.mu.Lock()
		e.reusePort = v != 0
		e.mu.Unlock()
	case tcpip.BindToDeviceOption:
		e.mu.Lock()
		defer e.mu.Unlock()
		if v == "" {
			e.bindToDevice = 0
			return nil
		}
		for nicID, nic := range e.stack.NICInfo() {
			if nic.Name == string(v) {
				e.bindToDevice = nicID
				return nil
			}
		}
		return tcpip.ErrUnknownDevice
	case tcpip.BroadcastOption:
		e.mu.Lock()
		e.broadcast = v != 0
		e.mu.Unlock()
		return nil
	case tcpip.IPv4TOSOption:
		e.mu.Lock()
		e.sendTOS = uint8(v)
		e.mu.Unlock()
		return nil
	case tcpip.IPv6TrafficClassOption:
		e.mu.Lock()
		e.sendTOS = uint8(v)
		e.mu.Unlock()
		return nil
	}
	return nil
}
func (e *endpoint) GetSockOptInt(opt tcpip.SockOpt) (int, *tcpip.Error) {
	switch opt {
	case tcpip.ReceiveQueueSizeOption:
		v := 0
		e.rcvMu.Lock()
		if !e.rcvList.Empty() {
			p := e.rcvList.Front()
			v = p.data.Size()
		}
		e.rcvMu.Unlock()
		return v, nil
	case tcpip.SendBufferSizeOption:
		e.mu.Lock()
		v := e.sndBufSize
		e.mu.Unlock()
		return v, nil
	case tcpip.ReceiveBufferSizeOption:
		e.rcvMu.Lock()
		v := e.rcvBufSizeMax
		e.rcvMu.Unlock()
		return v, nil
	}
	return -1, tcpip.ErrUnknownProtocolOption
}
func (e *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
	switch o := opt.(type) {
	case tcpip.ErrorOption:
		return nil
	case *tcpip.V6OnlyOption:
		if e.NetProto != header.IPv6ProtocolNumber {
			return tcpip.ErrUnknownProtocolOption
		}
		e.mu.Lock()
		v := e.v6only
		e.mu.Unlock()
		*o = 0
		if v {
			*o = 1
		}
		return nil
	case *tcpip.TTLOption:
		e.mu.Lock()
		*o = tcpip.TTLOption(e.ttl)
		e.mu.Unlock()
		return nil
	case *tcpip.MulticastTTLOption:
		e.mu.Lock()
		*o = tcpip.MulticastTTLOption(e.multicastTTL)
		e.mu.Unlock()
		return nil
	case *tcpip.MulticastInterfaceOption:
		e.mu.Lock()
		*o = tcpip.MulticastInterfaceOption{
			e.multicastNICID,
			e.multicastAddr,
		}
		e.mu.Unlock()
		return nil
	case *tcpip.MulticastLoopOption:
		e.mu.RLock()
		v := e.multicastLoop
		e.mu.RUnlock()
		*o = tcpip.MulticastLoopOption(v)
		return nil
	case *tcpip.ReuseAddressOption:
		*o = 0
		return nil
	case *tcpip.ReusePortOption:
		e.mu.RLock()
		v := e.reusePort
		e.mu.RUnlock()
		*o = 0
		if v {
			*o = 1
		}
		return nil
	case *tcpip.BindToDeviceOption:
		e.mu.RLock()
		defer e.mu.RUnlock()
		if nic, ok := e.stack.NICInfo()[e.bindToDevice]; ok {
			*o = tcpip.BindToDeviceOption(nic.Name)
			return nil
		}
		*o = tcpip.BindToDeviceOption("")
		return nil
	case *tcpip.KeepaliveEnabledOption:
		*o = 0
		return nil
	case *tcpip.BroadcastOption:
		e.mu.RLock()
		v := e.broadcast
		e.mu.RUnlock()
		*o = 0
		if v {
			*o = 1
		}
		return nil
	case *tcpip.IPv4TOSOption:
		e.mu.RLock()
		*o = tcpip.IPv4TOSOption(e.sendTOS)
		e.mu.RUnlock()
		return nil
	case *tcpip.IPv6TrafficClassOption:
		e.mu.RLock()
		*o = tcpip.IPv6TrafficClassOption(e.sendTOS)
		e.mu.RUnlock()
		return nil
	default:
		return tcpip.ErrUnknownProtocolOption
	}
}
func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, ttl uint8, useDefaultTTL bool, tos uint8) *tcpip.Error {
	hdr := buffer.NewPrependable(header.UDPMinimumSize + int(r.MaxHeaderLength()))
	udp := header.UDP(hdr.Prepend(header.UDPMinimumSize))
	length := uint16(hdr.UsedLength() + data.Size())
	udp.Encode(&header.UDPFields{
		SrcPort: localPort,
		DstPort: remotePort,
		Length:  length,
	})
	if r.Capabilities()&stack.CapabilityTXChecksumOffload == 0 {
		xsum := r.PseudoHeaderChecksum(ProtocolNumber, length)
		for _, v := range data.Views() {
			xsum = header.Checksum(v, xsum)
		}
		udp.SetChecksum(^udp.CalculateChecksum(xsum))
	}
	if useDefaultTTL {
		ttl = r.DefaultTTL()
	}
	if err := r.WritePacket(nil  , stack.NetworkHeaderParams{Protocol: ProtocolNumber, TTL: ttl, TOS: tos}, tcpip.PacketBuffer{
		Header: hdr,
		Data:   data,
	}); err != nil {
		r.Stats().UDP.PacketSendErrors.Increment()
		return err
	}
	r.Stats().UDP.PacketsSent.Increment()
	return nil
}
func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress, allowMismatch bool) (tcpip.NetworkProtocolNumber, *tcpip.Error) {
	netProto := e.NetProto
	if len(addr.Addr) == 0 {
		return netProto, nil
	}
	if header.IsV4MappedAddress(addr.Addr) {
		if e.v6only {
			return 0, tcpip.ErrNoRoute
		}
		netProto = header.IPv4ProtocolNumber
		addr.Addr = addr.Addr[header.IPv6AddressSize-header.IPv4AddressSize:]
		if addr.Addr == header.IPv4Any {
			addr.Addr = ""
		}
		if !allowMismatch && len(e.ID.LocalAddress) == 16 {
			return 0, tcpip.ErrNetworkUnreachable
		}
	}
	if l := len(e.ID.LocalAddress); l != 0 && l != len(addr.Addr) {
		return 0, tcpip.ErrInvalidEndpointState
	}
	return netProto, nil
}
func (e *endpoint) Disconnect() *tcpip.Error {
	e.mu.Lock()
	defer e.mu.Unlock()
	if e.state != StateConnected {
		return nil
	}
	id := stack.TransportEndpointID{}
	if e.BindNICID != 0 || e.ID.LocalAddress == "" {
		var err *tcpip.Error
		id = stack.TransportEndpointID{
			LocalPort:    e.ID.LocalPort,
			LocalAddress: e.ID.LocalAddress,
		}
		id, err = e.registerWithStack(e.RegisterNICID, e.effectiveNetProtos, id)
		if err != nil {
			return err
		}
		e.state = StateBound
	} else {
		if e.ID.LocalPort != 0 {
			e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.bindToDevice)
		}
		e.state = StateInitial
	}
	e.stack.UnregisterTransportEndpoint(e.RegisterNICID, e.effectiveNetProtos, ProtocolNumber, e.ID, e, e.bindToDevice)
	e.ID = id
	e.route.Release()
	e.route = stack.Route{}
	e.dstPort = 0
	return nil
}