1use std::collections::{HashMap, HashSet};
9use std::net::IpAddr;
10use std::num::NonZeroU64;
11
12use crate::Errno;
13use crate::client::InternalClient;
14use crate::logging::{log_debug, log_warn};
15use crate::messaging::Sender;
16use crate::protocol_family::ProtocolFamily;
17use crate::protocol_family::route::NetlinkRoute;
18use crate::util::respond_to_completer;
19use derivative::Derivative;
20use futures::StreamExt as _;
21use futures::channel::oneshot;
22use net_types::ip::IpVersion;
23use netlink_packet_core::{NLM_F_MULTIPART, NetlinkMessage};
24use netlink_packet_route::neighbour::{
25 NeighbourAddress, NeighbourAttribute, NeighbourFlags, NeighbourHeader, NeighbourMessage,
26 NeighbourState,
27};
28use netlink_packet_route::route::RouteType;
29use netlink_packet_route::{AddressFamily, RouteNetlinkMessage};
30use thiserror::Error;
31
32use {
33 fidl_fuchsia_net as fnet, fidl_fuchsia_net_ext as fnet_ext,
34 fidl_fuchsia_net_neighbor as fnet_neighbor, fidl_fuchsia_net_neighbor_ext as fnet_neighbor_ext,
35};
36
37#[derive(Debug, PartialEq)]
39pub(crate) enum NetlinkNeighborMessageConversionError {
40 InvalidInterfaceId(u64),
42}
43
44#[derive(Clone, Debug, Eq, PartialEq)]
47pub(crate) struct NetlinkNeighborMessage(pub(crate) NeighbourMessage);
48
49impl NetlinkNeighborMessage {
50 pub(crate) fn optionally_from(
51 neighbor: fnet_neighbor_ext::Entry,
52 ) -> Option<NetlinkNeighborMessage> {
53 match neighbor.try_into() {
54 Ok(message) => Some(message),
55 Err(NetlinkNeighborMessageConversionError::InvalidInterfaceId(id)) => {
56 log_warn!("Invalid interface id found in neighbor table entry: {}", id);
57 None
58 }
59 }
60 }
61
62 pub(crate) fn into_rtnl_new_neighbor(
64 self,
65 sequence_number: u32,
66 is_dump: bool,
67 ) -> NetlinkMessage<RouteNetlinkMessage> {
68 let NetlinkNeighborMessage(message) = self;
69 let mut msg: NetlinkMessage<RouteNetlinkMessage> =
70 RouteNetlinkMessage::NewNeighbour(message).into();
71 msg.header.sequence_number = sequence_number;
72 if is_dump {
73 msg.header.flags |= NLM_F_MULTIPART;
74 }
75 msg.finalize();
76 msg
77 }
78}
79
80impl TryFrom<fnet_neighbor_ext::Entry> for NetlinkNeighborMessage {
81 type Error = NetlinkNeighborMessageConversionError;
82
83 fn try_from(
84 neighbor: fnet_neighbor_ext::Entry,
85 ) -> Result<NetlinkNeighborMessage, NetlinkNeighborMessageConversionError> {
86 let mut header = NeighbourHeader::default();
87 let fnet_ext::IpAddress(addr) = neighbor.neighbor.into();
88 header.family = match addr {
89 IpAddr::V4(_) => AddressFamily::Inet,
90 IpAddr::V6(_) => AddressFamily::Inet6,
91 };
92 header.ifindex = neighbor.interface.try_into().map_err(|_| {
93 NetlinkNeighborMessageConversionError::InvalidInterfaceId(neighbor.interface)
94 })?;
95 header.state = match neighbor.state {
96 fnet_neighbor::EntryState::Delay => NeighbourState::Delay,
97 fnet_neighbor::EntryState::Incomplete => NeighbourState::Incomplete,
98 fnet_neighbor::EntryState::Probe => NeighbourState::Probe,
99 fnet_neighbor::EntryState::Reachable => NeighbourState::Reachable,
100 fnet_neighbor::EntryState::Stale => NeighbourState::Stale,
101 fnet_neighbor::EntryState::Static => NeighbourState::Permanent,
102 fnet_neighbor::EntryState::Unreachable => NeighbourState::Failed,
103 };
104 header.kind = RouteType::Unspec;
106
107 let mut attributes = vec![];
108 attributes.push(NeighbourAttribute::Destination(match addr {
109 IpAddr::V4(addr) => addr.into(),
110 IpAddr::V6(addr) => addr.into(),
111 }));
112 if let Some(mac) = neighbor.mac {
113 attributes.push(NeighbourAttribute::LinkLocalAddress(mac.octets.into()));
114 }
115 let mut msg = NeighbourMessage::default();
119 msg.header = header;
120 msg.attributes = attributes;
121 Ok(NetlinkNeighborMessage(msg))
122 }
123}
124
125#[derive(Copy, Clone, Debug, PartialEq, Eq)]
127pub(crate) enum GetNeighborArgs {
128 Dump { ip_version: Option<IpVersion>, interface: Option<NonZeroU64> },
129 Get { ip: fnet::IpAddress, interface: NonZeroU64 },
130}
131
132impl GetNeighborArgs {
133 pub(crate) fn try_from_rtnl_neighbor(
136 message: &NeighbourMessage,
137 is_dump: bool,
138 ) -> Result<Self, RequestError> {
139 if is_dump {
140 Self::dump_request_from_rtnl_neighbor(message)
141 } else {
142 Self::get_request_from_rtnl_neighbor(message)
143 }
144 }
145
146 fn dump_request_from_rtnl_neighbor(message: &NeighbourMessage) -> Result<Self, RequestError> {
147 let NeighbourHeader { family, flags, .. } = &message.header;
148 if flags.contains(NeighbourFlags::Proxy) {
149 log_warn!("unsupported flags in header for dump neighbor request: {flags:?}");
151 return Err(RequestError::UnsupportedRequest);
152 }
153 let ip_version = match family {
156 AddressFamily::Unspec => None,
157 AddressFamily::Inet => Some(IpVersion::V4),
158 AddressFamily::Inet6 => Some(IpVersion::V6),
159 family => {
160 log_warn!("invalid address family ({family:?}) in dump neighbors request");
161 return Err(RequestError::InvalidRequest);
162 }
163 };
164 let interface = message
168 .attributes
169 .iter()
170 .find_map(|attr| match attr {
171 NeighbourAttribute::IfIndex(ifindex) => Some(u64::from(*ifindex).try_into()),
172 _ => None,
173 })
174 .transpose()
175 .unwrap_or(None);
177 Ok(GetNeighborArgs::Dump { ip_version, interface })
178 }
179
180 fn get_request_from_rtnl_neighbor(message: &NeighbourMessage) -> Result<Self, RequestError> {
181 let NeighbourHeader { ifindex, family, state, flags, kind } = &message.header;
182 if *state != NeighbourState::None {
183 log_warn!("invalid state in header for get neighbor request: {state:?}");
184 return Err(RequestError::InvalidRequest);
185 }
186 if *kind != RouteType::Unspec {
187 log_warn!("invalid kind in header for get neighbor request: {kind:?}");
188 return Err(RequestError::InvalidRequest);
189 }
190 if flags.intersects(!NeighbourFlags::Proxy) {
191 log_warn!("invalid flags in header for get neighbor request: {flags:?}");
192 return Err(RequestError::InvalidRequest);
193 }
194 if flags.contains(NeighbourFlags::Proxy) {
195 log_warn!("unsupported flags in header for get neighbor request: {flags:?}");
197 return Err(RequestError::UnsupportedRequest);
198 }
199
200 let (address, unsupported) = message.attributes.iter().fold(
201 (None, false),
202 |(address_acc, unsupported_acc), attr| {
203 match attr {
204 NeighbourAttribute::Destination(addr) => {
205 (address_acc.or(Some(addr)), unsupported_acc)
208 }
209 _ => {
210 if !unsupported_acc {
211 log_warn!(
213 "unsupported request attribute: {attr:?} in get neighbor\
214 request; only `DST` is supported"
215 );
216 }
217 (address_acc, true)
218 }
219 }
220 },
221 );
222 if unsupported {
223 return Err(RequestError::InvalidRequest);
224 }
225 let ip = match address {
226 Some(NeighbourAddress::Inet(addr)) => fnet_ext::IpAddress(IpAddr::V4(*addr)).into(),
227 Some(NeighbourAddress::Inet6(addr)) => fnet_ext::IpAddress(IpAddr::V6(*addr)).into(),
228 Some(_) => {
229 log_warn!("invalid neighbor address: {address:?} in get neighbor request");
230 return Err(RequestError::InvalidRequest);
231 }
232 None => {
233 log_warn!("get neighbor request missing required `DST` attribute");
234 return Err(RequestError::InvalidRequest);
235 }
236 };
237 let expected_family = match ip {
238 fnet::IpAddress::Ipv4(_) => AddressFamily::Inet,
239 fnet::IpAddress::Ipv6(_) => AddressFamily::Inet6,
240 };
241 if *family != expected_family {
242 log_warn!(
243 "address family mismatch in get neighbor request;\
244 destination={ip:?} family={family:?}"
245 );
246 return Err(RequestError::InvalidRequest);
247 }
248 let interface = u64::from(*ifindex).try_into().map_err(|_| {
252 log_warn!("get neighbor request header must specify interface");
253 RequestError::InvalidRequest
254 })?;
255 Ok(GetNeighborArgs::Get { ip, interface })
256 }
257}
258
259#[derive(Copy, Clone, Debug, PartialEq, Eq)]
261pub(crate) enum NeighborRequestArgs {
262 Get(GetNeighborArgs),
264}
265
266#[derive(Copy, Clone, Debug, PartialEq, Eq)]
268pub(crate) enum RequestError {
269 UnsupportedRequest,
271 InvalidRequest,
273 NotFound,
275}
276
277impl From<RequestError> for Errno {
278 fn from(value: RequestError) -> Self {
279 match value {
280 RequestError::UnsupportedRequest => Errno::ENOTSUP,
281 RequestError::InvalidRequest => Errno::EINVAL,
282 RequestError::NotFound => Errno::ENOENT,
283 }
284 }
285}
286
287#[derive(Derivative)]
289#[derivative(Debug(bound = ""))]
290pub(crate) struct Request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>> {
291 pub args: NeighborRequestArgs,
293 pub sequence_number: u32,
298 pub client: InternalClient<NetlinkRoute, S>,
300 pub completer: oneshot::Sender<Result<(), RequestError>>,
302}
303
304#[derive(Debug, Error, PartialEq)]
306pub(crate) enum HandleWatchEventError {
307 #[error("Netstack reported removal of an unknown neighbor: {0:?}")]
309 UnknownNeighborRemoved(fnet_neighbor_ext::Entry),
310 #[error("Netstack reported change of an unknown neighbor: {0:?}")]
312 UnknownNeighborChanged(fnet_neighbor_ext::Entry),
313 #[error(
316 "Netstack reported addition of a neighbor that already exists: \
317 existing={existing:?}, new={new:?}"
318 )]
319 ConflictingNeighborAdded { existing: fnet_neighbor_ext::Entry, new: fnet_neighbor_ext::Entry },
320 #[error("Netstack reported unexpected event: {0:?}")]
323 UnexpectedEventReceived(fnet_neighbor_ext::Event),
324}
325
326#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
327struct NeighborKey {
328 interface: u64,
329 neighbor: fnet::IpAddress,
330}
331
332impl From<&fnet_neighbor_ext::Entry> for NeighborKey {
333 fn from(
334 fnet_neighbor_ext::Entry { interface, neighbor, .. }: &fnet_neighbor_ext::Entry,
335 ) -> NeighborKey {
336 NeighborKey { interface: *interface, neighbor: *neighbor }
337 }
338}
339
340pub(crate) struct NeighborsWorker {
344 neighbor_table: HashMap<NeighborKey, fnet_neighbor_ext::Entry>,
345}
346
347impl NeighborsWorker {
348 pub(crate) async fn create(
353 neighbors_view: &fnet_neighbor::ViewProxy,
354 ) -> (
355 Self,
356 impl futures::Stream<
357 Item = Result<fnet_neighbor_ext::Event, fnet_neighbor_ext::EntryIteratorError>,
358 > + Unpin
359 + 'static,
360 ) {
361 let mut neighbor_event_stream = Box::pin(
362 fnet_neighbor_ext::event_stream_from_view(neighbors_view)
363 .expect("connecting to fuchsia.net.neighbors.View FIDL should succeed"),
364 );
365 let existing_neighbors: HashSet<fnet_neighbor_ext::Entry> =
366 fnet_neighbor_ext::collect_neighbors_until_idle(neighbor_event_stream.by_ref())
367 .await
368 .expect("determining existing neighbors should succeed");
369 let existing_count = existing_neighbors.len();
370 let neighbor_table = existing_neighbors
371 .into_iter()
372 .map(|e| (NeighborKey::from(&e), e))
373 .collect::<HashMap<_, _>>();
374 assert_eq!(
375 neighbor_table.len(),
376 existing_count,
377 "conflicting existing entry in neighbor table"
378 );
379 (Self { neighbor_table }, neighbor_event_stream)
380 }
381
382 pub(crate) fn handle_neighbor_watcher_event(
383 &mut self,
384 event: fnet_neighbor_ext::Event,
385 ) -> Result<(), HandleWatchEventError> {
386 match event {
387 fnet_neighbor_ext::Event::Removed(entry) => {
388 match self.neighbor_table.remove(&(&entry).into()) {
389 Some(_) => Ok(()),
390 None => Err(HandleWatchEventError::UnknownNeighborRemoved(entry)),
391 }
392 }
393 fnet_neighbor_ext::Event::Added(entry) => {
394 match self.neighbor_table.insert((&entry).into(), entry.clone()) {
395 Some(existing) => Err(HandleWatchEventError::ConflictingNeighborAdded {
396 existing,
397 new: entry,
398 }),
399 None => Ok(()),
400 }
401 }
402 fnet_neighbor_ext::Event::Changed(entry) => {
403 match self.neighbor_table.insert((&entry).into(), entry.clone()) {
404 Some(_) => Ok(()),
405 None => Err(HandleWatchEventError::UnknownNeighborChanged(entry)),
406 }
407 }
408 e @ fnet_neighbor_ext::Event::Existing(_) | e @ fnet_neighbor_ext::Event::Idle => {
409 Err(HandleWatchEventError::UnexpectedEventReceived(e))
410 }
411 }
412 }
413
414 pub(crate) fn handle_request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>>(
415 &mut self,
416 Request { args, mut client, sequence_number, completer }: Request<S>,
417 ) {
418 let result = match args {
419 NeighborRequestArgs::Get(args) => match args {
420 GetNeighborArgs::Dump { ip_version, interface } => {
421 self.neighbor_table
422 .values()
423 .filter(|n| {
424 ip_version.map_or(true, |ip_version| match n.neighbor {
425 fnet::IpAddress::Ipv4(_) => ip_version == IpVersion::V4,
426 fnet::IpAddress::Ipv6(_) => ip_version == IpVersion::V6,
427 })
428 })
429 .filter(|n| interface.map_or(true, |i| n.interface == i.get()))
430 .filter_map(|e| NetlinkNeighborMessage::optionally_from(e.clone()))
431 .for_each(|m| {
432 client.send_unicast(m.into_rtnl_new_neighbor(sequence_number, true));
433 });
434 Ok(())
435 }
436 GetNeighborArgs::Get { ip, interface } => {
437 let neighbor = self
438 .neighbor_table
439 .get(&NeighborKey { interface: interface.get(), neighbor: ip })
440 .map(|e| NetlinkNeighborMessage::optionally_from(e.clone()))
441 .flatten();
442 match neighbor {
443 Some(msg) => {
444 client.send_unicast(msg.into_rtnl_new_neighbor(sequence_number, false));
445 Ok(())
446 }
447 None => Err(RequestError::NotFound),
448 }
449 }
450 },
451 };
452
453 log_debug!("handled request {args:?} from {client} with result = {result:?}");
454 respond_to_completer(client, completer, result, args);
455 }
456}
457
458#[cfg(test)]
459mod tests {
460 use crate::client::testutil::{CLIENT_ID_1, new_fake_client};
461 use crate::interfaces::testutil::FakeInterfacesHandler;
462 use crate::messaging::testutil::FakeSender;
463 use crate::route_eventloop::{
464 EventLoopComponent, EventLoopInputs, EventLoopSpec, IncludedWorkers, Optional, Required,
465 UnifiedRequest,
466 };
467
468 use super::*;
469
470 use assert_matches::assert_matches;
471 use fidl_fuchsia_net as fnet;
472 use fidl_fuchsia_net_neighbor::ViewRequest;
473 use fidl_fuchsia_net_neighbor_ext::testutil::EventSpec;
474 use futures::channel::mpsc;
475 use futures::{FutureExt, SinkExt};
476 use net_declare::{fidl_ip, std_ip_v4, std_ip_v6};
477 use netlink_packet_core::NetlinkPayload;
478 use netlink_packet_route::neighbour::{NeighbourAddress, NeighbourFlags};
479 use test_case::test_case;
480
481 fn valid_neighbor_entry() -> fnet_neighbor_ext::Entry {
482 fnet_neighbor_ext::Entry {
483 interface: 1,
484 neighbor: fidl_ip!("192.168.0.1"),
485 state: fnet_neighbor::EntryState::Reachable,
486 mac: Some(fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] }),
487 updated_at: 123456,
488 }
489 }
490
491 #[test]
492 fn netlink_neighbor_message_from_entry_invalid_iface_id() {
493 let entry = fnet_neighbor_ext::Entry { interface: u64::MAX, ..valid_neighbor_entry() };
494
495 assert_eq!(
496 NetlinkNeighborMessage::try_from(entry),
497 Err(NetlinkNeighborMessageConversionError::InvalidInterfaceId(u64::MAX))
498 );
499 }
500
501 #[test]
502 fn netlink_neighbor_message_from_entry_valid_iface_id() {
503 assert_matches!(
504 NetlinkNeighborMessage::try_from(fnet_neighbor_ext::Entry {
505 interface: 1,
506 ..valid_neighbor_entry()
507 }),
508 Ok(NetlinkNeighborMessage(NeighbourMessage {
509 header: NeighbourHeader { ifindex: 1, .. },
510 ..
511 }))
512 );
513 }
514
515 #[test_case(fnet_neighbor::EntryState::Delay, NeighbourState::Delay; "delay")]
516 #[test_case(fnet_neighbor::EntryState::Incomplete, NeighbourState::Incomplete; "incomplete")]
517 #[test_case(fnet_neighbor::EntryState::Probe, NeighbourState::Probe; "probe")]
518 #[test_case(fnet_neighbor::EntryState::Reachable, NeighbourState::Reachable; "reachable")]
519 #[test_case(fnet_neighbor::EntryState::Stale, NeighbourState::Stale; "stale")]
520 #[test_case(fnet_neighbor::EntryState::Static, NeighbourState::Permanent; "permanent")]
521 #[test_case(fnet_neighbor::EntryState::Unreachable, NeighbourState::Failed; "failed")]
522 fn netlink_neighbor_message_from_entry_state_converted(
523 fidl_state: fnet_neighbor::EntryState,
524 expected: NeighbourState,
525 ) {
526 assert_matches!(
527 NetlinkNeighborMessage::try_from(fnet_neighbor_ext::Entry {
528 state: fidl_state,
529 ..valid_neighbor_entry()
530 }),
531 Ok(NetlinkNeighborMessage(NeighbourMessage {
532 header: NeighbourHeader { state, .. },
533 ..
534 })) if state == expected
535 );
536 }
537
538 #[test]
539 fn netlink_neighbor_message_from_entry_ipv4() {
540 let fidl_entry = fnet_neighbor_ext::Entry {
541 neighbor: fidl_ip!("192.168.0.1"),
542 ..valid_neighbor_entry()
543 };
544 let NetlinkNeighborMessage(message) =
545 fidl_entry.try_into().expect("should be able to convert valid neighbor entry");
546
547 assert_eq!(message.header.family, AddressFamily::Inet);
548 let expected_address: NeighbourAddress = std_ip_v4!("192.168.0.1").into();
549 assert_matches!(
550 &message.attributes[..],
551 [
552 NeighbourAttribute::Destination(address),
553 NeighbourAttribute::LinkLocalAddress(_)
554 ] if *address == expected_address
555 );
556 }
557
558 #[test]
559 fn netlink_neighbor_message_from_entry_ipv6() {
560 let fidl_entry =
561 fnet_neighbor_ext::Entry { neighbor: fidl_ip!("fe80::1"), ..valid_neighbor_entry() };
562 let NetlinkNeighborMessage(message) =
563 fidl_entry.try_into().expect("should be able to convert valid neighbor entry");
564
565 assert_eq!(message.header.family, AddressFamily::Inet6);
566 let expected_address: NeighbourAddress = std_ip_v6!("fe80::1").into();
567 assert_matches!(
568 &message.attributes[..],
569 [
570 NeighbourAttribute::Destination(address),
571 NeighbourAttribute::LinkLocalAddress(_)
572 ] if *address == expected_address
573 );
574 }
575
576 #[test]
577 fn netlink_neighbor_message_from_entry_address_link_local_present() {
578 let fidl_entry = fnet_neighbor_ext::Entry {
579 mac: Some(fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] }),
580 ..valid_neighbor_entry()
581 };
582 let NetlinkNeighborMessage(message) =
583 fidl_entry.try_into().expect("should be able to convert valid neighbor entry");
584
585 assert_matches!(
586 &message.attributes[..],
587 [
588 NeighbourAttribute::Destination(_),
589 NeighbourAttribute::LinkLocalAddress(addr)
590 ] if addr == &[0, 1, 2, 3, 4, 5]
591 );
592 }
593
594 #[test]
595 fn netlink_neighbor_message_from_entry_address_link_local_absent() {
596 let fidl_entry = fnet_neighbor_ext::Entry { mac: None, ..valid_neighbor_entry() };
597 let NetlinkNeighborMessage(message) =
598 fidl_entry.try_into().expect("should be able to convert valid neighbor entry");
599
600 assert_matches!(&message.attributes[..], [NeighbourAttribute::Destination(_)]);
601 }
602
603 #[test]
604 fn netlink_neighbor_message_optionally_from_failure() {
605 assert_eq!(
606 NetlinkNeighborMessage::optionally_from(fnet_neighbor_ext::Entry {
607 interface: u64::MAX,
608 ..valid_neighbor_entry()
609 }),
610 None
611 );
612 }
613
614 #[test]
615 fn netlink_neighbor_message_optionally_from_success() {
616 let fidl_entry = fnet_neighbor_ext::Entry {
617 interface: 1,
618 neighbor: fidl_ip!("192.168.0.1"),
619 state: fnet_neighbor::EntryState::Reachable,
620 mac: None,
621 updated_at: 123456,
622 };
623
624 let mut expected_message = NeighbourMessage::default();
625 expected_message.header = NeighbourHeader {
626 ifindex: 1,
627 family: AddressFamily::Inet,
628 state: NeighbourState::Reachable,
629 flags: NeighbourFlags::empty(),
630 kind: RouteType::Unspec,
631 };
632 expected_message.attributes =
633 vec![NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into())];
634
635 assert_eq!(
636 NetlinkNeighborMessage::optionally_from(fidl_entry),
637 Some(NetlinkNeighborMessage(expected_message))
638 );
639 }
640
641 #[test]
642 fn netlink_neighbor_message_into_rtnl_new_neighbor() {
643 let message: NetlinkNeighborMessage = valid_neighbor_entry()
644 .try_into()
645 .expect("should be able to convert valid neighbor entry");
646 let NetlinkNeighborMessage(payload) = &message;
647
648 let expected_payload =
649 NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewNeighbour(payload.clone()));
650
651 let result = message.clone().into_rtnl_new_neighbor(1, true);
652 assert_eq!(result.payload, expected_payload);
653 assert_eq!(result.header.sequence_number, 1);
654 assert_eq!(result.header.flags & NLM_F_MULTIPART, NLM_F_MULTIPART);
655
656 let result = message.into_rtnl_new_neighbor(1, false);
657 assert_eq!(result.payload, expected_payload);
658 assert_ne!(result.header.flags & NLM_F_MULTIPART, NLM_F_MULTIPART);
659 }
660
661 #[test]
662 fn neighbor_keyed_by_interface_and_ip() {
663 let entry = fnet_neighbor_ext::Entry {
664 interface: 1,
665 neighbor: fidl_ip!("192.168.0.1"),
666 mac: None,
667 state: fnet_neighbor::EntryState::Reachable,
668 updated_at: 123456,
669 };
670
671 let same_iface_and_ip = fnet_neighbor_ext::Entry {
672 mac: Some(fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] }),
673 state: fnet_neighbor::EntryState::Stale,
674 updated_at: 654321,
675 ..entry
676 };
677 assert_eq!(NeighborKey::from(&entry), NeighborKey::from(&same_iface_and_ip));
678
679 let different_iface = fnet_neighbor_ext::Entry { interface: 2, ..entry };
680 assert_ne!(NeighborKey::from(&entry), NeighborKey::from(&different_iface));
681
682 let different_ip = fnet_neighbor_ext::Entry { neighbor: fidl_ip!("192.168.0.2"), ..entry };
683 assert_ne!(NeighborKey::from(&entry), NeighborKey::from(&different_ip));
684
685 let different_iface_and_ip =
686 fnet_neighbor_ext::Entry { interface: 2, neighbor: fidl_ip!("192.168.0.2"), ..entry };
687 assert_ne!(NeighborKey::from(&entry), NeighborKey::from(&different_iface_and_ip));
688 }
689
690 #[fuchsia::test]
691 #[should_panic(expected = "determining existing neighbors should succeed")]
692 async fn neighbors_worker_create_panics_on_view_protocol_error() {
693 let (view, view_server_end) = fidl::endpoints::create_proxy::<fnet_neighbor::ViewMarker>();
694 drop(view_server_end);
696
697 let (_worker, _remaining) = NeighborsWorker::create(&view).await;
698 }
699
700 #[fuchsia::test]
701 #[should_panic(expected = "determining existing neighbors should succeed")]
702 async fn neighbors_worker_create_panics_on_event_stream_error() {
703 let (view, view_server_end) = fidl::endpoints::create_proxy::<fnet_neighbor::ViewMarker>();
704 let mut view_request_stream = view_server_end.into_stream();
705
706 let entry_iter_fut = view_request_stream
707 .next()
708 .then(|req| {
709 match req
710 .expect("View request_stream unexpectedly ended")
711 .expect("failed to receive `OpenEntryIterator` request")
712 {
713 ViewRequest::OpenEntryIterator { it, .. } => {
714 drop(it);
716 futures::future::ready(())
717 }
718 }
719 })
720 .fuse();
721
722 let worker_fut = NeighborsWorker::create(&view);
723
724 let ((), (_worker, _remaining)) = futures::join!(entry_iter_fut, worker_fut);
725 }
726
727 #[fuchsia::test]
728 #[should_panic(expected = "conflicting existing entry")]
729 async fn neighbors_worker_create_panics_on_conflicting_entry() {
730 let events: Vec<_> = [
731 fnet_neighbor_ext::Entry {
734 state: fnet_neighbor::EntryState::Reachable,
735 ..valid_neighbor_entry()
736 },
737 fnet_neighbor_ext::Entry {
738 state: fnet_neighbor::EntryState::Stale,
739 ..valid_neighbor_entry()
740 },
741 ]
742 .into_iter()
743 .map(Into::into)
744 .map(fnet_neighbor::EntryIteratorItem::Existing)
745 .chain(std::iter::once(fnet_neighbor::EntryIteratorItem::Idle(fnet_neighbor::IdleEvent)))
746 .collect();
747 let batches = vec![events];
748 let (view, server_fut) =
749 fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(batches));
750
751 let worker_fut = NeighborsWorker::create(&view);
752
753 let ((), (_worker, _remaining)) = futures::join!(server_fut, worker_fut);
754 }
755
756 #[fuchsia::test]
757 async fn neighbors_worker_create_success() {
758 use fnet_neighbor_ext::testutil::EventSpec::*;
759 let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
760 Existing(1),
761 Existing(2),
762 Existing(3),
763 Idle,
764 Added(4),
765 ]);
766 let (view, server_fut) =
767 fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
768 events.clone(),
769 ]));
770
771 let worker_fut = NeighborsWorker::create(&view);
772
773 let ((), (worker, event_stream)) = futures::join!(server_fut, worker_fut);
774
775 let remaining_events: Vec<_> = event_stream.collect().await;
776 assert_matches!(
777 &remaining_events[..],
778 [
779 Ok(fnet_neighbor_ext::Event::Added(_)),
780 Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
781 fidl::Error::ClientChannelClosed { .. }
782 ))
783 ]
784 );
785
786 for event in events {
787 match event {
788 fnet_neighbor::EntryIteratorItem::Existing(fidl_entry) => {
789 let entry: fnet_neighbor_ext::Entry = fidl_entry.try_into().unwrap();
790 assert_eq!(worker.neighbor_table.get(&(&entry).into()), Some(&entry));
791 }
792 _ => {}
793 }
794 }
795 }
796
797 #[test_case(
798 EventSpec::Added(2),
799 |e| matches!(e, HandleWatchEventError::ConflictingNeighborAdded { .. });
800 "conflicting added"
801 )]
802 #[test_case(
803 EventSpec::Removed(4),
804 |e| matches!(e, HandleWatchEventError::UnknownNeighborRemoved(_));
805 "unknown removed"
806 )]
807 #[test_case(
808 EventSpec::Changed(4),
809 |e| matches!(e, HandleWatchEventError::UnknownNeighborChanged(_));
810 "unknown changed"
811 )]
812 #[test_case(
813 EventSpec::Existing(4),
814 |e| matches!(e, HandleWatchEventError::UnexpectedEventReceived(_));
815 "existing after initial collection"
816 )]
817 #[test_case(
818 EventSpec::Idle,
819 |e| matches!(e, HandleWatchEventError::UnexpectedEventReceived(_));
820 "idle after initial collection"
821 )]
822 #[fuchsia::test]
823 async fn neighbors_worker_handle_watch_event_failure(
824 spec: EventSpec,
825 error_matcher: fn(&HandleWatchEventError) -> bool,
826 ) {
827 use fnet_neighbor_ext::testutil::EventSpec::*;
828 let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
829 Existing(1),
830 Existing(2),
831 Existing(3),
832 Idle,
833 spec,
834 ]);
835 let (view, server_fut) =
836 fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
837 events.clone(),
838 ]));
839
840 let worker_fut = NeighborsWorker::create(&view);
841
842 let ((), (mut worker, event_stream)) = futures::join!(server_fut, worker_fut);
843
844 let remaining_events: Vec<_> = event_stream.collect().await;
845 assert_eq!(remaining_events.len(), 2);
846 match &remaining_events[0] {
847 Ok(event) => {
848 assert_matches!(
849 worker.handle_neighbor_watcher_event(event.clone()),
850 Err(error) if error_matcher(&error)
851 );
852 }
853 _ => panic!("expected bad event in stream"),
854 }
855 match &remaining_events[1] {
856 Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
857 fidl::Error::ClientChannelClosed { .. },
858 )) => {}
859 _ => panic!("expected PEER_CLOSED error at end of stream"),
860 }
861 }
862
863 #[fuchsia::test]
864 async fn neighbors_worker_handle_added_event() {
865 use fnet_neighbor_ext::testutil::EventSpec::*;
866 let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
867 Existing(1),
868 Existing(2),
869 Existing(3),
870 Idle,
871 Added(4),
872 ]);
873 let (view, server_fut) =
874 fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
875 events.clone(),
876 ]));
877
878 let worker_fut = NeighborsWorker::create(&view);
879
880 let ((), (mut worker, event_stream)) = futures::join!(server_fut, worker_fut);
881
882 let remaining_events: Vec<_> = event_stream.collect().await;
883 assert_eq!(remaining_events.len(), 2);
884 match &remaining_events[0] {
885 Ok(e @ fnet_neighbor_ext::Event::Added(entry)) => {
886 let key = NeighborKey::from(entry);
887 assert_eq!(worker.neighbor_table.get(&key), None);
888 assert_matches!(worker.handle_neighbor_watcher_event(e.clone()), Ok(_));
889 assert_eq!(worker.neighbor_table.get(&key), Some(entry));
890 }
891 _ => panic!("expected Added event in stream"),
892 }
893 match &remaining_events[1] {
894 Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
895 fidl::Error::ClientChannelClosed { .. },
896 )) => {}
897 _ => panic!("expected PEER_CLOSED error at end of stream"),
898 }
899 }
900
901 #[fuchsia::test]
902 async fn neighbors_worker_handle_removed_event() {
903 use fnet_neighbor_ext::testutil::EventSpec::*;
904 let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
905 Existing(1),
906 Existing(2),
907 Existing(3),
908 Idle,
909 Removed(2),
910 ]);
911 let (view, server_fut) =
912 fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
913 events.clone(),
914 ]));
915
916 let worker_fut = NeighborsWorker::create(&view);
917
918 let ((), (mut worker, event_stream)) = futures::join!(server_fut, worker_fut);
919
920 let remaining_events: Vec<_> = event_stream.collect().await;
921 assert_eq!(remaining_events.len(), 2);
922 match &remaining_events[0] {
923 Ok(e @ fnet_neighbor_ext::Event::Removed(entry)) => {
924 let key = NeighborKey::from(entry);
925 assert_eq!(worker.neighbor_table.get(&key), Some(entry));
926 assert_matches!(worker.handle_neighbor_watcher_event(e.clone()), Ok(_));
927 assert_eq!(worker.neighbor_table.get(&key), None);
928 }
929 _ => panic!("expected Removed event in stream"),
930 }
931 match &remaining_events[1] {
932 Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
933 fidl::Error::ClientChannelClosed { .. },
934 )) => {}
935 _ => panic!("expected PEER_CLOSED error at end of stream"),
936 }
937 }
938
939 #[fuchsia::test]
940 async fn neighbors_worker_handle_changed_event() {
941 use fnet_neighbor_ext::testutil::EventSpec::*;
942 let mut events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
943 Existing(1),
944 Existing(2),
945 Existing(3),
946 Idle,
947 Changed(2),
948 ]);
949 match &mut events[1] {
950 fnet_neighbor::EntryIteratorItem::Existing(entry) => {
951 entry.updated_at = Some(1234);
952 }
953 _ => panic!("expected Existing event in stream"),
954 }
955 match &mut events[4] {
956 fnet_neighbor::EntryIteratorItem::Changed(entry) => {
957 entry.updated_at = Some(5678);
958 }
959 _ => panic!("expected Changed event in stream"),
960 }
961
962 let (view, server_fut) =
963 fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
964 events.clone(),
965 ]));
966
967 let worker_fut = NeighborsWorker::create(&view);
968
969 let ((), (mut worker, event_stream)) = futures::join!(server_fut, worker_fut);
970
971 let remaining_events: Vec<_> = event_stream.collect().await;
972 assert_eq!(remaining_events.len(), 2);
973 match &remaining_events[0] {
974 Ok(e @ fnet_neighbor_ext::Event::Changed(entry)) => {
975 let key = NeighborKey::from(entry);
976 assert_matches!(
977 worker.neighbor_table.get(&key),
978 Some(fnet_neighbor_ext::Entry { updated_at: 1234, .. })
979 );
980 assert_matches!(worker.handle_neighbor_watcher_event(e.clone()), Ok(_));
981 assert_matches!(
982 worker.neighbor_table.get(&key),
983 Some(fnet_neighbor_ext::Entry { updated_at: 5678, .. })
984 );
985 }
986 _ => panic!("expected Changed event in stream"),
987 }
988 match &remaining_events[1] {
989 Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
990 fidl::Error::ClientChannelClosed { .. },
991 )) => {}
992 _ => panic!("expected PEER_CLOSED error at end of stream"),
993 }
994 }
995
996 #[fuchsia::test]
997 async fn neighbors_worker_handle_get_neighbor_not_found() {
998 let (mut sender_sink, client, _async_work_drain_task) =
999 new_fake_client(CLIENT_ID_1, vec![]);
1000 let (completer, completer_rcv) = oneshot::channel();
1001 let request = Request {
1002 args: NeighborRequestArgs::Get(GetNeighborArgs::Get {
1003 ip: fidl_ip!("192.168.0.1"),
1004 interface: NonZeroU64::new(1).unwrap(),
1005 }),
1006 sequence_number: 1,
1007 client,
1008 completer,
1009 };
1010
1011 let events: Vec<_> = [
1012 fnet_neighbor_ext::Entry {
1013 interface: 2,
1014 neighbor: fidl_ip!("192.168.0.1"),
1015 ..valid_neighbor_entry()
1016 },
1017 fnet_neighbor_ext::Entry {
1018 interface: 1,
1019 neighbor: fidl_ip!("fe80::2"),
1020 ..valid_neighbor_entry()
1021 },
1022 ]
1023 .into_iter()
1024 .map(Into::into)
1025 .map(fnet_neighbor::EntryIteratorItem::Existing)
1026 .chain(std::iter::once(fnet_neighbor::EntryIteratorItem::Idle(fnet_neighbor::IdleEvent)))
1027 .collect();
1028
1029 let batches = vec![events];
1030 let (view, server_fut) =
1031 fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(batches));
1032
1033 let worker_fut = NeighborsWorker::create(&view);
1034 let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1035
1036 worker.handle_request(request);
1037
1038 let result = completer_rcv.await.expect("completer channel should not be closed");
1039 assert_matches!(result, Err(RequestError::NotFound));
1040 assert_eq!(&sender_sink.take_messages()[..], &[]);
1041 }
1042
1043 #[test_case(
1044 GetNeighborArgs::Dump{ ip_version: None, interface: None },
1045 &[1, 2, 3, 4];
1046 "dump all"
1047 )]
1048 #[test_case(
1049 GetNeighborArgs::Dump{ ip_version: Some(IpVersion::V4), interface: None },
1050 &[1, 3];
1051 "dump ipv4 only"
1052 )]
1053 #[test_case(
1054 GetNeighborArgs::Dump{ ip_version: Some(IpVersion::V6), interface: None },
1055 &[2, 4];
1056 "dump ipv6 only"
1057 )]
1058 #[test_case(
1059 GetNeighborArgs::Dump{
1060 ip_version: Some(IpVersion::V6),
1061 interface: Some(NonZeroU64::new(4).unwrap())
1062 },
1063 &[4];
1064 "dump interface 4 ipv6"
1065 )]
1066 #[test_case(
1067 GetNeighborArgs::Dump{
1068 ip_version: Some(IpVersion::V4),
1069 interface: Some(NonZeroU64::new(4).unwrap())
1070 },
1071 &[];
1072 "dump interface 4 ipv4"
1073 )]
1074 #[test_case(
1075 GetNeighborArgs::Get{ ip: fidl_ip!("192.168.0.1"), interface: NonZeroU64::new(1).unwrap() },
1076 &[1];
1077 "get ipv4"
1078 )]
1079 #[test_case(
1080 GetNeighborArgs::Get{ ip: fidl_ip!("fe80::2"), interface: NonZeroU64::new(2).unwrap() },
1081 &[2];
1082 "get ipv6"
1083 )]
1084 #[fuchsia::test]
1085 async fn neighbors_worker_handle_get_request(
1086 get_args: GetNeighborArgs,
1087 expected_ifindexes: &[u32],
1088 ) {
1089 let (mut sender_sink, client, _async_work_drain_task) =
1090 new_fake_client(CLIENT_ID_1, vec![]);
1091 let (completer, completer_rcv) = oneshot::channel();
1092 let request = Request {
1093 args: NeighborRequestArgs::Get(get_args),
1094 sequence_number: 1,
1095 client,
1096 completer,
1097 };
1098
1099 let events: Vec<_> = [
1100 fnet_neighbor_ext::Entry {
1101 interface: 1,
1102 neighbor: fidl_ip!("192.168.0.1"),
1103 ..valid_neighbor_entry()
1104 },
1105 fnet_neighbor_ext::Entry {
1106 interface: 2,
1107 neighbor: fidl_ip!("fe80::2"),
1108 ..valid_neighbor_entry()
1109 },
1110 fnet_neighbor_ext::Entry {
1111 interface: 3,
1112 neighbor: fidl_ip!("192.168.0.3"),
1113 ..valid_neighbor_entry()
1114 },
1115 fnet_neighbor_ext::Entry {
1116 interface: 4,
1117 neighbor: fidl_ip!("fe80::4"),
1118 ..valid_neighbor_entry()
1119 },
1120 ]
1121 .into_iter()
1122 .map(Into::into)
1123 .map(fnet_neighbor::EntryIteratorItem::Existing)
1124 .chain(std::iter::once(fnet_neighbor::EntryIteratorItem::Idle(fnet_neighbor::IdleEvent)))
1125 .collect();
1126
1127 let batches = vec![events];
1128 let (view, server_fut) =
1129 fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(batches));
1130
1131 let worker_fut = NeighborsWorker::create(&view);
1132 let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1133
1134 worker.handle_request(request);
1135
1136 completer_rcv
1137 .await
1138 .expect("completer channel should not be closed")
1139 .expect("request handling result should be OK");
1140
1141 let mut ifindexes_seen = Vec::new();
1142 for sent_message in sender_sink.take_messages() {
1143 match sent_message.message.payload {
1144 NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewNeighbour(
1145 NeighbourMessage { header: NeighbourHeader { ifindex, .. }, .. },
1146 )) => {
1147 ifindexes_seen.push(ifindex);
1148 }
1149 _ => panic!("unexpected message sent"),
1150 }
1151 }
1152 ifindexes_seen.sort();
1153 assert_eq!(&ifindexes_seen[..], expected_ifindexes);
1154 }
1155
1156 #[test_case(
1157 false,
1158 NeighbourHeader {
1159 ifindex: 1,
1160 family: AddressFamily::Inet,
1161 ..Default::default()
1162 },
1163 &[
1164 NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
1165 ] => Ok(GetNeighborArgs::Get {
1166 ip: fidl_ip!("192.168.0.1"),
1167 interface: NonZeroU64::new(1).unwrap(),
1168 });
1169 "get ipv4 success"
1170 )]
1171 #[test_case(
1172 false,
1173 NeighbourHeader {
1174 ifindex: 1,
1175 family: AddressFamily::Inet6,
1176 ..Default::default()
1177 },
1178 &[
1179 NeighbourAttribute::Destination(std_ip_v6!("fe80::1").into()),
1180 ] => Ok(GetNeighborArgs::Get {
1181 ip: fidl_ip!("fe80::1"),
1182 interface: NonZeroU64::new(1).unwrap(),
1183 });
1184 "get ipv6 success"
1185 )]
1186 #[test_case(
1187 false,
1188 NeighbourHeader {
1189 ifindex: 1,
1190 family: AddressFamily::Inet,
1191 state: NeighbourState::Reachable,
1192 ..Default::default()
1193 },
1194 &[
1195 NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
1196 ] => Err(RequestError::InvalidRequest);
1197 "get invalid request state"
1198 )]
1199 #[test_case(
1200 false,
1201 NeighbourHeader {
1202 ifindex: 1,
1203 family: AddressFamily::Inet,
1204 kind: RouteType::Broadcast,
1205 ..Default::default()
1206 },
1207 &[
1208 NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
1209 ] => Err(RequestError::InvalidRequest);
1210 "get invalid request kind"
1211 )]
1212 #[test_case(
1213 false,
1214 NeighbourHeader {
1215 ifindex: 1,
1216 family: AddressFamily::Inet,
1217 flags: NeighbourFlags::Router,
1218 ..Default::default()
1219 },
1220 &[
1221 NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
1222 ] => Err(RequestError::InvalidRequest);
1223 "get invalid request flag"
1224 )]
1225 #[test_case(
1226 false,
1227 NeighbourHeader {
1228 ifindex: 1,
1229 family: AddressFamily::Inet,
1230 flags: NeighbourFlags::Proxy,
1231 ..Default::default()
1232 },
1233 &[
1234 NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
1235 ] => Err(RequestError::UnsupportedRequest);
1236 "get unsupported request flag"
1237 )]
1238 #[test_case(
1239 false,
1240 NeighbourHeader {
1241 ifindex: 1,
1242 family: AddressFamily::Inet6,
1243 ..Default::default()
1244 },
1245 &[
1246 NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
1247 ] => Err(RequestError::InvalidRequest);
1248 "get address family mismatch"
1249 )]
1250 #[test_case(
1251 false,
1252 NeighbourHeader {
1253 family: AddressFamily::Inet,
1254 ..Default::default()
1255 },
1256 &[
1257 NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
1258 ] => Err(RequestError::InvalidRequest);
1259 "get interface unspecified"
1260 )]
1261 #[test_case(
1262 false,
1263 NeighbourHeader {
1264 ifindex: 1,
1265 family: AddressFamily::Inet,
1266 ..Default::default()
1267 },
1268 &[] => Err(RequestError::InvalidRequest);
1269 "get destination unspecified"
1270 )]
1271 #[test_case(
1272 false,
1273 NeighbourHeader {
1274 ifindex: 1,
1275 family: AddressFamily::Inet,
1276 ..Default::default()
1277 },
1278 &[
1279 NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
1280 NeighbourAttribute::LinkLocalAddress(vec![0, 1, 2, 3, 4, 5]),
1281 ] => Err(RequestError::InvalidRequest);
1282 "get invalid attribute"
1283 )]
1284 #[test_case(
1285 true,
1286 NeighbourHeader::default(),
1287 &[] => Ok(GetNeighborArgs::Dump {
1288 ip_version: None,
1289 interface: None,
1290 });
1291 "dump all"
1292 )]
1293 #[test_case(
1294 true,
1295 NeighbourHeader {
1296 family: AddressFamily::Inet,
1297 ..Default::default()
1298 },
1299 &[] => Ok(GetNeighborArgs::Dump {
1300 ip_version: Some(IpVersion::V4),
1301 interface: None,
1302 });
1303 "dump ipv4 only"
1304 )]
1305 #[test_case(
1306 true,
1307 NeighbourHeader {
1308 family: AddressFamily::Inet6,
1309 ..Default::default()
1310 },
1311 &[] => Ok(GetNeighborArgs::Dump {
1312 ip_version: Some(IpVersion::V6),
1313 interface: None,
1314 });
1315 "dump ipv6 only"
1316 )]
1317 #[test_case(
1318 true,
1319 NeighbourHeader::default(),
1320 &[
1321 NeighbourAttribute::IfIndex(1),
1322 ] => Ok(GetNeighborArgs::Dump {
1323 ip_version: None,
1324 interface: Some(NonZeroU64::new(1).unwrap()),
1325 });
1326 "dump interface 1"
1327 )]
1328 #[test_case(
1329 true,
1330 NeighbourHeader::default(),
1331 &[
1332 NeighbourAttribute::IfIndex(0),
1333 ] => Ok(GetNeighborArgs::Dump {
1334 ip_version: None,
1335 interface: None,
1336 });
1337 "dump interface 0 treated as all interfaces"
1338 )]
1339 #[test_case(
1340 true,
1341 NeighbourHeader {
1342 family: AddressFamily::Local,
1343 ..Default::default()
1344 },
1345 &[] => Err(RequestError::InvalidRequest);
1346 "dump invalid address family"
1347 )]
1348 #[test_case(
1349 true,
1350 NeighbourHeader::default(),
1351 &[
1352 NeighbourAttribute::LinkLocalAddress(vec![0, 1, 2, 3, 4, 5]),
1353 ] => Ok(GetNeighborArgs::Dump {
1354 ip_version: None,
1355 interface: None,
1356 });
1357 "dump unsupported attribute ignored (non-strict)"
1358 )]
1359 #[test_case(
1360 true,
1361 NeighbourHeader {
1362 flags: NeighbourFlags::Proxy,
1363 ..Default::default()
1364 },
1365 &[] => Err(RequestError::UnsupportedRequest);
1366 "dump unsupported request flag"
1367 )]
1368 #[fuchsia::test]
1369 fn get_neighbor_args_try_from_rtnl_neighbor(
1370 is_dump: bool,
1371 header: NeighbourHeader,
1372 attrs: &[NeighbourAttribute],
1373 ) -> Result<GetNeighborArgs, RequestError> {
1374 let mut message = NeighbourMessage::default();
1375 message.header = header;
1376 message.attributes = attrs.to_vec();
1377 GetNeighborArgs::try_from_rtnl_neighbor(&message, is_dump)
1378 }
1379
1380 #[test_case(RequestError::UnsupportedRequest => Errno::ENOTSUP; "unsupported request")]
1381 #[test_case(RequestError::InvalidRequest => Errno::EINVAL; "invalid request")]
1382 #[test_case(RequestError::NotFound => Errno::ENOENT; "not found")]
1383 #[fuchsia::test]
1384 fn request_error_into_errno(error: RequestError) -> Errno {
1385 error.into()
1386 }
1387
1388 enum OnlyNeighbors {}
1389 impl EventLoopSpec for OnlyNeighbors {
1390 type NeighborWorker = Required;
1391
1392 type InterfacesProxy = Optional;
1393 type InterfacesStateProxy = Optional;
1394 type V4RoutesState = Optional;
1395 type V6RoutesState = Optional;
1396 type V4RoutesSetProvider = Optional;
1397 type V6RoutesSetProvider = Optional;
1398 type V4RouteTableProvider = Optional;
1399 type V6RouteTableProvider = Optional;
1400 type InterfacesHandler = Optional;
1401 type RouteClients = Optional;
1402
1403 type RoutesV4Worker = Optional;
1404 type RoutesV6Worker = Optional;
1405 type InterfacesWorker = Optional;
1406 type RuleV4Worker = Optional;
1407 type RuleV6Worker = Optional;
1408 type NduseroptWorker = Optional;
1409 }
1410
1411 const TEST_SEQUENCE_NUMBER: u32 = 1234;
1412
1413 #[fuchsia::test]
1414 async fn event_loop_with_watch_events_and_get_request() {
1415 let included_workers = IncludedWorkers {
1416 routes_v4: EventLoopComponent::Absent(Optional),
1417 routes_v6: EventLoopComponent::Absent(Optional),
1418 interfaces: EventLoopComponent::Absent(Optional),
1419 rules_v4: EventLoopComponent::Absent(Optional),
1420 rules_v6: EventLoopComponent::Absent(Optional),
1421 neighbors: EventLoopComponent::Present(()),
1422 nduseropt: EventLoopComponent::Absent(Optional),
1423 };
1424 let (mut request_sink, request_stream) = mpsc::channel(1);
1425
1426 let scope = fuchsia_async::Scope::new();
1429 let (neighbors_view, event_sender) = {
1430 use fnet_neighbor_ext::testutil::EventSpec::*;
1431 let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1432 Existing(1),
1433 Existing(2),
1434 Existing(3),
1435 Idle,
1436 Added(4),
1437 ]);
1438 let (event_sender, event_receiver) = mpsc::unbounded();
1439 event_sender.unbounded_send(events).expect("failed to send events");
1440 let (neighbors_view, neighbors_fut) =
1441 fnet_neighbor_ext::testutil::create_fake_view(event_receiver);
1442 let _join_handle = scope.spawn(neighbors_fut);
1443 (neighbors_view, event_sender)
1444 };
1445
1446 let (_async_work_sink, async_work_receiver) = mpsc::unbounded();
1449 let base_inputs: EventLoopInputs<
1450 FakeInterfacesHandler,
1451 FakeSender<RouteNetlinkMessage>,
1452 OnlyNeighbors,
1453 > = EventLoopInputs {
1454 neighbors_view: EventLoopComponent::Present(neighbors_view),
1455
1456 async_work_receiver,
1457
1458 interfaces_handler: EventLoopComponent::Absent(Optional),
1459 route_clients: EventLoopComponent::Absent(Optional),
1460 interfaces_proxy: EventLoopComponent::Absent(Optional),
1461 interfaces_state_proxy: EventLoopComponent::Absent(Optional),
1462 v4_routes_state: EventLoopComponent::Absent(Optional),
1463 v6_routes_state: EventLoopComponent::Absent(Optional),
1464 v4_main_route_table: EventLoopComponent::Absent(Optional),
1465 v6_main_route_table: EventLoopComponent::Absent(Optional),
1466 v4_route_table_provider: EventLoopComponent::Absent(Optional),
1467 v6_route_table_provider: EventLoopComponent::Absent(Optional),
1468 v4_rule_table: EventLoopComponent::Absent(Optional),
1469 v6_rule_table: EventLoopComponent::Absent(Optional),
1470 ndp_option_watcher_provider: EventLoopComponent::Absent(Optional),
1471
1472 unified_request_stream: request_stream,
1473 };
1474
1475 let mut state = base_inputs.initialize(included_workers).await;
1476 state.run_one_step_in_tests().await;
1478
1479 let (mut response_sink, neighbor_client, async_work_drain_task) =
1482 crate::client::testutil::new_fake_client::<NetlinkRoute>(
1483 crate::client::testutil::CLIENT_ID_1,
1484 [],
1485 );
1486 let _join_handle = scope.spawn(async_work_drain_task);
1487
1488 let (completer, waiter) = oneshot::channel();
1489 let get_request: UnifiedRequest<FakeSender<RouteNetlinkMessage>> =
1490 UnifiedRequest::NeighborsRequest(Request {
1491 args: NeighborRequestArgs::Get(GetNeighborArgs::Dump {
1492 ip_version: None,
1493 interface: None,
1494 }),
1495 sequence_number: TEST_SEQUENCE_NUMBER,
1496 client: neighbor_client.clone(),
1497 completer,
1498 });
1499 request_sink.send(get_request).await.unwrap();
1500
1501 state.run_one_step_in_tests().await;
1503 assert_matches!(waiter.await.unwrap(), Ok(()));
1504
1505 let responses = response_sink.take_messages();
1506 assert_eq!(responses.len(), 4); for response in responses {
1508 assert_matches!(
1509 response.message.payload,
1510 NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewNeighbour(_))
1511 );
1512 }
1513
1514 event_sender.close_channel();
1515 drop(neighbor_client);
1516 scope.join().await;
1517 }
1518}