|
@@ -339,12 +339,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;
|
|
@@ -366,8 +367,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) {
|
|
@@ -1335,6 +1337,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),
|
|
@@ -1347,10 +1350,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.
|
|
|
*/
|
|
@@ -1359,6 +1363,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
|
|
@@ -1644,6 +1656,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),
|
|
@@ -1655,7 +1668,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;
|
|
|
|