xref: /kvmtool/net/uip/tcp.c (revision 195544b78f883bd999ed2a4854a79aa1a7a9b633)
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>
6*195544b7SAsias 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;
71*195544b7SAsias He 	sk->addr.sin_addr.s_addr	= dip;
72*195544b7SAsias He 
73*195544b7SAsias He 	if (ntohl(dip) == arg->info->host_ip)
74*195544b7SAsias He 		sk->addr.sin_addr.s_addr = inet_addr("127.0.0.1");
753df5d593SAsias He 
763df5d593SAsias He 	ret = connect(sk->fd, (struct sockaddr *)&sk->addr, sizeof(sk->addr));
773df5d593SAsias He 	if (ret) {
783df5d593SAsias He 		free(sk);
793df5d593SAsias He 		return NULL;
803df5d593SAsias He 	}
813df5d593SAsias He 
823df5d593SAsias He 	sk->sip		= ip->sip;
833df5d593SAsias He 	sk->dip		= ip->dip;
843df5d593SAsias He 	sk->sport	= tcp->sport;
853df5d593SAsias He 	sk->dport	= tcp->dport;
863df5d593SAsias He 
873df5d593SAsias He 	mutex_lock(sk_lock);
883df5d593SAsias He 	list_add_tail(&sk->list, sk_head);
893df5d593SAsias He 	mutex_unlock(sk_lock);
903df5d593SAsias He 
913df5d593SAsias He 	return sk;
923df5d593SAsias He }
933df5d593SAsias He 
943df5d593SAsias He static int uip_tcp_payload_send(struct uip_tcp_socket *sk, u8 flag, u16 payload_len)
953df5d593SAsias He {
963df5d593SAsias He 	struct uip_info *info;
973df5d593SAsias He 	struct uip_eth *eth2;
983df5d593SAsias He 	struct uip_tcp *tcp2;
993df5d593SAsias He 	struct uip_buf *buf;
1003df5d593SAsias He 	struct uip_ip *ip2;
1013df5d593SAsias He 
1023df5d593SAsias He 	info		= sk->info;
1033df5d593SAsias He 
1043df5d593SAsias He 	/*
1053df5d593SAsias He 	 * Get free buffer to send data to guest
1063df5d593SAsias He 	 */
1073df5d593SAsias He 	buf		= uip_buf_get_free(info);
1083df5d593SAsias He 
1093df5d593SAsias He 	/*
1103df5d593SAsias He 	 * Cook a ethernet frame
1113df5d593SAsias He 	 */
1123df5d593SAsias He 	tcp2		= (struct uip_tcp *)buf->eth;
1133df5d593SAsias He 	eth2		= (struct uip_eth *)buf->eth;
1143df5d593SAsias He 	ip2		= (struct uip_ip *)buf->eth;
1153df5d593SAsias He 
1163df5d593SAsias He 	eth2->src	= info->host_mac;
1173df5d593SAsias He 	eth2->dst	= info->guest_mac;
1183df5d593SAsias He 	eth2->type	= htons(UIP_ETH_P_IP);
1193df5d593SAsias He 
1203df5d593SAsias He 	ip2->vhl	= UIP_IP_VER_4 | UIP_IP_HDR_LEN;
1213df5d593SAsias He 	ip2->tos	= 0;
1223df5d593SAsias He 	ip2->id		= 0;
1233df5d593SAsias He 	ip2->flgfrag	= 0;
1243df5d593SAsias He 	ip2->ttl	= UIP_IP_TTL;
1253df5d593SAsias He 	ip2->proto	= UIP_IP_P_TCP;
1263df5d593SAsias He 	ip2->csum	= 0;
1273df5d593SAsias He 	ip2->sip	= sk->dip;
1283df5d593SAsias He 	ip2->dip	= sk->sip;
1293df5d593SAsias He 
1303df5d593SAsias He 	tcp2->sport	= sk->dport;
1313df5d593SAsias He 	tcp2->dport	= sk->sport;
1323df5d593SAsias He 	tcp2->seq	= htonl(sk->seq_server);
1333df5d593SAsias He 	tcp2->ack	= htonl(sk->ack_server);
1343df5d593SAsias He 	/*
1353df5d593SAsias He 	 * Diable TCP options, tcp hdr len equals 20 bytes
1363df5d593SAsias He 	 */
1373df5d593SAsias He 	tcp2->off	= UIP_TCP_HDR_LEN;
1383df5d593SAsias He 	tcp2->flg	= flag;
1393df5d593SAsias He 	tcp2->win	= htons(UIP_TCP_WIN_SIZE);
1403df5d593SAsias He 	tcp2->csum	= 0;
1413df5d593SAsias He 	tcp2->urgent	= 0;
1423df5d593SAsias He 
1433df5d593SAsias He 	if (payload_len > 0)
1443df5d593SAsias He 		memcpy(uip_tcp_payload(tcp2), sk->payload, payload_len);
1453df5d593SAsias He 
1463df5d593SAsias He 	ip2->len	= htons(uip_tcp_hdrlen(tcp2) + payload_len + uip_ip_hdrlen(ip2));
1473df5d593SAsias He 	ip2->csum	= uip_csum_ip(ip2);
1483df5d593SAsias He 	tcp2->csum	= uip_csum_tcp(tcp2);
1493df5d593SAsias He 
1503df5d593SAsias He 	/*
1513df5d593SAsias He 	 * virtio_net_hdr
1523df5d593SAsias He 	 */
1533df5d593SAsias He 	buf->vnet_len	= sizeof(struct virtio_net_hdr);
1543df5d593SAsias He 	memset(buf->vnet, 0, buf->vnet_len);
1553df5d593SAsias He 
1563df5d593SAsias He 	buf->eth_len	= ntohs(ip2->len) + uip_eth_hdrlen(&ip2->eth);
1573df5d593SAsias He 
1583df5d593SAsias He 	/*
1593df5d593SAsias He 	 * Increase server seq
1603df5d593SAsias He 	 */
1613df5d593SAsias He 	sk->seq_server  += payload_len;
1623df5d593SAsias He 
1633df5d593SAsias He 	/*
1643df5d593SAsias He 	 * Send data received from socket to guest
1653df5d593SAsias He 	 */
1663df5d593SAsias He 	uip_buf_set_used(info, buf);
1673df5d593SAsias He 
1683df5d593SAsias He 	return 0;
1693df5d593SAsias He }
1703df5d593SAsias He 
1713df5d593SAsias He static void *uip_tcp_socket_thread(void *p)
1723df5d593SAsias He {
1733df5d593SAsias He 	struct uip_tcp_socket *sk;
1743df5d593SAsias He 	u8 *payload;
1753df5d593SAsias He 	int ret;
1763df5d593SAsias He 
1773df5d593SAsias He 	sk = p;
1783df5d593SAsias He 
1793df5d593SAsias He 	payload = malloc(UIP_MAX_TCP_PAYLOAD);
1803df5d593SAsias He 	sk->payload = payload;
1813df5d593SAsias He 	if (!sk->payload)
1823df5d593SAsias He 		goto out;
1833df5d593SAsias He 
1843df5d593SAsias He 	while (1) {
1853df5d593SAsias He 
1863df5d593SAsias He 		ret = read(sk->fd, payload, UIP_MAX_TCP_PAYLOAD);
1873df5d593SAsias He 
1883df5d593SAsias He 		if (ret <= 0 || ret > UIP_MAX_TCP_PAYLOAD)
1893df5d593SAsias He 			goto out;
1903df5d593SAsias He 
1913df5d593SAsias He 		uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, ret);
1923df5d593SAsias He 
1933df5d593SAsias He 	}
1943df5d593SAsias He 
1953df5d593SAsias He out:
1963df5d593SAsias He 	/*
1973df5d593SAsias He 	 * Close server to guest TCP connection
1983df5d593SAsias He 	 */
1993df5d593SAsias He 	uip_tcp_socket_close(sk, SHUT_RD);
2003df5d593SAsias He 
2013df5d593SAsias He 	uip_tcp_payload_send(sk, UIP_TCP_FLAG_FIN | UIP_TCP_FLAG_ACK, 0);
2023df5d593SAsias He 	sk->seq_server += 1;
2033df5d593SAsias He 
2043df5d593SAsias He 	sk->read_done = 1;
2053df5d593SAsias He 
2063df5d593SAsias He 	free(sk->payload);
2073df5d593SAsias He 	pthread_exit(NULL);
2083df5d593SAsias He 
2093df5d593SAsias He 	return NULL;
2103df5d593SAsias He }
2113df5d593SAsias He 
2123df5d593SAsias He static int uip_tcp_socket_receive(struct uip_tcp_socket *sk)
2133df5d593SAsias He {
2143df5d593SAsias He 	if (sk->thread == 0)
2153df5d593SAsias He 		return pthread_create(&sk->thread, NULL, uip_tcp_socket_thread, (void *)sk);
2163df5d593SAsias He 
2173df5d593SAsias He 	return 0;
2183df5d593SAsias He }
2193df5d593SAsias He 
2203df5d593SAsias He static int uip_tcp_socket_send(struct uip_tcp_socket *sk, struct uip_tcp *tcp)
2213df5d593SAsias He {
2223df5d593SAsias He 	int len;
2233df5d593SAsias He 	int ret;
2243df5d593SAsias He 	u8 *payload;
2253df5d593SAsias He 
2263df5d593SAsias He 	if (sk->write_done)
2273df5d593SAsias He 		return 0;
2283df5d593SAsias He 
2293df5d593SAsias He 	payload = uip_tcp_payload(tcp);
2303df5d593SAsias He 	len = uip_tcp_payloadlen(tcp);
2313df5d593SAsias He 
2323df5d593SAsias He 	ret = write(sk->fd, payload, len);
2333df5d593SAsias He 	if (ret != len)
2343df5d593SAsias He 		pr_warning("tcp send error");
2353df5d593SAsias He 
2363df5d593SAsias He 	return ret;
2373df5d593SAsias He }
2383df5d593SAsias He 
2393df5d593SAsias He int uip_tx_do_ipv4_tcp(struct uip_tx_arg *arg)
2403df5d593SAsias He {
2413df5d593SAsias He 	struct uip_tcp_socket *sk;
2423df5d593SAsias He 	struct uip_tcp *tcp;
2433df5d593SAsias He 	struct uip_ip *ip;
2443df5d593SAsias He 	int ret;
2453df5d593SAsias He 
2463df5d593SAsias He 	tcp = (struct uip_tcp *)arg->eth;
2473df5d593SAsias He 	ip = (struct uip_ip *)arg->eth;
2483df5d593SAsias He 
2493df5d593SAsias He 	/*
2503df5d593SAsias He 	 * Guest is trying to start a TCP session, let's fake SYN-ACK to guest
2513df5d593SAsias He 	 */
2523df5d593SAsias He 	if (uip_tcp_is_syn(tcp)) {
2533df5d593SAsias He 		sk = uip_tcp_socket_alloc(arg, ip->sip, ip->dip, tcp->sport, tcp->dport);
2543df5d593SAsias He 		if (!sk)
2553df5d593SAsias He 			return -1;
2563df5d593SAsias He 
2573df5d593SAsias He 		/*
2583df5d593SAsias He 		 * Setup ISN number
2593df5d593SAsias He 		 */
2603df5d593SAsias He 		sk->isn_guest  = uip_tcp_isn(tcp);
2613df5d593SAsias He 		sk->isn_server = uip_tcp_isn_alloc();
2623df5d593SAsias He 
2633df5d593SAsias He 		sk->seq_server = sk->isn_server;
2643df5d593SAsias He 		sk->ack_server = sk->isn_guest + 1;
2653df5d593SAsias He 		uip_tcp_payload_send(sk, UIP_TCP_FLAG_SYN | UIP_TCP_FLAG_ACK, 0);
2663df5d593SAsias He 		sk->seq_server += 1;
2673df5d593SAsias He 
2683df5d593SAsias He 		/*
2693df5d593SAsias He 		 * Start receive thread for data from remote to guest
2703df5d593SAsias He 		 */
2713df5d593SAsias He 		uip_tcp_socket_receive(sk);
2723df5d593SAsias He 
2733df5d593SAsias He 		goto out;
2743df5d593SAsias He 	}
2753df5d593SAsias He 
2763df5d593SAsias He 	/*
2773df5d593SAsias He 	 * Find socket we have allocated
2783df5d593SAsias He 	 */
2793df5d593SAsias He 	sk = uip_tcp_socket_find(arg, ip->sip, ip->dip, tcp->sport, tcp->dport);
2803df5d593SAsias He 	if (!sk)
2813df5d593SAsias He 		return -1;
2823df5d593SAsias He 
2833df5d593SAsias He 	sk->guest_acked = ntohl(tcp->ack);
2843df5d593SAsias He 
2853df5d593SAsias He 	if (uip_tcp_is_fin(tcp)) {
2863df5d593SAsias He 		if (sk->write_done)
2873df5d593SAsias He 			goto out;
2883df5d593SAsias He 
2893df5d593SAsias He 		sk->write_done = 1;
2903df5d593SAsias He 		sk->ack_server += 1;
2913df5d593SAsias He 		uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, 0);
2923df5d593SAsias He 
2933df5d593SAsias He 		/*
2943df5d593SAsias He 		 * Close guest to server TCP connection
2953df5d593SAsias He 		 */
2963df5d593SAsias He 		uip_tcp_socket_close(sk, SHUT_WR);
2973df5d593SAsias He 
2983df5d593SAsias He 		goto out;
2993df5d593SAsias He 	}
3003df5d593SAsias He 
3013df5d593SAsias He 	/*
3023df5d593SAsias He 	 * Ignore guest to server frames with zero tcp payload
3033df5d593SAsias He 	 */
3043df5d593SAsias He 	if (uip_tcp_payloadlen(tcp) == 0)
3053df5d593SAsias He 		goto out;
3063df5d593SAsias He 
3073df5d593SAsias He 	/*
3083df5d593SAsias He 	 * Sent out TCP data to remote host
3093df5d593SAsias He 	 */
3103df5d593SAsias He 	ret = uip_tcp_socket_send(sk, tcp);
3113df5d593SAsias He 	if (ret < 0)
3123df5d593SAsias He 		return -1;
3133df5d593SAsias He 	/*
3143df5d593SAsias He 	 * Send ACK to guest imediately
3153df5d593SAsias He 	 */
3163df5d593SAsias He 	sk->ack_server += ret;
3173df5d593SAsias He 	uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, 0);
3183df5d593SAsias He 
3193df5d593SAsias He out:
3203df5d593SAsias He 	return 0;
3213df5d593SAsias He }
322