github.com/database64128/shadowsocks-go@v1.7.0/service/udp_session_mmsg.go (about)

     1  //go:build linux || netbsd
     2  
     3  package service
     4  
     5  import (
     6  	"bytes"
     7  	"errors"
     8  	"net/netip"
     9  	"os"
    10  	"sync/atomic"
    11  	"time"
    12  	"unsafe"
    13  
    14  	"github.com/database64128/shadowsocks-go/conn"
    15  	"github.com/database64128/shadowsocks-go/router"
    16  	"github.com/database64128/shadowsocks-go/zerocopy"
    17  	"go.uber.org/zap"
    18  	"golang.org/x/sys/unix"
    19  )
    20  
    21  // sessionUplinkMmsg is used for passing information about relay uplink to the relay goroutine.
    22  type sessionUplinkMmsg struct {
    23  	csid          uint64
    24  	natConn       *conn.MmsgWConn
    25  	natConnSendCh <-chan *sessionQueuedPacket
    26  	natConnPacker zerocopy.ClientPacker
    27  	username      string
    28  }
    29  
    30  // sessionDownlinkMmsg is used for passing information about relay downlink to the relay goroutine.
    31  type sessionDownlinkMmsg struct {
    32  	csid               uint64
    33  	clientAddrInfop    *sessionClientAddrInfo
    34  	clientAddrInfo     *atomic.Pointer[sessionClientAddrInfo]
    35  	natConn            *conn.MmsgRConn
    36  	natConnRecvBufSize int
    37  	natConnUnpacker    zerocopy.ClientUnpacker
    38  	serverConn         *conn.MmsgWConn
    39  	serverConnPacker   zerocopy.ServerPacker
    40  	username           string
    41  }
    42  
    43  func (s *UDPSessionRelay) setStartFunc(batchMode string) {
    44  	switch batchMode {
    45  	case "sendmmsg", "":
    46  		s.startFunc = s.startMmsg
    47  	default:
    48  		s.startFunc = s.startGeneric
    49  	}
    50  }
    51  
    52  func (s *UDPSessionRelay) startMmsg() error {
    53  	serverConn, err := conn.ListenUDPRawConn(s.serverConnListenConfig, "udp", s.listenAddress)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	s.serverConn = serverConn.UDPConn
    58  
    59  	s.mwg.Add(1)
    60  
    61  	go func() {
    62  		s.recvFromServerConnRecvmmsg(serverConn.RConn())
    63  		s.mwg.Done()
    64  	}()
    65  
    66  	s.logger.Info("Started UDP session relay service",
    67  		zap.String("server", s.serverName),
    68  		zap.String("listenAddress", s.listenAddress),
    69  	)
    70  
    71  	return nil
    72  }
    73  
    74  func (s *UDPSessionRelay) recvFromServerConnRecvmmsg(serverConn *conn.MmsgRConn) {
    75  	n := s.serverRecvBatchSize
    76  	qpvec := make([]*sessionQueuedPacket, n)
    77  	namevec := make([]unix.RawSockaddrInet6, n)
    78  	iovec := make([]unix.Iovec, n)
    79  	cmsgvec := make([][]byte, n)
    80  	msgvec := make([]conn.Mmsghdr, n)
    81  
    82  	for i := range msgvec {
    83  		cmsgBuf := make([]byte, conn.SocketControlMessageBufferSize)
    84  		cmsgvec[i] = cmsgBuf
    85  		msgvec[i].Msghdr.Name = (*byte)(unsafe.Pointer(&namevec[i]))
    86  		msgvec[i].Msghdr.Namelen = unix.SizeofSockaddrInet6
    87  		msgvec[i].Msghdr.Iov = &iovec[i]
    88  		msgvec[i].Msghdr.SetIovlen(1)
    89  		msgvec[i].Msghdr.Control = &cmsgBuf[0]
    90  	}
    91  
    92  	var (
    93  		err                  error
    94  		recvmmsgCount        uint64
    95  		packetsReceived      uint64
    96  		payloadBytesReceived uint64
    97  		burstBatchSize       int
    98  	)
    99  
   100  	for {
   101  		for i := range iovec[:n] {
   102  			queuedPacket := s.getQueuedPacket()
   103  			qpvec[i] = queuedPacket
   104  			iovec[i].Base = &queuedPacket.buf[s.packetBufFrontHeadroom]
   105  			iovec[i].SetLen(s.packetBufRecvSize)
   106  			msgvec[i].Msghdr.SetControllen(conn.SocketControlMessageBufferSize)
   107  		}
   108  
   109  		n, err = serverConn.ReadMsgs(msgvec, 0)
   110  		if err != nil {
   111  			if errors.Is(err, os.ErrDeadlineExceeded) {
   112  				break
   113  			}
   114  
   115  			s.logger.Warn("Failed to batch read packets from serverConn",
   116  				zap.String("server", s.serverName),
   117  				zap.String("listenAddress", s.listenAddress),
   118  				zap.Error(err),
   119  			)
   120  
   121  			n = 1
   122  			s.putQueuedPacket(qpvec[0])
   123  			continue
   124  		}
   125  
   126  		recvmmsgCount++
   127  		packetsReceived += uint64(n)
   128  		if burstBatchSize < n {
   129  			burstBatchSize = n
   130  		}
   131  
   132  		s.server.Lock()
   133  
   134  		msgvecn := msgvec[:n]
   135  
   136  		for i := range msgvecn {
   137  			msg := &msgvecn[i]
   138  			queuedPacket := qpvec[i]
   139  
   140  			if msg.Msghdr.Controllen == 0 {
   141  				s.logger.Warn("Skipping packet with no control message from serverConn",
   142  					zap.String("server", s.serverName),
   143  					zap.String("listenAddress", s.listenAddress),
   144  				)
   145  
   146  				s.putQueuedPacket(queuedPacket)
   147  				continue
   148  			}
   149  
   150  			queuedPacket.clientAddrPort, err = conn.SockaddrToAddrPort(msg.Msghdr.Name, msg.Msghdr.Namelen)
   151  			if err != nil {
   152  				s.logger.Warn("Failed to parse sockaddr of packet from serverConn",
   153  					zap.String("server", s.serverName),
   154  					zap.String("listenAddress", s.listenAddress),
   155  					zap.Error(err),
   156  				)
   157  
   158  				s.putQueuedPacket(queuedPacket)
   159  				continue
   160  			}
   161  
   162  			err = conn.ParseFlagsForError(int(msg.Msghdr.Flags))
   163  			if err != nil {
   164  				s.logger.Warn("Packet from serverConn discarded",
   165  					zap.String("server", s.serverName),
   166  					zap.String("listenAddress", s.listenAddress),
   167  					zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   168  					zap.Uint32("packetLength", msg.Msglen),
   169  					zap.Error(err),
   170  				)
   171  
   172  				s.putQueuedPacket(queuedPacket)
   173  				continue
   174  			}
   175  
   176  			packet := queuedPacket.buf[s.packetBufFrontHeadroom : s.packetBufFrontHeadroom+int(msg.Msglen)]
   177  
   178  			csid, err := s.server.SessionInfo(packet)
   179  			if err != nil {
   180  				s.logger.Warn("Failed to extract session info from packet",
   181  					zap.String("server", s.serverName),
   182  					zap.String("listenAddress", s.listenAddress),
   183  					zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   184  					zap.Uint32("packetLength", msg.Msglen),
   185  					zap.Error(err),
   186  				)
   187  
   188  				s.putQueuedPacket(queuedPacket)
   189  				continue
   190  			}
   191  
   192  			entry, ok := s.table[csid]
   193  			if !ok {
   194  				entry = &session{}
   195  
   196  				entry.serverConnUnpacker, entry.username, err = s.server.NewUnpacker(packet, csid)
   197  				if err != nil {
   198  					s.logger.Warn("Failed to create unpacker for client session",
   199  						zap.String("server", s.serverName),
   200  						zap.String("listenAddress", s.listenAddress),
   201  						zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   202  						zap.Uint64("clientSessionID", csid),
   203  						zap.Uint32("packetLength", msg.Msglen),
   204  						zap.Error(err),
   205  					)
   206  
   207  					s.putQueuedPacket(queuedPacket)
   208  					continue
   209  				}
   210  			}
   211  
   212  			queuedPacket.targetAddr, queuedPacket.start, queuedPacket.length, err = entry.serverConnUnpacker.UnpackInPlace(queuedPacket.buf, queuedPacket.clientAddrPort, s.packetBufFrontHeadroom, int(msg.Msglen))
   213  			if err != nil {
   214  				s.logger.Warn("Failed to unpack packet from serverConn",
   215  					zap.String("server", s.serverName),
   216  					zap.String("listenAddress", s.listenAddress),
   217  					zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   218  					zap.String("username", entry.username),
   219  					zap.Uint64("clientSessionID", csid),
   220  					zap.Uint32("packetLength", msg.Msglen),
   221  					zap.Error(err),
   222  				)
   223  
   224  				s.putQueuedPacket(queuedPacket)
   225  				continue
   226  			}
   227  
   228  			payloadBytesReceived += uint64(queuedPacket.length)
   229  
   230  			var clientAddrInfop *sessionClientAddrInfo
   231  			cmsg := cmsgvec[i][:msg.Msghdr.Controllen]
   232  
   233  			updateClientAddrPort := entry.clientAddrPortCache != queuedPacket.clientAddrPort
   234  			updateClientPktinfo := !bytes.Equal(entry.clientPktinfoCache, cmsg)
   235  
   236  			if updateClientAddrPort {
   237  				entry.clientAddrPortCache = queuedPacket.clientAddrPort
   238  			}
   239  
   240  			if updateClientPktinfo {
   241  				entry.clientPktinfoCache = make([]byte, len(cmsg))
   242  				copy(entry.clientPktinfoCache, cmsg)
   243  			}
   244  
   245  			if updateClientAddrPort || updateClientPktinfo {
   246  				clientPktinfoAddr, clientPktinfoIfindex, err := conn.ParsePktinfoCmsg(cmsg)
   247  				if err != nil {
   248  					s.logger.Warn("Failed to parse pktinfo control message from serverConn",
   249  						zap.String("server", s.serverName),
   250  						zap.String("listenAddress", s.listenAddress),
   251  						zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   252  						zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   253  						zap.String("username", entry.username),
   254  						zap.Uint64("clientSessionID", csid),
   255  						zap.Error(err),
   256  					)
   257  
   258  					s.putQueuedPacket(queuedPacket)
   259  					continue
   260  				}
   261  
   262  				clientAddrInfop = &sessionClientAddrInfo{entry.clientAddrPortCache, entry.clientPktinfoCache}
   263  				entry.clientAddrInfo.Store(clientAddrInfop)
   264  
   265  				if ce := s.logger.Check(zap.DebugLevel, "Updated client address info"); ce != nil {
   266  					ce.Write(
   267  						zap.String("server", s.serverName),
   268  						zap.String("listenAddress", s.listenAddress),
   269  						zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   270  						zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   271  						zap.Stringer("clientPktinfoAddr", clientPktinfoAddr),
   272  						zap.Uint32("clientPktinfoIfindex", clientPktinfoIfindex),
   273  						zap.String("username", entry.username),
   274  						zap.Uint64("clientSessionID", csid),
   275  					)
   276  				}
   277  			}
   278  
   279  			if !ok {
   280  				natConnSendCh := make(chan *sessionQueuedPacket, s.sendChannelCapacity)
   281  				entry.natConnSendCh = natConnSendCh
   282  				s.table[csid] = entry
   283  
   284  				go func() {
   285  					var sendChClean bool
   286  
   287  					defer func() {
   288  						s.server.Lock()
   289  						close(natConnSendCh)
   290  						delete(s.table, csid)
   291  						s.server.Unlock()
   292  
   293  						if !sendChClean {
   294  							for queuedPacket := range natConnSendCh {
   295  								s.putQueuedPacket(queuedPacket)
   296  							}
   297  						}
   298  					}()
   299  
   300  					c, err := s.router.GetUDPClient(router.RequestInfo{
   301  						ServerIndex:    s.serverIndex,
   302  						Username:       entry.username,
   303  						SourceAddrPort: queuedPacket.clientAddrPort,
   304  						TargetAddr:     queuedPacket.targetAddr,
   305  					})
   306  					if err != nil {
   307  						s.logger.Warn("Failed to get UDP client for new NAT session",
   308  							zap.String("server", s.serverName),
   309  							zap.String("listenAddress", s.listenAddress),
   310  							zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   311  							zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   312  							zap.String("username", entry.username),
   313  							zap.Uint64("clientSessionID", csid),
   314  							zap.Error(err),
   315  						)
   316  						return
   317  					}
   318  
   319  					// Only add for the current goroutine here, since we don't want the router to block exiting.
   320  					s.wg.Add(1)
   321  					defer s.wg.Done()
   322  
   323  					clientInfo, natConnPacker, natConnUnpacker, err := c.NewSession()
   324  					if err != nil {
   325  						s.logger.Warn("Failed to create new UDP client session",
   326  							zap.String("server", s.serverName),
   327  							zap.String("client", clientInfo.Name),
   328  							zap.String("listenAddress", s.listenAddress),
   329  							zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   330  							zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   331  							zap.String("username", entry.username),
   332  							zap.Uint64("clientSessionID", csid),
   333  							zap.Error(err),
   334  						)
   335  						return
   336  					}
   337  
   338  					serverConnPacker, err := entry.serverConnUnpacker.NewPacker()
   339  					if err != nil {
   340  						s.logger.Warn("Failed to create packer for client session",
   341  							zap.String("server", s.serverName),
   342  							zap.String("client", clientInfo.Name),
   343  							zap.String("listenAddress", s.listenAddress),
   344  							zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   345  							zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   346  							zap.String("username", entry.username),
   347  							zap.Uint64("clientSessionID", csid),
   348  							zap.Error(err),
   349  						)
   350  						return
   351  					}
   352  
   353  					natConn, err := conn.ListenUDPRawConn(clientInfo.ListenConfig, "udp", "")
   354  					if err != nil {
   355  						s.logger.Warn("Failed to create UDP socket for new NAT session",
   356  							zap.String("server", s.serverName),
   357  							zap.String("client", clientInfo.Name),
   358  							zap.String("listenAddress", s.listenAddress),
   359  							zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   360  							zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   361  							zap.String("username", entry.username),
   362  							zap.Uint64("clientSessionID", csid),
   363  							zap.Error(err),
   364  						)
   365  						return
   366  					}
   367  
   368  					err = natConn.SetReadDeadline(time.Now().Add(s.natTimeout))
   369  					if err != nil {
   370  						s.logger.Warn("Failed to set read deadline on natConn",
   371  							zap.String("server", s.serverName),
   372  							zap.String("client", clientInfo.Name),
   373  							zap.String("listenAddress", s.listenAddress),
   374  							zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   375  							zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   376  							zap.Duration("natTimeout", s.natTimeout),
   377  							zap.String("username", entry.username),
   378  							zap.Uint64("clientSessionID", csid),
   379  							zap.Error(err),
   380  						)
   381  						natConn.Close()
   382  						return
   383  					}
   384  
   385  					oldState := entry.state.Swap(natConn.UDPConn)
   386  					if oldState != nil {
   387  						natConn.Close()
   388  						return
   389  					}
   390  
   391  					// No more early returns!
   392  					sendChClean = true
   393  
   394  					s.logger.Info("UDP session relay started",
   395  						zap.String("server", s.serverName),
   396  						zap.String("client", clientInfo.Name),
   397  						zap.String("listenAddress", s.listenAddress),
   398  						zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   399  						zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   400  						zap.String("username", entry.username),
   401  						zap.Uint64("clientSessionID", csid),
   402  					)
   403  
   404  					s.wg.Add(1)
   405  
   406  					go func() {
   407  						s.relayServerConnToNatConnSendmmsg(sessionUplinkMmsg{
   408  							csid:          csid,
   409  							natConn:       natConn.WConn(),
   410  							natConnSendCh: natConnSendCh,
   411  							natConnPacker: natConnPacker,
   412  							username:      entry.username,
   413  						})
   414  						natConn.Close()
   415  						s.wg.Done()
   416  					}()
   417  
   418  					s.relayNatConnToServerConnSendmmsg(sessionDownlinkMmsg{
   419  						csid:               csid,
   420  						clientAddrInfop:    clientAddrInfop,
   421  						clientAddrInfo:     &entry.clientAddrInfo,
   422  						natConn:            natConn.RConn(),
   423  						natConnRecvBufSize: clientInfo.MaxPacketSize,
   424  						natConnUnpacker:    natConnUnpacker,
   425  						serverConn:         serverConn.WConn(),
   426  						serverConnPacker:   serverConnPacker,
   427  						username:           entry.username,
   428  					})
   429  				}()
   430  
   431  				if ce := s.logger.Check(zap.DebugLevel, "New UDP session"); ce != nil {
   432  					ce.Write(
   433  						zap.String("server", s.serverName),
   434  						zap.String("listenAddress", s.listenAddress),
   435  						zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   436  						zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   437  						zap.String("username", entry.username),
   438  						zap.Uint64("clientSessionID", csid),
   439  					)
   440  				}
   441  			}
   442  
   443  			select {
   444  			case entry.natConnSendCh <- queuedPacket:
   445  			default:
   446  				if ce := s.logger.Check(zap.DebugLevel, "Dropping packet due to full send channel"); ce != nil {
   447  					ce.Write(
   448  						zap.String("server", s.serverName),
   449  						zap.String("listenAddress", s.listenAddress),
   450  						zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   451  						zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   452  						zap.String("username", entry.username),
   453  						zap.Uint64("clientSessionID", csid),
   454  					)
   455  				}
   456  
   457  				s.putQueuedPacket(queuedPacket)
   458  			}
   459  		}
   460  
   461  		s.server.Unlock()
   462  	}
   463  
   464  	for i := range qpvec {
   465  		s.putQueuedPacket(qpvec[i])
   466  	}
   467  
   468  	s.logger.Info("Finished receiving from serverConn",
   469  		zap.String("server", s.serverName),
   470  		zap.String("listenAddress", s.listenAddress),
   471  		zap.Uint64("recvmmsgCount", recvmmsgCount),
   472  		zap.Uint64("packetsReceived", packetsReceived),
   473  		zap.Uint64("payloadBytesReceived", payloadBytesReceived),
   474  		zap.Int("burstBatchSize", burstBatchSize),
   475  	)
   476  }
   477  
   478  func (s *UDPSessionRelay) relayServerConnToNatConnSendmmsg(uplink sessionUplinkMmsg) {
   479  	var (
   480  		destAddrPort     netip.AddrPort
   481  		packetStart      int
   482  		packetLength     int
   483  		err              error
   484  		sendmmsgCount    uint64
   485  		packetsSent      uint64
   486  		payloadBytesSent uint64
   487  		burstBatchSize   int
   488  	)
   489  
   490  	qpvec := make([]*sessionQueuedPacket, s.relayBatchSize)
   491  	namevec := make([]unix.RawSockaddrInet6, s.relayBatchSize)
   492  	iovec := make([]unix.Iovec, s.relayBatchSize)
   493  	msgvec := make([]conn.Mmsghdr, s.relayBatchSize)
   494  
   495  	for i := range msgvec {
   496  		msgvec[i].Msghdr.Name = (*byte)(unsafe.Pointer(&namevec[i]))
   497  		msgvec[i].Msghdr.Namelen = unix.SizeofSockaddrInet6
   498  		msgvec[i].Msghdr.Iov = &iovec[i]
   499  		msgvec[i].Msghdr.SetIovlen(1)
   500  	}
   501  
   502  main:
   503  	for {
   504  		var count int
   505  
   506  		// Block on first dequeue op.
   507  		queuedPacket, ok := <-uplink.natConnSendCh
   508  		if !ok {
   509  			break
   510  		}
   511  
   512  	dequeue:
   513  		for {
   514  			destAddrPort, packetStart, packetLength, err = uplink.natConnPacker.PackInPlace(queuedPacket.buf, queuedPacket.targetAddr, queuedPacket.start, queuedPacket.length)
   515  			if err != nil {
   516  				s.logger.Warn("Failed to pack packet for natConn",
   517  					zap.String("server", s.serverName),
   518  					zap.String("listenAddress", s.listenAddress),
   519  					zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   520  					zap.Stringer("targetAddress", &queuedPacket.targetAddr),
   521  					zap.String("username", uplink.username),
   522  					zap.Uint64("clientSessionID", uplink.csid),
   523  					zap.Int("payloadLength", queuedPacket.length),
   524  					zap.Error(err),
   525  				)
   526  
   527  				s.putQueuedPacket(queuedPacket)
   528  
   529  				if count == 0 {
   530  					continue main
   531  				}
   532  				goto next
   533  			}
   534  
   535  			qpvec[count] = queuedPacket
   536  			namevec[count] = conn.AddrPortToSockaddrInet6(destAddrPort)
   537  			iovec[count].Base = &queuedPacket.buf[packetStart]
   538  			iovec[count].SetLen(packetLength)
   539  			count++
   540  			payloadBytesSent += uint64(queuedPacket.length)
   541  
   542  			if count == s.relayBatchSize {
   543  				break
   544  			}
   545  
   546  		next:
   547  			select {
   548  			case queuedPacket, ok = <-uplink.natConnSendCh:
   549  				if !ok {
   550  					break dequeue
   551  				}
   552  			default:
   553  				break dequeue
   554  			}
   555  		}
   556  
   557  		if err := uplink.natConn.WriteMsgs(msgvec[:count], 0); err != nil {
   558  			s.logger.Warn("Failed to batch write packets to natConn",
   559  				zap.String("server", s.serverName),
   560  				zap.String("listenAddress", s.listenAddress),
   561  				zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   562  				zap.Stringer("lastTargetAddress", &qpvec[count-1].targetAddr),
   563  				zap.Stringer("lastWriteDestAddress", destAddrPort),
   564  				zap.String("username", uplink.username),
   565  				zap.Uint64("clientSessionID", uplink.csid),
   566  				zap.Error(err),
   567  			)
   568  		}
   569  
   570  		if err := uplink.natConn.SetReadDeadline(time.Now().Add(s.natTimeout)); err != nil {
   571  			s.logger.Warn("Failed to set read deadline on natConn",
   572  				zap.String("server", s.serverName),
   573  				zap.String("listenAddress", s.listenAddress),
   574  				zap.Stringer("clientAddress", &queuedPacket.clientAddrPort),
   575  				zap.Duration("natTimeout", s.natTimeout),
   576  				zap.String("username", uplink.username),
   577  				zap.Uint64("clientSessionID", uplink.csid),
   578  				zap.Error(err),
   579  			)
   580  		}
   581  
   582  		sendmmsgCount++
   583  		packetsSent += uint64(count)
   584  		if burstBatchSize < count {
   585  			burstBatchSize = count
   586  		}
   587  
   588  		qpvecn := qpvec[:count]
   589  
   590  		for i := range qpvecn {
   591  			s.putQueuedPacket(qpvecn[i])
   592  		}
   593  
   594  		if !ok {
   595  			break
   596  		}
   597  	}
   598  
   599  	s.logger.Info("Finished relay serverConn -> natConn",
   600  		zap.String("server", s.serverName),
   601  		zap.String("listenAddress", s.listenAddress),
   602  		zap.Stringer("lastWriteDestAddress", destAddrPort),
   603  		zap.String("username", uplink.username),
   604  		zap.Uint64("clientSessionID", uplink.csid),
   605  		zap.Uint64("sendmmsgCount", sendmmsgCount),
   606  		zap.Uint64("packetsSent", packetsSent),
   607  		zap.Uint64("payloadBytesSent", payloadBytesSent),
   608  		zap.Int("burstBatchSize", burstBatchSize),
   609  	)
   610  
   611  	s.collector.CollectUDPSessionUplink(uplink.username, packetsSent, payloadBytesSent)
   612  }
   613  
   614  func (s *UDPSessionRelay) relayNatConnToServerConnSendmmsg(downlink sessionDownlinkMmsg) {
   615  	clientAddrInfop := downlink.clientAddrInfop
   616  	clientAddrPort := downlink.clientAddrInfop.addrPort
   617  	clientPktinfo := downlink.clientAddrInfop.pktinfo
   618  	maxClientPacketSize := zerocopy.MaxPacketSizeForAddr(s.mtu, clientAddrPort.Addr())
   619  
   620  	serverConnPackerInfo := downlink.serverConnPacker.ServerPackerInfo()
   621  	natConnUnpackerInfo := downlink.natConnUnpacker.ClientUnpackerInfo()
   622  	headroom := zerocopy.UDPRelayHeadroom(serverConnPackerInfo.Headroom, natConnUnpackerInfo.Headroom)
   623  
   624  	var (
   625  		sendmmsgCount    uint64
   626  		packetsSent      uint64
   627  		payloadBytesSent uint64
   628  		burstBatchSize   int
   629  	)
   630  
   631  	rsa6, namelen := conn.AddrPortToSockaddrValue(clientAddrPort)
   632  	savec := make([]unix.RawSockaddrInet6, s.relayBatchSize)
   633  	bufvec := make([][]byte, s.relayBatchSize)
   634  	riovec := make([]unix.Iovec, s.relayBatchSize)
   635  	siovec := make([]unix.Iovec, s.relayBatchSize)
   636  	rmsgvec := make([]conn.Mmsghdr, s.relayBatchSize)
   637  	smsgvec := make([]conn.Mmsghdr, s.relayBatchSize)
   638  
   639  	for i := 0; i < s.relayBatchSize; i++ {
   640  		packetBuf := make([]byte, headroom.Front+downlink.natConnRecvBufSize+headroom.Rear)
   641  		bufvec[i] = packetBuf
   642  
   643  		riovec[i].Base = &packetBuf[headroom.Front]
   644  		riovec[i].SetLen(downlink.natConnRecvBufSize)
   645  
   646  		rmsgvec[i].Msghdr.Name = (*byte)(unsafe.Pointer(&savec[i]))
   647  		rmsgvec[i].Msghdr.Namelen = unix.SizeofSockaddrInet6
   648  		rmsgvec[i].Msghdr.Iov = &riovec[i]
   649  		rmsgvec[i].Msghdr.SetIovlen(1)
   650  
   651  		smsgvec[i].Msghdr.Name = (*byte)(unsafe.Pointer(&rsa6))
   652  		smsgvec[i].Msghdr.Namelen = namelen
   653  		smsgvec[i].Msghdr.Iov = &siovec[i]
   654  		smsgvec[i].Msghdr.SetIovlen(1)
   655  		smsgvec[i].Msghdr.Control = &clientPktinfo[0]
   656  		smsgvec[i].Msghdr.SetControllen(len(clientPktinfo))
   657  	}
   658  
   659  	for {
   660  		nr, err := downlink.natConn.ReadMsgs(rmsgvec, 0)
   661  		if err != nil {
   662  			if errors.Is(err, os.ErrDeadlineExceeded) {
   663  				break
   664  			}
   665  
   666  			s.logger.Warn("Failed to batch read packets from natConn",
   667  				zap.String("server", s.serverName),
   668  				zap.String("listenAddress", s.listenAddress),
   669  				zap.Stringer("clientAddress", clientAddrPort),
   670  				zap.String("username", downlink.username),
   671  				zap.Uint64("clientSessionID", downlink.csid),
   672  				zap.Error(err),
   673  			)
   674  			continue
   675  		}
   676  
   677  		if caip := downlink.clientAddrInfo.Load(); caip != clientAddrInfop {
   678  			clientAddrInfop = caip
   679  			clientAddrPort = caip.addrPort
   680  			clientPktinfo = caip.pktinfo
   681  			maxClientPacketSize = zerocopy.MaxPacketSizeForAddr(s.mtu, clientAddrPort.Addr())
   682  			rsa6, _ = conn.AddrPortToSockaddrValue(clientAddrPort) // namelen won't change
   683  
   684  			for i := range smsgvec {
   685  				smsgvec[i].Msghdr.Control = &clientPktinfo[0]
   686  				smsgvec[i].Msghdr.SetControllen(len(clientPktinfo))
   687  			}
   688  		}
   689  
   690  		var ns int
   691  		rmsgvecn := rmsgvec[:nr]
   692  
   693  		for i := range rmsgvecn {
   694  			msg := &rmsgvecn[i]
   695  
   696  			packetSourceAddrPort, err := conn.SockaddrToAddrPort(msg.Msghdr.Name, msg.Msghdr.Namelen)
   697  			if err != nil {
   698  				s.logger.Warn("Failed to parse sockaddr of packet from natConn",
   699  					zap.String("server", s.serverName),
   700  					zap.String("listenAddress", s.listenAddress),
   701  					zap.Stringer("clientAddress", clientAddrPort),
   702  					zap.String("username", downlink.username),
   703  					zap.Uint64("clientSessionID", downlink.csid),
   704  					zap.Error(err),
   705  				)
   706  				continue
   707  			}
   708  
   709  			err = conn.ParseFlagsForError(int(msg.Msghdr.Flags))
   710  			if err != nil {
   711  				s.logger.Warn("Failed to read packet from natConn",
   712  					zap.String("server", s.serverName),
   713  					zap.String("listenAddress", s.listenAddress),
   714  					zap.Stringer("clientAddress", clientAddrPort),
   715  					zap.Stringer("packetSourceAddress", packetSourceAddrPort),
   716  					zap.String("username", downlink.username),
   717  					zap.Uint64("clientSessionID", downlink.csid),
   718  					zap.Uint32("packetLength", msg.Msglen),
   719  					zap.Error(err),
   720  				)
   721  				continue
   722  			}
   723  
   724  			packetBuf := bufvec[i]
   725  
   726  			payloadSourceAddrPort, payloadStart, payloadLength, err := downlink.natConnUnpacker.UnpackInPlace(packetBuf, packetSourceAddrPort, headroom.Front, int(msg.Msglen))
   727  			if err != nil {
   728  				s.logger.Warn("Failed to unpack packet from natConn",
   729  					zap.String("server", s.serverName),
   730  					zap.String("listenAddress", s.listenAddress),
   731  					zap.Stringer("clientAddress", clientAddrPort),
   732  					zap.Stringer("packetSourceAddress", packetSourceAddrPort),
   733  					zap.String("username", downlink.username),
   734  					zap.Uint64("clientSessionID", downlink.csid),
   735  					zap.Uint32("packetLength", msg.Msglen),
   736  					zap.Error(err),
   737  				)
   738  				continue
   739  			}
   740  
   741  			packetStart, packetLength, err := downlink.serverConnPacker.PackInPlace(packetBuf, payloadSourceAddrPort, payloadStart, payloadLength, maxClientPacketSize)
   742  			if err != nil {
   743  				s.logger.Warn("Failed to pack packet for serverConn",
   744  					zap.String("server", s.serverName),
   745  					zap.String("listenAddress", s.listenAddress),
   746  					zap.Stringer("clientAddress", clientAddrPort),
   747  					zap.Stringer("packetSourceAddress", packetSourceAddrPort),
   748  					zap.Stringer("payloadSourceAddress", payloadSourceAddrPort),
   749  					zap.String("username", downlink.username),
   750  					zap.Uint64("clientSessionID", downlink.csid),
   751  					zap.Int("payloadLength", payloadLength),
   752  					zap.Int("maxClientPacketSize", maxClientPacketSize),
   753  					zap.Error(err),
   754  				)
   755  				continue
   756  			}
   757  
   758  			siovec[ns].Base = &packetBuf[packetStart]
   759  			siovec[ns].SetLen(packetLength)
   760  			ns++
   761  			payloadBytesSent += uint64(payloadLength)
   762  		}
   763  
   764  		if ns == 0 {
   765  			continue
   766  		}
   767  
   768  		err = downlink.serverConn.WriteMsgs(smsgvec[:ns], 0)
   769  		if err != nil {
   770  			s.logger.Warn("Failed to batch write packets to serverConn",
   771  				zap.String("server", s.serverName),
   772  				zap.String("listenAddress", s.listenAddress),
   773  				zap.Stringer("clientAddress", clientAddrPort),
   774  				zap.String("username", downlink.username),
   775  				zap.Uint64("clientSessionID", downlink.csid),
   776  				zap.Error(err),
   777  			)
   778  		}
   779  
   780  		sendmmsgCount++
   781  		packetsSent += uint64(ns)
   782  		if burstBatchSize < ns {
   783  			burstBatchSize = ns
   784  		}
   785  	}
   786  
   787  	s.logger.Info("Finished relay serverConn <- natConn",
   788  		zap.String("server", s.serverName),
   789  		zap.String("listenAddress", s.listenAddress),
   790  		zap.Stringer("clientAddress", clientAddrPort),
   791  		zap.String("username", downlink.username),
   792  		zap.Uint64("clientSessionID", downlink.csid),
   793  		zap.Uint64("sendmmsgCount", sendmmsgCount),
   794  		zap.Uint64("packetsSent", packetsSent),
   795  		zap.Uint64("payloadBytesSent", payloadBytesSent),
   796  		zap.Int("burstBatchSize", burstBatchSize),
   797  	)
   798  
   799  	s.collector.CollectUDPSessionDownlink(downlink.username, packetsSent, payloadBytesSent)
   800  }