Kernel+LibC+Userland: Start working on an IPv4 socket backend.

The first userland networking program will be "ping" :^)
This commit is contained in:
Andreas Kling 2019-03-12 15:51:42 +01:00
parent 8e667747f0
commit a017a77442
23 changed files with 374 additions and 3 deletions

View file

@ -28,7 +28,6 @@ public:
m_data[2] = c;
m_data[3] = d;
}
~IPv4Address() { }
byte operator[](int i) const
{

111
Kernel/IPv4Socket.cpp Normal file
View file

@ -0,0 +1,111 @@
#include <Kernel/IPv4Socket.h>
#include <Kernel/UnixTypes.h>
#include <Kernel/Process.h>
#include <Kernel/NetworkAdapter.h>
#include <LibC/errno_numbers.h>
Retained<IPv4Socket> IPv4Socket::create(int type, int protocol)
{
return adopt(*new IPv4Socket(type, protocol));
}
IPv4Socket::IPv4Socket(int type, int protocol)
: Socket(AF_INET, type, protocol)
{
kprintf("%s(%u) IPv4Socket{%p} created with type=%u\n", current->name().characters(), current->pid(), this, type);
}
IPv4Socket::~IPv4Socket()
{
}
bool IPv4Socket::get_address(sockaddr* address, socklen_t* address_size)
{
// FIXME: Look into what fallback behavior we should have here.
if (*address_size != sizeof(sockaddr_in))
return false;
memcpy(address, &m_peer_address, sizeof(sockaddr_in));
*address_size = sizeof(sockaddr_in);
return true;
}
KResult IPv4Socket::bind(const sockaddr* address, socklen_t address_size)
{
ASSERT(!is_connected());
if (address_size != sizeof(sockaddr_in))
return KResult(-EINVAL);
if (address->sa_family != AF_INET)
return KResult(-EINVAL);
ASSERT_NOT_REACHED();
}
KResult IPv4Socket::connect(const sockaddr* address, socklen_t address_size)
{
ASSERT(!m_bound);
if (address_size != sizeof(sockaddr_in))
return KResult(-EINVAL);
if (address->sa_family != AF_INET)
return KResult(-EINVAL);
ASSERT_NOT_REACHED();
}
void IPv4Socket::attach_fd(SocketRole)
{
++m_attached_fds;
}
void IPv4Socket::detach_fd(SocketRole)
{
--m_attached_fds;
}
bool IPv4Socket::can_read(SocketRole) const
{
ASSERT_NOT_REACHED();
}
ssize_t IPv4Socket::read(SocketRole role, byte* buffer, ssize_t size)
{
ASSERT_NOT_REACHED();
}
ssize_t IPv4Socket::write(SocketRole role, const byte* data, ssize_t size)
{
ASSERT_NOT_REACHED();
}
bool IPv4Socket::can_write(SocketRole role) const
{
ASSERT_NOT_REACHED();
}
ssize_t IPv4Socket::sendto(const void* data, size_t data_length, int flags, const sockaddr* addr, socklen_t addr_length)
{
(void)flags;
ASSERT(data_length);
if (addr_length != sizeof(sockaddr_in))
return -EINVAL;
// FIXME: Find the adapter some better way!
auto* adapter = NetworkAdapter::from_ipv4_address(IPv4Address(192, 168, 5, 2));
if (!adapter) {
// FIXME: Figure out which error code to return.
ASSERT_NOT_REACHED();
}
if (addr->sa_family != AF_INET) {
kprintf("sendto: Bad address family: %u is not AF_INET!\n", addr->sa_family);
return -EAFNOSUPPORT;
}
auto peer_address = IPv4Address((const byte*)&((const sockaddr_in*)addr)->sin_addr.s_addr);
kprintf("sendto: peer_address=%s\n", peer_address.to_string().characters());
// FIXME: If we can't find the right MAC address, block until it's available?
// I feel like this should happen in a layer below this code.
MACAddress mac_address;
adapter->send_ipv4(mac_address, peer_address, (IPv4Protocol)protocol(), ByteBuffer::copy((const byte*)data, data_length));
return data_length;
}

34
Kernel/IPv4Socket.h Normal file
View file

@ -0,0 +1,34 @@
#pragma once
#include <Kernel/Socket.h>
#include <Kernel/DoubleBuffer.h>
#include <Kernel/IPv4.h>
class IPv4Socket final : public Socket {
public:
static Retained<IPv4Socket> create(int type, int protocol);
virtual ~IPv4Socket() override;
virtual KResult bind(const sockaddr*, socklen_t) override;
virtual KResult connect(const sockaddr*, socklen_t) override;
virtual bool get_address(sockaddr*, socklen_t*) override;
virtual void attach_fd(SocketRole) override;
virtual void detach_fd(SocketRole) override;
virtual bool can_read(SocketRole) const override;
virtual ssize_t read(SocketRole, byte*, ssize_t) override;
virtual ssize_t write(SocketRole, const byte*, ssize_t) override;
virtual bool can_write(SocketRole) const override;
virtual ssize_t sendto(const void*, size_t, int, const sockaddr*, socklen_t) override;
private:
IPv4Socket(int type, int protocol);
virtual bool is_ipv4() const override { return true; }
bool m_bound { false };
int m_attached_fds { 0 };
IPv4Address m_peer_address;
DoubleBuffer m_for_client;
DoubleBuffer m_for_server;
};

View file

@ -163,3 +163,8 @@ bool LocalSocket::can_write(SocketRole role) const
return !m_accepted_fds_open || m_for_server.bytes_in_write_buffer() < 4096;
ASSERT_NOT_REACHED();
}
ssize_t LocalSocket::sendto(const void*, size_t, int, const sockaddr*, socklen_t)
{
ASSERT_NOT_REACHED();
}

