IPv4: Support overriding the default TTL (64)

Made getsockopt() and setsockopt() virtual so we can handle them in the
various Socket subclasses. The subclasses map kinda nicely to "levels".

This will allow us to implement things like "traceroute", although..
I spent some time trying to do that, but then hit a wall when it turned
out that the user-mode networking in QEMU doesn't preserve TTL in the
ICMP packets passing through.
This commit is contained in:
Andreas Kling 2019-09-19 21:40:06 +02:00
parent 482d5295f1
commit 8cfb859368
11 changed files with 58 additions and 10 deletions

View file

@ -195,7 +195,7 @@ ssize_t IPv4Socket::sendto(FileDescription&, const void* data, size_t data_lengt
#endif
if (type() == SOCK_RAW) {
routing_decision.adapter->send_ipv4(routing_decision.next_hop, m_peer_address, (IPv4Protocol)protocol(), (const u8*)data, data_length);
routing_decision.adapter->send_ipv4(routing_decision.next_hop, m_peer_address, (IPv4Protocol)protocol(), (const u8*)data, data_length, m_ttl);
return data_length;
}
@ -313,3 +313,38 @@ String IPv4Socket::absolute_path(const FileDescription&) const
return builder.to_string();
}
KResult IPv4Socket::setsockopt(int level, int option, const void* value, socklen_t value_size)
{
if (level != IPPROTO_IP)
return Socket::setsockopt(level, option, value, value_size);
switch (option) {
case IP_TTL:
if (value_size < sizeof(int))
return KResult(-EINVAL);
if (*(const int*)value < 0 || *(const int*)value > 255)
return KResult(-EINVAL);
m_ttl = (u8)*(const int*)value;
return KSuccess;
default:
return KResult(-ENOPROTOOPT);
}
}
KResult IPv4Socket::getsockopt(int level, int option, void* value, socklen_t* value_size)
{
if (level != IPPROTO_IP)
return Socket::getsockopt(level, option, value, value_size);
switch (option) {
case IP_TTL:
if (*value_size < sizeof(int))
return KResult(-EINVAL);
*(int*)value = m_ttl;
return KSuccess;
default:
return KResult(-ENOPROTOOPT);
}
}

View file

@ -31,6 +31,8 @@ public:
virtual bool can_write(FileDescription&) const override;
virtual ssize_t sendto(FileDescription&, const void*, size_t, int, const sockaddr*, socklen_t) override;
virtual ssize_t recvfrom(FileDescription&, void*, size_t, int flags, sockaddr*, socklen_t*) override;
virtual KResult setsockopt(int level, int option, const void*, socklen_t) override;
virtual KResult getsockopt(int level, int option, void*, socklen_t*) override;
void did_receive(const IPv4Address& peer_address, u16 peer_port, KBuffer&&);
@ -47,6 +49,8 @@ public:
String absolute_path(const FileDescription& description) const override;
u8 ttl() const { return m_ttl; }
protected:
IPv4Socket(int type, int protocol);
virtual const char* class_name() const override { return "IPv4Socket"; }
@ -83,5 +87,7 @@ private:
u32 m_bytes_received { 0 };
u8 m_ttl { 64 };
bool m_can_read { false };
};

View file

