diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 365925c9d262..b02d2472b003 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -171,6 +171,7 @@ extern const u8 ip_frag_ecn_table[16]; #define IPFRAG_OK 0 #define IPFRAG_DUP 1 #define IPFRAG_OVERLAP 2 +#define IPFRAG_MIXED_SECPATH 3 int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, int offset, int end); void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, diff --git a/include/net/xfrm.h b/include/net/xfrm.h index a6d69aaa6cd2..298ca9bb0083 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1201,6 +1201,23 @@ secpath_reset(struct sk_buff *skb) #endif } +static inline bool +secpath_equal(const struct sk_buff *skb1, const struct sk_buff *skb2) +{ + const struct sec_path *sp1 = skb_sec_path(skb1); + const struct sec_path *sp2 = skb_sec_path(skb2); + int i; + + if (!sp1 || !sp2) + return sp1 == sp2; + if (sp1->len != sp2->len) + return false; + for (i = 0; i < sp1->len; i++) + if (sp1->xvec[i] != sp2->xvec[i]) + return false; + return true; +} + static inline int xfrm_addr_any(const xfrm_address_t *addr, unsigned short family) { diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index b286ee429da8..c18da91c1f03 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -23,6 +23,7 @@ #include #include #include +#include /* Use skb->cb to track consecutive/adjacent fragments coming at * the end of the queue. Nodes in the rb-tree queue will @@ -441,6 +442,10 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, { struct sk_buff *last = q->fragments_tail; + /* The reassembled skb keeps the first fragment's secpath. */ + if (last && !secpath_equal(last, skb)) + return IPFRAG_MIXED_SECPATH; + /* An IP fragment is never a GSO packet, but an untrusted source * (virtio_net_hdr) may have attached GSO metadata to it. Do not let * that reach the reassembled skb, whose head keeps the first diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index c790d2f49487..36105dd19282 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -378,8 +378,9 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb, int *refs) err = -EINVAL; goto err; } + if (err == IPFRAG_OVERLAP) + __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS); err = -EINVAL; - __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS); discard_qp: inet_frag_kill(&qp->q, refs); __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index a7964125c764..8418ed9423f4 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -241,8 +241,9 @@ static int ip6_frag_queue(struct net *net, err = -EINVAL; goto err; } + if (err == IPFRAG_OVERLAP) + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASM_OVERLAPS); err = -EINVAL; - __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASM_OVERLAPS); discard_fq: inet_frag_kill(&fq->q, refs); __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMFAILS);