123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113 |
- #![deny(missing_docs)]
- use byteorder::{ByteOrder, NetworkEndian};
- use core::fmt;
- use super::{Error, Result};
- use crate::wire::ip::pretty_print_ip_payload;
- pub use super::IpProtocol as Protocol;
- /// Minimum MTU required of all links supporting IPv6. See [RFC 8200 § 5].
- ///
- /// [RFC 8200 § 5]: https://tools.ietf.org/html/rfc8200#section-5
- pub const MIN_MTU: usize = 1280;
- /// Size of IPv6 adderess in octets.
- ///
- /// [RFC 8200 § 2]: https://www.rfc-editor.org/rfc/rfc4291#section-2
- pub const ADDR_SIZE: usize = 16;
- /// The link-local [all nodes multicast address].
- ///
- /// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
- pub const LINK_LOCAL_ALL_NODES: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1);
- /// The link-local [all routers multicast address].
- ///
- /// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
- pub const LINK_LOCAL_ALL_ROUTERS: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2);
- /// The link-local [all MLVDv2-capable routers multicast address].
- ///
- /// [all MLVDv2-capable routers multicast address]: https://tools.ietf.org/html/rfc3810#section-11
- pub const LINK_LOCAL_ALL_MLDV2_ROUTERS: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x16);
- /// The link-local [all RPL nodes multicast address].
- ///
- /// [all RPL nodes multicast address]: https://www.rfc-editor.org/rfc/rfc6550.html#section-20.19
- pub const LINK_LOCAL_ALL_RPL_NODES: Address = Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x1a);
- /// The [scope] of an address.
- ///
- /// [scope]: https://www.rfc-editor.org/rfc/rfc4291#section-2.7
- #[repr(u8)]
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
- pub(crate) enum MulticastScope {
- /// Interface Local scope
- InterfaceLocal = 0x1,
- /// Link local scope
- LinkLocal = 0x2,
- /// Administratively configured
- AdminLocal = 0x4,
- /// Single site scope
- SiteLocal = 0x5,
- /// Organization scope
- OrganizationLocal = 0x8,
- /// Global scope
- Global = 0xE,
- /// Unknown scope
- Unknown = 0xFF,
- }
- impl From<u8> for MulticastScope {
- fn from(value: u8) -> Self {
- match value {
- 0x1 => Self::InterfaceLocal,
- 0x2 => Self::LinkLocal,
- 0x4 => Self::AdminLocal,
- 0x5 => Self::SiteLocal,
- 0x8 => Self::OrganizationLocal,
- 0xE => Self::Global,
- _ => Self::Unknown,
- }
- }
- }
- pub use core::net::Ipv6Addr as Address;
- pub(crate) trait AddressExt {
- /// Construct an IPv6 address from a sequence of octets, in big-endian.
- ///
- /// # Panics
- /// The function panics if `data` is not sixteen octets long.
- fn from_bytes(data: &[u8]) -> Address;
- /// Query whether the IPv6 address is an [unicast address].
- ///
- /// [unicast address]: https://tools.ietf.org/html/rfc4291#section-2.5
- ///
- /// `x_` prefix is to avoid a collision with the still-unstable method in `core::ip`.
- fn x_is_unicast(&self) -> bool;
- /// Query whether the IPv6 address is a [global unicast address].
- ///
- /// [global unicast address]: https://datatracker.ietf.org/doc/html/rfc3587
- fn is_global_unicast(&self) -> bool;
- /// Query whether the IPv6 address is in the [link-local] scope.
- ///
- /// [link-local]: https://tools.ietf.org/html/rfc4291#section-2.5.6
- fn is_link_local(&self) -> bool;
- /// Query whether the IPv6 address is a [Unique Local Address] (ULA).
- ///
- /// [Unique Local Address]: https://tools.ietf.org/html/rfc4193
- ///
- /// `x_` prefix is to avoid a collision with the still-unstable method in `core::ip`.
- fn x_is_unique_local(&self) -> bool;
- /// Helper function used to mask an address given a prefix.
- ///
- /// # Panics
- /// This function panics if `mask` is greater than 128.
- fn mask(&self, mask: u8) -> [u8; ADDR_SIZE];
- /// The solicited node for the given unicast address.
- ///
- /// # Panics
- /// This function panics if the given address is not
- /// unicast.
- fn solicited_node(&self) -> Address;
- /// Return the scope of the address.
- ///
- /// `x_` prefix is to avoid a collision with the still-unstable method in `core::ip`.
- fn x_multicast_scope(&self) -> MulticastScope;
- /// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`,
- /// where `prefix_len` is the number of leading zeroes. Return `None` otherwise.
- fn prefix_len(&self) -> Option<u8>;
- }
- impl AddressExt for Address {
- fn from_bytes(data: &[u8]) -> Address {
- let mut bytes = [0; ADDR_SIZE];
- bytes.copy_from_slice(data);
- Address::from(bytes)
- }
- fn x_is_unicast(&self) -> bool {
- !(self.is_multicast() || self.is_unspecified())
- }
- fn is_global_unicast(&self) -> bool {
- (self.octets()[0] >> 5) == 0b001
- }
- fn is_link_local(&self) -> bool {
- self.octets()[0..8] == [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- }
- fn x_is_unique_local(&self) -> bool {
- (self.octets()[0] & 0b1111_1110) == 0xfc
- }
- fn mask(&self, mask: u8) -> [u8; ADDR_SIZE] {
- assert!(mask <= 128);
- let mut bytes = [0u8; ADDR_SIZE];
- let idx = (mask as usize) / 8;
- let modulus = (mask as usize) % 8;
- let octets = self.octets();
- let (first, second) = octets.split_at(idx);
- bytes[0..idx].copy_from_slice(first);
- if idx < ADDR_SIZE {
- let part = second[0];
- bytes[idx] = part & (!(0xff >> modulus) as u8);
- }
- bytes
- }
- fn solicited_node(&self) -> Address {
- assert!(self.x_is_unicast());
- let o = self.octets();
- Address::from([
- 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, o[13],
- o[14], o[15],
- ])
- }
- fn x_multicast_scope(&self) -> MulticastScope {
- if self.is_multicast() {
- return MulticastScope::from(self.octets()[1] & 0b1111);
- }
- if self.is_link_local() {
- MulticastScope::LinkLocal
- } else if self.x_is_unique_local() || self.is_global_unicast() {
- // ULA are considered global scope
- // https://www.rfc-editor.org/rfc/rfc6724#section-3.1
- MulticastScope::Global
- } else {
- MulticastScope::Unknown
- }
- }
- fn prefix_len(&self) -> Option<u8> {
- let mut ones = true;
- let mut prefix_len = 0;
- for byte in self.octets() {
- let mut mask = 0x80;
- for _ in 0..8 {
- let one = byte & mask != 0;
- if ones {
- // Expect 1s until first 0
- if one {
- prefix_len += 1;
- } else {
- ones = false;
- }
- } else if one {
- // 1 where 0 was expected
- return None;
- }
- mask >>= 1;
- }
- }
- Some(prefix_len)
- }
- }
- /// A specification of an IPv6 CIDR block, containing an address and a variable-length
- /// subnet masking prefix length.
- #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
- pub struct Cidr {
- address: Address,
- prefix_len: u8,
- }
- impl Cidr {
- /// The [solicited node prefix].
- ///
- /// [solicited node prefix]: https://tools.ietf.org/html/rfc4291#section-2.7.1
- pub const SOLICITED_NODE_PREFIX: Cidr = Cidr {
- address: Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0),
- prefix_len: 104,
- };
- /// Create an IPv6 CIDR block from the given address and prefix length.
- ///
- /// # Panics
- /// This function panics if the prefix length is larger than 128.
- pub const fn new(address: Address, prefix_len: u8) -> Cidr {
- assert!(prefix_len <= 128);
- Cidr {
- address,
- prefix_len,
- }
- }
- /// Return the address of this IPv6 CIDR block.
- pub const fn address(&self) -> Address {
- self.address
- }
- /// Return the prefix length of this IPv6 CIDR block.
- pub const fn prefix_len(&self) -> u8 {
- self.prefix_len
- }
- /// Query whether the subnetwork described by this IPv6 CIDR block contains
- /// the given address.
- pub fn contains_addr(&self, addr: &Address) -> bool {
- // right shift by 128 is not legal
- if self.prefix_len == 0 {
- return true;
- }
- self.address.mask(self.prefix_len) == addr.mask(self.prefix_len)
- }
- /// Query whether the subnetwork described by this IPV6 CIDR block contains
- /// the subnetwork described by the given IPv6 CIDR block.
- pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
- self.prefix_len <= subnet.prefix_len && self.contains_addr(&subnet.address)
- }
- }
- impl fmt::Display for Cidr {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- // https://tools.ietf.org/html/rfc4291#section-2.3
- write!(f, "{}/{}", self.address, self.prefix_len)
- }
- }
- #[cfg(feature = "defmt")]
- impl defmt::Format for Cidr {
- fn format(&self, f: defmt::Formatter) {
- defmt::write!(f, "{}/{=u8}", self.address, self.prefix_len);
- }
- }
- /// A read/write wrapper around an Internet Protocol version 6 packet buffer.
- #[derive(Debug, PartialEq, Eq, Clone)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct Packet<T: AsRef<[u8]>> {
- buffer: T,
- }
- // Ranges and constants describing the IPv6 header
- //
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // |Version| Traffic Class | Flow Label |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Payload Length | Next Header | Hop Limit |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | |
- // + +
- // | |
- // + Source Address +
- // | |
- // + +
- // | |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | |
- // + +
- // | |
- // + Destination Address +
- // | |
- // + +
- // | |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- //
- // See https://tools.ietf.org/html/rfc2460#section-3 for details.
- mod field {
- use crate::wire::field::*;
- // 4-bit version number, 8-bit traffic class, and the
- // 20-bit flow label.
- pub const VER_TC_FLOW: Field = 0..4;
- // 16-bit value representing the length of the payload.
- // Note: Options are included in this length.
- pub const LENGTH: Field = 4..6;
- // 8-bit value identifying the type of header following this
- // one. Note: The same numbers are used in IPv4.
- pub const NXT_HDR: usize = 6;
- // 8-bit value decremented by each node that forwards this
- // packet. The packet is discarded when the value is 0.
- pub const HOP_LIMIT: usize = 7;
- // IPv6 address of the source node.
- pub const SRC_ADDR: Field = 8..24;
- // IPv6 address of the destination node.
- pub const DST_ADDR: Field = 24..40;
- }
- /// Length of an IPv6 header.
- pub const HEADER_LEN: usize = field::DST_ADDR.end;
- impl<T: AsRef<[u8]>> Packet<T> {
- /// Create a raw octet buffer with an IPv6 packet structure.
- #[inline]
- pub const fn new_unchecked(buffer: T) -> Packet<T> {
- Packet { buffer }
- }
- /// Shorthand for a combination of [new_unchecked] and [check_len].
- ///
- /// [new_unchecked]: #method.new_unchecked
- /// [check_len]: #method.check_len
- #[inline]
- pub fn new_checked(buffer: T) -> Result<Packet<T>> {
- let packet = Self::new_unchecked(buffer);
- packet.check_len()?;
- Ok(packet)
- }
- /// Ensure that no accessor method will panic if called.
- /// Returns `Err(Error)` if the buffer is too short.
- ///
- /// The result of this check is invalidated by calling [set_payload_len].
- ///
- /// [set_payload_len]: #method.set_payload_len
- #[inline]
- pub fn check_len(&self) -> Result<()> {
- let len = self.buffer.as_ref().len();
- if len < field::DST_ADDR.end || len < self.total_len() {
- Err(Error)
- } else {
- Ok(())
- }
- }
- /// Consume the packet, returning the underlying buffer.
- #[inline]
- pub fn into_inner(self) -> T {
- self.buffer
- }
- /// Return the header length.
- #[inline]
- pub const fn header_len(&self) -> usize {
- // This is not a strictly necessary function, but it makes
- // code more readable.
- field::DST_ADDR.end
- }
- /// Return the version field.
- #[inline]
- pub fn version(&self) -> u8 {
- let data = self.buffer.as_ref();
- data[field::VER_TC_FLOW.start] >> 4
- }
- /// Return the traffic class.
- #[inline]
- pub fn traffic_class(&self) -> u8 {
- let data = self.buffer.as_ref();
- ((NetworkEndian::read_u16(&data[0..2]) & 0x0ff0) >> 4) as u8
- }
- /// Return the flow label field.
- #[inline]
- pub fn flow_label(&self) -> u32 {
- let data = self.buffer.as_ref();
- NetworkEndian::read_u24(&data[1..4]) & 0x000fffff
- }
- /// Return the payload length field.
- #[inline]
- pub fn payload_len(&self) -> u16 {
- let data = self.buffer.as_ref();
- NetworkEndian::read_u16(&data[field::LENGTH])
- }
- /// Return the payload length added to the known header length.
- #[inline]
- pub fn total_len(&self) -> usize {
- self.header_len() + self.payload_len() as usize
- }
- /// Return the next header field.
- #[inline]
- pub fn next_header(&self) -> Protocol {
- let data = self.buffer.as_ref();
- Protocol::from(data[field::NXT_HDR])
- }
- /// Return the hop limit field.
- #[inline]
- pub fn hop_limit(&self) -> u8 {
- let data = self.buffer.as_ref();
- data[field::HOP_LIMIT]
- }
- /// Return the source address field.
- #[inline]
- pub fn src_addr(&self) -> Address {
- let data = self.buffer.as_ref();
- Address::from_bytes(&data[field::SRC_ADDR])
- }
- /// Return the destination address field.
- #[inline]
- pub fn dst_addr(&self) -> Address {
- let data = self.buffer.as_ref();
- Address::from_bytes(&data[field::DST_ADDR])
- }
- }
- impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
- /// Return a pointer to the payload.
- #[inline]
- pub fn payload(&self) -> &'a [u8] {
- let data = self.buffer.as_ref();
- let range = self.header_len()..self.total_len();
- &data[range]
- }
- }
- impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
- /// Set the version field.
- #[inline]
- pub fn set_version(&mut self, value: u8) {
- let data = self.buffer.as_mut();
- // Make sure to retain the lower order bits which contain
- // the higher order bits of the traffic class
- data[0] = (data[0] & 0x0f) | ((value & 0x0f) << 4);
- }
- /// Set the traffic class field.
- #[inline]
- pub fn set_traffic_class(&mut self, value: u8) {
- let data = self.buffer.as_mut();
- // Put the higher order 4-bits of value in the lower order
- // 4-bits of the first byte
- data[0] = (data[0] & 0xf0) | ((value & 0xf0) >> 4);
- // Put the lower order 4-bits of value in the higher order
- // 4-bits of the second byte
- data[1] = (data[1] & 0x0f) | ((value & 0x0f) << 4);
- }
- /// Set the flow label field.
- #[inline]
- pub fn set_flow_label(&mut self, value: u32) {
- let data = self.buffer.as_mut();
- // Retain the lower order 4-bits of the traffic class
- let raw = (((data[1] & 0xf0) as u32) << 16) | (value & 0x0fffff);
- NetworkEndian::write_u24(&mut data[1..4], raw);
- }
- /// Set the payload length field.
- #[inline]
- pub fn set_payload_len(&mut self, value: u16) {
- let data = self.buffer.as_mut();
- NetworkEndian::write_u16(&mut data[field::LENGTH], value);
- }
- /// Set the next header field.
- #[inline]
- pub fn set_next_header(&mut self, value: Protocol) {
- let data = self.buffer.as_mut();
- data[field::NXT_HDR] = value.into();
- }
- /// Set the hop limit field.
- #[inline]
- pub fn set_hop_limit(&mut self, value: u8) {
- let data = self.buffer.as_mut();
- data[field::HOP_LIMIT] = value;
- }
- /// Set the source address field.
- #[inline]
- pub fn set_src_addr(&mut self, value: Address) {
- let data = self.buffer.as_mut();
- data[field::SRC_ADDR].copy_from_slice(&value.octets());
- }
- /// Set the destination address field.
- #[inline]
- pub fn set_dst_addr(&mut self, value: Address) {
- let data = self.buffer.as_mut();
- data[field::DST_ADDR].copy_from_slice(&value.octets());
- }
- /// Return a mutable pointer to the payload.
- #[inline]
- pub fn payload_mut(&mut self) -> &mut [u8] {
- let range = self.header_len()..self.total_len();
- let data = self.buffer.as_mut();
- &mut data[range]
- }
- }
- impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match Repr::parse(self) {
- Ok(repr) => write!(f, "{repr}"),
- Err(err) => {
- write!(f, "IPv6 ({err})")?;
- Ok(())
- }
- }
- }
- }
- impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
- fn as_ref(&self) -> &[u8] {
- self.buffer.as_ref()
- }
- }
- /// A high-level representation of an Internet Protocol version 6 packet header.
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- pub struct Repr {
- /// IPv6 address of the source node.
- pub src_addr: Address,
- /// IPv6 address of the destination node.
- pub dst_addr: Address,
- /// Protocol contained in the next header.
- pub next_header: Protocol,
- /// Length of the payload including the extension headers.
- pub payload_len: usize,
- /// The 8-bit hop limit field.
- pub hop_limit: u8,
- }
- impl Repr {
- /// Parse an Internet Protocol version 6 packet and return a high-level representation.
- pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr> {
- // Ensure basic accessors will work
- packet.check_len()?;
- if packet.version() != 6 {
- return Err(Error);
- }
- Ok(Repr {
- src_addr: packet.src_addr(),
- dst_addr: packet.dst_addr(),
- next_header: packet.next_header(),
- payload_len: packet.payload_len() as usize,
- hop_limit: packet.hop_limit(),
- })
- }
- /// Return the length of a header that will be emitted from this high-level representation.
- pub const fn buffer_len(&self) -> usize {
- // This function is not strictly necessary, but it can make client code more readable.
- field::DST_ADDR.end
- }
- /// Emit a high-level representation into an Internet Protocol version 6 packet.
- pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
- // Make no assumptions about the original state of the packet buffer.
- // Make sure to set every byte.
- packet.set_version(6);
- packet.set_traffic_class(0);
- packet.set_flow_label(0);
- packet.set_payload_len(self.payload_len as u16);
- packet.set_hop_limit(self.hop_limit);
- packet.set_next_header(self.next_header);
- packet.set_src_addr(self.src_addr);
- packet.set_dst_addr(self.dst_addr);
- }
- }
- impl fmt::Display for Repr {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
- self.src_addr, self.dst_addr, self.next_header, self.hop_limit
- )
- }
- }
- #[cfg(feature = "defmt")]
- impl defmt::Format for Repr {
- fn format(&self, fmt: defmt::Formatter) {
- defmt::write!(
- fmt,
- "IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
- self.src_addr,
- self.dst_addr,
- self.next_header,
- self.hop_limit
- )
- }
- }
- use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
- // TODO: This is very similar to the implementation for IPv4. Make
- // a way to have less copy and pasted code here.
- impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
- fn pretty_print(
- buffer: &dyn AsRef<[u8]>,
- f: &mut fmt::Formatter,
- indent: &mut PrettyIndent,
- ) -> fmt::Result {
- let (ip_repr, payload) = match Packet::new_checked(buffer) {
- Err(err) => return write!(f, "{indent}({err})"),
- Ok(ip_packet) => match Repr::parse(&ip_packet) {
- Err(_) => return Ok(()),
- Ok(ip_repr) => {
- write!(f, "{indent}{ip_repr}")?;
- (ip_repr, ip_packet.payload())
- }
- },
- };
- pretty_print_ip_payload(f, indent, ip_repr, payload)
- }
- }
- #[cfg(test)]
- pub(crate) mod test {
- use super::*;
- use crate::wire::pretty_print::PrettyPrinter;
- #[allow(unused)]
- pub(crate) const MOCK_IP_ADDR_1: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
- #[allow(unused)]
- pub(crate) const MOCK_IP_ADDR_2: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2);
- #[allow(unused)]
- pub(crate) const MOCK_IP_ADDR_3: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 3);
- #[allow(unused)]
- pub(crate) const MOCK_IP_ADDR_4: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 4);
- #[allow(unused)]
- pub(crate) const MOCK_UNSPECIFIED: Address = Address::UNSPECIFIED;
- const LINK_LOCAL_ADDR: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
- const UNIQUE_LOCAL_ADDR: Address = Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1);
- const GLOBAL_UNICAST_ADDR: Address = Address::new(0x2001, 0xdb8, 0x3, 0, 0, 0, 0, 1);
- #[test]
- fn test_basic_multicast() {
- assert!(!LINK_LOCAL_ALL_ROUTERS.is_unspecified());
- assert!(LINK_LOCAL_ALL_ROUTERS.is_multicast());
- assert!(!LINK_LOCAL_ALL_ROUTERS.is_link_local());
- assert!(!LINK_LOCAL_ALL_ROUTERS.is_loopback());
- assert!(!LINK_LOCAL_ALL_ROUTERS.x_is_unique_local());
- assert!(!LINK_LOCAL_ALL_ROUTERS.is_global_unicast());
- assert!(!LINK_LOCAL_ALL_NODES.is_unspecified());
- assert!(LINK_LOCAL_ALL_NODES.is_multicast());
- assert!(!LINK_LOCAL_ALL_NODES.is_link_local());
- assert!(!LINK_LOCAL_ALL_NODES.is_loopback());
- assert!(!LINK_LOCAL_ALL_NODES.x_is_unique_local());
- assert!(!LINK_LOCAL_ALL_NODES.is_global_unicast());
- }
- #[test]
- fn test_basic_link_local() {
- assert!(!LINK_LOCAL_ADDR.is_unspecified());
- assert!(!LINK_LOCAL_ADDR.is_multicast());
- assert!(LINK_LOCAL_ADDR.is_link_local());
- assert!(!LINK_LOCAL_ADDR.is_loopback());
- assert!(!LINK_LOCAL_ADDR.x_is_unique_local());
- assert!(!LINK_LOCAL_ADDR.is_global_unicast());
- }
- #[test]
- fn test_basic_loopback() {
- assert!(!Address::LOCALHOST.is_unspecified());
- assert!(!Address::LOCALHOST.is_multicast());
- assert!(!Address::LOCALHOST.is_link_local());
- assert!(Address::LOCALHOST.is_loopback());
- assert!(!Address::LOCALHOST.x_is_unique_local());
- assert!(!Address::LOCALHOST.is_global_unicast());
- }
- #[test]
- fn test_unique_local() {
- assert!(!UNIQUE_LOCAL_ADDR.is_unspecified());
- assert!(!UNIQUE_LOCAL_ADDR.is_multicast());
- assert!(!UNIQUE_LOCAL_ADDR.is_link_local());
- assert!(!UNIQUE_LOCAL_ADDR.is_loopback());
- assert!(UNIQUE_LOCAL_ADDR.x_is_unique_local());
- assert!(!UNIQUE_LOCAL_ADDR.is_global_unicast());
- }
- #[test]
- fn test_global_unicast() {
- assert!(!GLOBAL_UNICAST_ADDR.is_unspecified());
- assert!(!GLOBAL_UNICAST_ADDR.is_multicast());
- assert!(!GLOBAL_UNICAST_ADDR.is_link_local());
- assert!(!GLOBAL_UNICAST_ADDR.is_loopback());
- assert!(!GLOBAL_UNICAST_ADDR.x_is_unique_local());
- assert!(GLOBAL_UNICAST_ADDR.is_global_unicast());
- }
- #[test]
- fn test_mask() {
- let addr = Address::new(0x0123, 0x4567, 0x89ab, 0, 0, 0, 0, 1);
- assert_eq!(
- addr.mask(11),
- [0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
- );
- assert_eq!(
- addr.mask(15),
- [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
- );
- assert_eq!(
- addr.mask(26),
- [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
- );
- assert_eq!(
- addr.mask(128),
- [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
- );
- assert_eq!(
- addr.mask(127),
- [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
- );
- }
- #[test]
- fn test_cidr() {
- // fe80::1/56
- // 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
- // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- let cidr = Cidr::new(LINK_LOCAL_ADDR, 56);
- let inside_subnet = [
- // fe80::2
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02,
- ],
- // fe80::1122:3344:5566:7788
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
- 0x77, 0x88,
- ],
- // fe80::ff00:0:0:0
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00,
- ],
- // fe80::ff
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xff,
- ],
- ];
- let outside_subnet = [
- // fe80:0:0:101::1
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01,
- ],
- // ::1
- [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01,
- ],
- // ff02::1
- [
- 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01,
- ],
- // ff02::2
- [
- 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02,
- ],
- ];
- let subnets = [
- // fe80::ffff:ffff:ffff:ffff/65
- (
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff,
- ],
- 65,
- ),
- // fe80::1/128
- (
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01,
- ],
- 128,
- ),
- // fe80::1234:5678/96
- (
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
- 0x34, 0x56, 0x78,
- ],
- 96,
- ),
- ];
- let not_subnets = [
- // fe80::101:ffff:ffff:ffff:ffff/55
- (
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff,
- ],
- 55,
- ),
- // fe80::101:ffff:ffff:ffff:ffff/56
- (
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff,
- ],
- 56,
- ),
- // fe80::101:ffff:ffff:ffff:ffff/57
- (
- [
- 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff,
- ],
- 57,
- ),
- // ::1/128
- (
- [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01,
- ],
- 128,
- ),
- ];
- for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
- assert!(cidr.contains_addr(&addr));
- }
- for addr in outside_subnet.iter().map(|a| Address::from_bytes(a)) {
- assert!(!cidr.contains_addr(&addr));
- }
- for subnet in subnets.iter().map(|&(a, p)| Cidr::new(Address::from(a), p)) {
- assert!(cidr.contains_subnet(&subnet));
- }
- for subnet in not_subnets
- .iter()
- .map(|&(a, p)| Cidr::new(Address::from(a), p))
- {
- assert!(!cidr.contains_subnet(&subnet));
- }
- let cidr_without_prefix = Cidr::new(LINK_LOCAL_ADDR, 0);
- assert!(cidr_without_prefix.contains_addr(&Address::LOCALHOST));
- }
- #[test]
- #[should_panic(expected = "length")]
- fn test_from_bytes_too_long() {
- let _ = Address::from_bytes(&[0u8; 15]);
- }
- #[test]
- fn test_scope() {
- use super::*;
- assert_eq!(
- Address::new(0xff01, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
- MulticastScope::InterfaceLocal
- );
- assert_eq!(
- Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
- MulticastScope::LinkLocal
- );
- assert_eq!(
- Address::new(0xff03, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
- MulticastScope::Unknown
- );
- assert_eq!(
- Address::new(0xff04, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
- MulticastScope::AdminLocal
- );
- assert_eq!(
- Address::new(0xff05, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
- MulticastScope::SiteLocal
- );
- assert_eq!(
- Address::new(0xff08, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
- MulticastScope::OrganizationLocal
- );
- assert_eq!(
- Address::new(0xff0e, 0, 0, 0, 0, 0, 0, 1).x_multicast_scope(),
- MulticastScope::Global
- );
- assert_eq!(
- LINK_LOCAL_ALL_NODES.x_multicast_scope(),
- MulticastScope::LinkLocal
- );
- // For source address selection, unicast addresses also have a scope:
- assert_eq!(
- LINK_LOCAL_ADDR.x_multicast_scope(),
- MulticastScope::LinkLocal
- );
- assert_eq!(
- GLOBAL_UNICAST_ADDR.x_multicast_scope(),
- MulticastScope::Global
- );
- assert_eq!(
- UNIQUE_LOCAL_ADDR.x_multicast_scope(),
- MulticastScope::Global
- );
- }
- static REPR_PACKET_BYTES: [u8; 52] = [
- 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00,
- 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
- ];
- static REPR_PAYLOAD_BYTES: [u8; 12] = [
- 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
- ];
- const fn packet_repr() -> Repr {
- Repr {
- src_addr: Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
- dst_addr: LINK_LOCAL_ALL_NODES,
- next_header: Protocol::Udp,
- payload_len: 12,
- hop_limit: 64,
- }
- }
- #[test]
- fn test_packet_deconstruction() {
- let packet = Packet::new_unchecked(&REPR_PACKET_BYTES[..]);
- assert_eq!(packet.check_len(), Ok(()));
- assert_eq!(packet.version(), 6);
- assert_eq!(packet.traffic_class(), 0);
- assert_eq!(packet.flow_label(), 0);
- assert_eq!(packet.total_len(), 0x34);
- assert_eq!(packet.payload_len() as usize, REPR_PAYLOAD_BYTES.len());
- assert_eq!(packet.next_header(), Protocol::Udp);
- assert_eq!(packet.hop_limit(), 0x40);
- assert_eq!(packet.src_addr(), Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
- assert_eq!(packet.dst_addr(), LINK_LOCAL_ALL_NODES);
- assert_eq!(packet.payload(), &REPR_PAYLOAD_BYTES[..]);
- }
- #[test]
- fn test_packet_construction() {
- let mut bytes = [0xff; 52];
- let mut packet = Packet::new_unchecked(&mut bytes[..]);
- // Version, Traffic Class, and Flow Label are not
- // byte aligned. make sure the setters and getters
- // do not interfere with each other.
- packet.set_version(6);
- assert_eq!(packet.version(), 6);
- packet.set_traffic_class(0x99);
- assert_eq!(packet.version(), 6);
- assert_eq!(packet.traffic_class(), 0x99);
- packet.set_flow_label(0x54321);
- assert_eq!(packet.traffic_class(), 0x99);
- assert_eq!(packet.flow_label(), 0x54321);
- packet.set_payload_len(0xc);
- packet.set_next_header(Protocol::Udp);
- packet.set_hop_limit(0xfe);
- packet.set_src_addr(LINK_LOCAL_ALL_ROUTERS);
- packet.set_dst_addr(LINK_LOCAL_ALL_NODES);
- packet
- .payload_mut()
- .copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
- let mut expected_bytes = [
- 0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ];
- let start = expected_bytes.len() - REPR_PAYLOAD_BYTES.len();
- expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
- assert_eq!(packet.check_len(), Ok(()));
- assert_eq!(&*packet.into_inner(), &expected_bytes[..]);
- }
- #[test]
- fn test_overlong() {
- let mut bytes = vec![];
- bytes.extend(&REPR_PACKET_BYTES[..]);
- bytes.push(0);
- assert_eq!(
- Packet::new_unchecked(&bytes).payload().len(),
- REPR_PAYLOAD_BYTES.len()
- );
- assert_eq!(
- Packet::new_unchecked(&mut bytes).payload_mut().len(),
- REPR_PAYLOAD_BYTES.len()
- );
- }
- #[test]
- fn test_total_len_overflow() {
- let mut bytes = vec![];
- bytes.extend(&REPR_PACKET_BYTES[..]);
- Packet::new_unchecked(&mut bytes).set_payload_len(0x80);
- assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error);
- }
- #[test]
- fn test_repr_parse_valid() {
- let packet = Packet::new_unchecked(&REPR_PACKET_BYTES[..]);
- let repr = Repr::parse(&packet).unwrap();
- assert_eq!(repr, packet_repr());
- }
- #[test]
- fn test_repr_parse_bad_version() {
- let mut bytes = [0; 40];
- let mut packet = Packet::new_unchecked(&mut bytes[..]);
- packet.set_version(4);
- packet.set_payload_len(0);
- let packet = Packet::new_unchecked(&*packet.into_inner());
- assert_eq!(Repr::parse(&packet), Err(Error));
- }
- #[test]
- fn test_repr_parse_smaller_than_header() {
- let mut bytes = [0; 40];
- let mut packet = Packet::new_unchecked(&mut bytes[..]);
- packet.set_version(6);
- packet.set_payload_len(39);
- let packet = Packet::new_unchecked(&*packet.into_inner());
- assert_eq!(Repr::parse(&packet), Err(Error));
- }
- #[test]
- fn test_repr_parse_smaller_than_payload() {
- let mut bytes = [0; 40];
- let mut packet = Packet::new_unchecked(&mut bytes[..]);
- packet.set_version(6);
- packet.set_payload_len(1);
- let packet = Packet::new_unchecked(&*packet.into_inner());
- assert_eq!(Repr::parse(&packet), Err(Error));
- }
- #[test]
- fn test_basic_repr_emit() {
- let repr = packet_repr();
- let mut bytes = vec![0xff; repr.buffer_len() + REPR_PAYLOAD_BYTES.len()];
- let mut packet = Packet::new_unchecked(&mut bytes);
- repr.emit(&mut packet);
- packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES);
- assert_eq!(&*packet.into_inner(), &REPR_PACKET_BYTES[..]);
- }
- #[test]
- fn test_pretty_print() {
- assert_eq!(
- format!(
- "{}",
- PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])
- ),
- "\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4"
- );
- }
- }
|