Pārlūkot izejas kodu

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

The first userland networking program will be "ping" :^)
Andreas Kling 6 gadi atpakaļ
vecāks
revīzija
a017a77442

+ 0 - 1
Kernel/IPv4.h

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

+ 111 - 0
Kernel/IPv4Socket.cpp

@@ -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 - 0
Kernel/IPv4Socket.h

@@ -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;
+};
+

+ 5 - 0
Kernel/LocalSocket.cpp

@@ -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();
+}

+ 1 - 0
Kernel/LocalSocket.h

@@ -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);

+ 1 - 0
Kernel/Makefile

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

+ 26 - 0
Kernel/Process.cpp

@@ -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)

+ 1 - 0
Kernel/Process.h

@@ -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);

+ 3 - 0
Kernel/Socket.cpp

@@ -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);
     }

+ 2 - 0
Kernel/Socket.h

@@ -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; }
 

+ 2 - 0
Kernel/Syscall.cpp

@@ -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;

+ 10 - 0
Kernel/Syscall.h

@@ -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();
 

+ 19 - 0
Kernel/UnixTypes.h

@@ -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];
+};

+ 1 - 0
Kernel/sync.sh

@@ -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

+ 1 - 0
LibC/Makefile

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

+ 19 - 0
LibC/arpa/inet.cpp

@@ -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 - 0
LibC/arpa/inet.h

@@ -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 - 0
LibC/netinet/ip_icmp.h

@@ -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
+

+ 7 - 0
LibC/sys/socket.cpp

@@ -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);
+}
+
 }
 

+ 23 - 2
LibC/sys/socket.h

@@ -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 - 0
Userland/.gitignore

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

+ 5 - 0
Userland/Makefile

@@ -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 - 0
Userland/ping.cpp

@@ -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;
+}