Setting up a Gentoo-Based Dual-Stack Router

Our network has been based around a home-built router for quite some time, ever since I got fed up with the crappy ActionTec router that Verizon bundled with our FiOS service. (If you’re going to offer high-speed internet, you should probably bundle equipment that can actually keep up.) I had originally followed a slightly older version of these instructions to get a nice basic router going. But I finally wanted better. I wanted the bright, shiny, new thing. I wanted IPv6.

So, here’s my instructions for going from an existing IPv4 router to dual-stack IPv4/6.

Note: I am using dnsmasq for DNS and DHCP, hostapd for wireless management, and an iptables firewall. Since Verizon still doesn’t widely support consumer IPv6, I’m using a tunnel broker to get my /6 address. If you’re using a different setup your mileage may vary. If you find anything that I appear to have forgotten, please let me know!

Step 1: Recompile the Kernel

This should be obvious: if you want to run ipv6 you need ipv6 support in your kernel. In order to trim as much off my kernel as possible I did not have it built in, and had to recompile.

You should also add netfilter support for ipv6 so that your firewall will work.

Networking support  --->
    Networking options  --->
        <*>   The IPv6 protocol  --->
            <*>   IPv6: IPv6-in-IPv4 tunnel (SIT driver)
        [*] Network packet filtering framework (Netfilter)  --->
            IPv6: Netfilter Configuration  --->
                <M> IPv6 NAT
                <M> IP6 tables support (required for filtering)
                <M>   Packet filtering
                <M>   ip6tables NAT support
                <M>     MASQUERADE target support
                ... other filtering options as you may need for your situation

Step 2: Update Your IPv6 Support

Again, it was never compiled in, in order to trim off unused bits of code. Add ‘ipv6’ to your USE variable and emerge --newuse world

Step 3: Install network tools (if they aren’t already)

emerge --noreplace sys-apps/iproute2 net-firewall/iptables

Step 4 (optional): Set up your tunnel

If your ISP doesn’t provide ipv6, and many don’t, you need to request an address range from a tunnel broker. I’m using Hurricane Electric, which is free, but there are others — see this list or just google it.

If you have multiple machines on your network (which is assumed, since this is a router guide), you may prefer a /48, so that autoconfig works nicely, instead of the default /64. This guide assumes a /48.

Going forward, replace 2001:470:891a: with your own /48 range.

Now activate your tunnel:

ip tunnel add he-ipv6 mode sit remote 1.2.3.4 local 5.6.7.8 ttl 255
ip link set he-ipv6 up
ip addr add 2001:470:1f06:2a3::2/64 dev he-ipv6
ip route add ::/0 dev he-ipv6
ip -f inet6 addr

Step 5: Update Your Net Config

I have two wired and one wireless card in my router. Here’s what my /etc/conf.d/net looks like:

# enp2s0 is my exterior wired nic (aka public facing)
# enp3s5 is my interior wired nic
# wlp3s6 is my interior wireless nic

dhcp_enp2s0="nodns" # we choose our own DNS, tyvm

config_enp3s5="192.168.0.1/24 2001:470:891a:0::/64"

modules_wlp3s6="!iwconfig !wpa_supplicant"
config_wlp3s6="192.168.1.1/24 2001:470:891a:1::/48"
dns_servers_wlp3s6="127.0.0.1"

After making appropriate changes, restart your NICs. If you’re working remotely, you may want to be connected via two paths instead of just one (so when you inevitably get bounced and can’t reconnect, you still have a way back in).

A properly-configured set of addresses looks like this:

# ip addr
1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 brd 127.255.255.255 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp2s0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:e0:4d:bf:03:f5 brd ff:ff:ff:ff:ff:ff
    inet 108.20.118.17/24 brd 108.20.118.255 scope global enp2s0
       valid_lft forever preferred_lft forever
    inet6 fe80::cbdf:25c0:c948:f4bb/64 scope link
       valid_lft forever preferred_lft forever
