summaryrefslogtreecommitdiff
path: root/hw/net/vmxnet3.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/net/vmxnet3.c')
-rw-r--r--hw/net/vmxnet3.c105
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;