The SA lookup tuple is (daddr, spi, proto, family, mark), but the
EEXIST pre-check and the xfrm_state_insert() vs xfrm_state_add()
decision only considered daddr and family, ignoring mark.
A migration that only changes the mark inserts a duplicate SA tuple
into the hash tables.

Before:
root@west:~# ip xfrm state add src 10.1.1.1 dst 10.1.1.2 proto esp \
        spi 0x1000  reqid 100 mode tunnel aead "rfc4106(gcm(aes))" \
        0x1111111111111111111111111111111111111111 96 \
        mark 0x1 mask 0xff

root@west:~# ip xfrm state add src 10.1.1.1 dst 10.1.1.2 proto esp \
        spi 0x1000  reqid 100 mode tunnel aead "rfc4106(gcm(aes))" \
        0x1111111111111111111111111111111111111111 96 \
        mark 0x2 mask 0xff
root@west:~# ip xfrm state migrate dst 10.1.1.2 proto esp spi 0x1000 \
        mark 0x1 mask 0xff \
        new-dst 10.1.1.2 new-src 10.1.1.1 new-reqid 100 \
        new-mark 0x2 mask 0xff
ip x s
src 10.1.1.1 dst 10.1.1.2
    proto esp spi 0x00001000 reqid 100 mode tunnel
    replay-window 0
    mark 0x2/0xff
    aead rfc4106(gcm(aes)) 0x1111111111111111111111111111111111111111 96
    anti-replay context: seq 0x0, oseq 0x0, bitmap 0x00000000
src 10.1.1.1 dst 10.1.1.2
    proto esp spi 0x00001000 reqid 100 mode tunnel
    replay-window 0
    mark 0x2/0xff
    aead rfc4106(gcm(aes)) 0x1111111111111111111111111111111111111111 96
    anti-replay context: seq 0x0, oseq 0x0, bitmap 0x00000000
    sel src 0.0.0.0/0 dst 0.0.0.0/0

Notice two states with same mark 0x2/0xff.
After:

Error: New SA tuple already occupied.

Fixes: a9d155ea9b44 ("xfrm: add XFRM_MSG_MIGRATE_STATE for single SA migration")
Reported-by: Sashiko <[email protected]>
Signed-off-by: Antony Antony <[email protected]>
---
 net/xfrm/xfrm_state.c |  8 +++++---
 net/xfrm/xfrm_user.c  | 15 ++++++++++-----
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index d78cfe481f75..bffe985e42ea 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2221,10 +2221,12 @@ int xfrm_state_migrate_install(const struct xfrm_state 
*x,
                               struct netlink_ext_ack *extack)
 {
        if (m->new_family == m->old_family &&
-           xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) {
+           xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family) &&
+           xc->mark.v == x->mark.v && xc->mark.m == x->mark.m) {
                /*
-                * Care is needed when the destination address of the state is
-                * to be updated as it is a part of triplet.
+                * Care is needed when the destination address or mark of the
+                * state is to be updated, as they are part of the lookup
+                * triplet.
                 */
                xfrm_state_insert(xc);
        } else {
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 87ef198993db..a2317e6e6802 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -3435,18 +3435,23 @@ static int xfrm_do_migrate_state(struct sk_buff *skb, 
struct nlmsghdr *nlh,
                                                       
x->nat_keepalive_interval);
 
        if (m.new_family != um->id.family ||
-           !xfrm_addr_equal(&m.new_daddr, &um->id.daddr, um->id.family)) {
+           !xfrm_addr_equal(&m.new_daddr, &um->id.daddr, um->id.family) ||
+           (m.new_mark && (m.new_mark->v != x->mark.v ||
+                          m.new_mark->m != x->mark.m))) {
                u32 new_mark_key = m.new_mark ? m.new_mark->v & m.new_mark->m :
-                                               m.old_mark.v & m.old_mark.m;
+                                               x->mark.v & x->mark.m;
                struct xfrm_state *x_new;
 
                x_new = xfrm_state_lookup(net, new_mark_key, &m.new_daddr,
                                          um->id.spi, um->id.proto, 
m.new_family);
                if (x_new) {
                        xfrm_state_put(x_new);
-                       NL_SET_ERR_MSG(extack, "New SA tuple already occupied");
-                       err = -EEXIST;
-                       goto out;
+                       if (x_new != x) {
+                               NL_SET_ERR_MSG(extack, "New SA tuple already 
occupied");
+                               err = -EEXIST;
+                               goto out;
+                       }
+                       /* self-match via wide mark mask; not a collision */
                }
        }
 

-- 
2.47.3


Reply via email to