1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 10:27:35 +00:00

Kernel: Add the SO_BINDTODEVICE socket option

This patch adds a way for a socket to ask to be routed through a
specific interface.
Currently, this option only applies to sending, however, it should also
apply to receiving...somehow :^)
This commit is contained in:
AnotherTest 2020-04-05 01:16:45 +04:30 committed by Andreas Kling
parent 7d0bf9b5a9
commit 77191d82dc
10 changed files with 186 additions and 13 deletions

View file

@ -210,7 +210,7 @@ ssize_t IPv4Socket::sendto(FileDescription&, const void* data, size_t data_lengt
m_peer_port = ntohs(ia.sin_port);
}
auto routing_decision = route_to(m_peer_address, m_local_address);
auto routing_decision = route_to(m_peer_address, m_local_address, bound_interface());
if (routing_decision.is_zero())
return -EHOSTUNREACH;

View file

@ -46,10 +46,22 @@ bool RoutingDecision::is_zero() const
return adapter.is_null() || next_hop.is_zero();
}
RoutingDecision route_to(const IPv4Address& target, const IPv4Address& source)
RoutingDecision route_to(const IPv4Address& target, const IPv4Address& source, const RefPtr<NetworkAdapter> through)
{
auto matches = [&](auto& adapter) {
if (!through)
return true;
return through == adapter;
};
auto if_matches = [&](auto& adapter, const auto& mac) -> RoutingDecision {
if (!matches(adapter))
return { nullptr, {} };
return { adapter, mac };
};
if (target[0] == 127)
return { LoopbackAdapter::the(), LoopbackAdapter::the().mac_address() };
return if_matches(LoopbackAdapter::the(), LoopbackAdapter::the().mac_address());
auto target_addr = target.to_u32();
auto source_addr = source.to_u32();
@ -57,17 +69,17 @@ RoutingDecision route_to(const IPv4Address& target, const IPv4Address& source)
RefPtr<NetworkAdapter> local_adapter = nullptr;
RefPtr<NetworkAdapter> gateway_adapter = nullptr;
NetworkAdapter::for_each([source_addr, &target_addr, &local_adapter, &gateway_adapter](auto& adapter) {
NetworkAdapter::for_each([source_addr, &target_addr, &local_adapter, &gateway_adapter, &matches](auto& adapter) {
auto adapter_addr = adapter.ipv4_address().to_u32();
auto adapter_mask = adapter.ipv4_netmask().to_u32();
if (source_addr != 0 && source_addr != adapter_addr)
return;
if ((target_addr & adapter_mask) == (adapter_addr & adapter_mask))
if ((target_addr & adapter_mask) == (adapter_addr & adapter_mask) && matches(adapter))
local_adapter = adapter;
if (adapter.ipv4_gateway().to_u32() != 0)
if (adapter.ipv4_gateway().to_u32() != 0 && matches(adapter))
gateway_adapter = adapter;
});

View file

@ -37,7 +37,7 @@ struct RoutingDecision {
bool is_zero() const;
};
RoutingDecision route_to(const IPv4Address& target, const IPv4Address& source);
RoutingDecision route_to(const IPv4Address& target, const IPv4Address& source, const RefPtr<NetworkAdapter> through = nullptr);
Lockable<HashMap<IPv4Address, MACAddress>>& arp_table();

View file

@ -25,6 +25,7 @@
*/
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <Kernel/FileSystem/FileDescription.h>
#include <Kernel/Net/IPv4Socket.h>
#include <Kernel/Net/LocalSocket.h>
@ -114,6 +115,16 @@ KResult Socket::setsockopt(int level, int option, const void* value, socklen_t v
return KResult(-EINVAL);
m_receive_timeout = *(const timeval*)value;
return KSuccess;
case SO_BINDTODEVICE: {
if (value_size != IFNAMSIZ)
return KResult(-EINVAL);
StringView ifname { (const char*)value };
auto device = NetworkAdapter::lookup_by_name(ifname);
if (!device)
return KResult(-ENODEV);
m_bound_interface = device;
return KSuccess;
}
default:
dbg() << "setsockopt(" << option << ") at SOL_SOCKET not implemented.";
return KResult(-ENOPROTOOPT);
@ -143,6 +154,19 @@ KResult Socket::getsockopt(FileDescription&, int level, int option, void* value,
*(int*)value = 0;
*value_size = sizeof(int);
return KSuccess;
case SO_BINDTODEVICE:
if (*value_size < IFNAMSIZ)
return KResult(-EINVAL);
if (m_bound_interface) {
const auto& name = m_bound_interface->name();
auto length = name.length() + 1;
memcpy(value, name.characters(), length);
*value_size = length;
return KSuccess;
} else {
*value_size = 0;
return KResult(-EFAULT);
}
default:
dbg() << "getsockopt(" << option << ") at SOL_SOCKET not implemented.";
return KResult(-ENOPROTOOPT);

View file

@ -33,6 +33,7 @@
#include <Kernel/FileSystem/File.h>
#include <Kernel/KResult.h>
#include <Kernel/Lock.h>
#include <Kernel/Net/NetworkAdapter.h>
#include <Kernel/UnixTypes.h>
namespace Kernel {
@ -57,9 +58,9 @@ public:
bool is_shut_down_for_reading() const { return m_shut_down_for_reading; }
enum class SetupState {
Unstarted, // we haven't tried to set the socket up yet
Unstarted, // we haven't tried to set the socket up yet
InProgress, // we're in the process of setting things up - for TCP maybe we've sent a SYN packet
Completed, // the setup process is complete, but not necessarily successful
Completed, // the setup process is complete, but not necessarily successful
};
enum class Role : u8 {
@ -118,6 +119,7 @@ public:
pid_t acceptor_pid() const { return m_acceptor.pid; }
uid_t acceptor_uid() const { return m_acceptor.uid; }
gid_t acceptor_gid() const { return m_acceptor.gid; }
const RefPtr<NetworkAdapter> bound_interface() const { return m_bound_interface; }
Lock& lock() { return m_lock; }
@ -165,13 +167,15 @@ private:
bool m_shut_down_for_reading { false };
bool m_shut_down_for_writing { false };
RefPtr<NetworkAdapter> m_bound_interface { nullptr };
timeval m_receive_timeout { 0, 0 };
timeval m_send_timeout { 0, 0 };
NonnullRefPtrVector<Socket> m_pending;
};
template<typename SocketType>
template <typename SocketType>
class SocketHandle {
public:
SocketHandle() {}

View file

@ -212,7 +212,7 @@ void TCPSocket::send_tcp_packet(u16 flags, const void* payload, size_t payload_s
return;
}
auto routing_decision = route_to(peer_address(), local_address());
auto routing_decision = route_to(peer_address(), local_address(), bound_interface());
ASSERT(!routing_decision.is_zero());
routing_decision.adapter->send_ipv4(
@ -225,7 +225,7 @@ void TCPSocket::send_tcp_packet(u16 flags, const void* payload, size_t payload_s
void TCPSocket::send_outgoing_packets()
{
auto routing_decision = route_to(peer_address(), local_address());
auto routing_decision = route_to(peer_address(), local_address(), bound_interface());
ASSERT(!routing_decision.is_zero());
auto now = kgettimeofday();

View file

@ -92,7 +92,7 @@ int UDPSocket::protocol_receive(const KBuffer& packet_buffer, void* buffer, size
int UDPSocket::protocol_send(const void* data, size_t data_length)
{
auto routing_decision = route_to(peer_address(), local_address());
auto routing_decision = route_to(peer_address(), local_address(), bound_interface());
if (routing_decision.is_zero())
return -EHOSTUNREACH;
auto buffer = ByteBuffer::create_zeroed(sizeof(UDPPacket) + data_length);

View file

@ -401,6 +401,7 @@ struct pollfd {
#define SO_ERROR 4
#define SO_PEERCRED 5
#define SO_REUSEADDR 6
#define SO_BINDTODEVICE 7
#define IPPROTO_IP 0
#define IPPROTO_ICMP 1