Commit d4353bc9 authored by Duanqiang Wen's avatar Duanqiang Wen
Browse files

net: wangxun: fix resolving ipv6 pakets extension header errors

wangxun inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IBCARJ


CVE: NA

----------------------

fix bug of all Outer ipv6 packets with extension header,
and fix bug of IP over IP with ipvX/ipv6 mode.

Fixes: e9f0b33c ("net: txgbe: Add support for Netswift 10G NIC")
Fixes: 206f9c11 ("net: ngbe: Add Netswift Giga NIC driver")
Signed-off-by: default avatarDuanqiang Wen <duanqiangwen@net-swift.com>
parent fb7ee60a
Loading
Loading
Loading
Loading
+63 −42
Original line number Diff line number Diff line
@@ -5146,37 +5146,6 @@ static void ngbe_service_task(struct work_struct *work)
	ngbe_service_event_complete(adapter);
}

static u8 get_ipv6_proto(struct sk_buff *skb, int offset)
{
	struct ipv6hdr *hdr = (struct ipv6hdr *)(skb->data + offset);
	u8 nexthdr = hdr->nexthdr;

	offset += sizeof(struct ipv6hdr);

	while (ipv6_ext_hdr(nexthdr)) {
		struct ipv6_opt_hdr _hdr, *hp;

		if (nexthdr == NEXTHDR_NONE)
			break;

		hp = skb_header_pointer(skb, offset, sizeof(_hdr), &_hdr);
		if (!hp)
			break;

		if (nexthdr == NEXTHDR_FRAGMENT) {
			break;
		} else if (nexthdr == NEXTHDR_AUTH) {
			offset +=  ipv6_authlen(hp);
		} else {
			offset +=  ipv6_optlen(hp);
		}

		nexthdr = hp->nexthdr;
	}

	return nexthdr;
}

union network_header {
	struct iphdr *ipv4;
	struct ipv6hdr *ipv6;
@@ -5189,6 +5158,10 @@ static ngbe_dptype encode_tx_desc_ptype(const struct ngbe_tx_buffer *first)
	u8 tun_prot = 0;
	u8 l4_prot = 0;
	u8 ptype = 0;
	unsigned char *exthdr;
	unsigned char *l4_hdr;
	__be16 frag_off;
	u32 len = 0;

	if (skb->encapsulation) {
		union network_header hdr;
@@ -5200,8 +5173,15 @@ static ngbe_dptype encode_tx_desc_ptype(const struct ngbe_tx_buffer *first)
				goto encap_frag;
			ptype = NGBE_PTYPE_TUN_IPV4;
			break;
		case __constant_htons(ETH_P_IPV6):
			tun_prot = get_ipv6_proto(skb, skb_network_offset(skb));
		case htons(ETH_P_IPV6):
			l4_hdr = skb_transport_header(skb);
			exthdr = skb_network_header(skb) +
						sizeof(struct ipv6hdr);
			tun_prot = ipv6_hdr(skb)->nexthdr;
			if (l4_hdr != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &tun_prot, &frag_off);

			if (tun_prot == NEXTHDR_FRAGMENT)
				goto encap_frag;
			ptype = NGBE_PTYPE_TUN_IPV6;
@@ -5210,7 +5190,8 @@ static ngbe_dptype encode_tx_desc_ptype(const struct ngbe_tx_buffer *first)
			goto exit;
		}

