mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-05 05:18:15 +03:00
92 lines
3.3 KiB
Go
92 lines
3.3 KiB
Go
package wireguard
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/xtls/xray-core/common/errors"
|
|
tunicmp "github.com/xtls/xray-core/proxy/tun/icmp"
|
|
"gvisor.dev/gvisor/pkg/buffer"
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
|
)
|
|
|
|
// CreateICMPEchoResponder answers ICMP echo requests from peers locally, the way
|
|
// the TUN inbound does: ICMP is not proxied, but ping and connectivity checks
|
|
// through the tunnel get a reply instead of timing out.
|
|
//
|
|
// In promiscuous mode gVisor skips its own IPv4 echo reply for addresses that are
|
|
// not assigned to the NIC and leaves it to a custom handler; IPv6 is registered
|
|
// too so both families behave the same.
|
|
func CreateICMPEchoResponder(gstack *stack.Stack) {
|
|
gstack.SetTransportProtocolHandler(icmp.ProtocolNumber4, func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
|
return handleICMPEcho(gstack, header.IPv4ProtocolNumber, id, pkt)
|
|
})
|
|
gstack.SetTransportProtocolHandler(icmp.ProtocolNumber6, func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
|
return handleICMPEcho(gstack, header.IPv6ProtocolNumber, id, pkt)
|
|
})
|
|
}
|
|
|
|
func handleICMPEcho(gstack *stack.Stack, netProto tcpip.NetworkProtocolNumber, id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
|
srcIP := id.RemoteAddress
|
|
dstIP := id.LocalAddress
|
|
if srcIP.Len() == 0 || dstIP.Len() == 0 {
|
|
return true
|
|
}
|
|
|
|
headerBytes := pkt.TransportHeader().Slice()
|
|
payloadBytes := pkt.Data().AsRange().ToSlice()
|
|
message := make([]byte, len(headerBytes)+len(payloadBytes))
|
|
copy(message, headerBytes)
|
|
copy(message[len(headerBytes):], payloadBytes)
|
|
|
|
if _, _, ok := tunicmp.ParseEchoRequest(netProto, message); !ok {
|
|
return true
|
|
}
|
|
|
|
reply, err := tunicmp.BuildLocalEchoReply(netProto, message, dstIP, srcIP)
|
|
if err != nil {
|
|
errors.LogInfoInner(context.Background(), err, "failed to build local icmp echo reply")
|
|
return true
|
|
}
|
|
if err := writeRawICMPPacket(gstack, netProto, reply, dstIP, srcIP); err != nil {
|
|
errors.LogInfoInner(context.Background(), err, "failed to write local icmp echo reply")
|
|
}
|
|
return true
|
|
}
|
|
|
|
func writeRawICMPPacket(gstack *stack.Stack, netProto tcpip.NetworkProtocolNumber, message []byte, srcIP, dstIP tcpip.Address) error {
|
|
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
|
ReserveHeaderBytes: header.IPv6MinimumSize,
|
|
Payload: buffer.MakeWithData(message),
|
|
})
|
|
defer pkt.DecRef()
|
|
|
|
if netProto == header.IPv4ProtocolNumber {
|
|
ipHdr := header.IPv4(pkt.NetworkHeader().Push(header.IPv4MinimumSize))
|
|
ipHdr.Encode(&header.IPv4Fields{
|
|
TotalLength: uint16(header.IPv4MinimumSize + len(message)),
|
|
TTL: 64,
|
|
Protocol: uint8(header.ICMPv4ProtocolNumber),
|
|
SrcAddr: srcIP,
|
|
DstAddr: dstIP,
|
|
})
|
|
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
|
} else {
|
|
ipHdr := header.IPv6(pkt.NetworkHeader().Push(header.IPv6MinimumSize))
|
|
ipHdr.Encode(&header.IPv6Fields{
|
|
PayloadLength: uint16(len(message)),
|
|
TransportProtocol: header.ICMPv6ProtocolNumber,
|
|
HopLimit: 64,
|
|
SrcAddr: srcIP,
|
|
DstAddr: dstIP,
|
|
})
|
|
}
|
|
|
|
if err := gstack.WriteRawPacket(1, netProto, buffer.MakeWithView(pkt.ToView())); err != nil {
|
|
return errors.New("failed to write raw icmp packet back to stack ", err)
|
|
}
|
|
return nil
|
|
}
|