Skip to main content

netlink/protocol_family/
sock_diag.rs

1// Copyright 2025 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//! A module for handling the `NETLINK_SOCK_DIAG` API.
6
7mod eventloop;
8mod request;
9
10pub(crate) use eventloop::SockDiagEventLoop;
11pub(crate) use request::NetlinkSockDiagRequestHandler;
12
13use std::convert::Infallible as Never;
14use std::num::NonZeroU32;
15
16use fidl_fuchsia_net_sockets_ext as fnet_sockets_ext;
17use netlink_packet_sock_diag::{SockDiagRequest, SockDiagResponse};
18
19use crate::client::{AsyncWorkCompletionWaiter, ExternalClient};
20use crate::messaging::{MessageWithPermission, Sender};
21use crate::multicast_groups::{
22    GroupSupport, InvalidLegacyGroupsError, InvalidModernGroupError, LegacyGroups, ModernGroup,
23    MulticastCapableNetlinkFamily,
24};
25use crate::protocol_family::{NamedNetlinkFamily, NetlinkClient, ProtocolFamily};
26
27/// An implementation of the `NETLINK_SOCK_DIAG` protocol family.
28pub(crate) struct NetlinkSockDiag;
29
30impl MulticastCapableNetlinkFamily for NetlinkSockDiag {
31    fn check_support(group: &ModernGroup) -> Result<GroupSupport, InvalidModernGroupError> {
32        if group.0 == linux_uapi::sknetlink_groups_SKNLGRP_NONE {
33            Ok(GroupSupport::Unsupported)
34        } else {
35            NetlinkSockDiagNotifiedGroup::try_from(*group).map(|_| GroupSupport::Supported)
36        }
37    }
38}
39
40#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
41pub(crate) enum NetlinkSockDiagNotifiedGroup {
42    TcpV4Destroy,
43    TcpV6Destroy,
44    UdpV4Destroy,
45    UdpV6Destroy,
46}
47
48impl TryFrom<ModernGroup> for NetlinkSockDiagNotifiedGroup {
49    type Error = InvalidModernGroupError;
50
51    fn try_from(ModernGroup(group): ModernGroup) -> Result<Self, Self::Error> {
52        match group {
53            linux_uapi::sknetlink_groups_SKNLGRP_INET_TCP_DESTROY => {
54                Ok(NetlinkSockDiagNotifiedGroup::TcpV4Destroy)
55            }
56            linux_uapi::sknetlink_groups_SKNLGRP_INET6_TCP_DESTROY => {
57                Ok(NetlinkSockDiagNotifiedGroup::TcpV6Destroy)
58            }
59            linux_uapi::sknetlink_groups_SKNLGRP_INET_UDP_DESTROY => {
60                Ok(NetlinkSockDiagNotifiedGroup::UdpV4Destroy)
61            }
62            linux_uapi::sknetlink_groups_SKNLGRP_INET6_UDP_DESTROY => {
63                Ok(NetlinkSockDiagNotifiedGroup::UdpV6Destroy)
64            }
65            _ => Err(InvalidModernGroupError),
66        }
67    }
68}
69
70impl From<NetlinkSockDiagNotifiedGroup> for ModernGroup {
71    fn from(group: NetlinkSockDiagNotifiedGroup) -> Self {
72        match group {
73            NetlinkSockDiagNotifiedGroup::TcpV4Destroy => {
74                ModernGroup(linux_uapi::sknetlink_groups_SKNLGRP_INET_TCP_DESTROY)
75            }
76            NetlinkSockDiagNotifiedGroup::TcpV6Destroy => {
77                ModernGroup(linux_uapi::sknetlink_groups_SKNLGRP_INET6_TCP_DESTROY)
78            }
79            NetlinkSockDiagNotifiedGroup::UdpV4Destroy => {
80                ModernGroup(linux_uapi::sknetlink_groups_SKNLGRP_INET_UDP_DESTROY)
81            }
82            NetlinkSockDiagNotifiedGroup::UdpV6Destroy => {
83                ModernGroup(linux_uapi::sknetlink_groups_SKNLGRP_INET6_UDP_DESTROY)
84            }
85        }
86    }
87}
88
89impl NetlinkSockDiagNotifiedGroup {
90    pub(crate) fn from_socket_state(socket: &fnet_sockets_ext::IpSocketState) -> Self {
91        match socket {
92            fnet_sockets_ext::IpSocketState::V4(s) => match &s.transport {
93                fnet_sockets_ext::IpSocketTransportState::Tcp(_) => {
94                    NetlinkSockDiagNotifiedGroup::TcpV4Destroy
95                }
96                fnet_sockets_ext::IpSocketTransportState::Udp(_) => {
97                    NetlinkSockDiagNotifiedGroup::UdpV4Destroy
98                }
99            },
100            fnet_sockets_ext::IpSocketState::V6(s) => match &s.transport {
101                fnet_sockets_ext::IpSocketTransportState::Tcp(_) => {
102                    NetlinkSockDiagNotifiedGroup::TcpV6Destroy
103                }
104                fnet_sockets_ext::IpSocketTransportState::Udp(_) => {
105                    NetlinkSockDiagNotifiedGroup::UdpV6Destroy
106                }
107            },
108        }
109    }
110}
111
112impl MessageWithPermission for SockDiagRequest {
113    fn permission(&self) -> crate::messaging::Permission {
114        match self {
115            SockDiagRequest::InetRequest(_) | SockDiagRequest::UnixRequest(_) => {
116                crate::messaging::Permission::NetlinkSockDiagRead
117            }
118            SockDiagRequest::InetSockDestroy(_) => {
119                crate::messaging::Permission::NetlinkSockDiagDestroy
120            }
121        }
122    }
123}
124
125/// A connection to the `NETLINK_SOCK_DIAG` protocol family.
126pub struct NetlinkSockDiagClient(pub(crate) ExternalClient<NetlinkSockDiag>);
127
128impl NetlinkClient for NetlinkSockDiagClient {
129    type Request = SockDiagRequest;
130
131    fn set_pid(&self, pid: NonZeroU32) {
132        let NetlinkSockDiagClient(client) = self;
133        client.set_port_number(pid)
134    }
135
136    fn add_membership(
137        &self,
138        group: ModernGroup,
139    ) -> Result<AsyncWorkCompletionWaiter, InvalidModernGroupError> {
140        let NetlinkSockDiagClient(client) = self;
141        client.add_membership(group)
142    }
143
144    fn del_membership(&self, group: ModernGroup) -> Result<(), InvalidModernGroupError> {
145        let NetlinkSockDiagClient(client) = self;
146        client.del_membership(group)
147    }
148
149    fn set_legacy_memberships(
150        &self,
151        legacy_memberships: LegacyGroups,
152    ) -> Result<AsyncWorkCompletionWaiter, InvalidLegacyGroupsError> {
153        let NetlinkSockDiagClient(client) = self;
154        client.set_legacy_memberships(legacy_memberships)
155    }
156}
157
158impl NamedNetlinkFamily for NetlinkSockDiag {
159    const NAME: &'static str = "NETLINK_SOCK_DIAG";
160}
161
162impl ProtocolFamily for NetlinkSockDiag {
163    type Request = SockDiagRequest;
164    type Response = SockDiagResponse;
165    type RequestHandler<S: Sender<Self::Response>> = NetlinkSockDiagRequestHandler<S>;
166    type NotifiedMulticastGroup = NetlinkSockDiagNotifiedGroup;
167    type AsyncWorkItem = Never;
168
169    fn should_notify_on_group_membership_change(
170        group: ModernGroup,
171    ) -> Option<Self::NotifiedMulticastGroup> {
172        NetlinkSockDiagNotifiedGroup::try_from(group).ok()
173    }
174}
175
176#[cfg(test)]
177mod testutil {
178    use net_declare::{std_ip_v4, std_ip_v6};
179    use net_types::ip::{Ip, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
180
181    pub(crate) trait TestIpExt: Ip {
182        const SRC_ADDR: Self::Addr;
183        const DST_ADDR: Self::Addr;
184        const LINUX_FAMILY: u8;
185    }
186
187    impl TestIpExt for Ipv4 {
188        const SRC_ADDR: Ipv4Addr = Ipv4Addr::new(std_ip_v4!("192.168.0.1").octets());
189        const DST_ADDR: Ipv4Addr = Ipv4Addr::new(std_ip_v4!("192.168.0.2").octets());
190        const LINUX_FAMILY: u8 = linux_uapi::AF_INET as u8;
191    }
192
193    impl TestIpExt for Ipv6 {
194        const SRC_ADDR: Ipv6Addr = Ipv6Addr::new(std_ip_v6!("2001:db8::1").segments());
195        const DST_ADDR: Ipv6Addr = Ipv6Addr::new(std_ip_v6!("2001:db8::2").segments());
196        const LINUX_FAMILY: u8 = linux_uapi::AF_INET6 as u8;
197    }
198}