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