Skip to content

Commit ff2be53

Browse files
committed
Add tests for 6 to 4 direction.
Uncovered some subtle bugs and learnt that the pnet API does not like to give you a slice of the exact packet back. Tests uncovered that the next protocol was not set in the 6 to 4 converter!
1 parent e1c44ab commit ff2be53

File tree

3 files changed

+129
-30
lines changed

3 files changed

+129
-30
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ authors = ["Rayhaan Jaufeerally <[email protected]>"]
77
"libc" = "0.2.41"
88
"pnet_packet" = "0.21.0"
99
"ipnetwork" = "0.13.0"
10+
"pnetlink" = "0.0.3"
1011

1112
[dependencies.pnet]
1213
version = "0.21.0"

src/main.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#![allow(non_upper_case_globals)]
22
#![allow(non_camel_case_types)]
33
#![allow(non_snake_case)]
4+
#![allow(dead_code)]
45
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
56

67
extern crate libc;
78
extern crate pnet_packet;
9+
extern crate pnetlink;
810

911
use std::ffi::CString;
1012
use std::fs::{File, OpenOptions};
@@ -17,12 +19,10 @@ use std::error::Error;
1719
use std::io::Write;
1820
use pnet_packet::Packet;
1921
use pnet_packet::PacketSize;
20-
use std::thread;
2122

2223
mod translator;
2324

2425
const TUN_PATH: &'static str = "/dev/net/tun";
25-
const TUN_MTU: usize = 1500;
2626

