mirror of
https://github.com/fumiama/WireGold.git
synced 2026-06-05 07:50:24 +08:00
feat(p2p): add ICMP backend support
This commit is contained in:
@@ -208,7 +208,10 @@ func (m *Me) NetworkConfigs() []any {
|
||||
|
||||
func (m *Me) Close() error {
|
||||
for i := 0; i < len(m.jobs); i++ {
|
||||
close(m.jobs[i])
|
||||
jb := m.jobs[i]
|
||||
if jb != nil {
|
||||
close(jb)
|
||||
}
|
||||
}
|
||||
m.connections = nil
|
||||
if bin.IsNonNilInterface(m.conn) {
|
||||
|
||||
@@ -137,8 +137,8 @@ func (m *Me) extractPeer(srcip, dstip net.IP, addr p2p.EndPoint) *Link {
|
||||
logrus.Warnln(file.Header(), "packet from", srcip, "to", dstip, "is refused")
|
||||
return nil
|
||||
}
|
||||
if bin.IsNilInterface(p.endpoint) || !p.endpoint.Euqal(addr) {
|
||||
if m.ep.Network() == "tcp" && !addr.Euqal(p.endpoint) {
|
||||
if bin.IsNilInterface(p.endpoint) || !p.endpoint.Equal(addr) {
|
||||
if m.ep.Network() == "tcp" && !addr.Equal(p.endpoint) {
|
||||
logrus.Infoln(file.Header(), "set endpoint of peer", p.peerip, "to", addr.String())
|
||||
p.endpoint = addr
|
||||
} else { // others are all no status link
|
||||
|
||||
@@ -75,16 +75,12 @@ func (l *Link) write2peer(b pbuf.Bytes, seq uint32) {
|
||||
if l.doublepacket {
|
||||
err := l.write2peer1(b, seq)
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Warnln("[send] double wr2peer", l.peerip, "err:", err)
|
||||
}
|
||||
logrus.Warnln("[send] double wr2peer", l.peerip, "err:", err)
|
||||
}
|
||||
}
|
||||
err := l.write2peer1(b, seq)
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Warnln("[send] wr2peer", l.peerip, "err:", err)
|
||||
}
|
||||
logrus.Warnln("[send] wr2peer", l.peerip, "err:", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ func Register(network string, initializer Initializer) (actual Initializer, hase
|
||||
type EndPoint interface {
|
||||
fmt.Stringer
|
||||
Network() string
|
||||
Euqal(EndPoint) bool
|
||||
Equal(EndPoint) bool
|
||||
Listen() (Conn, error)
|
||||
}
|
||||
|
||||
|
||||
264
gold/p2p/icmp/icmp.go
Normal file
264
gold/p2p/icmp/icmp.go
Normal file
@@ -0,0 +1,264 @@
|
||||
package icmp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/RomiChan/syncx"
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidBodyType = errors.New("invalid body type")
|
||||
)
|
||||
|
||||
var (
|
||||
echoid = os.Getpid()
|
||||
)
|
||||
|
||||
// peerState holds per-peer ICMP echo state within a Conn.
|
||||
type peerState struct {
|
||||
id int
|
||||
seq atomic.Uintptr
|
||||
seqpool *sync.Pool
|
||||
}
|
||||
|
||||
func newPeerState() *peerState {
|
||||
ps := &peerState{}
|
||||
ps.seqpool = &sync.Pool{
|
||||
New: func() any {
|
||||
return int(ps.seq.Add(1))
|
||||
},
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
type EndPoint netip.Addr
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return (*netip.Addr)(ep).String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return "icmp"
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Equal(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 (*netip.Addr)(ipep1).Compare(*(*netip.Addr)(ipep2)) == 0
|
||||
}
|
||||
|
||||
// network get ipv4/ipv6 info and choose different options.
|
||||
func (ep *EndPoint) network() (string, *netip.Addr) {
|
||||
nw := "ip4:icmp"
|
||||
if (*netip.Addr)(ep).Is6() {
|
||||
nw = "ip6:ipv6-icmp"
|
||||
}
|
||||
return nw, (*netip.Addr)(ep)
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
nw, addr := ep.network()
|
||||
conn, err := icmp.ListenPacket(nw, addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Conn{inner: conn}, nil
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
inner *icmp.PacketConn
|
||||
peers syncx.Map[netip.Addr, *peerState]
|
||||
}
|
||||
|
||||
func (conn *Conn) getOrCreatePeerState(addr netip.Addr) *peerState {
|
||||
if ps, ok := conn.peers.Load(addr); ok {
|
||||
return ps
|
||||
}
|
||||
ps := newPeerState()
|
||||
actual, _ := conn.peers.LoadOrStore(addr, ps)
|
||||
return actual
|
||||
}
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
return conn.inner.Close()
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return conn.inner.LocalAddr().String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
eps := conn.inner.LocalAddr().String()
|
||||
addr, err := netip.ParseAddrPort(eps)
|
||||
if err == nil {
|
||||
eps = addr.Addr().String()
|
||||
}
|
||||
ep, _ := NewEndpoint(eps)
|
||||
return ep
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (n int, ep p2p.EndPoint, err error) {
|
||||
buf := pbuf.NewBytes(8192)
|
||||
defer buf.ManualDestroy()
|
||||
var ipaddr netip.Addr
|
||||
buf.V(func(data []byte) {
|
||||
ok := false
|
||||
var msg *icmp.Message
|
||||
for !ok {
|
||||
var (
|
||||
cnt int
|
||||
addr net.Addr
|
||||
)
|
||||
cnt, addr, err = conn.inner.ReadFrom(data)
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv ReadFrom err:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ipaddr, err = netip.ParseAddr(addr.String())
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv ParseAddr err:", err, ", addr:", addr)
|
||||
}
|
||||
return
|
||||
}
|
||||
ep, err = NewEndpoint(ipaddr.String())
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv NewEndpoint err:", err, ", addr:", addr)
|
||||
}
|
||||
return
|
||||
}
|
||||
proton := ipv4.ICMPTypeEcho.Protocol()
|
||||
if ipaddr.Is6() {
|
||||
proton = ipv6.ICMPTypeEchoRequest.Protocol()
|
||||
}
|
||||
|
||||
msg, err = icmp.ParseMessage(proton, data[:cnt])
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv ParseMessage err:", err, ", addr:", addr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ok = msg.Type == ipv4.ICMPTypeEcho || msg.Type == ipv4.ICMPTypeEchoReply
|
||||
if ipaddr.Is6() {
|
||||
ok = msg.Type == ipv6.ICMPTypeEchoRequest || msg.Type == ipv6.ICMPTypeEchoReply
|
||||
}
|
||||
ok = ok && msg.Code == 1
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv from", ipaddr, ", is valid:", ok)
|
||||
}
|
||||
}
|
||||
body, okk := msg.Body.(*icmp.Echo)
|
||||
if !okk {
|
||||
err = ErrInvalidBodyType
|
||||
return
|
||||
}
|
||||
if msg.Type == ipv4.ICMPTypeEcho || msg.Type == ipv6.ICMPTypeEchoRequest {
|
||||
ps := conn.getOrCreatePeerState(ipaddr)
|
||||
ps.id = body.ID
|
||||
ps.seq.Store(uintptr(body.Seq))
|
||||
ps.seqpool.Put(body.Seq)
|
||||
}
|
||||
n = copy(b, body.Data)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv", n, "bytes data from", ipaddr)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||
icmpep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
addr := (*netip.Addr)(icmpep)
|
||||
ps := conn.getOrCreatePeerState(*addr)
|
||||
seq := ps.seqpool.Get().(int)
|
||||
id := ps.id
|
||||
isrequest := id == 0
|
||||
if isrequest {
|
||||
id = echoid
|
||||
}
|
||||
var (
|
||||
ip net.IP
|
||||
msg icmp.Message
|
||||
)
|
||||
if addr.Is4() {
|
||||
x := addr.As4()
|
||||
ip = x[:]
|
||||
msg = icmp.Message{
|
||||
Code: 1,
|
||||
Body: &icmp.Echo{
|
||||
ID: id,
|
||||
Seq: seq,
|
||||
Data: b,
|
||||
},
|
||||
}
|
||||
if isrequest {
|
||||
msg.Type = ipv4.ICMPTypeEcho
|
||||
} else {
|
||||
msg.Type = ipv4.ICMPTypeEchoReply
|
||||
}
|
||||
} else {
|
||||
x := addr.As16()
|
||||
ip = x[:]
|
||||
msg = icmp.Message{
|
||||
Code: 1,
|
||||
Body: &icmp.Echo{
|
||||
ID: id,
|
||||
Seq: seq,
|
||||
Data: b,
|
||||
},
|
||||
}
|
||||
if isrequest {
|
||||
msg.Type = ipv6.ICMPTypeEchoRequest
|
||||
} else {
|
||||
msg.Type = ipv6.ICMPTypeEchoReply
|
||||
}
|
||||
}
|
||||
buf := pbuf.NewBytes(8192)
|
||||
defer buf.ManualDestroy()
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
n int
|
||||
)
|
||||
buf.V(func(bin []byte) {
|
||||
data, err = msg.Marshal(bin[:0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = conn.inner.WriteTo(data, &net.IPAddr{
|
||||
IP: ip,
|
||||
Zone: addr.Zone(),
|
||||
})
|
||||
if err == nil {
|
||||
n = len(b)
|
||||
}
|
||||
})
|
||||
return n, err
|
||||
}
|
||||
26
gold/p2p/icmp/init.go
Normal file
26
gold/p2p/icmp/init.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Package icmp for non-privileged datagram-oriented ICMP endpoints,
|
||||
// currently only Darwin and Linux support this.
|
||||
package icmp
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
||||
addr, err := netip.ParseAddr(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*EndPoint)(&addr), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := file.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ func (ep *EndPoint) Network() string {
|
||||
return ep.addr.Network()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
||||
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
@@ -64,6 +64,9 @@ func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
n, addr, err := conn.conn.ReadFromIP(b)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return n, &EndPoint{
|
||||
addr: addr,
|
||||
ptcl: conn.ep.ptcl,
|
||||
|
||||
@@ -32,7 +32,7 @@ func (ep *EndPoint) Network() string {
|
||||
return ep.addr.Network()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
||||
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func (ep *EndPoint) Network() string {
|
||||
return (*net.UDPAddr)(ep).Network()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
||||
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
@@ -50,6 +50,9 @@ func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return n, (*EndPoint)(addr), err
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ func (ep *EndPoint) Network() string {
|
||||
return "udplite"
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Euqal(ep2 p2p.EndPoint) bool {
|
||||
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
@@ -52,6 +52,9 @@ func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return n, (*EndPoint)(addr), err
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ func init() {
|
||||
if err == nil {
|
||||
p, ok := peer.Me().IsInPeer(ps)
|
||||
if ok {
|
||||
if bin.IsNilInterface(p.EndPoint()) || !p.EndPoint().Euqal(addr) {
|
||||
if bin.IsNilInterface(p.EndPoint()) || !p.EndPoint().Equal(addr) {
|
||||
p.SetEndPoint(addr)
|
||||
logrus.Infoln(file.Header(), "notify set ep of peer", ps, "to", ep)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user