How to Create a Linux Router from a Mini-PC

Qotom WiFi Router-ready Mini-PC
Qotom WiFi Router-ready Mini-PC
If you are looking to create your own Linux home router with both ethernet and WiFi (because you’re a technical open-source computer control-freak like me), then you’ve come to the right place! My mission was to replace my aging, ailing Linksys WRT54G router which has dutifully powered our home internet for over 15 years starting when broadband internet first became available in our area (I live out in the country).  The WRT54G has served as a good nat-router until recently when it started occasionally tripping (rebooting itself).  I traced the pbm. to an overheating issue:  Cleaned throughly inside and out, did not fix.  Added a fan – this worked for a cpl. more months until it didn’t.  I had installed Tomato firmware on it years ago for more control and better performance, which worked very well for what it was:  16 meg nv-ram, ancient kernel v2.4x unupgradable due to size constraints, no storage so no ability to add any other programs not embedded in the Tomato image (ethtool, for example).  When it failed, I had a 2nd copy of it laying around for emergencies, but it only has the stock Linksys firmware and I was afraid to flash it w/Tomato since it’s the only router I had left, so brick it and no internet until I could replace it! Therefore, I decided to finally take the plunge and do what I had wanted to do for a long time, but couldn’t justify to myself (or wife) spending the money to buy what I really wanted:  a fanless OS-free Linux-compatable minicomputer w/multiple eithernet ports AND a WiFi nic. that could function as an access point (master mode)! The other must-haves were:  1) at least 2 ethernet ports (one for internet and a second to run my desktop on which sits next to the router), 2) at least 3 USB ports (keyboard, mouse, Linux install thumbdrive simultaneously for initial setup and emergency access when I brick the firewall rules and lock myself out! Note:  The keyboard and mouse have been removed and replaced with my shared USB printer now that the router is in “production”), 3) the aformentioned WiFi adapter (master mode supported in Linux)! Nice-2-haves:  1) SCSI plug to allow attaching a spinning disk if needed.
I ended up with this little beauty:  A Qotom Q330G4 Core I3-4005U which I ordered from Rack-IT Solutions on Amazon with 4 Gig of RAM, a 64 Gig SSD disk and the WiFi kit (duel antennie).  It arrived early (about 3 days after I ordered it), so I unpacked it, attached it to a mouse and HDMI monitor, then realized that I had no keyboard (my 2 spares were PS/2), so a quick trip to Best Buy secured a basic no-frills USB keyboard for about $13, and fired it up.  Much to my surprise, it already had PFSense installed on it, so it actually booted up! I decided to then power it back down, open the bottom of the case and plug in my “backup” hard drive containing my desktop Antix Linux into the dangling internal SCSI plug inside, poked around in the bios screen to enable it to boot first from the SCSI, and successfully booted it up to my familiar working Linux desktop environment.  I then poked around to see what hardware it had (inxi -F), verified the WiFi was working (connecting to my current WiFi), etc.
Next step was to download 64 bit Antix (my favorite “live”, systemd-free Linux distro), “burn” it to a thumbdrive, set the bios to allow booting from that first, and booted right in.  This way I could first look at the partition arrangement of the built-in SSD drive, back up it’s contents (PFSense, etc.), repartition, and install Antix Linux! I ended up creating 3 main partitions (all ext4):  / (root): 40G, /home: 12G, and /var: remainder – approx. 11G.  Note, I did not create any swap partition, and /home is fairly small (not really going to have users/data), at this is going to serve only as a router, not a user-computer or shared storage!
I then wiped the SSD with gparted, created the aformentioned partitions, installed grub into the MBR, removed the thumbdrive and SCSI spinning drive, and booted into a fresh stock Antix Linux, Buster addition and had a working desktop!
Now, to turn this thing into a nat-router! I had already done some research into what additional software was needed to run an actual (ethernet & WiFi) router with bridged ethernet and WiFi access point with a secure firewall, DNS, DHCP, etc.  One additional requirement was setting up a shared USB printer:  On my old router (no USB ports) and the ancient laser printer (USB or parallel ports but NO network option) I was using a old D-Link DP-301U “print server” (with a USB port and an ethernet port) to connect the printer to the router, but the plan was to get rid of that device and plug the printer directly into one of the new router’s USB ports and set it up directly using CUPS.  My research led me to this very helpful article:  “How to Build your Own Wireless Router“, namely the 2nd part of this three-part article.  Essentually, I needed to install five main packages:  bridge-utils, hostapd, dnsmasq, openssh-server, and firehol, “bridge” the four ethernet ports, and configure some basic things.  I also deleted a ton of desktop application packages, media players, most web browsers, word-processors, etc. that I knew I wasn’t going to be using as a router.
I)  Install essentials:  bridge-utils, hostapd, dnsmasq, openssh-server, and firehol.
Use apt-get, synaptic or your favorite package-manager to install these and their dependencies.
II)  Network and Bridge Setup:
Determine the network interface names and set up the bridge:
ifconfig revealed the network interfaces:  eth0-3 (4 ethernet ports), wlan0 (WiFi port), and lo (Loopback).  While in the learning / trial and error process of setting up the bridge, I ran into difficulty setting up eth0 as the “COM”/WAN port and someone on Google said something about eth1 being the one to have facing the internet, so I ended up going with that one as my WAN port.  Looking back, I suspect that that may not be required, but since everything’s up and running, I don’t want to go back and reconfigure everything to try to see if it matters whether eth1 (or eth0 or any of the others) could be the WAN port or not, so I don’t know, but you can try it yourself if you choose.  I ended up sticking with eth1 and that is what the configurations in this article assume.  The one file to edit here is:  /etc/network/interfaces:
#------------------------------------------------------------------------------

# /etc/network/interfaces

#

# interfaces(5) file used by ifup(8) and ifdown(8)

# Used by ceni but not by connman

#------------------------------------------------------------------------------

auto lo

iface lo inet loopback

auto eth1

iface eth1 inet static

    address [IP address your ISP assigns to you - see your old router]

    broadcast [What your ISP assigned to you]

    gateway [What your ISP assigned to you]

    netmask [What your ISP assigned to you, pbly starts w/255.]

    network [What your ISP assigned to you]

# Bridge LAN:

auto br0

iface br0 inet static

    bridge_ports eth0 eth2 eth3

    address 192.168.[your chosen LAN subnet]

    network 192.168.[your chosen LAN subnet].0

    netmask 255.255.255.0

    broadcast 192.168.[your chosen LAN subnet].255

    post-up /usr/sbin/dnsmasq \

        -x /run/dnsmasq.$IFACE.pid \

        --dhcp-leasefile=/var/lib/misc/dnsmasq.$IFACE.leases \

        --conf-file=/etc/dnsmasq.conf \

        --interface=$IFACE \

        --dhcp-range=[your loweset DHCP address],[highest DHCP address],255.255.255.0,720m \

# Optional:  I like my DNS cache entries to last for 20 minutes (for quick web-browsing):

        --min-cache-ttl=1200

    pre-down cat /run/dnsmasq.$IFACE.pid | /usr/bin/xargs kill

# Wifi:

auto wlan0

iface wlan0 inet static

    address [same IP as bridge address (under br0 above)]

    network [same IP as bridge network (under br0 above)]

    netmask [same IP as bridge netmask (under br0 above)]

    broadcast [same IP as bridge broadcase (under br0 above)]

    post-up /usr/sbin/hostapd \

        -P /run/hostapd.$IFACE.pid \

        -B /etc/hostapd/hostapd.conf

    pre-down cat /run/hostapd.$IFACE.pid | /usr/bin/xargs kill

Notes:
1)  If your ISP (internet service provider) uses DHCP to assign your IP and network info. then replace the first 6 lines under “auto eth1” with the single line: “iface eth1 inet dhcp”!
2)  The fields described in braces (“[]”) represent IP address values you’ll have to change / choose! Particularly, you’ll need to choose your subnet, ie. “192.168.1.*“, your router’s IP address in your subnet, ie. “192.168.1.2“, the appropriate network, netmask, and broadcast IPs, ie. “192.168.1.0“, “255.255.255.0“, and “192.168.1.255” respectively.  You’ll also need to choose the range (and thus maximum number of available DHCP addresses your router can assign at one time (unless you strictly use a static network), ie. “192.168.1.100 .. 192.168.1.199” for 100 possible addresses.  The
720m” after the DHCP range is your chosen lease time in minutes (I use 12 hours), change if you wish.  Everything else should remain as shown (unless your network device names differ from these).
3)  Until you’re ready to actually replace your router (you most likely won’t be until you have everything working properly!), you’ll probably want to back up your /etc/network/interfaces file, and copy it to two separate configuration files (I chose:  interfaces.SUBROUTER AND interfaces.PROD, remove your interfaces file and symlink it to interfaces.SUBROUTER.  The changes I described above will be in your “interfaces.PROD” file.  In your interfaces.SUBROUTER file, set up your eth1 interface to connect instead to your existing router instead of your ISP, and use a slightly different (different 3rd number in your IP addresses) subnet; and plug your router’s WAN port into an ethernet port on your existing router (so that it will temporarily be a subrouter behind your existing router) until you get everything working properly (so that you and your family can continue to use the internet uninterrupted until everything’s ready to switch over)! We will be doing this with some other configuration files too in this article and will have a script that will switch the router to “production” mode when you’re ready to “go live” with this as your main internet router!
4)  Note that wlan0 and lo are NOT included in the “bridge_ports” argument – lo will NOT be part of the bridge and wlan0 will be added by the hostapd daemon automatically!
III)  Setting up SSH
(access to router from your computer via WAN, as your computer is currently on your old router’s LAN and your new (sub)router has it’s own separate LAN!):
Since I don’t want to leave an extra monitor, keyboard, and mouse permanently attached to my router, I’ll be accessing my router from my main computer via SSH.  This involves installing OpenSSH server and setting up a public key.  I prefer using a public key with a passphrase over a simple password for better security to make it nearly impossible to be hacked into from the internet!
1)  I recommend changing the port your SSH server listens on as an extra wrinkle against attackers trying to hack into your router via the default port by editing your /etc/default/ssh file and specifying a nice random five-digit port# by adding the line:  SSHD_OPTS="-p #####".
2)  Create your public key (on your main computer) using the ssh-keygen program.  It will prompt you for the needed information.  Simply accept the default values, but enter a strong passphrase (like a password) when prompted.
3)  Copy the key over to the router using the command:
ssh-copy-id -p <your chosen SSH port> <router's login user-id>@<router's host name or IP address>
You will be prompted for that user’s password.  NOTE:  When you installed Linux on the router machine, you should have set up root (password) and a non-privileged user-account and password.  This is the unprivileged user-account and password you’ll use when logging directly into the router itself.  This should copy your newly-created public key file over to the router as /home/[router’s login user-id]/.ssh/
4)  I recommend setting up the ssh server daemon to NOT allow root login or simple password authentication (for extra security, since your router will be facing the world wild web)! To do this:  a) Edit /etc/ssh/sshd_config (on the router):  Make sure the lines:  “PermitRootLogin no” and “PasswordAuthentication no” are present and uncommented.  b) Edit /etc/ssh/ssh_config (on the router):  Make sure the lines “PasswordAuthentication no” and “PermitRootLogin no” are present there too and uncommented.  Comment (#) out the line “GSSAPIAuthentication yes“.  I also added the lines:  “Protocol 2“, “ForwardX11 yes“, “ControlMaster auto“, “ControlPath /tmp/ssh-%r@%h:%p“, and “AddressFamily inet“.  These force usage of the modern protocol, allow you to run X (graphic) apps on your router on your computer’s monitor, and improve speed and efficiency in your SSH connections (see https://tychoish.com/post/9-awesome-ssh-tricks/ for more info on these last two).
When finished, on your router, you should have the file “/home/[router’s login user-id]/.ssh/authorized_keys containing the public key you created in steps 3 & 4 above.  On your main computer in /home/[you]/.ssh/id_rsa and id_rsa.pub (the latter containing your public key, the same as in your router’s authorized_keys file.
To open a terminal window to your router, use the command:  “xterm -e ssh -X <router> -l <user> -p ##### &” where “router” is the name you created in your computer’s /etc/hosts file for your router’s assigned IP address, “user” is the aformentioned login user-id you created on your router when you installed Linux, and ##### is your chosen port# you added to your router’s “/etc/default/ssh” file in step 2 above.  If you created your public key with a passphrase (as you should have!) you will be prompted to enter it.
While we’re at it, let’s go ahead and set up the ability to run X apps on the router remotely on our computer’s monitor (since we plan to normally run the router headless):  To do this you’ll need to set it up by following the instructions on this site:  https://www.simplified.guide/ssh/x11-forwarding-as-root Then I created this handly little script which I invoke twice when SSH-ing into the router (if I plan to run an X app): once when I login as user, then again after su’ing to root (it’s a two-step process). Create this script and make it user-executable in /usr/local/bin/ on the router:
#!/bin/bash

#JWT:THIS SCRIPT I WROTE ALLOWS INVOKING X APPS AS SUDO ROOT FROM

#AUTHORIZED REMOTE HOSTS:

#TO USE, SSH IN AS DESIGNATED USER, INVOKE THIS SCRIPT, THEN SUDO ROOT,

#THEN INVOKE THIS SCRIPT AGAIN!

#IDEA CAME FROM:  https://www.simplified.guide/ssh/x11-forwarding-as-root

if [ "X$HOME" = "X/root" ]; then

    #2ND RUN (AFTER SUDO ROOT):

    XIT=`cat /home/[user]/.xit`

    xauth add $XIT

    export DISPLAY=localhost:10.0

    echo "..Step 2 of 2 complete."

else

    #1ST RUN (AS USER BEFORE SUDO ROOT):

    xauth list $DISPLAY >/home/[user]/.xit

    echo "..Step 1 of 2 complete."

fi

IV)  Setting up nat and firehol (firewall):
I had planned to wait and worry about firewall setup at the end when I had
everything else working on the (sub)router (as my router already has a firewall), but I learnt that one must set up some firewall (iptables) stuff before we can even nat! Therefore I’ll cover that next.
1)  Enable basic nat forwarding:  Back up your existing /etc/sysctl.conf file! Then edit it and search for the following lines, and change each value to “1” (or add any which don’t already exist), as shown below:
net.ipv4.conf.default.rp_filter = 1

net.ipv4.conf.all.rp_filter = 1

net.ipv4.ip_forward = 1

net.ipv4.conf.default.accept_redirects = 1

net.ipv4.conf.all.accept_redirects = 1

net.ipv4.conf.default.send_redirects = 1

net.ipv4.conf.all.send_redirects = 1

net.ipv4.conf.default.accept_source_route = 1

net.ipv4.conf.all.accept_source_route = 1

NOTE:  I only use ipv4.  If you’re going to use ipv6 also, there may be others, but that’s beyond the scope of this article.
2)  For better performance, I also added the following additional lines to my /etc/sysctl.conf file for routing (I commented the existing default values these fields had before these changes – you can see the system’s default value for a given parameter, ie. “net.core.default_qdisc” by doing a “cat /proc/sys/net/core/default_qdisc” command from a terminal – note changing the dots to slashes and prepending “/proc/sys/“):
net.ipv4.ip_nonlocal_bind = 1

net.ipv4.ip_dynaddr = 1

#net.core.default_qdisc WAS pfifo_fast (use cake or fq_codel, if possible)!

#NOTE: you'll likely want to use "cake" in lieu of "fq_codel" below, if you have a recent kernel.

net.core.default_qdisc = fq_codel

#net.ipv4.tcp_congestion_control WAS cubic

#NOTE: you'll want to use "bbr2" in lieu of "bbr" below, if you have a recent kernel.

net.ipv4.tcp_congestion_control = bbr

#net.core.somaxconn WAS 128

#We ARE a router, so we'll need to allow more tcp connections!:

net.core.somaxconn = 512

#net.ipv4.tcp_max_syn_backlog WAS 128

net.ipv4.tcp_max_syn_backlog = 256

net.ipv4.tcp_syncookies = 1

net.ipv4.tcp_ecn = 1

I also changed the following lines to better (for me, imho) values (You’ll pbly want to set your Linux computers with these tcp values too):
kernel.dmesg_restrict = 1

net.ipv4.tcp_tw_reuse = 1

net.ipv4.tcp_fin_timeout = 20

net.ipv4.tcp_keepalive_time = 180

net.ipv4.tcp_keepalive_intvl = 30

net.ipv4.tcp_keepalive_probes = 5

