diff --git a/Kernel/Net/IPv4Socket.cpp b/Kernel/Net/IPv4Socket.cpp index ad1bad00f8..8fd8012444 100644 --- a/Kernel/Net/IPv4Socket.cpp +++ b/Kernel/Net/IPv4Socket.cpp @@ -211,9 +211,10 @@ ErrorOr IPv4Socket::sendto(OpenFileDescription&, UserOrKernelBuffer cons if (!is_connected() && m_peer_address.is_zero()) return set_so_error(EPIPE); + auto allow_broadcast = m_broadcast_allowed ? AllowBroadcast::Yes : AllowBroadcast::No; auto allow_using_gateway = ((flags & MSG_DONTROUTE) || m_routing_disabled) ? AllowUsingGateway::No : AllowUsingGateway::Yes; auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr { return bound_device; }); - auto routing_decision = route_to(m_peer_address, m_local_address, adapter, allow_using_gateway); + auto routing_decision = route_to(m_peer_address, m_local_address, adapter, allow_broadcast, allow_using_gateway); if (routing_decision.is_zero()) return set_so_error(EHOSTUNREACH); diff --git a/Kernel/Net/Routing.cpp b/Kernel/Net/Routing.cpp index acf19a576f..de07bcce25 100644 --- a/Kernel/Net/Routing.cpp +++ b/Kernel/Net/Routing.cpp @@ -178,7 +178,7 @@ static MACAddress multicast_ethernet_address(IPv4Address const& address) return MACAddress { 0x01, 0x00, 0x5e, (u8)(address[1] & 0x7f), address[2], address[3] }; } -RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr const through, AllowUsingGateway allow_using_gateway) +RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr const through, AllowBroadcast allow_broadcast, AllowUsingGateway allow_using_gateway) { auto matches = [&](auto& adapter) { if (!through) @@ -291,8 +291,11 @@ RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, R // If it's a broadcast, we already know everything we need to know. // FIXME: We should also deal with the case where `target_addr` is // a broadcast to a subnet rather than a full broadcast. - if (target_addr == 0xffffffff && matches(adapter)) - return { adapter, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; + if (target_addr == 0xffffffff && matches(adapter)) { + if (allow_broadcast == AllowBroadcast::Yes) + return { adapter, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; + return { nullptr, {} }; + } if (adapter == NetworkingManagement::the().loopback_adapter()) return { adapter, adapter->mac_address() }; diff --git a/Kernel/Net/Routing.h b/Kernel/Net/Routing.h index a142fa63d2..b7fc5ad257 100644 --- a/Kernel/Net/Routing.h +++ b/Kernel/Net/Routing.h @@ -64,7 +64,12 @@ enum class AllowUsingGateway { No, }; -RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr const through = nullptr, AllowUsingGateway = AllowUsingGateway::Yes); +enum class AllowBroadcast { + Yes, + No, +}; + +RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr const through = nullptr, AllowBroadcast = AllowBroadcast::No, AllowUsingGateway = AllowUsingGateway::Yes); SpinlockProtected, LockRank::None>& arp_table(); SpinlockProtected& routing_table(); diff --git a/Kernel/Net/Socket.cpp b/Kernel/Net/Socket.cpp index 078bce0280..e8e7bedde5 100644 --- a/Kernel/Net/Socket.cpp +++ b/Kernel/Net/Socket.cpp @@ -130,6 +130,12 @@ ErrorOr Socket::setsockopt(int level, int option, Userspace u case SO_REUSEADDR: dbgln("FIXME: SO_REUSEADDR requested, but not implemented."); return {}; + case SO_BROADCAST: { + if (user_value_size != sizeof(int)) + return EINVAL; + m_broadcast_allowed = TRY(copy_typed_from_user(static_ptr_cast(user_value))) != 0; + return {}; + } default: dbgln("setsockopt({}) at SOL_SOCKET not implemented.", option); return ENOPROTOOPT; @@ -235,6 +241,14 @@ ErrorOr Socket::getsockopt(OpenFileDescription&, int level, int option, Us size = sizeof(routing_disabled); return copy_to_user(value_size, &size); } + case SO_BROADCAST: { + int broadcast_allowed = m_broadcast_allowed ? 1 : 0; + if (size < sizeof(broadcast_allowed)) + return EINVAL; + TRY(copy_to_user(static_ptr_cast(value), &broadcast_allowed)); + size = sizeof(broadcast_allowed); + return copy_to_user(value_size, &size); + } default: dbgln("getsockopt({}) at SOL_SOCKET not implemented.", option); return ENOPROTOOPT; diff --git a/Kernel/Net/Socket.h b/Kernel/Net/Socket.h index c18cccc40e..01c154c221 100644 --- a/Kernel/Net/Socket.h +++ b/Kernel/Net/Socket.h @@ -156,6 +156,7 @@ protected: ucred m_origin { 0, 0, 0 }; ucred m_acceptor { 0, 0, 0 }; bool m_routing_disabled { false }; + bool m_broadcast_allowed { false }; private: virtual bool is_socket() const final { return true; } diff --git a/Kernel/Net/UDPSocket.cpp b/Kernel/Net/UDPSocket.cpp index 5cb5814cce..70a8f21f1c 100644 --- a/Kernel/Net/UDPSocket.cpp +++ b/Kernel/Net/UDPSocket.cpp @@ -85,7 +85,8 @@ ErrorOr UDPSocket::protocol_receive(ReadonlyBytes raw_ipv4_packet, UserO ErrorOr UDPSocket::protocol_send(UserOrKernelBuffer const& data, size_t data_length) { auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr { return bound_device; }); - auto routing_decision = route_to(peer_address(), local_address(), adapter); + auto allow_broadcast = m_broadcast_allowed ? AllowBroadcast::Yes : AllowBroadcast::No; + auto routing_decision = route_to(peer_address(), local_address(), adapter, allow_broadcast); if (routing_decision.is_zero()) return set_so_error(EHOSTUNREACH); auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); diff --git a/Userland/Services/DHCPClient/DHCPv4Client.cpp b/Userland/Services/DHCPClient/DHCPv4Client.cpp index 54a6ed6fb3..77499b5c13 100644 --- a/Userland/Services/DHCPClient/DHCPv4Client.cpp +++ b/Userland/Services/DHCPClient/DHCPv4Client.cpp @@ -49,6 +49,11 @@ static bool send(InterfaceDescriptor const& iface, DHCPv4Packet const& packet, C dbgln("ERROR: setsockopt(SO_BINDTODEVICE) :: {}", strerror(errno)); return false; } + int allow_broadcast = 1; + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &allow_broadcast, sizeof(int)) < 0) { + dbgln("ERROR: setsockopt(SO_BROADCAST) :: {}", strerror(errno)); + return false; + } sockaddr_in dst; memset(&dst, 0, sizeof(dst));