| 1 | |
| 2 |
|
| 3 | #include <linux/ip.h>
|
| 4 | #include <linux/skbuff.h>
|
| 5 | #include <net/ip6_checksum.h>
|
| 6 | #include <net/psp.h>
|
| 7 | #include <net/sock.h>
|
| 8 |
|
| 9 | #include "netdevsim.h"
|
| 10 |
|
| 11 | void nsim_psp_handle_ext(struct sk_buff *skb, struct skb_ext *psp_ext)
|
| 12 | {
|
| 13 | if (psp_ext)
|
| 14 | __skb_ext_set(skb, SKB_EXT_PSP, psp_ext);
|
| 15 | }
|
| 16 |
|
| 17 | enum skb_drop_reason
|
| 18 | nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns,
|
| 19 | struct netdevsim *peer_ns, struct skb_ext **psp_ext)
|
| 20 | {
|
| 21 | enum skb_drop_reason rc = 0;
|
| 22 | struct psp_dev *peer_psd;
|
| 23 | struct psp_assoc *pas;
|
| 24 | struct net *net;
|
| 25 | int psp_len;
|
| 26 | void **ptr;
|
| 27 |
|
| 28 | rcu_read_lock();
|
| 29 | pas = psp_skb_get_assoc_rcu(skb);
|
| 30 | if (!pas) {
|
| 31 | rc = SKB_NOT_DROPPED_YET;
|
| 32 | goto out_unlock;
|
| 33 | }
|
| 34 |
|
| 35 | if (!skb_transport_header_was_set(skb)) {
|
| 36 | rc = SKB_DROP_REASON_PSP_OUTPUT;
|
| 37 | goto out_unlock;
|
| 38 | }
|
| 39 |
|
| 40 | ptr = psp_assoc_drv_data(pas);
|
| 41 | if (*ptr != ns) {
|
| 42 | rc = SKB_DROP_REASON_PSP_OUTPUT;
|
| 43 | goto out_unlock;
|
| 44 | }
|
| 45 |
|
| 46 | net = sock_net(skb->sk);
|
| 47 | if (!psp_dev_encapsulate(net, skb, pas->tx.spi, pas->version, 0)) {
|
| 48 | rc = SKB_DROP_REASON_PSP_OUTPUT;
|
| 49 | goto out_unlock;
|
| 50 | }
|
| 51 |
|
| 52 | psp_len = skb->len - skb_inner_transport_offset(skb);
|
| 53 | atomic64_inc(&ns->psp.tx_packets);
|
| 54 | atomic64_add(psp_len, &ns->psp.tx_bytes);
|
| 55 |
|
| 56 |
|
| 57 | peer_psd = rcu_dereference(peer_ns->psp.dev);
|
| 58 | if (peer_psd && peer_psd->config.versions & (1 << pas->version)) {
|
| 59 | bool strip_icv = false;
|
| 60 | u8 generation;
|
| 61 |
|
| 62 | |
| 63 | * In real life if generation was too old, then decryption would
|
| 64 | * fail. Here, we just make it so a bad key causes a bad
|
| 65 | * generation too, and psp_sk_rx_policy_check() will fail.
|
| 66 |
|
| 67 | generation = pas->tx.key[0];
|
| 68 |
|
| 69 | skb_ext_reset(skb);
|
| 70 | skb->mac_len = ETH_HLEN;
|
| 71 | if (psp_dev_rcv(skb, peer_psd->id, generation, strip_icv)) {
|
| 72 | rc = SKB_DROP_REASON_PSP_OUTPUT;
|
| 73 | goto out_unlock;
|
| 74 | }
|
| 75 |
|
| 76 | *psp_ext = skb->extensions;
|
| 77 | refcount_inc(&(*psp_ext)->refcnt);
|
| 78 | skb->decrypted = 1;
|
| 79 |
|
| 80 | atomic64_inc(&peer_ns->psp.rx_packets);
|
| 81 | atomic64_add(psp_len, &peer_ns->psp.rx_bytes);
|
| 82 | } else {
|
| 83 | struct ipv6hdr *ip6h __maybe_unused;
|
| 84 | struct iphdr *iph;
|
| 85 | struct udphdr *uh;
|
| 86 | __wsum csum;
|
| 87 | int udplen;
|
| 88 |
|
| 89 | |
| 90 | * headers still there as if this is a normal udp packet.
|
| 91 | * psp_dev_encapsulate() sets udp checksum to 0, so we need to
|
| 92 | * provide a valid checksum here, so the skb isn't dropped.
|
| 93 |
|
| 94 | uh = udp_hdr(skb);
|
| 95 | udplen = udp_get_len(skb, uh, skb_transport_offset(skb));
|
| 96 | csum = skb_checksum(skb, skb_transport_offset(skb),
|
| 97 | udplen, 0);
|
| 98 |
|
| 99 | switch (skb->protocol) {
|
| 100 | case htons(ETH_P_IP):
|
| 101 | iph = ip_hdr(skb);
|
| 102 | uh->check = udp_v4_check(udplen, iph->saddr,
|
| 103 | iph->daddr, csum);
|
| 104 | break;
|
| 105 | #if IS_ENABLED(CONFIG_IPV6)
|
| 106 | case htons(ETH_P_IPV6):
|
| 107 | ip6h = ipv6_hdr(skb);
|
| 108 | uh->check = udp_v6_check(udplen, &ip6h->saddr,
|
| 109 | &ip6h->daddr, csum);
|
| 110 | break;
|
| 111 | #endif
|
| 112 | }
|
| 113 |
|
| 114 | uh->check = uh->check ?: CSUM_MANGLED_0;
|
| 115 | skb->ip_summed = CHECKSUM_NONE;
|
| 116 | }
|
| 117 |
|
| 118 | out_unlock:
|
| 119 | rcu_read_unlock();
|
| 120 | return rc;
|
| 121 | }
|
| 122 |
|
| 123 | static int
|
| 124 | nsim_psp_set_config(struct psp_dev *psd, struct psp_dev_config *conf,
|
| 125 | struct netlink_ext_ack *extack)
|
| 126 | {
|
| 127 | return 0;
|
| 128 | }
|
| 129 |
|
| 130 | static int
|
| 131 | nsim_rx_spi_alloc(struct psp_dev *psd, u32 version,
|
| 132 | struct psp_key_parsed *assoc,
|
| 133 | struct netlink_ext_ack *extack)
|
| 134 | {
|
| 135 | struct netdevsim *ns = psd->drv_priv;
|
| 136 | int i;
|
| 137 |
|
| 138 |
|
| 139 | if ((ns->psp.spi & PSP_SPI_KEY_ID) == PSP_SPI_KEY_ID) {
|
| 140 | NL_SET_ERR_MSG(extack, "SPI space exhausted");
|
| 141 | return -ENOSPC;
|
| 142 | }
|
| 143 |
|
| 144 | assoc->spi = cpu_to_be32(++ns->psp.spi);
|
| 145 | assoc->key[0] = psd->generation;
|
| 146 | for (i = 1; i < PSP_MAX_KEY; i++)
|
| 147 | assoc->key[i] = ns->psp.spi + i;
|
| 148 |
|
| 149 | return 0;
|
| 150 | }
|
| 151 |
|
| 152 | static int nsim_assoc_add(struct psp_dev *psd, struct psp_assoc *pas,
|
| 153 | struct netlink_ext_ack *extack)
|
| 154 | {
|
| 155 | struct netdevsim *ns = psd->drv_priv;
|
| 156 | void **ptr = psp_assoc_drv_data(pas);
|
| 157 |
|
| 158 |
|
| 159 | *ptr = psd->drv_priv;
|
| 160 | ns->psp.assoc_cnt++;
|
| 161 |
|
| 162 | return 0;
|
| 163 | }
|
| 164 |
|
| 165 | static int nsim_key_rotate(struct psp_dev *psd, struct netlink_ext_ack *extack)
|
| 166 | {
|
| 167 | struct netdevsim *ns = psd->drv_priv;
|
| 168 |
|
| 169 | |
| 170 | * (will be pre-incremented, as 0 is an invalid SPI).
|
| 171 |
|
| 172 | if (ns->psp.spi & PSP_SPI_KEY_PHASE)
|
| 173 | ns->psp.spi = 0;
|
| 174 | else
|
| 175 | ns->psp.spi = PSP_SPI_KEY_PHASE;
|
| 176 |
|
| 177 | return 0;
|
| 178 | }
|
| 179 |
|
| 180 | static void nsim_assoc_del(struct psp_dev *psd, struct psp_assoc *pas)
|
| 181 | {
|
| 182 | struct netdevsim *ns = psd->drv_priv;
|
| 183 | void **ptr = psp_assoc_drv_data(pas);
|
| 184 |
|
| 185 | *ptr = NULL;
|
| 186 | ns->psp.assoc_cnt--;
|
| 187 | }
|
| 188 |
|
| 189 | static void nsim_get_stats(struct psp_dev *psd, struct psp_dev_stats *stats)
|
| 190 | {
|
| 191 | struct netdevsim *ns = psd->drv_priv;
|
| 192 |
|
| 193 | |
| 194 | * All required stats must be reported by the device!
|
| 195 |
|
| 196 | memset(stats, 0, sizeof(struct psp_dev_stats));
|
| 197 |
|
| 198 | stats->rx_bytes = atomic64_read(&ns->psp.rx_bytes);
|
| 199 | stats->rx_packets = atomic64_read(&ns->psp.rx_packets);
|
| 200 | stats->tx_bytes = atomic64_read(&ns->psp.tx_bytes);
|
| 201 | stats->tx_packets = atomic64_read(&ns->psp.tx_packets);
|
| 202 | }
|
| 203 |
|
| 204 | static struct psp_dev_ops nsim_psp_ops = {
|
| 205 | .set_config = nsim_psp_set_config,
|
| 206 | .rx_spi_alloc = nsim_rx_spi_alloc,
|
| 207 | .tx_key_add = nsim_assoc_add,
|
| 208 | .tx_key_del = nsim_assoc_del,
|
| 209 | .key_rotate = nsim_key_rotate,
|
| 210 | .get_stats = nsim_get_stats,
|
| 211 | };
|
| 212 |
|
| 213 | static struct psp_dev_caps nsim_psp_caps = {
|
| 214 | .versions = 1 << PSP_VERSION_HDR0_AES_GCM_128 |
|
| 215 | 1 << PSP_VERSION_HDR0_AES_GMAC_128 |
|
| 216 | 1 << PSP_VERSION_HDR0_AES_GCM_256 |
|
| 217 | 1 << PSP_VERSION_HDR0_AES_GMAC_256,
|
| 218 | .assoc_drv_spc = sizeof(void *),
|
| 219 | };
|
| 220 |
|
| 221 | static void __nsim_psp_uninit(struct netdevsim *ns, bool teardown)
|
| 222 | {
|
| 223 | struct psp_dev *psd;
|
| 224 |
|
| 225 | psd = rcu_dereference_protected(ns->psp.dev,
|
| 226 | teardown ||
|
| 227 | lockdep_is_held(&ns->psp.rereg_lock));
|
| 228 | if (psd) {
|
| 229 | rcu_assign_pointer(ns->psp.dev, NULL);
|
| 230 | synchronize_rcu();
|
| 231 | psp_dev_unregister(psd);
|
| 232 | }
|
| 233 | WARN_ON(ns->psp.assoc_cnt);
|
| 234 | }
|
| 235 |
|
| 236 | void nsim_psp_uninit(struct netdevsim *ns)
|
| 237 | {
|
| 238 | debugfs_remove(ns->psp.rereg);
|
| 239 | mutex_destroy(&ns->psp.rereg_lock);
|
| 240 | __nsim_psp_uninit(ns, true);
|
| 241 | }
|
| 242 |
|
| 243 | static ssize_t
|
| 244 | nsim_psp_rereg_write(struct file *file, const char __user *data, size_t count,
|
| 245 | loff_t *ppos)
|
| 246 | {
|
| 247 | struct netdevsim *ns = file->private_data;
|
| 248 | struct psp_dev *psd;
|
| 249 | ssize_t ret;
|
| 250 |
|
| 251 | mutex_lock(&ns->psp.rereg_lock);
|
| 252 | __nsim_psp_uninit(ns, false);
|
| 253 |
|
| 254 | psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns);
|
| 255 | if (IS_ERR(psd)) {
|
| 256 | ret = PTR_ERR(psd);
|
| 257 | goto out;
|
| 258 | }
|
| 259 |
|
| 260 | rcu_assign_pointer(ns->psp.dev, psd);
|
| 261 | ret = count;
|
| 262 | out:
|
| 263 | mutex_unlock(&ns->psp.rereg_lock);
|
| 264 | return ret;
|
| 265 | }
|
| 266 |
|
| 267 | static const struct file_operations nsim_psp_rereg_fops = {
|
| 268 | .open = simple_open,
|
| 269 | .write = nsim_psp_rereg_write,
|
| 270 | .llseek = generic_file_llseek,
|
| 271 | .owner = THIS_MODULE,
|
| 272 | };
|
| 273 |
|
| 274 | int nsim_psp_init(struct netdevsim *ns)
|
| 275 | {
|
| 276 | struct dentry *ddir = ns->nsim_dev_port->ddir;
|
| 277 | struct psp_dev *psd;
|
| 278 |
|
| 279 | psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns);
|
| 280 | if (IS_ERR(psd))
|
| 281 | return PTR_ERR(psd);
|
| 282 |
|
| 283 | rcu_assign_pointer(ns->psp.dev, psd);
|
| 284 |
|
| 285 | mutex_init(&ns->psp.rereg_lock);
|
| 286 | ns->psp.rereg = debugfs_create_file("psp_rereg", 0200, ddir, ns,
|
| 287 | &nsim_psp_rereg_fops);
|
| 288 | return 0;
|
| 289 | }
|