1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-28 09:55:06 +00:00

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[2] = c;
m_data[3] = d; m_data[3] = d;
} }
~IPv4Address() { }
byte operator[](int i) const 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; return !m_accepted_fds_open || m_for_server.bytes_in_write_buffer() < 4096;
ASSERT_NOT_REACHED(); 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 read(SocketRole, byte*, ssize_t) override;
virtual ssize_t write(SocketRole, const byte*, ssize_t) override; virtual ssize_t write(SocketRole, const byte*, ssize_t) override;
virtual bool can_write(SocketRole) const override; virtual bool can_write(SocketRole) const override;
virtual ssize_t sendto(const void*, size_t, int, const sockaddr*, socklen_t) override;
private: private:
explicit LocalSocket(int type); explicit LocalSocket(int type);

View file

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

View file

@ -2515,6 +2515,32 @@ KResult Process::wait_for_connect(Socket& socket)
return KSuccess; 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 { struct SharedBuffer {
SharedBuffer(pid_t pid1, pid_t pid2, int size) SharedBuffer(pid_t pid1, pid_t pid2, int size)
: m_pid1(pid1) : m_pid1(pid1)

View file

@ -230,6 +230,7 @@ public:
int sys$listen(int sockfd, int backlog); int sys$listen(int sockfd, int backlog);
int sys$accept(int sockfd, sockaddr*, socklen_t*); int sys$accept(int sockfd, sockaddr*, socklen_t*);
int sys$connect(int sockfd, const 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$restore_signal_mask(dword mask);
int sys$create_shared_buffer(pid_t peer_pid, int, void** buffer); int sys$create_shared_buffer(pid_t peer_pid, int, void** buffer);

View file

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

View file

@ -28,12 +28,14 @@ public:
virtual KResult connect(const sockaddr*, socklen_t) = 0; virtual KResult connect(const sockaddr*, socklen_t) = 0;
virtual bool get_address(sockaddr*, socklen_t*) = 0; virtual bool get_address(sockaddr*, socklen_t*) = 0;
virtual bool is_local() const { return false; } virtual bool is_local() const { return false; }
virtual bool is_ipv4() const { return false; }
virtual void attach_fd(SocketRole) = 0; virtual void attach_fd(SocketRole) = 0;
virtual void detach_fd(SocketRole) = 0; virtual void detach_fd(SocketRole) = 0;
virtual bool can_read(SocketRole) const = 0; virtual bool can_read(SocketRole) const = 0;
virtual ssize_t read(SocketRole, byte*, ssize_t) = 0; virtual ssize_t read(SocketRole, byte*, ssize_t) = 0;
virtual ssize_t write(SocketRole, const byte*, ssize_t) = 0; virtual ssize_t write(SocketRole, const byte*, ssize_t) = 0;
virtual bool can_write(SocketRole) const = 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; } 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); return current->sys$seal_shared_buffer((int)arg1);
case Syscall::SC_get_shared_buffer_size: case Syscall::SC_get_shared_buffer_size:
return current->sys$get_shared_buffer_size((int)arg1); return current->sys$get_shared_buffer_size((int)arg1);
case Syscall::SC_sendto:
return current->sys$sendto((const SC_sendto_params*)arg1);
default: default:
kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3);
break; break;

View file

@ -88,6 +88,7 @@
__ENUMERATE_SYSCALL(restore_signal_mask) \ __ENUMERATE_SYSCALL(restore_signal_mask) \
__ENUMERATE_SYSCALL(get_shared_buffer_size) \ __ENUMERATE_SYSCALL(get_shared_buffer_size) \
__ENUMERATE_SYSCALL(seal_shared_buffer) \ __ENUMERATE_SYSCALL(seal_shared_buffer) \
__ENUMERATE_SYSCALL(sendto) \
namespace Syscall { namespace Syscall {
@ -128,6 +129,15 @@ struct SC_select_params {
struct timeval* timeout; 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(); void initialize();
int sync(); int sync();

View file

@ -315,12 +315,20 @@ struct pollfd {
#define AF_MASK 0xff #define AF_MASK 0xff
#define AF_UNSPEC 0 #define AF_UNSPEC 0
#define AF_LOCAL 1 #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_TYPE_MASK 0xff
#define SOCK_STREAM 1 #define SOCK_STREAM 1
#define SOCK_RAW 3
#define SOCK_NONBLOCK 04000 #define SOCK_NONBLOCK 04000
#define SOCK_CLOEXEC 02000000 #define SOCK_CLOEXEC 02000000
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
struct sockaddr { struct sockaddr {
word sa_family; word sa_family;
char sa_data[14]; char sa_data[14];
@ -333,3 +341,14 @@ struct sockaddr_un {
word sun_family; word sun_family;
char sun_path[UNIX_PATH_MAX]; 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/su mnt/bin/su
cp -v ../Userland/env mnt/bin/env cp -v ../Userland/env mnt/bin/env
cp -v ../Userland/stat mnt/bin/stat cp -v ../Userland/stat mnt/bin/stat
cp -v ../Userland/ping mnt/bin/ping
chmod 4755 mnt/bin/su chmod 4755 mnt/bin/su
cp -v ../Applications/Terminal/Terminal mnt/bin/Terminal cp -v ../Applications/Terminal/Terminal mnt/bin/Terminal
cp -v ../Applications/FontEditor/FontEditor mnt/bin/FontEditor cp -v ../Applications/FontEditor/FontEditor mnt/bin/FontEditor

View file

@ -39,6 +39,7 @@ LIBC_OBJS = \
sys/wait.o \ sys/wait.o \
poll.o \ poll.o \
locale.o \ locale.o \
arpa/inet.o \
crt0.o crt0.o
ASM_OBJS = setjmp.no 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); __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/cdefs.h>
#include <sys/types.h> #include <sys/types.h>
#include <stdint.h>
__BEGIN_DECLS __BEGIN_DECLS
#define AF_MASK 0xff #define AF_MASK 0xff
#define AF_UNSPEC 0 #define AF_UNSPEC 0
#define AF_LOCAL 1 #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_TYPE_MASK 0xff
#define SOCK_STREAM 1 #define SOCK_STREAM 1
#define SOCK_RAW 3
#define SOCK_NONBLOCK 04000 #define SOCK_NONBLOCK 04000
#define SOCK_CLOEXEC 02000000 #define SOCK_CLOEXEC 02000000
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
struct sockaddr { struct sockaddr {
unsigned short sa_family; uint16_t sa_family;
char sa_data[14]; char sa_data[14];
}; };
#define UNIX_PATH_MAX 108 #define UNIX_PATH_MAX 108
struct sockaddr_un { struct sockaddr_un {
unsigned short sun_family; uint16_t sun_family;
char sun_path[UNIX_PATH_MAX]; 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 socket(int domain, int type, int protocol);
int bind(int sockfd, const sockaddr* addr, socklen_t); int bind(int sockfd, const sockaddr* addr, socklen_t);
int listen(int sockfd, int backlog); int listen(int sockfd, int backlog);
int accept(int sockfd, sockaddr*, socklen_t*); int accept(int sockfd, sockaddr*, socklen_t*);
int connect(int sockfd, const 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 __END_DECLS

1
Userland/.gitignore vendored
View file

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

View file

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