diary at Telent Netowrks

Configuring Homeplug from Linux#

Thu, 23 May 2019 22:42:08 +0000

The new house doesn't have structured cabling, and I won't be doing anything to address that until we start work on the extension. In the meantime, therefore, we're using Homeplug AV (networking over the power line). I had two plugs, I needed a third so I bought the cheapest one I could find on Ebay.

When it turned up it had no buttons - which made it a little difficult to add to the existing network. Usually you press the "pair" button on the new plug and then on the existing plug and count to ten and wave a dead chicken around[*] and do a little dance, but that doesn't work when there is no button. Here's how to do it, assuming (1) it's an Intellon chipset, and (2) you have a Linux or other Unix-like box with an ethernet adaptor to plug it into.

1. You need faifa . To build it, you need libpcap and libevent: here's a quick and hacky Nix derivation .

2. Pick 8 random bytes to use for your encryption key. I did dd bs=8 count=1 if=/dev/random |od -x. This is a shared secret which you will need to install on all your adaptors, so probably you should save it somewhere until you're done. Note: the faifa source code seems to suggest that the key you provide is further hashed before being used, so maybe any amount of random stuff is OK here and there's no need to be precious about the format.

3. For each adaptor you want to configure, you will need its MAC address. Hopefully this is printed somewhere on the device itself: I don't know how to get it programmatically.

4. Plug the first adaptor into the mains and attach it to the machine where you built faifa. Now run it

$ sudo faifa -i enp0s31f6 -m

5. You should get a long list of "supported frames" which I will assume are about as meaningful to you as they were to me, followed by a prompt to choose one. If you were to choose, for example, a000 (which is "Get Device/SW Version Request), it might respond something like this:

Choose the frame type (Ctrl-C to exit): 0xa000
Frame: Get Device/SW Version Request (0xA000)

Dump: Frame: Get Device/SW Version Confirm (A001), HomePlug-AV Version: 1.0 Status: Success Device ID: INT6400, Version: INT6000-MAC-4-1-4102-00-3679-20090724-FINAL-C, upgradeable: 0

6. To set the encryption key, the frame type is 0xa050 (Set Encryption Key Request). It prompts you further for "local or remote" (I have no idea what this means, but "local" worked), for the key itself, and for the MAC address.

Choose the frame type (Ctrl-C to exit): 0xa050
Frame: Set Encryption Key Request (0xA050)
Local or distant setting ?
0: distant
1: local
1
AES NMK key ?0001020304050607
Destination MAC address ?b0:48:7a:b9:00:00

Dump: Frame: Set Encryption Key Confirm (A051), HomePlug-AV Version: 1.0 Status: Success

7. Repeat for the next adapter, until you have done them all.

I make no claim that this is correct, but it seems to work for me, and now I can plug my new Odroid C2 (I'll write about that another time, but there's little to say so far, it just runs Kodi) into the TV without the use of a 5m HDMI cable.

[*] make sure it's dead. Waving a live chicken is stressful for all involved.

NixWRT next words#

Mon, 06 May 2019 10:02:50 +0000

Happy New Year!

Uh, yeah. I moved house quite recently and am only just starting to get back on top of stuff. Without further ado:

Things I have done

Some forward progress and some sideways movement. The forward progress is that I have been able to bring up WiFi on the GL-MT300A which was until recently my primary domestic router. I don't know how fast, reliable or stable it is, but judging by the number of OpenWRT patches I haven't applied, I suspect the answer is "not very, yet".

The sideways movement is that the top-level configs (backuphost.nix, wap.nix etc) are moved into the examples/ subdirectory and I have ceased pretending they're device-independent, because in practice they turn out not to be. Building abstraction layers over things like switch vlan settings is a distraction from building the things themselves - which is not to deny its importance, just to say that i don't want always to be having to do both at once.

Note that as of the time I write this, the repo is still in a state of some flux: the only thing that has a hope of building/booting is `defaultroute.nix` and that doesn't even work for use as a router as I haven't even tried to configure pppoe yet.

Things I have learned

The GL-MT300A is based on the Mediatek MTK7620A SoC (hopefully this means the hard parts are also done for the MT300Nv2, based on the very similar MTK7628NN - when I eventually find it again after moving house, I will try it). The Linux driver for this is rt2x00 but verifying this is correct/current/best was a long process because when I first compiled it into my kernel it utterly failed to find any hardware.

This took a certain amount of digging[1] to find out what the problem was. It turns out that although there's a config option for RT2800SOC which is enabled by SOC_MT7620 (which might make you think it's supposed to work) there is no description of this hardware in the upstream mt7620a device tree, so the kernel (reasonably) has no idea that it should be using this code for anything. The fix is two-fold: first, use the much more fully-featured device trees files from OpenWRT instead of upstream, and second, patch the driver so that it advertises a compatible attribute which matches the compatible attribute in the device tree node. It turns out that OpenWRT has done this already as well and now my kernel knows it has wlan hardware and a driver which are compatible with each other.

