Previously, if NM_VERSION_MIN_REQUIRED was not defined, it defaulted to
NM_VERSION. As a consequence, if NM_VERSION_MAX_ALLOWED was defined we
got a compilation error because MAX_ALLOWED < MIN_REQUIRED.
MAX_ALLOWED is used to get compilation warnings if you unintentionally
use a libnm's symbol introduced in a newer version. MIN_REQUIRED is used
to get rid of warnings about symbol deprecations.
Libnm users may want to use MAX_ALLOWED alone, because using a too new
symbol would fail to compile with older libnm. But they might want to
get deprecation warnings as soon as possible, so they want to leave
MIN_REQUIRED empty.
After the changes in release.sh in previous commits, during development
the value of NM_VERSION will always be the next version, not the latest
released one. As a consequence, we don't need to set MICRO+1 in
NM_API_VERSION, which was a temporary workaround.
After the previous commits, release.sh bumps the version after tagging
the release, and not before. Therefore, it expects that the version is
already the next one when doing the release.
Manually bump the version this time so release.sh sees the right value
the next time it's executed after these changes.
Fix typo freedestkop -> freedesktop.
Removed unused argument of check_news (additionally, it was incorrectly
using @ instead of $).
Fixed incorrect use of `$? = 0` that was always successful.
After tagging a release, create a commit bumping to the next version.
This effectively ends the change in the logic initiated in the previous
commit, from "bump version, then release" to "release, then bump
version".
The purpose of this is to have the right version set in nm_version.h and
nm_version_macros.h between two releases. Without this change, when we
introduced a new symbol, thus using the NM_AVAILABLE_IN_1_XX annotations,
we got compilation warnings until we did the next release (making the CI
to be red when configured the compilation to fail on warnings).
Don't bump the version before tagging the release. Instead, assume that
it's already correctly set. This is in preparation for the next commit
where we will bump the version after the release, not before.
But don't assume that in the case of rc1 and major releases. For rc1 we
switch from devel releases to RC releases, and in major we switch from
RC releases to stable releases. For example, when we are going to
release 1.58-rc1, the current version will be 1.57.X-dev, so we need to
bump to 1.58-rc1. When we're going to release 1.58.0, the current
version will be 1.58-rcX, so we need to bump to 1.58.0.
Previously the metric of the CLAT default route was set to the IPv6
route metric plus 50. Instead:
- If there is another non-CLAT default route on the device, use the
same metric plus 1, so that native connectivity is always
preferred.
- Otherwise, use the metric from the "ipv4.route-metric" property of
the connection profile.
There is no guarantee that the part of the packet we want to read or
write via direct packet access is linear. From the documentation of
bpf_skb_pull_data():
For direct packet access, testing that offsets to access are within
packet boundaries (test on skb->data_end) is susceptible to fail if
offsets are invalid, or if the requested data is in non-linear parts
of the skb. On failure the program can just bail out, or in the case
of a non-linear buffer, use a helper to make the data available. The
bpf_skb_load_bytes() helper is a first solution to access the
data. Another one consists in using bpf_skb_pull_data to pull in
once the non-linear parts, then retesting and eventually access the
data.
See: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2107#note_3288979
Reported-by: DasSkelett <dasskelett@dasskelett.dev>
Avoid the additional function call and perform the needed checks
directly in clat_handle_v4() and clat_handle_v6(). It will make easier
to check that the packet is linear is the next commit.
Convert IPv6 fragments into IPv4.
The PLAT fragments IPv4 packets larger than the IPv6 MTU size into
smaller IPv6 packets. The safest IPv6 MTU value to configure on a PLAT
is the minimum IPv6 MTU, 1280. Therefore, we can expect IPv6 fragments
to be quite common.
The current code takes the IPv6 MTU value from the IPv6 default
route. However, that value is always zero because NM doesn't set it
usually. Instead, it should use the IPv6 MTU sysctl value. The problem
is that at this point NM hasn't written the sysctl yet, and we need
some logic to find the actual value.
Reported-by: DasSkelett <dasskelett@dasskelett.dev>
The current "ip6_mtu" field of a l3cd is the IPv6 MTU received via
RA. Rename it accordingly and introduce another "ip6_mtu_static" field
that contains the value set in the ipv6.mtu connection property. It's
not used yet, but it will be in a following commit.
When running a traceroute for an IPv4 address, the nodes before the
NAT64 gateway return ICMPv6 Time Exceeded messages with a source IPv6
address not belonging to the NAT64 prefix. Such messages would be
normally dropped by the CLAT because the source address can't be
translated. This behavior complicates troubleshooting.
Follow the recommendation of
draft-ietf-v6ops-icmpext-xlat-v6only-source-01 and translate the
source address to the dummy IPv4 192.0.0.8.
bpf_redirect_neigh() looks up the next hop in the routing table and
then redirects the packet to the given ifindex. The problem is that
the routing table might contain a default route with lower metric on a
different device; in that case the FIB lookup returns a next hop on
the other device, and the packet can't be delivered.
Use bpf_redirect() instead; the IPv4 already has the right L2
destination because the IPv4 default route points to the IPv6 gateway.
Reported-by: DasSkelett <dasskelett@dasskelett.dev>
The TCX attachment type was added in kernel 6.6 (October 2023) and it
replaces the Traffic Control (TC) BPF attachment, providing better
usability. Convert the l3cfg code to use it.
When CLAT is enabled, we want to also enable and honor by default DHCP
option 108 (IPv6-only preferred), so that the host can avoid
requesting an IPv4 address and go IPv6-only.
ICMPv6 error messages contain a copy of the original packet that
caused the error. In a 464XLAT deployment, this inner packet is an
IPv6 packet (as translated by the PLAT), while the local host expects
to see the original IPv4 packet it generated.
Without translation, the local host can't match the error to an active
socket. This breaks functionality like Path MTU Discovery (PMTUD),
traceroute, and error reporting for connected UDP sockets.
This commit implements the translation of the inner headers from IPv6
to IPv4 for incoming ICMPv6 errors.
Some implementation notes:
- this only handles incoming ICMPv6; outgoing ICMPv4 is not yet
implemented, but it seems less important.
- the program uses different functions for rewriting the outer and
inner header. I tried using recursion but the verifier didn't seem
to like it.
- after rewriting the inner headers, the ICMP checksum is
incrementally updated based on difference of all the individual
modifications done to the inner headers. This has the advantage
that all the operations are fixed-size. But probably it would be
easier and faster to just calculate the checksum from scratch.
The program only needs to know the local IPv4 address, the local IPv6
address and the PREF64. There is no need to create multiple maps for
that, just pass a global configuration struct containing those 3
fields.
Improve the code style and consistency of some functions:
- declare only one variable per line
- add "const" keyword to read-only function arguments
- remove unneeded function arguments
- rename variables holding headers on the stack with the "_buf"
suffix
Avoid using pointer arithmetic in the BPF program, so that it requires
only CAP_BPF and not CAP_PERFMON. In this context "pointer arithmetic"
means adding a variable value to a packet pointer. This means that the
program no longer tries to parse variable-size headers (IPv4 options,
IPv6 extension headers). Those were already not supported before. It
also doesn't parse VLAN tags, but there should be no need for that. If
we use fixed offset, we can avoid using the parsing helpers from
libxdp.
There are 3 possible results from clat_translate_v6():
1. the packet didn't match the CLAT IPv6 address and must be
accepted;
2. the packet matches but it is invalid and so it must be dropped;
3. the packet matches and it is valid; clat_handle_v6() should
translate the packet to IPv4;
Before, the function returned TC_ACT_SHOT for both 2 and 3. Therefore,
clat_handle_v6() tried to rewrite also invalid packets.
Fix that by returning TC_ACT_UNSPEC for valid packets, meaning that
there isn't a final verdict yet.
When copying the IPv6 addresses via a direct assignement, the compiler
generates 32-bit operations that the verifier doesn't like:
> 237: (61) r3 = *(u32 *)(r8 +76) ; frame1: R3_w=pkt(r=0) R8=ctx()
> ; .saddr = ip6h->saddr, @ clat.bpf.c:124
> 238: (63) *(u32 *)(r10 -64) = r3
> invalid size of register spill
Use explicit memcpy() for those.
Also, check the packet length before accessing the ICMPv6 header.