vpncloud/src/payload.rs

160 lines
6.0 KiB
Rust
Raw Normal View History

2020-09-24 17:48:13 +00:00
// VpnCloud - Peer-to-Peer VPN
2021-02-08 09:11:20 +00:00
// Copyright (C) 2015-2021 Dennis Schwerdel
2020-09-24 17:48:13 +00:00
// This software is licensed under GPL-3 or newer (see LICENSE.md)
use crate::{error::Error, types::Address};
2020-10-21 21:09:49 +00:00
use std::io::{Cursor, Read};
2020-09-24 17:48:13 +00:00
pub trait Protocol: Sized {
fn parse(_: &[u8]) -> Result<(Address, Address), Error>;
}
/// An ethernet frame dissector
///
/// This dissector is able to extract the source and destination addresses of ethernet frames.
///
/// If the ethernet frame contains a VLAN tag, both addresses will be prefixed with that tag,
/// resulting in 8-byte addresses. Additional nested tags will be ignored.
pub struct Frame;
impl Protocol for Frame {
/// Parses an ethernet frame and extracts the source and destination addresses
///
/// # Errors
/// This method will fail when the given data is not a valid ethernet frame.
fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
2021-01-28 22:19:20 +00:00
// HOT PATH
2020-10-21 21:09:49 +00:00
let mut cursor = Cursor::new(data);
let mut src = [0; 16];
let mut dst = [0; 16];
let mut proto = [0; 2];
cursor
.read_exact(&mut dst[..6])
.and_then(|_| cursor.read_exact(&mut src[..6]))
.and_then(|_| cursor.read_exact(&mut proto))
.map_err(|_| Error::Parse("Frame is too short"))?;
if proto == [0x81, 0x00] {
src.copy_within(..6, 2);
dst.copy_within(..6, 2);
cursor.read_exact(&mut src[..2]).map_err(|_| Error::Parse("Vlan frame is too short"))?;
2020-10-21 21:42:03 +00:00
src[0] &= 0x0f; // restrict vlan id to 12 bits
2020-10-21 21:09:49 +00:00
dst[..2].copy_from_slice(&src[..2]);
2020-10-21 21:42:03 +00:00
if src[0..1] == [0, 0] {
// treat vlan id 0x000 as untagged
src.copy_within(2..8, 0);
dst.copy_within(2..8, 0);
2021-04-06 10:28:31 +00:00
return Ok((Address { data: src, len: 6 }, Address { data: dst, len: 6 }));
2020-10-21 21:42:03 +00:00
}
2020-09-24 17:48:13 +00:00
Ok((Address { data: src, len: 8 }, Address { data: dst, len: 8 }))
} else {
2020-10-21 21:09:49 +00:00
Ok((Address { data: src, len: 6 }, Address { data: dst, len: 6 }))
2020-09-24 17:48:13 +00:00
}
}
}
#[test]
fn decode_frame_without_vlan() {
let data = [6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8];
let (src, dst) = Frame::parse(&data).unwrap();
assert_eq!(src, Address { data: [1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 6 });
assert_eq!(dst, Address { data: [6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 6 });
}
#[test]
fn decode_frame_with_vlan() {
let data = [6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 0x81, 0, 4, 210, 1, 2, 3, 4, 5, 6, 7, 8];
let (src, dst) = Frame::parse(&data).unwrap();
assert_eq!(src, Address { data: [4, 210, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0], len: 8 });
assert_eq!(dst, Address { data: [4, 210, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0], len: 8 });
}
#[test]
fn decode_invalid_frame() {
assert!(Frame::parse(&[6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8]).is_ok());
// truncated frame
assert!(Frame::parse(&[]).is_err());
// truncated vlan frame
assert!(Frame::parse(&[6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 0x81, 0x00]).is_err());
}
/// An IP packet dissector
///
/// This dissector is able to extract the source and destination ip addresses of ipv4 packets and
/// ipv6 packets.
#[allow(dead_code)]
pub struct Packet;
impl Protocol for Packet {
/// Parses an ip packet and extracts the source and destination addresses
///
/// # Errors
/// This method will fail when the given data is not a valid ipv4 and ipv6 packet.
fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
2021-01-28 22:19:20 +00:00
// HOT PATH
2020-09-24 17:48:13 +00:00
if data.is_empty() {
2021-04-06 10:28:31 +00:00
return Err(Error::Parse("Empty header"));
2020-09-24 17:48:13 +00:00
}
let version = data[0] >> 4;
match version {
4 => {
if data.len() < 20 {
2021-04-06 10:28:31 +00:00
return Err(Error::Parse("Truncated IPv4 header"));
2020-09-24 17:48:13 +00:00
}
let src = Address::read_from_fixed(&data[12..], 4)?;
let dst = Address::read_from_fixed(&data[16..], 4)?;
Ok((src, dst))
}
6 => {
if data.len() < 40 {
2021-04-06 10:28:31 +00:00
return Err(Error::Parse("Truncated IPv6 header"));
2020-09-24 17:48:13 +00:00
}
let src = Address::read_from_fixed(&data[8..], 16)?;
let dst = Address::read_from_fixed(&data[24..], 16)?;
Ok((src, dst))
}
2021-04-06 10:28:31 +00:00
_ => Err(Error::Parse("Invalid IP protocol version")),
2020-09-24 17:48:13 +00:00
}
}
}
#[test]
fn decode_ipv4_packet() {
let data = [0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 1, 1, 192, 168, 1, 2];
let (src, dst) = Packet::parse(&data).unwrap();
assert_eq!(src, Address { data: [192, 168, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 4 });
assert_eq!(dst, Address { data: [192, 168, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 4 });
}
#[test]
fn decode_ipv6_packet() {
let data = [
0x60, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 6, 5,
2021-04-06 10:28:31 +00:00
4, 3, 2, 1,
2020-09-24 17:48:13 +00:00
];
let (src, dst) = Packet::parse(&data).unwrap();
assert_eq!(src, Address { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6], len: 16 });
assert_eq!(dst, Address { data: [0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1], len: 16 });
}
#[test]
fn decode_invalid_packet() {
assert!(Packet::parse(&[0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 1, 1, 192, 168, 1, 2]).is_ok());
assert!(Packet::parse(&[
0x60, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 6, 5,
4, 3, 2, 1
])
.is_ok());
// no data
assert!(Packet::parse(&[]).is_err());
// wrong version
assert!(Packet::parse(&[0x20]).is_err());
// truncated ipv4
assert!(Packet::parse(&[0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 1, 1, 192, 168, 1]).is_err());
// truncated ipv6
assert!(Packet::parse(&[
0x60, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 6, 5,
4, 3, 2
])
.is_err());
2021-04-06 10:28:31 +00:00
}