kvm tools: Add ivshmem device
[linux-2.6/next.git] / tools / kvm / net / uip / tcp.c
blob586a45cfd448353de85119f81ce43e754ee0a8da
1 #include "kvm/uip.h"
3 #include <linux/virtio_net.h>
4 #include <linux/kernel.h>
5 #include <linux/list.h>
7 static int uip_tcp_socket_close(struct uip_tcp_socket *sk, int how)
9 shutdown(sk->fd, how);
11 if (sk->write_done && sk->read_done) {
12 shutdown(sk->fd, SHUT_RDWR);
13 close(sk->fd);
15 mutex_lock(sk->lock);
16 list_del(&sk->list);
17 mutex_unlock(sk->lock);
19 free(sk);
22 return 0;
25 static struct uip_tcp_socket *uip_tcp_socket_find(struct uip_tx_arg *arg, u32 sip, u32 dip, u16 sport, u16 dport)
27 struct list_head *sk_head;
28 pthread_mutex_t *sk_lock;
29 struct uip_tcp_socket *sk;
31 sk_head = &arg->info->tcp_socket_head;
32 sk_lock = &arg->info->tcp_socket_lock;
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;
41 mutex_unlock(sk_lock);
43 return NULL;
46 static struct uip_tcp_socket *uip_tcp_socket_alloc(struct uip_tx_arg *arg, u32 sip, u32 dip, u16 sport, u16 dport)
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;
55 tcp = (struct uip_tcp *)arg->eth;
56 ip = (struct uip_ip *)arg->eth;
58 sk_head = &arg->info->tcp_socket_head;
59 sk_lock = &arg->info->tcp_socket_lock;
61 sk = malloc(sizeof(*sk));
62 memset(sk, 0, sizeof(*sk));
64 sk->lock = sk_lock;
65 sk->info = arg->info;
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;
72 ret = connect(sk->fd, (struct sockaddr *)&sk->addr, sizeof(sk->addr));
73 if (ret) {
74 free(sk);
75 return NULL;
78 sk->sip = ip->sip;
79 sk->dip = ip->dip;
80 sk->sport = tcp->sport;
81 sk->dport = tcp->dport;
83 mutex_lock(sk_lock);
84 list_add_tail(&sk->list, sk_head);
85 mutex_unlock(sk_lock);
87 return sk;
90 static int uip_tcp_payload_send(struct uip_tcp_socket *sk, u8 flag, u16 payload_len)
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;
98 info = sk->info;
101 * Get free buffer to send data to guest
103 buf = uip_buf_get_free(info);
106 * Cook a ethernet frame
108 tcp2 = (struct uip_tcp *)buf->eth;
109 eth2 = (struct uip_eth *)buf->eth;
110 ip2 = (struct uip_ip *)buf->eth;
112 eth2->src = info->host_mac;
113 eth2->dst = info->guest_mac;
114 eth2->type = htons(UIP_ETH_P_IP);
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;
126 tcp2->sport = sk->dport;
127 tcp2->dport = sk->sport;
128 tcp2->seq = htonl(sk->seq_server);
129 tcp2->ack = htonl(sk->ack_server);
131 * Diable TCP options, tcp hdr len equals 20 bytes
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;
139 if (payload_len > 0)
140 memcpy(uip_tcp_payload(tcp2), sk->payload, payload_len);
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);
147 * virtio_net_hdr
149 buf->vnet_len = sizeof(struct virtio_net_hdr);
150 memset(buf->vnet, 0, buf->vnet_len);
152 buf->eth_len = ntohs(ip2->len) + uip_eth_hdrlen(&ip2->eth);
155 * Increase server seq
157 sk->seq_server += payload_len;
160 * Send data received from socket to guest
162 uip_buf_set_used(info, buf);
164 return 0;
167 static void *uip_tcp_socket_thread(void *p)
169 struct uip_tcp_socket *sk;
170 u8 *payload;
171 int ret;
173 sk = p;
175 payload = malloc(UIP_MAX_TCP_PAYLOAD);
176 sk->payload = payload;
177 if (!sk->payload)
178 goto out;
180 while (1) {
182 ret = read(sk->fd, payload, UIP_MAX_TCP_PAYLOAD);
184 if (ret <= 0 || ret > UIP_MAX_TCP_PAYLOAD)
185 goto out;
187 uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, ret);
191 out:
193 * Close server to guest TCP connection
195 uip_tcp_socket_close(sk, SHUT_RD);
197 uip_tcp_payload_send(sk, UIP_TCP_FLAG_FIN | UIP_TCP_FLAG_ACK, 0);
198 sk->seq_server += 1;
200 sk->read_done = 1;
202 free(sk->payload);
203 pthread_exit(NULL);
205 return NULL;
208 static int uip_tcp_socket_receive(struct uip_tcp_socket *sk)
210 if (sk->thread == 0)
211 return pthread_create(&sk->thread, NULL, uip_tcp_socket_thread, (void *)sk);
213 return 0;
216 static int uip_tcp_socket_send(struct uip_tcp_socket *sk, struct uip_tcp *tcp)
218 int len;
219 int ret;
220 u8 *payload;
222 if (sk->write_done)
223 return 0;
225 payload = uip_tcp_payload(tcp);
226 len = uip_tcp_payloadlen(tcp);
228 ret = write(sk->fd, payload, len);
229 if (ret != len)
230 pr_warning("tcp send error");
232 return ret;
235 int uip_tx_do_ipv4_tcp(struct uip_tx_arg *arg)
237 struct uip_tcp_socket *sk;
238 struct uip_tcp *tcp;
239 struct uip_ip *ip;
240 int ret;
242 tcp = (struct uip_tcp *)arg->eth;
243 ip = (struct uip_ip *)arg->eth;
246 * Guest is trying to start a TCP session, let's fake SYN-ACK to guest
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;
254 * Setup ISN number
256 sk->isn_guest = uip_tcp_isn(tcp);
257 sk->isn_server = uip_tcp_isn_alloc();
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;
265 * Start receive thread for data from remote to guest
267 uip_tcp_socket_receive(sk);
269 goto out;
273 * Find socket we have allocated
275 sk = uip_tcp_socket_find(arg, ip->sip, ip->dip, tcp->sport, tcp->dport);
276 if (!sk)
277 return -1;
279 sk->guest_acked = ntohl(tcp->ack);
281 if (uip_tcp_is_fin(tcp)) {
282 if (sk->write_done)
283 goto out;
285 sk->write_done = 1;
286 sk->ack_server += 1;
287 uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, 0);
290 * Close guest to server TCP connection
292 uip_tcp_socket_close(sk, SHUT_WR);
294 goto out;
298 * Ignore guest to server frames with zero tcp payload
300 if (uip_tcp_payloadlen(tcp) == 0)
301 goto out;
304 * Sent out TCP data to remote host
306 ret = uip_tcp_socket_send(sk, tcp);
307 if (ret < 0)
308 return -1;
310 * Send ACK to guest imediately
312 sk->ack_server += ret;
313 uip_tcp_payload_send(sk, UIP_TCP_FLAG_ACK, 0);
315 out:
316 return 0;