xref: /kvmtool/net/uip/core.c (revision 6daffe57762cb5863eef0441a2d40cc57cdde37b)
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>
77200d3a1SSasha Levin #include <kvm/iovec.h>
891b10270SAsias He 
uip_tx(struct iovec * iov,u16 out,struct uip_info * info)90c847458SAsias He int uip_tx(struct iovec *iov, u16 out, struct uip_info *info)
100c847458SAsias He {
117668c3a6SSasha Levin 	void *vnet;
12*6daffe57SJean-Philippe Brucker 	ssize_t len;
130c847458SAsias He 	struct uip_tx_arg arg;
14*6daffe57SJean-Philippe Brucker 	size_t eth_len, vnet_len;
150c847458SAsias He 	struct uip_eth *eth;
16*6daffe57SJean-Philippe Brucker 	void *vnet_buf = NULL;
17*6daffe57SJean-Philippe Brucker 	void *eth_buf = NULL;
18*6daffe57SJean-Philippe Brucker 	size_t iovcount = out;
19*6daffe57SJean-Philippe Brucker 
200c847458SAsias He 	u16 proto;
210c847458SAsias He 
220c847458SAsias He 	/*
230c847458SAsias He 	 * Buffer from guest to device
240c847458SAsias He 	 */
25*6daffe57SJean-Philippe Brucker 	vnet_len = info->vnet_hdr_len;
260c847458SAsias He 	vnet	 = iov[0].iov_base;
270c847458SAsias He 
28*6daffe57SJean-Philippe Brucker 	len = iov_size(iov, iovcount);
29*6daffe57SJean-Philippe Brucker 	if (len <= (ssize_t)vnet_len)
30*6daffe57SJean-Philippe Brucker 		return -EINVAL;
31*6daffe57SJean-Philippe Brucker 
32*6daffe57SJean-Philippe Brucker 	/* Try to avoid memcpy if possible */
33*6daffe57SJean-Philippe Brucker 	if (iov[0].iov_len == vnet_len && out == 2) {
34*6daffe57SJean-Philippe Brucker 		/* Legacy layout: first descriptor for vnet header */
350c847458SAsias He 		eth	= iov[1].iov_base;
36*6daffe57SJean-Philippe Brucker 		eth_len	= iov[1].iov_len;
370c847458SAsias He 
38*6daffe57SJean-Philippe Brucker 	} else if (out == 1) {
39*6daffe57SJean-Philippe Brucker 		/* Single descriptor */
40*6daffe57SJean-Philippe Brucker 		eth	= (void *)vnet + vnet_len;
41*6daffe57SJean-Philippe Brucker 		eth_len	= iov[0].iov_len - vnet_len;
420c847458SAsias He 
43*6daffe57SJean-Philippe Brucker 	} else {
44*6daffe57SJean-Philippe Brucker 		/* Any layout */
45*6daffe57SJean-Philippe Brucker 		len = vnet_len;
46*6daffe57SJean-Philippe Brucker 		vnet = vnet_buf = malloc(len);
47*6daffe57SJean-Philippe Brucker 		if (!vnet)
486d6cc14bSWill Deacon 			return -ENOMEM;
490c847458SAsias He 
50*6daffe57SJean-Philippe Brucker 		len = memcpy_fromiovec_safe(vnet_buf, &iov, len, &iovcount);
51*6daffe57SJean-Philippe Brucker 		if (len)
52*6daffe57SJean-Philippe Brucker 			goto out_free_buf;
53*6daffe57SJean-Philippe Brucker 
54*6daffe57SJean-Philippe Brucker 		len = eth_len = iov_size(iov, iovcount);
55*6daffe57SJean-Philippe Brucker 		eth = eth_buf = malloc(len);
56*6daffe57SJean-Philippe Brucker 		if (!eth)
57*6daffe57SJean-Philippe Brucker 			goto out_free_buf;
58*6daffe57SJean-Philippe Brucker 
59*6daffe57SJean-Philippe Brucker 		len = memcpy_fromiovec_safe(eth_buf, &iov, len, &iovcount);
60*6daffe57SJean-Philippe Brucker 		if (len)
61*6daffe57SJean-Philippe Brucker 			goto out_free_buf;
620c847458SAsias He 	}
630c847458SAsias He 
640c847458SAsias He 	memset(&arg, 0, sizeof(arg));
650c847458SAsias He 
660c847458SAsias He 	arg.vnet_len = vnet_len;
670c847458SAsias He 	arg.eth_len = eth_len;
680c847458SAsias He 	arg.info = info;
690c847458SAsias He 	arg.vnet = vnet;
700c847458SAsias He 	arg.eth = eth;
710c847458SAsias He 
720c847458SAsias He 	/*
730c847458SAsias He 	 * Check package type
740c847458SAsias He 	 */
750c847458SAsias He 	proto = ntohs(eth->type);
760c847458SAsias He 
770c847458SAsias He 	switch (proto) {
780c847458SAsias He 	case UIP_ETH_P_ARP:
790c847458SAsias He 		uip_tx_do_arp(&arg);
800c847458SAsias He 		break;
810c847458SAsias He 	case UIP_ETH_P_IP:
820c847458SAsias He 		uip_tx_do_ipv4(&arg);
830c847458SAsias He 		break;
840c847458SAsias He 	}
850c847458SAsias He 
86*6daffe57SJean-Philippe Brucker 	free(vnet_buf);
87*6daffe57SJean-Philippe Brucker 	free(eth_buf);
880c847458SAsias He 
890c847458SAsias He 	return vnet_len + eth_len;
90*6daffe57SJean-Philippe Brucker 
91*6daffe57SJean-Philippe Brucker out_free_buf:
92*6daffe57SJean-Philippe Brucker 	free(vnet_buf);
93*6daffe57SJean-Philippe Brucker 	free(eth_buf);
94*6daffe57SJean-Philippe Brucker 	return -EINVAL;
950c847458SAsias He }
960c847458SAsias He 
uip_rx(struct iovec * iov,u16 in,struct uip_info * info)9760d2902cSAsias He int uip_rx(struct iovec *iov, u16 in, struct uip_info *info)
9860d2902cSAsias He {
9960d2902cSAsias He 	struct uip_buf *buf;
10060d2902cSAsias He 	int len;
10160d2902cSAsias He 
10260d2902cSAsias He 	/*
10360d2902cSAsias He 	 * Sleep until there is a buffer for guest
10460d2902cSAsias He 	 */
10560d2902cSAsias He 	buf = uip_buf_get_used(info);
10660d2902cSAsias He 
1077200d3a1SSasha Levin 	memcpy_toiovecend(iov, buf->vnet, 0, buf->vnet_len);
1087200d3a1SSasha Levin 	memcpy_toiovecend(iov, buf->eth, buf->vnet_len, buf->eth_len);
10960d2902cSAsias He 
11060d2902cSAsias He 	len = buf->vnet_len + buf->eth_len;
11160d2902cSAsias He 
11260d2902cSAsias He 	uip_buf_set_free(info, buf);
11360d2902cSAsias He 	return len;
11460d2902cSAsias He }
11560d2902cSAsias He 
uip_static_init(struct uip_info * info)1165f3aaf22SMarc Zyngier void uip_static_init(struct uip_info *info)
11791b10270SAsias He {
11891b10270SAsias He 	struct list_head *udp_socket_head;
11991b10270SAsias He 	struct list_head *tcp_socket_head;
12091b10270SAsias He 	struct list_head *buf_head;
12191b10270SAsias He 
12291b10270SAsias He 	udp_socket_head	= &info->udp_socket_head;
12391b10270SAsias He 	tcp_socket_head	= &info->tcp_socket_head;
12491b10270SAsias He 	buf_head	= &info->buf_head;
12591b10270SAsias He 
12691b10270SAsias He 	INIT_LIST_HEAD(udp_socket_head);
12791b10270SAsias He 	INIT_LIST_HEAD(tcp_socket_head);
12891b10270SAsias He 	INIT_LIST_HEAD(buf_head);
12991b10270SAsias He 
130d3476f7dSSasha Levin 	mutex_init(&info->udp_socket_lock);
131d3476f7dSSasha Levin 	mutex_init(&info->tcp_socket_lock);
132d3476f7dSSasha Levin 	mutex_init(&info->buf_lock);
13391b10270SAsias He 
13491b10270SAsias He 	pthread_cond_init(&info->buf_used_cond, NULL);
13591b10270SAsias He 	pthread_cond_init(&info->buf_free_cond, NULL);
13691b10270SAsias He 
1375f3aaf22SMarc Zyngier 	info->buf_used_nr = 0;
1385f3aaf22SMarc Zyngier }
1395f3aaf22SMarc Zyngier 
uip_init(struct uip_info * info)1405f3aaf22SMarc Zyngier int uip_init(struct uip_info *info)
1415f3aaf22SMarc Zyngier {
1425f3aaf22SMarc Zyngier 	struct list_head *buf_head;
1435f3aaf22SMarc Zyngier 	struct uip_buf *buf;
1445f3aaf22SMarc Zyngier 	int buf_nr;
1455f3aaf22SMarc Zyngier 	int i;
1465f3aaf22SMarc Zyngier 
1475f3aaf22SMarc Zyngier 	buf_head	= &info->buf_head;
1485f3aaf22SMarc Zyngier 	buf_nr		= info->buf_nr;
14991b10270SAsias He 
15091b10270SAsias He 	for (i = 0; i < buf_nr; i++) {
15191b10270SAsias He 		buf = malloc(sizeof(*buf));
15291b10270SAsias He 		memset(buf, 0, sizeof(*buf));
15391b10270SAsias He 
15491b10270SAsias He 		buf->status	= UIP_BUF_STATUS_FREE;
15591b10270SAsias He 		buf->info	= info;
15691b10270SAsias He 		buf->id		= i;
15791b10270SAsias He 		list_add_tail(&buf->list, buf_head);
15891b10270SAsias He 	}
15991b10270SAsias He 
16091b10270SAsias He 	list_for_each_entry(buf, buf_head, list) {
161643f6c08SSasha Levin 		buf->vnet_len   = info->vnet_hdr_len;
162643f6c08SSasha Levin 		buf->vnet	= malloc(buf->vnet_len);
16391b10270SAsias He 		buf->eth_len    = 1024*64 + sizeof(struct uip_pseudo_hdr);
164643f6c08SSasha Levin 		buf->eth	= malloc(buf->eth_len);
16591b10270SAsias He 
16691b10270SAsias He 		memset(buf->vnet, 0, buf->vnet_len);
16791b10270SAsias He 		memset(buf->eth, 0, buf->eth_len);
16891b10270SAsias He 	}
16991b10270SAsias He 
17091b10270SAsias He 	info->buf_free_nr = buf_nr;
17191b10270SAsias He 
17280f43f64SAsias He 	uip_dhcp_get_dns(info);
17380f43f64SAsias He 
17491b10270SAsias He 	return 0;
17591b10270SAsias He }
176d87b503fSJean-Philippe Brucker 
uip_exit(struct uip_info * info)177d87b503fSJean-Philippe Brucker void uip_exit(struct uip_info *info)
178d87b503fSJean-Philippe Brucker {
179d87b503fSJean-Philippe Brucker 	struct uip_buf *buf, *next;
180d87b503fSJean-Philippe Brucker 
181d87b503fSJean-Philippe Brucker 	uip_udp_exit(info);
182d87b503fSJean-Philippe Brucker 	uip_tcp_exit(info);
183d87b503fSJean-Philippe Brucker 	uip_dhcp_exit(info);
184d87b503fSJean-Philippe Brucker 
185d87b503fSJean-Philippe Brucker 	list_for_each_entry_safe(buf, next, &info->buf_head, list) {
186d87b503fSJean-Philippe Brucker 		free(buf->vnet);
187d87b503fSJean-Philippe Brucker 		free(buf->eth);
188d87b503fSJean-Philippe Brucker 		list_del(&buf->list);
189d87b503fSJean-Philippe Brucker 		free(buf);
190d87b503fSJean-Philippe Brucker 	}
191d87b503fSJean-Philippe Brucker 	uip_static_init(info);
192d87b503fSJean-Philippe Brucker }
193