The xfrm inbound and ICMP error paths can match inbound XFRM states that have a mark, but only if the skb mark is already correctly set to match the state mark. This typically requires iptables rules (potentially even per SA iptables rules), which impose configuration complexity.
In some cases, it may be useful to match such an SA anyway. An example is when processing an ICMP error to an ESP packet that we previously sent. In this case, the only information available to match the SA are the IP addresses and the outbound SPI. Therefore, if the output SA has a mark, the lookup will fail and the ICMP packet cannot be processed unless the packet is somehow already marked. Signed-off-by: Lorenzo Colitti <lore...@google.com> --- include/net/xfrm.h | 4 ++++ net/xfrm/xfrm_state.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 1ec0c4760646..9d3b7c0ac6e2 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1550,6 +1550,10 @@ struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark, const xfrm_address_t *saddr, u8 proto, unsigned short family); +struct xfrm_state *xfrm_state_lookup_loose(struct net *net, u32 mark, + const xfrm_address_t *daddr, + __be32 spi, u8 proto, + unsigned short family); #ifdef CONFIG_XFRM_SUB_POLICY int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, unsigned short family, struct net *net); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 1b7856be3eeb..ee678758547f 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -839,6 +839,38 @@ static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark, return NULL; } +struct xfrm_state *xfrm_state_lookup_loose(struct net *net, u32 mark, + const xfrm_address_t *daddr, + __be32 spi, u8 proto, + unsigned short family) +{ + unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family); + struct xfrm_state *x, *cand = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(x, net->xfrm.state_byspi + h, byspi) { + if (x->props.family != family || + x->id.spi != spi || + x->id.proto != proto || + !xfrm_addr_equal(&x->id.daddr, daddr, family)) + continue; + + if (((mark & x->mark.m) == x->mark.v) && + xfrm_state_hold_rcu(x)) { + if (cand) + xfrm_state_put(cand); + rcu_read_unlock(); + return x; + } + + if (!cand && xfrm_state_hold_rcu(x)) + cand = x; + } + + rcu_read_unlock(); + return cand; +} + static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark, const xfrm_address_t *daddr, const xfrm_address_t *saddr, -- 2.15.1.504.g5279b80103-goog