Before Wi-Fi: Recreating a Linux PPP Dial-up Connection
A hands-on retrospective using pppd, a modem chat script and the sound of the early internet coming to life.
Back in the day, circa 1994 my home pc consisted of a dual boot custom built Intel dx2/66 pc, 4mb RAM, 20Mb hard drive and Windows 3.11 for Workgroups. The second hard drive consisted of a Slackware Linux installation (from floppies) onto a second 40mb hard drive. I used a boot floppy to boot from the Linux disc on the primary path on the secondary controller (IDE)
I was a bit disenchanted with using Windows and Winsock (tcp stack) and it’s slip (Serial Line IP) dialler to my local ISP.
Dual booting into LINUX, X11 and fvwm (window manager) with Mozilla’s Netscape browser seemed like a much nicer prospect. To faciliate that I
would need to setup pppd to connect via /dev/ttyS0 and a custom dialer script which connected when I made a web request (invoked via a DNS lookup)
via my US Robotics Sportster modem
Long before a network connection became something a laptop silently found for itself, getting online was a sequence you could hear, watch and—when it failed—debug one line at a time.
This lab recreates that experience with Linux, pppd, a serial modem and a chat script. It is less about nostalgia than observability: every stage of the connection is exposed.
The stack
The moving parts were wonderfully small:
- a modem connected to a serial port such as
/dev/ttyS0 chatto speak the modem’s AT command languagepppdto negotiate the point-to-point network link- an ISP telephone number, username and password
chat handled the conversation before the network existed. pppd took over once the remote system was ready to speak PPP.
Talking to the modem
A minimal chat script looked something like this:
ABORT 'BUSY'
ABORT 'NO CARRIER'
ABORT 'NO DIALTONE'
TIMEOUT 45
'' ATZ
OK ATDT0202242322
CONNECT ''
Read it from left to right: expect nothing, reset the modem, wait for OK, dial, then wait for CONNECT. The abort strings turned common failures into useful answers instead of an indefinite wait.
The modem’s transcript was the progress indicator:
ATZ
OK
ATDT0202242322
CONNECT 14400
Those four lines represented a physical phone circuit, two modems agreeing how to communicate and a serial link ready for the next protocol.
Handing over to pppd
The peer configuration told pppd where to find the modem and how to invoke the script:
/dev/ttyS0 115200
connect "/usr/sbin/chat -v -f /etc/ppp/chat-ips"
noauth
defaultroute
usepeerdns
persist
Once authentication and negotiation completed, the logs became unmistakably network-shaped:
Serial connection established.
Using interface ppp0
Connect: ppp0 <--> /dev/ttyS0
local IP address 10.10.10.42
remote IP address 10.10.10.1
primary DNS address 10.10.10.2
At that point ppp0 was a real network interface. A default route and DNS configuration turned the narrow serial link into an internet connection available to the whole machine.
What the old setup taught well
Dial-up forced a clean mental model. First establish the physical connection. Then create the data link. Authenticate. Negotiate network parameters. Add routes and name resolution. Test each layer in order.
Modern platforms automate most of that work, which is exactly why the lesson remains useful. When a managed service says only that a connection failed, the quickest route to the answer is still to ask which layer never became healthy.
The 33.6 kbit/s link was slow. The feedback loop was not.
Next time
The next part of this reconstruction will put a small network behind the dial-up host, add forwarding and revisit the role a Squid proxy played when every downloaded byte mattered.