123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206 |
- use core::fmt;
- use core::convert::From;
- use crate::{Error, Result};
- use crate::phy::ChecksumCapabilities;
- #[cfg(feature = "proto-ipv4")]
- use crate::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr, Ipv4Cidr};
- #[cfg(feature = "proto-ipv6")]
- use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
- /// Internet protocol version.
- #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
- pub enum Version {
- Unspecified,
- #[cfg(feature = "proto-ipv4")]
- Ipv4,
- #[cfg(feature = "proto-ipv6")]
- Ipv6,
- #[doc(hidden)]
- __Nonexhaustive,
- }
- 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::Unrecognized)`.
- pub 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::Unrecognized)
- }
- }
- }
- impl fmt::Display for Version {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Version::Unspecified => write!(f, "IPv?"),
- #[cfg(feature = "proto-ipv4")]
- Version::Ipv4 => write!(f, "IPv4"),
- #[cfg(feature = "proto-ipv6")]
- Version::Ipv6 => write!(f, "IPv6"),
- Version::__Nonexhaustive => unreachable!()
- }
- }
- }
- 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,
- 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::Icmpv6 => write!(f, "ICMPv6"),
- Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"),
- Protocol::Ipv6Opts => write!(f, "IPv6-Opts"),
- Protocol::Unknown(id) => write!(f, "0x{:02x}", id)
- }
- }
- }
- /// An internetworking address.
- #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
- pub enum Address {
- /// An unspecified address.
- /// May be used as a placeholder for storage where the address is not assigned yet.
- Unspecified,
- /// An IPv4 address.
- #[cfg(feature = "proto-ipv4")]
- Ipv4(Ipv4Address),
- /// An IPv6 address.
- #[cfg(feature = "proto-ipv6")]
- Ipv6(Ipv6Address),
- #[doc(hidden)]
- __Nonexhaustive
- }
- impl Address {
- /// Create an address wrapping an IPv4 address with the given octets.
- #[cfg(feature = "proto-ipv4")]
- pub 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")]
- pub 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 an address as a sequence of octets, in big-endian.
- pub fn as_bytes(&self) -> &[u8] {
- match *self {
- Address::Unspecified => &[],
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(ref addr) => addr.as_bytes(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(ref addr) => addr.as_bytes(),
- Address::__Nonexhaustive => unreachable!()
- }
- }
- /// Query whether the address is a valid unicast address.
- pub fn is_unicast(&self) -> bool {
- match *self {
- Address::Unspecified => false,
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.is_unicast(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => addr.is_unicast(),
- Address::__Nonexhaustive => unreachable!()
- }
- }
- /// Query whether the address is a valid multicast address.
- pub fn is_multicast(&self) -> bool {
- match *self {
- Address::Unspecified => false,
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.is_multicast(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => addr.is_multicast(),
- Address::__Nonexhaustive => unreachable!()
- }
- }
- /// Query whether the address is the broadcast address.
- pub fn is_broadcast(&self) -> bool {
- match *self {
- Address::Unspecified => false,
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.is_broadcast(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(_) => false,
- Address::__Nonexhaustive => unreachable!()
- }
- }
- /// Query whether the address falls into the "unspecified" range.
- pub fn is_unspecified(&self) -> bool {
- match *self {
- Address::Unspecified => true,
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => addr.is_unspecified(),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => addr.is_unspecified(),
- Address::__Nonexhaustive => unreachable!()
- }
- }
- /// Return an unspecified address that has the same IP version as `self`.
- pub fn to_unspecified(&self) -> Address {
- match *self {
- Address::Unspecified => Address::Unspecified,
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED),
- Address::__Nonexhaustive => unreachable!()
- }
- }
- /// 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 to_prefix_len(&self) -> Option<u8> {
- let mut ones = true;
- let mut prefix_len = 0;
- for byte in self.as_bytes() {
- 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)
- }
- }
- #[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
- impl From<::std::net::IpAddr> for Address {
- fn from(x: ::std::net::IpAddr) -> Address {
- match x {
- ::std::net::IpAddr::V4(ipv4) => Address::Ipv4(ipv4.into()),
- ::std::net::IpAddr::V6(ipv6) => Address::Ipv6(ipv6.into()),
- }
- }
- }
- #[cfg(all(feature = "std", feature = "proto-ipv4"))]
- impl From<::std::net::Ipv4Addr> for Address {
- fn from(ipv4: ::std::net::Ipv4Addr) -> Address {
- Address::Ipv4(ipv4.into())
- }
- }
- #[cfg(all(feature = "std", feature = "proto-ipv6"))]
- impl From<::std::net::Ipv6Addr> for Address {
- fn from(ipv6: ::std::net::Ipv6Addr) -> Address {
- Address::Ipv6(ipv6.into())
- }
- }
- impl Default for Address {
- fn default() -> Address {
- Address::Unspecified
- }
- }
- #[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 {
- Address::Unspecified => write!(f, "*"),
- #[cfg(feature = "proto-ipv4")]
- Address::Ipv4(addr) => write!(f, "{}", addr),
- #[cfg(feature = "proto-ipv6")]
- Address::Ipv6(addr) => write!(f, "{}", addr),
- Address::__Nonexhaustive => unreachable!()
- }
- }
- }
- /// 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),
- #[doc(hidden)]
- __Nonexhaustive,
- }
- impl Cidr {
- /// Create a CIDR block from the given address and prefix length.
- ///
- /// # Panics
- /// This function panics if the given address is unspecified, or
- /// 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)),
- Address::Unspecified =>
- panic!("a CIDR block cannot be based on an unspecified address"),
- Address::__Nonexhaustive =>
- unreachable!()
- }
- }
- /// Return the IP address of this CIDR block.
- pub 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()),
- Cidr::__Nonexhaustive => unreachable!()
- }
- }
- /// Return the prefix length of this CIDR block.
- pub 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(),
- Cidr::__Nonexhaustive => unreachable!()
- }
- }
- /// 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(ref cidr), &Address::Ipv4(ref addr)) =>
- cidr.contains_addr(addr),
- #[cfg(feature = "proto-ipv6")]
- (&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) =>
- cidr.contains_addr(addr),
- #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
- (&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) =>
- false,
- (_, &Address::Unspecified) =>
- // a fully unspecified address covers both IPv4 and IPv6,
- // and no CIDR block can do that.
- false,
- (&Cidr::__Nonexhaustive, _) |
- (_, &Address::__Nonexhaustive) =>
- unreachable!()
- }
- }
- /// 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(ref cidr), &Cidr::Ipv4(ref other)) =>
- cidr.contains_subnet(other),
- #[cfg(feature = "proto-ipv6")]
- (&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) =>
- cidr.contains_subnet(other),
- #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
- (&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) =>
- false,
- (&Cidr::__Nonexhaustive, _) |
- (_, &Cidr::__Nonexhaustive) =>
- unreachable!()
- }
- }
- }
- #[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),
- Cidr::__Nonexhaustive => unreachable!()
- }
- }
- }
- /// An internet endpoint address.
- ///
- /// 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 Endpoint {
- pub addr: Address,
- pub port: u16
- }
- impl Endpoint {
- /// An endpoint with unspecified address and port.
- pub const UNSPECIFIED: Endpoint = Endpoint { addr: Address::Unspecified, port: 0 };
- /// Create an endpoint address from given address and port.
- pub fn new(addr: Address, port: u16) -> Endpoint {
- Endpoint { addr, port }
- }
- /// Query whether the endpoint has a specified address and port.
- pub fn is_specified(&self) -> bool {
- !self.addr.is_unspecified() && self.port != 0
- }
- }
- #[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
- impl From<::std::net::SocketAddr> for Endpoint {
- fn from(x: ::std::net::SocketAddr) -> Endpoint {
- Endpoint {
- addr: x.ip().into(),
- port: x.port(),
- }
- }
- }
- #[cfg(all(feature = "std", feature = "proto-ipv4"))]
- impl From<::std::net::SocketAddrV4> for Endpoint {
- fn from(x: ::std::net::SocketAddrV4) -> Endpoint {
- Endpoint {
- addr: x.ip().clone().into(),
- port: x.port(),
- }
- }
- }
- #[cfg(all(feature = "std", feature = "proto-ipv6"))]
- impl From<::std::net::SocketAddrV6> for Endpoint {
- fn from(x: ::std::net::SocketAddrV6) -> Endpoint {
- Endpoint {
- addr: x.ip().clone().into(),
- port: x.port(),
- }
- }
- }
- impl fmt::Display for Endpoint {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}:{}", self.addr, self.port)
- }
- }
- impl From<u16> for Endpoint {
- fn from(port: u16) -> Endpoint {
- Endpoint { addr: Address::Unspecified, port }
- }
- }
- impl<T: Into<Address>> From<(T, u16)> for Endpoint {
- fn from((addr, port): (T, u16)) -> Endpoint {
- Endpoint { addr: addr.into(), port }
- }
- }
- /// An IP packet representation.
- ///
- /// This enum abstracts the various versions of IP packets. It either contains a concrete
- /// high-level representation for some IP protocol version, or an unspecified representation,
- /// which permits the `IpAddress::Unspecified` addresses.
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub enum Repr {
- Unspecified {
- src_addr: Address,
- dst_addr: Address,
- protocol: Protocol,
- payload_len: usize,
- hop_limit: u8
- },
- #[cfg(feature = "proto-ipv4")]
- Ipv4(Ipv4Repr),
- #[cfg(feature = "proto-ipv6")]
- Ipv6(Ipv6Repr),
- #[doc(hidden)]
- __Nonexhaustive
- }
- #[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 {
- /// Return the protocol version.
- pub fn version(&self) -> Version {
- match *self {
- Repr::Unspecified { .. } => Version::Unspecified,
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(_) => Version::Ipv4,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(_) => Version::Ipv6,
- Repr::__Nonexhaustive => unreachable!()
- }
- }
- /// Return the source address.
- pub fn src_addr(&self) -> Address {
- match *self {
- Repr::Unspecified { src_addr, .. } => src_addr,
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
- Repr::__Nonexhaustive => unreachable!()
- }
- }
- /// Return the destination address.
- pub fn dst_addr(&self) -> Address {
- match *self {
- Repr::Unspecified { dst_addr, .. } => dst_addr,
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
- Repr::__Nonexhaustive => unreachable!()
- }
- }
- /// Return the protocol.
- pub fn protocol(&self) -> Protocol {
- match *self {
- Repr::Unspecified { protocol, .. } => protocol,
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => repr.protocol,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => repr.next_header,
- Repr::__Nonexhaustive => unreachable!()
- }
- }
- /// Return the payload length.
- pub fn payload_len(&self) -> usize {
- match *self {
- Repr::Unspecified { payload_len, .. } => payload_len,
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) => repr.payload_len,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) => repr.payload_len,
- Repr::__Nonexhaustive => unreachable!()
- }
- }
- /// Set the payload length.
- pub fn set_payload_len(&mut self, length: usize) {
- match *self {
- Repr::Unspecified { ref mut payload_len, .. } =>
- *payload_len = length,
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(Ipv4Repr { ref mut payload_len, .. }) =>
- *payload_len = length,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(Ipv6Repr { ref mut payload_len, .. }) =>
- *payload_len = length,
- Repr::__Nonexhaustive => unreachable!()
- }
- }
- /// Return the TTL value.
- pub fn hop_limit(&self) -> u8 {
- match *self {
- Repr::Unspecified { hop_limit, .. } => hop_limit,
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(Ipv6Repr { hop_limit, ..}) => hop_limit,
- Repr::__Nonexhaustive => unreachable!()
- }
- }
- /// Convert an unspecified representation into a concrete one, or return
- /// `Err(Error::Unaddressable)` if not possible.
- ///
- /// # Panics
- /// This function panics if source and destination addresses belong to different families,
- /// or the destination address is unspecified, since this indicates a logic error.
- pub fn lower(&self, fallback_src_addrs: &[Cidr]) -> Result<Repr> {
- macro_rules! resolve_unspecified {
- ($reprty:path, $ipty:path, $iprepr:expr, $fallbacks:expr) => {
- if $iprepr.src_addr.is_unspecified() {
- for cidr in $fallbacks {
- match cidr.address() {
- $ipty(addr) => {
- $iprepr.src_addr = addr;
- return Ok($reprty($iprepr));
- },
- _ => ()
- }
- }
- Err(Error::Unaddressable)
- } else {
- Ok($reprty($iprepr))
- }
- }
- }
- match self {
- #[cfg(feature = "proto-ipv4")]
- &Repr::Unspecified {
- src_addr: src_addr @ Address::Unspecified,
- dst_addr: Address::Ipv4(dst_addr),
- protocol, payload_len, hop_limit
- } |
- &Repr::Unspecified {
- src_addr: src_addr @ Address::Ipv4(_),
- dst_addr: Address::Ipv4(dst_addr),
- protocol, payload_len, hop_limit
- } if src_addr.is_unspecified() => {
- let mut src_addr = if let Address::Ipv4(src_ipv4_addr) = src_addr {
- Some(src_ipv4_addr)
- } else {
- None
- };
- for cidr in fallback_src_addrs {
- if let Address::Ipv4(addr) = cidr.address() {
- src_addr = Some(addr);
- break;
- }
- }
- Ok(Repr::Ipv4(Ipv4Repr {
- src_addr: src_addr.ok_or(Error::Unaddressable)?,
- dst_addr, protocol, payload_len, hop_limit
- }))
- }
- #[cfg(feature = "proto-ipv6")]
- &Repr::Unspecified {
- src_addr: src_addr @ Address::Unspecified,
- dst_addr: Address::Ipv6(dst_addr),
- protocol, payload_len, hop_limit
- } |
- &Repr::Unspecified {
- src_addr: src_addr @ Address::Ipv6(_),
- dst_addr: Address::Ipv6(dst_addr),
- protocol, payload_len, hop_limit
- } if src_addr.is_unspecified() => {
- let mut src_addr = if let Address::Ipv6(src_ipv6_addr) = src_addr {
- Some(src_ipv6_addr)
- } else {
- None
- };
- for cidr in fallback_src_addrs {
- if let Address::Ipv6(addr) = cidr.address() {
- src_addr = Some(addr);
- break;
- }
- }
- Ok(Repr::Ipv6(Ipv6Repr {
- src_addr: src_addr.ok_or(Error::Unaddressable)?,
- next_header: protocol,
- dst_addr, payload_len, hop_limit
- }))
- }
- #[cfg(feature = "proto-ipv4")]
- &Repr::Unspecified {
- src_addr: Address::Ipv4(src_addr),
- dst_addr: Address::Ipv4(dst_addr),
- protocol, payload_len, hop_limit
- } => {
- Ok(Repr::Ipv4(Ipv4Repr {
- src_addr: src_addr,
- dst_addr: dst_addr,
- protocol: protocol,
- payload_len: payload_len, hop_limit
- }))
- }
- #[cfg(feature = "proto-ipv6")]
- &Repr::Unspecified {
- src_addr: Address::Ipv6(src_addr),
- dst_addr: Address::Ipv6(dst_addr),
- protocol, payload_len, hop_limit
- } => {
- Ok(Repr::Ipv6(Ipv6Repr {
- src_addr: src_addr,
- dst_addr: dst_addr,
- next_header: protocol,
- payload_len: payload_len,
- hop_limit: hop_limit
- }))
- }
- #[cfg(feature = "proto-ipv4")]
- &Repr::Ipv4(mut repr) =>
- resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs),
- #[cfg(feature = "proto-ipv6")]
- &Repr::Ipv6(mut repr) =>
- resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs),
- &Repr::Unspecified { .. } =>
- panic!("source and destination IP address families do not match"),
- &Repr::__Nonexhaustive => unreachable!()
- }
- }
- /// Return the length of a header that will be emitted from this high-level representation.
- ///
- /// # Panics
- /// This function panics if invoked on an unspecified representation.
- pub fn buffer_len(&self) -> usize {
- match *self {
- Repr::Unspecified { .. } =>
- panic!("unspecified IP representation"),
- #[cfg(feature = "proto-ipv4")]
- Repr::Ipv4(repr) =>
- repr.buffer_len(),
- #[cfg(feature = "proto-ipv6")]
- Repr::Ipv6(repr) =>
- repr.buffer_len(),
- Repr::__Nonexhaustive =>
- unreachable!()
- }
- }
- /// Emit this high-level representation into a buffer.
- ///
- /// # Panics
- /// This function panics if invoked on an unspecified representation.
- pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, buffer: T, _checksum_caps: &ChecksumCapabilities) {
- match *self {
- Repr::Unspecified { .. } =>
- panic!("unspecified IP representation"),
- #[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)),
- Repr::__Nonexhaustive =>
- unreachable!()
- }
- }
- /// 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()`.
- ///
- /// # Panics
- /// This function panics if invoked on an unspecified representation.
- pub fn total_len(&self) -> usize {
- self.buffer_len() + self.payload_len()
- }
- }
- pub mod checksum {
- use byteorder::{ByteOrder, NetworkEndian};
- use super::*;
- 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)
- }
- /// Compute an IP pseudo header checksum.
- pub fn pseudo_header(src_addr: &Address, dst_addr: &Address,
- protocol: Protocol, length: u32) -> u16 {
- match (src_addr, dst_addr) {
- #[cfg(feature = "proto-ipv4")]
- (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
- let mut proto_len = [0u8; 4];
- proto_len[1] = protocol.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")]
- (&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => {
- let mut proto_len = [0u8; 8];
- proto_len[7] = protocol.into();
- NetworkEndian::write_u32(&mut proto_len[0..4], length);
- combine(&[
- data(src_addr.as_bytes()),
- data(dst_addr.as_bytes()),
- data(&proto_len[..])
- ])
- }
- _ => panic!("Unexpected pseudo header addresses: {}, {}",
- src_addr, dst_addr)
- }
- }
- // 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 crate::wire::Icmpv4Packet;
- #[cfg(feature = "proto-ipv4")]
- use super::pretty_print::PrettyPrint;
- use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
- use crate::wire::ip::checksum::format_checksum;
- let checksum_caps = ChecksumCapabilities::ignored();
- let repr = ip_repr.into();
- match repr.protocol() {
- #[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, "{}{}", indent, udp_repr)?;
- 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)]
- #[cfg(feature = "proto-ipv6")]
- pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 1]));
- #[cfg(feature = "proto-ipv6")]
- pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 2]));
- #[cfg(feature = "proto-ipv6")]
- pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 3]));
- #[cfg(feature = "proto-ipv6")]
- pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 4]));
- #[cfg(feature = "proto-ipv6")]
- pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv6(Ipv6Address::UNSPECIFIED);
- #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
- pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
- #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
- pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 2]));
- #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
- pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 3]));
- #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
- pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 4]));
- #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
- pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED);
- use super::*;
- use crate::wire::{IpAddress, IpProtocol,IpCidr};
- #[cfg(feature = "proto-ipv4")]
- use crate::wire::{Ipv4Address, Ipv4Repr};
- macro_rules! generate_common_tests {
- ($name:ident, $repr:ident, $ip_repr:path, $ip_addr:path,
- $addr_from:path, $nxthdr:ident, $bytes_a:expr, $bytes_b:expr,
- $unspecified:expr) => {
- mod $name {
- use super::*;
- #[test]
- fn test_ip_repr_lower() {
- let ip_addr_a = $addr_from(&$bytes_a);
- let ip_addr_b = $addr_from(&$bytes_b);
- let proto = IpProtocol::Icmp;
- let payload_len = 10;
- assert_eq!(
- Repr::Unspecified{
- src_addr: $ip_addr(ip_addr_a),
- dst_addr: $ip_addr(ip_addr_b),
- protocol: proto,
- hop_limit: 0x2a,
- payload_len,
- }.lower(&[]),
- Ok($ip_repr($repr{
- src_addr: ip_addr_a,
- dst_addr: ip_addr_b,
- $nxthdr: proto,
- hop_limit: 0x2a,
- payload_len
- }))
- );
- assert_eq!(
- Repr::Unspecified{
- src_addr: IpAddress::Unspecified,
- dst_addr: $ip_addr(ip_addr_b),
- protocol: proto,
- hop_limit: 64,
- payload_len
- }.lower(&[]),
- Err(Error::Unaddressable)
- );
- assert_eq!(
- Repr::Unspecified{
- src_addr: IpAddress::Unspecified,
- dst_addr: $ip_addr(ip_addr_b),
- protocol: proto,
- hop_limit: 64,
- payload_len
- }.lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
- Ok($ip_repr($repr{
- src_addr: ip_addr_a,
- dst_addr: ip_addr_b,
- $nxthdr: proto,
- hop_limit: 64,
- payload_len
- }))
- );
- assert_eq!(
- Repr::Unspecified{
- src_addr: $ip_addr($unspecified),
- dst_addr: $ip_addr(ip_addr_b),
- protocol: proto,
- hop_limit: 64,
- payload_len
- }.lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
- Ok($ip_repr($repr{
- src_addr: ip_addr_a,
- dst_addr: ip_addr_b,
- $nxthdr: proto,
- hop_limit: 64,
- payload_len
- }))
- );
- assert_eq!(
- Repr::Unspecified{
- src_addr: $ip_addr($unspecified),
- dst_addr: $ip_addr(ip_addr_b),
- protocol: proto,
- hop_limit: 64,
- payload_len
- }.lower(&[]),
- Ok($ip_repr($repr{
- src_addr: $unspecified,
- dst_addr: ip_addr_b,
- $nxthdr: proto,
- hop_limit: 64,
- payload_len
- }))
- );
- assert_eq!(
- $ip_repr($repr{
- src_addr: ip_addr_a,
- dst_addr: ip_addr_b,
- $nxthdr: proto,
- hop_limit: 255,
- payload_len
- }).lower(&[]),
- Ok($ip_repr($repr{
- src_addr: ip_addr_a,
- dst_addr: ip_addr_b,
- $nxthdr: proto,
- hop_limit: 255,
- payload_len
- }))
- );
- assert_eq!(
- $ip_repr($repr{
- src_addr: $unspecified,
- dst_addr: ip_addr_b,
- $nxthdr: proto,
- hop_limit: 255,
- payload_len
- }).lower(&[]),
- Err(Error::Unaddressable)
- );
- assert_eq!(
- $ip_repr($repr{
- src_addr: $unspecified,
- dst_addr: ip_addr_b,
- $nxthdr: proto,
- hop_limit: 64,
- payload_len
- }).lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
- Ok($ip_repr($repr{
- src_addr: ip_addr_a,
- dst_addr: ip_addr_b,
- $nxthdr: proto,
- hop_limit: 64,
- payload_len
- }))
- );
- }
- }
- };
- (ipv4 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
- generate_common_tests!(ipv4, Ipv4Repr, Repr::Ipv4, IpAddress::Ipv4,
- Ipv4Address::from_bytes, protocol, $addr_bytes_a,
- $addr_bytes_b, Ipv4Address::UNSPECIFIED);
- };
- (ipv6 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
- generate_common_tests!(ipv6, Ipv6Repr, Repr::Ipv6, IpAddress::Ipv6,
- Ipv6Address::from_bytes, next_header, $addr_bytes_a,
- $addr_bytes_b, Ipv6Address::UNSPECIFIED);
- }
- }
- #[cfg(feature = "proto-ipv4")]
- generate_common_tests!(ipv4
- [1, 2, 3, 4],
- [5, 6, 7, 8]);
- #[cfg(feature = "proto-ipv6")]
- generate_common_tests!(ipv6
- [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
- [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
- #[test]
- #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
- #[should_panic(expected = "source and destination IP address families do not match")]
- fn test_lower_between_families() {
- Repr::Unspecified {
- src_addr: Address::Ipv6(Ipv6Address::UNSPECIFIED),
- dst_addr: Address::Ipv4(Ipv4Address::UNSPECIFIED),
- protocol: IpProtocol::Icmpv6,
- hop_limit: 0xff,
- payload_len: 0
- }.lower(&[]);
- }
- #[test]
- fn endpoint_unspecified() {
- assert!(!Endpoint::UNSPECIFIED.is_specified());
- }
- #[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().to_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));
- }
- #[cfg(feature = "proto-ipv4")]
- fn to_prefix_len_ipv4_error() {
- assert_eq!(None, IpAddress::from(Ipv4Address::new(255,255,255,1)).to_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().to_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));
- }
- #[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)).to_prefix_len());
- }
- }
|