This post describes one way to quickly set up a two-computer network such that both machines on the network can access the outside internet. The use case in mind here is you’re at a cafe with a friend, the wifi there sucks, you have your mobile broadband connection and you want to share it with your friend.

Note that there are many ways to get to the desired end result here. This solution has your local laptop doing NAT and forwarding packets at the IP layer.

Supplies:

  • your debian/ubuntu laptop
  • a “mobile broadband” connection with some provider, like Verizon or T-Mobile
  • an ethernet cord

Step 0: Get your mobile broadband connection working, possibly with T-Mobile.

Step 1: Set up your machine to run a dhcp server for your local wired network

  • sudo apt-get install isc-dhcp-server
  • Edit /etc/dhcp/dhcpd.conf. Here are the relevant parts of mine. (Yes, the ‘authoritative’ directive is commented out, I’m not sure how essential this is or not – but I’m trying avoid the dhcp server from taking over my local machine’s default route.)
    default-lease-time 600;
    max-lease-time 7200;
    #authoritative;
    log-facility syslog;
    subnet 172.16.16.0 netmask 255.255.255.0 {
      range 172.16.16.10 172.16.16.250;
      option domain-name-servers 208.67.222.222; # opendns
      option routers 172.16.16.1;
    }
    
  • Edit /etc/defaults/isc-dhcp-server:
    mike@110psi:$ cat /etc/default/isc-dhcp-server | tail -n 1
    INTERFACES="eth4"
    
  • sudo ifconfig eth4 172.16.16.1 netmask 255.255.255.0
  • sudo /etc/init.d/isc-dhcpd-server restart

At this point you should physically connect your friend’s laptop to yours using an ethernet cable. To watch the connection happen: tail -f /var/log/syslog. You should be able to go between the two computers and ping each other. If you can’t, then you want to debug this until you can… the rest of this recipe won’t have any effect if your local wired net is broken.

Step 2: Fix up your routing table

Between pppd, your local dhcp server, and you issuing manual ifconfig commands, it’s easy for your local routing table to get in a bad state. Here’s what you want it to look like:

mike@110psi:~$ netstat -nr
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
10.0.0.1        0.0.0.0         255.255.255.255 UH        0 0          0 ppp0
172.16.16.0     0.0.0.0         255.255.255.0   U         0 0          0 eth4
0.0.0.0         0.0.0.0         0.0.0.0         U         0 0          0 ppp0

If you need to remove/add/edit routes, the tool you want to use is ‘route’. For example:

sudo route del default
sudo route add default gw 10.0.0.1

Step 3: Set up your computer to do some NAT

I scripted this out. Here’s the script:

#!/bin/sh
# nat and firewall
# for now, just nat.

ipt=/sbin/iptables
EXTIF=ppp0
INTIF=eth4

case "$1" in
	start)
		echo "Starting firewall:"

		echo -ne "\tClearing existing rules..."
		$ipt -F INPUT
		$ipt -F OUTPUT
		$ipt -F FORWARD
		$ipt -t nat -F
		echo " done."

		echo -ne "\tInput / Output rules..."
		$ipt -P INPUT ACCEPT
		$ipt -P OUTPUT ACCEPT
		echo " done."

		echo -ne "\tForwarding rules, and /proc/sys/net/ipv4/ip_forward..."
		echo "1" > /proc/sys/net/ipv4/ip_forward
		#$ipt -A FORWARD -i $EXTIF -o $INTIF -m state --state ESTABLISHED,RELATED -j ACCEPT
		$ipt -A FORWARD -i $EXTIF -o $INTIF -j ACCEPT
		$ipt -A FORWARD -i $INTIF -o $EXTIF -j ACCEPT
		echo " done."

		echo -ne "\tEnabling MASQUERADE on $EXTIF..."
		$ipt -t nat -A POSTROUTING -o $EXTIF -j MASQUERADE
		echo " done."

		echo "Firewall.sh is up."
	;;
	stop)
		echo -n "Stopping firewall...";
		$ipt -F INPUT
		$ipt -F OUTPUT
		$ipt -F FORWARD
		$ipt -P INPUT ACCEPT
		$ipt -P FORWARD ACCEPT
		$ipt -P OUTPUT ACCEPT
		$ipt -t nat -F
		echo "0" > /proc/sys/net/ipv4/ip_forward
		echo " done.";
		echo "Firewall.sh is down."
	;;
	*)
		N=/etc/init.d/firewall.sh
        	echo "Usage: $N {start|stop}" >&2
		exit 1
	;;
