<feed xmlns='http://www.w3.org/2005/Atom'>
<title>accel-ppp.git/drivers/ipoe, branch fix-ipoe-netns-immutable</title>
<subtitle>High performance PPTP/L2TP/SSTP/PPPoE/IPoE server for Linux (mirror of https://github.com/accel-ppp/accel-ppp.git)
</subtitle>
<id>https://git.amelek.net/accel-ppp/accel-ppp.git/atom?h=fix-ipoe-netns-immutable</id>
<link rel='self' href='https://git.amelek.net/accel-ppp/accel-ppp.git/atom?h=fix-ipoe-netns-immutable'/>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/'/>
<updated>2026-11-02T22:36:59+00:00</updated>
<entry>
<title>ipoe: use rtnl_newlink_params signature only from 6.15</title>
<updated>2026-11-02T22:36:59+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-11-02T22:36:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=b139b3b85f130b3f07eae278f3b34dee2a464a1b'/>
<id>urn:sha1:b139b3b85f130b3f07eae278f3b34dee2a464a1b</id>
<content type='text'>
struct rtnl_newlink_params was introduced in 6.15, but the newlink
callback switched to it at 6.13, so the module failed to build on
6.13 and 6.14 kernels with an incompatible pointer type for .newlink.

Signed-off-by: Denys Fedoryshchenko &lt;denys.f@collabora.com&gt;
</content>
</entry>
<entry>
<title>ipoe: probe netdevice.h for netns_local/netns_immutable</title>
<updated>2026-11-02T22:26:17+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-11-02T22:26:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=323a98362b1488f68de8878ad3fd3e58eab398fb'/>
<id>urn:sha1:323a98362b1488f68de8878ad3fd3e58eab398fb</id>
<content type='text'>
Stable kernels backport the netns_local -&gt; netns_immutable rename
(6.12.114+, e.g. Debian 13's 6.12.115), so LINUX_VERSION_CODE can no
longer tell which member the headers provide and the module fails to
build with "'struct net_device' has no member named 'netns_local'".

Grep the kernel's include/linux/netdevice.h from the Kbuild Makefile
and pass -DHAVE_NETNS_IMMUTABLE or -DHAVE_NETNS_LOCAL accordingly. The
version checks stay only as a fallback if the probe finds neither.

Signed-off-by: Denys Fedoryshchenko &lt;denys.f@collabora.com&gt;
</content>
</entry>
<entry>
<title>Merge pull request #358 from nuclearcat/fix/kernel-module-install</title>
<updated>2026-10-05T23:17:37+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-10-05T23:17:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=d33028de8b8b520dfde12d9d9d01f4cd576f23b3'/>
<id>urn:sha1:d33028de8b8b520dfde12d9d9d01f4cd576f23b3</id>
<content type='text'>
cmake: fix kernel module installation</content>
</entry>
<entry>
<title>cmake: fix kernel module installation</title>
<updated>2026-08-30T00:10:39+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-30T00:10:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=dbc9d0fb6863026b16f4805709acc7e4977d7e66'/>
<id>urn:sha1:dbc9d0fb6863026b16f4805709acc7e4977d7e66</id>
<content type='text'>
Install each module from its real build directory, propagate staged install roots, and fail when kbuild modules_install fails. Add a rootless CTest and CI regression for successful and failed installs.

Best practices followed:

Use the standard CMake DESTDIR and kbuild INSTALL_MOD_PATH staging interfaces:
https://cmake.org/cmake/help/latest/envvar/DESTDIR.html
https://docs.kernel.org/kbuild/modules.html#install-mod-path

Check child command status with execute_process RESULT_VARIABLE:
https://cmake.org/cmake/help/latest/command/execute_process.html

Register repeatable tests with CTest add_test:
https://cmake.org/cmake/help/latest/command/add_test.html
</content>
</entry>
<entry>
<title>ipoe: include net/rtnetlink.h, insert modules in ci even after a failure</title>
<updated>2026-08-08T16:12:49+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-08T16:12:49+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=9015abafb60f950a6b1a800f84cbf569b07828a1'/>
<id>urn:sha1:9015abafb60f950a6b1a800f84cbf569b07828a1</id>
<content type='text'>
rtnl_link_register() and struct rtnl_link_ops were reaching the driver
through some other header rather than through net/rtnetlink.h, which is
the kind of thing that only shows up when building against a different
kernel. Include it directly.

The workflows insert the kernel modules between two pytest runs, and the
runs that follow are marked 'if: always()' while the insmod steps are
not. A failure in an earlier, unrelated test therefore skips the insmod
but still runs the tests that need the module, which then report a
missing driver instead of the original problem. Mark the insmod steps
'if: always()' as well, so that the later runs test what they are
supposed to.
</content>
</entry>
<entry>
<title>ipoe: allow session interfaces to be removed with ip link del</title>
<updated>2026-08-08T15:12:56+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-08T15:12:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=4969b65c3f55c739a0c9178086f42ba3de906119'/>
<id>urn:sha1:4969b65c3f55c739a0c9178086f42ba3de906119</id>
<content type='text'>
Sessions left in the kernel by a dead daemon could only be got rid of by
restarting accel-pppd, which flushes all of them and so drops everyone
still online, or by unloading the module. Neither is of much use when a
single stale interface is holding an address and every subscriber that
is later handed it is refused with EEXIST.

Register rtnl_link_ops, so that RTM_DELLINK reaches the driver:

	ip link show type ipoe		enumerate them
	ip -d link show ipoe42		reports kind "ipoe"
	ip link del ipoe42		remove a single one

newlink returns EOPNOTSUPP rather than being left out. A session carries
private state that IPOE_CMD_CREATE sets up, and a kernel that falls back
to register_netdevice() when newlink is NULL would hand out a device
with zeroed private state and no percpu counters, which oopses on the
first packet or on ip -s link show.

dellink runs under rtnl and outside genl_mutex, and is therefore the
first writer in this driver that does not serialise against the genl
handlers: it can unlink a session that IPOE_CMD_DELETE is about to
unlink as well, since the device stays registered until both are done.
Add ipoe_session.dying, set under ipoe_wlock by whichever path starts
the teardown, and let the other one bail out.

That leaves rtnl nested inside ipoe_wlock, which is safe because no path
does it the other way round: ipoe_create() releases rtnl before taking
ipoe_wlock, ipoe_nl_cmd_delete() releases ipoe_wlock before calling
unregister_netdev(), and the interface commands use rtnl alone. dellink
does have to sleep in synchronize_rcu() and while draining the session
refcount with rtnl held, which the genl path avoids by deferring both
until after it has dropped everything.

A session left behind by a dead daemon is indistinguishable from one
that is still in use - same peer address, same lists, same flags - so
the removal cannot be refused on state alone. Warn instead, and only
when somebody is still subscribed to the packet multicast group, so that
clearing stale interfaces on a box where accel-pppd is not running stays
quiet.
</content>
</entry>
<entry>
<title>ipoe: flush sessions left by a previous instance with a single command</title>
<updated>2026-08-08T14:47:49+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-08T14:47:49+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=6693d18ae0232da2c6febc0decf099e4fd064381'/>
<id>urn:sha1:6693d18ae0232da2c6febc0decf099e4fd064381</id>
<content type='text'>
On startup accel-pppd is expected to drop everything a previous instance
left behind in the kernel. For sessions it did so by dumping them with
IPOE_CMD_GET and sending one IPOE_CMD_DELETE per ifindex. That dump was
written for the session backup code removed in 1972a7e5c and was never
adapted to its new, destructive role:

 - it was issued on the socket subscribed to the packet multicast group,
   so it competed with the notifications that the still attached stale
   rx handlers keep generating; on a loaded box the receive buffer
   overruns and rtnl_dump_filter() gives up with ENOBUFS,
 - its return value was discarded, so such a failure was silent,
 - it ran before the interfaces were detached, which is what produces
   those notifications in the first place,
 - it was skipped entirely when the multicast group could not be
   resolved, again with nothing but a warning about packet handling,
 - and every session cost a socket, a round trip, a grace period and a
   full unregister_netdev().

Whatever it missed stays in the kernel forever: the daemon has no record
of those sessions, so nothing ever deletes them, and every subscriber
later assigned one of their addresses is refused with EEXIST by
IPOE_CMD_MODIFY.

Add IPOE_CMD_FLUSH, which unlinks all sessions in one go, waits for a
single grace period and unregisters the devices with
unregister_netdevice_many(), and use it instead. The old path is kept as
a fallback for a module predating the command, which is reported as
EOPNOTSUPP, and now checks its return value and uses a private socket.

Reorder init() so the interfaces are detached before the multicast group
is joined, and so the flush also runs when only the group lookup failed.
</content>
</entry>
<entry>
<title>ipoe: do not skip a session when a dump spans several messages</title>
<updated>2026-08-08T14:46:44+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-08T14:46:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=4fb21bfd967ee834d2b5d5ea11d0838002d6ac1a'/>
<id>urn:sha1:4fb21bfd967ee834d2b5d5ea11d0838002d6ac1a</id>
<content type='text'>
ipoe_nl_cmd_dump_sessions() increments idx before calling fill_info(),
so once fill_info() fails because the skb is full, idx already points
past the session that did not fit. cb-&gt;args[0] is set to that value and
the next round resumes one entry too far, dropping the session from the
dump entirely - one lost session per message boundary, roughly one in
every 90 at the current record size.

Step idx back before leaving the loop.
</content>
</entry>
<entry>
<title>ipoe: fix ipoe_wlock double release in IPOE_CMD_DELETE</title>
<updated>2026-08-08T14:20:32+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-08T14:20:32+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=59124bbc26bf0211723b3eb5b5186d3dc7b72b0c'/>
<id>urn:sha1:59124bbc26bf0211723b3eb5b5186d3dc7b72b0c</id>
<content type='text'>
ipoe_nl_cmd_delete() drops ipoe_wlock before sleeping in
synchronize_rcu() and taking rtnl via unregister_netdev(), then falls
through into the out_unlock label and releases it a second time. Every
successful session delete therefore increments the semaphore count by
one.

Since the count only ever grows, ipoe_wlock stops providing mutual
exclusion after a handful of teardowns: with the count at N, up to N+1
writers may hold it simultaneously. This is currently masked because
ipoe_nl_family does not set parallel_ops, so genetlink serialises every
.doit/.dumpit under genl_mutex and no two writers can overlap in
practice. It turns into a real race on the session hash lists as soon as
that changes, or as soon as a writer is introduced outside the genl
handlers.

It also silently defeats the barrier in ipoe_fini(): the down()/up()
pair meant to wait for an in-flight writer is satisfied immediately by
the leaked count and waits for nothing.

Return directly after unregister_netdev() so the success path releases
the lock exactly once. The early up() is deliberate and stays where it
is - holding a sleeping semaphore across unregister_netdev() would nest
ipoe_wlock inside rtnl, while ipoe_create() takes rtnl first and
ipoe_wlock afterwards. Error paths, return values and lock ordering are
unchanged.
</content>
</entry>
<entry>
<title>ipoe: Fix OOPS if ipv6.disable=1</title>
<updated>2026-07-06T08:07:00+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-07-06T08:04:10+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=6930d048efe7be06cd36e7413914be0bec4a45b5'/>
<id>urn:sha1:6930d048efe7be06cd36e7413914be0bec4a45b5</id>
<content type='text'>
ipoe_recv() handles every ETH_P_IPV6 frame arriving on an IPoE
by calling ipoe_lookup_rt6()/ip6_route_output().
On ipv6.disable=1 IPv6 FIB is not initialized.
We can detect that by ipv6_mod_enabled() since kernel 4.8,
and return RX_HANDLER_PASS, so packet falls thru normal stack and get discarded.

Fixes: https://github.com/accel-ppp/accel-ppp/issues/313

Signed-off-by: Denys Fedoryshchenko &lt;denys.f@collabora.com&gt;
</content>
</entry>
</feed>
