diff --git a/switch/packet/Cargo.toml b/switch/packet/Cargo.toml index a3c9c7d..e1a6166 100644 --- a/switch/packet/Cargo.toml +++ b/switch/packet/Cargo.toml @@ -7,4 +7,3 @@ edition = "2021" [dependencies] byteorder = "1.4.3" -thiserror = "1.0.37" \ No newline at end of file diff --git a/switch/packet/src/arp/arp.rs b/switch/packet/src/arp/arp.rs index 4cf9a99..017e5e5 100644 --- a/switch/packet/src/arp/arp.rs +++ b/switch/packet/src/arp/arp.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, io}; /// 地址解析协议,由IP地址找到MAC地址 /// https://www.ietf.org/rfc/rfc6747.txt @@ -9,7 +9,6 @@ use std::fmt; | 源MAC地址 | 源ip地址 | | 目的MAC地址 | 目的ip地址 | */ -use crate::error::*; pub struct ArpPacket { buffer: B, @@ -19,9 +18,9 @@ impl> ArpPacket { pub fn unchecked(buffer: B) -> Self { Self { buffer } } - pub fn new(buffer: B) -> Result { + pub fn new(buffer: B) -> io::Result { if buffer.as_ref().len() != 28 { - Err(Error::InvalidPacket)? + Err(io::Error::from(io::ErrorKind::InvalidData))?; } let packet = Self::unchecked(buffer); Ok(packet) diff --git a/switch/packet/src/error.rs b/switch/packet/src/error.rs deleted file mode 100644 index da01a22..0000000 --- a/switch/packet/src/error.rs +++ /dev/null @@ -1,25 +0,0 @@ -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum Error { - #[error("the buffer is too small")] - SmallBuffer, - - #[error("the packet is invalid")] - InvalidPacket, - #[error("Unimplemented")] - Unimplemented, - // #[error("the vaue is invalid for the field")] - // InvalidValue, - // - // #[error("the value has already been defined")] - // AlreadyDefined, - // - // #[error(transparent)] - // Io(#[from] io::Error), - // - // #[error(transparent)] - // Nul(#[from] ffi::NulError), -} - -pub type Result = ::std::result::Result; diff --git a/switch/packet/src/ethernet/packet.rs b/switch/packet/src/ethernet/packet.rs index 40f9e26..8a2a918 100644 --- a/switch/packet/src/ethernet/packet.rs +++ b/switch/packet/src/ethernet/packet.rs @@ -1,5 +1,4 @@ -use std::fmt; -use crate::error::*; +use std::{fmt, io}; use crate::ethernet::protocol::Protocol; /// 以太网帧协议 @@ -18,11 +17,11 @@ impl> EthernetPacket { EthernetPacket { buffer } } - pub fn new(buffer: B) -> Result> { + pub fn new(buffer: B) -> io::Result> { let packet = EthernetPacket::unchecked(buffer); //头部固定14位 if packet.buffer.as_ref().len() < 14 { - Err(Error::SmallBuffer)? + Err(io::Error::from(io::ErrorKind::InvalidData))?; } Ok(packet) diff --git a/switch/packet/src/icmp/icmp.rs b/switch/packet/src/icmp/icmp.rs index c079302..5515d83 100644 --- a/switch/packet/src/icmp/icmp.rs +++ b/switch/packet/src/icmp/icmp.rs @@ -1,9 +1,9 @@ -use std::fmt; - +use std::{fmt, io}; use byteorder::{BigEndian, ReadBytesExt}; - use crate::cal_checksum; -use crate::error::*; +use crate::icmp::{Code, Kind}; +use crate::ip::ipv4::packet::IpV4Packet; + /// icmp 协议 /* https://www.rfc-editor.org/rfc/rfc792 0 1 2 3 @@ -17,8 +17,6 @@ use crate::error::*; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -use crate::icmp::{Code, Kind}; -use crate::ip::ipv4::packet::IpV4Packet; pub struct IcmpPacket { pub buffer: B, @@ -28,9 +26,9 @@ impl> IcmpPacket { pub fn unchecked(buffer: B) -> Self { Self { buffer } } - pub fn new(buffer: B) -> Result { + pub fn new(buffer: B) -> io::Result { if buffer.as_ref().len() < 8 { - Err(Error::SmallBuffer)? + Err(io::Error::from(io::ErrorKind::InvalidData))?; } let packet = Self::unchecked(buffer); Ok(packet) @@ -56,9 +54,7 @@ impl> IcmpPacket { Code::from(self.kind(), self.buffer.as_ref()[1]) } pub fn checksum(&self) -> u16 { - (&self.buffer.as_ref()[2..]) - .read_u16::() - .unwrap() + u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) } pub fn is_valid(&self) -> bool { self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 @@ -71,12 +67,8 @@ impl> IcmpPacket { | Kind::TimestampReply | Kind::InformationRequest | Kind::InformationReply => { - let ide = (&self.buffer.as_ref()[4..]) - .read_u16::() - .unwrap(); - let seq = (&self.buffer.as_ref()[6..]) - .read_u16::() - .unwrap(); + let ide =u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap()); + let seq = u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()); HeaderOther::Identifier(ide, seq) } Kind::DestinationUnreachable | Kind::TimeExceeded | Kind::SourceQuench => { @@ -110,6 +102,7 @@ impl> IcmpPacket { }, Kind::TimestampRequest | Kind::TimestampReply => { let mut buffer = Cursor::new(self.payload()); + Description::Timestamp( buffer.read_u32::().unwrap(), buffer.read_u32::().unwrap(), @@ -128,11 +121,11 @@ impl> fmt::Debug for IcmpPacket { } else { "icmp::Packet!" }) - .field("kind", &self.kind()) - .field("code", &self.code()) - .field("checksum", &self.checksum()) - .field("payload", &self.payload()) - .finish() + .field("kind", &self.kind()) + .field("code", &self.code()) + .field("checksum", &self.checksum()) + .field("payload", &self.payload()) + .finish() } } diff --git a/switch/packet/src/igmp/igmp_v1.rs b/switch/packet/src/igmp/igmp_v1.rs new file mode 100644 index 0000000..10dcbde --- /dev/null +++ b/switch/packet/src/igmp/igmp_v1.rs @@ -0,0 +1,117 @@ +use std::{fmt, io}; +use std::net::Ipv4Addr; +use crate::cal_checksum; + +/// igmp v1 +/* https://datatracker.ietf.org/doc/html/rfc1112 + 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| Type | Unused | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Group Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +/// v1版本的报文 +pub struct IgmpV1Packet { + pub buffer: B, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum IgmpV1Type { + /// 0x11 所有组224.0.0.1或者特定组 + Query, + /// 0x12 + ReportV1, + Unknown(u8), +} + +impl From for IgmpV1Type { + fn from(value: u8) -> IgmpV1Type { + use self::IgmpV1Type::*; + + match value { + 0x11 => Query, + 0x12 => ReportV1, + v => Unknown(v), + } + } +} + +impl Into for IgmpV1Type { + fn into(self) -> u8 { + match self { + IgmpV1Type::Query => 0x11, + IgmpV1Type::ReportV1 => 0x12, + IgmpV1Type::Unknown(v) => v + } + } +} + +impl> IgmpV1Packet { + pub fn unchecked(buffer: B) -> Self { + Self { buffer } + } + pub fn new(buffer: B) -> io::Result { + if buffer.as_ref().len() != 8 { + Err(io::Error::from(io::ErrorKind::InvalidData)) + } else { + let packet = Self::unchecked(buffer); + Ok(packet) + } + } +} + +impl> IgmpV1Packet { + pub fn version(&self) -> u8 { + self.buffer.as_ref()[0] >> 4 + } + pub fn igmp_type(&self) -> IgmpV1Type { + IgmpV1Type::from(self.buffer.as_ref()[0] & 0x0F) + } + pub fn unused(&self) -> u8 { + self.buffer.as_ref()[1] + } + pub fn checksum(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) + } + pub fn is_valid(&self) -> bool { + self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 + } + pub fn group_address(&self) -> Ipv4Addr { + let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); + Ipv4Addr::from(tmp) + } +} + +impl + AsMut<[u8]>> IgmpV1Packet { + pub fn set_version(&mut self, version: u8) { + self.buffer.as_mut()[0] = (version << 4) | 0x0F & self.buffer.as_mut()[0] + } + pub fn set_type(&mut self, igmp_type: IgmpV1Type) { + let t: u8 = igmp_type.into(); + self.buffer.as_mut()[0] = self.buffer.as_mut()[0] & 0xF0 | t + } + pub fn set_checksum(&mut self, checksum: u16) { + self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes()); + } + pub fn update_checksum(&mut self) { + self.set_checksum(0); + self.set_checksum(cal_checksum(self.buffer.as_ref())); + } + pub fn set_group_address(&mut self, group_address: Ipv4Addr) { + self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets()); + } +} + +impl> fmt::Debug for IgmpV1Packet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("igmp::V1") + .field("version", &self.version()) + .field("type", &self.igmp_type()) + .field("checksum", &self.checksum()) + .field("is_valid", &self.is_valid()) + .field("group_address", &self.group_address()) + .finish() + } +} \ No newline at end of file diff --git a/switch/packet/src/igmp/igmp_v2.rs b/switch/packet/src/igmp/igmp_v2.rs new file mode 100644 index 0000000..297b5b8 --- /dev/null +++ b/switch/packet/src/igmp/igmp_v2.rs @@ -0,0 +1,118 @@ +use std::{fmt, io}; +use std::net::Ipv4Addr; +use crate::cal_checksum; + +/// igmp v2 +/* https://www.rfc-editor.org/rfc/rfc2236.html + + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Max Resp Time | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Group Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/// v2版本的报文 +pub struct IgmpV2Packet { + pub buffer: B, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum IgmpV2Type { + /// 0x11 所有组224.0.0.1或者特定组 + Query, + /// 0x16 + ReportV2, + LeaveV2, + Unknown(u8), +} + +impl From for IgmpV2Type { + fn from(value: u8) -> IgmpV2Type { + use self::IgmpV2Type::*; + + match value { + 0x11 => Query, + 0x16 => ReportV2, + 0x17 => LeaveV2, + v => Unknown(v), + } + } +} + +impl Into for IgmpV2Type { + fn into(self) -> u8 { + match self { + IgmpV2Type::Query => 0x11, + IgmpV2Type::ReportV2 => 0x16, + IgmpV2Type::LeaveV2 => 0x17, + IgmpV2Type::Unknown(v) => v + } + } +} + +impl> IgmpV2Packet { + pub fn unchecked(buffer: B) -> Self { + Self { buffer } + } + pub fn new(buffer: B) -> io::Result { + if buffer.as_ref().len() != 8 { + Err(io::Error::from(io::ErrorKind::InvalidData)) + } else { + let packet = Self::unchecked(buffer); + Ok(packet) + } + } +} + +impl> IgmpV2Packet { + pub fn igmp_type(&self) -> IgmpV2Type { + IgmpV2Type::from(self.buffer.as_ref()[0]) + } + pub fn max_resp_time(&self) -> u8 { + self.buffer.as_ref()[1] + } + pub fn checksum(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) + } + pub fn is_valid(&self) -> bool { + self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 + } + pub fn group_address(&self) -> Ipv4Addr { + let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); + Ipv4Addr::from(tmp) + } +} + +impl + AsMut<[u8]>> IgmpV2Packet { + pub fn set_type(&mut self, igmp_type: IgmpV2Type) { + self.buffer.as_mut()[0] = igmp_type.into() + } + pub fn set_max_resp_time(&mut self, resp: u8) { + self.buffer.as_mut()[1] = resp + } + pub fn set_checksum(&mut self, checksum: u16) { + self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes()); + } + pub fn update_checksum(&mut self) { + self.set_checksum(0); + self.set_checksum(cal_checksum(self.buffer.as_ref())); + } + pub fn set_group_address(&mut self, group_address: Ipv4Addr) { + self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets()); + } +} + +impl> fmt::Debug for IgmpV2Packet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("igmp::V2") + .field("type", &self.igmp_type()) + .field("max_resp_time", &self.max_resp_time()) + .field("checksum", &self.checksum()) + .field("is_valid", &self.is_valid()) + .field("group_address", &self.group_address()) + .finish() + } +} diff --git a/switch/packet/src/igmp/igmp_v3.rs b/switch/packet/src/igmp/igmp_v3.rs new file mode 100644 index 0000000..f412d0e --- /dev/null +++ b/switch/packet/src/igmp/igmp_v3.rs @@ -0,0 +1,491 @@ +use std::{fmt, io}; +use std::net::Ipv4Addr; + +use crate::cal_checksum; + +/// igmp v3 +/* https://www.rfc-editor.org/rfc/rfc3376 +Query: + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type = 0x11 | Max Resp Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Group Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Resv |S| QRV | QQIC | Number of Sources (N) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Address [1] | + +- -+ + | Source Address [2] | + +- . -+ + . . . + . . . + +- -+ + | Source Address [N] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +----------------------------------------------------------------------------- + +Report: + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type = 0x22 | Reserved | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | Number of Group Records (M) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + . . + . Group Record [1] . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + . . + . Group Record [2] . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | . | + . . . + | . | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + . . + . Group Record [M] . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Group Record: + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Record Type | Aux Data Len | Number of Sources (N) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Multicast Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Address [1] | + +- -+ + | Source Address [2] | + +- -+ + . . . + . . . + . . . + +- -+ + | Source Address [N] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + . . + . Auxiliary Data . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Record Type: + 1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址 + 2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址 + 3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址 + 4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址 + 5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址 + 6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址 + */ +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum IgmpV3Type { + /// 0x11 所有组224.0.0.1或者特定组 + Query, + /// 0x22 + ReportV3, + Unknown(u8), +} + +impl From for IgmpV3Type { + fn from(value: u8) -> IgmpV3Type { + use self::IgmpV3Type::*; + + match value { + 0x11 => Query, + 0x22 => ReportV3, + v => Unknown(v), + } + } +} + +impl Into for IgmpV3Type { + fn into(self) -> u8 { + match self { + IgmpV3Type::Query => 0x11, + IgmpV3Type::ReportV3 => 0x22, + IgmpV3Type::Unknown(v) => v + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum IgmpV3RecordType { + //1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址 + ModeIsInclude, + //2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址 + ModeIsExclude, + //3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址 + ChangeToIncludeMode, + //4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址 + ChangeToExcludeMode, + //5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址 + AllowNewSources, + //6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址 + BlockOldSources, + Unknown(u8), +} + +impl From for IgmpV3RecordType { + fn from(value: u8) -> IgmpV3RecordType { + use self::IgmpV3RecordType::*; + + match value { + 1 => ModeIsInclude, + 2 => ModeIsExclude, + 3 => ChangeToIncludeMode, + 4 => ChangeToExcludeMode, + 5 => AllowNewSources, + 6 => BlockOldSources, + v => Unknown(v), + } + } +} + +impl Into for IgmpV3RecordType { + fn into(self) -> u8 { + use self::IgmpV3RecordType::*; + + match self { + ModeIsInclude => 1, + ModeIsExclude => 2, + ChangeToIncludeMode => 3, + ChangeToExcludeMode => 4, + AllowNewSources => 5, + BlockOldSources => 6, + Unknown(v) => v, + } + } +} + +/// v3版本的query报文 +pub struct IgmpV3QueryPacket { + pub buffer: B, +} + +impl> IgmpV3QueryPacket { + pub fn unchecked(buffer: B) -> Self { + Self { buffer } + } + pub fn new(buffer: B) -> io::Result { + if buffer.as_ref().len() < 12 { + Err(io::Error::from(io::ErrorKind::InvalidData)) + } else { + let packet = Self::unchecked(buffer); + Ok(packet) + } + } +} + +impl + AsMut<[u8]>> IgmpV3QueryPacket { + pub fn set_igmp_type(&mut self) { + self.buffer.as_mut()[0] = IgmpV3Type::Query.into(); + } + pub fn set_max_resp_code(&mut self, code: u8) { + self.buffer.as_mut()[1] = code; + } + pub fn set_group_address(&mut self, addr: Ipv4Addr) { + self.buffer.as_mut()[4..8].copy_from_slice(&addr.octets()) + } + pub fn set_checksum(&mut self, checksum: u16) { + self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes()) + } + pub fn set_qrv(&mut self, qrv: u8) { + self.buffer.as_mut()[8] = (self.buffer.as_ref()[8]&(!0x07)) | (qrv & 0x07) + } + pub fn set_qqic(&mut self, qqic: u8) { + self.buffer.as_mut()[9] = qqic + } + + pub fn update_checksum(&mut self) { + self.set_checksum(0); + let checksum = cal_checksum(self.buffer.as_ref()); + self.set_checksum(checksum); + } +} + +impl> IgmpV3QueryPacket { + pub fn igmp_type(&self) -> IgmpV3Type { + IgmpV3Type::from(self.buffer.as_ref()[0]) + } + pub fn max_resp_code(&self) -> u8 { + self.buffer.as_ref()[1] + } + pub fn checksum(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) + } + pub fn is_valid(&self) -> bool { + self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 + } + pub fn group_address(&self) -> Ipv4Addr { + let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); + Ipv4Addr::from(tmp) + } + /// 保留字段,设置为0 + pub fn resv(&self) -> u8 { + self.buffer.as_ref()[8] >> 4 + } + /// 标志位 + /// 该比特位为1时,所有收到此查询报文的其他路由器不启动定时器刷新过程,但是此查询报文并不抑制查询者选举过程和路由器的主机侧处理过程;默认未置位。 + pub fn s(&self) -> u8 { + (self.buffer.as_ref()[8] & 0x0F) >> 3 + } + /// 查询者向网络通告的健壮系数 + /// 此参数可使查询者使用自己的健壮系统同步其他组播路由器的健壮系数; + /// 其他路由器接收到查询报文时,如果发现该字段非0,则将自己的健壮系数调整为该字段的值;如果发现该字段为0,则不做处理。默认健壮系数值为2。 + pub fn qrv(&self) -> u8 { + self.buffer.as_ref()[8] & 0x07 + } + /// IGMP查询者的查询间隔 + /// 非查询者收到查询报文时,如果发现该字段非0,则将自己的查询间隔参数调整为该字段的值:如果发现该字段为0,则不做处理。默认值为60。 + pub fn qqic(&self) -> u8 { + self.buffer.as_ref()[9] + } + /// 报文中包含的组播源的数量 + /// 对于普遍组查询报文和特定组查询报文,该字段为0;对于特定源组查询报文,该字段非0 + pub fn source_number(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap()) + } + pub fn source_addresses(&self) -> Option> { + let num = self.source_number(); + if num == 0 { + None + } else { + let num = num as usize; + let mut list = Vec::with_capacity(num); + let buf = self.buffer.as_ref(); + let len = buf.len(); + for index in 0..num { + let start = (12 + index * 4) as usize; + let end = start + 4; + if end > len { + return None; + } + let tmp: [u8; 4] = buf[start..end].try_into().unwrap(); + list.push(Ipv4Addr::from(tmp)); + } + Some(list) + } + } + pub fn source_address(&self, index: u16) -> Option { + if self.source_number() >= index { + None + } else { + let start = (12 + index * 4) as usize; + let end = start + 4; + let buf = self.buffer.as_ref(); + let len = buf.len(); + if end > len { + return None; + } + let tmp: [u8; 4] = buf[start..end].try_into().unwrap(); + Some(Ipv4Addr::from(tmp)) + } + } +} + +/// v3版本的query报文 +pub struct IgmpV3ReportPacket { + pub buffer: B, +} + +impl> IgmpV3ReportPacket { + pub fn unchecked(buffer: B) -> Self { + Self { buffer } + } + pub fn new(buffer: B) -> io::Result { + if buffer.as_ref().len() < 8 { + Err(io::Error::from(io::ErrorKind::InvalidData)) + } else { + let packet = Self::unchecked(buffer); + Ok(packet) + } + } +} + +impl> IgmpV3ReportPacket { + pub fn igmp_type(&self) -> IgmpV3Type { + IgmpV3Type::from(self.buffer.as_ref()[0]) + } + pub fn reserved1(&self) -> u8 { + self.buffer.as_ref()[1] + } + pub fn checksum(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) + } + pub fn is_valid(&self) -> bool { + self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 + } + pub fn reserved2(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap()) + } + pub fn record_number(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) + } + pub fn group_records(&self) -> Option>> { + let num = self.record_number(); + if num == 0 { + None + } else { + let num = num as usize; + let mut list = Vec::with_capacity(num); + let mut start = 8 as usize; + let buf = self.buffer.as_ref(); + let len = buf.len(); + for _ in 0..num { + if start >= len { + return None; + } + if let Ok(record) = IgmpV3RecordPacket::new(&buf[start..]) { + let end = start + 8 + record.aux_data_len() as usize * 4 + record.source_number() as usize * 4; + if end > len { + return None; + } + list.push(IgmpV3RecordPacket::new(&buf[start..end]).unwrap()); + start = end; + } else { + return None; + } + } + Some(list) + } + } +} + + +/// group record +pub struct IgmpV3RecordPacket { + pub buffer: B, +} + +impl> IgmpV3RecordPacket { + pub fn unchecked(buffer: B) -> Self { + Self { buffer } + } + pub fn new(buffer: B) -> io::Result { + if buffer.as_ref().len() < 8 { + Err(io::Error::from(io::ErrorKind::InvalidData)) + } else { + let packet = Self::unchecked(buffer); + Ok(packet) + } + } +} + +impl> IgmpV3RecordPacket { + pub fn record_type(&self) -> IgmpV3RecordType { + IgmpV3RecordType::from(self.buffer.as_ref()[0]) + } + /// 辅助数据长度 以4字节为单位 + pub fn aux_data_len(&self) -> u8 { + self.buffer.as_ref()[1] + } + /// 源地址数 + pub fn source_number(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) + } + ///多播地址 + pub fn multicast_address(&self) -> Ipv4Addr { + let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); + Ipv4Addr::from(tmp) + } + pub fn source_addresses(&self) -> Option> { + let num = self.source_number(); + if num == 0 { + None + } else { + let num = num as usize; + let mut list = Vec::with_capacity(num); + let buf = self.buffer.as_ref(); + let len = buf.len(); + for index in 0..num { + let start = (8 + index * 4) as usize; + let end = start + 4; + if end > len { + return None; + } + let tmp: [u8; 4] = buf[start..end].try_into().unwrap(); + list.push(Ipv4Addr::from(tmp)); + } + Some(list) + } + } + pub fn source_address(&self, index: u16) -> Option { + if self.source_number() >= index { + None + } else { + let start = (8 + index * 4) as usize; + let end = start + 4; + if end > self.buffer.as_ref().len() { + return None; + } + let tmp: [u8; 4] = self.buffer.as_ref()[start..end].try_into().unwrap(); + Some(Ipv4Addr::from(tmp)) + } + } + /// 在文档中没有定义辅助数据的作用,通常应该是空的 + pub fn auxiliary_data(&self) -> &[u8] { + let start = 8 + self.source_number() as usize * 4; + let end = start + self.aux_data_len() as usize * 4; + if end > self.buffer.as_ref().len() { + return &[]; + } + &self.buffer.as_ref()[start..end] + } +} + +impl> fmt::Debug for IgmpV3QueryPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("igmp::V3Query") + .field("type", &self.igmp_type()) + .field("max_resp_code", &self.max_resp_code()) + .field("checksum", &self.checksum()) + .field("is_valid", &self.is_valid()) + .field("group_address", &self.group_address()) + .field("s", &self.s()) + .field("qrv", &self.qrv()) + .field("qqic", &self.qqic()) + .field("number of sources", &self.source_number()) + .field("source_addresses", &self.source_addresses()) + .finish() + } +} + +impl> fmt::Debug for IgmpV3ReportPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("igmp::V3Report") + .field("type", &self.igmp_type()) + .field("reserved1", &self.reserved1()) + .field("checksum", &self.checksum()) + .field("is_valid", &self.is_valid()) + .field("reserved2", &self.reserved2()) + .field("record_number", &self.record_number()) + .field("group_records", &self.group_records()) + .finish() + } +} + +impl> fmt::Debug for IgmpV3RecordPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("igmp::V3Record") + .field("record_type", &self.record_type()) + .field("aux_data_len", &self.aux_data_len()) + .field("source_number", &self.source_number()) + .field("multicast_address", &self.multicast_address()) + .field("source_addresses", &self.source_addresses()) + .field("auxiliary_data", &self.auxiliary_data()) + .finish() + } +} \ No newline at end of file diff --git a/switch/packet/src/igmp/mod.rs b/switch/packet/src/igmp/mod.rs new file mode 100644 index 0000000..79d04e1 --- /dev/null +++ b/switch/packet/src/igmp/mod.rs @@ -0,0 +1,46 @@ +pub mod igmp_v1; +pub mod igmp_v2; +pub mod igmp_v3; + +#[derive(Debug,Copy, Clone,Eq, PartialEq)] +pub enum IgmpType { + /// 0x11 所有组224.0.0.1或者特定组 + Query, + /// 0x12 + ReportV1, + /// 0x16 + ReportV2, + /// 0x22 + ReportV3, + /// 0x17 目标组固定是 224.0.0.2 + LeaveV2, + Unknown(u8), +} + +impl From for IgmpType { + fn from(value: u8) -> IgmpType { + use self::IgmpType::*; + + match value { + 0x11 => Query, + 0x12 => ReportV1, + 0x16 => ReportV2, + 0x22 => ReportV3, + 0x17 => LeaveV2, + v => Unknown(v), + } + } +} + +impl Into for IgmpType { + fn into(self) -> u8 { + match self { + IgmpType::Query => 0x11, + IgmpType::ReportV1 => 0x12, + IgmpType::ReportV2 => 0x16, + IgmpType::ReportV3 => 0x22, + IgmpType::LeaveV2 => 0x17, + IgmpType::Unknown(v) => v + } + } +} \ No newline at end of file diff --git a/switch/packet/src/ip/ipv4/packet.rs b/switch/packet/src/ip/ipv4/packet.rs index 94dc186..1308f71 100644 --- a/switch/packet/src/ip/ipv4/packet.rs +++ b/switch/packet/src/ip/ipv4/packet.rs @@ -1,9 +1,8 @@ -use std::fmt; +use std::{fmt, io}; use std::net::Ipv4Addr; use crate::cal_checksum; -use crate::error::*; use crate::ip::ipv4::protocol::Protocol; /// ip协议 @@ -38,16 +37,16 @@ impl> IpV4Packet { pub fn unchecked(buffer: B) -> Self { Self { buffer } } - pub fn new(buffer: B) -> Result { - if buffer.as_ref()[0] >> 4 != 4 { - Err(Error::Unimplemented)? - } + pub fn new(buffer: B) -> io::Result { if buffer.as_ref().len() < 20 { - Err(Error::SmallBuffer)? + Err(io::Error::new(io::ErrorKind::InvalidData, "len < 20"))?; + } + if buffer.as_ref()[0] >> 4 != 4 { + Err(io::Error::new(io::ErrorKind::InvalidData, "not ipv4"))?; } let packet = Self::unchecked(buffer); if packet.buffer.as_ref().len() < packet.header_len() as usize * 4 { - Err(Error::SmallBuffer)? + Err(io::Error::new(io::ErrorKind::InvalidData, "head_len err"))?; } Ok(packet) } @@ -59,17 +58,6 @@ impl> IpV4Packet { } pub fn payload(&self) -> &[u8] { &self.buffer.as_ref()[(self.header_len() as usize * 4)..] - // match self.protocol() { - // Protocol::Udp => { - // let udp = UdpPacket::new(IpAddr::V4(self.source_ip()), - // IpAddr::V4(self.destination_ip()), - // &self.buffer.as_ref()[(self.header_len() as usize * 4)..])?; - // Ok(crate::IpUpperLayer::UDP(udp)) - // } - // _ => { - // Ok(crate::IpUpperLayer::Unknown(self.buffer.as_ref())); - // } - // } } } @@ -82,13 +70,18 @@ impl + AsMut<[u8]>> IpV4Packet { let len = self.header_len() as usize * 4; &mut self.buffer.as_mut()[len..] } - + pub fn set_protocol(&mut self, value: Protocol) { + self.header_mut()[9] = value.into(); + } pub fn set_source_ip(&mut self, value: Ipv4Addr) { self.header_mut()[12..16].copy_from_slice(&value.octets()); } pub fn set_destination_ip(&mut self, value: Ipv4Addr) { self.header_mut()[16..20].copy_from_slice(&value.octets()); } + pub fn set_flags(&mut self, flags: u8) { + self.buffer.as_mut()[6] = (self.buffer.as_ref()[6] & 0b11100000) | (flags << 5) + } fn set_checksum(&mut self, value: u16) { self.header_mut()[10..12].copy_from_slice(&value.to_be_bytes()) } diff --git a/switch/packet/src/ip/mod.rs b/switch/packet/src/ip/mod.rs index 1ee3452..b82e44f 100644 --- a/switch/packet/src/ip/mod.rs +++ b/switch/packet/src/ip/mod.rs @@ -1,7 +1,6 @@ +use std::io; use ipv4::packet::IpV4Packet; -use crate::error::*; - pub mod ipv4; pub enum IpPacket { @@ -9,10 +8,10 @@ pub enum IpPacket { } impl> IpPacket { - pub fn new(buffer: B) -> Result { + pub fn new(buffer: B) -> io::Result { match buffer.as_ref()[0] >> 4 { 4 => Ok(IpPacket::V4(IpV4Packet::new(buffer)?)), - _ => Err(Error::InvalidPacket), + _ => Err(io::Error::from(io::ErrorKind::InvalidData)), } } } diff --git a/switch/packet/src/lib.rs b/switch/packet/src/lib.rs index 7f11f44..ac58415 100644 --- a/switch/packet/src/lib.rs +++ b/switch/packet/src/lib.rs @@ -3,8 +3,8 @@ use std::net::Ipv4Addr; use byteorder::BigEndian; use byteorder::ReadBytesExt; -pub mod error; pub mod icmp; +pub mod igmp; pub mod ip; pub mod tcp; pub mod udp; diff --git a/switch/packet/src/tcp/tcp.rs b/switch/packet/src/tcp/tcp.rs index d0ca2dc..3424abf 100644 --- a/switch/packet/src/tcp/tcp.rs +++ b/switch/packet/src/tcp/tcp.rs @@ -1,7 +1,6 @@ -use std::fmt; +use std::{fmt, io}; use std::net::Ipv4Addr; -use crate::error::*; use crate::tcp::Flags; /// tcp @@ -59,15 +58,15 @@ impl> TcpPacket { buffer, } } - pub fn new(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> Result> { + pub fn new(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> io::Result> { let packet = TcpPacket::unchecked(source_ip, destination_ip, buffer); if packet.buffer.as_ref().len() < 20 { - Err(Error::SmallBuffer)? + Err(io::Error::from(io::ErrorKind::InvalidData))?; } if packet.buffer.as_ref().len() < packet.data_offset() as usize * 4 { - Err(Error::SmallBuffer)? + Err(io::Error::from(io::ErrorKind::InvalidData))?; } Ok(packet) diff --git a/switch/packet/src/udp/udp.rs b/switch/packet/src/udp/udp.rs index b52e4b9..7b6808a 100644 --- a/switch/packet/src/udp/udp.rs +++ b/switch/packet/src/udp/udp.rs @@ -1,8 +1,6 @@ -use std::fmt; +use std::{fmt, io}; use std::net::Ipv4Addr; -use crate::error::*; - /// udp协议 /// /* @@ -62,9 +60,9 @@ impl> UdpPacket { buffer, } } - pub fn new(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> Result> { + pub fn new(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> io::Result> { if buffer.as_ref().len() < 8 { - Err(Error::SmallBuffer)? + Err(io::Error::from(io::ErrorKind::InvalidData))?; } let packet = Self::unchecked(source_ip, destination_ip, buffer); Ok(packet)