Saturday, June 26, 2010

Finding Your External IP Address

For various reasons you might want to find your public IP address, that is, the 32-bit number, expressed in pseudo-decimal form as something like 72.14.204.99, that tells the world how to reach your computer.

For most businesses and government offices this is trivial: your computer is assigned a static IP address, one that never changes with time. For home users, though, it's a little more complex.

Start with the IP address itself. The largest possible IP address is 255.255.255.255. That means that you can have no more than 4,294,967,296 unique IP addresses, and some of those are reserved for local use, as we'll see. We're coming up on 7 billion people in the world. Not everyone has a computer (yet, and remember iPhones, iPads, Kindle, and all 'Droids count), but many people have more than one (see iPhones, iPads, …). So there aren't enough IP addresses to go around. Eventually, everyone will switch over to the IPv6 address scheme, which will give us 340,282,366,920,938,463,463,374,607,431,768,211,456 (2128, or 3.4×1038) possible addresses, which is probably enough for every non-hydrogen nucleus in the observable universe to have its own address.

But we're not there yet, so something has to be done to shrink the address space. Your local ISP solves this problem by 1) charging you extra to get a static IP address; 2) giving you only one IP address per gateway (your cable, FIOS, DSL, or dial-up modem, or some other router or hub that connects you to the internet); and 3) taking your IP address away from you when you're not using it.

That last one is called dynamic IP addressing, and it's part of what we're going to talk about today. If you have a dynamic IP address, then when you turn your gateway connection (usually some type of modem or router) on, your ISP assigns you an IP address from its bank of addresses. As long as your connection is turned on, you'll have an IP address, but your ISP can change it at any time. It will almost certainly change it if you turn off your connection and turn it on again. So if you know your IP address right now, there's no reason to expect it will be the same tomorrow, or even five minutes from now — though to be fair, Verizon FIOS has kept our house on the same IP address for weeks.

And there is worse to come: Suppose your ISP has given you the address 244.117.16.25. The computer you're reading this on doesn't have a clue as to what that address is. Go back to 2), above. Your house has only one IP address, and it belongs to your gateway. Every other computer in the house belongs to your home network. When you want to download your email, your computer politely requests the gateway to go fetch it:

Please, sir, may I have my email?

Oh, all right. Just this once. But don't tell any of the other computers what we're doing.

Thank you, sir! I'll be eternally grateful

You'd better be, or no Internet for you.

Let's see how this works. ping is a command that will poke a stick at a computer and get information about it. Let's try it with a well known site:

ping google.com
PING google.com (72.14.204.99) 56(84) bytes of data.
64 bytes from iad04s01-in-f99.1e100.net (72.14.204.99): icmp_seq=1 ttl=53 time=21.8 ms
64 bytes from iad04s01-in-f99.1e100.net (72.14.204.99): icmp_seq=2 ttl=53 time=20.2 ms
64 bytes from iad04s01-in-f99.1e100.net (72.14.204.99): icmp_seq=3 ttl=53 time=23.4 ms
^C

(you'll need to hit ctrl-C to stop this)

So we see that google.com has an IP address of 72.14.204.99 — that's actually one of many, but don't worry about that. Now let's try it with a local machine:

ping george
PING george.home (192.168.1.2) 56(84) bytes of data.
64 bytes from george.home (192.168.1.2): icmp_seq=1 ttl=64 time=9.77 ms
64 bytes from george.home (192.168.1.2): icmp_seq=2 ttl=64 time=2.70 ms
64 bytes from george.home (192.168.1.2): icmp_seq=3 ttl=64 time=0.961 ms
^C

So far has the home network is concerned, george lives at 192.168.1.2, which is one of those reserved addresses we mentioned above. The range 192.168.0.0 to 192.168.255.255 is reserved for internal networks. As we read this, there are no doubt hundreds of thousands of computers with the local IP address of 192.168.1.2. Every one of them, however, has a different public IP address.

Great. But sometimes I need to know my external IP address, and it's apparent that Hal, here, hasn't got a clue as to what it is. How can I find it?

The easiest way is to go to another website. Every website you connect with gets your public IP address automatically, unless you use some kind of anonymous service. Some websites are set up to tell you what your IP address is. One example is What Is My IP?. They even provide a way to get your IP address with a simple one-line terminal command:

wget www.whatismyip.com/automation/n09230945.asp -O - -q ; echo

If you read your Gmail via Thunderbird, Evolution, Outlook, etc., and keep your web client on all day, gmail will tell you the address of your computer. Just log on to your gmail account and look for the line that says Last account activity: …. The IP address there is the last computer to download email. If you're using more than one computer from different locations, say you left your office machine on over the weekend and now you're using a home computer, click on the Details link and you'll find all of the activity for this account.

There's a company DynDNS, which will not only find your IP address, but give you a domain name that constantly updates to your current numerical address. Of course, they do this by having your computer constantly tell them the public IP address.

And, of course, your gateway knows its public IP address. Just log onto it through your browser (something like http://192.168.0.1, http://192.168.1.1, or http://192.168.100.1, depending on your system), and your IP address will be up there someplace.

But all of these systems have flaws: Say you need your IP address in a script. You can get it via your browser from your gmail or your gateway machine, but it's hard to parse a browser page inside a script. The What's My IP? one-liner works in a script, but what if their site goes down, or, worse, they go out of business? Same for DynDNS. We want a solution that works any time your gateway is on and your computer has an internet connect, and we want to get it from the command line.

Any other way just ain't elegant.

If you search the Internet you find surprisingly few ways (i.e., none) to do this. Partly I think it's because there isn't a consistent way to find your gateway from your current machine. For example, in the days when we used Comcast, we had a cable modem as the gateway. On the house side of the modem we connected a Linksys router, which was the hub of our home network. All the computers in the house were connected through the router to the modem — a two step-process. Now we have Verizon FIOS, with a Actiontec MI424WR router that serves as gateway/modem and router — a one-step process.

By stumbling around, and with a little help from nixCraft, I was able to come up with a solution that works for me. It's a bash script. Let me put it here (finally, I hear you say), and we'll dissect it below:

#! /bin/bash

# Determine the public IP address for your DHCP system by bootstrapping

#  calls to your gateway/router

# Requires "route," available in Ubuntu package net-tools, and
# nslookup, available in Ubuntu package dnstools

# Disclaimers:
# 1) This works with Verizon FIOS using the Actiontec MI424WR Router
#    If you have another system YMMV.
#
# 2) And, as you'll no doubt note, this is set up for a Debian system,
#    specifically Ubuntu 10.04 .  Package names and installation
#    procedures for other systems WILL vary.
#

###
# Start Here:
###

#
# Get Router internal Gateway IP
# Information found on
# http://www.cyberciti.biz/faq/how-to-find-gateway-ip-address/
# If route is not installed, run
# sudo apt-get install net-tools
# and make sure /sbin is in your $PATH
# Note "U" = up, and "G" = gateway

internalip=`route -n | grep UG | awk '{print $2}'`

echo Your Gateway\'s Internal IP Address is: $internalip

# Now we need to get the name of the router.  If you don't
#  have nslookup installed, run
# sudo apt-get install dnsutils

echo -n "Your Gateways's name is: "

routername=`nslookup $internalip | grep "name =" | awk '{print $4}'`

# On the Actiontec, at least, the name has an annoying "." at the end.
# Remove it:

routername=${routername%\.}

echo $routername

echo -n "Your Public IP Address is: "

nslookup $routername | grep Address | tail -1 | awk '{print $2}'

OK, what's going on? First of all script only works if you have the route and nslookup commands installed on your system. If they aren't there, in Ubuntu, and presumably and Debian system, you can install them using

sudo apt-get install net-tools dnsutils

The first call, route -n, will look at the Kernel's IP routing table:

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.1.0     0.0.0.0         255.255.255.0   U     1      0        0 eth0
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 eth0
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 eth0

which on this machine tells us that we have several local connections. The U under Flags means that the connection is up, and G means it's the gateway. From this we see that the numerical address of the gateway is 192.168.1.1. Let's see what nslookup can tell us about that. Note that the script has to parse all of the output to get this far.

$ nslookup 192.168.1.1
Server:  192.168.1.1
Address: 192.168.1.1#53

1.1.168.192.in-addr.arpa name = Wireless_Broadband_Router.home.

Here's something new, our gateway's got a name. Wireless_Broadband_Router.home. Again, the script has to do a little work to tease that name out of nslookup's output, but we'll just do it by hand here:

$ nslookup Wireless_Broadband_Router.home
Server:  192.168.1.1
Address: 192.168.1.1#53

Name: Wireless_Broadband_Router.home
Address: 192.168.1.1
Name: Wireless_Broadband_Router.home
Address: xxx.yyy.zzz.aaa

The first address, 192.168.1.1, is just the router's internal IP address. The last address (Of course I'm not giving you my IP address. I'm not daft, you know.) is the one we want. xxx.yyy.zzz.aaa is the external address of this machine.

Of course there are many, many, problems with this script.

  • So far it only works with the Verzion/Actiontec setup. A friend, running Ubuntu, wasn't able to pick out his gateway machine with the route -n command.
  • Some machines can have multiple gateways.
  • The Mac's route command doesn't work in the same way as under Linux, so you can't parse out the gateway, even if you have none.
  • You might be able to tease the gateway IP address out of the /etc/resolv.conf file. I don't know what happens if you have multiple gateways here. Under Ubuntu, the one numerical gateway is 192.168.1.1, on the Apple it gives me 192.168.1.1 and xxx.yyy.zzz.aaa, my public IP address.

If you try the script, leave a comment below. Let me know your internet setup (gateway/router), if it works, and any tweaks you did to make it work. Maybe we can come up with a universal script.

Now, by all that's geekly, why did we do all of this? That's the subject of an upcoming post, boys and girls …

Saturday, June 19, 2010

Smart ssh tricks

I want my computers to talk to one another.

More specifically, I want to be able to remotely log in to one of my computers from another one, and I want to be able to easily copy files from one computer to another. The easiest, most secure, way to do that these days is to use the Secure Shell protocol, aka ssh.

Examples: I have an account on a computer called grumpy (sleepy and doc were already taken). To log onto grumpy from hal, I open up a terminal window and type:

$ ssh rcjhawk@grumpy

The program then logs me onto grumpy, where I can access the system just as I can for any desktop machine.

Or, say, I have a file at /home/rcjhawk/Documents/strasburg_pictures.ppt on grumpy. To copy it over to Hal, I use the secure copy program, scp:

$ scp rcjhawk@grumpy:Documents/strasburg_pictures.ppt .

which, obviously, works pretty much like the regular copy program except that I have to specify the machine and account name right up front.

And, finally, I can send files the other way:

$ scp The_Hard_Times_of_Zach_Greinke.txt rcjhawk@grumpy:Documents

which sends that regrettable file into the Documents folder on grumpy.

Except:

You will probably find that this doesn't work out of the box in your Ubuntu setup. You'll get a message that looks something like this:

ssh: connect to host grumpy port 22: Connection refused

That's because Ubuntu ships ssh in two parts:

  • openssh-client: this lets you use ssh and scp from your machine to log onto another machine. This is installed with the standard Ubuntu installation, and is all you need if, for example, you want to log onto your work computer from home.
  • openssh-server, on the other hand, allows other machines to log onto your personal desktop machine. This is not installed by default in Ubuntu because it's an obvious security hole unless you know what you're doing. If you do know, or assume that you will learn, install the package with

    $ sudo apt-get install openssh-client

    Once you do that, sshd, the daemon that controls remote access, will automatically start up, and will start up every time you reboot the machine. Then you're good to go.

In the example above, grumpy needs to have openssh-server installed and running, but hal does not.

Note for Mac Users: You get the equivalent of openssh-server by going to System Preferences => Sharing and clicking Remote Login.

Security

ssh encrypts the transmissions between machines, but it also has other security features. The first time you try to log onto a machine, for example, you'll get a message that looks something like this:

$ ssh rcjhawk@grumpy
The authenticity of host 'grumpy (192.168.54.42)' can't be established.
RSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx.
Are you sure you want to continue connecting (yes/no)?

If you answer yes, then you'll get the message:

Warning: Permanently added 'grumpy,192.168.54.42' (RSA) to the list of known hosts.

After that you'll be able to log onto grumpy without the warning message. If you want to start over, i.e. not recognize any computer as being previously visited, erase the file $HOME/.ssh/known_hosts

You can also limit access to the machine by editing your /etc/hosts.deny and /etc/hosts.allow files. As I've noted before, if you don't want ever Tom, Dick, and Susie to have a chance to access your machine, your /etc/hosts.deny file should read:

ALL: ALL

after which only addresses listed in your /etc/hosts.allow file will have access to the machine. In my /etc/hosts.allow file I currently have

ALL: 192.168.1.0/255.255.255.0 ,

which allows all machines on our local network to access grumpy.

OK, everything is good, right? Well, not quite. As things are set up right now, every time you ssh or scp to grumpy you must enter a password. This isn't a big annoyance with ssh, because presumably you will keep the remote login running for a relatively long time. However, it is an annoyance with scp, if you want to copy a large number of files from different places, requiring a large number of scp commands.

The solution is to use ssh-keygen to generate a a key. Here's how it works:

Start the program:

$ssh-keygen -t dsa Generating public/private dsa key pair.
Enter file in which to save the key (/home/rcjhawk/.ssh/id_dsa):


We'll use the default directory location, so just hit return here.

Enter passphrase (empty for no passphrase):

Frankly, I don't know anyone who does use a passphrase. It does protect your key against someone who's gotten access to your account, but at that point you're probably hosed anyway. So for now we'll just hit return.

Enter same passphrase again:
Your identification has been saved in /home/rcjhawk/.ssh/id_dsa.
Your public key has been saved in /home/rcjhawk/.ssh/id_dsa.pub.

Now let's take a look at the .ssh directory:

$ ls -la $HOME.ssh
total 16
drwx------  2 rcjhawk rcjhawk  4096 2010-06-19 13:04 .
drwxr-xr-x 34 rcjhawk rcjhawk  4096 2010-06-19 13:01 ..
-rw-------  1 rcjhawk rcjhawk   672 2010-06-19 13:04 id_dsa
-rw-r--r--  1 rcjhawk rcjhawk   599 2010-06-19 13:04 id_dsa.pub

Here you'll notice two files. One, id_dsa, is your private key. The second, id_dsa.pub, is your public key. Key the private key private. Don't let anyone have access to it. Don't copy it over into the .ssh folder of another account, or to another computer. You'll note that both the .ssh directory and id_dsa are set so you are the only person who can read those files. Keep it that way.

The public key, now, that's another matter. If you place id_dsa.pub into the file $HOME/.ssh/authorized_keys on another computer, you'll be able to access that computer from your home computer. Here's one way to to it (I'm leaving out all the password prompts):

$ ssh rcjhawk@grumpy
Welcome to grumpy!
rcjhawk:~ $ cd .ssh
rcjhawk:~/.ssh $ scp rcjhawk@hal:.ssh/id_dsa.pub tempid
rcjhawk:~/.ssh $ cat tempid >> authorized_keys
rcjhawk:~/.ssh $ rm tempid
rcjhawk:~/.ssh $ exit

What did we do? We logged into grumpy, copied hal's public key over to grumpy's .ssh directory, and added it to the end of the authorized_keys file. I renamed the file during the copy so as not to write over grumpy's own id_dsa.pub file, if one was there. Notice that scp was able to get that file even though Hal's .ssh directory is copy protected, because you gave the system the right password.

The next time you try to ssh or scp from hal to grumpy, ssh will find the public key on grumpy, compare it to the private key on hal, and conclude that you are, indeed, allowed to log in or transfer files.

The discerning reader will note that I'm only applying this to my local network, which all sits behind a router and firewall. How, you may ask, can I use ssh to log onto my local machine from outside my home?

Well, I wish I could tell you, but at the moment I can't. It's something to be researched in the future. If you're reading this, and you know how to do it, post a link to the procedure in the comments.

Friday, June 11, 2010

A Sign From Above

Tonight coming home from work I learned that there was a backup on US-50 East between I-97 and the Severn River Bridge in Annapolis.

I knew this, because I was given a Sign. This sign, as a matter of fact, which today had an actual message.
The sign in question is about 15 miles from the start of the backup. Not to worry, I had plenty of time to read it because I was in a five-mile backup caused by people slowing down to read the sign.

Obscene comments about Maryland drivers omitted.

Friday, June 04, 2010

Stupid Bash Tricks

I grew up scripting with the C shell (csh), and I'm still more comfortable with it than any other shells. However, for really complicated scripts I find that BASH (the Bourne-again shell) is more flexible. The problem is that I don't really know how to use it all that well.

I could buy a book, I suppose, but that costs money. And anyway, much of what I want is already on the web. For example, we have

I've used all of these, but the only way to really remember something is to build an example, and refer back to it as needed. So here, presented for your consideration, are a couple of examples.

The first one just counts. It's basically here to show how to do math with variables in BASH:

#! /bin/bash

typeset -i count

count=$1
echo $count

while (( $count < $2 ))
do
 count=$count+1
 echo $count
done

Actually, that one is adapted from the Korn shell (ksh), but it works in bash. A purer bash form would be

#! /bin/bash

let count=$1
echo $count

while (( $count < $2 ))
do
 let count=$count+1
 echo $count
done

Either way, if we call the file count, then it counts from the first argument up to the second:

./count 5 10
5
6
7
8
9
10

On to another program. I often have programs that recompute a certain quantity repeatedly, say the total energy in a Density Functional Theory run. For example,

$ grep "TOTAL ENERGY=" INFO
                          TOTAL ENERGY=      -551.5802151120
                          TOTAL ENERGY=      -551.5798689965
                          TOTAL ENERGY=      -551.5810124810
                          TOTAL ENERGY=      -551.5809217653
                          TOTAL ENERGY=      -551.5796453493
                          TOTAL ENERGY=      -551.5797608209
                          TOTAL ENERGY=      -551.5806860076
                          TOTAL ENERGY=      -551.5804690663
                          TOTAL ENERGY=      -551.5793575483
                          TOTAL ENERGY=      -551.5794583810

where I only want the last line. For a single file, I could get that with

$ grep "TOTAL ENERGY=" INFO | tail -1
                          TOTAL ENERGY=      -551.5794583810

but I want to look at multiple files, make a note of the file I'm looking at, and extract the last entry. So I wrote this little script, which I call lastgrep. Among other things, it shows you how to use the shift command to delete elements from the calling string.

#! /bin/bash

# usage

# lastgrep string  file1 file2 file3 file4

# finds the last occurrence of string in each of the files
#  listed in the argument list.  If string contains white space
#  it should be quoted

string=$1

# Pop the first argument (string) off the stack.  The
#  remaining arguments should be files

shift

for file in $@
do
    echo -n $file

#    Put quotes around $string to keep white space in place

    grep "$string" $file | tail -1
done 

So if I have a set of files INFO in directories a100, a200, a300, I might do something like this:

$ lastgrep "TOTAL ENERGY=" a*/INFO
a100/INFO  TOTAL ENERGY=      -551.7810244761
a200/INFO  TOTAL ENERGY=      -551.6774784178
a300/INFO  TOTAL ENERGY=      -551.6192819148

What this doesn't have is a way of passing arguments to grep. That's for another time.

Tuesday, June 01, 2010

CUPS and a Wireless Printer

Eric Raymond once wrote a famous essay about the difficulty of using Linux's Common UNIX Printing System (CUPS) to set up a printer over a network. Thus, when we got ChildII's new HP C4780 Wireless Printer/Scanner I was less than optimistic about getting it Hal to access it. I mean, we're talking about a wireless printer which is talking to a Verizon FIOS wireless router, and through that to a Linux computer? No way.

I should note that I wouldn't go out and buy an inkjet printer on its own, anymore, particularly an HP. But we just got ChildII a MacBook Pro (new college uses Macs, and Mac Photoshop, for art projects), and they threw in an iPod Touch (which I get to glance at longingly) and the HP (which it's my duty to set up, operate, and maintain). So we got the thing, we might as well use it. Anyway, having a wireless or networked printer for a MacBook is an essential, given that the thing only has two USB and one Firewire port.

