. use <sys/ioccom.h> to make ioctls . use netbsd <sys/ioctl.h>, include minix ioctls . convert to varargs ioctl() like netbsd Change-Id: Id5165780df48172b28bf0003603516d30d7c1abb
		
			
				
	
	
		
			350 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			350 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include <stdlib.h>
 | 
						|
 | 
						|
#include <sys/ioc_net.h>
 | 
						|
#include <net/gen/in.h>
 | 
						|
#include <net/gen/ip_io.h>
 | 
						|
 | 
						|
#include <lwip/raw.h>
 | 
						|
#include <lwip/ip_addr.h>
 | 
						|
 | 
						|
#include <minix/netsock.h>
 | 
						|
#include "proto.h"
 | 
						|
 | 
						|
#define RAW_IP_BUF_SIZE	(32 << 10)
 | 
						|
 | 
						|
#define sock_alloc_buf(s)	debug_malloc(s)
 | 
						|
#define sock_free_buf(x)	debug_free(x)
 | 
						|
 | 
						|
struct raw_ip_recv_data {
 | 
						|
	ip_addr_t	ip;
 | 
						|
	struct pbuf *	pbuf;
 | 
						|
};
 | 
						|
 | 
						|
#define raw_ip_recv_alloc()	debug_malloc(sizeof(struct raw_ip_recv_data))
 | 
						|
 | 
						|
static void raw_ip_recv_free(void * data)
 | 
						|
{
 | 
						|
	if (((struct raw_ip_recv_data *)data)->pbuf)
 | 
						|
		pbuf_free(((struct raw_ip_recv_data *)data)->pbuf);
 | 
						|
	debug_free(data);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int raw_ip_op_open(struct socket * sock)
 | 
						|
{
 | 
						|
	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_close(struct socket * sock)
 | 
						|
{
 | 
						|
	/* deque and free all enqueued data before closing */
 | 
						|
	sock_dequeue_data_all(sock, raw_ip_recv_free);
 | 
						|
 | 
						|
	if (sock->pcb)
 | 
						|
		raw_remove(sock->pcb);
 | 
						|
	if (sock->buf)
 | 
						|
		sock_free_buf(sock->buf);
 | 
						|
 | 
						|
	/* mark it as unused */
 | 
						|
	sock->ops = NULL;
 | 
						|
}
 | 
						|
 | 
						|
static int raw_ip_op_close(struct socket * sock)
 | 
						|
{
 | 
						|
	debug_print("socket num %ld", get_sock_num(sock));
 | 
						|
 | 
						|
	raw_ip_close(sock);
 | 
						|
 | 
						|
	return OK;
 | 
						|
}
 | 
						|
 | 
						|
static int raw_ip_do_receive(struct sock_req *req,
 | 
						|
			struct pbuf *pbuf)
 | 
						|
{
 | 
						|
	struct pbuf * p;
 | 
						|
	size_t rem_len = req->size;
 | 
						|
	unsigned int written = 0, hdr_sz = 0;
 | 
						|
	int err;
 | 
						|
 | 
						|
	debug_print("user buffer size : %u\n", rem_len);
 | 
						|
 | 
						|
	for (p = pbuf; p && rem_len; p = p->next) {
 | 
						|
		size_t cp_len;
 | 
						|
 | 
						|
		cp_len = (rem_len < p->len) ? rem_len : p->len;
 | 
						|
		err = copy_to_user(req->endpt, p->payload, cp_len, req->grant,
 | 
						|
				hdr_sz + written);
 | 
						|
 | 
						|
		if (err != OK)
 | 
						|
			return err;
 | 
						|
 | 
						|
		written += cp_len;
 | 
						|
		rem_len -= cp_len;
 | 
						|
	}
 | 
						|
 | 
						|
	debug_print("copied %d bytes\n", written + hdr_sz);
 | 
						|
	return written + hdr_sz;
 | 
						|
}
 | 
						|
 | 
						|
static u8_t raw_ip_op_receive(void *arg,
 | 
						|
			__unused struct raw_pcb *pcb,
 | 
						|
			struct pbuf *pbuf,
 | 
						|
			ip_addr_t *addr)
 | 
						|
{
 | 
						|
	struct socket * sock = (struct socket *) arg;
 | 
						|
	struct raw_ip_recv_data * data;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	debug_print("socket num : %ld addr : %x\n",
 | 
						|
			get_sock_num(sock), (unsigned int) addr->addr);
 | 
						|
 | 
						|
	if (sock->flags & SOCK_FLG_OP_PENDING) {
 | 
						|
		/* we are resuming a suspended operation */
 | 
						|
		ret = raw_ip_do_receive(&sock->req, pbuf);
 | 
						|
 | 
						|
		send_req_reply(&sock->req, ret);
 | 
						|
		sock->flags &= ~SOCK_FLG_OP_PENDING;
 | 
						|
 | 
						|
		if (ret > 0) {
 | 
						|
			if (sock->usr_flags & NWIO_EXCL) {
 | 
						|
				pbuf_free(pbuf);
 | 
						|
				return 1;
 | 
						|
			} else
 | 
						|
				return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Do not enqueue more data than allowed */
 | 
						|
	if (sock->recv_data_size > RAW_IP_BUF_SIZE)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * nobody is waiting for the data or an error occured above, we enqueue
 | 
						|
	 * the packet
 | 
						|
	 */
 | 
						|
	if (!(data = raw_ip_recv_alloc())) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	data->ip = *addr;
 | 
						|
	if (sock->usr_flags & NWIO_EXCL) {
 | 
						|
		data->pbuf = pbuf;
 | 
						|
		ret = 1;
 | 
						|
	} else {
 | 
						|
		/* we store a copy of this packet */
 | 
						|
		data->pbuf = pbuf_alloc(PBUF_RAW, pbuf->tot_len, PBUF_RAM);
 | 
						|
		if (data->pbuf == NULL) {
 | 
						|
			debug_print("LWIP : cannot allocated new pbuf\n");
 | 
						|
			raw_ip_recv_free(data);
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
 | 
						|
		if (pbuf_copy(data->pbuf, pbuf) != ERR_OK) {
 | 
						|
			debug_print("LWIP : cannot copy pbuf\n");
 | 
						|
			raw_ip_recv_free(data);
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
 | 
						|
		ret = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If we didn't managed to enqueue the packet we report it as not
 | 
						|
	 * consumed
 | 
						|
	 */
 | 
						|
	if (sock_enqueue_data(sock, data, data->pbuf->tot_len) != OK) {
 | 
						|
		raw_ip_recv_free(data);
 | 
						|
		ret = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int raw_ip_op_read(struct socket * sock, struct sock_req * req, int blk)
 | 
						|
{
 | 
						|
	debug_print("socket num %ld", get_sock_num(sock));
 | 
						|
 | 
						|
	if (sock->pcb == NULL)
 | 
						|
		return EIO;
 | 
						|
 | 
						|
	if (sock->recv_head) {
 | 
						|
		/* data available receive immeditely */
 | 
						|
 | 
						|
		struct raw_ip_recv_data * data;
 | 
						|
		int ret;
 | 
						|
 | 
						|
		data = (struct raw_ip_recv_data *) sock->recv_head->data;
 | 
						|
 | 
						|
		ret = raw_ip_do_receive(req, data->pbuf);
 | 
						|
 | 
						|
		if (ret > 0) {
 | 
						|
			sock_dequeue_data(sock);
 | 
						|
			sock->recv_data_size -= data->pbuf->tot_len;
 | 
						|
			raw_ip_recv_free(data);
 | 
						|
		}
 | 
						|
		return ret;
 | 
						|
	} else if (!blk)
 | 
						|
		return EAGAIN;
 | 
						|
	else {
 | 
						|
		/* store the request so we know how to reply */
 | 
						|
		sock->req = *req;
 | 
						|
		/* operation is being processes */
 | 
						|
		sock->flags |= SOCK_FLG_OP_PENDING;
 | 
						|
 | 
						|
		debug_print("no data to read, suspending");
 | 
						|
		return EDONTREPLY;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int raw_ip_op_write(struct socket * sock, struct sock_req * req,
 | 
						|
	__unused int blk)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	struct pbuf * pbuf;
 | 
						|
	struct ip_hdr * ip_hdr;
 | 
						|
 | 
						|
	debug_print("socket num %ld data size %u",
 | 
						|
			get_sock_num(sock), req->size);
 | 
						|
 | 
						|
	if (sock->pcb == NULL)
 | 
						|
		return EIO;
 | 
						|
 | 
						|
	if (req->size > sock->buf_size)
 | 
						|
		return ENOMEM;
 | 
						|
 | 
						|
	pbuf = pbuf_alloc(PBUF_LINK, req->size, PBUF_RAM);
 | 
						|
	if (!pbuf)
 | 
						|
		return ENOMEM;
 | 
						|
 | 
						|
	if ((ret = copy_from_user(req->endpt, pbuf->payload, req->size,
 | 
						|
				  req->grant, 0)) != OK) {
 | 
						|
		pbuf_free(pbuf);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	ip_hdr = (struct ip_hdr *) pbuf->payload;
 | 
						|
	if (pbuf_header(pbuf, -IP_HLEN)) {
 | 
						|
		pbuf_free(pbuf);
 | 
						|
		return EIO;
 | 
						|
	}
 | 
						|
 | 
						|
	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 = req->size;
 | 
						|
	
 | 
						|
 | 
						|
	pbuf_free(pbuf);
 | 
						|
	
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int raw_ip_set_opt(struct socket * sock, endpoint_t endpt,
 | 
						|
	cp_grant_id_t grant)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
	nwio_ipopt_t ipopt;
 | 
						|
	struct raw_pcb * pcb;
 | 
						|
 | 
						|
	err = copy_from_user(endpt, &ipopt, sizeof(ipopt), grant, 0);
 | 
						|
 | 
						|
	if (err != OK)
 | 
						|
		return err;
 | 
						|
 | 
						|
	debug_print("ipopt.nwio_flags = 0x%x", ipopt.nwio_flags);
 | 
						|
	debug_print("ipopt.nwio_proto = 0x%x", ipopt.nwio_proto);
 | 
						|
	debug_print("ipopt.nwio_rem = 0x%x",
 | 
						|
				(unsigned int) ipopt.nwio_rem);
 | 
						|
 | 
						|
	if (sock->pcb == NULL) {
 | 
						|
		if (!(pcb = raw_new(ipopt.nwio_proto))) {
 | 
						|
			raw_ip_close(sock);
 | 
						|
			return ENOMEM;
 | 
						|
		}
 | 
						|
 | 
						|
		sock->pcb = pcb;
 | 
						|
	} else
 | 
						|
		pcb = (struct raw_pcb *) sock->pcb;
 | 
						|
 | 
						|
	if (pcb->protocol != ipopt.nwio_proto) {
 | 
						|
		debug_print("conflicting ip socket protocols\n");
 | 
						|
		return EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	sock->usr_flags = ipopt.nwio_flags;
 | 
						|
 | 
						|
#if 0
 | 
						|
	if (raw_bind(pcb, (ip_addr_t *)&ipopt.nwio_rem) == ERR_USE) {
 | 
						|
		raw_ip_close(sock);
 | 
						|
		return EADDRINUSE;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* register a receive hook */
 | 
						|
	raw_recv((struct raw_pcb *) sock->pcb, raw_ip_op_receive, sock);
 | 
						|
 | 
						|
	return OK;
 | 
						|
}
 | 
						|
 | 
						|
static int raw_ip_get_opt(struct socket * sock, endpoint_t endpt,
 | 
						|
	cp_grant_id_t grant)
 | 
						|
{
 | 
						|
	nwio_ipopt_t ipopt;
 | 
						|
	struct raw_pcb * pcb = (struct raw_pcb *) sock->pcb;
 | 
						|
 | 
						|
	assert(pcb);
 | 
						|
 | 
						|
	ipopt.nwio_rem = pcb->remote_ip.addr;
 | 
						|
	ipopt.nwio_flags = sock->usr_flags;
 | 
						|
 | 
						|
	return copy_to_user(endpt, &ipopt, sizeof(ipopt), grant, 0);
 | 
						|
}
 | 
						|
 | 
						|
static int raw_ip_op_ioctl(struct socket * sock, struct sock_req * req,
 | 
						|
	__unused int blk)
 | 
						|
{
 | 
						|
	int r;
 | 
						|
 | 
						|
	debug_print("socket num %ld req %c %ld %ld",
 | 
						|
			get_sock_num(sock),
 | 
						|
			(unsigned char) (req->req >> 8),
 | 
						|
			req->req & 0xff,
 | 
						|
			_MINIX_IOCTL_SIZE(req->req));
 | 
						|
	
 | 
						|
	switch (req->req) {
 | 
						|
	case NWIOSIPOPT:
 | 
						|
		r = raw_ip_set_opt(sock, req->endpt, req->grant);
 | 
						|
		break;
 | 
						|
	case NWIOGIPOPT:
 | 
						|
		r = raw_ip_get_opt(sock, req->endpt, req->grant);
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		/*
 | 
						|
		 * /dev/ip can be also accessed as a default device to be
 | 
						|
		 * configured
 | 
						|
		 */
 | 
						|
		r = nic_default_ioctl(req);
 | 
						|
	}
 | 
						|
 | 
						|
	return r;
 | 
						|
}
 | 
						|
 | 
						|
struct sock_ops sock_raw_ip_ops = {
 | 
						|
	.open		= raw_ip_op_open,
 | 
						|
	.close		= raw_ip_op_close,
 | 
						|
	.read		= raw_ip_op_read,
 | 
						|
	.write		= raw_ip_op_write,
 | 
						|
	.ioctl		= raw_ip_op_ioctl,
 | 
						|
	.select		= generic_op_select,
 | 
						|
	.select_reply	= generic_op_select_reply
 | 
						|
};
 |