Linux PPP Dial on Demand and IP Masquerade

A Hands-on How-toSM

from Brass Cannon Consulting

A little vague handwaving can often save hours of tedious explanation.

What's In Store

Suggestion: Skip directly to my new writeup on iptables. This page is seriously old.


Introduction

If you aren't a subscriber to Linux Journal, try to find the September 2001 issue. Here's a link directly to the article that caught my eye - "Taming the Wild Netfilter" - but the entire issue is a treasure trove of security and networking tips.

Why this page was written

As I explain in "What is a Hands-On How-To?" it took me about four days to put my LAN onto the Internet -- but much of that was spent reading several weeks of saved articles from the newsgroup comp.os.linux.networking. The main reason I wrote this page was to collect all of that information in one place... and hopefully cut down on some of the repeat traffic. The same few questions were being asked over and over again, to the point they were drowning out all other topics. I'm happy that this page (and others written by equally frustrated people) have improved that situation.

To all of you who patiently answered those questions and who have since sent me suggestions for this page: Thank you! To those of you who have suggested that I should contribute this to the LDP: I'm flattered, but I don't use TeX. Better I should leave the comprehensive and strictly-formatted stuff to others.

NB: This is NOT intended for someone who's trying to run an ISP on a Linux box; it's for a home/small-business user who wants to dial *into* an ISP with a Linux box, and share that connection with a reasonable number of locally networked machines (2 - 6, say). That allows us to make things simpler, because we can ignore complex issues that the official HOWTOs have to cover. The idea is to provide one concrete "Hands-On" example to those who are new to a topic, in the hope that the real HOWTO will then be more useful. Hence the distinction between a Brass Cannon "Hands-On How-To"SM and a "Linux HOW-TO".

State of the art, 1998 - Kernel 2.0 and IP Masquerade

A more general term for "IP Masquerade" is NAT or Network Address Translation. It's a technique that has evolved quickly over the last two years, and in fact most of the information below is already starting to look dated. But our goal isn't to provide the latest tools or the most complete reference; it's to provide a guided tour through one working setup. Perhaps it will be the exact one you need, but even if it isn't, having one clear example may help you make sense of the canonical How-To. That's not meant as criticism of the official HOWTO -- it's just that it has to cover many different topics, and it may be intimidating to a new user. I urge you to read it now to see how it matches up with my more limited case study; and re-read it again later, after you get your system working.

ASSUMPTIONS:

Step One: THE LAN

First, let's make sure your LAN (Local Area Network) is working right. There are three blocks of IP addresses that have been set aside for private IP networking: do not just make up arbitrary addresses or chaos will rise up and bite you. You do not want the legitimate owner of the "random" address you made up to come and knock on your door, especially since (if you let things go that far) he'll have a warrant to confiscate everything in your house that has an Ethernet port, and to the law-enforcement agents with him that will probably include your TV and toaster. (Unlikely? Yeah. But why don't we make sure it doesn't happen. All you have to do is use one of the reserved blocks.)

I'm using Class B addresses because I didn't know any better when I started my LAN quite a while ago, so all my machines are in the 172.16.0.x netblock. The addresses you can safely use are:

        10.x.x.x                netmask 255.0.0.0       class A
        172.[16-31].x.x         netmask 255.255.0.0     class B
        192.168.[0-255].x       netmask 255.255.255.0   class C

...where x is neither 0 nor 255, but any value in between.

"Class A, B, or C" is really obsolete terminology now that small blocks are assigned using CIDR, classless subnetting; but old habits die hard.

If you want to provide a connection for Windows boxes, create "hosts" and "lmhosts" files in your Windows directory. Here's what mine look like:

# hosts
# The IP address and the host name should be separated by at least one
# space.
172.16.0.1      bazooka
172.16.0.3      mortar
172.16.0.2      cannon
172.16.0.6      howie
127.0.0.1       localhost
# lmhosts
127.0.0.1       localhost
172.16.0.1      bazooka  #PRE
172.16.0.2      cannon   #PRE
172.16.0.3      mortar   #PRE
172.16.0.6      howie    #PRE

In Windows XP, look for these files in C:\windows\system32\drivers\etc Note that the files "hosts.sam" and "lmhosts.sam" are sample files, and editing them will not have any effect -- the files must be named "hosts" and "lmhosts" with no file extension.

The hosts file is called /etc/hosts on a Linux machine. The point of giving each machine a copy is that you don't want machines sending out nameserver queries every time you copy a file between two machines on your own LAN. You can easily give your own machines static, private addresses, so why not take advantage of that?

Now, on each Windows machine, go into the Network icon of Control Panel and make sure that the machine knows its own name and IP address. While you are in there, enter the Linux box as your GATEWAY machine. In my case, that means setting "howie" (172.16.0.6) as the gateway for all the others. Enter your ISP's nameserver address(es) as the nameserver for the Windows boxes -- that way when you go Web surfing to a non-local machine, the first thing the Windows box will do is send out a nameserver lookup, and that will trigger the Linux box to dial out. On the other hand, if your box is dialing out when you don't want it to, now you know why!

When you're done, you should be able to pop up a DOS window and ping any other machine on the local network. (You did choose TCP/IP as an available protocol, yes?) Don't try pinging an Internet address yet.

On the Linux box, update /etc/hosts to match the list that all the other machines have agreed to. You should now be able to telnet into your Linux box from any of the Windows machines (though not as "root").

You need to fix your DNS, probably, so that the Linux box can look up machines by name and find their numeric IP addresses. Bare minimum: update /etc/resolv.conf to include the NUMERIC address of your ISP's nameserver(s). This is a "resolver only" nameserver setup. You can have up to three nameservers. The file looks like this:

	# /etc/resolv.conf
	nameserver 123.123.123.123
	nameserver 123.123.123.124
	options attempts:5

The options line is something an alert reader found -- it tells the resolver to try each nameserver up to five times instead of the default (twice). This gives you up to 25 seconds to complete your dial up connection before Netscape gives you the "host not found" error.

Also make sure you have a file (/etc/host.conf when I first wrote this, but later changed to /etc/nsswitch.conf) that tells your machine to use your local hosts file first (hosts), before it uses the nameservers (bind):

	# /etc/host.conf  (circa 2002)
	order hosts,bind
	multi on

This was later replaced by a different configuration file called /etc/nsswitch.conf. To get the same effect as the host.conf file above, we want to tell your Linux machine to use its local /etc/hosts file first, and Internet DNS to find addresses if they do not appear in /etc/hosts. The entry to do that in nsswitch.conf looks like this:

	hosts: files dns

In case you're curious, this does not interfere with Samba (file and printer sharing) between the Linux box and the Windows boxes. (And Samba has absolutely nothing to do with anything else we're talking about here. A lot of people ask about it in connection with setting up their home networks, but it has nothing to do with giving that home network access to the Internet.)

If this is a fresh install of RedHat 5.0, check /etc/sysconfig/network for a line "FORWARD_IPV4=yes". Also check /etc/init.d/network to see that it's writing something (with the value "1") to the pseudo-file "/proc/sys/net/ipv4/ip_forward". This is turned off by default in 5.0. But if you've upgraded to RedHat 5.0 from an earlier release, you're probably in good shape, strange to say -- the 4.x versions set these values by default!

Step Two: PPPD AND DIAL-ON-DEMAND

Follow the instructions in the pppd distribution to properly install it. Those instructions don't need any embellishment... but when you recompile your kernel, be sure to choose "Prompt for experimental" and enable all the IP aliasing, forwarding, and masquerading features. We'll need those for the last step, which is providing Internet service to the rest of your LAN.

Another contributor emphasizes the importance of getting your modem init strings correct -- he writes:

You can see a transcript of everything Win95 does to your modem under the covers by going to: Control Panel->Modems->YourModem->Properties->Connection->Advanced Check the "Record to a log file" box and dial in. Then hang up and look at the C:\WINDOWS\MODEMLOG.TXT file, it's got all sorts of useful information, including how it inits your modem by default. Steal those strings as needed, or if you've got a modem that can do it, flash those settings as the default.

If you're compiling pppd from source, you may encounter a compile error in ppp.c -- there's a messed-up #define that thinks only development kernels (2.1.x) need a second parameter on FREE_SKB. That is not right. Make the #define pass the second parm unconditionally, and ppp.c will compile. There have been several posts in comp.os.linux.networking about that this (check www.deja.com to see the discussion in comp.os.linux.networking for 8 May - 11 May 1998).

If you have a working "chat" script to dial into your provider, keep it. If you don't, take a look at how you connect now. If you have a script on your Windows box that "types" your username and password in answer to a prompt and then types "ppp" at the command line, then you'll need to do the same thing with "chat." If Win95 just connects you up as though by magic, you're probably using PAP or CHAP, and you'll need to set up two files in /etc/ppp called "pap-secrets" and "chap-secrets". (Deja.com is your friend! I've posted about those separately, but I'll recap briefly here.)

The additional parameters to make pppd do dial on demand look like this -- you can put all of this into the file /etc/ppp/options:

demand
idle 60
ipcp-accept-remote
ipcp-accept-local
lock
noauth
defaultroute
user myaccount
remotename myisp
modem 57600 crtscts
connect '/usr/sbin/chat -v -f /etc/ppp/call-isp'

Taking them one at a time: "demand" means dial-on-demand, so when you run pppd it becomes a daemon (a server process that waits in the background); it does not dial immediately.

"idle 60" means the connection will drop in 60 seconds when it goes idle -- adjust the number of seconds to suit your case, but start with a small number so you can see that it does drop.

The "ipcp-accept" lines allow the ISP to assign you a dynamic address, regardless of what nonsense you've put in your configuration files, and yes, it works.

"lock" makes sure that pppd can control the modem and not be usurped by some other program, and "noauth" means that your ISP does not have to authenticate itself to you (remember, we decided you're a mere customer, not a fellow ISP).

"defaultroute" makes ppp reroute your LAN traffic out onto the Internet while you're connected.

I mentioned PAP authentication -- the next two lines tell pppd where to look in your pap-secrets file so it can log you on. Note that I am NOT looking for a Login: Password: prompt -- the PAP/CHAP negotiation takes care of all that. The "-secrets" files both look the same:

#user       remotename    secret
myaccount   myisp   mypassword

You can have more than one provider or account line; the values in the options file point pppd to the correct line so it knows what login name (myaccount) and password (mypassword) to use. Be sure that the secrets files are only readable by root!

That's about it; you may need to replace "modem" with "/dev/modem", and you should have a symbolic link to make /dev/modem point to an existing serial callout device, /dev/CUAn "57600" is a connect speed I only get some of the time, and "crtscts" specifies hardware modem control.

The last line points to a script that tells chat HOW to dial when it's time to dial. It looks like this:

 TIMEOUT        5
 ABORT  '\nBUSY\r'
 ABORT '\nNO ANSWER\r'
 ''     \rAT
 'OK-+++\c-OK'  ATH0
 TIMEOUT 40
 OK     ATDT555-1212
 CONNECT        ''

You may need to add these lines to the file /etc/conf.modules:

    alias ppp-compress-21 bsd_comp
    alias ppp-compress-24 ppp_deflate
    alias ppp-compress-26 ppp_deflate

This got rid of some error messages in my log file. This is also where you get rid of those pesky errors about "net-pf-x not found" -- those are the Appletalk and Netware modules which you probably chose not to compile:

    alias net-pf-4   off
    alias net-pf-5   off

For 2.0 kernels, you have to invoke pppd in a somewhat strange way; as root, enter:

    % pppd :10.0.0.2

What is that all about? You have to provide a dummy IP address when using the "demand" option -- it doesn't matter too much what it is, because you've also set it up to accept the dynamic address assigned to you by your provider, and this will override the address you put on the command line. But it should be an address that is outside of your LAN, i.e. not 172.16.x.x if you've set up a Class B as I did, nor 192.168.0.x if your LAN is set up as a Class C. To avoid hours of handwaving, just replace the 10.0.0.2 with the address of your ISP's main machine or nameserver.

When you enter this command line, it should NOT dial your phone. But if you then ping your provider, the modem should dial and connect you, and only a few pings will "fall on the (proverbial) floor." To improve on that, use the "options" line in /etc/resolv.conf as given above, and add this line to /etc/rc.d/rc.local:

    echo 1 > /proc/sys/net/ipv4/ip_dynaddr

This tries to makes your system "hold" outgoing packets until your dynamic IP address assignment is in place. With this set, you should be able to start up Netscape on a Windows box and enter a URL, which causes the Linux box to dial and connect you before Netscape gives up and displays its timeout message.

Before I heard about this trick, the Netscape connection would always time out and I'd have to hit the "reload" button. To avoid that, I would ping my ISP from the Windows machine (I added an icon to my desktop to make that easy) and wait for the modem to connect before trying to run Netscape -- nowadays I still do a "ping" command first, but I don't wait for the connection to complete before starting Netscape. In short: setting ip_dynaddr isn't perfect, but it helps.

If your browser or email program times out while the modem is dialing, don't use your email program to make the connection! Make the connection by pinging your provider or otherwise throwing away some packets you don't care about, then use the connection.

I've gotten more mail about this topic than the rest of the page -- I really don't have anything more to add.

Take a look at your /var/log/messages file; it should be relatively clean of nasty messages. (If you're not using RedHat, the messages file may be somewhere else; "locate" is your friend.) Use the tail command to see just the newest messages, or "tail -f" to follow along as new lines are added to the log:

    % tail /var/log/messages
    % tail -f /var/log/messages

Step Three: IP MASQUERADE

We're almost there!

The last step is making the Linux box dial and pass packets when your *other* machines need a connection. This is the part that cost me my remaining hair... I could NOT make it work with the "172.16.0.0/24" syntax that the HOWTO's recommend. It works now that I've specified my machines as individuals, including the Linux box itself. (Someone has suggested that it may be because I was using a Class B netblock with a Class C netmask. I haven't been able to confirm that, but many others have written to say that doing it my way worked for them.)

That said, here's a script that works for me.

8< cut here----------------------
#!/bin/sh
# /etc/ppp/masq  -- start/stop IP masquerade services (2.0 kernels)

NAME=$0
case "$1" in
  start)
        echo -n "Starting IP Masquerade support... "
# flush current settings:
        /sbin/ipfwadm -O -f
        /sbin/ipfwadm -I -f
        /sbin/ipfwadm -F -f
# default policy is "deny"
        /sbin/ipfwadm -F -p deny
# block Windows housekeeping traffic from triggering autodial etc:
        /sbin/ipfwadm -F -a deny -P tcp -S 0.0.0.0/0 137:139
        /sbin/ipfwadm -F -a deny -P udp -S 0.0.0.0/0 137:139
# provide masquerade services for the local machines - fix these addresses
# to match your LAN and be sure to include the Linux machine itself!
        /sbin/ipfwadm -F -a m -S 172.16.0.1 -D 0.0.0.0/0 -P all
        /sbin/ipfwadm -F -a m -S 172.16.0.2 -D 0.0.0.0/0 -P all
        /sbin/ipfwadm -F -a m -S 172.16.0.3 -D 0.0.0.0/0 -P all
        /sbin/ipfwadm -F -a m -S 172.16.0.6 -D 0.0.0.0/0 -P all
# install these modules if you have a need for them...
        /sbin/insmod ip_masq_cuseeme
        /sbin/insmod ip_masq_ftp
        /sbin/insmod ip_masq_irc
        /sbin/insmod ip_masq_quake
        /sbin/insmod ip_masq_raudio
        /sbin/insmod ip_masq_vdolive
        echo "Done."
        ;;

 stop)
        echo -n "Stopping IP masquerade..."
        /sbin/rmmod ip_masq_cuseeme
        /sbin/rmmod ip_masq_ftp
        /sbin/rmmod ip_masq_irc
        /sbin/rmmod ip_masq_quake
        /sbin/rmmod ip_masq_raudio
        /sbin/rmmod ip_masq_vdolive
        /sbin/ipfwadm -F -f
        echo "Done."
        ;;

 *)
        echo "Usage:  /etc/ppp/$NAME {start|stop}"
        exit 1
        ;;
esac

exit 0
8< cut here----------------------

I added /etc/ppp to my $PATH line, so any scripts there can be run by issuing their name on the command line. So, enter "masq start" and you should now be able to go to a Windows machine on your LAN, open a DOS box and type in "ping some.isp.com". There's a little delay while your modem dials, and the first batch of pings may still fall on the floor -- but as soon as your modem finishes dialing, another attempt to ping should work. It's convenient to do this from a Windows box because it sends only three pings and then stops; from a Linux box, use the -c (count) option so that it doesn't keep pinging forever:

    % ping -c 3 some.isp.com

Put these three lines at the end of your rc.local file, wherever it is, to make the changes permanent:

        /etc/ppp/masq start
        echo 1 > /proc/sys/net/ipv4/ip_dynaddr
        /usr/sbin/pppd :10.0.0.2

Now, here's the part that drove me nuts. I had been using the Wingate proxy server for over a year, so I thought I knew what was going on. Bzzzt! Wrong! I pointed Netscape at my Linux box, howie, and got back "FORBIDDEN You do not have permission to log onto http://www.netscape.com from this server." Guess what, folks?

IP MASQUERADE IS NOT THE SAME THING AS A PROXY SERVER.

You do NOT put your Linux box into Netscape's proxy setup. You do NOT put your Linux box into CuteFTP as the firewall. You do NOT use the tortured syntax in Eudora of "cannon#nic.com@howie". Take all that stuff out of your Windows app configuration files, because as far as your Windows boxes are concerned, you now have a DIRECT CONNECTION TO THE INTERNET!


ADDENDUM

A reader suggested a nifty shell-scripting trick to replace the multiple "insmod" lines above -- if you ever add a new module, you don't have to edit the script because it will automatically find it and load it along with the existing ones:

echo -n "Loading ipv4 masquerading modules for $(uname -r)... "
for i in $(/bin/ls -1 /lib/modules/$(/bin/uname -r)/ipv4/ip_masq_*) ; \
do
 /sbin/modprobe $i
done
echo "done."

I'm leaving my original version up so as not to confuse the new user with too much cleverness, but this is a nice trick that is handy whenever you have a set of files that need to be located based on the version number of your Linux kernel.


Changes with kernel 2.2.x

The story of my life; get it working and someone replaces it! No sooner did I complete the page to this point when the 2.2 kernel was released, and ipfwadm was "deprecated," or declared obsolete. Paul "Rusty" Russell, networking and kernel guru, replaced it with a more flexible tool called "ipchains." Technically, the work of address translation is actually done in the networking code of the kernel, and the ipfwadm and ipchains binaries are merely two versions of a user interface that allows you to tell the kernel what to do. Translating a working ipfwadm script to use the ipchains syntax isn't so hard -- see the ipchains HOWTO for the details I've omitted.

So, the user mode tool, ipchains, is like ipfwadm: it allows you to control the port forwarding features you compiled into your kernel. It won't work unless the kernel supports it, but compiling support into the kernel doesn't do much good without the tool to turn it on. For a while, the only way to get the ipchains tool was to go to Rusty's site at rustcorp.com.au; fortunately, most distributions have caught up.

Here's a modified copy of my script above, translated to use ipchains.

There are also "do it yourself firewall" scripts that will take advantage of ipchains -- one of the nicer ones is Bastille Linux. It started out as an attempt to build a secure Linux distribution from scratch, but for now it's taken the form of a general security lockdown (or "hardening") script, written in Perl and compatible with Mandrake or RedHat. Rather than try to reinvent the wheel, I suggest you pick up a copy and "Use the Source, Luke!" Look at what they're doing, read their explanation of why they are doing it, and either apply it as written or adapt it to your needs.


If you jumped down here without reading the PPP setup stuff, you might want to go back to the top.


State of the art 2000: NETFILTER

As it turns out, though, Rusty isn't the sort of chap to let grass grow under his feet. In the course of his analysis of networking issues, he came to the conclusion that ipchains is merely a special case and that it would be better to "deprecate" ipchains in favor of an even more flexible tool, now called netfilter. As above, netfilter is a kernel service, which you can direct through various user interface modules. It will simulate ipfwadm or ipchains, but the preferred user interface to netfilter is now called iptables. This assumed a 2.4 kernel with "experimental" features enabled... and the good news is that four years later, iptables is still the way you set up your software firewall rules under the 2.6 kernel. Whew!

In fact, when you set up a new Linux install (using Fedora, for example), you will be asked whether to install a software firewall, and what services if any you want to allow to pass through it. Let's say we wanted to have a web server on port 80 and 443 (for http and https), SSH access on port 22, and incoming mail on port 25. Those rules would make the default setup in /etc/sysconfig/iptables look something like this:

# Firewall configuration written by system-config-securitylevel
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:RH-Firewall-1-INPUT - [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp --icmp-type any -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT

Ugly, huh? Could be worse -- Rusty Russell wrote some friendly documentation for this and it will make sense if you look for it. Just to break the ice -- there are three rulesets that always exist by default. They are INPUT, FORWARD, and OUTPUT. To create and manage your firewall, you will begin by defining a new ruleset in addition to these, and then telling iptables to "jump" into it.

In this case, we are defining a new ruleset with the (ugly) name RH-Firewall-1-INPUT and we are jumping to it from the basic INPUT and FORWARD rules. In other words, any packet that finds itself in the INPUT or FORWARD rule will jump to the RH-Firewall-1-INPUT rules and subject itself to all of them as well.

RH-Firewall-1-INPUT is a set of rules, not just one rule. All of these tests will be checked until one of them applies:


More about iptables - Aug. 2007

It's been a while, but I've finally had a reason to sit down and work through an iptables setup from scratch, so I've written a new page.

If you're curious about assigning IP addresses to an Ethernet interface or creating "virtual Ethernet" interfaces on a single card, read on!



©1998-2007 Brass Cannon Consulting
Free for public distribution, on the condition that this notice appears on each copy.


You can discuss this article with the author in the Feedback section of the Brass Cannon webboard!


Google
 
Web handsonhowto.com



Hands-On How-To Index