esac

Start your NAT up with “sudo ./nat-script-name.sh start”.

Step 4: debug it because it doesn’t work.

Break the problem into pieces:

  • debug the connection between the two computers (/var/log/syslog, ifconfig, isc-dhcp-server and ping are your friends here)
  • debug your local routing table (using netstat and route commands)
  • debug your connection to your mobile broadband provider (pppd, wvdial, minicom, ping, etc)

If you find some variant of this recipe works better for your local machine, please post it in the comments so we can all share. Good luck.

It is possible – I’m writing this post via T-Mobile and my new webConnect Rocket.

Step 0: see if your webConnect Rocket is actually the same as mine.

mike@110psi:~$ lsusb
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 002: ID 19d2:1201 ONDA Communication S.p.A.
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

If you don’t have the “19d2:1201 ONDA Communication S.p.A.” line, then likely you have a different version of the webConnect Rocket than I do and this guide may or may not apply.

Step 1: usb_modeswitch. This is necessary because the webConnect, like many usb dongles these days, actually has its windows (and mac?) drivers loaded directly on it. When the dongle is first inserted into a windows machine, it appears as a hard drive with its driver on it. Windows installs the driver, then magically changes the device into a modem. We need to use usb_modeswitch to do that magic change ourselves.

sudo apt-get install usb-modeswitch

As of this writing, you need to manually add the file /etc/usb_modeswitch.d/19d2:1201 with text:

# t-mobile ZTE MF691  Rocket 2

DefaultVendor=  0x19d2
DefaultProduct= 0x1201

TargetVendor=   0x19d2
TargetProduct=  0x1203

MessageContent="5553424392020000000000000000061B000000020000000000000000000000"

For more details, see this forum post and this one. Hopefully this file will be included directly in the usb-modeswitch-data package soon, but for the meantime it looks like the maintainer is currently traveling.

Now, we need to configure udev to run usb_modeswitch automatically when it sees the webConnect hard drive. Add the following lines to the file /lib/udev/rules.d/40-usb_modeswitch.rules:

# t-mobile ZTE MF691  Rocket 2
ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1201", RUN+="usb_modeswitch '%b/%k'"

(Before you add those lines check that they haven’t already been added by upstream since this writing. No need to have them in there twice.)

At this point I’d recommend a) restarting your machine and the b) inserting your webConnect Rocket. To test everything’s good so far, you should be able to use minicom to connect to both /dev/ttyACM0 and /dev/ttyACM1 and issue some basic AT commands.

Step 2: I haven’t been able to get network-manager to work correctly with the webConnect rocket. I get a lot of “modem-manager: Got failure code 100: Unknown error” in the logs. If you’ve got the time, the relevant source is here.

However, we can use wvdial and pppd to connect. If you haven’t already:

sudo apt-get install wvdial pppd

You can run ‘wvdailconf’ as root to generate a basic /etc/wvdial.conf. Then you want to edit it to look like mine:

[Dialer Defaults]
Init1 = ATZ
Init2 = ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0
Modem Type = USB Modem
ISDN = 0
New PPPD = yes
Phone = *99***1#
Modem = /dev/ttyACM0
Username = user
Password = pass
Baud = 460800
Stupid Mode = 1

I suspect there may be other options that should be in here that would be helpful. But in any case, this is a working base case. If you find any additional helpful options, please let me know in the comments.

Step 3: Connect to T-Mobile! To connect, run

sudo wvdial

Wvdial will manage the connection process to T-Mobile, spawning an instance of pppd to maintain the connection. Once the connection is established, you can hit cntrl-c to kill the instance of wvdial while still leaving pppd alive and your connection to T-Mobile active.

To disconnect:

sudo pkill pppd

does the trick.

It’ll be nice once modem-manager (and hence network-manager) has functioning support for this device. I’ll update this post once it does. In the meantime… good luck!

A while ago I posted a short greasemonkey script to make saving a Stanford class video to disk a little easier. I’ve cleaned it up a little and posted in on userscripts.org as the Stanford Video MMS Commander. Same disclaimers as before apply, namely, this script is not to be used in any way to help violate Stanford’s terms and conditions.

AutoTracOnDreamhost is a short set of shell scripts I wrote up to automate the install of Trac on a Dreamhost account. I’m going to stop maintaining them. Why?

  • Dreamhost now has a one-click install for Trac (though it currently claims to install the outdated v0.11.4 of Trac, v0.12 having been released a few months ago)
  • As of v0.12, Trac supports multiple repositories for each project. The previous lack of support for this feature in earlier versions of Trac was a big motivating factor for me to write up AutoTracOnDreamhost in the first place, as I like to have completely separate source control repositories for each of the different projects I’m working on.

So, while it was always open source (under GPLv3) I’ve now released AutoTracOnDreamhost on GitHub to make access that much easier. I’ve also released what will likely be the final release of AutoTracOnDreamhost, version 1.2. The changes from v1.1 are pretty minor:

  • Bumped up the version numbers of the software packages the scripts install.
  • README updates
  • Did a test install to make sure it all still works. As of today (Sept. 10, 2010) AutoTracOnDreamhost completes a default install of Trac and friends without any problems on my Dreamhost machine (spilotro.dreamhost.com).

Debian-based distributions have a really useful series of directories to run cron scripts from. Any executable you place in /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly or /etc/cron.monthly will get run with that frequency. I like these for two reasons: 1) it’s file-based, which I find a lot easier to manage and keep track of than each user’s crontabs and 2) this eliminates one possible source of bugs in my work – namely, the scheduling of my cron tasks.

However, there is a downside to /etc/cron.daily and friends. All scripts in there run as root. It’s generally bad practice to run anything as root that doesn’t have to run as root… and this is especially true if your script has scary lines like “rm -rf $SOME_VAR” in it. You’re asking for trouble.

Unfortunately, AFAIK there is no way to drop privilege within a script. However, this can be done by spanning a whole new child process. So, let’s add a short preamble to all the scripts we place in /etc/cron.daily and friends:

#!/bin/sh

USER='some-low-privilege-user'
if [ `whoami` != "$USER" ]; then
  sudo -u $USER "$0"
  exit
fi

... rest of the script ...

Now we have some extra assurance our cron job isn’t going to go haywire and screw the whole machine!

Stanford has a lot of their lectures online. To access them, you’ve got to have… access! That means you need to already have an SUNet ID with proper permissions. The standard way to get those permissions is to apply to Stanford, get accepted, and start paying Stanford lots of $$$.

Stanford doesn’t want these lectures to get out on the open internet. So they do a few things to try to ensure that doesn’t happen. First, part of Stanford’s terms of use is that you will not redistribute their lectures. Note that the concepts and code I’m showing you here in this post could be used to help you illegally redistribute their lectures. I do not condone any such illegal use. By using the the concepts and code in this post in any way whatsoever (including just reading this post), you agree to follow Stanford’s terms of use.. If you don’t agree to these terms, please leave now.

In addition to their terms of use, Stanford places a more practical barrier in the way of illegal redistribution of their lectures – they only distribute their lectures as streaming WMV and Sliverlight. Now this sucks for me, the legal end-user, for a number of reasons. Most importantly, this means I need a high-bandwidth connection to watch my lectures. I can’t watch them offline.

Enter MEncoder and Greasemonkey.

MEncoder is a tool to save streaming video to disk. Greasemonkey is a Firefox browser extension that allows you to run custom javascript for any particular page displayed in the browser.

To save a lecture to disk for offline viewing, the basic command you want to run is:

mencoder mms://the-streaming-video.wmv -ovc copy -oac copy -O filename_out.avi

Now the ‘path-to-streaming-video.wmv’ you need to use will be unique for your authenticated SUNet session with Stanford’s servers. You can manually find this unique path by navigating with your browser to the lecture you’re interested in, viewing the source of the page, and manually scanning for the path. Or you can install the following small and simple Greasemonkey script.

// ==UserScript==
// @name          Stanford Video MMS Path Retriever
// @copyright     2010, Mike Fogel, http://fogel.ca
// @license       This SW may not be used to violate Stanford's terms of use.
// @namespace     http://fogel.ca/greasemonkey
// @description   Adds a header containing mms path to Stanford player page.
// @include       https://myvideosu.stanford.edu/player/slplayer.aspx*
// ==/UserScript==

function get_mms_path() {
  var o = document.getElementById('WMPlayer');
  return 'mms' +  o.data.substring('http'.length);
}

function add_header(message) {
var elem = document.createElement('div');
  elem.id = 'GM_mms_path';
  elem.style.color = 'green';
  elem.innerHTML = '<h2>'+message+'</h2>';
  document.body.insertBefore(elem, document.body.firstChild);
  return elem;
}

function set_selection(hdr) {
  var range = document.createRange();
  range.selectNodeContents(hdr.firstChild);
  selection = window.getSelection();
  selection.removeAllRanges();
  selection.addRange(range);
}

function main() {
  var mms = get_mms_path();
  var hdr = add_header(mms);
  set_selection(hdr);
}

main();

The Greasemonkey script scrapes the page, finds the ‘path-to-streaming-video.wmv’, adds it in large, bright green text to the top of the page, and selects it so you can just hit ‘cntrl-c’ to copy it to your clipboard. Then over in a terminal, you can paste that path into your MEncoder command. Running the command will take a long time – in fact, as long as the lecture is. This is because MEncoder simulates a regular streaming video player to the Stanford servers. MEncoder will convert and save the streaming video according the the suffix of the output filename you specify (eg. ‘.avi’ in my above example).

I’ve got a Verizon data uplink thinger for my laptop. This is the second day in a row they’re not able to route my packets. Yesterday I couldn’t access a shared host on WebFaction’s network. Today I can’t access my Slicehost VPS.

mike@110psi:~$ traceroute dev.thefirewoodguys.com
traceroute to dev.thefirewoodguys.com (67.23.12.250), 30 hops max, 60 byte packets
 1  64.sub-66-174-217.myvzw.com (66.174.217.64)  73.717 ms  74.616 ms  75.561 ms
 2  127.sub-66-174-217.myvzw.com (66.174.217.127)  85.524 ms  86.513 ms  86.485 ms
 3  138.sub-66-174-31.myvzw.com (66.174.31.138)  101.457 ms  101.434 ms  101.414 ms
 4  233.sub-66-174-217.myvzw.com (66.174.217.233)  99.385 ms  100.343 ms  75.903 ms
 5  194.sub-66-174-217.myvzw.com (66.174.217.194)  77.883 ms  77.870 ms  77.858 ms
 6  98.sub-66-174-31.myvzw.com (66.174.31.98)  77.847 ms  77.837 ms  72.877 ms
 7  169.sub-66-174-31.myvzw.com (66.174.31.169)  73.820 ms  74.790 ms  82.772 ms
 8  162.sub-66-174-31.myvzw.com (66.174.31.162)  81.781 ms  82.744 ms  83.737 ms
 9  169.sub-66-174-31.myvzw.com (66.174.31.169)  83.729 ms  83.721 ms  83.713 ms
