github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/tun/packetparse/udp.go (about)

     1  package packetparse
     2  
     3  import (
     4  	"github.com/google/gopacket"
     5  	"github.com/google/gopacket/layers"
     6  
     7  	"github.com/v2fly/v2ray-core/v5/common/net"
     8  )
     9  
    10  var (
    11  	errNotIPPacket  = newError("not an IP packet")
    12  	errNotUDPPacket = newError("not a UDP packet")
    13  )
    14  
    15  var nullDestination = net.UnixDestination(net.DomainAddress("null"))
    16  
    17  func TryParseAsUDPPacket(packet []byte) (src, dst net.Destination, data []byte, err error) {
    18  	parsedPacket := gopacket.NewPacket(packet, layers.LayerTypeIPv4, gopacket.DecodeOptions{
    19  		Lazy:                     true,
    20  		NoCopy:                   false,
    21  		SkipDecodeRecovery:       false,
    22  		DecodeStreamsAsDatagrams: false,
    23  	})
    24  
    25  	var srcIP net.Address
    26  	var dstIP net.Address
    27  	ipv4Layer := parsedPacket.Layer(layers.LayerTypeIPv4)
    28  
    29  	if ipv4Layer == nil {
    30  		parsedPacketAsIPv6 := gopacket.NewPacket(packet, layers.LayerTypeIPv6, gopacket.DecodeOptions{
    31  			Lazy:                     true,
    32  			NoCopy:                   false,
    33  			SkipDecodeRecovery:       false,
    34  			DecodeStreamsAsDatagrams: false,
    35  		})
    36  		ipv6Layer := parsedPacketAsIPv6.Layer(layers.LayerTypeIPv6)
    37  		if ipv6Layer == nil {
    38  			return nullDestination, nullDestination, nil, errNotIPPacket
    39  		}
    40  		ipv6 := ipv6Layer.(*layers.IPv6)
    41  		srcIP = net.IPAddress(ipv6.SrcIP)
    42  		dstIP = net.IPAddress(ipv6.DstIP)
    43  
    44  		parsedPacket = parsedPacketAsIPv6
    45  	} else {
    46  		ipv4 := ipv4Layer.(*layers.IPv4)
    47  		srcIP = net.IPAddress(ipv4.SrcIP)
    48  		dstIP = net.IPAddress(ipv4.DstIP)
    49  	}
    50  
    51  	udpLayer := parsedPacket.Layer(layers.LayerTypeUDP)
    52  	if udpLayer == nil {
    53  		return nullDestination, nullDestination, nil, errNotUDPPacket
    54  	}
    55  	udp := udpLayer.(*layers.UDP)
    56  	srcPort := net.Port(udp.SrcPort)
    57  	dstPort := net.Port(udp.DstPort)
    58  
    59  	src = net.UDPDestination(srcIP, srcPort)
    60  	dst = net.UDPDestination(dstIP, dstPort)
    61  	data = udp.Payload
    62  	return // nolint: nakedret
    63  }
    64  
    65  func TryConstructUDPPacket(src, dst net.Destination, data []byte) ([]byte, error) {
    66  	if src.Address.Family().IsIPv4() && dst.Address.Family().IsIPv4() {
    67  		return constructIPv4UDPPacket(src, dst, data)
    68  	}
    69  	if src.Address.Family().IsIPv6() && dst.Address.Family().IsIPv6() {
    70  		return constructIPv6UDPPacket(src, dst, data)
    71  	}
    72  	return nil, newError("not supported")
    73  }
    74  
    75  func constructIPv4UDPPacket(src, dst net.Destination, data []byte) ([]byte, error) {
    76  	ipv4 := &layers.IPv4{
    77  		Version:  4,
    78  		Protocol: layers.IPProtocolUDP,
    79  		SrcIP:    src.Address.IP(),
    80  		DstIP:    dst.Address.IP(),
    81  		TTL:      64, // set TTL to a reasonable non-zero value to allow non-local routing
    82  	}
    83  	udp := &layers.UDP{
    84  		SrcPort: layers.UDPPort(src.Port),
    85  		DstPort: layers.UDPPort(dst.Port),
    86  	}
    87  	err := udp.SetNetworkLayerForChecksum(ipv4)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	buffer := gopacket.NewSerializeBuffer()
    92  	if err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{
    93  		FixLengths:       true,
    94  		ComputeChecksums: true,
    95  	}, ipv4, udp, gopacket.Payload(data)); err != nil {
    96  		return nil, err
    97  	}
    98  	return buffer.Bytes(), nil
    99  }
   100  
   101  func constructIPv6UDPPacket(src, dst net.Destination, data []byte) ([]byte, error) {
   102  	ipv6 := &layers.IPv6{
   103  		Version:    6,
   104  		NextHeader: layers.IPProtocolUDP,
   105  		SrcIP:      src.Address.IP(),
   106  		DstIP:      dst.Address.IP(),
   107  		HopLimit:   64,
   108  	}
   109  	udp := &layers.UDP{
   110  		SrcPort: layers.UDPPort(src.Port),
   111  		DstPort: layers.UDPPort(dst.Port),
   112  	}
   113  	err := udp.SetNetworkLayerForChecksum(ipv6)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	buffer := gopacket.NewSerializeBuffer()
   118  	if err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{
   119  		FixLengths:       true,
   120  		ComputeChecksums: true,
   121  	}, ipv6, udp, gopacket.Payload(data)); err != nil {
   122  		return nil, err
   123  	}
   124  	return buffer.Bytes(), nil
   125  }