Setting up the HP required that I use the Mac or a PC, connected by USB to the printer, to enter the network login information. Given the connection between open source software and printers this left me a little chagrined, but I'm not RMS so I let it slide. Anyway, I didn't expect the thing to work with Linux, remember?

It took about a half-hour to install the software and drivers onto a Mac, and set up the printer. We tested the printer and scanner via wireless, and it all worked fine, though the scanner was pretty slow, much slower than my USB Canon. It all worked, though.

Then it took another half hour or so to install everything on a Windows PC. Again, it all worked flawlessly.

OK, let's try our luck. I left the printer on, went downstairs to Hal, clicked on System => Administration => Printing and watched the CUPS interface come up. Hit Add, Printer, Find Network Printer. Blinked. There it was, an HP C4700 type printer. Unlike the Canon, all the drivers were already available, I didn't have to play guess-the-correct-version. Within five minutes of clicking that Printing button I was printing a test page from Hal back to the printer.

Now all is not sweetness and light. While CUPS had no problem finding the printer, SANE, the corresponding program for scanners, couldn't find the HP. Not surprising, I guess, but it would have been nice if everything worked out. Of course I'm not particularly sure what good a remote scanner does anyone, especially if, like, me, they use it primarily to scan old pictures onto a disk.

But to summarize, things have much improved since the days of ESR's essay.

And I've got to look into getting a networked color Laser printer.