xref: /kvmtool/net/uip/core.c (revision 6daffe57762cb5863eef0441a2d40cc57cdde37b)
1 #include "kvm/mutex.h"
2 #include "kvm/uip.h"
3 
4 #include <linux/virtio_net.h>
5 #include <linux/kernel.h>
6 #include <linux/list.h>
7 #include <kvm/iovec.h>
8 
uip_tx(struct iovec * iov,u16 out,struct uip_info * info)9 int uip_tx(struct iovec *iov, u16 out, struct uip_info *info)
10 {
11 	void *vnet;
12 	ssize_t len;
13 	struct uip_tx_arg arg;
14 	size_t eth_len, vnet_len;
15 	struct uip_eth *eth;
16 	void *vnet_buf = NULL;
17 	void *eth_buf = NULL;
18 	size_t iovcount = out;
19 
20 	u16 proto;
21 
22 	/*
23 	 * Buffer from guest to device
24 	 */
25 	vnet_len = info->vnet_hdr_len;
26 	vnet	 = iov[0].iov_base;
27 
28 	len = iov_size(iov, iovcount);
29 	if (len <= (ssize_t)vnet_len)
30 		return -EINVAL;
31 
32 	/* Try to avoid memcpy if possible */
33 	if (iov[0].iov_len == vnet_len && out == 2) {
34 		/* Legacy layout: first descriptor for vnet header */
35 		eth	= iov[1].iov_base;
36 		eth_len	= iov[1].iov_len;
37 
38 	} else if (out == 1) {
39 		/* Single descriptor */
40 		eth	= (void *)vnet + vnet_len;
41 		eth_len	= iov[0].iov_len - vnet_len;
42 
43 	} else {
44 		/* Any layout */
45 		len = vnet_len;
46 		vnet = vnet_buf = malloc(len);
47 		if (!vnet)
48 			return -ENOMEM;
49 
50 		len = memcpy_fromiovec_safe(vnet_buf, &iov, len, &iovcount);
51 		if (len)
52 			goto out_free_buf;
53 
54 		len = eth_len = iov_size(iov, iovcount);
55 		eth = eth_buf = malloc(len);
56 		if (!eth)
57 			goto out_free_buf;
58 
59 		len = memcpy_fromiovec_safe(eth_buf, &iov, len, &iovcount);
60 		if (len)
61 			goto out_free_buf;
62 	}
63 
64 	memset(&arg, 0, sizeof(arg));
65 
66 	arg.vnet_len = vnet_len;
67 	arg.eth_len = eth_len;
68 	arg.info = info;
69 	arg.vnet = vnet;
70 	arg.eth = eth;
71 
72 	/*
73 	 * Check package type
74 	 */
75 	proto = ntohs(eth->type);
76 
77 	switch (proto) {
78 	case UIP_ETH_P_ARP:
79 		uip_tx_do_arp(&arg);
80 		break;
81 	case UIP_ETH_P_IP:
82 		uip_tx_do_ipv4(&arg);
83 		break;
84 	}
85 
86 	free(vnet_buf);
87 	free(eth_buf);
88 
89 	return vnet_len + eth_len;
90 
91 out_free_buf:
92 	free(vnet_buf);
93 	free(eth_buf);
94 	return -EINVAL;
95 }
96 
uip_rx(struct iovec * iov,u16 in,struct uip_info * info)97 int uip_rx(struct iovec *iov, u16 in, struct uip_info *info)
98 {
99 	struct uip_buf *buf;
100 	int len;
101 
102 	/*
103 	 * Sleep until there is a buffer for guest
104 	 */
105 	buf = uip_buf_get_used(info);
106 
107 	memcpy_toiovecend(iov, buf->vnet, 0, buf->vnet_len);
108 	memcpy_toiovecend(iov, buf->eth, buf->vnet_len, buf->eth_len);
109 
110 	len = buf->vnet_len + buf->eth_len;
111 
112 	uip_buf_set_free(info, buf);
113 	return len;
114 }
115 
uip_static_init(struct uip_info * info)116 void uip_static_init(struct uip_info *info)
117 {
118 	struct list_head *udp_socket_head;
119 	struct list_head *tcp_socket_head;
120 	struct list_head *buf_head;
121 
122 	udp_socket_head	= &info->udp_socket_head;
123 	tcp_socket_head	= &info->tcp_socket_head;
124 	buf_head	= &info->buf_head;
125 
126 	INIT_LIST_HEAD(udp_socket_head);
127 	INIT_LIST_HEAD(tcp_socket_head);
128 	INIT_LIST_HEAD(buf_head);
129 
130 	mutex_init(&info->udp_socket_lock);
131 	mutex_init(&info->tcp_socket_lock);
132 	mutex_init(&info->buf_lock);
133 
134 	pthread_cond_init(&info->buf_used_cond, NULL);
135 	pthread_cond_init(&info->buf_free_cond, NULL);
136 
137 	info->buf_used_nr = 0;
138 }
139 
uip_init(struct uip_info * info)140 int uip_init(struct uip_info *info)
141 {
142 	struct list_head *buf_head;
143 	struct uip_buf *buf;
144 	int buf_nr;
145 	int i;
146 
147 	buf_head	= &info->buf_head;
148 	buf_nr		= info->buf_nr;
149 
150 	for (i = 0; i < buf_nr; i++) {
151 		buf = malloc(sizeof(*buf));
152 		memset(buf, 0, sizeof(*buf));
153 
154 		buf->status	= UIP_BUF_STATUS_FREE;
155 		buf->info	= info;
156 		buf->id		= i;
157 		list_add_tail(&buf->list, buf_head);
158 	}
159 
160 	list_for_each_entry(buf, buf_head, list) {
161 		buf->vnet_len   = info->vnet_hdr_len;
162 		buf->vnet	= malloc(buf->vnet_len);
163 		buf->eth_len    = 1024*64 + sizeof(struct uip_pseudo_hdr);
164 		buf->eth	= malloc(buf->eth_len);
165 
166 		memset(buf->vnet, 0, buf->vnet_len);
167 		memset(buf->eth, 0, buf->eth_len);
168 	}
169 
170 	info->buf_free_nr = buf_nr;
171 
172 	uip_dhcp_get_dns(info);
173 
174 	return 0;
175 }
176 
uip_exit(struct uip_info * info)177 void uip_exit(struct uip_info *info)
178 {
179 	struct uip_buf *buf, *next;
180 
181 	uip_udp_exit(info);
182 	uip_tcp_exit(info);
183 	uip_dhcp_exit(info);
184 
185 	list_for_each_entry_safe(buf, next, &info->buf_head, list) {
186 		free(buf->vnet);
187 		free(buf->eth);
188 		list_del(&buf->list);
189 		free(buf);
190 	}
191 	uip_static_init(info);
192 }
193