View file

@ -19,6 +19,7 @@ public:
virtual ssize_t read(SocketRole, byte*, ssize_t) override;
virtual ssize_t write(SocketRole, const byte*, ssize_t) override;
virtual bool can_write(SocketRole) const override;
virtual ssize_t sendto(const void*, size_t, int, const sockaddr*, socklen_t) override;
private:
explicit LocalSocket(int type);

View file

@ -34,6 +34,7 @@ KERNEL_OBJS = \
PS2MouseDevice.o \
Socket.o \
LocalSocket.o \
IPv4Socket.o \
NetworkAdapter.o \
E1000NetworkAdapter.o \
NetworkTask.o

View file

@ -2515,6 +2515,32 @@ KResult Process::wait_for_connect(Socket& socket)
return KSuccess;
}
ssize_t Process::sys$sendto(const Syscall::SC_sendto_params* params)
{
if (!validate_read_typed(params))
return -EFAULT;
int sockfd = params->sockfd;
const void* data = params->data;
size_t data_length = params->data_length;
int flags = params->flags;
auto* addr = (const sockaddr*)params->addr;
auto addr_length = (socklen_t)params->addr_length;
if (!validate_read(data, data_length))
return -EFAULT;
if (!validate_read(addr, addr_length))
return -EFAULT;
auto* descriptor = file_descriptor(sockfd);
if (!descriptor)
return -EBADF;
if (!descriptor->is_socket())
return -ENOTSOCK;
auto& socket = *descriptor->socket();
kprintf("sendto %p (%u), flags=%u, addr: %p (%u)\n", data, data_length, flags, addr, addr_length);
return socket.sendto(data, data_length, flags, addr, addr_length);
}
struct SharedBuffer {
SharedBuffer(pid_t pid1, pid_t pid2, int size)
: m_pid1(pid1)

View file

@ -230,6 +230,7 @@ public:
int sys$listen(int sockfd, int backlog);
int sys$accept(int sockfd, sockaddr*, socklen_t*);
int sys$connect(int sockfd, const sockaddr*, socklen_t);
ssize_t sys$sendto(const Syscall::SC_sendto_params*);
int sys$restore_signal_mask(dword mask);
int sys$create_shared_buffer(pid_t peer_pid, int, void** buffer);

View file

@ -1,5 +1,6 @@
#include <Kernel/Socket.h>
#include <Kernel/LocalSocket.h>
#include <Kernel/IPv4Socket.h>
#include <Kernel/UnixTypes.h>
#include <Kernel/Process.h>
#include <LibC/errno_numbers.h>
@ -10,6 +11,8 @@ KResultOr<Retained<Socket>> Socket::create(int domain, int type, int protocol)
switch (domain) {
case AF_LOCAL:
return LocalSocket::create(type & SOCK_TYPE_MASK);
case AF_INET:
return IPv4Socket::create(type & SOCK_TYPE_MASK, protocol);
default:
return KResult(-EAFNOSUPPORT);
}

View file

@ -28,12 +28,14 @@ public:
virtual KResult connect(const sockaddr*, socklen_t) = 0;
virtual bool get_address(sockaddr*, socklen_t*) = 0;
virtual bool is_local() const { return false; }
virtual bool is_ipv4() const { return false; }
virtual void attach_fd(SocketRole) = 0;
virtual void detach_fd(SocketRole) = 0;
virtual bool can_read(SocketRole) const = 0;
virtual ssize_t read(SocketRole, byte*, ssize_t) = 0;
virtual ssize_t write(SocketRole, const byte*, ssize_t) = 0;
virtual bool can_write(SocketRole) const = 0;
virtual ssize_t sendto(const void*, size_t, int flags, const sockaddr*, socklen_t) = 0;
pid_t origin_pid() const { return m_origin_pid; }

View file

@ -227,6 +227,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
return current->sys$seal_shared_buffer((int)arg1);
case Syscall::SC_get_shared_buffer_size:
return current->sys$get_shared_buffer_size((int)arg1);
case Syscall::SC_sendto:
return current->sys$sendto((const SC_sendto_params*)arg1);
default:
kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3);
break;

