Unbreak IPv6.

No longer return ENXIO when trying to send an IPv6 packet in
nicvf_sq_add_hdr_subdesc().
Restructure the code so that the upper layer protocol parts are
agnostic of the L3 protocol (and no longer specific to IPv4).
With this basic IPv6 packets go through.  We are still seeing
weird behaviour which needs further diagnosis.

PR:			223669
In collaboration with:	emaste
MFC after:		3 days
This commit is contained in:
Bjoern A. Zeeb 2017-11-14 16:47:05 +00:00
parent d02819b5e6
commit c04716b1ad
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=325813

View File

@ -78,6 +78,8 @@ __FBSDID("$FreeBSD$");
#include <netinet/tcp_lro.h>
#include <netinet/udp.h>
#include <netinet6/ip6_var.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
@ -1752,7 +1754,7 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry,
struct tcphdr *th;
#endif
uint16_t etype;
int ehdrlen, iphlen, poff;
int ehdrlen, iphlen, poff, proto;
nic = sq->nic;
@ -1776,13 +1778,21 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry,
etype = ntohs(eh->evl_encap_proto);
}
poff = proto = -1;
switch (etype) {
#ifdef INET6
case ETHERTYPE_IPV6:
/* ARM64TODO: Add support for IPv6 */
hdr->csum_l3 = 0;
sq->snd_buff[qentry].mbuf = NULL;
return (ENXIO);
if (mbuf->m_len < ehdrlen + sizeof(struct ip6_hdr)) {
mbuf = m_pullup(mbuf, ehdrlen +sizeof(struct ip6_hdr));
sq->snd_buff[qentry].mbuf = NULL;
if (mbuf == NULL)
return (ENOBUFS);
}
poff = ip6_lasthdr(mbuf, ehdrlen, IPPROTO_IPV6, &proto);
if (poff < 0)
return (ENOBUFS);
poff += ehdrlen;
break;
#endif
#ifdef INET
case ETHERTYPE_IP:
@ -1796,72 +1806,71 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry,
ip = (struct ip *)(mbuf->m_data + ehdrlen);
iphlen = ip->ip_hl << 2;
poff = ehdrlen + iphlen;
if (mbuf->m_pkthdr.csum_flags != 0) {
hdr->csum_l3 = 1; /* Enable IP csum calculation */
switch (ip->ip_p) {
case IPPROTO_TCP:
if ((mbuf->m_pkthdr.csum_flags & CSUM_TCP) == 0)
break;
if (mbuf->m_len < (poff + sizeof(struct tcphdr))) {
mbuf = m_pullup(mbuf, poff + sizeof(struct tcphdr));
sq->snd_buff[qentry].mbuf = mbuf;
if (mbuf == NULL)
return (ENOBUFS);
}
hdr->csum_l4 = SEND_L4_CSUM_TCP;
break;
case IPPROTO_UDP:
if ((mbuf->m_pkthdr.csum_flags & CSUM_UDP) == 0)
break;
if (mbuf->m_len < (poff + sizeof(struct udphdr))) {
mbuf = m_pullup(mbuf, poff + sizeof(struct udphdr));
sq->snd_buff[qentry].mbuf = mbuf;
if (mbuf == NULL)
return (ENOBUFS);
}
hdr->csum_l4 = SEND_L4_CSUM_UDP;
break;
case IPPROTO_SCTP:
if ((mbuf->m_pkthdr.csum_flags & CSUM_SCTP) == 0)
break;
if (mbuf->m_len < (poff + sizeof(struct sctphdr))) {
mbuf = m_pullup(mbuf, poff + sizeof(struct sctphdr));
sq->snd_buff[qentry].mbuf = mbuf;
if (mbuf == NULL)
return (ENOBUFS);
}
hdr->csum_l4 = SEND_L4_CSUM_SCTP;
break;
default:
break;
}
hdr->l3_offset = ehdrlen;
hdr->l4_offset = ehdrlen + iphlen;
}
if ((mbuf->m_pkthdr.tso_segsz != 0) && nic->hw_tso) {
/*
* Extract ip again as m_data could have been modified.
*/
ip = (struct ip *)(mbuf->m_data + ehdrlen);
th = (struct tcphdr *)((caddr_t)ip + iphlen);
hdr->tso = 1;
hdr->tso_start = ehdrlen + iphlen + (th->th_off * 4);
hdr->tso_max_paysize = mbuf->m_pkthdr.tso_segsz;
hdr->inner_l3_offset = ehdrlen - 2;
nic->drv_stats.tx_tso++;
}
proto = ip->ip_p;
break;
#endif
default:
hdr->csum_l3 = 0;
}
#if defined(INET6) || defined(INET)
if (poff > 0 && mbuf->m_pkthdr.csum_flags != 0) {
hdr->csum_l3 = 1; /* Enable IP csum calculation */
switch (proto) {
case IPPROTO_TCP:
if ((mbuf->m_pkthdr.csum_flags & CSUM_TCP) == 0)
break;
if (mbuf->m_len < (poff + sizeof(struct tcphdr))) {
mbuf = m_pullup(mbuf, poff + sizeof(struct tcphdr));
sq->snd_buff[qentry].mbuf = mbuf;
if (mbuf == NULL)
return (ENOBUFS);
}
hdr->csum_l4 = SEND_L4_CSUM_TCP;
break;
case IPPROTO_UDP:
if ((mbuf->m_pkthdr.csum_flags & CSUM_UDP) == 0)
break;
if (mbuf->m_len < (poff + sizeof(struct udphdr))) {
mbuf = m_pullup(mbuf, poff + sizeof(struct udphdr));
sq->snd_buff[qentry].mbuf = mbuf;
if (mbuf == NULL)
return (ENOBUFS);
}
hdr->csum_l4 = SEND_L4_CSUM_UDP;
break;
case IPPROTO_SCTP:
if ((mbuf->m_pkthdr.csum_flags & CSUM_SCTP) == 0)
break;
if (mbuf->m_len < (poff + sizeof(struct sctphdr))) {
mbuf = m_pullup(mbuf, poff + sizeof(struct sctphdr));
sq->snd_buff[qentry].mbuf = mbuf;
if (mbuf == NULL)
return (ENOBUFS);
}
hdr->csum_l4 = SEND_L4_CSUM_SCTP;
break;
default:
break;
}
hdr->l3_offset = ehdrlen;
hdr->l4_offset = poff;
}
if ((mbuf->m_pkthdr.tso_segsz != 0) && nic->hw_tso) {
th = (struct tcphdr *)((caddr_t)(mbuf->m_data + poff));
hdr->tso = 1;
hdr->tso_start = poff + (th->th_off * 4);
hdr->tso_max_paysize = mbuf->m_pkthdr.tso_segsz;
hdr->inner_l3_offset = ehdrlen - 2;
nic->drv_stats.tx_tso++;
}
#endif
return (0);
}