net.core.netdev_max_backlog = 2048

net.ipv4.tcp_orphan_retries = 1

#WE HAVE NO SWAP PARTITION AND 4 GIGS OF RAM:

vm.swappiness = 0

net.ipv4.tcp_retrans_collapse = 1

net.ipv4.tcp_sack = 1

net.ipv4.tcp_fack = 1

net.ipv4.tcp_slow_start_after_idle = 0

net.ipv4.tcp_rfc1337 = 1

#THESE SEEM TO WORK BEST FOR OUR LOW-BANDWIDTH/LOW-LATENCY INTERNET CONNECTION (YMMV):

#SEE:  https://www.speedguide.net/analyzer.php

#(WHICH RECOMMENDS 128480 FOR *OUR* SPEED, BUT ASSUMES HIGHER 200ms+ LATENCY THAN WE HAVE):

net.core.rmem_default = 64240

net.core.wmem_default = 64240

net.core.rmem_max = 2055680

net.core.wmem_max = 2055680

#THE MIN. (1ST) VALUE ALLOWS 3 PACKETS (3xMSS):

#DON'T REMEMBER HOW I CALCULATED THE MAX (3RD) VALUES, BUT THEY'VE WORKED GREAT FOR YEARS FOR ME:

net.ipv4.tcp_rmem = 4380 64240 2055680

net.ipv4.tcp_wmem = 4380 64240 2055680

#SEE:  https://blog.cloudflare.com/http-2-prioritization-with-nginx/

net.ipv4.tcp_notsent_lowat = 16384

net.ipv4.tcp_fastopen = 3

3)  Run “sysctl -p” command to update to the new values.
4)  Firehol (firewall):  Nat routing also requires some iptables foo, so we’ll go ahead and set up the firewall now.  Firehol was reccommended by the article and proved to be an excellent program for setting this up quickly! Back up your default /etc/firehol/firehol.conf file, and then move it to /etc/firehol/firehol.conf.PROD.  Edit /etc/firehol/firehol.conf.PROD to look like the following (sans the preexisting comments):
version 6

# Accept all client traffic on WAN

interface eth1 wan

    client all accept

# Accept all traffic on LAN

interface br0 lan

    server all accept

    client all accept

# Route packets between LAN and WAN

router lan2wan inface br0 outface eth1

    masquerade

    route all accept

5)  Now copy /etc/firehol/firehol.conf.PROD to /etc/firehol/firehol.conf.SUBROUTER and symlink /etc/firehol/firehol.conf to point to it.  Add the following to the “.SUBROUTER” version just above the first line above (replacing [your subnet#] with the 3rd number in your OLD router’s LAN subnet).  This will allow you to access your new router from your computer (via SSH) on your existing LAN while you are tesing your new router as a subrouter, which is currently on a separate LAN (your computer is currently seen by your router as being outside on the “internet”).  NOTE: Make sure these are NOT in your firewall setup when you put your router into production!
6)  Now we would edit /etc/default/firehol and make sure START_FIREHOL=NO is set (to NOT start firehol at startup), but the latest version of firehol doesn’t seem to want to be started (manually) with this set, so I had to create the subdirectories:  /etc/NOrc3.d/ and /etc/NOrc5.d/, then move /etc/rc3.d/S03firehol and /etc/rc5.d/S03firehol respectively to these new subdirectories in order to prevent firehol from starting on startup and allow us to start it manually (we will ultimately start it manually in /usr/local/bin/rc_local.pl – see section VII below).  NOTE:  If you’re using systemd, then you will instead have to turn off the firehol service however systemd does it!
interface eth1 trusted src 192.168.[your computer's current subnet#].0/24

    server all accept

    client all accept

7)  Run the following command (as root on the router) to start your firewall:
/usr/sbin/firehol start

Firehol will generate a rather robust firewall for you consisting of over a hundred iptables rules and will block incoming connections not initiated by an outgoing connection!  When all said and done (including setting up ssh), we’ll make this auto-start, but for now, we’ll start the firewall manually for safety.  You might also want to test your firewall from your computer while it and your new router are on your old router (your new router’s “WAN”)!
TEST:  You should now be able to plug your router’s WAN port into one of the LAN ports on your existing router, reboot, and have internet access on your new router’s ethernet ports itself (through your old one) as if you were another computer on your lan.  If this fails, you need to check your configuration file and (/etc/network/interfaces.SUBROUTER, and eth1 should be configured for your existing router’s subnet, NOT your ISP’s) and make sure you have /etc/network/interfaces symlinked to it.
When all said and done (including setting up ssh), we’ll make this auto-start, but for now, we’ll start the firewall manually for safety.
V)  Configure hostapd (you did install it already, no?):
I created an 80211g/n network as this is what my router’s wireless nic. and the common denominator supported by all my WiFi devices.  If you can and wish to create an a/c network instead, set these instead to more appropriate values for your network.
Backup your original /etc/hostapd/hostapd.conf file and make the following edits:

1)  Uncomment the “bridge=” line and set it to “bridge=br0“.

2)  Uncomment the “driver=” line and set it to “driver=nl80211“.

3)  Uncomment the “ssid=” line (if commented) and set to your preferred network SSID string.

4)  Uncomment the “country_code” line and set to your two-letter country code (ie. “US” for the U.S.A.).

Yes, we expecially want to be legal here because if unset, your WiFi transmition and channel choices will likely be limited to a restrictive “common-denominator” or values!

