mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-11-10 06:16:53 -05:00
42c50d25e1
Fixes UDP packets being dropped in non-local routing path(e.g. forwarding to LAN or container veth) due to 0-TTL.
126 lines
3.5 KiB
Go
126 lines
3.5 KiB
Go
package packetparse
|
|
|
|
import (
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/layers"
|
|
|
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
|
)
|
|
|
|
var (
|
|
errNotIPPacket = newError("not an IP packet")
|
|
errNotUDPPacket = newError("not a UDP packet")
|
|
)
|
|
|
|
var nullDestination = net.UnixDestination(net.DomainAddress("null"))
|
|
|
|
func TryParseAsUDPPacket(packet []byte) (src, dst net.Destination, data []byte, err error) {
|
|
parsedPacket := gopacket.NewPacket(packet, layers.LayerTypeIPv4, gopacket.DecodeOptions{
|
|
Lazy: true,
|
|
NoCopy: false,
|
|
SkipDecodeRecovery: false,
|
|
DecodeStreamsAsDatagrams: false,
|
|
})
|
|
|
|
var srcIP net.Address
|
|
var dstIP net.Address
|
|
ipv4Layer := parsedPacket.Layer(layers.LayerTypeIPv4)
|
|
|
|
if ipv4Layer == nil {
|
|
parsedPacketAsIPv6 := gopacket.NewPacket(packet, layers.LayerTypeIPv6, gopacket.DecodeOptions{
|
|
Lazy: true,
|
|
NoCopy: false,
|
|
SkipDecodeRecovery: false,
|
|
DecodeStreamsAsDatagrams: false,
|
|
})
|
|
ipv6Layer := parsedPacketAsIPv6.Layer(layers.LayerTypeIPv6)
|
|
if ipv6Layer == nil {
|
|
return nullDestination, nullDestination, nil, errNotIPPacket
|
|
}
|
|
ipv6 := ipv6Layer.(*layers.IPv6)
|
|
srcIP = net.IPAddress(ipv6.SrcIP)
|
|
dstIP = net.IPAddress(ipv6.DstIP)
|
|
|
|
parsedPacket = parsedPacketAsIPv6
|
|
} else {
|
|
ipv4 := ipv4Layer.(*layers.IPv4)
|
|
srcIP = net.IPAddress(ipv4.SrcIP)
|
|
dstIP = net.IPAddress(ipv4.DstIP)
|
|
}
|
|
|
|
udpLayer := parsedPacket.Layer(layers.LayerTypeUDP)
|
|
if udpLayer == nil {
|
|
return nullDestination, nullDestination, nil, errNotUDPPacket
|
|
}
|
|
udp := udpLayer.(*layers.UDP)
|
|
srcPort := net.Port(udp.SrcPort)
|
|
dstPort := net.Port(udp.DstPort)
|
|
|
|
src = net.UDPDestination(srcIP, srcPort)
|
|
dst = net.UDPDestination(dstIP, dstPort)
|
|
data = udp.Payload
|
|
return // nolint: nakedret
|
|
}
|
|
|
|
func TryConstructUDPPacket(src, dst net.Destination, data []byte) ([]byte, error) {
|
|
if src.Address.Family().IsIPv4() && dst.Address.Family().IsIPv4() {
|
|
return constructIPv4UDPPacket(src, dst, data)
|
|
}
|
|
if src.Address.Family().IsIPv6() && dst.Address.Family().IsIPv6() {
|
|
return constructIPv6UDPPacket(src, dst, data)
|
|
}
|
|
return nil, newError("not supported")
|
|
}
|
|
|
|
func constructIPv4UDPPacket(src, dst net.Destination, data []byte) ([]byte, error) {
|
|
ipv4 := &layers.IPv4{
|
|
Version: 4,
|
|
Protocol: layers.IPProtocolUDP,
|
|
SrcIP: src.Address.IP(),
|
|
DstIP: dst.Address.IP(),
|
|
TTL: 64, // set TTL to a reasonable non-zero value to allow non-local routing
|
|
}
|
|
udp := &layers.UDP{
|
|
SrcPort: layers.UDPPort(src.Port),
|
|
DstPort: layers.UDPPort(dst.Port),
|
|
}
|
|
err := udp.SetNetworkLayerForChecksum(ipv4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buffer := gopacket.NewSerializeBuffer()
|
|
if err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{
|
|
FixLengths: true,
|
|
ComputeChecksums: true,
|
|
}, ipv4, udp, gopacket.Payload(data)); err != nil {
|
|
return nil, err
|
|
}
|
|
return buffer.Bytes(), nil
|
|
}
|
|
|
|
func constructIPv6UDPPacket(src, dst net.Destination, data []byte) ([]byte, error) {
|
|
ipv6 := &layers.IPv6{
|
|
Version: 6,
|
|
NextHeader: layers.IPProtocolUDP,
|
|
SrcIP: src.Address.IP(),
|
|
DstIP: dst.Address.IP(),
|
|
HopLimit: 64,
|
|
}
|
|
udp := &layers.UDP{
|
|
SrcPort: layers.UDPPort(src.Port),
|
|
DstPort: layers.UDPPort(dst.Port),
|
|
}
|
|
err := udp.SetNetworkLayerForChecksum(ipv6)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buffer := gopacket.NewSerializeBuffer()
|
|
if err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{
|
|
FixLengths: true,
|
|
ComputeChecksums: true,
|
|
}, ipv6, udp, gopacket.Payload(data)); err != nil {
|
|
return nil, err
|
|
}
|
|
return buffer.Bytes(), nil
|
|
}
|