diff --git a/gold/p2p/udplite/init.go b/gold/p2p/udplite/init.go new file mode 100644 index 0000000..70dfbdd --- /dev/null +++ b/gold/p2p/udplite/init.go @@ -0,0 +1,27 @@ +//go:build !darwin + +package udplite + +import ( + "net" + "net/netip" + + "github.com/fumiama/WireGold/gold/p2p" + "github.com/fumiama/WireGold/helper" +) + +func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) { + addr, err := netip.ParseAddrPort(endpoint) + if err != nil { + return nil, err + } + return (*EndPoint)(net.UDPAddrFromAddrPort(addr)), nil +} + +func init() { + name := helper.FolderName() + _, hasexist := p2p.Register(name, NewEndpoint) + if hasexist { + panic("network " + name + " has been registered") + } +} diff --git a/gold/p2p/udplite/lite.go b/gold/p2p/udplite/lite.go new file mode 100644 index 0000000..6ba6d30 --- /dev/null +++ b/gold/p2p/udplite/lite.go @@ -0,0 +1,95 @@ +//go:build !darwin + +package udplite + +import ( + "context" + "net" + "syscall" + "unsafe" + + "github.com/fumiama/WireGold/gold/head" +) + +// https://www.kernel.org/doc/Documentation/networking/udplite.txt +const ( + IPPROTO_UDPLITE = 136 + SOL_UDPLITE = 136 + UDPLITE_SEND_CSCOV = 10 + UDPLITE_RECV_CSCOV = 11 +) + +type sysListener struct { + net.ListenConfig + network, address string +} + +type sockaddr interface { + net.Addr +} + +//go:linkname toLocal net.(*UDPAddr).toLocal +func toLocal(a *net.UDPAddr, net string) sockaddr + +//go:linkname internetSocket net.internetSocket +func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error) + +//go:linkname newUDPConn net.newUDPConn +func newUDPConn(fd unsafe.Pointer) *net.UDPConn + +var sockaddrinterfaceinstance = toLocal(&net.UDPAddr{}, "") + +func (sl *sysListener) listenUDP(ctx context.Context, laddr *net.UDPAddr) (*net.UDPConn, error) { + var ctrlCtxFn func(cxt context.Context, network, address string, c syscall.RawConn) error + if sl.ListenConfig.Control != nil { + ctrlCtxFn = func(cxt context.Context, network, address string, c syscall.RawConn) error { + return sl.ListenConfig.Control(network, address, c) + } + } + sockladdr := sockaddrinterfaceinstance + *(**net.UDPAddr)(unsafe.Add(unsafe.Pointer(&sockladdr), unsafe.Sizeof(uintptr(0)))) = laddr + sockraddr := sockaddrinterfaceinstance + sockladdr = nil + fd, err := internetSocket(ctx, sl.network, sockladdr, sockraddr, syscall.SOCK_DGRAM, IPPROTO_UDPLITE, "listen", ctrlCtxFn) + if err != nil { + return nil, err + } + return newUDPConn(fd), nil +} + +func listenUDPLite(network string, laddr *net.UDPAddr) (*net.UDPConn, error) { + if laddr == nil { + laddr = &net.UDPAddr{} + } + sl := &sysListener{network: network, address: laddr.String()} + conn, err := sl.listenUDP(context.Background(), laddr) + if err != nil { + var laddrgeneral net.Addr + if laddr != nil { + laddrgeneral = laddr + } + return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddrgeneral, Err: err} + } + rc, err := conn.SyscallConn() + if err != nil { + _ = conn.Close() + return nil, err + } + var errsys error + err = rc.Control(func(fd uintptr) { + errsys = syscall.SetsockoptInt(int(fd), SOL_UDPLITE, UDPLITE_SEND_CSCOV, head.PacketHeadLen) + if errsys != nil { + return + } + errsys = syscall.SetsockoptInt(int(fd), SOL_UDPLITE, UDPLITE_RECV_CSCOV, head.PacketHeadLen) + }) + if err != nil { + _ = conn.Close() + return nil, err + } + if errsys != nil { + _ = conn.Close() + return nil, errsys + } + return conn, nil +} diff --git a/gold/p2p/udplite/stub.go b/gold/p2p/udplite/stub.go new file mode 100644 index 0000000..f4a4c9f --- /dev/null +++ b/gold/p2p/udplite/stub.go @@ -0,0 +1 @@ +package udplite diff --git a/gold/p2p/udplite/udp.go b/gold/p2p/udplite/udp.go new file mode 100644 index 0000000..a08de7a --- /dev/null +++ b/gold/p2p/udplite/udp.go @@ -0,0 +1,64 @@ +//go:build !darwin + +package udplite + +import ( + "net" + + "github.com/fumiama/WireGold/gold/p2p" +) + +type EndPoint net.UDPAddr + +func (ep *EndPoint) String() string { + return (*net.UDPAddr)(ep).String() +} + +func (ep *EndPoint) Network() string { + return (*net.UDPAddr)(ep).Network() +} + +func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool { + if ep == nil || ep2 == nil { + return ep == nil && ep2 == nil + } + udpep2, ok := ep2.(*EndPoint) + if !ok { + return false + } + udpep1 := ep + return udpep1.IP.Equal(udpep2.IP) && udpep1.Port == udpep2.Port && udpep1.Zone == udpep2.Zone +} + +func (ep *EndPoint) Listen() (p2p.Conn, error) { + conn, err := listenUDPLite((*net.UDPAddr)(ep).Network(), (*net.UDPAddr)(ep)) + return (*Conn)(conn), err +} + +type Conn net.UDPConn + +func (conn *Conn) Close() error { + return (*net.UDPConn)(conn).Close() +} + +func (conn *Conn) String() string { + return (*net.UDPConn)(conn).LocalAddr().String() +} + +func (conn *Conn) LocalAddr() p2p.EndPoint { + ep, _ := NewEndpoint((*net.UDPConn)(conn).LocalAddr().String()) + return ep +} + +func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) { + n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b) + return n, (*EndPoint)(addr), err +} + +func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) { + udpep, ok := ep.(*EndPoint) + if !ok { + return 0, p2p.ErrEndpointTypeMistatch + } + return (*net.UDPConn)(conn).WriteTo(b, (*net.UDPAddr)(udpep)) +} diff --git a/upper/services/tunnel/tunnel.go b/upper/services/tunnel/tunnel.go index f5df17b..6a57874 100644 --- a/upper/services/tunnel/tunnel.go +++ b/upper/services/tunnel/tunnel.go @@ -8,9 +8,10 @@ import ( "github.com/sirupsen/logrus" - _ "github.com/fumiama/WireGold/gold/p2p/ip" // support ip connection - _ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection - _ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection + _ "github.com/fumiama/WireGold/gold/p2p/ip" // support ip connection + _ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection + _ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection + _ "github.com/fumiama/WireGold/gold/p2p/udplite" // support udplite connection "github.com/fumiama/WireGold/gold/head" "github.com/fumiama/WireGold/gold/link" diff --git a/upper/services/tunnel/tunnel_test.go b/upper/services/tunnel/tunnel_test.go index b5eecbf..6c82213 100644 --- a/upper/services/tunnel/tunnel_test.go +++ b/upper/services/tunnel/tunnel_test.go @@ -192,6 +192,38 @@ func TestTunnelUDPSmallMTU(t *testing.T) { testTunnel(t, "udp", false, &buf, 1024) // test preshared } +func TestTunnelUDPLite(t *testing.T) { + logrus.SetLevel(logrus.DebugLevel) + logrus.SetFormatter(&logFormat{enableColor: false}) + + testTunnel(t, "udplite", true, nil, 4096) // test plain text + + testTunnel(t, "udplite", false, nil, 4096) // test normal + + var buf [32]byte + _, err := rand.Read(buf[:]) + if err != nil { + panic(err) + } + testTunnel(t, "udplite", false, &buf, 4096) // test preshared +} + +func TestTunnelUDPLiteSmallMTU(t *testing.T) { + logrus.SetLevel(logrus.DebugLevel) + logrus.SetFormatter(&logFormat{enableColor: false}) + + testTunnel(t, "udplite", true, nil, 1024) // test plain text + + testTunnel(t, "udplite", false, nil, 1024) // test normal + + var buf [32]byte + _, err := rand.Read(buf[:]) + if err != nil { + panic(err) + } + testTunnel(t, "udplite", false, &buf, 1024) // test preshared +} + func TestTunnelTCP(t *testing.T) { logrus.SetLevel(logrus.DebugLevel) logrus.SetFormatter(&logFormat{enableColor: false}) diff --git a/upper/services/wg/wg.go b/upper/services/wg/wg.go index b3fb9db..deffc18 100644 --- a/upper/services/wg/wg.go +++ b/upper/services/wg/wg.go @@ -9,8 +9,10 @@ import ( curve "github.com/fumiama/go-x25519" "github.com/sirupsen/logrus" - _ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection - _ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection + _ "github.com/fumiama/WireGold/gold/p2p/ip" // support ip connection + _ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection + _ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection + _ "github.com/fumiama/WireGold/gold/p2p/udplite" // support udplite connection "github.com/fumiama/WireGold/config" "github.com/fumiama/WireGold/gold/link"