UDP OpenVPN on Mikrotik 7

Illustration

Despite UDP being ubiquitous on pretty much any other OpenVPN platform, for a long while Mikrotik only supported TCP variant. With Mikrotik RouterOS 7 finally being released earlier this year, we at last got an UDP support for OpenVPN.

For some people UDP/TCP difference might not matter much. If you have a stable connection chances are you really don’t need to care - OpenVPN via TCP will serve you without any issues.

But, if you are dealing with multiple connections over a high latency and/or lossy network, UDP will be much faster as lost packets for one connection will not impact the other. How big the difference is? Well, I have a connection between USA and Croatia and it leaks like a sieve. My speed went from about 400 Kbps to 1000 Kbps just due to this change (tested using 2 parallel connections). I would say switching to UDP was well worth the effort for my use case.

Getting UDP enabled for OpenVPN server once you get Mikrotik 7.1 or higher running is trivial assuming you have OpenVPN via TCP already configured. You just change Protocol value to udp, update your client side with the same change (albeit for proto field) and you’re done.

But, in the interest of completeness, let’s see how one would create such config from scratch.

First we create all the certificate templates (give it at least 10 years validity):

/certificate
add name=ca-template common-name=^^example.com^^ days-valid=3650 \
  key-size=2048 key-usage=crl-sign,key-cert-sign
add name=server-template common-name=^^*.example.com^^ days-valid=3650 \
  key-size=2048 key-usage=digital-signature,key-encipherment,tls-server
add name=client-template common-name=^^client.example.com^^ days-valid=3650 \
  key-size=2048 key-usage=tls-client

As far as OpenVPN server is concerned, you can use whatever you want for certificate’s common name. Since some other VPNs are not as forgiving (yes SSTP, I am looking at you), I made it a habit to use either external IP or the host name here.

Once we have templates sorted out, we can do the signing:

/certificate
sign ca-template name=ca-certificate
sign server-template name=server-certificate ca=ca-certificate
sign client-template name=client-certificate ca=ca-certificate

And then exporting certificate material:

/certificate
export-certificate ca-certificate export-passphrase=""
export-certificate client-certificate export-passphrase=^^12345678^^

This should give you three files: cert_export_ca-certificate.crt, cert_export_client-certificate.crt, and cert_export_client-certificate.key. After copying these files to the computer for later I like to rename them to ca.crt, client.crt, and client.key respectively. It just makes everything a bit tidier.

Next we need a separate pool of IP addresses for clients. I will assume you have your clients in some other network (e.g. 192.168.1.x) and this new network is just for VPN:

/ip
pool add name="vpn-pool" ranges=192.168.8.10-192.168.8.99

Instead of editing the default encrypted profile, we can create a new one. If you use different DNS server, do change it here, and while at it, you should really use a bit more imaginative user/password pair:

/ppp
profile add name="vpn-profile" use-encryption=yes idle-timeout=10m \
  local-address=192.168.8.250 dns-server=1.1.1.1 remote-address=vpn-pool \
  secret add name=^^user^^ profile=vpn-profile password=^^password^^

Finally, we can enable OpenVPN server interface:

/interface ovpn-server server
  set default-profile=vpn-profile certificate=server-certificate require-client-certificate=yes \
  auth=sha1 cipher=aes128,aes192,aes256 enabled=yes protocol=udp

Assuming you’re using Windows, you can copy both ca.crt and client.crt to C:\Program Files\OpenVPN\config\ directory alongside client.ovpn. On Linux, one would do the same, just in the /etc/openvpn/client directory.

You don’t have client.ovpn? Well, one is in sample-config directory and we just need to change/add the highlighted items. And since we’re finally using UDP, we can leave proto as it is.

client
dev tun
proto udp
remote ^^example.com^^ 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert client.crt
key client.key
remote-cert-tls server
cipher AES-128-CBC
auth SHA1
auth-user-pass
redirect-gateway def1
verb 3

A bit annoying step is being asked for the private key passphrase (in the addition to the username/password pair). Mikrotik doesn’t allow export without it but fortunately we can use OpenSSL to change that:

openssl.exe rsa -in client.key -out client.key
 Enter pass phrase for client.key: 12345678
 writing RSA key

With this, your VPN connection should work like a charm.

PS: Do not forget to adjust firewall if necessary (TCP port 1194).

/ip firewall filter
add chain=input protocol=udp dst-port=1194 action=accept place-before=0 comment="Allow OpenVPN"

AddSeconds for TimeOnly

One really annoying fact of life you get when dealing with TimeOnly is that class has no AddSeconds method (AddMilliseconds or AddTicks either). But adding that missing functionality is not that hard - enter extension method.

While TimeOnly has no direct methods to add anything below a minute resolution, it does allow for creating a new instance using 100 ns ticks. So we might as well use it.

public static class TimeOnlyExtensions
{
    public static TimeOnly AddSeconds(this TimeOnly time, double seconds)
    {
        var ticks = (long)(seconds * 10000000 + (seconds >= 0 ? 0.5 : -0.5));
        return AddTicks(time, ticks);
    }

    public static TimeOnly AddMilliseconds(this TimeOnly time, int milliseconds)
    {
        var ticks = (long)(milliseconds * 10000 + (milliseconds >= 0 ? 0.5 : -0.5));
        return AddTicks(time, ticks);
    }

    public static TimeOnly AddTicks(this TimeOnly time, long ticks)
    {
        return new TimeOnly(time.Ticks + ticks);
    }
}

With one simple using static TimeOnlyExtensions; we get to correct this oversight. Hopefully this won’t be needed in the future. While easy enough, it’s annoying that such obvious methods are missing.

Endianness Fun in C#

Those doing parsing of network protocols will be familiar with BitConverter. For example, to read an integer that’s written in big-endian (aka network) order, one could write something like this:

Array.Reverse(buffer, offset, 4);
Console.WriteLine(BitConverter.ToInt32(buffer, offset));

If one is dealing with multiplatform code, they could even go with a slightly smarter code:

if (BitConverter.IsLittleEndian) { Array.Reverse(buffer, offset, 4); }
Console.WriteLine(BitConverter.ToInt32(buffer, offset));

However, that’s the old-style way of doing things. For a while now, .NET also offers BinaryPrimitives class in System.Buffers.Binary namespace. While this class was originally designed to be used with protocol buffers (which explains the namespace), there is no reason why you couldn’t use it anywhere else. Actually, considering how versatile the class is, I am stunned they didn’t just add it in the System namespace.

In any case, reading our 32-bit number is now as easy as:

Console.WriteLine(BinaryPrimitives.ReadInt32BigEndian(buffer));

And yes, this doesn’t accept offset argument. Boo-hoo! Just use Span and slice it.

var span = new ReadOnlySpan<byte>(buffer);
Console.WriteLine(BinaryPrimitives.ReadInt32BigEndian(span.Slice(offset)));

Much easier than remembering if you need to reverse array or not. :)

Calculating Mean for Microchip PIC

Illustration

Averaging ADC readings on microcontroller is often needed but also often quite problematic. The easiest way to do it is remembering the last N values, summing them together and simply dividing by count. Simple, reliable, and uses a BUNCH of memory. If you want to store 50 16-bit values, you will need 100 bytes. When dealing with Microchip PIC, that’s a fortune.

However, if you’re ok with a mean rather with an average, there’s an easier way (and I would argue that mean is a better measure anyways in 90% of cases). You can use Welford’s online algorithm to get mean value using only 3 bytes of static storage and a few bytes more when executing. Of course, use of such algorithm in microcontroller environment will require a bit of a trade off (the least of them is me using average and mean interchangeably further down in post).

For my case, I assumed I need it for unsigned 10-bit ADC values. In the context of Walford algorithm, this means my intermediate values can be up to 15-bits (16-bit signed integer is needed internally) and this leaves 5 bits to use as a fixed decimal point. My case also called just “averaging” a handful of values, so I was ok with a 8-bit counter.

This means my variables are as follows:

uint8_t walfordCount = 0;
int16_t walfordMeanEx = 0;

As compared to the standard Walford’s algorithm, a few things are different. First of all, if we have more than N entries, it will reduce count to half over and over again. This trickery is allowing for “sorta” running average. And yes, you shouldn’t really use Walford’s for running average but it’s close enough. Secondly, when adding the value into the internal variable, we convert it to the signed integer and shift it 5-bits to simulate a decimal point. The only expensive (in CPU terms) operation here is dividing by count as everything else is quite cheap (shift, subtractions, and comparisons).

void walford_add(uint16_t value) {
    walfordCount += 1;
    if (walfordCount &gt; WALFORD_CUTOFF) { walfordCount &gt;&gt;= 1; }
    int16_t delta = (int16_t)(value &lt;&lt; FIXED_DECIMAL) - walfordMeanEx;
    walfordMeanEx += delta / walfordCount;
}

To get a value out, we just shift variable 5-bits back to remove the “decimal point”.

uint16_t walford_getMean(void) {
    int16_t value = (walfordMeanEx &gt;&gt; FIXED_DECIMAL);
    return (value &gt; 0) ? (uint16_t)value : 0;
}

And that’s it. It’s a cheap (memory-wise) way to get an average on a 8-bit microcontrollers. As compromises go, I had worse.

In any case, you can download code here.

Disk Preclear

Illustration

Some time ago I got into a habit of shucking. And each shucked drive I first fill with a random data. Purpose of this is twofold; the primary reason being security as disk filled with random data doesn’t “leak” information when you place encrypted file system on it (except when that file system is ZFS with native encryption but that’s a long rant for some other time).

The second reason is drive verification before I add it to my pool. While disks are hearty bunch these days, they do fail. And they usually fail either really early or really late. Giving it a bit of exercise early in their lifetime can sometime provoke failure. And if that failure happens you can easily do RMA. Doing the warranty replacement is also possible once they are shucked but might involve messing with manufacturer and their idea of “intended purpose” that’s part of their terms of warranty. It’s easier this way.

Previously I used a good old dd on my server for the same purpose but that actually became slightly annoying as I filled all the bays. Just to initialize a new drive, I had to remove the old drive and hope other disks are ok while I randomize the new one (can take a few days). Furthermore, that also meant I was increasing the CPU load (remember random numbers are not cheap) and temperature without writing any useful data. A task that’s more suitable for my Windows desktop.

With .NET 6 out, I figured I could give it a ride and write a Windows Forms application.

In a default state application will randomly traverse disk writing random data and verifying what’s written. While access might be random, every sector is accessed once and only once even in this mode. This is my favorite mode by far and I find it strangely calming due to how it looks graphically. But then again, I was always a fan of just looking into the defragmentation screen. :)

If you want the same effect you would get using dd with urandom you can switch to the sequential access. The same random data but written quite a bit faster since drive doesn’t need to seek all over the place. Of course, if security is not much of a concern, once could also fill disk with zeros or a “standard” 0x55AA pattern. While slightly faster, those modes are quite a bit less exciting.

I created this application to fill the need I had and no other application seemed to do exactly what I wanted. If someone else finds it useful, it’s awesome. If not, I still had fun creating it and that’s all that matters in the end.

Application download and link to its source code is available here.