mirror of
https://github.com/fumiama/WireGold.git
synced 2026-06-23 12:00:34 +08:00
feat(p2p): add ip
This commit is contained in:
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
run: go build -v ./...
|
run: go build -v ./...
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test $(go list ./...)
|
run: sudo go test $(go list ./...) # ip test needs sudo
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
name: Lint
|
name: Lint
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func (m *Me) listen() (conn p2p.Conn, err error) {
|
|||||||
logrus.Debugln("[listen] lock index", i)
|
logrus.Debugln("[listen] lock index", i)
|
||||||
lbf := listenbuff[i*lstnbufgragsz : (i+1)*lstnbufgragsz]
|
lbf := listenbuff[i*lstnbufgragsz : (i+1)*lstnbufgragsz]
|
||||||
n, addr, err := conn.ReadFromPeer(lbf)
|
n, addr, err := conn.ReadFromPeer(lbf)
|
||||||
if m.loop == nil || errors.Is(err, net.ErrClosed) {
|
if m.connections == nil || errors.Is(err, net.ErrClosed) {
|
||||||
logrus.Warnln("[listen] quit listening")
|
logrus.Warnln("[listen] quit listening")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ type Me struct {
|
|||||||
subnet net.IPNet
|
subnet net.IPNet
|
||||||
// 本机 endpoint
|
// 本机 endpoint
|
||||||
ep p2p.EndPoint
|
ep p2p.EndPoint
|
||||||
// 本机环回 link
|
|
||||||
loop *Link
|
|
||||||
// 本机活跃的所有连接
|
// 本机活跃的所有连接
|
||||||
connections map[string]*Link
|
connections map[string]*Link
|
||||||
// 读写同步锁
|
// 读写同步锁
|
||||||
@@ -97,17 +95,6 @@ func NewMe(cfg *MyConfig) (m Me) {
|
|||||||
cache: ttl.NewCache[string, *Link](time.Minute),
|
cache: ttl.NewCache[string, *Link](time.Minute),
|
||||||
}
|
}
|
||||||
m.router.SetDefault(nil)
|
m.router.SetDefault(nil)
|
||||||
_, localp, err := net.SplitHostPort(m.EndPoint().String())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
m.loop = m.AddPeer(&PeerConfig{
|
|
||||||
PeerIP: m.me.String(),
|
|
||||||
EndPoint: "127.0.0.1:" + localp,
|
|
||||||
AllowedIPs: []string{cfg.MyIPwithMask},
|
|
||||||
NoPipe: cfg.NIC != nil,
|
|
||||||
MTU: cfg.MTU,
|
|
||||||
})
|
|
||||||
m.srcport = cfg.SrcPort
|
m.srcport = cfg.SrcPort
|
||||||
m.dstport = cfg.DstPort
|
m.dstport = cfg.DstPort
|
||||||
m.mtu = cfg.MTU & 0xfff8
|
m.mtu = cfg.MTU & 0xfff8
|
||||||
@@ -141,7 +128,6 @@ func (m *Me) EndPoint() p2p.EndPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Me) Close() error {
|
func (m *Me) Close() error {
|
||||||
m.loop = nil
|
|
||||||
m.connections = nil
|
m.connections = nil
|
||||||
if m.conn != nil {
|
if m.conn != nil {
|
||||||
_ = m.conn.Close()
|
_ = m.conn.Close()
|
||||||
@@ -222,6 +208,14 @@ func (m *Me) sendAllSameDst(packet []byte) (n int) {
|
|||||||
rem = rem[i:]
|
rem = rem[i:]
|
||||||
dst := waterutil.IPv4Destination(packet)
|
dst := waterutil.IPv4Destination(packet)
|
||||||
logrus.Debugln("[me] sending", len(packet), "bytes packet from :"+strconv.Itoa(int(m.SrcPort())), "to", dst.String()+":"+strconv.Itoa(int(m.DstPort())), "remain:", len(rem), "bytes")
|
logrus.Debugln("[me] sending", len(packet), "bytes packet from :"+strconv.Itoa(int(m.SrcPort())), "to", dst.String()+":"+strconv.Itoa(int(m.DstPort())), "remain:", len(rem), "bytes")
|
||||||
|
if m.me.Equal(dst) { // is to myself, write to nic (pipe not allow loopback)
|
||||||
|
logrus.Debugln("[me] loopback packet")
|
||||||
|
_, err := m.nic.Write(packet)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnln("[me] write to loopback err:", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
lnk := m.router.NextHop(dst.String())
|
lnk := m.router.NextHop(dst.String())
|
||||||
if lnk == nil {
|
if lnk == nil {
|
||||||
logrus.Warnln("[me] drop packet to", dst.String()+":"+strconv.Itoa(int(m.DstPort())), ": nil nexthop")
|
logrus.Warnln("[me] drop packet to", dst.String()+":"+strconv.Itoa(int(m.DstPort())), ": nil nexthop")
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func (l *Link) keepAlive(dur int64) {
|
|||||||
logrus.Infoln("[nat] start to keep alive")
|
logrus.Infoln("[nat] start to keep alive")
|
||||||
t := time.NewTicker(time.Second * time.Duration(dur))
|
t := time.NewTicker(time.Second * time.Duration(dur))
|
||||||
for range t.C {
|
for range t.C {
|
||||||
if l.me.loop == nil {
|
if l.me.connections == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n, err := l.WriteAndPut(head.NewPacket(head.ProtoHello, l.me.srcport, l.peerip, l.me.dstport, nil), false)
|
n, err := l.WriteAndPut(head.NewPacket(head.ProtoHello, l.me.srcport, l.peerip, l.me.dstport, nil), false)
|
||||||
|
|||||||
32
gold/p2p/ip/init.go
Normal file
32
gold/p2p/ip/init.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package ip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/fumiama/WireGold/gold/p2p"
|
||||||
|
"github.com/fumiama/WireGold/helper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewEndpoint(endpoint string, configs ...any) (p2p.EndPoint, error) {
|
||||||
|
addr, err := netip.ParseAddr(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ptcl := uint(0x04) // IPIP
|
||||||
|
return &EndPoint{
|
||||||
|
addr: &net.IPAddr{
|
||||||
|
IP: addr.AsSlice(),
|
||||||
|
Zone: addr.Zone(),
|
||||||
|
},
|
||||||
|
ptcl: ptcl,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
name := helper.FolderName()
|
||||||
|
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||||
|
if hasexist {
|
||||||
|
panic("network " + name + " has been registered")
|
||||||
|
}
|
||||||
|
}
|
||||||
79
gold/p2p/ip/ip.go
Normal file
79
gold/p2p/ip/ip.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package ip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/fumiama/WireGold/gold/p2p"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EndPoint struct {
|
||||||
|
addr *net.IPAddr
|
||||||
|
ptcl uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *EndPoint) String() string {
|
||||||
|
return ep.addr.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *EndPoint) Network() string {
|
||||||
|
return ep.addr.Network()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
||||||
|
if ep == nil || ep2 == nil {
|
||||||
|
return ep == nil && ep2 == nil
|
||||||
|
}
|
||||||
|
ipep2, ok := ep2.(*EndPoint)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ipep1 := ep
|
||||||
|
return ipep1.addr.IP.Equal(ipep2.addr.IP) &&
|
||||||
|
ipep1.addr.Zone == ipep2.addr.Zone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||||
|
conn, err := net.ListenIP(
|
||||||
|
"ip:"+strconv.Itoa(int(ep.ptcl)),
|
||||||
|
ep.addr,
|
||||||
|
)
|
||||||
|
return &Conn{
|
||||||
|
ep: ep,
|
||||||
|
conn: conn,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Conn struct {
|
||||||
|
ep *EndPoint
|
||||||
|
conn *net.IPConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) Close() error {
|
||||||
|
return conn.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) String() string {
|
||||||
|
return conn.conn.LocalAddr().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||||
|
ep, _ := NewEndpoint(conn.conn.LocalAddr().String())
|
||||||
|
return ep
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||||
|
n, addr, err := conn.conn.ReadFromIP(b)
|
||||||
|
return n, &EndPoint{
|
||||||
|
addr: addr,
|
||||||
|
ptcl: conn.ep.ptcl,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||||
|
ipep, ok := ep.(*EndPoint)
|
||||||
|
if !ok {
|
||||||
|
return 0, p2p.ErrEndpointTypeMistatch
|
||||||
|
}
|
||||||
|
return conn.conn.WriteToIP(b, ipep.addr)
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fumiama/WireGold/gold/p2p"
|
"github.com/fumiama/WireGold/gold/p2p"
|
||||||
|
"github.com/fumiama/WireGold/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -38,8 +39,9 @@ func newEndpoint(endpoint string, configs ...any) (*EndPoint, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
_, hasexist := p2p.Register("tcp", NewEndpoint)
|
name := helper.FolderName()
|
||||||
|
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||||
if hasexist {
|
if hasexist {
|
||||||
panic("network tcp has been registered")
|
panic("network " + name + " has been registered")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/fumiama/WireGold/gold/p2p"
|
"github.com/fumiama/WireGold/gold/p2p"
|
||||||
|
"github.com/fumiama/WireGold/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
||||||
@@ -16,8 +17,9 @@ func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
_, hasexist := p2p.Register("udp", NewEndpoint)
|
name := helper.FolderName()
|
||||||
|
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||||
if hasexist {
|
if hasexist {
|
||||||
panic("network udp has been registered")
|
panic("network " + name + " has been registered")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package helper
|
package helper
|
||||||
|
|
||||||
import "os"
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// IsExist 文件/路径存在
|
// IsExist 文件/路径存在
|
||||||
func IsExist(path string) bool {
|
func IsExist(path string) bool {
|
||||||
@@ -13,3 +17,21 @@ func IsNotExist(path string) bool {
|
|||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
return err != nil && os.IsNotExist(err)
|
return err != nil && os.IsNotExist(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FolderName 本文件所在最下级文件夹名
|
||||||
|
func FolderName() string {
|
||||||
|
_, file, _, ok := runtime.Caller(1)
|
||||||
|
if !ok {
|
||||||
|
return "<unk>"
|
||||||
|
}
|
||||||
|
i := strings.LastIndex(file, "/")
|
||||||
|
if i <= 0 {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
file = file[:i]
|
||||||
|
i = strings.LastIndex(file, "/")
|
||||||
|
if i <= 0 || i+1 >= len(file) {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
return file[i+1:]
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"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/tcp" // support tcp connection
|
||||||
_ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection
|
_ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,21 @@ func testTunnel(t *testing.T, nw string, isplain bool, pshk *[32]byte, mtu uint1
|
|||||||
t.Log("peer priv key:", hex.EncodeToString(peerpk.Private()[:]))
|
t.Log("peer priv key:", hex.EncodeToString(peerpk.Private()[:]))
|
||||||
t.Log("peer publ key:", hex.EncodeToString(peerpk.Public()[:]))
|
t.Log("peer publ key:", hex.EncodeToString(peerpk.Public()[:]))
|
||||||
|
|
||||||
|
epm := "127.0.0.1"
|
||||||
|
if nw != "ip" {
|
||||||
|
epm += ":0"
|
||||||
|
}
|
||||||
|
// under macos you need to run
|
||||||
|
//
|
||||||
|
// sudo ifconfig lo0 alias 127.0.0.2
|
||||||
|
epp := "127.0.0.2"
|
||||||
|
if nw != "ip" {
|
||||||
|
epp += ":0"
|
||||||
|
}
|
||||||
|
|
||||||
m := link.NewMe(&link.MyConfig{
|
m := link.NewMe(&link.MyConfig{
|
||||||
MyIPwithMask: "192.168.1.2/32",
|
MyIPwithMask: "192.168.1.2/32",
|
||||||
MyEndpoint: "127.0.0.1:0",
|
MyEndpoint: epm,
|
||||||
Network: nw,
|
Network: nw,
|
||||||
PrivateKey: selfpk.Private(),
|
PrivateKey: selfpk.Private(),
|
||||||
SrcPort: 1,
|
SrcPort: 1,
|
||||||
@@ -43,7 +55,7 @@ func testTunnel(t *testing.T, nw string, isplain bool, pshk *[32]byte, mtu uint1
|
|||||||
|
|
||||||
p := link.NewMe(&link.MyConfig{
|
p := link.NewMe(&link.MyConfig{
|
||||||
MyIPwithMask: "192.168.1.3/32",
|
MyIPwithMask: "192.168.1.3/32",
|
||||||
MyEndpoint: "127.0.0.1:0",
|
MyEndpoint: epp,
|
||||||
Network: nw,
|
Network: nw,
|
||||||
PrivateKey: peerpk.Private(),
|
PrivateKey: peerpk.Private(),
|
||||||
SrcPort: 1,
|
SrcPort: 1,
|
||||||
@@ -212,6 +224,38 @@ func TestTunnelTCPSmallMTU(t *testing.T) {
|
|||||||
testTunnel(t, "tcp", false, &buf, 1024) // test preshared
|
testTunnel(t, "tcp", false, &buf, 1024) // test preshared
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTunnelIP(t *testing.T) {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||||
|
|
||||||
|
testTunnel(t, "ip", true, nil, 4096) // test plain text
|
||||||
|
|
||||||
|
testTunnel(t, "ip", false, nil, 4096) // test normal
|
||||||
|
|
||||||
|
var buf [32]byte
|
||||||
|
_, err := rand.Read(buf[:])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
testTunnel(t, "ip", false, &buf, 4096) // test preshared
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTunnelIPSmallMTU(t *testing.T) {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||||
|
|
||||||
|
testTunnel(t, "ip", true, nil, 1024) // test plain text
|
||||||
|
|
||||||
|
testTunnel(t, "ip", false, nil, 1024) // test normal
|
||||||
|
|
||||||
|
var buf [32]byte
|
||||||
|
_, err := rand.Read(buf[:])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
testTunnel(t, "ip", false, &buf, 1024) // test preshared
|
||||||
|
}
|
||||||
|
|
||||||
// logFormat specialize for go-cqhttp
|
// logFormat specialize for go-cqhttp
|
||||||
type logFormat struct {
|
type logFormat struct {
|
||||||
enableColor bool
|
enableColor bool
|
||||||
|
|||||||
Reference in New Issue
Block a user