Beispiel #1
0
static term_t ol_tube_control(outlet_t *ol,
		uint32_t op, uint8_t *data, int dlen, term_t reply_to, heap_t *hp)
{
	uint8_t rbuf[256];
	uint8_t *reply = rbuf;

	if (op == TUBE_REQ_OPEN)
	{
		// peer_domid[4]
		if (dlen != 4)
			goto error;
		uint32_t peer_domid = GET_UINT_32(data);
		assert(ol->tube == 0);
		ol->tube = tube_make(peer_domid, (void *)ol);
		if (ol->tube == 0)
			return A_NO_MEMORY;
		// page_ref[4] evtchn_tx[4] evtchn_rx[4]
		*reply++ = TUBE_REP_OK;
		uint32_t page_ref, evtchn_tx, evtchn_rx;
		tube_info(ol->tube, &page_ref, &evtchn_tx, &evtchn_rx);
		PUT_UINT_32(reply, page_ref);
		reply += 4;
		PUT_UINT_32(reply, evtchn_tx);
		reply += 4;
		PUT_UINT_32(reply, evtchn_rx);
		reply += 4;
	}
	else if (op == TUBE_REQ_ATTACH)
	{
		// peer_domid[4] page_ref[4] evtchn_tx[4] evtchn_rx[4]
		if (dlen != 4 +4 +4 +4)
			goto error;
		uint32_t peer_domid = GET_UINT_32(data);
		uint32_t page_ref   = GET_UINT_32(data +4);
		uint32_t evtchn_tx  = GET_UINT_32(data +8);
		uint32_t evtchn_rx  = GET_UINT_32(data +12);
		assert(ol->tube == 0);
		ol->tube = tube_attach(peer_domid, page_ref, evtchn_rx, evtchn_tx, (void *)ol);
		if (ol->tube == 0)
			return A_NO_MEMORY;
		*reply++ = TUBE_REP_OK;
	}
	else if (op == TUBE_REQ_SEND_SLOTS)
	{
		if (dlen != 0)
			goto error;
		avail_send_slots(ol, reply_to);
		*reply++ = TUBE_REP_OK;
	}
	else
	{
error:
		*reply++ = TUBE_REP_ERROR;
	}

	int rlen = reply-rbuf;
	assert(rlen >= 1 && rlen <= sizeof(rbuf));
	term_t result = heap_str_N(hp, (char *)rbuf, rlen);
	return (result == noval) ?A_NO_MEMORY :result;
}
Beispiel #2
0
term_t cbif_rand_bytes1(proc_t *proc, term_t *regs)
{
	term_t N = regs[0];
	if (!is_int(N) || int_value(N) < 0)
		badarg(N);
	int len = int_value(N);

	uint8_t *ptr;
	term_t bin = heap_make_bin(&proc->hp, len, &ptr);
	uint8_t *p = ptr;
	while (p <= ptr +len -4)
	{
		uint32_t rnd = mt_lrand();
		PUT_UINT_32(p, rnd);
		p += 4;
	}
	uint32_t last = mt_lrand();
	switch(ptr +len -p)
	{
	case 3:
		*p++ = (uint8_t)(last >> 16);
	case 2:
		*p++ = (uint8_t)(last >> 8);
	case 1:
		*p++ = (uint8_t)last;
	case 0:
		break;
	}

	return bin;
}
Beispiel #3
0
static int ol_tcp_send(outlet_t *ol, int len, term_t reply_to)
{
	assert(ol->send_in_progress == 0);
	assert(ol->send_buf_left == 0);
	//debug("ol_tcp_send: len %d\n", len);
	// TCP_PB_1
	// TCP_PB_2
	// TCP_PB_4
	
	int buf_len = len;
	switch (ol->packet)
	{
	case TCP_PB_1:
		if (len < 0 || len > 255)
			return -BAD_ARG;
		ol->send_buffer[0] = len;
		buf_len = len +1;
		break;
	case TCP_PB_2:
		if (len < 0 || len > 65535)
			return -BAD_ARG;
		PUT_UINT_16(ol->send_buffer, len);
		buf_len = len +2;
		break;
	case TCP_PB_4:
		if (len < 0)
			return -BAD_ARG;
		PUT_UINT_32(ol->send_buffer, len);
		buf_len = len +4;
		break;
	}

	assert(buf_len <= ol->max_send_bufsize);

	ol->send_buf_left = buf_len;
	uint16_t write_len = (buf_len > TCP_SND_BUF)
		?TCP_SND_BUF
		:buf_len;
	ol->send_buf_ack = 0;
	ol->send_buf_off = write_len;

	//debug("ol_tcp_send: tcp_write(%d)\n", write_len);
	int rc = tcp_write(ol->tcp, ol->send_buffer, write_len, TCP_WRITE_FLAG_COPY);
	if (rc != ERR_OK)
	{
		//debug("ol_tcp_send: tcp_write() returns error %d\n", rc);
		inet_reply_error(ol->oid, reply_to, lwip_err_to_term(rc));
		return 0;
	}

	// Otherwise, the data are buffered until the next tcp_tmr timeout
	tcp_output(ol->tcp);

	// may set a timeout
	send_defer_reply(ol, reply_to);
	return 0;
}
Beispiel #4
0
static int ol_tcp_acc_get_opts(outlet_t *ol,
					uint8_t *data, int dlen, char *buf, int sz)
{
	uint8_t *p = data;
	char *q = buf;
	int left = sz;

	while (p < data +dlen)
	{
		int opt = *p++;
		uint32_t val;

		debug("%s(opt=%d)\n", __FUNCTION__, opt);
		switch (opt)
		{
		case INET_OPT_PRIORITY:
			//
			// See comment in ol_tcp_animate()
			//
#if LING_WITH_LWIP
			val = ol->tcp->prio;
#elif LING_WITH_LIBUV
			val = 0;
#endif
			break;

		case INET_OPT_TOS:
#if LING_WITH_LWIP
			val = ol->tcp->tos;
#elif LING_WITH_LIBUV
			val = 0;
#endif
			break;

		case TCP_OPT_NODELAY:
			val = tcp_get_nodelay(ol);
			break;

		default:
			if (inet_get_opt(ol, opt, &val) < 0)
				return -BAD_ARG;
		}

		if (left < 1 +4)
			return -TOO_LONG;
		*q++ = opt;
		left--;
		PUT_UINT_32(q, val);
		q += 4;
		left -= 4;
	}

	return q -buf;
}
Beispiel #5
0
static int ol_tcp_get_opts(outlet_t *ol,
					uint8_t *data, int dlen, char *buf, int sz)
{
	uint8_t *p = data;
	char *q = buf;
	int left = sz;

	while (p < data +dlen)
	{
		int opt = *p++;
		uint32_t val;

		switch (opt)
		{
		case INET_OPT_RCVBUF:
			val = ol->recv_bufsize;
			break;

		case INET_OPT_PRIORITY:
			//
			// See comment in ol_tcp_animate()
			//
			val = ol->tcp->prio;
			break;

		case INET_OPT_TOS:
			val = ol->tcp->tos;
			break;

		case TCP_OPT_NODELAY:
			val = tcp_nagle_disabled(ol->tcp);
			break;

		default:
			if (inet_get_opt(ol, opt, &val) < 0)
				return -BAD_ARG;
		}

		if (left < 1 +4)
			return -TOO_LONG;
		*q++ = opt;
		left--;
		PUT_UINT_32(q, val);
		q += 4;
		left -= 4;
	}

	return q -buf;
}
Beispiel #6
0
static term_t ol_tcp_control(outlet_t *ol,
		uint32_t op, uint8_t *data, int dlen, term_t reply_to, heap_t *hp)
{
	char rbuf[256];
	char *reply = rbuf;
	int sz;

	assert(ol != 0);
	assert(ol->tcp != 0 || op == INET_REQ_OPEN || op == INET_REQ_SUBSCRIBE);

	switch (op)
	{
	case INET_REQ_OPEN:
	{
		if (dlen != 2 || data[1] != INET_TYPE_STREAM)
			goto error;
		uint8_t family = data[0];
		if (family != INET_AF_INET && family != INET_AF_INET6)
			goto error;
		assert(ol->tcp == 0);

#if LWIP_IPV6
		ol->tcp = (family == INET_AF_INET6)
			?tcp_new_ip6()
			:tcp_new();
#else
		if (family != INET_AF_INET)
			goto error;
		ol->tcp = tcp_new();
#endif
		assert(ol->tcp != 0);

		// see comment in ol_tcp_animate()
		tcp_setprio(ol->tcp, TCP_PRIO_MAX +1);

		tcp_arg(ol->tcp, ol);	// callback arg
		tcp_recv(ol->tcp, recv_cb);
		tcp_sent(ol->tcp, sent_cb);
		tcp_err(ol->tcp, error_cb);

		*reply++ = INET_REP_OK;
	}
	break;

	case INET_REQ_CONNECT:
	{
		int is_ipv6 = PCB_ISIPV6(ol->tcp);
		if ((is_ipv6 && dlen != 4 +2 +16) || (!is_ipv6 && dlen != 4 +2 +4))
			goto error;

		uint32_t timeout = GET_UINT_32(data);
		uint16_t remote_port = GET_UINT_16(data +4);
	
		err_t err;
		if (!is_ipv6)
		{
			ip_addr_t where_to;
			where_to.addr = ntohl(GET_UINT_32(data +4 +2));
			err = tcp_connect(ol->tcp, &where_to, remote_port, connected_cb);
		}
		else
		{
#if LWIP_IPV6
			ip6_addr_t where_to;
			where_to.addr[0] = ntohl(GET_UINT_32(data +4 +2));
			where_to.addr[1] = ntohl(GET_UINT_32(data +4 +2 +4));
			where_to.addr[2] = ntohl(GET_UINT_32(data +4 +2 +8));
			where_to.addr[3] = ntohl(GET_UINT_32(data +4 +2 +12));
			err = tcp_connect_ip6(ol->tcp, &where_to, remote_port, connected_cb);
#else
			goto error;
#endif
		}

		// Does it make connections faster?
		tcp_output(ol->tcp);

		if (err == ERR_OK)
		{
			cr_defer_reply(ol, reply_to, timeout);

			*reply++ = INET_REP_OK;
			uint16_t ref = ASYNC_REF;	// Why this is needed? A constant will do.
			PUT_UINT_16(reply, ref);
			reply += 2;
		}
		else
		{
			//
			//TODO: ERR_RTE possible too (IPv6)
			//
			assert(err == ERR_MEM);
			REPLY_INET_ERROR("enomem");
		}
	}
	break;

	case INET_REQ_PEER:
	if (ol->tcp->state == CLOSED)
		REPLY_INET_ERROR("enotconn");
	else
	{
		*reply++ = INET_REP_OK;
		*reply++ = INET_AF_INET;
		uint16_t peer_port = ol->tcp->remote_port;
		PUT_UINT_16(reply, peer_port);
		reply += 2;
		if (PCB_ISIPV6(ol->tcp))
		{
			ip_addr_set_hton((ip_addr_t *)reply, (ip_addr_t *)&ol->tcp->remote_ip);
			reply += 4;
		}
		else
		{
#if LWIP_IPV6
			ip6_addr_set_hton((ip6_addr_t *)reply, (ip6_addr_t *)&ol->tcp->remote_ip);
			reply += 16;
#else
			goto error;
#endif
		}
	}
	break;

	case INET_REQ_NAME:
	if (ol->tcp->state == CLOSED)
		REPLY_INET_ERROR("enotconn");
	else
	{
		*reply++ = INET_REP_OK;
		int is_ipv6 = PCB_ISIPV6(ol->tcp);
		*reply++ = (is_ipv6) ?INET_AF_INET6 :INET_AF_INET;
		uint16_t name_port = ol->tcp->local_port;
		PUT_UINT_16(reply, name_port);
		reply += 2;
		if (PCB_ISIPV6(ol->tcp))
		{
			ip_addr_set_hton((ip_addr_t *)reply, (ip_addr_t *)&ol->tcp->local_ip);
			reply += 4;
		}
		else
		{
#if LWIP_IPV6
			ip6_addr_set_hton((ip6_addr_t *)reply, (ip6_addr_t *)&ol->tcp->local_ip);
			reply += 16;
#else
			goto error;
#endif
		}
	}
	break;

	case INET_REQ_BIND:
	{
		int is_ipv6 = PCB_ISIPV6(ol->tcp);
		if ((is_ipv6 && dlen != 2 +16) || (!is_ipv6 && dlen != 2 +4))
			goto error;
		uint16_t port = GET_UINT_16(data);
		if (!is_ipv6)
		{
			ip_addr_t addr;
			addr.addr = ntohl(GET_UINT_32(data +2));
			tcp_bind(ol->tcp, &addr, port); // always succeeds
		}
		else
		{
#if LWIP_IPV6
			ip6_addr_t addr;
			addr.addr[0] = ntohl(GET_UINT_32(data +2));
			addr.addr[1] = ntohl(GET_UINT_32(data +2 +4));
			addr.addr[2] = ntohl(GET_UINT_32(data +2 +8));
			addr.addr[3] = ntohl(GET_UINT_32(data +2 +12));
			tcp_bind_ip6(ol->tcp, &addr, port); // always succeeds
#else
			goto error;
#endif
		}

		uint16_t local_port = ol->tcp->local_port;
		*reply++ = INET_REP_OK;
		PUT_UINT_16(reply, local_port);
		reply += 2;
	}
	break;

	case INET_REQ_LISTEN:
	{
		assert(ol->recv_buf_node == 0);	// or use destroy_private()
		int backlog = GET_UINT_16(data);
		ol_tcp_acc_promote(ol, ol->tcp, backlog);
		*reply++ = INET_REP_OK;
	}
	break;

	case INET_REQ_SETOPTS:
	if (ol_tcp_set_opts(ol, data, dlen) < 0)
		goto error;

	*reply++ = INET_REP_OK;
	break;

	case INET_REQ_GETOPTS:
	sz = ol_tcp_get_opts(ol, data, dlen, rbuf+1, sizeof(rbuf) -1);
	if (sz < 0)
		goto error;

	*reply++ = INET_REP_OK;
	reply += sz;
	break;

	case INET_REQ_GETSTAT:
	//
	// lwIP can provide some of the statistics but not all
	//
	REPLY_INET_ERROR("enotsup");
	break;

	case INET_REQ_SUBSCRIBE:
	if (dlen != 1 && data[0] != INET_SUBS_EMPTY_OUT_Q)
		goto error;
	if (ol->empty_queue_in_progress)
		goto error;		//TODO: allow multiple subscriptions

	int qlen = tcp_sndqueuelen(ol->tcp);
	if (qlen > 0)
	{
		ol->empty_queue_in_progress = 1;
		ol->empty_queue_reply_to = reply_to;
	}

	*reply++ = INET_REP_OK;
	*reply++ = INET_SUBS_EMPTY_OUT_Q;
	PUT_UINT_32(reply, qlen);
	reply += 4;
	break;

	case TCP_REQ_RECV:
	if (dlen != 4 +4)
		goto error;

	uint32_t msecs = GET_UINT_32(data);
	uint32_t recv_num = GET_UINT_32(data +4);

	if (ol->active != INET_PASSIVE)
		goto error;
	if (ol->packet == TCP_PB_RAW && recv_num > ol->recv_bufsize)
		goto error;
	
	if (ol->peer_close_detected)
		inet_async_error(ol->oid, reply_to, ASYNC_REF, A_CLOSED);
	else
	{
		cr_defer_reply(ol, reply_to, msecs);

		if (ol->packet == TCP_PB_RAW)
			ol->recv_expected_size = recv_num;

		// Enough data may have already been buffered
		proc_t *cont_proc = scheduler_lookup(reply_to);
		assert(cont_proc != 0);
		if (recv_bake_packets(ol, cont_proc) < 0)
			goto error;
	}

	*reply++ = INET_REP_OK;
	uint16_t my_ref = ASYNC_REF;
	PUT_UINT_16(reply, my_ref);
	reply += 2;
	break;

	case TCP_REQ_SHUTDOWN:
	if (dlen != 1)
		goto error;

	uint8_t what = data[0];
	// 0 - read
	// 1 - write
	// 2 - read_write
	
	int shut_rx = (what == 0) || (what == 2);
	int shut_tx = (what == 1) || (what == 2);

	if (ol->tcp->state == LISTEN)
		REPLY_INET_ERROR("enotconn");
	else
	{
		tcp_shutdown(ol->tcp, shut_rx, shut_tx);
		// TODO: return code ignored

		*reply++ = INET_REP_OK;
	}
	break;

	default:
error:
	REPLY_INET_ERROR("einval");
	}

	int rlen = reply -rbuf;
	assert(rlen >= 1 && rlen <= sizeof(rbuf));
	term_t result = heap_str_N(hp, rbuf, rlen);
	if (result == noval)
		return A_NO_MEMORY;

	return result;
}
Beispiel #7
0
int build_getifaddrs_reply(char *buf, int sz)
{
	char *p = buf;
	more(1);
	*p++ = INET_REP_OK;

	// loopback interface
	more(3);
	*p++ = 'l'; *p++ = 'o';
	*p++ = 0;
	more(1 +4);
	*p++ = INET_IFOPT_FLAGS;
	uint32_t loflags = INET_IFF_UP | INET_IFF_LOOPBACK | INET_IFF_RUNNING;
	PUT_UINT_32(p, loflags);
	p += 4;
	more(1 +1 +4);
	*p++ = INET_IFOPT_ADDR;
	*p++ = INET_AF_INET;
	*p++ = 127; *p++ = 0; *p++ = 0; *p++ = 1;
	more(1 +1 +4);
	*p++ = INET_IFOPT_NETMASK;
	*p++ = INET_AF_INET;
	*p++ = 255; *p++ = 0; *p++ = 0; *p++ = 0;
	more(1);
	*p++ = 0; 	// end of 'lo' options

	// network front ends
	netfe_t *fe = net_front_ends;
	while (fe != 0)
	{
		// ethNN
		more(3);
		*p++ = 'e'; *p++ = 't'; *p++ = 'h';
		int n = fe->index;
		assert(n >= 0);
		do {
			more(1);
			*p++ = (n % 10) +'0';
			n /= 10;
		} while (n > 0);
		more(1);
		*p++ = 0;

		more(1 +4);
		*p++ = INET_IFOPT_FLAGS;
		uint32_t ethflags = INET_IFF_UP | INET_IFF_BROADCAST | INET_IFF_RUNNING | INET_IFF_MULTICAST;
		PUT_UINT_32(p, ethflags);
		p += 4;

		more(1 +2 +6);
		*p++ = INET_IFOPT_HWADDR;
		PUT_UINT_16(p, fe->mac_len);
		p += 2;
		memcpy(p, fe->mac, fe->mac_len);
		p += fe->mac_len;

		if (fe->attached_lwip_netif)
		{
			// can happen for eth0 only
			struct netif *nf = fe->attached_lwip_netif;
			if (!ip_addr_isany(&nf->ip_addr))
			{
				more(1 +1 +4);
				*p++ = INET_IFOPT_ADDR;
				*p++ = INET_AF_INET;
				*p++ = ip4_addr1(&nf->ip_addr);
				*p++ = ip4_addr2(&nf->ip_addr);
				*p++ = ip4_addr3(&nf->ip_addr);
				*p++ = ip4_addr4(&nf->ip_addr);
				more(1 +1 +4);
				*p++ = INET_IFOPT_NETMASK;
				*p++ = INET_AF_INET;
				*p++ = ip4_addr1(&nf->netmask);
				*p++ = ip4_addr2(&nf->netmask);
				*p++ = ip4_addr3(&nf->netmask);
				*p++ = ip4_addr4(&nf->netmask);
			}
		}

		more(1);
		*p++ = 0; 	// end of ethXX options

		fe = fe->next;
	}

	return p -buf;
}