1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-09 10:40:45 +00:00

Kernel: Properly support the SO_BROADCAST socket option

POSIX requires that broadcast sends will only be allowed if the
SO_BROADCAST socket option was set on the socket.
Also, broadcast sends to protocols that do not support broadcast (like
TCP), should always fail.
This commit is contained in:
Idan Horowitz 2023-12-24 19:01:09 +02:00 committed by Andreas Kling
parent 8b2beb2ebe
commit 545f4b6cc1
7 changed files with 36 additions and 6 deletions

View File

@ -211,9 +211,10 @@ ErrorOr<size_t> 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<NetworkAdapter> { 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);

View File

@ -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<NetworkAdapter> const through, AllowUsingGateway allow_using_gateway)
RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr<NetworkAdapter> 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() };

View File

@ -64,7 +64,12 @@ enum class AllowUsingGateway {
No,
};
RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr<NetworkAdapter> const through = nullptr, AllowUsingGateway = AllowUsingGateway::Yes);
enum class AllowBroadcast {
Yes,
No,
};
RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr<NetworkAdapter> const through = nullptr, AllowBroadcast = AllowBroadcast::No, AllowUsingGateway = AllowUsingGateway::Yes);
SpinlockProtected<HashMap<IPv4Address, MACAddress>, LockRank::None>& arp_table();
SpinlockProtected<Route::RouteList, LockRank::None>& routing_table();

View File

@ -130,6 +130,12 @@ ErrorOr<void> Socket::setsockopt(int level, int option, Userspace<void const*> 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<int const*>(user_value))) != 0;
return {};
}
default:
dbgln("setsockopt({}) at SOL_SOCKET not implemented.", option);
return ENOPROTOOPT;
@ -235,6 +241,14 @@ ErrorOr<void> 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<int*>(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;

View File

@ -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; }

View File

@ -85,7 +85,8 @@ ErrorOr<size_t> UDPSocket::protocol_receive(ReadonlyBytes raw_ipv4_packet, UserO
ErrorOr<size_t> UDPSocket::protocol_send(UserOrKernelBuffer const& data, size_t data_length)
{
auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr<NetworkAdapter> { 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();

View File

@ -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));