Initial version of networking support for Noux

Noux/net adds network functionality to noux. Currently most basic
network related system calls including 'accept', 'bind', 'connect',
'listen', 'recv', 'send', 'shutdown', and 'socket' are implemented by
wrapping lwip's network functions.

At the moment noux/net is rarely usable, though it is possible to
use netcat to send a message to a netcat server which listen on a
given port in noux/net.
This commit is contained in:
Josef Söntgen 2012-05-24 17:06:54 +02:00 committed by Christian Helmuth
parent db21cd6f0e
commit 6d8a7c0cc4
18 changed files with 1337 additions and 11 deletions

View File

@ -57,6 +57,19 @@ namespace Noux {
SYSCALL_UNLINK,
SYSCALL_RENAME,
SYSCALL_MKDIR,
SYSCALL_SOCKET,
SYSCALL_GETSOCKOPT,
SYSCALL_SETSOCKOPT,
SYSCALL_ACCEPT,
SYSCALL_BIND,
SYSCALL_LISTEN,
SYSCALL_SEND,
SYSCALL_SENDTO,
SYSCALL_RECV,
SYSCALL_GETPEERNAME,
SYSCALL_SHUTDOWN,
SYSCALL_CONNECT,
SYSCALL_GETADDRINFO,
SYSCALL_INVALID = -1
};
@ -86,6 +99,19 @@ namespace Noux {
NOUX_DECL_SYSCALL_NAME(UNLINK)
NOUX_DECL_SYSCALL_NAME(RENAME)
NOUX_DECL_SYSCALL_NAME(MKDIR)
NOUX_DECL_SYSCALL_NAME(SOCKET)
NOUX_DECL_SYSCALL_NAME(GETSOCKOPT)
NOUX_DECL_SYSCALL_NAME(SETSOCKOPT)
NOUX_DECL_SYSCALL_NAME(ACCEPT)
NOUX_DECL_SYSCALL_NAME(BIND)
NOUX_DECL_SYSCALL_NAME(LISTEN)
NOUX_DECL_SYSCALL_NAME(SEND)
NOUX_DECL_SYSCALL_NAME(SENDTO)
NOUX_DECL_SYSCALL_NAME(RECV)
NOUX_DECL_SYSCALL_NAME(GETPEERNAME)
NOUX_DECL_SYSCALL_NAME(SHUTDOWN)
NOUX_DECL_SYSCALL_NAME(CONNECT)
NOUX_DECL_SYSCALL_NAME(GETADDRINFO)
case SYSCALL_INVALID: return 0;
}
return 0;

View File

@ -57,6 +57,7 @@ namespace Noux {
typedef char Env[ENV_MAX_LEN];
typedef __SIZE_TYPE__ size_t;
typedef long int ssize_t;
/**
* Flags of 'mode' argument of open syscall
@ -132,6 +133,7 @@ namespace Noux {
enum Fcntl_cmd {
FCNTL_CMD_GET_FILE_STATUS_FLAGS,
FCNTL_CMD_SET_FILE_STATUS_FLAGS,
FCNTL_CMD_SET_FD_FLAGS
};
@ -218,6 +220,56 @@ namespace Noux {
bool zero() const { return (sec == 0) && (usec == 0); }
};
/**
* Socket related structures
*/
enum { MAX_HOSTNAME_LEN = 255 };
typedef char Hostname[MAX_HOSTNAME_LEN];
enum { MAX_SERVNAME_LEN = 255 };
typedef char Servname[MAX_SERVNAME_LEN];
enum { MAX_ADDRINFO_RESULTS = 4 };
struct in_addr
{
unsigned int s_addr;
};
struct sockaddr
{
unsigned char sa_len;
unsigned char sa_family;
char sa_data[14];
};
struct sockaddr_in {
unsigned char sin_len;
unsigned char sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
typedef unsigned socklen_t;
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
struct Addrinfo {
struct addrinfo addrinfo;
struct sockaddr ai_addr;
char ai_canonname[255];
};
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };
enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS };
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = NUM_GENERAL_ERRORS };
@ -292,7 +344,50 @@ namespace Noux {
SYSIO_DECL(rename, { Path from_path; Path to_path; }, { });
SYSIO_DECL(mkdir, { Path path; int mode; }, { });
SYSIO_DECL(mkdir, { Path path; int mode; }, { });
SYSIO_DECL(socket, { int domain; int type; int protocol; },
{ int fd; });
/* XXX for now abuse Chunk for passing optval */
SYSIO_DECL(getsockopt, { int fd; int level; int optname; Chunk optval;
socklen_t optlen; }, { int result; });
SYSIO_DECL(setsockopt, { int fd; int level;
int optname; Chunk optval;
socklen_t optlen; }, { });
SYSIO_DECL(accept, { int fd; struct sockaddr addr; socklen_t addrlen; },
{ int fd; });
SYSIO_DECL(bind, { int fd; struct sockaddr addr;
socklen_t addrlen; }, { int result; });
SYSIO_DECL(getpeername, { int fd; struct sockaddr addr;
socklen_t addrlen; }, { });
SYSIO_DECL(listen, { int fd; int type; int backlog; },
{ int result; });
SYSIO_DECL(send, { int fd; Chunk buf; size_t len; int flags; },
{ ssize_t len; });
SYSIO_DECL(sendto, { int fd; Chunk buf; size_t len; int flags;
struct sockaddr dest_addr; socklen_t addrlen; },
{ ssize_t len; });
SYSIO_DECL(recv, { int fd; Chunk buf; size_t len; int flags; },
{ size_t len; });
SYSIO_DECL(shutdown, { int fd; int how; }, { });
SYSIO_DECL(connect, { int fd; struct sockaddr addr; socklen_t addrlen; },
{ int result; });
SYSIO_DECL(getaddrinfo, { Hostname hostname; Servname servname;
Addrinfo hints;
Addrinfo res[MAX_ADDRINFO_RESULTS]; },
{ int addr_num; });
};
};
};