2727
pub struct InterfaceName {
2828
name: [i8; 16],
@@ -70,7 +70,6 @@ impl Drop for OwnedFd {
7070
pub struct TunnelIface {
7171
dev: File,
7272
sock: OwnedFd,
73-
ifidx: libc::c_int, // interface index.
7473
if_name: InterfaceName,
7574
}
7675

@@ -163,7 +162,7 @@ impl TunnelIface {
163162
unsafe { TunnelIface::create_iface(name.name, dev.as_raw_fd())?; }
164163

165164
// Create a control socket.
166-
let (sock, ifidx) = match TunnelIface::create_sock(name.name) {
165+
let (sock, _) = match TunnelIface::create_sock(name.name) {
167166
Err(reason) => return Err(String::from(format!("failed to create socket: {}", reason))),
168167
Ok((sock, ifidx)) => (sock, ifidx),
169168
};
@@ -172,7 +171,6 @@ impl TunnelIface {
172171
dev: dev,
173172
if_name: name,
174173
sock: sock,
175-
ifidx: ifidx,
176174
})
177175
}
178176
}

src/translator.rs

Lines changed: 125 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,18 @@ use std::collections::{HashMap, HashSet};
55
use std::net::{Ipv4Addr, Ipv6Addr};
66
use std::str::FromStr;
77

8+
use translator::pnet_packet::ip::IpNextHeaderProtocol;
89
use translator::pnet_packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
9-
use translator::pnet_packet::ipv6::{Ipv6Packet, MutableIpv6Packet};
10-
use translator::pnet_packet::icmp::{IcmpPacket, MutableIcmpPacket, IcmpType, IcmpTypes};
11-
use translator::pnet_packet::icmpv6::{Icmpv6Packet, MutableIcmpv6Packet, Icmpv6Type, Icmpv6Types};
12-
use translator::pnet_packet::Packet;
13-
use translator::pnet_packet::icmp;
14-
use translator::pnet_packet::ip;
15-
use translator::pnet_packet::ipv4;
16-
use translator::pnet_packet::tcp;
17-
use translator::pnet_packet::udp;
10+
use translator::pnet_packet::ipv6::{MutableIpv6Packet};
11+
use translator::pnet_packet::icmp::{IcmpPacket, MutableIcmpPacket, IcmpTypes, IcmpCode, IcmpType};
12+
use translator::pnet_packet::icmpv6::{Icmpv6Packet, MutableIcmpv6Packet, Icmpv6Types, Icmpv6Type, Icmpv6Code};
13+
use translator::pnet_packet::{Packet, icmp, ip, ipv4, tcp, udp, icmpv6};
1814
use translator::pnet_packet::MutablePacket;
19-
use translator::pnet_packet::PacketSize;
2015

2116
#[derive(Debug)]
2217
pub enum Xlat6to4Error {
2318
TTLExceeded,
19+
BufferTooSmall,
2420
Other(String),
2521
}
2622

@@ -91,28 +87,22 @@ impl HMNat64 {
9187

9288
// Takes an incoming v6 packet and returns the translated v4 packet.
9389
pub fn process_v6<'p>(&mut self, pkt: &mut MutableIpv6Packet, buf: &'p mut [u8]) -> Result<Ipv4Packet<'p>, Xlat6to4Error> {
94-
println!("called process_v6");
95-
let mut response: MutableIpv4Packet<'p> = pnet_packet::ipv4::MutableIpv4Packet::new(buf).unwrap();
96-
let v4src: Ipv4Addr = self.get_v4addr_for_host(pkt.get_source()).map_err(|e| Xlat6to4Error::Other(e))?;
90+
let mut response: MutableIpv4Packet<'p> = pnet_packet::ipv4::MutableIpv4Packet::new(buf)
91+
.ok_or(Xlat6to4Error::BufferTooSmall)?;
92+
let v4src: Ipv4Addr = self.get_v4addr_for_host(pkt.get_source())
93+
.map_err(|e| Xlat6to4Error::Other(e))?;
9794
let v4dst: Ipv4Addr = self.v6_to_v4(pkt.get_destination());
98-
let total_v4_length = 20 + pkt.get_payload_length(); // FIXME: use the IHL from the input packet instead of 20.
9995

10096
response.set_version(4);
10197
response.set_header_length(5);
10298
response.set_dscp(pkt.get_traffic_class());
103-
response.set_total_length(total_v4_length);
10499
response.set_identification(0); // TODO: Check what this should be.
105-
// Ipv4 flags: [ZERO, DF, MF]
106-
if total_v4_length <= 1260 {
107-
response.set_flags(0);
108-
} else {
109-
response.set_flags(2);
110-
}
111100
response.set_fragment_offset(0);
112101

113102
// Decrement and check the TTL.
114103
let ttl = pkt.get_hop_limit() - 1;
115104
if ttl == 0 {
105+
// TODO: Should return ICMP Time Exceeded message to the sender.
116106
return Err(Xlat6to4Error::TTLExceeded);
117107
}
118108
response.set_ttl(ttl);
@@ -121,8 +111,9 @@ impl HMNat64 {
121111
ip::IpNextHeaderProtocols::Icmpv6 => {
122112
let old_payload = Icmpv6Packet::new(pkt.payload()).unwrap();
123113
let new_payload = HMNat64::icmp6_to_icmp4(&old_payload)?;
114+
response.set_total_length(20 + new_payload.packet().len() as u16);
124115
response.set_payload(&new_payload.packet());
125-
response.set_total_length(total_v4_length + new_payload.packet().len() as u16);
116+
response.set_next_level_protocol(ip::IpNextHeaderProtocols::Icmp)
126117
},
127118
ip::IpNextHeaderProtocols::Tcp => {
128119
let mut tcp_pkt = tcp::MutableTcpPacket::new(pkt.payload_mut()).unwrap();
@@ -131,7 +122,9 @@ impl HMNat64 {
131122
&v4src,
132123
&v4dst);
133124
tcp_pkt.set_checksum(sum);
125+
response.set_total_length(20 + tcp_pkt.packet().len() as u16);
134126
response.set_payload(tcp_pkt.packet());
127+
response.set_next_level_protocol(ip::IpNextHeaderProtocols::Tcp);
135128
},
136129
ip::IpNextHeaderProtocols::Udp => {
137130
let mut udp_pkt = udp::MutableUdpPacket::new(pkt.payload_mut()).unwrap();
@@ -140,20 +133,30 @@ impl HMNat64 {
140133
&v4src,
141134
&v4dst);
142135
udp_pkt.set_checksum(sum);
136+
response.set_total_length(20 + udp_pkt.packet().len() as u16);
143137
response.set_payload(udp_pkt.packet());
138+
response.set_next_level_protocol(ip::IpNextHeaderProtocols::Udp)
144139
},
145140
_ => {
146-
// If the protocol is unknown leave the inner payload alone.
141+
// If the protocol is unknown leave the inner payload alone and forward.
142+
response.set_total_length(20 + pkt.payload().len() as u16);
147143
response.set_payload(pkt.payload());
144+
response.set_next_level_protocol(pkt.get_next_header());
148145
},
149146
}
150147

148+
// Ipv4 flags: [ZERO, DF, MF]
149+
if response.get_total_length() <= 1260 {
150+
response.set_flags(0);
151+
} else {
152+
response.set_flags(2);
153+
}
154+
151155
response.set_source(v4src);
152156
response.set_destination(v4dst);
153-
println!("out: src={:?}, dst={:?}", v4src, v4dst);
157+
println!("out: src={:?}, dst={:?}, in len: {}, out len: {}", v4src, v4dst, pkt.packet().len(), response.packet().len());
154158
let sum = ipv4::checksum(&response.to_immutable());
155159
response.set_checksum(sum);
156-
157160
Ok(response.consume_to_immutable())
158161
}
159162

@@ -362,6 +365,11 @@ mod tests {
362365
n64_prefix: &'a str,
363366
}
364367

368+
struct PacketTestCase {
369+
ipv4: Box<Vec<u8>>,
370+
ipv6: Box<Vec<u8>>,
371+
}
372+
365373
#[test]
366374
fn test_convert_u8_to_u16() {
367375
assert_eq!(0, u8_to_u16(0, 0));
@@ -470,4 +478,96 @@ mod tests {
470478
assert_eq!(test.ipv4, translator.v6_to_v4(test.ipv6));
471479
}
472480
}
481+
482+
// Create an ICMPv4 packet with given type, code, payload.
483+
fn tmpl_icmp4(typ: IcmpType, code: IcmpCode, payload: &[u8]) -> Box<Vec<u8>> {
484+
// Size of ICMP v4 packet is 4 bytes + payload.
485+
let buf = vec![0u8; 4 + payload.len()];
486+
let mut icmp4Pkt = MutableIcmpPacket::owned(buf).unwrap();
487+
icmp4Pkt.set_icmp_type(typ);
488+
icmp4Pkt.set_icmp_code(code);
489+
icmp4Pkt.set_payload(payload);
490+
let checksum: u16 = icmp::checksum(&icmp4Pkt.to_immutable());
491+
icmp4Pkt.set_checksum(checksum);
492+
Box::new(icmp4Pkt.packet().to_vec())
493+
}
494+
495+
// Create an ICMPv6 packet with given type, code and payload.
496+
fn tmpl_icmpv6(ipsrc: Ipv6Addr, ipdst: Ipv6Addr, typ: Icmpv6Type, code: Icmpv6Code, payload: &[u8]) -> Box<Vec<u8>> {
497+
let buf = vec![0u8; 4 + payload.len()];
498+
let mut icmpv6Pkt = MutableIcmpv6Packet::owned(buf).unwrap();
499+
icmpv6Pkt.set_icmpv6_type(typ);
500+
icmpv6Pkt.set_icmpv6_code(code);
501+
icmpv6Pkt.set_payload(payload);
502+
let checksum: u16 = icmpv6::checksum(&icmpv6Pkt.to_immutable(), &ipsrc, &ipdst);
503+
icmpv6Pkt.set_checksum(checksum);
504+
Box::new(icmpv6Pkt.packet().to_vec())
505+
}
506+
507+
// Wrap an IP payload with an IP header.
508+
fn wrap_iphdr(src: Ipv4Addr, dst: Ipv4Addr, ttl: u8, next: IpNextHeaderProtocol, payload: &[u8]) -> Box<Vec<u8>> {
509+
let buf = vec![0u8; 20 + payload.len()];
510+
let mut ip4Pkt = MutableIpv4Packet::owned(buf).unwrap();
511+
ip4Pkt.set_version(4);
512+
ip4Pkt.set_source(src);
513+
ip4Pkt.set_destination(dst);
514+
ip4Pkt.set_header_length(5);
515+
ip4Pkt.set_total_length(20 + payload.len() as u16);
516+
ip4Pkt.set_ttl(ttl);
517+
ip4Pkt.set_next_level_protocol(next);
518+
ip4Pkt.set_payload(payload);
519+
let checksum: u16 = ipv4::checksum(&ip4Pkt.to_immutable());
520+
ip4Pkt.set_checksum(checksum);
521+
Box::new(ip4Pkt.packet().to_vec())
522+
}
523+
524+
fn wrap_ip6hdr(src: Ipv6Addr, dst: Ipv6Addr, hopLim: u8, next: IpNextHeaderProtocol, payload: &[u8]) -> Box<Vec<u8>> {
525+
let buf = vec![0u8; 40 + payload.len()];
526+
let mut ip6Pkt = MutableIpv6Packet::owned(buf).unwrap();
527+
ip6Pkt.set_source(src);
528+
ip6Pkt.set_destination(dst);
529+
ip6Pkt.set_hop_limit(hopLim);
530+
ip6Pkt.set_next_header(next);
531+
ip6Pkt.set_payload_length(payload.len() as u16);
532+
ip6Pkt.set_payload(payload);
533+
Box::new(ip6Pkt.packet().to_vec())
534+
}
535+
536+
#[test]
537+
fn test_v6_to_v4() {
538+
let clientV4 = Ipv4Addr::new(10, 0, 0, 1);
539+
let tests: Vec<PacketTestCase> = vec![
540+
PacketTestCase {
541+
ipv6: wrap_ip6hdr(
542+
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0xcafe),
543+
Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 0x808, 0x808),
544+
255,
545+
ip::IpNextHeaderProtocols::Icmpv6,
546+
&*tmpl_icmpv6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0xcafe),
547+
Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 0x808, 0x808),
548+
icmpv6::Icmpv6Types::EchoRequest,
549+
icmpv6::ndp::Icmpv6Codes::NoCode,
550+
&vec![0xca, 0xfe, 0xbe, 0xef])
551+
),
552+
ipv4: wrap_iphdr(
553+
clientV4,
554+
Ipv4Addr::new(0x8, 0x8, 0x8, 0x8),
555+
254,
556+
ip::IpNextHeaderProtocols::Icmp,
557+
&*tmpl_icmp4(icmp::IcmpTypes::EchoRequest,
558+
icmp::echo_request::IcmpCodes::NoCode,
559+
&vec![0xca, 0xfe, 0xbe, 0xef])
560+
)
561+
},
562+
];
563+
let client_subnet = "10.0.0.0/24";
564+
for mut test in tests {
565+
let mut rbuf = [0u8; 1500];
566+
let mut translator = HMNat64::new("64:ff9b::/96", client_subnet).unwrap();
567+
let response: Ipv4Packet = translator.process_v6(&mut MutableIpv6Packet::new(&mut *test.ipv6).unwrap(), &mut rbuf).unwrap();
568+
let lim: usize = response.get_total_length() as usize;
569+
let pkt = &response.packet()[..lim];
570+
assert_eq!(*test.ipv4, pkt);
571+
}
572+
}
473573
}

0 commit comments

Comments
 (0)