3: enp3s5: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/ether 00:04:5a:42:a6:98 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.1/24 brd 192.168.0.255 scope global enp3s5
       valid_lft forever preferred_lft forever
    inet6 2001:470:891a::/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::204:5aff:fe42:a698/64 scope link
       valid_lft forever preferred_lft forever
4: wlp3s6: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:1a:ef:07:4d:a7 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.1/24 brd 192.168.1.255 scope global wlp3s6
       valid_lft forever preferred_lft forever
    inet6 2001:470:891a:1::/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::21a:efff:fe07:4da7/64 scope link
       valid_lft forever preferred_lft forever
5: sit0@NONE:  mtu 1480 qdisc noop state DOWN group default
    link/sit 0.0.0.0 brd 0.0.0.0
6: he-ipv6@NONE: &lt;POINTOPOINT,NOARP,UP,LOWER_UP&gt; mtu 1480 qdisc noqueue state UNKNOWN group default
    link/sit 108.20.118.17 peer 209.51.161.14
    inet6 2001:470:1f06:2a3::2/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::6c14:7611/64 scope link
       valid_lft forever preferred_lft forever

Test it with a ping:

ping6 www.kame.net

Step 6: Reconfigure dnsmasq

You’ll need to add router advertisments and your new addresses. Rather than hard-coding an address, dnsmasq offers a ‘constructor’ label which figures it out automatically. Here’s the relevant section from my /etc/dnsmasq.conf:

domain-needed
bogus-priv
domain=jonesling.us
dhcp-authoritative
enable-ra
dhcp-range=192.168.0.20,192.168.0.100,12h
dhcp-range=192.168.1.20,192.168.1.100,12h
dhcp-range=192.168.2.20,192.168.2.100,12h
dhcp-range=::,constructor:enp3s5,ra-names,slaac,12h
dhcp-range=::,constructor:wlp3s6,ra-names,slaac,12h
resolv-file=/etc/resolv.dnsmasq
selfmx
enable-ra

And restart it: /etc/init.d/dnsmasq restart

Step 7: Configure your firewall

Pretty much every iptables reference in your firewall config will be mirrored with an ip6tables command.

Here’s my script to set up iptables (if you see an error or something stupid, I would appreciate your criticism – paired with your reasoning on why it should be changed so I can know better for next time).

#!/bin/bash
# based on http://www.gentoo.org/doc/en/home-router-howto.xml

# set to '0' to lock the kids out
OPEN_INTERNET=1

# these systems can get shut out when OPEN_INTERNET isn't true
declare -a NO_SURFING=( 'wii-u'
                        'kids-computer'
                      )

# these systems never get shut out
declare -a OK_SURFING=( 'parents-computer'
                        'parents-phone'
                      )

# these ports take precedence over CLOSED_PORTS
declare -a OPEN_TCP_PORTS=( 'ssh'
                            'http'
                            'mail'
                            'submission'
                          )

declare -a OPEN_UDP_PORTS=( 'submission' )

# if the port is meant to be closed, we close tcp *AND* udp
declare -a CLOSED_PORTS=( '0:1055'
                          'svn'
                          'distcc'
                          'x11'
                          'nfs'
                          'icpv2'
                          'mysql'
                          'rtsp'
                          '3128' # squid
                          '3130' # squid ICP
                          '3551' # nisport
                        )

declare -a LAN_SERVICES=( "svn" )

# blacklisted IPs and ranges
# http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xhtml
declare -a IP_BLACKLIST=( # APINIC
                          # AFRINIC
                          # LACNIC
                          ...
                        )

LAN=enp3s5
WLAN=wlp3s6
WAN=enp2s0
SIT=he-ipv6

INSIDE=( $LAN $WLAN )

LOCAL_RANGE_IPV4='192.168.0.0/16'
LOCAL_RANGE_IPV6='2001:470:891a::'

# First we flush our current rules
iptables -F
iptables -t nat -F
ip6tables -F
ip6tables -t nat -F

