| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- package amneziawgnet
- import (
- "fmt"
- "net/netip"
- "gvisor.dev/gvisor/pkg/buffer"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/checksum"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/stack"
- "gvisor.dev/gvisor/pkg/tcpip/transport/udp"
- )
- // UDPHandler is called for every UDP packet a tunnel client sends, with its
- // source (the peer's tunnel-internal address) and its real,
- // dynamically-arbitrary destination -- recovered the same way the TCP
- // forwarder recovers its destination, from the packet's own transport
- // endpoint ID, never from a preconfigured table. The handler owns all flow
- // tracking and reply delivery (via WriteUDPReply): gVisor has no
- // udp.NewForwarder the way it does for TCP, so unlike AttachTCPForwarder
- // this can't just hand back a ready net.Conn.
- type UDPHandler func(src, dst netip.AddrPort, payload []byte)
- // AttachUDPHandler attaches a raw UDP handler to gstack, independently
- // enabling the same promiscuous+spoofing mode AttachTCPForwarder needs --
- // safe and idempotent to call regardless of whether AttachTCPForwarder was
- // attached to the same stack first, or at all. Adapted from xtls/xray-core's
- // proxy/wireguard/tun.go UDP path (MIT), which hand-tracks flows for the
- // identical reason: gVisor doesn't provide a UDP forwarder.
- func AttachUDPHandler(gstack *stack.Stack, handler UDPHandler) {
- enablePromiscuousRouting(gstack)
- gstack.SetTransportProtocolHandler(udp.ProtocolNumber, func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
- data := pkt.Clone().Data().AsRange().ToSlice()
- src := netip.AddrPortFrom(addrFromTcpip(id.RemoteAddress), id.RemotePort)
- dst := netip.AddrPortFrom(addrFromTcpip(id.LocalAddress), id.LocalPort)
- handler(src, dst, data)
- return true
- })
- }
- // WriteUDPReply injects a UDP packet into gstack as if it arrived from
- // `from` addressed to `to` -- i.e. a reply travelling back into the tunnel
- // toward the client -- constructed by hand since gVisor exposes no
- // connected-socket-style Write for an address the stack doesn't itself own.
- func WriteUDPReply(gstack *stack.Stack, from, to netip.AddrPort, payload []byte) error {
- udpLen := header.UDPMinimumSize + len(payload)
- srcIP := tcpip.AddrFromSlice(from.Addr().AsSlice())
- dstIP := tcpip.AddrFromSlice(to.Addr().AsSlice())
- isIPv4 := from.Addr().Is4()
- ipHdrSize := header.IPv6MinimumSize
- ipProtocol := header.IPv6ProtocolNumber
- if isIPv4 {
- ipHdrSize = header.IPv4MinimumSize
- ipProtocol = header.IPv4ProtocolNumber
- }
- pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
- ReserveHeaderBytes: ipHdrSize + header.UDPMinimumSize,
- Payload: buffer.MakeWithData(payload),
- })
- defer pkt.DecRef()
- udpHdr := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize))
- udpHdr.Encode(&header.UDPFields{
- SrcPort: from.Port(),
- DstPort: to.Port(),
- Length: uint16(udpLen),
- })
- xsum := header.PseudoHeaderChecksum(header.UDPProtocolNumber, srcIP, dstIP, uint16(udpLen))
- udpHdr.SetChecksum(^udpHdr.CalculateChecksum(checksum.Checksum(payload, xsum)))
- if isIPv4 {
- ipHdr := header.IPv4(pkt.NetworkHeader().Push(header.IPv4MinimumSize))
- ipHdr.Encode(&header.IPv4Fields{
- TotalLength: uint16(header.IPv4MinimumSize + udpLen),
- TTL: 64,
- Protocol: uint8(header.UDPProtocolNumber),
- SrcAddr: srcIP,
- DstAddr: dstIP,
- })
- ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
- } else {
- ipHdr := header.IPv6(pkt.NetworkHeader().Push(header.IPv6MinimumSize))
- ipHdr.Encode(&header.IPv6Fields{
- PayloadLength: uint16(udpLen),
- TransportProtocol: header.UDPProtocolNumber,
- HopLimit: 64,
- SrcAddr: srcIP,
- DstAddr: dstIP,
- })
- }
- if tcpipErr := gstack.WriteRawPacket(1, ipProtocol, buffer.MakeWithView(pkt.ToView())); tcpipErr != nil {
- return fmt.Errorf("amneziawgnet: WriteRawPacket: %s", tcpipErr)
- }
- return nil
- }
|