Support for ipv6 scope id


#1

Hi,

std::ne::Ipv6Addr is currently unable to parse IPv6 addresses with a scope identifier, like FE80::C001:1DFF:FEE0:0%eth0, as defined in RFC 4007. It is possible to work around that limitation by using something like:

use std::net::{Ipv4Addr, Ipv6Addr};
pub enum Ip {
    V4(Ipv4Addr),
    V6(Ipv6Addr, Option<String>),
}

And then implement FromStr for Ip.
But it’s pretty inconvenient, and I’d rather use std::net::IpAddr. Would it be possible to support this directly in std::net::Ipv6Addr? If so, what is the right path to make this happen? Open an issue on github?


#2

Ipv6Addr is Copy so it won’t be possible to support scope IDs in that type itself.


#3

All right… I was feeling that the String would make that complicated.


#4

Even if you could somehow maintain the Copy aspect, it’s likely you wouldn’t want to waste any space on a (AFAICT) rarely specified part of the address. To that end, a custom type (whether in std or otherwise) seems more appropriate anyway.


#5

That makes sense.
I created a custom type like this, for now:

use std::net::{Ipv4Addr, Ipv6Addr};
/// Represent an IP address. This type is similar to `std::net::IpAddr` but it supports IPv6 scope
/// identifiers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Ip {
    /// Represent an IPv4 address
    V4(Ipv4Addr),
    /// Represent an IPv6 and its scope identifier, if any
    V6(Ipv6Addr, Option<String>),
}

impl Into<IpAddr> for Ip {
    fn into(self) -> IpAddr {
        match self {
            Ip::V4(ip) => IpAddr::from(ip),
            Ip::V6(ip, _) => IpAddr::from(ip),
        }
    }
}

impl From<Ipv6Addr> for Ip {
    fn from(value: Ipv6Addr) -> Self {
        Ip::V6(value, None)
    }
}

impl From<Ipv4Addr> for Ip {
    fn from(value: Ipv4Addr) -> Self {
        Ip::V4(value)
    }
}

impl From<IpAddr> for Ip {
    fn from(value: IpAddr) -> Self {
        match value {
            IpAddr::V4(ip) => Ip::from(ip),
            IpAddr::V6(ip) => Ip::from(ip),
        }
    }
}

impl Ip {
    /// Parse a string representing an IP address.
    pub fn parse(s: &str) -> Result<Ip, AddrParseError> {
        let mut parts = s.split('%');
        let addr = parts.next().unwrap();
        match IpAddr::from_str(addr) {
            Ok(IpAddr::V4(ip)) => Ok(Ip::from(ip)),
            Ok(IpAddr::V6(ip)) => Ok(Ip::V6(ip, parts.next().map(|s| s.to_string()))),
            Err(e) => Err(e),
        }
    }