Make an IPv4 address range in a network rule cover only its own addresses - #31
Merged
Merged
Conversation
The network filter compares an IPv4 address in its IPv6 form ::ffff:a.b.c.d, whose first 96 bits are that fixed prefix, but the rule parser kept an IPv4 prefix length as written. 10.0.0.0/8 therefore compared the first 8 of 128 bits, which are zero for every IPv4 address and for ::1, and permitted all of them. Prefix lengths up to 128 were accepted for IPv4 as well. An IPv4 prefix is now validated as 1 to 32 and counted from bit 96. An IPv6 prefix keeps its meaning and its limit of 128, and /0 stays invalid. Eight checks hold ranges to both directions; against the old filter the four refusals fail. Both committed libnetblocker.so copies are rebuilt with the pinned toolchain.
A rule such as ::ffff:127.0.0.0/8 reads like an IPv4 range, but in IPv6 notation the prefix counts from bit 0, so it covered every IPv4 address and ::1. An IPv4 range in that notation needs at least 96 bits, as in ::ffff:127.0.0.0/104, so a shorter prefix on an IPv4-mapped address is now ignored, which leaves every such address refused. A native IPv6 range such as ::/8 keeps its meaning. The check uses ::ffff:127.0.0.0/8 and 127.0.0.1, so it fails whether the rule is wrongly read as IPv6 /8 or as the IPv4 range 127.0.0.0/8. Both committed libnetblocker.so copies are rebuilt with the pinned toolchain.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A network rule naming a range of IPv4 addresses, such as
10.0.0.0/8, permitted every IPv4 address and the IPv6 loopback instead of only the addresses in the range. A range now covers exactly its own addresses, and a prefix length that cannot belong to an IPv4 range is refused. None of the shipped policies names a range, so they are unaffected.Linked issues
No linked issues
1. Problem
The network filter compares an IPv4 address in its IPv6 form,
::ffff:a.b.c.d, whose first 96 bits are that fixed prefix. The rule parser kept an IPv4 prefix length as written, so10.0.0.0/8compared only the first 8 of those 128 bits. They are zero for every IPv4 address and for::1, so the range permitted all of them. Prefix lengths up to 128 were accepted for IPv4 too, and an IPv4 address written in IPv6 notation with a short prefix, such as::ffff:127.0.0.0/8, covered the same far larger space while reading like an IPv4 range.The layer at fault is the LD_PRELOAD network filter, which decides which hosts a submission may contact. It let a submission reach addresses the policy does not name. Found while reviewing the filter for #30.
2. Improvement from the user's perspective
An instructor who allows a range, for instance a university network, now allows that range and not every IPv4 address. A rule whose prefix cannot describe an IPv4 range, such as
/33or::ffff:127.0.0.0/8, is ignored instead of permitting everything.3. Improvement from the maintainer's perspective
The parser now counts an IPv4 prefix from bit 96, named as a constant with the reason beside it, and validates it against 32 rather than 128. An IPv4 range in IPv6 notation needs a prefix of at least 96 bits, as in
::ffff:10.0.0.0/104; a native IPv6 range such as::/8keeps its meaning. Ten new checks hold ranges to both directions, IPv4 and IPv6, and they fail against the filter as it was onmain, so the defect cannot come back unnoticed.4. Testing manual
Prerequisites
--privileged,--cap-addor--security-opt.Steps
docker run --rm -v "$PWD:/repo:ro" ubuntu:26.04 bash -c 'apt-get update -qq && apt-get install -y -qq gcc-14 libc6-dev binutils >/dev/null && cp -a /repo /w && bash /w/tests/network_cache_ports.sh'main:git show origin/main:ld_preloader/netblocker.c > /tmp/old-netblocker.c, then step 1 with-v /tmp/old-netblocker.c:/old.c:roadded andcp /old.c /w/ld_preloader/netblocker.c &&placed beforebash.tests/landlock-acceptance/README.mdgives them, on a machine that runs the image natively.Expected result
address rangesshows tenoklines, and the suite ends with61 passed, 0 failed, 0 skipped.FAILlines, all refusals the old filter did not make: an address outside an IPv4 range, the neighbour of a/32(actual: errno:111, meaning the connection reached a port where nothing listens), a/33prefix,::ffff:127.0.0.0/8, and::1under an IPv4 range. The suite ends with56 passed, 5 failed, 0 skipped.bestanden: 11,bestanden: 27andbestanden: 13, each withfehlgeschlagen: 0.Negative case (what must still be rejected)
127.0.0.1under10.0.0.0/8,127.0.0.2under127.0.0.1/32,127.0.0.1under the invalid127.0.0.0/33and::ffff:127.0.0.0/8,::1under127.0.0.0/8, and127.0.0.1under::1/128are all refused, while127.0.0.1under127.0.0.0/8,127.0.0.1/32and::ffff:127.0.0.0/104, and::1under::1/128, still connect (step 1).Layers exercised
Only the filter's rule parser changes. The acceptance suites in step 3 run all three layers with the rebuilt filter and pass unchanged, but their policies name no range, so they are a regression check rather than a check of this change.
5. Test case coverage regarding this PR
tests/network_cache_ports.sh, Ubuntu 26.04 container, arm64 and amd64127.0.0.1under127.0.0.0/8,127.0.0.1/32and::ffff:127.0.0.0/104;::1under::1/128. Refused:127.0.0.1under10.0.0.0/8,127.0.0.2under127.0.0.1/32, anything under127.0.0.0/33or::ffff:127.0.0.0/8,::1under127.0.0.0/8,127.0.0.1under::1/128.netblocker.cfrommain::ffff:127.0.0.0/8refusal, added in the second commit, fails.tests/landlock-acceptance/run-tests.sh, arm64 image,--network nonetests/landlock-acceptance/extra-tests.sh, sametests/landlock-acceptance/phase-test.sh, sameAll runs used kernel 7.0 under Docker Desktop.
Breaking changes and migration
This narrows the network filter. A rule naming an IPv4 range no longer permits addresses outside it or IPv6 addresses. A rule with an IPv4 prefix longer than 32 bits, or an IPv4 address in IPv6 notation with a prefix under 96 bits, is ignored rather than permitting everything. No shipped policy names a range. A policy that relied on the old behaviour has to name the addresses it actually needs, and writes an IPv4 range in IPv6 notation with
/96or more.Checklist
--privileged,--cap-addor--security-opt, or the manual says why that was not possible.Review progress