Add support for TCP option parsing and emission.

This commit is contained in:
whitequark 2017-01-27 00:17:13 +00:00
parent 4267ad2635
commit 4c3f454902
2 changed files with 165 additions and 7 deletions

View File

@ -148,5 +148,6 @@ pub use self::udp::Repr as UdpRepr;
pub use self::tcp::SeqNumber as TcpSeqNumber;
pub use self::tcp::Packet as TcpPacket;
pub use self::tcp::TcpOption;
pub use self::tcp::Repr as TcpRepr;
pub use self::tcp::Control as TcpControl;

View File

@ -89,6 +89,11 @@ mod field {
pub const FLG_ECE: u16 = 0x040;
pub const FLG_CWR: u16 = 0x080;
pub const FLG_NS: u16 = 0x100;
pub const OPT_END: u8 = 0x00;
pub const OPT_NOP: u8 = 0x01;
pub const OPT_MSS: u8 = 0x02;
pub const OPT_WS: u8 = 0x03;
}
impl<T: AsRef<[u8]>> Packet<T> {
@ -263,6 +268,14 @@ impl<T: AsRef<[u8]>> Packet<T> {
}
impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
/// Return a pointer to the options.
#[inline]
pub fn options(&self) -> &'a [u8] {
let header_len = self.header_len() as usize;
let data = self.buffer.as_ref();
&data[field::URGENT.end..header_len]
}
/// Return a pointer to the payload.
#[inline]
pub fn payload(&self) -> &'a [u8] {
@ -441,6 +454,14 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
}
impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
/// Return a pointer to the options.
#[inline]
pub fn options_mut(&mut self) -> &mut [u8] {
let header_len = self.header_len() as usize;
let data = self.buffer.as_mut();
&mut data[field::URGENT.end..header_len]
}
/// Return a mutable pointer to the payload data.
#[inline]
pub fn payload_mut(&mut self) -> &mut [u8] {
@ -450,6 +471,99 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
}
}
/// A representation of a single TCP option.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TcpOption<'a> {
EndOfList,
NoOperation,
MaxSegmentSize(u16),
WindowScale(u8),
Unknown { kind: u8, data: &'a [u8] }
}
impl<'a> TcpOption<'a> {
pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], TcpOption<'a>), Error> {
let (length, option);
match *buffer.get(0).ok_or(Error::Truncated)? {
field::OPT_END => {
length = 1;
option = TcpOption::EndOfList;
}
field::OPT_NOP => {
length = 1;
option = TcpOption::NoOperation;
}
kind => {
length = *buffer.get(1).ok_or(Error::Truncated)? as usize;
if buffer.len() < length { return Err(Error::Truncated) }
let data = &buffer[2..length];
match (kind, length) {
(field::OPT_END, _) |
(field::OPT_NOP, _) =>
unreachable!(),
(field::OPT_MSS, 4) =>
option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data)),
(field::OPT_MSS, _) =>
return Err(Error::Malformed),
(field::OPT_WS, 3) =>
option = TcpOption::WindowScale(data[0]),
(field::OPT_WS, _) =>
return Err(Error::Malformed),
(_, _) =>
option = TcpOption::Unknown { kind: kind, data: data }
}
}
}
Ok((&buffer[length..], option))
}
pub fn buffer_len(&self) -> usize {
match self {
&TcpOption::EndOfList => 1,
&TcpOption::NoOperation => 1,
&TcpOption::MaxSegmentSize(_) => 4,
&TcpOption::WindowScale(_) => 3,
&TcpOption::Unknown { data, .. } => 2 + data.len()
}
}
pub fn emit<'b>(&self, buffer: &'b mut [u8]) -> &'b mut [u8] {
let length;
match self {
&TcpOption::EndOfList => {
length = 1;
buffer[0] = field::OPT_END;
}
&TcpOption::NoOperation => {
length = 1;
buffer[0] = field::OPT_NOP;
}
_ => {
length = self.buffer_len();
buffer[1] = length as u8;
match self {
&TcpOption::EndOfList |
&TcpOption::NoOperation =>
unreachable!(),
&TcpOption::MaxSegmentSize(value) => {
buffer[0] = field::OPT_MSS;
NetworkEndian::write_u16(&mut buffer[2..], value)
}
&TcpOption::WindowScale(value) => {
buffer[0] = field::OPT_WS;
buffer[2] = value;
}
&TcpOption::Unknown { kind, data: provided } => {
buffer[0] = kind;
buffer[2..].copy_from_slice(provided)
}
}
}
}
&mut buffer[length..]
}
}
/// The control flags of a Transmission Control Protocol packet.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Control {
@ -606,14 +720,18 @@ mod test {
const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
static PACKET_BYTES: [u8; 24] =
static PACKET_BYTES: [u8; 28] =
[0xbf, 0x00, 0x00, 0x50,
0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0xcd, 0xef,
0x50, 0x35, 0x01, 0x23,
0x20, 0xbe, 0x02, 0x01,
0x60, 0x35, 0x01, 0x23,
0x01, 0xb6, 0x02, 0x01,
0x03, 0x03, 0x0c, 0x01,
0xaa, 0x00, 0x00, 0xff];
static OPTION_BYTES: [u8; 4] =
[0x03, 0x03, 0x0c, 0x01];
static PAYLOAD_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
@ -624,7 +742,7 @@ mod test {
assert_eq!(packet.dst_port(), 80);
assert_eq!(packet.seq_number(), SeqNumber(0x01234567));
assert_eq!(packet.ack_number(), SeqNumber(0x89abcdefu32 as i32));
assert_eq!(packet.header_len(), 20);
assert_eq!(packet.header_len(), 24);
assert_eq!(packet.fin(), true);
assert_eq!(packet.syn(), false);
assert_eq!(packet.rst(), true);
@ -633,20 +751,21 @@ mod test {
assert_eq!(packet.urg(), true);
assert_eq!(packet.window_len(), 0x0123);
assert_eq!(packet.urgent_at(), 0x0201);
assert_eq!(packet.checksum(), 0x20be);
assert_eq!(packet.checksum(), 0x01b6);
assert_eq!(packet.options(), &OPTION_BYTES[..]);
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
}
#[test]
fn test_construct() {
let mut bytes = vec![0; 24];
let mut bytes = vec![0; PACKET_BYTES.len()];
let mut packet = Packet::new(&mut bytes).unwrap();
packet.set_src_port(48896);
packet.set_dst_port(80);
packet.set_seq_number(SeqNumber(0x01234567));
packet.set_ack_number(SeqNumber(0x89abcdefu32 as i32));
packet.set_header_len(20);
packet.set_header_len(24);
packet.set_fin(true);
packet.set_syn(false);
packet.set_rst(true);
@ -656,6 +775,7 @@ mod test {
packet.set_window_len(0x0123);
packet.set_urgent_at(0x0201);
packet.set_checksum(0xEEEE);
packet.options_mut().copy_from_slice(&OPTION_BYTES[..]);
packet.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]);
packet.fill_checksum(&SRC_ADDR.into(), &DST_ADDR.into());
assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
@ -696,4 +816,41 @@ mod test {
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into());
assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]);
}
macro_rules! assert_option_parses {
($opt:expr, $data:expr) => ({
assert_eq!(TcpOption::parse($data), Ok((&[][..], $opt)));
let buffer = &mut [0; 20][..$opt.buffer_len()];
assert_eq!($opt.emit(buffer), &mut []);
assert_eq!(&*buffer, $data);
})
}
#[test]
fn test_tcp_options() {
assert_option_parses!(TcpOption::EndOfList,
&[0x00]);
assert_option_parses!(TcpOption::NoOperation,
&[0x01]);
assert_option_parses!(TcpOption::MaxSegmentSize(1500),
&[0x02, 0x04, 0x05, 0xdc]);
assert_option_parses!(TcpOption::WindowScale(12),
&[0x03, 0x03, 0x0c]);
assert_option_parses!(TcpOption::Unknown { kind: 12, data: &[1, 2, 3][..] },
&[0x0c, 0x05, 0x01, 0x02, 0x03])
}
#[test]
fn test_malformed_tcp_options() {
assert_eq!(TcpOption::parse(&[]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0x2, 0x02]),
Err(Error::Malformed));
assert_eq!(TcpOption::parse(&[0x3, 0x02]),
Err(Error::Malformed));
}
}