xref: /kvmtool/net/uip/core.c (revision d3476f7d3bb7cee0b620cf207c168cb4f5b5c41e)
191b10270SAsias He #include "kvm/mutex.h"
291b10270SAsias He #include "kvm/uip.h"
391b10270SAsias He 
491b10270SAsias He #include <linux/virtio_net.h>
591b10270SAsias He #include <linux/kernel.h>
691b10270SAsias He #include <linux/list.h>
791b10270SAsias He 
80c847458SAsias He int uip_tx(struct iovec *iov, u16 out, struct uip_info *info)
90c847458SAsias He {
100c847458SAsias He 	struct virtio_net_hdr *vnet;
110c847458SAsias He 	struct uip_tx_arg arg;
120c847458SAsias He 	int eth_len, vnet_len;
130c847458SAsias He 	struct uip_eth *eth;
140c847458SAsias He 	u8 *buf = NULL;
150c847458SAsias He 	u16 proto;
160c847458SAsias He 	int i;
170c847458SAsias He 
180c847458SAsias He 	/*
190c847458SAsias He 	 * Buffer from guest to device
200c847458SAsias He 	 */
210c847458SAsias He 	vnet_len = iov[0].iov_len;
220c847458SAsias He 	vnet	 = iov[0].iov_base;
230c847458SAsias He 
240c847458SAsias He 	eth_len	 = iov[1].iov_len;
250c847458SAsias He 	eth	 = iov[1].iov_base;
260c847458SAsias He 
270c847458SAsias He 	/*
280c847458SAsias He 	 * In case, ethernet frame is in more than one iov entry.
290c847458SAsias He 	 * Copy iov buffer into one linear buffer.
300c847458SAsias He 	 */
310c847458SAsias He 	if (out > 2) {
320c847458SAsias He 		eth_len = 0;
330c847458SAsias He 		for (i = 1; i < out; i++)
340c847458SAsias He 			eth_len += iov[i].iov_len;
350c847458SAsias He 
360c847458SAsias He 		buf = malloc(eth_len);
370c847458SAsias He 		if (!buf)
380c847458SAsias He 			return -1;
390c847458SAsias He 
400c847458SAsias He 		eth = (struct uip_eth *)buf;
410c847458SAsias He 		for (i = 1; i < out; i++) {
420c847458SAsias He 			memcpy(buf, iov[i].iov_base, iov[i].iov_len);
430c847458SAsias He 			buf += iov[i].iov_len;
440c847458SAsias He 		}
450c847458SAsias He 	}
460c847458SAsias He 
470c847458SAsias He 	memset(&arg, 0, sizeof(arg));
480c847458SAsias He 
490c847458SAsias He 	arg.vnet_len = vnet_len;
500c847458SAsias He 	arg.eth_len = eth_len;
510c847458SAsias He 	arg.info = info;
520c847458SAsias He 	arg.vnet = vnet;
530c847458SAsias He 	arg.eth = eth;
540c847458SAsias He 
550c847458SAsias He 	/*
560c847458SAsias He 	 * Check package type
570c847458SAsias He 	 */
580c847458SAsias He 	proto = ntohs(eth->type);
590c847458SAsias He 
600c847458SAsias He 	switch (proto) {
610c847458SAsias He 	case UIP_ETH_P_ARP:
620c847458SAsias He 		uip_tx_do_arp(&arg);
630c847458SAsias He 		break;
640c847458SAsias He 	case UIP_ETH_P_IP:
650c847458SAsias He 		uip_tx_do_ipv4(&arg);
660c847458SAsias He 		break;
670c847458SAsias He 	default:
680c847458SAsias He 		break;
690c847458SAsias He 	}
700c847458SAsias He 
710c847458SAsias He 	if (out > 2 && buf)
720c847458SAsias He 		free(eth);
730c847458SAsias He 
740c847458SAsias He 	return vnet_len + eth_len;
750c847458SAsias He }
760c847458SAsias He 
7760d2902cSAsias He int uip_rx(struct iovec *iov, u16 in, struct uip_info *info)
7860d2902cSAsias He {
7960d2902cSAsias He 	struct virtio_net_hdr *vnet;
8060d2902cSAsias He 	struct uip_eth *eth;
8160d2902cSAsias He 	struct uip_buf *buf;
8260d2902cSAsias He 	int vnet_len;
8360d2902cSAsias He 	int eth_len;
8460d2902cSAsias He 	char *p;
8560d2902cSAsias He 	int len;
8660d2902cSAsias He 	int cnt;
8760d2902cSAsias He 	int i;
8860d2902cSAsias He 
8960d2902cSAsias He 	/*
9060d2902cSAsias He 	 * Sleep until there is a buffer for guest
9160d2902cSAsias He 	 */
9260d2902cSAsias He 	buf = uip_buf_get_used(info);
9360d2902cSAsias He 
9460d2902cSAsias He 	/*
9560d2902cSAsias He 	 * Fill device to guest buffer, vnet hdr fisrt
9660d2902cSAsias He 	 */
9760d2902cSAsias He 	vnet_len = iov[0].iov_len;
9860d2902cSAsias He 	vnet = iov[0].iov_base;
9960d2902cSAsias He 	if (buf->vnet_len > vnet_len) {
10060d2902cSAsias He 		len = -1;
10160d2902cSAsias He 		goto out;
10260d2902cSAsias He 	}
10360d2902cSAsias He 	memcpy(vnet, buf->vnet, buf->vnet_len);
10460d2902cSAsias He 
10560d2902cSAsias He 	/*
10660d2902cSAsias He 	 * Then, the real eth data
10760d2902cSAsias He 	 * Note: Be sure buf->eth_len is not bigger than the buffer len that guest provides
10860d2902cSAsias He 	 */
10960d2902cSAsias He 	cnt = buf->eth_len;
11060d2902cSAsias He 	p = buf->eth;
11160d2902cSAsias He 	for (i = 1; i < in; i++) {
11260d2902cSAsias He 		eth_len = iov[i].iov_len;
11360d2902cSAsias He 		eth = iov[i].iov_base;
11460d2902cSAsias He 		if (cnt > eth_len) {
11560d2902cSAsias He 			memcpy(eth, p, eth_len);
11660d2902cSAsias He 			cnt -= eth_len;
11760d2902cSAsias He 			p += eth_len;
11860d2902cSAsias He 		} else {
11960d2902cSAsias He 			memcpy(eth, p, cnt);
12060d2902cSAsias He 			cnt -= cnt;
12160d2902cSAsias He 			break;
12260d2902cSAsias He 		}
12360d2902cSAsias He 	}
12460d2902cSAsias He 
12560d2902cSAsias He 	if (cnt) {
12660d2902cSAsias He 		pr_warning("uip_rx error");
12760d2902cSAsias He 		len = -1;
12860d2902cSAsias He 		goto out;
12960d2902cSAsias He 	}
13060d2902cSAsias He 
13160d2902cSAsias He 	len = buf->vnet_len + buf->eth_len;
13260d2902cSAsias He 
13360d2902cSAsias He out:
13460d2902cSAsias He 	uip_buf_set_free(info, buf);
13560d2902cSAsias He 	return len;
13660d2902cSAsias He }
13760d2902cSAsias He 
13891b10270SAsias He int uip_init(struct uip_info *info)
13991b10270SAsias He {
14091b10270SAsias He 	struct list_head *udp_socket_head;
14191b10270SAsias He 	struct list_head *tcp_socket_head;
14291b10270SAsias He 	struct list_head *buf_head;
14391b10270SAsias He 	struct uip_buf *buf;
14491b10270SAsias He 	int buf_nr;
14591b10270SAsias He 	int i;
14691b10270SAsias He 
14791b10270SAsias He 	udp_socket_head	= &info->udp_socket_head;
14891b10270SAsias He 	tcp_socket_head	= &info->tcp_socket_head;
14991b10270SAsias He 	buf_head	= &info->buf_head;
15091b10270SAsias He 	buf_nr		= info->buf_nr;
15191b10270SAsias He 
15291b10270SAsias He 	INIT_LIST_HEAD(udp_socket_head);
15391b10270SAsias He 	INIT_LIST_HEAD(tcp_socket_head);
15491b10270SAsias He 	INIT_LIST_HEAD(buf_head);
15591b10270SAsias He 
156*d3476f7dSSasha Levin 	mutex_init(&info->udp_socket_lock);
157*d3476f7dSSasha Levin 	mutex_init(&info->tcp_socket_lock);
158*d3476f7dSSasha Levin 	mutex_init(&info->buf_lock);
15991b10270SAsias He 
16091b10270SAsias He 	pthread_cond_init(&info->buf_used_cond, NULL);
16191b10270SAsias He 	pthread_cond_init(&info->buf_free_cond, NULL);
16291b10270SAsias He 
16391b10270SAsias He 
16491b10270SAsias He 	for (i = 0; i < buf_nr; i++) {
16591b10270SAsias He 		buf = malloc(sizeof(*buf));
16691b10270SAsias He 		memset(buf, 0, sizeof(*buf));
16791b10270SAsias He 
16891b10270SAsias He 		buf->status	= UIP_BUF_STATUS_FREE;
16991b10270SAsias He 		buf->info	= info;
17091b10270SAsias He 		buf->id		= i;
17191b10270SAsias He 		list_add_tail(&buf->list, buf_head);
17291b10270SAsias He 	}
17391b10270SAsias He 
17491b10270SAsias He 	list_for_each_entry(buf, buf_head, list) {
17591b10270SAsias He 		buf->vnet	= malloc(sizeof(struct virtio_net_hdr));
17691b10270SAsias He 		buf->vnet_len	= sizeof(struct virtio_net_hdr);
17791b10270SAsias He 		buf->eth	= malloc(1024*64 + sizeof(struct uip_pseudo_hdr));
17891b10270SAsias He 		buf->eth_len	= 1024*64 + sizeof(struct uip_pseudo_hdr);
17991b10270SAsias He 
18091b10270SAsias He 		memset(buf->vnet, 0, buf->vnet_len);
18191b10270SAsias He 		memset(buf->eth, 0, buf->eth_len);
18291b10270SAsias He 	}
18391b10270SAsias He 
18491b10270SAsias He 	info->buf_free_nr = buf_nr;
18591b10270SAsias He 	info->buf_used_nr = 0;
18691b10270SAsias He 
18780f43f64SAsias He 	uip_dhcp_get_dns(info);
18880f43f64SAsias He 
18991b10270SAsias He 	return 0;
19091b10270SAsias He }
191