Export std net bind

This commit is contained in:
世界 2023-12-15 18:44:05 +08:00
parent b2a20cdd77
commit c63bc19bc9
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
6 changed files with 129 additions and 40 deletions

View file

@ -16,6 +16,9 @@ import (
"sync"
"syscall"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/control"
M "github.com/sagernet/sing/common/metadata"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
@ -31,6 +34,9 @@ var (
// methods for sending and receiving multiple datagrams per-syscall. See the
// proposal in https://github.com/golang/go/issues/45886#issuecomment-1218301564.
type StdNetBind struct {
externalControl control.Func
reservedForEndpoint map[netip.AddrPort][3]uint8
mu sync.Mutex // protects all fields except as specified
ipv4 *net.UDPConn
ipv6 *net.UDPConn
@ -49,8 +55,11 @@ type StdNetBind struct {
blackhole6 bool
}
func NewStdNetBind() Bind {
func NewStdNetBind(externalControl control.Func) Bind {
return &StdNetBind{
externalControl: externalControl,
reservedForEndpoint: make(map[netip.AddrPort][3]uint8),
udpAddrPool: sync.Pool{
New: func() any {
return &net.UDPAddr{
@ -118,8 +127,29 @@ func (e *StdNetEndpoint) DstToString() string {
return e.AddrPort.String()
}
func listenNet(network string, port int) (*net.UDPConn, int, error) {
conn, err := listenConfig().ListenPacket(context.Background(), network, ":"+strconv.Itoa(port))
func listenNet(externalControl control.Func, network string, port int) (*net.UDPConn, int, error) {
var listenerAddr string
if network == "udp6" {
listenerAddr = "[::]:" + strconv.Itoa(port)
} else {
listenerAddr = ":" + strconv.Itoa(port)
}
var listener net.ListenConfig
listener.Control = func(network, address string, conn syscall.RawConn) error {
for _, wgControlFn := range controlFns {
err := wgControlFn(network, address, conn)
if err != nil {
return err
}
}
if externalControl != nil {
return externalControl(network, address, conn)
} else {
return nil
}
}
conn, err := listener.ListenPacket(context.Background(), network, listenerAddr)
if err != nil {
return nil, 0, err
}
@ -160,13 +190,13 @@ again:
var v4pc *ipv4.PacketConn
var v6pc *ipv6.PacketConn
v4conn, port, err = listenNet("udp4", port)
v4conn, port, err = listenNet(s.externalControl, "udp4", port)
if err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) {
return nil, 0, err
}
// Listen on the same port as we're using for ipv4.
v6conn, port, err = listenNet("udp6", port)
v6conn, port, err = listenNet(s.externalControl, "udp6", port)
if uport == 0 && errors.Is(err, errEADDRINUSE) && tries < 100 {
v4conn.Close()
tries++
@ -270,8 +300,10 @@ func (s *StdNetBind) receiveIP(
if sizes[i] == 0 {
continue
}
addrPort := msg.Addr.(*net.UDPAddr).AddrPort()
ep := &StdNetEndpoint{AddrPort: addrPort} // TODO: remove allocation
if msg.N > 3 {
common.ClearArray(bufs[i][1:4])
}
ep := &StdNetEndpoint{AddrPort: M.AddrPortFromNet(msg.Addr)} // TODO: remove allocation
getSrcFromControl(msg.OOB[:msg.NN], ep)
eps[i] = ep
}
@ -380,6 +412,14 @@ func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint, offset int) error {
retried bool
err error
)
for _, buf := range bufs {
if len(buf) > 3 {
reserved, loaded := s.reservedForEndpoint[endpoint.(*StdNetEndpoint).AddrPort]
if loaded {
copy(buf[1:4], reserved[:])
}
}
}
retry:
if offload {
n := coalesceMessages(ua, endpoint.(*StdNetEndpoint), bufs, offset, *msgs, setGSOSize)
@ -410,6 +450,10 @@ retry:
return err
}
func (s *StdNetBind) SetReservedForEndpoint(destination netip.AddrPort, reserved [3]byte) {
s.reservedForEndpoint[destination] = reserved
}
func (s *StdNetBind) send(conn *net.UDPConn, pc batchWriter, msgs []ipv6.Message) error {
var (
n int