smoltcp-rs / smoltcp

a smol tcp/ip stack
BSD Zero Clause License
3.63k stars 402 forks source link

UDP: setsockopt REUSEADDR suport, when we run process_udp #925

Open YXalix opened 2 months ago

YXalix commented 2 months ago

when we run process_udp, if there are two sockets that bind under the same Ipaddr, one of them will be ignored here

// iface/interface/udp.rs
#[cfg(feature = "socket-udp")]
for udp_socket in sockets
    .items_mut()
    .filter_map(|i| UdpSocket::downcast_mut(&mut i.socket))
{
    if udp_socket.accepts(self, &ip_repr, &udp_repr) {
        udp_socket.process(self, meta, &ip_repr, &udp_repr, udp_packet.payload());
        return None;
    }
}

Is it possible to iterate over all udp here and then return? I'm also happy to contribute pr to this great repo.

Dirbaio commented 2 months ago

what behavior would you consider correct? duplicating the packet to the two sockets? Is this what Linux does?

Also, I'm curious: What's the use case for binding 2 sockets to the same addr?

YXalix commented 2 months ago

Yeah, I think the correct behavior is packet will be duplicated to the two sockets related doc, and I'm just verify this behavior as follows:

import socket
import threading
import time

MULTICAST_ADDR = '239.255.0.1'
PORT = 12345
def send_broadcast_message():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    message = "Hello, world!"
    sock.sendto(message.encode(), ('239.255.0.1', 12345))
    print("Sent broadcast message: {}".format(message))
    sock.close()

def receive_broadcast_message():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    mreq = socket.inet_aton(MULTICAST_ADDR) + socket.inet_aton('127.0.0.1')
    sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, mreq)
    sock.bind(('239.255.0.1', 12345))
    data, addr = sock.recvfrom(1024)
    print("Received from {}: {}".format(addr, data.decode()))
    sock.close()

receive_thread1 = threading.Thread(target=receive_broadcast_message)
receive_thread1.daemon = True
receive_thread1.start()

receive_thread2 = threading.Thread(target=receive_broadcast_message)
receive_thread2.daemon = True
receive_thread2.start()

time.sleep(1)

send_thread = threading.Thread(target=send_broadcast_message)
send_thread.daemon = True
send_thread.start()

send_thread.join()
receive_thread1.join()
receive_thread2.join()

Background I met this requirement when i did fastdds support for starry-os. The RTPS protocol adopted by fastdds is simply to use UDP broadcast to discover participants, and then use UDP unicast for communication.

I'm also glad to receive your reply ✨