Skip to content

Commit

Permalink
Auto merge of rust-lang#85819 - CDirkx:is_unicast_link_local_strict, …
Browse files Browse the repository at this point in the history
…r=joshtriplett

Remove `Ipv6Addr::is_unicast_link_local_strict`

Removes the unstable method `Ipv6Addr::is_unicast_link_local_strict` and keeps the behaviour of `Ipv6Addr::is_unicast_link_local`, see also rust-lang#85604 where I have tried to summarize related discussion so far.

My intent is for `is_unicast_link_local`, `is_unicast_site_local` and `is_unicast_global` to have the semantics of checking if an address has Link-Local, Site-Local or Global scope, see also rust-lang#85696 which changes the behaviour of `is_unicast_global` and renames these methods to `has_unicast_XXX_scope` to reflect this.

For checking Link-Local scope we currently have two methods: `is_unicast_link_local` and `is_unicast_link_local_strict`. This is because of what appears to be conflicting definitions in [IETF RFC 4291](https://datatracker.ietf.org/doc/html/rfc4291).

From [IETF RFC 4291 section 2.4](https://datatracker.ietf.org/doc/html/rfc4291#section-2.4): "Link-Local unicast" (`FE80::/10`)
```text
Address type         Binary prefix        IPv6 notation   Section
------------         -------------        -------------   -------
Unspecified          00...0  (128 bits)   ::/128          2.5.2
Loopback             00...1  (128 bits)   ::1/128         2.5.3
Multicast            11111111             FF00::/8        2.7
Link-Local unicast   1111111010           FE80::/10       2.5.6
Global Unicast       (everything else)
```

From [IETF RFC 4291 section 2.5.6](https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.6): "Link-Local IPv6 Unicast Addresses" (`FE80::/64`)
```text
| 10 bits  |         54 bits         |          64 bits           |
+----------+-------------------------+----------------------------+
|1111111010|           0             |       interface ID         |
+----------+-------------------------+----------------------------+
```

With `is_unicast_link_local` checking `FE80::/10` and `is_unicast_link_local_strict` checking `FE80::/64`.

There is also [IETF RFC 5156 section 2.4](https://datatracker.ietf.org/doc/html/rfc5156#section-2.4) which defines "Link-Scoped Unicast" as `FE80::/10`.

It has been pointed out that implementations in other languages and the linux kernel all use `FE80::/10` (rust-lang#76098 (comment), rust-lang#76098 (comment)).

Given all of this I believe the correct interpretation to be the following: All addresses in `FE80::/10` are defined as having Link-Local scope, however currently only the block `FE80::/64` has been allocated for "Link-Local IPv6 Unicast Addresses". This might change in the future however; more addresses in `FE80::/10` could be allocated and those will have Link-Local scope. I therefore believe the current behaviour of `is_unicast_link_local` to be correct (if interpreting it to have the semantics of `has_unicast_link_local_scope`) and `is_unicast_link_local_strict` to be unnecessary, confusing and even a potential source of future bugs:

Currently there is no real difference in checking `FE80::/10` or `FE80::/64`, since any address in practice will be `FE80::/64`. However if an application uses `is_unicast_link_local_strict` to implement link-local (so non-global) behaviour, it will be incorrect in the future if addresses outside of `FE80::/64` are allocated.

r? `@joshtriplett` as reviewer of all the related PRs
  • Loading branch information
bors committed May 31, 2021
2 parents b348385 + c1f0c15 commit dc08641
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 101 deletions.
110 changes: 25 additions & 85 deletions library/std/src/net/ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,79 +1267,31 @@ impl Ipv6Addr {
(self.segments()[0] & 0xfe00) == 0xfc00
}

/// Returns [`true`] if the address is a unicast link-local address (`fe80::/64`).
/// Returns `true` if the address is a unicast address with link-local scope,
/// as defined in [RFC 4291].
///
/// A common misconception is to think that "unicast link-local addresses start with
/// `fe80::`", but [IETF RFC 4291] actually defines a stricter format for these addresses:
/// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4].
/// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6],
/// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format:
///
/// ```no_rust
/// | 10 |
/// | bits | 54 bits | 64 bits |
/// ```text
/// | 10 bits | 54 bits | 64 bits |
/// +----------+-------------------------+----------------------------+
/// |1111111010| 0 | interface ID |
/// +----------+-------------------------+----------------------------+
/// ```
/// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`,
/// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated,
/// and those addresses will have link-local scope.
///
/// This method validates the format defined in the RFC and won't recognize addresses
/// like `fe80:0:0:1::` or `fe81::` as unicast link-local addresses.
/// If you need a less strict validation, use [`Ipv6Addr::is_unicast_link_local()`] instead.
///
/// # Examples
///
/// ```
/// #![feature(ip)]
///
/// use std::net::Ipv6Addr;
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0);
/// assert!(ip.is_unicast_link_local_strict());
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff);
/// assert!(ip.is_unicast_link_local_strict());
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0);
/// assert!(!ip.is_unicast_link_local_strict());
/// assert!(ip.is_unicast_link_local());
///
/// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0);
/// assert!(!ip.is_unicast_link_local_strict());
/// assert!(ip.is_unicast_link_local());
/// ```
///
/// # See also
///
/// - [IETF RFC 4291 section 2.5.6]
/// - [RFC 4291 errata 4406] (which has been rejected but provides useful
/// insight)
/// - [`Ipv6Addr::is_unicast_link_local()`]
///
/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
/// [IETF RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6
/// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406
#[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[unstable(feature = "ip", issue = "27709")]
#[inline]
pub const fn is_unicast_link_local_strict(&self) -> bool {
matches!(self.segments(), [0xfe80, 0, 0, 0, ..])
}

/// Returns [`true`] if the address is a unicast link-local address (`fe80::/10`).
/// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope",
/// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it.
///
/// This method returns [`true`] for addresses in the range reserved by [RFC 4291 section 2.4],
/// i.e. addresses with the following format:
///
/// ```no_rust
/// | 10 |
/// | bits | 54 bits | 64 bits |
/// +----------+-------------------------+----------------------------+
/// |1111111010| arbitratry value | interface ID |
/// +----------+-------------------------+----------------------------+
/// ```
///
/// As a result, this method considers addresses such as `fe80:0:0:1::` or `fe81::` to be
/// unicast link-local addresses, whereas [`Ipv6Addr::is_unicast_link_local_strict()`] does not.
/// If you need a strict validation fully compliant with the RFC, use
/// [`Ipv6Addr::is_unicast_link_local_strict()`] instead.
/// [RFC 4291]: https://tools.ietf.org/html/rfc4291
/// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
/// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
/// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6
/// [loopback address]: Ipv6Addr::LOCALHOST
///
/// # Examples
///
Expand All @@ -1348,29 +1300,17 @@ impl Ipv6Addr {
///
/// use std::net::Ipv6Addr;
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0);
/// assert!(ip.is_unicast_link_local());
/// // The loopback address (`::1`) does not actually have link-local scope.
/// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false);
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff);
/// assert!(ip.is_unicast_link_local());
/// // Only addresses in `fe80::/10` have link-local scope.
/// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false);
/// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);
///
/// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0);
/// assert!(ip.is_unicast_link_local());
/// assert!(!ip.is_unicast_link_local_strict());
///
/// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0);
/// assert!(ip.is_unicast_link_local());
/// assert!(!ip.is_unicast_link_local_strict());
/// // Addresses outside the stricter `fe80::/64` also have link-local scope.
/// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true);
/// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);
/// ```
///
/// # See also
///
/// - [IETF RFC 4291 section 2.4]
/// - [RFC 4291 errata 4406] (which has been rejected but provides useful
/// insight)
///
/// [IETF RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
/// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406
#[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[unstable(feature = "ip", issue = "27709")]
#[inline]
Expand Down
18 changes: 2 additions & 16 deletions library/std/src/net/ip/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,6 @@ fn ipv6_properties() {
let unique_local: u16 = 1 << 2;
let global: u16 = 1 << 3;
let unicast_link_local: u16 = 1 << 4;
let unicast_link_local_strict: u16 = 1 << 5;
let unicast_site_local: u16 = 1 << 6;
let unicast_global: u16 = 1 << 7;
let documentation: u16 = 1 << 8;
Expand Down Expand Up @@ -524,11 +523,6 @@ fn ipv6_properties() {
} else {
assert!(!ip!($s).is_unicast_link_local());
}
if ($mask & unicast_link_local_strict) == unicast_link_local_strict {
assert!(ip!($s).is_unicast_link_local_strict());
} else {
assert!(!ip!($s).is_unicast_link_local_strict());
}
if ($mask & unicast_site_local) == unicast_site_local {
assert!(ip!($s).is_unicast_site_local());
} else {
Expand Down Expand Up @@ -587,7 +581,6 @@ fn ipv6_properties() {
let unique_local: u16 = 1 << 2;
let global: u16 = 1 << 3;
let unicast_link_local: u16 = 1 << 4;
let unicast_link_local_strict: u16 = 1 << 5;
let unicast_site_local: u16 = 1 << 6;
let unicast_global: u16 = 1 << 7;
let documentation: u16 = 1 << 8;
Expand Down Expand Up @@ -621,11 +614,7 @@ fn ipv6_properties() {
unicast_link_local
);

check!(
"fe80::",
&[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
unicast_link_local | unicast_link_local_strict
);
check!("fe80::", &[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local);

check!(
"febf:ffff::",
Expand All @@ -650,7 +639,7 @@ fn ipv6_properties() {
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff
],
unicast_link_local | unicast_link_local_strict
unicast_link_local
);

check!(
Expand Down Expand Up @@ -897,9 +886,6 @@ fn ipv6_const() {
const IS_UNIQUE_LOCAL: bool = IP_ADDRESS.is_unique_local();
assert!(!IS_UNIQUE_LOCAL);

const IS_UNICAST_LINK_LOCAL_STRICT: bool = IP_ADDRESS.is_unicast_link_local_strict();
assert!(!IS_UNICAST_LINK_LOCAL_STRICT);

const IS_UNICAST_LINK_LOCAL: bool = IP_ADDRESS.is_unicast_link_local();
assert!(!IS_UNICAST_LINK_LOCAL);

Expand Down

0 comments on commit dc08641

Please sign in to comment.