/// Receive an IPv6 packet from a device.
///
/// `frame_dst` specifies whether this packet was received in a broadcast or
/// unicast link-layer frame.
Receive an IPv6 packet from a device.
`frame_dst` specifies whether this packet was received in a broadcast or
unicast link-layer frame.
Receive an IPv6 packet from a device . ` frame_dst ` specifies whether this packet was received in a broadcast or unicast link - layer frame .
pub(crate) fn receive_ipv6_packet<B: BufferMut, D: BufferDispatcher<B>>(
    ctx: &mut Ctx<D>,
    device: DeviceId,
    frame_dst: FrameDestination,
    mut buffer: B,
) {
    increment_counter!(ctx, "receive_ipv6_packet");
    trace!("receive_ipv6_packet({})", device);
    let mut packet: Ipv6Packet<_> = match try_parse_ip_packet!(buffer) {
        Ok(packet) => packet,
        // Conditionally send an ICMP response if we encountered a parameter
        // problem error when parsing an IPv4 packet. Note, we do not always
        // send back an ICMP response as it can be used as an attack vector for
        // DDoS attacks. We only send back an ICMP response if the RFC requires
        // that we MUST send one, as noted by `must_send_icmp` and `action`.
        Err(IpParseError::ParameterProblem {
            src_ip,
            dst_ip,
            code,
            pointer,
            must_send_icmp,
            header_len: _,
            action,
        }) if must_send_icmp && action.should_send_icmp(&dst_ip) => {
            let dst_ip = try_unit!(SpecifiedAddr::new(dst_ip), debug!("receive_ipv6_packet: Received packet with unspecified destination IP address; dropping"));
            let src_ip = try_unit!(SpecifiedAddr::new(src_ip), trace!("receive_ipv6_packet: Cannot send ICMP error in response to packet with unspecified source IP address"));
            send_icmpv6_parameter_problem(
                ctx,
                device,
                frame_dst,
                src_ip,
                dst_ip,
                code,
                Icmpv6ParameterProblem::new(pointer),
                buffer,
                action.should_send_icmp_to_multicast(),
            );
            return;
        }
        _ => return, // TODO(joshlf): Do something with ICMP here?
    };
    trace!("receive_ipv6_packet: parsed packet: {:?}", packet);
    // TODO(ghanan): Act upon extension headers.
    let src_ip = try_unit!(packet.src_ipv6(), {
        debug!(
            "receive_ipv6_packet: received packet from non-unicast source {}; dropping",
            packet.src_ip()
        );
        increment_counter!(ctx, "receive_ipv6_packet: non-unicast source");
    });
    let dst_ip = try_unit!(SpecifiedAddr::new(packet.dst_ip()), debug!("receive_ipv6_packet: Received packet with unspecified destination IP address; dropping"));
    if Ipv6::LOOPBACK_SUBNET.contains(&dst_ip) {
        // A packet from outside this host was sent with the destination IP of
        // the loopback address, which is illegal. Loopback traffic is handled
        // explicitly in send_ip_packet.
        //
        // TODO(joshlf): Do something with ICMP here?
        debug!("got packet from remote host for loopback address {}", packet.dst_ip());
    } else if deliver_ipv6(ctx, device, dst_ip) {
        trace!("receive_ipv6_packet: delivering locally");
        // Process a potential IPv6 fragment if the destination is this host.
        //
        // We need to process extension headers in the order they appear in the
        // header. With some extension headers, we do not proceed to the next
        // header, and do some action immediately. For example, say we have an
        // IPv6 packet with two extension headers (routing extension header
        // before a fragment extension header). Until we get to the final
        // destination node in the routing header, we would need to reroute the
        // packet to the next destination without reassembling. Once the packet
        // gets to the last destination in the routing header, that node will
        // process the fragment extension header and handle reassembly.
        //
        // We also need to make sure the destination address is not actually a
        // tentative address we are performing NDP's Duplicate Address Detection
        // on.
        //
        // As per RFC 4862 section 5.4:
        //
        //   An address on which the Duplicate Address Detection procedure is
        //   applied is said to be tentative until the procedure has completed
        //   successfully. A tentative address is not considered "assigned to an
        //   interface" in the traditional sense.  That is, the interface must
        //   accept Neighbor Solicitation and Advertisement messages containing
        //   the tentative address in the Target Address field, but processes
        //   such packets differently from those whose Target Address matches an
        //   address assigned to the interface. Other packets addressed to the
        //   tentative address should be silently discarded. Note that the
        //   "other packets" include Neighbor Solicitation and Advertisement
        //   messages that have the tentative (i.e., unicast) address as the IP
        //   destination address and contain the tentative address in the Target
        //   Address field.  Such a case should not happen in normal operation,
        //   though, since these messages are multicasted in the Duplicate
        //   Address Detection procedure.
        //
        // That is, we accept no packets destined to a tentative address. NS and
        // NA packets should be addressed to a multicast address that we would
        // have joined during DAD so that we can receive those packets.
        //
        // Here we check to see if the packet's destination address is the IPv6
        // address assigned to the device. If it is, we check to see if it is
        // tentative. If the destination address is not the address assigned to
        // the device, or it is not tentative, we are okay to proceed; else, we
        // drop the packet.
        if crate::device::is_addr_tentative_on_device(ctx, &dst_ip, device) {
            // Silently drop as per RFC 4862 section 5.4
            trace!(
                "receive_ipv6_packet: Dropping packet as it is destined for a tentative address"
            );
            return;
        }
        // Handle extension headers first.
        match ipv6::handle_extension_headers(ctx, device, frame_dst, &packet, true) {
            Ipv6PacketAction::_Discard => {
                trace!("receive_ipv6_packet: handled IPv6 extension headers: discarding packet");
            }
            Ipv6PacketAction::Continue => {
                trace!("receive_ipv6_packet: handled IPv6 extension headers: dispatching packet");
                // TODO(joshlf):
                // - Do something with ICMP if we don't have a handler for that
                //   protocol?
                // - Check for already-expired TTL?
                let (_, _, proto, meta) = packet.into_metadata();
                dispatch_receive_ipv6_packet(
                    ctx,
                    Some(device),
                    frame_dst,
                    src_ip,
                    dst_ip,
                    proto,
                    buffer,
                    Some(meta),
                );
            }
            Ipv6PacketAction::ProcessFragment => {
                trace!(
                    "receive_ipv6_packet: handled IPv6 extension headers: handling fragmented packet"
                );
                // Note, the `process_fragment` function (which is called by the
                // `process_fragment!` macro) could panic if the packet does not
                // have fragment data. However, we are guaranteed that it will
                // not panic for an IPv6 packet because the fragment data is in
                // an (optional) fragment extension header which we attempt to
                // handle by calling `ipv6::handle_extension_headers`. We will
                // only end up here if its return value is
                // `Ipv6PacketAction::ProcessFragment` which is only possible
                // when the packet has the fragment extension header (even if
                // the fragment data has values that implies that the packet is
                // not fragmented).
                //
                // TODO(ghanan): Handle extension headers again since there
                //               could be some more in a reassembled packet
                //               (after the fragment header).
                process_fragment!(
                    ctx,
                    dispatch_receive_ipv6_packet,
                    device,
                    frame_dst,
                    buffer,
                    packet,
                    src_ip,
                    dst_ip,
                    Ipv6
                );
            }
        }
    } else {
        match forward(ctx, device, dst_ip) {
            ForwardDestination::Destination(dest) => {
                let ttl = packet.ttl();
                if ttl > 1 {
                    trace!("receive_ipv6_packet: forwarding");
                    // Handle extension headers first.
                    match ipv6::handle_extension_headers(ctx, device, frame_dst, &packet, false) {
                        Ipv6PacketAction::_Discard => {
                            trace!("receive_ipv6_packet: handled IPv6 extension headers: discarding packet");
                            return;
                        }
                        Ipv6PacketAction::Continue => {
                            trace!("receive_ipv6_packet: handled IPv6 extension headers: forwarding packet");
                        }
                        Ipv6PacketAction::ProcessFragment => unreachable!("When forwarding packets, we should only ever look at the hop by hop options extension header (if present)"),
                    }
                    packet.set_ttl(ttl - 1);
                    let (_, _, proto, meta) = drop_packet_and_undo_parse!(packet, buffer);
                    if let Err(buffer) =
                        crate::device::send_ip_frame(ctx, dest.device, dest.next_hop, buffer)
                    {
                        debug!("failed to forward IPv6 packet: MTU exceeded");
                        if let Ipv6SourceAddr::Unicast(src_ip) = src_ip {
                            trace!("receive_ipv6_packet: Sending ICMPv6 Packet Too Big");
                            // TODO(joshlf): Increment the TTL since we just
                            // decremented it. The fact that we don't do this is
                            // technically a violation of the ICMP spec (we're
                            // not encapsulating the original packet that caused
                            // the issue, but a slightly modified version of
                            // it), but it's not that big of a deal because it
                            // won't affect the sender's ability to figure out
                            // the minimum path MTU. This may break other logic,
                            // though, so we should still fix it eventually.
                            let mtu = crate::device::get_mtu(ctx, device);
                            crate::ip::icmp::send_icmpv6_packet_too_big(
                                ctx,
                                device,
                                frame_dst,
                                src_ip,
                                dst_ip,
                                proto,
                                mtu,
                                buffer,
                                meta.header_len(),
                            );
                        }
                    }
                } else {
                    // Hop Limit is 0 or would become 0 after decrement; see RFC
                    // 2460 Section 3.
                    debug!("received IPv6 packet dropped due to expired Hop Limit");
                    if let Ipv6SourceAddr::Unicast(src_ip) = src_ip {
                        let (_, _, proto, meta) = drop_packet_and_undo_parse!(packet, buffer);
                        icmp::send_icmpv6_ttl_expired(
                            ctx,
                            device,
                            frame_dst,
                            src_ip,
                            dst_ip,
                            proto,
                            buffer,
                            meta.header_len(),
                        );
                    }
                }
            }
            ForwardDestination::NoRouteToHost => {
                let (_, _, proto, meta) = drop_packet_and_undo_parse!(packet, buffer);
                debug!("received IPv6 packet with no known route to destination {}", dst_ip);
                if let Ipv6SourceAddr::Unicast(src_ip) = src_ip {
                    icmp::send_icmpv6_net_unreachable(
                        ctx,
                        device,
                        frame_dst,
                        src_ip,
                        dst_ip,
                        proto,
                        buffer,
                        meta.header_len(),
                    );
                }
            }
            ForwardDestination::ForwardingDisabled => {
                // TODO(fxbug.dev/21182): Check to make sure the behavior here
                // is the same as for IPv4.
                //
                // When forwarding is disabled, we silently drop packets not
                // destined for this host, based on RFC 1122 § 3.2.1.3, however
                // this RFC predates IPv6, so the v6 behavior could be
                // different.
                debug!(
                    "received IPv6 packet to non-local destination {} with forwarding disabled",
                    dst_ip
                );
            }
        }
    }
}
pub ( crate ) fn receive_ipv6_packet < B : BufferMut , D : BufferDispatcher < B > > ( ctx : & mut Ctx < D > , device : DeviceId , frame_dst : FrameDestination , mut buffer : B , ) { increment_counter ! ( ctx " " ) ; trace ! ( " " device ) ; let mut packet : Ipv6Packet < _ > = match try_parse_ip_packet ! ( buffer ) { Ok ( packet ) => packet , // Conditionally send an ICMP response if we encountered a parameter // problem error when parsing an IPv4 packet. Note, we do not always // send back an ICMP response as it can be used as an attack vector for // DDoS attacks. We only send back an ICMP response if the RFC requires // that we MUST send one, as noted by `must_send_icmp` and `action`. Err ( IpParseError :: ParameterProblem { src_ip , dst_ip , code , pointer , must_send_icmp , header_len : _ , action , } ) if must_send_icmp && action . should_send_icmp ( & dst_ip ) => { let dst_ip = try_unit ! ( SpecifiedAddr new ( dst_ip ) debug ( " " ) ) ; let src_ip = try_unit ! ( SpecifiedAddr new ( src_ip ) trace ( " " ) ) ; send_icmpv6_parameter_problem ( ctx , device , frame_dst , src_ip , dst_ip , code , Icmpv6ParameterProblem :: new ( pointer ) , buffer , action . should_send_icmp_to_multicast ( ) , ) ; return ; } _ => return , // TODO(joshlf): Do something with ICMP here? } ; trace ! ( " " packet ) ; // TODO(ghanan): Act upon extension headers. let src_ip = try_unit ! ( packet src_ipv6 ( ) { debug ( " " packet src_ip ( ) ) increment_counter ( ctx " " ) } ) ; let dst_ip = try_unit ! ( SpecifiedAddr new ( packet dst_ip ( ) ) debug ( " " ) ) ; if Ipv6 :: LOOPBACK_SUBNET . contains ( & dst_ip ) { // A packet from outside this host was sent with the destination IP of // the loopback address, which is illegal. Loopback traffic is handled // explicitly in send_ip_packet. // // TODO(joshlf): Do something with ICMP here? debug ! ( " " packet dst_ip ( ) ) ; } else if deliver_ipv6 ( ctx , device , dst_ip ) { trace ! ( " " ) ; // Process a potential IPv6 fragment if the destination is this host. // // We need to process extension headers in the order they appear in the // header. With some extension headers, we do not proceed to the next // header, and do some action immediately. For example, say we have an // IPv6 packet with two extension headers (routing extension header // before a fragment extension header). Until we get to the final // destination node in the routing header, we would need to reroute the // packet to the next destination without reassembling. Once the packet // gets to the last destination in the routing header, that node will // process the fragment extension header and handle reassembly. // // We also need to make sure the destination address is not actually a // tentative address we are performing NDP's Duplicate Address Detection // on. // // As per RFC 4862 section 5.4: // //   An address on which the Duplicate Address Detection procedure is //   applied is said to be tentative until the procedure has completed //   successfully. A tentative address is not considered "assigned to an //   interface" in the traditional sense.  That is, the interface must //   accept Neighbor Solicitation and Advertisement messages containing //   the tentative address in the Target Address field, but processes //   such packets differently from those whose Target Address matches an //   address assigned to the interface. Other packets addressed to the //   tentative address should be silently discarded. Note that the //   "other packets" include Neighbor Solicitation and Advertisement //   messages that have the tentative (i.e., unicast) address as the IP //   destination address and contain the tentative address in the Target //   Address field.  Such a case should not happen in normal operation, //   though, since these messages are multicasted in the Duplicate //   Address Detection procedure. // // That is, we accept no packets destined to a tentative address. NS and // NA packets should be addressed to a multicast address that we would // have joined during DAD so that we can receive those packets. // // Here we check to see if the packet's destination address is the IPv6 // address assigned to the device. If it is, we check to see if it is // tentative. If the destination address is not the address assigned to // the device, or it is not tentative, we are okay to proceed; else, we // drop the packet. if crate :: device :: is_addr_tentative_on_device ( ctx , & dst_ip , device ) { // Silently drop as per RFC 4862 section 5.4 trace ! ( " " ) ; return ; } // Handle extension headers first. match ipv6 :: handle_extension_headers ( ctx , device , frame_dst , & packet , true ) { Ipv6PacketAction :: _Discard => { trace ! ( " " ) ; } Ipv6PacketAction :: Continue => { trace ! ( " " ) ; // TODO(joshlf): // - Do something with ICMP if we don't have a handler for that //   protocol? // - Check for already-expired TTL? let ( _ , _ , proto , meta ) = packet . into_metadata ( ) ; dispatch_receive_ipv6_packet ( ctx , Some ( device ) , frame_dst , src_ip , dst_ip , proto , buffer , Some ( meta ) , ) ; } Ipv6PacketAction :: ProcessFragment => { trace ! ( " " ) ; // Note, the `process_fragment` function (which is called by the // `process_fragment!` macro) could panic if the packet does not // have fragment data. However, we are guaranteed that it will // not panic for an IPv6 packet because the fragment data is in // an (optional) fragment extension header which we attempt to // handle by calling `ipv6::handle_extension_headers`. We will // only end up here if its return value is // `Ipv6PacketAction::ProcessFragment` which is only possible // when the packet has the fragment extension header (even if // the fragment data has values that implies that the packet is // not fragmented). // // TODO(ghanan): Handle extension headers again since there //               could be some more in a reassembled packet //               (after the fragment header). process_fragment ! ( ctx dispatch_receive_ipv6_packet device frame_dst buffer packet src_ip dst_ip Ipv6 ) ; } } } else { match forward ( ctx , device , dst_ip ) { ForwardDestination :: Destination ( dest ) => { let ttl = packet . ttl ( ) ; if ttl > 1 { trace ! ( " " ) ; // Handle extension headers first. match ipv6 :: handle_extension_headers ( ctx , device , frame_dst , & packet , false ) { Ipv6PacketAction :: _Discard => { trace ! ( " " ) ; return ; } Ipv6PacketAction :: Continue => { trace ! ( " " ) ; } Ipv6PacketAction :: ProcessFragment => unreachable ! ( " " ) , } packet . set_ttl ( ttl - 1 ) ; let ( _ , _ , proto , meta ) = drop_packet_and_undo_parse ! ( packet buffer ) ; if let Err ( buffer ) = crate :: device :: send_ip_frame ( ctx , dest . device , dest . next_hop , buffer ) { debug ! ( " " ) ; if let Ipv6SourceAddr :: Unicast ( src_ip ) = src_ip { trace ! ( " " ) ; // TODO(joshlf): Increment the TTL since we just // decremented it. The fact that we don't do this is // technically a violation of the ICMP spec (we're // not encapsulating the original packet that caused // the issue, but a slightly modified version of // it), but it's not that big of a deal because it // won't affect the sender's ability to figure out // the minimum path MTU. This may break other logic, // though, so we should still fix it eventually. let mtu = crate :: device :: get_mtu ( ctx , device ) ; crate :: ip :: icmp :: send_icmpv6_packet_too_big ( ctx , device , frame_dst , src_ip , dst_ip , proto , mtu , buffer , meta . header_len ( ) , ) ; } } } else { // Hop Limit is 0 or would become 0 after decrement; see RFC // 2460 Section 3. debug ! ( " " ) ; if let Ipv6SourceAddr :: Unicast ( src_ip ) = src_ip { let ( _ , _ , proto , meta ) = drop_packet_and_undo_parse ! ( packet buffer ) ; icmp :: send_icmpv6_ttl_expired ( ctx , device , frame_dst , src_ip , dst_ip , proto , buffer , meta . header_len ( ) , ) ; } } } ForwardDestination :: NoRouteToHost => { let ( _ , _ , proto , meta ) = drop_packet_and_undo_parse ! ( packet buffer ) ; debug ! ( " " dst_ip ) ; if let Ipv6SourceAddr :: Unicast ( src_ip ) = src_ip { icmp :: send_icmpv6_net_unreachable ( ctx , device , frame_dst , src_ip , dst_ip , proto , buffer , meta . header_len ( ) , ) ; } } ForwardDestination :: ForwardingDisabled => { // TODO(fxbug.dev/21182): Check to make sure the behavior here // is the same as for IPv4. // // When forwarding is disabled, we silently drop packets not // destined for this host, based on RFC 1122 § 3.2.1.3, however // this RFC predates IPv6, so the v6 behavior could be // different. debug ! ( " " dst_ip ) ; } } } }
Receive an IPv6 packet from a device.
Receive an IPv6 packet from a device .
ctx
&mut Ctx<D>
device
DeviceId
frame_dst
FrameDestination
buffer
B