diff options
author | Mark Brown <broonie@kernel.org> | 2016-11-04 12:16:38 -0600 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-11-04 12:16:38 -0600 |
commit | cc9b94029e9ef51787af908e9856b1eed314bc00 (patch) | |
tree | 9675310b89d0f6fb1f7bd9423f0638c4ee5226fd /drivers/net/macsec.c | |
parent | 13bed58ce8748d430a26e353a09b89f9d613a71f (diff) | |
parent | 1b5b42216469b05ef4b5916cb40b127dfab1da88 (diff) |
Merge branch 'topic/error' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator into regulator-fixed
Diffstat (limited to 'drivers/net/macsec.c')
-rw-r--r-- | drivers/net/macsec.c | 230 |
1 files changed, 174 insertions, 56 deletions
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 47ee2c840b55..3ea47f28e143 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -18,6 +18,7 @@ #include <linux/rtnetlink.h> #include <net/genetlink.h> #include <net/sock.h> +#include <net/gro_cells.h> #include <uapi/linux/if_macsec.h> @@ -268,6 +269,8 @@ struct macsec_dev { struct net_device *real_dev; struct pcpu_secy_stats __percpu *stats; struct list_head secys; + struct gro_cells gro_cells; + unsigned int nest_level; }; /** @@ -342,7 +345,6 @@ static void free_rxsa(struct rcu_head *head) crypto_free_aead(sa->key.tfm); free_percpu(sa->stats); - macsec_rxsc_put(sa->sc); kfree(sa); } @@ -508,7 +510,7 @@ static bool macsec_validate_skb(struct sk_buff *skb, u16 icv_len) } #define MACSEC_NEEDED_HEADROOM (macsec_extra_len(true)) -#define MACSEC_NEEDED_TAILROOM MACSEC_MAX_ICV_LEN +#define MACSEC_NEEDED_TAILROOM MACSEC_STD_ICV_LEN static void macsec_fill_iv(unsigned char *iv, sci_t sci, u32 pn) { @@ -605,12 +607,41 @@ static void macsec_encrypt_done(struct crypto_async_request *base, int err) dev_put(dev); } +static struct aead_request *macsec_alloc_req(struct crypto_aead *tfm, + unsigned char **iv, + struct scatterlist **sg) +{ + size_t size, iv_offset, sg_offset; + struct aead_request *req; + void *tmp; + + size = sizeof(struct aead_request) + crypto_aead_reqsize(tfm); + iv_offset = size; + size += GCM_AES_IV_LEN; + + size = ALIGN(size, __alignof__(struct scatterlist)); + sg_offset = size; + size += sizeof(struct scatterlist) * (MAX_SKB_FRAGS + 1); + + tmp = kmalloc(size, GFP_ATOMIC); + if (!tmp) + return NULL; + + *iv = (unsigned char *)(tmp + iv_offset); + *sg = (struct scatterlist *)(tmp + sg_offset); + req = tmp; + + aead_request_set_tfm(req, tfm); + + return req; +} + static struct sk_buff *macsec_encrypt(struct sk_buff *skb, struct net_device *dev) { int ret; - struct scatterlist sg[MAX_SKB_FRAGS + 1]; - unsigned char iv[GCM_AES_IV_LEN]; + struct scatterlist *sg; + unsigned char *iv; struct ethhdr *eth; struct macsec_eth_header *hh; size_t unprotected_len; @@ -668,8 +699,6 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, macsec_fill_sectag(hh, secy, pn); macsec_set_shortlen(hh, unprotected_len - 2 * ETH_ALEN); - macsec_fill_iv(iv, secy->sci, pn); - skb_put(skb, secy->icv_len); if (skb->len - ETH_HLEN > macsec_priv(dev)->real_dev->mtu) { @@ -684,13 +713,15 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, return ERR_PTR(-EINVAL); } - req = aead_request_alloc(tx_sa->key.tfm, GFP_ATOMIC); + req = macsec_alloc_req(tx_sa->key.tfm, &iv, &sg); if (!req) { macsec_txsa_put(tx_sa); kfree_skb(skb); return ERR_PTR(-ENOMEM); } + macsec_fill_iv(iv, secy->sci, pn); + sg_init_table(sg, MAX_SKB_FRAGS + 1); skb_to_sgvec(skb, sg, 0, skb->len); @@ -832,6 +863,7 @@ static void macsec_decrypt_done(struct crypto_async_request *base, int err) struct net_device *dev = skb->dev; struct macsec_dev *macsec = macsec_priv(dev); struct macsec_rx_sa *rx_sa = macsec_skb_cb(skb)->rx_sa; + struct macsec_rx_sc *rx_sc = rx_sa->sc; int len, ret; u32 pn; @@ -850,7 +882,7 @@ static void macsec_decrypt_done(struct crypto_async_request *base, int err) macsec_reset_skb(skb, macsec->secy.netdev); len = skb->len; - ret = netif_rx(skb); + ret = gro_cells_receive(&macsec->gro_cells, skb); if (ret == NET_RX_SUCCESS) count_rx(dev, len); else @@ -860,8 +892,8 @@ static void macsec_decrypt_done(struct crypto_async_request *base, int err) out: macsec_rxsa_put(rx_sa); + macsec_rxsc_put(rx_sc); dev_put(dev); - return; } static struct sk_buff *macsec_decrypt(struct sk_buff *skb, @@ -871,8 +903,8 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, struct macsec_secy *secy) { int ret; - struct scatterlist sg[MAX_SKB_FRAGS + 1]; - unsigned char iv[GCM_AES_IV_LEN]; + struct scatterlist *sg; + unsigned char *iv; struct aead_request *req; struct macsec_eth_header *hdr; u16 icv_len = secy->icv_len; @@ -882,7 +914,7 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, if (!skb) return ERR_PTR(-ENOMEM); - req = aead_request_alloc(rx_sa->key.tfm, GFP_ATOMIC); + req = macsec_alloc_req(rx_sa->key.tfm, &iv, &sg); if (!req) { kfree_skb(skb); return ERR_PTR(-ENOMEM); @@ -914,7 +946,6 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, } macsec_skb_cb(skb)->req = req; - macsec_skb_cb(skb)->rx_sa = rx_sa; skb->dev = dev; aead_request_set_callback(req, 0, macsec_decrypt_done, skb); @@ -1024,6 +1055,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) struct pcpu_rx_sc_stats *rxsc_stats; struct pcpu_secy_stats *secy_stats; bool pulled_sci; + int ret; if (skb_headroom(skb) < ETH_HLEN) goto drop_direct; @@ -1076,6 +1108,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) list_for_each_entry_rcu(macsec, &rxd->secys, secys) { struct macsec_rx_sc *sc = find_rx_sc(&macsec->secy, sci); + sc = sc ? macsec_rxsc_get(sc) : NULL; if (sc) { secy = &macsec->secy; @@ -1141,6 +1174,8 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) } } + macsec_skb_cb(skb)->rx_sa = rx_sa; + /* Disabled && !changed text => skip validation */ if (hdr->tci_an & MACSEC_TCI_C || secy->validate_frames != MACSEC_VALIDATE_DISABLED) @@ -1148,8 +1183,10 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) if (IS_ERR(skb)) { /* the decrypt callback needs the reference */ - if (PTR_ERR(skb) != -EINPROGRESS) + if (PTR_ERR(skb) != -EINPROGRESS) { macsec_rxsa_put(rx_sa); + macsec_rxsc_put(rx_sc); + } rcu_read_unlock(); *pskb = NULL; return RX_HANDLER_CONSUMED; @@ -1165,16 +1202,23 @@ deliver: if (rx_sa) macsec_rxsa_put(rx_sa); - count_rx(dev, skb->len); + macsec_rxsc_put(rx_sc); + + ret = gro_cells_receive(&macsec->gro_cells, skb); + if (ret == NET_RX_SUCCESS) + count_rx(dev, skb->len); + else + macsec->secy.netdev->stats.rx_dropped++; rcu_read_unlock(); - *pskb = skb; - return RX_HANDLER_ANOTHER; + *pskb = NULL; + return RX_HANDLER_CONSUMED; drop: macsec_rxsa_put(rx_sa); drop_nosa: + macsec_rxsc_put(rx_sc); rcu_read_unlock(); drop_direct: kfree_skb(skb); @@ -1190,7 +1234,6 @@ nosci: list_for_each_entry_rcu(macsec, &rxd->secys, secys) { struct sk_buff *nskb; - int ret; secy_stats = this_cpu_ptr(macsec->stats); @@ -1234,23 +1277,23 @@ static struct crypto_aead *macsec_alloc_tfm(char *key, int key_len, int icv_len) struct crypto_aead *tfm; int ret; - tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC); - if (!tfm || IS_ERR(tfm)) - return NULL; + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + + if (IS_ERR(tfm)) + return tfm; ret = crypto_aead_setkey(tfm, key, key_len); - if (ret < 0) { - crypto_free_aead(tfm); - return NULL; - } + if (ret < 0) + goto fail; ret = crypto_aead_setauthsize(tfm, icv_len); - if (ret < 0) { - crypto_free_aead(tfm); - return NULL; - } + if (ret < 0) + goto fail; return tfm; +fail: + crypto_free_aead(tfm); + return ERR_PTR(ret); } static int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len, @@ -1258,12 +1301,12 @@ static int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len, { rx_sa->stats = alloc_percpu(struct macsec_rx_sa_stats); if (!rx_sa->stats) - return -1; + return -ENOMEM; rx_sa->key.tfm = macsec_alloc_tfm(sak, key_len, icv_len); - if (!rx_sa->key.tfm) { + if (IS_ERR(rx_sa->key.tfm)) { free_percpu(rx_sa->stats); - return -1; + return PTR_ERR(rx_sa->key.tfm); } rx_sa->active = false; @@ -1356,12 +1399,12 @@ static int init_tx_sa(struct macsec_tx_sa *tx_sa, char *sak, int key_len, { tx_sa->stats = alloc_percpu(struct macsec_tx_sa_stats); if (!tx_sa->stats) - return -1; + return -ENOMEM; tx_sa->key.tfm = macsec_alloc_tfm(sak, key_len, icv_len); - if (!tx_sa->key.tfm) { + if (IS_ERR(tx_sa->key.tfm)) { free_percpu(tx_sa->stats); - return -1; + return PTR_ERR(tx_sa->key.tfm); } tx_sa->active = false; @@ -1594,6 +1637,7 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) unsigned char assoc_num; struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1]; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; + int err; if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -1609,7 +1653,7 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); rx_sc = get_rxsc_from_nl(genl_info_net(info), attrs, tb_rxsc, &dev, &secy); - if (IS_ERR(rx_sc) || !macsec_rxsc_get(rx_sc)) { + if (IS_ERR(rx_sc)) { rtnl_unlock(); return PTR_ERR(rx_sc); } @@ -1630,13 +1674,19 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) } rx_sa = kmalloc(sizeof(*rx_sa), GFP_KERNEL); - if (!rx_sa || init_rx_sa(rx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), - secy->key_len, secy->icv_len)) { - kfree(rx_sa); + if (!rx_sa) { rtnl_unlock(); return -ENOMEM; } + err = init_rx_sa(rx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), + secy->key_len, secy->icv_len); + if (err < 0) { + kfree(rx_sa); + rtnl_unlock(); + return err; + } + if (tb_sa[MACSEC_SA_ATTR_PN]) { spin_lock_bh(&rx_sa->lock); rx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); @@ -1742,6 +1792,7 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) struct macsec_tx_sa *tx_sa; unsigned char assoc_num; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; + int err; if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -1778,13 +1829,19 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) } tx_sa = kmalloc(sizeof(*tx_sa), GFP_KERNEL); - if (!tx_sa || init_tx_sa(tx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), - secy->key_len, secy->icv_len)) { - kfree(tx_sa); + if (!tx_sa) { rtnl_unlock(); return -ENOMEM; } + err = init_tx_sa(tx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), + secy->key_len, secy->icv_len); + if (err < 0) { + kfree(tx_sa); + rtnl_unlock(); + return err; + } + nla_memcpy(tx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN); spin_lock_bh(&tx_sa->lock); @@ -2612,6 +2669,7 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.OutPktsUntagged++; u64_stats_update_end(&secy_stats->syncp); + skb->dev = macsec->real_dev; len = skb->len; ret = dev_queue_xmit(skb); count_tx(dev, ret, len); @@ -2642,15 +2700,24 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, #define MACSEC_FEATURES \ (NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST) +static struct lock_class_key macsec_netdev_addr_lock_key; + static int macsec_dev_init(struct net_device *dev) { struct macsec_dev *macsec = macsec_priv(dev); struct net_device *real_dev = macsec->real_dev; + int err; dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; + err = gro_cells_init(&macsec->gro_cells, dev); + if (err) { + free_percpu(dev->tstats); + return err; + } + dev->features = real_dev->features & MACSEC_FEATURES; dev->features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE; @@ -2669,6 +2736,9 @@ static int macsec_dev_init(struct net_device *dev) static void macsec_dev_uninit(struct net_device *dev) { + struct macsec_dev *macsec = macsec_priv(dev); + + gro_cells_destroy(&macsec->gro_cells); free_percpu(dev->tstats); } @@ -2678,8 +2748,9 @@ static netdev_features_t macsec_fix_features(struct net_device *dev, struct macsec_dev *macsec = macsec_priv(dev); struct net_device *real_dev = macsec->real_dev; - features &= real_dev->features & MACSEC_FEATURES; - features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE; + features &= (real_dev->features & MACSEC_FEATURES) | + NETIF_F_GSO_SOFTWARE | NETIF_F_SOFT_FEATURES; + features |= NETIF_F_LLTX; return features; } @@ -2842,6 +2913,13 @@ static int macsec_get_iflink(const struct net_device *dev) return macsec_priv(dev)->real_dev->ifindex; } + +static int macsec_get_nest_level(struct net_device *dev) +{ + return macsec_priv(dev)->nest_level; +} + + static const struct net_device_ops macsec_netdev_ops = { .ndo_init = macsec_dev_init, .ndo_uninit = macsec_dev_uninit, @@ -2855,6 +2933,7 @@ static const struct net_device_ops macsec_netdev_ops = { .ndo_start_xmit = macsec_start_xmit, .ndo_get_stats64 = macsec_get_stats64, .ndo_get_iflink = macsec_get_iflink, + .ndo_get_lock_subclass = macsec_get_nest_level, }; static const struct device_type macsec_type = { @@ -2894,6 +2973,7 @@ static void macsec_setup(struct net_device *dev) dev->priv_flags |= IFF_NO_QUEUE; dev->netdev_ops = &macsec_netdev_ops; dev->destructor = macsec_free_netdev; + SET_NETDEV_DEVTYPE(dev, &macsec_type); eth_zero_addr(dev->broadcast); } @@ -2979,22 +3059,31 @@ static void macsec_del_dev(struct macsec_dev *macsec) } } +static void macsec_common_dellink(struct net_device *dev, struct list_head *head) +{ + struct macsec_dev *macsec = macsec_priv(dev); + struct net_device *real_dev = macsec->real_dev; + + unregister_netdevice_queue(dev, head); + list_del_rcu(&macsec->secys); + macsec_del_dev(macsec); + netdev_upper_dev_unlink(real_dev, dev); + + macsec_generation++; +} + static void macsec_dellink(struct net_device *dev, struct list_head *head) { struct macsec_dev *macsec = macsec_priv(dev); struct net_device *real_dev = macsec->real_dev; struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev); - macsec_generation++; + macsec_common_dellink(dev, head); - unregister_netdevice_queue(dev, head); - list_del_rcu(&macsec->secys); if (list_empty(&rxd->secys)) { netdev_rx_handler_unregister(real_dev); kfree(rxd); } - - macsec_del_dev(macsec); } static int register_macsec_dev(struct net_device *real_dev, @@ -3111,6 +3200,18 @@ static int macsec_newlink(struct net *net, struct net_device *dev, if (err < 0) return err; + dev_hold(real_dev); + + macsec->nest_level = dev_get_nest_level(real_dev) + 1; + netdev_lockdep_set_classes(dev); + lockdep_set_class_and_subclass(&dev->addr_list_lock, + &macsec_netdev_addr_lock_key, + macsec_get_nest_level(dev)); + + err = netdev_upper_dev_link(real_dev, dev); + if (err < 0) + goto unregister; + /* need to be already registered so that ->init has run and * the MAC addr is set */ @@ -3123,12 +3224,12 @@ static int macsec_newlink(struct net *net, struct net_device *dev, if (rx_handler && sci_exists(real_dev, sci)) { err = -EBUSY; - goto unregister; + goto unlink; } err = macsec_add_dev(dev, sci, icv_len); if (err) - goto unregister; + goto unlink; if (data) macsec_changelink_common(dev, data); @@ -3139,12 +3240,12 @@ static int macsec_newlink(struct net *net, struct net_device *dev, macsec_generation++; - dev_hold(real_dev); - return 0; del_dev: macsec_del_dev(macsec); +unlink: + netdev_upper_dev_unlink(real_dev, dev); unregister: unregister_netdevice(dev); return err; @@ -3163,14 +3264,26 @@ static int macsec_validate_attr(struct nlattr *tb[], struct nlattr *data[]) if (data[IFLA_MACSEC_CIPHER_SUITE]) csid = nla_get_u64(data[IFLA_MACSEC_CIPHER_SUITE]); - if (data[IFLA_MACSEC_ICV_LEN]) + if (data[IFLA_MACSEC_ICV_LEN]) { icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]); + if (icv_len != DEFAULT_ICV_LEN) { + char dummy_key[DEFAULT_SAK_LEN] = { 0 }; + struct crypto_aead *dummy_tfm; + + dummy_tfm = macsec_alloc_tfm(dummy_key, + DEFAULT_SAK_LEN, + icv_len); + if (IS_ERR(dummy_tfm)) + return PTR_ERR(dummy_tfm); + crypto_free_aead(dummy_tfm); + } + } switch (csid) { case MACSEC_DEFAULT_CIPHER_ID: case MACSEC_DEFAULT_CIPHER_ALT: if (icv_len < MACSEC_MIN_ICV_LEN || - icv_len > MACSEC_MAX_ICV_LEN) + icv_len > MACSEC_STD_ICV_LEN) return -EINVAL; break; default: @@ -3302,8 +3415,12 @@ static int macsec_notify(struct notifier_block *this, unsigned long event, rxd = macsec_data_rtnl(real_dev); list_for_each_entry_safe(m, n, &rxd->secys, secys) { - macsec_dellink(m->secy.netdev, &head); + macsec_common_dellink(m->secy.netdev, &head); } + + netdev_rx_handler_unregister(real_dev); + kfree(rxd); + unregister_netdevice_many(&head); break; } @@ -3361,6 +3478,7 @@ static void __exit macsec_exit(void) genl_unregister_family(&macsec_fam); rtnl_link_unregister(&macsec_link_ops); unregister_netdevice_notifier(&macsec_notifier); + rcu_barrier(); } module_init(macsec_init); |