blob: 573b2e95ad8910ccd478fbad21a20173d3d7dae8 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! The Internet Control Message Protocol (ICMP).
use std::mem;
use log::trace;
use packet::{BufferMut, BufferSerializer, Serializer};
use crate::ip::{send_ip_packet, IpAddr, IpProto, Ipv4, Ipv6};
use crate::wire::icmp::{IcmpPacketBuilder, IcmpParseArgs, Icmpv4Packet, Icmpv6Packet};
use crate::{Context, EventDispatcher};
/// Receive an ICMP message in an IP packet.
pub fn receive_icmp_packet<D: EventDispatcher, A: IpAddr, B: BufferMut>(
ctx: &mut Context<D>, src_ip: A, dst_ip: A, buffer: B,
) -> bool {
trace!("receive_icmp_packet({}, {})", src_ip, dst_ip);
specialize_ip_addr!(
fn receive_icmp_packet<D, B>(ctx: &mut Context<D>, src_ip: Self, dst_ip: Self, buffer: B) -> bool
where
D: EventDispatcher,
B: BufferMut,
{
Ipv4Addr => {
let mut buffer = buffer;
let packet = match buffer.parse_with::<_, Icmpv4Packet<_>>(IcmpParseArgs::new(src_ip, dst_ip)) {
Ok(packet) => packet,
Err(err) => return false,
};
match packet {
Icmpv4Packet::EchoRequest(echo_request) => {
let req = *echo_request.message();
let code = echo_request.code();
// drop packet so we can re-use the underlying buffer
mem::drop(echo_request);
increment_counter!(ctx, "receive_icmp_packet::echo_request");
// we're responding to the sender, so these are flipped
let (src_ip, dst_ip) = (dst_ip, src_ip);
send_ip_packet(
ctx,
dst_ip,
IpProto::Icmp,
|src_ip| BufferSerializer::new_vec(buffer).encapsulate(IcmpPacketBuilder::<Ipv4, &[u8], _>::new(src_ip, dst_ip, code, req.reply())),
);
true
}
Icmpv4Packet::EchoReply(echo_reply) => {
increment_counter!(ctx, "receive_icmp_packet::echo_reply");
trace!("receive_icmp_packet: Received an EchoReply message");
true
}
_ => log_unimplemented!(
false,
"ip::icmp::receive_icmp_packet: Not implemented for this packet type"
),
}
}
Ipv6Addr => {
let mut buffer = buffer;
let packet = match buffer.parse_with::<_, Icmpv6Packet<_>>(IcmpParseArgs::new(src_ip, dst_ip)) {
Ok(packet) => packet,
Err(err) => return false,
};
match packet {
Icmpv6Packet::EchoRequest(echo_request) => {
let req = *echo_request.message();
let code = echo_request.code();
// drop packet so we can re-use the underlying buffer
mem::drop(echo_request);
increment_counter!(ctx, "receive_icmp_packet::echo_request");
// we're responding to the sender, so these are flipped
let (src_ip, dst_ip) = (dst_ip, src_ip);
send_ip_packet(
ctx,
dst_ip,
IpProto::Icmp,
|src_ip| BufferSerializer::new_vec(buffer).encapsulate(IcmpPacketBuilder::<Ipv6, &[u8], _>::new(src_ip, dst_ip, code, req.reply())),
);
true
}
Icmpv6Packet::EchoReply(echo_reply) => {
increment_counter!(ctx, "receive_icmp_packet::echo_reply");
trace!("receive_icmp_packet: Received an EchoReply message");
true
}
_ => log_unimplemented!(
false,
"ip::icmp::receive_icmp_packet: Not implemented for this packet type"
)
}
}
}
);
A::receive_icmp_packet(ctx, src_ip, dst_ip, buffer)
}
#[cfg(test)]
mod tests {
use packet::Buf;
use super::*;
use crate::ip::{Ip, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
use crate::testutil::DummyEventDispatcher;
use crate::Context;
#[test]
fn test_send_echo_request_v4() {
use crate::ip::testdata::icmp_echo_v4::*;
let mut ctx = Context::new(Default::default(), DummyEventDispatcher::default());
let src = <Ipv4 as Ip>::LOOPBACK_ADDRESS;
let dst = Ipv4Addr::new([192, 168, 1, 5]);
let mut bytes = REQUEST_IP_PACKET_BYTES.to_owned();
receive_icmp_packet(&mut ctx, src, dst, Buf::new(&mut bytes[20..], ..));
assert_eq!(
ctx.state()
.test_counters
.get("receive_icmp_packet::echo_request"),
&1
);
// Check that the echo request was replied to.
assert_eq!(ctx.state().test_counters.get("send_ip_packet"), &1);
assert_eq!(
ctx.state().test_counters.get("send_ip_packet::loopback"),
&1
);
assert_eq!(
ctx.state().test_counters.get("dispatch_receive_ip_packet"),
&1
);
assert_eq!(
ctx.state()
.test_counters
.get("receive_icmp_packet::echo_reply"),
&1
);
}
#[test]
fn test_send_echo_request_v6() {
use crate::ip::testdata::icmp_echo_v6::*;
let mut ctx = Context::new(Default::default(), DummyEventDispatcher::default());
let src = <Ipv6 as Ip>::LOOPBACK_ADDRESS;
let dst = Ipv6Addr::new([0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let mut bytes = REQUEST_IP_PACKET_BYTES.to_owned();
receive_icmp_packet(&mut ctx, src, dst, Buf::new(&mut bytes[40..], ..));
assert_eq!(
ctx.state()
.test_counters
.get("receive_icmp_packet::echo_request"),
&1
);
// Check that the echo request was replied to.
assert_eq!(ctx.state().test_counters.get("send_ip_packet"), &1);
assert_eq!(
ctx.state().test_counters.get("send_ip_packet::loopback"),
&1
);
assert_eq!(
ctx.state().test_counters.get("dispatch_receive_ip_packet"),
&1
);
assert_eq!(
ctx.state()
.test_counters
.get("receive_icmp_packet::echo_reply"),
&1
);
}
}