|
| 1 | +/* |
| 2 | +* Copyright 2019 Comcast Cable Communications Management, LLC |
| 3 | +* |
| 4 | +* Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +* you may not use this file except in compliance with the License. |
| 6 | +* You may obtain a copy of the License at |
| 7 | +* |
| 8 | +* http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +* |
| 10 | +* Unless required by applicable law or agreed to in writing, software |
| 11 | +* distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +* See the License for the specific language governing permissions and |
| 14 | +* limitations under the License. |
| 15 | +* |
| 16 | +* SPDX-License-Identifier: Apache-2.0 |
| 17 | +*/ |
| 18 | + |
| 19 | +use crate::ensure; |
| 20 | +use crate::packets::ethernet::Ethernet; |
| 21 | +use crate::packets::gre::{Gre, GreTunnelPacket}; |
| 22 | +use crate::packets::ip::v4::Ipv4; |
| 23 | +use crate::packets::ip::{IpPacket, ProtocolNumbers}; |
| 24 | +use crate::packets::{Datalink, Packet, Tunnel}; |
| 25 | +use anyhow::{anyhow, Result}; |
| 26 | +use std::marker::PhantomData; |
| 27 | + |
| 28 | +/// Arbitrary layer 3 protocol encapsulated in IPv4 with GRE based on |
| 29 | +/// [IETF RFC 2784]. |
| 30 | +/// |
| 31 | +/// The payload is first encapsulated in a GRE packet. The resulting |
| 32 | +/// GRE packet is then encapsulated in IPv4 and then forwarded. |
| 33 | +/// |
| 34 | +/// [IETF RFC 2784]: https://datatracker.ietf.org/doc/html/rfc2784 |
| 35 | +/// [IPv4]: crate::packets::ip::v4::Ipv4 |
| 36 | +#[derive(Debug)] |
| 37 | +pub struct Ip4Gre<P: Packet<Envelope = D>, D: Datalink = Ethernet> { |
| 38 | + _phantom1: PhantomData<P>, |
| 39 | + _phantom2: PhantomData<D>, |
| 40 | +} |
| 41 | + |
| 42 | +impl<P: Packet<Envelope = D>, D: Datalink> Tunnel for Ip4Gre<P, D> { |
| 43 | + type Payload = P; |
| 44 | + type Delivery = Ipv4<D>; |
| 45 | + |
| 46 | + /// Encapsulates the existing layer-3 packet by prepending an outer IPv4 |
| 47 | + /// and a GRE packets. |
| 48 | + fn encap(payload: Self::Payload) -> Result<Self::Delivery> { |
| 49 | + let envelope = payload.deparse(); |
| 50 | + let protocol_type = envelope.protocol_type(); |
| 51 | + let ip4 = envelope.push::<Self::Delivery>()?; |
| 52 | + let mut gre = ip4.push::<Gre<Self::Delivery>>()?; |
| 53 | + gre.set_protocol_type(protocol_type); |
| 54 | + gre.reconcile_all(); |
| 55 | + Ok(gre.deparse()) |
| 56 | + } |
| 57 | + |
| 58 | + /// Decapsulates the outer IPv4 and GRE packets and returns the original |
| 59 | + /// layer-3 packet. |
| 60 | + fn decap(delivery: Self::Delivery) -> Result<Self::Payload> { |
| 61 | + ensure!(delivery.gre_payload(), anyhow!("not an Ip4Gre tunnel.")); |
| 62 | + |
| 63 | + let gre = delivery.parse::<Gre<Self::Delivery>>()?; |
| 64 | + let protocol_type = gre.protocol_type(); |
| 65 | + |
| 66 | + let mut envelope = gre.remove()?.remove()?; |
| 67 | + envelope.set_protocol_type(protocol_type); |
| 68 | + envelope.parse::<Self::Payload>() |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +/// Generic impl for all IpPacket. |
| 73 | +impl<T: IpPacket> GreTunnelPacket for T { |
| 74 | + fn gre_payload(&self) -> bool { |
| 75 | + self.next_protocol() == ProtocolNumbers::Gre |
| 76 | + } |
| 77 | + |
| 78 | + fn mark_gre_payload(&mut self) { |
| 79 | + self.set_next_protocol(ProtocolNumbers::Gre) |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +#[cfg(test)] |
| 84 | +mod tests { |
| 85 | + use super::*; |
| 86 | + use crate::packets::ethernet::EtherTypes; |
| 87 | + use crate::packets::icmp::v4::EchoRequest; |
| 88 | + use crate::packets::icmp::v6::EchoReply; |
| 89 | + use crate::packets::ip::v6::Ipv6; |
| 90 | + use crate::packets::Mbuf; |
| 91 | + use crate::testils::byte_arrays::{IP4GRE_PACKET, IPIP_PACKET}; |
| 92 | + |
| 93 | + #[capsule::test] |
| 94 | + fn encap_ip4gre_payload() { |
| 95 | + let packet = Mbuf::new().unwrap(); |
| 96 | + let ethernet = packet.push::<Ethernet>().unwrap(); |
| 97 | + let ip6 = ethernet.push::<Ipv6>().unwrap(); |
| 98 | + let mut ip6 = ip6.push::<EchoReply>().unwrap().deparse(); |
| 99 | + ip6.reconcile(); |
| 100 | + let payload_len = ip6.len(); |
| 101 | + |
| 102 | + let delivery = ip6.encap::<Ip4Gre<Ipv6>>().unwrap(); |
| 103 | + assert_eq!(EtherTypes::Ipv4, delivery.envelope().protocol_type()); |
| 104 | + assert_eq!(ProtocolNumbers::Gre, delivery.protocol()); |
| 105 | + |
| 106 | + let gre = delivery.parse::<Gre<Ipv4>>().unwrap(); |
| 107 | + assert_eq!(EtherTypes::Ipv6, gre.protocol_type()); |
| 108 | + |
| 109 | + // check payload matches original packet length |
| 110 | + assert_eq!(payload_len, gre.payload_len()); |
| 111 | + } |
| 112 | + |
| 113 | + #[capsule::test] |
| 114 | + fn decap_ip4gre_delivery() { |
| 115 | + let packet = Mbuf::from_bytes(&IP4GRE_PACKET).unwrap(); |
| 116 | + let ethernet = packet.parse::<Ethernet>().unwrap(); |
| 117 | + let delivery = ethernet.parse::<Ipv4>().unwrap(); |
| 118 | + let payload = delivery.decap::<Ip4Gre<Ipv4>>().unwrap(); |
| 119 | + |
| 120 | + assert_eq!("1.1.1.1", payload.src().to_string()); |
| 121 | + assert_eq!("2.2.2.2", payload.dst().to_string()); |
| 122 | + |
| 123 | + // parse the payload's payload to verify packet integrity |
| 124 | + assert!(payload.parse::<EchoRequest>().is_ok()); |
| 125 | + } |
| 126 | + |
| 127 | + #[capsule::test] |
| 128 | + fn decap_not_ip4gre() { |
| 129 | + let packet = Mbuf::from_bytes(&IPIP_PACKET).unwrap(); |
| 130 | + let ethernet = packet.parse::<Ethernet>().unwrap(); |
| 131 | + let notip4gre = ethernet.parse::<Ipv4>().unwrap(); |
| 132 | + |
| 133 | + // not an ip4gre tunnel |
| 134 | + assert!(notip4gre.decap::<Ip4Gre<Ipv4>>().is_err()); |
| 135 | + } |
| 136 | +} |
0 commit comments