Using PPPoE on Linux

When it comes to internet connectivity in Switzerland, you might stumble across ADSL or VDSL. Most of the time you’ll end up with a router provided by the provider, which is already pre-configured with the connection information. This setup might work for the majority of users, but if you’re a pro-user or enterprise with special needs, you want more.

In our case, we were looking for a much more “dynamic” way to configure the internet access, and of course we wanted to route and filter the traffic on a Linux machine. So we ended up with a dial-up account (not that shitty 56k dial-up connection) and a PPPoE modem.

What is PPPoE?

PPPoE is the abbreviation for Point-to-Point protocol over Ethernet. Basically it’s a network protocol to encapsulate PPP frames inside Ethernet frames. There’s a really nice Wikipedia Article for PPPoE:

The Point-to-Point Protocol over Ethernet (PPPoE) is a network protocol for encapsulating PPP frames inside Ethernet frames. It appeared shortly after the year 2000, in the context of the boom of the DSL as the solution for tunneling packets over the DSL connection to the ISP’s IP network, and from there to the rest of the Internet. A 2005 networking book noted that “Most DSL providers use PPPoE, which provides authentication, encryption, and compression.” Typical use of PPPoE involves leveraging the PPP facilities for authenticating the user with a username and password, predominately via the PAP protocol and less often via CHAP.

Install & configure pppd for a PPPoE connection

To use PPPoE connections on Linux, we need some additional software. On Debian-based systems, we’re looking for pppd and pppoeconf:

apt-get update
apt-get install pppd pppoeconf

As we now have installed the required software, we need to configure it. The configuration of a provider (aka peer) is stored in the /etc/ppp/peers directory, and there’s already a default provider configuration file. Just have a look at it:

cat /etc/ppp/peers/provider

As we know exactly what kind of configuration parameters are required, we can create a new configuration file:

cat <<EOF >/etc/ppp/peers/example
# Use Roaring Penguin's PPPoE implementation.
plugin rp-pppoe.so eth0

# Login settigns.
user "foobar@example.com"
noauth
hide-password

# Connection settings.
persist
maxfail 0
holdoff 5

# LCP settings.
lcp-echo-interval 10
lcp-echo-failure 3

# PPPoE compliant settings.
noaccomp
default-asyncmap
mtu 1492

# IP settings.
noipdefault
defaultroute
EOF

With this configuration, we make sure that the PPPoE connection is always up (aka persist ), and that it is reopened automatically (aka maxfail & holdoff) in case it unexpectedly disconnects.The user  has to match the username you’ll get from your provider. Since we get our IP address dynamically (DHCP), we set the parameter noipdefault . Of course this is our main interface for the internet connection, so we want to have a defaultroute  in place.

Of course you still need a password for your PPPoE connection, so add one more line to the /etc/ppp/chap-secrets  file:

cat <<EOF >/etc/ppp/chap-secrets
foobar@example.com * my-secret-password *
EOF

Open the PPPoE connection

Now that you’ve everything configured, you should be able to open the PPPoE connection by calling:

pon example

Wait for a few seconds and then you should see a new interface:

# Show all interfaces
ip link show

# Show only PPPoE interface
ip link show ppp0

To stop the connection, you can execute:

poff -a

Hint: The command pon  can also be called without the peer argument, but then it looks for the default provider in /etc/ppp/peers/provider . If you want your connection to be the default provider, you can symlink your peer configuration to it:

cd /etc/ppp/peers
rm -f provider
ln -s example provider

Make the PPPoE connection coming up automatically

If you want the PPPoE connection come up automatically, you’ve several options. The good old fashioned way is an interfaces entry, at least on Debian-based systems.

# On Debian >= 8 via separate interfaces file in interfaces.d:
cat <<EOF >/etc/network/interfaces.d/example
auto example
iface example inet ppp
    pre-up /sbin/ip link set dev eth0 up
    provider example
EOF

# On Debian <8 via common interfaces file:
cat <<EOF >>/etc/network/interfaces
auto example
iface example inet ppp
    pre-up /sbin/ip link set dev eth0 up
    provider example
EOF

This works fine when you’re rebooting your machine. But if you’ve a system with systemd enabled, you might want to start / stop your connection via systemd  / systemctl , instead of the interfaces file:

cat <<EOF >/etc/systemd/system/pppoe.service                              
[Unit]
Description=PPPoE connection

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/bin/pon example
ExecStop=/usr/bin/poff -a

[Install]
WantedBy=default.target
EOF

systemctl daemon-reload

Now you’re able to manage the connection via systemctl :

# Open connection
systemctl start pppoe

# Close connection
systemctl stop pppoe

# See connection / pppd logs
systemctl logs pppoe

Routing & Firewalling

If you want to use your Linux machine as a router and firewall, you’ve to enable IP(v4) forwarding and add some iptables rules.

# Make IP forwarding persistent
cat <<EOF >/etc/sysctl.d/ip-forward.conf
net.ipv4.ip_forward=1
EOF

# Reload sysctl to enable IP forwarding
sysctl -p

# Add iptables rules for masquerading and MSS clamping
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
iptables -I FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

IP(v4) forwarding enables Linux to forward packages from one interface (network) to another.

With iptables  we need to setup IP masquerading, which is done via NAT table. IP masquerading helps us do “hide” the client on the remote host, by rewriting the source address of the package after routing. The remote system will only see the IP address of our Linux router, and not the IP address of the client behind. While this is required for the PPPoE connection, it can also be a security enhancement in routing of private networks.

The second iptables  rule helps us to solve a PPP “issue”. Ethernet packages have a fixed size and they will define the amount of payload the can carry via MTU (maximum transmission unit). By default, Ethernet packages have a MTU size of 1500 bytes, which is fine. However, PPPoE packages have to define an additional header of 6 bytes, and a PPP ID of 2 bytes. This means in PPPoE packages, we “lose” 8 bytes and so the MTU size is reduced to 1492. This isn’t a problem in the first place, but it will become one when a client (e.g. browser / surfing the internet) connects to a server (e.g. webserver). They negotiate a MTU size of 1500 bytes, and the server starts sending data with 1500 bytes chunks, while the PPPoE package can only hold 1492 bytes. There’s a technique called MSS clamping which will fix this issue. Cisco has a really nice white paper which describes exactly that.

IMPORTANT: While the IP(v4) forwarding is persistent, the iptables rules aren’t. So you might want to do a iptables save  or simply create a file with the iptables rules in /etc/ppp/ip-up.d/ , which will automatically be loaded as soon as the PPPoE connection is up.

13 Comments

  • vas

    hi,
    here you are authenticating ppoe client with ISP providers authentication server,

    What if I want to authenticate pppoe users locally.??

    So, my set uup would be:
    1. Ubuntu machine with 2 nic cards
    2. one nic has public static IP configuration
    3. Other nic is supposed to have one switch which will contain multiple ppoe client machines and they should only access the internet once they are authenticated

  • jonny

    Thanks a lot .
    I had setup rasberry pi as hotspot using pppoe. But only google or some random sites were opening. I suspected it to be gateway or dns issue. I had been searching for hours to fix it. I had no idea about this mtu thing. Really a life saver for me.
    Again Thanks a ton for your blog.

    • Dominique Barton

      A bit late, but you’re welcome 😉

      • xtc

        …late or not, (well, super-late by feb 2020 my guess, haha!), but in any case –
        thank you good sir for those crystal-clear instructions.

  • John ffitch

    my firewall as 2 ethernats; one is used for the LAn and the oter has pppoe running over it. I am replacing the hardware and updating fron a broken jessie to stretch but i am unsure how to configure pppoe to run on the second ethernet. You did not seem to cover than

    • Dominique Barton

      That should also work with the config above. In the provider file, simply replace `eth0` with the name of the interface, where the PPPoE modem is connected. Also replace the `pre-up /sbin/ip link set dev eth0 up` command in the `/etc/network/interfaces.d/` file.

  • Mike.P

    Interesting stuff, im a bit of a rookie and just read these sorts of articles and hope that the more i read, the more i absorb and i just start to understand linux a bit more everyday, thank you, Mike.

  • Why

    There are a lot of lines of code, where I need to click to just turn it on without all that over complicated stuff?

  • Simon

    I have only 1 nic..which i use to support ISP installation. sometimes we need to do direct test Pppoe but once I configure it to pppoe setup…I am unable to go back as dynamic ip to configure modem or router
    please advise

    Thanks

  • House Leveling

    I am sure glad that I learn something everyday. I am glad to have come across this article. Thank you for the great read.