duplicating UDP stream via socat - linux

I'm developing some gateway product connected to host PC via USB, with embedded linux inside.
My task is read to read logs UDP stream from device file and to forward it both to remote host and to save it to file in my file system (ramfs)
I do this using the following command:
socat -b1450 -u /dev/ueservice0 - | tee -a /tmp/ModemFW.log | socat -b1450 -u - UDP4-DATAGRAM:${IPADDR}:4566
The problem is that when there's heavy data traffic (FTP) stream running on the same USB interface approx. half of the stream doesn't reach the destination, though the ModemFW.log file contains all the logs.
The weird thing is that if I don't save the logs to file on ramfs everything works fine and the host receives all the stream even with the same heavy traffic:
socat -b1450 -u /dev/ueservice0 UDP4-DATAGRAM:${IPADDR}:4566
Does anybody know what is the problem that tee causes that half of the packets get lost? Is there another way to do it?
Any help will be very appreciated!

Related

How to run ssh over an existing TCP connection

I want to be able to SSH to a number linux devices at once, behind different NATs. I can't configure the network that they are on. However, I'm having trouble getting ssh to go over an existing connection.
I have full control over both my client and the devices. Here's the process so far:
On my client, I first run
socat TCP-LISTEN:5001,pktinfo,fork EXEC:./create_socket.sh,fdin=3,fdout=4,nofork
Contents of ./create_socket.sh:
ssh -N -M -S "~/sockets/${SOCAT_PEERADDR}" -o "ProxyCommand=socat - FD:3!!FD:4" "root#${SOCAT_PEERADDR}"
On the device, I'm running
socat TCP:my_host:4321 TCP:localhost:22
However, nothing comes in or out of FD:3!!FD:4, I assume because the ProxyCommand is a subprocess. I've also tried setting fdin=3,fdout=3 and changing ./create_socket.sh to:
ssh -N -M -S "~/sockets/${SOCAT_PEERADDR}" -o "ProxyUseFdpass=yes" -o "ProxyCommand=echo 3" "root#${host}"
This prints an error:
mm_receive_fd: no message header
proxy dialer did not pass back a connection
I believe this is because the fd should be sent in some way using sendmsg, but the fd doesn't originate from the subprocess anyways. I'd like to make it as simple as possible, and this feels close to workable.
You want to turn the client/server model on its head and make a generic server to spawn a client on-demand and in-response-to an incoming unauthenticated TCP connection from across a network boundary, and then tell that newly-spawned client to use that unauthenticated TCP session. I think that may have security considerations that you haven't thought of. If a malicious person spams connections to your computer, your computer will spawn a lot of SSH instances to connect back and these processes can take up a lot of local system resources while authenticating. You're effectively trying to set up SSH to automatically connect to an untrusted (unverified) remote-initiated machine across a network boundary. I can't stress how dangerous that could be for your client computer. Using the wrong options could expose any credentials you have or even give a malicious person full access to your machine.
It's also worth noting that the scenario you're asking to do, building a tunnel between multiple devices to multiplex additional connections across an untrusted network boundary, is exactly the purpose of VPN software. Yes, SSH can build tunnels. VPN software can build tunnels better. The concept would be that you'd run a VPN server on your client machine. The VPN server will create a new (virtual) network interface which represents only your devices. The devices would connect to the VPN server and be assigned an IP address. Then, from the client machine, you'd just initiate SSH to the device's VPN address and it will be routed over the virtual network interface and arrive at the device and be handled by its SSH daemon server. Then you don't need to muck around with socat or SSH options for port forwarding. And you'd get all the tooling and tutorials that exist around VPNs. I strongly encourage you to look at VPN software.
If you really want to use SSH, then I strongly encourage you to learn about securing SSH servers. You've stated that the devices are across network boundaries (NAT) and that your client system is unprotected. I'm not going to stop you from shooting yourself in the foot but it would be very easy to spectacularly do so in the situation you've stated. If you're in a work setting, you should talk to your system administrators to discuss firewall rules, bastion hosts, stuff like that.
Yes, you can do what you've stated. I strongly advise caution though. I advise it strongly enough that I won't suggest anything which would work with that as stated. I will suggest a variant with the same concepts but more authentication.
First, you've effectively set up your own SSH bounce server but without any of the common tooling compatible with SSH servers. So that's the first thing I'd fix: use SSH server software to authenticate incoming tunnel requests by using ssh client software to initiate the connection from the device instead of socat. ssh already has plenty of capabilities to create tunnels in both directions and you get authentication bundled with it (with socat, there's no authentication). The devices should be able to authenticate using encryption keys (ssh calls these identities). You'll need to connect once manually from the device to verify and authorize the remote encryption key fingerprint. You'll also need to copy the public key file (NOT the private key file) to your client machine and add it to your authorized_keys files. You can ask for help on that separately if you need it.
A second issue is that you appear to be using fd3 and fd4. I don't know why you're doing that. If anything, you should be using fd0 and fd1 since these are stdin and stdout, respectively. But you don't even need to do that if you're using socat to initiate a connection. Just use - where stdin and stdout are meant. It should be completely compatible with -o ProxyCommand without specifying any file descriptors. There's an example at the end of this answer.
The invocation from the device side might look like this (put it into a script file):
IDENTITY=/home/WavesAtParticles/.ssh/tunnel.id_rsa # on device
REMOTE_SOCKET=/home/WavesAtParticles/.ssh/$(hostname).sock # on client
REMOTEUSER=WavesAtParticles # on client
REMOTEHOST=remotehost # client hostname or IP address accessible from device
while true
do
echo "$(date -Is) connecting"
#
# Set up your SSH tunnel. Check stderr for known issues.
ssh \
-i "${IDENTITY}" \
-R "${REMOTE_SOCKET}:127.0.0.1:22" \
-o ExitOnForwardFailure=yes \
-o PasswordAuthentication=no \
-o IdentitiesOnly=yes \
-l "${REMOTEUSER}" \
"${REMOTEHOST}" \
"sleep inf" \
2> >(
read -r line
if echo "${line}" | grep -q "Error: remote port forwarding failed"
then
ssh \
-i "${IDENTITY}" \
-o PasswordAuthentication=no \
-o IdentitiesOnly=yes \
-l "${REMOTEUSER}" \
"${REMOTEHOST}" \
"rm ${REMOTE_SOCKET}" \
2>/dev/null # convince me this is wrong
echo "$(date -Is) removed stale socket"
fi
#
# Re-print stderr to the terminal
>&2 echo "${line}" # the stderr line we checked
>&2 cat - # and any unused stderr messages
)
echo "disconnected"
sleep 30
done
Remember, copying and pasting is bad in terms of shell scripts. At a minimum, I recommend you read man ssh and man ssh_config, and to check the script against shellcheck.net. The intent of the script is:
In a loop, have your device (re)connect to your client to maintain your tunnel.
If the connection drops or fails, then reconnect every 30 seconds.
Run ssh with the following parameters:
-i "${IDENTITY}": specify a private key to use for authentication.
-R "${REMOTE_SOCKET}:127.0.0.1:22": specify a connection request forwarder which accept connections on the Remote side /home/WavesAtParticles/$(hostname).sock then forward them to the local side by connecting to 127.0.0.1:22.
-o ExitOnForwardFailure=yes: if the remote side fails to set up the connection forwarder, then the local side should emit an error and die (and we check for this error in a subshell).
-o PasswordAuthentication=no: do not fall back to a password request, particularly since the local user isn't here to type it in
-o IdentitiesOnly=yes: do not use any default identity nor any identity offered by any local agent. Use only the one specified by -i.
-l "${REMOTEUSER}": log in as the specified user.
remotehost, eg your client machine that you want a device to connect to.
Sleep forever
If the connection failed because of a stale socket, then work around the issue by:
Log in separately
Delete the (stale) socket
Print today's date indicating when it was deleted
Loop again
There's an option which is intended to make this error-handling redundant: StreamLocalBindUnlink. However the option does not correctly work and has a bug open for years. I imagine that's because there really aren't many people who use ssh to forward over unix domain sockets. It's annoying but not difficult to workaround.
Using a unix domain socket should limit connectivity to whoever can reach the socket file (which should be only you and root if it's placed in your ${HOME}/.ssh directory and the directory has correct permissions). I don't know if that's important for your case or not.
On the other hand you can also simplify this a lot if you're willing to open a TCP port on 127.0.0.1 for each device. But then any other user on the same system can also connect. You should specifically listen on 127.0.0.1 which would then only accept connections from the same host to prevent external machines from reaching the forwarding port. You'd change the ${REMOTE_SOCKET} variable to, for example, 127.0.0.1:4567 to listen on port 4567 and only accept local connections. So you'd lose the named socket capability and permit any other user on the client machine to connect to your device, but gain a much simpler tunnel script (because you can remove the whole bit about parsing stderr to remove a stale socket file).
As long as your device is online (can reach your workstation's incoming port) and is running that script, and the authentication is valid, then the tunnel should also be online or coming-online. It will take some time to recover after a loss (and restore) of network connectivity, though. You can tune that with ConnectTimeout, TCPKeepAlive, and ServerAliveInterval options and the sleep 30 part of the loop. You could run it in a tmux session to keep it going even when you don't have a login session running. You could also run it as a system service on the device to bring it online even after recovering from a power failure.
Then from your client, you can connect in reverse:
ssh -o ProxyCommand='socat - unix-connect:/home/WavesAtParticles/remotehost.sock' -l WavesAtParticles .
In this invocation, you'll start ssh. It will then set up the proxycommand using socat. It will take its stdin/stdout and relay it through a connected AF_UNIX socket at the path provided. You'll need to update the path for the remote host you expect. But there's no need to specify file descriptors at all.
If ssh complains:
2019/08/26 18:09:52 socat[29914] E connect(5, AF=1 "/home/WavesAtParticles/remotehost.sock", 7): Connection refused
ssh_exchange_identification: Connection closed by remote host
then the tunnel is currently down and you should investigate the remotehost device's connectivity.
If you use the remote forwarding option with a TCP port listening instead of a unix domain socket, then the client-through-tunnel-to-remote invocation becomes even easier: ssh -p 4567 WavesAtParticles#localhost.
Again, you're trying to invert the client/server model and I don't think that's a very good idea to do with SSH.
I’m going to try this today:
http://localhost.run/
It seems like what you are looking for.
Not to answer your question but helpful for people who may not know:
Ngrok is the easiest way I’ve found. they do webservers as well as tcp connections. I’d recommend installing it through homebrew.
https://ngrok.com/product
$ ngrok http 5000
In the terminal for http, 5000 being the port of your application.
$ ngrok tcp 5000
In the terminal for tcp.
It’s free for testing(random changing domains).
For tcp connections remove “http://“ from the web address to get the IP address. Sorry I can’t remember. I think the client ports to 80 and I believe you can change that by adding port 5001 or something, google it to double check

Redirect TCP data to ttyS0

I have a closed application running on a different, but network accessible, Linux OS that is using SerialIO to open /dev/ttyS0.
How can I write to ttyS0 from a different device so that the existing application will see what I'm writing as actual serial data?
Ive tried quite a few different socat commands and havent had luck.
socat -d -d /dev/ttyS0,raw,echo=0,b9600 tcp-l:6174,reuseaddr
socat -d -d pty,link=/dev/ttyS0,raw,echo=0 tcp-l:6174,reuseaddr
Any ideas on what is the best way to do this so that the existing (untouchable) application will think nothing has happened?
Take a look at the ser2net daemon. It is able to act as a TCP serial server for either raw or RFC2217 connections.

Uploading files to busybox machine

For a project of mine I am using a very limited Linux busybox machine.
I am trying to upload files to that machine (connected to me via Ethernet) using telnet.
So far I had several ideas for implementing it:
Writing the files in chunks (using echo -e on chunks of 128 bytes) to the disk. The idea failed because the echo command doesn't have a -e option.
Redirecting socket into a file using something like /dev/tcp/192.168.1.2/12345 > /tmp/file. The idea failed because the /dev/ folder didn't contain the tcp device.
Using utilities such as nc / ncat / nfqueue to do so, the idea also failed because none of them exists and I can't install anything on that machine (no apt-get / yum etc..)
Using echo (without the -e option) to write a base-64 encoded file to the disk and then decode it. The idea failed because I couldn't find any utility to decode base64 strings
Do you have any creative idea to upload files?
Thank you for the fast replies.
I found on the machine a tftp client.
Therefore I could execute:
/usr/bin/tftp -l /tmp/tst -r testfile.txt -g <server ip>
after opening a tftp server on .
See if your busybox build includes rx .
This will give you XMODEM receive functionality on your target.
I asked our software group to add it to our image; it was added with very little effort.

run iperf broadcast traffic on linux

I am in the domain 192.168.1.xxx. I need to send broadcast traffic from a pc connected to a accesspoint to the android device wirelessly after connecting with a network through WiFi.
running iperf command iperf -c 192.168.1.255 -i 2 -t 60 -b 10000 -u on a Ubuntu 12.04 machine and running the command iperf -s -i 2 -u in adb shell.
But the client is unable to get the broadcast traffic and gives error as :
read failed : connection refused.
you cannot use iperf in broadcast mode.
if you are just testing whether broadcast works, you could try something like
# server
netcat -l -u -p 54321
# host
echo "foo" | netcat -ub 192.168.1.255 54321
note however, that on some systems, netcat might not be able to go into broadcast mode, so you might end up writing your own networking code.
You can't do iperf broadcast on linux.
Connection refused generally happens when the server and client are not on the same network or make sure the server IP address that you are specifying in the client is correct.
You can leverage DHCP's properties, DHCP is advertised with a broadcast DISCORVERY package, so by putting a DHCP-server and a DHCP-client (normal host) at the ends of your network you can test broadcast traffic within it.
If you get an address -> you're OK
If you keep waiting and nothing happens -> something went wrong
Hope this helps you ;)

Continuously send the content of a file through a server socket with netcat

I have a linux machine which is listening for connections on port 4450. Where there is an incomming connection, this is supposed to send continuously over the socket the content of a file. Did you do this before ?
What I've done so far was to send once the content of the file like this:
x=$(filename); echo $x | nc -l 4450
On the client side I have an Android app, which connects to the server and then using a BufferedReader gets the data from the stream and processes it.
Any help would be highly appreciated.
Thanks
Use socat instead of netcat (nc). With socat you can do almost everything that can be done with netcat. But socat has a lot more features and is easier to use.
socat TCP-LISTEN:4450,fork OPEN:/tmp/filename,rdonly
You can also use the output of a command instead of some file contents:
socat TCP-LISTEN:4450,fork EXEC:/bin/date

Resources