Skip to main content

openthread_fuchsia/backing/
udp.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::*;
6use fuchsia_async::net as fasync_net;
7use futures::never::Never;
8use openthread::ot::OtCastable;
9use openthread_sys::*;
10use std::ffi::c_void;
11use std::ptr::NonNull;
12use std::task::Poll;
13
14// TODO(https://fxbug.dev/42175223): At some point plumb this to OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT
15const DEFAULT_HOP_LIMIT: u8 = 64;
16
17pub(crate) fn poll_ot_udp_socket(
18    ot_udp_socket: &ot::UdpSocket<'_>,
19    instance: &ot::Instance,
20    cx: &mut std::task::Context<'_>,
21) -> Poll<Result<Never, anyhow::Error>> {
22    let socket = ot_udp_socket.get_async_udp_socket();
23    if let Some(socket) = socket {
24        let mut buffer = [0u8; crate::UDP_PACKET_MAX_LENGTH];
25        match socket.async_recv_from(&mut buffer, cx) {
26            Poll::Ready(Ok((len, sock_addr))) => {
27                let sock_addr = sock_addr.as_socket_ipv6().ok_or_else(|| {
28                    anyhow::format_err!("Expected IPv6 sockaddr, got something else")
29                })?;
30                let ot_sockaddr: ot::SockAddr = sock_addr.into();
31
32                debug!(
33                    tag = "udp";
34                    "otPlatUdp:{:?}: Incoming {} byte packet from {:?}",
35                    ot_udp_socket.as_ot_ptr(),
36                    len,
37                    ot_sockaddr
38                );
39
40                let mut message = ot::Message::udp_new(instance, None)?;
41                message.append(&buffer[..len])?;
42                let mut info = ot::message::Info::new(ot_udp_socket.sock_name(), ot_sockaddr);
43
44                if ot_udp_socket.get_netif_id() == ot::NetifIdentifier::Backbone {
45                    info.set_host_interface(true);
46                } else if ot_udp_socket.get_netif_id() == ot::NetifIdentifier::Thread {
47                    info.set_host_interface(false);
48                } else if let Some(host_iface) = unsafe {
49                    // SAFETY: This method (`poll`) is guaranteed to only be called from the same
50                    //         thread that OpenThread is being serviced on, which is ultimately
51                    //         the sole requirement for `PlatformBacking::as_ref()` to be
52                    //         considered safe.
53                    PlatformBacking::as_ref().lookup_netif_index(ot::NetifIdentifier::Backbone)
54                } {
55                    let scope_id = sock_addr.scope_id();
56                    debug!(
57                        tag = "udp";
58                        "inbound scope_id = {}, host_iface = {}", scope_id, host_iface
59                    );
60                    info.set_host_interface(scope_id == host_iface);
61                }
62
63                // TODO(https://fxbug.dev/42175223): Set hop count. Figure out how to get this info.
64                // TODO(https://fxbug.dev/42175223): Set ECN. Need to figure out how to get this info.
65                ot_udp_socket.handle_receive(&message, &info);
66            }
67            Poll::Ready(Err(err)) => {
68                return Poll::Ready(Err(err.into()));
69            }
70            Poll::Pending => {}
71        }
72    }
73    Poll::Pending
74}
75
76struct UdpSocketBacking {
77    socket: fasync_net::UdpSocket,
78    netif_id: ot::NetifIdentifier,
79}
80
81/// Returns true if this sockaddr needs a scope.
82fn dest_needs_scope(sockaddr: &std::net::SocketAddrV6) -> bool {
83    let dest_is_unicast_link_local = (sockaddr.ip().segments()[0] & 0xffc0) == 0xfe80;
84    let dest_is_multicast_link_local = sockaddr.ip().segments()[0] == 0xff02;
85    let dest_is_multicast_realm_local = sockaddr.ip().segments()[0] == 0xff03;
86
87    sockaddr.scope_id() == 0
88        && (dest_is_unicast_link_local
89            | dest_is_multicast_link_local
90            | dest_is_multicast_realm_local)
91}
92
93trait UdpSocketHelpers {
94    /// Gets a copy of the underlying ref-counted UdpSocketBacking.
95    fn get_async_udp_socket_backing(&self) -> Option<&UdpSocketBacking>;
96
97    /// Gets a copy of the underlying ref-counted UdpSocket.
98    fn get_async_udp_socket(&self) -> Option<&fasync_net::UdpSocket>;
99
100    /// Sets the UdpSocket.
101    fn set_async_udp_socket(&mut self, socket: fasync_net::UdpSocket);
102
103    /// Drops the underlying UDP socket
104    fn drop_async_udp_socket(&mut self);
105
106    fn get_netif_id(&self) -> ot::NetifIdentifier;
107    fn set_netif_id(&mut self, netif_id: ot::NetifIdentifier);
108
109    fn open(&mut self) -> ot::Result;
110    fn close(&mut self) -> ot::Result;
111    fn bind(&mut self) -> ot::Result;
112    fn bind_to_netif(&mut self, netif: ot::NetifIdentifier) -> ot::Result;
113    fn connect(&mut self) -> ot::Result;
114    fn send(
115        &mut self,
116        message: &'_ ot::Message<'_>,
117        message_info: &'_ ot::message::Info,
118    ) -> ot::Result;
119    fn join_mcast_group(&mut self, netif: ot::NetifIdentifier, addr: &ot::Ip6Address)
120    -> ot::Result;
121    fn leave_mcast_group(
122        &mut self,
123        netif: ot::NetifIdentifier,
124        addr: &ot::Ip6Address,
125    ) -> ot::Result;
126}
127
128impl UdpSocketHelpers for ot::UdpSocket<'_> {
129    fn get_async_udp_socket_backing(&self) -> Option<&UdpSocketBacking> {
130        self.get_handle().map(|handle| {
131            // SAFETY: The handle pointer always comes from the result from Box::leak(),
132            //         so it is safe to cast back into a reference.
133            unsafe { &*(handle.as_ptr() as *mut UdpSocketBacking) }
134        })
135    }
136
137    fn get_netif_id(&self) -> ot::NetifIdentifier {
138        self.get_async_udp_socket_backing()
139            .map(|x| x.netif_id)
140            .unwrap_or(ot::NetifIdentifier::Unspecified)
141    }
142
143    fn set_netif_id(&mut self, netif_id: ot::NetifIdentifier) {
144        self.get_handle()
145            .map(|handle| {
146                // SAFETY: The handle pointer always comes from the result from Box::leak(),
147                //         so it is safe to cast back into a reference.
148                unsafe { &mut *(handle.as_ptr() as *mut UdpSocketBacking) }
149            })
150            .unwrap()
151            .netif_id = netif_id;
152    }
153
154    fn get_async_udp_socket(&self) -> Option<&fasync_net::UdpSocket> {
155        self.get_async_udp_socket_backing().map(|x| &x.socket)
156    }
157
158    fn set_async_udp_socket(&mut self, socket: fasync_net::UdpSocket) {
159        assert!(self.get_handle().is_none());
160
161        let socket_backing =
162            UdpSocketBacking { socket, netif_id: ot::NetifIdentifier::Unspecified };
163
164        let boxed = Box::new(socket_backing);
165
166        // Get a reference to our socket while "leaking" the containing box, and
167        // then convert it to a pointer.
168        // We will reconstitute our box to free the memory in `drop_async_udp_socket()`.
169        let socket_ptr = Box::leak(boxed) as *mut UdpSocketBacking;
170
171        self.set_handle(Some(NonNull::new(socket_ptr as *mut c_void).unwrap()));
172    }
173
174    fn drop_async_udp_socket(&mut self) {
175        if let Some(handle) = self.get_handle() {
176            // Reconstitute our box from the pointer.
177            // SAFETY: The pointer we are passing into `Box::from_raw` came from `Box::leak`.
178            let boxed = unsafe {
179                Box::<UdpSocketBacking>::from_raw(handle.as_ptr() as *mut UdpSocketBacking)
180            };
181
182            // Explicitly drop the box for clarity.
183            std::mem::drop(boxed);
184
185            self.set_handle(None);
186        }
187    }
188
189    fn open(&mut self) -> ot::Result {
190        debug!(tag = "udp"; "otPlatUdp:{:?}: Opening", self.as_ot_ptr());
191
192        if self.get_handle().is_some() {
193            warn!(
194                tag = "udp";
195                "otPlatUdp:{:?}: Tried to open already open socket",
196                self.as_ot_ptr()
197            );
198            return Err(ot::Error::Already);
199        }
200
201        let socket = socket2::Socket::new(
202            socket2::Domain::IPV6,
203            socket2::Type::DGRAM,
204            Some(socket2::Protocol::UDP),
205        )
206        .map_err(|err| {
207            error!(tag = "udp"; "Error: {:?}", err);
208            Err(ot::Error::Failed)
209        })?;
210
211        let socket = fasync_net::UdpSocket::from_socket(socket.into()).map_err(|err| {
212            error!(tag = "udp"; "Error: {:?}", err);
213            Err(ot::Error::Failed)
214        })?;
215
216        self.set_async_udp_socket(socket);
217
218        Ok(())
219    }
220
221    fn close(&mut self) -> ot::Result {
222        debug!(tag = "udp"; "otPlatUdp:{:?}: Closing", self.as_ot_ptr());
223
224        if self.get_handle().is_none() {
225            warn!(
226                tag = "udp";
227                "otPlatUdp:{:?}: Tried to close already closed socket",
228                self.as_ot_ptr()
229            );
230            return Err(ot::Error::Already);
231        }
232
233        self.drop_async_udp_socket();
234
235        Ok(())
236    }
237
238    fn bind(&mut self) -> ot::Result {
239        if self.get_handle().is_none() {
240            warn!(tag = "udp"; "otPlatUdp:{:?}: Cannot bind, socket is closed.", self.as_ot_ptr());
241            return Err(ot::Error::InvalidState);
242        }
243
244        let mut sockaddr: std::net::SocketAddrV6 = self.sock_name().into();
245
246        // SAFETY: Must only be called from the same thread that OpenThread is running on.
247        //         This is guaranteed by the only caller of this method.
248        let platform_backing = unsafe { PlatformBacking::as_ref() };
249
250        if let Some(netif) = platform_backing.lookup_netif_index(self.get_netif_id()) {
251            sockaddr.set_scope_id(netif);
252        }
253
254        debug!(tag = "udp"; "otPlatUdp:{:?}: Bind to {}", self.as_ot_ptr(), sockaddr);
255
256        let socket = self.get_async_udp_socket().ok_or(ot::Error::Failed)?;
257        socket.as_ref().bind(&sockaddr.into()).map_err(move |err| {
258            error!(tag = "udp"; "Error: {:?}", err);
259            ot::Error::Failed
260        })?;
261
262        socket.as_ref().set_unicast_hops_v6(DEFAULT_HOP_LIMIT.into()).map_err(move |err| {
263            error!(tag = "udp"; "Error: {:?}", err);
264            ot::Error::Failed
265        })?;
266        socket.as_ref().set_multicast_hops_v6(DEFAULT_HOP_LIMIT.into()).map_err(move |err| {
267            error!(tag = "udp"; "Error: {:?}", err);
268            ot::Error::Failed
269        })?;
270
271        Ok(())
272    }
273
274    fn bind_to_netif(&mut self, net_if_id: ot::NetifIdentifier) -> ot::Result {
275        if self.get_handle().is_none() {
276            warn!(
277                tag = "udp";
278                "otPlatUdp:{:?}: Cannot bind_to_netif, socket is closed.",
279                self.as_ot_ptr()
280            );
281            return Err(ot::Error::InvalidState);
282        }
283
284        debug!(tag = "udp"; "otPlatUdp:{:?}: Bind to netif={:?}", self.as_ot_ptr(), net_if_id);
285
286        self.set_netif_id(net_if_id);
287
288        Ok(())
289    }
290
291    fn connect(&mut self) -> ot::Result {
292        if self.get_handle().is_none() {
293            warn!(
294                tag = "udp";
295                "otPlatUdp:{:?}: Cannot connect, socket is closed.",
296                self.as_ot_ptr()
297            );
298            return Err(ot::Error::InvalidState);
299        }
300
301        debug!(tag = "udp"; "otPlatUdp:{:?}: Connect to {:?}", self.as_ot_ptr(), self.peer_name());
302
303        // TODO(https://fxbug.dev/42175223): Investigate implications of leaving this unimplemented.
304        //                        It's not entirely clear why we have this call to connect
305        //                        when we always specify a destination for `send`.
306
307        Ok(())
308    }
309
310    fn send(&mut self, message: &ot::Message<'_>, info: &'_ ot::message::Info) -> ot::Result {
311        if self.get_handle().is_none() {
312            warn!(tag = "udp"; "otPlatUdp:{:?}: Cannot send, socket is closed.", self.as_ot_ptr());
313            return Err(ot::Error::InvalidState);
314        }
315
316        let data = message.to_vec();
317
318        debug!(
319            tag = "udp";
320            "otPlatUdp:{:?}: Sending {} byte packet to {:?}. {:?}",
321            self.as_ot_ptr(),
322            data.len(),
323            info.peer_name(),
324            info
325        );
326
327        let socket = self.get_async_udp_socket().ok_or(ot::Error::Failed)?;
328
329        // Set the multicast loop flag.
330        if info.multicast_loop() {
331            socket.as_ref().set_multicast_loop_v6(true).map_err(move |err| {
332                error!(tag = "udp"; "Error: {:?}", err);
333                ot::Error::Failed
334            })?;
335        }
336
337        let should_set_hop_limit = info.hop_limit() > 0 || info.allow_zero_hop_limit();
338
339        if should_set_hop_limit {
340            socket.as_ref().set_unicast_hops_v6(info.hop_limit().into()).map_err(move |err| {
341                error!(tag = "udp"; "Error: {:?}", err);
342                ot::Error::Failed
343            })?;
344            socket.as_ref().set_multicast_hops_v6(info.hop_limit().into()).map_err(move |err| {
345                error!(tag = "udp"; "Error: {:?}", err);
346                ot::Error::Failed
347            })?;
348        }
349
350        let mut sockaddr: std::net::SocketAddrV6 = info.peer_name().into();
351
352        if self.get_netif_id() == ot::NetifIdentifier::Unspecified && dest_needs_scope(&sockaddr) {
353            let netif_id = if info.is_host_interface() {
354                ot::NetifIdentifier::Backbone
355            } else {
356                ot::NetifIdentifier::Thread
357            };
358
359            // SAFETY: Must only be called from the same thread that OpenThread is running on.
360            //         This is guaranteed by the only caller of this method.
361            let platform_backing = unsafe { PlatformBacking::as_ref() };
362
363            let netif: ot::NetifIndex = platform_backing
364                .lookup_netif_index(netif_id)
365                .unwrap_or(ot::NETIF_INDEX_UNSPECIFIED);
366            sockaddr.set_scope_id(netif);
367        }
368
369        let ret = match socket.as_ref().send_to(&data, &sockaddr.into()) {
370            Ok(sent) if data.len() == sent => Ok(()),
371            Ok(sent) => {
372                warn!(
373                    tag = "udp";
374                    "otPlatUdpSend:{:?}: send_to did not send whole packet, only sent {} bytes",
375                    self.as_ot_ptr(),
376                    sent
377                );
378                Err(ot::Error::Failed)
379            }
380            Err(err) => {
381                warn!(
382                    tag = "udp";
383                    "otPlatUdpSend:{:?}: send_to({:?}) failed: {:?}",
384                    self.as_ot_ptr(),
385                    sockaddr,
386                    err
387                );
388                Err(ot::Error::Failed)
389            }
390        };
391
392        // Restore hop limit
393        if should_set_hop_limit {
394            socket.as_ref().set_unicast_hops_v6(DEFAULT_HOP_LIMIT.into()).map_err(move |err| {
395                error!(tag = "udp"; "Error: {:?}", err);
396                ot::Error::Failed
397            })?;
398            socket.as_ref().set_multicast_hops_v6(DEFAULT_HOP_LIMIT.into()).map_err(
399                move |err| {
400                    error!(tag = "udp"; "Error: {:?}", err);
401                    ot::Error::Failed
402                },
403            )?;
404        }
405
406        // Reset the multicast loop flag.
407        if info.multicast_loop() {
408            socket.as_ref().set_multicast_loop_v6(false).map_err(move |err| {
409                error!(tag = "udp"; "Error: {:?}", err);
410                ot::Error::Failed
411            })?;
412        }
413
414        ret
415    }
416
417    fn join_mcast_group(
418        &mut self,
419        netif: ot::NetifIdentifier,
420        addr: &ot::Ip6Address,
421    ) -> ot::Result {
422        if self.get_handle().is_none() {
423            warn!(
424                tag = "udp";
425                "otPlatUdp:{:?}: Cannot join_mcast_group, socket is closed.",
426                self.as_ot_ptr()
427            );
428            return Err(ot::Error::InvalidState);
429        }
430
431        debug!(
432            tag = "udp";
433            "otPlatUdp:{:?}: JoinMulticastGroup {:?} on netif {:?}",
434            self.as_ot_ptr(),
435            addr,
436            netif
437        );
438
439        let socket = self.get_async_udp_socket().ok_or(ot::Error::Failed)?;
440
441        // SAFETY: Must only be called from the same thread that OpenThread is running on.
442        //         This is guaranteed by the only caller of this method.
443        let platform_backing = unsafe { PlatformBacking::as_ref() };
444        let netif: ot::NetifIndex =
445            platform_backing.lookup_netif_index(netif).unwrap_or(ot::NETIF_INDEX_UNSPECIFIED);
446
447        match socket.as_ref().join_multicast_v6(addr, netif) {
448            Ok(()) => Ok(()),
449            Err(err) if err.kind() == std::io::ErrorKind::AddrInUse => Ok(()),
450            Err(err) => {
451                error!(
452                    tag = "udp";
453                    "otPlatUdp:{:?}: Error joining multicast group {addr}%{netif}: {err:?}",
454                    self.as_ot_ptr()
455                );
456                Err(ot::Error::Failed)
457            }
458        }
459    }
460
461    fn leave_mcast_group(
462        &mut self,
463        netif: ot::NetifIdentifier,
464        addr: &ot::Ip6Address,
465    ) -> ot::Result {
466        if self.get_handle().is_none() {
467            warn!(
468                tag = "udp";
469                "otPlatUdp:{:?}: Cannot leave_mcast_group, socket is closed.",
470                self.as_ot_ptr()
471            );
472            return Err(ot::Error::InvalidState);
473        }
474
475        debug!(
476            tag = "udp";
477            "otPlatUdp:{:?}: LeaveMulticastGroup {:?} on netif {:?}",
478            self.as_ot_ptr(),
479            addr,
480            netif
481        );
482        let socket = self.get_async_udp_socket().ok_or(ot::Error::Failed)?;
483
484        // SAFETY: Must only be called from the same thread that OpenThread is running on.
485        //         This is guaranteed by the only caller of this method.
486        let platform_backing = unsafe { PlatformBacking::as_ref() };
487        let netif: ot::NetifIndex =
488            platform_backing.lookup_netif_index(netif).unwrap_or(ot::NETIF_INDEX_UNSPECIFIED);
489
490        match socket.as_ref().leave_multicast_v6(addr, netif) {
491            Ok(()) => Ok(()),
492            Err(err) if err.kind() == std::io::ErrorKind::AddrInUse => Ok(()),
493            Err(err) => {
494                error!(
495                    tag = "udp";
496                    "otPlatUdp:{:?}: Error leaving multicast group {addr}%{netif}: {err:?}",
497                    self.as_ot_ptr()
498                );
499                Err(ot::Error::Failed)
500            }
501        }
502    }
503}
504
505#[unsafe(no_mangle)]
506unsafe extern "C" fn otPlatUdpSocket(ot_socket_ptr: *mut otUdpSocket) -> otError {
507    unsafe { ot::UdpSocket::mut_from_ot_mut_ptr(ot_socket_ptr) }.unwrap().open().into_ot_error()
508}
509
510#[unsafe(no_mangle)]
511unsafe extern "C" fn otPlatUdpClose(ot_socket_ptr: *mut otUdpSocket) -> otError {
512    unsafe { ot::UdpSocket::mut_from_ot_mut_ptr(ot_socket_ptr) }.unwrap().close().into_ot_error()
513}
514
515#[unsafe(no_mangle)]
516unsafe extern "C" fn otPlatUdpBind(ot_socket_ptr: *mut otUdpSocket) -> otError {
517    unsafe { ot::UdpSocket::mut_from_ot_mut_ptr(ot_socket_ptr) }.unwrap().bind().into_ot_error()
518}
519
520#[unsafe(no_mangle)]
521unsafe extern "C" fn otPlatUdpBindToNetif(
522    ot_socket_ptr: *mut otUdpSocket,
523    net_if_id: otNetifIdentifier,
524) -> otError {
525    unsafe { ot::UdpSocket::mut_from_ot_mut_ptr(ot_socket_ptr) }
526        .unwrap()
527        .bind_to_netif(ot::NetifIdentifier::from(net_if_id))
528        .into_ot_error()
529}
530
531#[unsafe(no_mangle)]
532unsafe extern "C" fn otPlatUdpConnect(ot_socket_ptr: *mut otUdpSocket) -> otError {
533    unsafe { ot::UdpSocket::mut_from_ot_mut_ptr(ot_socket_ptr) }.unwrap().connect().into_ot_error()
534}
535
536#[unsafe(no_mangle)]
537unsafe extern "C" fn otPlatUdpSend(
538    ot_socket_ptr: *mut otUdpSocket,
539    message: *mut otMessage,
540    message_info: *const otMessageInfo,
541) -> otError {
542    unsafe { ot::UdpSocket::mut_from_ot_mut_ptr(ot_socket_ptr) }
543        .unwrap()
544        .send(
545            unsafe { ot::Message::ref_from_ot_ptr(message) }.unwrap(),
546            unsafe { ot::message::Info::ref_from_ot_ptr(message_info) }.unwrap(),
547        )
548        .map(move |_| unsafe { otMessageFree(message) }) // Only free on success
549        .into_ot_error()
550}
551
552#[unsafe(no_mangle)]
553unsafe extern "C" fn otPlatUdpJoinMulticastGroup(
554    ot_socket_ptr: *mut otUdpSocket,
555    net_if_id: otNetifIdentifier,
556    addr: *const otIp6Address,
557) -> otError {
558    unsafe { ot::UdpSocket::mut_from_ot_mut_ptr(ot_socket_ptr) }
559        .unwrap()
560        .join_mcast_group(
561            net_if_id.into(),
562            unsafe { ot::Ip6Address::ref_from_ot_ptr(addr) }.unwrap(),
563        )
564        .into_ot_error()
565}
566
567#[unsafe(no_mangle)]
568unsafe extern "C" fn otPlatUdpLeaveMulticastGroup(
569    ot_socket_ptr: *mut otUdpSocket,
570    net_if_id: otNetifIdentifier,
571    addr: *const otIp6Address,
572) -> otError {
573    unsafe { ot::UdpSocket::mut_from_ot_mut_ptr(ot_socket_ptr) }
574        .unwrap()
575        .leave_mcast_group(
576            net_if_id.into(),
577            unsafe { ot::Ip6Address::ref_from_ot_ptr(addr) }.unwrap(),
578        )
579        .into_ot_error()
580}
581
582#[cfg(test)]
583mod test {
584    use super::*;
585    use std::net::{Ipv6Addr, SocketAddrV6};
586
587    #[test]
588    fn test_dest_needs_scope() {
589        assert!(!dest_needs_scope(&SocketAddrV6::new(
590            Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
591            8080,
592            0,
593            0
594        )));
595        assert!(!dest_needs_scope(&SocketAddrV6::new(
596            Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
597            8080,
598            0,
599            1
600        )));
601
602        assert!(dest_needs_scope(&SocketAddrV6::new(
603            Ipv6Addr::new(0xfe80, 0xdb8, 0, 0, 0, 0, 0, 1),
604            8080,
605            0,
606            0
607        )));
608        assert!(!dest_needs_scope(&SocketAddrV6::new(
609            Ipv6Addr::new(0xfe80, 0xdb8, 0, 0, 0, 0, 0, 1),
610            8080,
611            0,
612            1
613        )));
614
615        assert!(!dest_needs_scope(&SocketAddrV6::new(
616            Ipv6Addr::new(0xff05, 0xdb8, 0, 0, 0, 0, 0, 1),
617            8080,
618            0,
619            0
620        )));
621        assert!(!dest_needs_scope(&SocketAddrV6::new(
622            Ipv6Addr::new(0xff05, 0xdb8, 0, 0, 0, 0, 0, 1),
623            8080,
624            0,
625            1
626        )));
627
628        assert!(dest_needs_scope(&SocketAddrV6::new(
629            Ipv6Addr::new(0xff03, 0xdb8, 0, 0, 0, 0, 0, 1),
630            8080,
631            0,
632            0
633        )));
634        assert!(!dest_needs_scope(&SocketAddrV6::new(
635            Ipv6Addr::new(0xff03, 0xdb8, 0, 0, 0, 0, 0, 1),
636            8080,
637            0,
638            1
639        )));
640
641        assert!(dest_needs_scope(&SocketAddrV6::new(
642            Ipv6Addr::new(0xff02, 0xdb8, 0, 0, 0, 0, 0, 1),
643            8080,
644            0,
645            0
646        )));
647        assert!(!dest_needs_scope(&SocketAddrV6::new(
648            Ipv6Addr::new(0xff02, 0xdb8, 0, 0, 0, 0, 0, 1),
649            8080,
650            0,
651            1
652        )));
653
654        assert!(!dest_needs_scope(&SocketAddrV6::new(
655            Ipv6Addr::new(0xff01, 0xdb8, 0, 0, 0, 0, 0, 1),
656            8080,
657            0,
658            0
659        )));
660        assert!(!dest_needs_scope(&SocketAddrV6::new(
661            Ipv6Addr::new(0xff01, 0xdb8, 0, 0, 0, 0, 0, 1),
662            8080,
663            0,
664            1
665        )));
666    }
667}