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

Reply via email to