10  162.sub-66-174-31.myvzw.com (66.174.31.162)  75.838 ms  75.791 ms  76.679 ms
11  169.sub-66-174-31.myvzw.com (66.174.31.169)  77.664 ms  78.606 ms  78.576 ms
12  162.sub-66-174-31.myvzw.com (66.174.31.162)  69.817 ms  70.718 ms  77.700 ms
13  169.sub-66-174-31.myvzw.com (66.174.31.169)  78.646 ms  78.617 ms  78.588 ms
14  162.sub-66-174-31.myvzw.com (66.174.31.162)  78.565 ms  79.526 ms  79.503 ms
15  169.sub-66-174-31.myvzw.com (66.174.31.169)  79.487 ms  66.962 ms  66.941 ms
16  162.sub-66-174-31.myvzw.com (66.174.31.162)  67.833 ms  72.801 ms  73.800 ms
17  169.sub-66-174-31.myvzw.com (66.174.31.169)  73.787 ms  79.906 ms  80.854 ms
18  162.sub-66-174-31.myvzw.com (66.174.31.162)  80.840 ms  80.828 ms  81.807 ms
19  169.sub-66-174-31.myvzw.com (66.174.31.169)  81.795 ms  81.786 ms  91.778 ms
20  162.sub-66-174-31.myvzw.com (66.174.31.162)  92.730 ms  93.720 ms  70.790 ms
21  169.sub-66-174-31.myvzw.com (66.174.31.169)  71.735 ms  72.670 ms  77.668 ms
22  162.sub-66-174-31.myvzw.com (66.174.31.162)  75.614 ms  76.604 ms  82.729 ms
23  169.sub-66-174-31.myvzw.com (66.174.31.169)  81.718 ms  82.645 ms  84.634 ms
24  162.sub-66-174-31.myvzw.com (66.174.31.162)  86.585 ms  86.555 ms  86.529 ms
25  169.sub-66-174-31.myvzw.com (66.174.31.169)  86.505 ms  86.481 ms  86.464 ms
26  162.sub-66-174-31.myvzw.com (66.174.31.162)  67.786 ms  68.690 ms  69.682 ms
27  169.sub-66-174-31.myvzw.com (66.174.31.169)  74.649 ms  75.588 ms  75.559 ms
28  162.sub-66-174-31.myvzw.com (66.174.31.162)  73.911 ms  100.955 ms  100.945 ms
29  169.sub-66-174-31.myvzw.com (66.174.31.169)  101.868 ms  104.849 ms  104.840 ms
30  162.sub-66-174-31.myvzw.com (66.174.31.162)  103.834 ms  109.823 ms  110.813 ms

The solution? Disconnect and reconnect. Verizon will give you a new IP, and if you’re lucky, your packets will now (by chance) get routed around their fail.

This script will make a dump of the Django Book to your local filesystem so you can read it offline. Like say on a flight and you don’t want to pay 10 bucks for an hour of wifi. Even though you’re stoked you finally can, if you need/want to.

This script makes all the css links work so everything’s pretty. Also, Firefox by default won’t render index.html files as directory indexes when working in the ‘file://’ scheme, so this script rewrites internal links so that the index.html is explicit.

#!/bin/sh

# this script downloads a copy of the 2.0 django book, complete with css and
# sets it up so you can read it in-flight

WEB_RT="http://www.djangobook.com/en/2.0"
MIN_CHP=1
MAX_CHP=20

CSS1=http://new-media.djangobook.com/yui/container/assets/container.css
CSS2=http://new-media.djangobook.com/yui-ext/css/resizable.css
CSS3=http://new-media.djangobook.com/yui-ext/css/tabs.css
CSS4=http://new-media.djangobook.com/djangobook.css
CSS5=http://new-media.djangobook.com/yui/grids/grids-min.css
CSS6=http://new-media.djangobook.com/yui/reset/reset-min.css

