Bug 247700

Summary: rtadvd: fails to generate error when iface lacks a link-local address
Product: Base System Reporter: John W. O'Brien <john>
Component: standardsAssignee: Hiroki Sato <hrs>
Status: In Progress ---    
Severity: Affects Only Me CC: hrs, net
Priority: ---    
Version: 12.1-RELEASE   
Hardware: Any   
OS: Any   
See Also: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=254445
Attachments:
Description Flags
Reject scope-incompatible IPv6 source address none

Description John W. O'Brien 2020-07-02 00:18:03 UTC
Scenario
========

rtadvd is configured on an interface that has a globally unique address (GUA) and does not have a link-local address (LLA).

Expected behavior
=================

rtadvd emits an error and does not send Router Advertisement (RA) messages on the interface.

Observed behavior
=================

rtadvd sends RAs that are invalid per the specification [0], using a GUA source with an LLA destination. The kernel on a receiving node correctly identifies these as invalid and ignores them.

[0] https://tools.ietf.org/html/rfc4861#section-4.2

Analysis
========

rtadvd does not validate the configuration of each interface. It relies on the source address selection algorithm in the kernel. That is a reasonable design. The steps the algorithms performs to exclude source address candidates with scopes that are incompatible with the destination address appear to neglect this case. Therefore, this is probably not a bug in rtadvd, but in the source address selection algorithm.

That is, if an interface has a globally unique address (GUA) and no link-local address (LLA), and the destination is an LLA via that interface, then the kernel will erroneously select the GUA as the source address. However, I have not yet found a way to reproduce this behavior other than in the scenario above.
Comment 1 John W. O'Brien 2020-07-02 00:24:56 UTC
Created attachment 216122 [details]
Reject scope-incompatible IPv6 source address

I have very limited confidence in this patch because I am not sure that I fully understand the surrounding code and have not yet tested it. Its purpose is to more fully illustrate what I think the problem is and a possible solution. Feedback would be warmly welcomed.

Also, I discovered this bug on 12.1-RELEASE-p5. Though I have not attempted to reproduce it on any other version, I expect I could.
Comment 2 Hiroki Sato freebsd_committer freebsd_triage 2020-07-02 01:13:27 UTC
I'll take this.
Comment 3 Hiroki Sato freebsd_committer freebsd_triage 2020-07-02 17:24:55 UTC
It is a valid situation for a unicast communication where a global-scope address is the source address and a link-local-scope address is the destination address though it is not recognized as valid as a Router Advertisement message.  Limiting the address selection to the same zone as the destination's is too restrictive.  The current implementation prefers a source scope whose scope is larger than the destination's (c.f. Rule 2, Sec. 5, RFC 6724).  Even if the source is smaller than the destination, an address is selected in any way.  However, upon sending a packet, the network stack will discard the packet due to an error "no destination".

So in the situation with src=GUA/dst=LLA, a unicast communication works and it does not against the specifications.  Usually it does not happen because every interface has at least one LLA configured (c.f. Sec. 2.1, RFC 4291) and the source address selection algorithm always prefers a smaller scope.

For an interface with no LLA, I think NDP does not work in various ways because it (and MLDv2) heavily depends on LLA.  It is not limited to Router Advertisement messages.  For this reason, FreeBSD configures an EUI-64 LLA by default.

There are some scenarios where only GUAs are configured on an interface, however.  To prevent rtadvd(8) from sending invalid packets you reported, I think rtadvd(8) should check if the interface has an LLA or not.  I believe running rtadvd(8) on an interface with no LLA is a wrong configuration.

Please let me know if I understand your report correctly, and comments about my understanding about the issue you pointed out.  If the additional check on rtadvd(8) is sufficient, I will work on it.
Comment 4 John W. O'Brien 2020-07-02 22:42:07 UTC
(In reply to Hiroki Sato from comment #3)

> It is a valid situation for a unicast communication where a global-scope
> address is the source address and a link-local-scope address is the
> destination address though it is not recognized as valid as a Router
> Advertisement message.  Limiting the address selection to the same zone as
> the destination's is too restrictive.  The current implementation prefers a
> source scope whose scope is larger than the destination's (c.f. Rule 2, Sec.
> 5, RFC 6724).  Even if the source is smaller than the destination, an
> address is selected in any way.  However, upon sending a packet, the
> network stack will discard the packet due to an error "no destination".

My understanding of the source selection rules is definitely incomplete, and may also be flawed, so I will gladly defer to you on this matter.

> So in the situation with src=GUA/dst=LLA, a unicast communication works
> and it does not against the specifications.

An aspect of this about which I still harbor uncertainty is the question of whether every usable A->B address pair is also a usable B->A pair, assuming unicast semantics. In the rtadvd(8)/RA case, we enjoy the benefit of multicast semantics, where B->A is invalid regardless of the scopes. It was for that reason, in addition to the above, that I elected to report this against rtadvd(8) instead of against in6_src.c.

> Usually it does not happen because every interface has at least one LLA
> configured (c.f. Sec. 2.1, RFC 4291) and the source address selection
> algorithm always prefers a smaller scope.
> 
> For an interface with no LLA, I think NDP does not work in various ways
> because it (and MLDv2) heavily depends on LLA.  It is not limited to
> Router Advertisement messages.  For this reason, FreeBSD configures an
> EUI-64 LLA by default.

Accepting this as fact implies that there is another bug somewhere. The reason I discovered the rtadvd(8) problem in the first place was through a sequence of steps to create an if_bridge(4) and configure it as a subnet gateway (for multiple VNET jails via epair(4)s) that resulted, unnoticed, in the GUA && !LLA scenario without explicitly disabling AUTO_LINKLOCAL. I have not attempted to reproduce those steps.

> There are some scenarios where only GUAs are configured on an interface,
> however.  To prevent rtadvd(8) from sending invalid packets you reported,
> I think rtadvd(8) should check if the interface has an LLA or not.  I
> believe running rtadvd(8) on an interface with no LLA is a wrong
> configuration.

I concur with the last statement. If I achieve nothing else with this bug, I want to reduce substantially the time required to troubleshoot the specific scenario I encountered: rtadvd(8) + no LLA = fail immediately and emit an actionable error.

> Please let me know if I understand your report correctly, and comments about
> my understanding about the issue you pointed out.  If the additional check
> on rtadvd(8) is sufficient, I will work on it.

Yes, that would be sufficient. Thank you very much for your prompt and thorough response.