Skip to content

Commit

Permalink
initialize ipv4 packet decode tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul-weqe committed Jun 21, 2024
1 parent 9c52bf9 commit 6b1bbef
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 61 deletions.
13 changes: 13 additions & 0 deletions holo-vrrp/src/consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Copyright (c) The Holo Core Contributors
//
// SPDX-License-Identifier: MIT
//


pub const VRRP_MIN_PKT_LENGTH: usize = 16; // in bytes
pub const VRRP_MAX_PKT_LENGTH: usize = 80; // in bytes
pub const VRRP_MAX_IP_COUNT: usize = 16; // max number of IPs that can be supported

pub const IP_HDR_MIN_LENGTH: usize = 20;
pub const IP_HDR_MAX_LENGTH: usize = 24;
1 change: 1 addition & 0 deletions holo-vrrp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ pub mod northbound;
pub mod packet;
pub mod southbound;
pub mod tasks;
pub mod consts;
148 changes: 90 additions & 58 deletions holo-vrrp/src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//
// SPDX-License-Identifier: MIT
//

use crate::consts::*;
use std::net::{IpAddr, Ipv4Addr};

//use bitflags::bitflags;
Expand Down Expand Up @@ -40,22 +40,60 @@ pub type DecodeResult<T> = Result<T, DecodeError>;
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Deserialize, Serialize)]
pub struct VRRPPacket {
// version + type [4 bits each]
ver_type: u8,
vrid: u8,
priority: u8,
count_ip: u8,
auth_type: u8,
adver_int: u8,
checksum: u16,
ip_addresses: Vec<Ipv4Addr>,
pub version: u8,
pub hdr_type: u8,
pub vrid: u8,
pub priority: u8,
pub count_ip: u8,
pub auth_type: u8,
pub adver_int: u8,
pub checksum: u16,
pub ip_addresses: Vec<Ipv4Addr>,

// the following two are only used for backward compatibility.
auth_data: u32,
auth_data2: u32
pub auth_data: u32,
pub auth_data2: u32
}


// IP packet header
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |Version| IHL |Type of Service| Total Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Identification |Flags| Fragment Offset |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Time to Live | Protocol | Header Checksum |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Source Address |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Destination Address |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Options | Padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Deserialize, Serialize)]
pub struct IPv4Paket {
pub version: u8,
pub ihl: u8,
pub tos: u8,
pub total_length: u16,
pub identification: u16,
pub flags: u8,
pub offset: u16,
pub ttl: u8,
pub protocol: u8,
pub checksum: u16,
pub src_address: Ipv4Addr,
pub dst_address: Ipv4Addr,
pub options: Option<u32>,
pub padding: Option<u8>
}

// VRRP decode errors.