# Setup default policies to handle unmatched traffic
iptables  -P INPUT ACCEPT
iptables  -P OUTPUT ACCEPT
iptables  -P FORWARD DROP
ip6tables -P INPUT ACCEPT
ip6tables -P OUTPUT ACCEPT
ip6tables -P FORWARD DROP

# Then we lock our services so they only work from the LAN
iptables  -I INPUT 1 -i ${LAN}  -j ACCEPT
iptables  -I INPUT 1 -i ${WLAN} -j ACCEPT
iptables  -I INPUT 1 -i lo      -j ACCEPT
ip6tables -I INPUT 1 -i ${LAN}  -j ACCEPT
ip6tables -I INPUT 1 -i ${WLAN} -j ACCEPT
ip6tables -I INPUT 1 -i lo      -j ACCEPT

# block members of IP_BLACKLIST, plus any addresses passed in on the
# command line
for IP in ${IP_BLACKLIST[@]} ; do
    iptables -I INPUT -s ${IP} -p TCP --dport ssh -j DROP
done

for IP in $@; do
    iptables -I INPUT -s ${IP} -d 0/0 -j REJECT
done

iptables  -A INPUT -p UDP --dport bootps -i ${WAN} -j REJECT
ip6tables -A INPUT -p UDP --dport bootps -i ${SIT} -j REJECT
iptables  -A INPUT -p UDP --dport domain -i ${WAN} -j REJECT
ip6tables -A INPUT -p UDP --dport domain -i ${SIT} -j REJECT

# Explicitly allow access to services on the WAN
for SERVICE in ${LAN_SERVICES[@]} ; do
    for IFACE in ${INSIDE[@]} ; do
        iptables  -A INPUT -p TCP --dport svn -i ${IFACE} -j ACCEPT
        iptables  -A INPUT -p UDP --dport svn -i ${IFACE} -j ACCEPT
        ip6tables -A INPUT -p TCP --dport svn -i ${IFACE} -j ACCEPT
        ip6tables -A INPUT -p UDP --dport svn -i ${IFACE} -j ACCEPT
    done
done

# Allow access to our server from the WAN
for PORT in ${OPEN_TCP_PORTS[@]} ; do
    iptables  -A INPUT -p TCP --dport $PORT -i ${WAN} -j ACCEPT
    ip6tables -A INPUT -p TCP --dport $PORT -i ${SIT} -j ACCEPT
done

for PORT in ${OPEN_UPD_PORTS[@]} ; do
    iptables  -A INPUT -p UDP --dport PORT -i ${WAN} -j ACCEPT
    ip6tables -A INPUT -p UDP --dport PORT -i ${SIT} -j ACCEPT
done

# Drop TCP / UDP packets to privileged ports
for PORT in ${CLOSED_PORTS[@]} ; do
    iptables  -A INPUT -p TCP -i ${WAN} -d 0/0 --dport ${PORT} -j DROP
    ip6tables -A INPUT -p TCP -i ${SIT} -d 0/0 --dport ${PORT} -j DROP

    iptables  -A INPUT -p UDP -i ${WAN} -d 0/0 --dport ${PORT} -j DROP
    ip6tables -A INPUT -p UDP -i ${SIT} -d 0/0 --dport ${PORT} -j DROP
done

iptables  -I FORWARD -i ${LAN} -d $LOCAL_RANGE_IPV4 -j ACCEPT
iptables  -A FORWARD -i ${LAN} -s $LOCAL_RANGE_IPV4 -j ACCEPT
ip6tables -I FORWARD -i ${LAN} -d $LOCAL_RANGE_IPV6 -j ACCEPT
ip6tables -A FORWARD -i ${LAN} -s $LOCAL_RANGE_IPV6 -j ACCEPT

if (( OPEN_INTERNET )); then
    echo 'yay, everybody gets internet'
    iptables  -I FORWARD -i ${WLAN} -d $LOCAL_RANGE_IPV4 -j ACCEPT
    iptables  -A FORWARD -i ${WLAN} -s $LOCAL_RANGE_IPV4 -j ACCEPT
    ip6tables -I FORWARD -i ${WLAN} -d $LOCAL_RANGE_IPV6 -j ACCEPT
    ip6tables -A FORWARD -i ${WLAN} -s $LOCAL_RANGE_IPV6 -j ACCEPT
else
    echo "boo, only ${OK_SURFING[@]} get internet"
    for IP in ${OK_SURFING[@]}; do
        iptables -I FORWARD -i ${WLAN} -d $IP/255.255.255.255 -j ACCEPT
        iptables -A FORWARD -i ${WLAN} -s $IP/255.255.255.255 -j ACCEPT
    done
fi

iptables  -A FORWARD -i ${WAN} -d $LOCAL_RANGE_IPV4 -j ACCEPT
ip6tables -A FORWARD -i ${WAN} -d $LOCAL_RANGE_IPV6 -j ACCEPT

iptables  -t nat -A POSTROUTING -o ${WAN} -j MASQUERADE
ip6tables -t nat -A POSTROUTING -o ${SIT} -j MASQUERADE

# This is so when we boot we don't have to run the rules by hand
/etc/init.d/iptables save
/etc/init.d/ip6tables save

# fail2ban should be reloaded after flushing iptables
/etc/init.d/fail2ban reload

Step 8: Update your DNS

Add a AAAA record to your domain’s DNS record.  You may have to keep this one up-to-date yourself.

Interesting to note: you might be thinking “crap, what’s the ipv6 equivalent of these CNAME records?”  Stop worrying, there isn’t.  The CNAME is read like normal, but ipv6 clients will then look up the AAAA (instead of the A) record of the destination host.  It just works.

What?  You built your own router but you don’t have your own domain?  WTF is wrong with you?

Step 9: Reboot your clients

While I was working, I made a bunch of mistakes and my clients had multiple ipv6 addresses – making networking from them unstable as they didn’t necessarily know which address to use. Rebooting will clear them – and make sure your config is proper.

At this point your clients should be in ipv6 and you’re gonna be all excited to see if work.  Browsers take ipv6 addresses a little differently: http://[2001:470:1f06:2a3::2]/

IPv6

If you’re white and nerdy, like me, you know that your small victories aren’t like other peoples’ small victories.  Today’s small victory is IPv6.

I has it.

At this very moment, this blog can be served to you, or may already be served to you, over IPv6 if you have it too.

Setting it up on your home-built router isn’t straight-forward, especially if your ISP doesn’t offer IPv6 – you have to find a tunnel broker.  (I’m using Hurricane Electric, which provides free /64 and /48 tunnels.)  Clients seem to work fairly automatically.  Have fun figuring out all the little things you need to tweak on your router, though.

Things to note:

  • hostapd seems to knock off the IPv6 address of your wireless NIC when you start it – you need to re-add the address by hand, like this:
    ifconfig wlp3s6 inet6 add 2001:470:891a::/48
  • dnsmasq has a special tag to automatically read addresses from devices, called ‘constructor’, which is easier than copying your dynamic tunnel everywhere:
    dhcp-range=::,constructor:wlp3s6,ra-names,slaac,12h
  • You may use the IPv6 equivalent of ‘private’ IP addresses, but you don’t need to anymore.
  • dyn.com hides their non-typical DNS record types, and you have to enable the ‘expert interface’ to see AAAA and other record types, but otherwise there’s no difference in setting up dynamic host addressing.
    • One quirk that may not be immediately obvious: You don’t need to have separate IPv6 CNAME records.  An IPv6 client will check the CNAME, pull the destination hostname, then pull the AAAA record.
  • Most network tools have IPv6 equivalents – ping doesn’t work with IPv6 addresses, but ping6 does.
  • There’s a special format for using an IPv6 address in a web browser: http://[2001:470:1f06:2a3::2]/ if you go direct to the blog’s ipv6 address today.

But besides all that, it really works!

$ ping6 -c1 jonesling.us
PING jonesling.us(quinnjones-2-pt.tunnel.tserv4.nyc4.ipv6.he.net) 56 data bytes
64 bytes from quinnjones-2-pt.tunnel.tserv4.nyc4.ipv6.he.net: icmp_seq=1 ttl=64 time=0.508 ms

--- jonesling.us ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.508/0.508/0.508/0.000 ms

Flossing

i have flossed very regularly since my previous dental cleaning six months ago – at least twice a week, every week.  I have never flossed regularly before.  I always brush, but I never really flossed because I was lazy.

I had another cleaning today, and for the first time ever my teeth don’t feel funny.

I rate this experience 9/10, will floss again.

Airprint woes

It’s all my fault, really.  This wouldn’t have been an issue if I had just let Xtina use my computer to print her boarding pass, but in my defense I didn’t know that she was doing that.  So I gave her our iPad to use.

When it came time to print, she quite logically asked me how she would do that.  I, of course, did not know how — I’ve never tried printing from iPad or smartphone, though I vaguely knew it was possible.  The issue just never came up and I hate printers.

I knew that it would require avahi, so I started installing that on our printserver while I hit Google to see what else I would need.

The first hit was a very fine article by Linux Magazine, and it explained pretty much everything.  But it’s never that simple, because nothing printed and cups started using 100% of a CPU.

Repeated in the /var/log/cups/error_log a billion times were messages like these:

Request from "192.168.1.32" using invalid Host: field "dandelion.local:631"

That took a little more detective work because I didn’t read the Linux Magazine article carefully enough.  The solution was to add an additional directive to the cups config:

--- /backup/snapshots/dandelion.0/etc/cups/cupsd.conf   2015-06-08 08:33:31.000000000 -0400
+++ /etc/cups/cupsd.conf        2015-06-24 19:29:34.410488191 -0400
@@ -1,6 +1,7 @@
 LogLevel warn
 PageLogFormat
 # Allow remote access
+ServerAlias *
 Port 631
 Listen /run/cups/cups.sock
 # Share local printers on the local network.

Summary:

Gentoo packages required:

  • net-print/cups
  • net-dns/avahi

Also download and run airprint-generate after cups is configured and running.

If you have iOS 6+, which is pretty much a given nowadays, make sure you have the correct MIME types available, and add them if not:

echo 'image/urf urf string(0,UNIRAST<00>)' > \
    /usr/share/cups/mime/airprint.types
echo 'image/urf application/pdf 100 pdftoraster' > \
    /usr/share/cups/mime/airprint.convs

Add the appropriate services to your default runlevel, and start them as well:

# rc-update add cupsd default
# rc-update add cups-browsed default
# rc-update add avahi-daemon default

Offshoring Gone Wrong

Here’s a tale of offshoring gone wrong.  This doesn’t qualify as horribly wrong, nor a disaster, but only because very little money was on the line.

I used to work for a small software company with a well-known product that has a long pedigree (it shall remain nameless, but our major competitor was WinRar).  I actually miss working there —  well, I miss most of it, but I did leave voluntarily.  That’s a story for another time.

We had started translating our primary product into many languages, and we wanted to provide localized translations of our website as well.  In order to save some cash, management decided that we would outsource and offshore the translation of our company website.  Our new president knew of the perfect company to hire, too.

My boss — the VP — and the rest of the engineering and IT team were all a little nervous about dealing with this new company, not only because we didn’t have a great way to verify the work but also because we didn’t have a good relationship with the new president. (Distrust isn’t strong enough a word, but it describes it well enough for this story.)  The first couple of sub-projects came back and looked ok, though, so we started to think we were over-worrying the problem.

Our process was to scrape our own english site, determine which pages and what snippets we would translate, and send those items as plain-text to the translators.  After a couple of days we would start getting the translated documents back and we would build the site.

We had a few bumps along the way, such as getting plain-text documents with an unspecified code-page — we had asked for, but didn’t initially get, UTF-8, but we eventually had them send us the documents in Word to remove character-translation problems — but the process seemed to be working overall.  We ran the the translated documents through Google Translate to make sure the reverse translation (back to English) looked ok, and it did.  In retrospect, it was a little too perfect.