5)  Uncomment the “ieee80211d=1” line (for better WiFi support within the limits of your country).
6)  Set the “channel=” line to your preferred channel, as needed for your network (but different from your current router while this is still a subrouter!
7)  (My preference, for efficiency):  I change the “beacon_int=100” to 1000 (1 beacon sent per second), and the “dtim_period=2” line to 1.
8)  (My preference, but not required):  I change the “rts_threshold=-1” and “fragm_threshold=-1” lines to 2347 and 2346 respectively.
9)  I initially set the “macaddr_acl=0” line to 1 and set up a mac filter file for better WiFi security, but was forced to abandon usage of mac-filtering due to a couple of recent “iCrap” (Apple) devices obtained by my family which insist on RANDOMIZING THEIR MAC-ADDRESSES! These devices can be told to NOT randomize their mac-addresses for SPECIFIC networks (SSIDs), but this does not work either (since I’m not broadcasting a SSID, so Apple assumes the “unknown network”).  After a bunch of googling and T&E, I found some Apple documentation that in many words said that Apple will “allow” (play nice with) networks with either a non-broadcast SSID -OR- a mac-filter, but NOT both (unless you turn off the randomized mac-addresses on your Apple device for ALL networks, which defeats THEIR security when connecting to public networks (which I did not want to force my loved ones to do, for their own safety when they access public WiFi networks)! I opted to retain an unbroadcasted SSID and surrender the mac-filter, so in a way, Apple is now dictating terms of MY network security. :/ If you don’t have any of these borish devices and want to have a mac-filter, set this line to 1, and uncomment and set the “accept_mac_file=” line to your mac-filter file.
10)  I recommend changing the “auth_algs=” line from 3 to 1 for Shared key authentication and NOT allow open system authentication.
11)  I recommend uncommenting the line “ignore_broadcast_ssid=” line and setting to 1 to prevent publicly broadcasting your SSID to your neighbors for a little bit more WiFi security (a hacker with a packet sniffer can find it, but I doubt any of your neighbors are that sophisticated)!
12)  Uncomment the “wmm_enabled=1” and “ieee80211n=1” lines to allow for higher 80211n speeds (otherwise you’re basically limited to an 80211g network:  54Mbps max.).  With my configuration, I’m seeing WiFi speeds of (up to) 160Mbps so far – way higher than my slow 20th-centuryish rural 6Mbps “broadband” internet connection – but w/Cake, my TCP settings & other optimizations, I do get the absolute most out of it!).
13)  Run the command “iw list” on your router to determine the “Supported extended features” of your access point nic., which you can then uncomment the “ht_capab=” line to fully support your access point’s capabilities, but read the comments carefully just above this line first! NOTE:  Mine includes “HT-greenfield” support ([GF]), so I would’ve added that, but you should not include this one unless ALL your WiFi devices also support it, otherwise performance may degrade.
14)  Uncomment the “wpa=2” and “wpa_passphrase=” line and set the latter to your chosen secure WiFi passphrase!
15)  Uncomment the “wpa_key_mgmt=” line and set to “WPA-PSK“.
16)  Uncomment the “wpa_pairwise=” and “rsn_pairwise=” lines and set both to “CCMP” (I recommend leaving TKIP off as it is older and less secure – SEE:  https://help.keenetic.com/hc/en-us/articles/213968749-Ways-to-increase-connection-speed-bandwidth-and-stability-of-your-Wi-Fi-network).
These settings should provide you a fast and secure 80211g/n WiFi access point accessable from both Windows 7, 10, 11, Apple, and Linux devices.  I can not speak on 80211a/c networks, as my access point and some devices here do not support it.
17)  Now copy your /etc/hostapd/hostapd.conf file to /etc/hostapd/hostapd.conf.SUBROUTER, and them MOVE /etc/hostapd/hostapd.conf to /etc/hostapd/hostapd.conf.PROD, and symlink /etc/hostapd/hostapd.conf to /etc/hostapd/hostapd.conf.SUBROUTER (we’re still testing as a subrouter for now).  Edit /etc/hostapd/hostapd.conf.SUBROUTER and change your ssid, channel, and wpa_passphrase to something DIFFERENT so that it doesn’t clash with your current router while you test everything out.  You might also wish to broadcast your SSID while testing.
18)  Now we’ll have to prevent hostapd from starting up when the system boots (we need it to wait until the network is up, so we start it up in /etc/network/interfaces).  Unfortunately, there is no option to disable hostapd in /etc/default/hostapd!  Therefore, I had to move /etc/rc3/S02hostapd and /etc/rc5/S02hostapd to /etc/NOrc3.d/ and /etc/NOrc5.d/ respectively (that we created in section IV, part 6 above).  NOTE:  If you’re using systemd, then you will instead have to turn off the hostapd service however systemd does it!
19)  I ran to an ugly issue trying to get my regulatory domain set where hostapd and the wireless interface would recognize it (and operate at the full 30 watts, as allowed in the USA with 80211n speeds).  You can check to see if you have this issue by entering the command as root:  iw reg get. If you get your country code back and a proper list of channels and power, then you’re good, and can skip on down to part VI below.  Otherwise, this will take some more work.  If you get something else like “00” “–” “unregulated”, etc. you’ll need to read on!:  I’m not sure what the issue is but for me it ended up being that the two regulatory database files (/lib/firmware/regulatory.db*) were corrupt and not being loaded (dmesg will show an error like “failed to load regulatory database” or something similar (sudo dmesg | grep -i “regul”).  These come from the package:  wireless-regdb and, in debian-based distros like AntiX, are symlinked back around to corresponding files:  /lib/firmware/*-debian, which seem to NOT work?!  I ended up having to install them from github by creating an empty directory (as root):  /tmp/wireless-regdb/; cd /tmp; running the command:  git clone git://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git wireless-regdb; cd /tmp/wireless-regdb; renaming the two symlinks (/lib/firmware/regulatory.db*); copying the too files:  /tmp/wireless-regdb/regulatory.db and /tmp/wireless-regdb/regulatory.db.p7s over to /lib/firmware/, making sure they were owned by root with permissions 644.  Then, create or edit /etc/modprobe.d/cfg80211.conf with the line:  “options cfg80211 ieee80211_regdom=US” (replace “US” with your two-letter country-code)!  Now reboot the router and verify the proper domain is set (iw reg get), sudo dmesg | grep -i “regul” returns no errors, and run iw list shows the proper channels, frequencies, and power.
VI)  Setting up DNS and DHCP (dnsmasq):
Now, we need to set up DNS (so we can use the internet, cache DNS lookups), and (optionally) hand out DHCP leases to devices connecting to our access point – IF you’re allowing for dynamic IP addresses.
1)  Backup and edit /etc/dnsmasq.conf and uncomment / add / edit the following lines (see the comments in the file and / or the dnsmasq manpage for details on what these do):
domain-needed

bogus-priv

filterwin2k

resolv-file=/etc/resolv.dnsmasq

no-poll

except-interface=eth1

addn-hosts=/etc/hosts.dnsmasq

expand-hosts

#Set next to max. number of concurrent DHCP user-leases (same as your "--dhcp-range" setup in /etc/network/interfaces:

dhcp-lease-max=#

dhcp-authoritative

dhcp-rapid-commit

#optional:

cache-size=2048

local-ttl=1200

domain=[your-chosen-domain-name.org]

#IF you have Windows-7 devices on your network, add / uncomment these last two:

dhcp-option=252,"\n"

#NOTE:  below is 1i (ONEi) not li (lower-Li)!:

dhcp-option=vendor:MSFT,2,1i

2)  Create or edit the file /etc/resolv.dnsmasq to contain the same lines you use in your computer’s /etc/resolv.conf file (likely the IP address of your current router), ie.:
nameserver [your old router's IP]

3)  Rename this file to /etc/resolv.dnsmasq.SUBROUTER and symlink /etc/resolv.dnsmask to it.
4)  Copy /etc/resolv.dnsmasq.SUBROUTER to /etc/resolv.dnsmask.PROD, edit the latter changing it’s contents to the same as what’s in your current router’s /etc/resolv.conf file (whatever you currently use for DNS servers).  ie.:
nameserver [your ISP's primary DNS server IP]

nameserver [your ISP's secondary DNS server IP]

5)  Backup and edit /etc/resolv.conf and set it to contain the single line (This forces DNS to go through dnsmasq, which will use /etc/resolv.dnsmasq for the actual nameservers.  You should eventually set all your devices’ DNS lookup nameservers to be the IP address of your router, if this isn’t already the case, as it will consolidate all DNS on your network and take advantage of your router’s caching of DNS looks thus speeding up everyone’s web browsing!):
nameserver 127.0.0.1

6)  You will probably need to update your /etc/hosts file to make sure 127.0.0.1 points to both localhost and your router’s hostname.
7)  Now we’ll have to prevent dnsmasq from starting up when the system boots (we need it to wait until the network is up).  Therefore, edit /etc/default/dnsmasq and make sure “ENABLED=0” is set to prevent dnsmasq from starting up when the system boots (we need it to wait until the network is up, so we start it up in /etc/network/interfaces).  I can’t remember why now, but I also ended up moving /etc/rc3/S03dnsmasq and /etc/rc5/S03dnsmasq to /etc/NOrc3.d/ and /etc/NOrc5.d/ respectively (that we created in section IV, part 6 above), though this may be unnecessary here?  NOTE:  If you’re using systemd, then you will instead turn off the dnsmasq service however systemd does it!
VII)  Setting up bandwidth-management, traffic shaping, and removing buffer-bloat:
I live in a rural area of the U.S.A. where we only have very limited choices for internet (unless you want to spend big bucks).  Therefore, my “broadband” internet is only 6Mbps down, 2Mbps up.  Despite the paltry bandwidth my ISP does a pretty good job of actually delivering these rates pretty steadily and continuously! With these settings, I’m able to make the most of my bandwidth, being able to stream Sling-TV at 720p while streaming music and surfing the web without any lag while my wife is on a Skype call in her home-office and using the web all at the same time! Even if you are like most others blessed to have real broadband (25-100Mbps+), you’ll still want to implement this basic bandwidth-management and traffic shaping in order to minimize bufferbloat / lag as these can affect the fastest of internet connections when multiple users are using the internet simultaneously.  For example my mum has cable-internet boasting 50Mbps down, but her cable modem is so bufferbloated that it’s actually noticably laggier and “slower” than mine! Their speed is also a lot less consistant as it starts off new connections fast, but quickly throttles them back down to 6-8Mbps.
If you have a recent kernel installed on your router, you’ll want to configure “Cake” and limit your WAN bandwidth (both up and down, or at least up) to just below your actual provisioned bandwidth (if you have any latency / bufferbloat issues – see dslreports.com and the ping command)! What this will do is move the “chokepoint” from your ISP’s edge equipment to YOUR Linux router where YOU can control the traffic flow and buffering! This is particularly critical if using a (usually very over-bufferbloated) cable-modem.  Fortunately, my ISP uses an antenna on my roof containing a small router box that converts their over-the-air broadband to ethernet into my house (my router), which already seems configured with minimum buffering, as the DSLReports speedtest (dslreports.com) consistantly shows bufferbloat grades of A+ and speeds of 6.3Mbps down and 2.2-2.4Mbps speeds up (with just my old stock Linksys WRT54G router).  To use Cake, make sure the line “net.core.default_qdisc = cake” exists in /etc/sysctl.conf and is set to “cake“.  You can test whether your kernel supports Cake by trying “modprobe sch_cake“.  Note:  I ended up later removing the bandwidth limiting as I was already getting “A+” bufferbloat scores from dslreports.com and good Skype calls without them (I was only giving up bandwidth unnecessarily, but if you have a bufferbloated ISP-supplied device just above you, this should help you get much better results (particularly if you have more bandwidth to work with)!  I did keep the other cake settings, the byte_queue_limits and all the other code though for optimization, traffic-management and bufferbloat-removal!  You can also try just removing the download limiting while retaining the upload part (see the comments), as this might still help with any latency.
I normally run most of my custom configurations and tweaks from a Perl script I create as /usr/local/bin/rc_local.pl.  To do this, you’ll need to edit /etc/rc.local to include the following code:
if [ -x /usr/local/bin/rc_local.pl ]; then

    /usr/local/bin/rc_local.pl &

fi

Then, after creating /usr/local/bin/rc_local.pl with at least the following code (NOTE:  This is for MY 6/2Mbps internet connection and network configuration (using cake), so you’ll need to adjust for yours.  If your ISP uses PPOe or ASDL to connect you, be sure and see “man tc-cake” (manpage) for setting proper packet overhead bytes, etc!):  (SEE https://www.bufferbloat.net/projects/codel/wiki/Best_practices_for_benchmarking_Codel_and_FQ_Codel/#enabling-byte-queue-limits, https://www.bufferbloat.net/projects/codel/wiki/Cake/, https://wiki.gentoo.org/wiki/Traffic_shaping and https://www.bufferbloat.net/projects/codel/wiki/CakeTechnical/) tl;dr
#!/usr/bin/perl

#OPTIMIZE NETWORK INTERFACES (eth1 is WAN port, eth0, 2, 3 are LAN, wlan0 is WiFi access point):

foreach (my $i=0;$i<=3;$i++) {

    `/usr/sbin/tc qdisc delete root dev eth$i`;

    `/sbin/ethtool -G eth$i rx 80 tx 80`; #SET TO >=16 OR AS LOW AS IT ALLOWS.

    `/sbin/ethtool -K eth$i tso off gso off gro off`;

    `/sbin/ifconfig eth$i txqueuelen 1`; #DEFAULT 1000.

    if ($i == 1) { #OUTWARD (WAN) INTERFACE:

        for (my $j=0;$j<=1;$j++) { #MY ETHERNET DEVICES HAVE 2 HARDWARE QUEUES:

            `echo 3028 > /sys/class/net/eth$i/queues/tx-$j/byte_queue_limits/limit_max`;

        }

        #NOTE: THIS WILL LIMIT UPLOAD RATE TO 2.1Mb/s (JUST BELOW OUR PROVISIONED RATE):

        #(IF YOU DON'T WANT UPLOAD RATE LIMITED, REMOVE "bandwidth 2100kbit" -

        #IN NEXT LINE AND COMMENT REMAINING LINES IN THIS BLOCK).

        #IF BUFFER-BLOAT OR LAG PERSIST, TRY LOWERING TO 90-95% OF BANDWIDTH, THOUGH CAKE

        #SEEMS TO APPLY THAT TO THE SPECIFIED LIMIT ANYWAY (EXPERIMENT).

        `/usr/sbin/tc qdisc add dev eth1 root cake bandwidth 2100Kbit nat ethernet`;

        #(IF NOT LIMITING DOWNLOAD RATE, COMMENT OUT REMAINING LINES IN BLOCK):

        #NOW SET UP INGRESS INTERFACE eth1_ifb FOR DOWNLOAD RATE-LIMITING:

        #THIS WILL LIMIT DOWNLOAD RATE TO 6.2Mb/s: (JUST BELOW OUR PROVISIONED RATE)

        #EFFECTIVLY MOVING ANY BOTTLENECK HERE (WHERE WE CAN CONTROL)!

        #(from: https://www.bufferbloat.net/projects/codel/wiki/Cake/)

        `/usr/sbin/ip link add name eth1_ifb type ifb`;

        `/usr/sbin/tc qdisc add dev eth1 handle ffff: ingress`;

        `/usr/sbin/tc qdisc add dev eth1_ifb root cake bandwidth 6200Kbit nat ethernet`;

        `/usr/sbin/ip link set eth1_ifb up`;

        `/sbin/ifconfig eth1_ifb txqueuelen 1`;

        `/usr/sbin/tc filter add dev eth1 parent ffff: matchall action mirred egress redirect dev eth1_ifb`;

        `echo 3028 > /sys/class/net/eth1_ifb/queues/tx-0/byte_queue_limits/limit_max`;

    } else { #INWARD (LAN) INTERFACES:

        for (my $j=0;$j<=1;$j++) {

            `echo 3028 > /sys/class/net/eth$i/queues/tx-$j/byte_queue_limits/limit_max`;

        }

        `/usr/sbin/tc qdisc add root dev eth$i cake ethernet metro`; #METRO=RTT 10ms (LOCAL)

    }

}

#NOW SET UP WiFi (mine has 4 queues):

for (my $j=0;$j<=3;$j++) {

    `echo 3028 > /sys/class/net/wlan0/queues/tx-$j/byte_queue_limits/limit_max`;

}

`/sbin/ifconfig wlan0 txqueuelen 8`; #SEEMS TO NEED MORE BUFFERING HERE.

`tc qdisc delete root dev wlan0`;

`tc qdisc add root dev wlan0 cake diffserv4 metro`; #MIRROR HOSTAPD WHICH SETS UP 4 QUEUES.

#DON'T NEED AN EXTRA QUEUE ON THE BRIDGE!:

`/sbin/ifconfig br0 txqueuelen 0`;

#THIS SEEMS TO WORK BEST (SNAPPIER WEB-PAGE LOADS) - DEFAULT WAS 10 AND 10 *** SET YOUR GATEWAY IP! ***:

`/sbin/ip route change default via $ENV{GATEWAYIP} proto static initrwnd 20 initcwnd 20`;

#NOW START UP FIREWALL HERE (INSTEAD OF IN INIT.) NOW THAT THE NETWORK INTERFACES ARE ALL UP:

if (-e '/etc/firehol/firehol.conf' && -x '/usr/sbin/firehol') {

    `/usr/sbin/firehol start`;

}

#IF YOU WANT TO INSTALL AND USE FIREQOS TRAFFIC PRIORITY, UNCOMMENT (BUT I THINK IT REQUIRES

#THE OLDER FQ-CODEL, INSTEAD OF CAKE, WHICH SEEMS TO WORK FINE FOR US WITHOUT NEEDING THIS):

#I TRIED IT BEFORE SETTING UP CAKE AND 95%+ OF MY TRAFFIC STILL WENT INTO SAME QUEUE, YMMV,

#BUT YOU MIGHT HAVE TO SET UP COMPLEX DSCP PACKET MARKING TO GET MUCH OUT OF IT!:

#if (-e '/etc/firehol/fireqos.conf' && -x '/usr/sbin/fireqos') {

#    `/usr/sbin/fireqos start`;

#}

#KILL USELESS PROCESSES (THAT SEEM TO BE FORCED ON DURING BOOTUP BUT NOT NEEDED AFTER):

`/usr/bin/killall startpar`;

`/usr/bin/killall elogind-daemon`;

`/usr/bin/killall dbus-daemon`;

exit (0);

***** (Here’s the equivalent bandwidth-limiting code if you are using htb and fq_codel instead): *****
#NOTE: THIS WILL LIMIT UPLOAD RATE TO 2.1Mb/s (OUR PROVISIONED):

# LIMIT DOWNLOAD BANDWIDTH:

`/usr/sbin/tc qdisc add dev eth1 handle ffff: ingress`;

`/usr/sbin/ip link add name eth1_ifb type ifb`;

`/usr/sbin/ip link set eth1_ifb up`;

`/sbin/ifconfig eth1_ifb txqueuelen 1`; #DEFAULT 32.

`/usr/sbin/tc filter add dev eth1 parent ffff: matchall action mirred egress redirect dev eth1_ifb`;

`/usr/sbin/tc qdisc add dev eth1_ifb root handle 1: htb default 11`;

`/usr/sbin/tc class add dev eth1_ifb parent 1: classid 1:1 htb rate 6200kbit`;

`/usr/sbin/tc class add dev eth1_ifb parent 1:1 classid 1:11 htb rate 6200kbit prio 0 quantum 1514`;

`/usr/sbin/tc qdisc add dev eth1_ifb parent 1:11 fq_codel quantum 300 limit 1024 ecn`;

# LIMIT UPLOAD BANDWIDTH:

#(from:  https://wiki.gentoo.org/wiki/Traffic_shaping)

`/usr/sbin/tc qdisc add dev eth1 root handle 1: htb default 11`;

`/usr/sbin/tc class add dev eth1 parent 1: classid 1:1 htb rate 2100kbit`;

`/usr/sbin/tc class add dev eth1 parent 1:1 classid 1:11 htb rate 2100kbit prio 0 quantum 1514`;

`/usr/sbin/tc qdisc add dev eth1 parent 1:11 fq_codel quantum 300 limit 1024`;

`echo 3028 > /sys/class/net/eth1_ifb/queues/tx-0/byte_queue_limits/limit_max`;

With these settings and those in /etc/sysctl.conf, I get 14-25ms ping times and fast web-page loads, and clear Skype calls regardless on internet load, and no bufferbloat!
VIII)  Misc. system configuration changes (ymmv):
1)  /etc/gai.conf:  Since I only use ipv4, I uncommented the line “precedence ::ffff:0:0/96 100” per https://askubuntu.com/questions/32298/prefer-a-ipv4-dns-lookups-before-aaaaipv6-lookups.
2)  /etc/inittab:  Since I don’t plan to leave a monitor, keyboard, etc. attached to my router (headless), I can save a ton of memory and resources by NOT starting up X on bootup, so I changed the default runlevel from 5 (graphics/X) to 3 (console) in /etc/inittab and reduced the number of getty consoles from 6 to just 2.  You can still run X apps. on your computer’s monitor over SSH without X running on the router, and you can always hook up a monitor, mouse and keyboard, and manually start slim/X or manually switch runlevels back to 5 to do so (“sudo init 5“) when and if needed.
3)  /etc/default/console-setup:  Make console font small enough to see bootup messages and reduce number of getty consoles from 6 to 2:  ACTIVE_CONSOLES="/dev/tty[1-2]", FONTFACE="VGA" and FONTSIZE="8x16".
4)  /etc/default/grub:  Be sure to set GRUB_SAVEDEFAULT=true (so router always boots up in previous kernel by default, since we’ll be running headless).  I also added “quiet nosplash ipv6.disable=1 mitigations=off” to the GRUB_CMDLINE_LINUX_DEFAULT= string (since we’ll run headless, won’t be running a web browser to surf dodgy sites on the router, won’t be running any untrusted software (better speed), and I don’t care to bother with ipv6).
IX)  Switching from Testing (subrouter) to Production (main router) (and back):
If you’ve been following these instructions, there are at least four files for which we have set up separate “test” and “production” versions, with the actual file used symlinked to one or the other of the pairs (make sure each is currently sublinked to it’s corresponding .SUBROUTER version)! Depending on your environment and setup needs, you may wish to create such pairs for additional config. files, such as /etc/hosts, /etc/environment, etc. (and add them to the /usr/local/bin/switchroute.sh script below):
/etc/resolv.dnsmasq

/etc/network/interfaces

/etc/hostapd/hostapd.conf

/etc/firehol/firehol.conf

For each of these, we created a *.SUBROUTER and a *.PROD version for each and symlinked each to it’s corresponding SUBROUTER version, ie:  /etc/resolv.dnsmasq => /etc/resolv.dnsmasq.SUBROUTER.  Now we need to create a script to switch between the two.  As a test setup, we have the new router set up with a different sublink, WiFi channel, etc. and connected behind our old main router (so everyone else in the house could continue using the internet uninterrupted during our setup and testing process.  Now (assuming you’ve followed the instructions and set everything up properly for your environment, tested, and everything seems to be working:  you tested ethernet, firewall, WiFi connectivity and speed, etc.), and you are ready to make the big switchover, I’ve created a simple script to run (as root), followed by reconnecting your new router directly to your internet and removing your old router.  I call it /usr/local/bin/switchroute.sh:
#!/bin/bash

if [ "X$HOME" = "X/root" ]; then

    if [ "X$1" = "XTEST" ]; then

        echo "--Reconfiguring as TEST (subrouter) (REBOOT required)!..."

        rm -f /etc/resolv.dnsmasq

        ln -s /etc/resolv.dnsmasq.SUBROUTER /etc/resolv.dnsmasq

        rm -f /etc/network/interfaces

        ln -s /etc/network/interfaces.SUBROUTER /etc/network/interfaces

        rm -f /etc/firehol/firehol.conf

        ln -s /etc/firehol/firehol.conf.SUBROUTER /etc/firehol/firehol.conf

        rm -f /etc/hostapd/hostapd.conf

        ln -s /etc/hostapd/hostapd.conf.SUBROUTER /etc/hostapd/hostapd.conf

    else

        if [ "X$1" = "XPROD" ]; then

            echo "--Reconfiguring as PRODUCTION router (REBOOT required)!..."

            rm -f /etc/resolv.dnsmasq

            ln -s /etc/resolv.dnsmasq.PROD /etc/resolv.dnsmasq

            rm -f /etc/network/interfaces

            ln -s /etc/network/interfaces.PROD /etc/network/interfaces

            rm -f /etc/firehol/firehol.conf

            ln -s /etc/firehol/firehol.conf.PROD /etc/firehol/firehol.conf

            rm -f /etc/hostapd/hostapd.conf

            ln -s /etc/hostapd/hostapd.conf.PROD /etc/hostapd/hostapd.conf

        else

            echo "u:ERROR: Invalid usage! (sudo switchroute.sh TEST|PROD)!"

        fi

    fi

    ls -l /etc/resolv.dnsmasq

    ls -l /etc/network/interfaces

    ls -l /etc/firehol/firehol.conf

    ls -l /etc/hostapd/hostapd.conf

else

    echo "u:ERROR: This script must be run as root! (sudo switchroute.sh TEST|PROD)!"

    exit 1

fi

exit 0

Now, to make the big switch, run the script as:  sudo switchroute.sh PROD; then reconnect and reboot your router.  Test to make sure you can connect to the internet through it and that your fellow household users can connect their devices to the new access point (you may need to change the SSID, authentication method, passphraise, etc. on each device – unless you are kept the same setup identical to what you used on your old router).  If something went wrong and it’s not a quick-fix, you may need to switch back to your old router and reconnect up as a subrouter for testing.  This should be easy running the script as:  sudo switchroute.sh TEST, reconnecting the WAN port back to one of your old router’s ethernet ports, and rebooting (Yes, I did have to do this once in the process).  If you follow this and find I left something out, please be sure to comment on this post so I can correct this article!
/END

New Perl module: CAM::PDFTaxforms Released to CPAN

Cam Newton laughing
Cam Newton laughing;  2016 Wikipedia.
Today I released a new Perl module to CPAN entitled CAM::PDFTaxforms.  This is the result of some hacking I had to do to an old module called CAM::PDF years ago for a job assignment.  The task involved generating a partially filled out tax form based on users’ inputs from a logistics company’s website and other database data.  I quickly stumbled upon CAM::PDF which handled filling in data fields on tax and other PDF forms, but could not check any of the checkboxes on such forms.  The easiest solution was to hack up a copy of CAM::PDF to fix it to do this, much easier than inventing new code!  This was back in 2010.  I submitted my patch to Chris Dolan, the author and maintainer of CAM::PDF, and then filed CPAN bug #61354 with the patch, and got no response back.  I then tarred up my hacked version and added it to my download site in case I needed it again at another office or for anyone else needing this feature.  I went back a couple of times to check the status of the request and found that new versions had been released since then and that the list of open issues was quite long (I try to respond to, address, and close issues filed against MY modules quickly).
The other day, I got an email requesting info and assistance with MY hacked version, for which I had to dust off the old code that I haven’t used in several years and make sure it still worked (it did), so I replied him with instructions.  I haven’t heard back, so I assume he’s a happy user now.  Anyway, being a bit bored lately and with no other programming projects (I’m retired from my full-time programming career but still work on my own stuff) it got me to thinking about that code again and I decided to see if I could repackage it into a new module.  Revisiting the code, I found that it had a single main program source file and a utility script that I had modified, but also a subdirectory of several other module source files that it depended on.  Having developed and worked on several Perl-Tk modules in more recent years, I realized that I could just create a “wrapper” module that would allow me to simply create a CAM::PDF object, inheriting all it’s methods, but seemlessly override the handful that contained my changes, and create a new module with a similar name, and release it with the same license (which is open-source, and allows derived works).  Then I had to uninstall my old, hacked version of CAM::PDF (1.52.1) and install their latest version 1.60.
I decided to name it CAM::PDFTaxforms, took the methods that needed changing copying them into my new module, and start testing some IRS forms.  I did run into some issues with their latest forms, particularly their use of high-ASCII (and some NULL) characters in their internal PDF field names causing the program to not work at all.  I studied it and was able to fix that issue with a couple lines of new code, and a couple of other minor issues by applying a few patches that others had submitted to the CPAN bug system (that were also still open).  I tested some other recent IRS tax forms and finally have it working fairly well.  In theory one could write an entire tax-filing application with it (if one has an army of developers, accountants, and tons of time of their hands), but would have to redo it each year due to many forms changing (adding and removing fields and checkboxes).  The next step was to modify the build files, documentation, test harness, etc. from CAM::PDF to fit our module.  I also added a couple of new tests (and example programs).  One takes a blank 2018 IRS form 1040-Schedule B and a little text file containing pairs of banks and mutual funds and dollar amounts, and creates a filled-in version of it, including calculating and filling in the total lines.  The other one then “reads” it and checks that the fields are filled in with the proper data and checkboxes checked. Anyway, I hope others will find it useful!
You can find more of my CPAN modules here.
/end

Saving my Lawn Tractor with a Juryrigged Gascan

This spring I oiled up my 16 year old riding tractor mower, started it up and mowed my back pasture as I do every spring when the grass quickly starts to grow to knee-high in a span of about two weeks during the rainy season.  After getting everything mowed for the first of usually about three and a half times a season, I put the mower back into the barn and closed the door.  The next day I went out into the barn for something and noticed that the place smelled like the inside of a gas can.  I opened the doors and turned on the fan and started looking at the mower.  Sure enough, the sixteen year old plastic gas can had developed cracks around the opening and cap at the top.  I called my friendly local tractor-repair man to inquire about a new gas can for it.  He laughed and said that I would need to get a new mower as the gas cans in these models are embedded deep in the frame of the tractor, difficult to obtain (aftermarket), and would require $$$ of laaaabor to completely dissemble the tractor to get at and replace; and that, “Oh by the way, I have a great little used tractor that I can offer you a great deal on!”  Me:  Ooooee-kaaye, thanks, I’ll get back withya¡” (NOT! – I went and looked at it and it wasn’t big enough for my pasture).  I started googling and consulting with a general mechanic friend of mine about repairing the tank and for a second opinion.  I settled on some silicone-epoxy-based stuff and tried it.  It was messy, but seemed to work for the most part.  After draining and evaporating out all the remaining gas and doing all this, and adding some gas into the tank in preparation to resume mowing, I noticed the next morning that I was STILL smelling gas.  So, I got a flashlight and started examining as much of the rest of the tank as I could see through the holes and sides of all the sheets of metal surrounding it and, sure enough found at least one other leak that would be impossible to reach and repair.
RotopaX RX-1G Gas Can
RotopaX RX-1G Gas Can;  by (©) RotopaX(tm).
Ok, now what? I decided to sleep on it for awhile and then came up with the bright idea of seeing if I could make some kind of “auxiliary” tank for it that I could mount on the back.  So I decided to measure the back area and try to google and find a tank that would fit properly within the limited dimensions.  Google presented dozens of gas tanks in all different shapes and sizes, but I only found one that was even close to what I was envisioning:  A one-gallon mountable “ATV” tank from “RotopaX” via Amazon (link here).  A bit small, but it’d do.  the next size up was a 1.75 gallon version that would not fit.

Next step was to find a way to connect it to the tractor’s fuel line, along with a “straw” to reach the bottom of the tank to pull the gas.  I googled around some more for connectors.  This involved wading through dozens if not hundreds of different little doodad fittings.  Finally, I stumbled across something that might actually work:  a “Brass Hose Barb Bulkhead Union“.  This unique little fitting has just what I was looking for:  a screw-down bolt/nut to tightly affix to and seal with the wall of the gas tank, and two push-on fittings:  one to attach to the tractor’s fuel line and one to attach the internal extension, aka. “straw” part to reach the bottom of the tank!  I tried finding this part at the hardware stores, but no luck.  So, having planned out exactly how everything was going to work, I ordered both parts on Amazon.  The tank arrived literally the NEXT day!  Kudos to “Advanced Cycle Parts” for their lightning-FAST shipping!  The brass fitting, unbeknownst to me at the time of ordering, had to be shipped on a “Slow boat from China” and took over a MONTH to arrive at my doorstep.  Whilst waiting, I took the gas can and held it against the back of the tractor and contemplated possible ways to attach it and exactly how it should go on.  Connecting the tractor’s fuel line would be straight forward – it was just behind the back of the tractor connecting to the old gas can, removing it from the old can and turning it around to bend over the back meant it just perfectly reached over where it could connect to the top of the new can, provided I put the hole and the new brass fitting in just the right spot. 

Legines 6mm Brass Hose Barb FittingFortunately for me, we’ve been “blessed” this Summer with an extreme drought (literally no measurable rain in the month of June).  While all my Church brethren have been praying for rain every Sunday this Summer, I’ve been silently thanking the Lord for the drought as I had no way to mow the grass, which would quickly grow two feet within days of the next soaking rain!  When the brass fitting finally landed in my mailbox, I took it down to the auto parts store down the street and obtained a free foot-or-so long piece of matching rubber fuel line left over that was too short for them to sell, I guess, and quickly measured and drilled the requisite hole in the tank near the main opening and then realized another minor difficulty.  That was how to attach the aforementioned piece of hose internally and then attach the brass fitting onto the tank.  Both of these steps are easy-peasy themselves.  The “hard” part is that one must either attach the fitting first, then attach the hose inside where it’s difficult (impossible it turns out) to reach and get enough force on it to push it onto the fitting; or:  attach the hose first, then get it through the hole from underneath and hold it tight enough to twist down the nut properly (difficult, but doable, it turned out).  I ended up going he latter way, allowing me to also install a small hose-clamp over it, but first I had to run a long piece of tiny but reasonably rigid wire through the hose and make a small hook at the base, place that into the tank, then get the top end of the wire through the hole from inside to pull the bolt up through the hole and attach the nut to it.  Then I got my wife to firmly hold down the tank to the table while, with both hands twisting the nut on with pliers while straining to keep the bolt pulled through and perpendicular to the surface of the tank, which was unexpectedly difficult.  Once properly secure with a small O-ring washer under the nut, I pushed the wire with the hook on the bottom down and back up to remove it from the hose, closed up the tank and tried to blow into the brass end now protruding from the top to ensure it was air-tight, and it was ready to go!  Tools needed for all this: 2 small screw-on hose-clamps, one small rubber O-ring washer (to fit over the threaded part of the fitting), electric drill, pliers, flat-blade screwdriver, metal file, needle-nosed pliers, and box-blade (to cut rubber fuel-line “straw” to proper length).
My New Jury-rigged replacement Gas Can
“My New Jury-rigged replacement Gas Can”, by (© 2018) me.  (Click for larger image)
The one thing I had not thoroughly thought thru was exactly how to mount the tank to the tractor.  RotopaX sells some really nice metal brackets with twist-on handles just for this purpose, but they are expen$ive and must be bolted on, which I found was going to be a steep mountain to climb as 1) the metal plate on the back of the tractor between the original gas tank and the outside world was very heavy gauge, nearly 3/32″ thick steal, thicker I believe than some car bumpers these days.  2) there was no way to get behind it to attach any bolts!  Therefore, I was reduced to having to find a way to “strap” it on.  My thoughts had been using some sort of combination of bungee-cords, since there were numerous holes, brackets, gaps, etc. around the sides of the tractor body to attach them to, if I could find cords with the right lengths, which ended up not being difficult to do at all, at my local Wal-Mart!  So I obtained a “kit” composed of several cords of slightly-varying lengths and was able to quickly get it strapped on where it wouldn’t slide around in any direction or be jarred off.  I then, using another small hose clamp, attached the tractor’s fuel line to the protruding brass nipple I had created and filled her up with gas, sealed the lid and left it!  I sniffed around for fumes, closed the barn and left.  I came back next day and could not smell any fumes – SUCCESS!

The final step was to actually try it out.  I aired up the tires, charged the battery, and then opened up the cap slightly to allow air to come in but not enough to slosh out any gas.  This is easy to control because these new-fangled environmentally-correct eco-friendly gas cans have caps that lock into place and have gear-like teeth such that you can loosen it slightly and the teeth hold it exactly how much you loosened it and no more!  I expected the mower to require a lot of cranking in order to draw gas up into the now long-empty fuel line, but it fired right up almost as soon as I began cranking and ran perfectly!  I backed it out, got off, checked for leaks, drove out to the pasture and mowed one time around.  I then checked the bungee cords and made sure it was holding steady and snug, and again checked for any leaking or spilling gas – NONE!  I continued mowing without issue.  Now, I knew that this gas can was small (1 gal) and, due to the way it was mounted (horizontally), and with the intake diagonally on the corner, it would only hold about 3/4 gallon of fuel, so I knew I would have to gas up more frequently, but, after barely ten minutes (so it seemed) of mowing, I suddenly ran out of gas!  Sure enough, that was the case (no other problems), so I refilled and resumed.  NOTE:  Refilling is a bit tricky getting up under the back of the tractor-body to get to the opening, and being able to see when it’s almost full without overflowing it.  Fortunately for me, I have an old-style non eco-friendly gas can with a long, flexible snout that I can reach it with!  I managed to finish what little mowing that was needed and park it back in the barn before it ran out again though, as it STILL has NOT rained since this saga began in June, and it is now early August.  So, now I too, am praying for RAIN!

/end

Austerity – What is it, Really?

Thousands of anti-austerity protesters are expected to march in London today - Reuters
Anti-austerity protesters in London (June 2015)
image courtesy (© 2015) Reuters, via EveningStandard, (story: June 20, 2015).
A word often heard in the media, particularly in regards to foreign countries and usually associated with massive and sometimes violent protests is “Austerity”.  The dictionary defines it as a “Severe and rigid economy” and most people associate it with lean times and tight fiscal policies by governments. Austerity is actually a politically-loaded term and keyword.  Whenever you hear the word associated with a nation (government) or other governmental entity, it should indicate to you that said government is a “socialist” (redistributionist) government (one that is in the business of stealing from it’s productive citizens and enterprises, and “redistributing” it to those less productive).  Such a government has legalized plunder!  You will almost never hear the term or see it protested in a free society, hence the reason you have not traditionally heard it in reference to the United States.  The (often violent) protests are by those on the receiving end of the legalized plunder and “redistribution”.  It begins with the emotional appeal by “progressives” for the government to create “entitlements” for certain “groups” of people (to achieve “social justice”), then either robs (taxes) people (directly) not in these groups, borrows fiat money from a (private) “central bank” (The “Federal Reserve” is NEITHER!) that is more than happy to create “money” out of thin air and “lend” it to said government (AT INTEREST), thus robbing the industrious “non-entitled” and “entitled” alike (indirectly) through currency “inflation”!  When taxes and / or inflation rise too rapidly, the “non-entitled” start to complain too much as “the economy” starts to stall under the (too rapidly) rising weight of debt, taxes and plunder.  The government will sometimes implement “austerity” measures in attempt to countermand these symptoms (but does nothing to fix the real disease).  What this often involves is a (usually temporary) reduction in the GROWTH of “entitlements” or restrictions on the eligibility to join the “entitled” groups.  This, of course leads naturally to the “entitled” groups and the wannabees now blocked screaming in protest like stuck pigs!  They do have a legitimate reason for their protests – their real physical LIVELIHOOD is truly threatened just as much as a farmer who’s lost his crop or a working class person who’s a sole breadwinner for his family and has suddenly lost his job!  They have indeed become DEPENDENT on the largesse from the plunder of their fellow citizens.  Usually, after a period of time, however, the government is once again able to come to some type of “compromize” with all it’s “stakeholders” (the “entitled”, the “taxpayers”, and the “central bankers” / IMF who are counting on the free interest on the ever-increasing “debt”) that appears to partially ease some of the “pain” for each group, the “austerity” is ended or reduced, and the economy “recovers”, rinse, spit, repeat.
I must emphasize that “Austerity” is only applicable to and enacted by SOCIALIST governments.  Again, this is why you hear of it in places like Europe, Latin America, and (despite state-control of media) Communist countries but NOT the U.S., though with ever-growing entitlement programs like Social(ist) (in)Security, and Obamacare (now wedged into and very entrenched), you may likely start hearing it more here too.  They haven’t used the word itself yet, but the howls by the Democrats AND “moderate” (liberal, RINO) “Republicans”, indicate the advancing spread of the disease of socialism here.  You can see the most vocal opponents in the states that foolishly expanded their “medicare” entitlements under Obamacare, whereas states that didn’t have nothing to fear as fewer of their citizens have become “dependent” on it.  The feds GIVE and (then) the feds TAKE AWAY!  It is much easier for a (once) free society’s government to create entitlements than it is to later have to remove them!  This is why a well-educated (morally, economically and politically) citizenry is absolutely NECESSARY to avoid these pitfalls and maintain a free society! Temptation resisted is evil and suffering avoided.
In a free society, the government maintains a stable and constant level of “austerity” (as viewed by socialists) as indeed it should.  A free society eschews the creation of “entitlements” based on envy and plunder and sticks to the actual duties of government:  the common defense, punishment of crimes, enforcement of contracts and the rule of law, a uniform system of weights, measures, and currency (backed by substance, ie. gold and silver), and equal protection of everyone under the laws from violence and plunder.  It is up to families, churches, charitable organizations, benevolent and prosperous citizens, and local governments to tend to the needs of their fellowmen who are unable to provide for themselves.  By not creating “entitlements” by “protected” groups of citizens by granting them a claim on the property and labor of others, dependency and societal volatility are avoided.  This, along with maintaining a stable currency based on real substance and value (ie. gold and silver) and the subsequent avoidance of debt allow for economic stability to be maintained with the highest level of prosperity for the highest percentage of people.  Simply put, if you don’t create “entitled” groups and dependence, you don’t ever have to “take anything away” (austerity) resulting in massive howls and violent protests!  Government can’t take away anything it doesn’t first steal and then give away!  This also reduces the need for government expansion, debt, constant lobbying of government, government interference in the economy, and the dividing of citizens into opposing groups promoting peace and harmony.

Beautiful Vintage ’70s-era Two-piece Nozzle Set by Nelson

Beautiful vintage 2-piece nozzle set by NelsonI purchased this beautiful two-piece nozzle set with S&H Greenstamps way back in the day (late 1970’s when I was a teen).  The one on the right is solid brass.  The other is only painted brass, but solid metal with a solid brass nozzle head.  Both are very well-made and lasted well over twenty years of frequent use before it began leaking.  When the brass one began leaking, I simply unscreawed it and replaced the single O-ring inside, which was a standard size included in a typical O-ring set I purchased for a couple of bucks at the big-box home improvement store.  I tried to fix the other one by attempting to unscrew the nozzle head.  Unfortunately it was NOT attached with threads but somehow pressed or glued on such that once it came off, it could not be securely reattached, but instead pops right off when water pressure is applied.  Despite this, I’ve never had another handled nozzle work as well nor last nearly as long as this little beauty!  I’ve actually never seen another one like it either.  It appears that the brass nozzle was made by Nelson based on the worn logo shown on the base, but the other one has no information written anywhere on it, so I assume it’s also Nelson.  I looked in Google images for vintage nozzles and found numerous photos of nozzles like the brass one, but nothing resembling the “golden gun”, which was always my favorate!  In the photos below, you can see the one plastic piece, along with the separated nozzle head in the “gun” nozzle along with the O-ring in the brass one.  I’m very OPEN to any comments / suggestions on how to properly reattach the nozzle head to the “gun” one and from anyone else who still has one of these sets!
Nozzle Parts 1 of 2 Nozzle Parts 2 of 2
%d bloggers like this: