use super::super::*;
pub const NI_NONCE_LEN: usize = 8;
pub const NI_QTYPE_NOOP: u16 = 0;
pub const NI_QTYPE_NODE_NAME: u16 = 2;
pub const NI_QTYPE_NODE_ADDRESSES: u16 = 3;
pub const NI_QTYPE_IPV4_ADDRESSES: u16 = 4;
pub const NI_QUERY_CODE_SUBJECT_IPV6: u8 = 0;
pub const NI_QUERY_CODE_SUBJECT_NAME: u8 = 1;
pub const NI_QUERY_CODE_SUBJECT_IPV4: u8 = 2;
pub const NI_RESPONSE_CODE_SUCCESS: u8 = 0;
pub const NI_RESPONSE_CODE_REFUSED: u8 = 1;
pub const NI_RESPONSE_CODE_UNKNOWN_QTYPE: u8 = 2;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NodeInformation {
pub(crate) nonce: [u8; NI_NONCE_LEN],
pub(crate) data: Vec<u8>,
}
impl NodeInformation {
pub fn new(nonce: [u8; NI_NONCE_LEN]) -> Self {
Self {
nonce,
data: Vec::new(),
}
}
pub fn nonce(mut self, nonce: [u8; NI_NONCE_LEN]) -> Self {
self.nonce = nonce;
self
}
pub fn data(mut self, data: impl Into<Vec<u8>>) -> Self {
self.data = data.into();
self
}
pub fn nonce_value(&self) -> [u8; NI_NONCE_LEN] {
self.nonce
}
pub fn data_value(&self) -> &[u8] {
&self.data
}
}
impl Layer for NodeInformation {
fn name(&self) -> &'static str {
"NodeInformation"
}
fn summary(&self) -> String {
format!(
"NodeInformation(nonce={}, data={}B)",
hex_bytes(&self.nonce),
self.data.len()
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("nonce", hex_bytes(&self.nonce)),
("data_len", self.data.len().to_string()),
("data", hex_bytes(&self.data)),
]
}
fn encoded_len(&self) -> usize {
NI_NONCE_LEN + self.data.len()
}
fn compile(&self, _ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
out.extend_from_slice(&self.nonce);
out.extend_from_slice(&self.data);
Ok(())
}
impl_layer_object!(NodeInformation);
}
impl_layer_div!(NodeInformation);
fn node_info_rest_of_header(qtype: u16, flags: u16) -> [u8; 4] {
let qtype = qtype.to_be_bytes();
let flags = flags.to_be_bytes();
[qtype[0], qtype[1], flags[0], flags[1]]
}
impl Icmpv6 {
pub fn node_information_query(
qtype: u16,
flags: u16,
nonce: [u8; NI_NONCE_LEN],
data: impl Into<Vec<u8>>,
) -> Packet {
Self::node_information_message(
ICMPV6_NODE_INFORMATION_QUERY,
qtype,
flags,
NodeInformation::new(nonce).data(data),
)
}
pub fn node_information_response(
qtype: u16,
flags: u16,
nonce: [u8; NI_NONCE_LEN],
data: impl Into<Vec<u8>>,
) -> Packet {
Self::node_information_message(
ICMPV6_NODE_INFORMATION_RESPONSE,
qtype,
flags,
NodeInformation::new(nonce).data(data),
)
}
fn node_information_message(
icmp_type: u8,
qtype: u16,
flags: u16,
body: NodeInformation,
) -> Packet {
Self::new()
.icmp_type(icmp_type)
.code(0)
.rest_of_header(node_info_rest_of_header(qtype, flags))
/ body
}
}
pub(crate) fn decode_node_information(bytes: &[u8]) -> Result<NodeInformation> {
if bytes.len() < NI_NONCE_LEN {
return Err(CrafterError::buffer_too_short(
"icmpv6.node_information.nonce",
NI_NONCE_LEN,
bytes.len(),
));
}
let mut nonce = [0u8; NI_NONCE_LEN];
nonce.copy_from_slice(&bytes[..NI_NONCE_LEN]);
Ok(NodeInformation {
nonce,
data: bytes[NI_NONCE_LEN..].to_vec(),
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::protocols::icmp::{
Icmpv6, Icmpv6Body, NodeInformation, ICMPV6_NODE_INFORMATION_QUERY,
ICMPV6_NODE_INFORMATION_RESPONSE,
};
use crate::{Ipv6, NetworkLayer, Packet};
use core::net::Ipv6Addr;
const ICMPV6_OFFSET: usize = 40;
fn doc_src() -> Ipv6Addr {
Ipv6Addr::new(0x2001, 0x0db8, 1, 0, 0, 0, 0, 0x0010)
}
fn doc_dst() -> Ipv6Addr {
Ipv6Addr::new(0x2001, 0x0db8, 2, 0, 0, 0, 0, 0x0020)
}
#[test]
fn node_information_query_round_trips() {
let nonce = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
let subject = doc_dst().octets().to_vec();
let compiled = (Ipv6::new().src(doc_src()).dst(doc_dst()).hop_limit(64)
/ Icmpv6::node_information_query(
NI_QTYPE_NODE_ADDRESSES,
0x0010,
nonce,
subject.clone(),
))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(bytes[ICMPV6_OFFSET], ICMPV6_NODE_INFORMATION_QUERY);
assert_eq!(bytes[ICMPV6_OFFSET + 1], 0);
assert_eq!(
&bytes[ICMPV6_OFFSET + 4..ICMPV6_OFFSET + 6],
&NI_QTYPE_NODE_ADDRESSES.to_be_bytes()
);
assert_eq!(
&bytes[ICMPV6_OFFSET + 6..ICMPV6_OFFSET + 8],
&0x0010u16.to_be_bytes()
);
assert_eq!(&bytes[ICMPV6_OFFSET + 8..ICMPV6_OFFSET + 16], &nonce);
assert_eq!(&bytes[ICMPV6_OFFSET + 16..], &subject[..]);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, bytes).unwrap();
let icmpv6 = decoded.layer::<Icmpv6>().unwrap();
assert_eq!(icmpv6.icmp_type_value(), ICMPV6_NODE_INFORMATION_QUERY);
assert_eq!(
icmpv6.body(),
Icmpv6Body::NodeInformationQuery {
qtype: NI_QTYPE_NODE_ADDRESSES,
flags: 0x0010,
}
);
let ni = decoded.layer::<NodeInformation>().unwrap();
assert_eq!(ni.nonce_value(), nonce);
assert_eq!(ni.data_value(), &subject[..]);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn node_information_response_round_trips() {
let nonce = [0xde, 0xad, 0xbe, 0xef, 0x00, 0x11, 0x22, 0x33];
let mut answer = Vec::new();
answer.extend_from_slice(&3600u32.to_be_bytes());
answer.extend_from_slice(&doc_src().octets());
let compiled = (Ipv6::new().src(doc_dst()).dst(doc_src()).hop_limit(64)
/ Icmpv6::node_information_response(NI_QTYPE_NODE_ADDRESSES, 0, nonce, answer.clone()))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(bytes[ICMPV6_OFFSET], ICMPV6_NODE_INFORMATION_RESPONSE);
assert_eq!(bytes[ICMPV6_OFFSET + 1], 0);
assert_eq!(
&bytes[ICMPV6_OFFSET + 4..ICMPV6_OFFSET + 6],
&NI_QTYPE_NODE_ADDRESSES.to_be_bytes()
);
assert_eq!(&bytes[ICMPV6_OFFSET + 6..ICMPV6_OFFSET + 8], &[0, 0]);
assert_eq!(&bytes[ICMPV6_OFFSET + 8..ICMPV6_OFFSET + 16], &nonce);
assert_eq!(&bytes[ICMPV6_OFFSET + 16..], &answer[..]);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, bytes).unwrap();
let icmpv6 = decoded.layer::<Icmpv6>().unwrap();
assert_eq!(icmpv6.icmp_type_value(), ICMPV6_NODE_INFORMATION_RESPONSE);
assert_eq!(
icmpv6.body(),
Icmpv6Body::NodeInformationResponse {
qtype: NI_QTYPE_NODE_ADDRESSES,
flags: 0,
}
);
let ni = decoded.layer::<NodeInformation>().unwrap();
assert_eq!(ni.nonce_value(), nonce);
assert_eq!(ni.data_value(), &answer[..]);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn node_information_noop_query_round_trips() {
let nonce = [1, 2, 3, 4, 5, 6, 7, 8];
let compiled = (Ipv6::new().src(doc_src()).dst(doc_dst()).hop_limit(64)
/ Icmpv6::node_information_query(NI_QTYPE_NOOP, 0, nonce, Vec::new()))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(bytes.len(), ICMPV6_OFFSET + 8 + NI_NONCE_LEN);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, bytes).unwrap();
let ni = decoded.layer::<NodeInformation>().unwrap();
assert_eq!(ni.nonce_value(), nonce);
assert!(ni.data_value().is_empty());
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn node_information_short_body_is_error() {
assert!(decode_node_information(&[0u8; NI_NONCE_LEN - 1]).is_err());
}
}