1 use super::*; 2 3 #[cfg(feature = "socket-dhcpv4")] 4 use crate::socket::dhcpv4; 5 #[cfg(feature = "socket-icmp")] 6 use crate::socket::icmp; 7 use crate::socket::AnySocket; 8 9 use crate::phy::{Medium, TxToken}; 10 use crate::time::Instant; 11 use crate::wire::*; 12 13 impl InterfaceInner { process_ipv4<'a, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, ipv4_packet: &Ipv4Packet<&'a T>, frag: &'a mut FragmentsBuffer, ) -> Option<IpPacket<'a>>14 pub(super) fn process_ipv4<'a, T: AsRef<[u8]> + ?Sized>( 15 &mut self, 16 sockets: &mut SocketSet, 17 ipv4_packet: &Ipv4Packet<&'a T>, 18 frag: &'a mut FragmentsBuffer, 19 ) -> Option<IpPacket<'a>> { 20 let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); 21 if !self.is_unicast_v4(ipv4_repr.src_addr) { 22 // Discard packets with non-unicast source addresses. 23 net_debug!("non-unicast source address"); 24 return None; 25 } 26 27 #[cfg(feature = "proto-ipv4-fragmentation")] 28 let ip_payload = { 29 if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 { 30 let key = FragKey::Ipv4(ipv4_packet.get_key()); 31 32 let f = match frag.assembler.get(&key, self.now + frag.reassembly_timeout) { 33 Ok(f) => f, 34 Err(_) => { 35 net_debug!("No available packet assembler for fragmented packet"); 36 return None; 37 } 38 }; 39 40 if !ipv4_packet.more_frags() { 41 // This is the last fragment, so we know the total size 42 check!(f.set_total_size( 43 ipv4_packet.total_len() as usize - ipv4_packet.header_len() as usize 44 + ipv4_packet.frag_offset() as usize, 45 )); 46 } 47 48 if let Err(e) = f.add(ipv4_packet.payload(), ipv4_packet.frag_offset() as usize) { 49 net_debug!("fragmentation error: {:?}", e); 50 return None; 51 } 52 53 // NOTE: according to the standard, the total length needs to be 54 // recomputed, as well as the checksum. However, we don't really use 55 // the IPv4 header after the packet is reassembled. 56 match f.assemble() { 57 Some(payload) => payload, 58 None => return None, 59 } 60 } else { 61 ipv4_packet.payload() 62 } 63 }; 64 65 #[cfg(not(feature = "proto-ipv4-fragmentation"))] 66 let ip_payload = ipv4_packet.payload(); 67 68 let ip_repr = IpRepr::Ipv4(ipv4_repr); 69 70 #[cfg(feature = "socket-raw")] 71 let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload); 72 #[cfg(not(feature = "socket-raw"))] 73 let handled_by_raw_socket = false; 74 75 #[cfg(feature = "socket-dhcpv4")] 76 { 77 if ipv4_repr.next_header == IpProtocol::Udp && self.hardware_addr.is_some() { 78 let udp_packet = check!(UdpPacket::new_checked(ip_payload)); 79 if let Some(dhcp_socket) = sockets 80 .items_mut() 81 .find_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket)) 82 { 83 // First check for source and dest ports, then do `UdpRepr::parse` if they match. 84 // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) 85 if udp_packet.src_port() == dhcp_socket.server_port 86 && udp_packet.dst_port() == dhcp_socket.client_port 87 { 88 let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); 89 let udp_repr = check!(UdpRepr::parse( 90 &udp_packet, 91 &src_addr, 92 &dst_addr, 93 &self.caps.checksum 94 )); 95 let udp_payload = udp_packet.payload(); 96 97 dhcp_socket.process(self, &ipv4_repr, &udp_repr, udp_payload); 98 return None; 99 } 100 } 101 } 102 } 103 104 if !self.has_ip_addr(ipv4_repr.dst_addr) 105 && !self.has_multicast_group(ipv4_repr.dst_addr) 106 && !self.is_broadcast_v4(ipv4_repr.dst_addr) 107 { 108 // Ignore IP packets not directed at us, or broadcast, or any of the multicast groups. 109 // If AnyIP is enabled, also check if the packet is routed locally. 110 if !self.any_ip 111 || !ipv4_repr.dst_addr.is_unicast() 112 || self 113 .routes 114 .lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), self.now) 115 .map_or(true, |router_addr| !self.has_ip_addr(router_addr)) 116 { 117 return None; 118 } 119 } 120 121 match ipv4_repr.next_header { 122 IpProtocol::Icmp => self.process_icmpv4(sockets, ip_repr, ip_payload), 123 124 #[cfg(feature = "proto-igmp")] 125 IpProtocol::Igmp => self.process_igmp(ipv4_repr, ip_payload), 126 127 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] 128 IpProtocol::Udp => { 129 let udp_packet = check!(UdpPacket::new_checked(ip_payload)); 130 let udp_repr = check!(UdpRepr::parse( 131 &udp_packet, 132 &ipv4_repr.src_addr.into(), 133 &ipv4_repr.dst_addr.into(), 134 &self.checksum_caps(), 135 )); 136 137 self.process_udp( 138 sockets, 139 ip_repr, 140 udp_repr, 141 handled_by_raw_socket, 142 udp_packet.payload(), 143 ip_payload, 144 ) 145 } 146 147 #[cfg(feature = "socket-tcp")] 148 IpProtocol::Tcp => self.process_tcp(sockets, ip_repr, ip_payload), 149 150 _ if handled_by_raw_socket => None, 151 152 _ => { 153 // Send back as much of the original payload as we can. 154 let payload_len = 155 icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len()); 156 let icmp_reply_repr = Icmpv4Repr::DstUnreachable { 157 reason: Icmpv4DstUnreachable::ProtoUnreachable, 158 header: ipv4_repr, 159 data: &ip_payload[0..payload_len], 160 }; 161 self.icmpv4_reply(ipv4_repr, icmp_reply_repr) 162 } 163 } 164 } 165 166 #[cfg(feature = "medium-ethernet")] process_arp<'frame, T: AsRef<[u8]>>( &mut self, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>, ) -> Option<EthernetPacket<'frame>>167 pub(super) fn process_arp<'frame, T: AsRef<[u8]>>( 168 &mut self, 169 timestamp: Instant, 170 eth_frame: &EthernetFrame<&'frame T>, 171 ) -> Option<EthernetPacket<'frame>> { 172 let arp_packet = check!(ArpPacket::new_checked(eth_frame.payload())); 173 let arp_repr = check!(ArpRepr::parse(&arp_packet)); 174 175 match arp_repr { 176 ArpRepr::EthernetIpv4 { 177 operation, 178 source_hardware_addr, 179 source_protocol_addr, 180 target_protocol_addr, 181 .. 182 } => { 183 // Only process ARP packets for us. 184 if !self.has_ip_addr(target_protocol_addr) { 185 return None; 186 } 187 188 // Only process REQUEST and RESPONSE. 189 if let ArpOperation::Unknown(_) = operation { 190 net_debug!("arp: unknown operation code"); 191 return None; 192 } 193 194 // Discard packets with non-unicast source addresses. 195 if !source_protocol_addr.is_unicast() || !source_hardware_addr.is_unicast() { 196 net_debug!("arp: non-unicast source address"); 197 return None; 198 } 199 200 if !self.in_same_network(&IpAddress::Ipv4(source_protocol_addr)) { 201 net_debug!("arp: source IP address not in same network as us"); 202 return None; 203 } 204 205 // Fill the ARP cache from any ARP packet aimed at us (both request or response). 206 // We fill from requests too because if someone is requesting our address they 207 // are probably going to talk to us, so we avoid having to request their address 208 // when we later reply to them. 209 self.neighbor_cache.as_mut().unwrap().fill( 210 source_protocol_addr.into(), 211 source_hardware_addr.into(), 212 timestamp, 213 ); 214 215 if operation == ArpOperation::Request { 216 let src_hardware_addr = match self.hardware_addr { 217 Some(HardwareAddress::Ethernet(addr)) => addr, 218 _ => unreachable!(), 219 }; 220 221 Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { 222 operation: ArpOperation::Reply, 223 source_hardware_addr: src_hardware_addr, 224 source_protocol_addr: target_protocol_addr, 225 target_hardware_addr: source_hardware_addr, 226 target_protocol_addr: source_protocol_addr, 227 })) 228 } else { 229 None 230 } 231 } 232 } 233 } 234 process_icmpv4<'frame>( &mut self, _sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], ) -> Option<IpPacket<'frame>>235 pub(super) fn process_icmpv4<'frame>( 236 &mut self, 237 _sockets: &mut SocketSet, 238 ip_repr: IpRepr, 239 ip_payload: &'frame [u8], 240 ) -> Option<IpPacket<'frame>> { 241 let icmp_packet = check!(Icmpv4Packet::new_checked(ip_payload)); 242 let icmp_repr = check!(Icmpv4Repr::parse(&icmp_packet, &self.caps.checksum)); 243 244 #[cfg(feature = "socket-icmp")] 245 let mut handled_by_icmp_socket = false; 246 247 #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] 248 for icmp_socket in _sockets 249 .items_mut() 250 .filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket)) 251 { 252 if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { 253 icmp_socket.process(self, &ip_repr, &icmp_repr.into()); 254 handled_by_icmp_socket = true; 255 } 256 } 257 258 match icmp_repr { 259 // Respond to echo requests. 260 #[cfg(feature = "proto-ipv4")] 261 Icmpv4Repr::EchoRequest { 262 ident, 263 seq_no, 264 data, 265 } => { 266 let icmp_reply_repr = Icmpv4Repr::EchoReply { 267 ident, 268 seq_no, 269 data, 270 }; 271 match ip_repr { 272 IpRepr::Ipv4(ipv4_repr) => self.icmpv4_reply(ipv4_repr, icmp_reply_repr), 273 #[allow(unreachable_patterns)] 274 _ => unreachable!(), 275 } 276 } 277 278 // Ignore any echo replies. 279 Icmpv4Repr::EchoReply { .. } => None, 280 281 // Don't report an error if a packet with unknown type 282 // has been handled by an ICMP socket 283 #[cfg(feature = "socket-icmp")] 284 _ if handled_by_icmp_socket => None, 285 286 // FIXME: do something correct here? 287 _ => None, 288 } 289 } 290 icmpv4_reply<'frame, 'icmp: 'frame>( &self, ipv4_repr: Ipv4Repr, icmp_repr: Icmpv4Repr<'icmp>, ) -> Option<IpPacket<'frame>>291 pub(super) fn icmpv4_reply<'frame, 'icmp: 'frame>( 292 &self, 293 ipv4_repr: Ipv4Repr, 294 icmp_repr: Icmpv4Repr<'icmp>, 295 ) -> Option<IpPacket<'frame>> { 296 if !self.is_unicast_v4(ipv4_repr.src_addr) { 297 // Do not send ICMP replies to non-unicast sources 298 None 299 } else if self.is_unicast_v4(ipv4_repr.dst_addr) { 300 // Reply as normal when src_addr and dst_addr are both unicast 301 let ipv4_reply_repr = Ipv4Repr { 302 src_addr: ipv4_repr.dst_addr, 303 dst_addr: ipv4_repr.src_addr, 304 next_header: IpProtocol::Icmp, 305 payload_len: icmp_repr.buffer_len(), 306 hop_limit: 64, 307 }; 308 Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) 309 } else if self.is_broadcast_v4(ipv4_repr.dst_addr) { 310 // Only reply to broadcasts for echo replies and not other ICMP messages 311 match icmp_repr { 312 Icmpv4Repr::EchoReply { .. } => match self.ipv4_addr() { 313 Some(src_addr) => { 314 let ipv4_reply_repr = Ipv4Repr { 315 src_addr, 316 dst_addr: ipv4_repr.src_addr, 317 next_header: IpProtocol::Icmp, 318 payload_len: icmp_repr.buffer_len(), 319 hop_limit: 64, 320 }; 321 Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) 322 } 323 None => None, 324 }, 325 _ => None, 326 } 327 } else { 328 None 329 } 330 } 331 332 #[cfg(feature = "proto-ipv4-fragmentation")] dispatch_ipv4_frag<Tx: TxToken>(&mut self, tx_token: Tx, frag: &mut Fragmenter)333 pub(super) fn dispatch_ipv4_frag<Tx: TxToken>(&mut self, tx_token: Tx, frag: &mut Fragmenter) { 334 let caps = self.caps.clone(); 335 336 let mtu_max = self.ip_mtu(); 337 let ip_len = (frag.packet_len - frag.sent_bytes + frag.ipv4.repr.buffer_len()).min(mtu_max); 338 let payload_len = ip_len - frag.ipv4.repr.buffer_len(); 339 340 let more_frags = (frag.packet_len - frag.sent_bytes) != payload_len; 341 frag.ipv4.repr.payload_len = payload_len; 342 frag.sent_bytes += payload_len; 343 344 let mut tx_len = ip_len; 345 #[cfg(feature = "medium-ethernet")] 346 if matches!(caps.medium, Medium::Ethernet) { 347 tx_len += EthernetFrame::<&[u8]>::header_len(); 348 } 349 350 // Emit function for the Ethernet header. 351 #[cfg(feature = "medium-ethernet")] 352 let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { 353 let mut frame = EthernetFrame::new_unchecked(tx_buffer); 354 355 let src_addr = self.hardware_addr.unwrap().ethernet_or_panic(); 356 frame.set_src_addr(src_addr); 357 frame.set_dst_addr(frag.ipv4.dst_hardware_addr); 358 359 match repr.version() { 360 #[cfg(feature = "proto-ipv4")] 361 IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4), 362 #[cfg(feature = "proto-ipv6")] 363 IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), 364 } 365 }; 366 367 tx_token.consume(tx_len, |mut tx_buffer| { 368 #[cfg(feature = "medium-ethernet")] 369 if matches!(self.caps.medium, Medium::Ethernet) { 370 emit_ethernet(&IpRepr::Ipv4(frag.ipv4.repr), tx_buffer); 371 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; 372 } 373 374 let mut packet = 375 Ipv4Packet::new_unchecked(&mut tx_buffer[..frag.ipv4.repr.buffer_len()]); 376 frag.ipv4.repr.emit(&mut packet, &caps.checksum); 377 packet.set_ident(frag.ipv4.ident); 378 packet.set_more_frags(more_frags); 379 packet.set_dont_frag(false); 380 packet.set_frag_offset(frag.ipv4.frag_offset); 381 382 if caps.checksum.ipv4.tx() { 383 packet.fill_checksum(); 384 } 385 386 tx_buffer[frag.ipv4.repr.buffer_len()..][..payload_len].copy_from_slice( 387 &frag.buffer[frag.ipv4.frag_offset as usize + frag.ipv4.repr.buffer_len()..] 388 [..payload_len], 389 ); 390 391 // Update the frag offset for the next fragment. 392 frag.ipv4.frag_offset += payload_len as u16; 393 }) 394 } 395 396 #[cfg(feature = "proto-igmp")] igmp_report_packet<'any>( &self, version: IgmpVersion, group_addr: Ipv4Address, ) -> Option<IpPacket<'any>>397 pub(super) fn igmp_report_packet<'any>( 398 &self, 399 version: IgmpVersion, 400 group_addr: Ipv4Address, 401 ) -> Option<IpPacket<'any>> { 402 let iface_addr = self.ipv4_addr()?; 403 let igmp_repr = IgmpRepr::MembershipReport { 404 group_addr, 405 version, 406 }; 407 let pkt = IpPacket::Igmp(( 408 Ipv4Repr { 409 src_addr: iface_addr, 410 // Send to the group being reported 411 dst_addr: group_addr, 412 next_header: IpProtocol::Igmp, 413 payload_len: igmp_repr.buffer_len(), 414 hop_limit: 1, 415 // [#183](https://github.com/m-labs/smoltcp/issues/183). 416 }, 417 igmp_repr, 418 )); 419 Some(pkt) 420 } 421 422 #[cfg(feature = "proto-igmp")] igmp_leave_packet<'any>( &self, group_addr: Ipv4Address, ) -> Option<IpPacket<'any>>423 pub(super) fn igmp_leave_packet<'any>( 424 &self, 425 group_addr: Ipv4Address, 426 ) -> Option<IpPacket<'any>> { 427 self.ipv4_addr().map(|iface_addr| { 428 let igmp_repr = IgmpRepr::LeaveGroup { group_addr }; 429 IpPacket::Igmp(( 430 Ipv4Repr { 431 src_addr: iface_addr, 432 dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS, 433 next_header: IpProtocol::Igmp, 434 payload_len: igmp_repr.buffer_len(), 435 hop_limit: 1, 436 }, 437 igmp_repr, 438 )) 439 }) 440 } 441 } 442