summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorSergey Bugaev <bugaevc@serenityos.org>2021-05-04 14:42:47 +0300
committerAndreas Kling <kling@serenityos.org>2021-05-05 21:16:17 +0200
commit78459b92d50ee24f4312ff050fa8b63f82d0df05 (patch)
tree30dbe18941e66df70a1a44676c05c7ad4a52fa4a /Kernel
parentb9c367e13b0e8eeec9cb085876b3306c24a9f2fd (diff)
downloadserenity-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.cpp46
-rw-r--r--Kernel/Net/IPv4Socket.h5
-rw-r--r--Kernel/Net/NetworkTask.cpp12
-rw-r--r--Kernel/UnixTypes.h13
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;