use super::*;
fn parse_ipv6(data: &[u8]) -> crate::wire::Result<Packet<'_>> {
let ipv6_header = Ipv6Packet::new_checked(data)?;
let ipv6 = Ipv6Repr::parse(&ipv6_header)?;
match ipv6.next_header {
IpProtocol::HopByHop => todo!(),
IpProtocol::Icmp => todo!(),
IpProtocol::Igmp => todo!(),
IpProtocol::Tcp => todo!(),
IpProtocol::Udp => todo!(),
IpProtocol::Ipv6Route => todo!(),
IpProtocol::Ipv6Frag => todo!(),
IpProtocol::IpSecEsp => todo!(),
IpProtocol::IpSecAh => todo!(),
IpProtocol::Icmpv6 => {
let icmp = Icmpv6Repr::parse(
&ipv6.src_addr,
&ipv6.dst_addr,
&Icmpv6Packet::new_checked(ipv6_header.payload())?,
&Default::default(),
)?;
Ok(Packet::new_ipv6(ipv6, IpPayload::Icmpv6(icmp)))
}
IpProtocol::Ipv6NoNxt => todo!(),
IpProtocol::Ipv6Opts => todo!(),
IpProtocol::Unknown(_) => todo!(),
}
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn any_ip(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x8, 0x3a, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x3, 0x80, 0x0, 0x84, 0x3a, 0x0, 0x0, 0x0, 0x0,
];
assert_eq!(
parse_ipv6(&data),
Ok(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0003),
hop_limit: 64,
next_header: IpProtocol::Icmpv6,
payload_len: 8,
},
IpPayload::Icmpv6(Icmpv6Repr::EchoRequest {
ident: 0,
seq_no: 0,
data: b"",
})
))
);
let (mut iface, mut sockets, _device) = setup(medium);
iface.routes_mut().update(|routes| {
routes
.push(crate::iface::Route {
cidr: IpCidr::Ipv6(Ipv6Cidr::new(
Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0),
64,
)),
via_router: IpAddress::Ipv6(Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001)),
preferred_until: None,
expires_at: None,
})
.unwrap();
});
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
None
);
iface.set_any_ip(true);
assert!(
iface
.inner
.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
)
.is_some()
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn multicast_source_address(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x40, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1,
];
let response = None;
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn hop_by_hop_skip_with_icmp(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x0, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x3a, 0x0, 0x1, 0x0, 0xf, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88,
0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d,
];
let response = Some(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
hop_limit: 64,
next_header: IpProtocol::Icmpv6,
payload_len: 19,
},
IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
ident: 42,
seq_no: 420,
data: b"Lorem Ipsum",
}),
));
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn hop_by_hop_discard_with_icmp(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x0, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x3a, 0x0, 0x1, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88,
0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d,
];
let response = None;
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
fn hop_by_hop_discard_param_problem(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x0, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x3a, 0x0, 0xC0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88,
0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d,
];
let response = Some(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2),
next_header: IpProtocol::Icmpv6,
payload_len: 75,
hop_limit: 64,
},
IpPayload::Icmpv6(Icmpv6Repr::ParamProblem {
reason: Icmpv6ParamProblem::UnrecognizedOption,
pointer: 40,
header: Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1),
next_header: IpProtocol::HopByHop,
payload_len: 27,
hop_limit: 64,
},
data: &[
0x3a, 0x0, 0xC0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88, 0x0, 0x2a, 0x1,
0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d,
],
}),
));
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
fn hop_by_hop_discard_with_multicast(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x0, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x3a, 0x0, 0x80, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88,
0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d,
];
let response = Some(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2),
next_header: IpProtocol::Icmpv6,
payload_len: 75,
hop_limit: 64,
},
IpPayload::Icmpv6(Icmpv6Repr::ParamProblem {
reason: Icmpv6ParamProblem::UnrecognizedOption,
pointer: 40,
header: Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2),
dst_addr: Ipv6Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
next_header: IpProtocol::HopByHop,
payload_len: 27,
hop_limit: 64,
},
data: &[
0x3a, 0x0, 0x80, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88, 0x0, 0x2a, 0x1,
0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d,
],
}),
));
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn imcp_empty_echo_request(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x8, 0x3a, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x80, 0x0, 0x84, 0x3c, 0x0, 0x0, 0x0, 0x0,
];
assert_eq!(
parse_ipv6(&data),
Ok(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
hop_limit: 64,
next_header: IpProtocol::Icmpv6,
payload_len: 8,
},
IpPayload::Icmpv6(Icmpv6Repr::EchoRequest {
ident: 0,
seq_no: 0,
data: b"",
})
))
);
let response = Some(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
hop_limit: 64,
next_header: IpProtocol::Icmpv6,
payload_len: 8,
},
IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
ident: 0,
seq_no: 0,
data: b"",
}),
));
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn icmp_echo_request(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x13, 0x3a, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x80, 0x0, 0x2c, 0x88, 0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72,
0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d,
];
assert_eq!(
parse_ipv6(&data),
Ok(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
hop_limit: 64,
next_header: IpProtocol::Icmpv6,
payload_len: 19,
},
IpPayload::Icmpv6(Icmpv6Repr::EchoRequest {
ident: 42,
seq_no: 420,
data: b"Lorem Ipsum",
})
))
);
let response = Some(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
hop_limit: 64,
next_header: IpProtocol::Icmpv6,
payload_len: 19,
},
IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
ident: 42,
seq_no: 420,
data: b"Lorem Ipsum",
}),
));
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn icmp_echo_reply_as_input(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x13, 0x3a, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x81, 0x0, 0x2d, 0x56, 0x0, 0x0, 0x0, 0x0, 0x4c, 0x6f, 0x72, 0x65,
0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d,
];
assert_eq!(
parse_ipv6(&data),
Ok(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
hop_limit: 64,
next_header: IpProtocol::Icmpv6,
payload_len: 19,
},
IpPayload::Icmpv6(Icmpv6Repr::EchoReply {
ident: 0,
seq_no: 0,
data: b"Lorem Ipsum",
})
))
);
let response = None;
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn unknown_proto_with_multicast_dst_address(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1,
];
let response = Some(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
hop_limit: 64,
next_header: IpProtocol::Icmpv6,
payload_len: 48,
},
IpPayload::Icmpv6(Icmpv6Repr::ParamProblem {
reason: Icmpv6ParamProblem::UnrecognizedNxtHdr,
pointer: 40,
header: Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
dst_addr: Ipv6Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x0001),
hop_limit: 64,
next_header: IpProtocol::Unknown(0x0c),
payload_len: 0,
},
data: &[],
}),
));
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn unknown_proto(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1,
];
let response = Some(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
hop_limit: 64,
next_header: IpProtocol::Icmpv6,
payload_len: 48,
},
IpPayload::Icmpv6(Icmpv6Repr::ParamProblem {
reason: Icmpv6ParamProblem::UnrecognizedNxtHdr,
pointer: 40,
header: Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
hop_limit: 64,
next_header: IpProtocol::Unknown(0x0c),
payload_len: 0,
},
data: &[],
}),
));
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
}
#[rstest]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
fn ndisc_neighbor_advertisement_ethernet(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x20, 0x3a, 0xff, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x88, 0x0, 0x3b, 0x9f, 0x40, 0x0, 0x0, 0x0, 0xfe, 0x80, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1,
];
assert_eq!(
parse_ipv6(&data),
Ok(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
hop_limit: 255,
next_header: IpProtocol::Icmpv6,
payload_len: 32,
},
IpPayload::Icmpv6(Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
flags: NdiscNeighborFlags::SOLICITED,
target_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x0002),
lladdr: Some(RawHardwareAddress::from_bytes(&[0, 0, 0, 0, 0, 1])),
}))
))
);
let response = None;
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
assert_eq!(
iface.inner.neighbor_cache.lookup(
&IpAddress::Ipv6(Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002)),
iface.inner.now,
),
NeighborAnswer::Found(HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
0, 0, 0, 0, 0, 1
]))),
);
}
#[rstest]
#[case::ethernet(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
fn ndisc_neighbor_advertisement_ethernet_multicast_addr(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x20, 0x3a, 0xff, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x88, 0x0, 0x3b, 0xa0, 0x40, 0x0, 0x0, 0x0, 0xfe, 0x80, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x1, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
];
assert_eq!(
parse_ipv6(&data),
Ok(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
hop_limit: 255,
next_header: IpProtocol::Icmpv6,
payload_len: 32,
},
IpPayload::Icmpv6(Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
flags: NdiscNeighborFlags::SOLICITED,
target_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x0002),
lladdr: Some(RawHardwareAddress::from_bytes(&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
])),
}))
))
);
let response = None;
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
assert_eq!(
iface.inner.neighbor_cache.lookup(
&IpAddress::Ipv6(Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002)),
iface.inner.now,
),
NeighborAnswer::NotFound,
);
}
#[rstest]
#[case::ieee802154(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn ndisc_neighbor_advertisement_ieee802154(#[case] medium: Medium) {
let data = [
0x60, 0x0, 0x0, 0x0, 0x0, 0x28, 0x3a, 0xff, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x88, 0x0, 0x3b, 0x96, 0x40, 0x0, 0x0, 0x0, 0xfe, 0x80, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
assert_eq!(
parse_ipv6(&data),
Ok(Packet::new_ipv6(
Ipv6Repr {
src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002),
dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001),
hop_limit: 255,
next_header: IpProtocol::Icmpv6,
payload_len: 40,
},
IpPayload::Icmpv6(Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
flags: NdiscNeighborFlags::SOLICITED,
target_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x0002),
lladdr: Some(RawHardwareAddress::from_bytes(&[0, 0, 0, 0, 0, 0, 0, 1])),
}))
))
);
let response = None;
let (mut iface, mut sockets, _device) = setup(medium);
assert_eq!(
iface.inner.process_ipv6(
&mut sockets,
PacketMeta::default(),
HardwareAddress::default(),
&Ipv6Packet::new_checked(&data[..]).unwrap()
),
response
);
assert_eq!(
iface.inner.neighbor_cache.lookup(
&IpAddress::Ipv6(Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002)),
iface.inner.now,
),
NeighborAnswer::Found(HardwareAddress::Ieee802154(Ieee802154Address::from_bytes(
&[0, 0, 0, 0, 0, 0, 0, 1]
))),
);
}
#[rstest]
#[case(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
fn test_handle_valid_ndisc_request(#[case] medium: Medium) {
let (mut iface, mut sockets, _device) = setup(medium);
let mut eth_bytes = vec![0u8; 86];
let local_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1);
let remote_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2);
let local_hw_addr = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]);
let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]);
let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit {
target_addr: local_ip_addr,
lladdr: Some(remote_hw_addr.into()),
});
let ip_repr = IpRepr::Ipv6(Ipv6Repr {
src_addr: remote_ip_addr,
dst_addr: local_ip_addr.solicited_node(),
next_header: IpProtocol::Icmpv6,
hop_limit: 0xff,
payload_len: solicit.buffer_len(),
});
let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes);
frame.set_dst_addr(EthernetAddress([0x33, 0x33, 0x00, 0x00, 0x00, 0x00]));
frame.set_src_addr(remote_hw_addr);
frame.set_ethertype(EthernetProtocol::Ipv6);
ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default());
solicit.emit(
&remote_ip_addr,
&local_ip_addr.solicited_node(),
&mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]),
&ChecksumCapabilities::default(),
);
let icmpv6_expected = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
flags: NdiscNeighborFlags::SOLICITED,
target_addr: local_ip_addr,
lladdr: Some(local_hw_addr.into()),
});
let ipv6_expected = Ipv6Repr {
src_addr: local_ip_addr,
dst_addr: remote_ip_addr,
next_header: IpProtocol::Icmpv6,
hop_limit: 0xff,
payload_len: icmpv6_expected.buffer_len(),
};
assert_eq!(
iface.inner.process_ethernet(
&mut sockets,
PacketMeta::default(),
frame.into_inner(),
&mut iface.fragments
),
Some(EthernetPacket::Ip(Packet::new_ipv6(
ipv6_expected,
IpPayload::Icmpv6(icmpv6_expected)
)))
);
assert_eq!(
iface.inner.lookup_hardware_addr(
MockTxToken,
&IpAddress::Ipv6(remote_ip_addr),
&mut iface.fragmenter,
),
Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken))
);
}
#[rstest]
#[case(Medium::Ethernet)]
#[cfg(feature = "proto-ipv6-slaac")]
fn test_router_advertisement(#[case] medium: Medium) {
fn recv_icmpv6(
device: &mut crate::tests::TestingDevice,
timestamp: Instant,
) -> std::vec::Vec<Ipv6Packet<std::vec::Vec<u8>>> {
let caps = device.capabilities();
recv_all(device, timestamp)
.iter()
.filter_map(|frame| {
let ipv6_packet = match caps.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => {
let eth_frame = EthernetFrame::new_checked(frame).ok()?;
Ipv6Packet::new_checked(eth_frame.payload()).ok()?
}
#[cfg(feature = "medium-ip")]
Medium::Ip => Ipv6Packet::new_checked(&frame[..]).ok()?,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => todo!(),
};
let buf = ipv6_packet.into_inner().to_vec();
Some(Ipv6Packet::new_unchecked(buf))
})
.collect::<std::vec::Vec<_>>()
}
let prefix_addr = Ipv6Address::new(0x2001, 0xdb8, 0x3, 0, 0, 0, 0, 0);
let mut device = crate::tests::TestingDevice::new(medium);
let caps = device.capabilities();
let checksum_caps = &caps.checksum;
let mut eth_bytes = vec![0u8; 102];
let local_hw_addr = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]);
let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]);
let ll_prefix = Ipv6Cidr::new(Ipv6Cidr::LINK_LOCAL_PREFIX.address(), 64);
let local_ip_addr =
Ipv6Cidr::from_link_prefix(&ll_prefix, HardwareAddress::Ethernet(local_hw_addr)).unwrap();
let remote_ip_addr =
Ipv6Cidr::from_link_prefix(&ll_prefix, HardwareAddress::Ethernet(remote_hw_addr)).unwrap();
let mut config = Config::new(match medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => HardwareAddress::Ethernet(local_hw_addr),
_ => panic!("Not supported"),
});
config.slaac = true;
let mut iface = Interface::new(config, &mut device, Instant::ZERO);
iface.update_ip_addrs(|ip_addrs| {
ip_addrs.push(IpCidr::Ipv6(local_ip_addr)).unwrap();
});
let mut sockets = SocketSet::new(vec![]);
iface.poll(Instant::ZERO, &mut device, &mut sockets);
let transmitted: std::vec::Vec<Ipv6Packet<std::vec::Vec<u8>>> =
recv_icmpv6(&mut device, Instant::ZERO)
.into_iter()
.filter(|packet| {
packet.dst_addr() == IPV6_LINK_LOCAL_ALL_ROUTERS
})
.collect();
assert_eq!(transmitted.len(), 1);
for ipv6_packet in transmitted.into_iter() {
let buf = ipv6_packet.into_inner();
let ipv6_packet = Ipv6Packet::new_unchecked(buf.as_slice());
let ipv6_repr = Ipv6Repr::parse(&ipv6_packet).unwrap();
if ipv6_repr.dst_addr == IPV6_LINK_LOCAL_ALL_MLDV2_ROUTERS {
continue; }
let icmpv6_packet = Icmpv6Packet::new_checked(ipv6_packet.payload()).unwrap();
let icmp_repr = Icmpv6Repr::parse(
&ipv6_repr.src_addr,
&ipv6_repr.dst_addr,
&icmpv6_packet,
checksum_caps,
)
.unwrap();
assert_eq!(
icmp_repr,
Icmpv6Repr::Ndisc(NdiscRepr::RouterSolicit {
lladdr: Some(local_hw_addr.into()),
})
);
assert_eq!(ipv6_repr.dst_addr, IPV6_LINK_LOCAL_ALL_ROUTERS);
println!("repr {:?}", icmp_repr);
}
let mut prefix_information = NdiscPrefixInformation {
prefix: prefix_addr,
prefix_len: 64,
flags: NdiscPrefixInfoFlags::ADDRCONF,
valid_lifetime: Duration::from_secs(600),
preferred_lifetime: Duration::from_secs(300),
};
let mut advertisement = NdiscRepr::RouterAdvert {
hop_limit: 255,
flags: NdiscRouterFlags::empty(),
router_lifetime: Duration::from_secs(600),
reachable_time: Duration::from_secs(0),
retrans_time: Duration::from_secs(0),
lladdr: None,
mtu: None,
prefix_info: Some(prefix_information),
};
let ip_repr = IpRepr::Ipv6(Ipv6Repr {
src_addr: remote_ip_addr.address(),
dst_addr: local_ip_addr.address(),
next_header: IpProtocol::Icmpv6,
hop_limit: 255,
payload_len: advertisement.buffer_len(),
});
let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes);
frame.set_dst_addr(local_hw_addr);
frame.set_src_addr(remote_hw_addr);
frame.set_ethertype(EthernetProtocol::Ipv6);
ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default());
Icmpv6Repr::Ndisc(advertisement).emit(
&remote_ip_addr.address(),
&local_ip_addr.address(),
&mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]),
&ChecksumCapabilities::default(),
);
iface.inner.process_ethernet(
&mut sockets,
PacketMeta::default(),
frame.into_inner(),
&mut iface.fragments,
);
iface.poll(Instant::ZERO, &mut device, &mut sockets);
let expected_addrs = [
IpCidr::Ipv6(local_ip_addr),
IpCidr::Ipv6(Ipv6Cidr::new(
Ipv6Address::new(0x2001, 0xdb8, 0x3, 0x0, 0x2, 0x2ff, 0xfe02, 0x202),
64,
)),
];
for (generated, expected) in iface.ip_addrs().iter().zip(expected_addrs.iter()) {
assert_eq!(generated, expected);
}
iface.routes_mut().update(|route| {
assert_eq!(route.len(), 1);
assert_eq!(
route[0].cidr,
IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0), 0)
);
assert_eq!(
route[0].via_router,
IpAddress::Ipv6(remote_ip_addr.address())
);
assert_eq!(route[0].preferred_until, None);
assert_eq!(route[0].expires_at, None);
});
prefix_information.valid_lifetime = Duration::ZERO;
prefix_information.preferred_lifetime = Duration::ZERO;
if let NdiscRepr::RouterAdvert {
ref mut prefix_info,
..
} = advertisement
{
*prefix_info = Some(prefix_information);
}
let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes);
frame.set_dst_addr(local_hw_addr);
frame.set_src_addr(remote_hw_addr);
frame.set_ethertype(EthernetProtocol::Ipv6);
ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default());
Icmpv6Repr::Ndisc(advertisement).emit(
&remote_ip_addr.address(),
&local_ip_addr.address(),
&mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]),
&ChecksumCapabilities::default(),
);
iface.inner.process_ethernet(
&mut sockets,
PacketMeta::default(),
frame.into_inner(),
&mut iface.fragments,
);
let now = Instant::from_secs(10);
iface.poll(now, &mut device, &mut sockets);
assert_eq!(iface.ip_addrs().len(), 1);
iface.routes_mut().update(|route| {
assert_eq!(route.len(), 1);
});
if let NdiscRepr::RouterAdvert {
ref mut prefix_info,
ref mut router_lifetime,
..
} = advertisement
{
*prefix_info = None;
*router_lifetime = Duration::ZERO;
}
let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes);
frame.set_dst_addr(local_hw_addr);
frame.set_src_addr(remote_hw_addr);
frame.set_ethertype(EthernetProtocol::Ipv6);
ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default());
Icmpv6Repr::Ndisc(advertisement).emit(
&remote_ip_addr.address(),
&local_ip_addr.address(),
&mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]),
&ChecksumCapabilities::default(),
);
iface.inner.process_ethernet(
&mut sockets,
PacketMeta::default(),
frame.into_inner(),
&mut iface.fragments,
);
let now = Instant::from_secs(20);
iface.poll(now, &mut device, &mut sockets);
iface.routes_mut().update(|route| {
assert_eq!(route.len(), 0);
});
}
#[rstest]
#[case(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
#[case(Medium::Ieee802154)]
#[cfg(feature = "medium-ieee802154")]
fn test_solicited_node_addrs(#[case] medium: Medium) {
let (mut iface, _, _) = setup(medium);
let mut new_addrs = heapless::Vec::<IpCidr, IFACE_MAX_ADDR_COUNT>::new();
new_addrs
.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64))
.unwrap();
new_addrs
.push(IpCidr::new(
IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff),
64,
))
.unwrap();
iface.update_ip_addrs(|addrs| {
new_addrs.extend(addrs.to_vec());
*addrs = new_addrs;
});
assert!(
iface
.inner
.has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0002))
);
assert!(
iface
.inner
.has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0xffff))
);
assert!(
!iface
.inner
.has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0003))
);
}
#[rstest]
#[case(Medium::Ip)]
#[cfg(all(feature = "socket-udp", feature = "medium-ip"))]
#[case(Medium::Ethernet)]
#[cfg(all(feature = "socket-udp", feature = "medium-ethernet"))]
#[case(Medium::Ieee802154)]
#[cfg(all(feature = "socket-udp", feature = "medium-ieee802154"))]
fn test_icmp_reply_size(#[case] medium: Medium) {
use crate::wire::IPV6_MIN_MTU as MIN_MTU;
use crate::wire::Icmpv6DstUnreachable;
const MAX_PAYLOAD_LEN: usize = 1192;
let (mut iface, mut sockets, _device) = setup(medium);
let src_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
let dst_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2);
let udp_repr = UdpRepr {
src_port: 67,
dst_port: 68,
};
let mut bytes = vec![0xff; udp_repr.header_len() + MAX_PAYLOAD_LEN];
let mut packet = UdpPacket::new_unchecked(&mut bytes[..]);
udp_repr.emit(
&mut packet,
&src_addr.into(),
&dst_addr.into(),
MAX_PAYLOAD_LEN,
|buf| fill_slice(buf, 0x2a),
&ChecksumCapabilities::default(),
);
let ip_repr = Ipv6Repr {
src_addr,
dst_addr,
next_header: IpProtocol::Udp,
hop_limit: 64,
payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN,
};
let payload = packet.into_inner();
let expected_icmp_repr = Icmpv6Repr::DstUnreachable {
reason: Icmpv6DstUnreachable::PortUnreachable,
header: ip_repr,
data: &payload[..MAX_PAYLOAD_LEN],
};
let expected_ip_repr = Ipv6Repr {
src_addr: dst_addr,
dst_addr: src_addr,
next_header: IpProtocol::Icmpv6,
hop_limit: 64,
payload_len: expected_icmp_repr.buffer_len(),
};
assert_eq!(
expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(),
MIN_MTU
);
assert_eq!(
iface.inner.process_udp(
&mut sockets,
PacketMeta::default(),
false,
ip_repr.into(),
payload,
),
Some(Packet::new_ipv6(
expected_ip_repr,
IpPayload::Icmpv6(expected_icmp_repr)
))
);
}
#[cfg(feature = "medium-ip")]
#[test]
fn get_source_address() {
let (mut iface, _, _) = setup(Medium::Ip);
const OWN_LINK_LOCAL_ADDR: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
const OWN_UNIQUE_LOCAL_ADDR1: Ipv6Address = Ipv6Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 2);
const OWN_UNIQUE_LOCAL_ADDR2: Ipv6Address = Ipv6Address::new(0xfd01, 0, 0, 201, 1, 1, 1, 2);
const OWN_GLOBAL_UNICAST_ADDR1: Ipv6Address =
Ipv6Address::new(0x2001, 0x0db8, 0x0003, 0, 0, 0, 0, 1);
iface.update_ip_addrs(|addrs| {
addrs.clear();
addrs
.push(IpCidr::Ipv6(Ipv6Cidr::new(OWN_LINK_LOCAL_ADDR, 64)))
.unwrap();
addrs
.push(IpCidr::Ipv6(Ipv6Cidr::new(OWN_UNIQUE_LOCAL_ADDR1, 64)))
.unwrap();
addrs
.push(IpCidr::Ipv6(Ipv6Cidr::new(OWN_UNIQUE_LOCAL_ADDR2, 64)))
.unwrap();
addrs
.push(IpCidr::Ipv6(Ipv6Cidr::new(OWN_GLOBAL_UNICAST_ADDR1, 64)))
.unwrap();
});
const LINK_LOCAL_ADDR: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 42);
const UNIQUE_LOCAL_ADDR1: Ipv6Address = Ipv6Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1);
const UNIQUE_LOCAL_ADDR2: Ipv6Address = Ipv6Address::new(0xfd01, 0, 0, 201, 1, 1, 1, 1);
const UNIQUE_LOCAL_ADDR3: Ipv6Address = Ipv6Address::new(0xfd02, 0, 0, 201, 1, 1, 1, 1);
const UNIQUE_LOCAL_ADDR4: Ipv6Address = Ipv6Address::new(0xfd01, 0, 0, 201, 1, 1, 1, 3);
const GLOBAL_UNICAST_ADDR1: Ipv6Address =
Ipv6Address::new(0x2001, 0x0db8, 0x0003, 0, 0, 0, 0, 2);
const GLOBAL_UNICAST_ADDR2: Ipv6Address =
Ipv6Address::new(0x2001, 0x0db9, 0x0003, 0, 0, 0, 0, 2);
assert_eq!(
iface.inner.get_source_address_ipv6(&Ipv6Address::LOCALHOST),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.inner.get_source_address_ipv6(&LINK_LOCAL_ADDR),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR1),
OWN_UNIQUE_LOCAL_ADDR1
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR2),
OWN_UNIQUE_LOCAL_ADDR2
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR3),
OWN_UNIQUE_LOCAL_ADDR1
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR4),
OWN_UNIQUE_LOCAL_ADDR2
);
assert_eq!(
iface
.inner
.get_source_address_ipv6(&IPV6_LINK_LOCAL_ALL_NODES),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.inner.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR1),
OWN_GLOBAL_UNICAST_ADDR1
);
assert_eq!(
iface.inner.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR2),
OWN_GLOBAL_UNICAST_ADDR1
);
assert_eq!(
iface.get_source_address_ipv6(&LINK_LOCAL_ADDR),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR1),
OWN_UNIQUE_LOCAL_ADDR1
);
assert_eq!(
iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR2),
OWN_UNIQUE_LOCAL_ADDR2
);
assert_eq!(
iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR3),
OWN_UNIQUE_LOCAL_ADDR1
);
assert_eq!(
iface.get_source_address_ipv6(&IPV6_LINK_LOCAL_ALL_NODES),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR1),
OWN_GLOBAL_UNICAST_ADDR1
);
assert_eq!(
iface.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR2),
OWN_GLOBAL_UNICAST_ADDR1
);
}
#[cfg(feature = "medium-ip")]
#[test]
fn get_source_address_only_link_local() {
let (mut iface, _, _) = setup(Medium::Ip);
const OWN_LINK_LOCAL_ADDR: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
iface.update_ip_addrs(|ips| {
ips.clear();
ips.push(IpCidr::Ipv6(Ipv6Cidr::new(OWN_LINK_LOCAL_ADDR, 64)))
.unwrap();
});
const LINK_LOCAL_ADDR: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 42);
const UNIQUE_LOCAL_ADDR1: Ipv6Address = Ipv6Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1);
const UNIQUE_LOCAL_ADDR2: Ipv6Address = Ipv6Address::new(0xfd01, 0, 0, 201, 1, 1, 1, 1);
const UNIQUE_LOCAL_ADDR3: Ipv6Address = Ipv6Address::new(0xfd02, 0, 0, 201, 1, 1, 1, 1);
const GLOBAL_UNICAST_ADDR1: Ipv6Address =
Ipv6Address::new(0x2001, 0x0db8, 0x0003, 0, 0, 0, 0, 2);
const GLOBAL_UNICAST_ADDR2: Ipv6Address =
Ipv6Address::new(0x2001, 0x0db9, 0x0003, 0, 0, 0, 0, 2);
assert_eq!(
iface.inner.get_source_address_ipv6(&Ipv6Address::LOCALHOST),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.inner.get_source_address_ipv6(&LINK_LOCAL_ADDR),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR1),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR2),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR3),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface
.inner
.get_source_address_ipv6(&IPV6_LINK_LOCAL_ALL_NODES),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.inner.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR1),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.inner.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR2),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.get_source_address_ipv6(&LINK_LOCAL_ADDR),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR1),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR2),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR3),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.get_source_address_ipv6(&IPV6_LINK_LOCAL_ALL_NODES),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR1),
OWN_LINK_LOCAL_ADDR
);
assert_eq!(
iface.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR2),
OWN_LINK_LOCAL_ADDR
);
}
#[cfg(feature = "medium-ip")]
#[test]
fn get_source_address_empty_interface() {
let (mut iface, _, _) = setup(Medium::Ip);
iface.update_ip_addrs(|ips| ips.clear());
const LINK_LOCAL_ADDR: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 42);
const UNIQUE_LOCAL_ADDR1: Ipv6Address = Ipv6Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1);
const UNIQUE_LOCAL_ADDR2: Ipv6Address = Ipv6Address::new(0xfd01, 0, 0, 201, 1, 1, 1, 1);
const UNIQUE_LOCAL_ADDR3: Ipv6Address = Ipv6Address::new(0xfd02, 0, 0, 201, 1, 1, 1, 1);
const GLOBAL_UNICAST_ADDR1: Ipv6Address =
Ipv6Address::new(0x2001, 0x0db8, 0x0003, 0, 0, 0, 0, 2);
const GLOBAL_UNICAST_ADDR2: Ipv6Address =
Ipv6Address::new(0x2001, 0x0db9, 0x0003, 0, 0, 0, 0, 2);
assert_eq!(
iface.inner.get_source_address_ipv6(&Ipv6Address::LOCALHOST),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.inner.get_source_address_ipv6(&LINK_LOCAL_ADDR),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR1),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR2),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR3),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface
.inner
.get_source_address_ipv6(&IPV6_LINK_LOCAL_ALL_NODES),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.inner.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR1),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.inner.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR2),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.get_source_address_ipv6(&LINK_LOCAL_ADDR),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR1),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR2),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR3),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.get_source_address_ipv6(&IPV6_LINK_LOCAL_ALL_NODES),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR1),
Ipv6Address::LOCALHOST
);
assert_eq!(
iface.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR2),
Ipv6Address::LOCALHOST
);
}
#[rstest]
#[case(Medium::Ip)]
#[cfg(feature = "medium-ip")]
#[case(Medium::Ethernet)]
#[cfg(feature = "medium-ethernet")]
fn test_join_ipv6_multicast_group(#[case] medium: Medium) {
fn recv_icmpv6(
device: &mut crate::tests::TestingDevice,
timestamp: Instant,
) -> std::vec::Vec<Ipv6Packet<std::vec::Vec<u8>>> {
let caps = device.capabilities();
recv_all(device, timestamp)
.iter()
.filter_map(|frame| {
let ipv6_packet = match caps.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => {
let eth_frame = EthernetFrame::new_checked(frame).ok()?;
Ipv6Packet::new_checked(eth_frame.payload()).ok()?
}
#[cfg(feature = "medium-ip")]
Medium::Ip => Ipv6Packet::new_checked(&frame[..]).ok()?,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => todo!(),
};
let buf = ipv6_packet.into_inner().to_vec();
Some(Ipv6Packet::new_unchecked(buf))
})
.collect::<std::vec::Vec<_>>()
}
let (mut iface, mut sockets, mut device) = setup(medium);
let groups = [
Ipv6Address::new(0xff05, 0, 0, 0, 0, 0, 0, 0x00fb),
Ipv6Address::new(0xff0e, 0, 0, 0, 0, 0, 0, 0x0017),
];
let timestamp = Instant::from_millis(0);
iface.poll(timestamp, &mut device, &mut sockets);
let _ = recv_icmpv6(&mut device, timestamp);
for &group in &groups {
iface.join_multicast_group(group).unwrap();
assert!(iface.has_multicast_group(group));
}
assert!(iface.has_multicast_group(IPV6_LINK_LOCAL_ALL_NODES));
iface.poll(timestamp, &mut device, &mut sockets);
assert!(iface.has_multicast_group(IPV6_LINK_LOCAL_ALL_NODES));
let reports = recv_icmpv6(&mut device, timestamp);
assert_eq!(reports.len(), 2);
let caps = device.capabilities();
let checksum_caps = &caps.checksum;
for (&group_addr, ipv6_packet) in groups.iter().zip(reports) {
let buf = ipv6_packet.into_inner();
let ipv6_packet = Ipv6Packet::new_unchecked(buf.as_slice());
let _ipv6_repr = Ipv6Repr::parse(&ipv6_packet).unwrap();
let ip_payload = ipv6_packet.payload();
let hbh_header = Ipv6HopByHopHeader::new_checked(&ip_payload[..8]).unwrap();
let hbh_repr = Ipv6HopByHopRepr::parse(&hbh_header).unwrap();
assert_eq!(hbh_repr.options.len(), 3);
assert_eq!(
hbh_repr.options[0],
Ipv6OptionRepr::Unknown {
type_: Ipv6OptionType::Unknown(IpProtocol::Icmpv6.into()),
length: 0,
data: &[],
}
);
assert_eq!(
hbh_repr.options[1],
Ipv6OptionRepr::RouterAlert(Ipv6OptionRouterAlert::MulticastListenerDiscovery)
);
assert_eq!(hbh_repr.options[2], Ipv6OptionRepr::PadN(0));
let icmpv6_packet =
Icmpv6Packet::new_checked(&ip_payload[hbh_repr.buffer_len()..]).unwrap();
let icmpv6_repr = Icmpv6Repr::parse(
&ipv6_packet.src_addr(),
&ipv6_packet.dst_addr(),
&icmpv6_packet,
checksum_caps,
)
.unwrap();
let record_data = match icmpv6_repr {
Icmpv6Repr::Mld(MldRepr::Report {
nr_mcast_addr_rcrds,
data,
}) => {
assert_eq!(nr_mcast_addr_rcrds, 1);
data
}
other => panic!("unexpected icmpv6_repr: {:?}", other),
};
let record = MldAddressRecord::new_checked(record_data).unwrap();
let record_repr = MldAddressRecordRepr::parse(&record).unwrap();
assert_eq!(
record_repr,
MldAddressRecordRepr {
num_srcs: 0,
mcast_addr: group_addr,
record_type: MldRecordType::ChangeToInclude,
aux_data_len: 0,
payload: &[],
}
);
if !group_addr.is_solicited_node_multicast() {
iface.leave_multicast_group(group_addr).unwrap();
assert!(!iface.has_multicast_group(group_addr));
iface.poll(timestamp, &mut device, &mut sockets);
assert!(!iface.has_multicast_group(group_addr));
}
}
}
#[rstest]
#[case(Medium::Ethernet)]
#[cfg(all(feature = "multicast", feature = "medium-ethernet"))]
fn test_handle_valid_multicast_query(#[case] medium: Medium) {
fn recv_icmpv6(
device: &mut crate::tests::TestingDevice,
timestamp: Instant,
) -> std::vec::Vec<Ipv6Packet<std::vec::Vec<u8>>> {
let caps = device.capabilities();
recv_all(device, timestamp)
.iter()
.filter_map(|frame| {
let ipv6_packet = match caps.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => {
let eth_frame = EthernetFrame::new_checked(frame).ok()?;
Ipv6Packet::new_checked(eth_frame.payload()).ok()?
}
#[cfg(feature = "medium-ip")]
Medium::Ip => Ipv6Packet::new_checked(&frame[..]).ok()?,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => todo!(),
};
let buf = ipv6_packet.into_inner().to_vec();
Some(Ipv6Packet::new_unchecked(buf))
})
.collect::<std::vec::Vec<_>>()
}
let (mut iface, mut sockets, mut device) = setup(medium);
let mut timestamp = Instant::ZERO;
let mut eth_bytes = vec![0u8; 86];
let local_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 100);
let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]);
let query_ip_addr = Ipv6Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x1234);
iface.join_multicast_group(query_ip_addr).unwrap();
iface.poll(timestamp, &mut device, &mut sockets);
recv_icmpv6(&mut device, timestamp);
let queries = [
(
Ipv6Address::UNSPECIFIED,
IPV6_LINK_LOCAL_ALL_NODES,
vec![local_ip_addr.solicited_node(), query_ip_addr],
),
(query_ip_addr, query_ip_addr, vec![query_ip_addr]),
];
for (mcast_query, address, _results) in queries.iter() {
let query = Icmpv6Repr::Mld(MldRepr::Query {
max_resp_code: 1000,
mcast_addr: *mcast_query,
s_flag: false,
qrv: 1,
qqic: 60,
num_srcs: 0,
data: &[0, 0, 0, 0],
});
let ip_repr = IpRepr::Ipv6(Ipv6Repr {
src_addr: remote_ip_addr,
dst_addr: *address,
next_header: IpProtocol::Icmpv6,
hop_limit: 1,
payload_len: query.buffer_len(),
});
let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes);
frame.set_dst_addr(EthernetAddress([0x33, 0x33, 0x00, 0x00, 0x00, 0x00]));
frame.set_src_addr(remote_hw_addr);
frame.set_ethertype(EthernetProtocol::Ipv6);
ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default());
query.emit(
&remote_ip_addr,
address,
&mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]),
&ChecksumCapabilities::default(),
);
iface.inner.process_ethernet(
&mut sockets,
PacketMeta::default(),
frame.into_inner(),
&mut iface.fragments,
);
timestamp += crate::time::Duration::from_millis(1000);
iface.poll(timestamp, &mut device, &mut sockets);
}
let reports = recv_icmpv6(&mut device, timestamp);
assert_eq!(reports.len(), queries.len());
let caps = device.capabilities();
let checksum_caps = &caps.checksum;
for ((_mcast_query, _address, results), ipv6_packet) in queries.iter().zip(reports) {
let buf = ipv6_packet.into_inner();
let ipv6_packet = Ipv6Packet::new_unchecked(buf.as_slice());
let ipv6_repr = Ipv6Repr::parse(&ipv6_packet).unwrap();
let ip_payload = ipv6_packet.payload();
assert_eq!(ipv6_repr.dst_addr, IPV6_LINK_LOCAL_ALL_MLDV2_ROUTERS);
let hbh_header = Ipv6HopByHopHeader::new_checked(&ip_payload[..8]).unwrap();
let hbh_repr = Ipv6HopByHopRepr::parse(&hbh_header).unwrap();
assert_eq!(hbh_repr.options.len(), 3);
assert_eq!(
hbh_repr.options[0],
Ipv6OptionRepr::Unknown {
type_: Ipv6OptionType::Unknown(IpProtocol::Icmpv6.into()),
length: 0,
data: &[],
}
);
assert_eq!(
hbh_repr.options[1],
Ipv6OptionRepr::RouterAlert(Ipv6OptionRouterAlert::MulticastListenerDiscovery)
);
assert_eq!(hbh_repr.options[2], Ipv6OptionRepr::PadN(0));
let icmpv6_packet =
Icmpv6Packet::new_checked(&ip_payload[hbh_repr.buffer_len()..]).unwrap();
let icmpv6_repr = Icmpv6Repr::parse(
&ipv6_packet.src_addr(),
&ipv6_packet.dst_addr(),
&icmpv6_packet,
checksum_caps,
)
.unwrap();
let record_data = match icmpv6_repr {
Icmpv6Repr::Mld(MldRepr::Report {
nr_mcast_addr_rcrds,
data,
}) => {
assert_eq!(nr_mcast_addr_rcrds, results.len() as u16);
data
}
other => panic!("unexpected icmpv6_repr: {:?}", other),
};
let mut record_reprs = Vec::new();
let mut payload = record_data;
while !payload.is_empty() {
let record = MldAddressRecord::new_checked(payload).unwrap();
let mut record_repr = MldAddressRecordRepr::parse(&record).unwrap();
payload = record_repr.payload;
record_repr.payload = &[];
record_reprs.push(record_repr);
}
let expected_records = results
.iter()
.map(|addr| MldAddressRecordRepr {
num_srcs: 0,
mcast_addr: *addr,
record_type: MldRecordType::ModeIsExclude,
aux_data_len: 0,
payload: &[],
})
.collect::<Vec<_>>();
assert_eq!(record_reprs, expected_records);
}
}
#[rstest]
#[case(Medium::Ethernet)]
#[cfg(all(feature = "multicast", feature = "medium-ethernet"))]
fn test_solicited_node_multicast_autojoin(#[case] medium: Medium) {
let (mut iface, _, _) = setup(medium);
let addr1 = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
let addr2 = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2);
iface.update_ip_addrs(|ip_addrs| {
ip_addrs.clear();
ip_addrs.push(IpCidr::new(addr1.into(), 64)).unwrap();
});
assert!(iface.has_multicast_group(addr1.solicited_node()));
assert!(!iface.has_multicast_group(addr2.solicited_node()));
iface.update_ip_addrs(|ip_addrs| {
ip_addrs.clear();
ip_addrs.push(IpCidr::new(addr2.into(), 64)).unwrap();
});
assert!(!iface.has_multicast_group(addr1.solicited_node()));
assert!(iface.has_multicast_group(addr2.solicited_node()));
iface.update_ip_addrs(|ip_addrs| {
ip_addrs.clear();
ip_addrs.push(IpCidr::new(addr1.into(), 64)).unwrap();
ip_addrs.push(IpCidr::new(addr2.into(), 64)).unwrap();
});
assert!(iface.has_multicast_group(addr1.solicited_node()));
assert!(iface.has_multicast_group(addr2.solicited_node()));
iface.update_ip_addrs(|ip_addrs| {
ip_addrs.clear();
});
assert!(!iface.has_multicast_group(addr1.solicited_node()));
assert!(!iface.has_multicast_group(addr2.solicited_node()));
}