123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979 |
- use core::convert::From;
- use core::fmt;
- use super::{Error, Result};
- use crate::phy::ChecksumCapabilities;
- #[cfg(feature = "proto-ipv4")]
- use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr};
- #[cfg(feature = "proto-ipv6")]
- use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
- /// Internet protocol version.
- #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub enum Version {
- #[cfg(feature = "proto-ipv4")]
- Ipv4,
- #[cfg(feature = "proto-ipv6")]
- Ipv6,
- }
- impl Version {
- /// Return the version of an IP packet stored in the provided buffer.
- ///
- /// This function never returns `Ok(IpVersion::Unspecified)`; instead,
- /// unknown versions result in `Err(Error)`.
- pub const fn of_packet(data: &[u8]) -> Result<Version> {
- match data[0] >> 4 {
- #[cfg(feature = "proto-ipv4")]
- 4 => Ok(Version::Ipv4),
- #[cfg(feature = "proto-ipv6")]
- 6 => Ok(Version::Ipv6),
- _ => Err(Error),
- }
- }
- }
- impl fmt::Display for Version {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Version::Ipv4 => write!(f, "IPv4"),
- #[cfg(feature = "proto-ipv6")]
- Version::Ipv6 => write!(f, "IPv6"),
- }
- }
- }
- enum_with_unknown! {
- /// IP datagram encapsulated protocol.
- pub enum Protocol(u8) {
- HopByHop = 0x00,
- Icmp = 0x01,
- Igmp = 0x02,
- Tcp = 0x06,
- Udp = 0x11,
- Ipv6Route = 0x2b,
- Ipv6Frag = 0x2c,
- IpSecEsp = 0x32,
- IpSecAh = 0x33,
- Icmpv6 = 0x3a,
- Ipv6NoNxt = 0x3b,
- Ipv6Opts = 0x3c
- }
- }
- impl fmt::Display for Protocol {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Protocol::HopByHop => write!(f, "Hop-by-Hop"),
- Protocol::Icmp => write!(f, "ICMP"),
- Protocol::Igmp => write!(f, "IGMP"),
- Protocol::Tcp => write!(f, "TCP"),
- Protocol::Udp => write!(f, "UDP"),
- Protocol::Ipv6Route => write!(f, "IPv6-Route"),
- Protocol::Ipv6Frag => write!(f, "IPv6-Frag"),
- Protocol::IpSecEsp => write!(f, "IPsec-ESP"),
- Protocol::IpSecAh => write!(f, "IPsec-AH"),
- Protocol::Icmpv6 => write!(f, "ICMPv6"),
- Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"),
- Protocol::Ipv6Opts => write!(f, "IPv6-Opts"),
- Protocol::Unknown(id) => write!(f, "0x{id:02x}"),
- }
- }
- }
- /// An internetworking address.
- #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
- pub enum Address {
- /// An IPv4 address.
- #[cfg(feature = "proto-ipv4")]
- Ipv4(Ipv4Address),
- /// An IPv6 address.
- #[cfg(feature = "proto-ipv6")]
- Ipv6(Ipv6Address),
- }
- impl Address {
- /// Create an address wrapping an IPv4 address with the given octets.
- #[cfg(feature = "proto-ipv4")]
- pub const fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address {
- Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3))
- }
- /// Create an address wrapping an IPv6 address with the given octets.
- #[cfg(feature = "proto-ipv6")]
- #[allow(clippy::too_many_arguments)]
- pub const fn v6(
- a0: u16,
- a1: u16,
- a2: u16,
- a3: u16,
- a4: u16,
- a5: u16,
- a6: u16,
- a7: u16,
- ) -> Address {
- Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
- }
- /// Return the protocol version.
- pub const fn version(&self) -> Version {
- match self {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(_) => Version::Ipv4,
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(_) => Version::Ipv6,
- }
- }
- /// Return an address as a sequence of octets, in big-endian.
- pub const fn as_bytes(&self) -> &[u8] {
- match self {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.as_bytes(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => addr.as_bytes(),
- }
- }
- /// Query whether the address is a valid unicast address.
- pub fn is_unicast(&self) -> bool {
- match self {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.is_unicast(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => addr.is_unicast(),
- }
- }
- /// Query whether the address is a valid multicast address.
- pub const fn is_multicast(&self) -> bool {
- match self {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.is_multicast(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => addr.is_multicast(),
- }
- }
- /// Query whether the address is the broadcast address.
- pub fn is_broadcast(&self) -> bool {
- match self {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.is_broadcast(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(_) => false,
- }
- }
- /// Query whether the address falls into the "unspecified" range.
- pub fn is_unspecified(&self) -> bool {
- match self {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.is_unspecified(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => addr.is_unspecified(),
- }
- }
- /// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`,
- /// where `prefix_len` is the number of leading zeroes. Return `None` otherwise.
- pub fn prefix_len(&self) -> Option<u8> {
- match self {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.prefix_len(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => addr.prefix_len(),
- }
- }
- }
- #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
- impl From<::core::net::IpAddr> for Address {
- fn from(x: ::core::net::IpAddr) -> Address {
- match x {
- ::core::net::IpAddr::V4(ipv4) => Address::Ipv4(ipv4.into()),
- ::core::net::IpAddr::V6(ipv6) => Address::Ipv6(ipv6.into()),
- }
- }
- }
- impl From<Address> for ::core::net::IpAddr {
- fn from(x: Address) -> ::core::net::IpAddr {
- match x {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(ipv4) => ::core::net::IpAddr::V4(ipv4.into()),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(ipv6) => ::core::net::IpAddr::V6(ipv6.into()),
- }
- }
- }
- #[cfg(feature = "proto-ipv4")]
- impl From<::core::net::Ipv4Addr> for Address {
- fn from(ipv4: ::core::net::Ipv4Addr) -> Address {
- Address::Ipv4(ipv4.into())
- }
- }
- #[cfg(feature = "proto-ipv6")]
- impl From<::core::net::Ipv6Addr> for Address {
- fn from(ipv6: ::core::net::Ipv6Addr) -> Address {
- Address::Ipv6(ipv6.into())
- }
- }
- #[cfg(feature = "proto-ipv4")]
- impl From<Ipv4Address> for Address {
- fn from(addr: Ipv4Address) -> Self {
- Address::Ipv4(addr)
- }
- }
- #[cfg(feature = "proto-ipv6")]
- impl From<Ipv6Address> for Address {
- fn from(addr: Ipv6Address) -> Self {
- Address::Ipv6(addr)
- }
- }
- impl fmt::Display for Address {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => write!(f, "{addr}"),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => write!(f, "{addr}"),
- }
- }
- }
- #[cfg(feature = "defmt")]
- impl defmt::Format for Address {
- fn format(&self, f: defmt::Formatter) {
- match self {
- #[cfg(feature = "proto-ipv4")]
- &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr),
- #[cfg(feature = "proto-ipv6")]
- &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr),
- }
- }
- }
- /// A specification of a CIDR block, containing an address and a variable-length
- /// subnet masking prefix length.
- #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
- pub enum Cidr {
- #[cfg(feature = "proto-ipv4")]
- Ipv4(Ipv4Cidr),
- #[cfg(feature = "proto-ipv6")]
- Ipv6(Ipv6Cidr),
- }
- impl Cidr {
- /// Create a CIDR block from the given address and prefix length.
- ///
- /// # Panics
- /// This function panics if the given prefix length is invalid for the given address.
- pub fn new(addr: Address, prefix_len: u8) -> Cidr {
- match addr {
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
- }
- }
- /// Return the IP address of this CIDR block.
- pub const fn address(&self) -> Address {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
- #[cfg(feature = "proto-ipv6")]
- Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()),
- }
- }
- /// Return the prefix length of this CIDR block.
- pub const fn prefix_len(&self) -> u8 {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Cidr::Ipv4(cidr) => cidr.prefix_len(),
- #[cfg(feature = "proto-ipv6")]
- Cidr::Ipv6(cidr) => cidr.prefix_len(),
- }
- }
- /// Query whether the subnetwork described by this CIDR block contains
- /// the given address.
- pub fn contains_addr(&self, addr: &Address) -> bool {
- match (self, addr) {
- #[cfg(feature = "proto-ipv4")]
- (Cidr::Ipv4(cidr), Address::Ipv4(addr)) => cidr.contains_addr(addr),
- #[cfg(feature = "proto-ipv6")]
- (Cidr::Ipv6(cidr), Address::Ipv6(addr)) => cidr.contains_addr(addr),
- #[allow(unreachable_patterns)]
- _ => false,
- }
- }
- /// Query whether the subnetwork described by this CIDR block contains
- /// the subnetwork described by the given CIDR block.
- pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
- match (self, subnet) {
- #[cfg(feature = "proto-ipv4")]
- (Cidr::Ipv4(cidr), Cidr::Ipv4(other)) => cidr.contains_subnet(other),
- #[cfg(feature = "proto-ipv6")]
- (Cidr::Ipv6(cidr), Cidr::Ipv6(other)) => cidr.contains_subnet(other),
- #[allow(unreachable_patterns)]
- _ => false,
- }
- }
- }
- #[cfg(feature = "proto-ipv4")]
- impl From<Ipv4Cidr> for Cidr {
- fn from(addr: Ipv4Cidr) -> Self {
- Cidr::Ipv4(addr)
- }
- }
- #[cfg(feature = "proto-ipv6")]
- impl From<Ipv6Cidr> for Cidr {
- fn from(addr: Ipv6Cidr) -> Self {
- Cidr::Ipv6(addr)
- }
- }
- impl fmt::Display for Cidr {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Cidr::Ipv4(cidr) => write!(f, "{cidr}"),
- #[cfg(feature = "proto-ipv6")]
- Cidr::Ipv6(cidr) => write!(f, "{cidr}"),
- }
- }
- }
- #[cfg(feature = "defmt")]
- impl defmt::Format for Cidr {
- fn format(&self, f: defmt::Formatter) {
- match self {
- #[cfg(feature = "proto-ipv4")]
- &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr),
- #[cfg(feature = "proto-ipv6")]
- &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr),
- }
- }
- }
- /// An internet endpoint address.
- ///
- /// `Endpoint` always fully specifies both the address and the port.
- ///
- /// See also ['ListenEndpoint'], which allows not specifying the address
- /// in order to listen on a given port on any address.
- #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
- pub struct Endpoint {
- pub addr: Address,
- pub port: u16,
- }
- impl Endpoint {
- /// Create an endpoint address from given address and port.
- pub const fn new(addr: Address, port: u16) -> Endpoint {
- Endpoint { addr, port }
- }
- }
- #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
- impl From<::core::net::SocketAddr> for Endpoint {
- fn from(x: ::core::net::SocketAddr) -> Endpoint {
- Endpoint {
- addr: x.ip().into(),
- port: x.port(),
- }
- }
- }
- #[cfg(feature = "proto-ipv4")]
- impl From<::core::net::SocketAddrV4> for Endpoint {
- fn from(x: ::core::net::SocketAddrV4) -> Endpoint {
- Endpoint {
- addr: (*x.ip()).into(),
- port: x.port(),
- }
- }
- }
- #[cfg(feature = "proto-ipv6")]
- impl From<::core::net::SocketAddrV6> for Endpoint {
- fn from(x: ::core::net::SocketAddrV6) -> Endpoint {
- Endpoint {
- addr: (*x.ip()).into(),
- port: x.port(),
- }
- }
- }
- impl fmt::Display for Endpoint {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}:{}", self.addr, self.port)
- }
- }
- #[cfg(feature = "defmt")]
- impl defmt::Format for Endpoint {
- fn format(&self, f: defmt::Formatter) {
- defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
- }
- }
- impl<T: Into<Address>> From<(T, u16)> for Endpoint {
- fn from((addr, port): (T, u16)) -> Endpoint {
- Endpoint {
- addr: addr.into(),
- port,
- }
- }
- }
- /// An internet endpoint address for listening.
- ///
- /// In contrast with [`Endpoint`], `ListenEndpoint` allows not specifying the address,
- /// in order to listen on a given port at all our addresses.
- ///
- /// An endpoint can be constructed from a port, in which case the address is unspecified.
- #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
- pub struct ListenEndpoint {
- pub addr: Option<Address>,
- pub port: u16,
- }
- impl ListenEndpoint {
- /// Query whether the endpoint has a specified address and port.
- pub const fn is_specified(&self) -> bool {
- self.addr.is_some() && self.port != 0
- }
- }
- #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
- impl From<::core::net::SocketAddr> for ListenEndpoint {
- fn from(x: ::core::net::SocketAddr) -> ListenEndpoint {
- ListenEndpoint {
- addr: Some(x.ip().into()),
- port: x.port(),
- }
- }
- }
- #[cfg(feature = "proto-ipv4")]
- impl From<::core::net::SocketAddrV4> for ListenEndpoint {
- fn from(x: ::core::net::SocketAddrV4) -> ListenEndpoint {
- ListenEndpoint {
- addr: Some((*x.ip()).into()),
- port: x.port(),
- }
- }
- }
- #[cfg(feature = "proto-ipv6")]
- impl From<::core::net::SocketAddrV6> for ListenEndpoint {
- fn from(x: ::core::net::SocketAddrV6) -> ListenEndpoint {
- ListenEndpoint {
- addr: Some((*x.ip()).into()),
- port: x.port(),
- }
- }
- }
- impl fmt::Display for ListenEndpoint {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if let Some(addr) = self.addr {
- write!(f, "{}:{}", addr, self.port)
- } else {
- write!(f, "*:{}", self.port)
- }
- }
- }
- #[cfg(feature = "defmt")]
- impl defmt::Format for ListenEndpoint {
- fn format(&self, f: defmt::Formatter) {
- defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
- }
- }
- impl From<u16> for ListenEndpoint {
- fn from(port: u16) -> ListenEndpoint {
- ListenEndpoint { addr: None, port }
- }
- }
- impl From<Endpoint> for ListenEndpoint {
- fn from(endpoint: Endpoint) -> ListenEndpoint {
- ListenEndpoint {
- addr: Some(endpoint.addr),
- port: endpoint.port,
- }
- }
- }
- impl<T: Into<Address>> From<(T, u16)> for ListenEndpoint {
- fn from((addr, port): (T, u16)) -> ListenEndpoint {
- ListenEndpoint {
- addr: Some(addr.into()),
- port,
- }
- }
- }
- /// An IP packet representation.
- ///
- /// This enum abstracts the various versions of IP packets. It either contains an IPv4
- /// or IPv6 concrete high-level representation.
- #[derive(Debug, Clone, PartialEq, Eq)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub enum Repr {
- #[cfg(feature = "proto-ipv4")]
- Ipv4(Ipv4Repr),
- #[cfg(feature = "proto-ipv6")]
- Ipv6(Ipv6Repr),
- }
- #[cfg(feature = "proto-ipv4")]
- impl From<Ipv4Repr> for Repr {
- fn from(repr: Ipv4Repr) -> Repr {
- Repr::Ipv4(repr)
- }
- }
- #[cfg(feature = "proto-ipv6")]
- impl From<Ipv6Repr> for Repr {
- fn from(repr: Ipv6Repr) -> Repr {
- Repr::Ipv6(repr)
- }
- }
- impl Repr {
- /// Create a new IpRepr, choosing the right IP version for the src/dst addrs.
- ///
- /// # Panics
- ///
- /// Panics if `src_addr` and `dst_addr` are different IP version.
- pub fn new(
- src_addr: Address,
- dst_addr: Address,
- next_header: Protocol,
- payload_len: usize,
- hop_limit: u8,
- ) -> Self {
- match (src_addr, dst_addr) {
- #[cfg(feature = "proto-ipv4")]
- (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => Self::Ipv4(Ipv4Repr {
- src_addr,
- dst_addr,
- next_header,
- payload_len,
- hop_limit,
- }),
- #[cfg(feature = "proto-ipv6")]
- (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => Self::Ipv6(Ipv6Repr {
- src_addr,
- dst_addr,
- next_header,
- payload_len,
- hop_limit,
- }),
- #[allow(unreachable_patterns)]
- _ => panic!("IP version mismatch: src={src_addr:?} dst={dst_addr:?}"),
- }
- }
- /// Return the protocol version.
- pub const fn version(&self) -> Version {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(_) => Version::Ipv4,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(_) => Version::Ipv6,
- }
- }
- /// Return the source address.
- pub const fn src_addr(&self) -> Address {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
- }
- }
- /// Return the destination address.
- pub const fn dst_addr(&self) -> Address {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
- }
- }
- /// Return the next header (protocol).
- pub const fn next_header(&self) -> Protocol {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => repr.next_header,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => repr.next_header,
- }
- }
- /// Return the payload length.
- pub const fn payload_len(&self) -> usize {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => repr.payload_len,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => repr.payload_len,
- }
- }
- /// Set the payload length.
- pub fn set_payload_len(&mut self, length: usize) {
- match self {
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(Ipv4Repr { payload_len, .. }) => *payload_len = length,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(Ipv6Repr { payload_len, .. }) => *payload_len = length,
- }
- }
- /// Return the TTL value.
- pub const fn hop_limit(&self) -> u8 {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
- }
- }
- /// Return the length of a header that will be emitted from this high-level representation.
- pub const fn header_len(&self) -> usize {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => repr.buffer_len(),
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => repr.buffer_len(),
- }
- }
- /// Emit this high-level representation into a buffer.
- pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
- &self,
- buffer: T,
- _checksum_caps: &ChecksumCapabilities,
- ) {
- match *self {
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
- }
- }
- /// Return the total length of a packet that will be emitted from this
- /// high-level representation.
- ///
- /// This is the same as `repr.buffer_len() + repr.payload_len()`.
- pub const fn buffer_len(&self) -> usize {
- self.header_len() + self.payload_len()
- }
- }
- pub mod checksum {
- use byteorder::{ByteOrder, NetworkEndian};
- use super::*;
- const fn propagate_carries(word: u32) -> u16 {
- let sum = (word >> 16) + (word & 0xffff);
- ((sum >> 16) as u16) + (sum as u16)
- }
- /// Compute an RFC 1071 compliant checksum (without the final complement).
- pub fn data(mut data: &[u8]) -> u16 {
- let mut accum = 0;
- // For each 32-byte chunk...
- const CHUNK_SIZE: usize = 32;
- while data.len() >= CHUNK_SIZE {
- let mut d = &data[..CHUNK_SIZE];
- // ... take by 2 bytes and sum them.
- while d.len() >= 2 {
- accum += NetworkEndian::read_u16(d) as u32;
- d = &d[2..];
- }
- data = &data[CHUNK_SIZE..];
- }
- // Sum the rest that does not fit the last 32-byte chunk,
- // taking by 2 bytes.
- while data.len() >= 2 {
- accum += NetworkEndian::read_u16(data) as u32;
- data = &data[2..];
- }
- // Add the last remaining odd byte, if any.
- if let Some(&value) = data.first() {
- accum += (value as u32) << 8;
- }
- propagate_carries(accum)
- }
- /// Combine several RFC 1071 compliant checksums.
- pub fn combine(checksums: &[u16]) -> u16 {
- let mut accum: u32 = 0;
- for &word in checksums {
- accum += word as u32;
- }
- propagate_carries(accum)
- }
- #[cfg(feature = "proto-ipv4")]
- pub fn pseudo_header_v4(
- src_addr: &Ipv4Address,
- dst_addr: &Ipv4Address,
- next_header: Protocol,
- length: u32,
- ) -> u16 {
- let mut proto_len = [0u8; 4];
- proto_len[1] = next_header.into();
- NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
- combine(&[
- data(src_addr.as_bytes()),
- data(dst_addr.as_bytes()),
- data(&proto_len[..]),
- ])
- }
- #[cfg(feature = "proto-ipv6")]
- pub fn pseudo_header_v6(
- src_addr: &Ipv6Address,
- dst_addr: &Ipv6Address,
- next_header: Protocol,
- length: u32,
- ) -> u16 {
- let mut proto_len = [0u8; 4];
- proto_len[1] = next_header.into();
- NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
- combine(&[
- data(src_addr.as_bytes()),
- data(dst_addr.as_bytes()),
- data(&proto_len[..]),
- ])
- }
- pub fn pseudo_header(
- src_addr: &Address,
- dst_addr: &Address,
- next_header: Protocol,
- length: u32,
- ) -> u16 {
- match (src_addr, dst_addr) {
- #[cfg(feature = "proto-ipv4")]
- (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => {
- pseudo_header_v4(src_addr, dst_addr, next_header, length)
- }
- #[cfg(feature = "proto-ipv6")]
- (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => {
- pseudo_header_v6(src_addr, dst_addr, next_header, length)
- }
- #[allow(unreachable_patterns)]
- _ => unreachable!(),
- }
- }
- // We use this in pretty printer implementations.
- pub(crate) fn format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result {
- if !correct {
- write!(f, " (checksum incorrect)")
- } else {
- Ok(())
- }
- }
- }
- use crate::wire::pretty_print::PrettyIndent;
- pub fn pretty_print_ip_payload<T: Into<Repr>>(
- f: &mut fmt::Formatter,
- indent: &mut PrettyIndent,
- ip_repr: T,
- payload: &[u8],
- ) -> fmt::Result {
- #[cfg(feature = "proto-ipv4")]
- use super::pretty_print::PrettyPrint;
- use crate::wire::ip::checksum::format_checksum;
- #[cfg(feature = "proto-ipv4")]
- use crate::wire::Icmpv4Packet;
- use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
- let checksum_caps = ChecksumCapabilities::ignored();
- let repr = ip_repr.into();
- match repr.next_header() {
- #[cfg(feature = "proto-ipv4")]
- Protocol::Icmp => {
- indent.increase(f)?;
- Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
- }
- Protocol::Udp => {
- indent.increase(f)?;
- match UdpPacket::<&[u8]>::new_checked(payload) {
- Err(err) => write!(f, "{indent}({err})"),
- Ok(udp_packet) => {
- match UdpRepr::parse(
- &udp_packet,
- &repr.src_addr(),
- &repr.dst_addr(),
- &checksum_caps,
- ) {
- Err(err) => write!(f, "{indent}{udp_packet} ({err})"),
- Ok(udp_repr) => {
- write!(
- f,
- "{}{} len={}",
- indent,
- udp_repr,
- udp_packet.payload().len()
- )?;
- let valid =
- udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
- format_checksum(f, valid)
- }
- }
- }
- }
- }
- Protocol::Tcp => {
- indent.increase(f)?;
- match TcpPacket::<&[u8]>::new_checked(payload) {
- Err(err) => write!(f, "{indent}({err})"),
- Ok(tcp_packet) => {
- match TcpRepr::parse(
- &tcp_packet,
- &repr.src_addr(),
- &repr.dst_addr(),
- &checksum_caps,
- ) {
- Err(err) => write!(f, "{indent}{tcp_packet} ({err})"),
- Ok(tcp_repr) => {
- write!(f, "{indent}{tcp_repr}")?;
- let valid =
- tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
- format_checksum(f, valid)
- }
- }
- }
- }
- }
- _ => Ok(()),
- }
- }
- #[cfg(test)]
- pub(crate) mod test {
- #![allow(unused)]
- use super::*;
- use crate::wire::{IpAddress, IpCidr, IpProtocol};
- #[cfg(feature = "proto-ipv4")]
- use crate::wire::{Ipv4Address, Ipv4Repr};
- #[test]
- #[cfg(feature = "proto-ipv4")]
- fn to_prefix_len_ipv4() {
- fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
- assert_eq!(Some(prefix_len), mask.into().prefix_len());
- }
- test_eq(0, Ipv4Address::new(0, 0, 0, 0));
- test_eq(1, Ipv4Address::new(128, 0, 0, 0));
- test_eq(2, Ipv4Address::new(192, 0, 0, 0));
- test_eq(3, Ipv4Address::new(224, 0, 0, 0));
- test_eq(4, Ipv4Address::new(240, 0, 0, 0));
- test_eq(5, Ipv4Address::new(248, 0, 0, 0));
- test_eq(6, Ipv4Address::new(252, 0, 0, 0));
- test_eq(7, Ipv4Address::new(254, 0, 0, 0));
- test_eq(8, Ipv4Address::new(255, 0, 0, 0));
- test_eq(9, Ipv4Address::new(255, 128, 0, 0));
- test_eq(10, Ipv4Address::new(255, 192, 0, 0));
- test_eq(11, Ipv4Address::new(255, 224, 0, 0));
- test_eq(12, Ipv4Address::new(255, 240, 0, 0));
- test_eq(13, Ipv4Address::new(255, 248, 0, 0));
- test_eq(14, Ipv4Address::new(255, 252, 0, 0));
- test_eq(15, Ipv4Address::new(255, 254, 0, 0));
- test_eq(16, Ipv4Address::new(255, 255, 0, 0));
- test_eq(17, Ipv4Address::new(255, 255, 128, 0));
- test_eq(18, Ipv4Address::new(255, 255, 192, 0));
- test_eq(19, Ipv4Address::new(255, 255, 224, 0));
- test_eq(20, Ipv4Address::new(255, 255, 240, 0));
- test_eq(21, Ipv4Address::new(255, 255, 248, 0));
- test_eq(22, Ipv4Address::new(255, 255, 252, 0));
- test_eq(23, Ipv4Address::new(255, 255, 254, 0));
- test_eq(24, Ipv4Address::new(255, 255, 255, 0));
- test_eq(25, Ipv4Address::new(255, 255, 255, 128));
- test_eq(26, Ipv4Address::new(255, 255, 255, 192));
- test_eq(27, Ipv4Address::new(255, 255, 255, 224));
- test_eq(28, Ipv4Address::new(255, 255, 255, 240));
- test_eq(29, Ipv4Address::new(255, 255, 255, 248));
- test_eq(30, Ipv4Address::new(255, 255, 255, 252));
- test_eq(31, Ipv4Address::new(255, 255, 255, 254));
- test_eq(32, Ipv4Address::new(255, 255, 255, 255));
- }
- #[test]
- #[cfg(feature = "proto-ipv4")]
- fn to_prefix_len_ipv4_error() {
- assert_eq!(
- None,
- IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
- );
- }
- #[test]
- #[cfg(feature = "proto-ipv6")]
- fn to_prefix_len_ipv6() {
- fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
- assert_eq!(Some(prefix_len), mask.into().prefix_len());
- }
- test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
- test_eq(
- 128,
- Ipv6Address::new(
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
- ),
- );
- }
- #[test]
- #[cfg(feature = "proto-ipv6")]
- fn to_prefix_len_ipv6_error() {
- assert_eq!(
- None,
- IpAddress::from(Ipv6Address::new(
- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
- ))
- .prefix_len()
- );
- }
- }
|