Message ID | 1454264009-24094-10-git-send-email-wexu@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 02/01/2016 02:13 AM, wexu@redhat.com wrote: > From: Wei Xu <wei@wei-thinkpad.nay.redhat.com> > > A few more stuffs should be included to support this > 1. Corresponding chain lookup > 2. Coalescing callback for the protocol chain > 3. Filter & Sanity Check. > > Signed-off-by: Wei Xu <wexu@redhat.com> > --- > hw/net/virtio-net.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 102 insertions(+), 2 deletions(-) > > diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c > index 9b44762..c9f6bfc 100644 > --- a/hw/net/virtio-net.c > +++ b/hw/net/virtio-net.c > @@ -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 > > /* IPv4 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; Like I've replied in previous mails, need a helper or just store pointers to both ip and tcp in seg. > + > + 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; > + } And if you still want to handle coalescing in a layer style, better delay the check of ports to tcp function. > + > + /* 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; RSC_WANT? > +} > + > +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)); Same as IPv4, looks like a layer violation. > + 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);
On 02/01/2016 03:14 PM, Jason Wang wrote: > > On 02/01/2016 02:13 AM, wexu@redhat.com wrote: >> From: Wei Xu <wei@wei-thinkpad.nay.redhat.com> >> >> A few more stuffs should be included to support this >> 1. Corresponding chain lookup >> 2. Coalescing callback for the protocol chain >> 3. Filter & Sanity Check. >> >> Signed-off-by: Wei Xu <wexu@redhat.com> >> --- >> hw/net/virtio-net.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++- >> 1 file changed, 102 insertions(+), 2 deletions(-) >> >> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c >> index 9b44762..c9f6bfc 100644 >> --- a/hw/net/virtio-net.c >> +++ b/hw/net/virtio-net.c >> @@ -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 >> >> /* IPv4 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; > Like I've replied in previous mails, need a helper or just store > pointers to both ip and tcp in seg. OK. > >> + >> + 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; >> + } > And if you still want to handle coalescing in a layer style, better > delay the check of ports to tcp function. OK. > >> + >> + /* 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; > RSC_WANT? Yes, the is new code and not tested. > >> +} >> + >> +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)); > Same as IPv4, looks like a layer violation. OK. > >> + 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); >
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 9b44762..c9f6bfc 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -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 /* IPv4 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);