Skip to content

feat: Implement --pref64 (RFC 8781)#10

Open
DasSkelett wants to merge 2 commits intofreifunk-gluon:mainfrom
DasSkelett:pref64
Open

feat: Implement --pref64 (RFC 8781)#10
DasSkelett wants to merge 2 commits intofreifunk-gluon:mainfrom
DasSkelett:pref64

Conversation

@DasSkelett
Copy link
Contributor

@DasSkelett DasSkelett commented Jan 8, 2026

Hi! This implements the PREF64 option as defined in RFC 8781.
The background is that we want to do an IPv6-mostly Parker-based setup at FFMUC, i.e. DNS64 + NAT64 + 464XLAT on clients and on nodes as fallback for clients that don't support CLAT on their own.
PREF64 helps clients to discover the NAT64 prefix for CLAT, especially useful in compination with the DHCP option 108 ("IPv6-only preferred") from RFC 8925


The implementation should be relatively straightforward, adding the new --pref64 option, to be used e.g. like uradvd --pref64 64:ff9b::/96 -i eth0 -p 2001:db8::/64.
The most "confusing" part I believe is the combined nd_opt_pref64_scaled_lifetime_and_plc ("scaled lifetime" plus "prefix length code"), as per the standard they are of length 13 bit and 3 bit respectively, which is quite annoying to handle given there's no arbitrary-sized integer <C23. to my knowledge In the end it's only one bitwise operation combining the two values into a common 16 bit int, though.

Note that I don't usually write any code in C, so please pay extra attention to things like safe string & memory handling during review, which I might or might not have gotten right. (I tried my best and followed existing code whenever possible)

I was not able to test this with an actual client yet unfortunately, but at least Wireshark is able to parse the option correctly.
Successfully tested with:

  • an Android smartphone. To absolutely rule out that it could detect the prefix from any other source, I sent a different PREF64 than what is actually in use within the network and have also set a "Private DNS" server (aka DoT) that doesn't do DNS64.
    After reconnecting to the WiFi, Android started doing its own DNS64 + CLAT using the prefix from the PREF64 option.
  • two Linux machines running a self-compiled NetworkManager with CLAT support that fetches the prefix infromation from the PREF64 RA option

I have also cherry-picked the iovlen change from #4 to base this option on the new mechanism - maybe you want to merge #4 first so I can rebase on top afterwards.
Now waiting for #11

Copy link

@jluebbe jluebbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only checked that the default behavior is unchanged, which seems to be the case.

Copy link

@benzea benzea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess there is no point in permitting the option to be passed multiple times?

[OPT_PREFERRED_LIFETIME] = {"preferred-lifetime", required_argument, 0, 0},
[OPT_MAX_ROUTER_ADV_INTERVAL] = {"max-router-adv-interval", required_argument, 0, 0},
[OPT_MIN_ROUTER_ADV_INTERVAL] = {"min-router-adv-interval", required_argument, 0, 0},
[OPT_PREF64] = {"pref64", required_argument, 0, 0},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other options seem to be pretty verbose. Maybe this should be --nat64-prefix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "concept" is generally called pref64 in e.g. RFC6146 and RFC8781, but --nat64-prefix might give a better hint for people who aren't immediately familiar with it,so fine with me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, fine with me if that is what people are expecting.

@DasSkelett
Copy link
Contributor Author

I guess there is no point in permitting the option to be passed multiple times?

How would I restrict this? I believe this is missing for all the other options as well, right?

@benzea
Copy link

benzea commented Jan 23, 2026

I guess there is no point in permitting the option to be passed multiple times?

How would I restrict this? I believe this is missing for all the other options as well, right?

Ah, no, it is all good. I meant the other way around, actually add multiple entries into the frame if the option is passed multiple times.

This adds support for the PREF64 Router Advertisement option (RFC 8781).
To include the option in the RA, a prefix needs to be set using the new `--pref64` argument, e.g. `--pref64 64:ff9b::/96`.
Clients can use this prefix for IPv6 address synthesis aka local DNS64, or 464XLAT.
Clients usually require DHCPv4 option 108 ("IPv6-only preferred") to be set to actually enable the 464XLAT CLAT system.
memcpy(G.pref64_prefix, buf.s6_addr, 12);
G.pref64_enabled = true;

return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant return

fprintf(stderr, "uradvd: error: invalid PREF64 length %s (only prefixes of length 32, 40, 48, 56, 64 or 96 are supported).\n", slash+1);
exit(1);
}
preflen = (uint8_t)pl;
Copy link
Member

@neocturne neocturne Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preflen variable is not needed, pl can be used instead.


*slash = 0;
char *endptr;
ulong pl = strtoul(slash+1, &endptr, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be nice to assign slash+1 to a new const char* variable, as it appears a lot.

pref64.nd_opt_pref64_len = 2;
uint16_t scld_lifetime = (3u * G.max_rtr_adv_interval) / 8u; // RFC8781 Section 4.1
if (scld_lifetime > 8191) { /* 0x1FFF, max 13 bit value */
scld_lifetime = 8191;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might make sense to add a comment that this check can't be hit, as max_rtr_adv_interval can't be larger than 1800.

As the limit is specifically about the width of the field, I'd prefer writing this as scld_lifetime > 0x1FFF.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants