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