Goto sanos source index
//
// rawsock.c
//
// Raw socket interface
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <net/net.h>
static err_t recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *addr) {
struct socket *s = arg;
struct sockreq *req = s->waithead;
struct sockaddr_in *sin;
int rc;
if (req) {
rc = write_iovec(req->msg->msg_iov, req->msg->msg_iovlen, p->payload, p->len);
if (rc < p->len) rc = -EMSGSIZE;
if (req->msg->msg_name) {
if (req->msg->msg_namelen < sizeof(struct sockaddr_in)) {
rc = -EFAULT;
} else {
sin = (struct sockaddr_in *) req->msg->msg_name;
sin->sin_family = AF_INET;
sin->sin_port = 0;
sin->sin_addr.s_addr = addr->addr;
}
}
req->msg->msg_namelen = sizeof(struct sockaddr_in);
pbuf_free(p);
release_socket_request(req, rc);
} else {
if (p->next) {
kprintf("recv_raw: fragmented pbuf not supported\n");
return -EINVAL;
}
if (s->raw.recvtail) {
pbuf_chain(s->raw.recvtail, p);
s->raw.recvtail = p;
} else {
s->raw.recvhead = p;
s->raw.recvtail = p;
}
set_io_event(&s->iob, IOEVT_READ);
}
return 1;
}
static int rawsock_accept(struct socket *s, struct sockaddr *addr, int *addrlen, struct socket **retval) {
return -EINVAL;
}
static int rawsock_bind(struct socket *s, struct sockaddr *name, int namelen) {
int rc;
struct sockaddr_in *sin;
if (!name) return -EFAULT;
if (namelen < sizeof(struct sockaddr_in)) return -EFAULT;
sin = (struct sockaddr_in *) name;
if (sin->sin_family != AF_INET && sin->sin_family != AF_UNSPEC) return -EAFNOSUPPORT;
rc = raw_bind(s->raw.pcb, (struct ip_addr *) &sin->sin_addr);
if (rc < 0) return rc;
s->state = SOCKSTATE_BOUND;
return 0;
}
static int rawsock_close(struct socket *s) {
struct sockreq *req;
struct sockreq *next;
s->state = SOCKSTATE_CLOSED;
req = s->waithead;
while (req) {
next = req->next;
release_socket_request(req, -EABORT);
req = next;
}
if (s->raw.pcb) {
s->raw.pcb->recv_arg = NULL;
raw_remove(s->raw.pcb);
}
if (s->raw.recvhead) pbuf_free(s->raw.recvhead);
return 0;
}
static int rawsock_connect(struct socket *s, struct sockaddr *name, int namelen) {
int rc;
struct sockaddr_in *sin;
if (!name) return -EFAULT;
if (namelen < sizeof(struct sockaddr_in)) return -EFAULT;
sin = (struct sockaddr_in *) name;
if (sin->sin_family != AF_INET && sin->sin_family != AF_UNSPEC) return -EAFNOSUPPORT;
if (s->state == SOCKSTATE_CLOSED) return -EINVAL;
rc = raw_connect(s->raw.pcb, (struct ip_addr *) &sin->sin_addr);
if (rc < 0) return rc;
s->state = SOCKSTATE_CONNECTED;
return 0;
}
static int rawsock_getpeername(struct socket *s, struct sockaddr *name, int *namelen) {
struct sockaddr_in *sin;
if (!namelen) return -EFAULT;
if (*namelen < sizeof(struct sockaddr_in)) return -EFAULT;
if (s->state != SOCKSTATE_CONNECTED) return -EINVAL;
sin = (struct sockaddr_in *) name;
sin->sin_family = AF_INET;
sin->sin_port = 0;
sin->sin_addr.s_addr = s->raw.pcb->remote_ip.addr;
*namelen = sizeof(struct sockaddr_in);
return 0;
}
static int rawsock_getsockname(struct socket *s, struct sockaddr *name, int *namelen) {
struct sockaddr_in *sin;
if (!namelen) return -EFAULT;
if (*namelen < sizeof(struct sockaddr_in)) return -EFAULT;
if (s->state != SOCKSTATE_BOUND && s->state != SOCKSTATE_CONNECTED) return -EINVAL;
sin = (struct sockaddr_in *) name;
sin->sin_family = AF_INET;
sin->sin_port = 0;
sin->sin_addr.s_addr = s->raw.pcb->local_ip.addr;
*namelen = sizeof(struct sockaddr_in);
return 0;
}
static int rawsock_getsockopt(struct socket *s, int level, int optname, void *optval, int *optlen) {
return -ENOSYS;
}
static int rawsock_ioctl(struct socket *s, int cmd, void *data, size_t size) {
switch (cmd) {
case FIONBIO:
if (!data || size != 4) return -EFAULT;
if (*(int *) data) {
s->flags |= SOCK_NBIO;
} else {
s->flags &= ~SOCK_NBIO;
}
break;
default:
return -ENOSYS;
}
return 0;
}
static int rawsock_listen(struct socket *s, int backlog) {
return -EINVAL;
}
static int rawsock_recvmsg(struct socket *s, struct msghdr *msg, unsigned int flags) {
struct pbuf *p;
struct ip_hdr *iphdr;
int rc;
struct sockaddr_in *sin;
struct sockreq req;
p = s->raw.recvhead;
if (p) {
s->raw.recvhead = pbuf_dechain(p);
if (!s->raw.recvhead) s->raw.recvtail = NULL;
if (!s->raw.recvhead) clear_io_event(&s->iob, IOEVT_READ);
iphdr = p->payload;
rc = write_iovec(msg->msg_iov, msg->msg_iovlen, p->payload, p->len);
if (rc < p->len) rc = -EMSGSIZE;
if (msg->msg_name) {
if (msg->msg_namelen < sizeof(struct sockaddr_in)) return -EFAULT;
sin = (struct sockaddr_in *) msg->msg_name;
sin->sin_family = AF_INET;
sin->sin_port = 0;
sin->sin_addr.s_addr = iphdr->src.addr;
}
msg->msg_namelen = sizeof(struct sockaddr_in);
pbuf_free(p);
} else if (s->flags & SOCK_NBIO) {
rc = -EAGAIN;
} else {
rc = submit_socket_request(s, &req, SOCKREQ_RECV, msg, s->rcvtimeo);
}
return rc;
}
static int rawsock_sendmsg(struct socket *s, struct msghdr *msg, unsigned int flags) {
struct pbuf *p;
int size;
int rc;
size = get_iovec_size(msg->msg_iov, msg->msg_iovlen);
if (msg->msg_name) {
rc = rawsock_connect(s, msg->msg_name, msg->msg_namelen);
if (rc < 0) return rc;
}
if (s->state != SOCKSTATE_CONNECTED) return -ENOTCONN;
p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RW);
if (!p) return -ENOMEM;
rc = read_iovec(msg->msg_iov, msg->msg_iovlen, p->payload, size);
if (rc < 0) return rc;
rc = raw_send(s->raw.pcb, p);
if (rc < 0) {
pbuf_free(p);
return rc;
}
return size;
}
static int rawsock_setsockopt(struct socket *s, int level, int optname, const void *optval, int optlen) {
if (level == SOL_SOCKET)
{
switch (optname) {
case SO_SNDTIMEO:
if (optlen != 4) return -EINVAL;
s->sndtimeo = *(unsigned int *) optval;
break;
case SO_RCVTIMEO:
if (optlen != 4) return -EINVAL;
s->rcvtimeo = *(unsigned int *) optval;
break;
default:
return -ENOPROTOOPT;
}
} else {
return -ENOPROTOOPT;
}
return 0;
}
static int rawsock_shutdown(struct socket *s, int how) {
return -ENOSYS;
}
static int rawsock_socket(struct socket *s, int domain, int type, int protocol) {
s->raw.pcb = raw_new(protocol);
if (!s->raw.pcb) return -ENOMEM;
raw_recv(s->raw.pcb, recv_raw, s);
set_io_event(&s->iob, IOEVT_WRITE);
return 0;
}
struct sockops rawops = {
rawsock_accept,
rawsock_bind,
rawsock_close,
rawsock_connect,
rawsock_getpeername,
rawsock_getsockname,
rawsock_getsockopt,
rawsock_ioctl,
rawsock_listen,
rawsock_recvmsg,
rawsock_sendmsg,
rawsock_setsockopt,
rawsock_shutdown,
rawsock_socket,
};