[recovery-netstack] Tell IP layer: unicast or broadcast frame?

- Introduce the new FrameDestination type, expressing whether a
  frame was destined to a unicast link-layer address or a broadcast
  address
- Pass this as a parameter to ip::receive_ip_packet
- This functionality is exercised in a later commit, where ICMP
  needs the information in order to make decisions about whether to
  send an ICMP response (in particular, the RFC 1122 restrictions)

Change-Id: I2cc2055b151047a918eeea0f38115a4647d3e7c6
diff --git a/garnet/bin/recovery_netstack/core/src/device/ethernet.rs b/garnet/bin/recovery_netstack/core/src/device/ethernet.rs
index 04df64c..6c1a396 100644
--- a/garnet/bin/recovery_netstack/core/src/device/ethernet.rs
+++ b/garnet/bin/recovery_netstack/core/src/device/ethernet.rs
@@ -12,7 +12,7 @@
 use zerocopy::{AsBytes, FromBytes, Unaligned};
 
 use crate::device::arp::{ArpDevice, ArpHardwareType, ArpState};
-use crate::device::DeviceId;
+use crate::device::{DeviceId, FrameDestination};
 use crate::ip::{AddrSubnet, Ip, IpAddress, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
 use crate::wire::arp::peek_arp_types;
 use crate::wire::ethernet::{EthernetFrame, EthernetFrameBuilder};
@@ -263,6 +263,14 @@
 
     let (src, dst) = (frame.src_mac(), frame.dst_mac());
     let device = DeviceId::new_ethernet(device_id);
+    let frame_dst = if dst == get_device_state(ctx, device_id).mac {
+        FrameDestination::Unicast
+    } else if dst.is_broadcast() {
+        FrameDestination::Broadcast
+    } else {
+        return;
+    };
+
     match frame.ethertype() {
         Some(EtherType::Arp) => {
             let types = if let Ok(types) = peek_arp_types(buffer.as_ref()) {
@@ -280,8 +288,12 @@
                 types => debug!("got ARP packet for unsupported types: {:?}", types),
             }
         }
-        Some(EtherType::Ipv4) => crate::ip::receive_ip_packet::<D, _, Ipv4>(ctx, device, buffer),
-        Some(EtherType::Ipv6) => crate::ip::receive_ip_packet::<D, _, Ipv6>(ctx, device, buffer),
+        Some(EtherType::Ipv4) => {
+            crate::ip::receive_ip_packet::<D, _, Ipv4>(ctx, device, frame_dst, buffer)
+        }
+        Some(EtherType::Ipv6) => {
+            crate::ip::receive_ip_packet::<D, _, Ipv6>(ctx, device, frame_dst, buffer)
+        }
         Some(EtherType::Other(_)) | None => {} // TODO(joshlf)
     }
 }
diff --git a/garnet/bin/recovery_netstack/core/src/device/mod.rs b/garnet/bin/recovery_netstack/core/src/device/mod.rs
index aae58ee..3a10803 100644
--- a/garnet/bin/recovery_netstack/core/src/device/mod.rs
+++ b/garnet/bin/recovery_netstack/core/src/device/mod.rs
@@ -65,6 +65,24 @@
     }
 }
 
+// TODO(joshlf): Does the IP layer ever need to distinguish between broadcast
+// and multicast frames?
+
+/// The type of address used as the source address in a device-layer frame:
+/// unicast or broadcast.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub(crate) enum FrameDestination {
+    Unicast,
+    Broadcast,
+}
+
+impl FrameDestination {
+    /// Is this `FrameDestination::Broadcast`?
+    pub(crate) fn is_broadcast(&self) -> bool {
+        *self == FrameDestination::Broadcast
+    }
+}
+
 /// The state associated with the device layer.
 pub(crate) struct DeviceLayerState {
     // Invariant: even though each protocol has its own hash map, IDs (used as
diff --git a/garnet/bin/recovery_netstack/core/src/ip/icmp.rs b/garnet/bin/recovery_netstack/core/src/ip/icmp.rs
index b607cb552..76c3ca1 100644
--- a/garnet/bin/recovery_netstack/core/src/ip/icmp.rs
+++ b/garnet/bin/recovery_netstack/core/src/ip/icmp.rs
@@ -434,7 +434,7 @@
     use std::num::NonZeroU16;
 
     use super::*;
-    use crate::device::DeviceId;
+    use crate::device::{DeviceId, FrameDestination};
     use crate::ip::{receive_ip_packet, IpExt, Ipv4, Ipv4Addr};
     use crate::testutil::{DummyEventDispatcherBuilder, DUMMY_CONFIG};
     use crate::wire::icmp::{
@@ -482,7 +482,12 @@
         let mut ctx = DummyEventDispatcherBuilder::from_config(DUMMY_CONFIG).build();
         // currently only used by test_ttl_exceeded
         ctx.state_mut().ip.v4.forward = true;
-        receive_ip_packet::<_, _, Ipv4>(&mut ctx, DeviceId::new_ethernet(1), buffer);
+        receive_ip_packet::<_, _, Ipv4>(
+            &mut ctx,
+            DeviceId::new_ethernet(1),
+            FrameDestination::Unicast,
+            buffer,
+        );
 
         for counter in assert_counters {
             assert!(*ctx.state().test_counters.get(counter) > 0, "counter at zero: {}", counter);
diff --git a/garnet/bin/recovery_netstack/core/src/ip/mod.rs b/garnet/bin/recovery_netstack/core/src/ip/mod.rs
index 4fa24f0..d070ec10 100644
--- a/garnet/bin/recovery_netstack/core/src/ip/mod.rs
+++ b/garnet/bin/recovery_netstack/core/src/ip/mod.rs
@@ -23,7 +23,7 @@
 };
 use specialize_ip_macro::specialize_ip_address;
 
-use crate::device::DeviceId;
+use crate::device::{DeviceId, FrameDestination};
 use crate::ip::forwarding::{Destination, ForwardingTable};
 use crate::{Context, EventDispatcher};
 
@@ -160,9 +160,13 @@
 }
 
 /// Receive an IP packet from a device.
+///
+/// `frame_dst` specifies whether this packet was received in a broadcast or
+/// unicast link-layer frame.
 pub(crate) fn receive_ip_packet<D: EventDispatcher, B: BufferMut, I: Ip>(
     ctx: &mut Context<D>,
     device: DeviceId,
+    frame_dst: FrameDestination,
     mut buffer: B,
 ) {
     trace!("receive_ip_packet({})", device);