@@ -25,6 +25,10 @@
#include <linux/if_tun.h>
#include <linux/if_macvlan.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/netdevice.h>
+
#include <net/sock.h>
#include "vhost.h"
@@ -191,6 +195,42 @@ static void handle_tx(struct vhost_net *net)
unuse_mm(net->dev.mm);
}
+static int peek_head(struct sock *sk)
+{
+ struct sk_buff *head;
+ int ret;
+
+ lock_sock(sk);
+ head = skb_peek(&sk->sk_receive_queue);
+ if (likely(head)) {
+ ret = 1;
+ /* Userspace virtio server has the following hack so
+ * guests rely on it, and we have to replicate it, too: */
+ /* On linux guests, some apps that use recvmsg with AF_PACKET
+ * sockets, don't know how to handle CHECKSUM_PARTIAL;
+ * The interface to return the relevant information was added in
+ * 8dc4194474159660d7f37c495e3fc3f10d0db8cc,
+ * and older userspace does not use it.
+ * One important user of recvmsg with AF_PACKET is dhclient,
+ * so we add a work-around just for DHCP. */
+ /* We use source port to detect DHCP packets. */
+ if (skb->ip_summed == CHECKSUM_PARTIAL &&
+ skb->protocol == htons(ETH_P_IP) &&
+ skb_network_header_len(skb) >= sizeof(struct iphdr) &&
+ ip_hdr(skb)->protocol == IPPRODO_UDP &&
+ skb_headlen(skb) >= skb_transport_offset(skb) + sizeof(struct udphdr) &&
+ udp_hdr(skb)->source == htons(0x67)) {
+ skb_checksum_help(skb);
+ /* Restore ip_summed value: tun passes it to user. */
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ }
+ } else {
+ ret = 0;
+ }
+ release_sock(sk);
+ return len;
+}
+
/* Expects to be always run from workqueue - which acts as
* read-size critical section for our kind of RCU. */
static void handle_rx(struct vhost_net *net)
@@ -228,7 +268,7 @@ static void handle_rx(struct vhost_net *net)
vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
vq->log : NULL;
- for (;;) {
+ while (peek_head(sock)) {
head = vhost_get_vq_desc(&net->dev, vq, vq->iov,
ARRAY_SIZE(vq->iov),
&out, &in,