1

Add RFC6052 post

This commit is contained in:
Evan Pratten 2024-01-06 13:19:16 -05:00
parent 3796f507ed
commit 2fc5c0af7f
8 changed files with 129 additions and 0 deletions

View File

@ -0,0 +1,128 @@
---
title: Bitmasking RFC6052 addresses
description: Efficiently embedding and extracting IPv4 addresses in IPv6 addresses
date: 2024-01-22
tags:
- internet
draft: true
extra:
auto_center_images: true
aliases:
- /blog/rfc6052-bitmasks
---
[RFC6052: *IPv6 Addressing of IPv4/IPv6 Translators*](https://datatracker.ietf.org/doc/html/rfc6052) is a Standards Track RFC that defines a method for encoding IPv4 addresses in IPv6 addresses for internet protocol translators.
While I strongly recommend familiarizing yourself with the RFC before continuing here, I'll provide a super brief overview of what this looks like.
## Embedded addresses
The RFC provides this helpful ASCII diagram showing how an IPv4 address can sit inside of an IPv6 address:
```text
+--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|PL| 0-------------32--40--48--56--64--72--80--88--96--104---------|
+--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|32| prefix |v4(32) | u | suffix |
+--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|40| prefix |v4(24) | u |(8)| suffix |
+--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|48| prefix |v4(16) | u | (16) | suffix |
+--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|56| prefix |(8)| u | v4(24) | suffix |
+--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|64| prefix | u | v4(32) | suffix |
+--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|96| prefix | v4(32) |
+--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
```
The PL column on the left signifies the IPv6 prefix length, and the remaining columns mark the bit boundaries of the IPv6 address.
### Why?
Imagine a scenario where you are a client on a single-stacked IPv6 network, trying to communicate with a single-stack IPv4 host. Your network setup probably looks like this:
![A network using NAT64](/images/drawings/rfc6052-bitmasks/NAT64-setup.png)
As the request originator, you need an address to send your packet to. Since you are on an IPv6-only network though, the IPv4 address of the host is not very useful. Sending a packet to that address would be like sending physical mail addressed to only your friend's cell phone number.
So, to get around this, you need to derive an IPv6 address from that IPv4 address. This is where RFC6052 comes in.
### How?
Well, on the surface, RFC6052 is super simple. The RFC defines a "Well-Known Prefix" (WKP), specifically reserved to indicate that all addresses inside of it are actually IPv4 addresses in disguise.
So, if you were sending a packet to `192.0.2.8`, you'd instead address it to `64:ff9b::192.0.2.8` (yes, that syntax exists). With a valid IPv6 address as the destination, all your IPv6-capable routing gear can treat your packet as normal, and it becomes the responsibility of your NAT64 translator to extract the original IPv4 address and send the packet on its way across the IPv4 internet.
### The challenge
As you may have noticed from the ASCII table above, I only just showcased the simplest addressing option (the last one). All the others contain this `u` column, with the IPv4 address wrapped around it.
According to the RFC:
> Bits 64 to 71 of the address are reserved for compatibility with the host identifier format defined in the
> IPv6 addressing architecture [RFC4291](https://datatracker.ietf.org/doc/html/rfc4291).
> These bits MUST be set to zero.
This means that we can't simply append an IPv4 address to any IPv6 address and get an RFC6052-compliant address. We need to do some bit-shifting to make sure that the `u` column stays zeroed.
## Fiddling with bits
This section will be showing Rust code since it makes the types of the values very clear.
It is assumed that IPv4 addresses are stored as a `u32` and IPv6 addresses are stored as a `u128`.
I warn you, the following code put all its points in the "functionality" skill tree, and forgot about the "pretty" skill tree.
### Encoding
To stuff an IPv4 address into an IPv6 prefix, we can use the following set of operations:
```rust
let rfc6052_address = {
ipv6_prefix
| (
(
ipv4_address as u128
& (0xffff_ffffu128 << (32 + min(0, ipv6_prefix_len - 64)))
)
<< (128 - ipv6_prefix_len - 32)
)
| (
(
(ipv4_address as u128) << max(0, 128 - ipv6_prefix_len - 32 - 8)
)
& 0x00ff_ffff_ffff_ffff
)
};
```
This effectively splits the IPv4 address into two parts, moves them around the `u` boundary, then applies that ontop of the IPv6 prefix.
![A bad drawing of how this works](/images/drawings/rfc6052-bitmasks/ipv4-embed.png)
Of course, this isn't how the address would actually look (I did just mix Decimal and Hexadecimal in the same number), but you get the point.
### Decoding
Ok, so what about decoding?
```rust
let host_part = rfc6052_address & ((1 << (128 - ipv6_prefix_len)) - 1);
let ipv4_addr = {
(
(
(
host_part & 0xffff_ffff_ffff_ffff_0000_0000_0000_0000
)
| (host_part & 0x00ff_ffff_ffff_ffff) << 8
)
>> max(8, 128 - ipv6_prefix_len - 32)
) as u32
};
```
I find this one a bit simpler. It masks off the IPv6 prefix and the `u` column, then smushes the IPv4 address back into one piece.
![Another bad drawing of how this works](/images/drawings/rfc6052-bitmasks/ipv4-extract.png)

View File

@ -1,6 +1,7 @@
blockquote {
margin: 0;
padding: 0 1em;
margin-bottom: 1em;
color: #57606a;
border-left: 0.25em solid #d0d7de;

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB