@@ -46,12 +46,19 @@
#define TCP4_OFFSET (IP_OFFSET + sizeof(struct ip_header)) /* tcp4 header */
#define TCP4_PORT_OFFSET TCP4_OFFSET /* tcp4 port offset */
#define IP4_ADDR_SIZE 8 /* ipv4 saddr + daddr */
+
+#define IP6_ADDR_OFFSET (IP_OFFSET + 8) /* ipv6 address start */
+#define TCP6_OFFSET (IP_OFFSET + sizeof(struct ip6_header)) /* tcp6 header */
+#define TCP6_PORT_OFFSET TCP6_OFFSET /* tcp6 port offset */
+#define IP6_ADDR_SIZE 32 /* ipv6 saddr + daddr */
#define TCP_PORT_SIZE 4 /* sport + dport */
#define TCP_WINDOW 65535
/* ip4 max payload, 16 bits in the header */
#define MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header))
+/* ip6 max payload, payload in ipv6 don't include the header */
+#define MAX_IP6_PAYLOAD 65535
#define MAX_VIRTIO_IP_PAYLOAD (65535 + IP_OFFSET)
/* Purge coalesced packets timer interval */
@@ -1856,6 +1863,42 @@ static int32_t virtio_net_rsc_try_coalesce4(NetRscChain *chain,
o_data, &o_ip->ip_len, MAX_IP4_PAYLOAD);
}
+static int32_t virtio_net_rsc_try_coalesce6(NetRscChain *chain,
+ NetRscSeg *seg, const uint8_t *buf, size_t size)
+{
+ uint16_t o_ip_len, n_ip_len; /* len in ip header field */
+ uint16_t n_tcp_len, o_tcp_len; /* tcp header len */
+ uint16_t o_data, n_data; /* payload without virtio/eth/ip/tcp */
+ struct ip6_header *n_ip, *o_ip;
+ struct tcp_header *n_tcp, *o_tcp;
+
+ n_ip = (struct ip6_header *)(buf + IP_OFFSET);
+ n_ip_len = htons(n_ip->ip6_ctlun.ip6_un1.ip6_un1_plen);
+ n_tcp = (struct tcp_header *)(((uint8_t *)n_ip)\
+ + sizeof(struct ip6_header));
+ n_tcp_len = (htons(n_tcp->th_offset_flags) & 0xF000) >> 10;
+ n_data = n_ip_len - n_tcp_len;
+
+ o_ip = (struct ip6_header *)(seg->buf + IP_OFFSET);
+ o_ip_len = htons(o_ip->ip6_ctlun.ip6_un1.ip6_un1_plen);
+ o_tcp = (struct tcp_header *)(((uint8_t *)o_ip)\
+ + sizeof(struct ip6_header));
+ o_tcp_len = (htons(o_tcp->th_offset_flags) & 0xF000) >> 10;
+ o_data = o_ip_len - o_tcp_len;
+
+ if (memcmp(&n_ip->ip6_src, &o_ip->ip6_src, sizeof(struct in6_address))
+ || memcmp(&n_ip->ip6_dst, &o_ip->ip6_dst, sizeof(struct in6_address))
+ || (n_tcp->th_sport ^ o_tcp->th_sport)
+ || (n_tcp->th_dport ^ o_tcp->th_dport)) {
+ return RSC_NO_MATCH;
+ }
+
+ /* There is a difference between payload lenght in ipv4 and v6,
+ ip header is excluded in ipv6 */
+ return virtio_net_rsc_coalesce_tcp(chain, seg, buf,
+ n_tcp, n_tcp_len, n_data, o_tcp, o_tcp_len, o_data,
+ &o_ip->ip6_ctlun.ip6_un1.ip6_un1_plen, MAX_IP6_PAYLOAD);
+}
/* Pakcets with 'SYN' should bypass, other flag should be sent after drain
* to prevent out of order */
@@ -2015,6 +2058,59 @@ static size_t virtio_net_rsc_receive4(void *opq, NetClientState* nc,
virtio_net_rsc_try_coalesce4);
}
+static int32_t virtio_net_rsc_filter6(NetRscChain *chain, struct ip6_header *ip,
+ const uint8_t *buf, size_t size)
+{
+ uint16_t ip_len;
+
+ if (size < (TCP6_OFFSET + sizeof(tcp_header))) {
+ return RSC_BYPASS;
+ }
+
+ if (0x6 != (0xF & ip->ip6_ctlun.ip6_un1.ip6_un1_flow)) {
+ return RSC_BYPASS;
+ }
+
+ /* Both option and protocol is checked in this */
+ if (ip->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
+ return RSC_BYPASS;
+ }
+
+ /* Sanity check */
+ ip_len = htons(ip->ip6_ctlun.ip6_un1.ip6_un1_plen);
+ if (ip_len < sizeof(struct tcp_header)
+ || ip_len > (size - TCP6_OFFSET)) {
+ return RSC_BYPASS;
+ }
+
+ return 0;
+}
+
+static size_t virtio_net_rsc_receive6(void *opq, NetClientState* nc,
+ const uint8_t *buf, size_t size)
+{
+ int32_t ret;
+ NetRscChain *chain;
+ struct ip6_header *ip;
+
+ chain = (NetRscChain *)opq;
+ ip = (struct ip6_header *)(buf + IP_OFFSET);
+ if (RSC_WANT != virtio_net_rsc_filter6(chain, ip, buf, size)) {
+ return virtio_net_do_receive(nc, buf, size);
+ }
+
+ ret = virtio_net_rsc_parse_tcp_ctrl((uint8_t *)ip, sizeof(*ip));
+ if (RSC_BYPASS == ret) {
+ return virtio_net_do_receive(nc, buf, size);
+ } else if (RSC_FINAL == ret) {
+ return virtio_net_rsc_drain_one(chain, nc, buf, size, IP6_ADDR_OFFSET,
+ IP6_ADDR_SIZE, TCP6_PORT_OFFSET, TCP_PORT_SIZE);
+ }
+
+ return virtio_net_rsc_callback(chain, nc, buf, size,
+ virtio_net_rsc_try_coalesce6);
+}
+
static NetRscChain *virtio_net_rsc_lookup_chain(NetClientState *nc,
uint16_t proto)
{
@@ -2023,7 +2119,7 @@ static NetRscChain *virtio_net_rsc_lookup_chain(NetClientState *nc,
NICState *nic;
/* Only handle IPv4/6 */
- if (proto != (uint16_t)ETH_P_IP) {
+ if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) {
return NULL;
}
@@ -2044,7 +2140,11 @@ static NetRscChain *virtio_net_rsc_lookup_chain(NetClientState *nc,
chain->proto = proto;
chain->drain_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
virtio_net_rsc_purge, chain);
- chain->do_receive = virtio_net_rsc_receive4;
+ if (ETH_P_IP == proto) {
+ chain->do_receive = virtio_net_rsc_receive4;
+ } else {
+ chain->do_receive = virtio_net_rsc_receive6;
+ }
QTAILQ_INIT(&chain->buffers);
QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next);
1. Callback for IPv6 2. Filter & Sanity Check. Signed-off-by: Wei Xu <wexu@redhat.com> --- hw/net/virtio-net.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-)