<feed xmlns='http://www.w3.org/2005/Atom'>
<title>accel-ppp.git/.github/workflows, branch master</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=master</id>
<link rel='self' href='https://git.amelek.net/accel-ppp/accel-ppp.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/'/>
<updated>2026-09-11T18:37:47+00:00</updated>
<entry>
<title>ci: remove qemu build from BE (s390x) workflow using qemu from ubuntu 26.04</title>
<updated>2026-09-11T18:37:47+00:00</updated>
<author>
<name>Sergey V. Lobanov</name>
<email>sergey@lobanov.in</email>
</author>
<published>2026-09-11T18:17:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=fa7d316967a1cb0c4fa583a236757c62efd56d6a'/>
<id>urn:sha1:fa7d316967a1cb0c4fa583a236757c62efd56d6a</id>
<content type='text'>
</content>
</entry>
<entry>
<title>ci: remove debian11 as EoL, add ubuntu 26:04</title>
<updated>2026-09-11T17:58:01+00:00</updated>
<author>
<name>Sergey V. Lobanov</name>
<email>sergey@lobanov.in</email>
</author>
<published>2026-09-10T22:41:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=b8af4865bd4c0bd33b895e9f5fe66bbe8ea33980'/>
<id>urn:sha1:b8af4865bd4c0bd33b895e9f5fe66bbe8ea33980</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Merge pull request #346 from nuclearcat/ipoe-stale-session-flush</title>
<updated>2026-08-31T15:30:54+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-31T15:30:54+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=40575b78b7bb043c7a0ff4dd29a048be056dee9f'/>
<id>urn:sha1:40575b78b7bb043c7a0ff4dd29a048be056dee9f</id>
<content type='text'>
Ipoe stale session flush fixes</content>
</entry>
<entry>
<title>ipv6: assign DNS servers per session from RADIUS</title>
<updated>2026-08-12T07:42:35+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-04T15:39:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=58ef850d1705d88f006d761d32c55bde5205a27d'/>
<id>urn:sha1:58ef850d1705d88f006d761d32c55bde5205a27d</id>
<content type='text'>
The ipv6_nd and ipv6_dhcp modules could only advertise the DNS servers
configured in [ipv6-dns], the same set for every subscriber. RFC 6911
defines DNS-Server-IPv6-Address (attribute 169) for exactly this, and
the attribute was already in the shipped dictionary and in attr_defs.h;
nothing read it.

Give struct ap_session an ipv6_dns list, filled by the radius module
from that attribute, and have both modules advertise it when the session
has one: in the RDNSS option of the router advertisements, and in the
DNS_SERVERS option of DHCPv6 replies. Sessions without a list of their
own keep getting the configured servers, so nothing changes for anyone
not sending the attribute.

The selection is a single ipv6_dns_get() shared by both modules rather
than a copy in each: they already duplicate the whole [ipv6-dns] parser,
and two copies of a precedence rule are two chances to drift. It caps
what it returns, so neither the RDNSS option length (one byte, in units
of 8) nor the router advertisement buffer can be pushed around by what a
RADIUS server sends. The radius module caps at the same 3 servers as
[ipv6-dns] accepts and warns once when a reply carries more.

An Access-Accept which carries the attribute replaces the whole
previously assigned list rather than appending to it, so a re-authorized
session ends up with the servers of the latest reply and not with a
concatenation. One which does not carry it leaves the current list
alone, which is how the IPv4 MS-Primary-DNS-Server attribute already
behaves.

ipv6_dns_test.c covers the selection: assigned wins over configured,
empty list means "nothing assigned" rather than "no DNS", the cap holds
for both sources, and a caller with no room gets nothing rather than a
stomped buffer. Wired into the ASAN/UBSAN workflow.

Inspired by the per-session IPv6 DNS support in accel-ppp-ng (commit
2df6eb99), reimplemented against mainline's ap_session and ipdb types.
</content>
</entry>
<entry>
<title>l2tp: validate the deciphered length of hidden AVPs</title>
<updated>2026-08-12T07:42:35+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-04T14:40:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=9554ce05e13fdb8c55a54e259b4154c9aeaca58d'/>
<id>urn:sha1:9554ce05e13fdb8c55a54e259b4154c9aeaca58d</id>
<content type='text'>
decode_avp() only checked the 2 bytes length prefix of the Hidden AVP
Subformat when the ciphered attribute spanned more than one MD5 block.
For an attribute of at most 16 bytes it deciphered the single block and
returned straight away, leaving l2tp_recv() to take the prefix at face
value:

	orig_avp_len = ntohs(*(uint16_t *)avp-&gt;val) + sizeof(*avp);
	...
	attr-&gt;length = orig_avp_len - sizeof(*avp);

That prefix is an output of the cipher, so a peer which does not know
the secret (i.e. anyone able to reach the L2TP socket, no handshake
needed beyond a preceding Random-Vector AVP) turns it into 16 random
bits. Worse than an over-read: orig_avp_len is a uint16_t, so a prefix
of 0xffff wraps to 5, attr-&gt;length becomes -1, and the ATTR_TYPE_STRING
and ATTR_TYPE_OCTETS cases then run

	attr-&gt;val.string = _malloc(attr-&gt;length + 1);	/* _malloc(0) */
	memcpy(attr-&gt;val.string, orig_avp_val, attr-&gt;length);	/* SIZE_MAX */

