xref: /kvmtool/net/uip/tcp.c (revision a4d8c55eb29a7344befcec02a7834c7cbeedcaa1)
1 #include "kvm/uip.h"
2 
3 #include <kvm/kvm.h>
4 #include <linux/virtio_net.h>
5 #include <linux/kernel.h>
6 #include <linux/list.h>
7 #include <arpa/inet.h>
8 
9 static int uip_tcp_socket_close(struct uip_tcp_socket *sk, int how)
10 {
11 	shutdown(sk->fd, how);
12 
13 	if (sk->write_done && sk->read_done) {
14 		shutdown(sk->fd, SHUT_RDWR);
15 		close(sk->fd);
16 
17 		mutex_lock(sk->lock);
18 		list_del(&sk->list);
19 		mutex_unlock(sk->lock);
20 
21 		free(sk);
22 	}
23 
24 	return 0;
25 }
26 
27 static struct uip_tcp_socket *uip_tcp_socket_find(struct uip_tx_arg *arg, u32 sip, u32 dip, u16 sport, u16 dport)
28 {
29 	struct list_head *sk_head;
30 	pthread_mutex_t *sk_lock;
31 	struct uip_tcp_socket *sk;
32 
33 	sk_head = &arg->info->tcp_socket_head;
34 	sk_lock = &arg->info->tcp_socket_lock;
35 
36 	mutex_lock(sk_lock);
37 	list_for_each_entry(sk, sk_head, list) {
38 		if (sk->sip == sip && sk->dip == dip && sk->sport == sport && sk->dport == dport) {
39 			mutex_unlock(sk_lock);
40 			return sk;
41 		}
42 	}
43 	mutex_unlock(sk_lock);
44 
45 	return NULL;
46 }
47 
48 static struct uip_tcp_socket *uip_tcp_socket_alloc(struct uip_tx_arg *arg, u32 sip, u32 dip, u16 sport, u16 dport)
49 {
50 	struct list_head *sk_head;
51 	struct uip_tcp_socket *sk;
52 	pthread_mutex_t *sk_lock;
53 	struct uip_tcp *tcp;
54 	struct uip_ip *ip;
55 	int ret;
56 
57 	tcp = (struct uip_tcp *)arg->eth;
58 	ip = (struct uip_ip *)arg->eth;
59 
60 	sk_head = &arg->info->tcp_socket_head;
61 	sk_lock = &arg->info->tcp_socket_lock;
62 
63 	sk = malloc(sizeof(*sk));
64 	memset(sk, 0, sizeof(*sk));
65 
66 	sk->lock			= sk_lock;
67 	sk->info			= arg->info;
68 
69 	sk->fd				= socket(AF_INET, SOCK_STREAM, 0);
70 	sk->addr.sin_family		= AF_INET;
71 	sk->addr.sin_port		= dport;
72 	sk->addr.sin_addr.s_addr	= dip;
73 
74 	pthread_cond_init(&sk->cond, NULL);
75 
76 	if (ntohl(dip) == arg->info->host_ip)
77 		sk->addr.sin_addr.s_addr = inet_addr("127.0.0.1");
78 
79 	ret = connect(sk->fd, (struct sockaddr *)&sk->addr, sizeof(sk->addr));
80 	if (ret) {
81 		free(sk);
82 		return NULL;
83 	}
84 
85 	sk->sip		= ip->sip;
86 	sk->dip		= ip->dip;
87 	sk->sport	= tcp->sport;
88 	sk->dport	= tcp->dport;
89 
90 	mutex_lock(sk_lock);
91 	list_add_tail(&sk->list, sk_head);
92 	mutex_unlock(sk_lock);
93 
94 	return sk;
95 }
96 
97 static int uip_tcp_payload_send(struct uip_tcp_socket *sk, u8 flag, u16 payload_len)
98 {
99 	struct uip_info *info;
100 	struct uip_eth *eth2;
101 	struct uip_tcp *tcp2;
102 	struct uip_buf *buf;
103 	struct uip_ip *ip2;
104 
105 	info		= sk->info;
106 
107 	/*
108 	 * Get free buffer to send data to guest
109 	 */
110 	buf		= uip_buf_get_free(info);
111 
112 	/*
113 	 * Cook a ethernet frame
114 	 */
115 	tcp2		= (struct uip_tcp *)buf->eth;
116 	eth2		= (struct uip_eth *)buf->eth;
117 	ip2		= (struct uip_ip *)buf->eth;
118 
119 	eth2->src	= info->host_mac;
120 	eth2->dst	= info->guest_mac;
121 	eth2->type	= htons(UIP_ETH_P_IP);
122 
123 	ip2->vhl	= UIP_IP_VER_4 | UIP_IP_HDR_LEN;
124 	ip2->tos	= 0;
125 	ip2->id		= 0;
126 	ip2->flgfrag	= 0;
127 	ip2->ttl	= UIP_IP_TTL;
128 	ip2->proto	= UIP_IP_P_TCP;
129 	ip2->csum	= 0;
130 	ip2->sip	= sk->dip;
131 	ip2->dip	= sk->sip;
132 
133 	tcp2->sport	= sk->dport;
134 	tcp2->dport	= sk->sport;
135 	tcp2->seq	= htonl(sk->seq_server);
136 	tcp2->ack	= htonl(sk->ack_server);
137 	/*
138 	 * Diable TCP options, tcp hdr len equals 20 bytes
139 	 */
140 	tcp2->off	= UIP_TCP_HDR_LEN;
141 	tcp2->flg	= flag;
142 	tcp2->win	= htons(UIP_TCP_WIN_SIZE);
143 	tcp2->csum	= 0;
144 	tcp2->urgent	= 0;
145 
146 	if (payload_len > 0)
147 		memcpy(uip_tcp_payload(tcp2), sk->payload, payload_len);
148 
149 	ip2->len	= htons(uip_tcp_hdrlen(tcp2) + payload_len + uip_ip_hdrlen(ip2));
150 	ip2->csum	= uip_csum_ip(ip2);
151 	tcp2->csum	= uip_csum_tcp(tcp2);
152 
153 	/*
154 	 * virtio_net_hdr
155 	 */
156 	buf->vnet_len	= sizeof(struct virtio_net_hdr);
157 	memset(buf->vnet, 0, buf->vnet_len);
158 
159 	buf->eth_len	= ntohs(ip2->len) + uip_eth_hdrlen(&ip2->eth);
160 
161 	/*
162 	 * Increase server seq
163 	 */
164 	sk->seq_server  += payload_len;
165 
166 	/*
167 	 * Send data received from socket to guest
168 	 */
169 	uip_buf_set_used(info, buf);
170 
171 	return 0;
172 }
173 
174 static void *uip_tcp_socket_thread(void *p)
175 {
176 	struct uip_tcp_socket *sk;
177 	int len, left, ret;
178 	u8 *payload, *pos;
179 
180 	kvm__set_thread_name("uip-tcp");
181 
182 	sk = p;
183 
184 	payload = malloc(UIP_MAX_TCP_PAYLOAD);
185 	if (!payload)
186 		goto out;
187 
188 	while (1) {
189 		pos = payload;
190 
191 		ret = read(sk->fd, payload, UIP_MAX_TCP_PAYLOAD);
192 
193 		if (ret <= 0 || ret > UIP_MAX_TCP_PAYLOAD)
194 			goto out;
195 
196 		left = ret;
197 
198 		while (left > 0) {
199 			mutex_lock(sk->lock);
200 			while ((len = sk->guest_acked + sk->window_size - sk->seq_server) <= 0)
201 				pthread_cond_wait(&sk->cond, sk->lock);
202 			mutex_unlock(sk->lock);
203 
204 			sk->payload = pos;
205 			if (len > left)
206 				len = left;
207 			if (len > UIP_MAX_TCP_PAYLOAD)
208 				len = UIP_MAX_TCP_PAYLOAD;
209 			left -= len;
210 			pos += len;
211 
212 			uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, len);
213 		}
214 	}
215 
216 out:
217 	/*
218 	 * Close server to guest TCP connection
219 	 */
220 	uip_tcp_socket_close(sk, SHUT_RD);
221 
222 	uip_tcp_payload_send(sk, UIP_TCP_FLAG_FIN | UIP_TCP_FLAG_ACK, 0);
223 	sk->seq_server += 1;
224 
225 	sk->read_done = 1;
226 
227 	free(payload);
228 	pthread_exit(NULL);
229 
230 	return NULL;
231 }
232 
233 static int uip_tcp_socket_receive(struct uip_tcp_socket *sk)
234 {
235 	if (sk->thread == 0)
236 		return pthread_create(&sk->thread, NULL, uip_tcp_socket_thread, (void *)sk);
237 
238 	return 0;
239 }
240 
241 static int uip_tcp_socket_send(struct uip_tcp_socket *sk, struct uip_tcp *tcp)
242 {
243 	int len;
244 	int ret;
245 	u8 *payload;
246 
247 	if (sk->write_done)
248 		return 0;
249 
250 	payload = uip_tcp_payload(tcp);
251 	len = uip_tcp_payloadlen(tcp);
252 
253 	ret = write(sk->fd, payload, len);
254 	if (ret != len)
255 		pr_warning("tcp send error");
256 
257 	return ret;
258 }
259 
260 int uip_tx_do_ipv4_tcp(struct uip_tx_arg *arg)
261 {
262 	struct uip_tcp_socket *sk;
263 	struct uip_tcp *tcp;
264 	struct uip_ip *ip;
265 	int ret;
266 
267 	tcp = (struct uip_tcp *)arg->eth;
268 	ip = (struct uip_ip *)arg->eth;
269 
270 	/*
271 	 * Guest is trying to start a TCP session, let's fake SYN-ACK to guest
272 	 */
273 	if (uip_tcp_is_syn(tcp)) {
274 		sk = uip_tcp_socket_alloc(arg, ip->sip, ip->dip, tcp->sport, tcp->dport);
275 		if (!sk)
276 			return -1;
277 
278 		sk->window_size = ntohs(tcp->win);
279 
280 		/*
281 		 * Setup ISN number
282 		 */
283 		sk->isn_guest  = uip_tcp_isn(tcp);
284 		sk->isn_server = uip_tcp_isn_alloc();
285 
286 		sk->seq_server = sk->isn_server;
287 		sk->ack_server = sk->isn_guest + 1;
288 		uip_tcp_payload_send(sk, UIP_TCP_FLAG_SYN | UIP_TCP_FLAG_ACK, 0);
289 		sk->seq_server += 1;
290 
291 		/*
292 		 * Start receive thread for data from remote to guest
293 		 */
294 		uip_tcp_socket_receive(sk);
295 
296 		goto out;
297 	}
298 
299 	/*
300 	 * Find socket we have allocated
301 	 */
302 	sk = uip_tcp_socket_find(arg, ip->sip, ip->dip, tcp->sport, tcp->dport);
303 	if (!sk)
304 		return -1;
305 
306 	mutex_lock(sk->lock);
307 	sk->window_size = ntohs(tcp->win);
308 	sk->guest_acked = ntohl(tcp->ack);
309 	pthread_cond_signal(&sk->cond);
310 	mutex_unlock(sk->lock);
311 
312 	if (uip_tcp_is_fin(tcp)) {
313 		if (sk->write_done)
314 			goto out;
315 
316 		sk->write_done = 1;
317 		sk->ack_server += 1;
318 		uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, 0);
319 
320 		/*
321 		 * Close guest to server TCP connection
322 		 */
323 		uip_tcp_socket_close(sk, SHUT_WR);
324 
325 		goto out;
326 	}
327 
328 	/*
329 	 * Ignore guest to server frames with zero tcp payload
330 	 */
331 	if (uip_tcp_payloadlen(tcp) == 0)
332 		goto out;
333 
334 	/*
335 	 * Sent out TCP data to remote host
336 	 */
337 	ret = uip_tcp_socket_send(sk, tcp);
338 	if (ret < 0)
339 		return -1;
340 	/*
341 	 * Send ACK to guest imediately
342 	 */
343 	sk->ack_server += ret;
344 	uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, 0);
345 
346 out:
347 	return 0;
348 }
349