Skip to content

Commit 6fea7ce

Browse files
add ipip tunnel
1 parent b1e9b9d commit 6fea7ce

File tree

6 files changed

+310
-8
lines changed

6 files changed

+310
-8
lines changed

core/src/packets/icmp/v6/ndp/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -814,19 +814,19 @@ mod tests {
814814
/// ICMPv6 packet with invalid MTU-option length.
815815
#[rustfmt::skip]
816816
const INVALID_OPTION_LENGTH: [u8;78] = [
817-
// ** ethernet Header
817+
// ethernet Header
818818
0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
819819
0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
820820
0x86, 0xDD,
821-
// ** IPv6 Header
821+
// IPv6 Header
822822
0x60, 0x00, 0x00, 0x00,
823823
// payload length
824824
0x00, 0x18,
825825
0x3a,
826826
0xff,
827827
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xf0, 0x45, 0xff, 0xfe, 0x0c, 0x66, 0x4b,
828828
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
829-
// ** ICMPv6 Header
829+
// ICMPv6 Header
830830
// type
831831
0x86,
832832
// code

core/src/packets/ip/mod.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
//! Internet Protocol v4 and v6.
2020
21+
pub mod tunnels;
2122
pub mod v4;
2223
pub mod v6;
2324

@@ -61,17 +62,20 @@ pub mod ProtocolNumbers {
6162
/// User Datagram Protocol.
6263
pub const Udp: ProtocolNumber = ProtocolNumber(0x11);
6364

65+
/// IPv4 encapsulation.
66+
pub const Ipv4: ProtocolNumber = ProtocolNumber(0x04);
67+
6468
/// Routing Header for IPv6.
6569
pub const Ipv6Route: ProtocolNumber = ProtocolNumber(0x2B);
6670

6771
/// Fragment Header for IPv6.
6872
pub const Ipv6Frag: ProtocolNumber = ProtocolNumber(0x2C);
6973

70-
/// Internet Control Message Protocol for IPv6.
71-
pub const Icmpv6: ProtocolNumber = ProtocolNumber(0x3A);
72-
7374
/// Internet Control Message Protocol for IPv4.
7475
pub const Icmpv4: ProtocolNumber = ProtocolNumber(0x01);
76+
77+
/// Internet Control Message Protocol for IPv6.
78+
pub const Icmpv6: ProtocolNumber = ProtocolNumber(0x3A);
7579
}
7680

7781
impl fmt::Display for ProtocolNumber {
@@ -82,10 +86,11 @@ impl fmt::Display for ProtocolNumber {
8286
match *self {
8387
ProtocolNumbers::Tcp => "TCP".to_string(),
8488
ProtocolNumbers::Udp => "UDP".to_string(),
89+
ProtocolNumbers::Ipv4 => "IPv4".to_string(),
8590
ProtocolNumbers::Ipv6Route => "IPv6 Route".to_string(),
8691
ProtocolNumbers::Ipv6Frag => "IPv6 Frag".to_string(),
87-
ProtocolNumbers::Icmpv6 => "ICMPv6".to_string(),
8892
ProtocolNumbers::Icmpv4 => "ICMPv4".to_string(),
93+
ProtocolNumbers::Icmpv6 => "ICMPv6".to_string(),
8994
_ => format!("0x{:02x}", self.0),
9095
}
9196
)
@@ -272,10 +277,11 @@ mod tests {
272277
fn protocol_number_to_string() {
273278
assert_eq!("TCP", ProtocolNumbers::Tcp.to_string());
274279
assert_eq!("UDP", ProtocolNumbers::Udp.to_string());
280+
assert_eq!("IPv4", ProtocolNumbers::Ipv4.to_string());
275281
assert_eq!("IPv6 Route", ProtocolNumbers::Ipv6Route.to_string());
276282
assert_eq!("IPv6 Frag", ProtocolNumbers::Ipv6Frag.to_string());
277-
assert_eq!("ICMPv6", ProtocolNumbers::Icmpv6.to_string());
278283
assert_eq!("ICMPv4", ProtocolNumbers::Icmpv4.to_string());
284+
assert_eq!("ICMPv6", ProtocolNumbers::Icmpv6.to_string());
279285
assert_eq!("0x00", ProtocolNumber::new(0).to_string());
280286
}
281287
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
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+
//! IPv4 in IPv4 tunnel.
20+
21+
use crate::ensure;
22+
use crate::packets::ethernet::Ethernet;
23+
use crate::packets::ip::v4::Ipv4;
24+
use crate::packets::ip::ProtocolNumbers;
25+
use crate::packets::{Datalink, Packet, Tunnel};
26+
use anyhow::{anyhow, Result};
27+
use std::marker::PhantomData;
28+
29+
/// [IPv4] encapsulation within IPv4 based on [IETF RFC 2003].
30+
///
31+
/// IP in IP tunnel connects two separate IPv4 networks. an outer IP header
32+
/// is inserted before the datagram's existing IP header, as follows:
33+
///
34+
/// +---------------------------+
35+
/// | |
36+
/// | Outer IP Header |
37+
/// | |
38+
/// +---------------------------+ +---------------------------+
39+
/// | | | |
40+
/// | IP Header | | IP Header |
41+
/// | | | |
42+
/// +---------------------------+ ====> +---------------------------+
43+
/// | | | |
44+
/// | | | |
45+
/// | IP Payload | | IP Payload |
46+
/// | | | |
47+
/// | | | |
48+
/// +---------------------------+ +---------------------------+
49+
///
50+
/// This tunnel only supports unicast packets.
51+
///
52+
/// [IPv4]: Ipv4
53+
/// [IETF RFC 2003]: https://datatracker.ietf.org/doc/html/rfc2003
54+
#[derive(Debug)]
55+
pub struct IpIp<E: Datalink = Ethernet> {
56+
_phantom: PhantomData<E>,
57+
}
58+
59+
impl<E: Datalink> Tunnel for IpIp<E> {
60+
type Payload = Ipv4<E>;
61+
type Delivery = Ipv4<E>;
62+
63+
/// Encapsulates the existing IPv4 packet by prepending an outer IPv4
64+
/// packet.
65+
///
66+
/// The DSCP and ECN options are copied from existing header to the new
67+
/// outer header.
68+
///
69+
/// # Remarks
70+
///
71+
/// If the 'don't fragment' flag is set to true on the outer header, it
72+
/// must not be unset. Otherwise, it may be set after tunnel encapsulation.
73+
///
74+
/// # Errors
75+
///
76+
/// Returns an error if the payload's time-to-live is `0`. The packet
77+
/// should be discarded.
78+
fn encap(payload: Self::Payload) -> Result<Self::Delivery> {
79+
ensure!(payload.ttl() != 0, anyhow!("payload's TTL is 0."));
80+
81+
let dscp = payload.dscp();
82+
let ecn = payload.ecn();
83+
let dont_fragment = payload.dont_fragment();
84+
85+
let envelope = payload.deparse();
86+
let mut delivery = envelope.push::<Self::Delivery>()?;
87+
delivery.set_dscp(dscp);
88+
delivery.set_ecn(ecn);
89+
if dont_fragment {
90+
delivery.set_dont_fragment();
91+
}
92+
delivery.set_protocol(ProtocolNumbers::Ipv4);
93+
delivery.reconcile_all();
94+
95+
Ok(delivery)
96+
}
97+
98+
/// Decapsulates the outer IPv4 packet and returns the original payload
99+
/// IPv4 packet.
100+
///
101+
/// # Errors
102+
///
103+
/// Returns an error if the protocol is not set to `ProtocolNumbers::Ipv4`,
104+
/// indicating the packet is not from an IpIp tunnel.
105+
fn decap(delivery: Self::Delivery) -> Result<Self::Payload> {
106+
ensure!(
107+
delivery.protocol() == ProtocolNumbers::Ipv4,
108+
anyhow!("not an IPIP tunnel.")
109+
);
110+
111+
let envelope = delivery.remove()?;
112+
envelope.parse::<Self::Payload>()
113+
}
114+
}
115+
116+
#[cfg(test)]
117+
mod tests {
118+
use super::*;
119+
use crate::packets::ethernet::Ethernet;
120+
use crate::packets::icmp::v4::EchoRequest;
121+
use crate::packets::Mbuf;
122+
use crate::testils::byte_arrays::IPIP_PACKET;
123+
124+
#[capsule::test]
125+
fn encap_ipip_payload() {
126+
let packet = Mbuf::new().unwrap();
127+
let ethernet = packet.push::<Ethernet>().unwrap();
128+
let ip4 = ethernet.push::<Ipv4>().unwrap();
129+
let mut ip4 = ip4.push::<EchoRequest>().unwrap().deparse();
130+
ip4.set_dscp(5);
131+
ip4.set_ecn(1);
132+
ip4.set_dont_fragment();
133+
ip4.reconcile();
134+
let payload_len = ip4.len();
135+
136+
let delivery = ip4.encap::<IpIp>().unwrap();
137+
assert_eq!(5, delivery.dscp());
138+
assert_eq!(1, delivery.ecn());
139+
assert!(delivery.dont_fragment());
140+
assert_eq!(payload_len + 20, delivery.len());
141+
}
142+
143+
#[capsule::test]
144+
fn encap_0ttl_payload() {
145+
let packet = Mbuf::new().unwrap();
146+
let ethernet = packet.push::<Ethernet>().unwrap();
147+
let ip4 = ethernet.push::<Ipv4>().unwrap();
148+
let mut ip4 = ip4.push::<EchoRequest>().unwrap().deparse();
149+
ip4.set_ttl(0);
150+
ip4.reconcile();
151+
152+
assert!(ip4.encap::<IpIp>().is_err());
153+
}
154+
155+
#[capsule::test]
156+
fn decap_ipip_delivery() {
157+
let packet = Mbuf::from_bytes(&IPIP_PACKET).unwrap();
158+
let ethernet = packet.parse::<Ethernet>().unwrap();
159+
let delivery = ethernet.parse::<Ipv4>().unwrap();
160+
let payload = delivery.decap::<IpIp>().unwrap();
161+
162+
assert_eq!("1.1.1.1", payload.src().to_string());
163+
assert_eq!("2.2.2.2", payload.dst().to_string());
164+
165+
// parse the payload's payload to verify packet integrity
166+
assert!(payload.parse::<EchoRequest>().is_ok());
167+
}
168+
169+
#[capsule::test]
170+
fn decap_not_ipip() {
171+
let packet = Mbuf::new().unwrap();
172+
let ethernet = packet.push::<Ethernet>().unwrap();
173+
let ip4 = ethernet.push::<Ipv4>().unwrap();
174+
let notipip = ip4.push::<EchoRequest>().unwrap().deparse();
175+
176+
// the protocol is icmpv4 not ipv4, not an ipip tunnel
177+
assert!(notipip.decap::<IpIp>().is_err());
178+
}
179+
}

core/src/packets/ip/tunnels/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
//! Various IP in IP tunnels.
20+
21+
mod ipip;
22+
23+
pub use self::ipip::*;

core/src/packets/mod.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,30 @@ pub trait Packet {
300300
self.reconcile();
301301
self.envelope_mut().reconcile_all();
302302
}
303+
304+
/// Encapsulates the packet in a tunnel.
305+
///
306+
/// Returns the delivery packet added by the tunnel. Once encapsulated,
307+
/// the current packet and the current payload must not be modified.
308+
/// The delivery packet treats the current packet as its opaque payload.
309+
#[inline]
310+
fn encap<T: Tunnel<Payload = Self>>(self) -> Result<T::Delivery>
311+
where
312+
Self: Sized,
313+
{
314+
T::encap(self)
315+
}
316+
317+
/// Decapsulates the tunnel by removing the delivery packet.
318+
///
319+
/// Returns the payload packet encapsulated in the tunnel.
320+
#[inline]
321+
fn decap<T: Tunnel<Delivery = Self>>(self) -> Result<T::Payload>
322+
where
323+
Self: Sized,
324+
{
325+
T::decap(self)
326+
}
303327
}
304328

305329
/// A trait datalink layer protocol can implement.
@@ -325,6 +349,39 @@ pub trait Datalink: Packet {
325349
fn set_protocol_type(&mut self, ether_type: EtherType);
326350
}
327351

352+
/// A trait all tunnel protocols must implement.
353+
///
354+
/// This trait defines how the entry point should encapsulate the payload
355+
/// packet, and how the exit point should decapsulate the delivery packet.
356+
pub trait Tunnel {
357+
/// The original packet type before entering the tunnel.
358+
type Payload: Packet;
359+
360+
/// The packet type tunnel uses to deliver the datagram to the tunnel
361+
/// exit point.
362+
type Delivery: Packet;
363+
364+
/// Encapsulates the original packet and returns the new delivery packet.
365+
///
366+
/// Once encapsulated, the original packet and its payload becomes the
367+
/// opaque payload of the newly prepended delivery packet. The original
368+
/// packet cannot be parsed and manipulated until it's decapsulated. The
369+
/// encapsulator must construct the payload packet in its entirety before
370+
/// invoking this.
371+
///
372+
/// A tunnel protocol, for example GRE, may add multiple delivery packet
373+
/// headers to the datagram. The return type should be the first
374+
/// prepended packet type in the protocol stack.
375+
fn encap(payload: Self::Payload) -> Result<Self::Delivery>;
376+
377+
/// Decapsulates the delivery packet and returns the original packet.
378+
///
379+
/// Once decapsulated, the content of the delivery packet(s) are lost.
380+
/// The decapsulator is responsible for caching the information before
381+
/// invoking this.
382+
fn decap(delivery: Self::Delivery) -> Result<Self::Payload>;
383+
}
384+
328385
/// Immutable smart pointer to a struct.
329386
///
330387
/// A smart pointer that prevents the struct from being modified. The main

core/src/testils/byte_arrays.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,3 +398,40 @@ pub const ROUTER_SOLICIT_PACKET: [u8; 70] = [
398398
// source link-layer address option
399399
0x01, 0x01, 0x70, 0x3a, 0xcb, 0x1b, 0xf9, 0x7a
400400
];
401+
402+
/// IPv4 in IPv4 tunnel packet.
403+
#[rustfmt::skip]
404+
pub const IPIP_PACKET: [u8; 134] = [
405+
// ethernet header
406+
0xc2, 0x01, 0x57, 0x75, 0x00, 0x00,
407+
0xc2, 0x00, 0x57, 0x75, 0x00, 0x00,
408+
0x08, 0x00,
409+
// delivery IPv4 header
410+
0x45, 0x00,
411+
0x00, 0x78,
412+
0x00, 0x14, 0x00, 0x00,
413+
0xff, 0x04, 0xa7, 0x6b,
414+
0x0a, 0x00, 0x00, 0x01,
415+
0x0a, 0x00, 0x00, 0x02,
416+
// payload IPv4 header
417+
0x45, 0x00,
418+
0x00, 0x64,
419+
0x00, 0x14, 0x00, 0x00,
420+
0xff, 0x01, 0xb5, 0x7f,
421+
0x01, 0x01, 0x01, 0x01,
422+
0x02, 0x02, 0x02, 0x02,
423+
// payload ICMPv4 header
424+
0x08, 0x00,
425+
0x43, 0x05,
426+
// echo request payload data
427+
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428+
0x00, 0x09, 0x3b, 0x38, 0xab, 0xcd, 0xab, 0xcd,
429+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
430+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
431+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
432+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
433+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
434+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
435+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
436+
0xab, 0xcd, 0xab, 0xcd
437+
];

0 commit comments

Comments
 (0)