use alloc::vec::Vec;
use super::tables::{LinkEntry, ReverseEntry};
use super::types::{InterfaceId, TransportAction};
use crate::constants;
use crate::link::handshake::compute_link_id;
use crate::packet::RawPacket;
pub fn forward_transport_packet(
packet: &RawPacket,
next_hop: [u8; 16],
remaining_hops: u8,
_outbound_interface: InterfaceId,
) -> Vec<u8> {
if remaining_hops > 1 {
let mut new_raw = Vec::new();
new_raw.push(packet.raw[0]); new_raw.push(packet.hops); new_raw.extend_from_slice(&next_hop); new_raw.extend_from_slice(&packet.raw[(constants::TRUNCATED_HASHLENGTH / 8 + 2)..]);
new_raw
} else if remaining_hops == 1 {
let new_flags = (constants::HEADER_1 << 6)
| (constants::TRANSPORT_BROADCAST << 4)
| (packet.raw[0] & 0x0F);
let mut new_raw = Vec::new();
new_raw.push(new_flags);
new_raw.push(packet.hops);
new_raw.extend_from_slice(&packet.raw[(constants::TRUNCATED_HASHLENGTH / 8 + 2)..]);
new_raw
} else {
let mut new_raw = Vec::new();
new_raw.push(packet.raw[0]);
new_raw.push(packet.hops);
new_raw.extend_from_slice(&packet.raw[2..]);
new_raw
}
}
pub fn create_link_entry(
packet: &RawPacket,
next_hop: [u8; 16],
outbound_interface: InterfaceId,
remaining_hops: u8,
receiving_interface: InterfaceId,
now: f64,
proof_timeout: f64,
) -> ([u8; 16], LinkEntry) {
let hashable = packet.get_hashable_part();
let extra = if packet.data.len() > constants::LINK_ECPUBSIZE {
packet.data.len() - constants::LINK_ECPUBSIZE
} else {
0
};
let link_id = compute_link_id(&hashable, extra);
let entry = LinkEntry {
timestamp: now,
next_hop_transport_id: next_hop,
next_hop_interface: outbound_interface,
remaining_hops,
received_interface: receiving_interface,
taken_hops: packet.hops,
destination_hash: packet.destination_hash,
validated: false,
proof_timeout,
};
(link_id, entry)
}
pub fn create_reverse_entry(
packet: &RawPacket,
outbound_interface: InterfaceId,
receiving_interface: InterfaceId,
now: f64,
) -> ([u8; 16], ReverseEntry) {
let truncated_hash = packet.get_truncated_hash();
let entry = ReverseEntry {
receiving_interface,
outbound_interface,
timestamp: now,
};
(truncated_hash, entry)
}
pub fn route_proof_via_reverse(
packet: &RawPacket,
reverse_entry: &ReverseEntry,
receiving_interface: InterfaceId,
) -> Option<TransportAction> {
if receiving_interface == reverse_entry.outbound_interface {
let mut new_raw = Vec::new();
new_raw.push(packet.raw[0]);
new_raw.push(packet.hops);
new_raw.extend_from_slice(&packet.raw[2..]);
Some(TransportAction::SendOnInterface {
interface: reverse_entry.receiving_interface,
raw: new_raw,
})
} else {
None
}
}
pub fn route_via_link_table(
packet: &RawPacket,
link_entry: &LinkEntry,
receiving_interface: InterfaceId,
) -> Option<(InterfaceId, Vec<u8>)> {
let outbound_interface;
if link_entry.next_hop_interface == link_entry.received_interface {
if packet.hops == link_entry.remaining_hops || packet.hops == link_entry.taken_hops {
outbound_interface = link_entry.next_hop_interface;
} else {
return None;
}
} else {
if receiving_interface == link_entry.next_hop_interface {
if packet.hops == link_entry.remaining_hops {
outbound_interface = link_entry.received_interface;
} else {
return None;
}
} else if receiving_interface == link_entry.received_interface {
if packet.hops == link_entry.taken_hops {
outbound_interface = link_entry.next_hop_interface;
} else {
return None;
}
} else {
return None;
}
}
let mut new_raw = Vec::new();
new_raw.push(packet.raw[0]);
new_raw.push(packet.hops);
new_raw.extend_from_slice(&packet.raw[2..]);
Some((outbound_interface, new_raw))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packet::PacketFlags;
fn make_h2_packet(dest: &[u8; 16], transport_id: &[u8; 16], hops: u8) -> RawPacket {
let flags = PacketFlags {
header_type: constants::HEADER_2,
context_flag: constants::FLAG_UNSET,
transport_type: constants::TRANSPORT_TRANSPORT,
destination_type: constants::DESTINATION_SINGLE,
packet_type: constants::PACKET_TYPE_DATA,
};
RawPacket::pack(
flags,
hops,
dest,
Some(transport_id),
constants::CONTEXT_NONE,
b"payload",
)
.unwrap()
}
#[test]
fn test_forward_transport_multi_hop() {
let dest = [0x11; 16];
let transport_id = [0x22; 16];
let next_hop = [0x33; 16];
let packet = make_h2_packet(&dest, &transport_id, 2);
let new_raw = forward_transport_packet(&packet, next_hop, 3, InterfaceId(1));
let flags = crate::packet::PacketFlags::unpack(new_raw[0]);
assert_eq!(flags.header_type, constants::HEADER_2);
assert_eq!(new_raw[1], 2);
assert_eq!(&new_raw[2..18], &next_hop);
assert_eq!(&new_raw[18..34], &dest);
}
#[test]
fn test_forward_transport_last_hop_strips_header() {
let dest = [0x11; 16];
let transport_id = [0x22; 16];
let packet = make_h2_packet(&dest, &transport_id, 3);
let new_raw = forward_transport_packet(&packet, [0; 16], 1, InterfaceId(1));
let flags = crate::packet::PacketFlags::unpack(new_raw[0]);
assert_eq!(flags.header_type, constants::HEADER_1);
assert_eq!(flags.transport_type, constants::TRANSPORT_BROADCAST);
assert_eq!(&new_raw[2..18], &dest);
}
#[test]
fn test_route_proof_correct_interface() {
let flags = PacketFlags {
header_type: constants::HEADER_1,
context_flag: constants::FLAG_UNSET,
transport_type: constants::TRANSPORT_BROADCAST,
destination_type: constants::DESTINATION_SINGLE,
packet_type: constants::PACKET_TYPE_PROOF,
};
let packet = RawPacket::pack(
flags,
2,
&[0xAA; 16],
None,
constants::CONTEXT_NONE,
&[0xBB; 32],
)
.unwrap();
let reverse = ReverseEntry {
receiving_interface: InterfaceId(1),
outbound_interface: InterfaceId(2),
timestamp: 100.0,
};
let action = route_proof_via_reverse(&packet, &reverse, InterfaceId(2));
assert!(action.is_some());
match action.unwrap() {
TransportAction::SendOnInterface { interface, .. } => {
assert_eq!(interface, InterfaceId(1));
}
_ => panic!("Expected SendOnInterface"),
}
}
#[test]
fn test_route_proof_wrong_interface() {
let flags = PacketFlags {
header_type: constants::HEADER_1,
context_flag: constants::FLAG_UNSET,
transport_type: constants::TRANSPORT_BROADCAST,
destination_type: constants::DESTINATION_SINGLE,
packet_type: constants::PACKET_TYPE_PROOF,
};
let packet = RawPacket::pack(
flags,
2,
&[0xAA; 16],
None,
constants::CONTEXT_NONE,
&[0xBB; 32],
)
.unwrap();
let reverse = ReverseEntry {
receiving_interface: InterfaceId(1),
outbound_interface: InterfaceId(2),
timestamp: 100.0,
};
let action = route_proof_via_reverse(&packet, &reverse, InterfaceId(3));
assert!(action.is_none());
}
#[test]
fn test_route_via_link_table_different_interfaces() {
let flags = PacketFlags {
header_type: constants::HEADER_1,
context_flag: constants::FLAG_UNSET,
transport_type: constants::TRANSPORT_BROADCAST,
destination_type: constants::DESTINATION_LINK,
packet_type: constants::PACKET_TYPE_DATA,
};
let packet = RawPacket::pack(
flags,
3,
&[0xAA; 16],
None,
constants::CONTEXT_NONE,
b"data",
)
.unwrap();
let link = LinkEntry {
timestamp: 100.0,
next_hop_transport_id: [0; 16],
next_hop_interface: InterfaceId(1),
remaining_hops: 3,
received_interface: InterfaceId(2),
taken_hops: 5,
destination_hash: [0xAA; 16],
validated: true,
proof_timeout: 200.0,
};
let result = route_via_link_table(&packet, &link, InterfaceId(1));
assert!(result.is_some());
let (iface, _) = result.unwrap();
assert_eq!(iface, InterfaceId(2));
let packet2 = RawPacket::pack(
flags,
5,
&[0xAA; 16],
None,
constants::CONTEXT_NONE,
b"data",
)
.unwrap();
let result2 = route_via_link_table(&packet2, &link, InterfaceId(2));
assert!(result2.is_some());
let (iface2, _) = result2.unwrap();
assert_eq!(iface2, InterfaceId(1));
}
#[test]
fn test_route_via_link_table_wrong_hops() {
let flags = PacketFlags {
header_type: constants::HEADER_1,
context_flag: constants::FLAG_UNSET,
transport_type: constants::TRANSPORT_BROADCAST,
destination_type: constants::DESTINATION_LINK,
packet_type: constants::PACKET_TYPE_DATA,
};
let packet = RawPacket::pack(
flags,
99,
&[0xAA; 16],
None,
constants::CONTEXT_NONE,
b"data",
)
.unwrap();
let link = LinkEntry {
timestamp: 100.0,
next_hop_transport_id: [0; 16],
next_hop_interface: InterfaceId(1),
remaining_hops: 3,
received_interface: InterfaceId(2),
taken_hops: 5,
destination_hash: [0xAA; 16],
validated: true,
proof_timeout: 200.0,
};
let result = route_via_link_table(&packet, &link, InterfaceId(1));
assert!(result.is_none());
}
}