@ -58,7 +58,7 @@ void NetworkAdapter::send(const MACAddress& destination, const ARPPacket& packet
send_raw((const u8*)eth, size_in_bytes);
}
void NetworkAdapter::send_ipv4(const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, const u8* payload, size_t payload_size)
void NetworkAdapter::send_ipv4(const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, const u8* payload, size_t payload_size, u8 ttl)
{
size_t size_in_bytes = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet) + payload_size;
auto buffer = ByteBuffer::create_zeroed(size_in_bytes);
@ -74,7 +74,7 @@ void NetworkAdapter::send_ipv4(const MACAddress& destination_mac, const IPv4Addr
ipv4.set_protocol((u8)protocol);
ipv4.set_length(sizeof(IPv4Packet) + payload_size);
ipv4.set_ident(1);
ipv4.set_ttl(64);
ipv4.set_ttl(ttl);
ipv4.set_checksum(ipv4.compute_checksum());
m_packets_out++;
m_bytes_out += size_in_bytes;

View file

@ -34,7 +34,7 @@ public:
void set_ipv4_gateway(const IPv4Address&);
void send(const MACAddress&, const ARPPacket&);
void send_ipv4(const MACAddress&, const IPv4Address&, IPv4Protocol, const u8* payload, size_t payload_size);
void send_ipv4(const MACAddress&, const IPv4Address&, IPv4Protocol, const u8* payload, size_t payload_size, u8 ttl);
Optional<KBuffer> dequeue_packet();

View file

@ -272,7 +272,8 @@ void handle_icmp(const EthernetFrameHeader& eth, const IPv4Packet& ipv4_packet)
if (size_t icmp_payload_size = icmp_packet_size - sizeof(ICMPEchoPacket))
memcpy(response.payload(), request.payload(), icmp_payload_size);
response.header.set_checksum(internet_checksum(&response, icmp_packet_size));
adapter->send_ipv4(eth.source(), ipv4_packet.source(), IPv4Protocol::ICMP, buffer.data(), buffer.size());
// FIXME: What is the right TTL value here? Is 64 ok? Should we use the same TTL as the echo request?
adapter->send_ipv4(eth.source(), ipv4_packet.source(), IPv4Protocol::ICMP, buffer.data(), buffer.size(), 64);
}
}

View file

@ -76,8 +76,8 @@ public:
virtual ssize_t sendto(FileDescription&, const void*, size_t, int flags, const sockaddr*, socklen_t) = 0;
virtual ssize_t recvfrom(FileDescription&, void*, size_t, int flags, sockaddr*, socklen_t*) = 0;
KResult setsockopt(int level, int option, const void*, socklen_t);
KResult getsockopt(int level, int option, void*, socklen_t*);
virtual KResult setsockopt(int level, int option, const void*, socklen_t);
virtual KResult getsockopt(int level, int option, void*, socklen_t*);
pid_t origin_pid() const { return m_origin_pid; }
pid_t acceptor_pid() const { return m_acceptor_pid; }

View file

@ -170,7 +170,7 @@ void TCPSocket::send_tcp_packet(u16 flags, const void* payload, int payload_size
routing_decision.adapter->send_ipv4(
routing_decision.next_hop, peer_address(), IPv4Protocol::TCP,
buffer.data(), buffer.size());
buffer.data(), buffer.size(), ttl());
m_packets_out++;
m_bytes_out += buffer.size();
@ -210,7 +210,7 @@ void TCPSocket::send_outgoing_packets()
#endif
routing_decision.adapter->send_ipv4(
routing_decision.next_hop, peer_address(), IPv4Protocol::TCP,
packet.buffer.data(), packet.buffer.size());
packet.buffer.data(), packet.buffer.size(), ttl());
m_packets_out++;
m_bytes_out += packet.buffer.size();

View file

@ -77,7 +77,7 @@ int UDPSocket::protocol_send(const void* data, int data_length)
local_port(),
peer_address().to_string().characters(),
peer_port());
routing_decision.adapter->send_ipv4(routing_decision.next_hop, peer_address(), IPv4Protocol::UDP, buffer.data(), buffer.size());
routing_decision.adapter->send_ipv4(routing_decision.next_hop, peer_address(), IPv4Protocol::UDP, buffer.data(), buffer.size(), ttl());
return data_length;
}

View file

@ -337,10 +337,13 @@ struct pollfd {
#define SO_KEEPALIVE 3
#define SO_ERROR 4
#define IPPROTO_IP 0
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
#define IP_TTL 2
struct sockaddr {
u16 sa_family;
char sa_data[14];

View file

@ -11,4 +11,6 @@ in_addr_t inet_addr(const char*);
#define INADDR_ANY ((in_addr_t)0)
#define INADDR_NONE ((in_addr_t)-1)
#define IP_TTL 2
__END_DECLS

View file

@ -23,6 +23,7 @@ __BEGIN_DECLS
#define SOCK_NONBLOCK 04000
#define SOCK_CLOEXEC 02000000
#define IPPROTO_IP 0
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17