123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358 |
- //! Implementation of [RFC 6282] which specifies a compression format for IPv6 datagrams over
- //! IEEE802.154-based networks.
- //!
- //! [RFC 6282]: https://datatracker.ietf.org/doc/html/rfc6282
- use core::ops::Deref;
- use super::{Error, Result};
- use crate::wire::ieee802154::Address as LlAddress;
- use crate::wire::ipv6;
- use crate::wire::IpProtocol;
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct AddressContext<'a>(pub &'a [u8]);
- impl<'a> Deref for AddressContext<'a> {
- type Target = [u8];
- fn deref(&self) -> &Self::Target {
- self.0
- }
- }
- /// The representation of an unresolved address. 6LoWPAN compression of IPv6 addresses can be with
- /// and without context information. The decompression with context information is not yet
- /// implemented.
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub enum UnresolvedAddress<'a> {
- WithoutContext(AddressMode<'a>),
- WithContext((usize, AddressMode<'a>)),
- Reserved,
- }
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub enum AddressMode<'a> {
- /// The full address is carried in-line.
- FullInline(&'a [u8]),
- /// The first 64-bits of the address are elided. The value of those bits
- /// is the link-local prefix padded with zeros. The remaining 64 bits are
- /// carried in-line.
- InLine64bits(&'a [u8]),
- /// The first 112 bits of the address are elided. The value of the first
- /// 64 bits is the link-local prefix padded with zeros. The following 64 bits
- /// are 0000:00ff:fe00:XXXX, where XXXX are the 16 bits carried in-line.
- InLine16bits(&'a [u8]),
- /// The address is fully elided. The first 64 bits of the address are
- /// the link-local prefix padded with zeros. The remaining 64 bits are
- /// computed from the encapsulating header (e.g., 802.15.4 or IPv6 source address)
- /// as specified in Section 3.2.2.
- FullyElided,
- /// The address takes the form ffXX::00XX:XXXX:XXXX
- Multicast48bits(&'a [u8]),
- /// The address takes the form ffXX::00XX:XXXX.
- Multicast32bits(&'a [u8]),
- /// The address takes the form ff02::00XX.
- Multicast8bits(&'a [u8]),
- /// The unspecified address.
- Unspecified,
- NotSupported,
- }
- const LINK_LOCAL_PREFIX: [u8; 2] = [0xfe, 0x80];
- const EUI64_MIDDLE_VALUE: [u8; 2] = [0xff, 0xfe];
- impl<'a> UnresolvedAddress<'a> {
- pub fn resolve(
- self,
- ll_address: Option<LlAddress>,
- addr_context: &[AddressContext<'_>],
- ) -> Result<ipv6::Address> {
- let mut bytes = [0; 16];
- let copy_context = |index: usize, bytes: &mut [u8]| -> Result<()> {
- if index >= addr_context.len() {
- return Err(Error);
- }
- let context = addr_context[index];
- let len = context.len();
- if len > 8 {
- return Err(Error);
- }
- bytes[..len].copy_from_slice(&context);
- Ok(())
- };
- match self {
- UnresolvedAddress::WithoutContext(mode) => match mode {
- AddressMode::FullInline(addr) => Ok(ipv6::Address::from_bytes(addr)),
- AddressMode::InLine64bits(inline) => {
- bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]);
- bytes[8..].copy_from_slice(inline);
- Ok(ipv6::Address::from_bytes(&bytes[..]))
- }
- AddressMode::InLine16bits(inline) => {
- bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]);
- bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]);
- bytes[14..].copy_from_slice(inline);
- Ok(ipv6::Address::from_bytes(&bytes[..]))
- }
- AddressMode::FullyElided => {
- bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]);
- match ll_address {
- Some(LlAddress::Short(ll)) => {
- bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]);
- bytes[14..].copy_from_slice(&ll);
- }
- Some(addr @ LlAddress::Extended(_)) => match addr.as_eui_64() {
- Some(addr) => bytes[8..].copy_from_slice(&addr),
- None => return Err(Error),
- },
- Some(LlAddress::Absent) => return Err(Error),
- None => return Err(Error),
- }
- Ok(ipv6::Address::from_bytes(&bytes[..]))
- }
- AddressMode::Multicast48bits(inline) => {
- bytes[0] = 0xff;
- bytes[1] = inline[0];
- bytes[11..].copy_from_slice(&inline[1..][..5]);
- Ok(ipv6::Address::from_bytes(&bytes[..]))
- }
- AddressMode::Multicast32bits(inline) => {
- bytes[0] = 0xff;
- bytes[1] = inline[0];
- bytes[13..].copy_from_slice(&inline[1..][..3]);
- Ok(ipv6::Address::from_bytes(&bytes[..]))
- }
- AddressMode::Multicast8bits(inline) => {
- bytes[0] = 0xff;
- bytes[1] = 0x02;
- bytes[15] = inline[0];
- Ok(ipv6::Address::from_bytes(&bytes[..]))
- }
- _ => Err(Error),
- },
- UnresolvedAddress::WithContext(mode) => match mode {
- (_, AddressMode::Unspecified) => Ok(ipv6::Address::UNSPECIFIED),
- (index, AddressMode::InLine64bits(inline)) => {
- copy_context(index, &mut bytes[..])?;
- bytes[16 - inline.len()..].copy_from_slice(inline);
- Ok(ipv6::Address::from_bytes(&bytes[..]))
- }
- (index, AddressMode::InLine16bits(inline)) => {
- copy_context(index, &mut bytes[..])?;
- bytes[16 - inline.len()..].copy_from_slice(inline);
- Ok(ipv6::Address::from_bytes(&bytes[..]))
- }
- (index, AddressMode::FullyElided) => {
- match ll_address {
- Some(LlAddress::Short(ll)) => {
- bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]);
- bytes[14..].copy_from_slice(&ll);
- }
- Some(addr @ LlAddress::Extended(_)) => match addr.as_eui_64() {
- Some(addr) => bytes[8..].copy_from_slice(&addr),
- None => return Err(Error),
- },
- Some(LlAddress::Absent) => return Err(Error),
- None => return Err(Error),
- }
- copy_context(index, &mut bytes[..])?;
- Ok(ipv6::Address::from_bytes(&bytes[..]))
- }
- _ => Err(Error),
- },
- UnresolvedAddress::Reserved => Err(Error),
- }
- }
- }
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub enum SixlowpanPacket {
- FragmentHeader,
- IphcHeader,
- }
- const DISPATCH_FIRST_FRAGMENT_HEADER: u8 = 0b11000;
- const DISPATCH_FRAGMENT_HEADER: u8 = 0b11100;
- const DISPATCH_IPHC_HEADER: u8 = 0b011;
- const DISPATCH_UDP_HEADER: u8 = 0b11110;
- const DISPATCH_EXT_HEADER: u8 = 0b1110;
- impl SixlowpanPacket {
- /// Returns the type of the 6LoWPAN header.
- /// This can either be a fragment header or an IPHC header.
- ///
- /// # Errors
- /// Returns `[Error::Unrecognized]` when neither the Fragment Header dispatch or the IPHC
- /// dispatch is recognized.
- pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result<Self> {
- let raw = buffer.as_ref();
- if raw.is_empty() {
- return Err(Error);
- }
- if raw[0] >> 3 == DISPATCH_FIRST_FRAGMENT_HEADER || raw[0] >> 3 == DISPATCH_FRAGMENT_HEADER
- {
- Ok(Self::FragmentHeader)
- } else if raw[0] >> 5 == DISPATCH_IPHC_HEADER {
- Ok(Self::IphcHeader)
- } else {
- Err(Error)
- }
- }
- }
- pub mod frag {
- //! Implementation of the fragment headers from [RFC 4944 § 5.3].
- //!
- //! [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3
- use super::{DISPATCH_FIRST_FRAGMENT_HEADER, DISPATCH_FRAGMENT_HEADER};
- use crate::{
- wire::{Ieee802154Address, Ieee802154Repr},
- Error, Result,
- };
- use byteorder::{ByteOrder, NetworkEndian};
- /// Key used for identifying all the link fragments that belong to the same packet.
- #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct Key {
- pub(crate) ll_src_addr: Ieee802154Address,
- pub(crate) ll_dst_addr: Ieee802154Address,
- pub(crate) datagram_size: u16,
- pub(crate) datagram_tag: u16,
- }
- /// A read/write wrapper around a 6LoWPAN Fragment header.
- /// [RFC 4944 § 5.3] specifies the format of the header.
- ///
- /// A First Fragment header has the following format:
- /// ```txt
- /// 1 2 3
- /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- /// |1 1 0 0 0| datagram_size | datagram_tag |
- /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- /// ```
- ///
- /// Subsequent fragment headers have the following format:
- /// ```txt
- /// 1 2 3
- /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- /// |1 1 1 0 0| datagram_size | datagram_tag |
- /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- /// |datagram_offset|
- /// +-+-+-+-+-+-+-+-+
- /// ```
- ///
- /// [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3
- #[derive(Debug, Clone)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct Packet<T: AsRef<[u8]>> {
- buffer: T,
- }
- pub const FIRST_FRAGMENT_HEADER_SIZE: usize = 4;
- pub const NEXT_FRAGMENT_HEADER_SIZE: usize = 5;
- mod field {
- use crate::wire::field::*;
- pub const DISPATCH: usize = 0;
- pub const DATAGRAM_SIZE: Field = 0..2;
- pub const DATAGRAM_TAG: Field = 2..4;
- pub const DATAGRAM_OFFSET: usize = 4;
- pub const FIRST_FRAGMENT_REST: Rest = super::FIRST_FRAGMENT_HEADER_SIZE..;
- pub const NEXT_FRAGMENT_REST: Rest = super::NEXT_FRAGMENT_HEADER_SIZE..;
- }
- impl<T: AsRef<[u8]>> Packet<T> {
- /// Input a raw octet buffer with a 6LoWPAN Fragment header structure.
- pub const fn new_unchecked(buffer: T) -> Self {
- Self { buffer }
- }
- /// Shorthand for a combination of [new_unchecked] and [check_len].
- ///
- /// [new_unchecked]: #method.new_unchecked
- /// [check_len]: #method.check_len
- pub fn new_checked(buffer: T) -> Result<Self> {
- let packet = Self::new_unchecked(buffer);
- packet.check_len()?;
- let dispatch = packet.dispatch();
- if dispatch != DISPATCH_FIRST_FRAGMENT_HEADER && dispatch != DISPATCH_FRAGMENT_HEADER {
- return Err(Error::Malformed);
- }
- Ok(packet)
- }
- /// Ensure that no accessor method will panic if called.
- /// Returns `Err(Error::Truncated)` if the buffer is too short.
- pub fn check_len(&self) -> Result<()> {
- let buffer = self.buffer.as_ref();
- if buffer.is_empty() {
- return Err(Error::Truncated);
- }
- match self.dispatch() {
- DISPATCH_FIRST_FRAGMENT_HEADER if buffer.len() >= FIRST_FRAGMENT_HEADER_SIZE => {
- Ok(())
- }
- DISPATCH_FIRST_FRAGMENT_HEADER if buffer.len() < FIRST_FRAGMENT_HEADER_SIZE => {
- Err(Error::Truncated)
- }
- DISPATCH_FRAGMENT_HEADER if buffer.len() >= NEXT_FRAGMENT_HEADER_SIZE => Ok(()),
- DISPATCH_FRAGMENT_HEADER if buffer.len() < NEXT_FRAGMENT_HEADER_SIZE => {
- Err(Error::Truncated)
- }
- _ => Err(Error::Unrecognized),
- }
- }
- /// Consumes the frame, returning the underlying buffer.
- pub fn into_inner(self) -> T {
- self.buffer
- }
- /// Return the dispatch field.
- pub fn dispatch(&self) -> u8 {
- let raw = self.buffer.as_ref();
- raw[field::DISPATCH] >> 3
- }
- /// Return the total datagram size.
- pub fn datagram_size(&self) -> u16 {
- let raw = self.buffer.as_ref();
- NetworkEndian::read_u16(&raw[field::DATAGRAM_SIZE]) & 0b111_1111_1111
- }
- /// Return the datagram tag.
- pub fn datagram_tag(&self) -> u16 {
- let raw = self.buffer.as_ref();
- NetworkEndian::read_u16(&raw[field::DATAGRAM_TAG])
- }
- /// Return the datagram offset.
- pub fn datagram_offset(&self) -> u8 {
- match self.dispatch() {
- DISPATCH_FIRST_FRAGMENT_HEADER => 0,
- DISPATCH_FRAGMENT_HEADER => {
- let raw = self.buffer.as_ref();
- raw[field::DATAGRAM_OFFSET]
- }
- _ => unreachable!(),
- }
- }
- /// Returns `true` when this header is from the first fragment of a link.
- pub fn is_first_fragment(&self) -> bool {
- self.dispatch() == DISPATCH_FIRST_FRAGMENT_HEADER
- }
- /// Returns the key for identifying the packet it belongs to.
- pub fn get_key(&self, ieee802154_repr: &Ieee802154Repr) -> Key {
- Key {
- ll_src_addr: ieee802154_repr.src_addr.unwrap(),
- ll_dst_addr: ieee802154_repr.dst_addr.unwrap(),
- datagram_size: self.datagram_size(),
- datagram_tag: self.datagram_tag(),
- }
- }
- }
- impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
- /// Return the payload.
- pub fn payload(&self) -> &'a [u8] {
- match self.dispatch() {
- DISPATCH_FIRST_FRAGMENT_HEADER => {
- let raw = self.buffer.as_ref();
- &raw[field::FIRST_FRAGMENT_REST]
- }
- DISPATCH_FRAGMENT_HEADER => {
- let raw = self.buffer.as_ref();
- &raw[field::NEXT_FRAGMENT_REST]
- }
- _ => unreachable!(),
- }
- }
- }
- impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
- fn set_dispatch_field(&mut self, value: u8) {
- let raw = self.buffer.as_mut();
- raw[field::DISPATCH] = (raw[field::DISPATCH] & !(0b11111 << 3)) | (value << 3);
- }
- fn set_datagram_size(&mut self, size: u16) {
- let raw = self.buffer.as_mut();
- let mut v = NetworkEndian::read_u16(&raw[field::DATAGRAM_SIZE]);
- v = (v & !0b111_1111_1111) | size;
- NetworkEndian::write_u16(&mut raw[field::DATAGRAM_SIZE], v);
- }
- fn set_datagram_tag(&mut self, tag: u16) {
- let raw = self.buffer.as_mut();
- NetworkEndian::write_u16(&mut raw[field::DATAGRAM_TAG], tag);
- }
- fn set_datagram_offset(&mut self, offset: u8) {
- let raw = self.buffer.as_mut();
- raw[field::DATAGRAM_OFFSET] = offset;
- }
- }
- /// A high-level representation of a 6LoWPAN Fragment header.
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub enum Repr {
- FirstFragment { size: u16, tag: u16 },
- Fragment { size: u16, tag: u16, offset: u8 },
- }
- impl Repr {
- /// Parse a 6LoWPAN Fragment header.
- pub fn parse<T: AsRef<[u8]>>(packet: &Packet<T>) -> Result<Self> {
- let size = packet.datagram_size();
- let tag = packet.datagram_tag();
- match packet.dispatch() {
- DISPATCH_FIRST_FRAGMENT_HEADER => Ok(Self::FirstFragment { size, tag }),
- DISPATCH_FRAGMENT_HEADER => Ok(Self::Fragment {
- size,
- tag,
- offset: packet.datagram_offset(),
- }),
- _ => Err(Error::Malformed),
- }
- }
- /// Returns the length of the Fragment header.
- pub const fn buffer_len(&self) -> usize {
- match self {
- Self::FirstFragment { .. } => field::FIRST_FRAGMENT_REST.start,
- Self::Fragment { .. } => field::NEXT_FRAGMENT_REST.start,
- }
- }
- /// Emit a high-level representation into a 6LoWPAN Fragment header.
- pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
- match self {
- Self::FirstFragment { size, tag } => {
- packet.set_dispatch_field(DISPATCH_FIRST_FRAGMENT_HEADER);
- packet.set_datagram_size(*size);
- packet.set_datagram_tag(*tag);
- }
- Self::Fragment { size, tag, offset } => {
- packet.set_dispatch_field(DISPATCH_FRAGMENT_HEADER);
- packet.set_datagram_size(*size);
- packet.set_datagram_tag(*tag);
- packet.set_datagram_offset(*offset);
- }
- }
- }
- }
- }
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub enum NextHeader {
- Compressed,
- Uncompressed(IpProtocol),
- }
- pub mod iphc {
- //! Implementation of IP Header Compression from [RFC 6282 § 3.1].
- //! It defines the compression of IPv6 headers.
- //!
- //! [RFC 6282 § 3.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1
- use super::{
- AddressContext, AddressMode, Error, NextHeader, Result, UnresolvedAddress,
- DISPATCH_IPHC_HEADER,
- };
- use crate::wire::{ieee802154::Address as LlAddress, ipv6, IpProtocol};
- use byteorder::{ByteOrder, NetworkEndian};
- mod field {
- use crate::wire::field::*;
- pub const IPHC_FIELD: Field = 0..2;
- }
- macro_rules! get_field {
- ($name:ident, $mask:expr, $shift:expr) => {
- fn $name(&self) -> u8 {
- let data = self.buffer.as_ref();
- let raw = NetworkEndian::read_u16(&data[field::IPHC_FIELD]);
- ((raw >> $shift) & $mask) as u8
- }
- };
- }
- macro_rules! set_field {
- ($name:ident, $mask:expr, $shift:expr) => {
- fn $name(&mut self, val: u8) {
- let data = &mut self.buffer.as_mut()[field::IPHC_FIELD];
- let mut raw = NetworkEndian::read_u16(data);
- raw = (raw & !($mask << $shift)) | ((val as u16) << $shift);
- NetworkEndian::write_u16(data, raw);
- }
- };
- }
- /// A read/write wrapper around a 6LoWPAN IPHC header.
- /// [RFC 6282 § 3.1] specifies the format of the header.
- ///
- /// The header always start with the following base format (from [RFC 6282 § 3.1.1]):
- /// ```txt
- /// 0 1
- /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- /// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
- /// | 0 | 1 | 1 | TF |NH | HLIM |CID|SAC| SAM | M |DAC| DAM |
- /// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
- /// ```
- /// With:
- /// - TF: Traffic Class and Flow Label
- /// - NH: Next Header
- /// - HLIM: Hop Limit
- /// - CID: Context Identifier Extension
- /// - SAC: Source Address Compression
- /// - SAM: Source Address Mode
- /// - M: Multicast Compression
- /// - DAC: Destination Address Compression
- /// - DAM: Destination Address Mode
- ///
- /// Depending on the flags in the base format, the following fields are added to the header:
- /// - Traffic Class and Flow Label
- /// - Next Header
- /// - Hop Limit
- /// - IPv6 source address
- /// - IPv6 destinatino address
- ///
- /// [RFC 6282 § 3.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1
- /// [RFC 6282 § 3.1.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1.1
- #[derive(Debug, Clone)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct Packet<T: AsRef<[u8]>> {
- buffer: T,
- }
- impl<T: AsRef<[u8]>> Packet<T> {
- /// Input a raw octet buffer with a 6LoWPAN IPHC header structure.
- pub const fn new_unchecked(buffer: T) -> Self {
- Packet { buffer }
- }
- /// Shorthand for a combination of [new_unchecked] and [check_len].
- ///
- /// [new_unchecked]: #method.new_unchecked
- /// [check_len]: #method.check_len
- pub fn new_checked(buffer: T) -> Result<Self> {
- 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.
- pub fn check_len(&self) -> Result<()> {
- let buffer = self.buffer.as_ref();
- if buffer.len() < 2 {
- return Err(Error);
- }
- let mut offset = self.ip_fields_start()
- + self.traffic_class_size()
- + self.next_header_size()
- + self.hop_limit_size();
- offset += self.src_address_size();
- offset += self.dst_address_size();
- if offset as usize > buffer.len() {
- return Err(Error);
- }
- Ok(())
- }
- /// Consumes the frame, returning the underlying buffer.
- pub fn into_inner(self) -> T {
- self.buffer
- }
- /// Return the Next Header field.
- pub fn next_header(&self) -> NextHeader {
- let nh = self.nh_field();
- if nh == 1 {
- // The next header field is compressed.
- // It is also encoded using LOWPAN_NHC.
- NextHeader::Compressed
- } else {
- // The full 8 bits for Next Header are carried in-line.
- let start = (self.ip_fields_start() + self.traffic_class_size()) as usize;
- let data = self.buffer.as_ref();
- let nh = data[start..start + 1][0];
- NextHeader::Uncompressed(IpProtocol::from(nh))
- }
- }
- /// Return the Hop Limit.
- pub fn hop_limit(&self) -> u8 {
- match self.hlim_field() {
- 0b00 => {
- let start = (self.ip_fields_start()
- + self.traffic_class_size()
- + self.next_header_size()) as usize;
- let data = self.buffer.as_ref();
- data[start..start + 1][0]
- }
- 0b01 => 1,
- 0b10 => 64,
- 0b11 => 255,
- _ => unreachable!(),
- }
- }
- /// Return the Source Context Identifier.
- pub fn src_context_id(&self) -> Option<u8> {
- if self.cid_field() == 1 {
- let data = self.buffer.as_ref();
- Some(data[2] >> 4)
- } else {
- None
- }
- }
- /// Return the Destination Context Identifier.
- pub fn dst_context_id(&self) -> Option<u8> {
- if self.cid_field() == 1 {
- let data = self.buffer.as_ref();
- Some(data[2] & 0x0f)
- } else {
- None
- }
- }
- /// Return the ECN field (when it is inlined).
- pub fn ecn_field(&self) -> Option<u8> {
- match self.tf_field() {
- 0b00 | 0b01 | 0b10 => {
- let start = self.ip_fields_start() as usize;
- Some(self.buffer.as_ref()[start..][0] & 0b1100_0000)
- }
- 0b11 => None,
- _ => unreachable!(),
- }
- }
- /// Return the DSCP field (when it is inlined).
- pub fn dscp_field(&self) -> Option<u8> {
- match self.tf_field() {
- 0b00 | 0b10 => {
- let start = self.ip_fields_start() as usize;
- Some(self.buffer.as_ref()[start..][0] & 0b1111_11)
- }
- 0b01 | 0b11 => None,
- _ => unreachable!(),
- }
- }
- /// Return the flow label field (when it is inlined).
- pub fn flow_label_field(&self) -> Option<u16> {
- match self.tf_field() {
- 0b00 => {
- let start = self.ip_fields_start() as usize;
- Some(NetworkEndian::read_u16(
- &self.buffer.as_ref()[start..][2..4],
- ))
- }
- 0b01 => {
- let start = self.ip_fields_start() as usize;
- Some(NetworkEndian::read_u16(
- &self.buffer.as_ref()[start..][1..3],
- ))
- }
- 0b10 | 0b11 => None,
- _ => unreachable!(),
- }
- }
- /// Return the Source Address.
- pub fn src_addr(&self) -> Result<UnresolvedAddress> {
- let start = (self.ip_fields_start()
- + self.traffic_class_size()
- + self.next_header_size()
- + self.hop_limit_size()) as usize;
- let data = self.buffer.as_ref();
- match (self.sac_field(), self.sam_field()) {
- (0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline(
- &data[start..][..16],
- ))),
- (0, 0b01) => Ok(UnresolvedAddress::WithoutContext(
- AddressMode::InLine64bits(&data[start..][..8]),
- )),
- (0, 0b10) => Ok(UnresolvedAddress::WithoutContext(
- AddressMode::InLine16bits(&data[start..][..2]),
- )),
- (0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)),
- (1, 0b00) => Ok(UnresolvedAddress::WithContext((
- 0,
- AddressMode::Unspecified,
- ))),
- (1, 0b01) => {
- if let Some(id) = self.src_context_id() {
- Ok(UnresolvedAddress::WithContext((
- id as usize,
- AddressMode::InLine64bits(&data[start..][..8]),
- )))
- } else {
- Err(Error)
- }
- }
- (1, 0b10) => {
- if let Some(id) = self.src_context_id() {
- Ok(UnresolvedAddress::WithContext((
- id as usize,
- AddressMode::InLine16bits(&data[start..][..2]),
- )))
- } else {
- Err(Error)
- }
- }
- (1, 0b11) => {
- if let Some(id) = self.src_context_id() {
- Ok(UnresolvedAddress::WithContext((
- id as usize,
- AddressMode::FullyElided,
- )))
- } else {
- Err(Error)
- }
- }
- _ => Err(Error),
- }
- }
- /// Return the Destination Address.
- pub fn dst_addr(&self) -> Result<UnresolvedAddress> {
- let start = (self.ip_fields_start()
- + self.traffic_class_size()
- + self.next_header_size()
- + self.hop_limit_size()
- + self.src_address_size()) as usize;
- let data = self.buffer.as_ref();
- match (self.m_field(), self.dac_field(), self.dam_field()) {
- (0, 0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline(
- &data[start..][..16],
- ))),
- (0, 0, 0b01) => Ok(UnresolvedAddress::WithoutContext(
- AddressMode::InLine64bits(&data[start..][..8]),
- )),
- (0, 0, 0b10) => Ok(UnresolvedAddress::WithoutContext(
- AddressMode::InLine16bits(&data[start..][..2]),
- )),
- (0, 0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)),
- (0, 1, 0b00) => Ok(UnresolvedAddress::Reserved),
- (0, 1, 0b01) => {
- if let Some(id) = self.dst_context_id() {
- Ok(UnresolvedAddress::WithContext((
- id as usize,
- AddressMode::InLine64bits(&data[start..][..8]),
- )))
- } else {
- Err(Error)
- }
- }
- (0, 1, 0b10) => {
- if let Some(id) = self.dst_context_id() {
- Ok(UnresolvedAddress::WithContext((
- id as usize,
- AddressMode::InLine16bits(&data[start..][..2]),
- )))
- } else {
- Err(Error)
- }
- }
- (0, 1, 0b11) => {
- if let Some(id) = self.dst_context_id() {
- Ok(UnresolvedAddress::WithContext((
- id as usize,
- AddressMode::FullyElided,
- )))
- } else {
- Err(Error)
- }
- }
- (1, 0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline(
- &data[start..][..16],
- ))),
- (1, 0, 0b01) => Ok(UnresolvedAddress::WithoutContext(
- AddressMode::Multicast48bits(&data[start..][..6]),
- )),
- (1, 0, 0b10) => Ok(UnresolvedAddress::WithoutContext(
- AddressMode::Multicast32bits(&data[start..][..4]),
- )),
- (1, 0, 0b11) => Ok(UnresolvedAddress::WithoutContext(
- AddressMode::Multicast8bits(&data[start..][..1]),
- )),
- (1, 1, 0b00) => Ok(UnresolvedAddress::WithContext((
- 0,
- AddressMode::NotSupported,
- ))),
- (1, 1, 0b01 | 0b10 | 0b11) => Ok(UnresolvedAddress::Reserved),
- _ => Err(Error),
- }
- }
- get_field!(dispatch_field, 0b111, 13);
- get_field!(tf_field, 0b11, 11);
- get_field!(nh_field, 0b1, 10);
- get_field!(hlim_field, 0b11, 8);
- get_field!(cid_field, 0b1, 7);
- get_field!(sac_field, 0b1, 6);
- get_field!(sam_field, 0b11, 4);
- get_field!(m_field, 0b1, 3);
- get_field!(dac_field, 0b1, 2);
- get_field!(dam_field, 0b11, 0);
- /// Return the start for the IP fields.
- fn ip_fields_start(&self) -> u8 {
- 2 + self.cid_size()
- }
- /// Get the size in octets of the traffic class field.
- fn traffic_class_size(&self) -> u8 {
- match self.tf_field() {
- 0b00 => 4,
- 0b01 => 3,
- 0b10 => 1,
- 0b11 => 0,
- _ => unreachable!(),
- }
- }
- /// Get the size in octets of the next header field.
- fn next_header_size(&self) -> u8 {
- (self.nh_field() != 1) as u8
- }
- /// Get the size in octets of the hop limit field.
- fn hop_limit_size(&self) -> u8 {
- (self.hlim_field() == 0b00) as u8
- }
- /// Get the size in octets of the CID field.
- fn cid_size(&self) -> u8 {
- (self.cid_field() == 1) as u8
- }
- /// Get the size in octets of the source address.
- fn src_address_size(&self) -> u8 {
- match (self.sac_field(), self.sam_field()) {
- (0, 0b00) => 16, // The full address is carried in-line.
- (0, 0b01) => 8, // The first 64 bits are elided.
- (0, 0b10) => 2, // The first 112 bits are elided.
- (0, 0b11) => 0, // The address is fully elided.
- (1, 0b00) => 0, // The UNSPECIFIED address.
- (1, 0b01) => 8, // Address derived using context information.
- (1, 0b10) => 2, // Address derived using context information.
- (1, 0b11) => 0, // Address derived using context information.
- _ => unreachable!(),
- }
- }
- /// Get the size in octets of the address address.
- fn dst_address_size(&self) -> u8 {
- match (self.m_field(), self.dac_field(), self.dam_field()) {
- (0, 0, 0b00) => 16, // The full address is carried in-line.
- (0, 0, 0b01) => 8, // The first 64 bits are elided.
- (0, 0, 0b10) => 2, // The first 112 bits are elided.
- (0, 0, 0b11) => 0, // The address is fully elided.
- (0, 1, 0b00) => 0, // Reserved.
- (0, 1, 0b01) => 8, // Address derived using context information.
- (0, 1, 0b10) => 2, // Address derived using context information.
- (0, 1, 0b11) => 0, // Address derived using context information.
- (1, 0, 0b00) => 16, // The full address is carried in-line.
- (1, 0, 0b01) => 6, // The address takes the form ffXX::00XX:XXXX:XXXX.
- (1, 0, 0b10) => 4, // The address takes the form ffXX::00XX:XXXX.
- (1, 0, 0b11) => 1, // The address takes the form ff02::00XX.
- (1, 1, 0b00) => 6, // Match Unicast-Prefix-based IPv6.
- (1, 1, 0b01) => 0, // Reserved.
- (1, 1, 0b10) => 0, // Reserved.
- (1, 1, 0b11) => 0, // Reserved.
- _ => unreachable!(),
- }
- }
- /// Return the length of the header.
- pub fn header_len(&self) -> usize {
- let mut len = self.ip_fields_start();
- len += self.traffic_class_size();
- len += self.next_header_size();
- len += self.hop_limit_size();
- len += self.src_address_size();
- len += self.dst_address_size();
- len as usize
- }
- }
- impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
- /// Return a pointer to the payload.
- pub fn payload(&self) -> &'a [u8] {
- let mut len = self.ip_fields_start();
- len += self.traffic_class_size();
- len += self.next_header_size();
- len += self.hop_limit_size();
- len += self.src_address_size();
- len += self.dst_address_size();
- let len = len as usize;
- let data = self.buffer.as_ref();
- &data[len..]
- }
- }
- impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
- /// Set the dispatch field to `0b011`.
- fn set_dispatch_field(&mut self) {
- let data = &mut self.buffer.as_mut()[field::IPHC_FIELD];
- let mut raw = NetworkEndian::read_u16(data);
- raw = (raw & !(0b111 << 13)) | (0b11 << 13);
- NetworkEndian::write_u16(data, raw);
- }
- set_field!(set_tf_field, 0b11, 11);
- set_field!(set_nh_field, 0b1, 10);
- set_field!(set_hlim_field, 0b11, 8);
- set_field!(set_cid_field, 0b1, 7);
- set_field!(set_sac_field, 0b1, 6);
- set_field!(set_sam_field, 0b11, 4);
- set_field!(set_m_field, 0b1, 3);
- set_field!(set_dac_field, 0b1, 2);
- set_field!(set_dam_field, 0b11, 0);
- fn set_field(&mut self, idx: usize, value: &[u8]) {
- let raw = self.buffer.as_mut();
- raw[idx..idx + value.len()].copy_from_slice(value);
- }
- /// Set the Next Header.
- ///
- /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
- fn set_next_header(&mut self, nh: NextHeader, mut idx: usize) -> usize {
- match nh {
- NextHeader::Uncompressed(nh) => {
- self.set_nh_field(0);
- self.set_field(idx, &[nh.into()]);
- idx += 1;
- }
- NextHeader::Compressed => self.set_nh_field(1),
- }
- idx
- }
- /// Set the Hop Limit.
- ///
- /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
- fn set_hop_limit(&mut self, hl: u8, mut idx: usize) -> usize {
- match hl {
- 255 => self.set_hlim_field(0b11),
- 64 => self.set_hlim_field(0b10),
- 1 => self.set_hlim_field(0b01),
- _ => {
- self.set_hlim_field(0b00);
- self.set_field(idx, &[hl]);
- idx += 1;
- }
- }
- idx
- }
- /// Set the Source Address based on the IPv6 address and the Link-Local address.
- ///
- /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
- fn set_src_address(
- &mut self,
- src_addr: ipv6::Address,
- ll_src_addr: Option<LlAddress>,
- mut idx: usize,
- ) -> usize {
- self.set_cid_field(0);
- self.set_sac_field(0);
- let src = src_addr.as_bytes();
- if src_addr == ipv6::Address::UNSPECIFIED {
- self.set_sac_field(1);
- self.set_sam_field(0b00);
- } else if src_addr.is_link_local() {
- // We have a link local address.
- // The remainder of the address can be elided when the context contains
- // a 802.15.4 short address or a 802.15.4 extended address which can be
- // converted to a eui64 address.
- let is_eui_64 = ll_src_addr
- .map(|addr| {
- addr.as_eui_64()
- .map(|addr| addr[..] == src[8..])
- .unwrap_or(false)
- })
- .unwrap_or(false);
- if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
- let ll = [src[14], src[15]];
- if ll_src_addr == Some(LlAddress::Short(ll)) {
- // We have the context from the 802.15.4 frame.
- // The context contains the short address.
- // We can elide the source address.
- self.set_sam_field(0b11);
- } else {
- // We don't have the context from the 802.15.4 frame.
- // We cannot elide the source address, however we can elide 112 bits.
- self.set_sam_field(0b10);
- self.set_field(idx, &src[14..]);
- idx += 2;
- }
- } else if is_eui_64 {
- // We have the context from the 802.15.4 frame.
- // The context contains the extended address.
- // We can elide the source address.
- self.set_sam_field(0b11);
- } else {
- // We cannot elide the source address, however we can elide 64 bits.
- self.set_sam_field(0b01);
- self.set_field(idx, &src[8..]);
- idx += 8;
- }
- } else {
- // We cannot elide anything.
- self.set_sam_field(0b00);
- self.set_field(idx, src);
- idx += 16;
- }
- idx
- }
- /// Set the Destination Address based on the IPv6 address and the Link-Local address.
- ///
- /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
- fn set_dst_address(
- &mut self,
- dst_addr: ipv6::Address,
- ll_dst_addr: Option<LlAddress>,
- mut idx: usize,
- ) -> usize {
- self.set_dac_field(0);
- self.set_dam_field(0);
- self.set_m_field(0);
- let dst = dst_addr.as_bytes();
- if dst_addr.is_multicast() {
- self.set_m_field(1);
- if dst[1] == 0x02 && dst[2..15] == [0; 13] {
- self.set_dam_field(0b11);
- self.set_field(idx, &[dst[15]]);
- idx += 1;
- } else if dst[2..13] == [0; 11] {
- self.set_dam_field(0b10);
- self.set_field(idx, &[dst[1]]);
- idx += 1;
- self.set_field(idx, &dst[13..]);
- idx += 3;
- } else if dst[2..11] == [0; 9] {
- self.set_dam_field(0b01);
- self.set_field(idx, &[dst[1]]);
- idx += 1;
- self.set_field(idx, &dst[11..]);
- idx += 5;
- } else {
- self.set_dam_field(0b11);
- self.set_field(idx, dst);
- idx += 16;
- }
- } else if dst_addr.is_link_local() {
- let is_eui_64 = ll_dst_addr
- .map(|addr| {
- addr.as_eui_64()
- .map(|addr| addr[..] == dst[8..])
- .unwrap_or(false)
- })
- .unwrap_or(false);
- if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
- let ll = [dst[14], dst[15]];
- if ll_dst_addr == Some(LlAddress::Short(ll)) {
- self.set_dam_field(0b11);
- } else {
- self.set_dam_field(0b10);
- self.set_field(idx, &dst[14..]);
- idx += 2;
- }
- } else if is_eui_64 {
- self.set_dam_field(0b11);
- } else {
- self.set_dam_field(0b01);
- self.set_field(idx, &dst[8..]);
- idx += 8;
- }
- } else {
- self.set_dam_field(0b00);
- self.set_field(idx, dst);
- idx += 16;
- }
- idx
- }
- /// Return a mutable pointer to the payload.
- pub fn payload_mut(&mut self) -> &mut [u8] {
- let mut len = self.ip_fields_start();
- len += self.traffic_class_size();
- len += self.next_header_size();
- len += self.hop_limit_size();
- len += self.src_address_size();
- len += self.dst_address_size();
- let len = len as usize;
- let data = self.buffer.as_mut();
- &mut data[len..]
- }
- }
- /// A high-level representation of a 6LoWPAN IPHC header.
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct Repr {
- pub src_addr: ipv6::Address,
- pub ll_src_addr: Option<LlAddress>,
- pub dst_addr: ipv6::Address,
- pub ll_dst_addr: Option<LlAddress>,
- pub next_header: NextHeader,
- pub hop_limit: u8,
- // TODO(thvdveld): refactor the following fields into something else
- pub ecn: Option<u8>,
- pub dscp: Option<u8>,
- pub flow_label: Option<u16>,
- }
- impl Repr {
- /// Parse a 6LoWPAN IPHC header and return a high-level representation.
- ///
- /// The `ll_src_addr` and `ll_dst_addr` are the link-local addresses used for resolving the
- /// IPv6 packets.
- pub fn parse<T: AsRef<[u8]> + ?Sized>(
- packet: &Packet<&T>,
- ll_src_addr: Option<LlAddress>,
- ll_dst_addr: Option<LlAddress>,
- addr_context: &[AddressContext<'_>],
- ) -> Result<Self> {
- // Ensure basic accessors will work.
- packet.check_len()?;
- if packet.dispatch_field() != DISPATCH_IPHC_HEADER {
- // This is not an LOWPAN_IPHC packet.
- return Err(Error);
- }
- let src_addr = packet.src_addr()?.resolve(ll_src_addr, addr_context)?;
- let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr, addr_context)?;
- Ok(Self {
- src_addr,
- ll_src_addr,
- dst_addr,
- ll_dst_addr,
- next_header: packet.next_header(),
- hop_limit: packet.hop_limit(),
- ecn: packet.ecn_field(),
- dscp: packet.dscp_field(),
- flow_label: packet.flow_label_field(),
- })
- }
- /// Return the length of a header that will be emitted from this high-level representation.
- pub fn buffer_len(&self) -> usize {
- let mut len = 0;
- len += 2; // The minimal header length
- len += match self.next_header {
- NextHeader::Compressed => 0, // The next header is compressed (we don't need to inline what the next header is)
- NextHeader::Uncompressed(_) => 1, // The next header field is inlined
- };
- // Hop Limit size
- len += match self.hop_limit {
- 255 | 64 | 1 => 0, // We can inline the hop limit
- _ => 1,
- };
- // Add the length of the source address
- len += if self.src_addr == ipv6::Address::UNSPECIFIED {
- 0
- } else if self.src_addr.is_link_local() {
- let src = self.src_addr.as_bytes();
- let ll = [src[14], src[15]];
- let is_eui_64 = self
- .ll_src_addr
- .map(|addr| {
- addr.as_eui_64()
- .map(|addr| addr[..] == src[8..])
- .unwrap_or(false)
- })
- .unwrap_or(false);
- if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
- if self.ll_src_addr == Some(LlAddress::Short(ll)) {
- 0
- } else {
- 2
- }
- } else if is_eui_64 {
- 0
- } else {
- 8
- }
- } else {
- 16
- };
- // Add the size of the destination header
- let dst = self.dst_addr.as_bytes();
- len += if self.dst_addr.is_multicast() {
- if dst[1] == 0x02 && dst[2..15] == [0; 13] {
- 1
- } else if dst[2..13] == [0; 11] {
- 4
- } else if dst[2..11] == [0; 9] {
- 6
- } else {
- 16
- }
- } else if self.dst_addr.is_link_local() {
- let is_eui_64 = self
- .ll_dst_addr
- .map(|addr| {
- addr.as_eui_64()
- .map(|addr| addr[..] == dst[8..])
- .unwrap_or(false)
- })
- .unwrap_or(false);
- if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
- let ll = [dst[14], dst[15]];
- if self.ll_dst_addr == Some(LlAddress::Short(ll)) {
- 0
- } else {
- 2
- }
- } else if is_eui_64 {
- 0
- } else {
- 8
- }
- } else {
- 16
- };
- len += match (self.ecn, self.dscp, self.flow_label) {
- (Some(_), Some(_), Some(_)) => 4,
- (Some(_), None, Some(_)) => 3,
- (Some(_), Some(_), None) => 1,
- (None, None, None) => 0,
- _ => unreachable!(),
- };
- len
- }
- /// Emit a high-level representation into a 6LoWPAN IPHC header.
- pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
- let idx = 2;
- packet.set_dispatch_field();
- // FIXME(thvdveld): we don't set anything from the traffic flow.
- packet.set_tf_field(0b11);
- let idx = packet.set_next_header(self.next_header, idx);
- let idx = packet.set_hop_limit(self.hop_limit, idx);
- let idx = packet.set_src_address(self.src_addr, self.ll_src_addr, idx);
- packet.set_dst_address(self.dst_addr, self.ll_dst_addr, idx);
- }
- }
- #[cfg(test)]
- mod test {
- use super::*;
- #[test]
- fn iphc_fields() {
- let bytes = [
- 0x7a, 0x33, // IPHC
- 0x3a, // Next header
- ];
- let packet = Packet::new_unchecked(bytes);
- assert_eq!(packet.dispatch_field(), 0b011);
- assert_eq!(packet.tf_field(), 0b11);
- assert_eq!(packet.nh_field(), 0b0);
- assert_eq!(packet.hlim_field(), 0b10);
- assert_eq!(packet.cid_field(), 0b0);
- assert_eq!(packet.sac_field(), 0b0);
- assert_eq!(packet.sam_field(), 0b11);
- assert_eq!(packet.m_field(), 0b0);
- assert_eq!(packet.dac_field(), 0b0);
- assert_eq!(packet.dam_field(), 0b11);
- assert_eq!(
- packet.next_header(),
- NextHeader::Uncompressed(IpProtocol::Icmpv6)
- );
- assert_eq!(packet.src_address_size(), 0);
- assert_eq!(packet.dst_address_size(), 0);
- assert_eq!(packet.hop_limit(), 64);
- assert_eq!(
- packet.src_addr(),
- Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided))
- );
- assert_eq!(
- packet.dst_addr(),
- Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided))
- );
- let bytes = [
- 0x7e, 0xf7, // IPHC,
- 0x00, // CID
- ];
- let packet = Packet::new_unchecked(bytes);
- assert_eq!(packet.dispatch_field(), 0b011);
- assert_eq!(packet.tf_field(), 0b11);
- assert_eq!(packet.nh_field(), 0b1);
- assert_eq!(packet.hlim_field(), 0b10);
- assert_eq!(packet.cid_field(), 0b1);
- assert_eq!(packet.sac_field(), 0b1);
- assert_eq!(packet.sam_field(), 0b11);
- assert_eq!(packet.m_field(), 0b0);
- assert_eq!(packet.dac_field(), 0b1);
- assert_eq!(packet.dam_field(), 0b11);
- assert_eq!(packet.next_header(), NextHeader::Compressed);
- assert_eq!(packet.src_address_size(), 0);
- assert_eq!(packet.dst_address_size(), 0);
- assert_eq!(packet.hop_limit(), 64);
- assert_eq!(
- packet.src_addr(),
- Ok(UnresolvedAddress::WithContext((
- 0,
- AddressMode::FullyElided
- )))
- );
- assert_eq!(
- packet.dst_addr(),
- Ok(UnresolvedAddress::WithContext((
- 0,
- AddressMode::FullyElided
- )))
- );
- }
- }
- }
- pub mod nhc {
- //! Implementation of Next Header Compression from [RFC 6282 § 4].
- //!
- //! [RFC 6282 § 4]: https://datatracker.ietf.org/doc/html/rfc6282#section-4
- use super::{Error, NextHeader, Result, DISPATCH_EXT_HEADER, DISPATCH_UDP_HEADER};
- use crate::{
- phy::ChecksumCapabilities,
- wire::{
- ip::{checksum, Address as IpAddress},
- ipv6,
- udp::Repr as UdpRepr,
- IpProtocol,
- },
- };
- use byteorder::{ByteOrder, NetworkEndian};
- use ipv6::Address;
- macro_rules! get_field {
- ($name:ident, $mask:expr, $shift:expr) => {
- fn $name(&self) -> u8 {
- let data = self.buffer.as_ref();
- let raw = &data[0];
- ((raw >> $shift) & $mask) as u8
- }
- };
- }
- macro_rules! set_field {
- ($name:ident, $mask:expr, $shift:expr) => {
- fn $name(&mut self, val: u8) {
- let data = self.buffer.as_mut();
- let mut raw = data[0];
- raw = (raw & !($mask << $shift)) | (val << $shift);
- data[0] = raw;
- }
- };
- }
- #[derive(Debug, Clone)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- /// A read/write wrapper around a 6LoWPAN_NHC Header.
- /// [RFC 6282 § 4.2] specifies the format of the header.
- ///
- /// The header has the following format:
- /// ```txt
- /// 0 1 2 3 4 5 6 7
- /// +---+---+---+---+---+---+---+---+
- /// | 1 | 1 | 1 | 0 | EID |NH |
- /// +---+---+---+---+---+---+---+---+
- /// ```
- ///
- /// With:
- /// - EID: the extension header ID
- /// - NH: Next Header
- ///
- /// [RFC 6282 § 4.2]: https://datatracker.ietf.org/doc/html/rfc6282#section-4.2
- pub enum NhcPacket {
- ExtHeader,
- UdpHeader,
- }
- impl NhcPacket {
- /// Returns the type of the Next Header header.
- /// This can either be an Extenstion header or an 6LoWPAN Udp header.
- ///
- /// # Errors
- /// Returns `[Error::Unrecognized]` when neither the Extension Header dispatch or the Udp
- /// dispatch is recognized.
- pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result<Self> {
- let raw = buffer.as_ref();
- if raw.is_empty() {
- return Err(Error);
- }
- if raw[0] >> 4 == DISPATCH_EXT_HEADER {
- // We have a compressed IPv6 Extension Header.
- Ok(Self::ExtHeader)
- } else if raw[0] >> 3 == DISPATCH_UDP_HEADER {
- // We have a compressed UDP header.
- Ok(Self::UdpHeader)
- } else {
- Err(Error)
- }
- }
- }
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub enum ExtHeaderId {
- HopByHopHeader,
- RoutingHeader,
- FragmentHeader,
- DestinationOptionsHeader,
- MobilityHeader,
- Header,
- Reserved,
- }
- impl From<ExtHeaderId> for IpProtocol {
- fn from(val: ExtHeaderId) -> Self {
- match val {
- ExtHeaderId::HopByHopHeader => Self::HopByHop,
- ExtHeaderId::RoutingHeader => Self::Ipv6Route,
- ExtHeaderId::FragmentHeader => Self::Ipv6Frag,
- ExtHeaderId::DestinationOptionsHeader => Self::Ipv6Opts,
- ExtHeaderId::MobilityHeader => Self::Unknown(0),
- ExtHeaderId::Header => Self::Unknown(0),
- ExtHeaderId::Reserved => Self::Unknown(0),
- }
- }
- }
- /// A read/write wrapper around a 6LoWPAN NHC Extension header.
- #[derive(Debug, Clone)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct ExtHeaderPacket<T: AsRef<[u8]>> {
- buffer: T,
- }
- impl<T: AsRef<[u8]>> ExtHeaderPacket<T> {
- /// Input a raw octet buffer with a 6LoWPAN NHC Extension Header structure.
- pub const fn new_unchecked(buffer: T) -> Self {
- ExtHeaderPacket { buffer }
- }
- /// Shorthand for a combination of [new_unchecked] and [check_len].
- ///
- /// [new_unchecked]: #method.new_unchecked
- /// [check_len]: #method.check_len
- pub fn new_checked(buffer: T) -> Result<Self> {
- let packet = Self::new_unchecked(buffer);
- packet.check_len()?;
- if packet.eid_field() > 7 {
- return Err(Error);
- }
- Ok(packet)
- }
- /// Ensure that no accessor method will panic if called.
- /// Returns `Err(Error)` if the buffer is too short.
- pub fn check_len(&self) -> Result<()> {
- let buffer = self.buffer.as_ref();
- if buffer.is_empty() {
- return Err(Error);
- }
- let mut len = 1;
- len += self.next_header_size();
- if len <= buffer.len() {
- Ok(())
- } else {
- Err(Error)
- }
- }
- /// Consumes the frame, returning the underlying buffer.
- pub fn into_inner(self) -> T {
- self.buffer
- }
- get_field!(dispatch_field, 0b1111, 4);
- get_field!(eid_field, 0b111, 1);
- get_field!(nh_field, 0b1, 0);
- /// Return the Extension Header ID.
- pub fn extension_header_id(&self) -> ExtHeaderId {
- match self.eid_field() {
- 0 => ExtHeaderId::HopByHopHeader,
- 1 => ExtHeaderId::RoutingHeader,
- 2 => ExtHeaderId::FragmentHeader,
- 3 => ExtHeaderId::DestinationOptionsHeader,
- 4 => ExtHeaderId::MobilityHeader,
- 5 | 6 => ExtHeaderId::Reserved,
- 7 => ExtHeaderId::Header,
- _ => unreachable!(),
- }
- }
- /// Parse the next header field.
- pub fn next_header(&self) -> NextHeader {
- if self.nh_field() == 1 {
- NextHeader::Compressed
- } else {
- // The full 8 bits for Next Header are carried in-line.
- let start = 1;
- let data = self.buffer.as_ref();
- let nh = data[start];
- NextHeader::Uncompressed(IpProtocol::from(nh))
- }
- }
- /// Return the size of the Next Header field.
- fn next_header_size(&self) -> usize {
- // If nh is set, then the Next Header is compressed using LOWPAN_NHC
- match self.nh_field() {
- 0 => 1,
- 1 => 0,
- _ => unreachable!(),
- }
- }
- }
- impl<'a, T: AsRef<[u8]> + ?Sized> ExtHeaderPacket<&'a T> {
- /// Return a pointer to the payload.
- pub fn payload(&self) -> &'a [u8] {
- let start = 1 + self.next_header_size();
- &self.buffer.as_ref()[start..]
- }
- }
- impl<T: AsRef<[u8]> + AsMut<[u8]>> ExtHeaderPacket<T> {
- /// Return a mutable pointer to the payload.
- pub fn payload_mut(&mut self) -> &mut [u8] {
- let start = 2 + self.next_header_size();
- &mut self.buffer.as_mut()[start..]
- }
- /// Set the dispatch field to `0b1110`.
- fn set_dispatch_field(&mut self) {
- let data = self.buffer.as_mut();
- data[0] = (data[0] & !(0b1111 << 4)) | (DISPATCH_EXT_HEADER << 4);
- }
- set_field!(set_eid_field, 0b111, 1);
- set_field!(set_nh_field, 0b1, 0);
- /// Set the Extension Header ID field.
- fn set_extension_header_id(&mut self, ext_header_id: ExtHeaderId) {
- let id = match ext_header_id {
- ExtHeaderId::HopByHopHeader => 0,
- ExtHeaderId::RoutingHeader => 1,
- ExtHeaderId::FragmentHeader => 2,
- ExtHeaderId::DestinationOptionsHeader => 3,
- ExtHeaderId::MobilityHeader => 4,
- ExtHeaderId::Reserved => 5,
- ExtHeaderId::Header => 7,
- };
- self.set_eid_field(id);
- }
- /// Set the Next Header.
- fn set_next_header(&mut self, next_header: NextHeader) {
- match next_header {
- NextHeader::Compressed => self.set_nh_field(0b1),
- NextHeader::Uncompressed(nh) => {
- self.set_nh_field(0b0);
- let start = 1;
- let data = self.buffer.as_mut();
- data[start] = nh.into();
- }
- }
- }
- /// Set the length.
- fn set_length(&mut self, length: u8) {
- let start = 1 + self.next_header_size();
- let data = self.buffer.as_mut();
- data[start] = length;
- }
- }
- /// A high-level representation of an 6LoWPAN NHC Extension header.
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct ExtHeaderRepr {
- ext_header_id: ExtHeaderId,
- next_header: NextHeader,
- length: u8,
- }
- impl ExtHeaderRepr {
- /// Parse a 6LoWPAN NHC Extension Header packet and return a high-level representation.
- pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &ExtHeaderPacket<&T>) -> Result<Self> {
- // Ensure basic accessors will work.
- packet.check_len()?;
- if packet.dispatch_field() != DISPATCH_EXT_HEADER {
- return Err(Error);
- }
- Ok(Self {
- ext_header_id: packet.extension_header_id(),
- next_header: packet.next_header(),
- length: packet.payload().len() as u8,
- })
- }
- /// Return the length of a header that will be emitted from this high-level representation.
- pub fn buffer_len(&self) -> usize {
- let mut len = 1; // The minimal header size
- if self.next_header != NextHeader::Compressed {
- len += 1;
- }
- len += 1; // The length
- len
- }
- /// Emit a high-level representaiton into a 6LoWPAN NHC Extension Header packet.
- pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut ExtHeaderPacket<T>) {
- packet.set_dispatch_field();
- packet.set_extension_header_id(self.ext_header_id);
- packet.set_next_header(self.next_header);
- packet.set_length(self.length);
- }
- }
- /// A read/write wrapper around a 6LoWPAN_NHC UDP frame.
- /// [RFC 6282 § 4.3] specifies the format of the header.
- ///
- /// The base header has the following formath:
- /// ```txt
- /// 0 1 2 3 4 5 6 7
- /// +---+---+---+---+---+---+---+---+
- /// | 1 | 1 | 1 | 1 | 0 | C | P |
- /// +---+---+---+---+---+---+---+---+
- /// With:
- /// - C: checksum, specifies if the checksum is elided.
- /// - P: ports, specifies if the ports are elided.
- /// ```
- ///
- /// [RFC 6282 § 4.3]: https://datatracker.ietf.org/doc/html/rfc6282#section-4.3
- #[derive(Debug, Clone)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct UdpNhcPacket<T: AsRef<[u8]>> {
- buffer: T,
- }
- impl<T: AsRef<[u8]>> UdpNhcPacket<T> {
- /// Input a raw octet buffer with a LOWPAN_NHC frame structure for UDP.
- pub const fn new_unchecked(buffer: T) -> Self {
- Self { buffer }
- }
- /// Shorthand for a combination of [new_unchecked] and [check_len].
- ///
- /// [new_unchecked]: #method.new_unchecked
- /// [check_len]: #method.check_len
- pub fn new_checked(buffer: T) -> Result<Self> {
- let packet = Self::new_unchecked(buffer);
- packet.check_len()?;
- Ok(packet)
- }
- /// Ensure that no accessor method will panic if called.
- /// Returns `Err(Error::Truncated)` if the buffer is too short.
- pub fn check_len(&self) -> Result<()> {
- let buffer = self.buffer.as_ref();
- if buffer.is_empty() {
- return Err(Error);
- }
- let index = 1 + self.ports_size() + self.checksum_size();
- if index > buffer.len() {
- return Err(Error);
- }
- Ok(())
- }
- /// Consumes the frame, returning the underlying buffer.
- pub fn into_inner(self) -> T {
- self.buffer
- }
- get_field!(dispatch_field, 0b11111, 3);
- get_field!(checksum_field, 0b1, 2);
- get_field!(ports_field, 0b11, 0);
- /// Returns the index of the start of the next header compressed fields.
- const fn nhc_fields_start(&self) -> usize {
- 1
- }
- /// Return the source port number.
- pub fn src_port(&self) -> u16 {
- match self.ports_field() {
- 0b00 | 0b01 => {
- // The full 16 bits are carried in-line.
- let data = self.buffer.as_ref();
- let start = self.nhc_fields_start();
- NetworkEndian::read_u16(&data[start..start + 2])
- }
- 0b10 => {
- // The first 8 bits are elided.
- let data = self.buffer.as_ref();
- let start = self.nhc_fields_start();
- 0xf000 + data[start] as u16
- }
- 0b11 => {
- // The first 12 bits are elided.
- let data = self.buffer.as_ref();
- let start = self.nhc_fields_start();
- 0xf0b0 + (data[start] >> 4) as u16
- }
- _ => unreachable!(),
- }
- }
- /// Return the destination port number.
- pub fn dst_port(&self) -> u16 {
- match self.ports_field() {
- 0b00 => {
- // The full 16 bits are carried in-line.
- let data = self.buffer.as_ref();
- let idx = self.nhc_fields_start();
- NetworkEndian::read_u16(&data[idx + 2..idx + 4])
- }
- 0b01 => {
- // The first 8 bits are elided.
- let data = self.buffer.as_ref();
- let idx = self.nhc_fields_start();
- 0xf000 + data[idx] as u16
- }
- 0b10 => {
- // The full 16 bits are carried in-line.
- let data = self.buffer.as_ref();
- let idx = self.nhc_fields_start();
- NetworkEndian::read_u16(&data[idx + 1..idx + 1 + 2])
- }
- 0b11 => {
- // The first 12 bits are elided.
- let data = self.buffer.as_ref();
- let start = self.nhc_fields_start();
- 0xf0b0 + (data[start] & 0xff) as u16
- }
- _ => unreachable!(),
- }
- }
- /// Return the checksum.
- pub fn checksum(&self) -> Option<u16> {
- if self.checksum_field() == 0b0 {
- // The first 12 bits are elided.
- let data = self.buffer.as_ref();
- let start = self.nhc_fields_start() + self.ports_size();
- Some(NetworkEndian::read_u16(&data[start..start + 2]))
- } else {
- // The checksum is elided and needs to be recomputed on the 6LoWPAN termination point.
- None
- }
- }
- // Return the size of the checksum field.
- pub(crate) fn checksum_size(&self) -> usize {
- match self.checksum_field() {
- 0b0 => 2,
- 0b1 => 0,
- _ => unreachable!(),
- }
- }
- /// Returns the total size of both port numbers.
- pub(crate) fn ports_size(&self) -> usize {
- match self.ports_field() {
- 0b00 => 4, // 16 bits + 16 bits
- 0b01 => 3, // 16 bits + 8 bits
- 0b10 => 3, // 8 bits + 16 bits
- 0b11 => 1, // 4 bits + 4 bits
- _ => unreachable!(),
- }
- }
- }
- impl<'a, T: AsRef<[u8]> + ?Sized> UdpNhcPacket<&'a T> {
- /// Return a pointer to the payload.
- pub fn payload(&self) -> &'a [u8] {
- let start = 1 + self.ports_size() + self.checksum_size();
- &self.buffer.as_ref()[start..]
- }
- }
- impl<T: AsRef<[u8]> + AsMut<[u8]>> UdpNhcPacket<T> {
- /// Return a mutable pointer to the payload.
- pub fn payload_mut(&mut self) -> &mut [u8] {
- let start = 1 + self.ports_size() + 2; // XXX(thvdveld): we assume we put the checksum inlined.
- &mut self.buffer.as_mut()[start..]
- }
- /// Set the dispatch field to `0b11110`.
- fn set_dispatch_field(&mut self) {
- let data = self.buffer.as_mut();
- data[0] = (data[0] & !(0b11111 << 3)) | (DISPATCH_UDP_HEADER << 3);
- }
- set_field!(set_checksum_field, 0b1, 2);
- set_field!(set_ports_field, 0b11, 0);
- fn set_ports(&mut self, src_port: u16, dst_port: u16) {
- let mut idx = 1;
- match (src_port, dst_port) {
- (0xf0b0..=0xf0bf, 0xf0b0..=0xf0bf) => {
- // We can compress both the source and destination ports.
- self.set_ports_field(0b11);
- let data = self.buffer.as_mut();
- data[idx] = (((src_port - 0xf0b0) as u8) << 4) & ((dst_port - 0xf0b0) as u8);
- }
- (0xf000..=0xf0ff, _) => {
- // We can compress the source port, but not the destination port.
- self.set_ports_field(0b10);
- let data = self.buffer.as_mut();
- data[idx] = (src_port - 0xf000) as u8;
- idx += 1;
- NetworkEndian::write_u16(&mut data[idx..idx + 2], dst_port);
- }
- (_, 0xf000..=0xf0ff) => {
- // We can compress the destination port, but not the source port.
- self.set_ports_field(0b01);
- let data = self.buffer.as_mut();
- NetworkEndian::write_u16(&mut data[idx..idx + 2], src_port);
- idx += 2;
- data[idx] = (dst_port - 0xf000) as u8;
- }
- (_, _) => {
- // We cannot compress any port.
- self.set_ports_field(0b00);
- let data = self.buffer.as_mut();
- NetworkEndian::write_u16(&mut data[idx..idx + 2], src_port);
- idx += 2;
- NetworkEndian::write_u16(&mut data[idx..idx + 2], dst_port);
- }
- };
- }
- fn set_checksum(&mut self, checksum: u16) {
- self.set_checksum_field(0b0);
- let idx = 1 + self.ports_size();
- let data = self.buffer.as_mut();
- NetworkEndian::write_u16(&mut data[idx..idx + 2], checksum);
- }
- }
- /// A high-level representation of a 6LoWPAN NHC UDP header.
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct UdpNhcRepr(pub UdpRepr);
- impl<'a> UdpNhcRepr {
- /// Parse a 6LoWPAN NHC UDP packet and return a high-level representation.
- pub fn parse<T: AsRef<[u8]> + ?Sized>(
- packet: &UdpNhcPacket<&'a T>,
- src_addr: &ipv6::Address,
- dst_addr: &ipv6::Address,
- checksum_caps: &ChecksumCapabilities,
- ) -> Result<Self> {
- packet.check_len()?;
- if packet.dispatch_field() != DISPATCH_UDP_HEADER {
- return Err(Error);
- }
- if checksum_caps.udp.rx() {
- let payload_len = packet.payload().len();
- let chk_sum = !checksum::combine(&[
- checksum::pseudo_header(
- &IpAddress::Ipv6(*src_addr),
- &IpAddress::Ipv6(*dst_addr),
- crate::wire::ip::Protocol::Udp,
- payload_len as u32 + 8,
- ),
- packet.src_port(),
- packet.dst_port(),
- payload_len as u16 + 8,
- checksum::data(packet.payload()),
- ]);
- if let Some(checksum) = packet.checksum() {
- if chk_sum != checksum {
- return Err(Error);
- }
- }
- }
- Ok(Self(UdpRepr {
- src_port: packet.src_port(),
- dst_port: packet.dst_port(),
- }))
- }
- /// Return the length of a packet that will be emitted from this high-level representation.
- pub fn header_len(&self) -> usize {
- let mut len = 1; // The minimal header size
- len += 2; // XXX We assume we will add the checksum at the end
- // Check if we can compress the source and destination ports
- match (self.src_port, self.dst_port) {
- (0xf0b0..=0xf0bf, 0xf0b0..=0xf0bf) => len + 1,
- (0xf000..=0xf0ff, _) | (_, 0xf000..=0xf0ff) => len + 3,
- (_, _) => len + 4,
- }
- }
- /// Emit a high-level representation into a LOWPAN_NHC UDP header.
- pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
- &self,
- packet: &mut UdpNhcPacket<T>,
- src_addr: &Address,
- dst_addr: &Address,
- payload_len: usize,
- emit_payload: impl FnOnce(&mut [u8]),
- ) {
- packet.set_dispatch_field();
- packet.set_ports(self.src_port, self.dst_port);
- emit_payload(packet.payload_mut());
- let chk_sum = !checksum::combine(&[
- checksum::pseudo_header(
- &IpAddress::Ipv6(*src_addr),
- &IpAddress::Ipv6(*dst_addr),
- crate::wire::ip::Protocol::Udp,
- payload_len as u32 + 8,
- ),
- self.src_port,
- self.dst_port,
- payload_len as u16 + 8,
- checksum::data(packet.payload_mut()),
- ]);
- packet.set_checksum(chk_sum);
- }
- }
- impl core::ops::Deref for UdpNhcRepr {
- type Target = UdpRepr;
- fn deref(&self) -> &Self::Target {
- &self.0
- }
- }
- impl core::ops::DerefMut for UdpNhcRepr {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
- }
- #[cfg(test)]
- mod test {
- use super::*;
- #[test]
- fn ext_header_nhc_fields() {
- let bytes = [0xe3, 0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00];
- let packet = ExtHeaderPacket::new_checked(&bytes[..]).unwrap();
- assert_eq!(packet.next_header_size(), 0);
- assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER);
- assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader);
- assert_eq!(packet.payload(), [0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00]);
- }
- #[test]
- fn ext_header_emit() {
- let ext_header = ExtHeaderRepr {
- ext_header_id: ExtHeaderId::RoutingHeader,
- next_header: NextHeader::Compressed,
- length: 6,
- };
- let len = ext_header.buffer_len();
- let mut buffer = [0u8; 127];
- let mut packet = ExtHeaderPacket::new_unchecked(&mut buffer[..len]);
- ext_header.emit(&mut packet);
- assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER);
- assert_eq!(packet.next_header(), NextHeader::Compressed);
- assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader);
- }
- #[test]
- fn udp_nhc_fields() {
- let bytes = [0xf0, 0x16, 0x2e, 0x22, 0x3d, 0x28, 0xc4];
- let packet = UdpNhcPacket::new_checked(&bytes[..]).unwrap();
- assert_eq!(packet.dispatch_field(), DISPATCH_UDP_HEADER);
- assert_eq!(packet.checksum(), Some(0x28c4));
- assert_eq!(packet.src_port(), 5678);
- assert_eq!(packet.dst_port(), 8765);
- }
- #[test]
- fn udp_emit() {
- let udp = UdpNhcRepr(UdpRepr {
- src_port: 0xf0b1,
- dst_port: 0xf001,
- });
- let payload = b"Hello World!";
- let src_addr = ipv6::Address::default();
- let dst_addr = ipv6::Address::default();
- let len = udp.header_len() + payload.len();
- let mut buffer = [0u8; 127];
- let mut packet = UdpNhcPacket::new_unchecked(&mut buffer[..len]);
- udp.emit(&mut packet, &src_addr, &dst_addr, payload.len(), |buf| {
- buf.copy_from_slice(&payload[..])
- });
- assert_eq!(packet.dispatch_field(), DISPATCH_UDP_HEADER);
- assert_eq!(packet.src_port(), 0xf0b1);
- assert_eq!(packet.dst_port(), 0xf001);
- assert_eq!(packet.payload_mut(), b"Hello World!");
- }
- }
- }
- #[cfg(test)]
- mod test {
- use crate::phy::ChecksumCapabilities;
- use crate::time::Duration;
- use super::*;
- #[test]
- fn sixlowpan_fragment_emit() {
- let repr = frag::Repr::FirstFragment {
- size: 0xff,
- tag: 0xabcd,
- };
- let buffer = [0u8; 4];
- let mut packet = frag::Packet::new_unchecked(buffer);
- assert_eq!(repr.buffer_len(), 4);
- repr.emit(&mut packet);
- assert_eq!(packet.datagram_size(), 0xff);
- assert_eq!(packet.datagram_tag(), 0xabcd);
- assert_eq!(packet.into_inner(), [0xc0, 0xff, 0xab, 0xcd]);
- let repr = frag::Repr::Fragment {
- size: 0xff,
- tag: 0xabcd,
- offset: 0xcc,
- };
- let buffer = [0u8; 5];
- let mut packet = frag::Packet::new_unchecked(buffer);
- assert_eq!(repr.buffer_len(), 5);
- repr.emit(&mut packet);
- assert_eq!(packet.datagram_size(), 0xff);
- assert_eq!(packet.datagram_tag(), 0xabcd);
- assert_eq!(packet.into_inner(), [0xe0, 0xff, 0xab, 0xcd, 0xcc]);
- }
- #[test]
- fn sixlowpan_three_fragments() {
- use crate::iface::ReassemblyBuffer;
- use crate::time::Instant;
- use crate::wire::ieee802154::Frame as Ieee802154Frame;
- use crate::wire::ieee802154::Repr as Ieee802154Repr;
- use crate::wire::Ieee802154Address;
- let mut frags_cache = ReassemblyBuffer::new();
- let frame1: &[u8] = &[
- 0x41, 0xcc, 0x92, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9,
- 0x3e, 0x08, 0x28, 0x2f, 0x82, 0x93, 0x32, 0xc1, 0x33, 0x00, 0x3f, 0x6e, 0x33, 0x02,
- 0x35, 0x3d, 0xf0, 0xd2, 0x5f, 0x1b, 0x39, 0xb4, 0x6b, 0x4c, 0x6f, 0x72, 0x65, 0x6d,
- 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73,
- 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65,
- 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63,
- 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x71,
- 0x75, 0x61, 0x6d, 0x20, 0x64, 0x75, 0x69, 0x20, 0x6f, 0x64, 0x69, 0x6f, 0x2c, 0x20,
- 0x69, 0x61, 0x63, 0x75, 0x6c, 0x69, 0x73, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x72,
- ];
- let ieee802154_frame = Ieee802154Frame::new_checked(frame1).unwrap();
- let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap();
- let sixlowpan_frame =
- SixlowpanPacket::dispatch(ieee802154_frame.payload().unwrap()).unwrap();
- let frag = if let SixlowpanPacket::FragmentHeader = sixlowpan_frame {
- frag::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap()
- } else {
- unreachable!()
- };
- assert_eq!(frag.datagram_size(), 307);
- assert_eq!(frag.datagram_tag(), 0x003f);
- assert_eq!(frag.datagram_offset(), 0);
- let key = frag.get_key(&ieee802154_repr);
- let uncompressed = 40 + 8;
- let compressed = 5 + 7;
- let assr = frags_cache
- .get(&key, Instant::now() + Duration::from_secs(60))
- .unwrap();
- assr.set_total_size(frag.datagram_size() as usize - uncompressed + compressed)
- .unwrap();
- assr.set_offset_correction(-((uncompressed - compressed) as isize));
- assr.add(frag.payload(), 0).unwrap();
- let frame2: &[u8] = &[
- 0x41, 0xcc, 0x93, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9,
- 0x3e, 0x08, 0x28, 0x2f, 0x82, 0x93, 0x32, 0xe1, 0x33, 0x00, 0x3f, 0x11, 0x75, 0x74,
- 0x72, 0x75, 0x6d, 0x20, 0x61, 0x74, 0x2c, 0x20, 0x74, 0x72, 0x69, 0x73, 0x74, 0x69,
- 0x71, 0x75, 0x65, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75, 0x6e, 0x63, 0x20, 0x65,
- 0x72, 0x61, 0x74, 0x20, 0x63, 0x75, 0x72, 0x61, 0x65, 0x2e, 0x20, 0x4c, 0x6f, 0x72,
- 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72,
- 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e,
- 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69,
- 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74,
- ];
- let ieee802154_frame = Ieee802154Frame::new_checked(frame2).unwrap();
- let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap();
- let sixlowpan_frame =
- SixlowpanPacket::dispatch(ieee802154_frame.payload().unwrap()).unwrap();
- let frag = if let SixlowpanPacket::FragmentHeader = sixlowpan_frame {
- frag::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap()
- } else {
- unreachable!()
- };
- assert_eq!(frag.datagram_size(), 307);
- assert_eq!(frag.datagram_tag(), 0x003f);
- assert_eq!(frag.datagram_offset(), 136 / 8);
- let key = frag.get_key(&ieee802154_repr);
- let assr = frags_cache
- .get(&key, Instant::now() + Duration::from_secs(60))
- .unwrap();
- assr.add(frag.payload(), frag.datagram_offset() as usize * 8)
- .unwrap();
- let frame3: &[u8] = &[
- 0x41, 0xcc, 0x94, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9,
- 0x3e, 0x08, 0x28, 0x2f, 0x82, 0x93, 0x32, 0xe1, 0x33, 0x00, 0x3f, 0x1d, 0x2e, 0x20,
- 0x41, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x64, 0x75, 0x69, 0x20, 0x6f, 0x64,
- 0x69, 0x6f, 0x2c, 0x20, 0x69, 0x61, 0x63, 0x75, 0x6c, 0x69, 0x73, 0x20, 0x76, 0x65,
- 0x6c, 0x20, 0x72, 0x75, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x61, 0x74, 0x2c, 0x20, 0x74,
- 0x72, 0x69, 0x73, 0x74, 0x69, 0x71, 0x75, 0x65, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e,
- 0x75, 0x6e, 0x63, 0x20, 0x65, 0x72, 0x61, 0x74, 0x20, 0x63, 0x75, 0x72, 0x61, 0x65,
- 0x2e, 0x20, 0x0a,
- ];
- let ieee802154_frame = Ieee802154Frame::new_checked(frame3).unwrap();
- let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap();
- let sixlowpan_frame =
- SixlowpanPacket::dispatch(ieee802154_frame.payload().unwrap()).unwrap();
- let frag = if let SixlowpanPacket::FragmentHeader = sixlowpan_frame {
- frag::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap()
- } else {
- unreachable!()
- };
- assert_eq!(frag.datagram_size(), 307);
- assert_eq!(frag.datagram_tag(), 0x003f);
- assert_eq!(frag.datagram_offset(), 232 / 8);
- let key = frag.get_key(&ieee802154_repr);
- let assr = frags_cache
- .get(&key, Instant::now() + Duration::from_secs(60))
- .unwrap();
- assr.add(frag.payload(), frag.datagram_offset() as usize * 8)
- .unwrap();
- let assembled_packet = assr.assemble().unwrap();
- let sixlowpan_frame = SixlowpanPacket::dispatch(assembled_packet).unwrap();
- let iphc = if let SixlowpanPacket::IphcHeader = sixlowpan_frame {
- iphc::Packet::new_checked(assembled_packet).unwrap()
- } else {
- unreachable!()
- };
- let iphc_repr = iphc::Repr::parse(
- &iphc,
- ieee802154_repr.src_addr,
- ieee802154_repr.dst_addr,
- &[],
- )
- .unwrap();
- assert_eq!(
- iphc_repr.dst_addr,
- ipv6::Address::from_bytes(&[
- 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0xb, 0x42, 0x42, 0x42, 0x42, 0x42,
- 0x42,
- ]),
- );
- assert_eq!(
- iphc_repr.ll_dst_addr,
- Some(Ieee802154Address::from_bytes(&[
- 0x1a, 0xb, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
- ])),
- );
- assert_eq!(iphc_repr.next_header, NextHeader::Compressed);
- assert_eq!(iphc_repr.hop_limit, 64);
- let sixlowpan_frame = nhc::NhcPacket::dispatch(iphc.payload()).unwrap();
- let udp_hdr = if let nhc::NhcPacket::UdpHeader = sixlowpan_frame {
- nhc::UdpNhcPacket::new_checked(iphc.payload()).unwrap()
- } else {
- unreachable!()
- };
- let payload = udp_hdr.payload();
- assert_eq!(String::from_utf8_lossy(payload), "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. \n");
- let udp_repr = nhc::UdpNhcRepr::parse(
- &udp_hdr,
- &iphc_repr.src_addr,
- &iphc_repr.dst_addr,
- &ChecksumCapabilities::default(),
- )
- .unwrap();
- assert_eq!(udp_repr.src_port, 53855);
- assert_eq!(udp_repr.dst_port, 6969);
- assert_eq!(udp_hdr.checksum(), Some(0xb46b));
- }
- }
|