diff options
-rw-r--r-- | hw/arm/musicpal.c | 6 | ||||
-rw-r--r-- | hw/net/etraxfs_eth.c | 6 | ||||
-rw-r--r-- | hw/net/lan9118.c | 6 | ||||
-rw-r--r-- | hw/net/vmxnet3.c | 59 | ||||
-rw-r--r-- | hw/net/vmxnet_rx_pkt.c | 12 | ||||
-rw-r--r-- | hw/net/vmxnet_rx_pkt.h | 11 | ||||
-rw-r--r-- | net/net.c | 19 | ||||
-rw-r--r-- | net/socket.c | 2 |
8 files changed, 92 insertions, 29 deletions
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index a3b1314..42f66b3 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 4773dea..d600275 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 f169c38..4f0e840 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 706e060..59b06b8 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 acbca6a..aa54629 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 5f8352a..a425846 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 @@ -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 c752696..b1e3b1c 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; |