diff --git a/Kernel/IPv4.h b/Kernel/IPv4.h index 9b70764f85..55cac8d78a 100644 --- a/Kernel/IPv4.h +++ b/Kernel/IPv4.h @@ -28,7 +28,6 @@ public: m_data[2] = c; m_data[3] = d; } - ~IPv4Address() { } byte operator[](int i) const { diff --git a/Kernel/IPv4Socket.cpp b/Kernel/IPv4Socket.cpp new file mode 100644 index 0000000000..64a06609d7 --- /dev/null +++ b/Kernel/IPv4Socket.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include + +Retained 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; +} diff --git a/Kernel/IPv4Socket.h b/Kernel/IPv4Socket.h new file mode 100644 index 0000000000..98d4bda5d8 --- /dev/null +++ b/Kernel/IPv4Socket.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +class IPv4Socket final : public Socket { +public: + static Retained 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; +}; + diff --git a/Kernel/LocalSocket.cpp b/Kernel/LocalSocket.cpp index c1a77f1f92..5cd7f9b58d 100644 --- a/Kernel/LocalSocket.cpp +++ b/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(); +} diff --git a/Kernel/LocalSocket.h b/Kernel/LocalSocket.h index e0c49d7b79..b4972c8035 100644 --- a/Kernel/LocalSocket.h +++ b/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); diff --git a/Kernel/Makefile b/Kernel/Makefile index 16fba0effc..db5ee4f051 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -34,6 +34,7 @@ KERNEL_OBJS = \ PS2MouseDevice.o \ Socket.o \ LocalSocket.o \ + IPv4Socket.o \ NetworkAdapter.o \ E1000NetworkAdapter.o \ NetworkTask.o diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 00cf2746d5..13600f47f7 100644 --- a/Kernel/Process.cpp +++ b/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) diff --git a/Kernel/Process.h b/Kernel/Process.h index 6c9a17ae20..af58a4339a 100644 --- a/Kernel/Process.h +++ b/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); diff --git a/Kernel/Socket.cpp b/Kernel/Socket.cpp index 454ce920f3..3db9a1f80d 100644 --- a/Kernel/Socket.cpp +++ b/Kernel/Socket.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -10,6 +11,8 @@ KResultOr> 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); } diff --git a/Kernel/Socket.h b/Kernel/Socket.h index 1144ece54a..04575f616d 100644 --- a/Kernel/Socket.h +++ b/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; } diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 2f0cb277c9..3e38b66df8 100644 --- a/Kernel/Syscall.cpp +++ b/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; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 4a767b0f5f..6cb3c9ac59 100644 --- a/Kernel/Syscall.h +++ b/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(); diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index 300951cb4d..66bd1ffe6e 100644 --- a/Kernel/UnixTypes.h +++ b/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]; +}; diff --git a/Kernel/sync.sh b/Kernel/sync.sh index 00f5876218..36d566972e 100755 --- a/Kernel/sync.sh +++ b/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 diff --git a/LibC/Makefile b/LibC/Makefile index c11873f17d..ba3a69695b 100644 --- a/LibC/Makefile +++ b/LibC/Makefile @@ -39,6 +39,7 @@ LIBC_OBJS = \ sys/wait.o \ poll.o \ locale.o \ + arpa/inet.o \ crt0.o ASM_OBJS = setjmp.no diff --git a/LibC/arpa/inet.cpp b/LibC/arpa/inet.cpp new file mode 100644 index 0000000000..f4806609e9 --- /dev/null +++ b/LibC/arpa/inet.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +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; +} + +} + diff --git a/LibC/arpa/inet.h b/LibC/arpa/inet.h new file mode 100644 index 0000000000..8e913233bd --- /dev/null +++ b/LibC/arpa/inet.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +__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 + diff --git a/LibC/netinet/ip_icmp.h b/LibC/netinet/ip_icmp.h new file mode 100644 index 0000000000..7f18bea110 --- /dev/null +++ b/LibC/netinet/ip_icmp.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +__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 + diff --git a/LibC/sys/socket.cpp b/LibC/sys/socket.cpp index af13c8c052..1a85ffff96 100644 --- a/LibC/sys/socket.cpp +++ b/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, ¶ms); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + } diff --git a/LibC/sys/socket.h b/LibC/sys/socket.h index d5028388f1..59407872e1 100644 --- a/LibC/sys/socket.h +++ b/LibC/sys/socket.h @@ -2,34 +2,55 @@ #include #include +#include __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 diff --git a/Userland/.gitignore b/Userland/.gitignore index fc991ef960..17a5d60640 100644 --- a/Userland/.gitignore +++ b/Userland/.gitignore @@ -37,3 +37,4 @@ su env chown stat +ping diff --git a/Userland/Makefile b/Userland/Makefile index 3450d3d912..6acf59947b 100644 --- a/Userland/Makefile +++ b/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 $< diff --git a/Userland/ping.cpp b/Userland/ping.cpp new file mode 100644 index 0000000000..321cdff48c --- /dev/null +++ b/Userland/ping.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +NetworkOrdered 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; +}