Well, allegedly compatible.

<6>[    1.310547] ieee80211 phy0: rt2x00_set_rt: Info - RT chipset 6352, rev 0500 detected                
<3>[    1.318533] ieee80211 phy0: rt2800_init_eeprom: Error - Invalid RF chipset 0xbadd detected          
<3>[    1.326981] ieee80211 phy0: rt2x00lib_probe_dev: Error - Failed to allocate device

The fix for this is also in OpenWRT patches: it needs to be told to get the eeprom whereabouts from the device tree, and then it can read the actual eeprom instead of I-have-no-idea-where-it's-getting-that-from and it gets the right RF chipset ID.

One other thing I noticed while doing this is that the OpenWRT patch set here didn't apply cleanly to any kernel version I had previously associated with OpenWRT, so I went looking a bit deeper to find out what they did.

What they did will surprise you! At least, I need to dig further to confirm this absolutely, but on the evidence so far, it's surprising me. Here's how it looks: Not only do they have different kernel minor versions for different target devices (currently 4.9 for atheros devices and 4.14 for everything else ) but they build these kernels to exclude all wireless support, and then build a completely separate out-of-tree module based on a completely different tree to provide the appropriate wifi support modules. See the mac80211 Makefile

I have not replicated this pattern for NixWRT, mostly because I want to see if I can get away without having to. Right now I'm picking patches from that set and applying them selectively to my basic monolithic kernel, and it seems to be working. In this regard, let me briefly tout the last item on my "things I learned" list: filterdiff , a command line tool which manipulates diff files so that you can drop patches applying to files you don't have, rewrite file names, skip hunks and so on, which means I can apply patches cleanly direct from OpenWRT and don't have to maintain patches-on-patches.

Next step: I will probably turn this into a wireless range extender on a separate ssid than my usual, just because everything I read about the reliability of rx200 driver suggests that if I want something stable and performant I should also apply the rest of those (currently inapplicable) patches to fix txpower and other random stuff which I have no idea what it is, and my userbase - the family - won't like dogfooding on my behalf. One of the (many, most irrelevant) changes in the new place is the internet connection: my new ISP sent me a free router with a dual band radio, which is in some ways much nicer than the ones I'm building NixWRT on (came pre-configured with all the IPv6 setup, plus 5GHz band is practically unused around here whereas 2.4G is more congested than the A406 on a Saturday afternoon) and in other ways is doing my head in (weird slow GUI, no real shell, no way to tell which of the 14 devices in the house is saturating the line) but it'll do for the moment. Apparently it's a broadcom chipset so porting NixWRT to it might not be the best possible use of time.

Next next steps, therefore: research dualband router SoCs I would like to port NixWRT to, and also find out how to set up all the IPV6, because native IPv6 is kinda new and kinda fun.

[1] Which is to say, alternately staring at it and googling randomly

Moon on a stick#

Tue, 18 Dec 2018 22:31:25 +0000

Latest in my ever-expanding series of diversions from NixWRT is this foolish attempt to write a Wayland compositor using a language I don't actually understand. I'd add: not for the first time , except that now I check the link I see that was going to be a text editor not a compositor.

Anyway. This time it's a Wayland compositor, using the wlroots library (described by its author as "about 50,000 lines of code you were going to write anyway") and written in Lua. Mostly because when I get a bit further along with it I'm going to integrate Fennel into it, and then with a bit of luck will have a respectable replacement for the once-brilliant but now rather long in the tooth Sawfish . I say "when" but we all know I really mean "if".

At the time of writing it works to the extent that I have a Lua script that can set up the display appropriately then allow me to display a Konsole window into which I can type, and it renders a pointer which moves around in expected and unsurprising ways when I stroke my touchpad. It has no knowledge, though, of window decorations or of stacking order or even of focus or the channeling of pointer button/movement events onto a client.

