Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/coverity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ env:
LIBYANG_VERSION: 4.2.2
SYSREPO_VERSION: 4.2.10
SYSKLOGD_VERSION: 2.7.2
WATCHDOGD_VERSION: '4.0'

jobs:
coverity:
Expand Down Expand Up @@ -58,7 +59,7 @@ jobs:
sudo apt-get -y update
sudo apt-get -y install pkg-config libjansson-dev libev-dev \
libcrypt-dev libglib2.0-dev libpcre2-dev \
libuev-dev libavahi-client-dev
libuev-dev libavahi-client-dev libconfuse-dev

- name: Build dependencies
run: |
Expand All @@ -82,6 +83,10 @@ jobs:
git clone -b v${SYSKLOGD_VERSION} --depth 1 https://github.com/troglobit/sysklogd.git
(cd sysklogd && ./autogen.sh && ./configure --without-logger --without-systemd \
&& make && sudo make install)

git clone -b ${WATCHDOGD_VERSION} --depth 1 https://github.com/troglobit/watchdogd.git
(cd watchdogd && ./autogen.sh && ./configure --without-systemd \
&& make && sudo make install)
make dep

- name: Build applications for Coverity
Expand Down
2 changes: 2 additions & 0 deletions board/aarch64/linux_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ CONFIG_MDIO_BITBANG=y
CONFIG_MDIO_MVUSB=m
CONFIG_MDIO_MSCC_MIIM=y
CONFIG_MDIO_BUS_MUX_MMIOREG=y
CONFIG_PPP=m
CONFIG_PPPOE=m
CONFIG_USB_RTL8150=m
CONFIG_USB_RTL8152=m
CONFIG_USB_LAN78XX=m
Expand Down
2 changes: 2 additions & 0 deletions board/arm/linux_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ CONFIG_VETH=m
CONFIG_VIRTIO_NET=y
CONFIG_NLMON=y
CONFIG_NET_VRF=y
CONFIG_PPP=m
CONFIG_PPPOE=m
CONFIG_USB_USBNET=y
CONFIG_INPUT_EVDEV=y
# CONFIG_LEGACY_PTYS is not set
Expand Down
93 changes: 66 additions & 27 deletions board/common/rootfs/usr/libexec/odhcp6c.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ interface="$1"
state="$2"
RESOLV_CONF="/run/resolvconf/interfaces/${interface}-ipv6.conf"
NTPFILE="/run/chrony/dhcp-sources.d/${interface}-ipv6.sources"
NAME="/etc/net.d/${interface}-dhcpv6.conf"
NEXT="/run/odhcp6c-${interface}.conf" # outside of netd's watched dir

[ -n "$metric" ] || metric=5

Expand All @@ -28,10 +30,64 @@ err()

teardown_interface()
{
ip -6 route flush dev "$interface"
ip -6 address flush dev "$interface" scope global
}

# Routes go to netd, like DHCPv4 routes, so they are static routes with
# the configured route preference ($metric) as distance. The kernel
# metric from the RA is not used.
#
# add_route PREFIX NEXTHOP [INTERFACE] [SOURCE]
add_route()
{
{
echo "route {"
echo " prefix = \"$1\""
echo " nexthop = \"$2\""
[ -n "$3" ] && echo " interface = \"$3\""
[ -n "$4" ] && echo " source = \"$4\""
echo " distance = $metric"
echo " tag = 100"
echo "}"
} >> "$NEXT"
}

set_routes()
{
echo "# Generated by odhcp6c" > "$NEXT"

# $RA_ROUTES format: "prefix/len,gateway,valid,metric ..."
for entry in $RA_ROUTES; do
addr="${entry%%,*}"
entry="${entry#*,}"
gw="${entry%%,*}"

if [ -z "$gw" ]; then
add_route "$addr" "$interface"
continue
fi

add_route "$addr" "$gw" "$interface"

# Same route for traffic sourced from each delegated prefix
for prefix in $PREFIXES; do
add_route "$addr" "$gw" "$interface" "${prefix%%,*}"
done
done

# Unreachable route for delegated prefixes, prevents routing loops
for entry in $PREFIXES; do
add_route "${entry%%,*}" reject
done

# Only touch netd's config when the routes changed
if cmp -s "$NAME" "$NEXT"; then
rm -f "$NEXT"
else
mv "$NEXT" "$NAME"
fi
}

setup_interface()
{
# Merge RA addresses with DHCP addresses
Expand All @@ -57,28 +113,7 @@ setup_interface()
log "assigned address $addr (preferred=$preferred, valid=$valid)"
done

# Add routes from RA
for entry in $RA_ROUTES; do
addr="${entry%%,*}"
entry="${entry#*,}"
gw="${entry%%,*}"
entry="${entry#*,}"
valid="${entry%%,*}"
entry="${entry#*,}"
metric="${entry%%,*}"

if [ -n "$gw" ]; then
ip -6 route add "$addr" via "$gw" metric "$metric" dev "$interface" from "::/128"
else
ip -6 route add "$addr" metric "$metric" dev "$interface"
fi

# Add routes for delegated prefixes
for prefix in $PREFIXES; do
paddr="${prefix%%,*}"
[ -n "$gw" ] && ip -6 route add "$addr" via "$gw" metric "$metric" dev "$interface" from "$paddr"
done
done
set_routes
}

handle_prefixes()
Expand All @@ -93,9 +128,6 @@ handle_prefixes()

log "received delegated prefix $addr (preferred=$preferred, valid=$valid)"

# Add unreachable route to prevent routing loops
ip -6 route add unreachable "$addr" 2>/dev/null

# Future: Distribute to downstream interfaces
done
}
Expand Down Expand Up @@ -199,7 +231,13 @@ log "state: $state"
flock 9
case "$state" in
started)
# Initial state - clean up any stale config
# Initial state - clean up any stale config. Our
# routes go through netd with the configured route
# preference, so the kernel must not add its own
# default route from router advertisements as well.
rm -f "$NAME"
sysctl -w "net.ipv6.conf.$interface.accept_ra_defrtr=0" >/dev/null
ip -6 route flush dev "$interface" proto ra
teardown_interface
;;

Expand All @@ -222,6 +260,7 @@ log "state: $state"

