diff --git a/core/rmnet_descriptor.c b/core/rmnet_descriptor.c index 4a8eac3bef9a..dc0fb7581bf9 100644 --- a/core/rmnet_descriptor.c +++ b/core/rmnet_descriptor.c @@ -323,12 +323,13 @@ int rmnet_frag_descriptor_add_frags_from(struct rmnet_frag_descriptor *to, EXPORT_SYMBOL(rmnet_frag_descriptor_add_frags_from); int rmnet_frag_ipv6_skip_exthdr(struct rmnet_frag_descriptor *frag_desc, - int start, u8 *nexthdrp, __be16 *fragp) + int start, u8 *nexthdrp, __be16 *frag_offp, + bool *frag_hdrp) { u8 nexthdr = *nexthdrp; - *fragp = 0; - + *frag_offp = 0; + *frag_hdrp = false; while (ipv6_ext_hdr(nexthdr)) { struct ipv6_opt_hdr *hp, __hp; int hdrlen; @@ -350,8 +351,9 @@ int rmnet_frag_ipv6_skip_exthdr(struct rmnet_frag_descriptor *frag_desc, if (!fp) return -EINVAL; - *fragp = *fp; - if (ntohs(*fragp) & ~0x7) + *frag_offp = *fp; + *frag_hdrp = true; + if (ntohs(*frag_offp) & ~0x7) break; hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) { @@ -1144,6 +1146,7 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc, struct ipv6hdr *ip6h, __ip6h; int ip_len; __be16 frag_off; + bool frag_hdr; u8 protocol; ip6h = rmnet_frag_header_ptr(coal_desc, 0, sizeof(*ip6h), @@ -1156,10 +1159,11 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc, ip_len = rmnet_frag_ipv6_skip_exthdr(coal_desc, sizeof(*ip6h), &protocol, - &frag_off); + &frag_off, + &frag_hdr); coal_desc->trans_proto = protocol; - /* If we run into a problem, or this has a fragment header + /* If we run into a problem, or this is fragmented packet * (which should technically not be possible, if the HW * works as intended...), bail. */ @@ -1168,6 +1172,14 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc, return; } + if (frag_hdr) { + /* There is a fragment header, but this is not a + * fragmented packet. We can handle this, but it + * cannot be coalesced because of kernel limitations. + */ + gro = false; + } + coal_desc->ip_len = (u16)ip_len; if (coal_desc->ip_len > sizeof(*ip6h)) { /* Don't allow coalescing of any packets with IPv6 @@ -1449,6 +1461,7 @@ static int rmnet_frag_checksum_pkt(struct rmnet_frag_descriptor *frag_desc) struct ipv6hdr *ip6h, __ip6h; int ip_len; __be16 frag_off; + bool frag_hdr; u8 protocol; ip6h = rmnet_frag_header_ptr(frag_desc, offset, sizeof(*ip6h), @@ -1460,7 +1473,8 @@ static int rmnet_frag_checksum_pkt(struct rmnet_frag_descriptor *frag_desc) protocol = ip6h->nexthdr; ip_len = rmnet_frag_ipv6_skip_exthdr(frag_desc, offset + sizeof(*ip6h), - &protocol, &frag_off); + &protocol, &frag_off, + &frag_hdr); if (ip_len < 0 || frag_off) return -EINVAL; diff --git a/core/rmnet_descriptor.h b/core/rmnet_descriptor.h index f7bfa55bffe7..7117e4060d42 100644 --- a/core/rmnet_descriptor.h +++ b/core/rmnet_descriptor.h @@ -1,4 +1,5 @@ /* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + * ​​​​Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.​ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -75,7 +76,8 @@ int rmnet_frag_descriptor_add_frags_from(struct rmnet_frag_descriptor *to, struct rmnet_frag_descriptor *from, u32 off, u32 len); int rmnet_frag_ipv6_skip_exthdr(struct rmnet_frag_descriptor *frag_desc, - int start, u8 *nexthdrp, __be16 *fragp); + int start, u8 *nexthdrp, __be16 *frag_offp, + bool *frag_hdrp); /* QMAP command packets */ void rmnet_frag_command(struct rmnet_frag_descriptor *frag_desc,