Subject: [v6ops] RFC 7915 Issues

From: Alberto Leiva <[email-masked]>
Date: Wed, 11 Sep 2019 15:09:42 -0500

Hi

I ran into a few problems trying to properly update my translator to
RFC 7915. I want to turn the least pedantic ones into erratas, but
some of them probably require feedback. Here they are:

## Authentication Header

RFC 7915:

> For the first 'next header' that does not match one of the cases
> above, its Next Header value (which contains the transport
> protocol number) is copied to the protocol field in the IPv4
> header.  This means that all transport protocols are translated.
> Note:  Some translated protocols will fail at the receiver for
> various reasons: some are known to fail when translated (e.g.,
> IPsec Authentication Header (51))

Also RFC 7915:

> If the Next Header field of the Fragment Header is an
> extension header (except ESP, but including the Authentication
> Header (AH)), then the packet SHOULD be dropped and logged.

It seems as though the RFC is contradicting itself. Should we
"translate" AH or not? Though this can be sort of amended by noting
that the second requirement is in a fragment-enabled context, while
the first one is not.

I find it hard to believe that AH translation is supposed to depend on
fragmentation, though. This is most likely a bug born from the 6145
errata merge. Both 2765 and 6145 assume that AH is always translated;
the 6145 errata is the only part of 7915 that contradicts this.

The way I see it, if AH is not bound to work through the translator,
responding an ICMP error is probably most user-friendly. However, I
suppose it would trump any future AH extension attempts to work around
the problem. On the other hand, such an extension is unlikely. But
then again, I'm an engineer, and I like future-proof.

For all I know, there's probably a reason why AH was, for a long time,
meant not to be dropped, and I don't see any real arguments
challenging this. Or a debate, for that matter. The idea that AH
should be dropped appears to be just an interpretation that sneaked
its way into the standard through a bug report that was otherwise
relatively correct. (I'm not fond of defining length fields by way of
transformations rather than end results, myself.)

I mean, the errata says this:

> AH is not supposed to be translated. RFC 2765 had explicitly stated
> this and RFC 6145 also should continue to have this stated.
(https://www.rfc-editor.org/errata_search.php?rfc=6145)

It's plainly untrue. 2765 says "AH does not work through a
translator," not that it shouldn't be translated. The idea that AH
should cause a packet drop is emergent. It's certainly not there in
2765, it's arguably there in 6145's introduction ("it does not
translate IPv6 extension headers except the Fragment Header") but not
in its specification ("For the first 'next header' that does not match
one of the cases above, its Next Header value (...) is copied to the
protocol field"), and 7915 specifies plainly contradicting rules (seen
above). But regardless, it doesn't look like it was ever properly
discussed during the development of these RFC updates.

And by the way: The expression "X will not be translated" should be
defined in a glossary. Does it mean that packets containing X should
be dropped, does it mean that X should be copied without
modifications, or does it mean that X should be skipped while
translating? All of these spell "not translated" to me.

(From my reading of them, 6145 means the former, while 2765 means the
latter. I didn't bother to look what 7915 means.)

As of now, my intention is to go with the original intent (treat AH
like an unknown transport protocol). I feel it's the most properly
thought-out one.

## Simple Extension Headers

"Simple Extension Headers" is an expression I made up for the purposes
of this discussion. I use it to refer to the `Hop-By-Hop`,
`Destination Options` and `Routing` IPv6 headers. I group them because
they have basically identical semantics from a translator's point of
view.

RFC 7915:

> If any of an IPv6 Hop-by-Hop Options header, Destination Options
> header, or Routing header with the Segments Left field equal to zero
> are present in the IPv6 packet, those IPv6 extension headers MUST be
> ignored (...) and the packet translated normally.

Also RFC 7915:

> If the Next Header field of the Fragment Header is an extension
> header (except ESP), then the packet SHOULD be dropped and logged.

Should Simple Extension Headers be "ignored" or "dropped"?

This has the same quirks as the previous problem: One of them exists
within a Fragmentation context and the other one doesn't, and also,
frankly, it looks like an erratum hiccup.

I suppose it's interesting to note that the IPv6 spec defines a
recommended order for these headers, and according to it,
`Destination` is the only allowed one before and after `Fragment`.
It's only a recommendation, though. But the semantics of the
post-`Fragment` `Destination` are different from the pre-`Fragment`
`Destination`, so overall, I guess the Fragmentation context makes a
little more sense in this case. It's kind of hard to tell; RFC 2460
never defines actual `Destination` options so I have no idea what they
do.

At present, my translator always "skips" (ie. pretends they're not
there, but adjusts headers lengths accordingly) Simple Extension
Headers. I'm wondering if I should implement exactly what is
specified, though: "skip" pre-`Fragment` Simple Extension Headers, but
trigger a packet drop on post-`Fragment` Simple Extension Headers.

## Extension Headers (general notes)

When it comes to extension headers, the SIIT RFCs sort of brainfart.
The information is scattered all over the place and there's some
undefined behavior. If 7915bis ever comes along, I would recommend
adding an new section entirely dedicated to this topic.

>From my reading of it, this seems to be the overall intention. Lower
bullet points take precedence:

1. If AH is present, the packet will be dropped at some point. There's
no consensus on who's supposed to do this. If the translator drops it,
it seems it's supposed to return ICMPv6 Type/Code 1/1 (per
rfc7915#section-5.4).
2. If ESP is present, it should be treated like an unknown layer 4
header. (ie. copied, along with the rest of the packet, as is.)
3. Routing Header with nonzero segments left should cause a packet
drop with ICMPv6 4/0.
4. Otherwise, Simple Extension Headers between `IPv6` and `Fragment`
should be "ignored." (Word needs glossary definition, along with
"dropped" and "not translated.")
5. Known extension headers (as defined above) after `Fragment` should
cause a packet drop, and perhaps also an ICMPv6 1/1 (per
rfc7915#section-5.4).

According to my intuition, those 1/1 ICMPv6 errors are rather dubious
because they come from 6145 erratas, which use the expression "dropped
and logged" unlike the rest of the RFC. This suggests to me that there
was no effort to imitate the style, which means they might have
forgotten the "1/1 unless otherwise stated" rule (ie.
rfc7915#section-5.4). But whatever; my translator implements exactly
what it says. It says "dropped and logged," not "silently dropped and
logged."

Also, 1/1 (Communication Prohibited) is pretty strange. Isn't 4/1
(Parameter Problem) a closer fit?

Other extension headers (such as No Next Header and extension headers
defined by other RFCs) seemingly cause undefined behavior. In
particular, it's fairly irritating that extension headers and
transport protocols share the same nexthdr range, so there's no way to
tell the difference between unknown extension headers and unknown
transport protocols.

Therefore, my translator groups unknown extension headers with unknown
transport protocols, which means they are copied as-is.

## Extension Headers, another one

I've already quoted these paragraphs, but they're guilty of yet
another contradiction:

> Total Length:  If the Next Header field of the Fragment Header is an
> extension header (except ESP, but including the Authentication
> Header (AH)), then the packet SHOULD be dropped and logged.

> Fragment Offset:  (...)
> If the Next Header field of the Fragment Header is an extension
> header (except ESP), then the packet SHOULD be dropped and logged.

They are at odds with this one:

> Protocol:  For ICMPv6 (58), it is changed to ICMPv4 (1); otherwise,
> extension headers are skipped, and the Next Header field is copied
> from the last IPv6 header.

These are all from the same section (5.1.1).

## Naming

At this point, I think it's pretty clear that the terms "SIIT",
"Stateless NAT64", "NAT46" and "CLAT" (and maybe also "Stateful NAT64"
and "NAT64") all deserve glossary presence. Everyone (even different
RFCs) use different names and it confuses new users. I'd recommend
adding "IIT" as well.

...Well, 6877 already defines "CLAT." There's that.

Perhaps "SIIT" could mean the "-ion" version, while "Stateless NAT64"
could be the "-or" version. ("translation" vs "translator.")

Just a thought.

## Illegal Source Address

> Source Address: (...)
> If the translator gets an illegal source address (e.g., 0.0.0.0,
> 127.0.0.1, etc.), the translator SHOULD silently discard the
> packet (as discussed in Section 5.3.7 of [RFC1812]).

The IPv4 version of the Source Address translation has a serviceable
explanation of what constitutes an "illegal" source address.

But the IPv6 version doesn't:

> If the translator gets an illegal source address (e.g., ::1,
> etc.), the translator SHOULD silently drop the packet.

## Multicast

This paragraph is seemingly unaware of EAM:

> The IP/ICMP header translation specified in this document is
> consistent with requirements of multicast IP/ICMP headers.  However,
> IPv4 multicast addresses [RFC5771] cannot be mapped to IPv6 multicast
> addresses [RFC3307] based on the unicast mapping rule [RFC6052].  An
> example of experiments of the multicast address mapping can be found
> in [RFC6219].

Can't multicast work with EAM? (I honestly don't know.)

## DF and the Fragment Header

> When the IPv4 sender does not set the DF bit, the translator MUST NOT
> include the Fragment Header for the non-fragmented IPv6 packets.

This requirement is phrased awkwardly. (They were likely thinking a
little too hard on atomic fragment deprecation. The context it's
placed in also didn't help; The previous paragraph also starts with
"(...) when the IPv4 sender does not set the DF bit.")

The way I understand it, the translator MUST NOT include the Fragment
Header on non-fragmented IPv6 packets even if DF is enabled. The
presence of the Fragment Header really does not depend on DF at all.
(I mean, what happens if DF is enabled but the packet is already
fragmented anyway? Should the translator merge the fragments so the
header can be removed? Of course not.)

It should read

> The translator MUST NOT include the Fragment Header on non-fragmented IPv6 packets.

Or rather,

> The translator must include a Fragment Header if, and only if, the translated IPv6 packet is fragmented.

## DF and Fragments

The RFC doesn't really specify what happens when the translator
receives a fragmented packet with DF enabled. This is for obvious
reasons, but it does lead me to some tumbling around since I want to
do something meaningful anyway.

Drop packet? Translate like a DF-enabled packet, but skip the
lowest-ipv6-mtu fragmentation? Could be interesting.

## Usage of lowest-ipv6-mtu

> (the packet is a fragment
> or the DF bit is not set and the packet size is greater than the
> minimum IPv6 MTU in the network set by the translator configuration
> function)

Inconsistent, and ruins Ctrl+F. It should read

> (the packet is a fragment
> or the DF bit is not set and the packet size is greater than
> lowest-ipv6-mtu)

## Fragmented UDP Checksum Zero

RFC 7915:

> Fragmented IPv4 UDP packets that do not contain a UDP checksum (i.e.,
> the UDP checksum field is zero) are not of significant use on the
> Internet, and in general will not be translated by the IP/ICMP
> translator (Section 4.5).  However, when the translator is configured
> to forward the packet without a UDP checksum, the fragmented IPv4 UDP
> packets will be translated.

"Translation of fragmented UDP zero-checksum is configuration dependent."

Also RFC 7915:

> A stateless translator cannot compute the UDP checksum of
> fragmented packets, so when a stateless translator receives the
> first fragment of a fragmented UDP IPv4 packet and the checksum
> field is zero, the translator SHOULD drop the packet and generate
> a system management event that specifies at least the IP
> addresses and port numbers in the packet.

"Translation of fragmented UDP zero-checksum is not happening."

The former is nonsense to me, so my translator implements the latter.

This bug was introduced in RFC 6145. 2765 persistently advocates the drop.

## Header Lengths

This one is a whim of mine, but I'll mention in nonetheless.

Total Length and Payload Length are a mess:

> Payload Length:  Total length value from the IPv4 header, minus the
> size of the IPv4 header and IPv4 options, if present.

> Payload Length:  Total length value from the IPv4 header, plus 8
> for the Fragment Header, minus the size of the IPv4 header and
> IPv4 options, if present.

> If
> the translation of this "packet in error" changes the length of the
> datagram, the Total Length field in the outer IPv6 header MUST be
> updated.

> Total Length:  Payload length value from the IPv6 header, plus the
> size of the IPv4 header.

> However, the Total
> Length field and the Protocol field are adjusted to "skip" these
> extension headers.

> Total Length:  If the Next Header field of the Fragment Header is an
> extension header (except ESP, but including the Authentication
> Header (AH)), then the packet SHOULD be dropped and logged.  For
> other cases, the Total Length MUST be set to Payload Length value
> from IPv6 header, minus the length of the extension headers up to
> the Fragmentation Header, minus 8 for the Fragment Header, plus
> the size of the IPv4 header.

> The translation of this "packet in
> error" is likely to change the length of the datagram; thus, the
> Total Length field in the outer IPv4 header MUST be updated.

As a Linux kernel citizen, I have to deal with dumb kernel hacks when
computing these lengths. (Well, the current code is messy and I want
to simplify it, so I don't know if this will still hold true after a
bit of hammering.) In addition, the requirements above seem to be
incognizant of the maximum ICMP error lengths (576 and 1280), which as
far as I know, have not been officially deprecated anywhere.

So, speaking as an implementor, defining the lengths in terms of
transformations is not useful to me. I'd recommend something more
along the lines of

> Total Length: Length of the final IPv4 packet.

> Payload Length: Length of the final IPv6 packet - Length of the IPv6 header (40).

That would also allow big chunks of the earlier quotes to be obviated.