Jekyll2023-12-28T16:55:51+00:00https://umhau.github.io/feed.xmlumhau.github.iohacking journalNetwork Interface Madness2022-10-01T00:00:00+00:002022-10-01T00:00:00+00:00https://umhau.github.io/network-interface-madness<p>Well, this was fun to write. I’m trying to do some server automation; part of the process is automatically figuring out which network interfaces are available, and on which networks. I have a machine with 4+ ports: I really don’t want to go plugging and unplugging each one just to figure out which one is connected where.</p> <p>You might say, <em>“that’s easy! just run ifconfig, get the subnet associated with each interface, and parse the results. Bing, bang, boom, done.”</em> But there’s a few problems with that approach. First among them is that we aren’t assuming the existence of any kind of L3 network. In that case, how do you know which of your interfaces are on the same switch? What if the machine is on multiple L2 networks? What if you only care about VPN interfaces, or want to ignore wifi? <em>What if there’s no subnet?</em></p> <p>Anyway, that’s what I had to deal with.</p> <p>To solve the problem, I wrote a very ugly script. If you don’t like it….well, constructive analysis always welcome. Pull requests better still. To use it, specify the “types” of interfaces you want to analyze and about how much time it should take.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iface-groups <span class="nt">-i</span> eth <span class="nt">-t</span> 3 </code></pre></div></div> <p>The output is a little strange. The problem with the data involved is that it’s inherently 2-dimensional; I want to know which interfaces are grouped together. That doesn’t lend itself well to a linear output. So, on every line sent to STDOUT, I’m printing a space-separated list of all interfaces that are connected on the same L2 network. For example:</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>iface-groups <span class="nt">-i</span> eth <span class="nt">-t</span> 3 eth0 eth2 eth1 eth3 eth4 </code></pre></div></div> <p>You can parse this pretty well in a <code class="language-plaintext highlighter-rouge">while read</code> loop, like so:</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iface-groups <span class="nt">-i</span> eth <span class="nt">-t</span> 3 | <span class="k">while </span><span class="nb">read </span>group <span class="k">do </span><span class="nb">echo</span> <span class="s2">"these interfaces can see only each other: </span><span class="nv">$group</span><span class="s2">"</span> <span class="k">done</span> </code></pre></div></div> <p>Finally, you may be wondering about the available interface types. Well, you can read the script, or I’ll just list them out below. You can guess which is which, because I really don’t feel like specifying them right now. I just wrote this whole thing, gosh darn it!</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Available interface types: eth, wlan, bridge, vlan, bond, tap, dummy, ib, ibchild, ppp tipip, ip6tnl, lo, sit, gre, irda, wlan_aux, tun, isdn, mip6mnha </code></pre></div></div> <p>Anyway, that’s all a nice little prelude to actually dumping the script here. I got no idea what the license is; the type identification was pulled from stack overflow and the original source link is dead. So whatever. If you want an updated version, you’ll have to hunt through my github repos: I probably won’t update this post, and the project it’s for is still under wraps. You’ll have to check back in a few years when it’s finally presentable.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span> <span class="c"># written by me on October 1, 2022. Do what you will, for I have already won.</span> <span class="c"># enumerates the properties of each network interface</span> <span class="c"># eventually, it may be possible to assign rules on a per-network basis</span> <span class="c"># i.e., all ifaces on 10.111.13/24 should request DHCP</span> <span class="c"># unless, that's better done as a set of logic outside this program</span> <span class="c"># we need to know:</span> <span class="c"># - which interfaces are wired</span> <span class="c"># - which interfaces are connected</span> <span class="c"># - which interfaces are on the same L2 network</span> <span class="c"># ASSUMPTION: one interface is NOT connected to multiple L2 switches</span> <span class="nb">set</span> <span class="nt">-e</span> <span class="nb">help</span> <span class="o">()</span> <span class="o">{</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"usage:"</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\t</span><span class="s2">-i , --ifacetype=[TYPE]"</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\t</span><span class="s2">-t , --timelimit=[seconds]"</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"Multiple interface types may be specified, like so:"</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\t</span><span class="s2">iface-groups -i eth -i tun -t 5"</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"Available interface types:"</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\t</span><span class="s2">eth, wlan, bridge, vlan, bond, tap, dummy, ib, ibchild, ppp,"</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\t</span><span class="s2">ipip, ip6tnl, lo, sit, gre, irda, wlan_aux, tun, isdn, mip6mnha"</span> <span class="o">}</span> <span class="nv">timelimit</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="p">;</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$timelimit</span><span class="s2">"</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">timelimit</span><span class="o">=</span><span class="s1">'4'</span> <span class="nv">arp_period</span><span class="o">=</span><span class="s1">'1'</span> <span class="nv">ip_address_prefix</span><span class="o">=</span><span class="s1">'203.0.113'</span> <span class="c"># +----------------------+----------------------------+</span> <span class="c"># | Attribute | Value |</span> <span class="c"># +----------------------+----------------------------+</span> <span class="c"># | Address Block | 203.0.113.0/24 |</span> <span class="c"># | Name | Documentation (TEST-NET-3) |</span> <span class="c"># | RFC | [RFC5737] |</span> <span class="c"># | Allocation Date | January 2010 |</span> <span class="c"># | Termination Date | N/A |</span> <span class="c"># | Source | False |</span> <span class="c"># | Destination | False |</span> <span class="c"># | Forwardable | False |</span> <span class="c"># | Global | False |</span> <span class="c"># | Reserved-by-Protocol | False |</span> <span class="c"># +----------------------+----------------------------+</span> <span class="c"># </span> <span class="c"># Table 14: TEST-NET-3</span> <span class="nv">ifacetype</span><span class="o">=</span>eth <span class="nv">timelimit</span><span class="o">=</span>3 <span class="nv">lopts</span><span class="o">=</span>ifacetype:,timelimit: <span class="nv">sopts</span><span class="o">=</span>i:t: <span class="nv">PARSED</span><span class="o">=</span><span class="si">$(</span>getopt <span class="nt">--options</span><span class="o">=</span><span class="nv">$sopts</span> <span class="nt">--longoptions</span><span class="o">=</span><span class="nv">$lopts</span> <span class="nt">--name</span> <span class="s2">"</span><span class="nv">$0</span><span class="s2">"</span> <span class="nt">--</span> <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span><span class="si">)</span> <span class="nb">eval set</span> <span class="nt">--</span> <span class="s2">"</span><span class="nv">$PARSED</span><span class="s2">"</span> <span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do case</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="k">in</span> <span class="nt">-i</span><span class="p">|</span><span class="nt">--ifacetype</span><span class="p">)</span> <span class="nv">allowed_iface_types</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">allowed_iface_types</span>:+<span class="nv">$allowed_iface_types</span><span class="p"> </span><span class="k">}</span><span class="s2"> </span><span class="nv">$2</span><span class="s2">"</span> <span class="nb">shift </span>2 <span class="p">;;</span> <span class="nt">-t</span><span class="p">|</span><span class="nt">--timelimit</span><span class="p">)</span> <span class="nv">timelimit</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span> <span class="p">;</span> <span class="nb">shift </span>2 <span class="p">;;</span> <span class="nt">--</span><span class="p">)</span> <span class="nb">shift</span> <span class="p">;</span> <span class="nb">break</span> <span class="p">;;</span> <span class="k">*</span><span class="p">)</span> <span class="nb">help</span> <span class="p">;</span> <span class="nb">exit </span>1 <span class="p">;;</span> <span class="k">esac</span> <span class="k">done</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$allowed_iface_types</span><span class="s2">"</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">help</span> <span class="o">&amp;&amp;</span> <span class="nb">exit </span>1 <span class="nv">tempfile</span><span class="o">=</span>/tmp/iface_enumeration <span class="nv">groupingfolder</span><span class="o">=</span>/tmp/iface_enumeration_groups <span class="nb">rm</span> <span class="nt">-rf</span> <span class="nv">$tempfile</span> <span class="nv">$groupingfolder</span> <span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$tempfile</span> <span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$groupingfolder</span> <span class="k">if</span> <span class="o">!</span> <span class="nb">command</span> <span class="nt">-v</span> tcpdump &amp;&gt;/dev/null <span class="p">;</span> <span class="k">then </span><span class="nb">echo</span> <span class="s2">"tcpdump required"</span> <span class="p">;</span> <span class="nb">exit </span>2 <span class="p">;</span> <span class="k">fi </span><span class="nb">sudo</span> <span class="nt">-v</span> get_iface_type <span class="o">()</span> <span class="o">{</span> <span class="c"># function came from a dead link via stackoverflow</span> <span class="c"># http://gitorious.org/opensuse/sysconfig/blobs/master/scripts/functions</span> <span class="nb">local </span><span class="nv">IF</span><span class="o">=</span><span class="nv">$1</span> TYPE <span class="nb">test</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$IF</span><span class="s2">"</span> <span class="o">||</span> <span class="k">return </span>1 <span class="nb">test</span> <span class="nt">-d</span> /sys/class/net/<span class="nv">$IF</span> <span class="o">||</span> <span class="k">return </span>2 <span class="k">case</span> <span class="s2">"</span><span class="sb">`</span><span class="nb">cat</span> /sys/class/net/<span class="nv">$IF</span>/type<span class="sb">`</span><span class="s2">"</span> <span class="k">in </span>1<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>eth <span class="c"># Ethernet, may also be wireless, ...</span> <span class="k">if </span><span class="nb">test</span> <span class="nt">-d</span> /sys/class/net/<span class="nv">$IF</span>/wireless <span class="nt">-o</span> <span class="se">\</span> <span class="nt">-L</span> /sys/class/net/<span class="nv">$IF</span>/phy80211 <span class="p">;</span> <span class="k">then </span><span class="nv">TYPE</span><span class="o">=</span>wlan <span class="k">elif </span><span class="nb">test</span> <span class="nt">-d</span> /sys/class/net/<span class="nv">$IF</span>/bridge <span class="p">;</span> <span class="k">then </span><span class="nv">TYPE</span><span class="o">=</span>bridge <span class="k">elif </span><span class="nb">test</span> <span class="nt">-f</span> /proc/net/vlan/<span class="nv">$IF</span> <span class="p">;</span> <span class="k">then </span><span class="nv">TYPE</span><span class="o">=</span>vlan <span class="k">elif </span><span class="nb">test</span> <span class="nt">-d</span> /sys/class/net/<span class="nv">$IF</span>/bonding <span class="p">;</span> <span class="k">then </span><span class="nv">TYPE</span><span class="o">=</span>bond <span class="k">elif </span><span class="nb">test</span> <span class="nt">-f</span> /sys/class/net/<span class="nv">$IF</span>/tun_flags <span class="p">;</span> <span class="k">then </span><span class="nv">TYPE</span><span class="o">=</span>tap <span class="k">elif </span><span class="nb">test</span> <span class="nt">-d</span> /sys/devices/virtual/net/<span class="nv">$IF</span> <span class="p">;</span> <span class="k">then case</span> <span class="nv">$IF</span> <span class="k">in</span> <span class="o">(</span>dummy<span class="k">*</span><span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>dummy <span class="p">;;</span> <span class="k">esac</span> <span class="k">fi</span> <span class="p">;;</span> 24<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>eth <span class="p">;;</span> <span class="c"># firewire ;; # IEEE 1394 IPv4 - RFC 2734</span> 32<span class="p">)</span> <span class="c"># InfiniBand</span> <span class="k">if </span><span class="nb">test</span> <span class="nt">-d</span> /sys/class/net/<span class="nv">$IF</span>/bonding <span class="p">;</span> <span class="k">then </span><span class="nv">TYPE</span><span class="o">=</span>bond <span class="k">elif </span><span class="nb">test</span> <span class="nt">-d</span> /sys/class/net/<span class="nv">$IF</span>/create_child <span class="p">;</span> <span class="k">then </span><span class="nv">TYPE</span><span class="o">=</span>ib <span class="k">else </span><span class="nv">TYPE</span><span class="o">=</span>ibchild <span class="k">fi</span> <span class="p">;;</span> 512<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>ppp <span class="p">;;</span> 768<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>ipip <span class="p">;;</span> <span class="c"># IPIP tunnel</span> 769<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>ip6tnl <span class="p">;;</span> <span class="c"># IP6IP6 tunnel</span> 772<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>lo <span class="p">;;</span> 776<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>sit <span class="p">;;</span> <span class="c"># sit0 device - IPv6-in-IPv4</span> 778<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>gre <span class="p">;;</span> <span class="c"># GRE over IP</span> 783<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>irda <span class="p">;;</span> <span class="c"># Linux-IrDA</span> 801<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>wlan_aux <span class="p">;;</span> 65534<span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>tun <span class="p">;;</span> <span class="k">esac</span> <span class="c"># The following case statement still has to be replaced by something</span> <span class="c"># which does not rely on the interface names.</span> <span class="k">case</span> <span class="nv">$IF</span> <span class="k">in </span>ippp<span class="k">*</span><span class="p">|</span>isdn<span class="k">*</span><span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>isdn<span class="p">;;</span> mip6mnha<span class="k">*</span><span class="p">)</span> <span class="nv">TYPE</span><span class="o">=</span>mip6mnha<span class="p">;;</span> <span class="k">esac</span> <span class="nb">test</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$TYPE</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="nv">$TYPE</span> <span class="o">&amp;&amp;</span> <span class="k">return </span>0 <span class="k">return </span>3 <span class="o">}</span> <span class="c"># for every interface on the system</span> <span class="k">for </span>interface <span class="k">in</span> /sys/class/net/<span class="k">*</span> <span class="k">do </span><span class="nv">interface</span><span class="o">=</span><span class="si">$(</span><span class="nb">basename</span> <span class="nv">$interface</span><span class="si">)</span> <span class="c"># figure out what it is: wifi? ethernet? virtual bridge? infiniband?</span> <span class="nv">iface_type</span><span class="o">=</span><span class="si">$(</span>get_iface_type <span class="nv">$interface</span><span class="si">)</span> <span class="c"># for each of the useful interface types, specified above (or in an arg)</span> <span class="k">for </span>allowed_iface_type <span class="k">in</span> <span class="k">${</span><span class="nv">allowed_iface_types</span><span class="p">[@]</span><span class="k">}</span> <span class="k">do</span> <span class="c"># if the interface we're looking at is one of those allowed</span> <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$iface_type</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"</span><span class="nv">$allowed_iface_type</span><span class="s2">"</span> <span class="o">]</span> <span class="k">then</span> <span class="c"># if the interface is connected to something: switch, router, etc</span> <span class="k">if </span>tcpdump <span class="nt">--list-interfaces</span> | <span class="nb">grep</span> <span class="nv">$interface</span> | <span class="nb">grep</span> <span class="nt">-q</span> <span class="s1">'Running'</span> <span class="k">then</span> <span class="c"># add it do the list of viable interfaces</span> <span class="nv">viable_ifaces</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">viable_ifaces</span>:+<span class="nv">$viable_ifaces</span><span class="p"> </span><span class="k">}</span><span class="s2"> </span><span class="nv">$interface</span><span class="s2">"</span> <span class="k">fi</span> <span class="c"># don't check more types after a match: "there can be only one"</span> <span class="nb">break </span><span class="k">fi done done</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="k">${</span><span class="nv">viable_ifaces</span><span class="p">[@]</span><span class="k">}</span><span class="s2">"</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"no viable interfaces discovered"</span> <span class="o">&amp;&amp;</span> <span class="nb">exit </span>1 <span class="nv">ip_iterator</span><span class="o">=</span>0 <span class="nb">declare</span> <span class="nt">-A</span> iface_ip <span class="nv">starttime</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%s<span class="si">)</span> <span class="c"># for each viable interface we found</span> <span class="k">for </span>interface <span class="k">in</span> <span class="k">${</span><span class="nv">viable_ifaces</span><span class="p">[@]</span><span class="k">}</span> <span class="k">do</span> <span class="c"># create a unique, unused ip address (taken from TEST-NET-3, RFC 6890)</span> <span class="nv">ip_iterator</span><span class="o">=</span><span class="k">$((</span>ip_iterator+1<span class="k">))</span> <span class="nv">ip_address</span><span class="o">=</span><span class="s2">"</span><span class="nv">$ip_address_prefix</span><span class="s2">.</span><span class="nv">$ip_iterator</span><span class="s2">"</span> <span class="c"># associate that ip address with the current interface</span> iface_ip+<span class="o">=([</span><span class="s2">"</span><span class="nv">$ip_address</span><span class="s2">"</span><span class="o">]=</span><span class="s2">"</span><span class="nv">$interface</span><span class="s2">"</span><span class="o">)</span> <span class="c"># start listening on that interface for ARP requests</span> <span class="nb">sudo timeout</span> <span class="nv">$timelimit</span> tcpdump <span class="nt">--immediate-mode</span> <span class="nt">-i</span> <span class="nv">$interface</span> arp <span class="o">&gt;</span> <span class="nv">$tempfile</span>/<span class="nv">$interface</span> 2&gt;/dev/null &amp; <span class="c"># start sending ARP requests for the unique IP address on that interface</span> <span class="nb">sudo </span>arping <span class="nt">-I</span> <span class="nv">$interface</span> <span class="nt">-w</span> <span class="nv">$timelimit</span> <span class="nv">$ip_address</span> &amp;&gt;/dev/null &amp; <span class="k">done</span> <span class="c"># total time it took to start the listeners and senders</span> <span class="nv">endtime</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%s<span class="si">)</span> <span class="nv">initializationtime</span><span class="o">=</span><span class="k">$((</span> <span class="nv">$endtime</span> <span class="o">-</span> <span class="nv">$starttime</span> <span class="k">))</span> <span class="c"># wait for the listeners and senders to finish</span> <span class="nb">sleep</span> <span class="k">$((</span><span class="nv">$timelimit</span> <span class="o">+</span> <span class="nv">$initializationtime</span> <span class="o">+</span> <span class="m">1</span><span class="k">))</span> <span class="c"># for each viable interface, get the file with the results from the listener</span> <span class="k">for </span>ifacefile <span class="k">in</span> <span class="nv">$tempfile</span>/<span class="k">*</span> <span class="k">do </span><span class="nv">ifacename</span><span class="o">=</span><span class="si">$(</span><span class="nb">basename</span> <span class="nv">$ifacefile</span><span class="si">)</span> <span class="c"># if the iface is not already in a group of ifaces that can see each other</span> <span class="k">if</span> <span class="o">!</span> <span class="nb">grep</span> <span class="nt">-q</span> <span class="nv">$ifacename</span> <span class="nv">$groupingfolder</span>/<span class="k">*</span> 2&gt;/dev/null <span class="k">then</span> <span class="c"># create a new group with just that interface</span> <span class="nb">echo</span> <span class="nv">$ifacename</span> <span class="o">&gt;</span> <span class="nv">$groupingfolder</span>/<span class="si">$(</span><span class="nb">date</span> +%s%N<span class="si">)</span> <span class="k">fi</span> <span class="c"># for each ARP request seen by this interface, get the IP address requested</span> <span class="nb">grep</span> <span class="nv">$ip_address_prefix</span> <span class="nv">$ifacefile</span> | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s1">' '</span> <span class="nt">-f</span> 5 | <span class="k">while </span><span class="nb">read </span>ipaddress <span class="k">do</span> <span class="c"># get the group file of the interface that saw the request</span> <span class="nv">groupfile</span><span class="o">=</span><span class="si">$(</span><span class="nb">grep</span> <span class="nt">-l</span> <span class="nv">$ifacename</span> <span class="nv">$groupingfolder</span>/<span class="k">*</span> 2&gt;/dev/null<span class="si">)</span> <span class="c"># convert the ip address to the sender interface and add it to the group</span> <span class="nb">echo</span> <span class="k">${</span><span class="nv">iface_ip</span><span class="p">[</span><span class="nv">$ipaddress</span><span class="p">]</span><span class="k">}</span> <span class="o">&gt;&gt;</span> <span class="nv">$groupfile</span> <span class="k">done done</span> <span class="c"># for each group of interfaces that we collected</span> <span class="k">for </span>group <span class="k">in</span> <span class="nv">$groupingfolder</span>/<span class="k">*</span> <span class="k">do</span> <span class="c"># filter out duplicate interfaces within the group and print to stdout </span> <span class="nb">echo</span> <span class="si">$(</span><span class="nb">sort</span> <span class="nv">$group</span> | <span class="nb">uniq</span> | xargs<span class="si">)</span> <span class="k">done</span> </code></pre></div></div>umhauWell, this was fun to write. I’m trying to do some server automation; part of the process is automatically figuring out which network interfaces are available, and on which networks. I have a machine with 4+ ports: I really don’t want to go plugging and unplugging each one just to figure out which one is connected where.Set up Xen on Alpine Linux2022-09-23T00:00:00+00:002022-09-23T00:00:00+00:00https://umhau.github.io/xen-on-alpine<p>So I’m starting out on this new project. The first iteration is just VMs on a box - but they’re Xen VMs, and they’re on an Alpine box. Later iterations shall be revealed, all in good time. Much work has been done behind the scenes, and much work is yet to be done.</p> <h2 id="session-1-the-host">session 1: the host</h2> <p>Anyway, Xen vms on Alpine. Start with a clean alpine installation, then enable the community repository. Note that I used <a href="https://wiki.alpinelinux.org/wiki/Xen_Dom0#Existing_installation">this</a> source for most of the content here, <strong>but</strong> as written, that source has a couple bugs. i.e., my walkthrough works, and theirs doesn’t - quite.</p> <p>First become root. You should remain root for the duration of this walkthrough.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>su </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /etc/os-release <span class="nv">repository_server</span><span class="o">=</span><span class="s2">"dl-cdn.alpinelinux.org"</span> <span class="nv">alpine_version</span><span class="o">=</span><span class="s2">"v3.16"</span> <span class="nb">echo</span> <span class="s2">"https://</span><span class="nv">$repository_server</span><span class="s2">/alpine/</span><span class="nv">$alpine_version</span><span class="s2">/main"</span> <span class="o">&gt;</span> /etc/apk/repositories </code></pre></div></div> <p>Install the hypervisor, and related packages.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apk add xen xen-hypervisor seabios xen-qemu <span class="c"># ovmf</span> apk add grub grub-bios </code></pre></div></div> <p>Load the necessary kernel modules.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"xen-netback"</span> <span class="o">&gt;&gt;</span> /etc/modules <span class="nb">echo</span> <span class="s2">"xen-blkback"</span> <span class="o">&gt;&gt;</span> /etc/modules <span class="nb">echo</span> <span class="s2">"tun"</span> <span class="o">&gt;&gt;</span> /etc/modules </code></pre></div></div> <p>And add the xen daemons.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rc-update add xenconsoled rc-update add xendomains rc-update add xenqemu rc-update add xenstored </code></pre></div></div> <p>Then reboot; because kernel mods don’t immediately take effect, and those daemons haven’t autostarted yet.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>reboot </code></pre></div></div> <p>Now we get to do the tricky stuff: GRUB modifications. If you recall your Xen, you’ll remember that it’s got a special Dom0 that manages the other VMs on the host. We need to specify some configs for that VM, and the place to do that is in GRUB, before things like “RAM size” are determined.</p> <p>Add the following to the bottom of the grub config. Note that you may want to increase the size of the dom0 RAM; just change <code class="language-plaintext highlighter-rouge">1024M</code> to some larger number.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vim /etc/default/grub <span class="c"># You need to set the amount of RAM to allocate to the Dom0 Alpine install so that</span> <span class="c"># our future virtual machines will have enough memory.</span> <span class="nv">GRUB_CMDLINE_XEN_DEFAULT</span><span class="o">=</span><span class="s2">"dom0_mem=1024M,max:1024M"</span> <span class="nv">GRUB_DEFAULT</span><span class="o">=</span><span class="s2">"saved"</span> <span class="nv">GRUB_SAVEDEFAULT</span><span class="o">=</span><span class="s2">"true"</span> </code></pre></div></div> <p>Then apply the new config.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grub-mkconfig <span class="nt">-o</span> /boot/grub/grub.cfg grub-set-default <span class="s2">"</span><span class="si">$(</span><span class="nb">grep</span> ^menuentry /boot/grub/grub.cfg | <span class="nb">grep </span>Xen | <span class="nb">cut</span> <span class="nt">-d</span> <span class="se">\'</span> <span class="nt">-f</span> 2 | <span class="nb">head</span> <span class="nt">-1</span><span class="si">)</span><span class="s2">"</span> </code></pre></div></div> <blockquote> <p>That sets the default entry in GRUB to the first entry containing ‘Xen’. <strong>Run this every time you upgrade Alpine or Xen.</strong></p> </blockquote> <p>One last thing. Do you plan to use more than 8 VMs? Probably. If so, be sure to increase the maximum number of loop devices.</p> <blockquote> <p>In Alpine Linux, you will need to add the max_loop option to the loop module, then add the loop module to your initramfs.</p> </blockquote> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">touch</span> /etc/modprobe.d/loop.conf <span class="nb">echo</span> <span class="s2">"options loop max_loop=32"</span> <span class="o">&gt;</span> /etc/modprobe.d/loop.conf </code></pre></div></div> <p>Then update the modules list and reboot.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkinitfs reboot </code></pre></div></div> <p>If you want to confirm that the dom0 management vm is running, execute this:</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xl list </code></pre></div></div> <h2 id="session-2-the-guests">session 2: the guests</h2> <p>Alpine docs won’t help so much here. I’m working from the <a href="https://wiki.xenproject.org/wiki/Xen_Project_Beginners_Guide">xenproject</a> now.</p>umhauSo I’m starting out on this new project. The first iteration is just VMs on a box - but they’re Xen VMs, and they’re on an Alpine box. Later iterations shall be revealed, all in good time. Much work has been done behind the scenes, and much work is yet to be done.Notes Regarding the Installation of Linux on a mid-2012 Macbook Air2022-08-25T00:00:00+00:002022-08-25T00:00:00+00:00https://umhau.github.io/void-macbook-air-2012<p>Bought a macbook air recently. It’s an old one, but the hardware’s cheap and with a decent linux install you don’t really notice (except when you’re using firefox).</p> <ul> <li>FreeBSD couldn’t handle the wifi card. I had to compile the kernel with special GPL code, enable specific kernel modules, and even then it only worked randomly. And we all know that random success is far worse than failure.</li> </ul> <p>So back to the loving arms of Void.</p> <ul> <li>When booting, hold the option/alt key down until the boot menu shows up. This lets you pick a USB boot device.</li> </ul> <p>The installation went smoothly - since there’s no ethernet port, you’ll have to either install the full graphical desktop environment, or learn to enable the wifi via the command line. It’s not too hard. But it can be intellectually scary. Create the file below.</p> <h3 id="cli-wifi">cli wifi</h3> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># /etc/wpa_supplicant.conf</span> <span class="nv">ctrl_interface</span><span class="o">=</span>/run/wpa_supplicant <span class="nv">ctrl_interface_group</span><span class="o">=</span>wheel <span class="nv">update_config</span><span class="o">=</span>1 <span class="nv">network</span><span class="o">={</span> <span class="nv">ssid</span><span class="o">=</span><span class="s2">"name of wifi network"</span> <span class="nv">psk</span><span class="o">=</span><span class="s2">"wifi password"</span> <span class="o">}</span> </code></pre></div></div> <p>Then figure out what your wifi interface is. Run this:</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find /sys/class/net <span class="nt">-mindepth</span> 1 <span class="nt">-maxdepth</span> 1 <span class="nt">-lname</span> <span class="s1">'*virtual*'</span> <span class="nt">-prune</span> <span class="nt">-o</span> <span class="nt">-printf</span> <span class="s1">'%f\n'</span> | <span class="k">while </span><span class="nb">read </span>iface <span class="p">;</span> <span class="k">do </span><span class="nb">echo </span>interface: <span class="nv">$iface</span> <span class="k">done</span> </code></pre></div></div> <p>That will output all the network interfaces on your computer. If you’re using a macbook air 2012, and you don’t have a usb ethernet port plugged in, there will be just one output. It’ll probably look like this:</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wlp2s0b1 </code></pre></div></div> <p>Then run the following command to connect to your wifi network. Notice the wif interface. Make sure it matches. Ping google when you’re done, to see if it all worked.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wpa_supplicant <span class="nt">-B</span> <span class="nt">-i</span> wlp2s0b1 <span class="nt">-c</span> /etc/wpa_supplicant.conf </code></pre></div></div> <p>I installed the i3 window manager. Needed a bunch of extra packages. But that’s all easy, and contained in the install script. The problem I had was with the touchpad - the cursor would only move vertically, and only if I swiped side-to-side. Hunted for an hour; finally found the solution. Turns out the kernel was loading the hardware in the wrong order. <a href="https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1586552">This is the bug;</a> looks like they just figured ignoring it would make it go away.</p> <p>All you gotta do is reload the kernel modules and we’re golden. I wrapped it up in a script and run it on boot from <code class="language-plaintext highlighter-rouge">/etc/rc.local</code>.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>modprobe <span class="nt">-r</span> usbmouse modprobe <span class="nt">-r</span> bcm5974 modprobe bcm5974 </code></pre></div></div> <p>Also, if you want to modify the behavior of the mouse, this link should help: https://blog.inagaki.in/en/post/macbook-air-linux-3/</p> <p>After that it’s easy sailing. The keyboard is correctly mapped, so if you have a half-decent i3 config file a lot of things should “just work”.</p>umhauBought a macbook air recently. It’s an old one, but the hardware’s cheap and with a decent linux install you don’t really notice (except when you’re using firefox).Syncthings2022-08-14T00:00:00+00:002022-08-14T00:00:00+00:00https://umhau.github.io/multiple-syncthing-users<p>Syncthing is this cool program that is basically Dropbox Unlimited (tm): everything is only on your own server (except the NAT punching and where-are-you stuff), and your space is limited only by the storage on your devices.</p> <p>I want to set up a syncthing NAS. But, I want to make it available to two separate people, each with their own accounts. And I want to do it on FreeBSD. And I want to manage it remotely.</p> <p>That means we’re also using a program called zerotier. You know how it’s easier to talk to machines that are connected to the local network? This is a tiny program you install on machines around the world, you share a key with each, and then they can all see each other as if they’re on the same local network.</p> <p>I need to put that on the FreeBSD machine as well, so that I can access the management GUI without fragile port forwarding. The goal is that this machine can be plugged in anywhere, turned on, attached to an ethernet cable, and get right down to business.</p> <p>We’re doing this from a fresh FreeBSD installation.</p> <h2 id="installations">installations</h2> <p>The trick to multple syncthing users on a single machine is, literally, multiple users. I thought I’d need jails or some such, but as it turns out, all we need are to create new users. Here, I’m using <code class="language-plaintext highlighter-rouge">usr1</code> and <code class="language-plaintext highlighter-rouge">usr2</code>.</p> <p>I did that during the install process, so I won’t document the manual user creation process here. Go google it if you don’t know how.</p> <p>Become root, if you aren’t already.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>su </code></pre></div></div> <p>Perform the initial system updates. This will take a while.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>freebsd-update fetch freebsd-update <span class="nb">install</span> </code></pre></div></div> <p>Install the packages we need.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pkg <span class="nb">install </span>syncthing zerotier nano tmux vim htop </code></pre></div></div> <p>Do you have a zerotier account? Because you’ll need one. Unless you want to selfhost the sync servers; but that’s a project for another day. <a href="https://www.zerotier.com/download/">Go get one</a>, and set up a network. Then come back.</p> <h2 id="configurations">configurations</h2> <h3 id="cron">cron</h3> <p>Use cron to start two separate instances of syncthing on boot: one owned by user <code class="language-plaintext highlighter-rouge">usr1</code>, and the other owned by user <code class="language-plaintext highlighter-rouge">usr2</code>. Also start zerotier as the root user (which is default, since we’re using the root cron account).</p> <pre><code class="language-cron">su crontab -e @reboot su - usr1 -c /usr/local/bin/syncthing @reboot su - usr2 -c /usr/local/bin/syncthing @reboot zerotier-one -d </code></pre> <p>After we (eventually) reboot, we can use <code class="language-plaintext highlighter-rouge">top</code> (or <code class="language-plaintext highlighter-rouge">htop</code>) to see the two syncthing instances running.</p> <h3 id="zerotier">zerotier</h3> <p>Start zerotier.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zerotier-one <span class="nt">-d</span> </code></pre></div></div> <p>Go get the id for the network you want to join this machine to. If you got yourself a zerotier account, you’ll know what I’m talking about.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zerotier-cli join 012d30m32bkjqqewr </code></pre></div></div> <h3 id="pf">pf</h3> <p>Enable the pf firewall: we need to manually adjust what we’re allowing through the system.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sysrc <span class="nv">pf_enable</span><span class="o">=</span><span class="nb">yes </span>sysrc <span class="nv">pflog_enable</span><span class="o">=</span><span class="nb">yes </span>Then create a new firewall config file. <span class="sb">```</span>shell nano /etc/pf.conf </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ex_if <span class="o">=</span> <span class="s2">"em0"</span> zt_if <span class="o">=</span> <span class="s2">"zta098fdas123ij"</span> <span class="nb">set </span>skip on lo0 scrub <span class="k">in </span>block <span class="k">in </span>pass out pass <span class="k">in </span>on <span class="nv">$ex_if</span> proto tcp from any to <span class="o">(</span><span class="nv">$ex_if</span><span class="o">)</span> port <span class="o">{</span> ssh, domain, http, https, 22001, 22002, 8385, 8386 <span class="o">}</span> pass <span class="k">in </span>on <span class="nv">$zt_if</span> proto tcp from any to <span class="o">(</span><span class="nv">$zt_if</span><span class="o">)</span> port <span class="o">{</span> ssh, domain, http, https, 22001, 22002, 8385, 8386 <span class="o">}</span> pass <span class="k">in </span>on <span class="nv">$ex_if</span> proto udp to <span class="o">(</span><span class="nv">$ex_if</span><span class="o">)</span> port <span class="o">{</span> domain, 22001, 22002, 8385, 8386 <span class="o">}</span> pass <span class="k">in </span>on <span class="nv">$zt_if</span> proto udp to <span class="o">(</span><span class="nv">$zt_if</span><span class="o">)</span> port <span class="o">{</span> domain, 22001, 22002, 8385, 8386 <span class="o">}</span> pass <span class="k">in </span>on <span class="nv">$ex_if</span> proto udp to any port <span class="o">{</span> mdns, 21028, 21029 <span class="o">}</span> pass <span class="k">in </span>on <span class="nv">$zt_if</span> proto udp to any port <span class="o">{</span> mdns, 21028, 21029 <span class="o">}</span> pass <span class="k">in </span>on <span class="nv">$ex_if</span> inet proto icmp to <span class="o">(</span><span class="nv">$ex_if</span><span class="o">)</span> icmp-type <span class="o">{</span> unreach, redir, timex, echoreq <span class="o">}</span> pass <span class="k">in </span>on <span class="nv">$zt_if</span> inet proto icmp to <span class="o">(</span><span class="nv">$zt_if</span><span class="o">)</span> icmp-type <span class="o">{</span> unreach, redir, timex, echoreq <span class="o">}</span> </code></pre></div></div> <p>Note that the <code class="language-plaintext highlighter-rouge">zt_if</code> variable will be different for you - you’ll have to get it by running <code class="language-plaintext highlighter-rouge">ifconfig</code> and seeing what the zerotier interface actually is.</p> <h3 id="syncthing">syncthing</h3> <p>Note that in both examples above, we have opened several custom ports. By default, syncthing wants to use port 8384 to host the GUI and uses port 22000 for everything else (TCP and UDP, in case you know what that means). We need to adjust these, and we’ll have to use the command line.</p> <p>Summary of changes:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>usr1 port 22001 port 8385 port 21028 usr2 port 22002 port 8386 port 21029 GUI 0.0.0.0 </code></pre></div></div> <p>So. Become the user.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>su usr1 </code></pre></div></div> <p>Start syncthing, let it initialize, and then kill it with <code class="language-plaintext highlighter-rouge">CTRL-C</code>.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>syncthing </code></pre></div></div> <p>Open the syncthing config file.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>nano .config/syncthing/config.xml </code></pre></div></div> <p>Now you’ll have to modify several points in this (rather long) file. They will look similar to the sections below. Since we are <code class="language-plaintext highlighter-rouge">usr1</code>, the following are the changes we must make.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>usr1 port 22001 port 8385 port 21028 </code></pre></div></div> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;gui</span> <span class="na">enabled=</span><span class="s">"true"</span> <span class="na">tls=</span><span class="s">"true"</span> <span class="na">debugging=</span><span class="s">"false"</span><span class="nt">&gt;</span> <span class="nt">&lt;address&gt;</span>127.0.0.1:8384<span class="nt">&lt;/address&gt;</span> <span class="nt">&lt;user&gt;</span>usr1<span class="nt">&lt;/user&gt;</span> ⋮ <span class="nt">&lt;/gui&gt;</span> </code></pre></div></div> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;options&gt;</span> <span class="nt">&lt;listenAddress&gt;</span>default<span class="nt">&lt;/listenAddress&gt;</span> ⋮ <span class="nt">&lt;localAnnouncePort&gt;</span>21027<span class="nt">&lt;/localAnnouncePort&gt;</span> <span class="nt">&lt;localAnnounceMCAddr&gt;</span>[ff12::8384]:21027<span class="nt">&lt;/localAnnounceMCAddr&gt;</span> ⋮ <span class="nt">&lt;/options&gt;</span> </code></pre></div></div> <p>Alter the above to match the below.</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;gui</span> <span class="na">enabled=</span><span class="s">"true"</span> <span class="na">tls=</span><span class="s">"true"</span> <span class="na">debugging=</span><span class="s">"false"</span><span class="nt">&gt;</span> <span class="nt">&lt;address&gt;</span>0.0.0.0:8385<span class="nt">&lt;/address&gt;</span> <span class="nt">&lt;user&gt;</span>usr1<span class="nt">&lt;/user&gt;</span> ⋮ <span class="nt">&lt;/gui&gt;</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">0.0.0.0</code> indicates that we want to access the GUI from the rest of the local network; otherwise, we’d have to physically log into that machine running syncthing in order to get to the GUI.</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;options&gt;</span> <span class="nt">&lt;listenAddress&gt;</span>22001<span class="nt">&lt;/listenAddress&gt;</span> ⋮ <span class="nt">&lt;localAnnouncePort&gt;</span>21028<span class="nt">&lt;/localAnnouncePort&gt;</span> <span class="nt">&lt;localAnnounceMCAddr&gt;</span>[ff12::8385]:21028<span class="nt">&lt;/localAnnounceMCAddr&gt;</span> ⋮ <span class="nt">&lt;/options&gt;</span> </code></pre></div></div> <p>Now exit from <code class="language-plaintext highlighter-rouge">usr1</code>, become <code class="language-plaintext highlighter-rouge">usr2</code>, and perform the simlilar set of substitutions with the <code class="language-plaintext highlighter-rouge">usr2</code> settings.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>usr2 port 22002 port 8386 port 21029 </code></pre></div></div> <h2 id="cleanup">cleanup</h2> <p>Get the ip address of the syncthing host.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ifconfig </code></pre></div></div> <p>And reboot. If you did everything correctly, syncthing will automatically startup with the machine. You can access the GUI for each of the syncthing users with the IP address you just obtained, and the GUI port appended:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http://123.456.789.000:8385 http://123.456.789.000:8386 </code></pre></div></div> <p>Not only that, but you can also use the IP address of the machine that’s available through zerotier. That gives you global, secure access. From there you can connect each instance to whatever accounts you want.</p>umhauSyncthing is this cool program that is basically Dropbox Unlimited (tm): everything is only on your own server (except the NAT punching and where-are-you stuff), and your space is limited only by the storage on your devices.Uglify Your Windows Server2022-08-11T00:00:00+00:002022-08-11T00:00:00+00:00https://umhau.github.io/uglify-your-server<p>Sometimes I get confused. It’s not much, very often; a misplaced coffee cup here, a bit of code forgotten there. But sometimes it’s Important, and sometimes there’s a chance to head the chaos off. This is one of those latter things.</p> <p>I have to administer some Windows Servers, and I’ll often do that via remote desktop from my office computer. The problem is, windows is very good at making the experience seamless - which means that on two specific occasions, I’ve forgotten that I’m within the RDP session. I make a quick test, or (worse, much, worse) run a powershell command, and suddenly – the wrong machine is newly different.</p> <p>So what’s the best way to solve human error? Blood. Red blood.</p> <p>Or, lacking that, blood red aesthetics: change the theme of the windows server to be all-red. That way, I’ll never forget where I am. Problem is, you can’t do that in Windows Server:</p> <p><img src="/images/windows/color_and_appearance.png" alt="Just don't." /></p> <p>So, we have to modify the registry directly.</p> <p><em>(What’s the registry? It’s this giant, semi-centralized place where windows stores all the little miscellaneous bits of information that it needs to know about itself. One of those is, ‘what-color-to-make-the-window-borders’.)</em></p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>regedit </code></pre></div></div> <p>Go to the subsection here:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM </code></pre></div></div> <p>Then <code class="language-plaintext highlighter-rouge">modify</code> the entry redundantly titled</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ColorizationColor </code></pre></div></div> <p><img src="/images/windows/server_2012_colorizationcolor_registry.png" alt="Registry modification" /></p> <p>Colors are in HEX format, including alpha channel which is represented by c0. Go <a href="http://hslpicker.com/#cd0404">here</a> to find a new color.</p> <p>Note that the format includes transparency, so you have to modify the format a little bit, by sticking a <code class="language-plaintext highlighter-rouge">c0</code> to the front. In other words, the color <code class="language-plaintext highlighter-rouge">cd0404</code> you got from the color picker site becomes <code class="language-plaintext highlighter-rouge">c0cd0404</code>. Paste that into the registry entry, hit <code class="language-plaintext highlighter-rouge">Ok</code>, and you’re all set.</p> <p>If you’re using a remote desktop session, just quit and come back. If you’re local, I think you can just log out and in again.</p> <p>Behold the insanity:</p> <p><img src="/images/windows/uglification.png" alt="Horribleness" /></p>umhauSometimes I get confused. It’s not much, very often; a misplaced coffee cup here, a bit of code forgotten there. But sometimes it’s Important, and sometimes there’s a chance to head the chaos off. This is one of those latter things.The Zig Language, Session 1: Hello, World2022-08-01T00:00:00+00:002022-08-01T00:00:00+00:00https://umhau.github.io/zig-intro<p>I’ve been watching the <a href="https://ziglang.org/">Zig language</a> for a while now. Like <a href="https://www.freebsd.org/">some</a> <a href="https://en.wikipedia.org/wiki/Xen">other</a> <a href="https://en.wikipedia.org/wiki/Alpine_Linux">things</a> I’m interested in, the phrase “good engineering” seems appropriate. The syntax hasn’t stabilized yet, so this session focuses on the 0.9.1 version of the project. Far as I know, anything is subject to change.</p> <h3 id="manual-installation">manual installation</h3> <p>First things first. Let’s get ourselves downloaded and installed. Download a copy of Zig from their <a href="https://ziglang.org/download/">downloads page</a> and extract it.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> ~/zig <span class="nb">cd</span> ~/zig wget https://ziglang.org/download/0.9.1/zig-linux-x86_64-0.9.1.tar.xz <span class="nb">tar</span> <span class="nt">-xf</span> zig-linux-x86_64-0.9.1.tar.xz </code></pre></div></div> <p>Now you can either add that location to your <code class="language-plaintext highlighter-rouge">PATH</code>, or you can just put the binary somewhere already on your <code class="language-plaintext highlighter-rouge">PATH</code>.</p> <p><em>What’s your <code class="language-plaintext highlighter-rouge">PATH</code>? It’s the road of life laid before you, which you, oh weary traveler, must trod until the end of your days. May it bring you peace.</em></p> <p><em>Actually, it’s how you’re able to run programs directly from the commandline without specifying their location. Ever think about how you can do <code class="language-plaintext highlighter-rouge">cd ~/zig</code> without having to say where the <code class="language-plaintext highlighter-rouge">cd</code> program is located? And no, it’s not some builtin thing that just “comes with” your terminal program. Try <code class="language-plaintext highlighter-rouge">whereis cd</code> sometime. It’s fun.</em></p> <p>Now that we’ve extracted zig, we’re just going to skip adding it to our path. That’s annoying, because it’s another folder we have to remember not to move, and we have to manually edit files. Let’s just dump zig into <code class="language-plaintext highlighter-rouge">/usr/local/bin</code> and add a line to our system install script to do the whole thing automatically next time.</p> <p><em>You have a system install script, don’t you? A little script that you run when you set up a new machine, that gets everything set up just the way you like it? I love it. Doesn’t require a lot of maintanence if you do it right, and it doesn’t ever have to be totally manual either. Just enough automation that it holds your hand while you get it all running. (I guess, in a sense, it’s your younger self holding your older self’s hand. Now there’s a strange thought.)</em></p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo install</span> ~/zig/zig-linux-x86_64-0.9.1/zig /usr/local/bin/zig </code></pre></div></div> <p>Then we can just run zig from anywhere: <code class="language-plaintext highlighter-rouge">zig build</code></p> <p>I don’t think we need the <code class="language-plaintext highlighter-rouge">lib</code> folder that goes with the binary. If we do, there’s two options: dump the entire <code class="language-plaintext highlighter-rouge">~/zig/zig-linux-x86_64-0.9.1</code> folder into <code class="language-plaintext highlighter-rouge">/usr/local/bin</code>, or add the folder to the <code class="language-plaintext highlighter-rouge">PATH</code>; or just install from the OS package manager.</p> <h3 id="package-manager-installation">package manager installation</h3> <p>What OS are you on? I use Void Linux, though I’m working on transitioning to FreeBSD.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># void</span> <span class="nb">sudo </span>xbps-install zig <span class="c"># freebsd</span> doas pkg <span class="nb">install</span> <span class="nt">-y</span> xorg </code></pre></div></div> <h3 id="hello-world">hello world</h3> <p>Verify the version of zig you installed. Note that Void seems to be running behind; might be this is a good time to be jumping off that ship. I think in the future Alping and FreeBSD might be my go-tos…a nice minimal linux, and a longstanding BSD.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zig version </code></pre></div></div> <p>Now let’s make our first zig program. Create a file called <code class="language-plaintext highlighter-rouge">main.zig</code> and put the following inside:</p> <div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">std</span> <span class="o">=</span> <span class="nb">@import</span><span class="p">(</span><span class="s">"std"</span><span class="p">);</span> <span class="k">pub</span> <span class="k">fn</span> <span class="n">main</span><span class="p">()</span> <span class="k">void</span> <span class="p">{</span> <span class="n">std</span><span class="p">.</span><span class="py">debug</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"Hello, {s}!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">.</span><span class="p">{</span><span class="s">"World"</span><span class="p">});</span> <span class="p">}</span> </code></pre></div></div> <p>Then build and run it.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zig run main.zig </code></pre></div></div>umhauI’ve been watching the Zig language for a while now. Like some other things I’m interested in, the phrase “good engineering” seems appropriate. The syntax hasn’t stabilized yet, so this session focuses on the 0.9.1 version of the project. Far as I know, anything is subject to change.Intro to Lisp: Session 12022-06-01T00:00:00+00:002022-06-01T00:00:00+00:00https://umhau.github.io/concise-lisp-s1<p>I’ve been wanting to figure out lisp for a while. So, this is my rubber ducking session to figure it out. I’m working from the Scheme variation, rather than the (apparently) kitchen-sink common lisp variation…though they both seem pretty awesome.</p> <h1 id="setup">setup</h1> <p>We’re using femtolisp, because I think it’s cool.</p> <p>This is my foray into learning lisp. Rather than attempting to learn some part of the greater whole of Common Lisp, I’m using a smaller variation called (femtolisp)[https://github.com/JeffBezanson/femtolisp]. This is considered a dialect of Scheme, and a “lisp-1 with lexical scope,” (which means that)[https://stackoverflow.com/a/4578888] you cannot have a function and a variable that share the same name. I think it’s a neat version of lisp because it’s small and fast and claims to have among its focii “to keep the code concise and interesting.” Also, I think it was used in the development of the Julia language – and a similar lisp can be accessed through the Julia interface.</p> <p>There’s a number of concepts that seem based on lisp, that I haven’t run into before, including “tail recursion” and “gensyms.” I’ll include sections to try and cover the strange concepts. Also this should include documentation of the available functions, and how to use them.</p> <h2 id="installation--a-hello-world">Installation &amp; a ‘Hello World’</h2> <p>This is one of the simpler compilations I’ve encountered.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/JeffBezanson/femtolisp.git cd femtolisp make </code></pre></div></div> <p>I think you need a couple compiler-related dependencies, but femto is already set up on all my available machines, so I don’t have an easy way to check what those deps are.</p> <p>To run the program while in the <code class="language-plaintext highlighter-rouge">./femtolisp</code> directory, just do</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./flisp </code></pre></div></div> <h1 id="the-ten-commandments">the ten commandments</h1> <ol> <li> <p>When recurring on a list of atoms, lat, ask two questions about it: (null? lat) and else. When recurring on a number, n, ask two questions about it: (zero? n) and else. When recurring on a list of S-expressions, l, ask three question about it: (null? l), (atom? (car l)), and else.</p> </li> <li> <p>Use cons to build lists.</p> </li> <li> <p>When building a list, describe the first typical element, and the cons to it onto the natural recursion.</p> </li> <li> <p>Always change at least one argument while recurring. When recurring on a list of atoms, lat, use (cdr lat). When recurring on a number, n, use (sub1 n). And when recurring on a list of S-expressions, l, use (car l) and (cdr l) if neither (null? l) nor (atom? (car l)) are true. It must be changed to be closer to termination. The changing argument must be tested in the terminaion condition: when using cdr, test termination with null? and when using sub1, test termination with zero?.</p> </li> <li> <p>When building a value with +, always use 0 for the value of the terminating line, for adding 0 does not change the value of an addition. When building a value with ×, always use 1 for the value of the terminating line, for multiplying by 1 does not change the value of a multiplication. When building a value with cons, always consider () for the value of the terminating line.</p> </li> <li> <p>Simplify only after the function is correct.</p> </li> <li>Recur on the subparts that are of the same nature: <ul> <li>On the sublists of a list.</li> <li>On the subexpressions of an arithmetic expression.</li> </ul> </li> <li> <p>Use help functions to abstract from representations.</p> </li> <li> <p>Abstract common patterns with a new function.</p> </li> <li>Build functions to collect more than one value at a time.</li> </ol> <h1 id="the-five-rules">The Five Rules</h1> <ul> <li> <p><strong>The Law of Car</strong> The primitive car is defined only for non-empty lists.</p> </li> <li> <p><strong>The Law of Cdr</strong> The primitive cdr is defined only for non-empty lists. The cdr of any non-empty list is always another list.</p> </li> <li> <p><strong>The Law of Cons</strong> The primitive cons takes two arguments. The second argument to cons must be a list. The result is a list.</p> </li> <li> <p><strong>The Law of Null?</strong> The primitive null? is defined only for lists.</p> </li> <li> <p><strong>The Law of Eq?</strong> The primitive eq? takes two arguments. Each must be a non-numeric atom. There doesn’t seem to be a way to run <code class="language-plaintext highlighter-rouge">make install</code>, so you’ll have to do that manually, by trial and error. I’m content to just leave the stuff where it was compiled.</p> </li> </ul> <p>You can run it interactively (is that what’s known as a Read-Eval-Print Loop (REPL) ?), by running</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./flisp </code></pre></div></div> <p>from inside the directory. Print to the command line with</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(print "Hello World") </code></pre></div></div> <p>though, this will append a <code class="language-plaintext highlighter-rouge">#t</code> to the string. Don’t know why. Get out with</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(exit) </code></pre></div></div> <h2 id="the-impatient-schemer">The Impatient Schemer</h2> <p>Now we’re gonna do a crash course in Lisp, because why not. It’s probably been written only a thousand times before. However: it’s never been written by me, and that means I’ve never had the chance to learn by teaching. So here goes.</p>umhauI’ve been wanting to figure out lisp for a while. So, this is my rubber ducking session to figure it out. I’m working from the Scheme variation, rather than the (apparently) kitchen-sink common lisp variation…though they both seem pretty awesome.Testing Internal Network Access2022-05-01T00:00:00+00:002022-05-01T00:00:00+00:00https://umhau.github.io/tor-outside-access<p>This is cool for testing external access to a network:</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>torsocks ssh example.net </code></pre></div></div> <p><a href="https://www.jamieweb.net/blog/tor-is-a-great-sysadmin-tool/">Source</a>.</p>umhauThis is cool for testing external access to a network:mount anything with an ssh server2022-02-10T00:00:00+00:002022-02-10T00:00:00+00:00https://umhau.github.io/sshfs<p>Quick note, this is the favored incantation to prevent too many instances of lost connections.</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> /local/mount/point sshfs <span class="nt">-o</span> allow_root,reconnect,ServerAliveInterval<span class="o">=</span>15,ServerAliveCountMax<span class="o">=</span>3 user@remotelocation:/path/to/folder /local/mount/point </code></pre></div></div> <p>The extra arguments are the real magic here. If you notice, some of those help reestablish the connection - in practice, this means that sometimes it will come back after putting your laptop to sleep. I tend to put specific commands for specific mounts into scripts. However, you can also just put the first part with the ‘incantations’ into a bash alias.</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano ~/.bashrc </code></pre></div></div> <p>Add this to the end:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span><span class="nv">shfs</span><span class="o">=</span><span class="s1">'sshfs -o allow_root,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 '</span> </code></pre></div></div> <p>Then you can simplify mounting, and make it very similar to scp and ssh:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> /local/mount/point shfs user@remotelocation:/path/to/folder /local/mount/point </code></pre></div></div>umhauQuick note, this is the favored incantation to prevent too many instances of lost connections.Expand the netmask of a Windows DHCP Scope2022-02-03T00:00:00+00:002022-02-03T00:00:00+00:00https://umhau.github.io/windows-dhcp-server<p>I used to wonder why “Micro$oft” was so disliked in the opensource community. Naively, I supposed it had to do with the great evil of demanding money in return for services; or possibly the greater horror of refusing access to source code. But I have learned wisdom before my time, for I have met Active Directory. What was it Aeschylus said?</p> <blockquote> <p>Even in our sleep, pain which we cannot forget falls drop by drop upon the heart until, in our own despair, against our will, comes wisdom through the awful grace of God.</p> </blockquote> <p>I need to change the netmask on the dhcp server. My organization has outgrown the /24 allocation it was born with. Easy, right? Just backup a few config files, change a number in a <code class="language-plaintext highlighter-rouge">dhcp.conf</code>-style textfile and do whatever the windows equivalent of <code class="language-plaintext highlighter-rouge">sh /etc/netstart</code> is. <em>Oh, my sweet summer child.</em></p> <p>We’re dealing with scopes and superscopes: not subnets. We have to export the scope configuration, including DNS and WINS servers, as a semi-executable script. Then we modify the script to use the new netmask. Then we delete the old scope, along with all our DHCP leases and reservations. Then we create a new scope using our exported script.</p> <p>Before we go any further, backup the DHCP database. Do this by right-clicking on the root node of the tree in the left-hand bar of the DHCP manager, and select backup, and choose where you want the backup to go. Don’t lose this.</p> <h2 id="alternatives">alternatives</h2> <p>Since I’m just trying to change the netmask, it’s also possible to create a superscope and add the current scope. However, this will create two distinct subnets that can’t talk to each other, and the gateway will have to have separate ip addresses for each scope. Then you create firewall rules to let the scopes talk to each other.</p> <p>It could work, but the communication between the subnets seems like too much complexity and I need to make sure whoever comes after me can understand what’s going on. I’d rather just redo the main subnet and keep it dead simple.</p> <h2 id="doit">doit</h2> <p>Export the current scope. We’re using <code class="language-plaintext highlighter-rouge">netsh</code> instead of <code class="language-plaintext highlighter-rouge">Export-DhcpServer</code>, because I’ve had situations in the past where I exported to both, but the import failed on the latter and succeeded on the former. Open powershell as an administrator.</p> <div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">netsh.exe</span><span class="w"> </span><span class="nx">dhcp</span><span class="w"> </span><span class="nx">server</span><span class="w"> </span><span class="nx">\\DOMAIN_NAME</span><span class="w"> </span><span class="nx">scope</span><span class="w"> </span><span class="nx">192.168.1.0</span><span class="w"> </span><span class="nx">dump</span><span class="w"> </span><span class="err">&gt;</span><span class="w"> </span><span class="nx">C:\scope.txt</span><span class="w"> </span></code></pre></div></div> <p>This gets you a more-or-less executible script that you can modify and then reinsert. Broadly speaking you have to change two things: the scope and the netmask. Just use Ctrl-F and be done with it.</p> <p>In this example, I’m changing the subnet from 192.168.1.1/24 to 192.168.1.1/20.</p> <div class="language-md highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh"># any octet that varies (determined by the netmask) is set to 0</span> <span class="p">192.</span>168.1.0 -&gt; 192.168.0.0 <span class="gh"># set the netmask to the new value</span> <span class="p">255.</span>255.255.0 -&gt; 255.255.240.0 <span class="gh"># Also look for the command that sets the ip address range.</span> Add iprange 192.168.1.1 192.168.1.254 -&gt; Add iprange 192.168.1.1 192.168.15.254 </code></pre></div></div> <p>Once those changes are made, delete the scope from the DHCP manager. <em>(Yes, I know. But there’s no way to back it up besides the export you just made, and at least turning off DHCP doesn’t break too many things too quickly.)</em></p> <p>Then add the new scope by inserting the modified backup. The fun thing is, we can just execute it as a series of <code class="language-plaintext highlighter-rouge">netsh</code> commands. <em>(and BTW, that is a weird rabbithole of a program)</em> You probably had to save the modified script somewhere else, so change directories inside the cmd prompt first and then execute.</p> <div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cd</span><span class="w"> </span><span class="nx">C:\Users\admin\Documents\</span><span class="w"> </span><span class="n">netsh.exe</span><span class="w"> </span><span class="nx">exec</span><span class="w"> </span><span class="nx">scope-modified.txt</span><span class="w"> </span></code></pre></div></div> <p>And activate the scope.</p>umhauI used to wonder why “Micro$oft” was so disliked in the opensource community. Naively, I supposed it had to do with the great evil of demanding money in return for services; or possibly the greater horror of refusing access to source code. But I have learned wisdom before my time, for I have met Active Directory. What was it Aeschylus said?