CSS1_FILE=container.css
CSS2_FILE=resizable.css
CSS3_FILE=tabs.css
CSS4_FILE=djangobook.css
CSS5_FILE=grids-min.css
CSS6_FILE=reset-min.css

DIR_RT=django_book

echo "************ Setting up our dir structure *****************"
mkdir $DIR_RT
mkdir $DIR_RT/css
for i in `seq -f 'chapter%02.0f' $MIN_CHP $MAX_CHP`; do
  mkdir $DIR_RT/$i
done

echo "*********** Downloading all the content *****************"
wget $WEB_RT/ -P $DIR_RT
for i in $CSS1 $CSS2 $CSS3 $CSS4 $CSS5 $CSS6; do
  wget $i -P $DIR_RT/css/
done
for i in `seq -f 'chapter%02.0f' $MIN_CHP $MAX_CHP`; do
  wget $WEB_RT/$i/ -P  $DIR_RT/$i/
done

echo "************** Fixing up the css links ****************"
for i in `seq 1 6`; do
  eval "CSS_F=\"\$CSS${i}_FILE\""
  sed -i "s/href=\".*$CSS_F\"/href=\"css\/$CSS_F\"/" $DIR_RT/index.html
done

for i in `seq -f 'chapter%02.0f' $MIN_CHP $MAX_CHP`; do
  for j in `seq 1 6`; do
    eval "CSS_F=\"\$CSS${j}_FILE\""
    sed -i "s/href=\".*$CSS_F\"/href=\"..\/css\/$CSS_F\"/" $DIR_RT/$i/index.html
  done
done

echo "*************** Fixing up the page links **************"
sed -i "s/href='chapter[0-9]\{2\}\//&index.html/" $DIR_RT/index.html
for i in `seq -f 'chapter%02.0f' $MIN_CHP $MAX_CHP`; do
  sed -i "s/href=\"..\"/href=\"..\/index.html\"/" $DIR_RT/$i/index.html
  sed -i "s/href='..\/chapter[0-9]\{2\}\//&index.html/" $DIR_RT/$i/index.html
done

I just installed ProFont on my Debian machine, using this howto. If you’re looking for a monospace font that maintains readability at extra small sizes, ProFont’s for you. On my 1920×1280 15.4″ LCD display (147 pixels/inch), this allows me to stack up three 80-char wide terminals side by side by side, using ProFont at 7 pixels, while maintaining readability. Screen real estate galore!

gsynaptics gives you a nice little GUI in gnome to control the options on your synaptics touchpad, like the one my T61p came with. It’s easy to set up on a default debian install, you just need to add a few stanazas to your xorg.conf

Here’s the edits I needed from a default install xorg.conf:

  1. Make a backup of your current xorg.conf
  2. Remove the “Generic Mouse” section. You don’t need it anymore.
  3. Add a section for your touchpad like so:
    Section "InputDevice"
      Identifier  "Synaptics Touchpad"
      Driver      "synaptics"
      Option      "SendCoreEvents"  "true"
      Option      "Protocol"        "auto-dev"
      Option      "Device"          "/dev/psaux"
      Option      "SHMConfig"       "true"
    EndSection
    
  4. And add a line in your “ServerLayout” section referencing your new synaptics section. I didn’t have a ServerLayout section so I had to add one, mine looks like this:
    Section "ServerLayout"
      Identifier  "Default Layout"
      Screen      "Default Screen"
      InputDevice "Generic Keyboard"
      InputDevice "Synaptics Touchpad"
    EndSection
    

Now save everything you’re doing and then restart X11 by hitting cntrl-alt-backspace. If X won’t start back up, you can hit cntrl-alt-f1 and log in at a command prompt, restore your xorg.conf from the backup you made earlier, then run /etc/init.d/gdm restart.

In the event that things just aren’t working, your friend is /var/log/Xorg.0.log. That log file contains the results of your x-server’s parsing of your xorg.conf.