use super::{Error, Fault, Faults, Plmn, Tolerated};
use core::fmt;
use core::str::FromStr;
fn within(member: &'static str, value: u64, max: u64) -> Result<(), Error> {
match value <= max {
true => Ok(()),
false => Err(Error::new(member, Fault::Range { given: value, max })),
}
}
fn sized<const N: usize>(member: &'static str, octets: &[u8]) -> Result<[u8; N], Error> {
<[u8; N]>::try_from(octets).map_err(|_| Error::length(member, "octets", octets.len(), N, N))
}
fn first_octet(
member: &'static str,
given: u8,
expected: u8,
faults: &mut Faults,
) -> Result<(), Error> {
if given == expected {
Ok(())
} else if given & 0xf7 == expected {
faults.push(Error::new(member, Fault::Spare { given }));
Ok(())
} else {
Err(Error::new(member, Fault::Octet { given, expected }))
}
}
fn strict<T>(read: Tolerated<T>) -> Result<T, Error> {
match read.1.first() {
Some(fault) => Err(*fault),
None => Ok(read.0),
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct STmsi5g {
amf_set_id: u16,
amf_pointer: u8,
tmsi: u32,
}
impl STmsi5g {
pub fn new(amf_set_id: u16, amf_pointer: u8, tmsi: u32) -> Result<Self, Error> {
within("amf-set-id", amf_set_id.into(), 0x3ff)?;
within("amf-pointer", amf_pointer.into(), 0x3f)?;
Ok(Self {
amf_set_id,
amf_pointer,
tmsi,
})
}
pub fn amf_set_id(&self) -> u16 {
self.amf_set_id
}
pub fn amf_pointer(&self) -> u8 {
self.amf_pointer
}
pub fn tmsi(&self) -> u32 {
self.tmsi
}
pub fn to_u64(&self) -> u64 {
u64::from(self.amf_set_id) << 38 | u64::from(self.amf_pointer) << 32 | u64::from(self.tmsi)
}
pub fn from_u64(bits: u64) -> Result<Self, Error> {
within("5g-s-tmsi", bits, (1 << 48) - 1)?;
Ok(Self {
amf_set_id: (bits >> 38) as u16,
amf_pointer: (bits >> 32) as u8 & 0x3f,
tmsi: bits as u32,
})
}
fn parts(&self) -> [u8; 6] {
let [_, _, parts @ ..] = self.to_u64().to_be_bytes();
parts
}
fn from_parts(parts: [u8; 6]) -> Self {
let [a, b, c, d, e, f] = parts;
let bits = u64::from_be_bytes([0, 0, a, b, c, d, e, f]);
Self {
amf_set_id: (bits >> 38) as u16,
amf_pointer: (bits >> 32) as u8 & 0x3f,
tmsi: bits as u32,
}
}
pub fn to_nas_octets(&self) -> [u8; 7] {
let [a, b, c, d, e, f] = self.parts();
[0xf4, a, b, c, d, e, f]
}
pub fn from_nas_octets(octets: &[u8]) -> Result<Self, Error> {
strict(Self::from_nas_octets_tolerant(octets)?)
}
pub fn from_nas_octets_tolerant(octets: &[u8]) -> Result<Tolerated<Self>, Error> {
let [first, parts @ ..] = sized::<7>("5g-s-tmsi", octets)?;
let mut faults = Faults::new();
first_octet("5g-s-tmsi", first, 0xf4, &mut faults)?;
Ok((Self::from_parts(parts), faults))
}
}
impl fmt::Display for STmsi5g {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "5g-s-tmsi-{:012x}", self.to_u64())
}
}
impl fmt::Debug for STmsi5g {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "STmsi5g({self})")
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Guti5g {
plmn: Plmn,
amf_region_id: u8,
s_tmsi: STmsi5g,
}
impl Guti5g {
pub fn new(
plmn: Plmn,
amf_region_id: u8,
amf_set_id: u16,
amf_pointer: u8,
tmsi: u32,
) -> Result<Self, Error> {
let s_tmsi = STmsi5g::new(amf_set_id, amf_pointer, tmsi)?;
Ok(Self::of(plmn, amf_region_id, s_tmsi))
}
pub fn of(plmn: Plmn, amf_region_id: u8, s_tmsi: STmsi5g) -> Self {
Self {
plmn,
amf_region_id,
s_tmsi,
}
}
pub fn plmn(&self) -> Plmn {
self.plmn
}
pub fn amf_region_id(&self) -> u8 {
self.amf_region_id
}
pub fn amf_set_id(&self) -> u16 {
self.s_tmsi.amf_set_id
}
pub fn amf_pointer(&self) -> u8 {
self.s_tmsi.amf_pointer
}
pub fn tmsi(&self) -> u32 {
self.s_tmsi.tmsi
}
pub fn s_tmsi(&self) -> STmsi5g {
self.s_tmsi
}
pub fn to_nas_octets(&self) -> [u8; 11] {
let [p1, p2, p3] = self.plmn.to_nas_octets();
let [a, b, c, d, e, f] = self.s_tmsi.parts();
[0xf2, p1, p2, p3, self.amf_region_id, a, b, c, d, e, f]
}
pub fn from_nas_octets(octets: &[u8]) -> Result<Self, Error> {
strict(Self::from_nas_octets_tolerant(octets)?)
}
pub fn from_nas_octets_tolerant(octets: &[u8]) -> Result<Tolerated<Self>, Error> {
let [first, p1, p2, p3, amf_region_id, parts @ ..] = sized::<11>("5g-guti", octets)?;
let mut faults = Faults::new();
first_octet("5g-guti", first, 0xf2, &mut faults)?;
let plmn = Plmn::from_nas_octets([p1, p2, p3])?;
let guti = Self::of(plmn, amf_region_id, STmsi5g::from_parts(parts));
Ok((guti, faults))
}
}
impl fmt::Display for Guti5g {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let rest = u64::from(self.amf_region_id) << 48 | self.s_tmsi.to_u64();
write!(f, "5g-guti-{}{rest:014x}", self.plmn.digits())
}
}
impl fmt::Debug for Guti5g {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Guti5g({self})")
}
}
fn written(member: &'static str, prefix: &str, text: &str) -> Result<(Plmn, u64), Error> {
let expected = match prefix {
"5g-guti-" => "5g-guti-, the digits of the PLMN and 14 hexadecimal digits",
_ => "4g-guti-, the digits of the PLMN and 14 hexadecimal digits",
};
let form = Error::new(member, Fault::Form { expected });
let rest = text.strip_prefix(prefix).ok_or(form)?;
if !rest.is_ascii() || !(19..=20).contains(&rest.len()) {
return Err(form);
}
let (plmn, bits) = rest.split_at(rest.len() - 14);
let plmn = Plmn::new(&plmn[..3], &plmn[3..])?;
let plain = bits.bytes().all(|digit| digit.is_ascii_hexdigit());
let bits = u64::from_str_radix(bits, 16).ok().filter(|_| plain);
Ok((plmn, bits.ok_or(form)?))
}
impl FromStr for Guti5g {
type Err = Error;
fn from_str(text: &str) -> Result<Self, Error> {
let (plmn, bits) = written("5g-guti", "5g-guti-", text)?;
let s_tmsi = STmsi5g::from_u64(bits & ((1 << 48) - 1))?;
Ok(Self::of(plmn, (bits >> 48) as u8, s_tmsi))
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct STmsiEps {
mme_code: u8,
m_tmsi: u32,
}
impl STmsiEps {
pub fn new(mme_code: u8, m_tmsi: u32) -> Self {
Self { mme_code, m_tmsi }
}
pub fn mme_code(&self) -> u8 {
self.mme_code
}
pub fn m_tmsi(&self) -> u32 {
self.m_tmsi
}
pub fn to_u64(&self) -> u64 {
u64::from(self.mme_code) << 32 | u64::from(self.m_tmsi)
}
}
impl fmt::Display for STmsiEps {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "s-tmsi-{:010x}", self.to_u64())
}
}
impl fmt::Debug for STmsiEps {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "STmsiEps({self})")
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GutiEps {
plmn: Plmn,
mme_group_id: u16,
s_tmsi: STmsiEps,
}
impl GutiEps {
pub fn new(plmn: Plmn, mme_group_id: u16, mme_code: u8, m_tmsi: u32) -> Self {
Self {
plmn,
mme_group_id,
s_tmsi: STmsiEps::new(mme_code, m_tmsi),
}
}
pub fn plmn(&self) -> Plmn {
self.plmn
}
pub fn mme_group_id(&self) -> u16 {
self.mme_group_id
}
pub fn mme_code(&self) -> u8 {
self.s_tmsi.mme_code
}
pub fn m_tmsi(&self) -> u32 {
self.s_tmsi.m_tmsi
}
pub fn s_tmsi(&self) -> STmsiEps {
self.s_tmsi
}
pub fn to_nas_octets(&self) -> [u8; 11] {
let [p1, p2, p3] = self.plmn.to_nas_octets();
let [g1, g2] = self.mme_group_id.to_be_bytes();
let [t1, t2, t3, t4] = self.s_tmsi.m_tmsi.to_be_bytes();
[
0xf6,
p1,
p2,
p3,
g1,
g2,
self.s_tmsi.mme_code,
t1,
t2,
t3,
t4,
]
}
pub fn from_nas_octets(octets: &[u8]) -> Result<Self, Error> {
let [first, p1, p2, p3, g1, g2, mme_code, tmsi @ ..] = sized::<11>("guti", octets)?;
if first != 0xf6 {
let fault = Fault::Octet {
given: first,
expected: 0xf6,
};
return Err(Error::new("guti", fault));
}
let plmn = Plmn::from_nas_octets([p1, p2, p3])?;
let mme_group_id = u16::from_be_bytes([g1, g2]);
Ok(Self::new(
plmn,
mme_group_id,
mme_code,
u32::from_be_bytes(tmsi),
))
}
}
impl fmt::Display for GutiEps {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let rest = u64::from(self.mme_group_id) << 40 | self.s_tmsi.to_u64();
write!(f, "4g-guti-{}{rest:014x}", self.plmn.digits())
}
}
impl fmt::Debug for GutiEps {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "GutiEps({self})")
}
}
impl FromStr for GutiEps {
type Err = Error;
fn from_str(text: &str) -> Result<Self, Error> {
let (plmn, bits) = written("guti", "4g-guti-", text)?;
Ok(Self::new(
plmn,
(bits >> 40) as u16,
(bits >> 32) as u8,
bits as u32,
))
}
}