Skip to content

Commit 0626f22

Browse files
add ip6in4 tunnel
1 parent 6fea7ce commit 0626f22

File tree

5 files changed

+222
-18
lines changed

5 files changed

+222
-18
lines changed

core/src/packets/ip/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ pub mod ProtocolNumbers {
6565
/// IPv4 encapsulation.
6666
pub const Ipv4: ProtocolNumber = ProtocolNumber(0x04);
6767

68+
/// IPv6 encapsulation.
69+
pub const Ipv6: ProtocolNumber = ProtocolNumber(0x29);
70+
6871
/// Routing Header for IPv6.
6972
pub const Ipv6Route: ProtocolNumber = ProtocolNumber(0x2B);
7073

@@ -87,6 +90,7 @@ impl fmt::Display for ProtocolNumber {
8790
ProtocolNumbers::Tcp => "TCP".to_string(),
8891
ProtocolNumbers::Udp => "UDP".to_string(),
8992
ProtocolNumbers::Ipv4 => "IPv4".to_string(),
93+
ProtocolNumbers::Ipv6 => "IPv6".to_string(),
9094
ProtocolNumbers::Ipv6Route => "IPv6 Route".to_string(),
9195
ProtocolNumbers::Ipv6Frag => "IPv6 Frag".to_string(),
9296
ProtocolNumbers::Icmpv4 => "ICMPv4".to_string(),
@@ -278,6 +282,7 @@ mod tests {
278282
assert_eq!("TCP", ProtocolNumbers::Tcp.to_string());
279283
assert_eq!("UDP", ProtocolNumbers::Udp.to_string());
280284
assert_eq!("IPv4", ProtocolNumbers::Ipv4.to_string());
285+
assert_eq!("IPv6", ProtocolNumbers::Ipv6.to_string());
281286
assert_eq!("IPv6 Route", ProtocolNumbers::Ipv6Route.to_string());
282287
assert_eq!("IPv6 Frag", ProtocolNumbers::Ipv6Frag.to_string());
283288
assert_eq!("ICMPv4", ProtocolNumbers::Icmpv4.to_string());
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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+
//! IPv6 in IPv4 tunnel.
20+
21+
use crate::ensure;
22+
use crate::packets::ethernet::{EtherTypes, Ethernet};
23+
use crate::packets::ip::v4::Ipv4;
24+
use crate::packets::ip::v6::Ipv6;
25+
use crate::packets::ip::ProtocolNumbers;
26+
use crate::packets::{Datalink, Packet, Tunnel};
27+
use anyhow::{anyhow, Result};
28+
use std::marker::PhantomData;
29+
30+
/// [IPv6] encapsulation within [IPv4] based on [IETF RFC 4213].
31+
///
32+
/// An IPv4 compatibility mechanisms designed to be employed by IPv6 hosts
33+
/// and routers that need to interoperate with IPv4 hosts and utilize IPv4
34+
/// routing infrastructures.
35+
///
36+
/// +-------------+
37+
/// | IPv4 |
38+
/// | Header |
39+
/// +-------------+ +-------------+
40+
/// | IPv6 | | IPv6 |
41+
/// | Header | | Header |
42+
/// +-------------+ +-------------+
43+
/// | Transport | | Transport |
44+
/// | Layer | ===> | Layer |
45+
/// | Header | | Header |
46+
/// +-------------+ +-------------+
47+
/// | | | |
48+
/// ~ Data ~ ~ Data ~
49+
/// | | | |
50+
/// +-------------+ +-------------+
51+
///
52+
/// [IPv6]: Ipv6
53+
/// [IPv4]: Ipv4
54+
/// [IETF RFC 4213]: https://datatracker.ietf.org/doc/html/rfc4213
55+
#[derive(Debug)]
56+
pub struct Ip6in4<E: Datalink = Ethernet> {
57+
_phantom: PhantomData<E>,
58+
}
59+
60+
impl<E: Datalink> Tunnel for Ip6in4<E> {
61+
type Payload = Ipv6<E>;
62+
type Delivery = Ipv4<E>;
63+
64+
/// Encapsulates the existing IPv6 packet by prepending an outer IPv4
65+
/// packet.
66+
///
67+
/// The DSCP and ECN options are copied from existing header to the new
68+
/// outer header.
69+
///
70+
/// # Errors
71+
///
72+
/// Returns an error if the payload's hop limit is `0`. The packet should
73+
/// be discarded.
74+
fn encap(payload: Self::Payload) -> Result<Self::Delivery> {
75+
ensure!(
76+
payload.hop_limit() != 0,
77+
anyhow!("payload's hop limit is 0.")
78+
);
79+
80+
let dscp = payload.dscp();
81+
let ecn = payload.ecn();
82+
83+
let envelope = payload.deparse();
84+
let mut delivery = envelope.push::<Self::Delivery>()?;
85+
delivery.set_dscp(dscp);
86+
delivery.set_ecn(ecn);
87+
delivery.set_protocol(ProtocolNumbers::Ipv6);
88+
delivery.reconcile_all();
89+
90+
Ok(delivery)
91+
}
92+
93+
/// Decapsulates the outer IPv4 packet and returns the original payload
94+
/// IPv6 packet.
95+
///
96+
/// # Errors
97+
///
98+
/// Returns an error if the protocol is not set to `ProtocolNumbers::Ipv6`,
99+
/// indicating the packet is not from an Ip6in4 tunnel.
100+
fn decap(delivery: Self::Delivery) -> Result<Self::Payload> {
101+
ensure!(
102+
delivery.protocol() == ProtocolNumbers::Ipv6,
103+
anyhow!("not an Ip6in4 tunnel.")
104+
);
105+
106+
let mut envelope = delivery.remove()?;
107+
envelope.set_protocol_type(EtherTypes::Ipv6);
108+
envelope.parse::<Self::Payload>()
109+
}
110+
}
111+
112+
#[cfg(test)]
113+
mod tests {
114+
use super::*;
115+
use crate::packets::ethernet::Ethernet;
116+
use crate::packets::icmp::v6::EchoRequest;
117+
use crate::packets::Mbuf;
118+
use crate::testils::byte_arrays::{IP6IN4_PACKET, IPIP_PACKET};
119+
120+
#[capsule::test]
121+
fn encap_ip6in4_payload() {
122+
let packet = Mbuf::new().unwrap();
123+
let ethernet = packet.push::<Ethernet>().unwrap();
124+
let ip6 = ethernet.push::<Ipv6>().unwrap();
125+
let mut ip6 = ip6.push::<EchoRequest>().unwrap().deparse();
126+
ip6.set_dscp(5);
127+
ip6.set_ecn(1);
128+
ip6.reconcile();
129+
let payload_len = ip6.len();
130+
131+
let delivery = ip6.encap::<Ip6in4>().unwrap();
132+
assert_eq!(5, delivery.dscp());
133+
assert_eq!(1, delivery.ecn());
134+
assert_eq!(payload_len + 20, delivery.len());
135+
}
136+
137+
#[capsule::test]
138+
fn encap_0_hop_limit_payload() {
139+
let packet = Mbuf::new().unwrap();
140+
let ethernet = packet.push::<Ethernet>().unwrap();
141+
let ip6 = ethernet.push::<Ipv6>().unwrap();
142+
let mut ip6 = ip6.push::<EchoRequest>().unwrap().deparse();
143+
ip6.set_hop_limit(0);
144+
ip6.reconcile();
145+
146+
assert!(ip6.encap::<Ip6in4>().is_err());
147+
}
148+
149+
#[capsule::test]
150+
fn decap_ip6in4_delivery() {
151+
let packet = Mbuf::from_bytes(&IP6IN4_PACKET).unwrap();
152+
let ethernet = packet.parse::<Ethernet>().unwrap();
153+
let delivery = ethernet.parse::<Ipv4>().unwrap();
154+
let payload = delivery.decap::<Ip6in4>().unwrap();
155+
156+
assert_eq!("2001:db8:0:1::1", payload.src().to_string());
157+
assert_eq!("2001:db8:0:1::2", payload.dst().to_string());
158+
159+
// parse the payload's payload to verify packet integrity
160+
assert!(payload.parse::<EchoRequest>().is_ok());
161+
}
162+
163+
#[capsule::test]
164+
fn decap_not_ip6in4() {
165+
let packet = Mbuf::from_bytes(&IPIP_PACKET).unwrap();
166+
let ethernet = packet.parse::<Ethernet>().unwrap();
167+
let not6in4 = ethernet.parse::<Ipv4>().unwrap();
168+
169+
// not an ip6in4 tunnel
170+
assert!(not6in4.decap::<Ip6in4>().is_err());
171+
}
172+
}

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ impl<E: Datalink> Tunnel for IpIp<E> {
105105
fn decap(delivery: Self::Delivery) -> Result<Self::Payload> {
106106
ensure!(
107107
delivery.protocol() == ProtocolNumbers::Ipv4,
108-
anyhow!("not an IPIP tunnel.")
108+
anyhow!("not an IpIp tunnel.")
109109
);
110110

111111
let envelope = delivery.remove()?;
@@ -119,7 +119,7 @@ mod tests {
119119
use crate::packets::ethernet::Ethernet;
120120
use crate::packets::icmp::v4::EchoRequest;
121121
use crate::packets::Mbuf;
122-
use crate::testils::byte_arrays::IPIP_PACKET;
122+
use crate::testils::byte_arrays::{IP6IN4_PACKET, IPIP_PACKET};
123123

124124
#[capsule::test]
125125
fn encap_ipip_payload() {
@@ -141,7 +141,7 @@ mod tests {
141141
}
142142

143143
#[capsule::test]
144-
fn encap_0ttl_payload() {
144+
fn encap_0_ttl_payload() {
145145
let packet = Mbuf::new().unwrap();
146146
let ethernet = packet.push::<Ethernet>().unwrap();
147147
let ip4 = ethernet.push::<Ipv4>().unwrap();
@@ -168,12 +168,11 @@ mod tests {
168168

169169
#[capsule::test]
170170
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();
171+
let packet = Mbuf::from_bytes(&IP6IN4_PACKET).unwrap();
172+
let ethernet = packet.parse::<Ethernet>().unwrap();
173+
let notipip = ethernet.parse::<Ipv4>().unwrap();
175174

176-
// the protocol is icmpv4 not ipv4, not an ipip tunnel
175+
// not an ipip tunnel
177176
assert!(notipip.decap::<IpIp>().is_err());
178177
}
179178
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
//! Various IP in IP tunnels.
2020
21+
mod ip6in4;
2122
mod ipip;
2223

24+
pub use self::ip6in4::*;
2325
pub use self::ipip::*;

core/src/testils/byte_arrays.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -424,14 +424,40 @@ pub const IPIP_PACKET: [u8; 134] = [
424424
0x08, 0x00,
425425
0x43, 0x05,
426426
// 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
427+
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x3b, 0x38, 0xab, 0xcd, 0xab, 0xcd,
428+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
429+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
430+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
431+
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd
432+
];
433+
434+
/// IPv6 in IPv4 tunnel packet.
435+
#[rustfmt::skip]
436+
pub const IP6IN4_PACKET: [u8; 134] = [
437+
// ethernet header
438+
0xc2, 0x01, 0x42, 0x02, 0x00, 0x00,
439+
0xc2, 0x00, 0x42, 0x02, 0x00, 0x00,
440+
0x08, 0x00,
441+
// delivery IPv4 header
442+
0x45, 0x00,
443+
0x00, 0x78,
444+
0x00, 0x09, 0x00, 0x00,
445+
0xff, 0x29, 0xa7, 0x51,
446+
0x0a, 0x00, 0x00, 0x01,
447+
0x0a, 0x00, 0x00, 0x02,
448+
// payload IPv6 header
449+
0x60, 0x00, 0x00, 0x00,
450+
0x00, 0x3c,
451+
0x3a,
452+
0x40,
453+
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
454+
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
455+
// payload ICMPv6 header
456+
0x80, 0x00,
457+
0x7e, 0x8f,
458+
// echo request payload data
459+
0x18, 0xdc, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
460+
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
461+
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
462+
0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
437463
];

0 commit comments

Comments
 (0)