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