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