static err_t tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err) { struct socket * sock = (struct socket *) arg; debug_tcp_print("socket num %ld", get_sock_num(sock)); assert(err == ERR_OK && newpcb); assert(sock->flags & SOCK_FLG_OP_LISTENING); if (sock->flags & SOCK_FLG_OP_PENDING) { int ret; ret = tcp_do_accept(sock, &sock->mess, newpcb); sock_revive(sock, ret); sock->flags &= ~SOCK_FLG_OP_PENDING; if (ret == OK) { return ERR_OK; } /* in case of an error fall through */ } /* If we cannot accept rightaway we enqueue the connection for later */ debug_tcp_print("Enqueue connection sock %ld pcb %p\n", get_sock_num(sock), newpcb); if (sock_enqueue_data(sock, newpcb, 1) != OK) { tcp_abort(newpcb); return ERR_ABRT; } if (sock_select_read_set(sock)) sock_select_notify(sock); return ERR_OK; }
static void tcp_op_listen(struct socket * sock, message * m) { int backlog, err; struct tcp_pcb * new_pcb; debug_tcp_print("socket num %ld", get_sock_num(sock)); err = copy_from_user(m->m_source, &backlog, sizeof(backlog), (cp_grant_id_t) m->IO_GRANT, 0); new_pcb = tcp_listen_with_backlog((struct tcp_pcb *) sock->pcb, (u8_t) backlog); debug_tcp_print("listening pcb %p", new_pcb); if (!new_pcb) { debug_tcp_print("Cannot listen on socket %ld", get_sock_num(sock)); sock_reply(sock, EGENERIC); return; } /* advertise that this socket is willing to accept connections */ tcp_accept(new_pcb, tcp_accept_callback); sock->flags |= SOCK_FLG_OP_LISTENING; sock->pcb = new_pcb; sock_reply(sock, OK); }
static void tcp_op_accept(struct socket * sock, message * m) { debug_tcp_print("socket num %ld", get_sock_num(sock)); if (!(sock->flags & SOCK_FLG_OP_LISTENING)) { debug_tcp_print("socket %ld does not listen\n", get_sock_num(sock)); sock_reply(sock, EINVAL); return; } /* there is a connection ready to be accepted */ if (sock->recv_head) { int ret; struct tcp_pcb * pcb; pcb = (struct tcp_pcb *) sock->recv_head->data; assert(pcb); ret = tcp_do_accept(sock, m, pcb); sock_reply(sock, ret); if (ret == OK) sock_dequeue_data(sock); return; } debug_tcp_print("no ready connection, suspending\n"); sock_reply(sock, SUSPEND); sock->flags |= SOCK_FLG_OP_PENDING; }
static void tcp_op_read(struct socket * sock, message * m) { debug_tcp_print("socket num %ld", get_sock_num(sock)); if (!sock->pcb || ((struct tcp_pcb *) sock->pcb)->state != ESTABLISHED) { debug_tcp_print("Connection not established\n"); sock_reply(sock, ENOTCONN); return; } if (sock->recv_head) { /* data available receive immeditely */ int ret = read_from_tcp(sock, m); debug_tcp_print("read op finished"); sock_reply(sock, ret); } else { if (sock->flags & SOCK_FLG_CLOSED) { printf("socket %ld already closed!!! call from %d\n", get_sock_num(sock), m->USER_ENDPT); do_tcp_debug = 1; sock_reply(sock, 0); return; } /* operation is being processed */ debug_tcp_print("no data to read, suspending"); sock_reply(sock, SUSPEND); sock->flags |= SOCK_FLG_OP_PENDING | SOCK_FLG_OP_READING; } }
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_open(m, EINVAL); return; } sock = get_unused_sock(); if (!sock) { printf("LWIP : no free socket\n"); send_reply_open(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_print("new socket %ld", get_sock_num(sock)); send_reply_open(m, get_sock_num(sock)); } else { debug_print("failed %d", ret); send_reply_open(m, ret); } }
static void udp_op_write(struct socket * sock, message * m, __unused int blk) { int ret; struct pbuf * pbuf; debug_udp_print("socket num %ld data size %d", get_sock_num(sock), m->COUNT); pbuf = pbuf_alloc(PBUF_TRANSPORT, m->COUNT, PBUF_POOL); if (!pbuf) { ret = ENOMEM; goto write_err; } if ((ret = copy_from_user(m->m_source, pbuf->payload, m->COUNT, (cp_grant_id_t) m->IO_GRANT, 0)) != OK) { pbuf_free(pbuf); goto write_err; } if (sock->usr_flags & NWUO_RWDATONLY) ret = udp_op_send(sock, pbuf, m); else ret = udp_op_sendto(sock, pbuf, m); if (pbuf_free(pbuf) == 0) { panic("We cannot buffer udp packets yet!"); } write_err: sock_reply(sock, ret); }
static void udp_op_read(struct socket * sock, message * m, int blk) { debug_udp_print("socket num %ld", get_sock_num(sock)); if (sock->recv_head) { /* data available receive immeditely */ struct udp_recv_data * data; int ret; data = (struct udp_recv_data *) sock->recv_head->data; ret = udp_do_receive(sock, m, (struct udp_pcb *) sock->pcb, data->pbuf, &data->ip, data->port); if (ret > 0) { sock_dequeue_data(sock); sock->recv_data_size -= data->pbuf->tot_len; udp_recv_free(data); } sock_reply(sock, ret); } else if (!blk) sock_reply(sock, EAGAIN); else { /* store the message so we know how to reply */ sock->mess = *m; /* operation is being processes */ sock->flags |= SOCK_FLG_OP_PENDING; debug_udp_print("no data to read, suspending\n"); } }
static int udp_op_write(struct socket * sock, struct sock_req * req, __unused int blk) { int ret; struct pbuf * pbuf; debug_udp_print("socket num %ld data size %u", get_sock_num(sock), req->size); pbuf = pbuf_alloc(PBUF_TRANSPORT, req->size, PBUF_POOL); if (!pbuf) return ENOMEM; if ((ret = copy_from_user(req->endpt, pbuf->payload, req->size, req->grant, 0)) != OK) { pbuf_free(pbuf); return ret; } if (sock->usr_flags & NWUO_RWDATONLY) ret = udp_op_send(sock, pbuf, req->size); else ret = udp_op_sendto(sock, pbuf, req->size); if (pbuf_free(pbuf) == 0) { panic("We cannot buffer udp packets yet!"); } return ret; }
static void tcp_op_select_reply(struct socket * sock, message * m) { assert(sock->select_ep != NONE); debug_tcp_print("socket num %ld", get_sock_num(sock)); if (sock->flags & (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_REVIVING)) { debug_tcp_print("WARNING socket still blocking!"); return; } if (sock->flags & SOCK_FLG_SEL_READ) { if (sock->pcb == NULL || (sock->recv_head && !(sock->flags & SOCK_FLG_OP_WRITING)) || (!(sock->flags & SOCK_FLG_OP_LISTENING) && ((struct tcp_pcb *) sock->pcb)->state != ESTABLISHED)) { m->DEV_SEL_OPS |= SEL_RD; debug_tcp_print("read won't block"); } } if (sock->flags & SOCK_FLG_SEL_WRITE && (sock->pcb == NULL || ((struct tcp_pcb *) sock->pcb)->state == ESTABLISHED)) { m->DEV_SEL_OPS |= SEL_WR; debug_tcp_print("write won't block"); } if (m->DEV_SEL_OPS) sock->flags &= ~(SOCK_FLG_SEL_WRITE | SOCK_FLG_SEL_READ | SOCK_FLG_SEL_ERROR); }
static void tcp_error_callback(void *arg, err_t err) { int perr; struct socket * sock = (struct socket *) arg; debug_tcp_print("socket num %ld err %d", get_sock_num(sock), err); switch (err) { case ERR_RST: perr = ECONNREFUSED; break; case ERR_CLSD: perr = EPIPE; break; case ERR_CONN: perr = ENOTCONN; break; default: perr = EIO; } if (sock->flags & SOCK_FLG_OP_PENDING) { sock_revive(sock, perr); sock->flags &= ~SOCK_FLG_OP_PENDING; } else if (sock_select_set(sock)) sock_select_notify(sock); /* * When error callback is called the tcb either does not exist anymore * or is going to be deallocated soon after. We must not use the pcb * anymore */ sock->pcb = NULL; }
static int tcp_do_accept(struct socket * listen_sock, message * m, struct tcp_pcb * newpcb) { struct socket * newsock; unsigned sock_num; int ret; debug_tcp_print("socket num %ld", get_sock_num(listen_sock)); if ((ret = copy_from_user(m->m_source, &sock_num, sizeof(sock_num), (cp_grant_id_t) m->IO_GRANT, 0)) != OK) return EFAULT; if (!is_valid_sock_num(sock_num)) return EBADF; newsock = get_sock(sock_num); assert(newsock->pcb); /* because of previous open() */ /* we really want to forget about this socket */ tcp_err((struct tcp_pcb *)newsock->pcb, NULL); tcp_abandon((struct tcp_pcb *)newsock->pcb, 0); tcp_arg(newpcb, newsock); tcp_err(newpcb, tcp_error_callback); tcp_sent(newpcb, tcp_sent_callback); tcp_recv(newpcb, tcp_recv_callback); tcp_nagle_disable(newpcb); tcp_accepted(((struct tcp_pcb *)(listen_sock->pcb))); newsock->pcb = newpcb; debug_tcp_print("Accepted new connection using socket %d\n", sock_num); return OK; }
static void tcp_get_opt(struct socket * sock, message * m) { int err; nwio_tcpopt_t tcpopt; struct tcp_pcb * pcb = (struct tcp_pcb *) sock->pcb; debug_tcp_print("socket num %ld", get_sock_num(sock)); assert(pcb); if ((unsigned) m->COUNT < sizeof(tcpopt)) { sock_reply(sock, EINVAL); return; } /* FIXME : not used by the userspace library */ tcpopt.nwto_flags = 0; err = copy_to_user(m->m_source, &tcpopt, sizeof(tcpopt), (cp_grant_id_t) m->IO_GRANT, 0); if (err != OK) sock_reply(sock, err); sock_reply(sock, OK); }
static err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, __unused err_t err) { struct socket * sock = (struct socket *) arg; debug_tcp_print("socket num %ld err %d", get_sock_num(sock), err); if (sock->pcb == NULL) { if (sock_select_set(sock)) sock_select_notify(sock); return ERR_OK; } assert((struct tcp_pcb *)sock->pcb == tpcb); tcp_sent(tpcb, tcp_sent_callback); tcp_recv(tpcb, tcp_recv_callback); sock_revive(sock, OK); sock->flags &= ~(SOCK_FLG_OP_PENDING | SOCK_FLG_OP_CONNECTING); /* revive does the sock_select_notify() for us */ return ERR_OK; }
static void udp_recv_callback(void *arg, struct udp_pcb *pcb, struct pbuf *pbuf, ip_addr_t *addr, u16_t port) { struct socket * sock = (struct socket *) arg; struct udp_recv_data * data; debug_udp_print("socket num : %ld addr : %x port : %d\n", get_sock_num(sock), (unsigned int) addr->addr, port); if (sock->flags & SOCK_FLG_OP_PENDING) { /* we are resuming a suspended operation */ int ret; ret = udp_do_receive(sock, &sock->req, pcb, pbuf, addr, port); send_req_reply(&sock->req, ret); sock->flags &= ~SOCK_FLG_OP_PENDING; if (ret > 0) { pbuf_free(pbuf); return; } } /* Do not enqueue more data than allowed */ if (sock->recv_data_size > UDP_BUF_SIZE) { pbuf_free(pbuf); return; } /* * nobody is waiting for the data or an error occured above, we enqueue * the packet */ if (!(data = udp_recv_alloc())) { pbuf_free(pbuf); return; } data->ip = *addr; data->port = port; data->pbuf = pbuf; if (sock_enqueue_data(sock, data, data->pbuf->tot_len) != OK) { udp_recv_free(data); return; } /* * We don't need to notify when somebody is already waiting, reviving * read operation will do the trick for us. But we must announce new * data available here. */ if (sock_select_read_set(sock)) sock_select_notify(sock); }
int socket_open(devminor_t minor) { struct sock_ops * ops; struct socket * sock; int ret = OK; switch (minor) { 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 (minor - SOCK_TYPES < MAX_DEVS) return nic_open(minor - SOCK_TYPES); printf("LWIP unknown socket type %d\n", minor); return EINVAL; } sock = get_unused_sock(); if (!sock) { printf("LWIP : no free socket\n"); return EAGAIN; } sock->ops = ops; sock->select_ep = NONE; sock->recv_data_size = 0; if (sock->ops && sock->ops->open) ret = sock->ops->open(sock); if (ret == OK) { debug_print("new socket %ld", get_sock_num(sock)); ret = get_sock_num(sock); } else { debug_print("failed %d", ret); /* FIXME: shouldn't sock be freed now? */ } return ret; }
static void raw_ip_op_close(struct socket * sock, __unused message * m) { debug_print("socket num %ld", get_sock_num(sock)); raw_ip_close(sock); sock_reply_close(sock, OK); }
int raw_socket_input(struct pbuf * pbuf, struct nic * nic) { struct socket * sock; struct pbuf * pbuf_new; if ((sock = nic->raw_socket) == NULL) return 0; debug_print("socket num : %ld", get_sock_num(sock)); if (sock->flags & SOCK_FLG_OP_PENDING) { int ret; /* we are resuming a suspended operation */ ret = raw_receive(&sock->mess, pbuf); if (ret > 0) { sock_reply(sock, ret); sock->flags &= ~SOCK_FLG_OP_PENDING; return 0; } else { sock_reply(sock, ret); sock->flags &= ~SOCK_FLG_OP_PENDING; } } /* Do not enqueue more data than allowed */ if (sock->recv_data_size > RAW_BUF_SIZE) { return 0; } /* * nobody is waiting for the data or an error occured above, we enqueue * the packet. We store a copy of this packet */ pbuf_new = pbuf_alloc(PBUF_RAW, pbuf->tot_len, PBUF_RAM); if (pbuf_new == NULL) { debug_print("LWIP : cannot allocated new pbuf\n"); return 0; } if (pbuf_copy(pbuf_new, pbuf) != ERR_OK) { debug_print("LWIP : cannot copy pbuf\n"); return 0; } /* * If we didn't managed to enqueue the packet we report it as not * consumed */ if (sock_enqueue_data(sock, pbuf_new, pbuf_new->tot_len) != OK) { pbuf_free(pbuf_new); } return 0; }
static int raw_ip_op_open(struct socket * sock, __unused message * m) { debug_print("socket num %ld", get_sock_num(sock)); if (!(sock->buf = sock_alloc_buf(RAW_IP_BUF_SIZE))) { return ENOMEM; } sock->buf_size = RAW_IP_BUF_SIZE; return OK; }
static void raw_ip_op_write(struct socket * sock, message * m, __unused int blk) { int ret; struct pbuf * pbuf; struct ip_hdr * ip_hdr; debug_print("socket num %ld data size %d", get_sock_num(sock), m->COUNT); if (sock->pcb == NULL) { ret = EIO; goto write_err; } if ((size_t) m->COUNT > sock->buf_size) { ret = ENOMEM; goto write_err; } pbuf = pbuf_alloc(PBUF_LINK, m->COUNT, PBUF_RAM); if (!pbuf) { ret = ENOMEM; goto write_err; } if ((ret = copy_from_user(m->m_source, pbuf->payload, m->COUNT, (cp_grant_id_t) m->IO_GRANT, 0)) != OK) { pbuf_free(pbuf); goto write_err; } ip_hdr = (struct ip_hdr *) pbuf->payload; if (pbuf_header(pbuf, -IP_HLEN)) { pbuf_free(pbuf); ret = EIO; goto write_err; } if ((ret = raw_sendto((struct raw_pcb *)sock->pcb, pbuf, (ip_addr_t *) &ip_hdr->dest)) != OK) { debug_print("raw_sendto failed %d", ret); ret = EIO; } else ret = m->COUNT; pbuf_free(pbuf); write_err: sock_reply(sock, ret); }
static void tcp_op_get_cookie(struct socket * sock, message * m) { tcp_cookie_t cookie; unsigned sock_num; assert(sizeof(cookie) >= sizeof(sock)); sock_num = get_sock_num(sock); memcpy(&cookie, &sock_num, sizeof(sock_num)); if (copy_to_user(m->m_source, &cookie, sizeof(sock), (cp_grant_id_t) m->IO_GRANT, 0) == OK) sock_reply(sock, OK); else sock_reply(sock, EFAULT); }
static int tcp_op_open(struct socket * sock, __unused message * m) { struct tcp_pcb * pcb; int ret; debug_tcp_print("socket num %ld", get_sock_num(sock)); if (!(pcb = tcp_new())) return ENOMEM; debug_tcp_print("new tcp pcb %p\n", pcb); if ((ret = tcp_fill_new_socket(sock, pcb) != OK)) tcp_abandon(pcb, 0); return ret; }
static int udp_op_open(struct socket * sock, __unused message * m) { struct udp_pcb * pcb; debug_udp_print("socket num %ld", get_sock_num(sock)); if (!(pcb = udp_new())) return ENOMEM; sock->buf = NULL; sock->buf_size = 0; sock->pcb = pcb; return OK; }
static int udp_op_close(struct socket * sock) { debug_udp_print("socket num %ld", get_sock_num(sock)); /* deque and free all enqueued data before closing */ sock_dequeue_data_all(sock, udp_recv_free); if (sock->pcb) udp_remove(sock->pcb); assert(sock->buf == NULL); /* mark it as unused */ sock->ops = NULL; return OK; }
static void nic_op_close(struct socket * sock, __unused message * m) { struct nic * nic = (struct nic *)sock->data; debug_drv_print("socket %d", get_sock_num(sock)); sock_dequeue_data_all(sock, raw_recv_free); sock->ops = NULL; if (nic->raw_socket == sock) { nic->raw_socket = NULL; debug_drv_print("no active raw sock at %s", nic->name); } sock_reply_close(sock, OK); }
static void udp_op_close(struct socket * sock, __unused message * m) { debug_udp_print("socket num %ld", get_sock_num(sock)); /* deque and free all enqueued data before closing */ sock_dequeue_data_all(sock, udp_recv_free); if (sock->pcb) udp_remove(sock->pcb); assert(sock->buf == NULL); /* mark it as unused */ sock->ops = NULL; sock_reply_close(sock, OK); }
static void tcp_op_ioctl(struct socket * sock, message * m) { if (!sock->pcb) { sock_reply(sock, ENOTCONN); return; } debug_tcp_print("socket num %ld req %c %d %d", get_sock_num(sock), (m->REQUEST >> 8) & 0xff, m->REQUEST & 0xff, (m->REQUEST >> 16) & _IOCPARM_MASK); switch (m->REQUEST) { case NWIOGTCPCONF: tcp_get_conf(sock, m); break; case NWIOSTCPCONF: tcp_set_conf(sock, m); break; case NWIOTCPCONN: tcp_op_connect(sock); break; case NWIOTCPLISTENQ: tcp_op_listen(sock, m); break; case NWIOGTCPCOOKIE: tcp_op_get_cookie(sock, m); break; case NWIOTCPACCEPTTO: tcp_op_accept(sock, m); break; case NWIOTCPSHUTDOWN: tcp_op_shutdown_tx(sock); break; case NWIOGTCPOPT: tcp_get_opt(sock, m); break; case NWIOSTCPOPT: tcp_set_opt(sock, m); break; default: sock_reply(sock, EBADIOCTL); return; } }
static void nic_ioctl_set_ethopt(struct socket * sock, struct nic * nic, message * m) { int err; nwio_ethopt_t ethopt; assert(nic); if (!sock) { send_reply(m, EINVAL); return; } debug_drv_print("device /dev/%s", nic->name); /* * The device is not up yet, there is nothing to report or it is not * an ethernet device */ if (!nic->netif.flags & NETIF_FLAG_UP || !(nic->netif.flags & (NETIF_FLAG_ETHERNET | NETIF_FLAG_ETHARP))) { send_reply(m, ENODEV); return; } err = copy_from_user(m->m_source, ðopt, sizeof(ethopt), (cp_grant_id_t) m->IO_GRANT, 0); if (err != OK) send_reply(m, err); /* we want to get data from this sock */ if (ethopt.nweo_flags & NWEO_COPY) { if (nic->raw_socket) { send_reply(m, EBUSY); return; } nic->raw_socket = sock; debug_drv_print("active raw sock %d at %s", get_sock_num(sock), nic->name); } send_reply(m, OK); }
static void tcp_op_shutdown_tx(struct socket * sock) { err_t err; debug_tcp_print("socket num %ld", get_sock_num(sock)); err = tcp_shutdown((struct tcp_pcb *) sock->pcb, 0, 1); switch (err) { case ERR_OK: sock_reply(sock, OK); break; case ERR_CONN: sock_reply(sock, ENOTCONN); break; default: sock_reply(sock, EGENERIC); } }
static void tcp_set_opt(struct socket * sock, message * m) { int err; nwio_tcpopt_t tcpopt; struct tcp_pcb * pcb = (struct tcp_pcb *) sock->pcb; debug_tcp_print("socket num %ld", get_sock_num(sock)); assert(pcb); err = copy_from_user(m->m_source, &tcpopt, sizeof(tcpopt), (cp_grant_id_t) m->IO_GRANT, 0); if (err != OK) sock_reply(sock, err); /* FIXME : The userspace library does not use this */ sock_reply(sock, OK); }
static void tcp_set_conf(struct socket * sock, message * m) { int err; nwio_tcpconf_t tconf; struct tcp_pcb * pcb = (struct tcp_pcb *) sock->pcb; debug_tcp_print("socket num %ld", get_sock_num(sock)); assert(pcb); err = copy_from_user(m->m_source, &tconf, sizeof(tconf), (cp_grant_id_t) m->IO_GRANT, 0); if (err != OK) sock_reply(sock, err); debug_tcp_print("tconf.nwtc_flags = 0x%lx", tconf.nwtc_flags); debug_tcp_print("tconf.nwtc_remaddr = 0x%x", (unsigned int) tconf.nwtc_remaddr); debug_tcp_print("tconf.nwtc_remport = 0x%x", ntohs(tconf.nwtc_remport)); debug_tcp_print("tconf.nwtc_locaddr = 0x%x", (unsigned int) tconf.nwtc_locaddr); debug_tcp_print("tconf.nwtc_locport = 0x%x", ntohs(tconf.nwtc_locport)); sock->usr_flags = tconf.nwtc_flags; if (sock->usr_flags & NWTC_SET_RA) pcb->remote_ip.addr = tconf.nwtc_remaddr; if (sock->usr_flags & NWTC_SET_RP) pcb->remote_port = ntohs(tconf.nwtc_remport); if (sock->usr_flags & NWTC_LP_SET) { /* FIXME the user library can only bind to ANY anyway */ if (tcp_bind(pcb, IP_ADDR_ANY, ntohs(tconf.nwtc_locport)) == ERR_USE) { sock_reply(sock, EADDRINUSE); return; } } sock_reply(sock, OK); }