xref: /kvmtool/net/uip/tcp.c (revision 3909f9b57bbf5e9c8654e2592abc9dc35878630a)
13df5d593SAsias He #include "kvm/uip.h"
23df5d593SAsias He 
33df5d593SAsias He #include <linux/virtio_net.h>
43df5d593SAsias He #include <linux/kernel.h>
53df5d593SAsias He #include <linux/list.h>
6195544b7SAsias He #include <arpa/inet.h>
73df5d593SAsias He 
83df5d593SAsias He static int uip_tcp_socket_close(struct uip_tcp_socket *sk, int how)
93df5d593SAsias He {
103df5d593SAsias He 	shutdown(sk->fd, how);
113df5d593SAsias He 
123df5d593SAsias He 	if (sk->write_done && sk->read_done) {
133df5d593SAsias He 		shutdown(sk->fd, SHUT_RDWR);
143df5d593SAsias He 		close(sk->fd);
153df5d593SAsias He 
163df5d593SAsias He 		mutex_lock(sk->lock);
173df5d593SAsias He 		list_del(&sk->list);
183df5d593SAsias He 		mutex_unlock(sk->lock);
193df5d593SAsias He 
203df5d593SAsias He 		free(sk);
213df5d593SAsias He 	}
223df5d593SAsias He 
233df5d593SAsias He 	return 0;
243df5d593SAsias He }
253df5d593SAsias He 
263df5d593SAsias He static struct uip_tcp_socket *uip_tcp_socket_find(struct uip_tx_arg *arg, u32 sip, u32 dip, u16 sport, u16 dport)
273df5d593SAsias He {
283df5d593SAsias He 	struct list_head *sk_head;
293df5d593SAsias He 	pthread_mutex_t *sk_lock;
303df5d593SAsias He 	struct uip_tcp_socket *sk;
313df5d593SAsias He 
323df5d593SAsias He 	sk_head = &arg->info->tcp_socket_head;
333df5d593SAsias He 	sk_lock = &arg->info->tcp_socket_lock;
343df5d593SAsias He 
353df5d593SAsias He 	mutex_lock(sk_lock);
363df5d593SAsias He 	list_for_each_entry(sk, sk_head, list) {
373df5d593SAsias He 		if (sk->sip == sip && sk->dip == dip && sk->sport == sport && sk->dport == dport) {
383df5d593SAsias He 			mutex_unlock(sk_lock);
393df5d593SAsias He 			return sk;
403df5d593SAsias He 		}
413df5d593SAsias He 	}
423df5d593SAsias He 	mutex_unlock(sk_lock);
433df5d593SAsias He 
443df5d593SAsias He 	return NULL;
453df5d593SAsias He }
463df5d593SAsias He 
473df5d593SAsias He static struct uip_tcp_socket *uip_tcp_socket_alloc(struct uip_tx_arg *arg, u32 sip, u32 dip, u16 sport, u16 dport)
483df5d593SAsias He {
493df5d593SAsias He 	struct list_head *sk_head;
503df5d593SAsias He 	struct uip_tcp_socket *sk;
513df5d593SAsias He 	pthread_mutex_t *sk_lock;
523df5d593SAsias He 	struct uip_tcp *tcp;
533df5d593SAsias He 	struct uip_ip *ip;
543df5d593SAsias He 	int ret;
553df5d593SAsias He 
563df5d593SAsias He 	tcp = (struct uip_tcp *)arg->eth;
573df5d593SAsias He 	ip = (struct uip_ip *)arg->eth;
583df5d593SAsias He 
593df5d593SAsias He 	sk_head = &arg->info->tcp_socket_head;
603df5d593SAsias He 	sk_lock = &arg->info->tcp_socket_lock;
613df5d593SAsias He 
623df5d593SAsias He 	sk = malloc(sizeof(*sk));
633df5d593SAsias He 	memset(sk, 0, sizeof(*sk));
643df5d593SAsias He 
653df5d593SAsias He 	sk->lock			= sk_lock;
663df5d593SAsias He 	sk->info			= arg->info;
673df5d593SAsias He 
683df5d593SAsias He 	sk->fd				= socket(AF_INET, SOCK_STREAM, 0);
693df5d593SAsias He 	sk->addr.sin_family		= AF_INET;
703df5d593SAsias He 	sk->addr.sin_port		= dport;
71195544b7SAsias He 	sk->addr.sin_addr.s_addr	= dip;
72195544b7SAsias He 
73*3909f9b5SAsias He 	pthread_cond_init(&sk->cond, NULL);
74*3909f9b5SAsias He 
75195544b7SAsias He 	if (ntohl(dip) == arg->info->host_ip)
76195544b7SAsias He 		sk->addr.sin_addr.s_addr = inet_addr("127.0.0.1");
773df5d593SAsias He 
783df5d593SAsias He 	ret = connect(sk->fd, (struct sockaddr *)&sk->addr, sizeof(sk->addr));
793df5d593SAsias He 	if (ret) {
803df5d593SAsias He 		free(sk);
813df5d593SAsias He 		return NULL;
823df5d593SAsias He 	}
833df5d593SAsias He 
843df5d593SAsias He 	sk->sip		= ip->sip;
853df5d593SAsias He 	sk->dip		= ip->dip;
863df5d593SAsias He 	sk->sport	= tcp->sport;
873df5d593SAsias He 	sk->dport	= tcp->dport;
883df5d593SAsias He 
893df5d593SAsias He 	mutex_lock(sk_lock);
903df5d593SAsias He 	list_add_tail(&sk->list, sk_head);
913df5d593SAsias He 	mutex_unlock(sk_lock);
923df5d593SAsias He 
933df5d593SAsias He 	return sk;
943df5d593SAsias He }
953df5d593SAsias He 
963df5d593SAsias He static int uip_tcp_payload_send(struct uip_tcp_socket *sk, u8 flag, u16 payload_len)
973df5d593SAsias He {
983df5d593SAsias He 	struct uip_info *info;
993df5d593SAsias He 	struct uip_eth *eth2;
1003df5d593SAsias He 	struct uip_tcp *tcp2;
1013df5d593SAsias He 	struct uip_buf *buf;
1023df5d593SAsias He 	struct uip_ip *ip2;
1033df5d593SAsias He 
1043df5d593SAsias He 	info		= sk->info;
1053df5d593SAsias He 
1063df5d593SAsias He 	/*
1073df5d593SAsias He 	 * Get free buffer to send data to guest
1083df5d593SAsias He 	 */
1093df5d593SAsias He 	buf		= uip_buf_get_free(info);
1103df5d593SAsias He 
1113df5d593SAsias He 	/*
1123df5d593SAsias He 	 * Cook a ethernet frame
1133df5d593SAsias He 	 */
1143df5d593SAsias He 	tcp2		= (struct uip_tcp *)buf->eth;
1153df5d593SAsias He 	eth2		= (struct uip_eth *)buf->eth;
1163df5d593SAsias He 	ip2		= (struct uip_ip *)buf->eth;
1173df5d593SAsias He 
1183df5d593SAsias He 	eth2->src	= info->host_mac;
1193df5d593SAsias He 	eth2->dst	= info->guest_mac;
1203df5d593SAsias He 	eth2->type	= htons(UIP_ETH_P_IP);
1213df5d593SAsias He 
1223df5d593SAsias He 	ip2->vhl	= UIP_IP_VER_4 | UIP_IP_HDR_LEN;
1233df5d593SAsias He 	ip2->tos	= 0;
1243df5d593SAsias He 	ip2->id		= 0;
1253df5d593SAsias He 	ip2->flgfrag	= 0;
1263df5d593SAsias He 	ip2->ttl	= UIP_IP_TTL;
1273df5d593SAsias He 	ip2->proto	= UIP_IP_P_TCP;
1283df5d593SAsias He 	ip2->csum	= 0;
1293df5d593SAsias He 	ip2->sip	= sk->dip;
1303df5d593SAsias He 	ip2->dip	= sk->sip;
1313df5d593SAsias He 
1323df5d593SAsias He 	tcp2->sport	= sk->dport;
1333df5d593SAsias He 	tcp2->dport	= sk->sport;
1343df5d593SAsias He 	tcp2->seq	= htonl(sk->seq_server);
1353df5d593SAsias He 	tcp2->ack	= htonl(sk->ack_server);
1363df5d593SAsias He 	/*
1373df5d593SAsias He 	 * Diable TCP options, tcp hdr len equals 20 bytes
1383df5d593SAsias He 	 */
1393df5d593SAsias He 	tcp2->off	= UIP_TCP_HDR_LEN;
1403df5d593SAsias He 	tcp2->flg	= flag;
1413df5d593SAsias He 	tcp2->win	= htons(UIP_TCP_WIN_SIZE);
1423df5d593SAsias He 	tcp2->csum	= 0;
1433df5d593SAsias He 	tcp2->urgent	= 0;
1443df5d593SAsias He 
1453df5d593SAsias He 	if (payload_len > 0)
1463df5d593SAsias He 		memcpy(uip_tcp_payload(tcp2), sk->payload, payload_len);
1473df5d593SAsias He 
1483df5d593SAsias He 	ip2->len	= htons(uip_tcp_hdrlen(tcp2) + payload_len + uip_ip_hdrlen(ip2));
1493df5d593SAsias He 	ip2->csum	= uip_csum_ip(ip2);
1503df5d593SAsias He 	tcp2->csum	= uip_csum_tcp(tcp2);
1513df5d593SAsias He 
1523df5d593SAsias He 	/*
1533df5d593SAsias He 	 * virtio_net_hdr
1543df5d593SAsias He 	 */
1553df5d593SAsias He 	buf->vnet_len	= sizeof(struct virtio_net_hdr);
1563df5d593SAsias He 	memset(buf->vnet, 0, buf->vnet_len);
1573df5d593SAsias He 
1583df5d593SAsias He 	buf->eth_len	= ntohs(ip2->len) + uip_eth_hdrlen(&ip2->eth);
1593df5d593SAsias He 
1603df5d593SAsias He 	/*
1613df5d593SAsias He 	 * Increase server seq
1623df5d593SAsias He 	 */
1633df5d593SAsias He 	sk->seq_server  += payload_len;
1643df5d593SAsias He 
1653df5d593SAsias He 	/*
1663df5d593SAsias He 	 * Send data received from socket to guest
1673df5d593SAsias He 	 */
1683df5d593SAsias He 	uip_buf_set_used(info, buf);
1693df5d593SAsias He 
1703df5d593SAsias He 	return 0;
1713df5d593SAsias He }
1723df5d593SAsias He 
1733df5d593SAsias He static void *uip_tcp_socket_thread(void *p)
1743df5d593SAsias He {
1753df5d593SAsias He 	struct uip_tcp_socket *sk;
176*3909f9b5SAsias He 	int len, left, ret;
177*3909f9b5SAsias He 	u8 *payload, *pos;
1783df5d593SAsias He 
1793df5d593SAsias He 	sk = p;
1803df5d593SAsias He 
1813df5d593SAsias He 	payload = malloc(UIP_MAX_TCP_PAYLOAD);
182*3909f9b5SAsias He 	if (!payload)
1833df5d593SAsias He 		goto out;
1843df5d593SAsias He 
1853df5d593SAsias He 	while (1) {
186*3909f9b5SAsias He 		pos = payload;
1873df5d593SAsias He 
1883df5d593SAsias He 		ret = read(sk->fd, payload, UIP_MAX_TCP_PAYLOAD);
1893df5d593SAsias He 
1903df5d593SAsias He 		if (ret <= 0 || ret > UIP_MAX_TCP_PAYLOAD)
1913df5d593SAsias He 			goto out;
1923df5d593SAsias He 
193*3909f9b5SAsias He 		left = ret;
1943df5d593SAsias He 
195*3909f9b5SAsias He 		while (left > 0) {
196*3909f9b5SAsias He 			mutex_lock(sk->lock);
197*3909f9b5SAsias He 			while ((len = sk->guest_acked + sk->window_size - sk->seq_server) <= 0)
198*3909f9b5SAsias He 				pthread_cond_wait(&sk->cond, sk->lock);
199*3909f9b5SAsias He 			mutex_unlock(sk->lock);
200*3909f9b5SAsias He 
201*3909f9b5SAsias He 			sk->payload = pos;
202*3909f9b5SAsias He 			if (len > left)
203*3909f9b5SAsias He 				len = left;
204*3909f9b5SAsias He 			if (len > UIP_MAX_TCP_PAYLOAD)
205*3909f9b5SAsias He 				len = UIP_MAX_TCP_PAYLOAD;
206*3909f9b5SAsias He 			left -= len;
207*3909f9b5SAsias He 			pos += len;
208*3909f9b5SAsias He 
209*3909f9b5SAsias He 			uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, len);
210*3909f9b5SAsias He 		}
2113df5d593SAsias He 	}
2123df5d593SAsias He 
2133df5d593SAsias He out:
2143df5d593SAsias He 	/*
2153df5d593SAsias He 	 * Close server to guest TCP connection
2163df5d593SAsias He 	 */
2173df5d593SAsias He 	uip_tcp_socket_close(sk, SHUT_RD);
2183df5d593SAsias He 
2193df5d593SAsias He 	uip_tcp_payload_send(sk, UIP_TCP_FLAG_FIN | UIP_TCP_FLAG_ACK, 0);
2203df5d593SAsias He 	sk->seq_server += 1;
2213df5d593SAsias He 
2223df5d593SAsias He 	sk->read_done = 1;
2233df5d593SAsias He 
224*3909f9b5SAsias He 	free(payload);
2253df5d593SAsias He 	pthread_exit(NULL);
2263df5d593SAsias He 
2273df5d593SAsias He 	return NULL;
2283df5d593SAsias He }
2293df5d593SAsias He 
2303df5d593SAsias He static int uip_tcp_socket_receive(struct uip_tcp_socket *sk)
2313df5d593SAsias He {
2323df5d593SAsias He 	if (sk->thread == 0)
2333df5d593SAsias He 		return pthread_create(&sk->thread, NULL, uip_tcp_socket_thread, (void *)sk);
2343df5d593SAsias He 
2353df5d593SAsias He 	return 0;
2363df5d593SAsias He }
2373df5d593SAsias He 
2383df5d593SAsias He static int uip_tcp_socket_send(struct uip_tcp_socket *sk, struct uip_tcp *tcp)
2393df5d593SAsias He {
2403df5d593SAsias He 	int len;
2413df5d593SAsias He 	int ret;
2423df5d593SAsias He 	u8 *payload;
2433df5d593SAsias He 
2443df5d593SAsias He 	if (sk->write_done)
2453df5d593SAsias He 		return 0;
2463df5d593SAsias He 
2473df5d593SAsias He 	payload = uip_tcp_payload(tcp);
2483df5d593SAsias He 	len = uip_tcp_payloadlen(tcp);
2493df5d593SAsias He 
2503df5d593SAsias He 	ret = write(sk->fd, payload, len);
2513df5d593SAsias He 	if (ret != len)
2523df5d593SAsias He 		pr_warning("tcp send error");
2533df5d593SAsias He 
2543df5d593SAsias He 	return ret;
2553df5d593SAsias He }
2563df5d593SAsias He 
2573df5d593SAsias He int uip_tx_do_ipv4_tcp(struct uip_tx_arg *arg)
2583df5d593SAsias He {
2593df5d593SAsias He 	struct uip_tcp_socket *sk;
2603df5d593SAsias He 	struct uip_tcp *tcp;
2613df5d593SAsias He 	struct uip_ip *ip;
2623df5d593SAsias He 	int ret;
2633df5d593SAsias He 
2643df5d593SAsias He 	tcp = (struct uip_tcp *)arg->eth;
2653df5d593SAsias He 	ip = (struct uip_ip *)arg->eth;
2663df5d593SAsias He 
2673df5d593SAsias He 	/*
2683df5d593SAsias He 	 * Guest is trying to start a TCP session, let's fake SYN-ACK to guest
2693df5d593SAsias He 	 */
2703df5d593SAsias He 	if (uip_tcp_is_syn(tcp)) {
2713df5d593SAsias He 		sk = uip_tcp_socket_alloc(arg, ip->sip, ip->dip, tcp->sport, tcp->dport);
2723df5d593SAsias He 		if (!sk)
2733df5d593SAsias He 			return -1;
2743df5d593SAsias He 
275*3909f9b5SAsias He 		sk->window_size = ntohs(tcp->win);
276*3909f9b5SAsias He 
2773df5d593SAsias He 		/*
2783df5d593SAsias He 		 * Setup ISN number
2793df5d593SAsias He 		 */
2803df5d593SAsias He 		sk->isn_guest  = uip_tcp_isn(tcp);
2813df5d593SAsias He 		sk->isn_server = uip_tcp_isn_alloc();
2823df5d593SAsias He 
2833df5d593SAsias He 		sk->seq_server = sk->isn_server;
2843df5d593SAsias He 		sk->ack_server = sk->isn_guest + 1;
2853df5d593SAsias He 		uip_tcp_payload_send(sk, UIP_TCP_FLAG_SYN | UIP_TCP_FLAG_ACK, 0);
2863df5d593SAsias He 		sk->seq_server += 1;
2873df5d593SAsias He 
2883df5d593SAsias He 		/*
2893df5d593SAsias He 		 * Start receive thread for data from remote to guest
2903df5d593SAsias He 		 */
2913df5d593SAsias He 		uip_tcp_socket_receive(sk);
2923df5d593SAsias He 
2933df5d593SAsias He 		goto out;
2943df5d593SAsias He 	}
2953df5d593SAsias He 
2963df5d593SAsias He 	/*
2973df5d593SAsias He 	 * Find socket we have allocated
2983df5d593SAsias He 	 */
2993df5d593SAsias He 	sk = uip_tcp_socket_find(arg, ip->sip, ip->dip, tcp->sport, tcp->dport);
3003df5d593SAsias He 	if (!sk)
3013df5d593SAsias He 		return -1;
3023df5d593SAsias He 
303*3909f9b5SAsias He 	mutex_lock(sk->lock);
304*3909f9b5SAsias He 	sk->window_size = ntohs(tcp->win);
3053df5d593SAsias He 	sk->guest_acked = ntohl(tcp->ack);
306*3909f9b5SAsias He 	pthread_cond_signal(&sk->cond);
307*3909f9b5SAsias He 	mutex_unlock(sk->lock);
3083df5d593SAsias He 
3093df5d593SAsias He 	if (uip_tcp_is_fin(tcp)) {
3103df5d593SAsias He 		if (sk->write_done)
3113df5d593SAsias He 			goto out;
3123df5d593SAsias He 
3133df5d593SAsias He 		sk->write_done = 1;
3143df5d593SAsias He 		sk->ack_server += 1;
3153df5d593SAsias He 		uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, 0);
3163df5d593SAsias He 
3173df5d593SAsias He 		/*
3183df5d593SAsias He 		 * Close guest to server TCP connection
3193df5d593SAsias He 		 */
3203df5d593SAsias He 		uip_tcp_socket_close(sk, SHUT_WR);
3213df5d593SAsias He 
3223df5d593SAsias He 		goto out;
3233df5d593SAsias He 	}
3243df5d593SAsias He 
3253df5d593SAsias He 	/*
3263df5d593SAsias He 	 * Ignore guest to server frames with zero tcp payload
3273df5d593SAsias He 	 */
3283df5d593SAsias He 	if (uip_tcp_payloadlen(tcp) == 0)
3293df5d593SAsias He 		goto out;
3303df5d593SAsias He 
3313df5d593SAsias He 	/*
3323df5d593SAsias He 	 * Sent out TCP data to remote host
3333df5d593SAsias He 	 */
3343df5d593SAsias He 	ret = uip_tcp_socket_send(sk, tcp);
3353df5d593SAsias He 	if (ret < 0)
3363df5d593SAsias He 		return -1;
3373df5d593SAsias He 	/*
3383df5d593SAsias He 	 * Send ACK to guest imediately
3393df5d593SAsias He 	 */
3403df5d593SAsias He 	sk->ack_server += ret;
3413df5d593SAsias He 	uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, 0);
3423df5d593SAsias He 
3433df5d593SAsias He out:
3443df5d593SAsias He 	return 0;
3453df5d593SAsias He }
346