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" 19605d52e6SDmitry Fleytman #include "net_tx_pkt.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" 2411171010SDmitry Fleytman #include "hw/pci/pci.h" 25605d52e6SDmitry Fleytman 26605d52e6SDmitry 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 31605d52e6SDmitry Fleytman }; 32605d52e6SDmitry Fleytman 33605d52e6SDmitry Fleytman /* TX packet private context */ 34605d52e6SDmitry Fleytman struct NetTxPkt { 3511171010SDmitry Fleytman PCIDevice *pci_dev; 3611171010SDmitry Fleytman 37605d52e6SDmitry Fleytman struct virtio_net_hdr virt_hdr; 38605d52e6SDmitry Fleytman bool has_virt_hdr; 39605d52e6SDmitry Fleytman 40605d52e6SDmitry Fleytman struct iovec *raw; 41605d52e6SDmitry Fleytman uint32_t raw_frags; 42605d52e6SDmitry Fleytman uint32_t max_raw_frags; 43605d52e6SDmitry Fleytman 44605d52e6SDmitry Fleytman struct iovec *vec; 45605d52e6SDmitry Fleytman 46605d52e6SDmitry Fleytman uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN]; 47eb700029SDmitry Fleytman uint8_t l3_hdr[ETH_MAX_IP_DGRAM_LEN]; 48605d52e6SDmitry Fleytman 49605d52e6SDmitry Fleytman uint32_t payload_len; 50605d52e6SDmitry Fleytman 51605d52e6SDmitry Fleytman uint32_t payload_frags; 52605d52e6SDmitry Fleytman uint32_t max_payload_frags; 53605d52e6SDmitry Fleytman 54605d52e6SDmitry Fleytman uint16_t hdr_len; 55605d52e6SDmitry Fleytman eth_pkt_types_e packet_type; 56605d52e6SDmitry Fleytman uint8_t l4proto; 57eb700029SDmitry Fleytman 58eb700029SDmitry Fleytman bool is_loopback; 59605d52e6SDmitry Fleytman }; 60605d52e6SDmitry Fleytman 6111171010SDmitry Fleytman void net_tx_pkt_init(struct NetTxPkt **pkt, PCIDevice *pci_dev, 6211171010SDmitry Fleytman uint32_t max_frags, bool has_virt_hdr) 63605d52e6SDmitry Fleytman { 64605d52e6SDmitry Fleytman struct NetTxPkt *p = g_malloc0(sizeof *p); 65605d52e6SDmitry Fleytman 6611171010SDmitry Fleytman p->pci_dev = pci_dev; 6711171010SDmitry Fleytman 68605d52e6SDmitry Fleytman p->vec = g_malloc((sizeof *p->vec) * 69605d52e6SDmitry Fleytman (max_frags + NET_TX_PKT_PL_START_FRAG)); 70605d52e6SDmitry Fleytman 71605d52e6SDmitry Fleytman p->raw = g_malloc((sizeof *p->raw) * max_frags); 72605d52e6SDmitry Fleytman 73605d52e6SDmitry Fleytman p->max_payload_frags = max_frags; 74605d52e6SDmitry Fleytman p->max_raw_frags = max_frags; 75605d52e6SDmitry Fleytman p->has_virt_hdr = has_virt_hdr; 76605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; 77605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_VHDR_FRAG].iov_len = 78605d52e6SDmitry Fleytman p->has_virt_hdr ? sizeof p->virt_hdr : 0; 79605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; 80eb700029SDmitry Fleytman p->vec[NET_TX_PKT_L3HDR_FRAG].iov_base = &p->l3_hdr; 81605d52e6SDmitry Fleytman 82605d52e6SDmitry Fleytman *pkt = p; 83605d52e6SDmitry Fleytman } 84605d52e6SDmitry Fleytman 85605d52e6SDmitry Fleytman void net_tx_pkt_uninit(struct NetTxPkt *pkt) 86605d52e6SDmitry Fleytman { 87605d52e6SDmitry Fleytman if (pkt) { 88605d52e6SDmitry Fleytman g_free(pkt->vec); 89605d52e6SDmitry Fleytman g_free(pkt->raw); 90605d52e6SDmitry Fleytman g_free(pkt); 91605d52e6SDmitry Fleytman } 92605d52e6SDmitry Fleytman } 93605d52e6SDmitry Fleytman 94eb700029SDmitry Fleytman void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt) 95eb700029SDmitry Fleytman { 96eb700029SDmitry Fleytman uint16_t csum; 97eb700029SDmitry Fleytman assert(pkt); 98eb700029SDmitry Fleytman struct ip_header *ip_hdr; 99eb700029SDmitry Fleytman ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 100eb700029SDmitry Fleytman 101eb700029SDmitry Fleytman ip_hdr->ip_len = cpu_to_be16(pkt->payload_len + 102eb700029SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 103eb700029SDmitry Fleytman 104eb700029SDmitry Fleytman ip_hdr->ip_sum = 0; 105eb700029SDmitry Fleytman csum = net_raw_checksum((uint8_t *)ip_hdr, 106eb700029SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 107eb700029SDmitry Fleytman ip_hdr->ip_sum = cpu_to_be16(csum); 108eb700029SDmitry Fleytman } 109eb700029SDmitry Fleytman 110605d52e6SDmitry Fleytman void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt) 111605d52e6SDmitry Fleytman { 112605d52e6SDmitry Fleytman uint16_t csum; 113eb700029SDmitry Fleytman uint32_t cntr, cso; 114605d52e6SDmitry Fleytman assert(pkt); 115605d52e6SDmitry Fleytman uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; 116eb700029SDmitry Fleytman void *ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 117605d52e6SDmitry Fleytman 118605d52e6SDmitry Fleytman if (pkt->payload_len + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len > 119605d52e6SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN) { 120605d52e6SDmitry Fleytman return; 121605d52e6SDmitry Fleytman } 122605d52e6SDmitry Fleytman 123eb700029SDmitry Fleytman if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || 124eb700029SDmitry Fleytman gso_type == VIRTIO_NET_HDR_GSO_UDP) { 125605d52e6SDmitry Fleytman /* Calculate IP header checksum */ 126eb700029SDmitry Fleytman net_tx_pkt_update_ip_hdr_checksum(pkt); 127605d52e6SDmitry Fleytman 128605d52e6SDmitry Fleytman /* Calculate IP pseudo header checksum */ 129eb700029SDmitry Fleytman cntr = eth_calc_ip4_pseudo_hdr_csum(ip_hdr, pkt->payload_len, &cso); 130eb700029SDmitry Fleytman csum = cpu_to_be16(~net_checksum_finish(cntr)); 131eb700029SDmitry Fleytman } else if (gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { 132eb700029SDmitry Fleytman /* Calculate IP pseudo header checksum */ 133eb700029SDmitry Fleytman cntr = eth_calc_ip6_pseudo_hdr_csum(ip_hdr, pkt->payload_len, 134eb700029SDmitry Fleytman IP_PROTO_TCP, &cso); 135eb700029SDmitry Fleytman csum = cpu_to_be16(~net_checksum_finish(cntr)); 136eb700029SDmitry Fleytman } else { 137eb700029SDmitry Fleytman return; 138eb700029SDmitry Fleytman } 139eb700029SDmitry Fleytman 140605d52e6SDmitry Fleytman iov_from_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, 141605d52e6SDmitry Fleytman pkt->virt_hdr.csum_offset, &csum, sizeof(csum)); 142605d52e6SDmitry Fleytman } 143605d52e6SDmitry Fleytman 144605d52e6SDmitry Fleytman static void net_tx_pkt_calculate_hdr_len(struct NetTxPkt *pkt) 145605d52e6SDmitry Fleytman { 146605d52e6SDmitry Fleytman pkt->hdr_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len + 147605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; 148605d52e6SDmitry Fleytman } 149605d52e6SDmitry Fleytman 150605d52e6SDmitry Fleytman static bool net_tx_pkt_parse_headers(struct NetTxPkt *pkt) 151605d52e6SDmitry Fleytman { 152605d52e6SDmitry Fleytman struct iovec *l2_hdr, *l3_hdr; 153605d52e6SDmitry Fleytman size_t bytes_read; 154605d52e6SDmitry Fleytman size_t full_ip6hdr_len; 155605d52e6SDmitry Fleytman uint16_t l3_proto; 156605d52e6SDmitry Fleytman 157605d52e6SDmitry Fleytman assert(pkt); 158605d52e6SDmitry Fleytman 159605d52e6SDmitry Fleytman l2_hdr = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 160605d52e6SDmitry Fleytman l3_hdr = &pkt->vec[NET_TX_PKT_L3HDR_FRAG]; 161605d52e6SDmitry Fleytman 162605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, 163605d52e6SDmitry Fleytman ETH_MAX_L2_HDR_LEN); 164605d52e6SDmitry Fleytman if (bytes_read < sizeof(struct eth_header)) { 165605d52e6SDmitry Fleytman l2_hdr->iov_len = 0; 166605d52e6SDmitry Fleytman return false; 167605d52e6SDmitry Fleytman } 168605d52e6SDmitry Fleytman 169605d52e6SDmitry Fleytman l2_hdr->iov_len = sizeof(struct eth_header); 170605d52e6SDmitry Fleytman switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) { 171605d52e6SDmitry Fleytman case ETH_P_VLAN: 172605d52e6SDmitry Fleytman l2_hdr->iov_len += sizeof(struct vlan_header); 173605d52e6SDmitry Fleytman break; 174605d52e6SDmitry Fleytman case ETH_P_DVLAN: 175605d52e6SDmitry Fleytman l2_hdr->iov_len += 2 * sizeof(struct vlan_header); 176605d52e6SDmitry Fleytman break; 177605d52e6SDmitry Fleytman } 178605d52e6SDmitry Fleytman 179605d52e6SDmitry Fleytman if (bytes_read < l2_hdr->iov_len) { 180605d52e6SDmitry Fleytman l2_hdr->iov_len = 0; 181eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 182eb700029SDmitry Fleytman pkt->packet_type = ETH_PKT_UCAST; 183605d52e6SDmitry Fleytman return false; 184eb700029SDmitry Fleytman } else { 185eb700029SDmitry Fleytman l2_hdr->iov_len = ETH_MAX_L2_HDR_LEN; 186eb700029SDmitry Fleytman l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); 187eb700029SDmitry Fleytman pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base); 188605d52e6SDmitry Fleytman } 189605d52e6SDmitry Fleytman 190eb700029SDmitry Fleytman l3_proto = eth_get_l3_proto(l2_hdr, 1, l2_hdr->iov_len); 191605d52e6SDmitry Fleytman 192605d52e6SDmitry Fleytman switch (l3_proto) { 193605d52e6SDmitry Fleytman case ETH_P_IP: 194605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 195605d52e6SDmitry Fleytman l3_hdr->iov_base, sizeof(struct ip_header)); 196605d52e6SDmitry Fleytman 197605d52e6SDmitry Fleytman if (bytes_read < sizeof(struct ip_header)) { 198605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 199605d52e6SDmitry Fleytman return false; 200605d52e6SDmitry Fleytman } 201605d52e6SDmitry Fleytman 202605d52e6SDmitry Fleytman l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base); 203eb700029SDmitry Fleytman 204eb700029SDmitry Fleytman if (l3_hdr->iov_len < sizeof(struct ip_header)) { 205eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 206eb700029SDmitry Fleytman return false; 207eb700029SDmitry Fleytman } 208eb700029SDmitry Fleytman 209605d52e6SDmitry Fleytman pkt->l4proto = ((struct ip_header *) l3_hdr->iov_base)->ip_p; 210605d52e6SDmitry Fleytman 211eb700029SDmitry Fleytman if (IP_HDR_GET_LEN(l3_hdr->iov_base) != sizeof(struct ip_header)) { 212eb700029SDmitry Fleytman /* copy optional IPv4 header data if any*/ 213605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 214605d52e6SDmitry Fleytman l2_hdr->iov_len + sizeof(struct ip_header), 215605d52e6SDmitry Fleytman l3_hdr->iov_base + sizeof(struct ip_header), 216605d52e6SDmitry Fleytman l3_hdr->iov_len - sizeof(struct ip_header)); 217605d52e6SDmitry Fleytman if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) { 218605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 219605d52e6SDmitry Fleytman return false; 220605d52e6SDmitry Fleytman } 221eb700029SDmitry Fleytman } 222eb700029SDmitry Fleytman 223605d52e6SDmitry Fleytman break; 224605d52e6SDmitry Fleytman 225605d52e6SDmitry Fleytman case ETH_P_IPV6: 226eb700029SDmitry Fleytman { 227eb700029SDmitry Fleytman eth_ip6_hdr_info hdrinfo; 228eb700029SDmitry Fleytman 229605d52e6SDmitry Fleytman if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 230eb700029SDmitry Fleytman &hdrinfo)) { 231605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 232605d52e6SDmitry Fleytman return false; 233605d52e6SDmitry Fleytman } 234605d52e6SDmitry Fleytman 235eb700029SDmitry Fleytman pkt->l4proto = hdrinfo.l4proto; 236eb700029SDmitry Fleytman full_ip6hdr_len = hdrinfo.full_hdr_len; 237eb700029SDmitry Fleytman 238eb700029SDmitry Fleytman if (full_ip6hdr_len > ETH_MAX_IP_DGRAM_LEN) { 239eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 240eb700029SDmitry Fleytman return false; 241eb700029SDmitry Fleytman } 242605d52e6SDmitry Fleytman 243605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 244605d52e6SDmitry Fleytman l3_hdr->iov_base, full_ip6hdr_len); 245605d52e6SDmitry Fleytman 246605d52e6SDmitry Fleytman if (bytes_read < full_ip6hdr_len) { 247605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 248605d52e6SDmitry Fleytman return false; 249605d52e6SDmitry Fleytman } else { 250605d52e6SDmitry Fleytman l3_hdr->iov_len = full_ip6hdr_len; 251605d52e6SDmitry Fleytman } 252605d52e6SDmitry Fleytman break; 253eb700029SDmitry Fleytman } 254605d52e6SDmitry Fleytman default: 255605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 256605d52e6SDmitry Fleytman break; 257605d52e6SDmitry Fleytman } 258605d52e6SDmitry Fleytman 259605d52e6SDmitry Fleytman net_tx_pkt_calculate_hdr_len(pkt); 260605d52e6SDmitry Fleytman return true; 261605d52e6SDmitry Fleytman } 262605d52e6SDmitry Fleytman 263eb700029SDmitry Fleytman static void net_tx_pkt_rebuild_payload(struct NetTxPkt *pkt) 264605d52e6SDmitry Fleytman { 265eb700029SDmitry Fleytman pkt->payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len; 266605d52e6SDmitry Fleytman pkt->payload_frags = iov_copy(&pkt->vec[NET_TX_PKT_PL_START_FRAG], 267605d52e6SDmitry Fleytman pkt->max_payload_frags, 268605d52e6SDmitry Fleytman pkt->raw, pkt->raw_frags, 269eb700029SDmitry Fleytman pkt->hdr_len, pkt->payload_len); 270605d52e6SDmitry Fleytman } 271605d52e6SDmitry Fleytman 272605d52e6SDmitry Fleytman bool net_tx_pkt_parse(struct NetTxPkt *pkt) 273605d52e6SDmitry Fleytman { 274eb700029SDmitry Fleytman if (net_tx_pkt_parse_headers(pkt)) { 275605d52e6SDmitry Fleytman net_tx_pkt_rebuild_payload(pkt); 276eb700029SDmitry Fleytman return true; 277eb700029SDmitry Fleytman } else { 278eb700029SDmitry Fleytman return false; 279eb700029SDmitry Fleytman } 280605d52e6SDmitry Fleytman } 281605d52e6SDmitry Fleytman 282605d52e6SDmitry Fleytman struct virtio_net_hdr *net_tx_pkt_get_vhdr(struct NetTxPkt *pkt) 283605d52e6SDmitry Fleytman { 284605d52e6SDmitry Fleytman assert(pkt); 285605d52e6SDmitry Fleytman return &pkt->virt_hdr; 286605d52e6SDmitry Fleytman } 287605d52e6SDmitry Fleytman 288605d52e6SDmitry Fleytman static uint8_t net_tx_pkt_get_gso_type(struct NetTxPkt *pkt, 289605d52e6SDmitry Fleytman bool tso_enable) 290605d52e6SDmitry Fleytman { 291605d52e6SDmitry Fleytman uint8_t rc = VIRTIO_NET_HDR_GSO_NONE; 292605d52e6SDmitry Fleytman uint16_t l3_proto; 293605d52e6SDmitry Fleytman 294eb700029SDmitry Fleytman l3_proto = eth_get_l3_proto(&pkt->vec[NET_TX_PKT_L2HDR_FRAG], 1, 295605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len); 296605d52e6SDmitry Fleytman 297605d52e6SDmitry Fleytman if (!tso_enable) { 298605d52e6SDmitry Fleytman goto func_exit; 299605d52e6SDmitry Fleytman } 300605d52e6SDmitry Fleytman 301605d52e6SDmitry Fleytman rc = eth_get_gso_type(l3_proto, pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 302605d52e6SDmitry Fleytman pkt->l4proto); 303605d52e6SDmitry Fleytman 304605d52e6SDmitry Fleytman func_exit: 305605d52e6SDmitry Fleytman return rc; 306605d52e6SDmitry Fleytman } 307605d52e6SDmitry Fleytman 308605d52e6SDmitry Fleytman void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, 309605d52e6SDmitry Fleytman bool csum_enable, uint32_t gso_size) 310605d52e6SDmitry Fleytman { 311605d52e6SDmitry Fleytman struct tcp_hdr l4hdr; 312605d52e6SDmitry Fleytman assert(pkt); 313605d52e6SDmitry Fleytman 314605d52e6SDmitry Fleytman /* csum has to be enabled if tso is. */ 315605d52e6SDmitry Fleytman assert(csum_enable || !tso_enable); 316605d52e6SDmitry Fleytman 317605d52e6SDmitry Fleytman pkt->virt_hdr.gso_type = net_tx_pkt_get_gso_type(pkt, tso_enable); 318605d52e6SDmitry Fleytman 319605d52e6SDmitry Fleytman switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 320605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_NONE: 321605d52e6SDmitry Fleytman pkt->virt_hdr.hdr_len = 0; 322605d52e6SDmitry Fleytman pkt->virt_hdr.gso_size = 0; 323605d52e6SDmitry Fleytman break; 324605d52e6SDmitry Fleytman 325605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_UDP: 326eb700029SDmitry Fleytman pkt->virt_hdr.gso_size = gso_size; 327605d52e6SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header); 328605d52e6SDmitry Fleytman break; 329605d52e6SDmitry Fleytman 330605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV4: 331605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV6: 332605d52e6SDmitry Fleytman iov_to_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, 333605d52e6SDmitry Fleytman 0, &l4hdr, sizeof(l4hdr)); 334605d52e6SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t); 335eb700029SDmitry Fleytman pkt->virt_hdr.gso_size = gso_size; 336605d52e6SDmitry Fleytman break; 337605d52e6SDmitry Fleytman 338605d52e6SDmitry Fleytman default: 339605d52e6SDmitry Fleytman g_assert_not_reached(); 340605d52e6SDmitry Fleytman } 341605d52e6SDmitry Fleytman 342605d52e6SDmitry Fleytman if (csum_enable) { 343605d52e6SDmitry Fleytman switch (pkt->l4proto) { 344605d52e6SDmitry Fleytman case IP_PROTO_TCP: 345605d52e6SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 346605d52e6SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 347605d52e6SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum); 348605d52e6SDmitry Fleytman break; 349605d52e6SDmitry Fleytman case IP_PROTO_UDP: 350605d52e6SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 351605d52e6SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 352605d52e6SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum); 353605d52e6SDmitry Fleytman break; 354605d52e6SDmitry Fleytman default: 355605d52e6SDmitry Fleytman break; 356605d52e6SDmitry Fleytman } 357605d52e6SDmitry Fleytman } 358605d52e6SDmitry Fleytman } 359605d52e6SDmitry Fleytman 360eb700029SDmitry Fleytman void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt, 361eb700029SDmitry Fleytman uint16_t vlan, uint16_t vlan_ethtype) 362605d52e6SDmitry Fleytman { 363605d52e6SDmitry Fleytman bool is_new; 364605d52e6SDmitry Fleytman assert(pkt); 365605d52e6SDmitry Fleytman 366eb700029SDmitry Fleytman eth_setup_vlan_headers_ex(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base, 367eb700029SDmitry Fleytman vlan, vlan_ethtype, &is_new); 368605d52e6SDmitry Fleytman 369605d52e6SDmitry Fleytman /* update l2hdrlen */ 370605d52e6SDmitry Fleytman if (is_new) { 371605d52e6SDmitry Fleytman pkt->hdr_len += sizeof(struct vlan_header); 372605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len += 373605d52e6SDmitry Fleytman sizeof(struct vlan_header); 374605d52e6SDmitry Fleytman } 375605d52e6SDmitry Fleytman } 376605d52e6SDmitry Fleytman 377605d52e6SDmitry Fleytman bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa, 378605d52e6SDmitry Fleytman size_t len) 379605d52e6SDmitry Fleytman { 380605d52e6SDmitry Fleytman hwaddr mapped_len = 0; 381605d52e6SDmitry Fleytman struct iovec *ventry; 382605d52e6SDmitry Fleytman assert(pkt); 383605d52e6SDmitry Fleytman assert(pkt->max_raw_frags > pkt->raw_frags); 384605d52e6SDmitry Fleytman 385605d52e6SDmitry Fleytman if (!len) { 386605d52e6SDmitry Fleytman return true; 387605d52e6SDmitry Fleytman } 388605d52e6SDmitry Fleytman 389605d52e6SDmitry Fleytman ventry = &pkt->raw[pkt->raw_frags]; 390605d52e6SDmitry Fleytman mapped_len = len; 391605d52e6SDmitry Fleytman 39211171010SDmitry Fleytman ventry->iov_base = pci_dma_map(pkt->pci_dev, pa, 39311171010SDmitry Fleytman &mapped_len, DMA_DIRECTION_TO_DEVICE); 394605d52e6SDmitry Fleytman 395eb700029SDmitry Fleytman if ((ventry->iov_base != NULL) && (len == mapped_len)) { 396eb700029SDmitry Fleytman ventry->iov_len = mapped_len; 397eb700029SDmitry Fleytman pkt->raw_frags++; 398eb700029SDmitry Fleytman return true; 399eb700029SDmitry Fleytman } else { 400605d52e6SDmitry Fleytman return false; 401605d52e6SDmitry Fleytman } 402eb700029SDmitry Fleytman } 403605d52e6SDmitry Fleytman 404eb700029SDmitry Fleytman bool net_tx_pkt_has_fragments(struct NetTxPkt *pkt) 405eb700029SDmitry Fleytman { 406eb700029SDmitry Fleytman return pkt->raw_frags > 0; 407605d52e6SDmitry Fleytman } 408605d52e6SDmitry Fleytman 409605d52e6SDmitry Fleytman eth_pkt_types_e net_tx_pkt_get_packet_type(struct NetTxPkt *pkt) 410605d52e6SDmitry Fleytman { 411605d52e6SDmitry Fleytman assert(pkt); 412605d52e6SDmitry Fleytman 413605d52e6SDmitry Fleytman return pkt->packet_type; 414605d52e6SDmitry Fleytman } 415605d52e6SDmitry Fleytman 416605d52e6SDmitry Fleytman size_t net_tx_pkt_get_total_len(struct NetTxPkt *pkt) 417605d52e6SDmitry Fleytman { 418605d52e6SDmitry Fleytman assert(pkt); 419605d52e6SDmitry Fleytman 420605d52e6SDmitry Fleytman return pkt->hdr_len + pkt->payload_len; 421605d52e6SDmitry Fleytman } 422605d52e6SDmitry Fleytman 423605d52e6SDmitry Fleytman void net_tx_pkt_dump(struct NetTxPkt *pkt) 424605d52e6SDmitry Fleytman { 425605d52e6SDmitry Fleytman #ifdef NET_TX_PKT_DEBUG 426605d52e6SDmitry Fleytman assert(pkt); 427605d52e6SDmitry Fleytman 428605d52e6SDmitry Fleytman printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, " 429605d52e6SDmitry Fleytman "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type, 430605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len, 431605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len); 432605d52e6SDmitry Fleytman #endif 433605d52e6SDmitry Fleytman } 434605d52e6SDmitry Fleytman 435605d52e6SDmitry Fleytman void net_tx_pkt_reset(struct NetTxPkt *pkt) 436605d52e6SDmitry Fleytman { 437605d52e6SDmitry Fleytman int i; 438605d52e6SDmitry Fleytman 439605d52e6SDmitry Fleytman /* no assert, as reset can be called before tx_pkt_init */ 440605d52e6SDmitry Fleytman if (!pkt) { 441605d52e6SDmitry Fleytman return; 442605d52e6SDmitry Fleytman } 443605d52e6SDmitry Fleytman 444605d52e6SDmitry Fleytman memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr)); 445605d52e6SDmitry Fleytman 446605d52e6SDmitry Fleytman assert(pkt->vec); 447eb700029SDmitry Fleytman 448605d52e6SDmitry Fleytman pkt->payload_len = 0; 449605d52e6SDmitry Fleytman pkt->payload_frags = 0; 450605d52e6SDmitry Fleytman 451605d52e6SDmitry Fleytman assert(pkt->raw); 452605d52e6SDmitry Fleytman for (i = 0; i < pkt->raw_frags; i++) { 453605d52e6SDmitry Fleytman assert(pkt->raw[i].iov_base); 45411171010SDmitry Fleytman pci_dma_unmap(pkt->pci_dev, pkt->raw[i].iov_base, pkt->raw[i].iov_len, 45511171010SDmitry Fleytman DMA_DIRECTION_TO_DEVICE, 0); 456605d52e6SDmitry Fleytman } 457605d52e6SDmitry Fleytman pkt->raw_frags = 0; 458605d52e6SDmitry Fleytman 459605d52e6SDmitry Fleytman pkt->hdr_len = 0; 460605d52e6SDmitry Fleytman pkt->l4proto = 0; 461605d52e6SDmitry Fleytman } 462605d52e6SDmitry Fleytman 463605d52e6SDmitry Fleytman static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt) 464605d52e6SDmitry Fleytman { 465605d52e6SDmitry Fleytman struct iovec *iov = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 466605d52e6SDmitry Fleytman uint32_t csum_cntr; 467605d52e6SDmitry Fleytman uint16_t csum = 0; 468eb700029SDmitry Fleytman uint32_t cso; 469605d52e6SDmitry Fleytman /* num of iovec without vhdr */ 470605d52e6SDmitry Fleytman uint32_t iov_len = pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1; 471605d52e6SDmitry Fleytman uint16_t csl; 472605d52e6SDmitry Fleytman struct ip_header *iphdr; 473605d52e6SDmitry Fleytman size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset; 474605d52e6SDmitry Fleytman 475605d52e6SDmitry Fleytman /* Put zero to checksum field */ 476605d52e6SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 477605d52e6SDmitry Fleytman 478605d52e6SDmitry Fleytman /* Calculate L4 TCP/UDP checksum */ 479605d52e6SDmitry Fleytman csl = pkt->payload_len; 480605d52e6SDmitry Fleytman 481605d52e6SDmitry Fleytman /* add pseudo header to csum */ 482605d52e6SDmitry Fleytman iphdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 483eb700029SDmitry Fleytman csum_cntr = eth_calc_ip4_pseudo_hdr_csum(iphdr, csl, &cso); 484eb700029SDmitry Fleytman 485eb700029SDmitry Fleytman /* data checksum */ 486eb700029SDmitry Fleytman csum_cntr += 487eb700029SDmitry Fleytman net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl, cso); 488605d52e6SDmitry Fleytman 489605d52e6SDmitry Fleytman /* Put the checksum obtained into the packet */ 490605d52e6SDmitry Fleytman csum = cpu_to_be16(net_checksum_finish(csum_cntr)); 491605d52e6SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 492605d52e6SDmitry Fleytman } 493605d52e6SDmitry Fleytman 494605d52e6SDmitry Fleytman enum { 495605d52e6SDmitry Fleytman NET_TX_PKT_FRAGMENT_L2_HDR_POS = 0, 496605d52e6SDmitry Fleytman NET_TX_PKT_FRAGMENT_L3_HDR_POS, 497605d52e6SDmitry Fleytman NET_TX_PKT_FRAGMENT_HEADER_NUM 498605d52e6SDmitry Fleytman }; 499605d52e6SDmitry Fleytman 500605d52e6SDmitry Fleytman #define NET_MAX_FRAG_SG_LIST (64) 501605d52e6SDmitry Fleytman 502605d52e6SDmitry Fleytman static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt *pkt, 503605d52e6SDmitry Fleytman int *src_idx, size_t *src_offset, struct iovec *dst, int *dst_idx) 504605d52e6SDmitry Fleytman { 505605d52e6SDmitry Fleytman size_t fetched = 0; 506605d52e6SDmitry Fleytman struct iovec *src = pkt->vec; 507605d52e6SDmitry Fleytman 508605d52e6SDmitry Fleytman *dst_idx = NET_TX_PKT_FRAGMENT_HEADER_NUM; 509605d52e6SDmitry Fleytman 510eb700029SDmitry Fleytman while (fetched < IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size)) { 511605d52e6SDmitry Fleytman 512605d52e6SDmitry Fleytman /* no more place in fragment iov */ 513605d52e6SDmitry Fleytman if (*dst_idx == NET_MAX_FRAG_SG_LIST) { 514605d52e6SDmitry Fleytman break; 515605d52e6SDmitry Fleytman } 516605d52e6SDmitry Fleytman 517605d52e6SDmitry Fleytman /* no more data in iovec */ 518605d52e6SDmitry Fleytman if (*src_idx == (pkt->payload_frags + NET_TX_PKT_PL_START_FRAG)) { 519605d52e6SDmitry Fleytman break; 520605d52e6SDmitry Fleytman } 521605d52e6SDmitry Fleytman 522605d52e6SDmitry Fleytman 523605d52e6SDmitry Fleytman dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset; 524605d52e6SDmitry Fleytman dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset, 525eb700029SDmitry Fleytman IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size) - fetched); 526605d52e6SDmitry Fleytman 527605d52e6SDmitry Fleytman *src_offset += dst[*dst_idx].iov_len; 528605d52e6SDmitry Fleytman fetched += dst[*dst_idx].iov_len; 529605d52e6SDmitry Fleytman 530605d52e6SDmitry Fleytman if (*src_offset == src[*src_idx].iov_len) { 531605d52e6SDmitry Fleytman *src_offset = 0; 532605d52e6SDmitry Fleytman (*src_idx)++; 533605d52e6SDmitry Fleytman } 534605d52e6SDmitry Fleytman 535605d52e6SDmitry Fleytman (*dst_idx)++; 536605d52e6SDmitry Fleytman } 537605d52e6SDmitry Fleytman 538605d52e6SDmitry Fleytman return fetched; 539605d52e6SDmitry Fleytman } 540605d52e6SDmitry Fleytman 541eb700029SDmitry Fleytman static inline void net_tx_pkt_sendv(struct NetTxPkt *pkt, 542eb700029SDmitry Fleytman NetClientState *nc, const struct iovec *iov, int iov_cnt) 543eb700029SDmitry Fleytman { 544eb700029SDmitry Fleytman if (pkt->is_loopback) { 545eb700029SDmitry Fleytman nc->info->receive_iov(nc, iov, iov_cnt); 546eb700029SDmitry Fleytman } else { 547eb700029SDmitry Fleytman qemu_sendv_packet(nc, iov, iov_cnt); 548eb700029SDmitry Fleytman } 549eb700029SDmitry Fleytman } 550eb700029SDmitry Fleytman 551605d52e6SDmitry Fleytman static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, 552605d52e6SDmitry Fleytman NetClientState *nc) 553605d52e6SDmitry Fleytman { 554605d52e6SDmitry Fleytman struct iovec fragment[NET_MAX_FRAG_SG_LIST]; 555605d52e6SDmitry Fleytman size_t fragment_len = 0; 556605d52e6SDmitry Fleytman bool more_frags = false; 557605d52e6SDmitry Fleytman 558605d52e6SDmitry Fleytman /* some pointers for shorter code */ 559605d52e6SDmitry Fleytman void *l2_iov_base, *l3_iov_base; 560605d52e6SDmitry Fleytman size_t l2_iov_len, l3_iov_len; 561605d52e6SDmitry Fleytman int src_idx = NET_TX_PKT_PL_START_FRAG, dst_idx; 562605d52e6SDmitry Fleytman size_t src_offset = 0; 563605d52e6SDmitry Fleytman size_t fragment_offset = 0; 564605d52e6SDmitry Fleytman 565605d52e6SDmitry Fleytman l2_iov_base = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base; 566605d52e6SDmitry Fleytman l2_iov_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len; 567605d52e6SDmitry Fleytman l3_iov_base = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 568605d52e6SDmitry Fleytman l3_iov_len = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; 569605d52e6SDmitry Fleytman 570605d52e6SDmitry Fleytman /* Copy headers */ 571605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base; 572605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len; 573605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base; 574605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len; 575605d52e6SDmitry Fleytman 576605d52e6SDmitry Fleytman 577605d52e6SDmitry Fleytman /* Put as much data as possible and send */ 578605d52e6SDmitry Fleytman do { 579605d52e6SDmitry Fleytman fragment_len = net_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset, 580605d52e6SDmitry Fleytman fragment, &dst_idx); 581605d52e6SDmitry Fleytman 582605d52e6SDmitry Fleytman more_frags = (fragment_offset + fragment_len < pkt->payload_len); 583605d52e6SDmitry Fleytman 584605d52e6SDmitry Fleytman eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base, 585605d52e6SDmitry Fleytman l3_iov_len, fragment_len, fragment_offset, more_frags); 586605d52e6SDmitry Fleytman 587605d52e6SDmitry Fleytman eth_fix_ip4_checksum(l3_iov_base, l3_iov_len); 588605d52e6SDmitry Fleytman 589eb700029SDmitry Fleytman net_tx_pkt_sendv(pkt, nc, fragment, dst_idx); 590605d52e6SDmitry Fleytman 591605d52e6SDmitry Fleytman fragment_offset += fragment_len; 592605d52e6SDmitry Fleytman 593*ead315e4SPrasad J Pandit } while (fragment_len && more_frags); 594605d52e6SDmitry Fleytman 595605d52e6SDmitry Fleytman return true; 596605d52e6SDmitry Fleytman } 597605d52e6SDmitry Fleytman 598605d52e6SDmitry Fleytman bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) 599605d52e6SDmitry Fleytman { 600605d52e6SDmitry Fleytman assert(pkt); 601605d52e6SDmitry Fleytman 602605d52e6SDmitry Fleytman if (!pkt->has_virt_hdr && 603605d52e6SDmitry Fleytman pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 604605d52e6SDmitry Fleytman net_tx_pkt_do_sw_csum(pkt); 605605d52e6SDmitry Fleytman } 606605d52e6SDmitry Fleytman 607605d52e6SDmitry Fleytman /* 608605d52e6SDmitry Fleytman * Since underlying infrastructure does not support IP datagrams longer 609605d52e6SDmitry Fleytman * than 64K we should drop such packets and don't even try to send 610605d52e6SDmitry Fleytman */ 611605d52e6SDmitry Fleytman if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) { 612605d52e6SDmitry Fleytman if (pkt->payload_len > 613605d52e6SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN - 614605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len) { 615605d52e6SDmitry Fleytman return false; 616605d52e6SDmitry Fleytman } 617605d52e6SDmitry Fleytman } 618605d52e6SDmitry Fleytman 619605d52e6SDmitry Fleytman if (pkt->has_virt_hdr || 620605d52e6SDmitry Fleytman pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) { 621eb700029SDmitry Fleytman net_tx_pkt_sendv(pkt, nc, pkt->vec, 622605d52e6SDmitry Fleytman pkt->payload_frags + NET_TX_PKT_PL_START_FRAG); 623605d52e6SDmitry Fleytman return true; 624605d52e6SDmitry Fleytman } 625605d52e6SDmitry Fleytman 626605d52e6SDmitry Fleytman return net_tx_pkt_do_sw_fragmentation(pkt, nc); 627605d52e6SDmitry Fleytman } 628eb700029SDmitry Fleytman 629eb700029SDmitry Fleytman bool net_tx_pkt_send_loopback(struct NetTxPkt *pkt, NetClientState *nc) 630eb700029SDmitry Fleytman { 631eb700029SDmitry Fleytman bool res; 632eb700029SDmitry Fleytman 633eb700029SDmitry Fleytman pkt->is_loopback = true; 634eb700029SDmitry Fleytman res = net_tx_pkt_send(pkt, nc); 635eb700029SDmitry Fleytman pkt->is_loopback = false; 636eb700029SDmitry Fleytman 637eb700029SDmitry Fleytman return res; 638eb700029SDmitry Fleytman } 639