packet.rs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. use crate::phy::DeviceCapabilities;
  2. use crate::wire::*;
  3. #[allow(clippy::large_enum_variant)]
  4. #[derive(Debug, PartialEq)]
  5. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  6. #[cfg(feature = "medium-ethernet")]
  7. pub(crate) enum EthernetPacket<'a> {
  8. #[cfg(feature = "proto-ipv4")]
  9. Arp(ArpRepr),
  10. Ip(Packet<'a>),
  11. }
  12. #[derive(Debug, PartialEq)]
  13. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  14. pub(crate) enum Packet<'p> {
  15. #[cfg(feature = "proto-ipv4")]
  16. Ipv4(PacketV4<'p>),
  17. #[cfg(feature = "proto-ipv6")]
  18. Ipv6(PacketV6<'p>),
  19. }
  20. impl<'p> Packet<'p> {
  21. pub(crate) fn new(ip_repr: IpRepr, payload: IpPayload<'p>) -> Self {
  22. match ip_repr {
  23. #[cfg(feature = "proto-ipv4")]
  24. IpRepr::Ipv4(header) => Self::new_ipv4(header, payload),
  25. #[cfg(feature = "proto-ipv6")]
  26. IpRepr::Ipv6(header) => Self::new_ipv6(header, payload),
  27. }
  28. }
  29. #[cfg(feature = "proto-ipv4")]
  30. pub(crate) fn new_ipv4(ip_repr: Ipv4Repr, payload: IpPayload<'p>) -> Self {
  31. Self::Ipv4(PacketV4 {
  32. header: ip_repr,
  33. payload,
  34. })
  35. }
  36. #[cfg(feature = "proto-ipv6")]
  37. pub(crate) fn new_ipv6(ip_repr: Ipv6Repr, payload: IpPayload<'p>) -> Self {
  38. Self::Ipv6(PacketV6 {
  39. header: ip_repr,
  40. #[cfg(feature = "proto-ipv6-hbh")]
  41. hop_by_hop: None,
  42. #[cfg(feature = "proto-ipv6-fragmentation")]
  43. fragment: None,
  44. #[cfg(feature = "proto-ipv6-routing")]
  45. routing: None,
  46. payload,
  47. })
  48. }
  49. pub(crate) fn ip_repr(&self) -> IpRepr {
  50. match self {
  51. #[cfg(feature = "proto-ipv4")]
  52. Packet::Ipv4(p) => IpRepr::Ipv4(p.header),
  53. #[cfg(feature = "proto-ipv6")]
  54. Packet::Ipv6(p) => IpRepr::Ipv6(p.header),
  55. }
  56. }
  57. pub(crate) fn payload(&self) -> &IpPayload<'p> {
  58. match self {
  59. #[cfg(feature = "proto-ipv4")]
  60. Packet::Ipv4(p) => &p.payload,
  61. #[cfg(feature = "proto-ipv6")]
  62. Packet::Ipv6(p) => &p.payload,
  63. }
  64. }
  65. pub(crate) fn emit_payload(
  66. &self,
  67. _ip_repr: &IpRepr,
  68. payload: &mut [u8],
  69. caps: &DeviceCapabilities,
  70. ) {
  71. match self.payload() {
  72. #[cfg(feature = "proto-ipv4")]
  73. IpPayload::Icmpv4(icmpv4_repr) => {
  74. icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum)
  75. }
  76. #[cfg(all(feature = "proto-ipv4", feature = "multicast"))]
  77. IpPayload::Igmp(igmp_repr) => igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)),
  78. #[cfg(feature = "proto-ipv6")]
  79. IpPayload::Icmpv6(icmpv6_repr) => {
  80. let ipv6_repr = match _ip_repr {
  81. #[cfg(feature = "proto-ipv4")]
  82. IpRepr::Ipv4(_) => unreachable!(),
  83. IpRepr::Ipv6(repr) => repr,
  84. };
  85. icmpv6_repr.emit(
  86. &ipv6_repr.src_addr,
  87. &ipv6_repr.dst_addr,
  88. &mut Icmpv6Packet::new_unchecked(payload),
  89. &caps.checksum,
  90. )
  91. }
  92. #[cfg(feature = "proto-ipv6")]
  93. IpPayload::HopByHopIcmpv6(hbh_repr, icmpv6_repr) => {
  94. let ipv6_repr = match _ip_repr {
  95. #[cfg(feature = "proto-ipv4")]
  96. IpRepr::Ipv4(_) => unreachable!(),
  97. IpRepr::Ipv6(repr) => repr,
  98. };
  99. let ipv6_ext_hdr = Ipv6ExtHeaderRepr {
  100. next_header: IpProtocol::Icmpv6,
  101. length: 0,
  102. data: &[],
  103. };
  104. ipv6_ext_hdr.emit(&mut Ipv6ExtHeader::new_unchecked(
  105. &mut payload[..ipv6_ext_hdr.header_len()],
  106. ));
  107. let hbh_start = ipv6_ext_hdr.header_len();
  108. let hbh_end = hbh_start + hbh_repr.buffer_len();
  109. hbh_repr.emit(&mut Ipv6HopByHopHeader::new_unchecked(
  110. &mut payload[hbh_start..hbh_end],
  111. ));
  112. icmpv6_repr.emit(
  113. &ipv6_repr.src_addr,
  114. &ipv6_repr.dst_addr,
  115. &mut Icmpv6Packet::new_unchecked(&mut payload[hbh_end..]),
  116. &caps.checksum,
  117. );
  118. }
  119. #[cfg(feature = "socket-raw")]
  120. IpPayload::Raw(raw_packet) => payload.copy_from_slice(raw_packet),
  121. #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
  122. IpPayload::Udp(udp_repr, inner_payload) => udp_repr.emit(
  123. &mut UdpPacket::new_unchecked(payload),
  124. &_ip_repr.src_addr(),
  125. &_ip_repr.dst_addr(),
  126. inner_payload.len(),
  127. |buf| buf.copy_from_slice(inner_payload),
  128. &caps.checksum,
  129. ),
  130. #[cfg(feature = "socket-tcp")]
  131. IpPayload::Tcp(mut tcp_repr) => {
  132. // This is a terrible hack to make TCP performance more acceptable on systems
  133. // where the TCP buffers are significantly larger than network buffers,
  134. // e.g. a 64 kB TCP receive buffer (and so, when empty, a 64k window)
  135. // together with four 1500 B Ethernet receive buffers. If left untreated,
  136. // this would result in our peer pushing our window and sever packet loss.
  137. //
  138. // I'm really not happy about this "solution" but I don't know what else to do.
  139. if let Some(max_burst_size) = caps.max_burst_size {
  140. let mut max_segment_size = caps.max_transmission_unit;
  141. max_segment_size -= _ip_repr.header_len();
  142. max_segment_size -= tcp_repr.header_len();
  143. let max_window_size = max_burst_size * max_segment_size;
  144. if tcp_repr.window_len as usize > max_window_size {
  145. tcp_repr.window_len = max_window_size as u16;
  146. }
  147. }
  148. tcp_repr.emit(
  149. &mut TcpPacket::new_unchecked(payload),
  150. &_ip_repr.src_addr(),
  151. &_ip_repr.dst_addr(),
  152. &caps.checksum,
  153. );
  154. }
  155. #[cfg(feature = "socket-dhcpv4")]
  156. IpPayload::Dhcpv4(udp_repr, dhcp_repr) => udp_repr.emit(
  157. &mut UdpPacket::new_unchecked(payload),
  158. &_ip_repr.src_addr(),
  159. &_ip_repr.dst_addr(),
  160. dhcp_repr.buffer_len(),
  161. |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(),
  162. &caps.checksum,
  163. ),
  164. }
  165. }
  166. }
  167. #[derive(Debug, PartialEq)]
  168. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  169. #[cfg(feature = "proto-ipv4")]
  170. pub(crate) struct PacketV4<'p> {
  171. header: Ipv4Repr,
  172. payload: IpPayload<'p>,
  173. }
  174. #[derive(Debug, PartialEq)]
  175. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  176. #[cfg(feature = "proto-ipv6")]
  177. pub(crate) struct PacketV6<'p> {
  178. pub(crate) header: Ipv6Repr,
  179. #[cfg(feature = "proto-ipv6-hbh")]
  180. pub(crate) hop_by_hop: Option<Ipv6HopByHopRepr<'p>>,
  181. #[cfg(feature = "proto-ipv6-fragmentation")]
  182. pub(crate) fragment: Option<Ipv6FragmentRepr>,
  183. #[cfg(feature = "proto-ipv6-routing")]
  184. pub(crate) routing: Option<Ipv6RoutingRepr<'p>>,
  185. pub(crate) payload: IpPayload<'p>,
  186. }
  187. #[derive(Debug, PartialEq)]
  188. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  189. pub(crate) enum IpPayload<'p> {
  190. #[cfg(feature = "proto-ipv4")]
  191. Icmpv4(Icmpv4Repr<'p>),
  192. #[cfg(all(feature = "proto-ipv4", feature = "multicast"))]
  193. Igmp(IgmpRepr),
  194. #[cfg(feature = "proto-ipv6")]
  195. Icmpv6(Icmpv6Repr<'p>),
  196. #[cfg(feature = "proto-ipv6")]
  197. HopByHopIcmpv6(Ipv6HopByHopRepr<'p>, Icmpv6Repr<'p>),
  198. #[cfg(feature = "socket-raw")]
  199. Raw(&'p [u8]),
  200. #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
  201. Udp(UdpRepr, &'p [u8]),
  202. #[cfg(feature = "socket-tcp")]
  203. Tcp(TcpRepr<'p>),
  204. #[cfg(feature = "socket-dhcpv4")]
  205. Dhcpv4(UdpRepr, DhcpRepr<'p>),
  206. }
  207. impl<'p> IpPayload<'p> {
  208. #[cfg(feature = "proto-sixlowpan")]
  209. pub(crate) fn as_sixlowpan_next_header(&self) -> SixlowpanNextHeader {
  210. match self {
  211. #[cfg(feature = "proto-ipv4")]
  212. Self::Icmpv4(_) => unreachable!(),
  213. #[cfg(feature = "socket-dhcpv4")]
  214. Self::Dhcpv4(..) => unreachable!(),
  215. #[cfg(feature = "proto-ipv6")]
  216. Self::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
  217. #[cfg(feature = "proto-ipv6")]
  218. Self::HopByHopIcmpv6(_, _) => unreachable!(),
  219. #[cfg(all(feature = "proto-ipv4", feature = "multicast"))]
  220. Self::Igmp(_) => unreachable!(),
  221. #[cfg(feature = "socket-tcp")]
  222. Self::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp),
  223. #[cfg(feature = "socket-udp")]
  224. Self::Udp(..) => SixlowpanNextHeader::Compressed,
  225. #[cfg(feature = "socket-raw")]
  226. Self::Raw(_) => todo!(),
  227. }
  228. }
  229. }
  230. #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
  231. pub(crate) fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize {
  232. // Send back as much of the original payload as will fit within
  233. // the minimum MTU required by IPv4. See RFC 1812 § 4.3.2.3 for
  234. // more details.
  235. //
  236. // Since the entire network layer packet must fit within the minimum
  237. // MTU supported, the payload must not exceed the following:
  238. //
  239. // <min mtu> - IP Header Size * 2 - ICMPv4 DstUnreachable hdr size
  240. len.min(mtu - header_len * 2 - 8)
  241. }