637 lines
15 KiB
C++
637 lines
15 KiB
C++
/**
|
|
* \brief Front-end and glue to IP stack
|
|
* \author Sebastian Sumpf
|
|
* \date 2013-09-26
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2013 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.
|
|
*/
|
|
#include <base/env.h>
|
|
#include <base/signal.h>
|
|
#include <base/printf.h>
|
|
#include <base/thread.h>
|
|
|
|
#include <lxip/lxip.h>
|
|
#include <env.h>
|
|
#include <init.h>
|
|
#include <nic.h>
|
|
|
|
extern "C" void wait_for_continue();
|
|
|
|
static const bool verbose = false;
|
|
|
|
|
|
namespace Linux {
|
|
extern "C" {
|
|
#include <lx_emul.h>
|
|
#include <linux/socket.h>
|
|
#include <uapi/linux/in.h>
|
|
|
|
extern int sock_setsockopt(struct socket *sock, int level,
|
|
int op, char __user *optval,
|
|
unsigned int optlen);
|
|
extern int sock_getsockopt(struct socket *sock, int level,
|
|
int op, char __user *optval,
|
|
int __user *optlen);
|
|
}
|
|
}
|
|
|
|
namespace Net
|
|
{
|
|
class Socketcall;
|
|
|
|
enum Opcode { OP_SOCKET = 0, OP_CLOSE = 1, OP_BIND = 2, OP_LISTEN = 3,
|
|
OP_ACCEPT = 4, OP_POLL = 5, OP_RECV = 6, OP_CONNECT = 7,
|
|
OP_SEND = 8, OP_SETOPT = 9, OP_GETOPT = 10, OP_GETNAME = 11,
|
|
OP_PEERNAME = 12, OP_IOCTL = 13, OP_SHUTDOWN = 14 };
|
|
|
|
struct Call
|
|
{
|
|
Opcode opcode;
|
|
Lxip::Handle handle;
|
|
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
Lxip::Type type;
|
|
} socket;
|
|
struct
|
|
{
|
|
int backlog;
|
|
} listen;
|
|
struct
|
|
{
|
|
void *addr;
|
|
Lxip::uint32_t *len;
|
|
} accept;
|
|
struct
|
|
{
|
|
mutable void *buf;
|
|
Lxip::size_t len;
|
|
int flags;
|
|
void *addr;
|
|
Lxip::uint32_t *addr_len;
|
|
} msg;
|
|
struct
|
|
{
|
|
int level;
|
|
int optname;
|
|
const void *optval;
|
|
Lxip::uint32_t optlen;
|
|
int *optlen_ptr;
|
|
} sockopt;
|
|
struct {
|
|
bool block;
|
|
} poll;
|
|
struct {
|
|
int request;
|
|
unsigned long arg;
|
|
} ioctl;
|
|
struct {
|
|
int how;
|
|
} shutdown;
|
|
};
|
|
|
|
struct Linux::sockaddr_storage addr;
|
|
Lxip::uint32_t addr_len;
|
|
};
|
|
|
|
union Result
|
|
{
|
|
int err;
|
|
Lxip::ssize_t len;
|
|
};
|
|
|
|
};
|
|
|
|
|
|
class Net::Socketcall : public Genode::Signal_dispatcher_base,
|
|
public Genode::Signal_context_capability,
|
|
public Lxip::Socketcall,
|
|
public Genode::Thread<64 * 1024 * sizeof(Genode::addr_t)>
|
|
{
|
|
private:
|
|
|
|
Call _call;
|
|
Result _result;
|
|
Lxip::Handle _handle;
|
|
|
|
Genode::Signal_transmitter _signal;
|
|
Genode::Semaphore _block;
|
|
|
|
void _submit_and_block()
|
|
{
|
|
_signal.submit(); /* global submit */
|
|
_block.down();
|
|
}
|
|
|
|
void _unblock() { _block.up(); }
|
|
|
|
struct Linux::socket * call_socket()
|
|
{
|
|
return static_cast<struct Linux::socket *>(_call.handle.socket);
|
|
}
|
|
|
|
|
|
Lxip::uint32_t _family_handler(Lxip::uint16_t family, void *addr)
|
|
{
|
|
using namespace Linux;
|
|
|
|
if (!addr)
|
|
return 0;
|
|
|
|
switch (family)
|
|
{
|
|
case AF_INET:
|
|
|
|
struct sockaddr_in *in = (struct sockaddr_in *)addr;
|
|
struct sockaddr_in *out = (struct sockaddr_in *)&_call.addr;
|
|
|
|
out->sin_family = family;
|
|
out->sin_port = in->sin_port;
|
|
out->sin_addr.s_addr = in->sin_addr.s_addr;
|
|
|
|
return sizeof(struct sockaddr_in);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************
|
|
** Glue interface to Linux TCP/IP stack **
|
|
******************************************/
|
|
|
|
void _do_accept()
|
|
{
|
|
using namespace Linux;
|
|
|
|
struct socket *sock = call_socket();
|
|
struct socket *new_sock = (struct socket *)kzalloc(sizeof(struct socket), 0);
|
|
|
|
_handle.socket = 0;
|
|
|
|
if (!new_sock)
|
|
return;
|
|
|
|
new_sock->type = sock->type;
|
|
new_sock->ops = sock->ops;
|
|
|
|
if ((sock->ops->accept(sock, new_sock, 0)) < 0) {
|
|
kfree(new_sock);
|
|
return;
|
|
}
|
|
|
|
_handle.socket = static_cast<void *>(new_sock);
|
|
|
|
|
|
if (!_call.accept.addr)
|
|
return;
|
|
|
|
int len;
|
|
if ((new_sock->ops->getname(new_sock, (struct sockaddr *)&_call.addr,
|
|
&len, 2)) < 0)
|
|
return;
|
|
|
|
*_call.accept.len = min(*_call.accept.len, len);
|
|
Genode::memcpy(_call.accept.addr, &_call.addr, *_call.accept.len);
|
|
}
|
|
|
|
void _do_bind()
|
|
{
|
|
struct Linux::socket *sock = call_socket();
|
|
|
|
_result.err = sock->ops->bind(sock, (struct Linux::sockaddr *) &_call.addr,
|
|
_call.addr_len);
|
|
}
|
|
|
|
void _do_close()
|
|
{
|
|
using namespace Linux;
|
|
|
|
struct socket *s = call_socket();
|
|
if (s->ops)
|
|
s->ops->release(s);
|
|
|
|
kfree(s);
|
|
}
|
|
|
|
void _do_connect()
|
|
{
|
|
Linux::socket *sock = call_socket();
|
|
|
|
//XXX: have a look at the file flags
|
|
_result.err = sock->ops->connect(sock, (struct Linux::sockaddr *) &_call.addr,
|
|
_call.addr_len, 0);
|
|
}
|
|
|
|
void _do_getname(int peer)
|
|
{
|
|
int len = sizeof(Linux::sockaddr_storage);
|
|
_result.err = call_socket()->ops->getname(call_socket(),
|
|
(struct Linux::sockaddr *)&_call.addr,
|
|
&len, peer);
|
|
|
|
*_call.accept.len = Linux::min(*_call.accept.len, len);
|
|
Genode::memcpy(_call.accept.addr, &_call.addr, *_call.accept.len);
|
|
}
|
|
|
|
|
|
void _do_getopt()
|
|
{
|
|
_result.err = Linux::sock_getsockopt(call_socket(), _call.sockopt.level,
|
|
_call.sockopt.optname,
|
|
(char *)_call.sockopt.optval,
|
|
_call.sockopt.optlen_ptr);
|
|
}
|
|
|
|
void _do_ioctl()
|
|
{
|
|
_result.err = call_socket()->ops->ioctl(call_socket(),
|
|
_call.ioctl.request,
|
|
_call.ioctl.arg);
|
|
}
|
|
|
|
void _do_listen()
|
|
{
|
|
_result.err = call_socket()->ops->listen(call_socket(),
|
|
_call.listen.backlog);
|
|
}
|
|
|
|
void _do_poll()
|
|
{
|
|
using namespace Linux;
|
|
struct socket *sock = call_socket();
|
|
enum {
|
|
POLLIN_SET = (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR),
|
|
POLLOUT_SET = (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR),
|
|
POLLEX_SET = (POLLPRI)
|
|
};
|
|
|
|
/*
|
|
* Needed by udp_poll because it may check file->f_flags
|
|
*/
|
|
struct file f;
|
|
f.f_flags = 0;
|
|
|
|
/*
|
|
* Set socket wait queue to one so we can block poll in 'tcp_poll -> poll_wait'
|
|
*/
|
|
set_sock_wait(sock, _call.poll.block ? 1 : 0);
|
|
int mask = sock->ops->poll(&f, sock, 0);
|
|
set_sock_wait(sock, 0);
|
|
|
|
_result.err = 0;
|
|
if (mask & POLLIN_SET)
|
|
_result.err |= Lxip::POLLIN;
|
|
if (mask & POLLOUT_SET)
|
|
_result.err |= Lxip::POLLOUT;
|
|
if (mask & POLLEX_SET)
|
|
_result.err |= Lxip::POLLEX;
|
|
}
|
|
|
|
void _do_recv()
|
|
{
|
|
using namespace Linux;
|
|
struct msghdr msg;
|
|
struct iovec iov;
|
|
|
|
msg.msg_control = NULL;
|
|
msg.msg_controllen = 0;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_iov = &iov;
|
|
iov.iov_len = _call.msg.len;
|
|
iov.iov_base = _call.msg.buf;
|
|
msg.msg_name = _call.addr_len ? &_call.addr : 0;
|
|
msg.msg_namelen = _call.addr_len;
|
|
msg.msg_flags = 0;
|
|
|
|
if (_call.handle.non_block)
|
|
msg.msg_flags |= MSG_DONTWAIT;
|
|
|
|
//XXX: check for non-blocking flag
|
|
_result.err = call_socket()->ops->recvmsg(0, call_socket(), &msg,
|
|
_call.msg.len,
|
|
_call.msg.flags);
|
|
|
|
if (_call.msg.addr) {
|
|
*_call.msg.addr_len = min(*_call.msg.addr_len, msg.msg_namelen);
|
|
Genode::memcpy(_call.msg.addr, &_call.addr, *_call.msg.addr_len);
|
|
}
|
|
}
|
|
|
|
void _do_send()
|
|
{
|
|
using namespace Linux;
|
|
struct msghdr msg;
|
|
struct iovec iov;
|
|
|
|
_result.err = socket_check_state(call_socket());
|
|
if (_result.err < 0)
|
|
return;
|
|
|
|
msg.msg_control = NULL;
|
|
msg.msg_controllen = 0;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_iov = &iov;
|
|
iov.iov_len = _call.msg.len;
|
|
iov.iov_base = _call.msg.buf;
|
|
msg.msg_name = _call.addr_len ? &_call.addr : 0;
|
|
msg.msg_namelen = _call.addr_len;
|
|
msg.msg_flags = _call.msg.flags;
|
|
|
|
if (_call.handle.non_block)
|
|
msg.msg_flags |= MSG_DONTWAIT;
|
|
|
|
_result.err = call_socket()->ops->sendmsg(0, call_socket(), &msg,
|
|
_call.msg.len);
|
|
}
|
|
|
|
void _do_setopt()
|
|
{
|
|
_result.err = Linux::sock_setsockopt(call_socket(), _call.sockopt.level,
|
|
_call.sockopt.optname,
|
|
(char *)_call.sockopt.optval,
|
|
_call.sockopt.optlen);
|
|
}
|
|
|
|
void _do_shutdown()
|
|
{
|
|
_result.err = call_socket()->ops->shutdown(call_socket(),
|
|
_call.shutdown.how);
|
|
}
|
|
|
|
void _do_socket()
|
|
{
|
|
using namespace Linux;
|
|
int type = _call.socket.type == Lxip::TYPE_STREAM ? SOCK_STREAM :
|
|
SOCK_DGRAM;
|
|
|
|
struct socket *s = (struct socket *)kzalloc(sizeof(struct socket), 0);
|
|
|
|
if (!sock_create_kern(AF_INET, type, 0, &s)) {
|
|
_handle.socket = static_cast<void *>(s);
|
|
return;
|
|
}
|
|
|
|
_handle.socket = 0;
|
|
kfree(s);
|
|
}
|
|
|
|
public:
|
|
|
|
Socketcall()
|
|
: Thread("socketcall"),
|
|
_signal(Genode::Signal_context_capability(Env::receiver()->manage(this)))
|
|
{
|
|
start();
|
|
}
|
|
|
|
~Socketcall() { Env::receiver()->dissolve(this); }
|
|
|
|
void entry()
|
|
{
|
|
while (true) {
|
|
Genode::Signal s = Net::Env::receiver()->wait_for_signal();
|
|
static_cast<Genode::Signal_dispatcher_base *>(s.context())->dispatch(s.num());
|
|
}
|
|
}
|
|
|
|
/***********************
|
|
** Signal dispatcher **
|
|
***********************/
|
|
|
|
void dispatch(unsigned num)
|
|
{
|
|
if (verbose)
|
|
PDBG("SOCKET dispatch %u", _call.opcode);
|
|
|
|
switch (_call.opcode) {
|
|
|
|
case OP_ACCEPT : _do_accept(); break;
|
|
case OP_BIND : _do_bind(); break;
|
|
case OP_CLOSE : _do_close(); break;
|
|
case OP_CONNECT : _do_connect(); break;
|
|
case OP_GETNAME : _do_getname(0); break;
|
|
case OP_GETOPT : _do_getopt(); break;
|
|
case OP_IOCTL : _do_ioctl(); break;
|
|
case OP_PEERNAME : _do_getname(1); break;
|
|
case OP_LISTEN : _do_listen(); break;
|
|
case OP_POLL : _do_poll(); break;
|
|
case OP_RECV : _do_recv(); break;
|
|
case OP_SEND : _do_send(); break;
|
|
case OP_SETOPT : _do_setopt(); break;
|
|
case OP_SHUTDOWN : _do_shutdown(); break;
|
|
case OP_SOCKET : _do_socket(); break;
|
|
|
|
default:
|
|
_handle.socket = 0;
|
|
PWRN("Unkown opcode: %u\n", _call.opcode);
|
|
}
|
|
|
|
_unblock();
|
|
}
|
|
|
|
|
|
/**************************
|
|
** Socketcall interface **
|
|
**************************/
|
|
|
|
Lxip::Handle accept(Lxip::Handle h, void *addr, Lxip::uint32_t *len)
|
|
{
|
|
_call.opcode = OP_ACCEPT;
|
|
_call.handle = h;
|
|
_call.accept.addr = addr;
|
|
_call.accept.len = len;
|
|
|
|
_submit_and_block();
|
|
|
|
return _handle;
|
|
}
|
|
|
|
int bind(Lxip::Handle h, Lxip::uint16_t family, void *addr)
|
|
{
|
|
_call.opcode = OP_BIND;
|
|
_call.handle = h;
|
|
_call.addr_len = _family_handler(family, addr);
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
void close(Lxip::Handle h)
|
|
{
|
|
_call.opcode = OP_CLOSE;
|
|
_call.handle = h;
|
|
|
|
_submit_and_block();
|
|
}
|
|
|
|
int connect(Lxip::Handle h, Lxip::uint16_t family, void *addr)
|
|
{
|
|
_call.opcode = OP_CONNECT;
|
|
_call.handle = h;
|
|
_call.addr_len = _family_handler(family, addr);
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
int getpeername(Lxip::Handle h, void *addr, Lxip::uint32_t *len)
|
|
{
|
|
_call.opcode = OP_PEERNAME;
|
|
_call.handle = h;
|
|
_call.accept.len = len;
|
|
_call.accept.addr = addr;
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
int getsockname(Lxip::Handle h, void *addr, Lxip::uint32_t *len)
|
|
{
|
|
_call.opcode = OP_GETNAME;
|
|
_call.handle = h;
|
|
_call.accept.len = len;
|
|
_call.accept.addr = addr;
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
int getsockopt(Lxip::Handle h, int level, int optname,
|
|
void *optval, int *optlen)
|
|
{
|
|
_call.opcode = OP_GETOPT;
|
|
_call.handle = h;
|
|
_call.sockopt.level = level;
|
|
_call.sockopt.optname = optname;
|
|
_call.sockopt.optval = optval;
|
|
_call.sockopt.optlen_ptr = optlen;
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
int ioctl(Lxip::Handle h, int request, char *arg)
|
|
{
|
|
_call.opcode = OP_IOCTL;
|
|
_call.handle = h;
|
|
_call.ioctl.request = request;
|
|
_call.ioctl.arg = (unsigned long)arg;
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
int listen(Lxip::Handle h, int backlog)
|
|
{
|
|
_call.opcode = OP_LISTEN;
|
|
_call.handle = h;
|
|
_call.listen.backlog = backlog;
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
int poll(Lxip::Handle h, bool block)
|
|
{
|
|
_call.opcode = OP_POLL;
|
|
_call.handle = h;
|
|
_call.poll.block = block;
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
Lxip::ssize_t recv(Lxip::Handle h, void *buf, Lxip::size_t len, int flags,
|
|
Lxip::uint16_t family, void *addr,
|
|
Lxip::uint32_t *addr_len)
|
|
{
|
|
_call.opcode = OP_RECV;
|
|
_call.handle = h;
|
|
_call.msg.buf = buf;
|
|
_call.msg.len = len;
|
|
_call.msg.addr = addr;
|
|
_call.msg.addr_len = addr_len;
|
|
_call.msg.flags = flags;
|
|
_call.addr_len = _family_handler(family, addr);
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.len;
|
|
}
|
|
|
|
Lxip::ssize_t send(Lxip::Handle h, const void *buf, Lxip::size_t len, int flags,
|
|
Lxip::uint16_t family, void *addr)
|
|
{
|
|
_call.opcode = OP_SEND;
|
|
_call.handle = h;
|
|
_call.msg.buf = (void *)buf;
|
|
_call.msg.len = len;
|
|
_call.msg.flags = flags;
|
|
_call.addr_len = _family_handler(family, addr);
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.len;
|
|
}
|
|
|
|
int setsockopt(Lxip::Handle h, int level, int optname,
|
|
const void *optval, Lxip::uint32_t optlen)
|
|
{
|
|
_call.opcode = OP_SETOPT,
|
|
_call.handle = h;
|
|
_call.sockopt.level = level;
|
|
_call.sockopt.optname = optname;
|
|
_call.sockopt.optval = optval;
|
|
_call.sockopt.optlen = optlen;
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
int shutdown(Lxip::Handle h, int how)
|
|
{
|
|
_call.opcode = OP_SHUTDOWN;
|
|
_call.handle = h;
|
|
_call.shutdown.how = how;
|
|
|
|
_submit_and_block();
|
|
|
|
return _result.err;
|
|
}
|
|
|
|
Lxip::Handle socket(Lxip::Type type)
|
|
{
|
|
_call.opcode = OP_SOCKET;
|
|
_call.socket.type = type;
|
|
|
|
_submit_and_block();
|
|
|
|
return _handle;
|
|
}
|
|
};
|
|
|
|
|
|
Lxip::Socketcall & Lxip::init(char *address_config)
|
|
{
|
|
static int init = lxip_init(address_config);
|
|
static Net::Socketcall socketcall;
|
|
|
|
return socketcall;
|
|
}
|