mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-22 09:21:57 -05: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:
parent
8b2beb2ebe
commit
545f4b6cc1
7 changed files with 36 additions and 6 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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() };
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue