1e263cd49SDmitry Fleytman /* 2605d52e6SDmitry Fleytman * QEMU TX packets abstractions 3e263cd49SDmitry Fleytman * 4e263cd49SDmitry Fleytman * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) 5e263cd49SDmitry Fleytman * 6e263cd49SDmitry Fleytman * Developed by Daynix Computing LTD (http://www.daynix.com) 7e263cd49SDmitry Fleytman * 8e263cd49SDmitry Fleytman * Authors: 9e263cd49SDmitry Fleytman * Dmitry Fleytman <dmitry@daynix.com> 10e263cd49SDmitry Fleytman * Tamir Shomer <tamirs@daynix.com> 11e263cd49SDmitry Fleytman * Yan Vugenfirer <yan@daynix.com> 12e263cd49SDmitry Fleytman * 13e263cd49SDmitry Fleytman * This work is licensed under the terms of the GNU GPL, version 2 or later. 14e263cd49SDmitry Fleytman * See the COPYING file in the top-level directory. 15e263cd49SDmitry Fleytman * 16e263cd49SDmitry Fleytman */ 17e263cd49SDmitry Fleytman 18e9abfcb5SPaolo Bonzini #include "qemu/osdep.h" 19e263cd49SDmitry Fleytman #include "net/eth.h" 20e263cd49SDmitry Fleytman #include "net/checksum.h" 21e263cd49SDmitry Fleytman #include "net/tap.h" 22e263cd49SDmitry Fleytman #include "net/net.h" 23edf5ca5dSMarkus Armbruster #include "hw/pci/pci_device.h" 24a51db580SAkihiko Odaki #include "net_tx_pkt.h" 25e263cd49SDmitry Fleytman 26e263cd49SDmitry Fleytman enum { 27605d52e6SDmitry Fleytman NET_TX_PKT_VHDR_FRAG = 0, 28605d52e6SDmitry Fleytman NET_TX_PKT_L2HDR_FRAG, 29605d52e6SDmitry Fleytman NET_TX_PKT_L3HDR_FRAG, 30605d52e6SDmitry Fleytman NET_TX_PKT_PL_START_FRAG 31e263cd49SDmitry Fleytman }; 32e263cd49SDmitry Fleytman 33e263cd49SDmitry Fleytman /* TX packet private context */ 34605d52e6SDmitry Fleytman struct NetTxPkt { 35e263cd49SDmitry Fleytman struct virtio_net_hdr virt_hdr; 36e263cd49SDmitry Fleytman 37e263cd49SDmitry Fleytman struct iovec *raw; 38e263cd49SDmitry Fleytman uint32_t raw_frags; 39e263cd49SDmitry Fleytman uint32_t max_raw_frags; 40e263cd49SDmitry Fleytman 41e263cd49SDmitry Fleytman struct iovec *vec; 42e263cd49SDmitry Fleytman 43e263cd49SDmitry Fleytman uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN]; 442a5f744eSAkihiko Odaki union { 452a5f744eSAkihiko Odaki struct ip_header ip; 462a5f744eSAkihiko Odaki struct ip6_header ip6; 472a5f744eSAkihiko Odaki uint8_t octets[ETH_MAX_IP_DGRAM_LEN]; 482a5f744eSAkihiko Odaki } l3_hdr; 49e263cd49SDmitry Fleytman 50e263cd49SDmitry Fleytman uint32_t payload_len; 51e263cd49SDmitry Fleytman 52e263cd49SDmitry Fleytman uint32_t payload_frags; 53e263cd49SDmitry Fleytman uint32_t max_payload_frags; 54e263cd49SDmitry Fleytman 55e263cd49SDmitry Fleytman uint16_t hdr_len; 56e263cd49SDmitry Fleytman eth_pkt_types_e packet_type; 57e263cd49SDmitry Fleytman uint8_t l4proto; 58e263cd49SDmitry Fleytman }; 59e263cd49SDmitry Fleytman 60a51db580SAkihiko Odaki void net_tx_pkt_init(struct NetTxPkt **pkt, uint32_t max_frags) 61e263cd49SDmitry Fleytman { 62605d52e6SDmitry Fleytman struct NetTxPkt *p = g_malloc0(sizeof *p); 63e263cd49SDmitry Fleytman 6447882fa4SLi Qiang p->vec = g_new(struct iovec, max_frags + NET_TX_PKT_PL_START_FRAG); 65e263cd49SDmitry Fleytman 6647882fa4SLi Qiang p->raw = g_new(struct iovec, max_frags); 67e263cd49SDmitry Fleytman 68e263cd49SDmitry Fleytman p->max_payload_frags = max_frags; 69e263cd49SDmitry Fleytman p->max_raw_frags = max_frags; 70605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; 7155daf493SAkihiko Odaki p->vec[NET_TX_PKT_VHDR_FRAG].iov_len = sizeof p->virt_hdr; 72605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; 73eb700029SDmitry Fleytman p->vec[NET_TX_PKT_L3HDR_FRAG].iov_base = &p->l3_hdr; 74e263cd49SDmitry Fleytman 75e263cd49SDmitry Fleytman *pkt = p; 76e263cd49SDmitry Fleytman } 77e263cd49SDmitry Fleytman 78605d52e6SDmitry Fleytman void net_tx_pkt_uninit(struct NetTxPkt *pkt) 79e263cd49SDmitry Fleytman { 80e263cd49SDmitry Fleytman if (pkt) { 81e263cd49SDmitry Fleytman g_free(pkt->vec); 82e263cd49SDmitry Fleytman g_free(pkt->raw); 83e263cd49SDmitry Fleytman g_free(pkt); 84e263cd49SDmitry Fleytman } 85e263cd49SDmitry Fleytman } 86e263cd49SDmitry Fleytman 87eb700029SDmitry Fleytman void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt) 88eb700029SDmitry Fleytman { 89eb700029SDmitry Fleytman uint16_t csum; 90eb700029SDmitry Fleytman assert(pkt); 91eb700029SDmitry Fleytman 922a5f744eSAkihiko Odaki pkt->l3_hdr.ip.ip_len = cpu_to_be16(pkt->payload_len + 93eb700029SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 94eb700029SDmitry Fleytman 952a5f744eSAkihiko Odaki pkt->l3_hdr.ip.ip_sum = 0; 962a5f744eSAkihiko Odaki csum = net_raw_checksum(pkt->l3_hdr.octets, 97eb700029SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 982a5f744eSAkihiko Odaki pkt->l3_hdr.ip.ip_sum = cpu_to_be16(csum); 99eb700029SDmitry Fleytman } 100eb700029SDmitry Fleytman 101605d52e6SDmitry Fleytman void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt) 102e263cd49SDmitry Fleytman { 103e263cd49SDmitry Fleytman uint16_t csum; 104eb700029SDmitry Fleytman uint32_t cntr, cso; 105e263cd49SDmitry Fleytman assert(pkt); 106e263cd49SDmitry Fleytman uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; 107eb700029SDmitry Fleytman void *ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 108e263cd49SDmitry Fleytman 109605d52e6SDmitry Fleytman if (pkt->payload_len + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len > 110e263cd49SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN) { 111e263cd49SDmitry Fleytman return; 112e263cd49SDmitry Fleytman } 113e263cd49SDmitry Fleytman 114eb700029SDmitry Fleytman if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || 115eb700029SDmitry Fleytman gso_type == VIRTIO_NET_HDR_GSO_UDP) { 116e263cd49SDmitry Fleytman /* Calculate IP header checksum */ 117eb700029SDmitry Fleytman net_tx_pkt_update_ip_hdr_checksum(pkt); 118e263cd49SDmitry Fleytman 119e263cd49SDmitry Fleytman /* Calculate IP pseudo header checksum */ 120eb700029SDmitry Fleytman cntr = eth_calc_ip4_pseudo_hdr_csum(ip_hdr, pkt->payload_len, &cso); 121eb700029SDmitry Fleytman csum = cpu_to_be16(~net_checksum_finish(cntr)); 122eb700029SDmitry Fleytman } else if (gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { 123eb700029SDmitry Fleytman /* Calculate IP pseudo header checksum */ 124eb700029SDmitry Fleytman cntr = eth_calc_ip6_pseudo_hdr_csum(ip_hdr, pkt->payload_len, 125eb700029SDmitry Fleytman IP_PROTO_TCP, &cso); 126eb700029SDmitry Fleytman csum = cpu_to_be16(~net_checksum_finish(cntr)); 127eb700029SDmitry Fleytman } else { 128eb700029SDmitry Fleytman return; 129eb700029SDmitry Fleytman } 130eb700029SDmitry Fleytman 131605d52e6SDmitry Fleytman iov_from_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, 132e263cd49SDmitry Fleytman pkt->virt_hdr.csum_offset, &csum, sizeof(csum)); 133e263cd49SDmitry Fleytman } 134e263cd49SDmitry Fleytman 135605d52e6SDmitry Fleytman static void net_tx_pkt_calculate_hdr_len(struct NetTxPkt *pkt) 136e263cd49SDmitry Fleytman { 137605d52e6SDmitry Fleytman pkt->hdr_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len + 138605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; 139e263cd49SDmitry Fleytman } 140e263cd49SDmitry Fleytman 141605d52e6SDmitry Fleytman static bool net_tx_pkt_parse_headers(struct NetTxPkt *pkt) 142e263cd49SDmitry Fleytman { 143e263cd49SDmitry Fleytman struct iovec *l2_hdr, *l3_hdr; 144e263cd49SDmitry Fleytman size_t bytes_read; 145e263cd49SDmitry Fleytman size_t full_ip6hdr_len; 146e263cd49SDmitry Fleytman uint16_t l3_proto; 147e263cd49SDmitry Fleytman 148e263cd49SDmitry Fleytman assert(pkt); 149e263cd49SDmitry Fleytman 150605d52e6SDmitry Fleytman l2_hdr = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 151605d52e6SDmitry Fleytman l3_hdr = &pkt->vec[NET_TX_PKT_L3HDR_FRAG]; 152e263cd49SDmitry Fleytman 153e263cd49SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, 154e263cd49SDmitry Fleytman ETH_MAX_L2_HDR_LEN); 155a7278b36SDana Rubin if (bytes_read < sizeof(struct eth_header)) { 156e263cd49SDmitry Fleytman l2_hdr->iov_len = 0; 157e263cd49SDmitry Fleytman return false; 158a7278b36SDana Rubin } 159a7278b36SDana Rubin 160a7278b36SDana Rubin l2_hdr->iov_len = sizeof(struct eth_header); 161a7278b36SDana Rubin switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) { 162a7278b36SDana Rubin case ETH_P_VLAN: 163a7278b36SDana Rubin l2_hdr->iov_len += sizeof(struct vlan_header); 164a7278b36SDana Rubin break; 165a7278b36SDana Rubin case ETH_P_DVLAN: 166a7278b36SDana Rubin l2_hdr->iov_len += 2 * sizeof(struct vlan_header); 167a7278b36SDana Rubin break; 168a7278b36SDana Rubin } 169a7278b36SDana Rubin 170a7278b36SDana Rubin if (bytes_read < l2_hdr->iov_len) { 171a7278b36SDana Rubin l2_hdr->iov_len = 0; 172eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 173eb700029SDmitry Fleytman pkt->packet_type = ETH_PKT_UCAST; 174a7278b36SDana Rubin return false; 175eb700029SDmitry Fleytman } else { 176eb700029SDmitry Fleytman l2_hdr->iov_len = ETH_MAX_L2_HDR_LEN; 177eb700029SDmitry Fleytman l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); 178eb700029SDmitry Fleytman pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base); 179e263cd49SDmitry Fleytman } 180e263cd49SDmitry Fleytman 181eb700029SDmitry Fleytman l3_proto = eth_get_l3_proto(l2_hdr, 1, l2_hdr->iov_len); 182e263cd49SDmitry Fleytman 183e263cd49SDmitry Fleytman switch (l3_proto) { 184e263cd49SDmitry Fleytman case ETH_P_IP: 185e263cd49SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 186e263cd49SDmitry Fleytman l3_hdr->iov_base, sizeof(struct ip_header)); 187e263cd49SDmitry Fleytman 188e263cd49SDmitry Fleytman if (bytes_read < sizeof(struct ip_header)) { 189e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 190e263cd49SDmitry Fleytman return false; 191e263cd49SDmitry Fleytman } 192e263cd49SDmitry Fleytman 193e263cd49SDmitry Fleytman l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base); 194eb700029SDmitry Fleytman 195eb700029SDmitry Fleytman if (l3_hdr->iov_len < sizeof(struct ip_header)) { 196eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 197eb700029SDmitry Fleytman return false; 198eb700029SDmitry Fleytman } 199eb700029SDmitry Fleytman 2004f51e1d3SMarc-André Lureau pkt->l4proto = IP_HDR_GET_P(l3_hdr->iov_base); 201e263cd49SDmitry Fleytman 202eb700029SDmitry Fleytman if (IP_HDR_GET_LEN(l3_hdr->iov_base) != sizeof(struct ip_header)) { 203eb700029SDmitry Fleytman /* copy optional IPv4 header data if any*/ 204e263cd49SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 205e263cd49SDmitry Fleytman l2_hdr->iov_len + sizeof(struct ip_header), 206e263cd49SDmitry Fleytman l3_hdr->iov_base + sizeof(struct ip_header), 207e263cd49SDmitry Fleytman l3_hdr->iov_len - sizeof(struct ip_header)); 208e263cd49SDmitry Fleytman if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) { 209e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 210e263cd49SDmitry Fleytman return false; 211e263cd49SDmitry Fleytman } 212eb700029SDmitry Fleytman } 213eb700029SDmitry Fleytman 214e263cd49SDmitry Fleytman break; 215e263cd49SDmitry Fleytman 216e263cd49SDmitry Fleytman case ETH_P_IPV6: 217eb700029SDmitry Fleytman { 218eb700029SDmitry Fleytman eth_ip6_hdr_info hdrinfo; 219eb700029SDmitry Fleytman 220e263cd49SDmitry Fleytman if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 221eb700029SDmitry Fleytman &hdrinfo)) { 222e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 223e263cd49SDmitry Fleytman return false; 224e263cd49SDmitry Fleytman } 225e263cd49SDmitry Fleytman 226eb700029SDmitry Fleytman pkt->l4proto = hdrinfo.l4proto; 227eb700029SDmitry Fleytman full_ip6hdr_len = hdrinfo.full_hdr_len; 228eb700029SDmitry Fleytman 229eb700029SDmitry Fleytman if (full_ip6hdr_len > ETH_MAX_IP_DGRAM_LEN) { 230eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 231eb700029SDmitry Fleytman return false; 232eb700029SDmitry Fleytman } 233e263cd49SDmitry Fleytman 234e263cd49SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 235e263cd49SDmitry Fleytman l3_hdr->iov_base, full_ip6hdr_len); 236e263cd49SDmitry Fleytman 237e263cd49SDmitry Fleytman if (bytes_read < full_ip6hdr_len) { 238e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 239e263cd49SDmitry Fleytman return false; 240e263cd49SDmitry Fleytman } else { 241e263cd49SDmitry Fleytman l3_hdr->iov_len = full_ip6hdr_len; 242e263cd49SDmitry Fleytman } 243e263cd49SDmitry Fleytman break; 244eb700029SDmitry Fleytman } 245e263cd49SDmitry Fleytman default: 246e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 247e263cd49SDmitry Fleytman break; 248e263cd49SDmitry Fleytman } 249e263cd49SDmitry Fleytman 250605d52e6SDmitry Fleytman net_tx_pkt_calculate_hdr_len(pkt); 251e263cd49SDmitry Fleytman return true; 252e263cd49SDmitry Fleytman } 253e263cd49SDmitry Fleytman 254eb700029SDmitry Fleytman static void net_tx_pkt_rebuild_payload(struct NetTxPkt *pkt) 255e263cd49SDmitry Fleytman { 256eb700029SDmitry Fleytman pkt->payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len; 257605d52e6SDmitry Fleytman pkt->payload_frags = iov_copy(&pkt->vec[NET_TX_PKT_PL_START_FRAG], 258e263cd49SDmitry Fleytman pkt->max_payload_frags, 259e263cd49SDmitry Fleytman pkt->raw, pkt->raw_frags, 260eb700029SDmitry Fleytman pkt->hdr_len, pkt->payload_len); 261e263cd49SDmitry Fleytman } 262e263cd49SDmitry Fleytman 263605d52e6SDmitry Fleytman bool net_tx_pkt_parse(struct NetTxPkt *pkt) 264e263cd49SDmitry Fleytman { 265eb700029SDmitry Fleytman if (net_tx_pkt_parse_headers(pkt)) { 266605d52e6SDmitry Fleytman net_tx_pkt_rebuild_payload(pkt); 267eb700029SDmitry Fleytman return true; 268eb700029SDmitry Fleytman } else { 269eb700029SDmitry Fleytman return false; 270eb700029SDmitry Fleytman } 271e263cd49SDmitry Fleytman } 272e263cd49SDmitry Fleytman 273605d52e6SDmitry Fleytman struct virtio_net_hdr *net_tx_pkt_get_vhdr(struct NetTxPkt *pkt) 274e263cd49SDmitry Fleytman { 275e263cd49SDmitry Fleytman assert(pkt); 276e263cd49SDmitry Fleytman return &pkt->virt_hdr; 277e263cd49SDmitry Fleytman } 278e263cd49SDmitry Fleytman 279605d52e6SDmitry Fleytman static uint8_t net_tx_pkt_get_gso_type(struct NetTxPkt *pkt, 280e263cd49SDmitry Fleytman bool tso_enable) 281e263cd49SDmitry Fleytman { 282e263cd49SDmitry Fleytman uint8_t rc = VIRTIO_NET_HDR_GSO_NONE; 283e263cd49SDmitry Fleytman uint16_t l3_proto; 284e263cd49SDmitry Fleytman 285eb700029SDmitry Fleytman l3_proto = eth_get_l3_proto(&pkt->vec[NET_TX_PKT_L2HDR_FRAG], 1, 286605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len); 287e263cd49SDmitry Fleytman 288e263cd49SDmitry Fleytman if (!tso_enable) { 289e263cd49SDmitry Fleytman goto func_exit; 290e263cd49SDmitry Fleytman } 291e263cd49SDmitry Fleytman 292605d52e6SDmitry Fleytman rc = eth_get_gso_type(l3_proto, pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 293e263cd49SDmitry Fleytman pkt->l4proto); 294e263cd49SDmitry Fleytman 295e263cd49SDmitry Fleytman func_exit: 296e263cd49SDmitry Fleytman return rc; 297e263cd49SDmitry Fleytman } 298e263cd49SDmitry Fleytman 299f9a9eb16SAkihiko Odaki bool net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, 300e263cd49SDmitry Fleytman bool csum_enable, uint32_t gso_size) 301e263cd49SDmitry Fleytman { 302e263cd49SDmitry Fleytman struct tcp_hdr l4hdr; 303f9a9eb16SAkihiko Odaki size_t bytes_read; 304e263cd49SDmitry Fleytman assert(pkt); 305e263cd49SDmitry Fleytman 306e263cd49SDmitry Fleytman /* csum has to be enabled if tso is. */ 307e263cd49SDmitry Fleytman assert(csum_enable || !tso_enable); 308e263cd49SDmitry Fleytman 309605d52e6SDmitry Fleytman pkt->virt_hdr.gso_type = net_tx_pkt_get_gso_type(pkt, tso_enable); 310e263cd49SDmitry Fleytman 311e263cd49SDmitry Fleytman switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 312e263cd49SDmitry Fleytman case VIRTIO_NET_HDR_GSO_NONE: 313e263cd49SDmitry Fleytman pkt->virt_hdr.hdr_len = 0; 314e263cd49SDmitry Fleytman pkt->virt_hdr.gso_size = 0; 315e263cd49SDmitry Fleytman break; 316e263cd49SDmitry Fleytman 317e263cd49SDmitry Fleytman case VIRTIO_NET_HDR_GSO_UDP: 318eb700029SDmitry Fleytman pkt->virt_hdr.gso_size = gso_size; 319e263cd49SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header); 320e263cd49SDmitry Fleytman break; 321e263cd49SDmitry Fleytman 322e263cd49SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV4: 323e263cd49SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV6: 324f9a9eb16SAkihiko Odaki bytes_read = iov_to_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], 325f9a9eb16SAkihiko Odaki pkt->payload_frags, 0, &l4hdr, sizeof(l4hdr)); 32602ef5fdcSAkihiko Odaki if (bytes_read < sizeof(l4hdr) || 32702ef5fdcSAkihiko Odaki l4hdr.th_off * sizeof(uint32_t) < sizeof(l4hdr)) { 328f9a9eb16SAkihiko Odaki return false; 329f9a9eb16SAkihiko Odaki } 330f9a9eb16SAkihiko Odaki 331e263cd49SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t); 332eb700029SDmitry Fleytman pkt->virt_hdr.gso_size = gso_size; 333e263cd49SDmitry Fleytman break; 334e263cd49SDmitry Fleytman 335e263cd49SDmitry Fleytman default: 336dfc6f865SStefan Weil g_assert_not_reached(); 337e263cd49SDmitry Fleytman } 338e263cd49SDmitry Fleytman 339e263cd49SDmitry Fleytman if (csum_enable) { 340e263cd49SDmitry Fleytman switch (pkt->l4proto) { 341e263cd49SDmitry Fleytman case IP_PROTO_TCP: 342dd32b5eaSAkihiko Odaki if (pkt->payload_len < sizeof(struct tcp_hdr)) { 343dd32b5eaSAkihiko Odaki return false; 344dd32b5eaSAkihiko Odaki } 345e263cd49SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 346e263cd49SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 347e263cd49SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum); 348e263cd49SDmitry Fleytman break; 349e263cd49SDmitry Fleytman case IP_PROTO_UDP: 350dd32b5eaSAkihiko Odaki if (pkt->payload_len < sizeof(struct udp_hdr)) { 351dd32b5eaSAkihiko Odaki return false; 352dd32b5eaSAkihiko Odaki } 353e263cd49SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 354e263cd49SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 355e263cd49SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum); 356e263cd49SDmitry Fleytman break; 357e263cd49SDmitry Fleytman default: 358e263cd49SDmitry Fleytman break; 359e263cd49SDmitry Fleytman } 360e263cd49SDmitry Fleytman } 361f9a9eb16SAkihiko Odaki 362f9a9eb16SAkihiko Odaki return true; 363e263cd49SDmitry Fleytman } 364e263cd49SDmitry Fleytman 365eb700029SDmitry Fleytman void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt, 366eb700029SDmitry Fleytman uint16_t vlan, uint16_t vlan_ethtype) 367e263cd49SDmitry Fleytman { 368e263cd49SDmitry Fleytman bool is_new; 369e263cd49SDmitry Fleytman assert(pkt); 370e263cd49SDmitry Fleytman 371*0b117830SAkihiko Odaki eth_setup_vlan_headers(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base, 372eb700029SDmitry Fleytman vlan, vlan_ethtype, &is_new); 373e263cd49SDmitry Fleytman 374e263cd49SDmitry Fleytman /* update l2hdrlen */ 375e263cd49SDmitry Fleytman if (is_new) { 376e263cd49SDmitry Fleytman pkt->hdr_len += sizeof(struct vlan_header); 377605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len += 378e263cd49SDmitry Fleytman sizeof(struct vlan_header); 379e263cd49SDmitry Fleytman } 380e263cd49SDmitry Fleytman } 381e263cd49SDmitry Fleytman 382a51db580SAkihiko Odaki bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, void *base, size_t len) 383e263cd49SDmitry Fleytman { 384e263cd49SDmitry Fleytman struct iovec *ventry; 385e263cd49SDmitry Fleytman assert(pkt); 386035e69b0SMauro Matteo Cascella 387035e69b0SMauro Matteo Cascella if (pkt->raw_frags >= pkt->max_raw_frags) { 388035e69b0SMauro Matteo Cascella return false; 389035e69b0SMauro Matteo Cascella } 390e263cd49SDmitry Fleytman 391e263cd49SDmitry Fleytman ventry = &pkt->raw[pkt->raw_frags]; 392163246e1SAkihiko Odaki ventry->iov_base = base; 393163246e1SAkihiko Odaki ventry->iov_len = len; 394eb700029SDmitry Fleytman pkt->raw_frags++; 395163246e1SAkihiko Odaki 396eb700029SDmitry Fleytman return true; 397eb700029SDmitry Fleytman } 398e263cd49SDmitry Fleytman 399eb700029SDmitry Fleytman bool net_tx_pkt_has_fragments(struct NetTxPkt *pkt) 400eb700029SDmitry Fleytman { 401eb700029SDmitry Fleytman return pkt->raw_frags > 0; 402e263cd49SDmitry Fleytman } 403e263cd49SDmitry Fleytman 404605d52e6SDmitry Fleytman eth_pkt_types_e net_tx_pkt_get_packet_type(struct NetTxPkt *pkt) 405e263cd49SDmitry Fleytman { 406e263cd49SDmitry Fleytman assert(pkt); 407e263cd49SDmitry Fleytman 408e263cd49SDmitry Fleytman return pkt->packet_type; 409e263cd49SDmitry Fleytman } 410e263cd49SDmitry Fleytman 411605d52e6SDmitry Fleytman size_t net_tx_pkt_get_total_len(struct NetTxPkt *pkt) 412e263cd49SDmitry Fleytman { 413e263cd49SDmitry Fleytman assert(pkt); 414e263cd49SDmitry Fleytman 415e263cd49SDmitry Fleytman return pkt->hdr_len + pkt->payload_len; 416e263cd49SDmitry Fleytman } 417e263cd49SDmitry Fleytman 418605d52e6SDmitry Fleytman void net_tx_pkt_dump(struct NetTxPkt *pkt) 419e263cd49SDmitry Fleytman { 420605d52e6SDmitry Fleytman #ifdef NET_TX_PKT_DEBUG 421e263cd49SDmitry Fleytman assert(pkt); 422e263cd49SDmitry Fleytman 423e263cd49SDmitry Fleytman printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, " 424e263cd49SDmitry Fleytman "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type, 425605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len, 426605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len); 427e263cd49SDmitry Fleytman #endif 428e263cd49SDmitry Fleytman } 429e263cd49SDmitry Fleytman 430a51db580SAkihiko Odaki void net_tx_pkt_reset(struct NetTxPkt *pkt, 431a51db580SAkihiko Odaki NetTxPktFreeFrag callback, void *context) 432e263cd49SDmitry Fleytman { 433e263cd49SDmitry Fleytman int i; 434e263cd49SDmitry Fleytman 435e263cd49SDmitry Fleytman /* no assert, as reset can be called before tx_pkt_init */ 436e263cd49SDmitry Fleytman if (!pkt) { 437e263cd49SDmitry Fleytman return; 438e263cd49SDmitry Fleytman } 439e263cd49SDmitry Fleytman 440e263cd49SDmitry Fleytman memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr)); 441e263cd49SDmitry Fleytman 442e263cd49SDmitry Fleytman assert(pkt->vec); 443eb700029SDmitry Fleytman 444e263cd49SDmitry Fleytman pkt->payload_len = 0; 445e263cd49SDmitry Fleytman pkt->payload_frags = 0; 446e263cd49SDmitry Fleytman 447283f0a05SThomas Huth if (pkt->max_raw_frags > 0) { 448e263cd49SDmitry Fleytman assert(pkt->raw); 449e263cd49SDmitry Fleytman for (i = 0; i < pkt->raw_frags; i++) { 450e263cd49SDmitry Fleytman assert(pkt->raw[i].iov_base); 451a51db580SAkihiko Odaki callback(context, pkt->raw[i].iov_base, pkt->raw[i].iov_len); 452283f0a05SThomas Huth } 453e263cd49SDmitry Fleytman } 454e263cd49SDmitry Fleytman pkt->raw_frags = 0; 455e263cd49SDmitry Fleytman 456e263cd49SDmitry Fleytman pkt->hdr_len = 0; 457e263cd49SDmitry Fleytman pkt->l4proto = 0; 458e263cd49SDmitry Fleytman } 459e263cd49SDmitry Fleytman 460163246e1SAkihiko Odaki void net_tx_pkt_unmap_frag_pci(void *context, void *base, size_t len) 461163246e1SAkihiko Odaki { 462163246e1SAkihiko Odaki pci_dma_unmap(context, base, len, DMA_DIRECTION_TO_DEVICE, 0); 463163246e1SAkihiko Odaki } 464163246e1SAkihiko Odaki 465a51db580SAkihiko Odaki bool net_tx_pkt_add_raw_fragment_pci(struct NetTxPkt *pkt, PCIDevice *pci_dev, 466a51db580SAkihiko Odaki dma_addr_t pa, size_t len) 467163246e1SAkihiko Odaki { 468163246e1SAkihiko Odaki dma_addr_t mapped_len = len; 469a51db580SAkihiko Odaki void *base = pci_dma_map(pci_dev, pa, &mapped_len, DMA_DIRECTION_TO_DEVICE); 470163246e1SAkihiko Odaki if (!base) { 471163246e1SAkihiko Odaki return false; 472163246e1SAkihiko Odaki } 473163246e1SAkihiko Odaki 474a51db580SAkihiko Odaki if (mapped_len != len || !net_tx_pkt_add_raw_fragment(pkt, base, len)) { 475a51db580SAkihiko Odaki net_tx_pkt_unmap_frag_pci(pci_dev, base, mapped_len); 476163246e1SAkihiko Odaki return false; 477163246e1SAkihiko Odaki } 478163246e1SAkihiko Odaki 479163246e1SAkihiko Odaki return true; 480163246e1SAkihiko Odaki } 481163246e1SAkihiko Odaki 48202ef5fdcSAkihiko Odaki static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt, 48302ef5fdcSAkihiko Odaki struct iovec *iov, uint32_t iov_len, 48402ef5fdcSAkihiko Odaki uint16_t csl) 485e263cd49SDmitry Fleytman { 486e263cd49SDmitry Fleytman uint32_t csum_cntr; 487e263cd49SDmitry Fleytman uint16_t csum = 0; 488eb700029SDmitry Fleytman uint32_t cso; 489e263cd49SDmitry Fleytman /* num of iovec without vhdr */ 490e263cd49SDmitry Fleytman size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset; 4919a8d9492SAndrew uint16_t l3_proto = eth_get_l3_proto(iov, 1, iov->iov_len); 492e263cd49SDmitry Fleytman 493e263cd49SDmitry Fleytman /* Put zero to checksum field */ 494e263cd49SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 495e263cd49SDmitry Fleytman 496e263cd49SDmitry Fleytman /* Calculate L4 TCP/UDP checksum */ 4979a8d9492SAndrew csum_cntr = 0; 4989a8d9492SAndrew cso = 0; 499e263cd49SDmitry Fleytman /* add pseudo header to csum */ 5009a8d9492SAndrew if (l3_proto == ETH_P_IP) { 5019a8d9492SAndrew csum_cntr = eth_calc_ip4_pseudo_hdr_csum( 5029a8d9492SAndrew pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 5039a8d9492SAndrew csl, &cso); 5049a8d9492SAndrew } else if (l3_proto == ETH_P_IPV6) { 5059a8d9492SAndrew csum_cntr = eth_calc_ip6_pseudo_hdr_csum( 5069a8d9492SAndrew pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 5079a8d9492SAndrew csl, pkt->l4proto, &cso); 5089a8d9492SAndrew } 509eb700029SDmitry Fleytman 510eb700029SDmitry Fleytman /* data checksum */ 511eb700029SDmitry Fleytman csum_cntr += 512eb700029SDmitry Fleytman net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl, cso); 513e263cd49SDmitry Fleytman 514e263cd49SDmitry Fleytman /* Put the checksum obtained into the packet */ 5150dacea92SEd Swierk csum = cpu_to_be16(net_checksum_finish_nozero(csum_cntr)); 516e263cd49SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 517e263cd49SDmitry Fleytman } 518e263cd49SDmitry Fleytman 519605d52e6SDmitry Fleytman #define NET_MAX_FRAG_SG_LIST (64) 520e263cd49SDmitry Fleytman 521605d52e6SDmitry Fleytman static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt *pkt, 52202ef5fdcSAkihiko Odaki int *src_idx, size_t *src_offset, size_t src_len, 52302ef5fdcSAkihiko Odaki struct iovec *dst, int *dst_idx) 524e263cd49SDmitry Fleytman { 525e263cd49SDmitry Fleytman size_t fetched = 0; 526e263cd49SDmitry Fleytman struct iovec *src = pkt->vec; 527e263cd49SDmitry Fleytman 52802ef5fdcSAkihiko Odaki while (fetched < src_len) { 529e263cd49SDmitry Fleytman 530e263cd49SDmitry Fleytman /* no more place in fragment iov */ 531605d52e6SDmitry Fleytman if (*dst_idx == NET_MAX_FRAG_SG_LIST) { 532e263cd49SDmitry Fleytman break; 533e263cd49SDmitry Fleytman } 534e263cd49SDmitry Fleytman 535e263cd49SDmitry Fleytman /* no more data in iovec */ 536605d52e6SDmitry Fleytman if (*src_idx == (pkt->payload_frags + NET_TX_PKT_PL_START_FRAG)) { 537e263cd49SDmitry Fleytman break; 538e263cd49SDmitry Fleytman } 539e263cd49SDmitry Fleytman 540e263cd49SDmitry Fleytman 541e263cd49SDmitry Fleytman dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset; 542e263cd49SDmitry Fleytman dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset, 54302ef5fdcSAkihiko Odaki src_len - fetched); 544e263cd49SDmitry Fleytman 545e263cd49SDmitry Fleytman *src_offset += dst[*dst_idx].iov_len; 546e263cd49SDmitry Fleytman fetched += dst[*dst_idx].iov_len; 547e263cd49SDmitry Fleytman 548e263cd49SDmitry Fleytman if (*src_offset == src[*src_idx].iov_len) { 549e263cd49SDmitry Fleytman *src_offset = 0; 550e263cd49SDmitry Fleytman (*src_idx)++; 551e263cd49SDmitry Fleytman } 552e263cd49SDmitry Fleytman 553e263cd49SDmitry Fleytman (*dst_idx)++; 554e263cd49SDmitry Fleytman } 555e263cd49SDmitry Fleytman 556e263cd49SDmitry Fleytman return fetched; 557e263cd49SDmitry Fleytman } 558e263cd49SDmitry Fleytman 559ffbd2dbdSAkihiko Odaki static void net_tx_pkt_sendv( 560ffbd2dbdSAkihiko Odaki void *opaque, const struct iovec *iov, int iov_cnt, 561ffbd2dbdSAkihiko Odaki const struct iovec *virt_iov, int virt_iov_cnt) 562eb700029SDmitry Fleytman { 563ffbd2dbdSAkihiko Odaki NetClientState *nc = opaque; 564ffbd2dbdSAkihiko Odaki 565ffbd2dbdSAkihiko Odaki if (qemu_get_using_vnet_hdr(nc->peer)) { 566ffbd2dbdSAkihiko Odaki qemu_sendv_packet(nc, virt_iov, virt_iov_cnt); 567eb700029SDmitry Fleytman } else { 568eb700029SDmitry Fleytman qemu_sendv_packet(nc, iov, iov_cnt); 569eb700029SDmitry Fleytman } 570eb700029SDmitry Fleytman } 571eb700029SDmitry Fleytman 57202ef5fdcSAkihiko Odaki static bool net_tx_pkt_tcp_fragment_init(struct NetTxPkt *pkt, 57302ef5fdcSAkihiko Odaki struct iovec *fragment, 57402ef5fdcSAkihiko Odaki int *pl_idx, 57502ef5fdcSAkihiko Odaki size_t *l4hdr_len, 57602ef5fdcSAkihiko Odaki int *src_idx, 57702ef5fdcSAkihiko Odaki size_t *src_offset, 57802ef5fdcSAkihiko Odaki size_t *src_len) 57902ef5fdcSAkihiko Odaki { 58002ef5fdcSAkihiko Odaki struct iovec *l4 = fragment + NET_TX_PKT_PL_START_FRAG; 58102ef5fdcSAkihiko Odaki size_t bytes_read = 0; 58202ef5fdcSAkihiko Odaki struct tcp_hdr *th; 58302ef5fdcSAkihiko Odaki 58402ef5fdcSAkihiko Odaki if (!pkt->payload_frags) { 58502ef5fdcSAkihiko Odaki return false; 58602ef5fdcSAkihiko Odaki } 58702ef5fdcSAkihiko Odaki 58802ef5fdcSAkihiko Odaki l4->iov_len = pkt->virt_hdr.hdr_len - pkt->hdr_len; 58902ef5fdcSAkihiko Odaki l4->iov_base = g_malloc(l4->iov_len); 59002ef5fdcSAkihiko Odaki 59102ef5fdcSAkihiko Odaki *src_idx = NET_TX_PKT_PL_START_FRAG; 59202ef5fdcSAkihiko Odaki while (pkt->vec[*src_idx].iov_len < l4->iov_len - bytes_read) { 59302ef5fdcSAkihiko Odaki memcpy((char *)l4->iov_base + bytes_read, pkt->vec[*src_idx].iov_base, 59402ef5fdcSAkihiko Odaki pkt->vec[*src_idx].iov_len); 59502ef5fdcSAkihiko Odaki 59602ef5fdcSAkihiko Odaki bytes_read += pkt->vec[*src_idx].iov_len; 59702ef5fdcSAkihiko Odaki 59802ef5fdcSAkihiko Odaki (*src_idx)++; 59902ef5fdcSAkihiko Odaki if (*src_idx >= pkt->payload_frags + NET_TX_PKT_PL_START_FRAG) { 60002ef5fdcSAkihiko Odaki g_free(l4->iov_base); 60102ef5fdcSAkihiko Odaki return false; 60202ef5fdcSAkihiko Odaki } 60302ef5fdcSAkihiko Odaki } 60402ef5fdcSAkihiko Odaki 60502ef5fdcSAkihiko Odaki *src_offset = l4->iov_len - bytes_read; 60602ef5fdcSAkihiko Odaki memcpy((char *)l4->iov_base + bytes_read, pkt->vec[*src_idx].iov_base, 60702ef5fdcSAkihiko Odaki *src_offset); 60802ef5fdcSAkihiko Odaki 60902ef5fdcSAkihiko Odaki th = l4->iov_base; 61002ef5fdcSAkihiko Odaki th->th_flags &= ~(TH_FIN | TH_PUSH); 61102ef5fdcSAkihiko Odaki 61202ef5fdcSAkihiko Odaki *pl_idx = NET_TX_PKT_PL_START_FRAG + 1; 61302ef5fdcSAkihiko Odaki *l4hdr_len = l4->iov_len; 61402ef5fdcSAkihiko Odaki *src_len = pkt->virt_hdr.gso_size; 61502ef5fdcSAkihiko Odaki 61602ef5fdcSAkihiko Odaki return true; 61702ef5fdcSAkihiko Odaki } 61802ef5fdcSAkihiko Odaki 61902ef5fdcSAkihiko Odaki static void net_tx_pkt_tcp_fragment_deinit(struct iovec *fragment) 62002ef5fdcSAkihiko Odaki { 62102ef5fdcSAkihiko Odaki g_free(fragment[NET_TX_PKT_PL_START_FRAG].iov_base); 62202ef5fdcSAkihiko Odaki } 62302ef5fdcSAkihiko Odaki 62402ef5fdcSAkihiko Odaki static void net_tx_pkt_tcp_fragment_fix(struct NetTxPkt *pkt, 62502ef5fdcSAkihiko Odaki struct iovec *fragment, 62602ef5fdcSAkihiko Odaki size_t fragment_len, 62702ef5fdcSAkihiko Odaki uint8_t gso_type) 62802ef5fdcSAkihiko Odaki { 62902ef5fdcSAkihiko Odaki struct iovec *l3hdr = fragment + NET_TX_PKT_L3HDR_FRAG; 63002ef5fdcSAkihiko Odaki struct iovec *l4hdr = fragment + NET_TX_PKT_PL_START_FRAG; 63102ef5fdcSAkihiko Odaki struct ip_header *ip = l3hdr->iov_base; 63202ef5fdcSAkihiko Odaki struct ip6_header *ip6 = l3hdr->iov_base; 63302ef5fdcSAkihiko Odaki size_t len = l3hdr->iov_len + l4hdr->iov_len + fragment_len; 63402ef5fdcSAkihiko Odaki 63502ef5fdcSAkihiko Odaki switch (gso_type) { 63602ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV4: 63702ef5fdcSAkihiko Odaki ip->ip_len = cpu_to_be16(len); 63802ef5fdcSAkihiko Odaki eth_fix_ip4_checksum(l3hdr->iov_base, l3hdr->iov_len); 63902ef5fdcSAkihiko Odaki break; 64002ef5fdcSAkihiko Odaki 64102ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV6: 64202ef5fdcSAkihiko Odaki len -= sizeof(struct ip6_header); 64302ef5fdcSAkihiko Odaki ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = cpu_to_be16(len); 64402ef5fdcSAkihiko Odaki break; 64502ef5fdcSAkihiko Odaki } 64602ef5fdcSAkihiko Odaki } 64702ef5fdcSAkihiko Odaki 64802ef5fdcSAkihiko Odaki static void net_tx_pkt_tcp_fragment_advance(struct NetTxPkt *pkt, 64902ef5fdcSAkihiko Odaki struct iovec *fragment, 65002ef5fdcSAkihiko Odaki size_t fragment_len, 65102ef5fdcSAkihiko Odaki uint8_t gso_type) 65202ef5fdcSAkihiko Odaki { 65302ef5fdcSAkihiko Odaki struct iovec *l3hdr = fragment + NET_TX_PKT_L3HDR_FRAG; 65402ef5fdcSAkihiko Odaki struct iovec *l4hdr = fragment + NET_TX_PKT_PL_START_FRAG; 65502ef5fdcSAkihiko Odaki struct ip_header *ip = l3hdr->iov_base; 65602ef5fdcSAkihiko Odaki struct tcp_hdr *th = l4hdr->iov_base; 65702ef5fdcSAkihiko Odaki 65802ef5fdcSAkihiko Odaki if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4) { 65902ef5fdcSAkihiko Odaki ip->ip_id = cpu_to_be16(be16_to_cpu(ip->ip_id) + 1); 66002ef5fdcSAkihiko Odaki } 66102ef5fdcSAkihiko Odaki 66202ef5fdcSAkihiko Odaki th->th_seq = cpu_to_be32(be32_to_cpu(th->th_seq) + fragment_len); 66302ef5fdcSAkihiko Odaki th->th_flags &= ~TH_CWR; 66402ef5fdcSAkihiko Odaki } 66502ef5fdcSAkihiko Odaki 66602ef5fdcSAkihiko Odaki static void net_tx_pkt_udp_fragment_init(struct NetTxPkt *pkt, 66702ef5fdcSAkihiko Odaki int *pl_idx, 66802ef5fdcSAkihiko Odaki size_t *l4hdr_len, 66902ef5fdcSAkihiko Odaki int *src_idx, size_t *src_offset, 67002ef5fdcSAkihiko Odaki size_t *src_len) 67102ef5fdcSAkihiko Odaki { 67202ef5fdcSAkihiko Odaki *pl_idx = NET_TX_PKT_PL_START_FRAG; 67302ef5fdcSAkihiko Odaki *l4hdr_len = 0; 67402ef5fdcSAkihiko Odaki *src_idx = NET_TX_PKT_PL_START_FRAG; 67502ef5fdcSAkihiko Odaki *src_offset = 0; 67602ef5fdcSAkihiko Odaki *src_len = IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size); 67702ef5fdcSAkihiko Odaki } 67802ef5fdcSAkihiko Odaki 67902ef5fdcSAkihiko Odaki static void net_tx_pkt_udp_fragment_fix(struct NetTxPkt *pkt, 68002ef5fdcSAkihiko Odaki struct iovec *fragment, 68102ef5fdcSAkihiko Odaki size_t fragment_offset, 68202ef5fdcSAkihiko Odaki size_t fragment_len) 68302ef5fdcSAkihiko Odaki { 68402ef5fdcSAkihiko Odaki bool more_frags = fragment_offset + fragment_len < pkt->payload_len; 68502ef5fdcSAkihiko Odaki uint16_t orig_flags; 68602ef5fdcSAkihiko Odaki struct iovec *l3hdr = fragment + NET_TX_PKT_L3HDR_FRAG; 68702ef5fdcSAkihiko Odaki struct ip_header *ip = l3hdr->iov_base; 68802ef5fdcSAkihiko Odaki uint16_t frag_off_units = fragment_offset / IP_FRAG_UNIT_SIZE; 68902ef5fdcSAkihiko Odaki uint16_t new_ip_off; 69002ef5fdcSAkihiko Odaki 69102ef5fdcSAkihiko Odaki assert(fragment_offset % IP_FRAG_UNIT_SIZE == 0); 69202ef5fdcSAkihiko Odaki assert((frag_off_units & ~IP_OFFMASK) == 0); 69302ef5fdcSAkihiko Odaki 69402ef5fdcSAkihiko Odaki orig_flags = be16_to_cpu(ip->ip_off) & ~(IP_OFFMASK | IP_MF); 69502ef5fdcSAkihiko Odaki new_ip_off = frag_off_units | orig_flags | (more_frags ? IP_MF : 0); 69602ef5fdcSAkihiko Odaki ip->ip_off = cpu_to_be16(new_ip_off); 69702ef5fdcSAkihiko Odaki ip->ip_len = cpu_to_be16(l3hdr->iov_len + fragment_len); 69802ef5fdcSAkihiko Odaki 69902ef5fdcSAkihiko Odaki eth_fix_ip4_checksum(l3hdr->iov_base, l3hdr->iov_len); 70002ef5fdcSAkihiko Odaki } 70102ef5fdcSAkihiko Odaki 702605d52e6SDmitry Fleytman static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, 703a51db580SAkihiko Odaki NetTxPktSend callback, 704ffbd2dbdSAkihiko Odaki void *context) 705e263cd49SDmitry Fleytman { 70602ef5fdcSAkihiko Odaki uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; 707e263cd49SDmitry Fleytman 70802ef5fdcSAkihiko Odaki struct iovec fragment[NET_MAX_FRAG_SG_LIST]; 70902ef5fdcSAkihiko Odaki size_t fragment_len; 71002ef5fdcSAkihiko Odaki size_t l4hdr_len; 71102ef5fdcSAkihiko Odaki size_t src_len; 71202ef5fdcSAkihiko Odaki 71302ef5fdcSAkihiko Odaki int src_idx, dst_idx, pl_idx; 71402ef5fdcSAkihiko Odaki size_t src_offset; 715e263cd49SDmitry Fleytman size_t fragment_offset = 0; 716ffbd2dbdSAkihiko Odaki struct virtio_net_hdr virt_hdr = { 717ffbd2dbdSAkihiko Odaki .flags = pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM ? 718ffbd2dbdSAkihiko Odaki VIRTIO_NET_HDR_F_DATA_VALID : 0 719ffbd2dbdSAkihiko Odaki }; 720e263cd49SDmitry Fleytman 721e263cd49SDmitry Fleytman /* Copy headers */ 722ffbd2dbdSAkihiko Odaki fragment[NET_TX_PKT_VHDR_FRAG].iov_base = &virt_hdr; 723ffbd2dbdSAkihiko Odaki fragment[NET_TX_PKT_VHDR_FRAG].iov_len = sizeof(virt_hdr); 72402ef5fdcSAkihiko Odaki fragment[NET_TX_PKT_L2HDR_FRAG] = pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 72502ef5fdcSAkihiko Odaki fragment[NET_TX_PKT_L3HDR_FRAG] = pkt->vec[NET_TX_PKT_L3HDR_FRAG]; 726e263cd49SDmitry Fleytman 72702ef5fdcSAkihiko Odaki switch (gso_type) { 72802ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV4: 72902ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV6: 73002ef5fdcSAkihiko Odaki if (!net_tx_pkt_tcp_fragment_init(pkt, fragment, &pl_idx, &l4hdr_len, 73102ef5fdcSAkihiko Odaki &src_idx, &src_offset, &src_len)) { 73202ef5fdcSAkihiko Odaki return false; 73302ef5fdcSAkihiko Odaki } 73402ef5fdcSAkihiko Odaki break; 73502ef5fdcSAkihiko Odaki 73602ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_UDP: 73702ef5fdcSAkihiko Odaki net_tx_pkt_do_sw_csum(pkt, &pkt->vec[NET_TX_PKT_L2HDR_FRAG], 73802ef5fdcSAkihiko Odaki pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1, 73902ef5fdcSAkihiko Odaki pkt->payload_len); 74002ef5fdcSAkihiko Odaki net_tx_pkt_udp_fragment_init(pkt, &pl_idx, &l4hdr_len, 74102ef5fdcSAkihiko Odaki &src_idx, &src_offset, &src_len); 74202ef5fdcSAkihiko Odaki break; 74302ef5fdcSAkihiko Odaki 74402ef5fdcSAkihiko Odaki default: 74502ef5fdcSAkihiko Odaki abort(); 74602ef5fdcSAkihiko Odaki } 747e263cd49SDmitry Fleytman 748e263cd49SDmitry Fleytman /* Put as much data as possible and send */ 74902ef5fdcSAkihiko Odaki while (true) { 75002ef5fdcSAkihiko Odaki dst_idx = pl_idx; 75102ef5fdcSAkihiko Odaki fragment_len = net_tx_pkt_fetch_fragment(pkt, 75202ef5fdcSAkihiko Odaki &src_idx, &src_offset, src_len, fragment, &dst_idx); 75302ef5fdcSAkihiko Odaki if (!fragment_len) { 75402ef5fdcSAkihiko Odaki break; 75502ef5fdcSAkihiko Odaki } 756e263cd49SDmitry Fleytman 75702ef5fdcSAkihiko Odaki switch (gso_type) { 75802ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV4: 75902ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_TCPV6: 76002ef5fdcSAkihiko Odaki net_tx_pkt_tcp_fragment_fix(pkt, fragment, fragment_len, gso_type); 76102ef5fdcSAkihiko Odaki net_tx_pkt_do_sw_csum(pkt, fragment + NET_TX_PKT_L2HDR_FRAG, 76202ef5fdcSAkihiko Odaki dst_idx - NET_TX_PKT_L2HDR_FRAG, 76302ef5fdcSAkihiko Odaki l4hdr_len + fragment_len); 76402ef5fdcSAkihiko Odaki break; 765e263cd49SDmitry Fleytman 76602ef5fdcSAkihiko Odaki case VIRTIO_NET_HDR_GSO_UDP: 76702ef5fdcSAkihiko Odaki net_tx_pkt_udp_fragment_fix(pkt, fragment, fragment_offset, 76802ef5fdcSAkihiko Odaki fragment_len); 76902ef5fdcSAkihiko Odaki break; 77002ef5fdcSAkihiko Odaki } 771e263cd49SDmitry Fleytman 772ffbd2dbdSAkihiko Odaki callback(context, 773ffbd2dbdSAkihiko Odaki fragment + NET_TX_PKT_L2HDR_FRAG, dst_idx - NET_TX_PKT_L2HDR_FRAG, 774ffbd2dbdSAkihiko Odaki fragment + NET_TX_PKT_VHDR_FRAG, dst_idx - NET_TX_PKT_VHDR_FRAG); 775e263cd49SDmitry Fleytman 77602ef5fdcSAkihiko Odaki if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || 77702ef5fdcSAkihiko Odaki gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { 77802ef5fdcSAkihiko Odaki net_tx_pkt_tcp_fragment_advance(pkt, fragment, fragment_len, 77902ef5fdcSAkihiko Odaki gso_type); 78002ef5fdcSAkihiko Odaki } 781e263cd49SDmitry Fleytman 78202ef5fdcSAkihiko Odaki fragment_offset += fragment_len; 78302ef5fdcSAkihiko Odaki } 78402ef5fdcSAkihiko Odaki 78502ef5fdcSAkihiko Odaki if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || 78602ef5fdcSAkihiko Odaki gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { 78702ef5fdcSAkihiko Odaki net_tx_pkt_tcp_fragment_deinit(fragment); 78802ef5fdcSAkihiko Odaki } 789e263cd49SDmitry Fleytman 790e263cd49SDmitry Fleytman return true; 791e263cd49SDmitry Fleytman } 792e263cd49SDmitry Fleytman 793605d52e6SDmitry Fleytman bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) 794e263cd49SDmitry Fleytman { 795ffbd2dbdSAkihiko Odaki bool offload = qemu_get_using_vnet_hdr(nc->peer); 796ffbd2dbdSAkihiko Odaki return net_tx_pkt_send_custom(pkt, offload, net_tx_pkt_sendv, nc); 797ffbd2dbdSAkihiko Odaki } 79855daf493SAkihiko Odaki 799ffbd2dbdSAkihiko Odaki bool net_tx_pkt_send_custom(struct NetTxPkt *pkt, bool offload, 800a51db580SAkihiko Odaki NetTxPktSend callback, void *context) 801ffbd2dbdSAkihiko Odaki { 802e263cd49SDmitry Fleytman assert(pkt); 803e263cd49SDmitry Fleytman 8044cf3a638SAkihiko Odaki uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; 8054cf3a638SAkihiko Odaki 806e263cd49SDmitry Fleytman /* 807e263cd49SDmitry Fleytman * Since underlying infrastructure does not support IP datagrams longer 808e263cd49SDmitry Fleytman * than 64K we should drop such packets and don't even try to send 809e263cd49SDmitry Fleytman */ 8104cf3a638SAkihiko Odaki if (VIRTIO_NET_HDR_GSO_NONE != gso_type) { 811e263cd49SDmitry Fleytman if (pkt->payload_len > 812e263cd49SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN - 813605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len) { 814e263cd49SDmitry Fleytman return false; 815e263cd49SDmitry Fleytman } 816e263cd49SDmitry Fleytman } 817e263cd49SDmitry Fleytman 8184cf3a638SAkihiko Odaki if (offload || gso_type == VIRTIO_NET_HDR_GSO_NONE) { 81902ef5fdcSAkihiko Odaki if (!offload && pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 82002ef5fdcSAkihiko Odaki net_tx_pkt_do_sw_csum(pkt, &pkt->vec[NET_TX_PKT_L2HDR_FRAG], 82102ef5fdcSAkihiko Odaki pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1, 82202ef5fdcSAkihiko Odaki pkt->payload_len); 82302ef5fdcSAkihiko Odaki } 82402ef5fdcSAkihiko Odaki 825e219d309SAndrew net_tx_pkt_fix_ip6_payload_len(pkt); 826ffbd2dbdSAkihiko Odaki callback(context, pkt->vec + NET_TX_PKT_L2HDR_FRAG, 827ffbd2dbdSAkihiko Odaki pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - NET_TX_PKT_L2HDR_FRAG, 828ffbd2dbdSAkihiko Odaki pkt->vec + NET_TX_PKT_VHDR_FRAG, 829ffbd2dbdSAkihiko Odaki pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - NET_TX_PKT_VHDR_FRAG); 830e263cd49SDmitry Fleytman return true; 831e263cd49SDmitry Fleytman } 832e263cd49SDmitry Fleytman 833ffbd2dbdSAkihiko Odaki return net_tx_pkt_do_sw_fragmentation(pkt, callback, context); 834eb700029SDmitry Fleytman } 835e219d309SAndrew 836e219d309SAndrew void net_tx_pkt_fix_ip6_payload_len(struct NetTxPkt *pkt) 837e219d309SAndrew { 838e219d309SAndrew struct iovec *l2 = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 839e219d309SAndrew if (eth_get_l3_proto(l2, 1, l2->iov_len) == ETH_P_IPV6) { 840e219d309SAndrew /* 841e219d309SAndrew * TODO: if qemu would support >64K packets - add jumbo option check 842e219d309SAndrew * something like that: 843e219d309SAndrew * 'if (ip6->ip6_plen == 0 && !has_jumbo_option(ip6)) {' 844e219d309SAndrew */ 8452a5f744eSAkihiko Odaki if (pkt->l3_hdr.ip6.ip6_plen == 0) { 846e219d309SAndrew if (pkt->payload_len <= ETH_MAX_IP_DGRAM_LEN) { 8472a5f744eSAkihiko Odaki pkt->l3_hdr.ip6.ip6_plen = htons(pkt->payload_len); 848e219d309SAndrew } 849e219d309SAndrew /* 850e219d309SAndrew * TODO: if qemu would support >64K packets 851e219d309SAndrew * add jumbo option for packets greater then 65,535 bytes 852e219d309SAndrew */ 853e219d309SAndrew } 854e219d309SAndrew } 855e219d309SAndrew } 856