View file

@ -88,6 +88,7 @@
__ENUMERATE_SYSCALL(restore_signal_mask) \
__ENUMERATE_SYSCALL(get_shared_buffer_size) \
__ENUMERATE_SYSCALL(seal_shared_buffer) \
__ENUMERATE_SYSCALL(sendto) \
namespace Syscall {
@ -128,6 +129,15 @@ struct SC_select_params {
struct timeval* timeout;
};
struct SC_sendto_params {
int sockfd;
const void* data;
size_t data_length;
int flags;
const void* addr; // const sockaddr*
size_t addr_length; // socklen_t
};
void initialize();
int sync();

View file

@ -315,12 +315,20 @@ struct pollfd {
#define AF_MASK 0xff
#define AF_UNSPEC 0
#define AF_LOCAL 1
#define AF_INET 2
#define PF_LOCAL AF_LOCAL
#define PF_INET AF_INET
#define SOCK_TYPE_MASK 0xff
#define SOCK_STREAM 1
#define SOCK_RAW 3
#define SOCK_NONBLOCK 04000
#define SOCK_CLOEXEC 02000000
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
struct sockaddr {
word sa_family;
char sa_data[14];
@ -333,3 +341,14 @@ struct sockaddr_un {
word sun_family;
char sun_path[UNIX_PATH_MAX];
};
struct in_addr {
uint32_t s_addr;
};
struct sockaddr_in {
int16_t sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};

View file

@ -74,6 +74,7 @@ cp -v ../Userland/df mnt/bin/df
cp -v ../Userland/su mnt/bin/su
cp -v ../Userland/env mnt/bin/env
cp -v ../Userland/stat mnt/bin/stat
cp -v ../Userland/ping mnt/bin/ping
chmod 4755 mnt/bin/su
cp -v ../Applications/Terminal/Terminal mnt/bin/Terminal
cp -v ../Applications/FontEditor/FontEditor mnt/bin/FontEditor

View file

@ -39,6 +39,7 @@ LIBC_OBJS = \
sys/wait.o \
poll.o \
locale.o \
arpa/inet.o \
crt0.o
ASM_OBJS = setjmp.no

19
LibC/arpa/inet.cpp Normal file
View file

@ -0,0 +1,19 @@
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
extern "C" {
const char* inet_ntop(int af, const void* src, char* dst, socklen_t len)
{
if (af != AF_INET) {
errno = EAFNOSUPPORT;
return nullptr;
}
auto* bytes = (const unsigned char*)src;
snprintf(dst, len, "%u.%u.%u.%u", bytes[3], bytes[2], bytes[1], bytes[0]);
return (const char*)dst;
}
}

22
LibC/arpa/inet.h Normal file
View file

@ -0,0 +1,22 @@
#pragma once
#include <sys/cdefs.h>
#include <sys/socket.h>
__BEGIN_DECLS
const char* inet_ntop(int af, const void* src, char* dst, socklen_t);
static inline uint16_t htons(uint16_t hs)
{
uint8_t* s = (uint8_t*)&hs;
return (uint16_t)(s[0] << 8 | s[1]);
}
static inline uint16_t ntohs(uint16_t ns)
{
return htons(ns);
}
__END_DECLS

22
LibC/netinet/ip_icmp.h Normal file
View file

@ -0,0 +1,22 @@
#pragma once
#include <sys/cdefs.h>
#include <stdint.h>
__BEGIN_DECLS
struct icmphdr {
uint8_t type;
uint8_t code;
uint16_t checksum;
union {
struct {
uint16_t id;
uint16_t sequence;
} echo;
uint32_t gateway;
} un;
};
__END_DECLS

View file

@ -34,5 +34,12 @@ int connect(int sockfd, const sockaddr* addr, socklen_t addrlen)
__RETURN_WITH_ERRNO(rc, rc, -1);
}
ssize_t sendto(int sockfd, const void* data, size_t data_length, int flags, const struct sockaddr* addr, socklen_t addr_length)
{
Syscall::SC_sendto_params params { sockfd, data, data_length, flags, addr, addr_length };
int rc = syscall(SC_sendto, &params);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
}

View file

@ -2,34 +2,55 @@
#include <sys/cdefs.h>
#include <sys/types.h>
#include <stdint.h>
__BEGIN_DECLS
#define AF_MASK 0xff
#define AF_UNSPEC 0
#define AF_LOCAL 1
#define AF_INET 2
#define PF_LOCAL AF_LOCAL
#define PF_INET AF_INET
#define SOCK_TYPE_MASK 0xff
#define SOCK_STREAM 1
#define SOCK_RAW 3
#define SOCK_NONBLOCK 04000
#define SOCK_CLOEXEC 02000000
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
struct sockaddr {
unsigned short sa_family;
uint16_t sa_family;
char sa_data[14];
};
#define UNIX_PATH_MAX 108
struct sockaddr_un {
unsigned short sun_family;
uint16_t sun_family;
char sun_path[UNIX_PATH_MAX];
};
struct in_addr {
uint32_t s_addr;
};
struct sockaddr_in {
uint16_t sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
int socket(int domain, int type, int protocol);
int bind(int sockfd, const sockaddr* addr, socklen_t);
int listen(int sockfd, int backlog);
int accept(int sockfd, sockaddr*, socklen_t*);
int connect(int sockfd, const sockaddr*, socklen_t);
ssize_t sendto(int sockfd, const void*, size_t, int flags, const struct sockaddr*, socklen_t);
__END_DECLS

1
Userland/.gitignore vendored
View file

@ -37,3 +37,4 @@ su
env
chown
stat
ping

View file

@ -33,6 +33,7 @@ OBJS = \
su.o \
env.o \
stat.o \
ping.o \
rm.o
APPS = \
@ -71,6 +72,7 @@ APPS = \
su \
env \
stat \
ping \
rm
ARCH_FLAGS =
@ -198,6 +200,9 @@ env: env.o
stat: stat.o
$(LD) -o $@ $(LDFLAGS) $< -lc
ping: ping.o
$(LD) -o $@ $(LDFLAGS) $< -lc
.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<

58
Userland/ping.cpp Normal file
View file

@ -0,0 +1,58 @@
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <stdio.h>
#include <string.h>
#include <Kernel/NetworkOrdered.h>
NetworkOrdered<word> internet_checksum(const void* ptr, size_t count)
{
dword checksum = 0;
auto* w = (const word*)ptr;
while (count > 1) {
checksum += convert_between_host_and_network(*w++);
if (checksum & 0x80000000)
checksum = (checksum & 0xffff) | (checksum >> 16);
count -= 2;
}
while (checksum >> 16)
checksum = (checksum & 0xffff) + (checksum >> 16);
return ~checksum & 0xffff;
}
int main(int argc, char** argv)
{
int fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (fd < 0) {
perror("socket");
return 1;
}
sockaddr_in peer_address;
memset(&peer_address, 0, sizeof(peer_address));
peer_address.sin_family = AF_INET;
peer_address.sin_port = 0;
peer_address.sin_addr.s_addr = 0x0105a8c0; // 192.168.5.1
struct PingPacket {
struct icmphdr header;
char msg[64 - sizeof(struct icmphdr)];
};
PingPacket ping_packet;
memset(&ping_packet, 0, sizeof(PingPacket));
ping_packet.header.type = 8; // Echo request
strcpy(ping_packet.msg, "Hello there!\n");
ping_packet.header.checksum = htons(internet_checksum(&ping_packet, sizeof(PingPacket)));
int rc = sendto(fd, &ping_packet, sizeof(PingPacket), 0, (const struct sockaddr*)&peer_address, sizeof(sockaddr_in));
if (rc < 0) {
perror("sendto");
return 1;
}
return 0;
}