So, fast forward a couple of weeks, we get the third or fourth package back. My boss noticed something… odd on one of the pages. It was worth calling the rest of the team into the office to check it out, stat!

If you guessed that it was an artifact from Google Translate’s page – just a straight copy and paste from browser to Word document that picked up a little too much – you’d be correct.  Cue immediate back-pedalling from the vendor that “it was just that one document” and “the other translations were done by hand” and by native speakers.  Haha, not so much.


Author’s Note: Though this post may seem, at first glance, to be a warning against offshoring, it’s really a warning about hiring executives with too-cozy relationships with vendors.  I’ve seen offshore projects go well and go sour, but the nepotism I saw with the above-mentioned new company president were almost always followed by a bitter taste in our mouths.

On Minimum Wage and the Post-Scarcity Economy

8 Hours for Work, 8 Hours for Rest, 8 Hours for What We Will

Lately, it seems like everyone is talking about the minimum wage.  Should we raise it?  Should it stay where it is?  Should we lower it, or even abolish it?  This looks like it will be a major question in the upcoming presidential campaign, as the candidates have already staked positions (with some candidates making it part of their platform).

It seems that everyone has a strong opinion on this.  We can all agree that the outcome will have real consequences but no-one agrees on what the outcomes will be.

Smart people, with real data to back up their claim, may predict any of the following:

ScenarioEffect on the Economy
Raise The Minimum Wage (Doom Scenario)Prices rise, employment falls as businesses stop hiring and even trim their workforce to meet payroll costs. Wide-spread unemployment coupled with higher prices wrecks the economy.
Raise the Minimum Wage (Sunny Scenario)Profits rise as minimum wage workers, now with more money in their pockets, go on a spending spree. Lost profits are made up by increased volume. Some people trade 60+ hour work-weeks for more leisure time, which means there are more jobs to go around just as demand picks up – unemployment falls rapidly and wages generally get a bump – but competition keeps prices down.
Keep the Minimum Wage the Same (Doom Scenario)Stalemate: as the spending power of the dollar slowly falls over time, minimum wage workers slide further into poverty. Demand for welfare services and charity slowly rise alongside. The economy stagnates as a permanent class of have-nots emerges.
Keep the Minimum Wage the Same (Don’t Rock the Boat Scenario)Stalemate: the economy keeps chugging along without major disruption. Nobody is happy, but things remain stable.
Keep the Minimum Wage the Same (Sunny Scenario)With the certainty of stable wages, business are better able to plan ahead. Productivity gains translate to better wages as a reward – for those able to best achieve those gains.
Lower or Abolish the Minimum Wage (Doom Scenario)The average worker desperately agrees to more and more work for less and less pay, in a vicious race to the bottom.  Oligarchs take over the country and the middle class disappears.
Lower or Abolish the Minimum Wage (Sunny Scenario)Businesses pay dilettante workers (high school kids, mostly) much lower wages, but adults make significantly more; the money saved by paying unproductive workers less are passed on to consumers, so most people see their dollars go further.

Every choice has at least two contradictory outcomes.  They can’t all be right.

Those arguments are almost beside the point, however.  What we’re really arguing about is what happens as we approach, but haven’t quite reached, a post-scarcity economy.

An easy definition of a post-scarcity economy that many people can relate to is Star Trek: there’s money, but it’s de-emphasized because everyone gets what they need to live. No guarantees of easy meet-ups with green women as you aimlessly flit around in spaceships for five-year missions, though. See also: The Star Trek Economy: (Mostly) Post-Scarcity (Mostly) Socialism

(The wikipedia entry doesn’t do justice to the concept of post-scarcity.  It’s not exactly Utopia, and it’s not unlimited wealth.  It’s the ability to produce enough material wealth, at near-zero cost, to provide a reasonable standard of living to everyone.  It’s like Europe without the taxes.)

