123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463 |
- //! 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 super::{Error, Result};
- use crate::wire::ieee802154::Address as LlAddress;
- use crate::wire::ipv6;
- use crate::wire::IpProtocol;
- const ADDRESS_CONTEXT_LENGTH: usize = 8;
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct AddressContext(pub [u8; ADDRESS_CONTEXT_LENGTH]);
- /// 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];
- bytes[..ADDRESS_CONTEXT_LENGTH].copy_from_slice(&context.0);
- 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::{Error, Result};
- use crate::wire::{Ieee802154Address, Ieee802154Repr};
- 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);
- }
- 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);
- }
- 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)
- }
- DISPATCH_FRAGMENT_HEADER if buffer.len() >= NEXT_FRAGMENT_HEADER_SIZE => Ok(()),
- DISPATCH_FRAGMENT_HEADER if buffer.len() < NEXT_FRAGMENT_HEADER_SIZE => Err(Error),
- _ => Err(Error),
- }
- }
- /// 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)]
- pub enum Repr {
- FirstFragment { size: u16, tag: u16 },
- Fragment { size: u16, tag: u16, offset: u8 },
- }
- impl core::fmt::Display for Repr {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- match self {
- Repr::FirstFragment { size, tag } => {
- write!(f, "FirstFrag size={size} tag={tag}")
- }
- Repr::Fragment { size, tag, offset } => {
- write!(f, "NthFrag size={size} tag={tag} offset={offset}")
- }
- }
- }
- }
- #[cfg(feature = "defmt")]
- impl defmt::Format for Repr {
- fn format(&self, fmt: defmt::Formatter) {
- match self {
- Repr::FirstFragment { size, tag } => {
- defmt::write!(fmt, "FirstFrag size={} tag={}", size, tag);
- }
- Repr::Fragment { size, tag, offset } => {
- defmt::write!(fmt, "NthFrag size={} tag={} offset={}", size, tag, offset);
- }
- }
- }
- }
- 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),
- }
- }
- /// 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)]
- pub enum NextHeader {
- Compressed,
- Uncompressed(IpProtocol),
- }
- impl core::fmt::Display for NextHeader {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- match self {
- NextHeader::Compressed => write!(f, "compressed"),
- NextHeader::Uncompressed(protocol) => write!(f, "{protocol}"),
- }
- }
- }
- #[cfg(feature = "defmt")]
- impl defmt::Format for NextHeader {
- fn format(&self, fmt: defmt::Formatter) {
- match self {
- NextHeader::Compressed => defmt::write!(fmt, "compressed"),
- NextHeader::Uncompressed(protocol) => defmt::write!(fmt, "{}", protocol),
- }
- }
- }
- 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] & 0b111111)
- }
- 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)]
- 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 core::fmt::Display for Repr {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- write!(
- f,
- "IPHC src={} dst={} nxt-hdr={} hop-limit={}",
- self.src_addr, self.dst_addr, self.next_header, self.hop_limit
- )
- }
- }
- #[cfg(feature = "defmt")]
- impl defmt::Format for Repr {
- fn format(&self, fmt: defmt::Formatter) {
- defmt::write!(
- fmt,
- "IPHC src={} dst={} nxt-hdr={} hop-limit={}",
- self.src_addr,
- self.dst_addr,
- self.next_header,
- self.hop_limit
- );
- }
- }
- 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 = 2 + 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);
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- use crate::wire::{Ipv6RoutingHeader, Ipv6RoutingRepr};
- #[cfg(feature = "proto-rpl")]
- use crate::wire::{
- Ipv6Option, Ipv6OptionRepr, Ipv6OptionsIterator, RplHopByHopRepr, RplInstanceId,
- };
- #[cfg(feature = "proto-rpl")]
- const RPL_HOP_BY_HOP_PACKET: [u8; 9] =
- [0xe0, 0x3a, 0x06, 0x63, 0x04, 0x00, 0x1e, 0x03, 0x00];
- const ROUTING_SR_PACKET: [u8; 32] = [
- 0xe3, 0x1e, 0x03, 0x03, 0x99, 0x30, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00,
- 0x05, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
- 0x02, 0x00, 0x00, 0x00,
- ];
- #[test]
- #[cfg(feature = "proto-rpl")]
- fn test_rpl_hop_by_hop_option_deconstruct() {
- let header = ExtHeaderPacket::new_checked(&RPL_HOP_BY_HOP_PACKET).unwrap();
- assert_eq!(
- header.next_header(),
- NextHeader::Uncompressed(IpProtocol::Icmpv6)
- );
- assert_eq!(header.extension_header_id(), ExtHeaderId::HopByHopHeader);
- let options = header.payload();
- let mut options = Ipv6OptionsIterator::new(options);
- let rpl_repr = options.next().unwrap();
- let rpl_repr = rpl_repr.unwrap();
- match rpl_repr {
- Ipv6OptionRepr::Rpl(rpl) => {
- assert_eq!(
- rpl,
- RplHopByHopRepr {
- down: false,
- rank_error: false,
- forwarding_error: false,
- instance_id: RplInstanceId::from(0x1e),
- sender_rank: 0x0300,
- }
- );
- }
- _ => unreachable!(),
- }
- }
- #[test]
- #[cfg(feature = "proto-rpl")]
- fn test_rpl_hop_by_hop_option_emit() {
- let repr = Ipv6OptionRepr::Rpl(RplHopByHopRepr {
- down: false,
- rank_error: false,
- forwarding_error: false,
- instance_id: RplInstanceId::from(0x1e),
- sender_rank: 0x0300,
- });
- let ext_hdr = ExtHeaderRepr {
- ext_header_id: ExtHeaderId::HopByHopHeader,
- next_header: NextHeader::Uncompressed(IpProtocol::Icmpv6),
- length: repr.buffer_len() as u8,
- };
- let mut buffer = vec![0u8; ext_hdr.buffer_len() + repr.buffer_len()];
- ext_hdr.emit(&mut ExtHeaderPacket::new_unchecked(
- &mut buffer[..ext_hdr.buffer_len()],
- ));
- repr.emit(&mut Ipv6Option::new_unchecked(
- &mut buffer[ext_hdr.buffer_len()..],
- ));
- assert_eq!(&buffer[..], RPL_HOP_BY_HOP_PACKET);
- }
- #[test]
- fn test_source_routing_deconstruct() {
- let header = ExtHeaderPacket::new_checked(&ROUTING_SR_PACKET).unwrap();
- assert_eq!(header.next_header(), NextHeader::Compressed);
- assert_eq!(header.extension_header_id(), ExtHeaderId::RoutingHeader);
- let routing_hdr = Ipv6RoutingHeader::new_checked(header.payload()).unwrap();
- let repr = Ipv6RoutingRepr::parse(&routing_hdr).unwrap();
- assert_eq!(
- repr,
- Ipv6RoutingRepr::Rpl {
- segments_left: 3,
- cmpr_i: 9,
- cmpr_e: 9,
- pad: 3,
- addresses: &[
- 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x06, 0x00, 0x06, 0x00, 0x06,
- 0x00, 0x06, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00
- ],
- }
- );
- }
- #[test]
- fn test_source_routing_emit() {
- let routing_hdr = Ipv6RoutingRepr::Rpl {
- segments_left: 3,
- cmpr_i: 9,
- cmpr_e: 9,
- pad: 3,
- addresses: &[
- 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00,
- 0x06, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
- ],
- };
- let ext_hdr = ExtHeaderRepr {
- ext_header_id: ExtHeaderId::RoutingHeader,
- next_header: NextHeader::Compressed,
- length: routing_hdr.buffer_len() as u8,
- };
- let mut buffer = vec![0u8; ext_hdr.buffer_len() + routing_hdr.buffer_len()];
- ext_hdr.emit(&mut ExtHeaderPacket::new_unchecked(
- &mut buffer[..ext_hdr.buffer_len()],
- ));
- routing_hdr.emit(&mut Ipv6RoutingHeader::new_unchecked(
- &mut buffer[ext_hdr.buffer_len()..],
- ));
- assert_eq!(&buffer[..], ROUTING_SR_PACKET);
- }
- }
- /// 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(), [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 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::wire::ieee802154::Frame as Ieee802154Frame;
- use crate::wire::ieee802154::Repr as Ieee802154Repr;
- use crate::wire::Ieee802154Address;
- let key = frag::Key {
- ll_src_addr: Ieee802154Address::Extended([50, 147, 130, 47, 40, 8, 62, 217]),
- ll_dst_addr: Ieee802154Address::Extended([26, 11, 66, 66, 66, 66, 66, 66]),
- datagram_size: 307,
- datagram_tag: 63,
- };
- 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);
- assert_eq!(frag.get_key(&ieee802154_repr), key);
- 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);
- assert_eq!(frag.get_key(&ieee802154_repr), key);
- 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);
- assert_eq!(frag.get_key(&ieee802154_repr), key);
- }
- }
|