1605d52e6SDmitry Fleytman /* 2605d52e6SDmitry Fleytman * QEMU TX packets abstractions 3605d52e6SDmitry Fleytman * 4605d52e6SDmitry Fleytman * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) 5605d52e6SDmitry Fleytman * 6605d52e6SDmitry Fleytman * Developed by Daynix Computing LTD (http://www.daynix.com) 7605d52e6SDmitry Fleytman * 8605d52e6SDmitry Fleytman * Authors: 9605d52e6SDmitry Fleytman * Dmitry Fleytman <dmitry@daynix.com> 10605d52e6SDmitry Fleytman * Tamir Shomer <tamirs@daynix.com> 11605d52e6SDmitry Fleytman * Yan Vugenfirer <yan@daynix.com> 12605d52e6SDmitry Fleytman * 13605d52e6SDmitry Fleytman * This work is licensed under the terms of the GNU GPL, version 2 or later. 14605d52e6SDmitry Fleytman * See the COPYING file in the top-level directory. 15605d52e6SDmitry Fleytman * 16605d52e6SDmitry Fleytman */ 17605d52e6SDmitry Fleytman 18e9abfcb5SPaolo Bonzini #include "qemu/osdep.h" 19f199b13bSAkihiko Odaki #include "qemu/crc32c.h" 20605d52e6SDmitry Fleytman #include "net/eth.h" 21605d52e6SDmitry Fleytman #include "net/checksum.h" 22605d52e6SDmitry Fleytman #include "net/tap.h" 23605d52e6SDmitry Fleytman #include "net/net.h" 24edf5ca5dSMarkus Armbruster #include "hw/pci/pci_device.h" 25a51db580SAkihiko Odaki #include "net_tx_pkt.h" 26605d52e6SDmitry Fleytman 27605d52e6SDmitry Fleytman enum { 28605d52e6SDmitry Fleytman NET_TX_PKT_VHDR_FRAG = 0, 29605d52e6SDmitry Fleytman NET_TX_PKT_L2HDR_FRAG, 30605d52e6SDmitry Fleytman NET_TX_PKT_L3HDR_FRAG, 31605d52e6SDmitry Fleytman NET_TX_PKT_PL_START_FRAG 32605d52e6SDmitry Fleytman }; 33605d52e6SDmitry Fleytman 34605d52e6SDmitry Fleytman /* TX packet private context */ 35605d52e6SDmitry Fleytman struct NetTxPkt { 36605d52e6SDmitry Fleytman struct virtio_net_hdr virt_hdr; 37605d52e6SDmitry Fleytman 38605d52e6SDmitry Fleytman struct iovec *raw; 39605d52e6SDmitry Fleytman uint32_t raw_frags; 40605d52e6SDmitry Fleytman uint32_t max_raw_frags; 41605d52e6SDmitry Fleytman 42605d52e6SDmitry Fleytman struct iovec *vec; 43605d52e6SDmitry Fleytman 44aaa8a15cSAkihiko Odaki struct { 45aaa8a15cSAkihiko Odaki struct eth_header eth; 46aaa8a15cSAkihiko Odaki struct vlan_header vlan[3]; 47aaa8a15cSAkihiko Odaki } l2_hdr; 482a5f744eSAkihiko Odaki union { 492a5f744eSAkihiko Odaki struct ip_header ip; 502a5f744eSAkihiko Odaki struct ip6_header ip6; 512a5f744eSAkihiko Odaki uint8_t octets[ETH_MAX_IP_DGRAM_LEN]; 522a5f744eSAkihiko Odaki } l3_hdr; 53605d52e6SDmitry Fleytman 54605d52e6SDmitry Fleytman uint32_t payload_len; 55605d52e6SDmitry Fleytman 56605d52e6SDmitry Fleytman uint32_t payload_frags; 57605d52e6SDmitry Fleytman uint32_t max_payload_frags; 58605d52e6SDmitry Fleytman 59605d52e6SDmitry Fleytman uint16_t hdr_len; 60605d52e6SDmitry Fleytman eth_pkt_types_e packet_type; 61605d52e6SDmitry Fleytman uint8_t l4proto; 62605d52e6SDmitry Fleytman }; 63605d52e6SDmitry Fleytman 64a51db580SAkihiko Odaki void net_tx_pkt_init(struct NetTxPkt **pkt, uint32_t max_frags) 65605d52e6SDmitry Fleytman { 66605d52e6SDmitry Fleytman struct NetTxPkt *p = g_malloc0(sizeof *p); 67605d52e6SDmitry Fleytman 6847882fa4SLi Qiang p->vec = g_new(struct iovec, max_frags + NET_TX_PKT_PL_START_FRAG); 69605d52e6SDmitry Fleytman 7047882fa4SLi Qiang p->raw = g_new(struct iovec, max_frags); 71605d52e6SDmitry Fleytman 72605d52e6SDmitry Fleytman p->max_payload_frags = max_frags; 73605d52e6SDmitry Fleytman p->max_raw_frags = max_frags; 74605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; 7555daf493SAkihiko Odaki p->vec[NET_TX_PKT_VHDR_FRAG].iov_len = sizeof p->virt_hdr; 76605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; 77eb700029SDmitry Fleytman p->vec[NET_TX_PKT_L3HDR_FRAG].iov_base = &p->l3_hdr; 78605d52e6SDmitry Fleytman 79605d52e6SDmitry Fleytman *pkt = p; 80605d52e6SDmitry Fleytman } 81605d52e6SDmitry Fleytman 82605d52e6SDmitry Fleytman void net_tx_pkt_uninit(struct NetTxPkt *pkt) 83605d52e6SDmitry Fleytman { 84605d52e6SDmitry Fleytman if (pkt) { 85605d52e6SDmitry Fleytman g_free(pkt->vec); 86605d52e6SDmitry Fleytman g_free(pkt->raw); 87605d52e6SDmitry Fleytman g_free(pkt); 88605d52e6SDmitry Fleytman } 89605d52e6SDmitry Fleytman } 90605d52e6SDmitry Fleytman 91eb700029SDmitry Fleytman void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt) 92eb700029SDmitry Fleytman { 93eb700029SDmitry Fleytman uint16_t csum; 94eb700029SDmitry Fleytman assert(pkt); 95eb700029SDmitry Fleytman 962a5f744eSAkihiko Odaki pkt->l3_hdr.ip.ip_len = cpu_to_be16(pkt->payload_len + 97eb700029SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 98eb700029SDmitry Fleytman 992a5f744eSAkihiko Odaki pkt->l3_hdr.ip.ip_sum = 0; 1002a5f744eSAkihiko Odaki csum = net_raw_checksum(pkt->l3_hdr.octets, 101eb700029SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 1022a5f744eSAkihiko Odaki pkt->l3_hdr.ip.ip_sum = cpu_to_be16(csum); 103eb700029SDmitry Fleytman } 104eb700029SDmitry Fleytman 105605d52e6SDmitry Fleytman void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt) 106605d52e6SDmitry Fleytman { 107605d52e6SDmitry Fleytman uint16_t csum; 108eb700029SDmitry Fleytman uint32_t cntr, cso; 109605d52e6SDmitry Fleytman assert(pkt); 110605d52e6SDmitry Fleytman uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; 111eb700029SDmitry Fleytman void *ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 112605d52e6SDmitry Fleytman 113605d52e6SDmitry Fleytman if (pkt->payload_len + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len > 114605d52e6SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN) { 115605d52e6SDmitry Fleytman return; 116605d52e6SDmitry Fleytman } 117605d52e6SDmitry Fleytman 118eb700029SDmitry Fleytman if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || 119eb700029SDmitry Fleytman gso_type == VIRTIO_NET_HDR_GSO_UDP) { 120605d52e6SDmitry Fleytman /* Calculate IP header checksum */ 121eb700029SDmitry Fleytman net_tx_pkt_update_ip_hdr_checksum(pkt); 122605d52e6SDmitry Fleytman 123605d52e6SDmitry Fleytman /* Calculate IP pseudo header checksum */ 124eb700029SDmitry Fleytman cntr = eth_calc_ip4_pseudo_hdr_csum(ip_hdr, pkt->payload_len, &cso); 125eb700029SDmitry Fleytman csum = cpu_to_be16(~net_checksum_finish(cntr)); 126eb700029SDmitry Fleytman } else if (gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { 127eb700029SDmitry Fleytman /* Calculate IP pseudo header checksum */ 128eb700029SDmitry Fleytman cntr = eth_calc_ip6_pseudo_hdr_csum(ip_hdr, pkt->payload_len, 129eb700029SDmitry Fleytman IP_PROTO_TCP, &cso); 130eb700029SDmitry Fleytman csum = cpu_to_be16(~net_checksum_finish(cntr)); 131eb700029SDmitry Fleytman } else { 132eb700029SDmitry Fleytman return; 133eb700029SDmitry Fleytman } 134eb700029SDmitry Fleytman 135605d52e6SDmitry Fleytman iov_from_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, 136605d52e6SDmitry Fleytman pkt->virt_hdr.csum_offset, &csum, sizeof(csum)); 137605d52e6SDmitry Fleytman } 138605d52e6SDmitry Fleytman 139f199b13bSAkihiko Odaki bool net_tx_pkt_update_sctp_checksum(struct NetTxPkt *pkt) 140f199b13bSAkihiko Odaki { 141f199b13bSAkihiko Odaki uint32_t csum = 0; 142f199b13bSAkihiko Odaki struct iovec *pl_start_frag = pkt->vec + NET_TX_PKT_PL_START_FRAG; 143f199b13bSAkihiko Odaki 144*83ddb3dbSPhilippe Mathieu-Daudé if (iov_size(pl_start_frag, pkt->payload_frags) < 8 + sizeof(csum)) { 145*83ddb3dbSPhilippe Mathieu-Daudé return false; 146*83ddb3dbSPhilippe Mathieu-Daudé } 147*83ddb3dbSPhilippe Mathieu-Daudé 148f199b13bSAkihiko Odaki if (iov_from_buf(pl_start_frag, pkt->payload_frags, 8, &csum, sizeof(csum)) < sizeof(csum)) { 149f199b13bSAkihiko Odaki return false; 150f199b13bSAkihiko Odaki } 151f199b13bSAkihiko Odaki 152f199b13bSAkihiko Odaki csum = cpu_to_le32(iov_crc32c(0xffffffff, pl_start_frag, pkt->payload_frags)); 153f199b13bSAkihiko Odaki if (iov_from_buf(pl_start_frag, pkt->payload_frags, 8, &csum, sizeof(csum)) < sizeof(csum)) { 154f199b13bSAkihiko Odaki return false; 155f199b13bSAkihiko Odaki } 156f199b13bSAkihiko Odaki 157f199b13bSAkihiko Odaki return true; 158f199b13bSAkihiko Odaki } 159f199b13bSAkihiko Odaki 160605d52e6SDmitry Fleytman static void net_tx_pkt_calculate_hdr_len(struct NetTxPkt *pkt) 161605d52e6SDmitry Fleytman { 162605d52e6SDmitry Fleytman pkt->hdr_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len + 163605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; 164605d52e6SDmitry Fleytman } 165605d52e6SDmitry Fleytman 166605d52e6SDmitry Fleytman static bool net_tx_pkt_parse_headers(struct NetTxPkt *pkt) 167605d52e6SDmitry Fleytman { 168605d52e6SDmitry Fleytman struct iovec *l2_hdr, *l3_hdr; 169605d52e6SDmitry Fleytman size_t bytes_read; 170605d52e6SDmitry Fleytman size_t full_ip6hdr_len; 171605d52e6SDmitry Fleytman uint16_t l3_proto; 172605d52e6SDmitry Fleytman 173605d52e6SDmitry Fleytman assert(pkt); 174605d52e6SDmitry Fleytman 175605d52e6SDmitry Fleytman l2_hdr = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 176605d52e6SDmitry Fleytman l3_hdr = &pkt->vec[NET_TX_PKT_L3HDR_FRAG]; 177605d52e6SDmitry Fleytman 178605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, 179605d52e6SDmitry Fleytman ETH_MAX_L2_HDR_LEN); 180605d52e6SDmitry Fleytman if (bytes_read < sizeof(struct eth_header)) { 181605d52e6SDmitry Fleytman l2_hdr->iov_len = 0; 182605d52e6SDmitry Fleytman return false; 183605d52e6SDmitry Fleytman } 184605d52e6SDmitry Fleytman 185605d52e6SDmitry Fleytman l2_hdr->iov_len = sizeof(struct eth_header); 186605d52e6SDmitry Fleytman switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) { 187605d52e6SDmitry Fleytman case ETH_P_VLAN: 188605d52e6SDmitry Fleytman l2_hdr->iov_len += sizeof(struct vlan_header); 189605d52e6SDmitry Fleytman break; 190605d52e6SDmitry Fleytman case ETH_P_DVLAN: 191605d52e6SDmitry Fleytman l2_hdr->iov_len += 2 * sizeof(struct vlan_header); 192605d52e6SDmitry Fleytman break; 193605d52e6SDmitry Fleytman } 194605d52e6SDmitry Fleytman 195605d52e6SDmitry Fleytman if (bytes_read < l2_hdr->iov_len) { 196605d52e6SDmitry Fleytman l2_hdr->iov_len = 0; 197eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 198eb700029SDmitry Fleytman pkt->packet_type = ETH_PKT_UCAST; 199605d52e6SDmitry Fleytman return false; 200eb700029SDmitry Fleytman } else { 201eb700029SDmitry Fleytman l2_hdr->iov_len = ETH_MAX_L2_HDR_LEN; 202eb700029SDmitry Fleytman l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); 203eb700029SDmitry Fleytman pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base); 204605d52e6SDmitry Fleytman } 205605d52e6SDmitry Fleytman 206eb700029SDmitry Fleytman l3_proto = eth_get_l3_proto(l2_hdr, 1, l2_hdr->iov_len); 207605d52e6SDmitry Fleytman 208605d52e6SDmitry Fleytman switch (l3_proto) { 209605d52e6SDmitry Fleytman case ETH_P_IP: 210605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 211605d52e6SDmitry Fleytman l3_hdr->iov_base, sizeof(struct ip_header)); 212605d52e6SDmitry Fleytman 213605d52e6SDmitry Fleytman if (bytes_read < sizeof(struct ip_header)) { 214605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 215605d52e6SDmitry Fleytman return false; 216605d52e6SDmitry Fleytman } 217605d52e6SDmitry Fleytman 218605d52e6SDmitry Fleytman l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base); 219eb700029SDmitry Fleytman 220eb700029SDmitry Fleytman if (l3_hdr->iov_len < sizeof(struct ip_header)) { 221eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 222eb700029SDmitry Fleytman return false; 223eb700029SDmitry Fleytman } 224eb700029SDmitry Fleytman 2254f51e1d3SMarc-André Lureau pkt->l4proto = IP_HDR_GET_P(l3_hdr->iov_base); 226605d52e6SDmitry Fleytman 227eb700029SDmitry Fleytman if (IP_HDR_GET_LEN(l3_hdr->iov_base) != sizeof(struct ip_header)) { 228eb700029SDmitry Fleytman /* copy optional IPv4 header data if any*/ 229605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 230605d52e6SDmitry Fleytman l2_hdr->iov_len + sizeof(struct ip_header), 231605d52e6SDmitry Fleytman l3_hdr->iov_base + sizeof(struct ip_header), 232605d52e6SDmitry Fleytman l3_hdr->iov_len - sizeof(struct ip_header)); 233605d52e6SDmitry Fleytman if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) { 234605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 235605d52e6SDmitry Fleytman return false; 236605d52e6SDmitry Fleytman } 237eb700029SDmitry Fleytman } 238eb700029SDmitry Fleytman 239605d52e6SDmitry Fleytman break; 240605d52e6SDmitry Fleytman 241605d52e6SDmitry Fleytman case ETH_P_IPV6: 242eb700029SDmitry Fleytman { 243eb700029SDmitry Fleytman eth_ip6_hdr_info hdrinfo; 244eb700029SDmitry Fleytman 245605d52e6SDmitry Fleytman if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 246eb700029SDmitry Fleytman &hdrinfo)) { 247605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 248605d52e6SDmitry Fleytman return false; 249605d52e6SDmitry Fleytman } 250605d52e6SDmitry Fleytman 251eb700029SDmitry Fleytman pkt->l4proto = hdrinfo.l4proto; 252eb700029SDmitry Fleytman full_ip6hdr_len = hdrinfo.full_hdr_len; 253eb700029SDmitry Fleytman 254eb700029SDmitry Fleytman if (full_ip6hdr_len > ETH_MAX_IP_DGRAM_LEN) { 255eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 256eb700029SDmitry Fleytman return false; 257eb700029SDmitry Fleytman } 258605d52e6SDmitry Fleytman 259605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 260605d52e6SDmitry Fleytman l3_hdr->iov_base, full_ip6hdr_len); 261605d52e6SDmitry Fleytman 262605d52e6SDmitry Fleytman if (bytes_read < full_ip6hdr_len) { 263605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 264605d52e6SDmitry Fleytman return false; 265605d52e6SDmitry Fleytman } else { 266605d52e6SDmitry Fleytman l3_hdr->iov_len = full_ip6hdr_len; 267605d52e6SDmitry Fleytman } 268605d52e6SDmitry Fleytman break; 269eb700029SDmitry Fleytman } 270605d52e6SDmitry Fleytman default: 271605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 272605d52e6SDmitry Fleytman break; 273605d52e6SDmitry Fleytman } 274605d52e6SDmitry Fleytman 275605d52e6SDmitry Fleytman net_tx_pkt_calculate_hdr_len(pkt); 276605d52e6SDmitry Fleytman return true; 277605d52e6SDmitry Fleytman } 278605d52e6SDmitry Fleytman 279eb700029SDmitry Fleytman static void net_tx_pkt_rebuild_payload(struct NetTxPkt *pkt) 280605d52e6SDmitry Fleytman { 281eb700029SDmitry Fleytman pkt->payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len; 282605d52e6SDmitry Fleytman pkt->payload_frags = iov_copy(&pkt->vec[NET_TX_PKT_PL_START_FRAG], 283605d52e6SDmitry Fleytman pkt->max_payload_frags, 284605d52e6SDmitry Fleytman pkt->raw, pkt->raw_frags, 285eb700029SDmitry Fleytman pkt->hdr_len, pkt->payload_len); 286605d52e6SDmitry Fleytman } 287605d52e6SDmitry Fleytman 288605d52e6SDmitry Fleytman bool net_tx_pkt_parse(struct NetTxPkt *pkt) 289605d52e6SDmitry Fleytman { 290eb700029SDmitry Fleytman if (net_tx_pkt_parse_headers(pkt)) { 291605d52e6SDmitry Fleytman net_tx_pkt_rebuild_payload(pkt); 292eb700029SDmitry Fleytman return true; 293eb700029SDmitry Fleytman } else { 294eb700029SDmitry Fleytman return false; 295eb700029SDmitry Fleytman } 296605d52e6SDmitry Fleytman } 297605d52e6SDmitry Fleytman 298605d52e6SDmitry Fleytman struct virtio_net_hdr *net_tx_pkt_get_vhdr(struct NetTxPkt *pkt) 299605d52e6SDmitry Fleytman { 300605d52e6SDmitry Fleytman assert(pkt); 301605d52e6SDmitry Fleytman return &pkt->virt_hdr; 302605d52e6SDmitry Fleytman } 303605d52e6SDmitry Fleytman 304605d52e6SDmitry Fleytman static uint8_t net_tx_pkt_get_gso_type(struct NetTxPkt *pkt, 305605d52e6SDmitry Fleytman bool tso_enable) 306605d52e6SDmitry Fleytman { 307605d52e6SDmitry Fleytman uint8_t rc = VIRTIO_NET_HDR_GSO_NONE; 308605d52e6SDmitry Fleytman uint16_t l3_proto; 309605d52e6SDmitry Fleytman 310eb700029SDmitry Fleytman l3_proto = eth_get_l3_proto(&pkt->vec[NET_TX_PKT_L2HDR_FRAG], 1, 311605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len); 312605d52e6SDmitry Fleytman 313605d52e6SDmitry Fleytman if (!tso_enable) { 314605d52e6SDmitry Fleytman goto func_exit; 315605d52e6SDmitry Fleytman } 316605d52e6SDmitry Fleytman 317605d52e6SDmitry Fleytman rc = eth_get_gso_type(l3_proto, pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 318605d52e6SDmitry Fleytman pkt->l4proto); 319605d52e6SDmitry Fleytman 320605d52e6SDmitry Fleytman func_exit: 321605d52e6SDmitry Fleytman return rc; 322605d52e6SDmitry Fleytman } 323605d52e6SDmitry Fleytman 324f9a9eb16SAkihiko Odaki bool net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, 325605d52e6SDmitry Fleytman bool csum_enable, uint32_t gso_size) 326605d52e6SDmitry Fleytman { 327605d52e6SDmitry Fleytman struct tcp_hdr l4hdr; 328f9a9eb16SAkihiko Odaki size_t bytes_read; 329605d52e6SDmitry Fleytman assert(pkt); 330605d52e6SDmitry Fleytman 331605d52e6SDmitry Fleytman /* csum has to be enabled if tso is. */ 332605d52e6SDmitry Fleytman assert(csum_enable || !tso_enable); 333605d52e6SDmitry Fleytman 334605d52e6SDmitry Fleytman pkt->virt_hdr.gso_type = net_tx_pkt_get_gso_type(pkt, tso_enable); 335605d52e6SDmitry Fleytman 336605d52e6SDmitry Fleytman switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 337605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_NONE: 338605d52e6SDmitry Fleytman pkt->virt_hdr.hdr_len = 0; 339605d52e6SDmitry Fleytman pkt->virt_hdr.gso_size = 0; 340605d52e6SDmitry Fleytman break; 341605d52e6SDmitry Fleytman 342605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_UDP: 343eb700029SDmitry Fleytman pkt->virt_hdr.gso_size = gso_size; 344605d52e6SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header); 345605d52e6SDmitry Fleytman break; 346605d52e6SDmitry Fleytman 347605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV4: 348605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV6: 349f9a9eb16SAkihiko Odaki bytes_read = iov_to_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], 350f9a9eb16SAkihiko Odaki pkt->payload_frags, 0, &l4hdr, sizeof(l4hdr)); 35102ef5fdcSAkihiko Odaki if (bytes_read < sizeof(l4hdr) || 35202ef5fdcSAkihiko Odaki l4hdr.th_off * sizeof(uint32_t) < sizeof(l4hdr)) { 353f9a9eb16SAkihiko Odaki return false; 354f9a9eb16SAkihiko Odaki } 355f9a9eb16SAkihiko Odaki 356605d52e6SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t); 357eb700029SDmitry Fleytman pkt->virt_hdr.gso_size = gso_size; 358605d52e6SDmitry Fleytman break; 359605d52e6SDmitry Fleytman 360605d52e6SDmitry Fleytman default: 361605d52e6SDmitry Fleytman g_assert_not_reached(); 362605d52e6SDmitry Fleytman } 363605d52e6SDmitry Fleytman 364605d52e6SDmitry Fleytman if (csum_enable) { 365605d52e6SDmitry Fleytman switch (pkt->l4proto) { 366605d52e6SDmitry Fleytman case IP_PROTO_TCP: 367dd32b5eaSAkihiko Odaki if (pkt->payload_len < sizeof(struct tcp_hdr)) { 368dd32b5eaSAkihiko Odaki return false; 369dd32b5eaSAkihiko Odaki } 370605d52e6SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 371605d52e6SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 372605d52e6SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum); 373605d52e6SDmitry Fleytman break; 374605d52e6SDmitry Fleytman case IP_PROTO_UDP: 375dd32b5eaSAkihiko Odaki if (pkt->payload_len < sizeof(struct udp_hdr)) { 376dd32b5eaSAkihiko Odaki return false; 377dd32b5eaSAkihiko Odaki } 378605d52e6SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 379605d52e6SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 380605d52e6SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum); 381605d52e6SDmitry Fleytman break; 382605d52e6SDmitry Fleytman default: 383605d52e6SDmitry Fleytman break; 384605d52e6SDmitry Fleytman } 385605d52e6SDmitry Fleytman } 386f9a9eb16SAkihiko Odaki 387f9a9eb16SAkihiko Odaki return true; 388605d52e6SDmitry Fleytman } 389605d52e6SDmitry Fleytman 390eb700029SDmitry Fleytman void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt, 391eb700029SDmitry Fleytman uint16_t vlan, uint16_t vlan_ethtype) 392605d52e6SDmitry Fleytman { 393605d52e6SDmitry Fleytman assert(pkt); 394605d52e6SDmitry Fleytman 3950b117830SAkihiko Odaki eth_setup_vlan_headers(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base, 396aaa8a15cSAkihiko Odaki &pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len, 397aaa8a15cSAkihiko Odaki vlan, vlan_ethtype); 398605d52e6SDmitry Fleytman 399605d52e6SDmitry Fleytman pkt->hdr_len += sizeof(struct vlan_header); 400605d52e6SDmitry Fleytman } 401605d52e6SDmitry Fleytman 402a51db580SAkihiko Odaki bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, void *base, size_t len) 403605d52e6SDmitry Fleytman { 404605d52e6SDmitry Fleytman struct iovec *ventry; 405605d52e6SDmitry Fleytman assert(pkt); 406035e69b0SMauro Matteo Cascella 407035e69b0SMauro Matteo Cascella if (pkt->raw_frags >= pkt->max_raw_frags) { 408035e69b0SMauro Matteo Cascella return false; 409035e69b0SMauro Matteo Cascella } 410605d52e6SDmitry Fleytman 411605d52e6SDmitry Fleytman ventry = &pkt->raw[pkt->raw_frags]; 412163246e1SAkihiko Odaki ventry->iov_base = base; 413163246e1SAkihiko Odaki ventry->iov_len = len; 414eb700029SDmitry Fleytman pkt->raw_frags++; 415163246e1SAkihiko Odaki 416eb700029SDmitry Fleytman return true; 417eb700029SDmitry Fleytman } 418605d52e6SDmitry Fleytman 419eb700029SDmitry Fleytman bool net_tx_pkt_has_fragments(struct NetTxPkt *pkt) 420eb700029SDmitry Fleytman { 421eb700029SDmitry Fleytman return pkt->raw_frags > 0; 422605d52e6SDmitry Fleytman } 423605d52e6SDmitry Fleytman 424605d52e6SDmitry Fleytman eth_pkt_types_e net_tx_pkt_get_packet_type(struct NetTxPkt *pkt) 425605d52e6SDmitry Fleytman { 426605d52e6SDmitry Fleytman assert(pkt); 427605d52e6SDmitry Fleytman 428605d52e6SDmitry Fleytman return pkt->packet_type; 429605d52e6SDmitry Fleytman } 430605d52e6SDmitry Fleytman 431605d52e6SDmitry Fleytman size_t net_tx_pkt_get_total_len(struct NetTxPkt *pkt) 432605d52e6SDmitry Fleytman { 433605d52e6SDmitry Fleytman assert(pkt); 434605d52e6SDmitry Fleytman 435605d52e6SDmitry Fleytman return pkt->hdr_len + pkt->payload_len; 436605d52e6SDmitry Fleytman } 437605d52e6SDmitry Fleytman 438605d52e6SDmitry Fleytman void net_tx_pkt_dump(struct NetTxPkt *pkt) 439605d52e6SDmitry Fleytman { 440605d52e6SDmitry Fleytman #ifdef NET_TX_PKT_DEBUG 441605d52e6SDmitry Fleytman assert(pkt); 442605d52e6SDmitry Fleytman 443605d52e6SDmitry Fleytman printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, " 444605d52e6SDmitry Fleytman "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type, 445605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len, 446605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len); 447605d52e6SDmitry Fleytman #endif 448605d52e6SDmitry Fleytman } 449605d52e6SDmitry Fleytman 450a51db580SAkihiko Odaki void net_tx_pkt_reset(struct NetTxPkt *pkt, 451a51db580SAkihiko Odaki NetTxPktFreeFrag callback, void *context) 452605d52e6SDmitry Fleytman { 453605d52e6SDmitry Fleytman int i; 454605d52e6SDmitry Fleytman 455605d52e6SDmitry Fleytman /* no assert, as reset can be called before tx_pkt_init */ 456605d52e6SDmitry Fleytman if (!pkt) { 457605d52e6SDmitry Fleytman return; 458605d52e6SDmitry Fleytman } 459605d52e6SDmitry Fleytman 460605d52e6SDmitry Fleytman memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr)); 461605d52e6SDmitry Fleytman 462605d52e6SDmitry Fleytman assert(pkt->vec); 463eb700029SDmitry Fleytman 464605d52e6SDmitry Fleytman pkt->payload_len = 0; 465605d52e6SDmitry Fleytman pkt->payload_frags = 0; 466605d52e6SDmitry Fleytman 467283f0a05SThomas Huth if (pkt->max_raw_frags > 0) { 468605d52e6SDmitry Fleytman assert(pkt->raw); 469605d52e6SDmitry Fleytman for (i = 0; i < pkt->raw_frags; i++) { 470605d52e6SDmitry Fleytman assert(pkt->raw[i].iov_base); 471a51db580SAkihiko Odaki callback(context, pkt->raw[i].iov_base, pkt->raw[i].iov_len); 472283f0a05SThomas Huth } 473605d52e6SDmitry Fleytman } 474605d52e6SDmitry Fleytman pkt->raw_frags = 0; 475605d52e6SDmitry Fleytman 476605d52e6SDmitry Fleytman pkt->hdr_len = 0; 477605d52e6SDmitry Fleytman pkt->l4proto = 0; 478605d52e6SDmitry Fleytman } 479605d52e6SDmitry Fleytman 480163246e1SAkihiko Odaki void net_tx_pkt_unmap_frag_pci(void *context, void *base, size_t len) 481163246e1SAkihiko Odaki { 482163246e1SAkihiko Odaki pci_dma_unmap(context, base, len, DMA_DIRECTION_TO_DEVICE, 0); 483163246e1SAkihiko Odaki } 484163246e1SAkihiko Odaki 485a51db580SAkihiko Odaki bool net_tx_pkt_add_raw_fragment_pci(struct NetTxPkt *pkt, PCIDevice *pci_dev, 486a51db580SAkihiko Odaki dma_addr_t pa, size_t len) 487163246e1SAkihiko Odaki { 488163246e1SAkihiko Odaki dma_addr_t mapped_len = len; 489a51db580SAkihiko Odaki void *base = pci_dma_map(pci_dev, pa, &mapped_len, DMA_DIRECTION_TO_DEVICE); 490163246e1SAkihiko Odaki if (!base) { 491163246e1SAkihiko Odaki return false; 492163246e1SAkihiko Odaki } 493163246e1SAkihiko Odaki 494a51db580SAkihiko Odaki if (mapped_len != len || !net_tx_pkt_add_raw_fragment(pkt, base, len)) { 495a51db580SAkihiko Odaki net_tx_pkt_unmap_frag_pci(pci_dev, base, mapped_len); 496163246e1SAkihiko Odaki return false; 497163246e1SAkihiko Odaki } 498163246e1SAkihiko Odaki 499163246e1SAkihiko Odaki return true; 500163246e1SAkihiko Odaki } 501163246e1SAkihiko Odaki 50202ef5fdcSAkihiko Odaki static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt, 50302ef5fdcSAkihiko Odaki struct iovec *iov, uint32_t iov_len, 50402ef5fdcSAkihiko Odaki uint16_t csl) 505605d52e6SDmitry Fleytman { 506605d52e6SDmitry Fleytman uint32_t csum_cntr; 507605d52e6SDmitry Fleytman uint16_t csum = 0; 508eb700029SDmitry Fleytman uint32_t cso; 509605d52e6SDmitry Fleytman /* num of iovec without vhdr */ 510605d52e6SDmitry Fleytman size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset; 5119a8d9492SAndrew uint16_t l3_proto = eth_get_l3_proto(iov, 1, iov->iov_len); 512605d52e6SDmitry Fleytman 513605d52e6SDmitry Fleytman /* Put zero to checksum field */ 514605d52e6SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 515605d52e6SDmitry Fleytman 516605d52e6SDmitry Fleytman /* Calculate L4 TCP/UDP checksum */ 5179a8d9492SAndrew csum_cntr = 0; 5189a8d9492SAndrew cso = 0; 519605d52e6SDmitry Fleytman /* add pseudo header to csum */ 5209a8d9492SAndrew if (l3_proto == ETH_P_IP) { 5219a8d9492SAndrew csum_cntr = eth_calc_ip4_pseudo_hdr_csum( 5229a8d9492SAndrew pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 5239a8d9492SAndrew csl, &cso); 5249a8d9492SAndrew } else if (l3_proto == ETH_P_IPV6) { 5259a8d9492SAndrew csum_cntr = eth_calc_ip6_pseudo_hdr_csum( 5269a8d9492SAndrew pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 5279a8d9492SAndrew csl, pkt->l4proto, &cso); 5289a8d9492SAndrew } 529eb700029SDmitry Fleytman 530eb700029SDmitry Fleytman /* data checksum */ 531eb700029SDmitry Fleytman csum_cntr += 532eb700029SDmitry Fleytman net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl, cso); 533605d52e6SDmitry Fleytman 534605d52e6SDmitry Fleytman /* Put the checksum obtained into the packet */ 5350dacea92SEd Swierk csum = cpu_to_be16(net_checksum_finish_nozero(csum_cntr)); 536605d52e6SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 537605d52e6SDmitry Fleytman } 538605d52e6SDmitry Fleytman 539605d52e6SDmitry Fleytman #define NET_MAX_FRAG_SG_LIST (64) 540605d52e6SDmitry Fleytman 541605d52e6SDmitry Fleytman static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt *pkt, 54202ef5fdcSAkihiko Odaki int *src_idx, size_t *src_offset, size_t src_len, 54302ef5fdcSAkihiko Odaki struct iovec *dst, int *dst_idx) 544605d52e6SDmitry Fleytman { 545605d52e6SDmitry Fleytman size_t fetched = 0; 546605d52e6SDmitry Fleytman struct iovec *src = pkt->vec; 547605d52e6SDmitry Fleytman 54802ef5fdcSAkihiko Odaki while (fetched < src_len) { 549605d52e6SDmitry Fleytman 550605d52e6SDmitry Fleytman /* no more place in fragment iov */ 551605d52e6SDmitry Fleytman if (*dst_idx == NET_MAX_FRAG_SG_LIST) { 552605d52e6SDmitry Fleytman break; 553605d52e6SDmitry Fleytman } 554605d52e6SDmitry Fleytman 555605d52e6SDmitry Fleytman /* no more data in iovec */ 556605d52e6SDmitry Fleytman if (*src_idx == (pkt->payload_frags + NET_TX_PKT_PL_START_FRAG)) { 557605d52e6SDmitry Fleytman break; 558605d52e6SDmitry Fleytman } 559605d52e6SDmitry Fleytman 560605d52e6SDmitry Fleytman 561605d52e6SDmitry Fleytman dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset; 562605d52e6SDmitry Fleytman dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset, 56302ef5fdcSAkihiko Odaki src_len - fetched); 564605d52e6SDmitry Fleytman 565605d52e6SDmitry Fleytman *src_offset += dst[*dst_idx].iov_len; 566605d52e6SDmitry Fleytman fetched += dst[*dst_idx].iov_len; 567605d52e6SDmitry Fleytman 568605d52e6SDmitry Fleytman if (*src_offset == src[*src_idx].iov_len) { 569605d52e6SDmitry Fleytman *src_offset = 0; 570605d52e6SDmitry Fleytman (*src_idx)++; 571605d52e6SDmitry Fleytman } 572605d52e6SDmitry Fleytman 573605d52e6SDmitry Fleytman (*dst_idx)++; 574605d52e6SDmitry Fleytman } 575605d52e6SDmitry Fleytman 576605d52e6SDmitry Fleytman return fetched; 577605d52e6SDmitry Fleytman } 578605d52e6SDmitry Fleytman 579ffbd2dbdSAkihiko Odaki static void net_tx_pkt_sendv( 580ffbd2dbdSAkihiko Odaki void *opaque, const struct iovec *iov, int iov_cnt, 581ffbd2dbdSAkihiko Odaki const struct iovec *virt_iov, int virt_iov_cnt) 582eb700029SDmitry Fleytman { 583ffbd2dbdSAkihiko Odaki NetClientState *nc = opaque; 584ffbd2dbdSAkihiko Odaki 585ffbd2dbdSAkihiko Odaki if (qemu_get_using_vnet_hdr(nc->peer)) { 586ffbd2dbdSAkihiko Odaki qemu_sendv_packet(nc, virt_iov, virt_iov_cnt); 587eb700029SDmitry Fleytman } else { 588eb700029SDmitry Fleytman qemu_sendv_packet(nc, iov, iov_cnt); 589eb700029SDmitry Fleytman } 590eb700029SDmitry Fleytman } 591eb700029SDmitry Fleytman 59202ef5fdcSAkihiko Odaki static bool net_tx_pkt_tcp_fragment_init(struct NetTxPkt *pkt, 59302ef5fdcSAkihiko Odaki struct iovec *fragment, 59402ef5fdcSAkihiko Odaki int *pl_idx, 59502ef5fdcSAkihiko Odaki size_t *l4hdr_len, 59602ef5fdcSAkihiko Odaki int *src_idx, 59702ef5fdcSAkihiko Odaki size_t *src_offset, 59802ef5fdcSAkihiko Odaki size_t *src_len) 59902ef5fdcSAkihiko Odaki { 60002ef5fdcSAkihiko Odaki struct iovec *l4 = fragment + NET_TX_PKT_PL_START_FRAG; 60102ef5fdcSAkihiko Odaki size_t bytes_read = 0; 60202ef5fdcSAkihiko Odaki struct tcp_hdr *th; 60302ef5fdcSAkihiko Odaki 60402ef5fdcSAkihiko Odaki if (!pkt->payload_frags) { 60502ef5fdcSAkihiko Odaki return false; 60602ef5fdcSAkihiko Odaki } 60702ef5fdcSAkihiko Odaki 60802ef5fdcSAkihiko Odaki l4->iov_len = pkt->virt_hdr.hdr_len - pkt->hdr_len; 60902ef5fdcSAkihiko Odaki l4->iov_base = g_malloc(l4->iov_len); 61002ef5fdcSAkihiko Odaki 61102ef5fdcSAkihiko Odaki *src_idx = NET_TX_PKT_PL_START_FRAG; 61202ef5fdcSAkihiko Odaki while (pkt->vec[*src_idx].iov_len < l4->iov_len - bytes_read) { 61302ef5fdcSAkihiko Odaki memcpy((char *)l4->iov_base + bytes_read, pkt->vec[*src_idx].iov_base, 61402ef5fdcSAkihiko Odaki pkt->vec[*src_idx].iov_len); 61502ef5fdcSAkihiko Odaki 61602ef5fdcSAkihiko Odaki bytes_read += pkt->vec[*src_idx].iov_len; 61702ef5fdcSAkihiko Odaki 61802ef5fdcSAkihiko Odaki (*src_idx)++; 61902ef5fdcSAkihiko Odaki if (*src_idx >= pkt->payload_frags + NET_TX_PKT_PL_START_FRAG) { 62002ef5fdcSAkihiko Odaki g_free(l4->iov_base); 62102ef5fdcSAkihiko Odaki return false; 62202ef5fdcSAkihiko Odaki } 62302ef5fdcSAkihiko Odaki } 62402ef5fdcSAkihiko Odaki 62502ef5fdcSAkihiko Odaki *src_offset = l4->iov_len - bytes_read; 62602ef5fdcSAkihiko Odaki memcpy((char *)l4->iov_base + bytes_read, pkt->vec[*src_idx].iov_base, 62702ef5fdcSAkihiko Odaki *src_offset); 62802ef5fdcSAkihiko Odaki 62902ef5fdcSAkihiko Odaki th = l4->iov_base; 63002ef5fdcSAkihiko Odaki th->th_flags &= ~(TH_FIN | TH_PUSH); 63102ef5fdcSAkihiko Odaki 63202ef5fdcSAkihiko Odaki *pl_idx = NET_TX_PKT_PL_START_FRAG + 1; 63302ef5fdcSAkihiko Odaki *l4hdr_len = l4->iov_len; 63402ef5fdcSAkihiko Odaki *src_len = pkt->virt_hdr.gso_size; 63502ef5fdcSAkihiko Odaki 63602ef5fdcSAkihiko Odaki return true; 63702ef5fdcSAkihiko Odaki } 63802ef5fdcSAkihiko Odaki 63902ef5fdcSAkihiko Odaki static void net_tx_pkt_tcp_fragment_deinit(struct iovec *fragment) 64002ef5fdcSAkihiko Odaki { 64102ef5fdcSAkihiko Odaki g_free(fragment[NET_TX_PKT_PL_START_FRAG].iov_base); 64202ef5fdcSAkihiko Odaki } 64302ef5fdcSAkihiko Odaki 64402ef5fdcSAkihiko Odaki static void net_tx_pkt_tcp_fragment_fix(struct NetTxPkt *pkt, 64502ef5fdcSAkihiko Odaki struct iovec *fragment, 64602ef5fdcSAkihiko Odaki size_t fragment_len, 64702ef5fdcSAkihiko Odaki uint8_t gso_type) 64802ef5fdcSAkihiko Odaki { 64902ef5fdcSAkihiko Odaki struct iovec *l3hdr = fragment + NET_TX_PKT_L3HDR_FRAG; 65002ef5fdcSAkihiko Odaki struct iovec *l4hdr = fragment + NET_TX_PKT_PL_START_FRAG; 65102ef5fdcSAkihiko Odaki struct ip_header *ip = l3hdr->iov_base; 65202ef5fdcSAkihiko Odaki struct ip6_header *ip6 = l3hdr->iov_base; 65302ef5fdcSAkihiko Odaki size_t len = l3hdr->iov_len + l4hdr->iov_len + fragment_len; 65402ef5fdcSAkihiko Odaki 65502ef5fdcSAkihiko Odaki switch (gso_type) { 65602ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV4: 65702ef5fdcSAkihiko Odaki ip->ip_len = cpu_to_be16(len); 65802ef5fdcSAkihiko Odaki eth_fix_ip4_checksum(l3hdr->iov_base, l3hdr->iov_len); 65902ef5fdcSAkihiko Odaki break; 66002ef5fdcSAkihiko Odaki 66102ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV6: 66202ef5fdcSAkihiko Odaki len -= sizeof(struct ip6_header); 66302ef5fdcSAkihiko Odaki ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = cpu_to_be16(len); 66402ef5fdcSAkihiko Odaki break; 66502ef5fdcSAkihiko Odaki } 66602ef5fdcSAkihiko Odaki } 66702ef5fdcSAkihiko Odaki 66802ef5fdcSAkihiko Odaki static void net_tx_pkt_tcp_fragment_advance(struct NetTxPkt *pkt, 66902ef5fdcSAkihiko Odaki struct iovec *fragment, 67002ef5fdcSAkihiko Odaki size_t fragment_len, 67102ef5fdcSAkihiko Odaki uint8_t gso_type) 67202ef5fdcSAkihiko Odaki { 67302ef5fdcSAkihiko Odaki struct iovec *l3hdr = fragment + NET_TX_PKT_L3HDR_FRAG; 67402ef5fdcSAkihiko Odaki struct iovec *l4hdr = fragment + NET_TX_PKT_PL_START_FRAG; 67502ef5fdcSAkihiko Odaki struct ip_header *ip = l3hdr->iov_base; 67602ef5fdcSAkihiko Odaki struct tcp_hdr *th = l4hdr->iov_base; 67702ef5fdcSAkihiko Odaki 67802ef5fdcSAkihiko Odaki if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4) { 67902ef5fdcSAkihiko Odaki ip->ip_id = cpu_to_be16(be16_to_cpu(ip->ip_id) + 1); 68002ef5fdcSAkihiko Odaki } 68102ef5fdcSAkihiko Odaki 68202ef5fdcSAkihiko Odaki th->th_seq = cpu_to_be32(be32_to_cpu(th->th_seq) + fragment_len); 68302ef5fdcSAkihiko Odaki th->th_flags &= ~TH_CWR; 68402ef5fdcSAkihiko Odaki } 68502ef5fdcSAkihiko Odaki 68602ef5fdcSAkihiko Odaki static void net_tx_pkt_udp_fragment_init(struct NetTxPkt *pkt, 68702ef5fdcSAkihiko Odaki int *pl_idx, 68802ef5fdcSAkihiko Odaki size_t *l4hdr_len, 68902ef5fdcSAkihiko Odaki int *src_idx, size_t *src_offset, 69002ef5fdcSAkihiko Odaki size_t *src_len) 69102ef5fdcSAkihiko Odaki { 69202ef5fdcSAkihiko Odaki *pl_idx = NET_TX_PKT_PL_START_FRAG; 69302ef5fdcSAkihiko Odaki *l4hdr_len = 0; 69402ef5fdcSAkihiko Odaki *src_idx = NET_TX_PKT_PL_START_FRAG; 69502ef5fdcSAkihiko Odaki *src_offset = 0; 69602ef5fdcSAkihiko Odaki *src_len = IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size); 69702ef5fdcSAkihiko Odaki } 69802ef5fdcSAkihiko Odaki 69902ef5fdcSAkihiko Odaki static void net_tx_pkt_udp_fragment_fix(struct NetTxPkt *pkt, 70002ef5fdcSAkihiko Odaki struct iovec *fragment, 70102ef5fdcSAkihiko Odaki size_t fragment_offset, 70202ef5fdcSAkihiko Odaki size_t fragment_len) 70302ef5fdcSAkihiko Odaki { 70402ef5fdcSAkihiko Odaki bool more_frags = fragment_offset + fragment_len < pkt->payload_len; 70502ef5fdcSAkihiko Odaki uint16_t orig_flags; 70602ef5fdcSAkihiko Odaki struct iovec *l3hdr = fragment + NET_TX_PKT_L3HDR_FRAG; 70702ef5fdcSAkihiko Odaki struct ip_header *ip = l3hdr->iov_base; 70802ef5fdcSAkihiko Odaki uint16_t frag_off_units = fragment_offset / IP_FRAG_UNIT_SIZE; 70902ef5fdcSAkihiko Odaki uint16_t new_ip_off; 71002ef5fdcSAkihiko Odaki 71102ef5fdcSAkihiko Odaki assert(fragment_offset % IP_FRAG_UNIT_SIZE == 0); 71202ef5fdcSAkihiko Odaki assert((frag_off_units & ~IP_OFFMASK) == 0); 71302ef5fdcSAkihiko Odaki 71402ef5fdcSAkihiko Odaki orig_flags = be16_to_cpu(ip->ip_off) & ~(IP_OFFMASK | IP_MF); 71502ef5fdcSAkihiko Odaki new_ip_off = frag_off_units | orig_flags | (more_frags ? IP_MF : 0); 71602ef5fdcSAkihiko Odaki ip->ip_off = cpu_to_be16(new_ip_off); 71702ef5fdcSAkihiko Odaki ip->ip_len = cpu_to_be16(l3hdr->iov_len + fragment_len); 71802ef5fdcSAkihiko Odaki 71902ef5fdcSAkihiko Odaki eth_fix_ip4_checksum(l3hdr->iov_base, l3hdr->iov_len); 72002ef5fdcSAkihiko Odaki } 72102ef5fdcSAkihiko Odaki 722605d52e6SDmitry Fleytman static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, 723a51db580SAkihiko Odaki NetTxPktSend callback, 724ffbd2dbdSAkihiko Odaki void *context) 725605d52e6SDmitry Fleytman { 72602ef5fdcSAkihiko Odaki uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; 727605d52e6SDmitry Fleytman 72802ef5fdcSAkihiko Odaki struct iovec fragment[NET_MAX_FRAG_SG_LIST]; 72902ef5fdcSAkihiko Odaki size_t fragment_len; 73002ef5fdcSAkihiko Odaki size_t l4hdr_len; 73102ef5fdcSAkihiko Odaki size_t src_len; 73202ef5fdcSAkihiko Odaki 73302ef5fdcSAkihiko Odaki int src_idx, dst_idx, pl_idx; 73402ef5fdcSAkihiko Odaki size_t src_offset; 735605d52e6SDmitry Fleytman size_t fragment_offset = 0; 736ffbd2dbdSAkihiko Odaki struct virtio_net_hdr virt_hdr = { 737ffbd2dbdSAkihiko Odaki .flags = pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM ? 738ffbd2dbdSAkihiko Odaki VIRTIO_NET_HDR_F_DATA_VALID : 0 739ffbd2dbdSAkihiko Odaki }; 740605d52e6SDmitry Fleytman 741605d52e6SDmitry Fleytman /* Copy headers */ 742ffbd2dbdSAkihiko Odaki fragment[NET_TX_PKT_VHDR_FRAG].iov_base = &virt_hdr; 743ffbd2dbdSAkihiko Odaki fragment[NET_TX_PKT_VHDR_FRAG].iov_len = sizeof(virt_hdr); 74402ef5fdcSAkihiko Odaki fragment[NET_TX_PKT_L2HDR_FRAG] = pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 74502ef5fdcSAkihiko Odaki fragment[NET_TX_PKT_L3HDR_FRAG] = pkt->vec[NET_TX_PKT_L3HDR_FRAG]; 746605d52e6SDmitry Fleytman 74702ef5fdcSAkihiko Odaki switch (gso_type) { 74802ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV4: 74902ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV6: 75002ef5fdcSAkihiko Odaki if (!net_tx_pkt_tcp_fragment_init(pkt, fragment, &pl_idx, &l4hdr_len, 75102ef5fdcSAkihiko Odaki &src_idx, &src_offset, &src_len)) { 75202ef5fdcSAkihiko Odaki return false; 75302ef5fdcSAkihiko Odaki } 75402ef5fdcSAkihiko Odaki break; 75502ef5fdcSAkihiko Odaki 75602ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_UDP: 75702ef5fdcSAkihiko Odaki net_tx_pkt_do_sw_csum(pkt, &pkt->vec[NET_TX_PKT_L2HDR_FRAG], 75802ef5fdcSAkihiko Odaki pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1, 75902ef5fdcSAkihiko Odaki pkt->payload_len); 76002ef5fdcSAkihiko Odaki net_tx_pkt_udp_fragment_init(pkt, &pl_idx, &l4hdr_len, 76102ef5fdcSAkihiko Odaki &src_idx, &src_offset, &src_len); 76202ef5fdcSAkihiko Odaki break; 76302ef5fdcSAkihiko Odaki 76402ef5fdcSAkihiko Odaki default: 76502ef5fdcSAkihiko Odaki abort(); 76602ef5fdcSAkihiko Odaki } 767605d52e6SDmitry Fleytman 768605d52e6SDmitry Fleytman /* Put as much data as possible and send */ 76902ef5fdcSAkihiko Odaki while (true) { 77002ef5fdcSAkihiko Odaki dst_idx = pl_idx; 77102ef5fdcSAkihiko Odaki fragment_len = net_tx_pkt_fetch_fragment(pkt, 77202ef5fdcSAkihiko Odaki &src_idx, &src_offset, src_len, fragment, &dst_idx); 77302ef5fdcSAkihiko Odaki if (!fragment_len) { 77402ef5fdcSAkihiko Odaki break; 77502ef5fdcSAkihiko Odaki } 776605d52e6SDmitry Fleytman 77702ef5fdcSAkihiko Odaki switch (gso_type) { 77802ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV4: 77902ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV6: 78002ef5fdcSAkihiko Odaki net_tx_pkt_tcp_fragment_fix(pkt, fragment, fragment_len, gso_type); 78102ef5fdcSAkihiko Odaki net_tx_pkt_do_sw_csum(pkt, fragment + NET_TX_PKT_L2HDR_FRAG, 78202ef5fdcSAkihiko Odaki dst_idx - NET_TX_PKT_L2HDR_FRAG, 78302ef5fdcSAkihiko Odaki l4hdr_len + fragment_len); 78402ef5fdcSAkihiko Odaki break; 785605d52e6SDmitry Fleytman 78602ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_UDP: 78702ef5fdcSAkihiko Odaki net_tx_pkt_udp_fragment_fix(pkt, fragment, fragment_offset, 78802ef5fdcSAkihiko Odaki fragment_len); 78902ef5fdcSAkihiko Odaki break; 79002ef5fdcSAkihiko Odaki } 791605d52e6SDmitry Fleytman 792ffbd2dbdSAkihiko Odaki callback(context, 793ffbd2dbdSAkihiko Odaki fragment + NET_TX_PKT_L2HDR_FRAG, dst_idx - NET_TX_PKT_L2HDR_FRAG, 794ffbd2dbdSAkihiko Odaki fragment + NET_TX_PKT_VHDR_FRAG, dst_idx - NET_TX_PKT_VHDR_FRAG); 795605d52e6SDmitry Fleytman 79602ef5fdcSAkihiko Odaki if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || 79702ef5fdcSAkihiko Odaki gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { 79802ef5fdcSAkihiko Odaki net_tx_pkt_tcp_fragment_advance(pkt, fragment, fragment_len, 79902ef5fdcSAkihiko Odaki gso_type); 80002ef5fdcSAkihiko Odaki } 801605d52e6SDmitry Fleytman 80202ef5fdcSAkihiko Odaki fragment_offset += fragment_len; 80302ef5fdcSAkihiko Odaki } 80402ef5fdcSAkihiko Odaki 80502ef5fdcSAkihiko Odaki if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || 80602ef5fdcSAkihiko Odaki gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { 80702ef5fdcSAkihiko Odaki net_tx_pkt_tcp_fragment_deinit(fragment); 80802ef5fdcSAkihiko Odaki } 809605d52e6SDmitry Fleytman 810605d52e6SDmitry Fleytman return true; 811605d52e6SDmitry Fleytman } 812605d52e6SDmitry Fleytman 813605d52e6SDmitry Fleytman bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) 814605d52e6SDmitry Fleytman { 815ffbd2dbdSAkihiko Odaki bool offload = qemu_get_using_vnet_hdr(nc->peer); 816ffbd2dbdSAkihiko Odaki return net_tx_pkt_send_custom(pkt, offload, net_tx_pkt_sendv, nc); 817ffbd2dbdSAkihiko Odaki } 81855daf493SAkihiko Odaki 819ffbd2dbdSAkihiko Odaki bool net_tx_pkt_send_custom(struct NetTxPkt *pkt, bool offload, 820a51db580SAkihiko Odaki NetTxPktSend callback, void *context) 821ffbd2dbdSAkihiko Odaki { 822605d52e6SDmitry Fleytman assert(pkt); 823605d52e6SDmitry Fleytman 8244cf3a638SAkihiko Odaki uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; 8254cf3a638SAkihiko Odaki 826605d52e6SDmitry Fleytman /* 827605d52e6SDmitry Fleytman * Since underlying infrastructure does not support IP datagrams longer 828605d52e6SDmitry Fleytman * than 64K we should drop such packets and don't even try to send 829605d52e6SDmitry Fleytman */ 8304cf3a638SAkihiko Odaki if (VIRTIO_NET_HDR_GSO_NONE != gso_type) { 831605d52e6SDmitry Fleytman if (pkt->payload_len > 832605d52e6SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN - 833605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len) { 834605d52e6SDmitry Fleytman return false; 835605d52e6SDmitry Fleytman } 836605d52e6SDmitry Fleytman } 837605d52e6SDmitry Fleytman 8384cf3a638SAkihiko Odaki if (offload || gso_type == VIRTIO_NET_HDR_GSO_NONE) { 83902ef5fdcSAkihiko Odaki if (!offload && pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 84089a8de36SAkihiko Odaki pkt->virt_hdr.flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; 84102ef5fdcSAkihiko Odaki net_tx_pkt_do_sw_csum(pkt, &pkt->vec[NET_TX_PKT_L2HDR_FRAG], 84202ef5fdcSAkihiko Odaki pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1, 84302ef5fdcSAkihiko Odaki pkt->payload_len); 84402ef5fdcSAkihiko Odaki } 84502ef5fdcSAkihiko Odaki 846e219d309SAndrew net_tx_pkt_fix_ip6_payload_len(pkt); 847ffbd2dbdSAkihiko Odaki callback(context, pkt->vec + NET_TX_PKT_L2HDR_FRAG, 848ffbd2dbdSAkihiko Odaki pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - NET_TX_PKT_L2HDR_FRAG, 849ffbd2dbdSAkihiko Odaki pkt->vec + NET_TX_PKT_VHDR_FRAG, 850ffbd2dbdSAkihiko Odaki pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - NET_TX_PKT_VHDR_FRAG); 851605d52e6SDmitry Fleytman return true; 852605d52e6SDmitry Fleytman } 853605d52e6SDmitry Fleytman 854ffbd2dbdSAkihiko Odaki return net_tx_pkt_do_sw_fragmentation(pkt, callback, context); 855eb700029SDmitry Fleytman } 856e219d309SAndrew 857e219d309SAndrew void net_tx_pkt_fix_ip6_payload_len(struct NetTxPkt *pkt) 858e219d309SAndrew { 859e219d309SAndrew struct iovec *l2 = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 860e219d309SAndrew if (eth_get_l3_proto(l2, 1, l2->iov_len) == ETH_P_IPV6) { 861e219d309SAndrew /* 862e219d309SAndrew * TODO: if qemu would support >64K packets - add jumbo option check 863e219d309SAndrew * something like that: 864e219d309SAndrew * 'if (ip6->ip6_plen == 0 && !has_jumbo_option(ip6)) {' 865e219d309SAndrew */ 8662a5f744eSAkihiko Odaki if (pkt->l3_hdr.ip6.ip6_plen == 0) { 867e219d309SAndrew if (pkt->payload_len <= ETH_MAX_IP_DGRAM_LEN) { 8682a5f744eSAkihiko Odaki pkt->l3_hdr.ip6.ip6_plen = htons(pkt->payload_len); 869e219d309SAndrew } 870e219d309SAndrew /* 871e219d309SAndrew * TODO: if qemu would support >64K packets 872e219d309SAndrew * add jumbo option for packets greater then 65,535 bytes 873e219d309SAndrew */ 874e219d309SAndrew } 875e219d309SAndrew } 876e219d309SAndrew } 877