Skip to content

Instantly share code, notes, and snippets.

@afresh1
Last active January 3, 2024 21:52
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save afresh1/791343380b4410687d51fdd94f20bd42 to your computer and use it in GitHub Desktop.
Save afresh1/791343380b4410687d51fdd94f20bd42 to your computer and use it in GitHub Desktop.
CenturyLink PPPoE and 6rd on an OpenBSD router

Replacing the CenturyLink provided ethernet router with OpenBSD

Unfortunately CenturyLink provisions their fiber to the home with a PPPoE authentication over vlan 201, this makes replacing the router more difficult than it should be. I also had to call CenturyLink support to get the password for the PPPoE connection.

cnmac0 is the egress interface on my EdgeRouter Lite.

You also need to add match on pppoe0 scrub (max-mss 1452) to your pf.conf because otherwise many things don't work. (Thanks Bryan) Specifically, 40 less than the mtu on pppoe0.

If you're doing ipsec over this link, you also need to scrub the enc0 max-mss to 64 smaller than the max-mss on the pppoe interface. match on enc0 scrub (max-mss 1428).

Overall it ends up being fairly forward, the PPPoE config is copied directly from the man page with the minor change that CenturyLink uses chap instead of pap.

The IPv6 setup was based on these resources from some DuckDuckGo.com searches

#!/bin/sh
# http://internethelp.centurylink.com/internethelp/modem-q1000-ipv6rd.html
# http://undeadly.org/cgi?action=article&sid=20130828151241
# https://forum.openwrt.org/viewtopic.php?id=37516
# https://www.reddit.com/r/ipv6/comments/15u4hi/6rd_centurylinkqwest/
# https://gist.github.com/afresh1/791343380b4410687d51fdd94f20bd42
# Put this in root's crontab with -s:
# * * * * * -ns /usr/local/sbin/6rd.sh
if=gif0
publicif=pppoe0
internalif=vlan40
guestif=vlan41
search_domain=home.hewus.com
# CenturyLink
rdprefix=2602
rdmask=24
v4dest=205.171.2.64
v4ip=''
while [ -z "$v4ip" -o "$v4ip" = "0.0.0.0" ]; do
v4ip=$( ifconfig $publicif |
sed -ne 's/[[:space:]]*inet[[:space:]]\([^[:space:]]*\).*/\1/p' )
sleep 1
done
if [ "$v4ip" = "0.0.0.0" ]; then
echo "No IP on $publicif" >&2
exit 1
fi
v6ip=$( ifconfig $if |
sed -ne "s/[[:space:]]*inet6[[:space:]]\($rdprefix[^[:space:]]*\).*prefixlen[[:space:]]*\([[:digit:]]*\)/\1\/\2/p" )
v6format="$rdprefix:%x:%x%02x:%x"
v6prefix=$( printf ${v6format} $( echo $v4ip | tr '.' ' ' ) )
v6dest=$( printf ${v6format}00::1 $( echo $v4dest | tr '.' ' ' ) )
# The prefix I get to use is the rdmask + the 32 bits of the v4 address
# Some OS's only seem to accept a /64 (linux, macos)
v6external=${v6prefix}00::1/$rdmask
v6internal=${v6prefix}40::1/$(( rdmask + 32 + 8 ))
v6guest=${v6prefix}41::1/$(( rdmask + 32 + 8 ))
# We're already configured, don't try again
[ "$v6ip" = "$v6external" ] && exit
# somehow we can use the same mtu on the gif as on the publicif
mtu="$( ifconfig "$publicif" | sed -ne 's/.* mtu \([0-9]*\)$/\1/p' )"
# add "include: /var/unbound/etc/6rd.conf" to the end of unbound.conf
cat <<EOL >/var/unbound/etc/6rd.conf
server:
interface: ${v6internal%/*}
access-control: $v6internal allow
interface: ${v6guest%/*}
access-control: $v6guest allow
# I have this already configured, you might have to uncomment
# local-zone: "$search_domain." static
local-data: "$( hostname ). IN AAAA ${v6external%/*}"
local-data: "ns1.$search_domain. IN AAAA ${v6internal%/*}"
local-data: "ns2.$search_domain. IN AAAA ${v6guest%/*}"
EOL
cat <<EOL >/etc/rad.conf
mtu $mtu
dns { search $search_domain }
interface $internalif { dns { nameserver ${v6internal%/*} } }
interface $guestif { dns { nameserver ${v6guest%/*} } }
EOL
ifconfig $if mtu $mtu
ifconfig $if tunnel $v4ip $v4dest
# reset any old v6 addresses
/sbin/route -qn delete -inet6 default
ifconfig $if -inet6
ifconfig $internalif -inet6
ifconfig $guestif -inet6
set -x # because I want to know the new IPs
# and add them back in
ifconfig $guestif inet6 $v6guest
ifconfig $internalif inet6 $v6internal
ifconfig $if inet6 $v6external
/sbin/route -qn add -inet6 default $v6dest
rcctl reload rad
rcctl restart unbound # reload didn't seem to listen on new interfaces
# mtu is 8 less than vlan201
# or the seeming max of 1492 that CenturyLink allows
inet 0.0.0.0 255.255.255.255 NONE mtu 1492 \
pppoedev vlan201 authproto chap \
authname 'myaddress@qwest.net' authkey 'mypassword' up
dest 0.0.0.1
!/sbin/route add default -ifp \$if 0.0.0.1
!/usr/local/sbin/6rd.sh
# the parent interface needs to be able to hold this, but commonly can
# you may need to set mtu 1518 or something on cnmac0
mtu 1500
vnetid 201 parent cnmac0
up
@dgwynne
Copy link

dgwynne commented Jul 13, 2017

hostname.vlan201 should read:

vnetid 201 parent cnmac0
up

EDIT (Andrew): I fixed it, was using the olde timey vlandev cnmac0

@garnoth
Copy link

garnoth commented Feb 12, 2021

I'm looking to adapt this to my setup. What's with publicif=vlan41 ? shouldn't the public side be on vlan201 which is connected to cnmac0 in your situation? Or is this something specific to your setup?

@afresh1
Copy link
Author

afresh1 commented Feb 12, 2021

@garnoth: I should improve that. By "public" I mean "guest network for the public to use" as opposed to the "internal network for me to use". It's specifically the open SSID on my wifi network that visitors use.

@dramaley
Copy link

dramaley commented Mar 3, 2023

Thank you for these notes! I just got CenturyLink fiber internet this week, and was able to get my OpenBSD router connected directly to the transceiver without needing CL's router at all. That's exactly the configuration i want, and it's nice and fast.

@dgwynne
Copy link

dgwynne commented Mar 3, 2023

I've used tpmr(4) to snoop the traffic between the ISP router and their service to figure out what's going on before, like what VLANs are in use, pppoe or not, or pppoe creds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment