diff --git a/Documentation/netlink/specs/psp.yaml b/Documentation/netlink/specs/psp.yaml
index d5713f5d1af7..f6af1a510d17 100644
--- a/Documentation/netlink/specs/psp.yaml
+++ b/Documentation/netlink/specs/psp.yaml
@@ -283,7 +283,9 @@ operations:
post: psp-device-unlock
-
name: tx-assoc
- doc: Add a PSP Tx association.
+ doc: |
+ Add a PSP Tx association.
+ If the socket already has a Tx association, rekey its Tx direction.
attribute-set: assoc
do:
request:diff --git a/Documentation/networking/psp.rst b/Documentation/networking/psp.rst
index fab797506b0a..43f222b38d3d 100644
--- a/Documentation/networking/psp.rst
+++ b/Documentation/networking/psp.rst
@@ -145,7 +145,8 @@ Rekeying
A connection which has completed the exchange described above and is
in the "PSP Full" state can be rekeyed in place without being torn
-down, using the same netlink calls as in connection setup.
+down. The Tx and Rx directions are rekeyed separately using the same
+netlink calls as in connection setup.
``rx-assoc`` allocates a new Rx key and SPI, which should be passed
to the peer exactly as during the initial exchange. The SPI which was
@@ -154,6 +155,8 @@ next Rx rekey, or until the device key it was derived from has been
rotated out, so packets the peer sent before it learned the new SPI
are still received.
+``tx-assoc`` installs a new Tx key, which takes effect immediately.
+
A rekey is rejected with ``-EINVAL`` if it would change the PSP device
or the PSP version of the association. It is rejected with ``-EBUSY``
if the socket has not completed its initial exchange in both
diff --git a/net/psp/psp_sock.c b/net/psp/psp_sock.c
index c1e38845f08c..d497f41c483e 100644
--- a/net/psp/psp_sock.c
+++ b/net/psp/psp_sock.c
@@ -280,6 +280,53 @@ psp_sock_set_tx_key(struct sock *sk, struct psp_dev *psd, struct psp_assoc *pas,
return err;
}
+static int
+psp_sock_tx_rekey(struct sock *sk, struct psp_dev *psd, struct psp_assoc *pas,
+ struct psp_key_parsed *key, struct netlink_ext_ack *extack)
+{
+ struct psp_assoc *new;
+ int err;
+
+ if (psp_dev_has_sadb(psd)) {
+ NL_SET_ERR_MSG(extack, "Tx rekey not supported on this device");
+ return -EOPNOTSUPP;
+ }
+ if (!pas->peer_tx) {
+ NL_SET_ERR_MSG(extack, "Socket PSP state is not fully established");
+ return -EBUSY;
+ }
+
+ new = kzalloc_flex(*new, drv_data, psd->caps->assoc_drv_spc,
+ GFP_KERNEL_ACCOUNT);
+ if (!new)
+ return -ENOMEM;
+
+ new->psd = pas->psd;
+ new->dev_id = pas->dev_id;
+ new->generation = pas->generation;
+ new->version = pas->version;
+ new->peer_tx = 1;
+ new->prev_spi = pas->prev_spi;
+ new->prev_generation = pas->prev_generation;
+ refcount_set(&new->refcnt, 1);
+ memcpy(&new->rx, &pas->rx, sizeof(new->rx));
+
+ err = psp_assoc_set_tx(psd, new, key, extack);
+ if (err) {
+ kfree(new);
+ return err;
+ }
+
+ psp_dev_get(new->psd);
+ list_add(&new->assocs_list, &pas->assocs_list);
+ list_move(&pas->assocs_list, &psd->stale_assocs);
+
+ rcu_assign_pointer(sk->psp_assoc, new);
+ psp_assoc_put(pas);
+
+ return 0;
+}
+
int psp_sock_assoc_set_tx(struct sock *sk, struct psp_dev *psd,
u32 version, struct psp_key_parsed *key,
struct netlink_ext_ack *extack)@@ -312,13 +359,11 @@ int psp_sock_assoc_set_tx(struct sock *sk, struct psp_dev *psd,
err = -EINVAL;
goto exit_unlock;
}
- if (pas->tx.spi) {
- NL_SET_ERR_MSG(extack, "Tx key already set");
- err = -EBUSY;
- goto exit_unlock;
- }
+ if (pas->tx.spi)
+ err = psp_sock_tx_rekey(sk, psd, pas, key, extack);
+ else
+ err = psp_sock_set_tx_key(sk, psd, pas, key, extack);
- err = psp_sock_set_tx_key(sk, psd, pas, key, extack);
exit_unlock:
release_sock(sk);
return err;
--
2.52.0