Achieving anything new this week has been rather hampered by (1) my
decision to try out XMonad, and then (2) the kids all picked up some
kind of vomiting bug. I do not intend you to infer any connection.
(XMonad: dunno yet, haven't really tried using it for long enough.
The mouse pointer is impossibly small and I'm going to have to fix
that sooner or later, but I only need it for gui apps and all I've
really tried using thus far is emacs and rxvt)
But let's see if I can explain why I've been hung up on switches
lately. If you've ever wanted to know why your OpenWRT router has
network interfaces with names like eth0.1 (no, it's not a misguided
decision to do semantic versioning on them)
maybe this is for you.
First, you need to know about VLANs. A VLAN is a "virtual LAN": a way
to multiplex traffic for multiple independent LANs onto the same
cable. In the picture on your right, VLAN 5 connects ports 1 and 3 on
switch A with ports 2 and 4 on switch B, and VLAN 6 connects port 2 on
A with 3 on B.
The switches do this by "tagging" the packets (frames) according to
per-port rules. If alice sends to fred, her frames will be tagged
with VLAN ID 5 when they enter switch A, sent (with the tag intact) to
switch B, and then untagged again as they are sent out of port 3 to
fred. Long story short -
frames from end-user devices get tagged
frames to end-user devices get detagged
frames between VLAN-aware devices (usually, one switch and another)
have had tags applied already and are transmitted without change
This is super useful, I have no doubt, if you're running an enterprise
network and need to keep devices separated without having to run
multiple sets of cables to every desk. But how is it relevant to *WRT? Because the hardware you're running it on, despite any
impressions you might have had from its inclusion of two or five or
eight RJ45 sockets, quite likely only has one ethernet device that
Linux can see. This device is connected (internally, inside the SoC)
to a builtin switch, which is also connected to all the sockets you
can see. So if you want to address them separately - for example, you
want to connect your upstream connection to one of them without giving
it full access to your LAN and vice versa - you do it with VLAN
configuration.
Make the switch
Set the upstream port to tag VLAN 1 and the LAN ports to tag VLAN 2,
and the "CPU port" (the one connected to the eth0 device that Linux
sees) to allow VLANs 1 and 2 but not to tag/untag either - i.e. it
receives tagged packets and particapates in the VLAN as if it were
another switch.
We would do this with swconfig but we haven't yet because this turns
out to be the default configuration anyway, at least on the MT300A.
(I have no idea whether it's hardware or u-boot or the devicetree
config that makes it be this way - at some point I dare say I will
find out though)
Configure Linux
So we have a switch on
the SoC which is sending VLAN tagged frames to the ethernet interface
- we need to tell Linux to expect them. (You might say it needs
configuring to serve VLAN).
ip link add link eth0 name eth0.1 type vlan id 1
ip link add link eth0 name eth0.2 type vlan id 2
ip addr add 192.168.0.251/24 dev eth0.2
ip link set dev eth0 up
and now for most purposes you can treat eth0.1 and eth0.2 as
though you have two network interfaces.
I'd like to close by saying "Simples!" but when I googled that term I
learned of the existence of
contemporary mereology as a field of
philosophical study, and I am not sure anything can ever be simple
again.
worked out a possibly slightly icky way to define one-shot services with monit - monit check process expects to read a pidfile and verify that the corresponding process exists, which is not a good match for a configuration script that runs only once and then exits. So instead we create a *.stamp file in /run after running the script and use check file to see if we've been here.
devised some moderately sensible data structures and code to specify network interfaces and stuff.
added a DHCP client . Which in principle was dead easy but in practice would have been even easier if I'd realised earlier that udhcpcpdpdc (sorry, still haven't learned how to stop spelling that one) ...d requires its config script to have the executable bit set - it fails silently if it's not.
What's left for Milestone 0? Nothing really, apart from cleanup.
There is a shockingly large amount of "arbitrary" involved in deciding
what goes in which nix file, and there really ought to be some kind of
principled separation of what/where/how (what the app does, the
specifics of the hardware where it runs, and the implementation
details), and there are other cleanups to do too. Also need to do
something like overlays so that the packages defined in nixwrt
override the ones in nixpkgs. Also also need to update it to work
with nixpkgs master - or at least with 18.03 without my random
patches. Also also also add e2fsutils so we don't have to plug the
USB disk in somewhere else just to fsck it.
In other news, if you can read this then you are looking at my new shell host and the DNS for ww.telent.net has updated. Still haven't moved everything off the old one, but this is just one more step (and a large one) along the way.
My backup server runs! Since last week I plugged in my actual USB
external drive and added a DHCP client, USB support, ext[234]fs and a
few more useful busybox apps - cp, chmod, chown, that kind of
thing. Now it's running and my home desktop is backing up to it four
times a day.
Because the Nix store is world-readable, I didn't want to put passwords in it - so instead I chuck them in files elsewhere and read them at script exection time.
To get duplicity running in NixOS I added this stuff to my configuration.nix
This blog entry is super-short because after writing a much longer one
I extracted all the interesting bits and turned them into an FAQ document in the
Git repo. So go and read that now.
There has been no nixwrt update this week because no nixwrt changes
this week. I've done a little bit of refactoring though nothing really
worth writing about - mostly I've been treating the last week or two
as a nixbreak.
Instead I've been learning some cryptography and some Haskell, by
means of trying to write programs that do one, using the
other. Specifically, the Cryptopals challenges . Lessons:
Give me docs or give me death, I am undecided which
I have not yet reached the point of "I have type signatures, why do I need docs?". I need docs,
preferably with examples. Crypto libraries, I am looking at you specifically here.
In the interests of being vaguely constructive in this criticism, here
is what you do to get AES128-ECB decryption with whatever the default
crypto that ships with GHC 8.2.2 is.
(Note that, as far as I understand it, there is no reason ever to
use ECB mode except in educational examples which will demonstrate to
you just how awful a choice it is)
import qualified Crypto.Cipher.AES as AES
decode' keytext payload =
let key = AES.initAES keytext
in AES.decryptECB key payload
It accepts ByteString arguments. There are probably good ways to
get ByteStrings that I haven't found yet, but if you start with an ordinary string you could try
See above. There seem to be an awful lot of incompatible ways to
represent "sequences of small integers that map onto ASCII characters"
in Haskell. At the moment I'm using arrays of Word8 as my "primary"
represnetation and converting to and from other formats when I need to
do so to call library functions and stuff.
"Stop, collaborate and listen"
They're not messing about when they say "an appreciation for
early-90's MTV hip-hop can't hurt either". A lot of the examples are a
bit ... Vanilla?
I have implemented a plausible AES-CBC mode (using ECB as a building
block), currently working on challenge 11.
It's taken more than a little while to get NixWRT back into the brain
again, partly due to needing a change and partly due to work/family
stuff. Currently in progress: breaking the backuphost.nix monolith
into composable modules, such that I (or a user, when in the fulness
of time the project acquires any users) can add the various parts
separately.
This was prompted by a question on the NixOS Discourse
site
- and the current status, which is experimental, is that a module is a function which gets some options and a pointer to nixpkgs, then operates on the big configuration attrset and returns an augmented version of it. That's handwaving, so let me show you bits of it: in modules/default.nix we have for example
This differs from the NixOS module system because we pass per-module
config to the module itself instead of having it root around in one
big configuration attrset. (In theory this means we could have two
modules with the same name but differently customised, which might be
useful). But it's also dependent on the order that the module
functions are applied in, which makes me feel it would be nice to do
some kind of fixpoint thing the way that overlays do. Or at least to
find some nice way to have modules add their configuration to busybox
and for them to be able to refer to busybox and for it to be the
same busybox derivation used by all modules. The same consideration
may very well apply to the kernel.
Anyway, it may be that I need to spend a bit more time on this before
I understand why NixOS modules do it differently and I should switch
to their approach, or it may be that my requirements are not those of
NixOS.
In other news I have bought a proper USB TTL serial cable, so next
time I crack open a new piece of router hardware I can plug it
straight into a USB port on my desktop system instead of needing a
Raspberry Pi as an sshable serial console. The next target is a
TrendNET 712BR
and I may have to think a bit about how to develop on it because
unlike the MT300A or even the Yun it doesn't have an awful lot of
spare RAM. Will have to think of something after I've found out
how lobotomised its u-boot is or isn't.