1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-09-20 02:46:10 -04:00
v2fly/app/tun/packetparse/udp.go
Huang-Huang Bao 42c50d25e1 fix: set TTL for incoming UDP IP packets from TUN with PacketAddr dispatcher
Fixes UDP packets being dropped in non-local routing path(e.g. forwarding to LAN or container veth) due to 0-TTL.
2023-11-01 12:58:10 +00:00

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
}