Skip to content

Commit

Permalink
compat: Fixup ipv6 fragmentation on 4.9.135+ kernels
Browse files Browse the repository at this point in the history
Upstream commit 648700f76b03 ("inet: frags: use rhashtables...") changed
how ipv6 fragmentation is implemented.  This patch was backported to
the upstream stable 4.9.x kernel starting at 4.9.135.

This patch creates the compatibility layer changes required to both
compile and also operate correctly with ipv6 fragmentation on these
kernels. Check if the inet_frags 'rnd' field is present to key on
whether the upstream patch is present.  Also update Travis to the
latest 4.9 kernel release so that this patch is compile tested.

Passes Travis:
https://travis-ci.org/gvrose8192/ovs-experimental/builds/587185472

Cc: William Tu <u9012063@gmail.com>
Cc: Yi-Hung Wei <yihung.wei@gmail.com>
Cc: Yifeng Sun <pkusunyifeng@gmail.com>
Acked-by: Yi-Hung Wei <yihung.wei@gmail.com>
Signed-off-by: Greg Rose <gvrose8192@gmail.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
  • Loading branch information
gvrose8192 authored and blp committed Sep 23, 2019
1 parent 4781869 commit 05b4f29
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 3 deletions.
4 changes: 3 additions & 1 deletion acinclude.m4
Expand Up @@ -694,7 +694,9 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
OVS_FIND_PARAM_IFELSE([$KSRC/include/net/protocol.h],
[udp_add_offload], [net],
[OVS_DEFINE([HAVE_UDP_ADD_OFFLOAD_TAKES_NET])])
OVS_FIND_FIELD_IFELSE([$KSRC/include/net/inet_frag.h], [inet_frags],
[rnd],
[OVS_DEFINE([HAVE_INET_FRAGS_RND])])
if cmp -s datapath/linux/kcompat.h.new \
datapath/linux/kcompat.h >/dev/null 2>&1; then
Expand Down
52 changes: 50 additions & 2 deletions datapath/linux/compat/nf_conntrack_reasm.c
Expand Up @@ -73,6 +73,7 @@ static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
}

#ifdef HAVE_INET_FRAGS_RND
static unsigned int nf_hash_frag(__be32 id, const struct in6_addr *saddr,
const struct in6_addr *daddr)
{
Expand All @@ -99,6 +100,7 @@ static unsigned int nf_hashfn(struct inet_frag_queue *q)
return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr);
}

#endif /* HAVE_INET_FRAGS_RND */
static void nf_ct_frag6_expire(unsigned long data)
{
struct frag_queue *fq;
Expand All @@ -107,9 +109,14 @@ static void nf_ct_frag6_expire(unsigned long data)
fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q);
net = container_of(fq->q.net, struct net, nf_frag.frags);

#ifdef HAVE_INET_FRAGS_RND
ip6_expire_frag_queue(net, fq, &nf_frags);
#else
ip6_expire_frag_queue(net, fq);
#endif
}

#ifdef HAVE_INET_FRAGS_RND
/* Creation primitives. */
static inline struct frag_queue *fq_find(struct net *net, __be32 id,
u32 user, struct in6_addr *src,
Expand Down Expand Up @@ -140,7 +147,27 @@ static inline struct frag_queue *fq_find(struct net *net, __be32 id,
}
return container_of(q, struct frag_queue, q);
}
#else
static struct frag_queue *fq_find(struct net *net, __be32 id, u32 user,
const struct ipv6hdr *hdr, int iif)
{
struct frag_v6_compare_key key = {
.id = id,
.saddr = hdr->saddr,
.daddr = hdr->daddr,
.user = user,
.iif = iif,
};
struct inet_frag_queue *q;

q = inet_frag_find(&net->nf_frag.frags, &key);
if (!q)
return NULL;

return container_of(q, struct frag_queue, q);
}

#endif /* HAVE_INET_FRAGS_RND */

static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
const struct frag_hdr *fhdr, int nhoff)
Expand Down Expand Up @@ -289,7 +316,11 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
return 0;

discard_fq:
#ifdef HAVE_INET_FRAGS_RND
inet_frag_kill(&fq->q, &nf_frags);
#else
inet_frag_kill(&fq->q);
#endif
err:
return -1;
}
Expand All @@ -311,7 +342,11 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_devic
int payload_len;
u8 ecn;

#ifdef HAVE_INET_FRAGS_RND
inet_frag_kill(&fq->q, &nf_frags);
#else
inet_frag_kill(&fq->q);
#endif

WARN_ON(head == NULL);
WARN_ON(NFCT_FRAG6_CB(head)->offset != 0);
Expand Down Expand Up @@ -530,8 +565,13 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
local_bh_enable();
#endif

#ifdef HAVE_INET_FRAGS_RND
fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr,
ip6_frag_ecn(hdr));
#else
fq = fq_find(net, fhdr->identification, user, hdr,
skb->dev ? skb->dev->ifindex : 0);
#endif
if (fq == NULL)
return -ENOMEM;

Expand All @@ -553,7 +593,11 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)

out_unlock:
spin_unlock_bh(&fq->q.lock);
#ifdef HAVE_INET_FRAGS_RND
inet_frag_put(&fq->q, &nf_frags);
#else
inet_frag_put(&fq->q);
#endif
return ret;
}

Expand All @@ -571,13 +615,17 @@ int rpl_nf_ct_frag6_init(void)
int ret = 0;

nf_defrag_ipv6_enable();
#ifdef HAVE_INET_FRAGS_RND
nf_frags.hashfn = nf_hashfn;
nf_frags.match = ip6_frag_match;
#else
nf_frags.rhash_params = ip6_rhash_params;
#endif
nf_frags.constructor = ip6_frag_init;
nf_frags.destructor = NULL;
nf_frags.qsize = sizeof(struct frag_queue);
nf_frags.match = ip6_frag_match;
nf_frags.frag_expire = nf_ct_frag6_expire;
#ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK
#if defined(HAVE_INET_FRAGS_WITH_FRAGS_WORK) || !defined(HAVE_INET_FRAGS_RND)
nf_frags.frags_cache_name = nf_frags_cache_name;
#endif
#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8,0)
Expand Down

0 comments on commit 05b4f29

Please sign in to comment.