mirror of
https://github.com/smoltcp-rs/smoltcp.git
synced 2025-09-29 22:02:05 +00:00
Handle parsing and display of IPv4 mapped IPv6 addresses.
* The IPv6 address parser now handles IPv4 mapped IPv6 addresses which take on the form ::ffff:x.x.x.x. * Implement Display for IPv4 mapped IPv6 addresses * Implement From<IPv4Address> for IPv6Address Fixes #86
This commit is contained in:
parent
8d908127d3
commit
07b78a8d50
@ -137,6 +137,18 @@ impl<'a> Parser<'a> {
|
||||
Err(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn accept_ipv4_mapped_ipv6_part(&mut self, parts: &mut [u16], idx: &mut usize) -> Result<()> {
|
||||
let octets = self.accept_ipv4_octets()?;
|
||||
|
||||
parts[*idx] = ((octets[0] as u16) << 8) | (octets[1] as u16);
|
||||
*idx += 1;
|
||||
parts[*idx] = ((octets[2] as u16) << 8) | (octets[3] as u16);
|
||||
*idx += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn accept_ipv6_part(&mut self, (head, tail): (&mut [u16; 8], &mut [u16; 6]),
|
||||
(head_idx, tail_idx): (&mut usize, &mut usize),
|
||||
@ -169,12 +181,27 @@ impl<'a> Parser<'a> {
|
||||
// Valid u16 to be added to the address
|
||||
head[*head_idx] = part as u16;
|
||||
*head_idx += 1;
|
||||
|
||||
if *head_idx == 6 && head[0..*head_idx] == [0, 0, 0, 0, 0, 0xffff] {
|
||||
self.try(|p| {
|
||||
p.accept_char(b':')?;
|
||||
p.accept_ipv4_mapped_ipv6_part(head, head_idx)
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
Some(part) if *tail_idx < 6 => {
|
||||
// Valid u16 to be added to the address
|
||||
tail[*tail_idx] = part as u16;
|
||||
*tail_idx += 1;
|
||||
|
||||
if *tail_idx == 1 && tail[0] == 0xffff
|
||||
&& head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
|
||||
self.try(|p| {
|
||||
p.accept_char(b':')?;
|
||||
p.accept_ipv4_mapped_ipv6_part(tail, tail_idx)
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
Some(_) => {
|
||||
@ -237,8 +264,7 @@ impl<'a> Parser<'a> {
|
||||
Ok(Ipv6Address::from_parts(&addr))
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
fn accept_ipv4(&mut self) -> Result<Ipv4Address> {
|
||||
fn accept_ipv4_octets(&mut self) -> Result<[u8; 4]> {
|
||||
let mut octets = [0u8; 4];
|
||||
for n in 0..4 {
|
||||
octets[n] = self.accept_number(3, 0x100, false)? as u8;
|
||||
@ -246,6 +272,12 @@ impl<'a> Parser<'a> {
|
||||
self.accept_char(b'.')?;
|
||||
}
|
||||
}
|
||||
Ok(octets)
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
fn accept_ipv4(&mut self) -> Result<Ipv4Address> {
|
||||
let octets = self.accept_ipv4_octets()?;
|
||||
Ok(Ipv4Address(octets))
|
||||
}
|
||||
|
||||
@ -510,6 +542,27 @@ mod test {
|
||||
// Long number
|
||||
assert_eq!(Ipv6Address::from_str("::000001"),
|
||||
Err(()));
|
||||
// IPv4-Mapped address
|
||||
assert_eq!(Ipv6Address::from_str("::ffff:192.168.1.1"),
|
||||
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"),
|
||||
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
|
||||
assert_eq!(Ipv6Address::from_str("0::ffff:192.168.1.1"),
|
||||
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
|
||||
// Only ffff is allowed in position 6 when IPv4 mapped
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"),
|
||||
Err(()));
|
||||
// Positions 1-5 must be 0 when IPv4 mapped
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"),
|
||||
Err(()));
|
||||
// Out of range ipv4 octet
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"),
|
||||
Err(()));
|
||||
// Invalid hex in ipv4 octet
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"),
|
||||
Err(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -135,6 +135,23 @@ impl Address {
|
||||
*self == Self::LOOPBACK
|
||||
}
|
||||
|
||||
/// Query whether the IPv6 address is an [IPv4 mapped IPv6 address].
|
||||
///
|
||||
/// [IPv4 mapped IPv6 address]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
|
||||
pub fn is_ipv4_mapped(&self) -> bool {
|
||||
self.0[0..12] == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff]
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
/// Convert an IPv4 mapped IPv6 address to an IPv4 address.
|
||||
pub fn as_ipv4(&self) -> Option<::wire::ipv4::Address> {
|
||||
if self.is_ipv4_mapped() {
|
||||
Some(::wire::ipv4::Address::new(self.0[12], self.0[13], self.0[14], self.0[15]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function used to mask an addres given a prefix.
|
||||
///
|
||||
/// # Panics
|
||||
@ -156,6 +173,10 @@ impl Address {
|
||||
|
||||
impl fmt::Display for Address {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.is_ipv4_mapped() {
|
||||
return write!(f, "::ffff:{}.{}.{}.{}", self.0[12], self.0[13], self.0[14], self.0[15])
|
||||
}
|
||||
|
||||
// The string representation of an IPv6 address should
|
||||
// collapse a series of 16 bit sections that evaluate
|
||||
// to 0 to "::"
|
||||
@ -204,6 +225,16 @@ impl fmt::Display for Address {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
/// Convert the given IPv4 address into a IPv4-mapped IPv6 address
|
||||
impl From<::wire::ipv4::Address> for Address {
|
||||
fn from(address: ::wire::ipv4::Address) -> Self {
|
||||
let octets = address.0;
|
||||
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff,
|
||||
octets[0], octets[1], octets[2], octets[3]])
|
||||
}
|
||||
}
|
||||
|
||||
/// A specification of an IPv6 CIDR block, containing an address and a variable-length
|
||||
/// subnet masking prefix length.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
@ -606,6 +637,9 @@ mod test {
|
||||
use super::{Packet, Protocol, Repr};
|
||||
use wire::pretty_print::{PrettyPrinter};
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use wire::ipv4::Address as Ipv4Address;
|
||||
|
||||
static LINK_LOCAL_ADDR: Address = Address([0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
@ -646,6 +680,14 @@ mod test {
|
||||
format!("{}", LINK_LOCAL_ADDR));
|
||||
assert_eq!("fe80::7f00:0:1",
|
||||
format!("{}", Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)));
|
||||
assert_eq!("::",
|
||||
format!("{}", Address::UNSPECIFIED));
|
||||
assert_eq!("::1",
|
||||
format!("{}", Address::LOOPBACK));
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
assert_eq!("::ffff:192.168.1.1",
|
||||
format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -703,6 +745,31 @@ mod test {
|
||||
assert_eq!(addr.mask(127), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
#[test]
|
||||
fn test_is_ipv4_mapped() {
|
||||
assert_eq!(false, Address::UNSPECIFIED.is_ipv4_mapped());
|
||||
assert_eq!(true, Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped());
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
#[test]
|
||||
fn test_as_ipv4() {
|
||||
assert_eq!(None, Address::UNSPECIFIED.as_ipv4());
|
||||
|
||||
let ipv4 = Ipv4Address::new(192, 168, 1, 1);
|
||||
assert_eq!(Some(ipv4), Address::from(ipv4).as_ipv4());
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
#[test]
|
||||
fn test_from_ipv4_address() {
|
||||
assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]),
|
||||
Address::from(Ipv4Address::new(192, 168, 1, 1)));
|
||||
assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 222, 1, 41, 90]),
|
||||
Address::from(Ipv4Address::new(222, 1, 41, 90)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cidr() {
|
||||
let cidr = Cidr::new(LINK_LOCAL_ADDR, 64);
|
||||
|
Loading…
x
Reference in New Issue
Block a user