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" 19605d52e6SDmitry Fleytman #include "net_tx_pkt.h" 20e263cd49SDmitry Fleytman #include "net/eth.h" 21e263cd49SDmitry Fleytman #include "net/checksum.h" 22e263cd49SDmitry Fleytman #include "net/tap.h" 23e263cd49SDmitry Fleytman #include "net/net.h" 24edf5ca5dSMarkus Armbruster #include "hw/pci/pci_device.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 { 3511171010SDmitry Fleytman PCIDevice *pci_dev; 3611171010SDmitry Fleytman 37e263cd49SDmitry Fleytman struct virtio_net_hdr virt_hdr; 38e263cd49SDmitry Fleytman bool has_virt_hdr; 39e263cd49SDmitry Fleytman 40e263cd49SDmitry Fleytman struct iovec *raw; 41e263cd49SDmitry Fleytman uint32_t raw_frags; 42e263cd49SDmitry Fleytman uint32_t max_raw_frags; 43e263cd49SDmitry Fleytman 44e263cd49SDmitry Fleytman struct iovec *vec; 45e263cd49SDmitry Fleytman 46e263cd49SDmitry Fleytman uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN]; 47eb700029SDmitry Fleytman uint8_t l3_hdr[ETH_MAX_IP_DGRAM_LEN]; 48e263cd49SDmitry Fleytman 49e263cd49SDmitry Fleytman uint32_t payload_len; 50e263cd49SDmitry Fleytman 51e263cd49SDmitry Fleytman uint32_t payload_frags; 52e263cd49SDmitry Fleytman uint32_t max_payload_frags; 53e263cd49SDmitry Fleytman 54e263cd49SDmitry Fleytman uint16_t hdr_len; 55e263cd49SDmitry Fleytman eth_pkt_types_e packet_type; 56e263cd49SDmitry Fleytman uint8_t l4proto; 57eb700029SDmitry Fleytman 58eb700029SDmitry Fleytman bool is_loopback; 59e263cd49SDmitry Fleytman }; 60e263cd49SDmitry Fleytman 6111171010SDmitry Fleytman void net_tx_pkt_init(struct NetTxPkt **pkt, PCIDevice *pci_dev, 6211171010SDmitry Fleytman uint32_t max_frags, bool has_virt_hdr) 63e263cd49SDmitry Fleytman { 64605d52e6SDmitry Fleytman struct NetTxPkt *p = g_malloc0(sizeof *p); 65e263cd49SDmitry Fleytman 6611171010SDmitry Fleytman p->pci_dev = pci_dev; 6711171010SDmitry Fleytman 6847882fa4SLi Qiang p->vec = g_new(struct iovec, max_frags + NET_TX_PKT_PL_START_FRAG); 69e263cd49SDmitry Fleytman 7047882fa4SLi Qiang p->raw = g_new(struct iovec, max_frags); 71e263cd49SDmitry Fleytman 72e263cd49SDmitry Fleytman p->max_payload_frags = max_frags; 73e263cd49SDmitry Fleytman p->max_raw_frags = max_frags; 74e263cd49SDmitry Fleytman p->has_virt_hdr = has_virt_hdr; 75605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; 76605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_VHDR_FRAG].iov_len = 77e263cd49SDmitry Fleytman p->has_virt_hdr ? sizeof p->virt_hdr : 0; 78605d52e6SDmitry Fleytman p->vec[NET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; 79eb700029SDmitry Fleytman p->vec[NET_TX_PKT_L3HDR_FRAG].iov_base = &p->l3_hdr; 80e263cd49SDmitry Fleytman 81e263cd49SDmitry Fleytman *pkt = p; 82e263cd49SDmitry Fleytman } 83e263cd49SDmitry Fleytman 84605d52e6SDmitry Fleytman void net_tx_pkt_uninit(struct NetTxPkt *pkt) 85e263cd49SDmitry Fleytman { 86e263cd49SDmitry Fleytman if (pkt) { 87e263cd49SDmitry Fleytman g_free(pkt->vec); 88e263cd49SDmitry Fleytman g_free(pkt->raw); 89e263cd49SDmitry Fleytman g_free(pkt); 90e263cd49SDmitry Fleytman } 91e263cd49SDmitry Fleytman } 92e263cd49SDmitry Fleytman 93eb700029SDmitry Fleytman void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt) 94eb700029SDmitry Fleytman { 95eb700029SDmitry Fleytman uint16_t csum; 96eb700029SDmitry Fleytman assert(pkt); 97eb700029SDmitry Fleytman struct ip_header *ip_hdr; 98eb700029SDmitry Fleytman ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 99eb700029SDmitry Fleytman 100eb700029SDmitry Fleytman ip_hdr->ip_len = cpu_to_be16(pkt->payload_len + 101eb700029SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 102eb700029SDmitry Fleytman 103eb700029SDmitry Fleytman ip_hdr->ip_sum = 0; 104eb700029SDmitry Fleytman csum = net_raw_checksum((uint8_t *)ip_hdr, 105eb700029SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); 106eb700029SDmitry Fleytman ip_hdr->ip_sum = cpu_to_be16(csum); 107eb700029SDmitry Fleytman } 108eb700029SDmitry Fleytman 109605d52e6SDmitry Fleytman void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt) 110e263cd49SDmitry Fleytman { 111e263cd49SDmitry Fleytman uint16_t csum; 112eb700029SDmitry Fleytman uint32_t cntr, cso; 113e263cd49SDmitry Fleytman assert(pkt); 114e263cd49SDmitry Fleytman uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; 115eb700029SDmitry Fleytman void *ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 116e263cd49SDmitry Fleytman 117605d52e6SDmitry Fleytman if (pkt->payload_len + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len > 118e263cd49SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN) { 119e263cd49SDmitry Fleytman return; 120e263cd49SDmitry Fleytman } 121e263cd49SDmitry Fleytman 122eb700029SDmitry Fleytman if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || 123eb700029SDmitry Fleytman gso_type == VIRTIO_NET_HDR_GSO_UDP) { 124e263cd49SDmitry Fleytman /* Calculate IP header checksum */ 125eb700029SDmitry Fleytman net_tx_pkt_update_ip_hdr_checksum(pkt); 126e263cd49SDmitry Fleytman 127e263cd49SDmitry Fleytman /* Calculate IP pseudo header checksum */ 128eb700029SDmitry Fleytman cntr = eth_calc_ip4_pseudo_hdr_csum(ip_hdr, pkt->payload_len, &cso); 129eb700029SDmitry Fleytman csum = cpu_to_be16(~net_checksum_finish(cntr)); 130eb700029SDmitry Fleytman } else if (gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { 131eb700029SDmitry Fleytman /* Calculate IP pseudo header checksum */ 132eb700029SDmitry Fleytman cntr = eth_calc_ip6_pseudo_hdr_csum(ip_hdr, pkt->payload_len, 133eb700029SDmitry Fleytman IP_PROTO_TCP, &cso); 134eb700029SDmitry Fleytman csum = cpu_to_be16(~net_checksum_finish(cntr)); 135eb700029SDmitry Fleytman } else { 136eb700029SDmitry Fleytman return; 137eb700029SDmitry Fleytman } 138eb700029SDmitry Fleytman 139605d52e6SDmitry Fleytman iov_from_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, 140e263cd49SDmitry Fleytman pkt->virt_hdr.csum_offset, &csum, sizeof(csum)); 141e263cd49SDmitry Fleytman } 142e263cd49SDmitry Fleytman 143605d52e6SDmitry Fleytman static void net_tx_pkt_calculate_hdr_len(struct NetTxPkt *pkt) 144e263cd49SDmitry Fleytman { 145605d52e6SDmitry Fleytman pkt->hdr_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len + 146605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; 147e263cd49SDmitry Fleytman } 148e263cd49SDmitry Fleytman 149605d52e6SDmitry Fleytman static bool net_tx_pkt_parse_headers(struct NetTxPkt *pkt) 150e263cd49SDmitry Fleytman { 151e263cd49SDmitry Fleytman struct iovec *l2_hdr, *l3_hdr; 152e263cd49SDmitry Fleytman size_t bytes_read; 153e263cd49SDmitry Fleytman size_t full_ip6hdr_len; 154e263cd49SDmitry Fleytman uint16_t l3_proto; 155e263cd49SDmitry Fleytman 156e263cd49SDmitry Fleytman assert(pkt); 157e263cd49SDmitry Fleytman 158605d52e6SDmitry Fleytman l2_hdr = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 159605d52e6SDmitry Fleytman l3_hdr = &pkt->vec[NET_TX_PKT_L3HDR_FRAG]; 160e263cd49SDmitry Fleytman 161e263cd49SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, 162e263cd49SDmitry Fleytman ETH_MAX_L2_HDR_LEN); 163a7278b36SDana Rubin if (bytes_read < sizeof(struct eth_header)) { 164e263cd49SDmitry Fleytman l2_hdr->iov_len = 0; 165e263cd49SDmitry Fleytman return false; 166a7278b36SDana Rubin } 167a7278b36SDana Rubin 168a7278b36SDana Rubin l2_hdr->iov_len = sizeof(struct eth_header); 169a7278b36SDana Rubin switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) { 170a7278b36SDana Rubin case ETH_P_VLAN: 171a7278b36SDana Rubin l2_hdr->iov_len += sizeof(struct vlan_header); 172a7278b36SDana Rubin break; 173a7278b36SDana Rubin case ETH_P_DVLAN: 174a7278b36SDana Rubin l2_hdr->iov_len += 2 * sizeof(struct vlan_header); 175a7278b36SDana Rubin break; 176a7278b36SDana Rubin } 177a7278b36SDana Rubin 178a7278b36SDana Rubin if (bytes_read < l2_hdr->iov_len) { 179a7278b36SDana Rubin l2_hdr->iov_len = 0; 180eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 181eb700029SDmitry Fleytman pkt->packet_type = ETH_PKT_UCAST; 182a7278b36SDana Rubin return false; 183eb700029SDmitry Fleytman } else { 184eb700029SDmitry Fleytman l2_hdr->iov_len = ETH_MAX_L2_HDR_LEN; 185eb700029SDmitry Fleytman l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); 186eb700029SDmitry Fleytman pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base); 187e263cd49SDmitry Fleytman } 188e263cd49SDmitry Fleytman 189eb700029SDmitry Fleytman l3_proto = eth_get_l3_proto(l2_hdr, 1, l2_hdr->iov_len); 190e263cd49SDmitry Fleytman 191e263cd49SDmitry Fleytman switch (l3_proto) { 192e263cd49SDmitry Fleytman case ETH_P_IP: 193e263cd49SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 194e263cd49SDmitry Fleytman l3_hdr->iov_base, sizeof(struct ip_header)); 195e263cd49SDmitry Fleytman 196e263cd49SDmitry Fleytman if (bytes_read < sizeof(struct ip_header)) { 197e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 198e263cd49SDmitry Fleytman return false; 199e263cd49SDmitry Fleytman } 200e263cd49SDmitry Fleytman 201e263cd49SDmitry Fleytman l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base); 202eb700029SDmitry Fleytman 203eb700029SDmitry Fleytman if (l3_hdr->iov_len < sizeof(struct ip_header)) { 204eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 205eb700029SDmitry Fleytman return false; 206eb700029SDmitry Fleytman } 207eb700029SDmitry Fleytman 2084f51e1d3SMarc-André Lureau pkt->l4proto = IP_HDR_GET_P(l3_hdr->iov_base); 209e263cd49SDmitry Fleytman 210eb700029SDmitry Fleytman if (IP_HDR_GET_LEN(l3_hdr->iov_base) != sizeof(struct ip_header)) { 211eb700029SDmitry Fleytman /* copy optional IPv4 header data if any*/ 212e263cd49SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 213e263cd49SDmitry Fleytman l2_hdr->iov_len + sizeof(struct ip_header), 214e263cd49SDmitry Fleytman l3_hdr->iov_base + sizeof(struct ip_header), 215e263cd49SDmitry Fleytman l3_hdr->iov_len - sizeof(struct ip_header)); 216e263cd49SDmitry Fleytman if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) { 217e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 218e263cd49SDmitry Fleytman return false; 219e263cd49SDmitry Fleytman } 220eb700029SDmitry Fleytman } 221eb700029SDmitry Fleytman 222e263cd49SDmitry Fleytman break; 223e263cd49SDmitry Fleytman 224e263cd49SDmitry Fleytman case ETH_P_IPV6: 225eb700029SDmitry Fleytman { 226eb700029SDmitry Fleytman eth_ip6_hdr_info hdrinfo; 227eb700029SDmitry Fleytman 228e263cd49SDmitry Fleytman if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 229eb700029SDmitry Fleytman &hdrinfo)) { 230e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 231e263cd49SDmitry Fleytman return false; 232e263cd49SDmitry Fleytman } 233e263cd49SDmitry Fleytman 234eb700029SDmitry Fleytman pkt->l4proto = hdrinfo.l4proto; 235eb700029SDmitry Fleytman full_ip6hdr_len = hdrinfo.full_hdr_len; 236eb700029SDmitry Fleytman 237eb700029SDmitry Fleytman if (full_ip6hdr_len > ETH_MAX_IP_DGRAM_LEN) { 238eb700029SDmitry Fleytman l3_hdr->iov_len = 0; 239eb700029SDmitry Fleytman return false; 240eb700029SDmitry Fleytman } 241e263cd49SDmitry Fleytman 242e263cd49SDmitry Fleytman bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, 243e263cd49SDmitry Fleytman l3_hdr->iov_base, full_ip6hdr_len); 244e263cd49SDmitry Fleytman 245e263cd49SDmitry Fleytman if (bytes_read < full_ip6hdr_len) { 246e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 247e263cd49SDmitry Fleytman return false; 248e263cd49SDmitry Fleytman } else { 249e263cd49SDmitry Fleytman l3_hdr->iov_len = full_ip6hdr_len; 250e263cd49SDmitry Fleytman } 251e263cd49SDmitry Fleytman break; 252eb700029SDmitry Fleytman } 253e263cd49SDmitry Fleytman default: 254e263cd49SDmitry Fleytman l3_hdr->iov_len = 0; 255e263cd49SDmitry Fleytman break; 256e263cd49SDmitry Fleytman } 257e263cd49SDmitry Fleytman 258605d52e6SDmitry Fleytman net_tx_pkt_calculate_hdr_len(pkt); 259e263cd49SDmitry Fleytman return true; 260e263cd49SDmitry Fleytman } 261e263cd49SDmitry Fleytman 262eb700029SDmitry Fleytman static void net_tx_pkt_rebuild_payload(struct NetTxPkt *pkt) 263e263cd49SDmitry Fleytman { 264eb700029SDmitry Fleytman pkt->payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len; 265605d52e6SDmitry Fleytman pkt->payload_frags = iov_copy(&pkt->vec[NET_TX_PKT_PL_START_FRAG], 266e263cd49SDmitry Fleytman pkt->max_payload_frags, 267e263cd49SDmitry Fleytman pkt->raw, pkt->raw_frags, 268eb700029SDmitry Fleytman pkt->hdr_len, pkt->payload_len); 269e263cd49SDmitry Fleytman } 270e263cd49SDmitry Fleytman 271605d52e6SDmitry Fleytman bool net_tx_pkt_parse(struct NetTxPkt *pkt) 272e263cd49SDmitry Fleytman { 273eb700029SDmitry Fleytman if (net_tx_pkt_parse_headers(pkt)) { 274605d52e6SDmitry Fleytman net_tx_pkt_rebuild_payload(pkt); 275eb700029SDmitry Fleytman return true; 276eb700029SDmitry Fleytman } else { 277eb700029SDmitry Fleytman return false; 278eb700029SDmitry Fleytman } 279e263cd49SDmitry Fleytman } 280e263cd49SDmitry Fleytman 281605d52e6SDmitry Fleytman struct virtio_net_hdr *net_tx_pkt_get_vhdr(struct NetTxPkt *pkt) 282e263cd49SDmitry Fleytman { 283e263cd49SDmitry Fleytman assert(pkt); 284e263cd49SDmitry Fleytman return &pkt->virt_hdr; 285e263cd49SDmitry Fleytman } 286e263cd49SDmitry Fleytman 287605d52e6SDmitry Fleytman static uint8_t net_tx_pkt_get_gso_type(struct NetTxPkt *pkt, 288e263cd49SDmitry Fleytman bool tso_enable) 289e263cd49SDmitry Fleytman { 290e263cd49SDmitry Fleytman uint8_t rc = VIRTIO_NET_HDR_GSO_NONE; 291e263cd49SDmitry Fleytman uint16_t l3_proto; 292e263cd49SDmitry Fleytman 293eb700029SDmitry Fleytman l3_proto = eth_get_l3_proto(&pkt->vec[NET_TX_PKT_L2HDR_FRAG], 1, 294605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len); 295e263cd49SDmitry Fleytman 296e263cd49SDmitry Fleytman if (!tso_enable) { 297e263cd49SDmitry Fleytman goto func_exit; 298e263cd49SDmitry Fleytman } 299e263cd49SDmitry Fleytman 300605d52e6SDmitry Fleytman rc = eth_get_gso_type(l3_proto, pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 301e263cd49SDmitry Fleytman pkt->l4proto); 302e263cd49SDmitry Fleytman 303e263cd49SDmitry Fleytman func_exit: 304e263cd49SDmitry Fleytman return rc; 305e263cd49SDmitry Fleytman } 306e263cd49SDmitry Fleytman 307*f9a9eb16SAkihiko Odaki bool net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, 308e263cd49SDmitry Fleytman bool csum_enable, uint32_t gso_size) 309e263cd49SDmitry Fleytman { 310e263cd49SDmitry Fleytman struct tcp_hdr l4hdr; 311*f9a9eb16SAkihiko Odaki size_t bytes_read; 312e263cd49SDmitry Fleytman assert(pkt); 313e263cd49SDmitry Fleytman 314e263cd49SDmitry Fleytman /* csum has to be enabled if tso is. */ 315e263cd49SDmitry Fleytman assert(csum_enable || !tso_enable); 316e263cd49SDmitry Fleytman 317605d52e6SDmitry Fleytman pkt->virt_hdr.gso_type = net_tx_pkt_get_gso_type(pkt, tso_enable); 318e263cd49SDmitry Fleytman 319e263cd49SDmitry Fleytman switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 320e263cd49SDmitry Fleytman case VIRTIO_NET_HDR_GSO_NONE: 321e263cd49SDmitry Fleytman pkt->virt_hdr.hdr_len = 0; 322e263cd49SDmitry Fleytman pkt->virt_hdr.gso_size = 0; 323e263cd49SDmitry Fleytman break; 324e263cd49SDmitry Fleytman 325e263cd49SDmitry Fleytman case VIRTIO_NET_HDR_GSO_UDP: 326eb700029SDmitry Fleytman pkt->virt_hdr.gso_size = gso_size; 327e263cd49SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header); 328e263cd49SDmitry Fleytman break; 329e263cd49SDmitry Fleytman 330e263cd49SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV4: 331e263cd49SDmitry Fleytman case VIRTIO_NET_HDR_GSO_TCPV6: 332*f9a9eb16SAkihiko Odaki bytes_read = iov_to_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], 333*f9a9eb16SAkihiko Odaki pkt->payload_frags, 0, &l4hdr, sizeof(l4hdr)); 334*f9a9eb16SAkihiko Odaki if (bytes_read < sizeof(l4hdr)) { 335*f9a9eb16SAkihiko Odaki return false; 336*f9a9eb16SAkihiko Odaki } 337*f9a9eb16SAkihiko Odaki 338e263cd49SDmitry Fleytman pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t); 339eb700029SDmitry Fleytman pkt->virt_hdr.gso_size = gso_size; 340e263cd49SDmitry Fleytman break; 341e263cd49SDmitry Fleytman 342e263cd49SDmitry Fleytman default: 343dfc6f865SStefan Weil g_assert_not_reached(); 344e263cd49SDmitry Fleytman } 345e263cd49SDmitry Fleytman 346e263cd49SDmitry Fleytman if (csum_enable) { 347e263cd49SDmitry Fleytman switch (pkt->l4proto) { 348e263cd49SDmitry Fleytman case IP_PROTO_TCP: 349e263cd49SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 350e263cd49SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 351e263cd49SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum); 352e263cd49SDmitry Fleytman break; 353e263cd49SDmitry Fleytman case IP_PROTO_UDP: 354e263cd49SDmitry Fleytman pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 355e263cd49SDmitry Fleytman pkt->virt_hdr.csum_start = pkt->hdr_len; 356e263cd49SDmitry Fleytman pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum); 357e263cd49SDmitry Fleytman break; 358e263cd49SDmitry Fleytman default: 359e263cd49SDmitry Fleytman break; 360e263cd49SDmitry Fleytman } 361e263cd49SDmitry Fleytman } 362*f9a9eb16SAkihiko Odaki 363*f9a9eb16SAkihiko Odaki return true; 364e263cd49SDmitry Fleytman } 365e263cd49SDmitry Fleytman 366eb700029SDmitry Fleytman void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt, 367eb700029SDmitry Fleytman uint16_t vlan, uint16_t vlan_ethtype) 368e263cd49SDmitry Fleytman { 369e263cd49SDmitry Fleytman bool is_new; 370e263cd49SDmitry Fleytman assert(pkt); 371e263cd49SDmitry Fleytman 372eb700029SDmitry Fleytman eth_setup_vlan_headers_ex(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base, 373eb700029SDmitry Fleytman vlan, vlan_ethtype, &is_new); 374e263cd49SDmitry Fleytman 375e263cd49SDmitry Fleytman /* update l2hdrlen */ 376e263cd49SDmitry Fleytman if (is_new) { 377e263cd49SDmitry Fleytman pkt->hdr_len += sizeof(struct vlan_header); 378605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len += 379e263cd49SDmitry Fleytman sizeof(struct vlan_header); 380e263cd49SDmitry Fleytman } 381e263cd49SDmitry Fleytman } 382e263cd49SDmitry Fleytman 383605d52e6SDmitry Fleytman bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa, 384e263cd49SDmitry Fleytman size_t len) 385e263cd49SDmitry Fleytman { 386e263cd49SDmitry Fleytman hwaddr mapped_len = 0; 387e263cd49SDmitry Fleytman struct iovec *ventry; 388e263cd49SDmitry Fleytman assert(pkt); 389035e69b0SMauro Matteo Cascella 390035e69b0SMauro Matteo Cascella if (pkt->raw_frags >= pkt->max_raw_frags) { 391035e69b0SMauro Matteo Cascella return false; 392035e69b0SMauro Matteo Cascella } 393e263cd49SDmitry Fleytman 394e263cd49SDmitry Fleytman if (!len) { 395e263cd49SDmitry Fleytman return true; 396e263cd49SDmitry Fleytman } 397e263cd49SDmitry Fleytman 398e263cd49SDmitry Fleytman ventry = &pkt->raw[pkt->raw_frags]; 399e263cd49SDmitry Fleytman mapped_len = len; 400e263cd49SDmitry Fleytman 40111171010SDmitry Fleytman ventry->iov_base = pci_dma_map(pkt->pci_dev, pa, 40211171010SDmitry Fleytman &mapped_len, DMA_DIRECTION_TO_DEVICE); 403e263cd49SDmitry Fleytman 404eb700029SDmitry Fleytman if ((ventry->iov_base != NULL) && (len == mapped_len)) { 405eb700029SDmitry Fleytman ventry->iov_len = mapped_len; 406eb700029SDmitry Fleytman pkt->raw_frags++; 407eb700029SDmitry Fleytman return true; 408eb700029SDmitry Fleytman } else { 409e263cd49SDmitry Fleytman return false; 410e263cd49SDmitry Fleytman } 411eb700029SDmitry Fleytman } 412e263cd49SDmitry Fleytman 413eb700029SDmitry Fleytman bool net_tx_pkt_has_fragments(struct NetTxPkt *pkt) 414eb700029SDmitry Fleytman { 415eb700029SDmitry Fleytman return pkt->raw_frags > 0; 416e263cd49SDmitry Fleytman } 417e263cd49SDmitry Fleytman 418605d52e6SDmitry Fleytman eth_pkt_types_e net_tx_pkt_get_packet_type(struct NetTxPkt *pkt) 419e263cd49SDmitry Fleytman { 420e263cd49SDmitry Fleytman assert(pkt); 421e263cd49SDmitry Fleytman 422e263cd49SDmitry Fleytman return pkt->packet_type; 423e263cd49SDmitry Fleytman } 424e263cd49SDmitry Fleytman 425605d52e6SDmitry Fleytman size_t net_tx_pkt_get_total_len(struct NetTxPkt *pkt) 426e263cd49SDmitry Fleytman { 427e263cd49SDmitry Fleytman assert(pkt); 428e263cd49SDmitry Fleytman 429e263cd49SDmitry Fleytman return pkt->hdr_len + pkt->payload_len; 430e263cd49SDmitry Fleytman } 431e263cd49SDmitry Fleytman 432605d52e6SDmitry Fleytman void net_tx_pkt_dump(struct NetTxPkt *pkt) 433e263cd49SDmitry Fleytman { 434605d52e6SDmitry Fleytman #ifdef NET_TX_PKT_DEBUG 435e263cd49SDmitry Fleytman assert(pkt); 436e263cd49SDmitry Fleytman 437e263cd49SDmitry Fleytman printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, " 438e263cd49SDmitry Fleytman "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type, 439605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len, 440605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len); 441e263cd49SDmitry Fleytman #endif 442e263cd49SDmitry Fleytman } 443e263cd49SDmitry Fleytman 444605d52e6SDmitry Fleytman void net_tx_pkt_reset(struct NetTxPkt *pkt) 445e263cd49SDmitry Fleytman { 446e263cd49SDmitry Fleytman int i; 447e263cd49SDmitry Fleytman 448e263cd49SDmitry Fleytman /* no assert, as reset can be called before tx_pkt_init */ 449e263cd49SDmitry Fleytman if (!pkt) { 450e263cd49SDmitry Fleytman return; 451e263cd49SDmitry Fleytman } 452e263cd49SDmitry Fleytman 453e263cd49SDmitry Fleytman memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr)); 454e263cd49SDmitry Fleytman 455e263cd49SDmitry Fleytman assert(pkt->vec); 456eb700029SDmitry Fleytman 457e263cd49SDmitry Fleytman pkt->payload_len = 0; 458e263cd49SDmitry Fleytman pkt->payload_frags = 0; 459e263cd49SDmitry Fleytman 460283f0a05SThomas Huth if (pkt->max_raw_frags > 0) { 461e263cd49SDmitry Fleytman assert(pkt->raw); 462e263cd49SDmitry Fleytman for (i = 0; i < pkt->raw_frags; i++) { 463e263cd49SDmitry Fleytman assert(pkt->raw[i].iov_base); 464283f0a05SThomas Huth pci_dma_unmap(pkt->pci_dev, pkt->raw[i].iov_base, 465283f0a05SThomas Huth pkt->raw[i].iov_len, DMA_DIRECTION_TO_DEVICE, 0); 466283f0a05SThomas Huth } 467e263cd49SDmitry Fleytman } 468e263cd49SDmitry Fleytman pkt->raw_frags = 0; 469e263cd49SDmitry Fleytman 470e263cd49SDmitry Fleytman pkt->hdr_len = 0; 471e263cd49SDmitry Fleytman pkt->l4proto = 0; 472e263cd49SDmitry Fleytman } 473e263cd49SDmitry Fleytman 474605d52e6SDmitry Fleytman static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt) 475e263cd49SDmitry Fleytman { 476605d52e6SDmitry Fleytman struct iovec *iov = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 477e263cd49SDmitry Fleytman uint32_t csum_cntr; 478e263cd49SDmitry Fleytman uint16_t csum = 0; 479eb700029SDmitry Fleytman uint32_t cso; 480e263cd49SDmitry Fleytman /* num of iovec without vhdr */ 481605d52e6SDmitry Fleytman uint32_t iov_len = pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1; 482e263cd49SDmitry Fleytman uint16_t csl; 483e263cd49SDmitry Fleytman size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset; 4849a8d9492SAndrew uint16_t l3_proto = eth_get_l3_proto(iov, 1, iov->iov_len); 485e263cd49SDmitry Fleytman 486e263cd49SDmitry Fleytman /* Put zero to checksum field */ 487e263cd49SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 488e263cd49SDmitry Fleytman 489e263cd49SDmitry Fleytman /* Calculate L4 TCP/UDP checksum */ 490e263cd49SDmitry Fleytman csl = pkt->payload_len; 491e263cd49SDmitry Fleytman 4929a8d9492SAndrew csum_cntr = 0; 4939a8d9492SAndrew cso = 0; 494e263cd49SDmitry Fleytman /* add pseudo header to csum */ 4959a8d9492SAndrew if (l3_proto == ETH_P_IP) { 4969a8d9492SAndrew csum_cntr = eth_calc_ip4_pseudo_hdr_csum( 4979a8d9492SAndrew pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 4989a8d9492SAndrew csl, &cso); 4999a8d9492SAndrew } else if (l3_proto == ETH_P_IPV6) { 5009a8d9492SAndrew csum_cntr = eth_calc_ip6_pseudo_hdr_csum( 5019a8d9492SAndrew pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, 5029a8d9492SAndrew csl, pkt->l4proto, &cso); 5039a8d9492SAndrew } 504eb700029SDmitry Fleytman 505eb700029SDmitry Fleytman /* data checksum */ 506eb700029SDmitry Fleytman csum_cntr += 507eb700029SDmitry Fleytman net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl, cso); 508e263cd49SDmitry Fleytman 509e263cd49SDmitry Fleytman /* Put the checksum obtained into the packet */ 5100dacea92SEd Swierk csum = cpu_to_be16(net_checksum_finish_nozero(csum_cntr)); 511e263cd49SDmitry Fleytman iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); 512e263cd49SDmitry Fleytman } 513e263cd49SDmitry Fleytman 514e263cd49SDmitry Fleytman enum { 515605d52e6SDmitry Fleytman NET_TX_PKT_FRAGMENT_L2_HDR_POS = 0, 516605d52e6SDmitry Fleytman NET_TX_PKT_FRAGMENT_L3_HDR_POS, 517605d52e6SDmitry Fleytman NET_TX_PKT_FRAGMENT_HEADER_NUM 518e263cd49SDmitry Fleytman }; 519e263cd49SDmitry Fleytman 520605d52e6SDmitry Fleytman #define NET_MAX_FRAG_SG_LIST (64) 521e263cd49SDmitry Fleytman 522605d52e6SDmitry Fleytman static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt *pkt, 523e263cd49SDmitry Fleytman int *src_idx, size_t *src_offset, struct iovec *dst, int *dst_idx) 524e263cd49SDmitry Fleytman { 525e263cd49SDmitry Fleytman size_t fetched = 0; 526e263cd49SDmitry Fleytman struct iovec *src = pkt->vec; 527e263cd49SDmitry Fleytman 528605d52e6SDmitry Fleytman *dst_idx = NET_TX_PKT_FRAGMENT_HEADER_NUM; 529e263cd49SDmitry Fleytman 530eb700029SDmitry Fleytman while (fetched < IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size)) { 531e263cd49SDmitry Fleytman 532e263cd49SDmitry Fleytman /* no more place in fragment iov */ 533605d52e6SDmitry Fleytman if (*dst_idx == NET_MAX_FRAG_SG_LIST) { 534e263cd49SDmitry Fleytman break; 535e263cd49SDmitry Fleytman } 536e263cd49SDmitry Fleytman 537e263cd49SDmitry Fleytman /* no more data in iovec */ 538605d52e6SDmitry Fleytman if (*src_idx == (pkt->payload_frags + NET_TX_PKT_PL_START_FRAG)) { 539e263cd49SDmitry Fleytman break; 540e263cd49SDmitry Fleytman } 541e263cd49SDmitry Fleytman 542e263cd49SDmitry Fleytman 543e263cd49SDmitry Fleytman dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset; 544e263cd49SDmitry Fleytman dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset, 545eb700029SDmitry Fleytman IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size) - fetched); 546e263cd49SDmitry Fleytman 547e263cd49SDmitry Fleytman *src_offset += dst[*dst_idx].iov_len; 548e263cd49SDmitry Fleytman fetched += dst[*dst_idx].iov_len; 549e263cd49SDmitry Fleytman 550e263cd49SDmitry Fleytman if (*src_offset == src[*src_idx].iov_len) { 551e263cd49SDmitry Fleytman *src_offset = 0; 552e263cd49SDmitry Fleytman (*src_idx)++; 553e263cd49SDmitry Fleytman } 554e263cd49SDmitry Fleytman 555e263cd49SDmitry Fleytman (*dst_idx)++; 556e263cd49SDmitry Fleytman } 557e263cd49SDmitry Fleytman 558e263cd49SDmitry Fleytman return fetched; 559e263cd49SDmitry Fleytman } 560e263cd49SDmitry Fleytman 561eb700029SDmitry Fleytman static inline void net_tx_pkt_sendv(struct NetTxPkt *pkt, 562eb700029SDmitry Fleytman NetClientState *nc, const struct iovec *iov, int iov_cnt) 563eb700029SDmitry Fleytman { 564eb700029SDmitry Fleytman if (pkt->is_loopback) { 5658c552542SJason Wang qemu_receive_packet_iov(nc, iov, iov_cnt); 566eb700029SDmitry Fleytman } else { 567eb700029SDmitry Fleytman qemu_sendv_packet(nc, iov, iov_cnt); 568eb700029SDmitry Fleytman } 569eb700029SDmitry Fleytman } 570eb700029SDmitry Fleytman 571605d52e6SDmitry Fleytman static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, 572e263cd49SDmitry Fleytman NetClientState *nc) 573e263cd49SDmitry Fleytman { 574605d52e6SDmitry Fleytman struct iovec fragment[NET_MAX_FRAG_SG_LIST]; 575e263cd49SDmitry Fleytman size_t fragment_len = 0; 576e263cd49SDmitry Fleytman bool more_frags = false; 577e263cd49SDmitry Fleytman 578e263cd49SDmitry Fleytman /* some pointers for shorter code */ 579e263cd49SDmitry Fleytman void *l2_iov_base, *l3_iov_base; 580e263cd49SDmitry Fleytman size_t l2_iov_len, l3_iov_len; 581605d52e6SDmitry Fleytman int src_idx = NET_TX_PKT_PL_START_FRAG, dst_idx; 582e263cd49SDmitry Fleytman size_t src_offset = 0; 583e263cd49SDmitry Fleytman size_t fragment_offset = 0; 584e263cd49SDmitry Fleytman 585605d52e6SDmitry Fleytman l2_iov_base = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base; 586605d52e6SDmitry Fleytman l2_iov_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len; 587605d52e6SDmitry Fleytman l3_iov_base = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; 588605d52e6SDmitry Fleytman l3_iov_len = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; 589e263cd49SDmitry Fleytman 590e263cd49SDmitry Fleytman /* Copy headers */ 591605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base; 592605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len; 593605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base; 594605d52e6SDmitry Fleytman fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len; 595e263cd49SDmitry Fleytman 596e263cd49SDmitry Fleytman 597e263cd49SDmitry Fleytman /* Put as much data as possible and send */ 598e263cd49SDmitry Fleytman do { 599605d52e6SDmitry Fleytman fragment_len = net_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset, 600e263cd49SDmitry Fleytman fragment, &dst_idx); 601e263cd49SDmitry Fleytman 602e263cd49SDmitry Fleytman more_frags = (fragment_offset + fragment_len < pkt->payload_len); 603e263cd49SDmitry Fleytman 604e263cd49SDmitry Fleytman eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base, 605e263cd49SDmitry Fleytman l3_iov_len, fragment_len, fragment_offset, more_frags); 606e263cd49SDmitry Fleytman 607e263cd49SDmitry Fleytman eth_fix_ip4_checksum(l3_iov_base, l3_iov_len); 608e263cd49SDmitry Fleytman 609eb700029SDmitry Fleytman net_tx_pkt_sendv(pkt, nc, fragment, dst_idx); 610e263cd49SDmitry Fleytman 611e263cd49SDmitry Fleytman fragment_offset += fragment_len; 612e263cd49SDmitry Fleytman 613ead315e4SPrasad J Pandit } while (fragment_len && more_frags); 614e263cd49SDmitry Fleytman 615e263cd49SDmitry Fleytman return true; 616e263cd49SDmitry Fleytman } 617e263cd49SDmitry Fleytman 618605d52e6SDmitry Fleytman bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) 619e263cd49SDmitry Fleytman { 620e263cd49SDmitry Fleytman assert(pkt); 621e263cd49SDmitry Fleytman 622e263cd49SDmitry Fleytman if (!pkt->has_virt_hdr && 623e263cd49SDmitry Fleytman pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 624605d52e6SDmitry Fleytman net_tx_pkt_do_sw_csum(pkt); 625e263cd49SDmitry Fleytman } 626e263cd49SDmitry Fleytman 627e263cd49SDmitry Fleytman /* 628e263cd49SDmitry Fleytman * Since underlying infrastructure does not support IP datagrams longer 629e263cd49SDmitry Fleytman * than 64K we should drop such packets and don't even try to send 630e263cd49SDmitry Fleytman */ 631e263cd49SDmitry Fleytman if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) { 632e263cd49SDmitry Fleytman if (pkt->payload_len > 633e263cd49SDmitry Fleytman ETH_MAX_IP_DGRAM_LEN - 634605d52e6SDmitry Fleytman pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len) { 635e263cd49SDmitry Fleytman return false; 636e263cd49SDmitry Fleytman } 637e263cd49SDmitry Fleytman } 638e263cd49SDmitry Fleytman 639e263cd49SDmitry Fleytman if (pkt->has_virt_hdr || 640e263cd49SDmitry Fleytman pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) { 641e219d309SAndrew net_tx_pkt_fix_ip6_payload_len(pkt); 642eb700029SDmitry Fleytman net_tx_pkt_sendv(pkt, nc, pkt->vec, 643605d52e6SDmitry Fleytman pkt->payload_frags + NET_TX_PKT_PL_START_FRAG); 644e263cd49SDmitry Fleytman return true; 645e263cd49SDmitry Fleytman } 646e263cd49SDmitry Fleytman 647605d52e6SDmitry Fleytman return net_tx_pkt_do_sw_fragmentation(pkt, nc); 648e263cd49SDmitry Fleytman } 649eb700029SDmitry Fleytman 650eb700029SDmitry Fleytman bool net_tx_pkt_send_loopback(struct NetTxPkt *pkt, NetClientState *nc) 651eb700029SDmitry Fleytman { 652eb700029SDmitry Fleytman bool res; 653eb700029SDmitry Fleytman 654eb700029SDmitry Fleytman pkt->is_loopback = true; 655eb700029SDmitry Fleytman res = net_tx_pkt_send(pkt, nc); 656eb700029SDmitry Fleytman pkt->is_loopback = false; 657eb700029SDmitry Fleytman 658eb700029SDmitry Fleytman return res; 659eb700029SDmitry Fleytman } 660e219d309SAndrew 661e219d309SAndrew void net_tx_pkt_fix_ip6_payload_len(struct NetTxPkt *pkt) 662e219d309SAndrew { 663e219d309SAndrew struct iovec *l2 = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; 664e219d309SAndrew if (eth_get_l3_proto(l2, 1, l2->iov_len) == ETH_P_IPV6) { 665e219d309SAndrew struct ip6_header *ip6 = (struct ip6_header *) pkt->l3_hdr; 666e219d309SAndrew /* 667e219d309SAndrew * TODO: if qemu would support >64K packets - add jumbo option check 668e219d309SAndrew * something like that: 669e219d309SAndrew * 'if (ip6->ip6_plen == 0 && !has_jumbo_option(ip6)) {' 670e219d309SAndrew */ 671e219d309SAndrew if (ip6->ip6_plen == 0) { 672e219d309SAndrew if (pkt->payload_len <= ETH_MAX_IP_DGRAM_LEN) { 673e219d309SAndrew ip6->ip6_plen = htons(pkt->payload_len); 674e219d309SAndrew } 675e219d309SAndrew /* 676e219d309SAndrew * TODO: if qemu would support >64K packets 677e219d309SAndrew * add jumbo option for packets greater then 65,535 bytes 678e219d309SAndrew */ 679e219d309SAndrew } 680e219d309SAndrew } 681e219d309SAndrew } 682