Skip to main content

netget/server/
socket_helpers.rs

1//! Socket helper utilities for creating sockets with proper options
2
3use anyhow::Result;
4#[cfg(not(target_arch = "wasm32"))]
5use socket2::{Domain, Socket, Type};
6#[cfg(unix)]
7use std::net::Ipv4Addr;
8use std::net::SocketAddr;
9#[cfg(unix)]
10use std::os::unix::io::FromRawFd;
11use tokio::net::{TcpListener, UdpSocket};
12
13/// OSPF protocol number (IPPROTO_OSPFIGP)
14#[cfg(unix)]
15const IPPROTO_OSPF: i32 = 89;
16
17/// AllSPFRouters multicast group
18#[cfg(unix)]
19const OSPF_ALL_SPF_ROUTERS: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 5);
20
21/// AllDRouters multicast group
22#[cfg(unix)]
23const OSPF_ALL_DROUTERS: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 6);
24
25/// Create a TCP listener with SO_REUSEADDR enabled
26///
27/// This allows immediate port reuse after stopping a server,
28/// which is essential for quick restart workflows in the TUI.
29#[cfg(not(target_arch = "wasm32"))]
30pub async fn create_reusable_tcp_listener(addr: SocketAddr) -> Result<TcpListener> {
31    let socket = if addr.is_ipv4() {
32        Socket::new(Domain::IPV4, Type::STREAM, None)?
33    } else {
34        Socket::new(Domain::IPV6, Type::STREAM, None)?
35    };
36
37    // Enable SO_REUSEADDR for immediate port reuse
38    socket.set_reuse_address(true)?;
39
40    // Bind to the address
41    socket.bind(&addr.into())?;
42
43    // Start listening
44    socket.listen(128)?;
45
46    // Convert to tokio TcpListener
47    socket.set_nonblocking(true)?;
48    let std_listener: std::net::TcpListener = socket.into();
49    let tokio_listener = TcpListener::from_std(std_listener)?;
50
51    Ok(tokio_listener)
52}
53
54/// Create a UDP socket with SO_REUSEADDR enabled
55///
56/// This allows immediate port reuse after stopping a server.
57#[cfg(not(target_arch = "wasm32"))]
58pub async fn create_reusable_udp_socket(addr: SocketAddr) -> Result<UdpSocket> {
59    let socket = if addr.is_ipv4() {
60        Socket::new(Domain::IPV4, Type::DGRAM, None)?
61    } else {
62        Socket::new(Domain::IPV6, Type::DGRAM, None)?
63    };
64
65    // Enable SO_REUSEADDR for immediate port reuse
66    socket.set_reuse_address(true)?;
67
68    // Bind to the address
69    socket.bind(&addr.into())?;
70
71    // Convert to tokio UdpSocket
72    socket.set_nonblocking(true)?;
73    let std_socket: std::net::UdpSocket = socket.into();
74    let tokio_socket = UdpSocket::from_std(std_socket)?;
75
76    Ok(tokio_socket)
77}
78
79/// On the browser's virtual network (`crates/netget-tokio-wasm`) there are no socket options
80/// to set; binding is registering the port.
81#[cfg(target_arch = "wasm32")]
82pub async fn create_reusable_tcp_listener(addr: SocketAddr) -> Result<TcpListener> {
83    Ok(TcpListener::bind(addr).await?)
84}
85
86/// UDP has no virtual counterpart; this reports `Unsupported` from the shim.
87#[cfg(target_arch = "wasm32")]
88pub async fn create_reusable_udp_socket(addr: SocketAddr) -> Result<UdpSocket> {
89    Ok(UdpSocket::bind(addr).await?)
90}
91
92/// Create a raw IP socket for OSPF (protocol 89)
93///
94/// This creates a raw socket that receives OSPF packets directly from IP layer.
95/// Requires root/CAP_NET_RAW privileges.
96///
97/// # Arguments
98/// * `interface_addr` - IP address of the interface to bind to
99/// * `join_all_routers` - Whether to join AllSPFRouters multicast group (224.0.0.5)
100/// * `join_dr_routers` - Whether to join AllDRouters multicast group (224.0.0.6)
101///
102/// # Returns
103/// A non-blocking raw socket configured for OSPF
104#[cfg(unix)]
105pub fn create_ospf_raw_socket(
106    interface_addr: Ipv4Addr,
107    join_all_routers: bool,
108    join_dr_routers: bool,
109) -> Result<Socket> {
110    // Create raw IP socket for OSPF protocol (89)
111    // Using unsafe to create SOCK_RAW socket via libc
112    let socket = unsafe {
113        let fd = libc::socket(libc::AF_INET, libc::SOCK_RAW, IPPROTO_OSPF);
114        if fd < 0 {
115            return Err(std::io::Error::last_os_error().into());
116        }
117        Socket::from_raw_fd(fd)
118    };
119
120    // Set socket to non-blocking mode
121    socket.set_nonblocking(true)?;
122
123    // Set SO_REUSEADDR for address reuse
124    socket.set_reuse_address(true)?;
125
126    // Join AllSPFRouters multicast group (224.0.0.5) if requested
127    if join_all_routers {
128        socket.join_multicast_v4(&OSPF_ALL_SPF_ROUTERS, &interface_addr)?;
129    }
130
131    // Join AllDRouters multicast group (224.0.0.6) if requested (for DR/BDR)
132    if join_dr_routers {
133        socket.join_multicast_v4(&OSPF_ALL_DROUTERS, &interface_addr)?;
134    }
135
136    // Set multicast TTL to 1 (link-local only per RFC 2328)
137    socket.set_multicast_ttl_v4(1)?;
138
139    // Set multicast loopback to true (receive our own multicasts)
140    socket.set_multicast_loop_v4(true)?;
141
142    // Set multicast interface to the specified interface
143    socket.set_multicast_if_v4(&interface_addr)?;
144
145    Ok(socket)
146}