1*605d52e6SDmitry Fleytman /* 2*605d52e6SDmitry Fleytman * QEMU TX packets abstractions 3*605d52e6SDmitry Fleytman * 4*605d52e6SDmitry Fleytman * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) 5*605d52e6SDmitry Fleytman * 6*605d52e6SDmitry Fleytman * Developed by Daynix Computing LTD (http://www.daynix.com) 7*605d52e6SDmitry Fleytman * 8*605d52e6SDmitry Fleytman * Authors: 9*605d52e6SDmitry Fleytman * Dmitry Fleytman <dmitry@daynix.com> 10*605d52e6SDmitry Fleytman * Tamir Shomer <tamirs@daynix.com> 11*605d52e6SDmitry Fleytman * Yan Vugenfirer <yan@daynix.com> 12*605d52e6SDmitry Fleytman * 13*605d52e6SDmitry Fleytman * This work is licensed under the terms of the GNU GPL, version 2 or later. 14*605d52e6SDmitry Fleytman * See the COPYING file in the top-level directory. 15*605d52e6SDmitry Fleytman * 16*605d52e6SDmitry Fleytman */ 17*605d52e6SDmitry Fleytman 18*605d52e6SDmitry Fleytman #include "qemu/osdep.h" 19*605d52e6SDmitry Fleytman #include "hw/hw.h" 20*605d52e6SDmitry Fleytman #include "net_tx_pkt.h" 21*605d52e6SDmitry Fleytman #include "net/eth.h" 22*605d52e6SDmitry Fleytman #include "qemu-common.h" 23*605d52e6SDmitry Fleytman #include "qemu/iov.h" 24*605d52e6SDmitry Fleytman #include "net/checksum.h" 25*605d52e6SDmitry Fleytman #include "net/tap.h" 26*605d52e6SDmitry Fleytman #include "net/net.h" 27*605d52e6SDmitry Fleytman 28*605d52e6SDmitry Fleytman enum { 29*605d52e6SDmitry Fleytman NET_TX_PKT_VHDR_FRAG = 0, 30*605d52e6SDmitry Fleytman NET_TX_PKT_L2HDR_FRAG, 31*605d52e6SDmitry Fleytman NET_TX_PKT_L3HDR_FRAG, 32*605d52e6SDmitry Fleytman NET_TX_PKT_PL_START_FRAG 33*605d52e6SDmitry Fleytman }; 34*605d52e6SDmitry Fleytman 35*605d52e6SDmitry Fleytman /* TX packet private context */ 36*605d52e6SDmitry Fleytman struct NetTxPkt { 37*605d52e6SDmitry Fleytman struct virtio_net_hdr virt_hdr; 38*605d52e6SDmitry Fleytman bool has_virt_hdr; 39*605d52e6SDmitry Fleytman 40*605d52e6SDmitry Fleytman struct iovec *raw; 41*605d52e6SDmitry Fleytman uint32_t raw_frags; 42*605d52e6SDmitry Fleytman uint32_t max_raw_frags; 43*605d52e6SDmitry Fleytman 44*605d52e6SDmitry Fleytman struct iovec *vec; 45*605d52e6SDmitry Fleytman 46*605d52e6SDmitry Fleytman uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN]; 47*605d52e6SDmitry Fleytman 48*605d52e6SDmitry Fleytman uint32_t payload_len; 49*605d52e6SDmitry Fleytman 50*605d52e6SDmitry Fleytman uint32_t payload_frags; 51*605d52e6SDmitry Fleytman uint32_t max_payload_frags; 52*605d52e6SDmitry Fleytman 53*605d52e6SDmitry Fleytman uint16_t hdr_len; 54*605d52e6SDmitry Fleytman eth_pkt_types_e packet_type; 55*605d52e6SDmitry Fleytman uint8_t l4proto; 56*605d52e6SDmitry Fleytman }; 57*605d52e6SDmitry Fleytman 58*605d52e6SDmitry Fleytman void net_tx_pkt_init(struct NetTxPkt **pkt, uint32_t max_frags, 59*605d52e6SDmitry Fleytman bool has_virt_hdr) 60*605d52e6SDmitry Fleytman { 61*605d52e6SDmitry Fleytman struct NetTxPkt *p = g_malloc0(sizeof *p); 62*605d52e6SDmitry Fleytman 63*605d52e6SDmitry Fleytman p->vec = g_malloc((sizeof *p->vec) * 64*605d52e6SDmitry Fleytman (max_frags + NET_TX_PKT_PL_START_FRAG)); 65*605d52e6SDmitry Fleytman 66*605d52e6SDmitry Fleytman p->raw = g_malloc((sizeof *p->raw) * max_frags); 67*605d52e6SDmitry Fleytman 68*605d52e6SDmitry Fleytman p->max_payload_frags = max_frags; 69*605d52e6SDmitry Fleytman p->max_raw_frags = max_frags; 70*605d52e6SDmitry Fleytman p->has_virt_hdr = has_virt_hdr; 71*605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; 72*605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_VHDR_FRAG].iov_len = 73*605d52e6SDmitry Fleytman p->has_virt_hdr ? sizeof p->virt_hdr : 0; 74*605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; 75*605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_L3HDR_FRAG].iov_base = NULL; 76*605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_L3HDR_FRAG].iov_len = 0; 77*605d52e6SDmitry Fleytman 78*605d52e6SDmitry Fleytman *pkt = p; 79*605d52e6SDmitry Fleytman } 80*605d52e6SDmitry Fleytman 81*605d52e6SDmitry Fleytman void net_tx_pkt_uninit(struct NetTxPkt *pkt) 82*605d52e6SDmitry Fleytman { 83*605d52e6SDmitry Fleytman if (pkt) { 84*605d52e6SDmitry Fleytman g_free(pkt->vec); 85*605d52e6SDmitry Fleytman g_free(pkt->raw); 86*605d52e6SDmitry Fleytman g_free(pkt); 87*605d52e6SDmitry Fleytman } 88*605d52e6SDmitry Fleytman } 89*605d52e6SDmitry Fleytman 90*605d52e6SDmitry Fleytman void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt) 91*605d52e6SDmitry Fleytman { 92*605d52e6SDmitry Fleytman uint16_t csum; 93*605d52e6SDmitry Fleytman uint32_t ph_raw_csum; 94*605d52e6SDmitry Fleytman assert(pkt); 95*605d52e6SDmitry Fleytman uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; 96*605d52e6SDmitry Fleytman struct ip_header *ip_hdr; 97*605d52e6SDmitry Fleytman 98*605d52e6SDmitry Fleytman if (VIRTIO_NET_HDR_GSO_TCPV4 != gso_type && 99*605d52e6SDmitry Fleytman VIRTIO_NET_HDR_GSO_UDP != gso_type) { 100*605d52e6SDmitry Fleytman return; 101*605d52e6SDmitry Fleytman } 102*605d52e6SDmitry Fleytman 103*605d52e6SDmitry Fleytman ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 104*605d52e6SDmitry Fleytman 105*605d52e6SDmitry Fleytman if (pkt->payload_len + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len > 106*605d52e6SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN) { 107*605d52e6SDmitry Fleytman return; 108*605d52e6SDmitry Fleytman } 109*605d52e6SDmitry Fleytman 110*605d52e6SDmitry Fleytman ip_hdr->ip_len = cpu_to_be16(pkt->payload_len + 111*605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 112*605d52e6SDmitry Fleytman 113*605d52e6SDmitry Fleytman /* Calculate IP header checksum */ 114*605d52e6SDmitry Fleytman ip_hdr->ip_sum = 0; 115*605d52e6SDmitry Fleytman csum = net_raw_checksum((uint8_t *)ip_hdr, 116*605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 117*605d52e6SDmitry Fleytman ip_hdr->ip_sum = cpu_to_be16(csum); 118*605d52e6SDmitry Fleytman 119*605d52e6SDmitry Fleytman /* Calculate IP pseudo header checksum */ 120*605d52e6SDmitry Fleytman ph_raw_csum = eth_calc_pseudo_hdr_csum(ip_hdr, pkt->payload_len); 121*605d52e6SDmitry Fleytman csum = cpu_to_be16(~net_checksum_finish(ph_raw_csum)); 122*605d52e6SDmitry Fleytman iov_from_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, 123*605d52e6SDmitry Fleytman pkt->virt_hdr.csum_offset, &csum, sizeof(csum)); 124*605d52e6SDmitry Fleytman } 125*605d52e6SDmitry Fleytman 126*605d52e6SDmitry Fleytman static void net_tx_pkt_calculate_hdr_len(struct NetTxPkt *pkt) 127*605d52e6SDmitry Fleytman { 128*605d52e6SDmitry Fleytman pkt->hdr_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len + 129*605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; 130*605d52e6SDmitry Fleytman } 131*605d52e6SDmitry Fleytman 132*605d52e6SDmitry Fleytman static bool net_tx_pkt_parse_headers(struct NetTxPkt *pkt) 133*605d52e6SDmitry Fleytman { 134*605d52e6SDmitry Fleytman struct iovec *l2_hdr, *l3_hdr; 135*605d52e6SDmitry Fleytman size_t bytes_read; 136*605d52e6SDmitry Fleytman size_t full_ip6hdr_len; 137*605d52e6SDmitry Fleytman uint16_t l3_proto; 138*605d52e6SDmitry Fleytman 139*605d52e6SDmitry Fleytman assert(pkt); 140*605d52e6SDmitry Fleytman 141*605d52e6SDmitry Fleytman l2_hdr = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 142*605d52e6SDmitry Fleytman l3_hdr = &pkt->vec[NET_TX_PKT_L3HDR_FRAG]; 143*605d52e6SDmitry Fleytman 144*605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, 145*605d52e6SDmitry Fleytman ETH_MAX_L2_HDR_LEN); 146*605d52e6SDmitry Fleytman if (bytes_read < sizeof(struct eth_header)) { 147*605d52e6SDmitry Fleytman l2_hdr->iov_len = 0; 148*605d52e6SDmitry Fleytman return false; 149*605d52e6SDmitry Fleytman } 150*605d52e6SDmitry Fleytman 151*605d52e6SDmitry Fleytman l2_hdr->iov_len = sizeof(struct eth_header); 152*605d52e6SDmitry Fleytman switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) { 153*605d52e6SDmitry Fleytman case ETH_P_VLAN: 154*605d52e6SDmitry Fleytman l2_hdr->iov_len += sizeof(struct vlan_header); 155*605d52e6SDmitry Fleytman break; 156*605d52e6SDmitry Fleytman case ETH_P_DVLAN: 157*605d52e6SDmitry Fleytman l2_hdr->iov_len += 2 * sizeof(struct vlan_header); 158*605d52e6SDmitry Fleytman break; 159*605d52e6SDmitry Fleytman } 160*605d52e6SDmitry Fleytman 161*605d52e6SDmitry Fleytman if (bytes_read < l2_hdr->iov_len) { 162*605d52e6SDmitry Fleytman l2_hdr->iov_len = 0; 163*605d52e6SDmitry Fleytman return false; 164*605d52e6SDmitry Fleytman } 165*605d52e6SDmitry Fleytman 166*605d52e6SDmitry Fleytman l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len); 167*605d52e6SDmitry Fleytman 168*605d52e6SDmitry Fleytman switch (l3_proto) { 169*605d52e6SDmitry Fleytman case ETH_P_IP: 170*605d52e6SDmitry Fleytman l3_hdr->iov_base = g_malloc(ETH_MAX_IP4_HDR_LEN); 171*605d52e6SDmitry Fleytman 172*605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 173*605d52e6SDmitry Fleytman l3_hdr->iov_base, sizeof(struct ip_header)); 174*605d52e6SDmitry Fleytman 175*605d52e6SDmitry Fleytman if (bytes_read < sizeof(struct ip_header)) { 176*605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 177*605d52e6SDmitry Fleytman return false; 178*605d52e6SDmitry Fleytman } 179*605d52e6SDmitry Fleytman 180*605d52e6SDmitry Fleytman l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base); 181*605d52e6SDmitry Fleytman pkt->l4proto = ((struct ip_header *) l3_hdr->iov_base)->ip_p; 182*605d52e6SDmitry Fleytman 183*605d52e6SDmitry Fleytman /* copy optional IPv4 header data */ 184*605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 185*605d52e6SDmitry Fleytman l2_hdr->iov_len + sizeof(struct ip_header), 186*605d52e6SDmitry Fleytman l3_hdr->iov_base + sizeof(struct ip_header), 187*605d52e6SDmitry Fleytman l3_hdr->iov_len - sizeof(struct ip_header)); 188*605d52e6SDmitry Fleytman if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) { 189*605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 190*605d52e6SDmitry Fleytman return false; 191*605d52e6SDmitry Fleytman } 192*605d52e6SDmitry Fleytman break; 193*605d52e6SDmitry Fleytman 194*605d52e6SDmitry Fleytman case ETH_P_IPV6: 195*605d52e6SDmitry Fleytman if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 196*605d52e6SDmitry Fleytman &pkt->l4proto, &full_ip6hdr_len)) { 197*605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 198*605d52e6SDmitry Fleytman return false; 199*605d52e6SDmitry Fleytman } 200*605d52e6SDmitry Fleytman 201*605d52e6SDmitry Fleytman l3_hdr->iov_base = g_malloc(full_ip6hdr_len); 202*605d52e6SDmitry Fleytman 203*605d52e6SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 204*605d52e6SDmitry Fleytman l3_hdr->iov_base, full_ip6hdr_len); 205*605d52e6SDmitry Fleytman 206*605d52e6SDmitry Fleytman if (bytes_read < full_ip6hdr_len) { 207*605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 208*605d52e6SDmitry Fleytman return false; 209*605d52e6SDmitry Fleytman } else { 210*605d52e6SDmitry Fleytman l3_hdr->iov_len = full_ip6hdr_len; 211*605d52e6SDmitry Fleytman } 212*605d52e6SDmitry Fleytman break; 213*605d52e6SDmitry Fleytman 214*605d52e6SDmitry Fleytman default: 215*605d52e6SDmitry Fleytman l3_hdr->iov_len = 0; 216*605d52e6SDmitry Fleytman break; 217*605d52e6SDmitry Fleytman } 218*605d52e6SDmitry Fleytman 219*605d52e6SDmitry Fleytman net_tx_pkt_calculate_hdr_len(pkt); 220*605d52e6SDmitry Fleytman pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base); 221*605d52e6SDmitry Fleytman return true; 222*605d52e6SDmitry Fleytman } 223*605d52e6SDmitry Fleytman 224*605d52e6SDmitry Fleytman static bool net_tx_pkt_rebuild_payload(struct NetTxPkt *pkt) 225*605d52e6SDmitry Fleytman { 226*605d52e6SDmitry Fleytman size_t payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len; 227*605d52e6SDmitry Fleytman 228*605d52e6SDmitry Fleytman pkt->payload_frags = iov_copy(&pkt->vec[NET_TX_PKT_PL_START_FRAG], 229*605d52e6SDmitry Fleytman pkt->max_payload_frags, 230*605d52e6SDmitry Fleytman pkt->raw, pkt->raw_frags, 231*605d52e6SDmitry Fleytman pkt->hdr_len, payload_len); 232*605d52e6SDmitry Fleytman 233*605d52e6SDmitry Fleytman if (pkt->payload_frags != (uint32_t) -1) { 234*605d52e6SDmitry Fleytman pkt->payload_len = payload_len; 235*605d52e6SDmitry Fleytman return true; 236*605d52e6SDmitry Fleytman } else { 237*605d52e6SDmitry Fleytman return false; 238*605d52e6SDmitry Fleytman } 239*605d52e6SDmitry Fleytman } 240*605d52e6SDmitry Fleytman 241*605d52e6SDmitry Fleytman bool net_tx_pkt_parse(struct NetTxPkt *pkt) 242*605d52e6SDmitry Fleytman { 243*605d52e6SDmitry Fleytman return net_tx_pkt_parse_headers(pkt) && 244*605d52e6SDmitry Fleytman net_tx_pkt_rebuild_payload(pkt); 245*605d52e6SDmitry Fleytman } 246*605d52e6SDmitry Fleytman 247*605d52e6SDmitry Fleytman struct virtio_net_hdr *net_tx_pkt_get_vhdr(struct NetTxPkt *pkt) 248*605d52e6SDmitry Fleytman { 249*605d52e6SDmitry Fleytman assert(pkt); 250*605d52e6SDmitry Fleytman return &pkt->virt_hdr; 251*605d52e6SDmitry Fleytman } 252*605d52e6SDmitry Fleytman 253*605d52e6SDmitry Fleytman static uint8_t net_tx_pkt_get_gso_type(struct NetTxPkt *pkt, 254*605d52e6SDmitry Fleytman bool tso_enable) 255*605d52e6SDmitry Fleytman { 256*605d52e6SDmitry Fleytman uint8_t rc = VIRTIO_NET_HDR_GSO_NONE; 257*605d52e6SDmitry Fleytman uint16_t l3_proto; 258*605d52e6SDmitry Fleytman 259*605d52e6SDmitry Fleytman l3_proto = eth_get_l3_proto(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base, 260*605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len); 261*605d52e6SDmitry Fleytman 262*605d52e6SDmitry Fleytman if (!tso_enable) { 263*605d52e6SDmitry Fleytman goto func_exit; 264*605d52e6SDmitry Fleytman } 265*605d52e6SDmitry Fleytman 266*605d52e6SDmitry Fleytman rc = eth_get_gso_type(l3_proto, pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 267*605d52e6SDmitry Fleytman pkt->l4proto); 268*605d52e6SDmitry Fleytman 269*605d52e6SDmitry Fleytman func_exit: 270*605d52e6SDmitry Fleytman return rc; 271*605d52e6SDmitry Fleytman } 272*605d52e6SDmitry Fleytman 273*605d52e6SDmitry Fleytman void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, 274*605d52e6SDmitry Fleytman bool csum_enable, uint32_t gso_size) 275*605d52e6SDmitry Fleytman { 276*605d52e6SDmitry Fleytman struct tcp_hdr l4hdr; 277*605d52e6SDmitry Fleytman assert(pkt); 278*605d52e6SDmitry Fleytman 279*605d52e6SDmitry Fleytman /* csum has to be enabled if tso is. */ 280*605d52e6SDmitry Fleytman assert(csum_enable || !tso_enable); 281*605d52e6SDmitry Fleytman 282*605d52e6SDmitry Fleytman pkt->virt_hdr.gso_type = net_tx_pkt_get_gso_type(pkt, tso_enable); 283*605d52e6SDmitry Fleytman 284*605d52e6SDmitry Fleytman switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 285*605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_NONE: 286*605d52e6SDmitry Fleytman pkt->virt_hdr.hdr_len = 0; 287*605d52e6SDmitry Fleytman pkt->virt_hdr.gso_size = 0; 288*605d52e6SDmitry Fleytman break; 289*605d52e6SDmitry Fleytman 290*605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_UDP: 291*605d52e6SDmitry Fleytman pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size); 292*605d52e6SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header); 293*605d52e6SDmitry Fleytman break; 294*605d52e6SDmitry Fleytman 295*605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV4: 296*605d52e6SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV6: 297*605d52e6SDmitry Fleytman iov_to_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, 298*605d52e6SDmitry Fleytman 0, &l4hdr, sizeof(l4hdr)); 299*605d52e6SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t); 300*605d52e6SDmitry Fleytman pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size); 301*605d52e6SDmitry Fleytman break; 302*605d52e6SDmitry Fleytman 303*605d52e6SDmitry Fleytman default: 304*605d52e6SDmitry Fleytman g_assert_not_reached(); 305*605d52e6SDmitry Fleytman } 306*605d52e6SDmitry Fleytman 307*605d52e6SDmitry Fleytman if (csum_enable) { 308*605d52e6SDmitry Fleytman switch (pkt->l4proto) { 309*605d52e6SDmitry Fleytman case IP_PROTO_TCP: 310*605d52e6SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 311*605d52e6SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 312*605d52e6SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum); 313*605d52e6SDmitry Fleytman break; 314*605d52e6SDmitry Fleytman case IP_PROTO_UDP: 315*605d52e6SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 316*605d52e6SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 317*605d52e6SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum); 318*605d52e6SDmitry Fleytman break; 319*605d52e6SDmitry Fleytman default: 320*605d52e6SDmitry Fleytman break; 321*605d52e6SDmitry Fleytman } 322*605d52e6SDmitry Fleytman } 323*605d52e6SDmitry Fleytman } 324*605d52e6SDmitry Fleytman 325*605d52e6SDmitry Fleytman void net_tx_pkt_setup_vlan_header(struct NetTxPkt *pkt, uint16_t vlan) 326*605d52e6SDmitry Fleytman { 327*605d52e6SDmitry Fleytman bool is_new; 328*605d52e6SDmitry Fleytman assert(pkt); 329*605d52e6SDmitry Fleytman 330*605d52e6SDmitry Fleytman eth_setup_vlan_headers(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base, 331*605d52e6SDmitry Fleytman vlan, &is_new); 332*605d52e6SDmitry Fleytman 333*605d52e6SDmitry Fleytman /* update l2hdrlen */ 334*605d52e6SDmitry Fleytman if (is_new) { 335*605d52e6SDmitry Fleytman pkt->hdr_len += sizeof(struct vlan_header); 336*605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len += 337*605d52e6SDmitry Fleytman sizeof(struct vlan_header); 338*605d52e6SDmitry Fleytman } 339*605d52e6SDmitry Fleytman } 340*605d52e6SDmitry Fleytman 341*605d52e6SDmitry Fleytman bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa, 342*605d52e6SDmitry Fleytman size_t len) 343*605d52e6SDmitry Fleytman { 344*605d52e6SDmitry Fleytman hwaddr mapped_len = 0; 345*605d52e6SDmitry Fleytman struct iovec *ventry; 346*605d52e6SDmitry Fleytman assert(pkt); 347*605d52e6SDmitry Fleytman assert(pkt->max_raw_frags > pkt->raw_frags); 348*605d52e6SDmitry Fleytman 349*605d52e6SDmitry Fleytman if (!len) { 350*605d52e6SDmitry Fleytman return true; 351*605d52e6SDmitry Fleytman } 352*605d52e6SDmitry Fleytman 353*605d52e6SDmitry Fleytman ventry = &pkt->raw[pkt->raw_frags]; 354*605d52e6SDmitry Fleytman mapped_len = len; 355*605d52e6SDmitry Fleytman 356*605d52e6SDmitry Fleytman ventry->iov_base = cpu_physical_memory_map(pa, &mapped_len, false); 357*605d52e6SDmitry Fleytman ventry->iov_len = mapped_len; 358*605d52e6SDmitry Fleytman pkt->raw_frags += !!ventry->iov_base; 359*605d52e6SDmitry Fleytman 360*605d52e6SDmitry Fleytman if ((ventry->iov_base == NULL) || (len != mapped_len)) { 361*605d52e6SDmitry Fleytman return false; 362*605d52e6SDmitry Fleytman } 363*605d52e6SDmitry Fleytman 364*605d52e6SDmitry Fleytman return true; 365*605d52e6SDmitry Fleytman } 366*605d52e6SDmitry Fleytman 367*605d52e6SDmitry Fleytman eth_pkt_types_e net_tx_pkt_get_packet_type(struct NetTxPkt *pkt) 368*605d52e6SDmitry Fleytman { 369*605d52e6SDmitry Fleytman assert(pkt); 370*605d52e6SDmitry Fleytman 371*605d52e6SDmitry Fleytman return pkt->packet_type; 372*605d52e6SDmitry Fleytman } 373*605d52e6SDmitry Fleytman 374*605d52e6SDmitry Fleytman size_t net_tx_pkt_get_total_len(struct NetTxPkt *pkt) 375*605d52e6SDmitry Fleytman { 376*605d52e6SDmitry Fleytman assert(pkt); 377*605d52e6SDmitry Fleytman 378*605d52e6SDmitry Fleytman return pkt->hdr_len + pkt->payload_len; 379*605d52e6SDmitry Fleytman } 380*605d52e6SDmitry Fleytman 381*605d52e6SDmitry Fleytman void net_tx_pkt_dump(struct NetTxPkt *pkt) 382*605d52e6SDmitry Fleytman { 383*605d52e6SDmitry Fleytman #ifdef NET_TX_PKT_DEBUG 384*605d52e6SDmitry Fleytman assert(pkt); 385*605d52e6SDmitry Fleytman 386*605d52e6SDmitry Fleytman printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, " 387*605d52e6SDmitry Fleytman "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type, 388*605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len, 389*605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len); 390*605d52e6SDmitry Fleytman #endif 391*605d52e6SDmitry Fleytman } 392*605d52e6SDmitry Fleytman 393*605d52e6SDmitry Fleytman void net_tx_pkt_reset(struct NetTxPkt *pkt) 394*605d52e6SDmitry Fleytman { 395*605d52e6SDmitry Fleytman int i; 396*605d52e6SDmitry Fleytman 397*605d52e6SDmitry Fleytman /* no assert, as reset can be called before tx_pkt_init */ 398*605d52e6SDmitry Fleytman if (!pkt) { 399*605d52e6SDmitry Fleytman return; 400*605d52e6SDmitry Fleytman } 401*605d52e6SDmitry Fleytman 402*605d52e6SDmitry Fleytman memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr)); 403*605d52e6SDmitry Fleytman 404*605d52e6SDmitry Fleytman g_free(pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base); 405*605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base = NULL; 406*605d52e6SDmitry Fleytman 407*605d52e6SDmitry Fleytman assert(pkt->vec); 408*605d52e6SDmitry Fleytman for (i = NET_TX_PKT_L2HDR_FRAG; 409*605d52e6SDmitry Fleytman i < pkt->payload_frags + NET_TX_PKT_PL_START_FRAG; i++) { 410*605d52e6SDmitry Fleytman pkt->vec[i].iov_len = 0; 411*605d52e6SDmitry Fleytman } 412*605d52e6SDmitry Fleytman pkt->payload_len = 0; 413*605d52e6SDmitry Fleytman pkt->payload_frags = 0; 414*605d52e6SDmitry Fleytman 415*605d52e6SDmitry Fleytman assert(pkt->raw); 416*605d52e6SDmitry Fleytman for (i = 0; i < pkt->raw_frags; i++) { 417*605d52e6SDmitry Fleytman assert(pkt->raw[i].iov_base); 418*605d52e6SDmitry Fleytman cpu_physical_memory_unmap(pkt->raw[i].iov_base, pkt->raw[i].iov_len, 419*605d52e6SDmitry Fleytman false, pkt->raw[i].iov_len); 420*605d52e6SDmitry Fleytman pkt->raw[i].iov_len = 0; 421*605d52e6SDmitry Fleytman } 422*605d52e6SDmitry Fleytman pkt->raw_frags = 0; 423*605d52e6SDmitry Fleytman 424*605d52e6SDmitry Fleytman pkt->hdr_len = 0; 425*605d52e6SDmitry Fleytman pkt->packet_type = 0; 426*605d52e6SDmitry Fleytman pkt->l4proto = 0; 427*605d52e6SDmitry Fleytman } 428*605d52e6SDmitry Fleytman 429*605d52e6SDmitry Fleytman static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt) 430*605d52e6SDmitry Fleytman { 431*605d52e6SDmitry Fleytman struct iovec *iov = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 432*605d52e6SDmitry Fleytman uint32_t csum_cntr; 433*605d52e6SDmitry Fleytman uint16_t csum = 0; 434*605d52e6SDmitry Fleytman /* num of iovec without vhdr */ 435*605d52e6SDmitry Fleytman uint32_t iov_len = pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1; 436*605d52e6SDmitry Fleytman uint16_t csl; 437*605d52e6SDmitry Fleytman struct ip_header *iphdr; 438*605d52e6SDmitry Fleytman size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset; 439*605d52e6SDmitry Fleytman 440*605d52e6SDmitry Fleytman /* Put zero to checksum field */ 441*605d52e6SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 442*605d52e6SDmitry Fleytman 443*605d52e6SDmitry Fleytman /* Calculate L4 TCP/UDP checksum */ 444*605d52e6SDmitry Fleytman csl = pkt->payload_len; 445*605d52e6SDmitry Fleytman 446*605d52e6SDmitry Fleytman /* data checksum */ 447*605d52e6SDmitry Fleytman csum_cntr = 448*605d52e6SDmitry Fleytman net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl); 449*605d52e6SDmitry Fleytman /* add pseudo header to csum */ 450*605d52e6SDmitry Fleytman iphdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 451*605d52e6SDmitry Fleytman csum_cntr += eth_calc_pseudo_hdr_csum(iphdr, csl); 452*605d52e6SDmitry Fleytman 453*605d52e6SDmitry Fleytman /* Put the checksum obtained into the packet */ 454*605d52e6SDmitry Fleytman csum = cpu_to_be16(net_checksum_finish(csum_cntr)); 455*605d52e6SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 456*605d52e6SDmitry Fleytman } 457*605d52e6SDmitry Fleytman 458*605d52e6SDmitry Fleytman enum { 459*605d52e6SDmitry Fleytman NET_TX_PKT_FRAGMENT_L2_HDR_POS = 0, 460*605d52e6SDmitry Fleytman NET_TX_PKT_FRAGMENT_L3_HDR_POS, 461*605d52e6SDmitry Fleytman NET_TX_PKT_FRAGMENT_HEADER_NUM 462*605d52e6SDmitry Fleytman }; 463*605d52e6SDmitry Fleytman 464*605d52e6SDmitry Fleytman #define NET_MAX_FRAG_SG_LIST (64) 465*605d52e6SDmitry Fleytman 466*605d52e6SDmitry Fleytman static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt *pkt, 467*605d52e6SDmitry Fleytman int *src_idx, size_t *src_offset, struct iovec *dst, int *dst_idx) 468*605d52e6SDmitry Fleytman { 469*605d52e6SDmitry Fleytman size_t fetched = 0; 470*605d52e6SDmitry Fleytman struct iovec *src = pkt->vec; 471*605d52e6SDmitry Fleytman 472*605d52e6SDmitry Fleytman *dst_idx = NET_TX_PKT_FRAGMENT_HEADER_NUM; 473*605d52e6SDmitry Fleytman 474*605d52e6SDmitry Fleytman while (fetched < pkt->virt_hdr.gso_size) { 475*605d52e6SDmitry Fleytman 476*605d52e6SDmitry Fleytman /* no more place in fragment iov */ 477*605d52e6SDmitry Fleytman if (*dst_idx == NET_MAX_FRAG_SG_LIST) { 478*605d52e6SDmitry Fleytman break; 479*605d52e6SDmitry Fleytman } 480*605d52e6SDmitry Fleytman 481*605d52e6SDmitry Fleytman /* no more data in iovec */ 482*605d52e6SDmitry Fleytman if (*src_idx == (pkt->payload_frags + NET_TX_PKT_PL_START_FRAG)) { 483*605d52e6SDmitry Fleytman break; 484*605d52e6SDmitry Fleytman } 485*605d52e6SDmitry Fleytman 486*605d52e6SDmitry Fleytman 487*605d52e6SDmitry Fleytman dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset; 488*605d52e6SDmitry Fleytman dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset, 489*605d52e6SDmitry Fleytman pkt->virt_hdr.gso_size - fetched); 490*605d52e6SDmitry Fleytman 491*605d52e6SDmitry Fleytman *src_offset += dst[*dst_idx].iov_len; 492*605d52e6SDmitry Fleytman fetched += dst[*dst_idx].iov_len; 493*605d52e6SDmitry Fleytman 494*605d52e6SDmitry Fleytman if (*src_offset == src[*src_idx].iov_len) { 495*605d52e6SDmitry Fleytman *src_offset = 0; 496*605d52e6SDmitry Fleytman (*src_idx)++; 497*605d52e6SDmitry Fleytman } 498*605d52e6SDmitry Fleytman 499*605d52e6SDmitry Fleytman (*dst_idx)++; 500*605d52e6SDmitry Fleytman } 501*605d52e6SDmitry Fleytman 502*605d52e6SDmitry Fleytman return fetched; 503*605d52e6SDmitry Fleytman } 504*605d52e6SDmitry Fleytman 505*605d52e6SDmitry Fleytman static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, 506*605d52e6SDmitry Fleytman NetClientState *nc) 507*605d52e6SDmitry Fleytman { 508*605d52e6SDmitry Fleytman struct iovec fragment[NET_MAX_FRAG_SG_LIST]; 509*605d52e6SDmitry Fleytman size_t fragment_len = 0; 510*605d52e6SDmitry Fleytman bool more_frags = false; 511*605d52e6SDmitry Fleytman 512*605d52e6SDmitry Fleytman /* some pointers for shorter code */ 513*605d52e6SDmitry Fleytman void *l2_iov_base, *l3_iov_base; 514*605d52e6SDmitry Fleytman size_t l2_iov_len, l3_iov_len; 515*605d52e6SDmitry Fleytman int src_idx = NET_TX_PKT_PL_START_FRAG, dst_idx; 516*605d52e6SDmitry Fleytman size_t src_offset = 0; 517*605d52e6SDmitry Fleytman size_t fragment_offset = 0; 518*605d52e6SDmitry Fleytman 519*605d52e6SDmitry Fleytman l2_iov_base = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base; 520*605d52e6SDmitry Fleytman l2_iov_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len; 521*605d52e6SDmitry Fleytman l3_iov_base = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 522*605d52e6SDmitry Fleytman l3_iov_len = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; 523*605d52e6SDmitry Fleytman 524*605d52e6SDmitry Fleytman /* Copy headers */ 525*605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base; 526*605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len; 527*605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base; 528*605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len; 529*605d52e6SDmitry Fleytman 530*605d52e6SDmitry Fleytman 531*605d52e6SDmitry Fleytman /* Put as much data as possible and send */ 532*605d52e6SDmitry Fleytman do { 533*605d52e6SDmitry Fleytman fragment_len = net_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset, 534*605d52e6SDmitry Fleytman fragment, &dst_idx); 535*605d52e6SDmitry Fleytman 536*605d52e6SDmitry Fleytman more_frags = (fragment_offset + fragment_len < pkt->payload_len); 537*605d52e6SDmitry Fleytman 538*605d52e6SDmitry Fleytman eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base, 539*605d52e6SDmitry Fleytman l3_iov_len, fragment_len, fragment_offset, more_frags); 540*605d52e6SDmitry Fleytman 541*605d52e6SDmitry Fleytman eth_fix_ip4_checksum(l3_iov_base, l3_iov_len); 542*605d52e6SDmitry Fleytman 543*605d52e6SDmitry Fleytman qemu_sendv_packet(nc, fragment, dst_idx); 544*605d52e6SDmitry Fleytman 545*605d52e6SDmitry Fleytman fragment_offset += fragment_len; 546*605d52e6SDmitry Fleytman 547*605d52e6SDmitry Fleytman } while (more_frags); 548*605d52e6SDmitry Fleytman 549*605d52e6SDmitry Fleytman return true; 550*605d52e6SDmitry Fleytman } 551*605d52e6SDmitry Fleytman 552*605d52e6SDmitry Fleytman bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) 553*605d52e6SDmitry Fleytman { 554*605d52e6SDmitry Fleytman assert(pkt); 555*605d52e6SDmitry Fleytman 556*605d52e6SDmitry Fleytman if (!pkt->has_virt_hdr && 557*605d52e6SDmitry Fleytman pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 558*605d52e6SDmitry Fleytman net_tx_pkt_do_sw_csum(pkt); 559*605d52e6SDmitry Fleytman } 560*605d52e6SDmitry Fleytman 561*605d52e6SDmitry Fleytman /* 562*605d52e6SDmitry Fleytman * Since underlying infrastructure does not support IP datagrams longer 563*605d52e6SDmitry Fleytman * than 64K we should drop such packets and don't even try to send 564*605d52e6SDmitry Fleytman */ 565*605d52e6SDmitry Fleytman if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) { 566*605d52e6SDmitry Fleytman if (pkt->payload_len > 567*605d52e6SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN - 568*605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len) { 569*605d52e6SDmitry Fleytman return false; 570*605d52e6SDmitry Fleytman } 571*605d52e6SDmitry Fleytman } 572*605d52e6SDmitry Fleytman 573*605d52e6SDmitry Fleytman if (pkt->has_virt_hdr || 574*605d52e6SDmitry Fleytman pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) { 575*605d52e6SDmitry Fleytman qemu_sendv_packet(nc, pkt->vec, 576*605d52e6SDmitry Fleytman pkt->payload_frags + NET_TX_PKT_PL_START_FRAG); 577*605d52e6SDmitry Fleytman return true; 578*605d52e6SDmitry Fleytman } 579*605d52e6SDmitry Fleytman 580*605d52e6SDmitry Fleytman return net_tx_pkt_do_sw_fragmentation(pkt, nc); 581*605d52e6SDmitry Fleytman } 582