// SPDX-License-Identifier: GPL-2.0 #include <linux/ip.h>#include <linux/skbuff.h>#include <net/ip6_checksum.h>#include <net/psp.h>#include <net/sock.h> #include "netdevsim.h" void nsim_psp_handle_ext(struct sk_buff *skb, struct skb_ext *psp_ext){ if (psp_ext) __skb_ext_set(skb, SKB_EXT_PSP, psp_ext);} enum skb_drop_reasonnsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, struct netdevsim *peer_ns, struct skb_ext **psp_ext){ enum skb_drop_reason rc = 0; struct psp_dev *peer_psd; struct psp_assoc *pas; struct net *net; int psp_len; void **ptr; rcu_read_lock(); pas = psp_skb_get_assoc_rcu(skb); if (!pas) { rc = SKB_NOT_DROPPED_YET; goto out_unlock; } if (!skb_transport_header_was_set(skb)) { rc = SKB_DROP_REASON_PSP_OUTPUT; goto out_unlock; } ptr = psp_assoc_drv_data(pas); if (*ptr != ns) { rc = SKB_DROP_REASON_PSP_OUTPUT; goto out_unlock; } net = sock_net(skb->sk); if (!psp_dev_encapsulate(net, skb, pas->tx.spi, pas->version, 0)) { rc = SKB_DROP_REASON_PSP_OUTPUT; goto out_unlock; } psp_len = skb->len - skb_inner_transport_offset(skb); atomic64_inc(&ns->psp.tx_packets); atomic64_add(psp_len, &ns->psp.tx_bytes); /* Now pretend we just received this frame */ peer_psd = rcu_dereference(peer_ns->psp.dev); if (peer_psd && peer_psd->config.versions & (1 << pas->version)) { bool strip_icv = false; u8 generation; /* We cheat a bit and put the generation in the key. * In real life if generation was too old, then decryption would * fail. Here, we just make it so a bad key causes a bad * generation too, and psp_sk_rx_policy_check() will fail. */ generation = pas->tx.key[0]; skb_ext_reset(skb); skb->mac_len = ETH_HLEN; if (psp_dev_rcv(skb, peer_psd->id, generation, strip_icv)) { rc = SKB_DROP_REASON_PSP_OUTPUT; goto out_unlock; } *psp_ext = skb->extensions; refcount_inc(&(*psp_ext)->refcnt); skb->decrypted = 1; atomic64_inc(&peer_ns->psp.rx_packets); atomic64_add(psp_len, &peer_ns->psp.rx_bytes); } else { struct ipv6hdr *ip6h __maybe_unused; struct iphdr *iph; struct udphdr *uh; __wsum csum; int udplen; /* Do not decapsulate. Receive the skb with the udp and psp * headers still there as if this is a normal udp packet. * psp_dev_encapsulate() sets udp checksum to 0, so we need to * provide a valid checksum here, so the skb isn't dropped. */ uh = udp_hdr(skb); udplen = udp_get_len(skb, uh, skb_transport_offset(skb)); csum = skb_checksum(skb, skb_transport_offset(skb), udplen, 0); switch (skb->protocol) { case htons(ETH_P_IP): iph = ip_hdr(skb); uh->check = udp_v4_check(udplen, iph->saddr, iph->daddr, csum); break;#if IS_ENABLED(CONFIG_IPV6) case htons(ETH_P_IPV6): ip6h = ipv6_hdr(skb); uh->check = udp_v6_check(udplen, &ip6h->saddr, &ip6h->daddr, csum); break;#endif } uh->check = uh->check ?: CSUM_MANGLED_0; skb->ip_summed = CHECKSUM_NONE; } out_unlock: rcu_read_unlock(); return rc;} static intnsim_psp_set_config(struct psp_dev *psd, struct psp_dev_config *conf, struct netlink_ext_ack *extack){ return 0;} static intnsim_rx_spi_alloc(struct psp_dev *psd, u32 version, struct psp_key_parsed *assoc, struct netlink_ext_ack *extack){ struct netdevsim *ns = psd->drv_priv; int i; /* Check if incrementing the spi would change the phase bit */ if ((ns->psp.spi & PSP_SPI_KEY_ID) == PSP_SPI_KEY_ID) { NL_SET_ERR_MSG(extack, "SPI space exhausted"); return -ENOSPC; } assoc->spi = cpu_to_be32(++ns->psp.spi); assoc->key[0] = psd->generation; for (i = 1; i < PSP_MAX_KEY; i++) assoc->key[i] = ns->psp.spi + i; return 0;} static int nsim_assoc_add(struct psp_dev *psd, struct psp_assoc *pas, struct netlink_ext_ack *extack){ struct netdevsim *ns = psd->drv_priv; void **ptr = psp_assoc_drv_data(pas); /* Copy drv_priv from psd to assoc */ *ptr = psd->drv_priv; ns->psp.assoc_cnt++; return 0;} static int nsim_key_rotate(struct psp_dev *psd, struct netlink_ext_ack *extack){ struct netdevsim *ns = psd->drv_priv; /* Flip key phase and reset SPI to 0 within that space * (will be pre-incremented, as 0 is an invalid SPI). */ if (ns->psp.spi & PSP_SPI_KEY_PHASE) ns->psp.spi = 0; else ns->psp.spi = PSP_SPI_KEY_PHASE; return 0;} static void nsim_assoc_del(struct psp_dev *psd, struct psp_assoc *pas){ struct netdevsim *ns = psd->drv_priv; void **ptr = psp_assoc_drv_data(pas); *ptr = NULL; ns->psp.assoc_cnt--;} static void nsim_get_stats(struct psp_dev *psd, struct psp_dev_stats *stats){ struct netdevsim *ns = psd->drv_priv; /* WARNING: do *not* blindly zero stats in real drivers! * All required stats must be reported by the device! */ memset(stats, 0, sizeof(struct psp_dev_stats)); stats->rx_bytes = atomic64_read(&ns->psp.rx_bytes); stats->rx_packets = atomic64_read(&ns->psp.rx_packets); stats->tx_bytes = atomic64_read(&ns->psp.tx_bytes); stats->tx_packets = atomic64_read(&ns->psp.tx_packets);} static struct psp_dev_ops nsim_psp_ops = { .set_config = nsim_psp_set_config, .rx_spi_alloc = nsim_rx_spi_alloc, .tx_key_add = nsim_assoc_add, .tx_key_del = nsim_assoc_del, .key_rotate = nsim_key_rotate, .get_stats = nsim_get_stats,}; static struct psp_dev_caps nsim_psp_caps = { .versions = 1 << PSP_VERSION_HDR0_AES_GCM_128 | 1 << PSP_VERSION_HDR0_AES_GMAC_128 | 1 << PSP_VERSION_HDR0_AES_GCM_256 | 1 << PSP_VERSION_HDR0_AES_GMAC_256, .assoc_drv_spc = sizeof(void *),}; static void __nsim_psp_uninit(struct netdevsim *ns, bool teardown){ struct psp_dev *psd; psd = rcu_dereference_protected(ns->psp.dev, teardown || lockdep_is_held(&ns->psp.rereg_lock)); if (psd) { rcu_assign_pointer(ns->psp.dev, NULL); synchronize_rcu(); psp_dev_unregister(psd); } WARN_ON(ns->psp.assoc_cnt);} void nsim_psp_uninit(struct netdevsim *ns){ debugfs_remove(ns->psp.rereg); mutex_destroy(&ns->psp.rereg_lock); __nsim_psp_uninit(ns, true);} static ssize_tnsim_psp_rereg_write(struct file *file, const char __user *data, size_t count, loff_t *ppos){ struct netdevsim *ns = file->private_data; struct psp_dev *psd; ssize_t ret; mutex_lock(&ns->psp.rereg_lock); __nsim_psp_uninit(ns, false); psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns); if (IS_ERR(psd)) { ret = PTR_ERR(psd); goto out; } rcu_assign_pointer(ns->psp.dev, psd); ret = count;out: mutex_unlock(&ns->psp.rereg_lock); return ret;} static const struct file_operations nsim_psp_rereg_fops = { .open = simple_open, .write = nsim_psp_rereg_write, .llseek = generic_file_llseek, .owner = THIS_MODULE,}; int nsim_psp_init(struct netdevsim *ns){ struct dentry *ddir = ns->nsim_dev_port->ddir; struct psp_dev *psd; psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns); if (IS_ERR(psd)) return PTR_ERR(psd); rcu_assign_pointer(ns->psp.dev, psd); mutex_init(&ns->psp.rereg_lock); ns->psp.rereg = debugfs_create_file("psp_rereg", 0200, ddir, ns, &nsim_psp_rereg_fops); return 0;}
// SPDX-License-Identifier: GPL-2.0
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <net/ip6_checksum.h>
#include <net/psp.h>
#include <net/sock.h>
#include "netdevsim.h"
void nsim_psp_handle_ext(struct sk_buff *skb, struct skb_ext *psp_ext)
{
if (psp_ext)
__skb_ext_set(skb, SKB_EXT_PSP, psp_ext);
}
enum skb_drop_reason
nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns,
struct netdevsim *peer_ns, struct skb_ext **psp_ext)
{
enum skb_drop_reason rc = 0;
struct psp_dev *peer_psd;
struct psp_assoc *pas;
struct net *net;
int psp_len;
void **ptr;
rcu_read_lock();
pas = psp_skb_get_assoc_rcu(skb);
if (!pas) {
rc = SKB_NOT_DROPPED_YET;
goto out_unlock;
}
if (!skb_transport_header_was_set(skb)) {
rc = SKB_DROP_REASON_PSP_OUTPUT;
goto out_unlock;
}
ptr = psp_assoc_drv_data(pas);
if (*ptr != ns) {
rc = SKB_DROP_REASON_PSP_OUTPUT;
goto out_unlock;
}
net = sock_net(skb->sk);
if (!psp_dev_encapsulate(net, skb, pas->tx.spi, pas->version, 0)) {
rc = SKB_DROP_REASON_PSP_OUTPUT;
goto out_unlock;
}
psp_len = skb->len - skb_inner_transport_offset(skb);
atomic64_inc(&ns->psp.tx_packets);
atomic64_add(psp_len, &ns->psp.tx_bytes);
/* Now pretend we just received this frame */
peer_psd = rcu_dereference(peer_ns->psp.dev);
if (peer_psd && peer_psd->config.versions & (1 << pas->version)) {
bool strip_icv = false;
u8 generation;
/* We cheat a bit and put the generation in the key.
* In real life if generation was too old, then decryption would
* fail. Here, we just make it so a bad key causes a bad
* generation too, and psp_sk_rx_policy_check() will fail.
*/
generation = pas->tx.key[0];
skb_ext_reset(skb);
skb->mac_len = ETH_HLEN;
if (psp_dev_rcv(skb, peer_psd->id, generation, strip_icv)) {
rc = SKB_DROP_REASON_PSP_OUTPUT;
goto out_unlock;
}
*psp_ext = skb->extensions;
refcount_inc(&(*psp_ext)->refcnt);
skb->decrypted = 1;
atomic64_inc(&peer_ns->psp.rx_packets);
atomic64_add(psp_len, &peer_ns->psp.rx_bytes);
} else {
struct ipv6hdr *ip6h __maybe_unused;
struct iphdr *iph;
struct udphdr *uh;
__wsum csum;
int udplen;
/* Do not decapsulate. Receive the skb with the udp and psp
* headers still there as if this is a normal udp packet.
* psp_dev_encapsulate() sets udp checksum to 0, so we need to
* provide a valid checksum here, so the skb isn't dropped.
*/
uh = udp_hdr(skb);
udplen = udp_get_len(skb, uh, skb_transport_offset(skb));
csum = skb_checksum(skb, skb_transport_offset(skb),
udplen, 0);
switch (skb->protocol) {
case htons(ETH_P_IP):
iph = ip_hdr(skb);
uh->check = udp_v4_check(udplen, iph->saddr,
iph->daddr, csum);
break;
#if IS_ENABLED(CONFIG_IPV6)
case htons(ETH_P_IPV6):
ip6h = ipv6_hdr(skb);
uh->check = udp_v6_check(udplen, &ip6h->saddr,
&ip6h->daddr, csum);
break;
#endif
}
uh->check = uh->check ?: CSUM_MANGLED_0;
skb->ip_summed = CHECKSUM_NONE;
}
out_unlock:
rcu_read_unlock();
return rc;
}
static int
nsim_psp_set_config(struct psp_dev *psd, struct psp_dev_config *conf,
struct netlink_ext_ack *extack)
{
return 0;
}
static int
nsim_rx_spi_alloc(struct psp_dev *psd, u32 version,
struct psp_key_parsed *assoc,
struct netlink_ext_ack *extack)
{
struct netdevsim *ns = psd->drv_priv;
int i;
/* Check if incrementing the spi would change the phase bit */
if ((ns->psp.spi & PSP_SPI_KEY_ID) == PSP_SPI_KEY_ID) {
NL_SET_ERR_MSG(extack, "SPI space exhausted");
return -ENOSPC;
}
assoc->spi = cpu_to_be32(++ns->psp.spi);
assoc->key[0] = psd->generation;
for (i = 1; i < PSP_MAX_KEY; i++)
assoc->key[i] = ns->psp.spi + i;
return 0;
}
static int nsim_assoc_add(struct psp_dev *psd, struct psp_assoc *pas,
struct netlink_ext_ack *extack)
{
struct netdevsim *ns = psd->drv_priv;
void **ptr = psp_assoc_drv_data(pas);
/* Copy drv_priv from psd to assoc */
*ptr = psd->drv_priv;
ns->psp.assoc_cnt++;
return 0;
}
static int nsim_key_rotate(struct psp_dev *psd, struct netlink_ext_ack *extack)
{
struct netdevsim *ns = psd->drv_priv;
/* Flip key phase and reset SPI to 0 within that space
* (will be pre-incremented, as 0 is an invalid SPI).
*/
if (ns->psp.spi & PSP_SPI_KEY_PHASE)
ns->psp.spi = 0;
else
ns->psp.spi = PSP_SPI_KEY_PHASE;
return 0;
}
static void nsim_assoc_del(struct psp_dev *psd, struct psp_assoc *pas)
{
struct netdevsim *ns = psd->drv_priv;
void **ptr = psp_assoc_drv_data(pas);
*ptr = NULL;
ns->psp.assoc_cnt--;
}
static void nsim_get_stats(struct psp_dev *psd, struct psp_dev_stats *stats)
{
struct netdevsim *ns = psd->drv_priv;
/* WARNING: do *not* blindly zero stats in real drivers!
* All required stats must be reported by the device!
*/
memset(stats, 0, sizeof(struct psp_dev_stats));
stats->rx_bytes = atomic64_read(&ns->psp.rx_bytes);
stats->rx_packets = atomic64_read(&ns->psp.rx_packets);
stats->tx_bytes = atomic64_read(&ns->psp.tx_bytes);
stats->tx_packets = atomic64_read(&ns->psp.tx_packets);
}
static struct psp_dev_ops nsim_psp_ops = {
.set_config = nsim_psp_set_config,
.rx_spi_alloc = nsim_rx_spi_alloc,
.tx_key_add = nsim_assoc_add,
.tx_key_del = nsim_assoc_del,
.key_rotate = nsim_key_rotate,
.get_stats = nsim_get_stats,
};
static struct psp_dev_caps nsim_psp_caps = {
.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128 |
1 << PSP_VERSION_HDR0_AES_GMAC_128 |
1 << PSP_VERSION_HDR0_AES_GCM_256 |
1 << PSP_VERSION_HDR0_AES_GMAC_256,
.assoc_drv_spc = sizeof(void *),
};
static void __nsim_psp_uninit(struct netdevsim *ns, bool teardown)
{
struct psp_dev *psd;
psd = rcu_dereference_protected(ns->psp.dev,
teardown ||
lockdep_is_held(&ns->psp.rereg_lock));
if (psd) {
rcu_assign_pointer(ns->psp.dev, NULL);
synchronize_rcu();
psp_dev_unregister(psd);
}
WARN_ON(ns->psp.assoc_cnt);
}
void nsim_psp_uninit(struct netdevsim *ns)
{
debugfs_remove(ns->psp.rereg);
mutex_destroy(&ns->psp.rereg_lock);
__nsim_psp_uninit(ns, true);
}
static ssize_t
nsim_psp_rereg_write(struct file *file, const char __user *data, size_t count,
loff_t *ppos)
{
struct netdevsim *ns = file->private_data;
struct psp_dev *psd;
ssize_t ret;
mutex_lock(&ns->psp.rereg_lock);
__nsim_psp_uninit(ns, false);
psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns);
if (IS_ERR(psd)) {
ret = PTR_ERR(psd);
goto out;
}
rcu_assign_pointer(ns->psp.dev, psd);
ret = count;
out:
mutex_unlock(&ns->psp.rereg_lock);
return ret;
}
static const struct file_operations nsim_psp_rereg_fops = {
.open = simple_open,
.write = nsim_psp_rereg_write,
.llseek = generic_file_llseek,
.owner = THIS_MODULE,
};
int nsim_psp_init(struct netdevsim *ns)
{
struct dentry *ddir = ns->nsim_dev_port->ddir;
struct psp_dev *psd;
psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns);
if (IS_ERR(psd))
return PTR_ERR(psd);
rcu_assign_pointer(ns->psp.dev, psd);
mutex_init(&ns->psp.rereg_lock);
ns->psp.rereg = debugfs_create_file("psp_rereg", 0200, ddir, ns,
&nsim_psp_rereg_fops);
return 0;
}