unbound|stopped)
# Lost server or client stopped
rm -f "$NAME"
teardown_interface
rm -f "$RESOLV_CONF"
rm -f "$NTPFILE"
Expand Down
2 changes: 2 additions & 0 deletions board/riscv64/linux_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ CONFIG_DWMAC_DWC_QOS_ETH=y
CONFIG_DWMAC_STARFIVE=y
CONFIG_MICROCHIP_PHY=y
CONFIG_MOTORCOMM_PHY=y
CONFIG_PPP=m
CONFIG_PPPOE=m
CONFIG_USB_RTL8150=m
CONFIG_USB_RTL8152=m
CONFIG_USB_LAN78XX=m
Expand Down
2 changes: 2 additions & 0 deletions board/x86_64/linux_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,8 @@ CONFIG_E1000=y
CONFIG_NE2K_PCI=y
CONFIG_8139CP=y
CONFIG_ROCKER=y
CONFIG_PPP=m
CONFIG_PPPOE=m
# CONFIG_WLAN is not set
CONFIG_INPUT_EVDEV=y
CONFIG_SERIAL_8250=y
Expand Down
1 change: 1 addition & 0 deletions configs/aarch64_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ BR2_PACKAGE_NMAP_NPING=y
BR2_PACKAGE_ODHCP6C=y
BR2_PACKAGE_OPENRESOLV=y
BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_PPPD=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_TRACEROUTE=y
Expand Down
1 change: 1 addition & 0 deletions configs/aarch64_minimal_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y
BR2_PACKAGE_ODHCP6C=y
BR2_PACKAGE_OPENRESOLV=y
BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_PPPD=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_WHOIS=y
Expand Down
1 change: 1 addition & 0 deletions configs/arm_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ BR2_PACKAGE_NMAP_NPING=y
BR2_PACKAGE_ODHCP6C=y
BR2_PACKAGE_OPENRESOLV=y
BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_PPPD=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_TRACEROUTE=y
Expand Down
1 change: 1 addition & 0 deletions configs/arm_minimal_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y
BR2_PACKAGE_ODHCP6C=y
BR2_PACKAGE_OPENRESOLV=y
BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_PPPD=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_WHOIS=y
Expand Down
1 change: 1 addition & 0 deletions configs/riscv64_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ BR2_PACKAGE_NMAP_NPING=y
BR2_PACKAGE_ODHCP6C=y
BR2_PACKAGE_OPENRESOLV=y
BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_PPPD=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_TRACEROUTE=y
Expand Down
1 change: 1 addition & 0 deletions configs/x86_64_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ BR2_PACKAGE_NMAP_NPING=y
BR2_PACKAGE_ODHCP6C=y
BR2_PACKAGE_OPENRESOLV=y
BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_PPPD=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_TRACEROUTE=y
Expand Down
1 change: 1 addition & 0 deletions configs/x86_64_minimal_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y
BR2_PACKAGE_ODHCP6C=y
BR2_PACKAGE_OPENRESOLV=y
BR2_PACKAGE_OPENSSH=y
BR2_PACKAGE_PPPD=y
BR2_PACKAGE_SOCAT=y
BR2_PACKAGE_TCPDUMP=y
BR2_PACKAGE_WHOIS=y
Expand Down
13 changes: 13 additions & 0 deletions doc/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ All notable changes to the project are documented in this file.
- Add IPv6 dynamic routing: RIPng and OSPFv3. Both reuse the existing
ietf-rip and ietf-ospf models, selected per control-plane-protocol by the
`ripng`/`ospfv3` type and the IPv6 address-family
- Add PPPoE client, issue #1569. A PPPoE session is an interface of type
`pppoe` on top of the interface facing the provider, with the password in
the keystore. Its default route and DNS servers are used like those from
a DHCP server, and the TCP MSS of forwarded connections is clamped to the
session MTU, see [PPPoE Client][pppoe]

### Fixes

- Fix #1423: the default route from a DHCPv6 client, learned from router
advertisements, is now a static route with the DHCPv6 route preference,
like a DHCPv4 route. Before, the route preference setting was ignored

[pppoe]: https://www.kernelkit.org/infix/latest/pppoe/

[v26.09.0][] - 2026-09-30
-------------------------
Expand Down
1 change: 1 addition & 0 deletions doc/iface.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Available types can be listed from the CLI:
lag IEEE link aggregate interface.
loopback Linux loopback interface.
other Other interface, i.e., unknown.
pppoe PPP over Ethernet (PPPoE) client session.
veth Linux virtual Ethernet pair.
vlan Layer 2 Virtual LAN using 802.1Q.
vxlan Virtual eXtensible LAN tunnel interface.
Expand Down
2 changes: 1 addition & 1 deletion doc/keystore.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ managed via CLI, NETCONF, or RESTCONF.

| **Symmetric Key Format** | **Use Case** |
|-----------------------------|-----------------------------------|
| `passphrase-key-format` | Human-readable passphrases (WiFi) |
| `passphrase-key-format` | Human-readable passphrases (WiFi, PPPoE) |
| `octet-string-key-format` | Raw symmetric keys (WireGuard) |

