Sharing: it's a beautiful Linux solution

Copyright 1999 by Chris Jones. All Rights Reserved.

Abstract

It's easy to convince your networked PCs to share a single internet connection. The solution detailed below shows how to use diald (Dial Daemon) to connect your SOHO network to the Internet. Additionally, enhancements to this configuration are included.

Assumptions

I assume that you have read the Net-3 HOWTO, by Terry Dawson, and that you have access to or have read the Network Administrator's Guide (NAG), by Olaf Kirch. If you haven't done this, you should have practical experience working with a large TCP/IP network. If you have any questions about networking concepts throughout this document, please consult the Net-3 HOWTO or NAG.

Background

There are a number of products on the market that provide firewalling, routing, and modem sharing, and the prices are quite affordable1. However, if you're like me and would rather have complete control instead of an opaque, packaged solution, you should definitely consider doing what I did: recycle an older computer and use it for a Linux router/firewall.

There's not much use for a 100 MHz Pentium computer in the Microsoft world. Modern applications require faster processors (to run animated paperclips), and while the 32 mb of RAM the computer has is certainly sufficient, it comes with an ATI-32 video card built into the motherboard which isn't powerful enough to run the latest, greatest 3D gorefests.

I've loved Linux since the 0.95 days. I actually bought hardware around the Linux/X-Free86 compatibility lists (until my wife got toys like low-end scanners, digital cameras, and color printers). When the Pentium-100 wasn't able to run her Windows apps anymore, I had an opportunity to upgrade to that machine. Since then, she's gone through two upgrade cycles, and I've received the cast-offs. :-)

The main Linux box is still that Pentium-100, loaded with all our old 72-pin SIMMs to bring it up to 32 mb of RAM. It's got 1.5 gb of disk space (spread across two drives, with 128 mb of swap) and works well with RedHat 5.2. I recently dumped the old configuration to upgrade to RedHat 5.2 and applied my new administration standards to it (namely, keep every change in a separate /usr/local/ and symlink those files back to their place in the root filesystem).

I've been promising to share the modem with my wife for over a year. I had successfully set this up at a small company I worked with for six months, and figured it would be no problem to do at home. What I didn't count on was being laid off and changing jobs, moving, and not having a decent place to put the PCs for months. When I finally got everything together, I forgot about my plans to network the PCs and share the modem--I was too busy at work to want to do more than boot up a computer to play Quake II for a few hours a night.

Networking

We have four computers on our home-based network:

