Dial-up From Scratch: What Running Your Own ISP on a Pi Teaches You About Networking
Source: lobsters
Jeff Geerling’s recent project does something most people would call pointless: it turns a Raspberry Pi into a functioning dial-up ISP, complete with analog modem negotiation, PPP sessions, and actual internet routing over a copper phone line. The practical value is close to zero. The educational value is surprisingly high.
Dial-up is gone from most of the world, but the protocols underneath it never disappeared. PPP is still used in DSL (PPPoE), in VPN tunnels, and in cellular data connections. Understanding how a 1990s ISP actually worked, at the packet level, gives you a much cleaner mental model of how modern connection-oriented networking stacks are layered. Running it on a Pi is just the most satisfying way to do that.
The Hardware Side
The first thing you need is an analog modem. Not a cable modem, not a DSL modem: a real POTS (Plain Old Telephone System) modem that generates the audio tones you remember from the 1990s. Most of these are USB devices with onboard DSPs and present as serial ports via CDC-ACM or a vendor-specific driver. On Linux they show up as /dev/ttyUSB0 or /dev/ttyACM0.
The Raspberry Pi 5 has native UART exposed on the GPIO header (/dev/ttyAMA0), but for this kind of project a USB modem is far more practical. You need something Hayes-compatible that speaks AT commands. US Robotics 5686E external modems are popular for this because they have a proper hardware UART and a physical speaker you can mute with ATM0. Cheap USB “soft” modems (Winmodems) offload DSP work to the host CPU and often have poor Linux driver support.
On the phone line side, you need a real POTS line or a FXO/FXS ATA that gives you an analog interface to work with. Some builders use VoIP gateways configured to pass audio without compression, since codecs like G.711 truncate the high-frequency content that modem tones depend on. G.726 or G.729 will break a modem connection entirely.
Initializing the Modem
Before any software stack comes into play, the modem needs to be configured via AT commands. A basic initialization string for answering calls looks like:
ATZ ; reset to factory defaults
ATE0 ; disable echo
ATM0 ; speaker off
ATS0=1 ; auto-answer on first ring
AT&C1 ; DCD follows carrier
AT&D2 ; DTR drop = hang up
The S0 register controls auto-answer behavior. Setting it to 1 means the modem picks up on the first ring, which is what you want for an ISP that is always ready. &C1 and &D2 are critical for software to detect when a caller hangs up, by watching the DCD and DTR signals respectively.
The Software Stack: mgetty and pppd
The traditional Linux dial-up ISP stack has two primary components: mgetty and pppd.
mgetty watches the serial port, answers incoming calls, handles the modem chat sequence, and then either presents a login prompt or hands off to pppd. The relevant section of /etc/mgetty/mgetty.config looks like:
port ttyUSB0
speed 115200
data-only yes
debug 4
The data-only flag skips fax and voice call detection and goes straight to PPP mode. With a direct-PPP setup you can also invoke pppd directly from mgetty’s login.config:
/AutoPPP/ - a_ppp /usr/sbin/pppd auth -chap +pap login debug
This pattern matches the CLIENT string that Windows dial-up clients send during PPP negotiation and launches pppd automatically.
pppd handles the actual PPP protocol. A minimal /etc/ppp/options for an ISP server:
auth
+pap
-chap
ms-dns 8.8.8.8
ms-dns 8.8.4.4
proxyarp
192.168.100.1:192.168.100.2
netmask 255.255.255.255
lock
crtscts
nodefaultroute
The 192.168.100.1:192.168.100.2 syntax assigns the local (server) IP and the remote (client) IP for this PPP session. proxyarp makes the Pi respond to ARP requests for the client’s IP on the LAN side, which simplifies routing. auth forces authentication before the session is established.
PAP credentials go in /etc/ppp/pap-secrets:
# client server secret IP addresses
testuser * password123 *
For a real ISP you would replace this with a RADIUS backend using pppd’s radius plugin, but for a Pi lab setup, flat files work fine.
What PPP Actually Does
PPP (defined in RFC 1661) is a three-phase protocol. Understanding it explains a lot about how connection-oriented networking works generally.
First comes LCP (Link Control Protocol), which negotiates the link parameters: maximum transmission unit, authentication method, compression, and magic numbers for loopback detection. Both ends exchange Configure-Request packets and ack or nak each option until they converge.
Next comes authentication. With PAP, the client sends the username and password in plaintext. With CHAP, the server sends a challenge, and the client responds with an MD5 hash of the challenge plus the password, so the password itself never crosses the wire.
Finally comes NCP (Network Control Protocol). For IPv4, this is IPCP, which negotiates IP addresses and DNS servers. Once IPCP completes and both ends have agreed on addresses, the link is up and IP packets can flow.
The whole negotiation takes about two seconds on a real connection. When you heard the modem screech, the first few seconds of that noise were the modems negotiating physical layer parameters (carrier frequency, symbol rate, equalization), and then PPP negotiation happened in the digital layer on top.
Routing the Traffic
Once a client connects, the Pi needs to forward their packets to the internet. This requires IP forwarding and NAT:
# Enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
# Or persistently in /etc/sysctl.conf:
net.ipv4.ip_forward = 1
# NAT outbound traffic from PPP clients
iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o eth0 -j MASQUERADE
# Allow forwarded traffic
iptables -A FORWARD -i ppp+ -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o ppp+ -m state --state RELATED,ESTABLISHED -j ACCEPT
The ppp+ wildcard matches all PPP interfaces (ppp0, ppp1, etc.), which matters if you want to handle multiple simultaneous connections by running multiple modem lines.
The Modem Standard Rabbit Hole
V.90 and V.92 are the standards you most associate with 56k dial-up, but they only achieve near-56 kbps in the downstream direction, from the ISP to the client. The asymmetry is fundamental to the standard. V.90 (1998) uses PCM coding in one direction, taking advantage of the fact that ISPs were already connected to the phone network via digital T1/E1 lines. The upstream from the client is limited to 33.6 kbps by analog signal constraints.
V.92 (2000) improved upstream to 48 kbps and added Modem on Hold, which let you pause a data call to take a voice call on the same line. It was the last significant revision to the standard. The FCC’s Part 68 rules limit signal power on POTS lines, which sets a hard ceiling on what any modem standard can achieve over copper.
In practice, a Pi-based ISP will see connections at whatever rate the line quality and the modems negotiate. On a clean POTS line, 33.6 kbps (V.34) is reliably achievable. Getting close to 56 kbps requires the ISP side to have a digital connection to the phone network, which a Pi on a consumer phone line does not.
What Real ISP Modem Pools Looked Like
A 1990s ISP’s modem pool was a rack of Lucent MAX or Cisco AS5300 access servers, each handling 24 or 30 channels from a T1/E1 span. The ISP terminated a DS3 or multiple T1s, each T1 carrying 24 phone calls. Every call that came in was answered by a port on the access server, which ran PPP on one side and Ethernet on the other, with a RADIUS server handling authentication and accounting.
The Pi project collapses this entire rack into a single board plus a USB modem. The RADIUS server becomes pap-secrets. The T1 becomes a consumer phone line. The scale is off by orders of magnitude, but the architecture is identical.
Why This Matters Today
The copper POTS network is being actively decommissioned. AT&T has been seeking regulatory approval to retire copper in more markets, replacing it with fiber or fixed wireless. In some regions, POTS lines are genuinely difficult to obtain as a new service. Running this project in 2026 requires finding either an existing copper line or a VoIP gateway that accurately passes analog modem tones, which is its own challenge.
That constraint aside, the project matters because it forces you to confront layers of networking that higher-level tooling hides completely. When you configure a WireGuard tunnel or debug a PPPoE DSL connection, you are working with direct descendants of the same LCP/NCP negotiation that PPP has always used. Understanding the original makes the derivatives less opaque.
There is also a genuine niche use case: areas with no broadband but surviving POTS service. A shared dial-up connection running at 33.6 kbps is still useful for email, SSH, and text-based web access. It is not a joke bandwidth for those tasks. Lynx on a 33.6k connection loads a properly structured HTML page in under a second.
Running Multiple Lines
The natural next step after getting one connection working is running multiple simultaneous sessions. Each physical modem requires its own serial port and its own phone line. The Pi 5 has one native UART but multiple USB ports, so USB hubs with individually-powered ports and multiple USB modems can extend this.
pppd manages each connection as a separate process with its own interface (ppp0, ppp1, …), so the routing and iptables rules scale without modification. The binding between a phone number and a modem port is handled at the physical layer: each modem is plugged into one phone jack, and whichever number a caller dials determines which modem answers.
For anything beyond two or three lines, the more tractable approach is a multi-port serial card or a proper access server, but at that point you have left the Pi project territory entirely.
The Honest Assessment
This is a project you do because it is interesting, not because you need a dial-up ISP. The Raspberry Pi is a capable platform for it: Linux has solid PPP support, the GPIO UART is available for direct serial use, and the documentation for pppd and mgetty is comprehensive even if it predates most modern Linux users by a decade.
What you learn is not how to run dial-up. You learn how connection-oriented protocols negotiate, how authentication integrates into the link layer, how NAT works when the client is a single IP behind a point-to-point link, and how the phone network’s physical constraints shaped the entire internet access industry for twenty years. That is worth an afternoon of configuration and a trip to eBay to find a USB modem.