diff options
author | Sergey Bugaev <bugaevc@serenityos.org> | 2021-05-04 14:42:47 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-05 21:16:17 +0200 |
commit | 78459b92d50ee24f4312ff050fa8b63f82d0df05 (patch) | |
tree | 30dbe18941e66df70a1a44676c05c7ad4a52fa4a /Kernel | |
parent | b9c367e13b0e8eeec9cb085876b3306c24a9f2fd (diff) | |
download | serenity-78459b92d50ee24f4312ff050fa8b63f82d0df05.zip |
Kernel: Implement IP multicast support
An IP socket can now join a multicast group by using the
IP_ADD_MEMBERSHIP sockopt, which will cause it to start receiving
packets sent to the multicast address, even though this address does
not belong to this host.
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/Net/IPv4Socket.cpp | 46 | ||||
-rw-r--r-- | Kernel/Net/IPv4Socket.h | 5 | ||||
-rw-r--r-- | Kernel/Net/NetworkTask.cpp | 12 | ||||
-rw-r--r-- | Kernel/UnixTypes.h | 13 |
4 files changed, 69 insertions, 7 deletions
diff --git a/Kernel/Net/IPv4Socket.cpp b/Kernel/Net/IPv4Socket.cpp index d59a327a8b..e4e1a26a01 100644 --- a/Kernel/Net/IPv4Socket.cpp +++ b/Kernel/Net/IPv4Socket.cpp @@ -466,6 +466,42 @@ KResult IPv4Socket::setsockopt(int level, int option, Userspace<const void*> use m_ttl = value; return KSuccess; } + case IP_MULTICAST_LOOP: { + if (user_value_size != 1) + return EINVAL; + u8 value; + if (!copy_from_user(&value, static_ptr_cast<const u8*>(user_value))) + return EFAULT; + if (value != 0 && value != 1) + return EINVAL; + m_multicast_loop = value; + return KSuccess; + } + case IP_ADD_MEMBERSHIP: { + if (user_value_size != sizeof(ip_mreq)) + return EINVAL; + ip_mreq mreq; + if (!copy_from_user(&mreq, static_ptr_cast<const ip_mreq*>(user_value))) + return EFAULT; + if (mreq.imr_interface.s_addr != INADDR_ANY) + return ENOTSUP; + IPv4Address address { (const u8*)&mreq.imr_multiaddr.s_addr }; + if (!m_multicast_memberships.contains_slow(address)) + m_multicast_memberships.append(address); + return KSuccess; + } + case IP_DROP_MEMBERSHIP: { + if (user_value_size != sizeof(ip_mreq)) + return EINVAL; + ip_mreq mreq; + if (!copy_from_user(&mreq, static_ptr_cast<const ip_mreq*>(user_value))) + return EFAULT; + if (mreq.imr_interface.s_addr != INADDR_ANY) + return ENOTSUP; + IPv4Address address { (const u8*)&mreq.imr_multiaddr.s_addr }; + m_multicast_memberships.remove_first_matching([&address](auto& a) { return a == address; }); + return KSuccess; + } default: return ENOPROTOOPT; } @@ -490,6 +526,16 @@ KResult IPv4Socket::getsockopt(FileDescription& description, int level, int opti if (!copy_to_user(value_size, &size)) return EFAULT; return KSuccess; + case IP_MULTICAST_LOOP: { + if (size < 1) + return EINVAL; + if (!copy_to_user(static_ptr_cast<u8*>(value), (const u8*)&m_multicast_loop)) + return EFAULT; + size = 1; + if (!copy_to_user(value_size, &size)) + return EFAULT; + return KSuccess; + } default: return ENOPROTOOPT; } diff --git a/Kernel/Net/IPv4Socket.h b/Kernel/Net/IPv4Socket.h index ed7f021bc1..b5cea0f26f 100644 --- a/Kernel/Net/IPv4Socket.h +++ b/Kernel/Net/IPv4Socket.h @@ -54,6 +54,8 @@ public: u16 peer_port() const { return m_peer_port; } void set_peer_port(u16 port) { m_peer_port = port; } + const Vector<IPv4Address>& multicast_memberships() const { return m_multicast_memberships; } + IPv4SocketTuple tuple() const { return IPv4SocketTuple(m_local_address, m_local_port, m_peer_address, m_peer_port); } String absolute_path(const FileDescription& description) const override; @@ -96,6 +98,9 @@ private: IPv4Address m_local_address; IPv4Address m_peer_address; + Vector<IPv4Address> m_multicast_memberships; + bool m_multicast_loop { true }; + struct ReceivedPacket { IPv4Address peer_address; u16 peer_port; diff --git a/Kernel/Net/NetworkTask.cpp b/Kernel/Net/NetworkTask.cpp index cf8f617e7e..8a8e3b21d9 100644 --- a/Kernel/Net/NetworkTask.cpp +++ b/Kernel/Net/NetworkTask.cpp @@ -258,12 +258,6 @@ void handle_udp(const IPv4Packet& ipv4_packet, const Time& packet_timestamp) return; } - auto adapter = NetworkAdapter::from_ipv4_address(ipv4_packet.destination()); - if (!adapter && ipv4_packet.destination() != IPv4Address(255, 255, 255, 255)) { - dbgln_if(UDP_DEBUG, "handle_udp: this packet is not for me, it's for {}", ipv4_packet.destination()); - return; - } - auto& udp_packet = *static_cast<const UDPPacket*>(ipv4_packet.payload()); dbgln_if(UDP_DEBUG, "handle_udp: source={}:{}, destination={}:{}, length={}", ipv4_packet.source(), udp_packet.source_port(), @@ -278,7 +272,11 @@ void handle_udp(const IPv4Packet& ipv4_packet, const Time& packet_timestamp) VERIFY(socket->type() == SOCK_DGRAM); VERIFY(socket->local_port() == udp_packet.destination_port()); - socket->did_receive(ipv4_packet.source(), udp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()), packet_timestamp); + + auto& destination = ipv4_packet.destination(); + + if (destination == IPv4Address(255, 255, 255, 255) || NetworkAdapter::from_ipv4_address(destination) || socket->multicast_memberships().contains_slow(destination)) + socket->did_receive(ipv4_packet.source(), udp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()), packet_timestamp); } void handle_tcp(const IPv4Packet& ipv4_packet, const Time& packet_timestamp) diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index b220b41343..0e3ea314ed 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -525,6 +525,9 @@ enum { #define IPPROTO_UDP 17 #define IP_TTL 2 +#define IP_MULTICAST_LOOP 3 +#define IP_ADD_MEMBERSHIP 4 +#define IP_DROP_MEMBERSHIP 5 struct ucred { pid_t pid; @@ -548,6 +551,7 @@ struct sockaddr_un { struct in_addr { uint32_t s_addr; }; +typedef uint32_t in_addr_t; struct sockaddr_in { int16_t sin_family; @@ -556,6 +560,15 @@ struct sockaddr_in { char sin_zero[8]; }; +struct ip_mreq { + struct in_addr imr_multiaddr; + struct in_addr imr_interface; +}; + +#define INADDR_ANY ((in_addr_t)0) +#define INADDR_NONE ((in_addr_t)-1) +#define INADDR_LOOPBACK 0x7f000001 + typedef u32 __u32; typedef u16 __u16; typedef u8 __u8; |