This article is the 7th day article of KLab Engineer Advent Calendar 2019. I was late when I was having trouble debugging ...
The topic of systemd, TCP proxies and Wake On LAN.
I'm a Mac user, but sometimes I use my Windows machine at home via Remote Desktop.
This Windows machine is usually put to sleep, so if you want to use it, you must first throw a Wake On LAN magic packet to wake it up [^ 1].
[^ 1]: It's a remote desktop dedicated machine and it's out of reach because it's not normally used.
However, it is not uncommon to forget the MAC address of a Windows machine or forget the command to throw a magic packet in the first place.
Of course, the commands I used before are recorded in the history, so I can manage it, but in the first place, it seems to be troublesome to manually throw magic packets every time.
So, I configured a TCP proxy with systemd and created a mechanism to automatically throw magic packets before connecting to the proxy. I have a Raspberry Pi that is always running in my house, so I decided to operate a TCP proxy here.
This eliminates the hassle of throwing magic packets on your Mac, and allows you to log on to Windows simply by selecting the connection destination from the GUI of the remote desktop.
This article will introduce the details of this mechanism. Since there is no protocol-dependent part, it should be possible to use it other than remote desktop.
systemd is a high-performance service management program used in recent Linux.
First, I will introduce the process of throwing a magic packet. This was written in the oneshot service template.
[Unit] Description = Sending Wake on LAN packet (oneshot) After = network-online.target Wants = network-online.target [Service] Type = oneshot ExecStart = /usr/sbin/etherwake %i
A service template is a mechanism that converts a part of the service name (instance character string) into a variable character string and passes it to the service. Here, the MAC address is specified as the instance string and used.
$ sudo systemctl start [email protected]:00:5e:00:53:01.service
This will send a magic packet to the MAC address 00: 00: 5e: 00: 53: 01.
Next, we will create a TCP proxy with systemd. This is a 2-file structure.
[Unit] Description = Socket for RDP (Remote Desktop Protocol) proxy [Socket] ListenStream = 0.0.0.0:3389 Accept = yes [Install] WantedBy = sockets.target
After installing the above file, enable automatic startup.
$ sudo systemctl enable rdp-proxy.socket $ sudo systemctl start rdp-proxy.socket
ListenStream =, it listens on TCP 3389 and passes it to the service [^ 2]. Also, if ʻAccept = yes`, you need to create a service template with the same name.
[^ 2]: You can also use
ListenDatagram = to listen on UDP.
[Unit] Description = Server for RDP (Remote Desktop Protocol) Proxy Requires = [email protected]:00:5e:00:53:01.service After = network-online.target [email protected]:00:5e:00:53:01.service Wants = network-online.target [Service] Type = simple ExecStartPre = /usr/bin/timeout 60 /bin/sh -c 'until /bin/nc -w 5 -z 192.0.2.1 3389; do sleep 1; done' ExecStart = /bin/nc -q 10 192.0.2.1 3389 StandardInput = socket StandardOutput = socket StandardError = journal
Requires specifies the service that throws the magic packet created earlier.
The main process is proxying using netcat. One process will be started for each connection.
However, simply proxying with netcat did not work as expected. Because there is a time lag between throwing the magic packet and the remote desktop server going into the listen state, you have to wait for a while before you can connect. Therefore, before connecting, I used ʻExecStartPre` to check if TCP connection was possible, and proxyed after it became possible to connect.
The IP address and MAC address are for example only, so change them as appropriate when using the above settings.
When I actually used it, it was more practical than I expected. I'm using Raspberry Pi 2 as a proxy, but there seems to be no performance problem (because I had enough CPU load and network bandwidth as far as I experimented).
By the way, in my environment, there is a time lag of about 30 seconds from connecting to the proxy to connecting to the sleeping Windows by remote desktop. It's not unbearable, but I think it's too late, so there may be some improvements.
I made a TCP proxy with systemd and showed that any external command can be executed before its connection. This time, I threw a Wake On LAN magic packet with an external command, but it seems that it can also be applied such as connecting after launching an AWS instance.
Anyway, systemd can do anything ...