(Decorations? Yes, I'm with the KDE guys on this one. Requiring clients to render their own window borders and resize themselves on the screen is like a bank requiring customers to track their own account balances and know which market funds their deposits were invested in: how can you ever be sure the app you thought was "Password Wallet Master Key Entry" was actually that and not, I dunno, "I'd like to add you to my professional Botnet" if you don't have some kind of trustable fence around the rectangle it's permitted to draw into. Honour system?)

Some brief notes on how I'm doing it, for the benefit of future-me and potential benefit of present-other. I'm using LuaJIT: not for speed but because the FFI can parse (most) C header files and there's a lot of FFI in this project. So far I have made a fairly literal transcription of the code in Drew DeVault's blog posts parts I-III, and then a rather sketchier interpretation of the code in Input handling in wlroots

Some stuff has changed since the blog posts were written. Here are the differences and/or relevant gotchas that I noted or can remember

Carbon crush saga#

Thu, 08 Nov 2018 00:10:21 +0000

There are a few reasons I've not been blogging much lately, but chief among them is that a month or so ago my laptop was crushed by a taxi, and setting up my blogging environment anywhere else was deemed Too Much Of A Faff.

Happily my contents insurance company eventually agreed to pay me money to replace it, and now I have replaced it, and various kinds of pre-crush activity including blogging and nixwrt hacking may now resume. This entry is mostly just a placeholder and "welcome back" message, but it does contain a link to my presentation at Nixcon 2018

re-frame / I'm gonna live forever#

Wed, 19 Sep 2018 21:18:11 +0000

An unusual - and potentially ill-advised - method and apparatus for the querying of an external database from a re-frame Single Page Application.

Bit of background

Re-frame, is described by its author as "a pattern for writing SPAs in ClojureScript, using Reagent" and "... impressively buzzword-compliant". Both these things are true.

The documentation talks a lot about "dominoes": there is a six step loop that goes around and around.

Then the user clicks on things, and DOM event handlers are called, and the handlers call dispatch, and the loop goes around again.

Signal graph

When you read the example application, the query functions you see are so simple that you might wonder why we even bother with them -

;; 4 lines of code to register a query called :time that returns
;; an app-db key of the same name, what is the point of this?
(rf/reg-sub
  :time
  (fn [db _]     ;; db is current app state. 2nd unused param is query vector
    (:time db))) ;; return a query computation over the application state

but where it gets interesting is that

so we actually have a query graph with the app-db at one end of it and the view functions at the other end, and only the relevant bits of it are computed when needed. Hold onto that thought

Prior art

The re-frame documentation has an incredibly useful page on Subscribing to external data which describes two sensible ways to do it and warns against a third:

With subscriptions

If we start from the premise that components must always get their data from a subscription handler, the obvious route is to create a subscription handler which kicks off an async request to the external data source, then returns a reaction which wraps some path within the app-db where the results will be found when ready. The async request, when finished, runs a callback function which stores the results in the said path.

Bit of a learning curve here if you've only previously looked at the example app, because you won't have learned what a reaction is. A reaction is a "a macro which wraps some computation (a block of code) and returns a ratom holding the result of that computation" and a ratom (short for react atom) is like a regular clojure atom but with extra gubbins so you can subscribe to it and be told every time it changes.

With events

With a different premise, we get a different answer. Your requirement to look something up externally is most probably driven by some user command which is mapped onto a reframe event. In this approach, the event handler is responsible for getting the data it needs to handle the event, and pushing it into the app-db. Probably this means you have one event to initiate the transfer which dispatches another event when the server eventually responds.

Using React lifecycle methods (don't do it)

Views should be dumb. Don't do this.

A third way (unless it is the fourth way)

What if we said: the external data lookup is notionally a "pure function" (let us suppose that the server gives the same response every time it is provided with the same inputs, and for the moment let us handwave over error conditions), so should sit in the middle of the signal graph somewhere and provide its output as a subscribable value instead of scribbling into app-db.

The reason I started doing this was that I was trying to write an XHR subscription handler per the first approach above which subscribed to app-db so that it could find the query terms, and because it was also writing to app-db, it was triggering itself. Probably I was doing something wrong (it occurs to me now that it should probably have subscribed to a query of only the search term and not to the entirety of app-db) but that thought did not occur to me at the time, so this pushed me into looking for another way.

So let me start by showing some example code, and then I can try explaining it:

(defn get-search-results-from-server [term handler]
  (ajax-request
    {:method          :get
     :uri             "/search"
     :timeout 5000
     :params          {:q term :limit 25}
     :format          (ajax/url-request-format)
     :response-format (ajax/json-response-format {:keywords? true})
     :handler handler}))

(rf/reg-sub-raw :search-result (fn [db _] (let [out (reagent/atom [])] (ratom/run! (let [term (rf/subscribe [:search-term])] (when-not (str/blank? term) (reset! out []) (get-search-results-from-server term (fn [[ok? new-value]] (and ok? (reset! out new-value))))))) (reaction out))))

What are we doing here? The value we want to send on through the signal graph is not returned by anything we call, it's provided as an input to our ajax callback function. This means we can't use the reaction macro, so we have to create a reaction by hand and take care ourselves of updating the value it wraps. To do this we use reagent.ratom/run!, which (to my limited understanding) is kinda sorta half of the reaction macro - like reaction it runs a loop every time the subscriptions change but unlike reaction we have to call reset! on our ratom every time we want to provide a new value downstream.

On the whole I think I like this pattern, at least for services which (we can reasonably pretend) are functional - i.e. they give the same answer every time when called with the same inputs. The external service doesn't put stuff in app-db, which I guess might be an issue if it needs to be merged with other data from other sources or if multiple downstream subscriptions want to use it (but in that case why can't they subscribe to it?) but I haven't/can't see how that hypothetical would become real.

Come on then. Tell me why it doesn't work.