		if (tun_prot == IPPROTO_IPIP) {
		if (tun_prot == IPPROTO_IPIP ||
		    tun_prot == IPPROTO_IPV6) {
			hdr.raw = (void *)inner_ip_hdr(skb);
			ptype |= NGBE_PTYPE_PKT_IPIP;
		} else if (tun_prot == IPPROTO_UDP) {
@@ -5228,8 +5209,14 @@ static ngbe_dptype encode_tx_desc_ptype(const struct ngbe_tx_buffer *first)
			}
			break;
		case 6:
			l4_prot = get_ipv6_proto(skb,
						 skb_inner_network_offset(skb));
			l4_hdr = skb_inner_transport_header(skb);
			exthdr = skb_inner_network_header(skb) +
						sizeof(struct ipv6hdr);
			l4_prot = inner_ipv6_hdr(skb)->nexthdr;
			if (l4_hdr != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &l4_prot, &frag_off);

			ptype |= NGBE_PTYPE_PKT_IPV6;
			if (l4_prot == NEXTHDR_FRAGMENT) {
				ptype |= NGBE_PTYPE_TYP_IPFRAG;
@@ -5251,8 +5238,15 @@ static ngbe_dptype encode_tx_desc_ptype(const struct ngbe_tx_buffer *first)
			}
			break;
#ifdef NETIF_F_IPV6_CSUM
		case __constant_htons(ETH_P_IPV6):
			l4_prot = get_ipv6_proto(skb, skb_network_offset(skb));
		case htons(ETH_P_IPV6):
			l4_hdr = skb_transport_header(skb);
			exthdr = skb_network_header(skb) +
						sizeof(struct ipv6hdr);
			l4_prot = ipv6_hdr(skb)->nexthdr;
			if (l4_hdr != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &l4_prot, &frag_off);

			ptype = NGBE_PTYPE_PKT_IP | NGBE_PTYPE_PKT_IPV6;
			if (l4_prot == NEXTHDR_FRAGMENT) {
				ptype |= NGBE_PTYPE_TYP_IPFRAG;
@@ -5314,6 +5308,9 @@ static int ngbe_tso(struct ngbe_ring *tx_ring,
	struct iphdr *iph;
	u32 tunhdr_eiplen_tunlen = 0;
	u8 tun_prot = 0;
	unsigned char *exthdr;
	unsigned char *l4_hdr;
	__be16 frag_off;
	bool enc = skb->encapsulation;

	struct ipv6hdr *ipv6h;
@@ -5379,8 +5376,14 @@ static int ngbe_tso(struct ngbe_ring *tx_ring,
			tun_prot = ip_hdr(skb)->protocol;
			first->tx_flags |= NGBE_TX_FLAGS_OUTER_IPV4;
			break;
		case __constant_htons(ETH_P_IPV6):
		case htons(ETH_P_IPV6):
			l4_hdr = skb_transport_header(skb);
			exthdr = skb_network_header(skb) +
						sizeof(struct ipv6hdr);
			tun_prot = ipv6_hdr(skb)->nexthdr;
			if (l4_hdr != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &tun_prot, &frag_off);
			break;
		default:
			break;
@@ -5405,6 +5408,7 @@ static int ngbe_tso(struct ngbe_ring *tx_ring,
					NGBE_TXD_TUNNEL_LEN_SHIFT);
			break;
		case IPPROTO_IPIP:
		case IPPROTO_IPV6:
			tunhdr_eiplen_tunlen = (((char *)inner_ip_hdr(skb) -
						(char *)ip_hdr(skb)) >> 2) <<
						NGBE_TXD_OUTER_IPLEN_SHIFT;
@@ -5446,6 +5450,10 @@ static void ngbe_tx_csum(struct ngbe_ring *tx_ring,
				  NGBE_TXD_MACLEN_SHIFT;
	} else {
		u8 l4_prot = 0;
		unsigned char *exthdr;
		unsigned char *l4_hdr;
		__be16 frag_off;

		union {
			struct iphdr *ipv4;
			struct ipv6hdr *ipv6;
@@ -5465,8 +5473,16 @@ static void ngbe_tx_csum(struct ngbe_ring *tx_ring,
			case __constant_htons(ETH_P_IP):
				tun_prot = ip_hdr(skb)->protocol;
				break;
			case __constant_htons(ETH_P_IPV6):
			case htons(ETH_P_IPV6):
				l4_hdr = skb_transport_header(skb);
				exthdr = skb_network_header(skb) +
							sizeof(struct ipv6hdr);
				tun_prot = ipv6_hdr(skb)->nexthdr;
				if (l4_hdr != exthdr)
					ipv6_skip_exthdr(skb,
							 exthdr - skb->data,
							 &tun_prot, &frag_off);

				break;
			default:
				if (unlikely(net_ratelimit())) {
@@ -5496,6 +5512,7 @@ static void ngbe_tx_csum(struct ngbe_ring *tx_ring,
					NGBE_TXD_TUNNEL_LEN_SHIFT);
				break;
			case IPPROTO_IPIP:
			case IPPROTO_IPV6:
				tunhdr_eiplen_tunlen =
					(((char *)inner_ip_hdr(skb)-
					(char *)ip_hdr(skb)) >> 2) <<
@@ -5521,7 +5538,11 @@ static void ngbe_tx_csum(struct ngbe_ring *tx_ring,
		case 6:
			vlan_macip_lens |=
				(transport_hdr.raw - network_hdr.raw) >> 1;
			exthdr = network_hdr.raw + sizeof(struct ipv6hdr);
			l4_prot = network_hdr.ipv6->nexthdr;
			if (transport_hdr.raw != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &l4_prot, &frag_off);
			break;
		default:
			break;
+52 −39
Original line number Diff line number Diff line
@@ -5902,37 +5902,6 @@ static void txgbe_service_task(struct work_struct *work)
	txgbe_service_event_complete(adapter);
}

static u8 get_ipv6_proto(struct sk_buff *skb, int offset)
{
	struct ipv6hdr *hdr = (struct ipv6hdr *)(skb->data + offset);
	u8 nexthdr = hdr->nexthdr;

	offset += sizeof(struct ipv6hdr);

	while (ipv6_ext_hdr(nexthdr)) {
		struct ipv6_opt_hdr _hdr, *hp;

		if (nexthdr == NEXTHDR_NONE)
			break;

		hp = skb_header_pointer(skb, offset, sizeof(_hdr), &_hdr);
		if (!hp)
			break;

		if (nexthdr == NEXTHDR_FRAGMENT) {
			break;
		} else if (nexthdr == NEXTHDR_AUTH) {
			offset +=  ipv6_authlen(hp);
		} else {
			offset +=  ipv6_optlen(hp);
		}

		nexthdr = hp->nexthdr;
	}

	return nexthdr;
}

union network_header {
	struct iphdr *ipv4;
	struct ipv6hdr *ipv6;
@@ -5942,11 +5911,12 @@ union network_header {
static txgbe_dptype encode_tx_desc_ptype(const struct txgbe_tx_buffer *first)
{
	struct sk_buff *skb = first->skb;

	u8 tun_prot = 0;

	u8 l4_prot = 0;
	u8 ptype = 0;
	unsigned char *exthdr;
	unsigned char *l4_hdr;
	__be16 frag_off;

	if (skb->encapsulation) {
		union network_header hdr;
@@ -5959,7 +5929,13 @@ static txgbe_dptype encode_tx_desc_ptype(const struct txgbe_tx_buffer *first)
			ptype = TXGBE_PTYPE_TUN_IPV4;
			break;
		case __constant_htons(ETH_P_IPV6):
			tun_prot = get_ipv6_proto(skb, skb_network_offset(skb));
			l4_hdr = skb_transport_header(skb);
			exthdr = skb_network_header(skb) +
						sizeof(struct ipv6hdr);
			tun_prot = ipv6_hdr(skb)->nexthdr;
			if (l4_hdr != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &tun_prot, &frag_off);
			if (tun_prot == NEXTHDR_FRAGMENT)
				goto encap_frag;
			ptype = TXGBE_PTYPE_TUN_IPV6;
@@ -5968,7 +5944,8 @@ static txgbe_dptype encode_tx_desc_ptype(const struct txgbe_tx_buffer *first)
			goto exit;
		}

		if (tun_prot == IPPROTO_IPIP) {
		if (tun_prot == IPPROTO_IPIP ||
		    tun_prot == IPPROTO_IPV6) {
			hdr.raw = (void *)inner_ip_hdr(skb);
			ptype |= TXGBE_PTYPE_PKT_IPIP;
		} else if (tun_prot == IPPROTO_UDP) {
@@ -6015,8 +5992,13 @@ static txgbe_dptype encode_tx_desc_ptype(const struct txgbe_tx_buffer *first)
			}
			break;
		case 6:
			l4_prot = get_ipv6_proto(skb,
						 skb_inner_network_offset(skb));
			l4_hdr = skb_inner_transport_header(skb);
			exthdr = skb_inner_network_header(skb) +
						sizeof(struct ipv6hdr);
			l4_prot = inner_ipv6_hdr(skb)->nexthdr;
			if (l4_hdr != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &l4_prot, &frag_off);
			ptype |= TXGBE_PTYPE_PKT_IPV6;
			if (l4_prot == NEXTHDR_FRAGMENT) {
				ptype |= TXGBE_PTYPE_TYP_IPFRAG;
@@ -6040,7 +6022,14 @@ static txgbe_dptype encode_tx_desc_ptype(const struct txgbe_tx_buffer *first)
			break;
#ifdef NETIF_F_IPV6_CSUM
		case __constant_htons(ETH_P_IPV6):
			l4_prot = get_ipv6_proto(skb, skb_network_offset(skb));
			l4_hdr = skb_transport_header(skb);
			exthdr = skb_network_header(skb) +
						sizeof(struct ipv6hdr);
			l4_prot = ipv6_hdr(skb)->nexthdr;
			if (l4_hdr != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &l4_prot, &frag_off);

			ptype = TXGBE_PTYPE_PKT_IP | TXGBE_PTYPE_PKT_IPV6;
			if (l4_prot == NEXTHDR_FRAGMENT) {
				ptype |= TXGBE_PTYPE_TYP_IPFRAG;
@@ -6102,8 +6091,10 @@ static int txgbe_tso(struct txgbe_ring *tx_ring,
	struct tcphdr *tcph;
	struct iphdr *iph;
	u32 tunhdr_eiplen_tunlen = 0;

	u8 tun_prot = 0;
	unsigned char *exthdr;
	unsigned char *l4_hdr;
	__be16 frag_off;
	bool enc = skb->encapsulation;

		struct ipv6hdr *ipv6h;
@@ -6176,7 +6167,13 @@ static int txgbe_tso(struct txgbe_ring *tx_ring,
			first->tx_flags |= TXGBE_TX_FLAGS_OUTER_IPV4;
			break;
		case __constant_htons(ETH_P_IPV6):
			l4_hdr = skb_transport_header(skb);
			exthdr = skb_network_header(skb) +
						sizeof(struct ipv6hdr);
			tun_prot = ipv6_hdr(skb)->nexthdr;
			if (l4_hdr != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &tun_prot, &frag_off);
			break;
		default:
			break;
@@ -6201,6 +6198,7 @@ static int txgbe_tso(struct txgbe_ring *tx_ring,
					TXGBE_TXD_TUNNEL_LEN_SHIFT);
			break;
		case IPPROTO_IPIP:
		case IPPROTO_IPV6:
			tunhdr_eiplen_tunlen = (((char *)inner_ip_hdr(skb)-
						(char *)ip_hdr(skb)) >> 2) <<
						TXGBE_TXD_OUTER_IPLEN_SHIFT;
@@ -6242,6 +6240,9 @@ static void txgbe_tx_csum(struct txgbe_ring *tx_ring,
				  TXGBE_TXD_MACLEN_SHIFT;
	} else {
		u8 l4_prot = 0;
		unsigned char *exthdr;
		unsigned char *l4_hdr;
		__be16 frag_off;

		union {
			struct iphdr *ipv4;
@@ -6263,7 +6264,14 @@ static void txgbe_tx_csum(struct txgbe_ring *tx_ring,
				tun_prot = ip_hdr(skb)->protocol;
				break;
			case __constant_htons(ETH_P_IPV6):
				l4_hdr = skb_transport_header(skb);
				exthdr = skb_network_header(skb) +
							sizeof(struct ipv6hdr);
				tun_prot = ipv6_hdr(skb)->nexthdr;
				if (l4_hdr != exthdr)
					ipv6_skip_exthdr(skb,
							 exthdr - skb->data,
							 &tun_prot, &frag_off);
				break;
			default:
				if (unlikely(net_ratelimit())) {
@@ -6293,6 +6301,7 @@ static void txgbe_tx_csum(struct txgbe_ring *tx_ring,
					TXGBE_TXD_TUNNEL_LEN_SHIFT);
				break;
			case IPPROTO_IPIP:
			case IPPROTO_IPV6:
				tunhdr_eiplen_tunlen =
					(((char *)inner_ip_hdr(skb)-
					(char *)ip_hdr(skb)) >> 2) <<
@@ -6318,7 +6327,11 @@ static void txgbe_tx_csum(struct txgbe_ring *tx_ring,
		case 6:
			vlan_macip_lens |=
				(transport_hdr.raw - network_hdr.raw) >> 1;
			exthdr = network_hdr.raw + sizeof(struct ipv6hdr);
			l4_prot = network_hdr.ipv6->nexthdr;
			if (transport_hdr.raw != exthdr)
				ipv6_skip_exthdr(skb, exthdr - skb->data,
						 &l4_prot, &frag_off);
			break;
		default:
			break;