David van Moolenbroek 7ecc6a9247 libc: enable all functionality in net/
Some functions in lib/libc/net were disabled on MINIX3 only, but with
a few added header files they build just fine, even though some of
them rely on system functionality that has not yet been implemented.
Since the functionality is unlikely to be used in practice (because
it typically requires the use of protocol families that themselves are
not yet supported, such as IPv6), already enabling it right now helps
in building packages that rely on the functionality being present at
compile time, while not posing any practical risk of breaking the same
packages at run time.

Change-Id: Idee8e3963c9e300bde9575429f0e77b0565acaef
2016-03-13 16:03:39 +00:00

561 lines
12 KiB
C

/*
* This file implements handling of socket-related requests from VFS
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <minix/ipc.h>
#include <minix/com.h>
#include <minix/callnr.h>
#include <minix/sysutil.h>
#include <minix/netsock.h>
#include <lwip/tcp.h>
#include <sys/ioc_net.h>
char * netsock_user_name = NULL;
#define NETSOCK_USER_NAME (netsock_user_name ? netsock_user_name : "NETSOCK")
#define debug_print(str, ...) printf("%s : %s:%d : " str "\n", \
NETSOCK_USER_NAME, __func__, __LINE__, ##__VA_ARGS__)
#if 0
#define debug_sock_print(...) debug_print(__VA_ARGS__)
#else
#define debug_sock_print(...)
#endif
#if 0
#define debug_sock_select_print(...) debug_print(__VA_ARGS__)
#else
#define debug_sock_select_print(...) debug_sock_print(__VA_ARGS__)
#endif
#define netsock_panic(str, ...) panic("%s : " str, NETSOCK_USER_NAME, \
##__VA_ARGS__)
#define netsock_error(str, ...) printf("%s : " str, NETSOCK_USER_NAME, \
##__VA_ARGS__)
struct socket socket_array[MAX_SOCKETS];
static int netsock_open(devminor_t minor, int access, endpoint_t user_endpt);
static int netsock_close(devminor_t minor);
static ssize_t netsock_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static ssize_t netsock_write(devminor_t minor, u64_t position,
endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
cdev_id_t id);
static int netsock_ioctl(devminor_t minor, unsigned long request,
endpoint_t endpt, cp_grant_id_t grant, int flags,
endpoint_t user_endpt, cdev_id_t id);
static int netsock_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
static int netsock_select(devminor_t minor, unsigned int ops,
endpoint_t endpt);
static struct chardriver netsock_tab = {
.cdr_open = netsock_open,
.cdr_close = netsock_close,
.cdr_read = netsock_read,
.cdr_write = netsock_write,
.cdr_ioctl = netsock_ioctl,
.cdr_cancel = netsock_cancel,
.cdr_select = netsock_select
};
#define recv_q_alloc() debug_malloc(sizeof(struct recv_q))
#define recv_q_free debug_free
struct mq {
struct sock_req req;
struct mq * prev;
struct mq * next;
};
#define mq_alloc() debug_malloc(sizeof(struct mq))
#define mq_free debug_free
static struct mq * mq_head, *mq_tail;
static int mq_enqueue(struct sock_req * req)
{
struct mq * mq;
debug_sock_print("sock %d op %d", req->minor, req->type);
mq = mq_alloc();
if (mq == NULL)
return -1;
mq->next = NULL;
mq->req = *req;
if (mq_head) {
mq->prev = mq_tail;
mq_tail->next = mq;
mq_tail = mq;
}
else {
mq->prev = NULL;
mq_head = mq_tail = mq;
}
return 0;
}
__unused static struct mq * mq_dequeue_head(void)
{
struct mq * ret;
if (!mq_head)
return NULL;
ret = mq_head;
if (mq_head != mq_tail) {
mq_head = mq_head->next;
mq_head->prev = NULL;
} else
mq_head = mq_tail = NULL;
debug_sock_print("socket %d\n", ret->req.minor);
return ret;
}
static void mq_dequeue(struct mq * mq)
{
if (mq_head == mq_tail)
mq_head = mq_tail = NULL;
else {
if (mq->prev == NULL) {
mq_head = mq->next;
mq_head->prev = NULL;
} else
mq->prev->next = mq->next;
if (mq->next == NULL) {
mq_tail = mq->prev;
mq_tail->next = NULL;
} else
mq->next->prev = mq->prev;
}
}
static int mq_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
{
struct mq * mq;
for (mq = mq_tail; mq; mq = mq->prev) {
if (minor == mq->req.minor && endpt == mq->req.endpt &&
id == mq->req.id) {
debug_sock_print("socket %d\n", minor);
break;
}
}
if (mq) {
mq_dequeue(mq);
mq_free(mq);
}
/* FIXME: shouldn't this return (!!mq) ? */
return 1;
}
int sock_enqueue_data(struct socket * sock, void * data, unsigned size)
{
struct recv_q * r;
if (!(r = recv_q_alloc()))
return ENOMEM;
r->data = data;
r->next = NULL;
if (sock->recv_head) {
sock->recv_tail->next = r;
sock->recv_tail = r;
} else {
sock->recv_head = sock->recv_tail = r;
}
assert(size > 0);
sock->recv_data_size += size;
return OK;
}
void * sock_dequeue_data(struct socket * sock)
{
void * data;
struct recv_q * r;
if ((r = sock->recv_head)) {
data = r->data;
if (!(sock->recv_head = r->next))
sock->recv_tail = NULL;
recv_q_free(r);
return data;
}
return NULL;
}
void sock_dequeue_data_all(struct socket * sock,
recv_data_free_fn data_free)
{
void * data;
while ((data = sock_dequeue_data(sock)))
data_free(data);
sock->recv_data_size = 0;
}
void send_req_reply(struct sock_req * req, int status)
{
if (status == EDONTREPLY)
return;
chardriver_reply_task(req->endpt, req->id, status);
}
void sock_select_notify(struct socket * sock)
{
unsigned int ops;
debug_sock_select_print("socket num %ld", get_sock_num(sock));
assert(sock->select_ep != NONE);
ops = sock->ops->select_reply(sock);
if (ops == 0) {
debug_sock_select_print("called from %p sflags 0x%x TXsz %d RXsz %d\n",
__builtin_return_address(0), sock->flags,
sock->buf_size, sock->recv_data_size);
return;
}
chardriver_reply_select(sock->select_ep, get_sock_num(sock), ops);
sock_clear_select(sock);
sock->select_ep = NONE;
}
struct socket * get_unused_sock(void)
{
int i;
for (i = SOCK_TYPES + MAX_DEVS; i < MAX_SOCKETS; i++) {
if (socket_array[i].ops == NULL) {
/* clear it all */
memset(&socket_array[i], 0, sizeof(struct socket));
return &socket_array[i];
}
}
return NULL;
}
static int socket_request_socket(struct socket * sock, struct sock_req * req)
{
int r, blocking = (req->flags & CDEV_NONBLOCK) ? 0 : 1;
switch (req->type) {
case SOCK_REQ_READ:
if (sock->ops && sock->ops->read)
r = sock->ops->read(sock, req, blocking);
else
r = EINVAL;
break;
case SOCK_REQ_WRITE:
if (sock->ops && sock->ops->write)
r = sock->ops->write(sock, req, blocking);
else
r = EINVAL;
break;
case SOCK_REQ_IOCTL:
if (sock->ops && sock->ops->ioctl)
r = sock->ops->ioctl(sock, req, blocking);
else
r = EINVAL;
break;
default:
netsock_panic("cannot happen!");
}
return r;
}
static int netsock_open(devminor_t minor, int UNUSED(access),
endpoint_t UNUSED(user_endpt))
{
int r;
if ((r = socket_open(minor)) < 0)
return r;
return CDEV_CLONED | r;
}
static int netsock_close(devminor_t minor)
{
struct socket *sock;
if (!(sock = get_sock(minor)))
return EINVAL;
if (sock->ops && sock->ops->close) {
sock->flags &= ~SOCK_FLG_OP_PENDING;
return sock->ops->close(sock);
} else
return EINVAL;
}
static int netsock_request(struct socket *sock, struct sock_req *req)
{
const char *o __unused;
/*
* If an operation is pending (blocking operation) or writing is
* still going on and we're reading, suspend the new operation
*/
if ((sock->flags & SOCK_FLG_OP_PENDING) ||
(req->type == SOCK_REQ_READ &&
sock->flags & SOCK_FLG_OP_WRITING)) {
if (sock->flags & SOCK_FLG_OP_READING)
o = "READ";
else if (sock->flags & SOCK_FLG_OP_WRITING)
o = "WRITE";
else
o = "non R/W op";
debug_sock_print("socket %ld is busy by %s flgs 0x%x\n",
get_sock_num(sock), o, sock->flags);
if (mq_enqueue(req) != 0) {
debug_sock_print("Enqueuing suspended call failed");
return ENOMEM;
}
return EDONTREPLY;
}
return socket_request_socket(sock, req);
}
static ssize_t netsock_read(devminor_t minor, u64_t UNUSED(position),
endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
cdev_id_t id)
{
struct socket *sock;
struct sock_req req;
if (!(sock = get_sock(minor)))
return EINVAL;
/* Build a request record for this request. */
req.type = SOCK_REQ_READ;
req.minor = minor;
req.endpt = endpt;
req.grant = grant;
req.size = size;
req.flags = flags;
req.id = id;
/* Process the request. */
return netsock_request(sock, &req);
}
static ssize_t netsock_write(devminor_t minor, u64_t UNUSED(position),
endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
cdev_id_t id)
{
struct socket *sock;
struct sock_req req;
if (!(sock = get_sock(minor)))
return EINVAL;
/* Build a request record for this request. */
req.type = SOCK_REQ_WRITE;
req.minor = minor;
req.endpt = endpt;
req.grant = grant;
req.size = size;
req.flags = flags;
req.id = id;
/* Process the request. */
return netsock_request(sock, &req);
}
static int netsock_ioctl(devminor_t minor, unsigned long request,
endpoint_t endpt, cp_grant_id_t grant, int flags,
endpoint_t UNUSED(user_endpt), cdev_id_t id)
{
struct socket *sock;
struct sock_req req;
if (!(sock = get_sock(minor)))
return EINVAL;
/* Build a request record for this request. */
req.type = SOCK_REQ_IOCTL;
req.minor = minor;
req.req = request;
req.endpt = endpt;
req.grant = grant;
req.flags = flags;
req.id = id;
/* Process the request. */
return netsock_request(sock, &req);
}
static int netsock_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
{
struct socket *sock;
if (!(sock = get_sock(minor)))
return EDONTREPLY;
debug_sock_print("socket num %ld", get_sock_num(sock));
/* Cancel the last operation in the queue */
if (mq_cancel(minor, endpt, id))
return EINTR;
/* Cancel any ongoing blocked read */
if ((sock->flags & SOCK_FLG_OP_PENDING) &&
(sock->flags & SOCK_FLG_OP_READING) &&
endpt == sock->req.endpt && id == sock->req.id) {
sock->flags &= ~SOCK_FLG_OP_PENDING;
return EINTR;
}
/* The request may not be found. This is OK. Do not reply. */
return EDONTREPLY;
}
static int netsock_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
{
struct socket *sock;
int r;
/*
* Select is always executed immediately and is never suspended.
* Although, it sets actions which must be monitored
*/
if (!(sock = get_sock(minor)))
return EBADF;
assert(sock->select_ep == NONE || sock->select_ep == endpt);
if (sock->ops && sock->ops->select) {
sock->select_ep = endpt;
r = sock->ops->select(sock, ops);
if (!sock_select_set(sock))
sock->select_ep = NONE;
} else
r = EINVAL;
return r;
}
void socket_request(message * m, int ipc_status)
{
debug_sock_print("request %d", m->m_type);
/* Let the chardriver library decode the request for us. */
chardriver_process(&netsock_tab, m, ipc_status);
}
void mq_process(void)
{
struct mq * mq;
struct socket * sock;
int r;
mq = mq_head;
while(mq) {
struct mq * next = mq->next;
sock = get_sock(mq->req.minor);
if (!(sock->flags & SOCK_FLG_OP_PENDING) &&
!(mq->req.type == SOCK_REQ_READ &&
sock->flags & SOCK_FLG_OP_WRITING)) {
debug_sock_print("resuming op on sock %ld\n",
get_sock_num(sock));
sock->req = mq->req;
r = socket_request_socket(sock, &sock->req);
send_req_reply(&sock->req, r);
mq_dequeue(mq);
mq_free(mq);
return;
}
mq = next;
}
}
int generic_op_select(struct socket * sock, unsigned int sel)
{
int retsel = 0;
debug_sock_print("socket num %ld 0x%x", get_sock_num(sock), sel);
/* in this case any operation would block, no error */
if (sock->flags & SOCK_FLG_OP_PENDING) {
if (sel & CDEV_NOTIFY) {
if (sel & CDEV_OP_RD)
sock->flags |= SOCK_FLG_SEL_READ;
if (sel & CDEV_OP_WR)
sock->flags |= SOCK_FLG_SEL_WRITE;
/* FIXME we do not monitor error */
}
return 0;
}
if (sel & CDEV_OP_RD) {
if (sock->recv_head)
retsel |= CDEV_OP_RD;
else if (sel & CDEV_NOTIFY)
sock->flags |= SOCK_FLG_SEL_READ;
}
/* FIXME generic packet socket never blocks on write */
if (sel & CDEV_OP_WR)
retsel |= CDEV_OP_WR;
/* FIXME CDEV_OP_ERR is ignored, we do not generate exceptions */
return retsel;
}
int generic_op_select_reply(struct socket * sock)
{
unsigned int sel = 0;
assert(sock->select_ep != NONE);
debug_sock_print("socket num %ld", get_sock_num(sock));
/* unused for generic packet socket, see generic_op_select() */
assert((sock->flags & (SOCK_FLG_SEL_WRITE | SOCK_FLG_SEL_ERROR)) == 0);
if (sock->flags & SOCK_FLG_OP_PENDING) {
debug_sock_print("WARNING socket still blocking!");
return 0;
}
if (sock->flags & SOCK_FLG_SEL_READ && sock->recv_head)
sel |= CDEV_OP_RD;
if (sel)
sock->flags &= ~(SOCK_FLG_SEL_WRITE | SOCK_FLG_SEL_READ |
SOCK_FLG_SEL_ERROR);
return sel;
}