Routing TCP packets to a local UDP port

As an alternative to SSH port forwarding, you may forward those UDP packets to your local machine by using point-to-point tunnel devices, SSH and the procedure explained below.

In the EC2 instance, do the following:

  1. In /etc/ssh/sshd_config file, set configuration parameter PermitTunnel to either point-to-point or yes.

  2. Create a point-to-point tunnel device that will receive UDP packets from the public network:

    $ sudo ip tuntap add dev tun8975 mode tun user ubuntu
    $ sudo ip addr add dev tun8975 172.16.19.22 peer 172.16.19.21
    $ sudo ip link set dev tun8975 up
    
  3. Configure either IPTABLES or NFTABLES to forward such UDP packets to the tunnel device

    $ sudo iptables -t nat -A PREROUTING '!' -i tun8975 -p udp --dport 8975 -j DNAT --to-destination 172.16.19.21:8975
    
    $ sudo nft add rule ip nat PREROUTING iifname '!=' "tun8975" udp dport 8975 dnat to 172.16.19.21:8975
    
  4. Ensure that kernel allows IP packet forwarding among network interfaces:

    $ sudo sysctl -w net.ipv4.ip_forward=1
    

Afterwards, in your local machine, do the following:

  1. Create a point-to-point tunnel device that SSH will use to output forwarded UDP packets:

    $ sudo ip tuntap add dev tun8975 mode tun
    $ sudo ip addr add dev tun8975 172.16.19.21 peer 172.16.19.22
    $ sudo ip link set dev tun8975 up
    
  2. Request SSH to forward traffic between the tunnel devices through its secure channel:

    $ sudo ssh -4 -o Tunnel=point-to-point -w 8975:8975 -i ~/.ssh/xxxx.pem [email protected]
    

I see two issues in your approach to solve the communication problem.

The first issue is in the command run in the local machine. I believe that the -l flag on the UDP part of the netcat command pipeline should be removed and another process in your local machine (which could be another netcat) should listen to UDP port 8975 and receive datagrams sent to that port. netcat in this scenario would run in client mode and generate such datagrams.

The second issue is on the way these command lines are constructed:

  • On the remote machine
    $ netcat -l -u -p 8975 > /tmp/udp2tcp | netcat -l -p 10000 < /tmp/udp2tcp
    
  • On the local machine
    $ netcat localhost 10000 > /tmp/tcp2udp | netcat -l -u -p 8975 < /tmp/tcp2udp
    

They mix redirections and pipelines. Although I did read Pipelines and Redirection sections from Bash manual page and figured out that pipelines would be processed before redirects, I am unable to understand how the shell would connect standard streams using such syntax.

netcat works by forwarding data from its standard input to the sending side of the socket and forwarding data from the receiving side of the socket to its standard output. It does so regardless of whether such socket is a client socket connecting to a remote server or a server socket accepting a connection from a remote client. Because of that, in order to forwarding data between a TCP and a UDP socket, you will need to connect standard streams appropriately, that is, you will need to connect STDOUT of the TCP netcat to STDIN of the UDP netcat and STDOUT of the UDP netcat to STDIN of the TCP netcat at the same time.

Perhaps the command lines you are running already achieve that objective. Despite of that, I propose a more explicit data forwarding, by using these commands:

  • On the remote machine

    $ cat /tmp/udp2tcp | netcat -l -u -p 8975 | netcat -l -p 10000 > /tmp/udp2tcp
    
  • On the local machine

    $ cat /tmp/tcp2udp | netcat localhost 10000 | netcat -u localhost 8975 > /tmp/tcp2udp
    

In both cases, an anonymous pipe would connect first netcat's STDOUT to second netcat's STDIN and a named pipe (fifo) would connect second netcat's STDOUT back to first netcat's STDIN.

Consider instructing netcat to use only IPv4 stack by adding the -4 flag to command line arguments. This might help if the process listening to UDP port 8975 is not compatible with IPv6.