The first difficulty, of course, was to make every computer see the network and be visible. The NeXT box was tricky to configure, but only because it was designed to work through the admin applets and I'm used to editing rc files and the /etc directory. The Intel Ether Express card was also a problem because its hardware is permanently set to the same IRQ as the second serial port. Ouch! This meant that I wouldn't be able to use a mouse with the Linux box (I don't have a PS/2-style mouse port on the box). No X Windows based configuration although, thankfully, Linuxconf works with Curses.

Choosing the network

The very first decision to make is which network to use. Unless you have an assigned subnet, as part of a permanent connection to as ISP, for instance, you will want to choose one of the reserved private networks listed below:

A big business is likely to choose a class A address because it affords them a huge base of IPs and great flexibility in subnetting. I've never personally seen anyone use the reserved class B network. The reserved class A is appropriate for a small office or home network--it's unlikely that these networks will use more than 254 devices, and if that situation did occur, the network could be segmented with gateways between class C subnets.

I chose the reserved class C subnet 192.168.1.0 (netmask 255.255.255.0) because my traffic will not be routed beyond my network, I don't want to use any real Internet addresses which could potentially cause a conflict with a real Internet assigned address, and it's a conveniently small size to manage.

I planned ahead, as well, choosing my default DNS, server, and gateway addresses at this point. I decided to use DNS and my default server on the 192.168.1.1 address, with my gateway occupying the extreme end at 192.168.1.254. Companies I have worked for in the past have used gateways at .1 in a class C network, with DNS being served on a different network, or the absurd assignment gateways at an arbitrary address range of .181 to .183.

Configuration

I started by configuring my Windows machines. I had to use the Linksys provided utilities to set the IRQ and DMA on each card individually. I really don't trust PnP unless every card in the machine is compatible, and one of the sound cards definitely pre-dates PnP. It wasn't very long before the two Windows boxes saw each other and were sharing printers and files. The IP addresses I chose were arbitrary with the only real characteristic being that they were "in the middle" between my chosen DNS and gateway addresses.

I added the Linux box, next. Linuxconf made configuring the Ether Express card a breeze. Initially, I added the aliased IP address to the Linux box wrong, but Linuxconf caught the error and figured out what I wanted it to do. The Linux box now had two IPs and all the computers could ping each other successfully across the network.

Network: Mischief (192.168.1.0, netmask 255.255.255.0)
Computer Aliases IP address
menagery becky 192.168.1.30
chris win95 192.168.1.130
next 192.168.1.9
linux   192.168.1.1
gw 192.168.1.254
(sl0) 192.168.1.253

All computers in the network are connected to a single five port, 10-base-T Linksys hub via category 3 and category 5 RJ-45 twisted pair cable of distances ranging from two meters to 30 meters.

All computers, except for linux have these minimal network characteristics defined:

linux acts as a caching nameserver and has its default route set by diald. I use a common hostfile instead of local DNS to make my life easier. I only have four real hosts, so it's not a big deal. My /etc/hosts file looks like this:

To allow the linux box to act as a caching nameserver, I used the default configuration that came with RedHat 5.2, turned on the named, and edited my /etc/resolv.conf file to include my ISP's nameservers. This means that nameserver requests require a PPP connection, but that is fine for my setup. I want to be online as much as possible while using the PCs. My /etc/resolv.conf file is below:

linux has a network, mischief, defined in /etc/networks.

I never see the internet alias in my routing table. I do see the mischief network.

The routing table of linux looks like this when not connected to the Internet (you won't see this if diald is running because it will assume the default route):

And just in case you're curious, this is the routing table when diald is running and connected:

How to make dial-on-demand work

First, make PPP work. You should use your X windows tools (like Linuxconf) for this. Note that my ISP, one.net, has a "username" instead of "login" prompt, so set your chat script up appropriately (I use "ame" for the trigger). I always run my PPP connections in debug mode because I tend to see a lot of errors with my cheap modem. (I'm getting ADSL soon.)

Next, get Eric Schenk's diald and the diald patch. diald-1.16 won't compile without the patch to diald-1.16.5 on glibc systems. Check freshmeat.net's software finder for more recent updates. diald is also available from sunsite/metalab, but the patch is only available from the diald homepage. Make diald, but make sure that you edit the config.h file first! Some file locations differ under RedHat 5.1 and 5.2 from the diald defaults.

When you have made diald, configure a /etc/diald.conf file. Mine is below (warts and all):

You'll note that with my RedHat installation I can use the chat script built by the configuration utilities (Linuxconf, in my case), that is /etc/sysconfig/network-scripts/chat-ppp0.

Now, you should have a working diald. If you execute diald and then try to ping an outside, host, you modem should dial and your Linux box should attempt to connect to your ISP.

Starting diald automatically

If you successfully used diald, you should kill it off and add these lines to your /etc/rc.d/rc.local file. Ideally, these would be executed in a runlevel with a proper start/stop script, but I'm lazy.

The first line (ipfwadm) is dangerous. It essentially lets the firewall masquerade all input. A better firewall configuration is highly recommended. Not many people know how to spoof meaningful packets, but it would be possible for someone who knows how to spoof into your 192.168.1.x network. Potentially, they could access your insecure Win95/98 shares. See Setting up a secure firewall system for more information on fixing this hole.

The second line starts the diald binary in your chosen location. The default was /usr/sbin/diald, but I'm a stickler for seperate /usr/local and / partitions.

Back to Linuxconf...

Ensure that you have added an alias to your eth0 device. You can use Linuxconf or the Network Control panel applet (on RedHat systems) to do this. I chose Linuxconf, and I even added it the wrong way (as Adapter 2, with a device name of eth0:0), but it still worked. Don't repeat my mistake. (Hint: it should be under Networking -> Client Tasks -> Host name search path)

Oh, and while you're in Linuxconf, don't forget to turn on IPV4 packet forwarding, but don't export any routes and don't specify a default gateway. Diald does that for you. If you're a hands-on administrator, edit your /etc/sysconfig/network file and make sure that it has a line that says FORWARD_IPV4="yes".

Finally, you can reboot and if you were lucky and I didn't leave anything out, you can ping your ISP automagically.

Using mail

Before you can use e-mail, you need to decide how you are going to use it. I'm not in an X Windows environment, so many graphical tools aren't applicable--I can't use the latest and greatest version of Balsa, for instance, nor can I use Netscape Communicator for Linux. I could use a Windows 95 box and Microsoft Outlook or Eudora, but I would prefer to stay true to Open Source and Linux.

Fetching your mail

Fetchmail is a terrific tool by Eric S. Raymond. It has an interesting history that helps to explain why Open Source works for software development. I chose Fetchmail as the tool to grab my mail from my ISP.

It's worth noting that my mail is delivered in a POP-only mailbox. This is becoming more common as ISPs begin to use NT systems and avoid UNIX shells. Although my ISP offers a UNIX shell account and web hosting, I have a POP-only mail address through a special offer (my wife's e-mail address is the primary for the account, so she has the dial in, the default e-mail address, and the web space). This situation absolutely required the use of some way to use the POP server to retrieve my mail. I considered the very nice tool Eudora, but I realized that I wouldn't be able to access my mail from work via telnet if I used Eudora on my PC. Additionally, it would mean that my mail is removed from my native Linux environment and placed into Windows.

I've used Fetchmail in the past and was impressed with its ease of use and stability. I grabbed the latest build from http://www.tuxedo.org/~esr/fetchmail/. As root, I read the appropriate README files, configured it and ran make. It installed without a hitch. Note that I installed it to /usr/local/bin/fetchmail, and renamed my old version of Fetchmail to /usr/bin/__fetchmail. I then symlinked /usr/local/bin/fetchmail to /usr/bin/fetchmail.

Configuration was tricky. The best option is to use a working example, which is what I did. I'm including the interesting parts of my /home/cjones/.fetchmailrc file (I copied esr's example and modified it for my needs).

You'll note that I included a line that isn't really necessary: user cjones is cjones here. Additionally, I specified the POP3 port just to reduce the possibility of errors. You can test your .fetchmailrc without them--I am happy to have one that works.

Before your start Fetchmail, you have four more tasks. First, make sure than sendmail (or some other MTA) is running:

If it's not running, you should use Linuxconf to turn the service on. Fetchmail relies on a mail transfer agent (MTA) to deliver mail to local users.

Because my ISP requires that all connections to the Internet be live, with real activity, I have to limit the polling of Fetchmail to a reasonable time--otherwise, I'll be on-line all night long! To do this, I created a simple shell script to start Fetchmail. This script has the added bonus of allowing the user to perform more extensive work around or with Fetchmail in the future. Note that all actions beyond this point should be performed as the normal user who will use fetchmail (in my case, I was logged in as cjones).

First, I created a script called /home/cjones/runfetchmail and made it executable (chmod u+x). The script, at this point, contains two lines:

Fetchmail uses the name of the user running it to retrieve the .fetchmailrc from his home directory when starting. You can test this shell script by entering ~/runfetchmail

Next, I created a simple shell script to stop fetchmail automatically, /home/cjones/stopfetchmail. Its two lines look like:

Fetchmail is kind enough to record its process id in my home directory and shuts down gracefully when given a SIGTERM. Again, this script can be tested with the command ~/stopfetchmail.

Finally, I created cron jobs to automatically start Fetchmail in the morning and stop it when I should be going to bed. I created a crontab file in my home directory (/home/cjones/.crontab) and entered the following lines:

I then registered the crontab with the cron process via the command crontab ~/.crontab. To verify that the crontab was entered correctly, I entered the command crontab -l and saw my cron schedule.

I was now set to start fetchmail (manually, since it wasn't 8 am anymore). I'm pleased to report that my mail is delivered without a hitch.

A Note on MUTT and domain names

Careful readers will note that my computer is called linux.mischief. This poses a problem to the real world. I post my mail messages to the SMTP of the destination, but because I don't have a real DNS name for my server, an empty name was being presented.

I chose the MUTT mail user agent (MUA) because it is very customizable, works well in terminal mode, and has the right ethic. (I had been pointed to it by a review in 32-Bits Online.) It works without any special .muttrc files and a version without a serious security hole comes with RedHat 5.2. The best of all worlds!

I'm including a copy of my /home/cjones/.muttrc file here so you can avoid some pitfalls I ran into. Note that one majordomo listserv I have attempted to use queries my server for my servername and ignores the name I specify, so all the wrinkles haven't been ironed out yet. Note that I have two additional include files for my .muttrc that I'm not putting on-line. Read the MUTT Manual to get an idea of what is going on inside these files.

You can find better, more customized examples from the MUTT homepage.

To fix the problem with your mail address not being recognized as what you specify, you need to tell sendmail to masquerade your hostname. I did that this way:

  1. Move /etc/sendmail.cf to /usr/local/etc and setting a symbolic link back to /etc/sendmail.cf
  2. Use less to search for the string DM (enter /DM when paging with less)
  3. Enter the vi editor when you find a line that has DM by itself, and a line above mentioning masquerading
  4. Move the cursor to the end of the DM line and then hit a (append) to add your mail domain name (mine is one.net to that line. No spaces. Hit followed by :wq to save your changes.
  5. You can now quit less and restart sendmail with /etc/rc.d/init.d/sendmail restart
Your computer now masquerades your ISP mail host.

Setting up a secure firewall system

There are two elements in an approach to firewall design: paranoia and trust. Striking a balance is what allows you to create a usable firewall design. I tend toward trust in my personal use, but my work environment is characterized by varying zones of paranoia. Ideally, I would have three zones of security (Internet, DMZ, and intranet). Unfortunately, I don't have the hardware (or the real need) to do this with my home network. My network relies very heavily on the single Linux server.

As noted above, the default firewall design listed above includes IP masquerading, but in an insecure way. The challenge is to provide good security, while allowing my home network of Windows boxes to access the Internet as transparently as the insecure method.

A great resource for Linux Kernels of the 2.0 vintage is XOS paper of Linux firewalls. These instructions are exceptionally clear and great for a permanent, small office connection to the Internet. They're not quite what I need, though. Another good source is the IP Masquerading HOWTO.

Keeping your connection up during business hours

My ISP doesn't like keep-alive programs. Fetchmail, by itself, won't keep my connection up. The best option I have is to have my wife browse the web while I'm at work.

Out of the box, however, diald has some really short timeouts for connections to your ISP. I increased the timeouts so that idle times will be just short of those used by my ISP. I changed a few key lines in my /usr/lib/diald/standard.filter file. I'm including the surrounding comment block so that it's easier to find my changes, which consist of commenting out the original line, and replacing it with my version.

Network Time Protocol

A useful service to utilize during on-line hours is the network time protocol (NTP). RedHat 5.2 includes a control script, /etc/rc.d/init.d/xntpd, which can be used to start and stop the daemon. To make this run during hours you expect the server to be on-line, add these lines to your /etc/crontab, then re-read your crontab file with crontab /etc/crontab:

By default, NTP won't cause diald to bring up a link (see /usr/lib/diald/standard.filter). These crontab entries are more a courtesy than anything else.

Letting the world know your IP

It would be nice to be able to access your Linux server from work. My wife uses the Internet from home all day, and it seems reasonable that I should be able to check my mail at home, send files back and forth from work, and help troubleshoot problems with her connection by using my PC from work. But first I need to know the IP address of my connection.

I read the man page for pppd and learned how to execute scripts after it has finished running. First, I created an /etc/ppp/ipup.local file, called by /etc/ppp/ipup, which is called by pppd immediately after a successful login and negotiation of IP addresses. On my RedHat 5.2 system, /etc/ppp/ipup sets the default route and expects local changes to be found in ipup.local. The ipup.local file is very simple:

I added two additional files, recordip which is an expect script that connects to my ISP's ftp server, and registerip.sh which sets up the environment for recordip and captures STDIO and STDERR to an ftp.log file.

/usr/local/etc/ppp/recordip connects to the ftp server and uploads the ip.data file for the web. It looks like:

/usr/local/etc/ppp/registerip.sh waits for five seconds to ensure the link is up, then captures debugging information and execute the expect script. This shell script looks like:

There are other ways to do this, some of which I considered and tried. I discounted using a Perl script to POST to a CGI script on the webserver because ftp provides better security (imagine doing a simple, unathenticated POST to your ISP--and someone malicious doing that as well). I also toyed with building .netrc files for FTP, but the only one I found that worked well was to use expect scripts. While .netrc with a special user seemed to be the simplest, cleanest implementation, it didn't end up working.

The final step is to create a redirector on the web site. There isn't any programming involved on my ISP--I was able to build it using a server-side include (SSI), index.shtml:

Now, I can retrieve the IP address of my Linux server at any time by examining the ip.data file. I can browse my home Linux server by visiting http://w3.one.net/~menagery/chris/home/. It's not as cool as Dynamic DNS, but it's good enough for me. This works with Internet Explorer, but Netscape Navigator has trouble with the line break that SSI puts in the META tag. My next revision will include a CGI Perl script to output the HTML page.

Sharing the LaserJet 6L with Windows

I attached a LaserJet 6L printer to my sole printer port on this computer. My intention is to have it print Linux documentation, program code that I'm working on, as well as share the 600dpi output with the Windows computers (via Samba).

I use the simple lpd printer daemon with the /etc/printcap file to define the printer point. Because it is my default printer in this configuration (one of my line printers is currently purposed for printing labels for a club, and the other is disconnected until or unless I get another computer or parallel port), I feel comfortable assigning the default lp device to the printer.

Note, though, that the printer interprets line feeds, the default UNIX end-of-line character, literally and expects carriage returns to properly print lines. To get around this I need a print filter. For this, I used my favorite print filter, MagicFilter, which automatically prints files in an appropriate manner for HP PCL printers (among others).

MagicFilter installs easily: I ran the ./configure command, followed by make ; make install ; make install_filters. I didn't want all those filters in /usr/local/bin because I only need a few. I moved all the filters to a /usr/local/bin/filters directory and symlinked the ljet4l-filter file in /usr/local/bin. This is enough of a filter to get me started, though I still need to modify the filter source file (and recompile) to optimize for the LaserJet 6L.

With the filter in place, I needed to create the /etc/printcap, which is below:

This file, line by line:

Next, I needed to create the hplj6-filter file which would execute MagicFilter for file output. I created this simple shell script and marked it chmod a+x so everyone could execute it. (This may be overkill, but I run a trusted system.)

Before testing, I needed to tell the lpd to reread /etc/printcap. On my RedHat system, I entered the command /etc/rc.d/init.d/lpd restart. Then, to test my configuration I printed the /etc/printcap file with the lpr /etc/printcap command. Assuming that the printout looks fine, I know that my configuration works.

Sharing the printer with Windows

Modem++: ADSL

Coming January 15th, 1999.

Changes to the configuration:

Todo:

A Better Redirector

Cincinnati Bell ADSL has become notoriously bad at staying up consistently. As a first step to keeping track of our current IP, I revisited the IP poster sequence with FTP and SSI. It's clumsy, I know that, and I felt guilty that I created something so crufty.

To atone, I offer my fixed version in Perl. There are only three files needed, and they are mercifully short. Starting with the webserver, create these two Perl files and make them executable on your ISP's platform--my ISP requires mode 755 and the .cgi extension to be executable from the webserver. No problem! Additionally, on my ISP's installation of Apache, these files run under my user account permissions, obviating the need to create a world writable directory. Phew!

The first, recordip.cgi, records the IP of any host connecting. That means someone could spoof your IP by connecting to this URL. I've set up a cron job to refresh my IP every five minutes (this also helps when Cincinnati Bell drops the ADSL connection for no real reason and I'm at work where I can't manually record it).

The next file, index.cgi, is called by default by requesting http://w3.one.net/~menagery/chris/home. It's also a very straightforward script.

Finally, the short cron job to record the current IP is contained in this shell script called post_ip (URLs have been changed to protect my IP):

Of course, this presupposes that you have the Lynx web browser. Now, you'll need to add the simple script to your /etc/crontab file. Hint: if you have a MAILTO line in your crontab, change the address to "" or add a seperate crontab--it's unlikely that you'll want a new mail every five minutes containing the fact the post_ip ran well.


Notes

1:
Although I cannot recommend any of these products, as I have never used them, a perusal of a local computer warehouse store circular revealed these commercial products that would solve some of the issues raised in this article.

Of course, these solutions aren't Open Source.

Acknowledgements and Thanks

I'm no genius. I didn't do all this on my own. A huge number of talented people built all the tools I use, and I merely stand on the shoulders of giants. Some of these are: