I had (have?) a detailed discussion of how to use
pppd (Point-To-Point Protocol Daemon)
in the pipeline. However, Twitter friend Gravis
posted a very nice article on how PPP
the protocol works, and I felt compelled to convert my original detailed post
into a more concise form!
The Point-To-Point Protocol (PPP) is a Layer 2 protocol that is meant to connect computers in a network, like Ethernet or Wifi. However, unlike Ethernet and Wifi, PPP doesn't specify how voltages should be sent from one computer to another. The Fundamentals section in Gravis' article provides great detail as to the rationale. An important consequence of PPP not specifying how voltages are sent is that lots of computing hardware can be used to send and receive PPP packets, such as RS-232 ports.
This article will focus on sending/receiving PPP packets over Universal Asynchronous Receiver Transmitter (UART). Due to their simplicitly, UARTs are extremely common hardware for communications and debugging on Systems-on-a-Chip (SoCs), FPGAs, Single-Board Computers (SBCs), and just many computers in general. Considering their proliferation, a UART becomes a powerful communications interface when combined with PPP and TCP/IP; you now have a means to connect computers without any other I/O interfaces to the rest of the TCP/IP-speaking Internet if you so choose!
One common PPP implementation is the Point-To-Point Protocol Daemon
for Unix systems, or
pppd provides both a daemon (service) and kernel
component to create a network interface that can send TCP/IP packets and PPP
packets to a UART
You can use
pppd to set up two *nix computers to communicate to each
other over their own local TCP/IP network. In fact that's a major characteristic
of a Point To Point network! In such a setup, both computers can contact
each other via applications like
ssh, but are blissfully unaware of any other
computers that exist in the world.
In the absence of any dedicated networking hardware, you can still use
pppd on Unix
systems to connect systems to the Internet. This article will describe how to
pppd to connect to the Internet proper, using my current setup at home as
an example. My setup should be applicable to most use cases, as long as one
of the computers in your PPP links has another network interface besides
UART, such as an Ethernet port or Wifi adapter.
I have two Single-Board Computers (SBCs) deployed at present- an ASUS TinkerBoard running a Debian variant, and a Raspberry Pi 1 Model A+. The TinkerBoard SoC has 5 UARTs, an Ethernet port, and a Wifi adapter/antenna. 4 UARTs are exposed on GPIO headers. I use one of the additional UARTs exclusively for PPP. The Raspberry Pi has only a single UART. This is multiplexed between a serial console and PPP session.
UARTs are often exposed as three or more pin headers on your computer's PCB- receive (RX), transmit (TX), and ground (GND).
If you don't have a TinkerBoard and RPi, the important part of my setup is that the TinkerBoard has multiple pieces of hardware for network interfaces- Wifi, Ethernet, and UART- while my RPi has only one- a UART. By routing UART traffic between two computers via PPP to/from other existing networking hardware, it is possible to connect the UART-only computer to the Internet.
My config file settings below can be duplicated for many other pieces of hardware, as long as the above condition of "at least one of the computers has more than a UART for networking" is met.
Besides the command line,
pppd initializes itself using a configuration file
containing options under the
/etc/ppp directory. Options file names take
the form of
$DEVICE represents the device name of
your serial port. You will need to know the names that your Unix system
assigned your serial ports before setting up
pppd. In my case, I have
UART2 on the TinkerBoard GPIO header specifically for the PPP link.
This is called
ttyS1 by Linux 1
I have posted my
/etc/ppp/options.ttyS1 config file in full below, and I will
briefly explain what each options entails after the config file. If you want
more information, I encourage you to consult the
which details all possible options that
pppd understands on the command line
or in a config file:
noauth local nodetach nocrtscts xonxoff 192.168.1.164:192.168.1.165 persist proxyarp
As explained in Gravis' article, the PPP Protocol contains a number of
authentication modes. Because I physically have access to both SBCs a few
inches away, I don't bother with authentication to bring up the
interfaces using the
noauth option. Readers should feel free to experiment,
but I have no experience with PPP links that require authentication.
pppd from relying on the Carrier Detect (CD) and Data
Terminal Ready (DTR) signals common on RS-232 serial ports. We only connect RX,
TX, and GND between the RPi to the TinkerBoard, and I'm not even sure if the
common on UARTs are even available on the RPi side.
pppd from forking to become a background process. This
is required so
pppd plays nice
Simple service types.
From debugging when I initially set the PPP link up in early 2018, I needed
nocrtscts to disable hardware flow control
on the TinkerBoard side. As noted above, we only connect RX, TX, and GND
between the two SBCs. I found out via logic analyzer traces that without this
pppd on the TinkerBoard will wait forever for the flow control to
indicate that the RPi is ready :).
I don't know or remember why this option isn't required on the RPi side. The man page states that hardware flow control settings are left unchanged if no option to enable or disable it was given. It's possible NetBSD on the RPi doesn't enable hardware flow control on the UART (if the signals even exist), but Debian on the TinkerBoard does?
If you are connecting two Unix computers whose serial ports have hardware flow
control signals, feel free to experiment by removing both
local options and see what happens- once you have a working setup :)!
xonxoff enables software flow control on the serial line. I don't believe
it interacts with hardware flow control options.
I've found this option actually increases network speeds at higher baud
rates. I used to have a table comparing speeds with
xonxoff and without
xonxoff, but I seem to have lost it aside from two old entries without
units (probably kB/s):
# Baud XONXOFF No Flow Ctrl 500000 47107 48450 750000 65203 20411
Also, I don't believe
750000 baud works anymore after a
192.168.1.164:192.168.1.165- Format is
<local_IP_address>:<remote_IP_address>, this option tells
IP addresses the peers should use. Because of the
I have for
pppd on the RPi side, the RPi will accept whatever IP address
is given by the TinkerBoard. As written out, the above option will set the
TinkerBoard's IP address to
192.168.1.164 and the RPi's IP address to
I don't know how netmasks work with
pppd at this time. However, my
experience is that
pppd "does the right thing" so it thinks the
interface and Wifi and Ethernet interfaces are part of the same network
proxyarp and IP forwarding is enabled.
pppd to reconnect if the connection is lost, instead of
terminating. I'm not sure how essential this option is when paired with
an init system (which can also tell a daemon to restart).
proxyarp is probably the most interesting and complex option in this
particular PPP setup. I will try my best to explain concisely and simply.
is Proxy ARP is is required because while PPP doesn't use ARP, other Link-Layer
protocols for encapsulating TCP/IP packets do, including Ethernet and Wifi.
Using either the Ethernet or Wifi network interface, the TinkerBoard can answer
ARP requests on behalf of the RPi. The
ppp interface on either device never
sees the ARP requests, and I'm not sure
pppd knows how to handle them as
part of the PPP protocol bits sent down the wire anyway.
Any traffic that's meant for the RPi from one of the other interfaces needs
to somehow reach the UART interface with the PPP link.
pppd itself only does
Proxy ARP for you, and (as far as I understand) will not check other interfaces
for packets meant for one of the two computers reachable on the UART interface.
Instead, you must ask the OS to forward packets for you from one interface to
another, effectively turning one of your computers with a PPP link into a
Normally, Linux will not forward packets from one interface to another by default. You have two options:
sysctl -w net.ipv4.ip_forward=1at least once after each reboot when you want to connect the UART-only computer to the Internet.
net.ipv4.ip_forward = 1in
/etc/sysctl.conffor IP forwarding to persist between reboots.
Note that IP forwarding is not required to set up a Point-To-Point link.
[Unit] Description=PPP Wants=network-online.target After=network-online.target [Service] Type=idle ExecStart=/usr/sbin/pppd -d /dev/ttyS2 576000 Restart=always TimeoutStartSec=120 [Install] WantedBy=multi-user.target Alias=ppp.service
Since my TinkerBoard is running a Debian variant, it also comes with
So we create a unit file for our
Because my Raspberry Pi is an original Model A+, it doesn't have an Ethernet port. This means that short of using a USB Wifi adapter (which I find flaky), using the UART is one of the only ways to connect to Internet. However, there's only one UART on the Raspberry Pi (I don't know at present how to use the mini UART), and I need that UART occassionally for a serial console for debugging.
As it turns out, there's nothing preventing me from using the same UART for
a serial console and a PPP connection a long as I don't try to use
pppd and a
serial console at the same time over the same UART.
NetBSD comes with
pppd as part of the
base set tarball, so there is no
bootstrapping problem with needing to download
pppd before using
pppd wasn't part of the base distribution, I would use a separate computer to
pppd package for your distro and then transfer it via flash
drive ("sneakernet") or use Kermit
over the UART via a USB-to-serial cable connection.
getty implementation provided with NetBSD is capable of multiplexing
login via a serial console with a PPP session. A Linux
such capability may be coming soon.
# PPP network link login # # these entries can be used by ISPs or others who want to be able # to offer both a "shell" and a PPP login on the same port. Setting # the "pp" attribute allows getty(8) to recognize a PPP link start # negotiation, and invoke the program listed, in addition to normal # login(1). # # N.B.: if PPP is recognized, this bypasses normal login/password # exchange; the expectation is that you'll configure pppd (or whatever) # to require a PAP or CHAP handshake for authentication after PPP is # started up. # # It is also recommended that you use hardware (CTS/RTS) flow control # on the port, and run the port as fast as possible, to allow modems # extra time to do data compression, if enabled. # ppp:np:ce:ck:pp=/usr/sbin/pppd: # ppp.19200|PPP-19200:sp#19200:tc=ppp: ppp.38400|PPP-38400:sp#38400:tc=ppp: ppp.57600|PPP-57600:sp#57600:tc=ppp: ppp.115200|PPP-115200:sp#115200:tc=ppp: ppp.230400|PPP-230400:sp#230400:tc=ppp: ppp.460800|PPP-460800:sp#460800:tc=ppp: ppp.500000|PPP-500000:sp#500000:tc=ppp: ppp.576000|PPP-576000:sp#576000:tc=ppp:
# $NetBSD: ttys,v 1.8 2019/09/25 23:09:21 abs Exp $ # # from: @(#)ttys 5.1 (Berkeley) 4/17/89 # # name getty type status comments # console "/usr/libexec/getty ppp.576000" vt100 on secure constty "/usr/libexec/getty default" vt100 off secure ttyE0 "/usr/libexec/getty Pc" wsvt25 off secure ttyE1 "/usr/libexec/getty Pc" wsvt25 off secure ttyE2 "/usr/libexec/getty Pc" wsvt25 off secure ttyE3 "/usr/libexec/getty Pc" wsvt25 off secure tty00 "/usr/libexec/getty default" unknown off secure tty01 "/usr/libexec/getty default" unknown off secure tty02 "/usr/libexec/getty default" unknown off secure tty03 "/usr/libexec/getty default" unknown off secure tty04 "/usr/libexec/getty default" unknown off secure tty05 "/usr/libexec/getty default" unknown off secure tty06 "/usr/libexec/getty default" unknown off secure tty07 "/usr/libexec/getty default" unknown off secure
Now, we need to set up a config file on the RPi side of things. It is perfectly
fine to use
console (as in
/dev/console) as the serial device name.
NetBSD will assign
/dev/console to the UART exposed on the GPIO header,
which is what we want.
ipcp-accept-local noauth local xonxoff defaultroute
Here areshort explanations on new options that
pppd needs for this scenario
where the RPi spawns
pppd instances when it detects PPP activity from the
pppdto accept an IP address from the other peer and use it as our own. There is an analogous
ipcp-accept-remoteoption that I don't use. I don't remember why I only use
ipcp-accept-local, but it's possible it's not actually required. I will need to re-test at some point!
defaultroutesays "if I (the current peer) can't figure out where a packet should go, send it to the peer at the other end of the PPP link and see if they can figure it out." Since the TinkerBoard is acting as a router, it should be able to route packets to the outside world on its Wifi or Ethernet interface.
# Generated by resolvconf domain $DOMAIN nameserver $NAMESERVER
One of the computers in your PPP links needs to have another network interface besides a UART.
If you want to set up two *nix computers to communicate to each other via
pppd, you're all set using the above steps! However, you probably want
to access the Internet proper from a computer that only has a UART (e.g. no
Ethernet or Wifi) for commmunication with the outside world. In this case,
a bit of extra setup is required.
After you've verified
dd if=/dev/urandom bs=512 count=1024 | dbclient email@example.com 'cat > /dev/null'for speed test.
|Baud Rate||Data Copied||Speed|
|230400||524288 bytes||22.1 kB/s|
|460800||524288 bytes||41.2 kB/s|
|500000||524288 bytes||40.9 kB/s|
|576000||524288 bytes||44.3 kB/s|
|921600||524288 bytes||44.1 kB/s|
|1000000||524288 bytes||29.9 kB/s|
UARTs are not designed for high speeds, emitting voltages over a single-ended interface suspectible to noise. However, the speeds you can get (tens of kBps) are acceptable for a number of background tasks and downloading software.
For better or worse, connecting a computer to the outside world is a prerequisite for making the computer useful over long periods of time. While there is a lot of work to get data from source to destination
The following section briefly discusses problems I have personally run into when getting a PPP link between my TinkerBoard and RPi to work.
Make sure your
options file contains
local on both the TinkerBoard
and RPi. It is possible that without this option,
pppd on the TinkerBoard
will wait for hardware flow control signals that will never arrive.
sshinto the RPi from a remote computer besides the TinkerBoard.
Make sure IP forwarding is enabled on the TinkerBoard.
Make sure your nameserver in
resolv.conf is set up properly. If using
DHCP, this is taken care of for you. However,
pppd setup falls under
"static" IP, and while it handles most static IP configuration steps via its
config file, setting up your nameserver is not one of those steps.
I think this is a NetBSD issue; NetBSD uses the break condition (abnormally
long low voltage) to start the kernel debugger. Any noise on the line during
TinkerBoard and/or NetBSD boot could create that condition, causing the RPi to
stop responding. And my own experience is that
pppd doesn't tolerate the boot
messages sent from the RPi well, seeing those messages as garbage. Therefore I
recommend not starting the
pppd link at boot via
Rather, wait until you need to access the other end of the UART after boot
has completed on both ends of the link, and then run
systemctl start rpippp.service.
1 TinkerBoard UART naming is confusing. The RockChip that powers the TinkerBoard
has 5 UARTs. From reading the manual and comparing pinouts,
is not exposed on the GPIO header and isn't assigned a device node. So
ttyS1, etc. I'll need to look into this again at some point,
because I remember
UART0 at some point in the past getting a