github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/service/udp_nat_mmsg.go (about)

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