Skip to content

Commit 5a0aebd

Browse files
Add ICMP socket syscalls for toybox ping
1 parent 718e013 commit 5a0aebd

5 files changed

Lines changed: 182 additions & 10 deletions

File tree

source/includes/stream.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ void fd_table_init(void);
4646
bool fd_valid(int fd);
4747
vfs_file_t *fd_get_file(int fd);
4848
int fd_open(const char *path, int flags);
49+
int fd_create_virtual(const char *path, int flags);
4950
int fd_close(int fd);
5051
int fd_dup(int oldfd);
5152
int fd_dup2(int oldfd, int newfd);

source/includes/syscalls.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#define LINUX_SYS_GETPID 39
4040
#define LINUX_SYS_SOCKET 41
4141
#define LINUX_SYS_CONNECT 42
42+
#define LINUX_SYS_SENDTO 44
43+
#define LINUX_SYS_RECVFROM 45
44+
#define LINUX_SYS_SETSOCKOPT 54
4245
#define LINUX_SYS_CLONE 56
4346
#define LINUX_SYS_FORK 57
4447
#define LINUX_SYS_WAIT4 61
@@ -117,6 +120,8 @@
117120
#define LINUX_ECHILD 10
118121
#define LINUX_ENOTSOCK 88
119122
#define LINUX_EAFNOSUPPORT 97
123+
#define LINUX_EPROTONOSUPPORT 93
124+
#define LINUX_EOPNOTSUPP 95
120125

121126
#define LINUX_PROT_NONE 0x0
122127
#define LINUX_PROT_READ 0x1

source/kernel/C/shell/commands/wget.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
#include <net/net.h>
44
int cmd_wget(int argc, char **argv) {
55
if (argc < 3) {
6-
printf("usage: wget <http-url> <output-file>");
6+
printf("usage: wget <http-url> <output-file> (https:// requires TLS, not linked yet)");
77
return 1;
88
}
99
int r = http_get_to_file(argv[1], argv[2]);
1010
if (r == NET_OK) {
1111
printf("saved %s", argv[2]);
1212
return 0;
1313
}
14-
printf("wget: failed (%d). HTTPS/TLS is currently out of scope; use http://", r);
14+
if (r == NET_ENOTSUP)
15+
printf("wget: HTTPS needs TLS support; socket/syscall plumbing is present, but TLS is not linked yet");
16+
else
17+
printf("wget: failed (%d)", r);
1518
return 1;
1619
}

source/kernel/C/stream.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,23 @@ int fd_open(const char *path, int flags) {
208208
return fd;
209209
}
210210

211+
int fd_create_virtual(const char *path, int flags) {
212+
int fd = fd_alloc_slot();
213+
if (fd < 0)
214+
return -1;
215+
216+
fd_object_t *object = fd_object_alloc(NULL, false, flags);
217+
if (!object)
218+
return -1;
219+
220+
if (path)
221+
vfs_normalize_path(path, object->path, sizeof(object->path));
222+
223+
fd_table[fd].used = true;
224+
fd_table[fd].object = object;
225+
return fd;
226+
}
227+
211228
int fd_close(int fd) {
212229
if (!fd_valid(fd))
213230
return -1;

source/kernel/C/syscalls.c

Lines changed: 154 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <executables/elf.h>
3636
#include <heap.h>
3737
#include <multitasking.h>
38+
#include <net/net.h>
3839
#include <rtc.h>
3940
#include <tty.h>
4041

@@ -59,6 +60,37 @@ typedef struct {
5960
uint64_t inode;
6061
} vfs_stat_info_t;
6162

63+
#define LINUX_AF_INET 2
64+
#define LINUX_SOCK_RAW 3
65+
#define LINUX_IPPROTO_ICMP 1
66+
67+
typedef struct {
68+
uint16_t family;
69+
uint16_t port;
70+
uint32_t addr;
71+
uint8_t zero[8];
72+
} linux_sockaddr_in_t;
73+
74+
typedef struct {
75+
bool used;
76+
int fd;
77+
int domain;
78+
int type;
79+
int protocol;
80+
uint8_t reply[128];
81+
size_t reply_len;
82+
net_ipv4_t reply_addr;
83+
} linux_socket_t;
84+
85+
static linux_socket_t linux_sockets[16];
86+
87+
static linux_socket_t *linux_socket_by_fd(int fd) {
88+
for (int i = 0; i < (int)(sizeof(linux_sockets) / sizeof(linux_sockets[0])); i++)
89+
if (linux_sockets[i].used && linux_sockets[i].fd == fd)
90+
return &linux_sockets[i];
91+
return NULL;
92+
}
93+
6294
// char current_exec_path[256] = "/";
6395
uint64_t current_fs_base = 0;
6496
uint32_t current_umask = 022;
@@ -781,6 +813,10 @@ static uint64 sys_close(uint64_t fd) {
781813
if (!fd_valid((int)fd))
782814
return -LINUX_EBADF;
783815

816+
linux_socket_t *sock = linux_socket_by_fd((int)fd);
817+
if (sock)
818+
memset(sock, 0, sizeof(*sock));
819+
784820
return fd_close((int)fd) == 0 ? 0 : -LINUX_EBADF;
785821
}
786822

@@ -909,14 +945,32 @@ static uint64 sys_writev(uint64_t fd, const linux_iovec_t *iov, uint64_t iovcnt)
909945
}
910946

911947
static uint64 sys_socket(uint64_t domain, uint64_t type, uint64_t protocol) {
912-
(void)type;
913-
(void)protocol;
914-
915-
// Networking stack is not exposed through Linux sockets yet.
916-
// Return Linux-like "address family not supported" instead of a fake fd.
917-
if (domain == 1 || domain == 2 || domain == 10)
948+
if (domain != LINUX_AF_INET)
918949
return -LINUX_EAFNOSUPPORT;
919-
return -LINUX_EAFNOSUPPORT;
950+
if (type != LINUX_SOCK_RAW || protocol != LINUX_IPPROTO_ICMP)
951+
return -LINUX_EPROTONOSUPPORT;
952+
953+
linux_socket_t *sock = NULL;
954+
for (int i = 0; i < (int)(sizeof(linux_sockets) / sizeof(linux_sockets[0])); i++) {
955+
if (!linux_sockets[i].used) {
956+
sock = &linux_sockets[i];
957+
break;
958+
}
959+
}
960+
if (!sock)
961+
return -LINUX_ENFILE;
962+
963+
int fd = fd_create_virtual("/dev/socket/icmp", VFS_RDWR);
964+
if (fd < 0)
965+
return -LINUX_ENFILE;
966+
967+
memset(sock, 0, sizeof(*sock));
968+
sock->used = true;
969+
sock->fd = fd;
970+
sock->domain = (int)domain;
971+
sock->type = (int)type;
972+
sock->protocol = (int)protocol;
973+
return fd;
920974
}
921975

922976
static uint64 sys_connect(uint64_t fd, const void *addr, uint64_t addrlen) {
@@ -928,6 +982,84 @@ static uint64 sys_connect(uint64_t fd, const void *addr, uint64_t addrlen) {
928982
return -LINUX_ENOTSOCK;
929983
}
930984

985+
static uint64 sys_sendto(uint64_t fd, const void *buf, uint64_t len, uint64_t flags,
986+
const void *addr, uint64_t addrlen) {
987+
(void)flags;
988+
linux_socket_t *sock = linux_socket_by_fd((int)fd);
989+
if (!sock)
990+
return fd_valid((int)fd) ? -LINUX_ENOTSOCK : -LINUX_EBADF;
991+
if (!buf || !addr || addrlen < sizeof(linux_sockaddr_in_t))
992+
return -LINUX_EINVAL;
993+
994+
const linux_sockaddr_in_t *in = (const linux_sockaddr_in_t *)addr;
995+
if (in->family != LINUX_AF_INET)
996+
return -LINUX_EAFNOSUPPORT;
997+
998+
net_ipv4_t dst = net_ntohl(in->addr);
999+
uint16_t id = 0x4242;
1000+
uint16_t seq = 0;
1001+
if (len >= 8) {
1002+
const uint8_t *icmp = (const uint8_t *)buf;
1003+
id = ((uint16_t)icmp[4] << 8) | icmp[5];
1004+
seq = ((uint16_t)icmp[6] << 8) | icmp[7];
1005+
}
1006+
1007+
int r = icmp_ping(dst, id, seq, 500000);
1008+
if (r != NET_OK)
1009+
return -LINUX_ETIMEDOUT;
1010+
1011+
sock->reply_addr = dst;
1012+
sock->reply_len = len < sizeof(sock->reply) ? len : sizeof(sock->reply);
1013+
memcpy(sock->reply, buf, sock->reply_len);
1014+
if (sock->reply_len >= 1)
1015+
sock->reply[0] = 0; /* echo reply */
1016+
if (sock->reply_len >= 4) {
1017+
sock->reply[2] = 0;
1018+
sock->reply[3] = 0;
1019+
uint16_t sum = net_checksum(sock->reply, sock->reply_len);
1020+
sock->reply[2] = (uint8_t)(sum >> 8);
1021+
sock->reply[3] = (uint8_t)sum;
1022+
}
1023+
return len;
1024+
}
1025+
1026+
static uint64 sys_recvfrom(uint64_t fd, void *buf, uint64_t len, uint64_t flags,
1027+
void *addr, uint64_t *addrlen) {
1028+
(void)flags;
1029+
linux_socket_t *sock = linux_socket_by_fd((int)fd);
1030+
if (!sock)
1031+
return fd_valid((int)fd) ? -LINUX_ENOTSOCK : -LINUX_EBADF;
1032+
if (!buf)
1033+
return -LINUX_EINVAL;
1034+
if (!sock->reply_len)
1035+
return -LINUX_EAGAIN;
1036+
1037+
size_t n = sock->reply_len < len ? sock->reply_len : len;
1038+
memcpy(buf, sock->reply, n);
1039+
sock->reply_len = 0;
1040+
1041+
if (addr && addrlen && *addrlen >= sizeof(linux_sockaddr_in_t)) {
1042+
linux_sockaddr_in_t *in = (linux_sockaddr_in_t *)addr;
1043+
memset(in, 0, sizeof(*in));
1044+
in->family = LINUX_AF_INET;
1045+
in->addr = net_htonl(sock->reply_addr);
1046+
*addrlen = sizeof(*in);
1047+
}
1048+
return n;
1049+
}
1050+
1051+
static uint64 sys_setsockopt(uint64_t fd, uint64_t level, uint64_t optname,
1052+
const void *optval, uint64_t optlen) {
1053+
(void)level;
1054+
(void)optname;
1055+
(void)optval;
1056+
(void)optlen;
1057+
linux_socket_t *sock = linux_socket_by_fd((int)fd);
1058+
if (!sock)
1059+
return fd_valid((int)fd) ? -LINUX_ENOTSOCK : -LINUX_EBADF;
1060+
return 0;
1061+
}
1062+
9311063
static uint64 sys_ioctl(uint64_t fd, uint64_t req, uint64_t arg) {
9321064
if (!fd_valid((int)fd))
9331065
return -LINUX_EBADF;
@@ -1679,6 +1811,11 @@ static const char *names[] = {
16791811
[1] = "write",
16801812
[2] = "open",
16811813
[3] = "close",
1814+
[41] = "socket",
1815+
[42] = "connect",
1816+
[44] = "sendto",
1817+
[45] = "recvfrom",
1818+
[54] = "setsockopt",
16821819
[9] = "mmap",
16831820
[11] = "munmap",
16841821
[12] = "brk",
@@ -1792,6 +1929,15 @@ uint64_t syscall_dispatch(
17921929
case LINUX_SYS_CONNECT:
17931930
return sys_connect(arg1, (const void *)arg2, arg3);
17941931

1932+
case LINUX_SYS_SENDTO:
1933+
return sys_sendto(arg1, (const void *)arg2, arg3, arg4, (const void *)arg5, arg6);
1934+
1935+
case LINUX_SYS_RECVFROM:
1936+
return sys_recvfrom(arg1, (void *)arg2, arg3, arg4, (void *)arg5, (uint64_t *)arg6);
1937+
1938+
case LINUX_SYS_SETSOCKOPT:
1939+
return sys_setsockopt(arg1, arg2, arg3, (const void *)arg4, arg5);
1940+
17951941
case LINUX_SYS_CLONE:
17961942
return sys_fork();
17971943

@@ -1934,4 +2080,4 @@ uint64_t syscall_dispatch(
19342080
printf(linux_syscalls_prefix "Unknown, returning -ENOSYS for (%u)", nr);
19352081
return -LINUX_ENOSYS;
19362082
}
1937-
}
2083+
}

0 commit comments

Comments
 (0)