mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 08:24:58 +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:
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())
|
if (!is_connected() && m_peer_address.is_zero())
|
||||||
return set_so_error(EPIPE);
|
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 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 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())
|
if (routing_decision.is_zero())
|
||||||
return set_so_error(EHOSTUNREACH);
|
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] };
|
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) {
|
auto matches = [&](auto& adapter) {
|
||||||
if (!through)
|
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.
|
// 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
|
// FIXME: We should also deal with the case where `target_addr` is
|
||||||
// a broadcast to a subnet rather than a full broadcast.
|
// a broadcast to a subnet rather than a full broadcast.
|
||||||
if (target_addr == 0xffffffff && matches(adapter))
|
if (target_addr == 0xffffffff && matches(adapter)) {
|
||||||
return { adapter, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
|
if (allow_broadcast == AllowBroadcast::Yes)
|
||||||
|
return { adapter, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
|
||||||
|
return { nullptr, {} };
|
||||||
|
}
|
||||||
|
|
||||||
if (adapter == NetworkingManagement::the().loopback_adapter())
|
if (adapter == NetworkingManagement::the().loopback_adapter())
|
||||||
return { adapter, adapter->mac_address() };
|
return { adapter, adapter->mac_address() };
|
||||||
|
|
|
@ -64,7 +64,12 @@ enum class AllowUsingGateway {
|
||||||
No,
|
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<HashMap<IPv4Address, MACAddress>, LockRank::None>& arp_table();
|
||||||
SpinlockProtected<Route::RouteList, LockRank::None>& routing_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:
|
case SO_REUSEADDR:
|
||||||
dbgln("FIXME: SO_REUSEADDR requested, but not implemented.");
|
dbgln("FIXME: SO_REUSEADDR requested, but not implemented.");
|
||||||
return {};
|
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:
|
default:
|
||||||
dbgln("setsockopt({}) at SOL_SOCKET not implemented.", option);
|
dbgln("setsockopt({}) at SOL_SOCKET not implemented.", option);
|
||||||
return ENOPROTOOPT;
|
return ENOPROTOOPT;
|
||||||
|
@ -235,6 +241,14 @@ ErrorOr<void> Socket::getsockopt(OpenFileDescription&, int level, int option, Us
|
||||||
size = sizeof(routing_disabled);
|
size = sizeof(routing_disabled);
|
||||||
return copy_to_user(value_size, &size);
|
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:
|
default:
|
||||||
dbgln("getsockopt({}) at SOL_SOCKET not implemented.", option);
|
dbgln("getsockopt({}) at SOL_SOCKET not implemented.", option);
|
||||||
return ENOPROTOOPT;
|
return ENOPROTOOPT;
|
||||||
|
|
|
@ -156,6 +156,7 @@ protected:
|
||||||
ucred m_origin { 0, 0, 0 };
|
ucred m_origin { 0, 0, 0 };
|
||||||
ucred m_acceptor { 0, 0, 0 };
|
ucred m_acceptor { 0, 0, 0 };
|
||||||
bool m_routing_disabled { false };
|
bool m_routing_disabled { false };
|
||||||
|
bool m_broadcast_allowed { false };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool is_socket() const final { return true; }
|
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)
|
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 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())
|
if (routing_decision.is_zero())
|
||||||
return set_so_error(EHOSTUNREACH);
|
return set_so_error(EHOSTUNREACH);
|
||||||
auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset();
|
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));
|
dbgln("ERROR: setsockopt(SO_BINDTODEVICE) :: {}", strerror(errno));
|
||||||
return false;
|
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;
|
sockaddr_in dst;
|
||||||
memset(&dst, 0, sizeof(dst));
|
memset(&dst, 0, sizeof(dst));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue