1 use super::check;
2 use super::icmp_reply_payload_len;
3 use super::InterfaceInner;
4 use super::IpPacket;
5 use super::SocketSet;
6 
7 #[cfg(feature = "socket-icmp")]
8 use crate::socket::icmp;
9 use crate::socket::AnySocket;
10 
11 use crate::wire::*;
12 
13 impl InterfaceInner {
14     #[cfg(feature = "proto-ipv6")]
process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, ipv6_packet: &Ipv6Packet<&'frame T>, ) -> Option<IpPacket<'frame>>15     pub(super) fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>(
16         &mut self,
17         sockets: &mut SocketSet,
18         ipv6_packet: &Ipv6Packet<&'frame T>,
19     ) -> Option<IpPacket<'frame>> {
20         let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet));
21 
22         if !ipv6_repr.src_addr.is_unicast() {
23             // Discard packets with non-unicast source addresses.
24             net_debug!("non-unicast source address");
25             return None;
26         }
27 
28         let ip_payload = ipv6_packet.payload();
29 
30         #[cfg(feature = "socket-raw")]
31         let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload);
32         #[cfg(not(feature = "socket-raw"))]
33         let handled_by_raw_socket = false;
34 
35         self.process_nxt_hdr(
36             sockets,
37             ipv6_repr,
38             ipv6_repr.next_header,
39             handled_by_raw_socket,
40             ip_payload,
41         )
42     }
43 
44     /// Given the next header value forward the payload onto the correct process
45     /// function.
46     #[cfg(feature = "proto-ipv6")]
process_nxt_hdr<'frame>( &mut self, sockets: &mut SocketSet, ipv6_repr: Ipv6Repr, nxt_hdr: IpProtocol, handled_by_raw_socket: bool, ip_payload: &'frame [u8], ) -> Option<IpPacket<'frame>>47     pub(super) fn process_nxt_hdr<'frame>(
48         &mut self,
49         sockets: &mut SocketSet,
50         ipv6_repr: Ipv6Repr,
51         nxt_hdr: IpProtocol,
52         handled_by_raw_socket: bool,
53         ip_payload: &'frame [u8],
54     ) -> Option<IpPacket<'frame>> {
55         match nxt_hdr {
56             IpProtocol::Icmpv6 => self.process_icmpv6(sockets, ipv6_repr.into(), ip_payload),
57 
58             #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
59             IpProtocol::Udp => {
60                 let udp_packet = check!(UdpPacket::new_checked(ip_payload));
61                 let udp_repr = check!(UdpRepr::parse(
62                     &udp_packet,
63                     &ipv6_repr.src_addr.into(),
64                     &ipv6_repr.dst_addr.into(),
65                     &self.checksum_caps(),
66                 ));
67 
68                 self.process_udp(
69                     sockets,
70                     ipv6_repr.into(),
71                     udp_repr,
72                     handled_by_raw_socket,
73                     udp_packet.payload(),
74                     ip_payload,
75                 )
76             }
77 
78             #[cfg(feature = "socket-tcp")]
79             IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload),
80 
81             IpProtocol::HopByHop => {
82                 self.process_hopbyhop(sockets, ipv6_repr, handled_by_raw_socket, ip_payload)
83             }
84 
85             #[cfg(feature = "socket-raw")]
86             _ if handled_by_raw_socket => None,
87 
88             _ => {
89                 // Send back as much of the original payload as we can.
90                 let payload_len =
91                     icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
92                 let icmp_reply_repr = Icmpv6Repr::ParamProblem {
93                     reason: Icmpv6ParamProblem::UnrecognizedNxtHdr,
94                     // The offending packet is after the IPv6 header.
95                     pointer: ipv6_repr.buffer_len() as u32,
96                     header: ipv6_repr,
97                     data: &ip_payload[0..payload_len],
98                 };
99                 self.icmpv6_reply(ipv6_repr, icmp_reply_repr)
100             }
101         }
102     }
103 
104     #[cfg(feature = "proto-ipv6")]
process_icmpv6<'frame>( &mut self, _sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], ) -> Option<IpPacket<'frame>>105     pub(super) fn process_icmpv6<'frame>(
106         &mut self,
107         _sockets: &mut SocketSet,
108         ip_repr: IpRepr,
109         ip_payload: &'frame [u8],
110     ) -> Option<IpPacket<'frame>> {
111         let icmp_packet = check!(Icmpv6Packet::new_checked(ip_payload));
112         let icmp_repr = check!(Icmpv6Repr::parse(
113             &ip_repr.src_addr(),
114             &ip_repr.dst_addr(),
115             &icmp_packet,
116             &self.caps.checksum,
117         ));
118 
119         #[cfg(feature = "socket-icmp")]
120         let mut handled_by_icmp_socket = false;
121 
122         #[cfg(all(feature = "socket-icmp", feature = "proto-ipv6"))]
123         for icmp_socket in _sockets
124             .items_mut()
125             .filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket))
126         {
127             if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) {
128                 icmp_socket.process(self, &ip_repr, &icmp_repr.into());
129                 handled_by_icmp_socket = true;
130             }
131         }
132 
133         match icmp_repr {
134             // Respond to echo requests.
135             Icmpv6Repr::EchoRequest {
136                 ident,
137                 seq_no,
138                 data,
139             } => match ip_repr {
140                 IpRepr::Ipv6(ipv6_repr) => {
141                     let icmp_reply_repr = Icmpv6Repr::EchoReply {
142                         ident,
143                         seq_no,
144                         data,
145                     };
146                     self.icmpv6_reply(ipv6_repr, icmp_reply_repr)
147                 }
148                 #[allow(unreachable_patterns)]
149                 _ => unreachable!(),
150             },
151 
152             // Ignore any echo replies.
153             Icmpv6Repr::EchoReply { .. } => None,
154 
155             // Forward any NDISC packets to the ndisc packet handler
156             #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
157             Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr {
158                 IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(ipv6_repr, repr),
159                 #[allow(unreachable_patterns)]
160                 _ => unreachable!(),
161             },
162 
163             // Don't report an error if a packet with unknown type
164             // has been handled by an ICMP socket
165             #[cfg(feature = "socket-icmp")]
166             _ if handled_by_icmp_socket => None,
167 
168             // FIXME: do something correct here?
169             _ => None,
170         }
171     }
172 
173     #[cfg(all(
174         any(feature = "medium-ethernet", feature = "medium-ieee802154"),
175         feature = "proto-ipv6"
176     ))]
process_ndisc<'frame>( &mut self, ip_repr: Ipv6Repr, repr: NdiscRepr<'frame>, ) -> Option<IpPacket<'frame>>177     pub(super) fn process_ndisc<'frame>(
178         &mut self,
179         ip_repr: Ipv6Repr,
180         repr: NdiscRepr<'frame>,
181     ) -> Option<IpPacket<'frame>> {
182         match repr {
183             NdiscRepr::NeighborAdvert {
184                 lladdr,
185                 target_addr,
186                 flags,
187             } => {
188                 let ip_addr = ip_repr.src_addr.into();
189                 if let Some(lladdr) = lladdr {
190                     let lladdr = check!(lladdr.parse(self.caps.medium));
191                     if !lladdr.is_unicast() || !target_addr.is_unicast() {
192                         return None;
193                     }
194                     if flags.contains(NdiscNeighborFlags::OVERRIDE)
195                         || !self
196                             .neighbor_cache
197                             .as_mut()
198                             .unwrap()
199                             .lookup(&ip_addr, self.now)
200                             .found()
201                     {
202                         self.neighbor_cache
203                             .as_mut()
204                             .unwrap()
205                             .fill(ip_addr, lladdr, self.now)
206                     }
207                 }
208                 None
209             }
210             NdiscRepr::NeighborSolicit {
211                 target_addr,
212                 lladdr,
213                 ..
214             } => {
215                 if let Some(lladdr) = lladdr {
216                     let lladdr = check!(lladdr.parse(self.caps.medium));
217                     if !lladdr.is_unicast() || !target_addr.is_unicast() {
218                         return None;
219                     }
220                     self.neighbor_cache.as_mut().unwrap().fill(
221                         ip_repr.src_addr.into(),
222                         lladdr,
223                         self.now,
224                     );
225                 }
226 
227                 if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) {
228                     let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
229                         flags: NdiscNeighborFlags::SOLICITED,
230                         target_addr,
231                         #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
232                         lladdr: Some(self.hardware_addr.unwrap().into()),
233                     });
234                     let ip_repr = Ipv6Repr {
235                         src_addr: target_addr,
236                         dst_addr: ip_repr.src_addr,
237                         next_header: IpProtocol::Icmpv6,
238                         hop_limit: 0xff,
239                         payload_len: advert.buffer_len(),
240                     };
241                     Some(IpPacket::Icmpv6((ip_repr, advert)))
242                 } else {
243                     None
244                 }
245             }
246             _ => None,
247         }
248     }
249 
250     #[cfg(feature = "proto-ipv6")]
process_hopbyhop<'frame>( &mut self, sockets: &mut SocketSet, ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool, ip_payload: &'frame [u8], ) -> Option<IpPacket<'frame>>251     pub(super) fn process_hopbyhop<'frame>(
252         &mut self,
253         sockets: &mut SocketSet,
254         ipv6_repr: Ipv6Repr,
255         handled_by_raw_socket: bool,
256         ip_payload: &'frame [u8],
257     ) -> Option<IpPacket<'frame>> {
258         let hbh_pkt = check!(Ipv6HopByHopHeader::new_checked(ip_payload));
259         let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_pkt));
260         for opt_repr in hbh_repr.options() {
261             let opt_repr = check!(opt_repr);
262             match opt_repr {
263                 Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (),
264                 Ipv6OptionRepr::Unknown { type_, .. } => {
265                     match Ipv6OptionFailureType::from(type_) {
266                         Ipv6OptionFailureType::Skip => (),
267                         Ipv6OptionFailureType::Discard => {
268                             return None;
269                         }
270                         _ => {
271                             // FIXME(dlrobertson): Send an ICMPv6 parameter problem message
272                             // here.
273                             return None;
274                         }
275                     }
276                 }
277             }
278         }
279         self.process_nxt_hdr(
280             sockets,
281             ipv6_repr,
282             hbh_repr.next_header,
283             handled_by_raw_socket,
284             &ip_payload[hbh_repr.buffer_len()..],
285         )
286     }
287 
288     #[cfg(feature = "proto-ipv6")]
icmpv6_reply<'frame, 'icmp: 'frame>( &self, ipv6_repr: Ipv6Repr, icmp_repr: Icmpv6Repr<'icmp>, ) -> Option<IpPacket<'frame>>289     pub(super) fn icmpv6_reply<'frame, 'icmp: 'frame>(
290         &self,
291         ipv6_repr: Ipv6Repr,
292         icmp_repr: Icmpv6Repr<'icmp>,
293     ) -> Option<IpPacket<'frame>> {
294         if ipv6_repr.dst_addr.is_unicast() {
295             let ipv6_reply_repr = Ipv6Repr {
296                 src_addr: ipv6_repr.dst_addr,
297                 dst_addr: ipv6_repr.src_addr,
298                 next_header: IpProtocol::Icmpv6,
299                 payload_len: icmp_repr.buffer_len(),
300                 hop_limit: 64,
301             };
302             Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr)))
303         } else {
304             // Do not send any ICMP replies to a broadcast destination address.
305             None
306         }
307     }
308 }
309