Skip to content

networkd: RFC compliant autonomous prefix handling#5636

Merged
poettering merged 1 commit intosystemd:masterfrom
hendrikw01:master
Mar 31, 2017
Merged

networkd: RFC compliant autonomous prefix handling#5636
poettering merged 1 commit intosystemd:masterfrom
hendrikw01:master

Conversation

@hendrikw01
Copy link
Contributor

@hendrikw01 hendrikw01 commented Mar 25, 2017

Previously, lifetime_valid of a Router Advertisement was not handled the way RFC4862 has specified.
In particular: Sections 5.5.3.d and 5.5.3.e

This pull request fixes: #3879 and #5638

@heftig
Copy link
Contributor

heftig commented Mar 25, 2017

No, I think this is still wrong. If I understand section 5.5.3.e correctly, an incoming advertisement with preferred lifetime and valid lifetime as zero for a prefix that is already known should:

  • Immediately deprecate the address
  • Keep the address for 2 more hours, unless the advertisement has been authenticated

@hendrikw01
Copy link
Contributor Author

hendrikw01 commented Mar 25, 2017

Yes, the router sends the first RA after reconnect with:
ValidLifetime 7199 and referredLifetime 0 for the old prefix.
networkd/kernel then flags the address as deprecated and sets a lifetime of 2 hours. This behaviour is exactly right but then the router starts to send ValidLifetime 0 for that already deprecated prefix.

@hendrikw01
Copy link
Contributor Author

hendrikw01 commented Mar 25, 2017

This bug can be used as a denial-of-service attack.
I have tested to send an RA with radvd:

prefix 2001:db8:1:0::/64
        {
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
                AdvPreferredLifetime 0;
                AdvValidLifetime 0;
         };

Only this RA is needed to force the network interface of all systemd-networkd nodes in the local network to go into the fail state and then looses all ipv4/6 addresses after they expire.

Mär 25 21:31:04 bpi systemd-networkd[210]: NDISC: Received Router Advertisement: flags none preference low lifetime 30 sec
Mär 25 21:31:04 bpi systemd-networkd[210]: NDISC: Invoking callback for 'r'.
Mär 25 21:31:04 bpi systemd-networkd[210]: eth0: Could not set NDisc route or address: Invalid argument
Mär 25 21:31:04 bpi systemd-networkd[210]: eth0: Failed

@hendrikw01 hendrikw01 closed this Mar 26, 2017
@hendrikw01 hendrikw01 reopened this Mar 26, 2017
@hendrikw01 hendrikw01 changed the title networkd: remove ipv6 address if needed networkd: RFC compliant autonomous prefix handling Mar 26, 2017
@hendrikw01
Copy link
Contributor Author

I have created a simple test suite to validate the correct behavior using radvd:
https://github.com/hendrikw01/ra_lifetime_test

My implementation does work as expected.

Copy link
Member

@keszybz keszybz left a comment

Choose a reason for hiding this comment

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

So this looks OK, as long as the two patches are squashed together, and the stylistic issues are fixed.

@pfl can you take a look?


/* see RFC4862 section 5.5.3.e */
r = address_get(link, address->family, &address->in_addr, address->prefixlen, &existing_address);
if(r > 0){
Copy link
Member

Choose a reason for hiding this comment

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

See CODING_STYLE: space before ( and after ).

This patch uses 4ch indentation, instead of 8ch, but it is updated in the second patch. It seems that they should be squashed together.


static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
_cleanup_address_free_ Address *address = NULL;
Address *existing_address = NULL;
Copy link
Member

Choose a reason for hiding this comment

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

Initialization is not needed.

else if(existing_address->cinfo.ifa_valid <= NDISC_PREFIX_LFT_MIN)
address->cinfo.ifa_valid = existing_address->cinfo.ifa_valid;
else
address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
Copy link
Member

Choose a reason for hiding this comment

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

So this first branch of the outer if follows the RFC word for word. Looks good.


#define NDISC_DNSSL_MAX 64U
#define NDISC_RDNSS_MAX 64U
#define NDISC_PREFIX_LFT_MIN 7200
Copy link
Member

Choose a reason for hiding this comment

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

Please add U like in other constants.

else if(lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
address->cinfo.ifa_valid = lifetime_remaining;
else
address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
Copy link
Member

Choose a reason for hiding this comment

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

I made a comment here, but it seems github lost it: this follows the RFC very closely, should be correct.

@keszybz keszybz requested a review from pfl March 30, 2017 02:49
r = address_get(link, address->family, &address->in_addr, address->prefixlen, &existing_address);
if(r > 0){
if(lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > existing_address->cinfo.ifa_valid)
address->cinfo.ifa_valid = lifetime_valid;
Copy link
Contributor

Choose a reason for hiding this comment

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

This one is clear and from the RFC. ACK.

if(lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > existing_address->cinfo.ifa_valid)
address->cinfo.ifa_valid = lifetime_valid;
else if(existing_address->cinfo.ifa_valid <= NDISC_PREFIX_LFT_MIN)
address->cinfo.ifa_valid = existing_address->cinfo.ifa_valid;
Copy link
Contributor

Choose a reason for hiding this comment

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

ACK. We don't have secured Router Advertisments so the bottom half of section 5.5.3, part e), 2nd bullet does not apply.

else if(existing_address->cinfo.ifa_valid <= NDISC_PREFIX_LFT_MIN)
address->cinfo.ifa_valid = existing_address->cinfo.ifa_valid;
else
address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
Copy link
Contributor

Choose a reason for hiding this comment

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

ACK.

else
address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
}else if(lifetime_valid > 0)
address->cinfo.ifa_valid = lifetime_valid;
Copy link
Contributor

Choose a reason for hiding this comment

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

This happens when r = address_get() < 0, right? Shouldn't this also check if lifetime_valid > NDISC_PREFIX_LFT_MIN like above?

Copy link
Member

Choose a reason for hiding this comment

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

I thought about this too, and in the end that seems to be the same as bullet 1, i.e. having the address invalidated because remaining lifetime is <= 0. In that case this assignment would be correct. Let's see what @hendrikw01 says.

Thanks for the review!

Copy link
Contributor Author

@hendrikw01 hendrikw01 Mar 30, 2017

Choose a reason for hiding this comment

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

lifetime_valid doesn´t need to be 2 hours or longer. That means the router can add a prefix/address with a lifetime of 30 secs and renew it every 10 secs. This behavior is totally fine but a bogus RA can´t force it to invalidate faster. If we got new prefix (address_get() < 0) we can´t decide if this is a legit Prefix or not, so the only option is to assume it is, and if it is the router updates the prefix before it gets invalid.

@hendrikw01
Copy link
Contributor Author

Now, I think I have all issues covered. From my side, it can be merged.

@heftig
Copy link
Contributor

heftig commented Mar 30, 2017

I think you're supposed to squash your fixups, not just push more commits.

Previously, `lifetime_valid` of a Router Advertisement was not handled
the way RFC4862 has specified.

In particular: Sections 5.5.3.d and  5.5.3.e
@hendrikw01
Copy link
Contributor Author

Sorry, first time for me to squash something in git.

@poettering poettering merged commit 6554550 into systemd:master Mar 31, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

networkd: IPv6 address is lost after router reconnects to ISP

5 participants