diff options
Diffstat (limited to 'drivers/net/macsec.c')
| -rw-r--r-- | drivers/net/macsec.c | 60 |
1 files changed, 46 insertions, 14 deletions
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 78a19b134632..87f3f4bdb026 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -19,6 +19,7 @@ #include <net/gro_cells.h> #include <net/macsec.h> #include <net/dst_metadata.h> +#include <net/rtnetlink.h> #include <net/netdev_lock.h> #include <linux/phy.h> #include <linux/byteorder/generic.h> @@ -486,6 +487,9 @@ static pn_t tx_sa_update_pn(struct macsec_tx_sa *tx_sa, spin_lock_bh(&tx_sa->lock); pn = tx_sa->next_pn_halves; + if (unlikely(pn.full64 == 0)) + goto out; + if (secy->xpn) tx_sa->next_pn++; else @@ -493,6 +497,8 @@ static pn_t tx_sa_update_pn(struct macsec_tx_sa *tx_sa, if (tx_sa->next_pn == 0) __macsec_pn_wrapped(secy, tx_sa); + +out: spin_unlock_bh(&tx_sa->lock); return pn; @@ -638,7 +644,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, return ERR_PTR(-ENOMEM); } } else { - skb = skb_unshare(skb, GFP_ATOMIC); + skb = skb_unclone_full(skb, GFP_ATOMIC); if (!skb) { macsec_txsa_put(tx_sa); return ERR_PTR(-ENOMEM); @@ -947,7 +953,7 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, aead_request_set_crypt(req, sg, sg, len, iv); aead_request_set_ad(req, macsec_hdr_len(macsec_skb_cb(skb)->has_sci)); - skb = skb_unshare(skb, GFP_ATOMIC); + skb = skb_unclone_full(skb, GFP_ATOMIC); if (!skb) { aead_request_free(req); return ERR_PTR(-ENOMEM); @@ -1153,7 +1159,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) if (hdr->eth.h_proto != htons(ETH_P_MACSEC)) return handle_not_macsec(skb); - skb = skb_unshare(skb, GFP_ATOMIC); + skb = skb_unclone_full(skb, GFP_ATOMIC); *pskb = skb; if (!skb) return RX_HANDLER_CONSUMED; @@ -3967,6 +3973,33 @@ static int macsec_changelink_common(struct net_device *dev, return 0; } +/* Both a change of the offload state (mdo_add_secy()/mdo_del_secy()) and + * any request against an already offloaded device (mdo_upd_secy()) reach + * the driver of real_dev, which may live in a different network namespace + * from the macsec device. Require CAP_NET_ADMIN over real_dev's namespace + * for those; local attributes of a non-offloaded device are unaffected. + */ +static int macsec_changelink_check_netns(struct net_device *dev, + struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct macsec_dev *macsec = macsec_priv(dev); + bool reaches_real_dev; + + reaches_real_dev = macsec_is_offloaded(macsec); + if (data[IFLA_MACSEC_OFFLOAD] && + nla_get_u8(data[IFLA_MACSEC_OFFLOAD]) != macsec->offload) + reaches_real_dev = true; + + if (!reaches_real_dev || + rtnl_dev_link_net_capable(dev, dev_net(macsec->real_dev))) + return 0; + + NL_SET_ERR_MSG(extack, + "Changing a MACsec device whose real device is in another network namespace requires CAP_NET_ADMIN in that namespace"); + return -EPERM; +} + static int macsec_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) @@ -3981,6 +4014,10 @@ static int macsec_changelink(struct net_device *dev, struct nlattr *tb[], if (!data) return 0; + ret = macsec_changelink_check_netns(dev, data, extack); + if (ret) + return ret; + if (data[IFLA_MACSEC_CIPHER_SUITE] || data[IFLA_MACSEC_ICV_LEN] || data[IFLA_MACSEC_SCI] || @@ -4143,9 +4180,6 @@ static void macsec_init_secy(struct net_device *dev, sci_t sci, u8 icv_len) struct macsec_dev *macsec = macsec_priv(dev); struct macsec_secy *secy = &macsec->secy; - if (sci == MACSEC_UNDEF_SCI) - sci = dev_to_sci(dev, MACSEC_PORT_ES); - secy->netdev = dev; secy->operational = true; secy->key_len = DEFAULT_SAK_LEN; @@ -4178,7 +4212,7 @@ static int macsec_newlink(struct net_device *dev, u8 icv_len = MACSEC_DEFAULT_ICV_LEN; struct net_device *real_dev; int err, mtu; - sci_t sci; + sci_t sci = MACSEC_UNDEF_SCI; if (!tb[IFLA_LINK]) return -EINVAL; @@ -4231,9 +4265,13 @@ static int macsec_newlink(struct net_device *dev, sci = nla_get_sci(data[IFLA_MACSEC_SCI]); else if (data && data[IFLA_MACSEC_PORT]) sci = dev_to_sci(dev, nla_get_be16(data[IFLA_MACSEC_PORT])); - else + + if (sci == MACSEC_UNDEF_SCI) sci = dev_to_sci(dev, MACSEC_PORT_ES); + if (rx_handler && sci_exists(real_dev, sci)) + return -EBUSY; + /* Registration can notify listeners before returning. */ macsec_init_secy(dev, sci, icv_len); if (data) { @@ -4254,11 +4292,6 @@ static int macsec_newlink(struct net_device *dev, if (err < 0) goto unregister; - if (rx_handler && sci_exists(real_dev, sci)) { - err = -EBUSY; - goto unlink; - } - /* If h/w offloading is available, propagate to the device */ if (macsec_is_offloaded(macsec)) { const struct macsec_ops *ops; @@ -4290,7 +4323,6 @@ static int macsec_newlink(struct net_device *dev, del_dev: macsec_del_dev(macsec); -unlink: netdev_upper_dev_unlink(real_dev, dev); unregister: unregister_netdevice(dev); |
