summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--hw/arm/musicpal.c6
-rw-r--r--hw/net/etraxfs_eth.c6
-rw-r--r--hw/net/lan9118.c6
-rw-r--r--hw/net/vmxnet3.c59
-rw-r--r--hw/net/vmxnet_rx_pkt.c12
-rw-r--r--hw/net/vmxnet_rx_pkt.h11
-rw-r--r--net/net.c19
-rw-r--r--net/socket.c2
8 files changed, 92 insertions, 29 deletions
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index a3b1314d96..42f66b33e1 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -187,11 +187,6 @@ static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc)
     le32_to_cpus(&desc->next);
 }
 
-static int eth_can_receive(NetClientState *nc)
-{
-    return 1;
-}
-
 static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
 {
     mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
@@ -381,7 +376,6 @@ static void eth_cleanup(NetClientState *nc)
 static NetClientInfo net_mv88w8618_info = {
     .type = NET_CLIENT_OPTIONS_KIND_NIC,
     .size = sizeof(NICState),
-    .can_receive = eth_can_receive,
     .receive = eth_receive,
     .cleanup = eth_cleanup,
 };
diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c
index 4773dea927..d6002750f0 100644
--- a/hw/net/etraxfs_eth.c
+++ b/hw/net/etraxfs_eth.c
@@ -520,11 +520,6 @@ static int eth_match_groupaddr(ETRAXFSEthState *eth, const unsigned char *sa)
     return match;
 }
 
-static int eth_can_receive(NetClientState *nc)
-{
-    return 1;
-}
-
 static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
 {
     unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -584,7 +579,6 @@ static const MemoryRegionOps eth_ops = {
 static NetClientInfo net_etraxfs_info = {
     .type = NET_CLIENT_OPTIONS_KIND_NIC,
     .size = sizeof(NICState),
-    .can_receive = eth_can_receive,
     .receive = eth_receive,
     .link_status_changed = eth_set_link,
 };
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index f169c383df..4f0e840f0e 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -461,11 +461,6 @@ static void lan9118_reset(DeviceState *d)
     lan9118_reload_eeprom(s);
 }
 
-static int lan9118_can_receive(NetClientState *nc)
-{
-    return 1;
-}
-
 static void rx_fifo_push(lan9118_state *s, uint32_t val)
 {
     int fifo_pos;
@@ -1312,7 +1307,6 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = {
 static NetClientInfo net_lan9118_info = {
     .type = NET_CLIENT_OPTIONS_KIND_NIC,
     .size = sizeof(NICState),
-    .can_receive = lan9118_can_receive,
     .receive = lan9118_receive,
     .link_status_changed = lan9118_set_link,
 };
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 706e0606a9..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)
 {
@@ -1897,6 +1954,8 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size)
         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) {
diff --git a/hw/net/vmxnet_rx_pkt.c b/hw/net/vmxnet_rx_pkt.c
index acbca6a3db..aa5462931e 100644
--- a/hw/net/vmxnet_rx_pkt.c
+++ b/hw/net/vmxnet_rx_pkt.c
@@ -92,9 +92,6 @@ void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data,
     }
 
     pkt->tci = tci;
-
-    eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6,
-        &pkt->isudp, &pkt->istcp);
 }
 
 void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt)
@@ -131,6 +128,15 @@ size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt)
     return pkt->tot_len;
 }
 
+void vmxnet_rx_pkt_set_protocols(struct VmxnetRxPkt *pkt, const void *data,
+                                 size_t len)
+{
+    assert(pkt);
+
+    eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6,
+        &pkt->isudp, &pkt->istcp);
+}
+
 void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt,
                                  bool *isip4, bool *isip6,
                                  bool *isudp, bool *istcp)
diff --git a/hw/net/vmxnet_rx_pkt.h b/hw/net/vmxnet_rx_pkt.h
index 5f8352a468..a425846b52 100644
--- a/hw/net/vmxnet_rx_pkt.h
+++ b/hw/net/vmxnet_rx_pkt.h
@@ -55,6 +55,17 @@ void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr);
 size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt);
 
 /**
+ * parse and set packet analysis results
+ *
+ * @pkt:            packet
+ * @data:           pointer to the data buffer to be parsed
+ * @len:            data length
+ *
+ */
+void vmxnet_rx_pkt_set_protocols(struct VmxnetRxPkt *pkt, const void *data,
+                                 size_t len);
+
+/**
  * fetches packet analysis results
  *
  * @pkt:            packet
diff --git a/net/net.c b/net/net.c
index 6ff7fec1bb..28a5597b8d 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1257,14 +1257,19 @@ void qmp_set_link(const char *name, bool up, Error **errp)
 static void net_vm_change_state_handler(void *opaque, int running,
                                         RunState state)
 {
-    /* Complete all queued packets, to guarantee we don't modify
-     * state later when VM is not running.
-     */
-    if (!running) {
-        NetClientState *nc;
-        NetClientState *tmp;
+    NetClientState *nc;
+    NetClientState *tmp;
 
-        QTAILQ_FOREACH_SAFE(nc, &net_clients, next, tmp) {
+    QTAILQ_FOREACH_SAFE(nc, &net_clients, next, tmp) {
+        if (running) {
+            /* Flush queued packets and wake up backends. */
+            if (nc->peer && qemu_can_send_packet(nc)) {
+                qemu_flush_queued_packets(nc->peer);
+            }
+        } else {
+            /* Complete all queued packets, to guarantee we don't modify
+             * state later when VM is not running.
+             */
             qemu_flush_or_purge_queued_packets(nc, true);
         }
     }
diff --git a/net/socket.c b/net/socket.c
index c752696cbb..b1e3b1c8d9 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -213,7 +213,7 @@ static void net_socket_send(void *opaque)
             if (s->index >= s->packet_len) {
                 s->index = 0;
                 s->state = 0;
-                if (qemu_send_packet_async(&s->nc, s->buf, size,
+                if (qemu_send_packet_async(&s->nc, s->buf, s->packet_len,
                                            net_socket_send_completed) == 0) {
                     net_socket_read_poll(s, false);
                     break;