/servers/lwip/socket.c
C | 644 lines | 493 code | 97 blank | 54 comment | 114 complexity | 72cf40928c9fc9dcdaf7cc7140b04a63 MD5 | raw file
Possible License(s): MIT, WTFPL, AGPL-1.0, BSD-3-Clause, GPL-3.0, LGPL-2.0, JSON, 0BSD
- /*
- * 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 <lwip/tcp.h>
- #include <sys/ioc_net.h>
- #include "inet_config.h"
- #include "proto.h"
- #include "socket.h"
- #if 0
- #define debug_sock_print(str, ...) printf("LWIP %s:%d : " str "\n", \
- __func__, __LINE__, ##__VA_ARGS__)
- #else
- #define debug_sock_print(...) debug_print(__VA_ARGS__)
- #endif
- struct socket socket[MAX_SOCKETS];
- static int notified;
- #define recv_q_alloc() debug_malloc(sizeof(struct recv_q))
- #define recv_q_free debug_free
- struct mq {
- message m;
- 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(message * m)
- {
- struct mq * mq;
- debug_sock_print("sock %d op %d", m->DEVICE, m->m_type);
- mq = mq_alloc();
- if (mq == NULL)
- return -1;
- mq->next = NULL;
- mq->m = *m;
- 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->m.DEVICE);
- 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(message * m)
- {
- struct mq * mq;
- for (mq = mq_tail; mq; mq = mq->prev) {
- if (m->DEVICE == mq->m.DEVICE &&
- m->USER_ENDPT == mq->m.USER_ENDPT &&
- m->IO_GRANT == mq->m.IO_GRANT) {
- debug_sock_print("socket %d\n", mq->m.DEVICE);
- break;
- }
- }
- mq_dequeue(mq);
- mq_free(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;
- }
- static void set_reply_msg(message * m, int status)
- {
- int proc, ref;
- proc= m->USER_ENDPT;
- ref= (int)m->IO_GRANT;
- m->REP_ENDPT= proc;
- m->REP_STATUS= status;
- m->REP_IO_GRANT= ref;
- }
- void send_reply(message * m, int status)
- {
- int result;
- debug_sock_print("status %d", status);
- set_reply_msg(m, status);
-
- m->m_type = TASK_REPLY;
- result = send(m->m_source, m);
- if (result != OK)
- panic("LWIP : unable to send (err %d)", result);
- }
- void sock_revive(struct socket * sock, int status)
- {
- int result;
- assert(!(sock->flags & SOCK_FLG_OP_REVIVING));
- assert(sock->flags & (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_SUSPENDED));
- if (notified) {
- debug_sock_print("already notified");
- return;
- }
- else {
- assert(sock->mess.m_type != DEV_REVIVE);
- notified = 1;
- }
-
- debug_sock_print("socket num %ld, status %d",
- get_sock_num(sock), status);
- sock->mess.m_type = DEV_REVIVE;
- set_reply_msg(&sock->mess, status);
-
- result = notify(sock->mess.m_source);
- if (result != OK)
- panic("LWIP : unable to notify (err %d)", result);
- sock->flags |= SOCK_FLG_OP_REVIVING;
- }
- void sock_select_notify(struct socket * sock)
- {
- int result;
- debug_sock_print("socket num %ld", get_sock_num(sock));
- assert(sock->select_ep != NONE);
- sock->flags |= SOCK_FLG_SEL_CHECK;
- if (notified) {
- debug_sock_print("already notified");
- return;
- }
- else
- notified = 1;
-
- result = notify(sock->select_ep);
- if (result != OK)
- panic("LWIP : unable to notify (err %d)", result);
- }
- void sock_reply(struct socket * sock, int status)
- {
- debug_sock_print("socket num %ld status %d type %d",
- get_sock_num(sock), status, sock->mess.m_type);
- /*
- * If the status is SUSPEND send the
- * message only if this operation wasn't
- * suspended already, e.g. by enqueing the
- * message when the socket was busy
- * because of another pending message
- *
- * If there is a pending operation or we a reprocessing a suspended
- * operation, revive.
- *
- * Otherwise send a message straightaway
- */
- if (status == SUSPEND) {
- if (sock->flags & SOCK_FLG_OP_SUSPENDED) {
- debug_sock_print("suspended before");
- sock->flags &= ~SOCK_FLG_OP_SUSPENDED;
- return;
- }
- message m = sock->mess;
- debug_sock_print("SUSPEND");
- send_reply(&m, status);
- } else if (sock->flags & (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_SUSPENDED)) {
- sock_revive(sock, status);
- /*
- * From now on, we process suspended calls as any other. The
- * status is set and will be collected
- */
- sock->flags &= ~SOCK_FLG_OP_SUSPENDED;
- } else
- send_reply(&sock->mess, status);
- }
- struct socket * get_unused_sock(void)
- {
- int i;
- for (i = SOCK_TYPES + MAX_DEVS; i < MAX_SOCKETS; i++) {
- if (socket[i].ops == NULL) {
- /* clear it all */
- memset(&socket[i], 0, sizeof(struct socket));
- return &socket[i];
- }
- }
- return NULL;
- }
- struct socket * get_nic_sock(unsigned dev)
- {
- if (dev < MAX_DEVS)
- return &socket[dev + SOCK_TYPES];
- else
- return NULL;
- }
- static void socket_open(message * m)
- {
- struct sock_ops * ops;
- struct socket * sock;
- int ret = OK;
- switch (m->DEVICE) {
- case SOCK_TYPE_TCP:
- ops = &sock_tcp_ops;
- break;
- case SOCK_TYPE_UDP:
- ops = &sock_udp_ops;
- break;
- case SOCK_TYPE_IP:
- ops = &sock_raw_ip_ops;
- break;
- default:
- if (m->DEVICE - SOCK_TYPES < MAX_DEVS) {
- m->DEVICE -= SOCK_TYPES;
- nic_open(m);
- return;
- }
- printf("LWIP unknown socket type %d\n", m->DEVICE);
- send_reply(m, EINVAL);
- return;
- }
-
- sock = get_unused_sock();
- if (!sock) {
- printf("LWIP : no free socket\n");
- send_reply(m, EAGAIN);
- return;
- }
- sock->ops = ops;
- sock->select_ep = NONE;
- sock->recv_data_size = 0;
- if (sock->ops && sock->ops->open)
- ret = sock->ops->open(sock, m);
-
- if (ret == OK) {
- debug_sock_print("new socket %ld", get_sock_num(sock));
- send_reply(m, get_sock_num(sock));
- } else {
- debug_sock_print("failed %d", ret);
- send_reply(m, ret);
- }
- }
- static void do_status(message * m)
- {
- int i;
- debug_sock_print("called");
- notified = 0;
- for (i = 0; i < MAX_SOCKETS; i++) {
- struct socket * sock = &socket[i];
- if (!sock->ops) {
- continue;
- }
- if (sock->flags & (SOCK_FLG_OP_REVIVING)) {
- /*
- * We send the reply and we are done with this request
- */
- debug_sock_print("status %d ep %d sent sock %ld type %d",
- sock->mess.REP_STATUS,
- sock->mess.REP_ENDPT,
- get_sock_num(sock),
- sock->mess.m_type);
- send(m->m_source, &sock->mess);
- /*
- * Remove only the reviving flag, i.e. the status has
- * been consumed. SOCK_FLG_OP_PENDING may stay set. For
- * instance in case of a TCP write, the application is
- * already notified while the process of sending is
- * still going on
- */
- sock->flags &= ~SOCK_FLG_OP_REVIVING;
- return;
- }
- /*
- * We check select AFTER possible reviving an operation,
- * otherwise the select will fail as the socket is still
- * blocking
- */
- if (sock_select_check_set(sock)) {
- if (sock->ops && sock->ops->select_reply) {
- message msg;
- msg.m_type = DEV_IO_READY;
- msg.DEV_MINOR = get_sock_num(sock);
- msg.DEV_SEL_OPS = 0;
- sock->ops->select_reply(sock, &msg);
- if (msg.DEV_SEL_OPS) {
- int result;
- debug_sock_print("socket num %d select "
- "result 0x%x sent",
- msg.DEV_MINOR,
- msg.DEV_SEL_OPS);
- result = send(sock->select_ep, &msg);
- if (result != OK)
- panic("LWIP : unable to send "
- "(err %d)", result);
- sock_clear_select(sock);
- sock->select_ep = NONE;
- return;
- }
- }
- }
- }
- debug_sock_print("no status");
- m->m_type = DEV_NO_STATUS;
- send(m->m_source, m);
- }
- static void socket_request_socket(struct socket * sock, message * m)
- {
- switch (m->m_type) {
- case DEV_READ_S:
- if (sock && sock->ops && sock->ops->read)
- sock->ops->read(sock, m);
- else
- send_reply(m, EINVAL);
- return;
- case DEV_WRITE_S:
- if (sock && sock->ops && sock->ops->write)
- sock->ops->write(sock, m);
- else
- send_reply(m, EINVAL);
- return;
- case DEV_IOCTL_S:
- if (sock && sock->ops && sock->ops->ioctl)
- sock->ops->ioctl(sock, m);
- else
- send_reply(m, EINVAL);
- return;
- default:
- panic("LWIP : cannot happen!");
- }
- }
- void socket_request(message * m)
- {
- struct socket * sock;
- switch (m->m_type) {
- case DEV_OPEN:
- socket_open(m);
- return;
- case DEV_CLOSE:
- sock = get_sock(m->DEVICE);
- if (sock && sock->ops && sock->ops->close) {
- sock->flags &= ~SOCK_FLG_OP_PENDING;
- sock->mess = *m;
- sock->ops->close(sock, m);
- } else
- send_reply(m, EINVAL);
- return;
- case DEV_READ_S:
- case DEV_WRITE_S:
- case DEV_IOCTL_S:
- sock = get_sock(m->DEVICE);
- if (!sock) {
- send_reply(m, EINVAL);
- return;
- }
- /*
- * If an operation is pending (blocking operation) or writing is
- * still going and we want to read, suspend the new operation
- */
- if ((sock->flags & (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_REVIVING)) |
- (m->m_type == DEV_READ_S &&
- sock->flags & SOCK_FLG_OP_WRITING)) {
- char * o = "\0";
- 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\n",
- get_sock_num(sock), o);
- if (mq_enqueue(m) == 0) {
- send_reply(m, SUSPEND);
- } else {
- debug_sock_print("Enqueuing suspended "
- "call failed");
- send_reply(m, ENOMEM);
- }
- return;
- }
- sock->mess = *m;
- socket_request_socket(sock, m);
- return;
- case CANCEL:
- sock = get_sock(m->DEVICE);
- debug_sock_print("socket num %ld", get_sock_num(sock));
- /* Cancel the last operation in the queue */
- if (mq_cancel(m)) {
- send_reply(m, EINTR);
- return;
- /* ... or a blocked read */
- } else if (sock->flags & SOCK_FLG_OP_PENDING &&
- sock->flags & SOCK_FLG_OP_READING) {
- sock->flags &= ~SOCK_FLG_OP_PENDING;
- send_reply(m, EINTR);
- return;
- /*
- * .. or return the status of the operation which was finished
- * before canceled
- */
- } else if (sock->flags & SOCK_FLG_OP_REVIVING) {
- sock->flags &= ~SOCK_FLG_OP_REVIVING;
- send_reply(m, sock->mess.REP_STATUS);
- } else
- panic("LWIP : no operation to cancel");
- return;
- case DEV_SELECT:
- /*
- * Select is always executed immediately and is never suspended.
- * Although, it sets actions which must be monitored
- */
- sock = get_sock(m->DEVICE);
- assert(sock->select_ep == NONE || sock->select_ep == m->m_source);
-
- if (sock && sock->ops && sock->ops->select) {
- sock->ops->select(sock, m);
- if (sock_select_set(sock))
- sock->select_ep = m->m_source;
- } else
- send_reply(m, EINVAL);
- return;
- case DEV_STATUS:
- do_status(m);
- return;
- default:
- printf("LWIP : unknown message from VFS, type %d\n",
- m->m_type);
- }
- send_reply(m, EGENERIC);
- }
- void mq_process(void)
- {
- struct mq * mq;
- struct socket * sock;
- mq = mq_head;
- while(mq) {
- struct mq * next = mq->next;
-
- sock = get_sock(mq->m.DEVICE);
- if (!(sock->flags &
- (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_REVIVING)) &&
- !(mq->m.m_type == DEV_READ_S &&
- sock->flags & SOCK_FLG_OP_WRITING)) {
- sock->flags = SOCK_FLG_OP_SUSPENDED;
- debug_sock_print("resuming op on sock %ld\n",
- get_sock_num(sock));
- sock->mess = mq->m;
- socket_request_socket(sock, &sock->mess);
- mq_dequeue(mq);
- mq_free(mq);
- return;
- }
- mq = next;
- }
- }
- void generic_op_select(struct socket * sock, message * m)
- {
- int retsel = 0, sel;
- debug_print("socket num %ld 0x%x", get_sock_num(sock), m->USER_ENDPT);
- sel = m->USER_ENDPT;
- /* in this case any operation would block, no error */
- if (sock->flags & SOCK_FLG_OP_PENDING) {
- if (sel & SEL_NOTIFY) {
- if (sel & SEL_RD)
- sock->flags |= SOCK_FLG_SEL_READ;
- if (sel & SEL_WR)
- sock->flags |= SOCK_FLG_SEL_WRITE;
- /* FIXME we do not monitor error */
- }
- send_reply(m, 0);
- return;
- }
- if (sel & SEL_RD) {
- if (sock->recv_head)
- retsel |= SEL_RD;
- else if (sel & SEL_NOTIFY)
- sock->flags |= SOCK_FLG_SEL_READ;
- }
- /* FIXME generic packet socket never blocks on write */
- if (sel & SEL_WR)
- retsel |= SEL_WR;
- /* FIXME SEL_ERR is ignored, we do not generate exceptions */
- send_reply(m, retsel);
- }
- void generic_op_select_reply(struct socket * sock, __unused message * m)
- {
- assert(sock->select_ep != NONE);
- debug_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 | SOCK_FLG_OP_REVIVING)) {
- debug_print("WARNING socket still blocking!");
- return;
- }
- if (sock->flags & SOCK_FLG_SEL_READ && sock->recv_head)
- m->DEV_SEL_OPS |= SEL_RD;
-
- if (m->DEV_SEL_OPS)
- sock->flags &= ~(SOCK_FLG_SEL_WRITE | SOCK_FLG_SEL_READ |
- SOCK_FLG_SEL_ERROR);
- }