
Apparently, 4-6 XLAT scenarios can insert an empty fragment header into the IPv6 header chain to indicate that fragmentation is supported in the host IPv4 network. This header contains 0 for both the fragment_offset and MF fields, so simply checking the value of frag_off passed to the rmnet_frag_ipv6_skip_exthdr() function is not sufficient to properly catch this rogue header. Instead, we need to implement a "less clever" version. kernel oops at net/core/skbuff.c:4217! Call trace: skb_segment+0xcf0/0xd2c __udp_gso_segment+0xa4/0x544 udp6_ufo_fragment+0x2dc/0x344 ipv6_gso_segment+0x170/0x350 skb_mac_gso_segment+0xd4/0x1b0 __skb_gso_segment+0xcc/0x12c udp_rcv_segment.76914+0x54/0x16c udpv6_queue_rcv_skb+0x78/0x148 __udp6_lib_rcv+0x38c/0x4cc udpv6_rcv+0x20/0x30 ip6_protocol_deliver_rcu+0x3c0/0x63c ip6_input+0x60/0x184 ip6_rcv_finish+0x84/0x150 ipv6_rcv+0x5c/0x14c __netif_receive_skb+0x80/0x184 CRs-Fixed: 3358773 Change-Id: Ica2779b1da17bc46d397b42283369f3750edbf82 Signed-off-by: Sean Tranchetti <quic_stranche@quicinc.com>
120 linhas
3.5 KiB
C
120 linhas
3.5 KiB
C
/* Copyright (c) 2013-2021, 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
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* RMNET Packet Descriptor Framework
|
|
*
|
|
*/
|
|
|
|
#ifndef _RMNET_DESCRIPTOR_H_
|
|
#define _RMNET_DESCRIPTOR_H_
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/list.h>
|
|
#include <linux/skbuff.h>
|
|
#include "rmnet_config.h"
|
|
#include "rmnet_map.h"
|
|
|
|
struct rmnet_frag_descriptor_pool {
|
|
struct list_head free_list;
|
|
u32 pool_size;
|
|
};
|
|
|
|
struct rmnet_fragment {
|
|
struct list_head list;
|
|
skb_frag_t frag;
|
|
};
|
|
|
|
struct rmnet_frag_descriptor {
|
|
struct list_head list;
|
|
struct list_head frags;
|
|
struct net_device *dev;
|
|
u32 coal_bufsize;
|
|
u32 coal_bytes;
|
|
u32 len;
|
|
u32 hash;
|
|
u32 priority;
|
|
__be32 tcp_seq;
|
|
__be16 ip_id;
|
|
__be16 tcp_flags;
|
|
u16 data_offset;
|
|
u16 gso_size;
|
|
u16 gso_segs;
|
|
u16 ip_len;
|
|
u16 trans_len;
|
|
u8 ip_proto;
|
|
u8 trans_proto;
|
|
u8 pkt_id;
|
|
u8 csum_valid:1,
|
|
hdrs_valid:1,
|
|
ip_id_set:1,
|
|
tcp_seq_set:1,
|
|
flush_shs:1,
|
|
tcp_flags_set:1,
|
|
reserved:2;
|
|
};
|
|
|
|
/* Descriptor management */
|
|
struct rmnet_frag_descriptor *
|
|
rmnet_get_frag_descriptor(struct rmnet_port *port);
|
|
void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc,
|
|
struct rmnet_port *port);
|
|
void *rmnet_frag_pull(struct rmnet_frag_descriptor *frag_desc,
|
|
struct rmnet_port *port, unsigned int size);
|
|
void *rmnet_frag_trim(struct rmnet_frag_descriptor *frag_desc,
|
|
struct rmnet_port *port, unsigned int size);
|
|
void *rmnet_frag_header_ptr(struct rmnet_frag_descriptor *frag_desc, u32 off,
|
|
u32 len, void *buf);
|
|
int rmnet_frag_descriptor_add_frag(struct rmnet_frag_descriptor *frag_desc,
|
|
struct page *p, u32 page_offset, u32 len);
|
|
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 *frag_offp,
|
|
bool *frag_hdrp);
|
|
|
|
/* QMAP command packets */
|
|
void rmnet_frag_command(struct rmnet_frag_descriptor *frag_desc,
|
|
struct rmnet_map_header *qmap, struct rmnet_port *port);
|
|
int rmnet_frag_flow_command(struct rmnet_frag_descriptor *frag_desc,
|
|
struct rmnet_port *port, u16 pkt_len);
|
|
|
|
/* Ingress data handlers */
|
|
void rmnet_frag_deaggregate(struct sk_buff *skb, struct rmnet_port *port,
|
|
struct list_head *list, u32 priority);
|
|
void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc,
|
|
struct rmnet_port *port);
|
|
int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc,
|
|
struct rmnet_port *port,
|
|
struct list_head *list,
|
|
u16 len);
|
|
void rmnet_frag_ingress_handler(struct sk_buff *skb,
|
|
struct rmnet_port *port);
|
|
|
|
int rmnet_descriptor_init(struct rmnet_port *port);
|
|
void rmnet_descriptor_deinit(struct rmnet_port *port);
|
|
|
|
static inline void *rmnet_frag_data_ptr(struct rmnet_frag_descriptor *frag_desc)
|
|
{
|
|
struct rmnet_fragment *frag;
|
|
|
|
frag = list_first_entry_or_null(&frag_desc->frags,
|
|
struct rmnet_fragment, list);
|
|
|
|
if (!frag)
|
|
return NULL;
|
|
|
|
return skb_frag_address(&frag->frag);
|
|
}
|
|
|
|
#endif /* _RMNET_DESCRIPTOR_H_ */
|