View File

@ -7,7 +7,7 @@ if {[have_spec linux]} {
exit 0
}
build "core init drivers/timer drivers/uart noux lib/libc_noux noux-pkg/coreutils"
build "core init drivers/timer drivers/uart noux/minimal lib/libc_noux noux-pkg/coreutils"
# strip coreutils binaries and create tar archive
exec sh -c "[cross_dev_prefix]strip bin/coreutils/bin/*"

View File

@ -10,7 +10,7 @@ if {![have_spec x86]} {
#exec rm -rf noux-pkg/bash bin/bash
set build_components {
core init drivers/timer noux lib/libc_noux
core init drivers/timer noux/minimal lib/libc_noux
drivers/framebuffer drivers/pci drivers/input
server/terminal server/ram_fs
test/libports/ncurses

View File

@ -7,7 +7,7 @@ if {[have_spec linux]} {
exit 0
}
build "core init drivers/timer drivers/uart noux lib/libc_noux test/noux_fork"
build "core init drivers/timer drivers/uart noux/minimal lib/libc_noux test/noux_fork"
# strip coreutils binaries and create tar archive
exec tar cfv bin/noux_fork.tar -h -C bin test-noux_fork

View File

@ -4,7 +4,7 @@ if {![have_spec x86]} {
}
set build_components {
core init drivers/timer noux lib/libc_noux
core init drivers/timer noux/minimal lib/libc_noux
drivers/framebuffer drivers/pci drivers/input
server/terminal server/ram_fs
test/libports/ncurses

View File

@ -10,7 +10,7 @@ if {![have_spec x86]} {
#exec rm -rf noux-pkg/vim bin/vim
set build_components {
core init drivers/timer noux lib/libc_noux
core init drivers/timer noux/minimal lib/libc_noux
drivers/framebuffer drivers/pci drivers/input
server/terminal
}

View File

@ -540,6 +540,10 @@ namespace {
bool supports_unlink(char const *) { return true; }
bool supports_rename(const char *, const char *) { return true; }
bool supports_mkdir(const char *, mode_t) { return true; }
bool supports_socket(int, int, int) { return true; }
bool supports_freeaddrinfo(struct addrinfo *) { return true; }
bool supports_getaddrinfo(const char *, const char *,
struct addrinfo **) { return true; }
Libc::File_descriptor *open(char const *, int);
ssize_t write(Libc::File_descriptor *, const void *, ::size_t);
@ -559,6 +563,31 @@ namespace {
int unlink(char const *path);
int rename(const char *oldpath, const char *newpath);
int mkdir(const char *path, mode_t mode);
/* Network related functions */
Libc::File_descriptor *socket(int, int, int);
Libc::File_descriptor *accept(Libc::File_descriptor *,
struct sockaddr *, socklen_t *);
int bind(Libc::File_descriptor *, const struct sockaddr *,
socklen_t);
int connect(Libc::File_descriptor *, const struct sockaddr *addr,
socklen_t addrlen);
void freeaddrinfo(struct addrinfo *);
int getaddrinfo(const char *, const char *, const struct addrinfo *,
struct addrinfo **);
int getpeername(Libc::File_descriptor *, struct sockaddr *,
socklen_t *);
int listen(Libc::File_descriptor *, int);
ssize_t send(Libc::File_descriptor *, const void *, ::size_t,
int flags);
ssize_t sendto(Libc::File_descriptor *, const void *, size_t, int,
const struct sockaddr *, socklen_t);
ssize_t recv(Libc::File_descriptor *, void *, ::size_t, int);
int getsockopt(Libc::File_descriptor *, int, int, void *,
socklen_t *);
int setsockopt(Libc::File_descriptor *, int , int , const void *,
socklen_t);
int shutdown(Libc::File_descriptor *, int how);
};
@ -1015,6 +1044,373 @@ namespace {
return 0;
}
Libc::File_descriptor *Plugin::socket(int domain, int type, int protocol)
{
sysio()->socket_in.domain = domain;
sysio()->socket_in.type = type;
sysio()->socket_in.protocol = protocol;
if (!noux()->syscall(Noux::Session::SYSCALL_SOCKET))
return 0;
Libc::Plugin_context *context = noux_context(sysio()->socket_out.fd);
return Libc::file_descriptor_allocator()->alloc(this, context,
sysio()->socket_out.fd);
}
int Plugin::getsockopt(Libc::File_descriptor *fd, int level, int optname,
void *optval, socklen_t *optlen)
{
sysio()->getsockopt_in.fd = noux_fd(fd->context);
sysio()->getsockopt_in.level = level;
sysio()->getsockopt_in.optname = optname;
/* wipe-old state */
sysio()->getsockopt_in.optlen = *optlen;
Genode::memset(sysio()->getsockopt_in.optval, 0,
sizeof (sysio()->getsockopt_in.optval));
if (!noux()->syscall(Noux::Session::SYSCALL_GETSOCKOPT))
return -1;
Genode::memcpy(optval, sysio()->setsockopt_in.optval,
sysio()->getsockopt_in.optlen);
return 0;
}
int Plugin::setsockopt(Libc::File_descriptor *fd, int level, int optname,
const void *optval, socklen_t optlen)
{
if (optlen > sizeof(sysio()->setsockopt_in.optval)) {
/* XXX */
return -1;
}
sysio()->setsockopt_in.fd = noux_fd(fd->context);
sysio()->setsockopt_in.level = level;
sysio()->setsockopt_in.optname = optname;
sysio()->setsockopt_in.optlen = optlen;
Genode::memcpy(sysio()->setsockopt_in.optval, optval, optlen);
if (!noux()->syscall(Noux::Session::SYSCALL_SETSOCKOPT)) {
/* XXX */
return -1;
}
return 0;
}
Libc::File_descriptor *Plugin::accept(Libc::File_descriptor *fd, struct sockaddr *addr,
socklen_t *addrlen)
{
sysio()->accept_in.fd = noux_fd(fd->context);
if (addr != NULL) {
Genode::memcpy(&sysio()->accept_in.addr, addr, sizeof (struct sockaddr));
sysio()->accept_in.addrlen = *addrlen;
}
else {
Genode::memset(&sysio()->accept_in.addr, 0, sizeof (struct sockaddr));
sysio()->accept_in.addrlen = 0;
}
if (!noux()->syscall(Noux::Session::SYSCALL_ACCEPT)) {
return 0;
}
if (addr != NULL)
*addrlen = sysio()->accept_in.addrlen;
Libc::Plugin_context *context = noux_context(sysio()->accept_out.fd);
return Libc::file_descriptor_allocator()->alloc(this, context,
sysio()->accept_out.fd);
}
int Plugin::bind(Libc::File_descriptor *fd, const struct sockaddr *addr,
socklen_t addrlen)
{
sysio()->bind_in.fd = noux_fd(fd->context);
Genode::memcpy(&sysio()->bind_in.addr, addr, sizeof (struct sockaddr));
sysio()->bind_in.addrlen = addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_BIND)) {
errno = EACCES;
return -1;
}
return 0;
}
int Plugin::connect(Libc::File_descriptor *fd, const struct sockaddr *addr,
socklen_t addrlen)
{
sysio()->connect_in.fd = noux_fd(fd->context);
Genode::memcpy(&sysio()->connect_in.addr, addr, sizeof (struct sockaddr));
sysio()->connect_in.addrlen = addrlen;
if (!noux()->syscall(Noux::Session::SYSCALL_CONNECT)) {
/* XXX errno */
return -1;
}
return 0;
}
void Plugin::freeaddrinfo(struct addrinfo *res)
{
#if 0
struct addrinfo *next;
while (res) {
if (res->ai_addr) {
free(res->ai_addr);
}
if (res->ai_canonname) {
free(res->ai_canonname);
}
next = res->ai_next;
free(res);
res = next;
}
#endif
}
int Plugin::getaddrinfo(const char *hostname, const char *servname,
const struct addrinfo *hints, struct addrinfo **res)
{
#if 0
const char *service = NULL;
/**
* We have to fetch the portnumber manually because lwip only
* supports getting the service by portnumber. So we first check
* if servname is already a ascii portnumber and if it is not we
* call getservent(servername, NULL).
*/
char buf[6] = { 0 };
int port = atoi(servname);
if (port <= 0 || port > 0xffff) {
struct servent *se = getservbyname(servname, NULL);
if (se != NULL) {
port = htons(se->s_port);
snprintf(buf, 6, "%d", port);
service = buf;
}
else {
return -1;
}
}
else
service = servname;
size_t len = strlen(hostname);
len = min(len, 255);
memcpy(sysio()->getaddrinfo_in.hostname, hostname, len);
sysio()->getaddrinfo_in.hostname[len] = '\0';
len = strlen(service);
len = min(len, 255);
memcpy(sysio()->getaddrinfo_in.servname, service, len);
sysio()->getaddrinfo_in.servname[len] = '\0';
if (!noux()->syscall(Noux::Session::SYSCALL_GETADDRINFO))
return -1;
struct addrinfo *rp = 0, *result;
for (int i = 0; i < sysio()->getaddrinfo_out.addr_num; i++) {
if (!rp) {
rp = (struct addrinfo *)malloc(sizeof (struct addrinfo));
*res = rp;
}
else {
rp->ai_next = (struct addrinfo *)malloc(sizeof (struct addrinfo));
rp = rp->ai_next;
}
rp->ai_flags = sysio()->getaddrinfo_in.res[i].addrinfo.ai_flags;
rp->ai_family = sysio()->getaddrinfo_in.res[i].addrinfo.ai_family;
rp->ai_socktype = sysio()->getaddrinfo_in.res[i].addrinfo.ai_socktype;
rp->ai_protocol = sysio()->getaddrinfo_in.res[i].addrinfo.ai_protocol;
rp->ai_addrlen = sysio()->getaddrinfo_in.res[i].addrinfo.ai_addrlen;
if (sysio()->getaddrinfo_in.res[i].ai_addr.sa_len != 0) {
rp->ai_addr = (struct sockaddr *)malloc(sizeof (struct sockaddr));
memcpy(rp->ai_addr, &sysio()->getaddrinfo_in.res[i].ai_addr, sizeo
f (struct sockaddr));
}
else
rp->ai_addr = 0;
if (sysio()->getaddrinfo_in.res[i].ai_canonname != 0) {
size_t len = strlen(sysio()->getaddrinfo_in.res[i].ai_canonname) +
1;
rp->ai_canonname = (char *)malloc(len);
strncpy(rp->ai_canonname, sysio()->getaddrinfo_in.res[i].ai_canonn
ame, len);
}
else
rp->ai_canonname = 0;
rp->ai_next = 0;
}
#endif
return -1;
}
int Plugin::getpeername(Libc::File_descriptor *fd, struct sockaddr *addr,
socklen_t *addrlen)
{
sysio()->getpeername_in.fd = noux_fd(fd->context);
if (!noux()->syscall(Noux::Session::SYSCALL_GETPEERNAME)) {
/* errno */
return -1;
}
Genode::memcpy(&sysio()->getpeername_in.addr, addr,
sizeof (struct sockaddr));
sysio()->bind_in.addrlen = *addrlen;
return 0;
}
int Plugin::listen(Libc::File_descriptor *fd, int backlog)
{
sysio()->listen_in.fd = noux_fd(fd->context);
sysio()->listen_in.backlog = backlog;
if (!noux()->syscall(Noux::Session::SYSCALL_LISTEN)) {
/* errno = EACCES; */
return -1;
}
return 0;
}
ssize_t Plugin::recv(Libc::File_descriptor *fd, void *buf, ::size_t len, int flags)
{
Genode::size_t sum_recv_count = 0;
while (len) {
Genode::size_t curr_len =
Genode::min(len, sizeof(sysio()->recv_in.buf));
sysio()->recv_in.fd = noux_fd(fd->context);
sysio()->recv_in.len = curr_len;
if (!noux()->syscall(Noux::Session::SYSCALL_RECV)) {
/* XXX set errno */
return -1;
}
Genode::memcpy(buf, sysio()->recv_in.buf, sysio()->recv_in.len);
sum_recv_count += sysio()->recv_in.len;
if (sysio()->recv_out.len < sysio()->recv_in.len)
break;
if (sysio()->recv_out.len <= len)
len -= sysio()->recv_out.len;
else
break;
}
return sum_recv_count;
}
ssize_t Plugin::send(Libc::File_descriptor *fd, const void *buf, ::size_t len, int flags)
{
/* remember original len for the return value */
int const orig_count = len;
char *src = (char *)buf;
sysio()->send_in.fd = noux_fd(fd->context);
while (len > 0) {
Genode::size_t curr_len = Genode::min(sizeof (sysio()->send_in.buf), len);
sysio()->send_in.len = curr_len;
Genode::memcpy(sysio()->send_in.buf, src, curr_len);
if (!noux()->syscall(Noux::Session::SYSCALL_SEND)) {
PERR("write error %d", sysio()->error.general);
}
len -= curr_len;
src += curr_len;
}
return orig_count;
}
ssize_t Plugin::sendto(Libc::File_descriptor *fd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
int const orig_count = len;
if (addrlen > sizeof (sysio()->sendto_in.dest_addr)) {
/* XXX errno */
return -1;
}
sysio()->sendto_in.fd = noux_fd(fd->context);
sysio()->sendto_in.addrlen = addrlen;
Genode::memcpy(&sysio()->sendto_in.dest_addr, dest_addr, addrlen);
/* wipe-out sendto buffer */
Genode::memset(sysio()->sendto_in.buf, 0, sizeof (sysio()->sendto_in.buf));
char *src = (char *)buf;
while (len > 0) {
size_t curr_len = Genode::min(sizeof *sysio()->sendto_in.buf, len);
sysio()->sendto_in.len = curr_len;
Genode::memcpy(sysio()->sendto_in.buf, src, curr_len);
if (!noux()->syscall(Noux::Session::SYSCALL_SENDTO)) {
return -1;
}
len -= curr_len;
src += curr_len;
}
return orig_count;
}
int Plugin::shutdown(Libc::File_descriptor *fd, int how)
{
sysio()->shutdown_in.fd = noux_fd(fd->context);
sysio()->shutdown_in.how = how;
if (!noux()->syscall(Noux::Session::SYSCALL_SHUTDOWN)) {
return -1;
}
return 0;
}
} /* unnamed namespace */

View File

@ -252,6 +252,12 @@ namespace Noux {
io->unregister_wake_up_notifier(&notifier);
}
/**
* Method for handling noux network related system calls
*/
bool _syscall_net(Syscall sc);
public:
struct Binary_does_not_exist : Exception { };

View File

@ -52,7 +52,7 @@
#include <dir_file_system.h>
enum { verbose_syscall = false };
enum { verbose_syscall = true };
namespace Noux {
@ -66,6 +66,9 @@ namespace Noux {
extern "C" void wait_for_continue();
extern void (*close_socket)(int);
extern void init_network();
/*****************************
** Noux syscall dispatcher **
@ -169,6 +172,13 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
case SYSCALL_CLOSE:
{
/**
* We have to explicitly close Socket_io_channel fd's because
* these are currently handled separately.
*/
if (close_socket)
close_socket(_sysio->close_in.fd);
remove_io_channel(_sysio->close_in.fd);
return true;
}
@ -464,6 +474,22 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
return _root_dir->mkdir(_sysio, Absolute_path(_sysio->mkdir_in.path,
_env.pwd()).base());
case SYSCALL_SOCKET:
case SYSCALL_GETSOCKOPT:
case SYSCALL_SETSOCKOPT:
case SYSCALL_ACCEPT:
case SYSCALL_BIND:
case SYSCALL_LISTEN:
case SYSCALL_SEND:
case SYSCALL_SENDTO:
case SYSCALL_RECV:
case SYSCALL_GETPEERNAME:
case SYSCALL_SHUTDOWN:
case SYSCALL_CONNECT:
case SYSCALL_GETADDRINFO:
{
return _syscall_net(sc);
}
case SYSCALL_INVALID: break;
}
}
@ -610,6 +636,8 @@ int main(int argc, char **argv)
static Dir_file_system
root_dir(config()->xml_node().sub_node("fstab"));
/* initialize network */
init_network();
/*
* Entrypoint used to virtualize child resources such as RAM, RM
*/

View File

@ -0,0 +1,21 @@
/*
* \brief Dummy stubs for network-related Noux functions
* \author Norman Feske
* \date 2012-05-24
*/
/*
* Copyright (C) 2011-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <child.h>
void (*close_socket)(int) = 0;
void init_network() { }
bool Noux::Child::_syscall_net(Noux::Session::Syscall sc) { return false; }

View File

@ -0,0 +1,8 @@
TARGET = noux
LIBS = cxx env server process signal
SRC_CC = main.cc dummy_net.cc
INC_DIR += $(PRG_DIR)
INC_DIR += $(PRG_DIR)/../
vpath main.cc $(PRG_DIR)/..
vpath dummy_net.cc $(PRG_DIR)

View File

@ -0,0 +1,4 @@
This directory contains the implementation of network related stuff for noux.
Currently network support is provided by using the libc and lwip directly
(only for initialization of the actual network subsystem).

360
ports/src/noux/net/net.cc Normal file
View File

@ -0,0 +1,360 @@
/*
* \brief Unix emulation environment for Genode
* \author Josef Soentgen
* \date 2012-04-13
*/
/*
* Copyright (C) 2011-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <cap_session/connection.h>
#include <dataspace/client.h>
#include <lwip/genode.h>
/* Noux includes */
#include <child.h>
#include <socket_descriptor_registry.h>
#include <socket_io_channel.h>
#include <shared_pointer.h>
using namespace Noux;
/* Libc includes */
#include <sys/select.h>
#include <sys/time.h>
#include <netdb.h>
void (*libc_select_notify)();
void (*close_socket)(int);
/* set select() timeout to lwip's lowest possible value */
struct timeval timeout = { 0, 10000 };
/**
* This callback function is called from lwip via the libc_select_notify
* function pointer if an event occurs.
*/
static void select_notify()
{
fd_set readfds;
fd_set writefds;
fd_set exceptfds;
int ready;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
/* for now set each currently used socket descriptor true */
for (int sd = 0; sd < MAX_SOCKET_DESCRIPTORS; sd++) {
if (Socket_descriptor_registry<Socket_io_channel>::instance()->sd_in_use(sd)) {
int real_sd = Socket_descriptor_registry<Socket_io_channel>::instance()->io_channel_by_sd(sd)->get_socket();
FD_SET(real_sd, &readfds);
FD_SET(real_sd, &writefds);
FD_SET(real_sd, &exceptfds);
}
}
ready = ::select(MAX_SOCKET_DESCRIPTORS, &readfds, &writefds, &exceptfds, &timeout);
/* if any socket is ready for reading */
if (ready > 0) {
for (int sd = 0; sd < MAX_SOCKET_DESCRIPTORS; sd++) {
if (Socket_descriptor_registry<Socket_io_channel>::instance()->sd_in_use(sd)) {
int real_sd = Socket_descriptor_registry<Socket_io_channel>::instance()->io_channel_by_sd(sd)->get_socket();
if (FD_ISSET(real_sd, &readfds)
|| FD_ISSET(real_sd, &writefds)
|| FD_ISSET(real_sd, &exceptfds)) {
Shared_pointer<Socket_io_channel> sio = Socket_descriptor_registry<Socket_io_channel>::instance()->io_channel_by_sd(sd);
if (FD_ISSET(real_sd, &readfds))
sio->set_unblock(true, false, false);
if (FD_ISSET(real_sd, &writefds))
sio->set_unblock(false, true, false);
if (FD_ISSET(real_sd, &exceptfds))
sio->set_unblock(false, false, true);
sio->invoke_all_notifiers();
}
}
}
}
}
static void _close_socket(int sd)
{
Socket_descriptor_registry<Socket_io_channel>::instance()->remove_io_channel(sd);
}
/**
* Initialise the network subsystem by directly using lwip
*/
void init_network()
{
lwip_tcpip_init();
lwip_nic_init(0, 0, 0);
if (!libc_select_notify)
libc_select_notify = select_notify;
if (!close_socket)
close_socket = _close_socket;
}
/*********************************
** Noux net syscall dispatcher **
*********************************/
#define GET_SOCKET_IO_CHANNEL(fd, handle) \
Shared_pointer<Io_channel> io = _lookup_channel(fd); \
Shared_pointer<Socket_io_channel> handle = \
io.dynamic_pointer_cast<Socket_io_channel>();
bool Noux::Child::_syscall_net(Noux::Session::Syscall sc)
{
switch (sc) {
/**
* Keep compiler from complaining
*/
case SYSCALL_GETCWD:
case SYSCALL_WRITE:
case SYSCALL_READ:
case SYSCALL_STAT:
case SYSCALL_LSTAT:
case SYSCALL_FSTAT:
case SYSCALL_FCNTL:
case SYSCALL_OPEN:
case SYSCALL_CLOSE:
case SYSCALL_IOCTL:
case SYSCALL_LSEEK:
case SYSCALL_DIRENT:
case SYSCALL_FCHDIR:
case SYSCALL_EXECVE:
case SYSCALL_SELECT:
case SYSCALL_FORK:
case SYSCALL_GETPID:
case SYSCALL_WAIT4:
case SYSCALL_PIPE:
case SYSCALL_DUP2:
case SYSCALL_INVALID:
case SYSCALL_UNLINK:
case SYSCALL_RENAME:
case SYSCALL_MKDIR:
break;
case SYSCALL_SOCKET:
{
Socket_io_channel *socket_io_channel = new Socket_io_channel();
if (!socket_io_channel->socket(_sysio)) {
delete socket_io_channel;
return false;
}
Shared_pointer<Io_channel> io_channel(socket_io_channel, Genode::env()->heap());
_sysio->socket_out.fd = add_io_channel(io_channel);
/* add socket to registry */
Socket_descriptor_registry<Socket_io_channel>::instance()->add_io_channel(io_channel.dynamic_pointer_cast<Socket_io_channel>(),
_sysio->socket_out.fd);
return true;
}
case SYSCALL_GETSOCKOPT:
{
GET_SOCKET_IO_CHANNEL(_sysio->getsockopt_in.fd, socket_io_channel)
if (!socket_io_channel->getsockopt(_sysio))
return false;
return true;
}
case SYSCALL_SETSOCKOPT:
{
GET_SOCKET_IO_CHANNEL(_sysio->setsockopt_in.fd, socket_io_channel)
if (!socket_io_channel->setsockopt(_sysio)) {
return false;
}
return true;
}
case SYSCALL_ACCEPT:
{
GET_SOCKET_IO_CHANNEL(_sysio->accept_in.fd, socket_io_channel)
int new_socket = socket_io_channel->accept(_sysio);
if (new_socket == -1)
return false;
Socket_io_channel *new_socket_io_channel = new Socket_io_channel(new_socket);
Shared_pointer<Io_channel> channel(new_socket_io_channel, Genode::env()->heap());
_sysio->accept_out.fd = add_io_channel(channel);
/* add new socket to registry */
Socket_descriptor_registry<Socket_io_channel>::instance()->add_io_channel(channel.dynamic_pointer_cast<Socket_io_channel>(),
_sysio->accept_out.fd);
return true;
}
case SYSCALL_BIND:
{
GET_SOCKET_IO_CHANNEL(_sysio->bind_in.fd, socket_io_channel)
if (socket_io_channel->bind(_sysio) == -1)
return false;
return true;
}
case SYSCALL_LISTEN:
{
GET_SOCKET_IO_CHANNEL(_sysio->listen_in.fd, socket_io_channel)
if (socket_io_channel->listen(_sysio) == -1)
return false;
return true;
}
case SYSCALL_SEND:
{
GET_SOCKET_IO_CHANNEL(_sysio->send_in.fd, socket_io_channel)
ssize_t len = socket_io_channel->send(_sysio);
if (len == -1)
return false;
_sysio->send_out.len = len;
return true;
}
case SYSCALL_SENDTO:
{
GET_SOCKET_IO_CHANNEL(_sysio->sendto_in.fd, socket_io_channel)
ssize_t len = socket_io_channel->sendto(_sysio);
if (len == -1)
return false;
_sysio->sendto_out.len = len;
return true;
}
case SYSCALL_RECV:
{
GET_SOCKET_IO_CHANNEL(_sysio->recv_in.fd, socket_io_channel)
ssize_t len = socket_io_channel->recv(_sysio);
if (len == -1)
return false;
_sysio->recv_out.len = len;
return true;
}
case SYSCALL_GETPEERNAME:
{
GET_SOCKET_IO_CHANNEL(_sysio->getpeername_in.fd, socket_io_channel)
int res = socket_io_channel->getpeername(_sysio);
if (res == -1)
return false;
return true;
}
case SYSCALL_SHUTDOWN:
{
GET_SOCKET_IO_CHANNEL(_sysio->shutdown_in.fd, socket_io_channel)
int res = socket_io_channel->shutdown(_sysio);
if (res == -1)
return false;
/* remove sd from registry */
_close_socket(_sysio->shutdown_in.fd);
return true;
}
case SYSCALL_CONNECT:
{
GET_SOCKET_IO_CHANNEL(_sysio->connect_in.fd, socket_io_channel);
int res = socket_io_channel->connect(_sysio);
if (res == -1)
return false;
return true;
}
case SYSCALL_GETADDRINFO:
{
#if 0
struct addrinfo *result, *rp = NULL;
int res = lwip_getaddrinfo(_sysio->getaddrinfo_in.hostname,
_sysio->getaddrinfo_in.servname,
(const struct addrinfo *)&_sysio->getaddrinfo_in.hints,
&result);
if (res != 0) {
PERR("::getaddrinfo() returns %d", res);
return false;
}
PINF("SYSCALL_GETADDRINFO: deep-copy");
/* wipe-out old state */
memset(_sysio->getaddrinfo_in.res, 0, sizeof (_sysio->getaddrinfo_in.res));
int i = 0; rp = result;
while (i < Noux::Sysio::MAX_ADDRINFO_RESULTS && rp != NULL) {
memcpy(&_sysio->getaddrinfo_in.res[i].addrinfo, rp, sizeof (struct addrinfo));
if (rp->ai_addr) {
memcpy(&_sysio->getaddrinfo_in.res[i].ai_addr, rp->ai_addr, sizeof (struct sockaddr));
}
else
memset(&_sysio->getaddrinfo_in.res[i].ai_addr, 0, sizeof (struct sockaddr));
if (rp->ai_canonname) {
memcpy(&_sysio->getaddrinfo_in.res[i].ai_canonname,
rp->ai_canonname, strlen(rp->ai_canonname));
PINF("kopiere canonname: '%s'", rp->ai_canonname);
}
else
memset(&_sysio->getaddrinfo_in.res[i].ai_canonname, 0,
sizeof (_sysio->getaddrinfo_in.res[i].ai_canonname));
i++; rp = rp->ai_next;
}
_sysio->getaddrinfo_out.addr_num = i;
PINF("SYSCALL_GETADDRINFO: deep-copy successfull");
lwip_freeaddrinfo(result);
return true;
#endif
return false;
}
}
return false;
}

View File

@ -0,0 +1,106 @@
/*
* \brief I/O channel for sockets
* \author Josef Söntgen
* \date 2012-04-12
*/
/*
* Copyright (C) 2011-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _NOUX__SOCKET_DESCRIPTOR_REGISTRY_H_
#define _NOUX__SOCKET_DESCRIPTOR_REGISTRY_H_
/* Noux includes */
#include <shared_pointer.h>
namespace Noux {
enum { MAX_SOCKET_DESCRIPTORS = 64 };
template<typename T>
class Socket_descriptor_registry
{
private:
struct {
bool allocated;
Shared_pointer<T> io_channel;
} _sds[MAX_SOCKET_DESCRIPTORS];
void _assign_sd(int sd, Shared_pointer<T> &io_channel)
{
_sds[sd].io_channel = io_channel;
_sds[sd].allocated = true;
}
bool _is_valid_sd(int sd) const
{
return (sd >= 0) && (sd < MAX_SOCKET_DESCRIPTORS);
}
void _reset_sd(int sd)
{
_sds[sd].io_channel = Shared_pointer<T>();
_sds[sd].allocated = false;
}
public:
Socket_descriptor_registry()
{
for (unsigned i = 0; i < MAX_SOCKET_DESCRIPTORS; i++)
_reset_sd(i);
}
int add_io_channel(Shared_pointer<T> io_channel, int sd = -1)
{
if (sd == -1) {
PERR("Could not allocate socket descriptor");
return -1;
}
if (!_is_valid_sd(sd)) {
PERR("Socket descriptor %d is out of range", sd);
return -2;
}
_assign_sd(sd, io_channel);
return sd;
}
void remove_io_channel(int sd)
{
if (!_is_valid_sd(sd))
PERR("Socket descriptor %d is out of range", sd);
else
_reset_sd(sd);
}
bool sd_in_use(int sd) const
{
return (_is_valid_sd(sd) && _sds[sd].allocated);
}
Shared_pointer<T> io_channel_by_sd(int sd) const
{
if (!sd_in_use(sd)) {
PWRN("Socket descriptor %d is not open", sd);
return Shared_pointer<T>();
}
return _sds[sd].io_channel;
}
static Socket_descriptor_registry<T> *instance()
{
static Socket_descriptor_registry<T> _sdr;
return &_sdr;
}
};
};
#endif /* _NOUX__SOCKET_DESCRIPTOR_REGISTRY_H_ */

View File

@ -0,0 +1,268 @@
/*
* \brief I/O channel for sockets
* \author Josef Söntgen
* \date 2012-04-12
*/
/*
* Copyright (C) 2011-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _NOUX__SOCKET_IO_CHANNEL_H_
#define _NOUX__SOCKET_IO_CHANNEL_H_
/* Genode includes */
#include <base/printf.h>
/* Noux includes */
#include <io_channel.h>
#include <noux_session/sysio.h>
/* Libc includes */
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
namespace Noux {
class Socket_io_channel : public Io_channel
{
private:
int _socket;
enum { UNBLOCK_READ = 0x1,
UNBLOCK_WRITE = 0x2,
UNBLOCK_EXCEPT = 0x4 };
int _unblock;
public:
Socket_io_channel()
:
_socket(-1),
_unblock(0)
{ }
Socket_io_channel(int s)
:
_socket(s),
_unblock(0)
{ }
~Socket_io_channel()
{
if (_socket != -1)
::shutdown(_socket, SHUT_RDWR);
}
int get_socket() const
{
return _socket;
}
void set_unblock(bool rd, bool wr, bool ex)
{
if (rd)
_unblock |= UNBLOCK_READ;
if (wr)
_unblock |= UNBLOCK_WRITE;
if (ex)
_unblock |= UNBLOCK_EXCEPT;
}
bool fstat(Sysio *sysio) { return false; }
bool fcntl(Sysio *sysio)
{
/*
* For now this fcntl() only contains stubs to keep programs
* happy. lwip-1.3.2 which is currently used does not provide
* its own lwip_fcntl(). We could only use lwip_ioctl() to set
* the socket O_NONBLOCKING if we get this kind of request.
*/
switch (sysio->fcntl_in.cmd) {
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
{
PWRN("FCNTL_CMD_GET_FILE_STATUS_FLAGS currently only returns 0");
sysio->fcntl_out.result = 0;
return true;
}
case Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS:
{
PWRN("FCNTL_CMD_SET_FILE_STATUS_FLAGS currently only returns 0");
sysio->fcntl_out.result = 0;
return true;
}
default:
PWRN("invalid fcntl command: %d", sysio->fcntl_in.cmd);
sysio->error.fcntl = Sysio::FCNTL_ERR_CMD_INVALID;
return false;
}
}
bool fchdir(Sysio *sysio, Pwd *pwd) { return false; }
bool dirent(Sysio *sysio) { return false; }
bool check_unblock(bool rd, bool wr, bool ex) const
{
if (_unblock & UNBLOCK_READ)
return true;
if (_unblock & UNBLOCK_WRITE)
return true;
if (_unblock & UNBLOCK_EXCEPT)
return true;
return false;
}
size_t write(Sysio *sysio)
{
size_t written = ::write(_socket, sysio->write_in.chunk,
sysio->write_in.count);
return written;
}
bool read(Sysio *sysio)
{
size_t const max_count =
Genode::min(sysio->read_in.count,
sizeof(sysio->read_out.chunk));
sysio->read_out.count = ::read(_socket, sysio->read_out.chunk,
max_count);
return true;
}
bool socket(Sysio *sysio)
{
_socket = ::socket(sysio->socket_in.domain,
sysio->socket_in.type,
sysio->socket_in.protocol);
return (_socket == -1) ? false : true;
}
bool getsockopt(Sysio *sysio)
{
int result = ::getsockopt(_socket, sysio->getsockopt_in.level,
sysio->getsockopt_in.optname,
sysio->getsockopt_in.optval,
&sysio->getsockopt_in.optlen);
return (result == -1) ? false : true;
}
bool setsockopt(Sysio *sysio)
{
/*
* Filter options out because lwip only supports several socket
* options. Therefore for now we silently return 0 and notify
* the user via debug message.
*/
switch (sysio->setsockopt_in.optname) {
case SO_DEBUG:
case SO_LINGER:
case SO_REUSEADDR:
PWRN("SOL_SOCKET option '%d' is currently not supported, however we report success",
sysio->setsockopt_in.optname);
return true;
}
int result = ::setsockopt(_socket, sysio->setsockopt_in.level,
sysio->setsockopt_in.optname,
sysio->setsockopt_in.optval,
sysio->setsockopt_in.optlen);
return (result == -1) ? false : true;
}
int accept(Sysio *sysio)
{
int result;
if (sysio->accept_in.addrlen == 0) {
result = ::accept(_socket, NULL, NULL);
}
else {
result = ::accept(_socket, (sockaddr *)&sysio->accept_in.addr,
&sysio->accept_in.addrlen);
}
return result;
}
int bind(Sysio *sysio)
{
return ::bind(_socket, (const struct sockaddr *)&sysio->bind_in.addr,
sysio->bind_in.addrlen);
}
int connect(Sysio *sysio)
{
return ::connect(_socket, (struct sockaddr *)&sysio->connect_in.addr,
sysio->connect_in.addrlen);
}
int getpeername(Sysio *sysio)
{
return ::getpeername(_socket, (struct sockaddr *)&sysio->getpeername_in.addr,
(socklen_t *)&sysio->getpeername_in.addrlen);
}
bool ioctl(Sysio *sysio)
{
int result = ::ioctl(_socket, sysio->ioctl_in.request, NULL);
return result ? false : true;
}
int listen(Sysio *sysio)
{
return ::listen(_socket, sysio->listen_in.backlog);
}
ssize_t recv(Sysio *sysio)
{
return ::recv(_socket, sysio->recv_in.buf, sysio->recv_in.len,
sysio->recv_in.flags);
}
ssize_t send(Sysio *sysio)
{
return ::send(_socket, sysio->send_in.buf, sysio->send_in.len,
sysio->send_in.flags);
}
ssize_t sendto(Sysio *sysio)
{
ssize_t result = ::sendto(_socket, sysio->sendto_in.buf, sysio->sendto_in.len,
sysio->sendto_in.flags,
(const struct sockaddr *) &sysio->sendto_in.dest_addr,
sysio->sendto_in.addrlen);
return result;
}
int shutdown(Sysio *sysio)
{
return ::shutdown(_socket, sysio->shutdown_in.how);
}
};
}
#endif /* _NOUX__SOCKET_IO_CHANNEL_H_ */

View File

@ -0,0 +1,12 @@
TARGET = noux_net
LIBS = cxx env server process signal lwip
LIBS += libc libc_lwip
SRC_CC = main.cc net.cc
INC_DIR += $(PRG_DIR)
INC_DIR += $(PRG_DIR)/../
vpath main.cc $(PRG_DIR)/..
vpath net.cc $(PRG_DIR)

View File

@ -1,4 +0,0 @@
TARGET = noux
LIBS = cxx env server process signal
SRC_CC = main.cc
INC_DIR += $(PRG_DIR)