dhcpv6_client/
client.rs

1// Copyright 2020 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
5//! Implements a DHCPv6 client.
6use std::collections::hash_map::DefaultHasher;
7use std::collections::{HashMap, HashSet};
8use std::hash::{Hash, Hasher};
9use std::net::{IpAddr, SocketAddr};
10use std::ops::Add;
11use std::pin::Pin;
12use std::str::FromStr as _;
13use std::time::Duration;
14
15use fidl::endpoints::{ControlHandle as _, ServerEnd};
16use fidl_fuchsia_net_dhcpv6::{
17    ClientMarker, ClientRequest, ClientRequestStream, ClientWatchAddressResponder,
18    ClientWatchPrefixesResponder, ClientWatchServersResponder, Duid, Empty, Lifetimes,
19    LinkLayerAddress, LinkLayerAddressPlusTime, Prefix, PrefixDelegationConfig,
20    RELAY_AGENT_AND_SERVER_LINK_LOCAL_MULTICAST_ADDRESS, RELAY_AGENT_AND_SERVER_PORT,
21};
22use fidl_fuchsia_net_dhcpv6_ext::{
23    AddressConfig, ClientConfig, InformationConfig, NewClientParams,
24};
25use futures::{Future, FutureExt as _, StreamExt as _, TryStreamExt as _, select, stream};
26use {
27    fidl_fuchsia_net as fnet, fidl_fuchsia_net_ext as fnet_ext, fidl_fuchsia_net_name as fnet_name,
28    fuchsia_async as fasync,
29};
30
31use anyhow::{Context as _, Result};
32use assert_matches::assert_matches;
33use byteorder::{NetworkEndian, WriteBytesExt as _};
34use dns_server_watcher::DEFAULT_DNS_PORT;
35use log::{debug, error, warn};
36use net_types::MulticastAddress as _;
37use net_types::ip::{Ip as _, Ipv6, Ipv6Addr, Subnet, SubnetError};
38use packet::ParsablePacket;
39use packet_formats_dhcp::v6;
40use rand::SeedableRng;
41use rand::rngs::StdRng;
42
43/// A thin wrapper around `zx::MonotonicInstant` that implements `dhcpv6_core::Instant`.
44#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
45pub(crate) struct MonotonicInstant(zx::MonotonicInstant);
46
47impl MonotonicInstant {
48    fn now() -> MonotonicInstant {
49        MonotonicInstant(zx::MonotonicInstant::get())
50    }
51}
52
53impl dhcpv6_core::Instant for MonotonicInstant {
54    fn duration_since(&self, MonotonicInstant(earlier): MonotonicInstant) -> Duration {
55        let Self(this) = *self;
56
57        let diff: zx::MonotonicDuration = this - earlier;
58
59        Duration::from_nanos(diff.into_nanos().try_into().unwrap_or_else(|e| {
60            panic!(
61                "failed to calculate duration since {:?} with instant {:?}: {}",
62                earlier, this, e,
63            )
64        }))
65    }
66
67    fn checked_add(&self, duration: Duration) -> Option<MonotonicInstant> {
68        Some(self.add(duration))
69    }
70}
71
72impl Add<Duration> for MonotonicInstant {
73    type Output = MonotonicInstant;
74
75    fn add(self, duration: Duration) -> MonotonicInstant {
76        let MonotonicInstant(this) = self;
77        MonotonicInstant(this + duration.into())
78    }
79}
80
81#[derive(Debug, thiserror::Error)]
82pub enum ClientError {
83    #[error("fidl error")]
84    Fidl(#[source] fidl::Error),
85    #[error("got watch request while the previous one is pending")]
86    DoubleWatch,
87    #[error("unsupported DHCPv6 configuration")]
88    UnsupportedConfigs,
89    #[error("socket create error")]
90    SocketCreate(std::io::Error),
91    #[error("socket receive error")]
92    SocketRecv(std::io::Error),
93    #[error("unimplemented DHCPv6 functionality: {:?}()", _0)]
94    Unimplemented(String),
95}
96
97/// Theoretical size limit for UDP datagrams.
98///
99/// NOTE: This does not take [jumbograms](https://tools.ietf.org/html/rfc2675) into account.
100const MAX_UDP_DATAGRAM_SIZE: usize = 65_535;
101
102#[pin_project::pin_project]
103struct Timers {
104    #[pin]
105    retransmission: fasync::Timer,
106    #[pin]
107    refresh: fasync::Timer,
108    #[pin]
109    renew: fasync::Timer,
110    #[pin]
111    rebind: fasync::Timer,
112    #[pin]
113    restart_server_discovery: fasync::Timer,
114
115    #[cfg(test)]
116    scheduled: HashSet<dhcpv6_core::client::ClientTimerType>,
117}
118
119impl Default for Timers {
120    fn default() -> Self {
121        let unscheduled = || fasync::Timer::new(fasync::MonotonicInstant::INFINITE);
122        Self {
123            retransmission: unscheduled(),
124            refresh: unscheduled(),
125            renew: unscheduled(),
126            rebind: unscheduled(),
127            restart_server_discovery: unscheduled(),
128            #[cfg(test)]
129            scheduled: Default::default(),
130        }
131    }
132}
133
134/// A DHCPv6 client.
135pub(crate) struct Client<S: for<'a> AsyncSocket<'a>> {
136    /// The interface the client is running on.
137    interface_id: u64,
138    /// Stores the hash of the last observed version of DNS servers by a watcher.
139    ///
140    /// The client uses this hash to determine whether new changes in DNS servers are observed and
141    /// updates should be replied to the watcher.
142    last_observed_dns_hash: u64,
143    /// Stores a responder to send DNS server updates.
144    dns_responder: Option<ClientWatchServersResponder>,
145    /// Stores a responder to send acquired addresses.
146    address_responder: Option<ClientWatchAddressResponder>,
147    /// Holds the discovered prefixes and their lifetimes.
148    prefixes: HashMap<fnet::Ipv6AddressWithPrefix, Lifetimes>,
149    /// Indicates whether or not the prefixes has changed since last yielded.
150    prefixes_changed: bool,
151    /// Stores a responder to send acquired prefixes.
152    prefixes_responder: Option<ClientWatchPrefixesResponder>,
153    /// Maintains the state for the client.
154    state_machine: dhcpv6_core::client::ClientStateMachine<MonotonicInstant, StdRng>,
155    /// The socket used to communicate with DHCPv6 servers.
156    socket: S,
157    /// The address to send outgoing messages to.
158    server_addr: SocketAddr,
159    /// All timers.
160    timers: Pin<Box<Timers>>,
161    /// A stream of FIDL requests to this client.
162    request_stream: ClientRequestStream,
163}
164
165/// A trait that allows stubbing [`fuchsia_async::net::UdpSocket`] in tests.
166pub(crate) trait AsyncSocket<'a> {
167    type RecvFromFut: Future<Output = Result<(usize, SocketAddr), std::io::Error>> + 'a;
168    type SendToFut: Future<Output = Result<usize, std::io::Error>> + 'a;
169
170    fn recv_from(&'a self, buf: &'a mut [u8]) -> Self::RecvFromFut;
171    fn send_to(&'a self, buf: &'a [u8], addr: SocketAddr) -> Self::SendToFut;
172}
173
174impl<'a> AsyncSocket<'a> for fasync::net::UdpSocket {
175    type RecvFromFut = fasync::net::UdpRecvFrom<'a>;
176    type SendToFut = fasync::net::SendTo<'a>;
177
178    fn recv_from(&'a self, buf: &'a mut [u8]) -> Self::RecvFromFut {
179        self.recv_from(buf)
180    }
181    fn send_to(&'a self, buf: &'a [u8], addr: SocketAddr) -> Self::SendToFut {
182        self.send_to(buf, addr)
183    }
184}
185
186/// Converts `InformationConfig` to a collection of `v6::OptionCode`.
187fn to_dhcpv6_option_codes(
188    InformationConfig { dns_servers }: InformationConfig,
189) -> Vec<v6::OptionCode> {
190    dns_servers.then_some(v6::OptionCode::DnsServers).into_iter().collect()
191}
192
193fn to_configured_addresses(
194    AddressConfig { address_count, preferred_addresses }: AddressConfig,
195) -> Result<HashMap<v6::IAID, HashSet<Ipv6Addr>>, ClientError> {
196    let preferred_addresses = preferred_addresses.unwrap_or(Vec::new());
197    if preferred_addresses.len() > address_count.into() {
198        return Err(ClientError::UnsupportedConfigs);
199    }
200
201    // TODO(https://fxbug.dev/42157844): make IAID consistent across
202    // configurations.
203    Ok((0..)
204        .map(v6::IAID::new)
205        .zip(
206            preferred_addresses
207                .into_iter()
208                .map(|fnet::Ipv6Address { addr, .. }| HashSet::from([Ipv6Addr::from(addr)]))
209                .chain(std::iter::repeat_with(HashSet::new)),
210        )
211        .take(address_count.into())
212        .collect())
213}
214
215// The client only supports a single IA_PD.
216//
217// TODO(https://fxbug.dev/42065403): Support multiple IA_PDs.
218const IA_PD_IAID: v6::IAID = v6::IAID::new(0);
219
220/// Creates a state machine for the input client config.
221fn create_state_machine(
222    duid: Option<dhcpv6_core::ClientDuid>,
223    transaction_id: [u8; 3],
224    ClientConfig {
225        information_config,
226        non_temporary_address_config,
227        prefix_delegation_config,
228    }: ClientConfig,
229) -> Result<
230    (
231        dhcpv6_core::client::ClientStateMachine<MonotonicInstant, StdRng>,
232        dhcpv6_core::client::Actions<MonotonicInstant>,
233    ),
234    ClientError,
235> {
236    let information_option_codes = to_dhcpv6_option_codes(information_config);
237    let configured_non_temporary_addresses = to_configured_addresses(non_temporary_address_config)?;
238    let configured_delegated_prefixes = prefix_delegation_config
239        .map(|prefix_delegation_config| {
240            let prefix = match prefix_delegation_config {
241                PrefixDelegationConfig::Empty(Empty {}) => Ok(None),
242                PrefixDelegationConfig::PrefixLength(prefix_len) => {
243                    if prefix_len == 0 {
244                        // Should have used `PrefixDelegationConfig::Empty`.
245                        return Err(ClientError::UnsupportedConfigs);
246                    }
247
248                    Subnet::new(Ipv6::UNSPECIFIED_ADDRESS, prefix_len).map(Some)
249                }
250                PrefixDelegationConfig::Prefix(fnet::Ipv6AddressWithPrefix {
251                    addr: fnet::Ipv6Address { addr, .. },
252                    prefix_len,
253                }) => {
254                    let addr = Ipv6Addr::from_bytes(addr);
255                    if addr == Ipv6::UNSPECIFIED_ADDRESS {
256                        // Should have used `PrefixDelegationConfig::PrefixLength`.
257                        return Err(ClientError::UnsupportedConfigs);
258                    }
259
260                    Subnet::new(addr, prefix_len).map(Some)
261                }
262            };
263
264            match prefix {
265                Ok(o) => Ok(HashMap::from([(IA_PD_IAID, HashSet::from_iter(o.into_iter()))])),
266                Err(SubnetError::PrefixTooLong | SubnetError::HostBitsSet) => {
267                    Err(ClientError::UnsupportedConfigs)
268                }
269            }
270        })
271        .transpose()?;
272
273    let now = MonotonicInstant::now();
274    match (
275        information_option_codes.is_empty(),
276        configured_non_temporary_addresses.is_empty(),
277        configured_delegated_prefixes,
278    ) {
279        (true, true, None) => Err(ClientError::UnsupportedConfigs),
280        (false, true, None) => {
281            if duid.is_some() {
282                Err(ClientError::UnsupportedConfigs)
283            } else {
284                Ok(dhcpv6_core::client::ClientStateMachine::start_stateless(
285                    transaction_id,
286                    information_option_codes,
287                    StdRng::from_os_rng(),
288                    now,
289                ))
290            }
291        }
292        (
293            _request_information,
294            _configure_non_temporary_addresses,
295            configured_delegated_prefixes,
296        ) => Ok(dhcpv6_core::client::ClientStateMachine::start_stateful(
297            transaction_id,
298            if let Some(duid) = duid {
299                duid
300            } else {
301                return Err(ClientError::UnsupportedConfigs);
302            },
303            configured_non_temporary_addresses,
304            configured_delegated_prefixes.unwrap_or_else(Default::default),
305            information_option_codes,
306            StdRng::from_os_rng(),
307            now,
308        )),
309    }
310}
311
312/// Calculates a hash for the input.
313fn hash<H: Hash>(h: &H) -> u64 {
314    let mut dh = DefaultHasher::new();
315    let () = h.hash(&mut dh);
316    dh.finish()
317}
318
319fn subnet_to_address_with_prefix(prefix: Subnet<Ipv6Addr>) -> fnet::Ipv6AddressWithPrefix {
320    fnet::Ipv6AddressWithPrefix {
321        addr: fnet::Ipv6Address { addr: prefix.network().ipv6_bytes() },
322        prefix_len: prefix.prefix(),
323    }
324}
325
326impl<S: for<'a> AsyncSocket<'a>> Client<S> {
327    /// Starts the client in `config`.
328    ///
329    /// Input `transaction_id` is used to label outgoing messages and match incoming ones.
330    pub(crate) async fn start(
331        duid: Option<dhcpv6_core::ClientDuid>,
332        transaction_id: [u8; 3],
333        config: ClientConfig,
334        interface_id: u64,
335        socket_fn: impl FnOnce() -> std::io::Result<S>,
336        server_addr: SocketAddr,
337        request_stream: ClientRequestStream,
338    ) -> Result<Self, ClientError> {
339        let (state_machine, actions) = create_state_machine(duid, transaction_id, config)?;
340        let mut client = Self {
341            state_machine,
342            interface_id,
343            socket: socket_fn().map_err(ClientError::SocketCreate)?,
344            server_addr,
345            request_stream,
346            // Server watcher's API requires blocking iff the first call would return an empty list,
347            // so initialize this field with a hash of an empty list.
348            last_observed_dns_hash: hash(&Vec::<Ipv6Addr>::new()),
349            dns_responder: None,
350            address_responder: None,
351            prefixes: Default::default(),
352            prefixes_changed: false,
353            prefixes_responder: None,
354            timers: Box::pin(Default::default()),
355        };
356        let () = client.run_actions(actions).await?;
357        Ok(client)
358    }
359
360    /// Runs a list of actions sequentially.
361    async fn run_actions(
362        &mut self,
363        actions: dhcpv6_core::client::Actions<MonotonicInstant>,
364    ) -> Result<(), ClientError> {
365        stream::iter(actions)
366            .map(Ok)
367            .try_fold(self, |client, action| async move {
368                match action {
369                    dhcpv6_core::client::Action::SendMessage(buf) => {
370                        let () = match client.socket.send_to(&buf, client.server_addr).await {
371                            Ok(size) => assert_eq!(size, buf.len()),
372                            Err(e) => warn!(
373                                "failed to send message to {}: {}; will retransmit later",
374                                client.server_addr, e
375                            ),
376                        };
377                    }
378                    dhcpv6_core::client::Action::ScheduleTimer(timer_type, timeout) => {
379                        client.schedule_timer(timer_type, timeout)
380                    }
381                    dhcpv6_core::client::Action::CancelTimer(timer_type) => {
382                        client.cancel_timer(timer_type)
383                    }
384                    dhcpv6_core::client::Action::UpdateDnsServers(servers) => {
385                        let () = client.maybe_send_dns_server_updates(servers)?;
386                    }
387                    dhcpv6_core::client::Action::IaNaUpdates(_) => {
388                        // TODO(https://fxbug.dev/42178828): add actions to
389                        // (re)schedule preferred and valid lifetime timers.
390                        // TODO(https://fxbug.dev/42178817): Add
391                        // action to remove the previous address.
392                        // TODO(https://fxbug.dev/42177252): Add action to add
393                        // the new address and cancel timers for old address.
394                    }
395                    dhcpv6_core::client::Action::IaPdUpdates(mut updates) => {
396                        let updates = {
397                            let ret =
398                                updates.remove(&IA_PD_IAID).expect("Update missing for IAID");
399                            debug_assert_eq!(updates, HashMap::new());
400                            ret
401                        };
402
403                        let Self { prefixes, prefixes_changed, .. } = client;
404
405                        let now = zx::MonotonicInstant::get();
406                        let nonzero_timevalue_to_zx_time = |tv| match tv {
407                            v6::NonZeroTimeValue::Finite(tv) => {
408                                now + zx::MonotonicDuration::from_seconds(tv.get().into())
409                            }
410                            v6::NonZeroTimeValue::Infinity => zx::MonotonicInstant::INFINITE,
411                        };
412
413                        let calculate_lifetimes = |dhcpv6_core::client::Lifetimes {
414                            preferred_lifetime,
415                            valid_lifetime,
416                        }| {
417                            Lifetimes {
418                                preferred_until: match preferred_lifetime {
419                                    v6::TimeValue::Zero => zx::MonotonicInstant::ZERO,
420                                    v6::TimeValue::NonZero(preferred_lifetime) => {
421                                        nonzero_timevalue_to_zx_time(preferred_lifetime)
422                                    },
423                                }.into_nanos(),
424                                valid_until: nonzero_timevalue_to_zx_time(valid_lifetime)
425                                    .into_nanos(),
426                            }
427                        };
428
429                        for (prefix, update) in updates.into_iter() {
430                            let fidl_prefix = subnet_to_address_with_prefix(prefix);
431
432                            match update {
433                                dhcpv6_core::client::IaValueUpdateKind::Added(lifetimes) => {
434                                    assert_matches!(
435                                        prefixes.insert(
436                                            fidl_prefix,
437                                            calculate_lifetimes(lifetimes)
438                                        ),
439                                        None,
440                                        "must not know about prefix {} to add it with lifetimes {:?}",
441                                        prefix, lifetimes,
442                                    );
443                                }
444                                dhcpv6_core::client::IaValueUpdateKind::UpdatedLifetimes(updated_lifetimes) => {
445                                    assert_matches!(
446                                        prefixes.get_mut(&fidl_prefix),
447                                        Some(lifetimes) => {
448                                            *lifetimes = calculate_lifetimes(updated_lifetimes);
449                                        },
450                                        "must know about prefix {} to update lifetimes with {:?}",
451                                        prefix, updated_lifetimes,
452                                    );
453                                }
454                                dhcpv6_core::client::IaValueUpdateKind::Removed => {
455                                    assert_matches!(
456                                        prefixes.remove(&fidl_prefix),
457                                        Some(_),
458                                        "must know about prefix {} to remove it",
459                                        prefix
460                                    );
461                                }
462                            }
463                        }
464
465                        // Mark the client has having updated prefixes so that
466                        // callers of `WatchPrefixes` receive the update.
467                        *prefixes_changed = true;
468                        client.maybe_send_prefixes()?;
469                    }
470                };
471                Ok(client)
472            })
473            .await
474            .map(|_: &mut Client<S>| ())
475    }
476
477    /// Sends the latest DNS servers if a watcher is watching, and the latest set of servers are
478    /// different from what the watcher has observed last time.
479    fn maybe_send_dns_server_updates(&mut self, servers: Vec<Ipv6Addr>) -> Result<(), ClientError> {
480        let servers_hash = hash(&servers);
481        if servers_hash == self.last_observed_dns_hash {
482            Ok(())
483        } else {
484            Ok(match self.dns_responder.take() {
485                Some(responder) => {
486                    self.send_dns_server_updates(responder, servers, servers_hash)?
487                }
488                None => (),
489            })
490        }
491    }
492
493    fn maybe_send_prefixes(&mut self) -> Result<(), ClientError> {
494        let Self { prefixes, prefixes_changed, prefixes_responder, .. } = self;
495
496        if !*prefixes_changed {
497            return Ok(());
498        }
499
500        let responder = if let Some(responder) = prefixes_responder.take() {
501            responder
502        } else {
503            return Ok(());
504        };
505
506        let prefixes = prefixes
507            .iter()
508            .map(|(prefix, lifetimes)| Prefix { prefix: *prefix, lifetimes: *lifetimes })
509            .collect::<Vec<_>>();
510
511        responder.send(&prefixes).map_err(ClientError::Fidl)?;
512        *prefixes_changed = false;
513        Ok(())
514    }
515
516    /// Sends a list of DNS servers to a watcher through the input responder and updates the last
517    /// observed hash.
518    fn send_dns_server_updates(
519        &mut self,
520        responder: ClientWatchServersResponder,
521        servers: Vec<Ipv6Addr>,
522        hash: u64,
523    ) -> Result<(), ClientError> {
524        let response: Vec<_> = servers
525            .iter()
526            .map(|addr| {
527                let address = fnet::Ipv6Address { addr: addr.ipv6_bytes() };
528                let zone_index =
529                    if is_unicast_link_local_strict(&address) { self.interface_id } else { 0 };
530
531                fnet_name::DnsServer_ {
532                    address: Some(fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
533                        address,
534                        zone_index,
535                        port: DEFAULT_DNS_PORT,
536                    })),
537                    source: Some(fnet_name::DnsServerSource::Dhcpv6(
538                        fnet_name::Dhcpv6DnsServerSource {
539                            source_interface: Some(self.interface_id),
540                            ..Default::default()
541                        },
542                    )),
543                    ..Default::default()
544                }
545            })
546            .collect();
547        let () = responder
548            .send(&response)
549            // The channel will be closed on error, so return an error to stop the client.
550            .map_err(ClientError::Fidl)?;
551        self.last_observed_dns_hash = hash;
552        Ok(())
553    }
554
555    /// Schedules a timer for `timer_type` to fire at `instant`.
556    ///
557    /// If a timer for `timer_type` is already scheduled, the timer is
558    /// updated to fire at the new time.
559    fn schedule_timer(
560        &mut self,
561        timer_type: dhcpv6_core::client::ClientTimerType,
562        MonotonicInstant(instant): MonotonicInstant,
563    ) {
564        let timers = self.timers.as_mut().project();
565        let timer = match timer_type {
566            dhcpv6_core::client::ClientTimerType::Retransmission => timers.retransmission,
567            dhcpv6_core::client::ClientTimerType::Refresh => timers.refresh,
568            dhcpv6_core::client::ClientTimerType::Renew => timers.renew,
569            dhcpv6_core::client::ClientTimerType::Rebind => timers.rebind,
570            dhcpv6_core::client::ClientTimerType::RestartServerDiscovery => {
571                timers.restart_server_discovery
572            }
573        };
574        #[cfg(test)]
575        let _: bool = if instant == zx::MonotonicInstant::INFINITE {
576            timers.scheduled.remove(&timer_type)
577        } else {
578            timers.scheduled.insert(timer_type)
579        };
580        timer.reset(fasync::MonotonicInstant::from_zx(instant));
581    }
582
583    /// Cancels a previously scheduled timer for `timer_type`.
584    ///
585    /// If a timer was not previously scheduled for `timer_type`, this
586    /// call is effectively a no-op.
587    fn cancel_timer(&mut self, timer_type: dhcpv6_core::client::ClientTimerType) {
588        self.schedule_timer(timer_type, MonotonicInstant(zx::MonotonicInstant::INFINITE))
589    }
590
591    /// Handles a timeout.
592    async fn handle_timeout(
593        &mut self,
594        timer_type: dhcpv6_core::client::ClientTimerType,
595    ) -> Result<(), ClientError> {
596        // This timer just fired.
597        self.cancel_timer(timer_type);
598
599        let actions = self.state_machine.handle_timeout(timer_type, MonotonicInstant::now());
600        self.run_actions(actions).await
601    }
602
603    /// Handles a received message.
604    async fn handle_message_recv(&mut self, mut msg: &[u8]) -> Result<(), ClientError> {
605        let msg = match v6::Message::parse(&mut msg, ()) {
606            Ok(msg) => msg,
607            Err(e) => {
608                // Discard invalid messages.
609                //
610                // https://tools.ietf.org/html/rfc8415#section-16.
611                warn!("failed to parse received message: {}", e);
612                return Ok(());
613            }
614        };
615        let actions = self.state_machine.handle_message_receive(msg, MonotonicInstant::now());
616        self.run_actions(actions).await
617    }
618
619    /// Handles a FIDL request sent to this client.
620    fn handle_client_request(&mut self, request: ClientRequest) -> Result<(), ClientError> {
621        debug!("handling client request: {:?}", request);
622        match request {
623            ClientRequest::WatchServers { responder } => match self.dns_responder {
624                Some(_) => {
625                    // Drop the previous responder to close the channel.
626                    self.dns_responder = None;
627                    // Return an error to stop the client because the channel is closed.
628                    Err(ClientError::DoubleWatch)
629                }
630                None => {
631                    let dns_servers = self.state_machine.get_dns_servers();
632                    let servers_hash = hash(&dns_servers);
633                    if servers_hash != self.last_observed_dns_hash {
634                        // Something has changed from the last time, update the watcher.
635                        let () =
636                            self.send_dns_server_updates(responder, dns_servers, servers_hash)?;
637                    } else {
638                        // Nothing has changed, update the watcher later.
639                        self.dns_responder = Some(responder);
640                    }
641                    Ok(())
642                }
643            },
644            ClientRequest::WatchAddress { responder } => match self.address_responder.take() {
645                // The responder will be dropped and cause the channel to be closed.
646                Some(ClientWatchAddressResponder { .. }) => Err(ClientError::DoubleWatch),
647                None => {
648                    // TODO(https://fxbug.dev/42152192): Implement the address watcher.
649                    warn!("WatchAddress call will block forever as it is unimplemented");
650                    self.address_responder = Some(responder);
651                    Ok(())
652                }
653            },
654            ClientRequest::WatchPrefixes { responder } => match self.prefixes_responder.take() {
655                // The responder will be dropped and cause the channel to be closed.
656                Some(ClientWatchPrefixesResponder { .. }) => Err(ClientError::DoubleWatch),
657                None => {
658                    self.prefixes_responder = Some(responder);
659                    self.maybe_send_prefixes()
660                }
661            },
662            // TODO(https://fxbug.dev/42152193): Implement Shutdown.
663            ClientRequest::Shutdown { responder: _ } => {
664                Err(ClientError::Unimplemented("Shutdown".to_string()))
665            }
666        }
667    }
668
669    /// Handles the next event and returns the result.
670    ///
671    /// Takes a pre-allocated buffer to avoid repeated allocation.
672    ///
673    /// The returned `Option` is `None` if `request_stream` on the client is closed.
674    async fn handle_next_event(&mut self, buf: &mut [u8]) -> Result<Option<()>, ClientError> {
675        let timers = self.timers.as_mut().project();
676        let timer_type = select! {
677            () = timers.retransmission => {
678                dhcpv6_core::client::ClientTimerType::Retransmission
679            },
680            () = timers.refresh => {
681                dhcpv6_core::client::ClientTimerType::Refresh
682            },
683            () = timers.renew => {
684                dhcpv6_core::client::ClientTimerType::Renew
685            },
686            () = timers.rebind => {
687                dhcpv6_core::client::ClientTimerType::Rebind
688            },
689            () = timers.restart_server_discovery => {
690                dhcpv6_core::client::ClientTimerType::RestartServerDiscovery
691            },
692            recv_from_res = self.socket.recv_from(buf).fuse() => {
693                let (size, _addr) = recv_from_res.map_err(ClientError::SocketRecv)?;
694                let () = self.handle_message_recv(&buf[..size]).await?;
695                return Ok(Some(()));
696            },
697            request = self.request_stream.try_next() => {
698                let request = request.map_err(ClientError::Fidl)?;
699                return request.map(|request| self.handle_client_request(request)).transpose();
700            }
701        };
702        let () = self.handle_timeout(timer_type).await?;
703        Ok(Some(()))
704    }
705
706    #[cfg(test)]
707    fn assert_scheduled(
708        &self,
709        timers: impl IntoIterator<Item = dhcpv6_core::client::ClientTimerType>,
710    ) {
711        assert_eq!(self.timers.as_ref().scheduled, timers.into_iter().collect())
712    }
713}
714
715/// Creates a socket listening on the input address.
716fn create_socket(addr: SocketAddr) -> std::io::Result<fasync::net::UdpSocket> {
717    let socket = socket2::Socket::new(
718        socket2::Domain::IPV6,
719        socket2::Type::DGRAM,
720        Some(socket2::Protocol::UDP),
721    )?;
722    // It is possible to run multiple clients on the same address.
723    let () = socket.set_reuse_port(true)?;
724    let () = socket.bind(&addr.into())?;
725    fasync::net::UdpSocket::from_socket(socket.into())
726}
727
728/// Returns `true` if the input address is a link-local address (`fe80::/64`).
729///
730/// TODO(https://github.com/rust-lang/rust/issues/27709): use is_unicast_link_local_strict() in
731/// stable rust when it's available.
732fn is_unicast_link_local_strict(addr: &fnet::Ipv6Address) -> bool {
733    addr.addr[..8] == [0xfe, 0x80, 0, 0, 0, 0, 0, 0]
734}
735
736fn duid_from_fidl(duid: Duid) -> Result<dhcpv6_core::ClientDuid, ()> {
737    /// According to [RFC 8415, section 11.2], DUID of type DUID-LLT has a type value of 1
738    ///
739    /// [RFC 8415, section 11.2]: https://datatracker.ietf.org/doc/html/rfc8415#section-11.2
740    const DUID_TYPE_LLT: [u8; 2] = [0, 1];
741    /// According to [RFC 8415, section 11.4], DUID of type DUID-LL has a type value of 3
742    ///
743    /// [RFC 8415, section 11.4]: https://datatracker.ietf.org/doc/html/rfc8415#section-11.4
744    const DUID_TYPE_LL: [u8; 2] = [0, 3];
745    /// According to [RFC 8415, section 11.5], DUID of type DUID-UUID has a type value of 4.
746    ///
747    /// [RFC 8415, section 11.5]: https://datatracker.ietf.org/doc/html/rfc8415#section-11.5
748    const DUID_TYPE_UUID: [u8; 2] = [0, 4];
749    /// According to [RFC 8415, section 11.2], the hardware type of Ethernet as assigned by
750    /// [IANA] is 1.
751    ///
752    /// [RFC 8415, section 11.2]: https://datatracker.ietf.org/doc/html/rfc8415#section-11.2
753    /// [IANA]: https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
754    const HARDWARE_TYPE_ETHERNET: [u8; 2] = [0, 1];
755    match duid {
756        // DUID-LLT with a MAC address is 14 bytes (2 bytes for the type + 2
757        // bytes for the hardware type + 4 bytes for the timestamp + 6 bytes
758        // for the MAC address), which is guaranteed to fit in the 18-byte limit
759        // of `ClientDuid`.
760        Duid::LinkLayerAddressPlusTime(LinkLayerAddressPlusTime {
761            time,
762            link_layer_address: LinkLayerAddress::Ethernet(mac),
763        }) => {
764            let mut duid = dhcpv6_core::ClientDuid::new();
765            duid.try_extend_from_slice(&DUID_TYPE_LLT).unwrap();
766            duid.try_extend_from_slice(&HARDWARE_TYPE_ETHERNET).unwrap();
767            duid.write_u32::<NetworkEndian>(time).unwrap();
768            duid.try_extend_from_slice(&mac.octets).unwrap();
769            Ok(duid)
770        }
771        // DUID-LL with a MAC address is 10 bytes (2 bytes for the type + 2
772        // bytes for the hardware type + 6 bytes for the MAC address), which
773        // is guaranteed to fit in the 18-byte limit of `ClientDuid`.
774        Duid::LinkLayerAddress(LinkLayerAddress::Ethernet(mac)) => Ok(DUID_TYPE_LL
775            .into_iter()
776            .chain(HARDWARE_TYPE_ETHERNET.into_iter())
777            .chain(mac.octets.into_iter())
778            .collect()),
779        // DUID-UUID is 18 bytes (2 bytes for the type + 16 bytes for the UUID),
780        // which is guaranteed to fit in the 18-byte limit of `ClientDuid`.
781        Duid::Uuid(uuid) => Ok(DUID_TYPE_UUID.into_iter().chain(uuid.into_iter()).collect()),
782        _ => Err(()),
783    }
784}
785
786/// Starts a client based on `params`.
787///
788/// `request` will be serviced by the client.
789pub(crate) async fn serve_client(
790    NewClientParams { interface_id, address, duid, config }: NewClientParams,
791    request: ServerEnd<ClientMarker>,
792) -> Result<()> {
793    if Ipv6Addr::from(address.address.addr).is_multicast()
794        || (is_unicast_link_local_strict(&address.address) && address.zone_index != interface_id)
795    {
796        return request
797            .close_with_epitaph(zx::Status::INVALID_ARGS)
798            .context("closing request channel with epitaph");
799    }
800
801    let fnet_ext::SocketAddress(addr) = fnet::SocketAddress::Ipv6(address).into();
802    let servers_addr = IpAddr::from_str(RELAY_AGENT_AND_SERVER_LINK_LOCAL_MULTICAST_ADDRESS)
803        .with_context(|| {
804            format!(
805                "{} should be a valid IPv6 address",
806                RELAY_AGENT_AND_SERVER_LINK_LOCAL_MULTICAST_ADDRESS,
807            )
808        })?;
809    let duid = match duid.map(|fidl| duid_from_fidl(fidl)).transpose() {
810        Ok(duid) => duid,
811        Err(()) => {
812            return request
813                .close_with_epitaph(zx::Status::INVALID_ARGS)
814                .context("closing request channel with epitaph");
815        }
816    };
817    let (request_stream, control_handle) = request.into_stream_and_control_handle();
818    let mut client = match Client::<fasync::net::UdpSocket>::start(
819        duid,
820        dhcpv6_core::client::transaction_id(),
821        config,
822        interface_id,
823        || create_socket(addr),
824        SocketAddr::new(servers_addr, RELAY_AGENT_AND_SERVER_PORT),
825        request_stream,
826    )
827    .await
828    {
829        Ok(client) => client,
830        Err(ClientError::UnsupportedConfigs) => {
831            control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
832            return Ok(());
833        }
834        Err(e) => {
835            return Err(e.into());
836        }
837    };
838    let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
839    loop {
840        match client.handle_next_event(&mut buf).await? {
841            Some(()) => (),
842            None => break Ok(()),
843        }
844    }
845}
846
847#[cfg(test)]
848mod tests {
849    use std::pin::pin;
850    use std::task::Poll;
851
852    use fidl::endpoints::{
853        ClientEnd, create_proxy, create_proxy_and_stream, create_request_stream,
854    };
855    use fidl_fuchsia_net_dhcpv6::{self as fnet_dhcpv6, ClientProxy, DEFAULT_CLIENT_PORT};
856    use fuchsia_async as fasync;
857    use futures::{TryFutureExt as _, join, poll};
858
859    use assert_matches::assert_matches;
860    use net_declare::{
861        fidl_ip_v6, fidl_ip_v6_with_prefix, fidl_mac, fidl_socket_addr, fidl_socket_addr_v6,
862        net_ip_v6, net_subnet_v6, std_socket_addr,
863    };
864    use net_types::ip::IpAddress as _;
865    use packet::serialize::InnerPacketBuilder;
866    use test_case::test_case;
867
868    use super::*;
869
870    /// Creates a test socket bound to an ephemeral port on localhost.
871    fn create_test_socket() -> (fasync::net::UdpSocket, SocketAddr) {
872        let addr: SocketAddr = std_socket_addr!("[::1]:0");
873        let socket = std::net::UdpSocket::bind(addr).expect("failed to create test socket");
874        let addr = socket.local_addr().expect("failed to get address of test socket");
875        (fasync::net::UdpSocket::from_socket(socket).expect("failed to create test socket"), addr)
876    }
877
878    struct ReceivedMessage {
879        transaction_id: [u8; 3],
880        // Client IDs are optional in Information Request messages.
881        //
882        // Per RFC 8415 section 18.2.6,
883        //
884        //   The client SHOULD include a Client Identifier option (see
885        //   Section 21.2) to identify itself to the server (however, see
886        //   Section 4.3.1 of [RFC7844] for reasons why a client may not want to
887        //   include this option).
888        //
889        // Per RFC 7844 section 4.3.1,
890        //
891        //   According to [RFC3315], a DHCPv6 client includes its client
892        //   identifier in most of the messages it sends. There is one exception,
893        //   however: the client is allowed to omit its client identifier when
894        //   sending Information-request messages.
895        client_id: Option<Vec<u8>>,
896    }
897
898    /// Asserts `socket` receives a message of `msg_type` from
899    /// `want_from_addr`.
900    async fn assert_received_message(
901        socket: &fasync::net::UdpSocket,
902        want_from_addr: SocketAddr,
903        msg_type: v6::MessageType,
904    ) -> ReceivedMessage {
905        let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
906        let (size, from_addr) =
907            socket.recv_from(&mut buf).await.expect("failed to receive on test server socket");
908        assert_eq!(from_addr, want_from_addr);
909        let buf = &mut &buf[..size]; // Implements BufferView.
910        let msg = v6::Message::parse(buf, ()).expect("failed to parse message");
911        assert_eq!(msg.msg_type(), msg_type);
912
913        let mut client_id = None;
914        for opt in msg.options() {
915            match opt {
916                v6::ParsedDhcpOption::ClientId(id) => {
917                    assert_eq!(core::mem::replace(&mut client_id, Some(id.to_vec())), None)
918                }
919                _ => {}
920            }
921        }
922
923        ReceivedMessage { transaction_id: *msg.transaction_id(), client_id: client_id }
924    }
925
926    const TEST_MAC: fnet::MacAddress = fidl_mac!("00:01:02:03:04:05");
927
928    #[test_case(
929        Duid::LinkLayerAddress(LinkLayerAddress::Ethernet(TEST_MAC)),
930        &[0, 3, 0, 1, 0, 1, 2, 3, 4, 5];
931        "ll"
932    )]
933    #[test_case(
934        Duid::LinkLayerAddressPlusTime(LinkLayerAddressPlusTime {
935            time: 0,
936            link_layer_address: LinkLayerAddress::Ethernet(TEST_MAC),
937        }),
938        &[0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5];
939        "llt"
940    )]
941    #[test_case(
942        Duid::Uuid([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]),
943        &[0, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
944        "uuid"
945    )]
946    #[fuchsia::test]
947    fn test_duid_from_fidl(duid: Duid, want: &[u8]) {
948        assert_eq!(duid_from_fidl(duid), Ok(dhcpv6_core::ClientDuid::try_from(want).unwrap()));
949    }
950
951    #[fuchsia::test]
952    fn test_create_client_with_unsupported_config() {
953        let prefix_delegation_configs = [
954            None,
955            // Prefix length config without a non-zero length.
956            Some(PrefixDelegationConfig::PrefixLength(0)),
957            // Prefix length too long.
958            Some(PrefixDelegationConfig::PrefixLength(Ipv6Addr::BYTES * 8 + 1)),
959            // Network-bits unset.
960            Some(PrefixDelegationConfig::Prefix(fidl_ip_v6_with_prefix!("::/64"))),
961            // Host-bits set.
962            Some(PrefixDelegationConfig::Prefix(fidl_ip_v6_with_prefix!("a::1/64"))),
963        ];
964
965        for prefix_delegation_config in prefix_delegation_configs.iter() {
966            assert_matches!(
967                create_state_machine(
968                    prefix_delegation_config.is_some().then(|| CLIENT_ID.into()),
969                    [1, 2, 3],
970                    ClientConfig {
971                        information_config: Default::default(),
972                        non_temporary_address_config: Default::default(),
973                        prefix_delegation_config: prefix_delegation_config.clone(),
974                    }
975                ),
976                Err(ClientError::UnsupportedConfigs),
977                "prefix_delegation_config={:?}",
978                prefix_delegation_config
979            );
980        }
981    }
982
983    const STATELESS_CLIENT_CONFIG: ClientConfig = ClientConfig {
984        information_config: InformationConfig { dns_servers: true },
985        non_temporary_address_config: AddressConfig { address_count: 0, preferred_addresses: None },
986        prefix_delegation_config: None,
987    };
988
989    #[fuchsia::test]
990    async fn test_client_stops_on_channel_close() {
991        let (client_proxy, server_end) = create_proxy::<ClientMarker>();
992
993        let ((), client_res) = join!(
994            async { drop(client_proxy) },
995            serve_client(
996                NewClientParams {
997                    interface_id: 1,
998                    address: fidl_socket_addr_v6!("[::1]:546"),
999                    config: STATELESS_CLIENT_CONFIG,
1000                    duid: None,
1001                },
1002                server_end,
1003            ),
1004        );
1005        client_res.expect("client future should return with Ok");
1006    }
1007
1008    fn client_proxy_watch_servers(
1009        client_proxy: &fnet_dhcpv6::ClientProxy,
1010    ) -> impl Future<Output = Result<(), fidl::Error>> {
1011        client_proxy.watch_servers().map_ok(|_: Vec<fidl_fuchsia_net_name::DnsServer_>| ())
1012    }
1013
1014    fn client_proxy_watch_address(
1015        client_proxy: &fnet_dhcpv6::ClientProxy,
1016    ) -> impl Future<Output = Result<(), fidl::Error>> {
1017        client_proxy.watch_address().map_ok(
1018            |_: (
1019                fnet::Subnet,
1020                fidl_fuchsia_net_interfaces_admin::AddressParameters,
1021                fidl::endpoints::ServerEnd<
1022                    fidl_fuchsia_net_interfaces_admin::AddressStateProviderMarker,
1023                >,
1024            )| (),
1025        )
1026    }
1027
1028    fn client_proxy_watch_prefixes(
1029        client_proxy: &fnet_dhcpv6::ClientProxy,
1030    ) -> impl Future<Output = Result<(), fidl::Error>> {
1031        client_proxy.watch_prefixes().map_ok(|_: Vec<fnet_dhcpv6::Prefix>| ())
1032    }
1033
1034    #[test_case(client_proxy_watch_servers; "watch_servers")]
1035    #[test_case(client_proxy_watch_address; "watch_address")]
1036    #[test_case(client_proxy_watch_prefixes; "watch_prefixes")]
1037    #[fuchsia::test]
1038    async fn test_client_should_return_error_on_double_watch<F>(watch: F)
1039    where
1040        F: AsyncFn(&fnet_dhcpv6::ClientProxy) -> Result<(), fidl::Error>,
1041    {
1042        let (client_proxy, server_end) = create_proxy::<ClientMarker>();
1043
1044        let (caller1_res, caller2_res, client_res) = join!(
1045            watch(&client_proxy),
1046            watch(&client_proxy),
1047            serve_client(
1048                NewClientParams {
1049                    interface_id: 1,
1050                    address: fidl_socket_addr_v6!("[::1]:546"),
1051                    config: STATELESS_CLIENT_CONFIG,
1052                    duid: None,
1053                },
1054                server_end,
1055            )
1056        );
1057
1058        assert_matches!(
1059            caller1_res,
1060            Err(fidl::Error::ClientChannelClosed { status: zx::Status::PEER_CLOSED, .. })
1061        );
1062        assert_matches!(
1063            caller2_res,
1064            Err(fidl::Error::ClientChannelClosed { status: zx::Status::PEER_CLOSED, .. })
1065        );
1066        assert!(
1067            client_res
1068                .expect_err("client should fail with double watch error")
1069                .to_string()
1070                .contains("got watch request while the previous one is pending")
1071        );
1072    }
1073
1074    const VALID_INFORMATION_CONFIGS: [InformationConfig; 2] =
1075        [InformationConfig { dns_servers: false }, InformationConfig { dns_servers: true }];
1076
1077    const VALID_DELEGATED_PREFIX_CONFIGS: [Option<PrefixDelegationConfig>; 4] = [
1078        Some(PrefixDelegationConfig::Empty(Empty {})),
1079        Some(PrefixDelegationConfig::PrefixLength(1)),
1080        Some(PrefixDelegationConfig::PrefixLength(127)),
1081        Some(PrefixDelegationConfig::Prefix(fidl_ip_v6_with_prefix!("a::/64"))),
1082    ];
1083
1084    // Can't be a const variable because we allocate a vector.
1085    fn get_valid_non_temporary_address_configs() -> [AddressConfig; 5] {
1086        [
1087            Default::default(),
1088            AddressConfig { address_count: 1, preferred_addresses: None },
1089            AddressConfig { address_count: 1, preferred_addresses: Some(Vec::new()) },
1090            AddressConfig {
1091                address_count: 1,
1092                preferred_addresses: Some(vec![fidl_ip_v6!("a::1")]),
1093            },
1094            AddressConfig {
1095                address_count: 2,
1096                preferred_addresses: Some(vec![fidl_ip_v6!("a::2")]),
1097            },
1098        ]
1099    }
1100
1101    #[fuchsia::test]
1102    fn test_client_starts_with_valid_args() {
1103        for information_config in VALID_INFORMATION_CONFIGS {
1104            for non_temporary_address_config in get_valid_non_temporary_address_configs() {
1105                for prefix_delegation_config in VALID_DELEGATED_PREFIX_CONFIGS {
1106                    let mut exec = fasync::TestExecutor::new();
1107
1108                    let (client_proxy, server_end) = create_proxy::<ClientMarker>();
1109
1110                    let test_fut = async {
1111                        join!(
1112                            client_proxy.watch_servers(),
1113                            serve_client(
1114                                NewClientParams {
1115                                    interface_id: 1,
1116                                    address: fidl_socket_addr_v6!("[::1]:546"),
1117                                    config: ClientConfig {
1118                                        information_config: information_config.clone(),
1119                                        non_temporary_address_config: non_temporary_address_config
1120                                            .clone(),
1121                                        prefix_delegation_config: prefix_delegation_config.clone(),
1122                                    },
1123                                    duid: (non_temporary_address_config.address_count != 0
1124                                        || prefix_delegation_config.is_some())
1125                                    .then(|| fnet_dhcpv6::Duid::LinkLayerAddress(
1126                                        fnet_dhcpv6::LinkLayerAddress::Ethernet(fidl_mac!(
1127                                            "00:11:22:33:44:55"
1128                                        ))
1129                                    )),
1130                                },
1131                                server_end
1132                            )
1133                        )
1134                    };
1135                    let mut test_fut = pin!(test_fut);
1136                    assert_matches!(
1137                        exec.run_until_stalled(&mut test_fut),
1138                        Poll::Pending,
1139                        "information_config={:?}, non_temporary_address_config={:?}, prefix_delegation_config={:?}",
1140                        information_config,
1141                        non_temporary_address_config,
1142                        prefix_delegation_config
1143                    );
1144                }
1145            }
1146        }
1147    }
1148
1149    const CLIENT_ID: [u8; 18] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
1150
1151    #[fuchsia::test]
1152    async fn test_client_starts_in_correct_mode() {
1153        for information_config @ InformationConfig { dns_servers } in VALID_INFORMATION_CONFIGS {
1154            for non_temporary_address_config @ AddressConfig {
1155                address_count,
1156                preferred_addresses: _,
1157            } in get_valid_non_temporary_address_configs()
1158            {
1159                for prefix_delegation_config in VALID_DELEGATED_PREFIX_CONFIGS {
1160                    let (stateful, want_msg_type) =
1161                        if address_count == 0 && prefix_delegation_config.is_none() {
1162                            if !dns_servers {
1163                                continue;
1164                            } else {
1165                                (false, v6::MessageType::InformationRequest)
1166                            }
1167                        } else {
1168                            (true, v6::MessageType::Solicit)
1169                        };
1170
1171                    let (_, client_stream): (ClientEnd<ClientMarker>, _) =
1172                        create_request_stream::<ClientMarker>();
1173
1174                    let (client_socket, client_addr) = create_test_socket();
1175                    let (server_socket, server_addr) = create_test_socket();
1176                    println!(
1177                        "{:?} {:?} {:?}",
1178                        information_config, non_temporary_address_config, prefix_delegation_config
1179                    );
1180                    let _: Client<fasync::net::UdpSocket> = Client::start(
1181                        stateful.then(|| CLIENT_ID.into()),
1182                        [1, 2, 3], /* transaction ID */
1183                        ClientConfig {
1184                            information_config: information_config.clone(),
1185                            non_temporary_address_config: non_temporary_address_config.clone(),
1186                            prefix_delegation_config: prefix_delegation_config.clone(),
1187                        },
1188                        1, /* interface ID */
1189                        || Ok(client_socket),
1190                        server_addr,
1191                        client_stream,
1192                    )
1193                    .await
1194                        .unwrap_or_else(|e| panic!(
1195                            "failed to create test client: {}; information_config={:?}, non_temporary_address_config={:?}, prefix_delegation_config={:?}",
1196                            e, information_config, non_temporary_address_config, prefix_delegation_config
1197                        ));
1198
1199                    let _: ReceivedMessage =
1200                        assert_received_message(&server_socket, client_addr, want_msg_type).await;
1201                }
1202            }
1203        }
1204    }
1205
1206    // TODO(https://fxbug.dev/335656784): Replace this with a netemul test that isn't
1207    // sensitive to implementation details.
1208    #[fuchsia::test]
1209    async fn test_client_fails_to_start_with_invalid_args() {
1210        for params in vec![
1211            // Interface ID and zone index mismatch on link-local address.
1212            NewClientParams {
1213                interface_id: 2,
1214                address: fnet::Ipv6SocketAddress {
1215                    address: fidl_ip_v6!("fe80::1"),
1216                    port: DEFAULT_CLIENT_PORT,
1217                    zone_index: 1,
1218                },
1219                config: STATELESS_CLIENT_CONFIG,
1220                duid: None,
1221            },
1222            // Multicast address is invalid.
1223            NewClientParams {
1224                interface_id: 1,
1225                address: fnet::Ipv6SocketAddress {
1226                    address: fidl_ip_v6!("ff01::1"),
1227                    port: DEFAULT_CLIENT_PORT,
1228                    zone_index: 1,
1229                },
1230                config: STATELESS_CLIENT_CONFIG,
1231                duid: None,
1232            },
1233            // Stateless with DUID.
1234            NewClientParams {
1235                interface_id: 1,
1236                address: fidl_socket_addr_v6!("[2001:db8::1]:12345"),
1237                config: STATELESS_CLIENT_CONFIG,
1238                duid: Some(fnet_dhcpv6::Duid::LinkLayerAddress(
1239                    fnet_dhcpv6::LinkLayerAddress::Ethernet(fidl_mac!("00:11:22:33:44:55")),
1240                )),
1241            },
1242            // Stateful missing DUID.
1243            NewClientParams {
1244                interface_id: 1,
1245                address: fidl_socket_addr_v6!("[2001:db8::1]:12345"),
1246                config: ClientConfig {
1247                    information_config: InformationConfig { dns_servers: true },
1248                    non_temporary_address_config: AddressConfig {
1249                        address_count: 1,
1250                        preferred_addresses: None,
1251                    },
1252                    prefix_delegation_config: None,
1253                },
1254                duid: None,
1255            },
1256        ] {
1257            let (client_proxy, server_end) = create_proxy::<ClientMarker>();
1258            let () =
1259                serve_client(params, server_end).await.expect("start server failed unexpectedly");
1260            // Calling any function on the client proxy should fail due to channel closed with
1261            // `INVALID_ARGS`.
1262            assert_matches!(
1263                client_proxy.watch_servers().await,
1264                Err(fidl::Error::ClientChannelClosed { status: zx::Status::INVALID_ARGS, .. })
1265            );
1266        }
1267    }
1268
1269    #[test]
1270    fn test_is_unicast_link_local_strict() {
1271        assert_eq!(is_unicast_link_local_strict(&fidl_ip_v6!("fe80::")), true);
1272        assert_eq!(is_unicast_link_local_strict(&fidl_ip_v6!("fe80::1")), true);
1273        assert_eq!(is_unicast_link_local_strict(&fidl_ip_v6!("fe80::ffff:1:2:3")), true);
1274        assert_eq!(is_unicast_link_local_strict(&fidl_ip_v6!("fe80::1:0:0:0:0")), false);
1275        assert_eq!(is_unicast_link_local_strict(&fidl_ip_v6!("fe81::")), false);
1276    }
1277
1278    fn create_test_dns_server(
1279        address: fnet::Ipv6Address,
1280        source_interface: u64,
1281        zone_index: u64,
1282    ) -> fnet_name::DnsServer_ {
1283        fnet_name::DnsServer_ {
1284            address: Some(fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
1285                address,
1286                zone_index,
1287                port: DEFAULT_DNS_PORT,
1288            })),
1289            source: Some(fnet_name::DnsServerSource::Dhcpv6(fnet_name::Dhcpv6DnsServerSource {
1290                source_interface: Some(source_interface),
1291                ..Default::default()
1292            })),
1293            ..Default::default()
1294        }
1295    }
1296
1297    async fn send_msg_with_options(
1298        socket: &fasync::net::UdpSocket,
1299        to_addr: SocketAddr,
1300        transaction_id: [u8; 3],
1301        msg_type: v6::MessageType,
1302        options: &[v6::DhcpOption<'_>],
1303    ) -> Result<()> {
1304        let builder = v6::MessageBuilder::new(msg_type, transaction_id, options);
1305        let mut buf = vec![0u8; builder.bytes_len()];
1306        let () = builder.serialize(&mut buf);
1307        let size = socket.send_to(&buf, to_addr).await?;
1308        assert_eq!(size, buf.len());
1309        Ok(())
1310    }
1311
1312    #[fuchsia::test]
1313    fn test_client_should_respond_to_dns_watch_requests() {
1314        let mut exec = fasync::TestExecutor::new();
1315        let transaction_id = [1, 2, 3];
1316
1317        let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
1318
1319        let (client_socket, client_addr) = create_test_socket();
1320        let (server_socket, server_addr) = create_test_socket();
1321        let mut client = exec
1322            .run_singlethreaded(Client::<fasync::net::UdpSocket>::start(
1323                None,
1324                transaction_id,
1325                STATELESS_CLIENT_CONFIG,
1326                1, /* interface ID */
1327                || Ok(client_socket),
1328                server_addr,
1329                client_stream,
1330            ))
1331            .expect("failed to create test client");
1332
1333        type WatchServersResponseFut = <fnet_dhcpv6::ClientProxy as fnet_dhcpv6::ClientProxyInterface>::WatchServersResponseFut;
1334        type WatchServersResponse = <WatchServersResponseFut as Future>::Output;
1335
1336        struct Test<'a> {
1337            client: &'a mut Client<fasync::net::UdpSocket>,
1338            buf: Vec<u8>,
1339            watcher_fut: WatchServersResponseFut,
1340        }
1341
1342        impl<'a> Test<'a> {
1343            fn new(
1344                client: &'a mut Client<fasync::net::UdpSocket>,
1345                client_proxy: &ClientProxy,
1346            ) -> Self {
1347                Self {
1348                    client,
1349                    buf: vec![0u8; MAX_UDP_DATAGRAM_SIZE],
1350                    watcher_fut: client_proxy.watch_servers(),
1351                }
1352            }
1353
1354            async fn handle_next_event(&mut self) {
1355                self.client
1356                    .handle_next_event(&mut self.buf)
1357                    .await
1358                    .expect("test client failed to handle next event")
1359                    .expect("request stream closed");
1360            }
1361
1362            async fn refresh_client(&mut self) {
1363                // Make the client ready for another reply immediately on signal, so it can
1364                // start receiving updates without waiting for the full refresh timeout which is
1365                // unrealistic in tests.
1366                if self
1367                    .client
1368                    .timers
1369                    .as_ref()
1370                    .scheduled
1371                    .contains(&dhcpv6_core::client::ClientTimerType::Refresh)
1372                {
1373                    self.client
1374                        .handle_timeout(dhcpv6_core::client::ClientTimerType::Refresh)
1375                        .await
1376                        .expect("test client failed to handle timeout");
1377                } else {
1378                    panic!("no refresh timer is scheduled and refresh is requested in test");
1379                }
1380            }
1381
1382            // Drive both the DHCPv6 client's event handling logic and the DNS server
1383            // watcher until the DNS server watcher receives an update from the client (or
1384            // the client unexpectedly exits).
1385            fn run(&mut self) -> impl Future<Output = WatchServersResponse> + use<'_, 'a> {
1386                let Self { client, buf, watcher_fut } = self;
1387                async move {
1388                    let client_fut = async {
1389                        loop {
1390                            client
1391                                .handle_next_event(buf)
1392                                .await
1393                                .expect("test client failed to handle next event")
1394                                .expect("request stream closed");
1395                        }
1396                    }
1397                    .fuse();
1398                    let mut client_fut = pin!(client_fut);
1399                    let mut watcher_fut = watcher_fut.fuse();
1400                    select! {
1401                        () = client_fut => panic!("test client returned unexpectedly"),
1402                        r = watcher_fut => r,
1403                    }
1404                }
1405            }
1406        }
1407
1408        {
1409            // No DNS configurations received yet.
1410            let mut test = Test::new(&mut client, &client_proxy);
1411
1412            // Handle the WatchServers request.
1413            exec.run_singlethreaded(test.handle_next_event());
1414            assert!(
1415                test.client.dns_responder.is_some(),
1416                "WatchServers responder should be present"
1417            );
1418
1419            // Send an empty list to the client, should not update watcher.
1420            let () = exec
1421                .run_singlethreaded(send_msg_with_options(
1422                    &server_socket,
1423                    client_addr,
1424                    transaction_id,
1425                    v6::MessageType::Reply,
1426                    &[v6::DhcpOption::ServerId(&[1, 2, 3]), v6::DhcpOption::DnsServers(&[])],
1427                ))
1428                .expect("failed to send test reply");
1429            // Wait for the client to handle the next event (processing the reply we just
1430            // sent). Note that it is not enough to simply drive the client future until it
1431            // is stalled as we do elsewhere in the test, because we have no guarantee that
1432            // the netstack has delivered the UDP packet to the client by the time the
1433            // `send_to` call returned.
1434            exec.run_singlethreaded(test.handle_next_event());
1435            assert_matches!(exec.run_until_stalled(&mut pin!(test.run())), Poll::Pending);
1436
1437            // Send a list of DNS servers, the watcher should be updated accordingly.
1438            exec.run_singlethreaded(test.refresh_client());
1439            let dns_servers = [net_ip_v6!("fe80::1:2")];
1440            let () = exec
1441                .run_singlethreaded(send_msg_with_options(
1442                    &server_socket,
1443                    client_addr,
1444                    transaction_id,
1445                    v6::MessageType::Reply,
1446                    &[
1447                        v6::DhcpOption::ServerId(&[1, 2, 3]),
1448                        v6::DhcpOption::DnsServers(&dns_servers),
1449                    ],
1450                ))
1451                .expect("failed to send test reply");
1452            let want_servers = vec![create_test_dns_server(
1453                fidl_ip_v6!("fe80::1:2"),
1454                1, /* source interface */
1455                1, /* zone index */
1456            )];
1457            let servers = exec.run_singlethreaded(test.run()).expect("get servers");
1458            assert_eq!(servers, want_servers);
1459        } // drop `test_fut` so `client_fut` is no longer mutably borrowed.
1460
1461        {
1462            // No new changes, should not update watcher.
1463            let mut test = Test::new(&mut client, &client_proxy);
1464
1465            // Handle the WatchServers request.
1466            exec.run_singlethreaded(test.handle_next_event());
1467            assert!(
1468                test.client.dns_responder.is_some(),
1469                "WatchServers responder should be present"
1470            );
1471
1472            // Send the same list of DNS servers, should not update watcher.
1473            exec.run_singlethreaded(test.refresh_client());
1474            let dns_servers = [net_ip_v6!("fe80::1:2")];
1475            let () = exec
1476                .run_singlethreaded(send_msg_with_options(
1477                    &server_socket,
1478                    client_addr,
1479                    transaction_id,
1480                    v6::MessageType::Reply,
1481                    &[
1482                        v6::DhcpOption::ServerId(&[1, 2, 3]),
1483                        v6::DhcpOption::DnsServers(&dns_servers),
1484                    ],
1485                ))
1486                .expect("failed to send test reply");
1487            // Wait for the client to handle the next event (processing the reply we just
1488            // sent). Note that it is not enough to simply drive the client future until it
1489            // is stalled as we do elsewhere in the test, because we have no guarantee that
1490            // the netstack has delivered the UDP packet to the client by the time the
1491            // `send_to` call returned.
1492            exec.run_singlethreaded(test.handle_next_event());
1493            assert_matches!(exec.run_until_stalled(&mut pin!(test.run())), Poll::Pending);
1494
1495            // Send a different list of DNS servers, should update watcher.
1496            exec.run_singlethreaded(test.refresh_client());
1497            let dns_servers = [net_ip_v6!("fe80::1:2"), net_ip_v6!("1234::5:6")];
1498            let () = exec
1499                .run_singlethreaded(send_msg_with_options(
1500                    &server_socket,
1501                    client_addr,
1502                    transaction_id,
1503                    v6::MessageType::Reply,
1504                    &[
1505                        v6::DhcpOption::ServerId(&[1, 2, 3]),
1506                        v6::DhcpOption::DnsServers(&dns_servers),
1507                    ],
1508                ))
1509                .expect("failed to send test reply");
1510            let want_servers = vec![
1511                create_test_dns_server(
1512                    fidl_ip_v6!("fe80::1:2"),
1513                    1, /* source interface */
1514                    1, /* zone index */
1515                ),
1516                // Only set zone index for link local addresses.
1517                create_test_dns_server(
1518                    fidl_ip_v6!("1234::5:6"),
1519                    1, /* source interface */
1520                    0, /* zone index */
1521                ),
1522            ];
1523            let servers = exec.run_singlethreaded(test.run()).expect("get servers");
1524            assert_eq!(servers, want_servers);
1525        } // drop `test_fut` so `client_fut` is no longer mutably borrowed.
1526
1527        {
1528            // Send an empty list of DNS servers, should update watcher,
1529            // because this is different from what the watcher has seen
1530            // last time.
1531            let mut test = Test::new(&mut client, &client_proxy);
1532
1533            exec.run_singlethreaded(test.refresh_client());
1534            let () = exec
1535                .run_singlethreaded(send_msg_with_options(
1536                    &server_socket,
1537                    client_addr,
1538                    transaction_id,
1539                    v6::MessageType::Reply,
1540                    &[v6::DhcpOption::ServerId(&[1, 2, 3]), v6::DhcpOption::DnsServers(&[])],
1541                ))
1542                .expect("failed to send test reply");
1543            let want_servers = Vec::<fnet_name::DnsServer_>::new();
1544            assert_eq!(exec.run_singlethreaded(test.run()).expect("get servers"), want_servers);
1545        } // drop `test_fut` so `client_fut` is no longer mutably borrowed.
1546    }
1547
1548    #[fuchsia::test]
1549    async fn test_client_should_respond_with_dns_servers_on_first_watch_if_non_empty() {
1550        let transaction_id = [1, 2, 3];
1551
1552        let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
1553
1554        let (client_socket, client_addr) = create_test_socket();
1555        let (server_socket, server_addr) = create_test_socket();
1556        let client = Client::<fasync::net::UdpSocket>::start(
1557            None,
1558            transaction_id,
1559            STATELESS_CLIENT_CONFIG,
1560            1, /* interface ID */
1561            || Ok(client_socket),
1562            server_addr,
1563            client_stream,
1564        )
1565        .await
1566        .expect("failed to create test client");
1567
1568        let dns_servers = [net_ip_v6!("fe80::1:2"), net_ip_v6!("1234::5:6")];
1569        let () = send_msg_with_options(
1570            &server_socket,
1571            client_addr,
1572            transaction_id,
1573            v6::MessageType::Reply,
1574            &[v6::DhcpOption::ServerId(&[4, 5, 6]), v6::DhcpOption::DnsServers(&dns_servers)],
1575        )
1576        .await
1577        .expect("failed to send test message");
1578
1579        let buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
1580        let handle_client_events_fut =
1581            futures::stream::try_unfold((client, buf), |(mut client, mut buf)| async {
1582                client
1583                    .handle_next_event(&mut buf)
1584                    .await
1585                    .map(|res| res.map(|()| ((), (client, buf))))
1586            })
1587            .try_fold((), |(), ()| futures::future::ready(Ok(())))
1588            .fuse();
1589        let mut handle_client_events_fut = pin!(handle_client_events_fut);
1590
1591        let want_servers = vec![
1592            create_test_dns_server(
1593                fidl_ip_v6!("fe80::1:2"),
1594                1, /* source interface */
1595                1, /* zone index */
1596            ),
1597            create_test_dns_server(
1598                fidl_ip_v6!("1234::5:6"),
1599                1, /* source interface */
1600                0, /* zone index */
1601            ),
1602        ];
1603        let found_servers = select!(
1604            status = handle_client_events_fut => panic!("client unexpectedly exited: {status:?}"),
1605            found_servers = client_proxy.watch_servers() => found_servers.expect(
1606                "watch servers should succeed"),
1607        );
1608        assert_eq!(found_servers, want_servers);
1609    }
1610
1611    #[fuchsia::test]
1612    async fn watch_prefixes() {
1613        const SERVER_ID: [u8; 3] = [3, 4, 5];
1614        const PREFERRED_LIFETIME_SECS: u32 = 1000;
1615        const VALID_LIFETIME_SECS: u32 = 2000;
1616        // Use the smallest possible value to enter the Renewing state
1617        // as fast as possible to keep the test's run-time as low as possible.
1618        const T1: u32 = 1;
1619        const T2: u32 = 2000;
1620
1621        let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
1622
1623        let (client_socket, client_addr) = create_test_socket();
1624        let (server_socket, server_addr) = create_test_socket();
1625        let mut client = Client::<fasync::net::UdpSocket>::start(
1626            Some(CLIENT_ID.into()),
1627            [1, 2, 3],
1628            ClientConfig {
1629                information_config: Default::default(),
1630                non_temporary_address_config: Default::default(),
1631                prefix_delegation_config: Some(PrefixDelegationConfig::Empty(Empty {})),
1632            },
1633            1, /* interface ID */
1634            || Ok(client_socket),
1635            server_addr,
1636            client_stream,
1637        )
1638        .await
1639        .expect("failed to create test client");
1640
1641        let client_fut = async {
1642            let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
1643            loop {
1644                select! {
1645                    res = client.handle_next_event(&mut buf).fuse() => {
1646                        match res.expect("test client failed to handle next event") {
1647                            Some(()) => (),
1648                            None => break (),
1649                        };
1650                    }
1651                }
1652            }
1653        }
1654        .fuse();
1655        let mut client_fut = pin!(client_fut);
1656
1657        let update_prefix = net_subnet_v6!("a::/64");
1658        let remove_prefix = net_subnet_v6!("b::/64");
1659        let add_prefix = net_subnet_v6!("c::/64");
1660
1661        // Go through the motions to assign a prefix.
1662        let client_id = {
1663            let ReceivedMessage { client_id, transaction_id } =
1664                assert_received_message(&server_socket, client_addr, v6::MessageType::Solicit)
1665                    .await;
1666            // Client IDs are mandatory in stateful DHCPv6.
1667            let client_id = client_id.unwrap();
1668
1669            let ia_prefix = [
1670                v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
1671                    PREFERRED_LIFETIME_SECS,
1672                    VALID_LIFETIME_SECS,
1673                    update_prefix,
1674                    &[],
1675                )),
1676                v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
1677                    PREFERRED_LIFETIME_SECS,
1678                    VALID_LIFETIME_SECS,
1679                    remove_prefix,
1680                    &[],
1681                )),
1682            ];
1683            let () = send_msg_with_options(
1684                &server_socket,
1685                client_addr,
1686                transaction_id,
1687                v6::MessageType::Advertise,
1688                &[
1689                    v6::DhcpOption::ServerId(&SERVER_ID),
1690                    v6::DhcpOption::ClientId(&client_id),
1691                    v6::DhcpOption::Preference(u8::MAX),
1692                    v6::DhcpOption::IaPd(v6::IaPdSerializer::new(IA_PD_IAID, T1, T2, &ia_prefix)),
1693                ],
1694            )
1695            .await
1696            .expect("failed to send adv message");
1697
1698            // Wait for the client to send a Request and send Reply so a prefix
1699            // is assigned.
1700            let transaction_id = select! {
1701                () = client_fut => panic!("should never return"),
1702                res = assert_received_message(
1703                    &server_socket,
1704                    client_addr,
1705                    v6::MessageType::Request,
1706                ).fuse() => {
1707                    let ReceivedMessage { client_id: req_client_id, transaction_id } = res;
1708                    assert_eq!(Some(&client_id), req_client_id.as_ref());
1709                    transaction_id
1710                },
1711            };
1712
1713            let () = send_msg_with_options(
1714                &server_socket,
1715                client_addr,
1716                transaction_id,
1717                v6::MessageType::Reply,
1718                &[
1719                    v6::DhcpOption::ServerId(&SERVER_ID),
1720                    v6::DhcpOption::ClientId(&client_id),
1721                    v6::DhcpOption::IaPd(v6::IaPdSerializer::new(IA_PD_IAID, T1, T2, &ia_prefix)),
1722                ],
1723            )
1724            .await
1725            .expect("failed to send reply message");
1726
1727            client_id
1728        };
1729
1730        let check_watch_prefixes_result =
1731            |res: Result<Vec<Prefix>, _>,
1732             before_handling_reply,
1733             preferred_lifetime_secs: u32,
1734             valid_lifetime_secs: u32,
1735             expected_prefixes| {
1736                assert_matches!(
1737                    res.unwrap()[..],
1738                    [
1739                        Prefix {
1740                            prefix: got_prefix1,
1741                            lifetimes: Lifetimes {
1742                                preferred_until: preferred_until1,
1743                                valid_until: valid_until1,
1744                            },
1745                        },
1746                        Prefix {
1747                            prefix: got_prefix2,
1748                            lifetimes: Lifetimes {
1749                                preferred_until: preferred_until2,
1750                                valid_until: valid_until2,
1751                            },
1752                        },
1753                    ] => {
1754                        let now = zx::MonotonicInstant::get();
1755                        let preferred_until = zx::MonotonicInstant::from_nanos(preferred_until1);
1756                        let valid_until = zx::MonotonicInstant::from_nanos(valid_until1);
1757
1758                        let preferred_for = zx::MonotonicDuration::from_seconds(
1759                            preferred_lifetime_secs.into(),
1760                        );
1761                        let valid_for = zx::MonotonicDuration::from_seconds(valid_lifetime_secs.into());
1762
1763                        assert_eq!(
1764                            HashSet::from([got_prefix1, got_prefix2]),
1765                            HashSet::from(expected_prefixes),
1766                        );
1767                        assert!(preferred_until >= before_handling_reply + preferred_for);
1768                        assert!(preferred_until <= now + preferred_for);
1769                        assert!(valid_until >= before_handling_reply + valid_for);
1770                        assert!(valid_until <= now + valid_for);
1771
1772                        assert_eq!(preferred_until1, preferred_until2);
1773                        assert_eq!(valid_until1, valid_until2);
1774                    }
1775                )
1776            };
1777
1778        // Wait for a prefix to become assigned from the perspective of the DHCPv6
1779        // FIDL client.
1780        {
1781            // watch_prefixes should not return before a lease is negotiated. Note
1782            // that the client has not yet handled the Reply message.
1783            let mut watch_prefixes = client_proxy.watch_prefixes().fuse();
1784            assert_matches!(poll!(&mut watch_prefixes), Poll::Pending);
1785            let before_handling_reply = zx::MonotonicInstant::get();
1786            select! {
1787                () = client_fut => panic!("should never return"),
1788                res = watch_prefixes => check_watch_prefixes_result(
1789                    res,
1790                    before_handling_reply,
1791                    PREFERRED_LIFETIME_SECS,
1792                    VALID_LIFETIME_SECS,
1793                    [
1794                        subnet_to_address_with_prefix(update_prefix),
1795                        subnet_to_address_with_prefix(remove_prefix),
1796                    ],
1797                ),
1798            }
1799        }
1800
1801        // Wait for the client to attempt to renew the lease and go through the
1802        // motions to update the lease.
1803        {
1804            let transaction_id = select! {
1805                () = client_fut => panic!("should never return"),
1806                res = assert_received_message(
1807                    &server_socket,
1808                    client_addr,
1809                    v6::MessageType::Renew,
1810                ).fuse() => {
1811                    let ReceivedMessage { client_id: ren_client_id, transaction_id } = res;
1812                    assert_eq!(ren_client_id.as_ref(), Some(&client_id));
1813                    transaction_id
1814                },
1815            };
1816
1817            const NEW_PREFERRED_LIFETIME_SECS: u32 = 2 * PREFERRED_LIFETIME_SECS;
1818            const NEW_VALID_LIFETIME_SECS: u32 = 2 * VALID_LIFETIME_SECS;
1819            let ia_prefix = [
1820                v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
1821                    NEW_PREFERRED_LIFETIME_SECS,
1822                    NEW_VALID_LIFETIME_SECS,
1823                    update_prefix,
1824                    &[],
1825                )),
1826                v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
1827                    NEW_PREFERRED_LIFETIME_SECS,
1828                    NEW_VALID_LIFETIME_SECS,
1829                    add_prefix,
1830                    &[],
1831                )),
1832                v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(0, 0, remove_prefix, &[])),
1833            ];
1834
1835            let () = send_msg_with_options(
1836                &server_socket,
1837                client_addr,
1838                transaction_id,
1839                v6::MessageType::Reply,
1840                &[
1841                    v6::DhcpOption::ServerId(&SERVER_ID),
1842                    v6::DhcpOption::ClientId(&client_id),
1843                    v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
1844                        v6::IAID::new(0),
1845                        T1,
1846                        T2,
1847                        &ia_prefix,
1848                    )),
1849                ],
1850            )
1851            .await
1852            .expect("failed to send reply message");
1853
1854            let before_handling_reply = zx::MonotonicInstant::get();
1855            select! {
1856                () = client_fut => panic!("should never return"),
1857                res = client_proxy.watch_prefixes().fuse() => check_watch_prefixes_result(
1858                    res,
1859                    before_handling_reply,
1860                    NEW_PREFERRED_LIFETIME_SECS,
1861                    NEW_VALID_LIFETIME_SECS,
1862                    [
1863                        subnet_to_address_with_prefix(update_prefix),
1864                        subnet_to_address_with_prefix(add_prefix),
1865                    ],
1866                ),
1867            }
1868        }
1869    }
1870
1871    #[fuchsia::test]
1872    async fn test_client_schedule_and_cancel_timers() {
1873        let (_client_end, client_stream) = create_request_stream::<ClientMarker>();
1874
1875        let (client_socket, _client_addr) = create_test_socket();
1876        let (_server_socket, server_addr) = create_test_socket();
1877        let mut client = Client::<fasync::net::UdpSocket>::start(
1878            None,
1879            [1, 2, 3], /* transaction ID */
1880            STATELESS_CLIENT_CONFIG,
1881            1, /* interface ID */
1882            || Ok(client_socket),
1883            server_addr,
1884            client_stream,
1885        )
1886        .await
1887        .expect("failed to create test client");
1888
1889        // Stateless DHCP client starts by scheduling a retransmission timer.
1890        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1891
1892        let () = client.cancel_timer(dhcpv6_core::client::ClientTimerType::Retransmission);
1893        client.assert_scheduled([]);
1894
1895        let now = MonotonicInstant::now();
1896        let () = client.schedule_timer(
1897            dhcpv6_core::client::ClientTimerType::Refresh,
1898            now + Duration::from_nanos(1),
1899        );
1900        let () = client.schedule_timer(
1901            dhcpv6_core::client::ClientTimerType::Retransmission,
1902            now + Duration::from_nanos(2),
1903        );
1904        client.assert_scheduled([
1905            dhcpv6_core::client::ClientTimerType::Retransmission,
1906            dhcpv6_core::client::ClientTimerType::Refresh,
1907        ]);
1908
1909        // We are allowed to reschedule a timer to fire at a new time.
1910        let now = MonotonicInstant::now();
1911        client.schedule_timer(
1912            dhcpv6_core::client::ClientTimerType::Refresh,
1913            now + Duration::from_nanos(1),
1914        );
1915        client.schedule_timer(
1916            dhcpv6_core::client::ClientTimerType::Retransmission,
1917            now + Duration::from_nanos(2),
1918        );
1919
1920        let () = client.cancel_timer(dhcpv6_core::client::ClientTimerType::Refresh);
1921        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1922
1923        // Ok to cancel a timer that is not scheduled.
1924        client.cancel_timer(dhcpv6_core::client::ClientTimerType::Refresh);
1925
1926        let () = client.cancel_timer(dhcpv6_core::client::ClientTimerType::Retransmission);
1927        client.assert_scheduled([]);
1928
1929        // Ok to cancel a timer that is not scheduled.
1930        client.cancel_timer(dhcpv6_core::client::ClientTimerType::Retransmission);
1931    }
1932
1933    #[fuchsia::test]
1934    async fn test_handle_next_event_on_stateless_client() {
1935        let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
1936
1937        let (client_socket, client_addr) = create_test_socket();
1938        let (server_socket, server_addr) = create_test_socket();
1939        let mut client = Client::<fasync::net::UdpSocket>::start(
1940            None,
1941            [1, 2, 3], /* transaction ID */
1942            STATELESS_CLIENT_CONFIG,
1943            1, /* interface ID */
1944            || Ok(client_socket),
1945            server_addr,
1946            client_stream,
1947        )
1948        .await
1949        .expect("failed to create test client");
1950
1951        // Starting the client in stateless should send an information request out.
1952        let ReceivedMessage { client_id, transaction_id: _ } = assert_received_message(
1953            &server_socket,
1954            client_addr,
1955            v6::MessageType::InformationRequest,
1956        )
1957        .await;
1958        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1959
1960        let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
1961        // Trigger a retransmission.
1962        assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
1963        let ReceivedMessage { client_id: got_client_id, transaction_id: _ } =
1964            assert_received_message(
1965                &server_socket,
1966                client_addr,
1967                v6::MessageType::InformationRequest,
1968            )
1969            .await;
1970        assert_eq!(got_client_id, client_id);
1971        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1972
1973        // Message targeting another transaction ID should be ignored.
1974        let () = send_msg_with_options(
1975            &server_socket,
1976            client_addr,
1977            [5, 6, 7],
1978            v6::MessageType::Reply,
1979            &[],
1980        )
1981        .await
1982        .expect("failed to send test message");
1983        assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
1984        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1985
1986        // Invalid messages should be discarded. Empty buffer is invalid.
1987        let size =
1988            server_socket.send_to(&[], client_addr).await.expect("failed to send test message");
1989        assert_eq!(size, 0);
1990        assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
1991        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1992
1993        // Message targeting this client should cause the client to transition state.
1994        let () = send_msg_with_options(
1995            &server_socket,
1996            client_addr,
1997            [1, 2, 3],
1998            v6::MessageType::Reply,
1999            &[v6::DhcpOption::ServerId(&[4, 5, 6])],
2000        )
2001        .await
2002        .expect("failed to send test message");
2003        assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
2004        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Refresh]);
2005
2006        // Reschedule a shorter timer for Refresh so we don't spend time waiting in test.
2007        client.schedule_timer(
2008            dhcpv6_core::client::ClientTimerType::Refresh,
2009            MonotonicInstant::now() + Duration::from_nanos(1),
2010        );
2011
2012        // Trigger a refresh.
2013        assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
2014        let ReceivedMessage { client_id, transaction_id: _ } = assert_received_message(
2015            &server_socket,
2016            client_addr,
2017            v6::MessageType::InformationRequest,
2018        )
2019        .await;
2020        assert_eq!(got_client_id, client_id,);
2021        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
2022
2023        let test_fut = async {
2024            assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
2025            client
2026                .dns_responder
2027                .take()
2028                .expect("test client did not get a channel responder")
2029                .send(&[fnet_name::DnsServer_ {
2030                    address: Some(fidl_socket_addr!("[fe01::2:3]:42")),
2031                    source: Some(fnet_name::DnsServerSource::Dhcpv6(
2032                        fnet_name::Dhcpv6DnsServerSource {
2033                            source_interface: Some(42),
2034                            ..Default::default()
2035                        },
2036                    )),
2037                    ..Default::default()
2038                }])
2039                .expect("failed to send response on test channel");
2040        };
2041        let (watcher_res, ()) = join!(client_proxy.watch_servers(), test_fut);
2042        let servers = watcher_res.expect("failed to watch servers");
2043        assert_eq!(
2044            servers,
2045            vec![fnet_name::DnsServer_ {
2046                address: Some(fidl_socket_addr!("[fe01::2:3]:42")),
2047                source: Some(fnet_name::DnsServerSource::Dhcpv6(
2048                    fnet_name::Dhcpv6DnsServerSource {
2049                        source_interface: Some(42),
2050                        ..Default::default()
2051                    },
2052                )),
2053                ..Default::default()
2054            }]
2055        );
2056
2057        // Drop the channel should cause `handle_next_event(&mut buf)` to return `None`.
2058        drop(client_proxy);
2059        assert_matches!(client.handle_next_event(&mut buf).await, Ok(None));
2060    }
2061
2062    #[fuchsia::test]
2063    async fn test_handle_next_event_on_stateful_client() {
2064        let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
2065
2066        let (client_socket, client_addr) = create_test_socket();
2067        let (server_socket, server_addr) = create_test_socket();
2068        let mut client = Client::<fasync::net::UdpSocket>::start(
2069            Some(CLIENT_ID.into()),
2070            [1, 2, 3], /* transaction ID */
2071            ClientConfig {
2072                information_config: Default::default(),
2073                non_temporary_address_config: AddressConfig {
2074                    address_count: 1,
2075                    preferred_addresses: None,
2076                },
2077                prefix_delegation_config: None,
2078            },
2079            1, /* interface ID */
2080            || Ok(client_socket),
2081            server_addr,
2082            client_stream,
2083        )
2084        .await
2085        .expect("failed to create test client");
2086
2087        // Starting the client in stateful should send out a solicit.
2088        let _: ReceivedMessage =
2089            assert_received_message(&server_socket, client_addr, v6::MessageType::Solicit).await;
2090        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
2091
2092        let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
2093        // Drop the channel should cause `handle_next_event(&mut buf)` to return `None`.
2094        drop(client_proxy);
2095        assert_matches!(client.handle_next_event(&mut buf).await, Ok(None));
2096    }
2097
2098    #[fuchsia::test]
2099    #[should_panic = "received unexpected refresh timeout in state InformationRequesting"]
2100    async fn test_handle_next_event_respects_timer_order() {
2101        let (_client_end, client_stream) = create_request_stream::<ClientMarker>();
2102
2103        let (client_socket, client_addr) = create_test_socket();
2104        let (server_socket, server_addr) = create_test_socket();
2105        let mut client = Client::<fasync::net::UdpSocket>::start(
2106            None,
2107            [1, 2, 3], /* transaction ID */
2108            STATELESS_CLIENT_CONFIG,
2109            1, /* interface ID */
2110            || Ok(client_socket),
2111            server_addr,
2112            client_stream,
2113        )
2114        .await
2115        .expect("failed to create test client");
2116
2117        let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
2118        // A retransmission timer is scheduled when starting the client in stateless mode. Cancel
2119        // it and create a new one with a longer timeout so the test is not flaky.
2120        let () = client.schedule_timer(
2121            dhcpv6_core::client::ClientTimerType::Retransmission,
2122            MonotonicInstant::now() + Duration::from_secs(1_000_000),
2123        );
2124        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
2125
2126        // Trigger a message receive, the message is later discarded because transaction ID doesn't
2127        // match.
2128        let () = send_msg_with_options(
2129            &server_socket,
2130            client_addr,
2131            [5, 6, 7],
2132            v6::MessageType::Reply,
2133            &[],
2134        )
2135        .await
2136        .expect("failed to send test message");
2137        // There are now two pending events, the message receive is handled first because the timer
2138        // is far into the future.
2139        assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
2140        // The retransmission timer is still here.
2141        client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
2142
2143        // Inserts a refresh timer that precedes the retransmission.
2144        let () = client.schedule_timer(
2145            dhcpv6_core::client::ClientTimerType::Refresh,
2146            MonotonicInstant::now() + Duration::from_nanos(1),
2147        );
2148        // This timer is scheduled.
2149        client.assert_scheduled([
2150            dhcpv6_core::client::ClientTimerType::Retransmission,
2151            dhcpv6_core::client::ClientTimerType::Refresh,
2152        ]);
2153
2154        // Now handle_next_event(&mut buf) should trigger a refresh because it
2155        // precedes retransmission. Refresh is not expected while in
2156        // InformationRequesting state and should lead to a panic.
2157        let unreachable = client.handle_next_event(&mut buf).await;
2158        panic!("{unreachable:?}");
2159    }
2160
2161    #[fuchsia::test]
2162    async fn test_handle_next_event_fails_on_recv_err() {
2163        struct StubSocket {}
2164        impl<'a> AsyncSocket<'a> for StubSocket {
2165            type RecvFromFut = futures::future::Ready<Result<(usize, SocketAddr), std::io::Error>>;
2166            type SendToFut = futures::future::Ready<Result<usize, std::io::Error>>;
2167
2168            fn recv_from(&'a self, _buf: &'a mut [u8]) -> Self::RecvFromFut {
2169                futures::future::ready(Err(std::io::Error::other("test recv error")))
2170            }
2171            fn send_to(&'a self, buf: &'a [u8], _addr: SocketAddr) -> Self::SendToFut {
2172                futures::future::ready(Ok(buf.len()))
2173            }
2174        }
2175
2176        let (_client_end, client_stream) = create_request_stream::<ClientMarker>();
2177
2178        let mut client = Client::<StubSocket>::start(
2179            None,
2180            [1, 2, 3], /* transaction ID */
2181            STATELESS_CLIENT_CONFIG,
2182            1, /* interface ID */
2183            || Ok(StubSocket {}),
2184            std_socket_addr!("[::1]:0"),
2185            client_stream,
2186        )
2187        .await
2188        .expect("failed to create test client");
2189
2190        assert_matches!(
2191            client.handle_next_event(&mut [0u8]).await,
2192            Err(ClientError::SocketRecv(err)) if err.kind() == std::io::ErrorKind::Other
2193        );
2194    }
2195}