#[derive(Debug, Eq, PartialEq)]
#[derive(Deserialize, Serialize)]
pub enum DecodeError {
Expand Down Expand Up @@ -87,48 +125,14 @@ pub enum PacketLengthError {
}


// IP packet header
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |Version| IHL |Type of Service| Total Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Identification |Flags| Fragment Offset |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Time to Live | Protocol | Header Checksum |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Source Address |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Destination Address |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Options | Padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
struct IPv4Paket {
version: u8,
ihl: u8,
tos: u8,
total_length: u16,
identification: u16,
flags: u8,
offset: u16,
ttl: u8,
protocol: u8,
checksum: u16,
src_address: Ipv4Addr,
dst_address: Ipv4Addr,
options: Option<u32>,
padding: Option<u8>
}

// ===== impl Packet =====

impl VRRPPacket {
// Encodes VRRP packet into a bytes buffer.
pub fn encode(&self) -> BytesMut {
let mut buf = BytesMut::with_capacity(114);
buf.put_u8(self.ver_type);
let ver_type = (self.version << 4) | self.hdr_type;
buf.put_u8(self.vrid);
buf.put_u8(self.priority);
buf.put_u8(self.count_ip);
Expand All @@ -144,27 +148,27 @@ impl VRRPPacket {
}

// Decodes VRRP packet from a bytes buffer.
pub fn decode(data: &[u8]) -> Result<Self, DecodeError> {
pub fn decode(data: &[u8]) -> DecodeResult<Self> {

// 1. pkt length verification
let pkt_size = data.len();
let count_ip = data[3];

// with the minimum number of valid IP addresses being 0,
// The minimum number of bytes for the VRRP packet is 16
if pkt_size < 16 {
if pkt_size < VRRP_MIN_PKT_LENGTH {
return Err(DecodeError::PacketLengthError(PacketLengthError::TooShort(pkt_size)))
}

// with the max number of valid IP addresses being 16,
// The maximum number of bytes the VRRP packet can be is 80
if pkt_size > 80 {
if pkt_size > VRRP_MAX_PKT_LENGTH {
return Err(DecodeError::PacketLengthError(PacketLengthError::TooLong(pkt_size)))
}

// max number of IP addresses allowed.
// This will be based on the count_ip field
if count_ip > 16 {
if count_ip as usize > VRRP_MAX_IP_COUNT {
return Err(
DecodeError::PacketLengthError(PacketLengthError::AddressCount(count_ip as usize))
)
Expand All @@ -184,6 +188,8 @@ impl VRRPPacket {

let mut buf: Bytes = Bytes::copy_from_slice(data);
let ver_type = buf.get_u8();
let version = ver_type >> 4;
let hdr_type = ver_type & 0x0F;
let vrid = buf.get_u8();
let priority = buf.get_u8();
let count_ip = buf.get_u8();
Expand All @@ -200,7 +206,8 @@ impl VRRPPacket {


Ok(Self {
ver_type,
version,
hdr_type,
vrid,
priority,
count_ip,
Expand Down Expand Up @@ -244,13 +251,37 @@ impl IPv4Paket {
buf
}

pub fn decode(data: &[u8]) -> Self {
pub fn decode(data: &[u8]) -> DecodeResult<Self> {
let mut buf = Bytes::copy_from_slice(data);


// ver_ihl -> version[4 bits] + ihl[4 bits]
let ver_ihl = buf.get_u8();
let version = ver_ihl >> 4;
let ihl = ver_ihl &0x0F;
let ihl = ver_ihl & 0x0F;

// verify if header length matches packet information
// A Malory may have declared a wrong number of ips
// in count_ip than they actually have in the body. This may
// lead to trying to read data that is either out of bounds or
// fully not reading data sent.
if ihl as usize != data.len() / 4 {
return Err(DecodeError::PacketLengthError(
PacketLengthError::CorruptedLength
));
}

if ihl < (IP_HDR_MIN_LENGTH as u8 / 4) {
return Err(DecodeError::PacketLengthError(
PacketLengthError::TooShort(ihl as usize * 4)
))
}

if ihl > (IP_HDR_MAX_LENGTH as u8 / 4) {
return Err(DecodeError::PacketLengthError(
PacketLengthError::TooLong(ihl as usize * 4)
));
}

let tos = buf.get_u8();
let total_length = buf.get_u16();
Expand All @@ -269,12 +300,13 @@ impl IPv4Paket {

let mut options: Option<u32> = None;
let mut padding: Option<u8> = None;
if ihl > 20 {

if ihl > IP_HDR_MIN_LENGTH as u8 {
let opt_pad = buf.get_u32();
options = Some(opt_pad >> 8);
padding = Some((opt_pad & 0xFF) as u8);
}
Self {
Ok(Self {
version,
ihl,
tos,
Expand All @@ -289,7 +321,7 @@ impl IPv4Paket {
dst_address,
options,
padding
}
})
}
}

Expand Down
78 changes: 75 additions & 3 deletions holo-vrrp/tests/packet/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@
// SPDX-License-Identifier: MIT
//

use std::net::Ipv4Addr;

// the ipv4 Packet header details that will be used in IPV4 tests
use holo_vrrp::packet::IPv4Paket;
use holo_vrrp::packet::{PacketLengthError, DecodeError};

// the ipv4 Packet header details that will be used in IPV4 tests.
// It may have slight modifications based on the specific test
// but that will be specified beforehand.
//
// - version: 4
// - header length: 20
// - header length: 5
// - tos: 0
// - total length: 52
// - identification: 0x6cb8
Expand All @@ -23,8 +30,73 @@ fn valid_pkt_data() -> [u8; 20] {
0x6c, 0xb8, 0x40, 0x00,
0x33, 0x06, 0xfe, 0x74,
0xd0, 0x73, 0xe7, 0x6a,
0xc0, 0xa8, 0x61, 0x10
0xc0, 0xa8, 0x64, 0x10
]
}


#[test]
fn encode_valid_pkt() {
let pkt_wrapped = IPv4Paket::decode(&valid_pkt_data());
assert!(pkt_wrapped.is_ok());

let pkt = pkt_wrapped.unwrap();
let expected = IPv4Paket {
version: 4,
ihl: 5,
tos: 0,
total_length: 52,
identification: 0x6cb8,
flags: 0b0100,
offset: 0b0000000000000000,
ttl: 51,
protocol: 6,
checksum: 0xfe74,
src_address: Ipv4Addr::new(208, 115, 231, 106),
dst_address: Ipv4Addr::new(192, 168, 100, 16),
options: None,
padding: None
};
assert_eq!(expected, pkt);
}


#[test]
fn test_hdr_length_corruption() {
let data = &mut valid_pkt_data();

// change length from 4 to 5
data[0] = 0x44;

let pkt = IPv4Paket::decode(data);
assert_eq!(
pkt,
Err(DecodeError::PacketLengthError(PacketLengthError::CorruptedLength))
);
}

#[test]
fn test_header_too_short() {
let data = [
0x43, 0x00, 0x00, 0x34,
0x6c, 0xb8, 0x40, 0x00,
0x33, 0x06, 0xfe, 0x74,
];
let pkt = IPv4Paket::decode(&data);
assert_eq!(
pkt,
Err(DecodeError::PacketLengthError(PacketLengthError::TooShort(12)))
);
}

#[test]
fn test_header_too_long() {

let data = &mut [0x00; 28];
data[0] = 0x47;
let pkt = IPv4Paket::decode(data);
assert_eq!(
pkt,
Err(DecodeError::PacketLengthError(PacketLengthError::TooLong(28)))
);
}

0 comments on commit 6b1bbef

Please sign in to comment.