17699353dSShannon Nelson // SPDX-License-Identifier: GPL-2.0 27699353dSShannon Nelson /* Copyright(c) 2018 Oracle and/or its affiliates. All rights reserved. */ 37699353dSShannon Nelson 47699353dSShannon Nelson #include <crypto/aead.h> 57699353dSShannon Nelson #include <linux/debugfs.h> 67699353dSShannon Nelson #include <net/xfrm.h> 77699353dSShannon Nelson 87699353dSShannon Nelson #include "netdevsim.h" 97699353dSShannon Nelson 107699353dSShannon Nelson #define NSIM_IPSEC_AUTH_BITS 128 117699353dSShannon Nelson 127699353dSShannon Nelson static ssize_t nsim_dbg_netdev_ops_read(struct file *filp, 137699353dSShannon Nelson char __user *buffer, 147699353dSShannon Nelson size_t count, loff_t *ppos) 157699353dSShannon Nelson { 167699353dSShannon Nelson struct netdevsim *ns = filp->private_data; 177699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 187699353dSShannon Nelson size_t bufsize; 197699353dSShannon Nelson char *buf, *p; 207699353dSShannon Nelson int len; 217699353dSShannon Nelson int i; 227699353dSShannon Nelson 237699353dSShannon Nelson /* the buffer needed is 247699353dSShannon Nelson * (num SAs * 3 lines each * ~60 bytes per line) + one more line 257699353dSShannon Nelson */ 267699353dSShannon Nelson bufsize = (ipsec->count * 4 * 60) + 60; 277699353dSShannon Nelson buf = kzalloc(bufsize, GFP_KERNEL); 287699353dSShannon Nelson if (!buf) 297699353dSShannon Nelson return -ENOMEM; 307699353dSShannon Nelson 317699353dSShannon Nelson p = buf; 322da222f6STakashi Iwai p += scnprintf(p, bufsize - (p - buf), 337699353dSShannon Nelson "SA count=%u tx=%u\n", 347699353dSShannon Nelson ipsec->count, ipsec->tx); 357699353dSShannon Nelson 367699353dSShannon Nelson for (i = 0; i < NSIM_IPSEC_MAX_SA_COUNT; i++) { 377699353dSShannon Nelson struct nsim_sa *sap = &ipsec->sa[i]; 387699353dSShannon Nelson 397699353dSShannon Nelson if (!sap->used) 407699353dSShannon Nelson continue; 417699353dSShannon Nelson 422da222f6STakashi Iwai p += scnprintf(p, bufsize - (p - buf), 437699353dSShannon Nelson "sa[%i] %cx ipaddr=0x%08x %08x %08x %08x\n", 447699353dSShannon Nelson i, (sap->rx ? 'r' : 't'), sap->ipaddr[0], 457699353dSShannon Nelson sap->ipaddr[1], sap->ipaddr[2], sap->ipaddr[3]); 462da222f6STakashi Iwai p += scnprintf(p, bufsize - (p - buf), 477699353dSShannon Nelson "sa[%i] spi=0x%08x proto=0x%x salt=0x%08x crypt=%d\n", 487699353dSShannon Nelson i, be32_to_cpu(sap->xs->id.spi), 497699353dSShannon Nelson sap->xs->id.proto, sap->salt, sap->crypt); 502da222f6STakashi Iwai p += scnprintf(p, bufsize - (p - buf), 517699353dSShannon Nelson "sa[%i] key=0x%08x %08x %08x %08x\n", 527699353dSShannon Nelson i, sap->key[0], sap->key[1], 537699353dSShannon Nelson sap->key[2], sap->key[3]); 547699353dSShannon Nelson } 557699353dSShannon Nelson 567699353dSShannon Nelson len = simple_read_from_buffer(buffer, count, ppos, buf, p - buf); 577699353dSShannon Nelson 587699353dSShannon Nelson kfree(buf); 597699353dSShannon Nelson return len; 607699353dSShannon Nelson } 617699353dSShannon Nelson 627699353dSShannon Nelson static const struct file_operations ipsec_dbg_fops = { 637699353dSShannon Nelson .owner = THIS_MODULE, 647699353dSShannon Nelson .open = simple_open, 657699353dSShannon Nelson .read = nsim_dbg_netdev_ops_read, 667699353dSShannon Nelson }; 677699353dSShannon Nelson 687699353dSShannon Nelson static int nsim_ipsec_find_empty_idx(struct nsim_ipsec *ipsec) 697699353dSShannon Nelson { 707699353dSShannon Nelson u32 i; 717699353dSShannon Nelson 727699353dSShannon Nelson if (ipsec->count == NSIM_IPSEC_MAX_SA_COUNT) 737699353dSShannon Nelson return -ENOSPC; 747699353dSShannon Nelson 757699353dSShannon Nelson /* search sa table */ 767699353dSShannon Nelson for (i = 0; i < NSIM_IPSEC_MAX_SA_COUNT; i++) { 777699353dSShannon Nelson if (!ipsec->sa[i].used) 787699353dSShannon Nelson return i; 797699353dSShannon Nelson } 807699353dSShannon Nelson 817699353dSShannon Nelson return -ENOSPC; 827699353dSShannon Nelson } 837699353dSShannon Nelson 847699353dSShannon Nelson static int nsim_ipsec_parse_proto_keys(struct xfrm_state *xs, 857699353dSShannon Nelson u32 *mykey, u32 *mysalt) 867699353dSShannon Nelson { 877699353dSShannon Nelson const char aes_gcm_name[] = "rfc4106(gcm(aes))"; 88*09adf756STaehee Yoo struct net_device *dev = xs->xso.real_dev; 897699353dSShannon Nelson unsigned char *key_data; 907699353dSShannon Nelson char *alg_name = NULL; 917699353dSShannon Nelson int key_len; 927699353dSShannon Nelson 937699353dSShannon Nelson if (!xs->aead) { 947699353dSShannon Nelson netdev_err(dev, "Unsupported IPsec algorithm\n"); 957699353dSShannon Nelson return -EINVAL; 967699353dSShannon Nelson } 977699353dSShannon Nelson 987699353dSShannon Nelson if (xs->aead->alg_icv_len != NSIM_IPSEC_AUTH_BITS) { 997699353dSShannon Nelson netdev_err(dev, "IPsec offload requires %d bit authentication\n", 1007699353dSShannon Nelson NSIM_IPSEC_AUTH_BITS); 1017699353dSShannon Nelson return -EINVAL; 1027699353dSShannon Nelson } 1037699353dSShannon Nelson 1047699353dSShannon Nelson key_data = &xs->aead->alg_key[0]; 1057699353dSShannon Nelson key_len = xs->aead->alg_key_len; 1067699353dSShannon Nelson alg_name = xs->aead->alg_name; 1077699353dSShannon Nelson 1087699353dSShannon Nelson if (strcmp(alg_name, aes_gcm_name)) { 1097699353dSShannon Nelson netdev_err(dev, "Unsupported IPsec algorithm - please use %s\n", 1107699353dSShannon Nelson aes_gcm_name); 1117699353dSShannon Nelson return -EINVAL; 1127699353dSShannon Nelson } 1137699353dSShannon Nelson 1147699353dSShannon Nelson /* 160 accounts for 16 byte key and 4 byte salt */ 1157699353dSShannon Nelson if (key_len > NSIM_IPSEC_AUTH_BITS) { 1167699353dSShannon Nelson *mysalt = ((u32 *)key_data)[4]; 1177699353dSShannon Nelson } else if (key_len == NSIM_IPSEC_AUTH_BITS) { 1187699353dSShannon Nelson *mysalt = 0; 1197699353dSShannon Nelson } else { 1207699353dSShannon Nelson netdev_err(dev, "IPsec hw offload only supports 128 bit keys with optional 32 bit salt\n"); 1217699353dSShannon Nelson return -EINVAL; 1227699353dSShannon Nelson } 1237699353dSShannon Nelson memcpy(mykey, key_data, 16); 1247699353dSShannon Nelson 1257699353dSShannon Nelson return 0; 1267699353dSShannon Nelson } 1277699353dSShannon Nelson 1287699353dSShannon Nelson static int nsim_ipsec_add_sa(struct xfrm_state *xs) 1297699353dSShannon Nelson { 1307699353dSShannon Nelson struct nsim_ipsec *ipsec; 1317699353dSShannon Nelson struct net_device *dev; 1327699353dSShannon Nelson struct netdevsim *ns; 1337699353dSShannon Nelson struct nsim_sa sa; 1347699353dSShannon Nelson u16 sa_idx; 1357699353dSShannon Nelson int ret; 1367699353dSShannon Nelson 137*09adf756STaehee Yoo dev = xs->xso.real_dev; 1387699353dSShannon Nelson ns = netdev_priv(dev); 1397699353dSShannon Nelson ipsec = &ns->ipsec; 1407699353dSShannon Nelson 1417699353dSShannon Nelson if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) { 1427699353dSShannon Nelson netdev_err(dev, "Unsupported protocol 0x%04x for ipsec offload\n", 1437699353dSShannon Nelson xs->id.proto); 1447699353dSShannon Nelson return -EINVAL; 1457699353dSShannon Nelson } 1467699353dSShannon Nelson 1477699353dSShannon Nelson if (xs->calg) { 1487699353dSShannon Nelson netdev_err(dev, "Compression offload not supported\n"); 1497699353dSShannon Nelson return -EINVAL; 1507699353dSShannon Nelson } 1517699353dSShannon Nelson 1527699353dSShannon Nelson /* find the first unused index */ 1537699353dSShannon Nelson ret = nsim_ipsec_find_empty_idx(ipsec); 1547699353dSShannon Nelson if (ret < 0) { 1557699353dSShannon Nelson netdev_err(dev, "No space for SA in Rx table!\n"); 1567699353dSShannon Nelson return ret; 1577699353dSShannon Nelson } 1587699353dSShannon Nelson sa_idx = (u16)ret; 1597699353dSShannon Nelson 1607699353dSShannon Nelson memset(&sa, 0, sizeof(sa)); 1617699353dSShannon Nelson sa.used = true; 1627699353dSShannon Nelson sa.xs = xs; 1637699353dSShannon Nelson 1647699353dSShannon Nelson if (sa.xs->id.proto & IPPROTO_ESP) 1657699353dSShannon Nelson sa.crypt = xs->ealg || xs->aead; 1667699353dSShannon Nelson 1677699353dSShannon Nelson /* get the key and salt */ 1687699353dSShannon Nelson ret = nsim_ipsec_parse_proto_keys(xs, sa.key, &sa.salt); 1697699353dSShannon Nelson if (ret) { 1707699353dSShannon Nelson netdev_err(dev, "Failed to get key data for SA table\n"); 1717699353dSShannon Nelson return ret; 1727699353dSShannon Nelson } 1737699353dSShannon Nelson 1747699353dSShannon Nelson if (xs->xso.flags & XFRM_OFFLOAD_INBOUND) { 1757699353dSShannon Nelson sa.rx = true; 1767699353dSShannon Nelson 1777699353dSShannon Nelson if (xs->props.family == AF_INET6) 1787699353dSShannon Nelson memcpy(sa.ipaddr, &xs->id.daddr.a6, 16); 1797699353dSShannon Nelson else 1807699353dSShannon Nelson memcpy(&sa.ipaddr[3], &xs->id.daddr.a4, 4); 1817699353dSShannon Nelson } 1827699353dSShannon Nelson 1837699353dSShannon Nelson /* the preparations worked, so save the info */ 1847699353dSShannon Nelson memcpy(&ipsec->sa[sa_idx], &sa, sizeof(sa)); 1857699353dSShannon Nelson 1867699353dSShannon Nelson /* the XFRM stack doesn't like offload_handle == 0, 1877699353dSShannon Nelson * so add a bitflag in case our array index is 0 1887699353dSShannon Nelson */ 1897699353dSShannon Nelson xs->xso.offload_handle = sa_idx | NSIM_IPSEC_VALID; 1907699353dSShannon Nelson ipsec->count++; 1917699353dSShannon Nelson 1927699353dSShannon Nelson return 0; 1937699353dSShannon Nelson } 1947699353dSShannon Nelson 1957699353dSShannon Nelson static void nsim_ipsec_del_sa(struct xfrm_state *xs) 1967699353dSShannon Nelson { 197*09adf756STaehee Yoo struct netdevsim *ns = netdev_priv(xs->xso.real_dev); 1987699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 1997699353dSShannon Nelson u16 sa_idx; 2007699353dSShannon Nelson 2017699353dSShannon Nelson sa_idx = xs->xso.offload_handle & ~NSIM_IPSEC_VALID; 2027699353dSShannon Nelson if (!ipsec->sa[sa_idx].used) { 2037699353dSShannon Nelson netdev_err(ns->netdev, "Invalid SA for delete sa_idx=%d\n", 2047699353dSShannon Nelson sa_idx); 2057699353dSShannon Nelson return; 2067699353dSShannon Nelson } 2077699353dSShannon Nelson 2087699353dSShannon Nelson memset(&ipsec->sa[sa_idx], 0, sizeof(struct nsim_sa)); 2097699353dSShannon Nelson ipsec->count--; 2107699353dSShannon Nelson } 2117699353dSShannon Nelson 2127699353dSShannon Nelson static bool nsim_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs) 2137699353dSShannon Nelson { 214*09adf756STaehee Yoo struct netdevsim *ns = netdev_priv(xs->xso.real_dev); 2157699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 2167699353dSShannon Nelson 2177699353dSShannon Nelson ipsec->ok++; 2187699353dSShannon Nelson 2197699353dSShannon Nelson return true; 2207699353dSShannon Nelson } 2217699353dSShannon Nelson 2227699353dSShannon Nelson static const struct xfrmdev_ops nsim_xfrmdev_ops = { 2237699353dSShannon Nelson .xdo_dev_state_add = nsim_ipsec_add_sa, 2247699353dSShannon Nelson .xdo_dev_state_delete = nsim_ipsec_del_sa, 2257699353dSShannon Nelson .xdo_dev_offload_ok = nsim_ipsec_offload_ok, 2267699353dSShannon Nelson }; 2277699353dSShannon Nelson 2287699353dSShannon Nelson bool nsim_ipsec_tx(struct netdevsim *ns, struct sk_buff *skb) 2297699353dSShannon Nelson { 23056d1ac32SFlorian Westphal struct sec_path *sp = skb_sec_path(skb); 2317699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 2327699353dSShannon Nelson struct xfrm_state *xs; 2337699353dSShannon Nelson struct nsim_sa *tsa; 2347699353dSShannon Nelson u32 sa_idx; 2357699353dSShannon Nelson 2367699353dSShannon Nelson /* do we even need to check this packet? */ 23756d1ac32SFlorian Westphal if (!sp) 2387699353dSShannon Nelson return true; 2397699353dSShannon Nelson 24056d1ac32SFlorian Westphal if (unlikely(!sp->len)) { 2417699353dSShannon Nelson netdev_err(ns->netdev, "no xfrm state len = %d\n", 24256d1ac32SFlorian Westphal sp->len); 2437699353dSShannon Nelson return false; 2447699353dSShannon Nelson } 2457699353dSShannon Nelson 2467699353dSShannon Nelson xs = xfrm_input_state(skb); 2477699353dSShannon Nelson if (unlikely(!xs)) { 2487699353dSShannon Nelson netdev_err(ns->netdev, "no xfrm_input_state() xs = %p\n", xs); 2497699353dSShannon Nelson return false; 2507699353dSShannon Nelson } 2517699353dSShannon Nelson 2527699353dSShannon Nelson sa_idx = xs->xso.offload_handle & ~NSIM_IPSEC_VALID; 253c02462d8SColin Ian King if (unlikely(sa_idx >= NSIM_IPSEC_MAX_SA_COUNT)) { 2547699353dSShannon Nelson netdev_err(ns->netdev, "bad sa_idx=%d max=%d\n", 2557699353dSShannon Nelson sa_idx, NSIM_IPSEC_MAX_SA_COUNT); 2567699353dSShannon Nelson return false; 2577699353dSShannon Nelson } 2587699353dSShannon Nelson 2597699353dSShannon Nelson tsa = &ipsec->sa[sa_idx]; 2607699353dSShannon Nelson if (unlikely(!tsa->used)) { 2617699353dSShannon Nelson netdev_err(ns->netdev, "unused sa_idx=%d\n", sa_idx); 2627699353dSShannon Nelson return false; 2637699353dSShannon Nelson } 2647699353dSShannon Nelson 2657699353dSShannon Nelson if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) { 2667699353dSShannon Nelson netdev_err(ns->netdev, "unexpected proto=%d\n", xs->id.proto); 2677699353dSShannon Nelson return false; 2687699353dSShannon Nelson } 2697699353dSShannon Nelson 2707699353dSShannon Nelson ipsec->tx++; 2717699353dSShannon Nelson 2727699353dSShannon Nelson return true; 2737699353dSShannon Nelson } 2747699353dSShannon Nelson 2757699353dSShannon Nelson void nsim_ipsec_init(struct netdevsim *ns) 2767699353dSShannon Nelson { 2777699353dSShannon Nelson ns->netdev->xfrmdev_ops = &nsim_xfrmdev_ops; 2787699353dSShannon Nelson 2797699353dSShannon Nelson #define NSIM_ESP_FEATURES (NETIF_F_HW_ESP | \ 2807699353dSShannon Nelson NETIF_F_HW_ESP_TX_CSUM | \ 2817699353dSShannon Nelson NETIF_F_GSO_ESP) 2827699353dSShannon Nelson 2837699353dSShannon Nelson ns->netdev->features |= NSIM_ESP_FEATURES; 2847699353dSShannon Nelson ns->netdev->hw_enc_features |= NSIM_ESP_FEATURES; 2857699353dSShannon Nelson 286e05b2d14SJiri Pirko ns->ipsec.pfile = debugfs_create_file("ipsec", 0400, 287e05b2d14SJiri Pirko ns->nsim_dev_port->ddir, ns, 2887699353dSShannon Nelson &ipsec_dbg_fops); 2897699353dSShannon Nelson } 2907699353dSShannon Nelson 2917699353dSShannon Nelson void nsim_ipsec_teardown(struct netdevsim *ns) 2927699353dSShannon Nelson { 2937699353dSShannon Nelson struct nsim_ipsec *ipsec = &ns->ipsec; 2947699353dSShannon Nelson 2957699353dSShannon Nelson if (ipsec->count) 2967699353dSShannon Nelson netdev_err(ns->netdev, "tearing down IPsec offload with %d SAs left\n", 2977699353dSShannon Nelson ipsec->count); 2987699353dSShannon Nelson debugfs_remove_recursive(ipsec->pfile); 2997699353dSShannon Nelson } 300