summaryrefslogtreecommitdiff
path: root/drivers/net/macsec.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/macsec.c')
-rw-r--r--drivers/net/macsec.c60
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);