## Asymmetric Keys
Expand Down
1 change: 1 addition & 0 deletions doc/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ other traffic would be bridged as usual.
| [eth](ethernet.md#physical-ethernet-interfaces) | ieee802-ethernet-interface | Physical Ethernet device/port |
| | infix-ethernet-interface | |
| [veth](ethernet.md#veth-pairs) | infix-if-veth | Virtual Ethernet pair, typically one end is in a container |
| [pppoe](pppoe.md) | infix-if-ppp | PPPoE client session on an Ethernet or VLAN interface |
| [*common*](iface.md) | ietf-interfaces, | Properties common to all interface types |
| | infix-interfaces | |

Expand Down
95 changes: 95 additions & 0 deletions doc/pppoe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# PPPoE Client

Many Internet service providers connect their customers with PPP over
Ethernet (PPPoE, RFC 2516). The device then logs in with a user name
and password, and gets its IPv4 address and DNS servers from the
provider's server.

A PPPoE client session is an interface of type `pppoe`, stacked on top
of the Ethernet interface, or VLAN, that connects to the provider. The
interface exists as soon as it is configured, but is down until the
device has logged in, and goes down again when the session ends. The
client keeps trying to log in until it succeeds, and logs in again when
a session is lost.

## Configuration

The password is stored in the [keystore](keystore.md), as a symmetric
key with `passphrase-key-format`:

<pre class="cli"><code>admin@example:/> <b>configure</b>
admin@example:/config/> <b>edit keystore symmetric-key isp</b>
admin@example:/config/keystore/…/isp/> <b>set key-format passphrase-key-format</b>
admin@example:/config/keystore/…/isp/> <b>edit cleartext-symmetric-key</b>
Passphrase: ********
Retype passphrase: ********
admin@example:/config/keystore/…/isp/> <b>end</b>
</code></pre>

Then create the PPPoE interface on top of the interface facing the
provider, here `eth0`. The settings common to all PPP links, the user
name and password, are in `ppp`, and the PPPoE specific ones in `pppoe`:

<pre class="cli"><code>admin@example:/config/> <b>edit interface pppoe0</b>
admin@example:/config/interface/pppoe0/> <b>set pppoe lower-layer-if eth0</b>
admin@example:/config/interface/pppoe0/> <b>set ppp username user@isp.example</b>
admin@example:/config/interface/pppoe0/> <b>set ppp secret isp</b>
admin@example:/config/interface/pppoe0/> <b>show</b>
type pppoe;
ppp {
username user@isp.example;
secret isp;
}
pppoe {
lower-layer-if eth0;
}
admin@example:/config/interface/pppoe0/> <b>leave</b>
</code></pre>

> [!TIP]
> If you name your PPPoE interface `pppoeN`, where `N` is a number, the
> CLI infers the interface type automatically. Any other name, e.g.,
> `wan`, works too, with an explicit `set type pppoe`.

The password may not contain control characters, `"`, or `\`.

Some providers run several services, or several servers, on the same
network. Set `service-name` or `ac-name` to only connect to a given
service, or a given access concentrator.

## Default Route and DNS

By default the session installs a default route, with route preference
5, the same as a route learned from a DHCP server. Change it with `ppp
route-preference`, or turn the route off with `ppp default-route false`,
e.g., when the PPPoE session is a backup for another uplink.

The DNS servers from the provider are used by default. Set `ppp
peer-dns false` to use only the DNS servers configured on the device.

## TCP MSS Clamping

PPPoE takes 8 bytes of every Ethernet frame, so the session MTU is 1492
bytes, lower than the 1500 bytes of the hosts on the local network.
Hosts rely on path MTU discovery to adjust, which fails where ICMP is
blocked on the way, and their TCP connections then stall on large
packets.

To avoid that, the device clamps the maximum segment size (MSS) in the
handshake of every TCP connection it forwards through the session, to
fit the session MTU. This does not depend on the firewall being
enabled. Set `pppoe mss-clamping false` to turn it off.

## IPv6

When IPv6 is enabled on the PPP interface the session also negotiates
IPv6, which gives the interface a link-local address. Global addresses
and delegated prefixes come from the provider's router advertisements
and DHCPv6 server, enable the [DHCPv6 client](ip.md) on the PPP
interface to use them.

## Forwarding

To route traffic between the local network and the provider, enable
IPv4 (and IPv6) forwarding on the PPPoE interface and on the local
interfaces, see [IP Addressing](ip.md).
Loading
Loading