use bitvec::prelude::*;
use bytes::{BufMut, Bytes};
use someip_parse::SomeIpHeader;
use std::convert::TryInto;
use std::{
convert::TryFrom,
fmt::Display,
io::ErrorKind,
net::{Ipv4Addr, Ipv6Addr},
};
use crate::SomeIpPacket;
#[derive(PartialEq, Debug, Clone)]
pub struct SDMessage {
header: SomeIpHeader,
entries: Vec<ServiceEntry>,
options: Vec<SDOption>,
flags_reserved: u32,
}
impl SDMessage {
pub fn set_options(&mut self, options: Vec<SDOption>) {
self.options = options;
}
pub fn is_reboot(&self) -> bool {
let flags: &BitSlice<u32, Msb0> = self.flags_reserved.view_bits();
flags[0]
}
pub fn set_reboot(&mut self, reboot: bool) {
let flags: &mut BitSlice<u32, Msb0> = self.flags_reserved.view_bits_mut();
flags.set(0, reboot);
}
pub fn is_unicast(&self) -> bool {
let flags: &BitSlice<u32, Msb0> = self.flags_reserved.view_bits();
flags[1]
}
pub fn set_unicast(&mut self, unicast: bool) {
let flags: &mut BitSlice<u32, Msb0> = self.flags_reserved.view_bits_mut();
flags.set(1, unicast);
}
pub fn is_exp_initial_data_control(&self) -> bool {
let flags: BitArray<u32, Msb0> = BitArray::from(self.flags_reserved);
flags[2]
}
pub fn set_exp_initial_data_control(&mut self, initial_data_ctrl: bool) {
let flags: &mut BitSlice<u32, Msb0> = self.flags_reserved.view_bits_mut();
flags.set(2, initial_data_ctrl);
}
pub fn add_entry(
&mut self,
mut entry: ServiceEntry,
config_index1: u8,
config_run1: u8,
config_index2: u8,
config_run2: u8,
) -> Result<(), std::io::Error> {
if config_index1 > 0xF || config_run1 > 0xF || config_index2 > 0xF || config_run2 > 0xF {
return Err(std::io::Error::from(ErrorKind::InvalidInput));
}
if config_run1 > 0 && (config_index1 + config_run1) as usize >= self.options.len() {
log::error!("config index+run1 is out of range. Options entries don't exist");
return Err(std::io::Error::from(ErrorKind::NotFound));
}
if config_run2 > 0 && (config_index2 + config_run2) as usize >= self.options.len() {
log::error!("config index+run2 is out of range. Options entries don't exist");
return Err(std::io::Error::from(ErrorKind::NotFound));
}
entry.set_index1_options(config_index1, config_run1);
entry.set_index2_options(config_index2, config_run2);
self.entries.push(entry);
Ok(())
}
}
impl Default for SDMessage {
fn default() -> Self {
SDMessage {
header: SomeIpHeader {
message_id: 0xFFFF_8100,
interface_version: 0x1,
message_type: someip_parse::MessageType::Notification,
length: 20, ..Default::default()
},
entries: Vec::new(),
options: Vec::new(),
flags_reserved: 0,
}
}
}
impl From<SDMessage> for SomeIpPacket {
fn from(msg: SDMessage) -> Self {
let mut payload: Vec<u8> = Vec::new();
payload.put_u32(msg.flags_reserved);
payload.put_u32(msg.entries.len() as u32); for entry in msg.entries {
let e_raw = entry.as_buffer();
payload.extend(e_raw);
}
let mut option_raw = Vec::new();
for option in msg.options {
let o_raw: Vec<u8> = option.into();
option_raw.extend(o_raw);
}
payload.put_u32(option_raw.len() as u32); payload.extend(option_raw);
SomeIpPacket::new(msg.header, Bytes::from(payload))
}
}
impl TryFrom<SomeIpPacket> for SDMessage {
type Error = ();
fn try_from(pkt: SomeIpPacket) -> Result<Self, Self::Error> {
if !pkt.header().is_someip_sd()
|| pkt.header().interface_version != 1
|| pkt.header().message_type != someip_parse::MessageType::Notification
{
return Err(());
}
let header = pkt.header().clone();
let payload = pkt.payload();
let flags_reserved = u32::from_be_bytes(payload[0..4].try_into().unwrap());
let num_entries = u32::from_be_bytes(payload[4..4 + 4].try_into().unwrap()) as usize;
let option_length_index = 8 + num_entries * 16;
if payload.len() < option_length_index {
log::error!("Invalid packet. Not enough bytes for service entries");
return Err(());
}
let entries_slice = &payload[8..option_length_index];
let mut entries = Vec::new();
for entry in entries_slice.chunks(16) {
if let Ok(entry) = ServiceEntry::try_from(entry) {
entries.push(entry);
} else {
log::error!("Failed to parse ServiceENtry");
return Err(());
}
}
if entries.len() != num_entries {
log::error!("Incorrect number of entries");
return Err(());
}
let options_slice = &payload[option_length_index..];
let options_length_in_bytes =
u32::from_be_bytes(options_slice[0..4].try_into().unwrap()) as usize;
let options_buffer_slice = &options_slice[4..];
if options_length_in_bytes != options_buffer_slice.len() {
log::error!(
"Inconsistent options length field expected:{} actual:{}",
options_length_in_bytes,
options_buffer_slice.len()
);
return Err(());
}
let mut options = Vec::new();
let mut option_start_index = 0;
loop {
if option_start_index + 2 > options_buffer_slice.len() {
break;
}
let len = u16::from_be_bytes(
options_buffer_slice[option_start_index..option_start_index + 2]
.try_into()
.unwrap(),
) as usize;
let option_end_index = option_start_index + len + 3;
let option_slice = &options_buffer_slice[option_start_index..option_end_index];
if let Ok(option) = SDOption::try_from(option_slice) {
options.push(option);
} else {
log::error!("Error parsing SDOption");
return Err(());
}
option_start_index = option_end_index;
}
Ok(SDMessage {
header,
entries,
options,
flags_reserved,
})
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum SDOptionTransportProto {
UDP,
TCP,
}
#[derive(Debug, PartialEq, Clone)]
pub struct ConfigurationKey(String);
impl ConfigurationKey {
pub fn new(name: &str) -> Self {
if !name.is_ascii() {
panic!("Only ascii names are allowed");
} else {
ConfigurationKey(String::from(name))
}
}
}
impl Display for ConfigurationKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug,PartialEq,Clone)]
#[rustfmt::skip]
pub enum SDOption {
Configurations( Vec<(ConfigurationKey, String)>),
LoadBalancing { priority: u16, weight: u16 },
Ipv4Endpoint { addr : Ipv4Addr , transport : SDOptionTransportProto, port: u16},
Ipv6Endpoint { addr : Ipv6Addr , transport : SDOptionTransportProto, port: u16},
Ipv4Multicast { addr: Ipv4Addr, port: u16},
Ipv6Multicast { addr: Ipv6Addr, port: u16},
Ipv4SDEndpoint {addr : Ipv4Addr, port: u16},
Ipv6SDEndpoint {addr : Ipv6Addr, port: u16},
}
impl TryFrom<&[u8]> for SDOption {
type Error = ();
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
let length: usize = u16::from_be_bytes(data[0..2].try_into().unwrap()) as usize;
if data.len() != length + 3 {
log::error!("Invalid length in packet");
return Err(());
}
let ty: u8 = data[2];
let packet = &data[4..];
match ty {
0x01 => {
let configs = parse_configuration_string(&packet[..length - 1]);
Ok(SDOption::Configurations(configs))
}
0x02 => {
if length != 5 {
Err(())
} else {
let priority = u16::from_be_bytes(data[4..6].try_into().unwrap());
let weight = u16::from_be_bytes(data[6..8].try_into().unwrap());
Ok(SDOption::LoadBalancing { priority, weight })
}
}
0x04 => {
if length != 9 {
Err(())
} else {
let address = u32::from_be_bytes(data[4..8].try_into().unwrap());
let l4_proto = match data[9] {
0x6 => SDOptionTransportProto::TCP,
0x11 => SDOptionTransportProto::UDP,
_ => return Err(()),
};
let port = u16::from_be_bytes(data[10..12].try_into().unwrap());
Ok(SDOption::Ipv4Endpoint {
addr: Ipv4Addr::from(address),
transport: l4_proto,
port,
})
}
}
0x06 => {
if length != 0x15 {
Err(())
} else {
let address = u128::from_be_bytes(data[4..4 + 16].try_into().unwrap());
let l4_proto = match data[21] {
0x6 => SDOptionTransportProto::TCP,
0x11 => SDOptionTransportProto::UDP,
_ => return Err(()),
};
let port = u16::from_be_bytes(data[22..24].try_into().unwrap());
Ok(SDOption::Ipv6Endpoint {
addr: Ipv6Addr::from(address),
transport: l4_proto,
port,
})
}
}
0x14 => {
if length != 0x9 {
Err(())
} else {
let address = u32::from_be_bytes(data[4..8].try_into().unwrap());
let _l4_proto = match data[9] {
0x11 => SDOptionTransportProto::UDP, _ => return Err(()),
};
let port = u16::from_be_bytes(data[10..12].try_into().unwrap());
Ok(SDOption::Ipv4Multicast {
addr: Ipv4Addr::from(address),
port,
})
}
}
0x16 => {
if length != 0x15 {
Err(())
} else {
let address = u128::from_be_bytes(data[4..4 + 16].try_into().unwrap());
let _l4_proto = match data[21] {
0x11 => SDOptionTransportProto::UDP,
_ => return Err(()),
};
let port = u16::from_be_bytes(data[22..24].try_into().unwrap());
Ok(SDOption::Ipv6Multicast {
addr: Ipv6Addr::from(address),
port,
})
}
}
0x24 => {
if length != 0x9 {
Err(())
} else {
let address = u32::from_be_bytes(data[4..8].try_into().unwrap());
let _l4_proto = match data[9] {
0x11 => SDOptionTransportProto::UDP, _ => return Err(()),
};
let port = u16::from_be_bytes(data[10..12].try_into().unwrap());
Ok(SDOption::Ipv4SDEndpoint {
addr: Ipv4Addr::from(address),
port,
})
}
}
0x26 => {
if length != 0x15 {
Err(())
} else {
let address = u128::from_be_bytes(data[4..4 + 16].try_into().unwrap());
let _l4_proto = match data[21] {
0x11 => SDOptionTransportProto::UDP,
_ => return Err(()),
};
let port = u16::from_be_bytes(data[22..24].try_into().unwrap());
Ok(SDOption::Ipv6SDEndpoint {
addr: Ipv6Addr::from(address),
port,
})
}
}
_ => Err(()),
}
}
}
fn parse_configuration_string(data: &[u8]) -> Vec<(ConfigurationKey, String)> {
let mut current_pos: usize = 0;
let mut entries: Vec<(ConfigurationKey, String)> = Vec::new();
loop {
let len = data[current_pos] as usize;
if len == 0 {
break;
}
if len + current_pos >= data.len() {
println!(
"Invalid data in buffer : current_pos({}) total:{}",
current_pos,
data.len()
);
break;
}
let record = &data[current_pos + 1..(len + current_pos + 1)];
if let Ok(record) = String::from_utf8(record.to_owned()) {
let mut iter = record.split('=');
if let Some(key) = iter.next() {
if let Some(value) = iter.next() {
entries.push((ConfigurationKey::new(key), String::from(value)));
} else {
break;
}
} else {
break;
}
} else {
break;
}
current_pos += len + 1;
}
entries
}
#[allow(clippy::from_over_into)]
impl Into<Vec<u8>> for SDOption {
fn into(self) -> Vec<u8> {
let mut option_bytes = Vec::new();
match self {
SDOption::Configurations(configs) => {
let mut cfg_entry: Vec<u8> = Vec::new();
for config in configs {
let cfg_string = format!("{}={}", config.0, config.1);
let len = cfg_string.len() as u8;
cfg_entry.push(len);
cfg_entry.extend(cfg_string.as_bytes());
}
cfg_entry.push(0u8);
let config_option_len: u16 = cfg_entry.len() as u16 + 1; let len_bytes = config_option_len.to_be_bytes();
option_bytes.extend(len_bytes);
option_bytes.push(0x01); option_bytes.push(0x0); option_bytes.extend(cfg_entry);
}
SDOption::LoadBalancing { priority, weight } => {
option_bytes.put_u16(5);
option_bytes.push(0x2);
option_bytes.push(0); option_bytes.put_u16(priority);
option_bytes.put_u16(weight);
}
SDOption::Ipv4Endpoint {
addr,
transport,
port,
} => {
option_bytes.put_u16(9);
option_bytes.push(4);
option_bytes.push(0); option_bytes.put_u32(addr.into());
option_bytes.push(0); if let SDOptionTransportProto::TCP = transport {
option_bytes.push(0x6);
} else {
option_bytes.push(0x11);
}
option_bytes.put_u16(port);
}
SDOption::Ipv6Endpoint {
addr,
transport,
port,
} => {
option_bytes.put_u16(0x15);
option_bytes.push(0x6);
option_bytes.push(0); option_bytes.put_u128(addr.into());
option_bytes.push(0); if let SDOptionTransportProto::TCP = transport {
option_bytes.push(0x6);
} else {
option_bytes.push(0x11);
}
option_bytes.put_u16(port);
}
SDOption::Ipv4Multicast { addr, port } => {
option_bytes.put_u16(0x9);
option_bytes.push(0x14);
option_bytes.push(0); option_bytes.put_u32(addr.into());
option_bytes.push(0); option_bytes.push(0x11); option_bytes.put_u16(port);
}
SDOption::Ipv6Multicast { addr, port } => {
option_bytes.put_u16(0x15);
option_bytes.push(0x16); option_bytes.push(0); option_bytes.put_u128(addr.into());
option_bytes.push(0); option_bytes.push(0x11); option_bytes.put_u16(port);
}
SDOption::Ipv4SDEndpoint { addr, port } => {
option_bytes.put_u16(0x9);
option_bytes.push(0x24);
option_bytes.push(0); option_bytes.put_u32(addr.into());
option_bytes.push(0); option_bytes.push(0x11); option_bytes.put_u16(port);
}
SDOption::Ipv6SDEndpoint { addr, port } => {
option_bytes.put_u16(0x15);
option_bytes.push(0x26); option_bytes.push(0); option_bytes.put_u128(addr.into());
option_bytes.push(0); option_bytes.push(0x11); option_bytes.put_u16(port);
}
}
option_bytes
}
}
#[derive(PartialEq, Debug)]
pub enum ServiceEntryType {
Find,
Offer,
}
#[allow(clippy::from_over_into)]
impl Into<u8> for ServiceEntryType {
fn into(self) -> u8 {
match self {
ServiceEntryType::Find => 0,
ServiceEntryType::Offer => 1,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ServiceEntry {
data: BitArray<[u8; 16], Msb0>,
}
impl Default for ServiceEntry {
fn default() -> Self {
let mut entry = ServiceEntry {
data: BitArray::default(),
};
entry.set_major_version(0xFF);
entry.set_minor_version(0xFFFFFFFF);
entry.set_instance_id(0xFFFF);
entry
}
}
impl TryFrom<&[u8]> for ServiceEntry {
type Error = ();
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
if buf.len() != 16 {
log::error!("Invalid length for ServiceEntry buffer");
return Err(());
}
let mut data = [0u8; 16];
for (i, b) in buf.iter().enumerate() {
data[i] = *b;
}
let mut entry = ServiceEntry {
data: BitArray::new(data),
};
match buf[0] {
0 => entry.set_service_entry_type(ServiceEntryType::Find),
1 => entry.set_service_entry_type(ServiceEntryType::Offer),
_ => return Err(()),
}
Ok(entry)
}
}
impl ServiceEntry {
pub fn set_service_entry_type(&mut self, ty: ServiceEntryType) {
let ty: u8 = ty.into();
self.data[..8].store(ty);
}
pub fn service_entry_type(&self) -> Option<ServiceEntryType> {
let ty: u8 = self.data[..8].load();
match ty {
0 => Some(ServiceEntryType::Find),
1 => Some(ServiceEntryType::Offer),
_ => None,
}
}
pub fn set_index1_options(&mut self, i: u8, num_options: u8) {
self.data[8..16].store(i);
self.data[24..28].store(num_options);
}
pub fn index1_options(&self) -> (u8, u8) {
(self.data[8..16].load(), self.data[24..28].load())
}
pub fn set_index2_options(&mut self, i: u8, num_options: u8) {
self.data[16..24].store(i);
self.data[28..32].store(num_options);
}
pub fn index2_options(&self) -> (u8, u8) {
(self.data[16..24].load(), self.data[28..32].load())
}
pub fn set_service_id(&mut self, service_id: u16) {
self.data[32..48].store(service_id);
}
pub fn service_id(&self) -> u16 {
self.data[32..48].load()
}
pub fn set_instance_id(&mut self, instance_id: u16) {
self.data[48..64].store(instance_id);
}
pub fn instance_id(&self) -> u16 {
self.data[48..64].load()
}
pub fn set_major_version(&mut self, version: u8) {
self.data[64..72].store(version);
}
pub fn major_version(&self) -> u8 {
self.data[64..72].load()
}
pub fn set_ttl(&mut self, ttl: u32) {
self.data[72..96].store(ttl);
}
pub fn ttl(&self) -> u32 {
self.data[72..96].load()
}
pub fn set_minor_version(&mut self, minor_version: u32) {
self.data[96..].store(minor_version);
}
pub fn minor_version(&self) -> u32 {
self.data[96..].load()
}
pub fn as_buffer(&self) -> &[u8] {
self.data.as_raw_slice()
}
}
#[derive(Default)]
pub struct EventGroupEntry {
data: BitArray<[u8; 16], Msb0>,
}
impl EventGroupEntry {
pub fn set_type(&mut self, ty: ServiceEntryType) {
let ty: u8 = ty.into();
self.data[..8].store(ty);
}
pub fn set_index_1_options(&mut self, i: u8, num_options: u8) {
self.data[8..16].store(i);
self.data[24..28].store(num_options);
}
pub fn set_index_2_options(&mut self, i: u8, num_options: u8) {
self.data[16..24].store(i);
self.data[28..32].store(num_options);
}
pub fn set_initial_data_requested(&mut self, flag: bool) {
self.data[104..105].store(if flag { 1u8 } else { 0u8 });
}
pub fn set_major_version(&mut self, version: u8) {
self.data[64..72].store(version);
}
pub fn set_ttl(&mut self, ttl: u32) {
self.data[72..96].store(ttl);
}
pub fn set_counter(&mut self, counter: u8) {
self.data[108..112].store(counter);
}
pub fn set_event_group_id(&mut self, eventgroup_id: u16) {
self.data[112..128].store(eventgroup_id);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sd_message_basic() {
let message = SDMessage::default();
let pkt: SomeIpPacket = message.clone().into();
let new_msg = SDMessage::try_from(pkt).unwrap();
assert_eq!(message, new_msg);
}
#[test]
fn test_sd_message() {
let mut message = SDMessage::default();
let options = vec![
SDOption::Ipv4Endpoint {
addr: Ipv4Addr::new(127, 0, 0, 1),
transport: SDOptionTransportProto::TCP,
port: 8090,
},
SDOption::Configurations(vec![(
ConfigurationKey::new("ConfigKey"),
String::from("ConfigValue"),
)]),
];
message.set_options(options);
let mut entry = ServiceEntry::default();
entry.set_service_entry_type(ServiceEntryType::Offer);
entry.set_service_id(42);
message.add_entry(entry, 0, 1, 0, 0).unwrap();
message.set_exp_initial_data_control(true);
message.set_reboot(true);
let mut entry = ServiceEntry::default();
entry.set_service_entry_type(ServiceEntryType::Find);
entry.set_service_id(0x24);
if message.add_entry(entry, 0, 2, 0, 0).is_ok() {
panic!("This should fail");
}
let mut entry = ServiceEntry::default();
entry.set_service_entry_type(ServiceEntryType::Find);
entry.set_service_id(0x24);
if message.add_entry(entry, 0, 0, 0, 2).is_ok() {
panic!("This should fail");
}
let pkt: SomeIpPacket = message.into();
let new_entry = SDMessage::try_from(pkt).unwrap();
assert!(new_entry.is_reboot());
assert!(new_entry.is_exp_initial_data_control());
println!("SDMessage:{:?}", new_entry);
}
#[test]
fn test_service_entry() {
let mut entry = ServiceEntry::default();
entry.set_service_entry_type(ServiceEntryType::Find);
assert_eq!(entry.service_entry_type().unwrap(), ServiceEntryType::Find);
entry.set_index1_options(1, 2);
assert_eq!((1, 2), entry.index1_options());
entry.set_index2_options(2, 3);
assert_eq!((2, 3), entry.index2_options());
entry.set_instance_id(0x9);
assert_eq!(0x9, entry.instance_id());
entry.set_service_id(0x10);
assert_eq!(0x10, entry.service_id());
entry.set_major_version(1);
assert_eq!(1, entry.major_version());
entry.set_minor_version(0xF0F0F0F0);
assert_eq!(0xF0F0F0F0, entry.minor_version());
entry.set_ttl(500);
assert_eq!(500, entry.ttl());
assert_eq!(
entry.as_buffer(),
&[
0, 1, 2, 0b00100011, 0b00010000, 0, 0b1001, 0, 1, 0b11110100, 1, 0, 0xF0, 0xF0,
0xF0, 0xF0
]
);
let sl = entry.as_buffer();
let new_entry = ServiceEntry::try_from(&sl[..]).unwrap();
assert_eq!(entry, new_entry);
}
#[test]
fn test_config_option_configurations() {
let options = vec![
(
ConfigurationKey::new("key1"),
String::from("ConfigValueOption"),
),
(ConfigurationKey::new("key2"), String::from("value2")),
(ConfigurationKey::new("key3"), String::from("value3")),
];
let sd_entry = SDOption::Configurations(options);
let bytes: Vec<u8> = sd_entry.into();
let serialized = SDOption::try_from(&bytes[..]).expect("Unable to parse into SDOption");
if let SDOption::Configurations(options) = serialized {
assert_eq!(options[0].0, ConfigurationKey::new("key1"));
assert_eq!(options[0].1, String::from("ConfigValueOption"));
assert_eq!(options[1].0, ConfigurationKey::new("key2"));
assert_eq!(options[1].1, String::from("value2"));
assert_eq!(options[2].0, ConfigurationKey::new("key3"));
assert_eq!(options[2].1, String::from("value3"));
} else {
panic!("Expected Configurations");
}
}
#[test]
fn test_config_option_load_balance() {
let sd_entry = SDOption::LoadBalancing {
priority: 5,
weight: 15,
};
let bytes: Vec<u8> = sd_entry.into();
let serialized = SDOption::try_from(&bytes[..]).expect("Unable to parse into SDOption");
if let SDOption::LoadBalancing { priority, weight } = serialized {
assert_eq!(priority, 5);
assert_eq!(weight, 15);
} else {
panic!("Expected LoadBalancing");
}
}
#[test]
fn test_config_option_ipv4endpoint() {
let sd_entry = SDOption::Ipv4Endpoint {
addr: Ipv4Addr::new(127, 0, 0, 1),
transport: SDOptionTransportProto::TCP,
port: 8080,
};
let bytes: Vec<u8> = sd_entry.into();
let serialized = SDOption::try_from(&bytes[..]).expect("Unable to parse into SDOption");
if let SDOption::Ipv4Endpoint {
addr,
transport,
port,
} = serialized
{
assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1));
assert_eq!(transport, SDOptionTransportProto::TCP);
assert_eq!(port, 8080);
} else {
panic!("Expected IpV4Endpoint");
}
}
#[test]
fn test_config_option_ipv6endpoint() {
let sd_entry = SDOption::Ipv6Endpoint {
addr: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
transport: SDOptionTransportProto::TCP,
port: 8080,
};
let bytes: Vec<u8> = sd_entry.into();
let serialized = SDOption::try_from(&bytes[..]).expect("Unable to parse into SDOption");
if let SDOption::Ipv6Endpoint {
addr,
transport,
port,
} = serialized
{
assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
assert_eq!(transport, SDOptionTransportProto::TCP);
assert_eq!(port, 8080);
} else {
panic!("Expected IpV6Endpoint");
}
}
#[test]
fn test_config_option_ipv4multicast() {
let sd_entry = SDOption::Ipv4Multicast {
addr: Ipv4Addr::new(127, 0, 0, 1),
port: 8080,
};
let bytes: Vec<u8> = sd_entry.into();
let serialized = SDOption::try_from(&bytes[..]).expect("Unable to parse into SDOption");
if let SDOption::Ipv4Multicast { addr, port } = serialized {
assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1));
assert_eq!(port, 8080);
} else {
panic!("Expected IpV6Endpoint");
}
}
#[test]
fn test_config_option_ip6multicast() {
let sd_entry = SDOption::Ipv6Multicast {
addr: Ipv6Addr::new(127, 0, 0, 1, 0, 0, 0, 0),
port: 8080,
};
let bytes: Vec<u8> = sd_entry.into();
let serialized = SDOption::try_from(&bytes[..]).expect("Unable to parse into SDOption");
if let SDOption::Ipv6Multicast { addr, port } = serialized {
assert_eq!(addr, Ipv6Addr::new(127, 0, 0, 1, 0, 0, 0, 0));
assert_eq!(port, 8080);
} else {
panic!("Expected IpV6Endpoint");
}
}
#[test]
fn test_config_option_ipv4_sd_endpoint() {
let sd_entry = SDOption::Ipv4SDEndpoint {
addr: Ipv4Addr::new(127, 0, 0, 1),
port: 8080,
};
let bytes: Vec<u8> = sd_entry.into();
let serialized = SDOption::try_from(&bytes[..]).expect("Unable to parse into SDOption");
if let SDOption::Ipv4SDEndpoint { addr, port } = serialized {
assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1));
assert_eq!(port, 8080);
} else {
panic!("Expected IpV4 SD Endpoint");
}
}
#[test]
fn test_config_option_ipv6_sd_endpoint() {
let sd_entry = SDOption::Ipv6SDEndpoint {
addr: Ipv6Addr::new(127, 0, 0, 1, 0, 0, 0, 0),
port: 8080,
};
let bytes: Vec<u8> = sd_entry.into();
let serialized = SDOption::try_from(&bytes[..]).expect("Unable to parse into SDOption");
if let SDOption::Ipv6SDEndpoint { addr, port } = serialized {
assert_eq!(addr, Ipv6Addr::new(127, 0, 0, 1, 0, 0, 0, 0));
assert_eq!(port, 8080);
} else {
panic!("Expected IpV4 SD Endpoint");
}
}
}