Across the economy, per-person productivity is rising at logarithmic rates. Consider cars: they used to take days, literally days, to manufacture.  In the early 21st century manufacturers produce a technologically-superior car in about two shifts – 16 hours.  One factory, with fewer people, can make over 400% more cars than a century ago.

We’re continually figuring out how to improve our business processes to make them more efficient, and using technology to supplant our workforce as well.  Self checkout lines at the supermarket easily come to mind.  There are now McDonalds restaurants where you order from a kiosk instead of a cashier.  We bank online instead of writing checks.

This is clearly what people want.  Technology makes our lives easier and more efficient when we don’t have to drive to the bank, or worry that our food order wasn’t understood correctly.

Bookkeeping department circa 1900
Bookkeeping department. Listed by source as c. 1900, but likely to be 1890s. Bookkeeping is being done by writing by hand in ledger books. Credit: http://www.officemuseum.com/photo_gallery_1890s_ii.htm

Technology saves us from the most dreary drudge work, like totalling accounting ledgers or weaving fabric by hand.

As mechanisation progresses, and more of our needs are met with fewer people involved, what happens to the rest of the people?  That’s really what the minimum wage argument is about – the lessening demand for labor.  As productivity rises, meaning we make more stuff for the same amount of labor, demand for labor (a.k.a. workers) decreases over time.  The law of supply and demand tells us that lower demand leads to lower prices (wages).

What do we do when we don’t need much of anybody to feed and clothe us? Our population isn’t growing so demand will not rise, and that means you can’t make up the difference in extra productivity with extra demand.  What do you do then?


Author’s Note: My intent is to pose ideas and ask questions, letting the reader answer them.  I’m purposely taking an apolitical view of the minimum wage in this article; if you can point out bias on my part I will gladly fix it.

My only assertion is that a post-scarcity economy may be looming.  I’m not claiming that the sky is falling.  Technological change has changed the labor landscape before and people worried about lost jobs, some even lost their jobs, but somehow new work came along.

We have a long history of upheaval caused by technology, with bouts of low employment followed by booms of full employment.  Ask any stable-hand or stone axe maker how their employment prospects are nowadays, and then introduce them to your local programmer or aerospace engineer to make them cry.

New technology replaces old.  A century ago a computer had barely been dreamed of by a few – thank you Mr. Babbage – but today that’s how I make my living.  That’s technological progress.

I believe that an upcoming cycle in the not-so-distant future may be different, in that afterwards we will have entered a post-scarcity economy – and we probably won’t even realize it until after the fact.  We’re not going to live in a utopia yet, but that day may come to pass while some of us are still in living memory.

The Troublesome Broadcast Message

Back in the good-ole days of Windows NT (circa 1998) I was a member of IT support at a large multi-national corporation.  The campus I worked for was about five thousand people large.

Background: Windows 98/98/NT 4.0 had a neat little utility to send pop-up messages to specific machines.  It was a front end to the net send built-in command, and messages would appear almost instantaneously on the recipient’s machine in nice little window.  (Similar functionality still exists in more recent versions of Windows, but the messenger service no longer starts by default.)

So, one slow day a bunch of us were shooting the shit and getting a little rowdy.  I think there were some flying objects and maybe a nerf gun involved.  One of the upper-level techs, who shall remain unnamed, fired off a message to someone else: “John, look out behind you”.

Only, he didn’t get the machine name right.  He broadcast it to the entire campus.  5000+ machines.

A lesser-known feature of the net send command, and therefore of the messenger utility, was the ability to message an entire workgroup or domain.  To do so, you only need to specify the workgroup or domain name in the recipient box.  And that’s what he did – he intended John’s machine name but the domain was the default in the box — and he forgot to change it.

Hoo boy, that was some trouble, and being a political organization it nearly took the form of someone’s-getting-fired-type trouble.  It took the ‘lizard king’ email storm to finally let it die down completely.  I’ll save that story for another day, when I dig the entire email chain out of archives and obfuscate some details.