that is an unbounded memcpy() into a zero sized allocation. Remotely
triggerable heap corruption on any tunnel with a secret configured. The
accel-ppp encoder always pads hidden AVPs by at least 16 bytes, so it
never produces an attribute short enough to reach this path; only a
crafted packet does.

Fix it at the source rather than at the call site: decode_avp() now
returns the deciphered length through an output parameter, and the
length is bounded against the room actually available in the received
AVP on every path out of the function, single block included. Callers
can no longer re-derive it from the AVP body and get it wrong, and the
existing multi-block check keeps guarding the deciphering loop itself.

Add packet_test.c, a standalone test which drives the real parser
through a real UDP socket: hand-crafted packets cover the length prefix
checks (including the boundaries of what fits and the single block path
the encoder cannot produce), and l2tp_packet_send()/l2tp_recv() round
trips cover the multi-block cipher and the unaligned AVP accessors. It
reproduces the corruption above under ASan on unpatched code. Wire it,
and the so far unused bitpool_test.c, into the ASAN/UBSAN workflow.

Inspired by the equivalent hardening in accel-ppp-ng (commit a8ca0f3f),
which bounds the length at the call site; the fix here is placed inside
decode_avp() and covered by a regression test.
</content>
</entry>
<entry>
<title>ci: survive the negative branch counters gcov reports</title>
<updated>2026-08-08T16:19:13+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-08-08T16:19:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=60702f40ac55d474ce26bd8e1d904feb9e5f160f'/>
<id>urn:sha1:60702f40ac55d474ce26bd8e1d904feb9e5f160f</id>
<content type='text'>
The coverage job fails while gcovr reads the data for triton.c:

    Unrecognized GCOV output ... branch  2 taken -1
    NegativeHits: Got negative hit value in gcov line 'branch  2 taken -1'

gcov emits those now and then, it is gcc bug 68080 and not something the
source can avoid. gcovr 6 and later treat it as a fatal parse error
unless --gcov-ignore-parse-errors names the case to tolerate.

Passing that option unconditionally is not enough, since the job runs on
both ubuntu-24.04 and ubuntu-22.04 and the gcovr in the latter predates
it and takes no value. Ask gcovr whether it knows the option before
adding it, and print what was decided so the log says which one ran.
</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>sstp: add ppposeq transport to avoid userspace HDLC framing</title>
<updated>2026-08-04T20:41:46+00:00</updated>
<author>
<name>Vladislav Grishenko</name>
<email>themiron@mail.ru</email>
</author>
<published>2026-07-30T10:48:55+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=3b08133ee131bca35b10bcc133f6adaf5d125c21'/>
<id>urn:sha1:3b08133ee131bca35b10bcc133f6adaf5d125c21</id>
<content type='text'>
A pty is a byte stream, so the tty flip buffer merges frames written
back to back and sstp has to re-delimit them with async HDLC escaping
and a CRC-16 FCS. On a 1452-byte payload that is ~3600 ns per frame,
most of it spent on the FCS.

PPPOSEQ is a pppox protocol whose socket is the ppp endpoint itself,
so one datagram is one frame and no framing is needed at all. The
same payload takes ~380 ns per frame, about 9 times less. Requires
kernel 2.6.37, the first with PX_MAX_PROTO 3, whose remaining slot
it claims.
Supported kernels are from 2.6.37 to 7.2.

The new ppp-mode option selects the transport; auto, the default,
falls back to async when the module is unavailable, so hosts with
prebuilt kernels are unaffected.

PPP_SYNC is removed, being disabled and unfixable over a pty: frame
boundaries cannot be recovered from the stream, and coalescing cannot
be prevented since frames arrive from the network stack.
</content>
</entry>
<entry>
<title>ci: harden s390x qemu test against modloop/disk flakes</title>
<updated>2026-06-10T12:57:46+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-06-10T12:57:46+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=0484b5d7c6c11f6ae832bf504d528e04d5fa31aa'/>
<id>urn:sha1:0484b5d7c6c11f6ae832bf504d528e04d5fa31aa</id>
<content type='text'>
The s390x netboot initramfs ships virtio_net but not virtio_blk; the
block driver only becomes available after the modloop image is
downloaded over the network at boot. When that download fails, sshd
still comes up but /dev/vda never appears and setup-disk dies with
"/dev/vda is not a block device suitable for partitioning".

- Wait for /sys/block/vda/device before running setup-alpine,
  restarting the modloop and hwdrivers services on each retry.
- Log the QEMU console (screen -L) for both VM launches and dump the
  logs at the end of the job, so failures of the second boot (the
  other recurring flake, where ssh never comes back after reboot)
  are debuggable.

Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>ci: Tightening warning condition in dmesg checks</title>
<updated>2026-05-03T23:58:15+00:00</updated>
<author>
<name>Denys Fedoryshchenko</name>
<email>denys.f@collabora.com</email>
</author>
<published>2026-05-03T23:57:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/accel-ppp/accel-ppp.git/commit/?id=a8556b814669802996d30aa06deac4b19327b694'/>
<id>urn:sha1:a8556b814669802996d30aa06deac4b19327b694</id>
<content type='text'>
The boot-time RETBleed: WARNING: Spectre v2 mitigation...
line matches the broad WARNING: regex.
The intent of that pattern was to catch kernel WARN_ON() splats,
which always begin with WARNING: CPU:. We can tighten checks, to avoid false positive.

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