diff options
Diffstat (limited to 'hw/net/vmxnet3.c')
-rw-r--r-- | hw/net/vmxnet3.c | 105 |
1 files changed, 71 insertions, 34 deletions
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 8eea58989b..59b06b8412 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -885,6 +885,63 @@ vmxnet3_get_next_rx_descr(VMXNET3State *s, bool is_head, } } +/* In case packet was csum offloaded (either NEEDS_CSUM or DATA_VALID), + * the implementation always passes an RxCompDesc with a "Checksum + * calculated and found correct" to the OS (cnc=0 and tuc=1, see + * vmxnet3_rx_update_descr). This emulates the observed ESXi behavior. + * + * Therefore, if packet has the NEEDS_CSUM set, we must calculate + * and place a fully computed checksum into the tcp/udp header. + * Otherwise, the OS driver will receive a checksum-correct indication + * (CHECKSUM_UNNECESSARY), but with the actual tcp/udp checksum field + * having just the pseudo header csum value. + * + * While this is not a problem if packet is destined for local delivery, + * in the case the host OS performs forwarding, it will forward an + * incorrectly checksummed packet. + */ +static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt, + const void *pkt_data, + size_t pkt_len) +{ + struct virtio_net_hdr *vhdr; + bool isip4, isip6, istcp, isudp; + uint8_t *data; + int len; + + if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) { + return; + } + + vhdr = vmxnet_rx_pkt_get_vhdr(pkt); + if (!VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM)) { + return; + } + + vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); + if (!(isip4 || isip6) || !(istcp || isudp)) { + return; + } + + vmxnet3_dump_virt_hdr(vhdr); + + /* Validate packet len: csum_start + scum_offset + length of csum field */ + if (pkt_len < (vhdr->csum_start + vhdr->csum_offset + 2)) { + VMW_PKPRN("packet len:%d < csum_start(%d) + csum_offset(%d) + 2, " + "cannot calculate checksum", + len, vhdr->csum_start, vhdr->csum_offset); + return; + } + + data = (uint8_t *)pkt_data + vhdr->csum_start; + len = pkt_len - vhdr->csum_start; + /* Put the checksum obtained into the packet */ + stw_be_p(data + vhdr->csum_offset, net_raw_checksum(data, len)); + + vhdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; + vhdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID; +} + static void vmxnet3_rx_update_descr(struct VmxnetRxPkt *pkt, struct Vmxnet3_RxCompDesc *rxcd) { @@ -1879,6 +1936,12 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) return -1; } + if (s->peer_has_vhdr) { + vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf); + buf += sizeof(struct virtio_net_hdr); + size -= sizeof(struct virtio_net_hdr); + } + /* Pad to minimum Ethernet frame length */ if (size < sizeof(min_buf)) { memcpy(min_buf, buf, size); @@ -1887,16 +1950,12 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) size = sizeof(min_buf); } - if (s->peer_has_vhdr) { - vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf); - buf += sizeof(struct virtio_net_hdr); - size -= sizeof(struct virtio_net_hdr); - } - vmxnet_rx_pkt_set_packet_type(s->rx_pkt, get_eth_packet_type(PKT_GET_ETH_HDR(buf))); if (vmxnet3_rx_filter_may_indicate(s, buf, size)) { + vmxnet_rx_pkt_set_protocols(s->rx_pkt, buf, size); + vmxnet3_rx_need_csum_calculate(s->rx_pkt, buf, size); vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping); bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1; if (bytes_indicated < size) { @@ -1912,12 +1971,6 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) return bytes_indicated; } -static void vmxnet3_cleanup(NetClientState *nc) -{ - VMXNET3State *s = qemu_get_nic_opaque(nc); - s->nic = NULL; -} - static void vmxnet3_set_link_status(NetClientState *nc) { VMXNET3State *s = qemu_get_nic_opaque(nc); @@ -1937,7 +1990,6 @@ static NetClientInfo net_vmxnet3_info = { .size = sizeof(NICState), .can_receive = vmxnet3_can_receive, .receive = vmxnet3_receive, - .cleanup = vmxnet3_cleanup, .link_status_changed = vmxnet3_set_link_status, }; @@ -2132,7 +2184,7 @@ static const MemoryRegionOps b1_ops = { }, }; -static int vmxnet3_pci_init(PCIDevice *pci_dev) +static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) { DeviceState *dev = DEVICE(pci_dev); VMXNET3State *s = VMXNET3(pci_dev); @@ -2171,8 +2223,6 @@ static int vmxnet3_pci_init(PCIDevice *pci_dev) register_savevm(dev, "vmxnet3-msix", -1, 1, vmxnet3_msix_save, vmxnet3_msix_load, s); - - return 0; } static void vmxnet3_instance_init(Object *obj) @@ -2235,6 +2285,7 @@ static const VMStateDescription vmxstate_vmxnet3_mcast_list = { .version_id = 1, .minimum_version_id = 1, .pre_load = vmxnet3_mcast_list_pre_load, + .needed = vmxnet3_mc_list_needed, .fields = (VMStateField[]) { VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0, mcast_list_buff_size), @@ -2479,25 +2530,12 @@ static const VMStateDescription vmstate_vmxnet3 = { VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmxstate_vmxnet3_mcast_list, - .needed = vmxnet3_mc_list_needed - }, - { - /* empty element. */ - } + .subsections = (const VMStateDescription*[]) { + &vmxstate_vmxnet3_mcast_list, + NULL } }; -static void -vmxnet3_write_config(PCIDevice *pci_dev, uint32_t addr, uint32_t val, int len) -{ - pci_default_write_config(pci_dev, addr, val, len); - msix_write_config(pci_dev, addr, val, len); - msi_write_config(pci_dev, addr, val, len); -} - static Property vmxnet3_properties[] = { DEFINE_NIC_PROPERTIES(VMXNET3State, conf), DEFINE_PROP_END_OF_LIST(), @@ -2508,7 +2546,7 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); - c->init = vmxnet3_pci_init; + c->realize = vmxnet3_pci_realize; c->exit = vmxnet3_pci_uninit; c->vendor_id = PCI_VENDOR_ID_VMWARE; c->device_id = PCI_DEVICE_ID_VMWARE_VMXNET3; @@ -2516,7 +2554,6 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) c->class_id = PCI_CLASS_NETWORK_ETHERNET; c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; - c->config_write = vmxnet3_write_config, dc->desc = "VMWare Paravirtualized Ethernet v3"; dc->reset = vmxnet3_qdev_reset; dc->vmsd = &vmstate_vmxnet3; |