1*7699353dSShannon Nelson // SPDX-License-Identifier: GPL-2.0 2*7699353dSShannon Nelson /* Copyright(c) 2018 Oracle and/or its affiliates. All rights reserved. */ 3*7699353dSShannon Nelson 4*7699353dSShannon Nelson #include <crypto/aead.h> 5*7699353dSShannon Nelson #include <linux/debugfs.h> 6*7699353dSShannon Nelson #include <net/xfrm.h> 7*7699353dSShannon Nelson 8*7699353dSShannon Nelson #include "netdevsim.h" 9*7699353dSShannon Nelson 10*7699353dSShannon Nelson #define NSIM_IPSEC_AUTH_BITS 128 11*7699353dSShannon Nelson 12*7699353dSShannon Nelson static ssize_t nsim_dbg_netdev_ops_read(struct file *filp, 13*7699353dSShannon Nelson char __user *buffer, 14*7699353dSShannon Nelson size_t count, loff_t *ppos) 15*7699353dSShannon Nelson { 16*7699353dSShannon Nelson struct netdevsim *ns = filp->private_data; 17*7699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 18*7699353dSShannon Nelson size_t bufsize; 19*7699353dSShannon Nelson char *buf, *p; 20*7699353dSShannon Nelson int len; 21*7699353dSShannon Nelson int i; 22*7699353dSShannon Nelson 23*7699353dSShannon Nelson /* the buffer needed is 24*7699353dSShannon Nelson * (num SAs * 3 lines each * ~60 bytes per line) + one more line 25*7699353dSShannon Nelson */ 26*7699353dSShannon Nelson bufsize = (ipsec->count * 4 * 60) + 60; 27*7699353dSShannon Nelson buf = kzalloc(bufsize, GFP_KERNEL); 28*7699353dSShannon Nelson if (!buf) 29*7699353dSShannon Nelson return -ENOMEM; 30*7699353dSShannon Nelson 31*7699353dSShannon Nelson p = buf; 32*7699353dSShannon Nelson p += snprintf(p, bufsize - (p - buf), 33*7699353dSShannon Nelson "SA count=%u tx=%u\n", 34*7699353dSShannon Nelson ipsec->count, ipsec->tx); 35*7699353dSShannon Nelson 36*7699353dSShannon Nelson for (i = 0; i < NSIM_IPSEC_MAX_SA_COUNT; i++) { 37*7699353dSShannon Nelson struct nsim_sa *sap = &ipsec->sa[i]; 38*7699353dSShannon Nelson 39*7699353dSShannon Nelson if (!sap->used) 40*7699353dSShannon Nelson continue; 41*7699353dSShannon Nelson 42*7699353dSShannon Nelson p += snprintf(p, bufsize - (p - buf), 43*7699353dSShannon Nelson "sa[%i] %cx ipaddr=0x%08x %08x %08x %08x\n", 44*7699353dSShannon Nelson i, (sap->rx ? 'r' : 't'), sap->ipaddr[0], 45*7699353dSShannon Nelson sap->ipaddr[1], sap->ipaddr[2], sap->ipaddr[3]); 46*7699353dSShannon Nelson p += snprintf(p, bufsize - (p - buf), 47*7699353dSShannon Nelson "sa[%i] spi=0x%08x proto=0x%x salt=0x%08x crypt=%d\n", 48*7699353dSShannon Nelson i, be32_to_cpu(sap->xs->id.spi), 49*7699353dSShannon Nelson sap->xs->id.proto, sap->salt, sap->crypt); 50*7699353dSShannon Nelson p += snprintf(p, bufsize - (p - buf), 51*7699353dSShannon Nelson "sa[%i] key=0x%08x %08x %08x %08x\n", 52*7699353dSShannon Nelson i, sap->key[0], sap->key[1], 53*7699353dSShannon Nelson sap->key[2], sap->key[3]); 54*7699353dSShannon Nelson } 55*7699353dSShannon Nelson 56*7699353dSShannon Nelson len = simple_read_from_buffer(buffer, count, ppos, buf, p - buf); 57*7699353dSShannon Nelson 58*7699353dSShannon Nelson kfree(buf); 59*7699353dSShannon Nelson return len; 60*7699353dSShannon Nelson } 61*7699353dSShannon Nelson 62*7699353dSShannon Nelson static const struct file_operations ipsec_dbg_fops = { 63*7699353dSShannon Nelson .owner = THIS_MODULE, 64*7699353dSShannon Nelson .open = simple_open, 65*7699353dSShannon Nelson .read = nsim_dbg_netdev_ops_read, 66*7699353dSShannon Nelson }; 67*7699353dSShannon Nelson 68*7699353dSShannon Nelson static int nsim_ipsec_find_empty_idx(struct nsim_ipsec *ipsec) 69*7699353dSShannon Nelson { 70*7699353dSShannon Nelson u32 i; 71*7699353dSShannon Nelson 72*7699353dSShannon Nelson if (ipsec->count == NSIM_IPSEC_MAX_SA_COUNT) 73*7699353dSShannon Nelson return -ENOSPC; 74*7699353dSShannon Nelson 75*7699353dSShannon Nelson /* search sa table */ 76*7699353dSShannon Nelson for (i = 0; i < NSIM_IPSEC_MAX_SA_COUNT; i++) { 77*7699353dSShannon Nelson if (!ipsec->sa[i].used) 78*7699353dSShannon Nelson return i; 79*7699353dSShannon Nelson } 80*7699353dSShannon Nelson 81*7699353dSShannon Nelson return -ENOSPC; 82*7699353dSShannon Nelson } 83*7699353dSShannon Nelson 84*7699353dSShannon Nelson static int nsim_ipsec_parse_proto_keys(struct xfrm_state *xs, 85*7699353dSShannon Nelson u32 *mykey, u32 *mysalt) 86*7699353dSShannon Nelson { 87*7699353dSShannon Nelson const char aes_gcm_name[] = "rfc4106(gcm(aes))"; 88*7699353dSShannon Nelson struct net_device *dev = xs->xso.dev; 89*7699353dSShannon Nelson unsigned char *key_data; 90*7699353dSShannon Nelson char *alg_name = NULL; 91*7699353dSShannon Nelson int key_len; 92*7699353dSShannon Nelson 93*7699353dSShannon Nelson if (!xs->aead) { 94*7699353dSShannon Nelson netdev_err(dev, "Unsupported IPsec algorithm\n"); 95*7699353dSShannon Nelson return -EINVAL; 96*7699353dSShannon Nelson } 97*7699353dSShannon Nelson 98*7699353dSShannon Nelson if (xs->aead->alg_icv_len != NSIM_IPSEC_AUTH_BITS) { 99*7699353dSShannon Nelson netdev_err(dev, "IPsec offload requires %d bit authentication\n", 100*7699353dSShannon Nelson NSIM_IPSEC_AUTH_BITS); 101*7699353dSShannon Nelson return -EINVAL; 102*7699353dSShannon Nelson } 103*7699353dSShannon Nelson 104*7699353dSShannon Nelson key_data = &xs->aead->alg_key[0]; 105*7699353dSShannon Nelson key_len = xs->aead->alg_key_len; 106*7699353dSShannon Nelson alg_name = xs->aead->alg_name; 107*7699353dSShannon Nelson 108*7699353dSShannon Nelson if (strcmp(alg_name, aes_gcm_name)) { 109*7699353dSShannon Nelson netdev_err(dev, "Unsupported IPsec algorithm - please use %s\n", 110*7699353dSShannon Nelson aes_gcm_name); 111*7699353dSShannon Nelson return -EINVAL; 112*7699353dSShannon Nelson } 113*7699353dSShannon Nelson 114*7699353dSShannon Nelson /* 160 accounts for 16 byte key and 4 byte salt */ 115*7699353dSShannon Nelson if (key_len > NSIM_IPSEC_AUTH_BITS) { 116*7699353dSShannon Nelson *mysalt = ((u32 *)key_data)[4]; 117*7699353dSShannon Nelson } else if (key_len == NSIM_IPSEC_AUTH_BITS) { 118*7699353dSShannon Nelson *mysalt = 0; 119*7699353dSShannon Nelson } else { 120*7699353dSShannon Nelson netdev_err(dev, "IPsec hw offload only supports 128 bit keys with optional 32 bit salt\n"); 121*7699353dSShannon Nelson return -EINVAL; 122*7699353dSShannon Nelson } 123*7699353dSShannon Nelson memcpy(mykey, key_data, 16); 124*7699353dSShannon Nelson 125*7699353dSShannon Nelson return 0; 126*7699353dSShannon Nelson } 127*7699353dSShannon Nelson 128*7699353dSShannon Nelson static int nsim_ipsec_add_sa(struct xfrm_state *xs) 129*7699353dSShannon Nelson { 130*7699353dSShannon Nelson struct nsim_ipsec *ipsec; 131*7699353dSShannon Nelson struct net_device *dev; 132*7699353dSShannon Nelson struct netdevsim *ns; 133*7699353dSShannon Nelson struct nsim_sa sa; 134*7699353dSShannon Nelson u16 sa_idx; 135*7699353dSShannon Nelson int ret; 136*7699353dSShannon Nelson 137*7699353dSShannon Nelson dev = xs->xso.dev; 138*7699353dSShannon Nelson ns = netdev_priv(dev); 139*7699353dSShannon Nelson ipsec = &ns->ipsec; 140*7699353dSShannon Nelson 141*7699353dSShannon Nelson if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) { 142*7699353dSShannon Nelson netdev_err(dev, "Unsupported protocol 0x%04x for ipsec offload\n", 143*7699353dSShannon Nelson xs->id.proto); 144*7699353dSShannon Nelson return -EINVAL; 145*7699353dSShannon Nelson } 146*7699353dSShannon Nelson 147*7699353dSShannon Nelson if (xs->calg) { 148*7699353dSShannon Nelson netdev_err(dev, "Compression offload not supported\n"); 149*7699353dSShannon Nelson return -EINVAL; 150*7699353dSShannon Nelson } 151*7699353dSShannon Nelson 152*7699353dSShannon Nelson /* find the first unused index */ 153*7699353dSShannon Nelson ret = nsim_ipsec_find_empty_idx(ipsec); 154*7699353dSShannon Nelson if (ret < 0) { 155*7699353dSShannon Nelson netdev_err(dev, "No space for SA in Rx table!\n"); 156*7699353dSShannon Nelson return ret; 157*7699353dSShannon Nelson } 158*7699353dSShannon Nelson sa_idx = (u16)ret; 159*7699353dSShannon Nelson 160*7699353dSShannon Nelson memset(&sa, 0, sizeof(sa)); 161*7699353dSShannon Nelson sa.used = true; 162*7699353dSShannon Nelson sa.xs = xs; 163*7699353dSShannon Nelson 164*7699353dSShannon Nelson if (sa.xs->id.proto & IPPROTO_ESP) 165*7699353dSShannon Nelson sa.crypt = xs->ealg || xs->aead; 166*7699353dSShannon Nelson 167*7699353dSShannon Nelson /* get the key and salt */ 168*7699353dSShannon Nelson ret = nsim_ipsec_parse_proto_keys(xs, sa.key, &sa.salt); 169*7699353dSShannon Nelson if (ret) { 170*7699353dSShannon Nelson netdev_err(dev, "Failed to get key data for SA table\n"); 171*7699353dSShannon Nelson return ret; 172*7699353dSShannon Nelson } 173*7699353dSShannon Nelson 174*7699353dSShannon Nelson if (xs->xso.flags & XFRM_OFFLOAD_INBOUND) { 175*7699353dSShannon Nelson sa.rx = true; 176*7699353dSShannon Nelson 177*7699353dSShannon Nelson if (xs->props.family == AF_INET6) 178*7699353dSShannon Nelson memcpy(sa.ipaddr, &xs->id.daddr.a6, 16); 179*7699353dSShannon Nelson else 180*7699353dSShannon Nelson memcpy(&sa.ipaddr[3], &xs->id.daddr.a4, 4); 181*7699353dSShannon Nelson } 182*7699353dSShannon Nelson 183*7699353dSShannon Nelson /* the preparations worked, so save the info */ 184*7699353dSShannon Nelson memcpy(&ipsec->sa[sa_idx], &sa, sizeof(sa)); 185*7699353dSShannon Nelson 186*7699353dSShannon Nelson /* the XFRM stack doesn't like offload_handle == 0, 187*7699353dSShannon Nelson * so add a bitflag in case our array index is 0 188*7699353dSShannon Nelson */ 189*7699353dSShannon Nelson xs->xso.offload_handle = sa_idx | NSIM_IPSEC_VALID; 190*7699353dSShannon Nelson ipsec->count++; 191*7699353dSShannon Nelson 192*7699353dSShannon Nelson return 0; 193*7699353dSShannon Nelson } 194*7699353dSShannon Nelson 195*7699353dSShannon Nelson static void nsim_ipsec_del_sa(struct xfrm_state *xs) 196*7699353dSShannon Nelson { 197*7699353dSShannon Nelson struct netdevsim *ns = netdev_priv(xs->xso.dev); 198*7699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 199*7699353dSShannon Nelson u16 sa_idx; 200*7699353dSShannon Nelson 201*7699353dSShannon Nelson sa_idx = xs->xso.offload_handle & ~NSIM_IPSEC_VALID; 202*7699353dSShannon Nelson if (!ipsec->sa[sa_idx].used) { 203*7699353dSShannon Nelson netdev_err(ns->netdev, "Invalid SA for delete sa_idx=%d\n", 204*7699353dSShannon Nelson sa_idx); 205*7699353dSShannon Nelson return; 206*7699353dSShannon Nelson } 207*7699353dSShannon Nelson 208*7699353dSShannon Nelson memset(&ipsec->sa[sa_idx], 0, sizeof(struct nsim_sa)); 209*7699353dSShannon Nelson ipsec->count--; 210*7699353dSShannon Nelson } 211*7699353dSShannon Nelson 212*7699353dSShannon Nelson static bool nsim_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs) 213*7699353dSShannon Nelson { 214*7699353dSShannon Nelson struct netdevsim *ns = netdev_priv(xs->xso.dev); 215*7699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 216*7699353dSShannon Nelson 217*7699353dSShannon Nelson ipsec->ok++; 218*7699353dSShannon Nelson 219*7699353dSShannon Nelson return true; 220*7699353dSShannon Nelson } 221*7699353dSShannon Nelson 222*7699353dSShannon Nelson static const struct xfrmdev_ops nsim_xfrmdev_ops = { 223*7699353dSShannon Nelson .xdo_dev_state_add = nsim_ipsec_add_sa, 224*7699353dSShannon Nelson .xdo_dev_state_delete = nsim_ipsec_del_sa, 225*7699353dSShannon Nelson .xdo_dev_offload_ok = nsim_ipsec_offload_ok, 226*7699353dSShannon Nelson }; 227*7699353dSShannon Nelson 228*7699353dSShannon Nelson bool nsim_ipsec_tx(struct netdevsim *ns, struct sk_buff *skb) 229*7699353dSShannon Nelson { 230*7699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 231*7699353dSShannon Nelson struct xfrm_state *xs; 232*7699353dSShannon Nelson struct nsim_sa *tsa; 233*7699353dSShannon Nelson u32 sa_idx; 234*7699353dSShannon Nelson 235*7699353dSShannon Nelson /* do we even need to check this packet? */ 236*7699353dSShannon Nelson if (!skb->sp) 237*7699353dSShannon Nelson return true; 238*7699353dSShannon Nelson 239*7699353dSShannon Nelson if (unlikely(!skb->sp->len)) { 240*7699353dSShannon Nelson netdev_err(ns->netdev, "no xfrm state len = %d\n", 241*7699353dSShannon Nelson skb->sp->len); 242*7699353dSShannon Nelson return false; 243*7699353dSShannon Nelson } 244*7699353dSShannon Nelson 245*7699353dSShannon Nelson xs = xfrm_input_state(skb); 246*7699353dSShannon Nelson if (unlikely(!xs)) { 247*7699353dSShannon Nelson netdev_err(ns->netdev, "no xfrm_input_state() xs = %p\n", xs); 248*7699353dSShannon Nelson return false; 249*7699353dSShannon Nelson } 250*7699353dSShannon Nelson 251*7699353dSShannon Nelson sa_idx = xs->xso.offload_handle & ~NSIM_IPSEC_VALID; 252*7699353dSShannon Nelson if (unlikely(sa_idx > NSIM_IPSEC_MAX_SA_COUNT)) { 253*7699353dSShannon Nelson netdev_err(ns->netdev, "bad sa_idx=%d max=%d\n", 254*7699353dSShannon Nelson sa_idx, NSIM_IPSEC_MAX_SA_COUNT); 255*7699353dSShannon Nelson return false; 256*7699353dSShannon Nelson } 257*7699353dSShannon Nelson 258*7699353dSShannon Nelson tsa = &ipsec->sa[sa_idx]; 259*7699353dSShannon Nelson if (unlikely(!tsa->used)) { 260*7699353dSShannon Nelson netdev_err(ns->netdev, "unused sa_idx=%d\n", sa_idx); 261*7699353dSShannon Nelson return false; 262*7699353dSShannon Nelson } 263*7699353dSShannon Nelson 264*7699353dSShannon Nelson if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) { 265*7699353dSShannon Nelson netdev_err(ns->netdev, "unexpected proto=%d\n", xs->id.proto); 266*7699353dSShannon Nelson return false; 267*7699353dSShannon Nelson } 268*7699353dSShannon Nelson 269*7699353dSShannon Nelson ipsec->tx++; 270*7699353dSShannon Nelson 271*7699353dSShannon Nelson return true; 272*7699353dSShannon Nelson } 273*7699353dSShannon Nelson 274*7699353dSShannon Nelson void nsim_ipsec_init(struct netdevsim *ns) 275*7699353dSShannon Nelson { 276*7699353dSShannon Nelson ns->netdev->xfrmdev_ops = &nsim_xfrmdev_ops; 277*7699353dSShannon Nelson 278*7699353dSShannon Nelson #define NSIM_ESP_FEATURES (NETIF_F_HW_ESP | \ 279*7699353dSShannon Nelson NETIF_F_HW_ESP_TX_CSUM | \ 280*7699353dSShannon Nelson NETIF_F_GSO_ESP) 281*7699353dSShannon Nelson 282*7699353dSShannon Nelson ns->netdev->features |= NSIM_ESP_FEATURES; 283*7699353dSShannon Nelson ns->netdev->hw_enc_features |= NSIM_ESP_FEATURES; 284*7699353dSShannon Nelson 285*7699353dSShannon Nelson ns->ipsec.pfile = debugfs_create_file("ipsec", 0400, ns->ddir, ns, 286*7699353dSShannon Nelson &ipsec_dbg_fops); 287*7699353dSShannon Nelson } 288*7699353dSShannon Nelson 289*7699353dSShannon Nelson void nsim_ipsec_teardown(struct netdevsim *ns) 290*7699353dSShannon Nelson { 291*7699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 292*7699353dSShannon Nelson 293*7699353dSShannon Nelson if (ipsec->count) 294*7699353dSShannon Nelson netdev_err(ns->netdev, "tearing down IPsec offload with %d SAs left\n", 295*7699353dSShannon Nelson ipsec->count); 296*7699353dSShannon Nelson debugfs_remove_recursive(ipsec->pfile); 297*7699353dSShannon Nelson } 298