18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/net/macsec.c - MACsec device
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Sabrina Dubroca <sd@queasysnail.net>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/types.h>
98c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
108c2ecf20Sopenharmony_ci#include <linux/socket.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <crypto/aead.h>
138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
148c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
168c2ecf20Sopenharmony_ci#include <linux/refcount.h>
178c2ecf20Sopenharmony_ci#include <net/genetlink.h>
188c2ecf20Sopenharmony_ci#include <net/sock.h>
198c2ecf20Sopenharmony_ci#include <net/gro_cells.h>
208c2ecf20Sopenharmony_ci#include <net/macsec.h>
218c2ecf20Sopenharmony_ci#include <linux/phy.h>
228c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h>
238c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <uapi/linux/if_macsec.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define MACSEC_SCI_LEN 8
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* SecTAG length = macsec_eth_header without the optional SCI */
308c2ecf20Sopenharmony_ci#define MACSEC_TAG_LEN 6
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct macsec_eth_header {
338c2ecf20Sopenharmony_ci	struct ethhdr eth;
348c2ecf20Sopenharmony_ci	/* SecTAG */
358c2ecf20Sopenharmony_ci	u8  tci_an;
368c2ecf20Sopenharmony_ci#if defined(__LITTLE_ENDIAN_BITFIELD)
378c2ecf20Sopenharmony_ci	u8  short_length:6,
388c2ecf20Sopenharmony_ci		  unused:2;
398c2ecf20Sopenharmony_ci#elif defined(__BIG_ENDIAN_BITFIELD)
408c2ecf20Sopenharmony_ci	u8        unused:2,
418c2ecf20Sopenharmony_ci	    short_length:6;
428c2ecf20Sopenharmony_ci#else
438c2ecf20Sopenharmony_ci#error	"Please fix <asm/byteorder.h>"
448c2ecf20Sopenharmony_ci#endif
458c2ecf20Sopenharmony_ci	__be32 packet_number;
468c2ecf20Sopenharmony_ci	u8 secure_channel_id[8]; /* optional */
478c2ecf20Sopenharmony_ci} __packed;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define MACSEC_TCI_VERSION 0x80
508c2ecf20Sopenharmony_ci#define MACSEC_TCI_ES      0x40 /* end station */
518c2ecf20Sopenharmony_ci#define MACSEC_TCI_SC      0x20 /* SCI present */
528c2ecf20Sopenharmony_ci#define MACSEC_TCI_SCB     0x10 /* epon */
538c2ecf20Sopenharmony_ci#define MACSEC_TCI_E       0x08 /* encryption */
548c2ecf20Sopenharmony_ci#define MACSEC_TCI_C       0x04 /* changed text */
558c2ecf20Sopenharmony_ci#define MACSEC_AN_MASK     0x03 /* association number */
568c2ecf20Sopenharmony_ci#define MACSEC_TCI_CONFID  (MACSEC_TCI_E | MACSEC_TCI_C)
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* minimum secure data length deemed "not short", see IEEE 802.1AE-2006 9.7 */
598c2ecf20Sopenharmony_ci#define MIN_NON_SHORT_LEN 48
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define GCM_AES_IV_LEN 12
628c2ecf20Sopenharmony_ci#define DEFAULT_ICV_LEN 16
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define for_each_rxsc(secy, sc)				\
658c2ecf20Sopenharmony_ci	for (sc = rcu_dereference_bh(secy->rx_sc);	\
668c2ecf20Sopenharmony_ci	     sc;					\
678c2ecf20Sopenharmony_ci	     sc = rcu_dereference_bh(sc->next))
688c2ecf20Sopenharmony_ci#define for_each_rxsc_rtnl(secy, sc)			\
698c2ecf20Sopenharmony_ci	for (sc = rtnl_dereference(secy->rx_sc);	\
708c2ecf20Sopenharmony_ci	     sc;					\
718c2ecf20Sopenharmony_ci	     sc = rtnl_dereference(sc->next))
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define pn_same_half(pn1, pn2) (!(((pn1) >> 31) ^ ((pn2) >> 31)))
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistruct gcm_iv_xpn {
768c2ecf20Sopenharmony_ci	union {
778c2ecf20Sopenharmony_ci		u8 short_secure_channel_id[4];
788c2ecf20Sopenharmony_ci		ssci_t ssci;
798c2ecf20Sopenharmony_ci	};
808c2ecf20Sopenharmony_ci	__be64 pn;
818c2ecf20Sopenharmony_ci} __packed;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct gcm_iv {
848c2ecf20Sopenharmony_ci	union {
858c2ecf20Sopenharmony_ci		u8 secure_channel_id[8];
868c2ecf20Sopenharmony_ci		sci_t sci;
878c2ecf20Sopenharmony_ci	};
888c2ecf20Sopenharmony_ci	__be32 pn;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#define MACSEC_VALIDATE_DEFAULT MACSEC_VALIDATE_STRICT
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistruct pcpu_secy_stats {
948c2ecf20Sopenharmony_ci	struct macsec_dev_stats stats;
958c2ecf20Sopenharmony_ci	struct u64_stats_sync syncp;
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/**
998c2ecf20Sopenharmony_ci * struct macsec_dev - private data
1008c2ecf20Sopenharmony_ci * @secy: SecY config
1018c2ecf20Sopenharmony_ci * @real_dev: pointer to underlying netdevice
1028c2ecf20Sopenharmony_ci * @stats: MACsec device stats
1038c2ecf20Sopenharmony_ci * @secys: linked list of SecY's on the underlying device
1048c2ecf20Sopenharmony_ci * @offload: status of offloading on the MACsec device
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_cistruct macsec_dev {
1078c2ecf20Sopenharmony_ci	struct macsec_secy secy;
1088c2ecf20Sopenharmony_ci	struct net_device *real_dev;
1098c2ecf20Sopenharmony_ci	struct pcpu_secy_stats __percpu *stats;
1108c2ecf20Sopenharmony_ci	struct list_head secys;
1118c2ecf20Sopenharmony_ci	struct gro_cells gro_cells;
1128c2ecf20Sopenharmony_ci	enum macsec_offload offload;
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/**
1168c2ecf20Sopenharmony_ci * struct macsec_rxh_data - rx_handler private argument
1178c2ecf20Sopenharmony_ci * @secys: linked list of SecY's on this underlying device
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_cistruct macsec_rxh_data {
1208c2ecf20Sopenharmony_ci	struct list_head secys;
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic struct macsec_dev *macsec_priv(const struct net_device *dev)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	return (struct macsec_dev *)netdev_priv(dev);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic struct macsec_rxh_data *macsec_data_rcu(const struct net_device *dev)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	return rcu_dereference_bh(dev->rx_handler_data);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic struct macsec_rxh_data *macsec_data_rtnl(const struct net_device *dev)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	return rtnl_dereference(dev->rx_handler_data);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistruct macsec_cb {
1398c2ecf20Sopenharmony_ci	struct aead_request *req;
1408c2ecf20Sopenharmony_ci	union {
1418c2ecf20Sopenharmony_ci		struct macsec_tx_sa *tx_sa;
1428c2ecf20Sopenharmony_ci		struct macsec_rx_sa *rx_sa;
1438c2ecf20Sopenharmony_ci	};
1448c2ecf20Sopenharmony_ci	u8 assoc_num;
1458c2ecf20Sopenharmony_ci	bool valid;
1468c2ecf20Sopenharmony_ci	bool has_sci;
1478c2ecf20Sopenharmony_ci};
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic struct macsec_rx_sa *macsec_rxsa_get(struct macsec_rx_sa __rcu *ptr)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct macsec_rx_sa *sa = rcu_dereference_bh(ptr);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (!sa || !sa->active)
1548c2ecf20Sopenharmony_ci		return NULL;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (!refcount_inc_not_zero(&sa->refcnt))
1578c2ecf20Sopenharmony_ci		return NULL;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return sa;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic struct macsec_rx_sa *macsec_active_rxsa_get(struct macsec_rx_sc *rx_sc)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct macsec_rx_sa *sa = NULL;
1658c2ecf20Sopenharmony_ci	int an;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	for (an = 0; an < MACSEC_NUM_AN; an++)	{
1688c2ecf20Sopenharmony_ci		sa = macsec_rxsa_get(rx_sc->sa[an]);
1698c2ecf20Sopenharmony_ci		if (sa)
1708c2ecf20Sopenharmony_ci			break;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	return sa;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void free_rx_sc_rcu(struct rcu_head *head)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc = container_of(head, struct macsec_rx_sc, rcu_head);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	free_percpu(rx_sc->stats);
1808c2ecf20Sopenharmony_ci	kfree(rx_sc);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic struct macsec_rx_sc *macsec_rxsc_get(struct macsec_rx_sc *sc)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	return refcount_inc_not_zero(&sc->refcnt) ? sc : NULL;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void macsec_rxsc_put(struct macsec_rx_sc *sc)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&sc->refcnt))
1918c2ecf20Sopenharmony_ci		call_rcu(&sc->rcu_head, free_rx_sc_rcu);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void free_rxsa(struct rcu_head *head)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct macsec_rx_sa *sa = container_of(head, struct macsec_rx_sa, rcu);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	crypto_free_aead(sa->key.tfm);
1998c2ecf20Sopenharmony_ci	free_percpu(sa->stats);
2008c2ecf20Sopenharmony_ci	kfree(sa);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void macsec_rxsa_put(struct macsec_rx_sa *sa)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&sa->refcnt))
2068c2ecf20Sopenharmony_ci		call_rcu(&sa->rcu, free_rxsa);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic struct macsec_tx_sa *macsec_txsa_get(struct macsec_tx_sa __rcu *ptr)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct macsec_tx_sa *sa = rcu_dereference_bh(ptr);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (!sa || !sa->active)
2148c2ecf20Sopenharmony_ci		return NULL;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (!refcount_inc_not_zero(&sa->refcnt))
2178c2ecf20Sopenharmony_ci		return NULL;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return sa;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic void free_txsa(struct rcu_head *head)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct macsec_tx_sa *sa = container_of(head, struct macsec_tx_sa, rcu);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	crypto_free_aead(sa->key.tfm);
2278c2ecf20Sopenharmony_ci	free_percpu(sa->stats);
2288c2ecf20Sopenharmony_ci	kfree(sa);
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void macsec_txsa_put(struct macsec_tx_sa *sa)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&sa->refcnt))
2348c2ecf20Sopenharmony_ci		call_rcu(&sa->rcu, free_txsa);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic struct macsec_cb *macsec_skb_cb(struct sk_buff *skb)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct macsec_cb) > sizeof(skb->cb));
2408c2ecf20Sopenharmony_ci	return (struct macsec_cb *)skb->cb;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci#define MACSEC_PORT_ES (htons(0x0001))
2448c2ecf20Sopenharmony_ci#define MACSEC_PORT_SCB (0x0000)
2458c2ecf20Sopenharmony_ci#define MACSEC_UNDEF_SCI ((__force sci_t)0xffffffffffffffffULL)
2468c2ecf20Sopenharmony_ci#define MACSEC_UNDEF_SSCI ((__force ssci_t)0xffffffff)
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci#define MACSEC_GCM_AES_128_SAK_LEN 16
2498c2ecf20Sopenharmony_ci#define MACSEC_GCM_AES_256_SAK_LEN 32
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci#define DEFAULT_SAK_LEN MACSEC_GCM_AES_128_SAK_LEN
2528c2ecf20Sopenharmony_ci#define DEFAULT_XPN false
2538c2ecf20Sopenharmony_ci#define DEFAULT_SEND_SCI true
2548c2ecf20Sopenharmony_ci#define DEFAULT_ENCRYPT false
2558c2ecf20Sopenharmony_ci#define DEFAULT_ENCODING_SA 0
2568c2ecf20Sopenharmony_ci#define MACSEC_XPN_MAX_REPLAY_WINDOW (((1 << 30) - 1))
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic bool send_sci(const struct macsec_secy *secy)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	const struct macsec_tx_sc *tx_sc = &secy->tx_sc;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return tx_sc->send_sci ||
2638c2ecf20Sopenharmony_ci		(secy->n_rx_sc > 1 && !tx_sc->end_station && !tx_sc->scb);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic sci_t make_sci(u8 *addr, __be16 port)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	sci_t sci;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	memcpy(&sci, addr, ETH_ALEN);
2718c2ecf20Sopenharmony_ci	memcpy(((char *)&sci) + ETH_ALEN, &port, sizeof(port));
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return sci;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic sci_t macsec_frame_sci(struct macsec_eth_header *hdr, bool sci_present)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	sci_t sci;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (sci_present)
2818c2ecf20Sopenharmony_ci		memcpy(&sci, hdr->secure_channel_id,
2828c2ecf20Sopenharmony_ci		       sizeof(hdr->secure_channel_id));
2838c2ecf20Sopenharmony_ci	else
2848c2ecf20Sopenharmony_ci		sci = make_sci(hdr->eth.h_source, MACSEC_PORT_ES);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return sci;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic unsigned int macsec_sectag_len(bool sci_present)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	return MACSEC_TAG_LEN + (sci_present ? MACSEC_SCI_LEN : 0);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic unsigned int macsec_hdr_len(bool sci_present)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	return macsec_sectag_len(sci_present) + ETH_HLEN;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic unsigned int macsec_extra_len(bool sci_present)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	return macsec_sectag_len(sci_present) + sizeof(__be16);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci/* Fill SecTAG according to IEEE 802.1AE-2006 10.5.3 */
3058c2ecf20Sopenharmony_cistatic void macsec_fill_sectag(struct macsec_eth_header *h,
3068c2ecf20Sopenharmony_ci			       const struct macsec_secy *secy, u32 pn,
3078c2ecf20Sopenharmony_ci			       bool sci_present)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	const struct macsec_tx_sc *tx_sc = &secy->tx_sc;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	memset(&h->tci_an, 0, macsec_sectag_len(sci_present));
3128c2ecf20Sopenharmony_ci	h->eth.h_proto = htons(ETH_P_MACSEC);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (sci_present) {
3158c2ecf20Sopenharmony_ci		h->tci_an |= MACSEC_TCI_SC;
3168c2ecf20Sopenharmony_ci		memcpy(&h->secure_channel_id, &secy->sci,
3178c2ecf20Sopenharmony_ci		       sizeof(h->secure_channel_id));
3188c2ecf20Sopenharmony_ci	} else {
3198c2ecf20Sopenharmony_ci		if (tx_sc->end_station)
3208c2ecf20Sopenharmony_ci			h->tci_an |= MACSEC_TCI_ES;
3218c2ecf20Sopenharmony_ci		if (tx_sc->scb)
3228c2ecf20Sopenharmony_ci			h->tci_an |= MACSEC_TCI_SCB;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	h->packet_number = htonl(pn);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* with GCM, C/E clear for !encrypt, both set for encrypt */
3288c2ecf20Sopenharmony_ci	if (tx_sc->encrypt)
3298c2ecf20Sopenharmony_ci		h->tci_an |= MACSEC_TCI_CONFID;
3308c2ecf20Sopenharmony_ci	else if (secy->icv_len != DEFAULT_ICV_LEN)
3318c2ecf20Sopenharmony_ci		h->tci_an |= MACSEC_TCI_C;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	h->tci_an |= tx_sc->encoding_sa;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void macsec_set_shortlen(struct macsec_eth_header *h, size_t data_len)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	if (data_len < MIN_NON_SHORT_LEN)
3398c2ecf20Sopenharmony_ci		h->short_length = data_len;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/* Checks if a MACsec interface is being offloaded to an hardware engine */
3438c2ecf20Sopenharmony_cistatic bool macsec_is_offloaded(struct macsec_dev *macsec)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	if (macsec->offload == MACSEC_OFFLOAD_MAC ||
3468c2ecf20Sopenharmony_ci	    macsec->offload == MACSEC_OFFLOAD_PHY)
3478c2ecf20Sopenharmony_ci		return true;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return false;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci/* Checks if underlying layers implement MACsec offloading functions. */
3538c2ecf20Sopenharmony_cistatic bool macsec_check_offload(enum macsec_offload offload,
3548c2ecf20Sopenharmony_ci				 struct macsec_dev *macsec)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	if (!macsec || !macsec->real_dev)
3578c2ecf20Sopenharmony_ci		return false;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (offload == MACSEC_OFFLOAD_PHY)
3608c2ecf20Sopenharmony_ci		return macsec->real_dev->phydev &&
3618c2ecf20Sopenharmony_ci		       macsec->real_dev->phydev->macsec_ops;
3628c2ecf20Sopenharmony_ci	else if (offload == MACSEC_OFFLOAD_MAC)
3638c2ecf20Sopenharmony_ci		return macsec->real_dev->features & NETIF_F_HW_MACSEC &&
3648c2ecf20Sopenharmony_ci		       macsec->real_dev->macsec_ops;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return false;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic const struct macsec_ops *__macsec_get_ops(enum macsec_offload offload,
3708c2ecf20Sopenharmony_ci						 struct macsec_dev *macsec,
3718c2ecf20Sopenharmony_ci						 struct macsec_context *ctx)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	if (ctx) {
3748c2ecf20Sopenharmony_ci		memset(ctx, 0, sizeof(*ctx));
3758c2ecf20Sopenharmony_ci		ctx->offload = offload;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		if (offload == MACSEC_OFFLOAD_PHY)
3788c2ecf20Sopenharmony_ci			ctx->phydev = macsec->real_dev->phydev;
3798c2ecf20Sopenharmony_ci		else if (offload == MACSEC_OFFLOAD_MAC)
3808c2ecf20Sopenharmony_ci			ctx->netdev = macsec->real_dev;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (offload == MACSEC_OFFLOAD_PHY)
3848c2ecf20Sopenharmony_ci		return macsec->real_dev->phydev->macsec_ops;
3858c2ecf20Sopenharmony_ci	else
3868c2ecf20Sopenharmony_ci		return macsec->real_dev->macsec_ops;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/* Returns a pointer to the MACsec ops struct if any and updates the MACsec
3908c2ecf20Sopenharmony_ci * context device reference if provided.
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_cistatic const struct macsec_ops *macsec_get_ops(struct macsec_dev *macsec,
3938c2ecf20Sopenharmony_ci					       struct macsec_context *ctx)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	if (!macsec_check_offload(macsec->offload, macsec))
3968c2ecf20Sopenharmony_ci		return NULL;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return __macsec_get_ops(macsec->offload, macsec, ctx);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci/* validate MACsec packet according to IEEE 802.1AE-2018 9.12 */
4028c2ecf20Sopenharmony_cistatic bool macsec_validate_skb(struct sk_buff *skb, u16 icv_len, bool xpn)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct macsec_eth_header *h = (struct macsec_eth_header *)skb->data;
4058c2ecf20Sopenharmony_ci	int len = skb->len - 2 * ETH_ALEN;
4068c2ecf20Sopenharmony_ci	int extra_len = macsec_extra_len(!!(h->tci_an & MACSEC_TCI_SC)) + icv_len;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* a) It comprises at least 17 octets */
4098c2ecf20Sopenharmony_ci	if (skb->len <= 16)
4108c2ecf20Sopenharmony_ci		return false;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* b) MACsec EtherType: already checked */
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* c) V bit is clear */
4158c2ecf20Sopenharmony_ci	if (h->tci_an & MACSEC_TCI_VERSION)
4168c2ecf20Sopenharmony_ci		return false;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* d) ES or SCB => !SC */
4198c2ecf20Sopenharmony_ci	if ((h->tci_an & MACSEC_TCI_ES || h->tci_an & MACSEC_TCI_SCB) &&
4208c2ecf20Sopenharmony_ci	    (h->tci_an & MACSEC_TCI_SC))
4218c2ecf20Sopenharmony_ci		return false;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* e) Bits 7 and 8 of octet 4 of the SecTAG are clear */
4248c2ecf20Sopenharmony_ci	if (h->unused)
4258c2ecf20Sopenharmony_ci		return false;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* rx.pn != 0 if not XPN (figure 10-5 with 802.11AEbw-2013 amendment) */
4288c2ecf20Sopenharmony_ci	if (!h->packet_number && !xpn)
4298c2ecf20Sopenharmony_ci		return false;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* length check, f) g) h) i) */
4328c2ecf20Sopenharmony_ci	if (h->short_length)
4338c2ecf20Sopenharmony_ci		return len == extra_len + h->short_length;
4348c2ecf20Sopenharmony_ci	return len >= extra_len + MIN_NON_SHORT_LEN;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci#define MACSEC_NEEDED_HEADROOM (macsec_extra_len(true))
4388c2ecf20Sopenharmony_ci#define MACSEC_NEEDED_TAILROOM MACSEC_STD_ICV_LEN
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic void macsec_fill_iv_xpn(unsigned char *iv, ssci_t ssci, u64 pn,
4418c2ecf20Sopenharmony_ci			       salt_t salt)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	struct gcm_iv_xpn *gcm_iv = (struct gcm_iv_xpn *)iv;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	gcm_iv->ssci = ssci ^ salt.ssci;
4468c2ecf20Sopenharmony_ci	gcm_iv->pn = cpu_to_be64(pn) ^ salt.pn;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic void macsec_fill_iv(unsigned char *iv, sci_t sci, u32 pn)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct gcm_iv *gcm_iv = (struct gcm_iv *)iv;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	gcm_iv->sci = sci;
4548c2ecf20Sopenharmony_ci	gcm_iv->pn = htonl(pn);
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic struct macsec_eth_header *macsec_ethhdr(struct sk_buff *skb)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	return (struct macsec_eth_header *)skb_mac_header(skb);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic sci_t dev_to_sci(struct net_device *dev, __be16 port)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	return make_sci(dev->dev_addr, port);
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic void __macsec_pn_wrapped(struct macsec_secy *secy,
4688c2ecf20Sopenharmony_ci				struct macsec_tx_sa *tx_sa)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	pr_debug("PN wrapped, transitioning to !oper\n");
4718c2ecf20Sopenharmony_ci	tx_sa->active = false;
4728c2ecf20Sopenharmony_ci	if (secy->protect_frames)
4738c2ecf20Sopenharmony_ci		secy->operational = false;
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_civoid macsec_pn_wrapped(struct macsec_secy *secy, struct macsec_tx_sa *tx_sa)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	spin_lock_bh(&tx_sa->lock);
4798c2ecf20Sopenharmony_ci	__macsec_pn_wrapped(secy, tx_sa);
4808c2ecf20Sopenharmony_ci	spin_unlock_bh(&tx_sa->lock);
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(macsec_pn_wrapped);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic pn_t tx_sa_update_pn(struct macsec_tx_sa *tx_sa,
4858c2ecf20Sopenharmony_ci			    struct macsec_secy *secy)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	pn_t pn;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	spin_lock_bh(&tx_sa->lock);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	pn = tx_sa->next_pn_halves;
4928c2ecf20Sopenharmony_ci	if (secy->xpn)
4938c2ecf20Sopenharmony_ci		tx_sa->next_pn++;
4948c2ecf20Sopenharmony_ci	else
4958c2ecf20Sopenharmony_ci		tx_sa->next_pn_halves.lower++;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (tx_sa->next_pn == 0)
4988c2ecf20Sopenharmony_ci		__macsec_pn_wrapped(secy, tx_sa);
4998c2ecf20Sopenharmony_ci	spin_unlock_bh(&tx_sa->lock);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return pn;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic void macsec_encrypt_finish(struct sk_buff *skb, struct net_device *dev)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = netdev_priv(dev);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	skb->dev = macsec->real_dev;
5098c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
5108c2ecf20Sopenharmony_ci	skb->protocol = eth_hdr(skb)->h_proto;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic unsigned int macsec_msdu_len(struct sk_buff *skb)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(skb->dev);
5168c2ecf20Sopenharmony_ci	struct macsec_secy *secy = &macsec->secy;
5178c2ecf20Sopenharmony_ci	bool sci_present = macsec_skb_cb(skb)->has_sci;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	return skb->len - macsec_hdr_len(sci_present) - secy->icv_len;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic void macsec_count_tx(struct sk_buff *skb, struct macsec_tx_sc *tx_sc,
5238c2ecf20Sopenharmony_ci			    struct macsec_tx_sa *tx_sa)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	unsigned int msdu_len = macsec_msdu_len(skb);
5268c2ecf20Sopenharmony_ci	struct pcpu_tx_sc_stats *txsc_stats = this_cpu_ptr(tx_sc->stats);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	u64_stats_update_begin(&txsc_stats->syncp);
5298c2ecf20Sopenharmony_ci	if (tx_sc->encrypt) {
5308c2ecf20Sopenharmony_ci		txsc_stats->stats.OutOctetsEncrypted += msdu_len;
5318c2ecf20Sopenharmony_ci		txsc_stats->stats.OutPktsEncrypted++;
5328c2ecf20Sopenharmony_ci		this_cpu_inc(tx_sa->stats->OutPktsEncrypted);
5338c2ecf20Sopenharmony_ci	} else {
5348c2ecf20Sopenharmony_ci		txsc_stats->stats.OutOctetsProtected += msdu_len;
5358c2ecf20Sopenharmony_ci		txsc_stats->stats.OutPktsProtected++;
5368c2ecf20Sopenharmony_ci		this_cpu_inc(tx_sa->stats->OutPktsProtected);
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci	u64_stats_update_end(&txsc_stats->syncp);
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic void count_tx(struct net_device *dev, int ret, int len)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
5448c2ecf20Sopenharmony_ci		struct pcpu_sw_netstats *stats = this_cpu_ptr(dev->tstats);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
5478c2ecf20Sopenharmony_ci		stats->tx_packets++;
5488c2ecf20Sopenharmony_ci		stats->tx_bytes += len;
5498c2ecf20Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic void macsec_encrypt_done(struct crypto_async_request *base, int err)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct sk_buff *skb = base->data;
5568c2ecf20Sopenharmony_ci	struct net_device *dev = skb->dev;
5578c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
5588c2ecf20Sopenharmony_ci	struct macsec_tx_sa *sa = macsec_skb_cb(skb)->tx_sa;
5598c2ecf20Sopenharmony_ci	int len, ret;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	aead_request_free(macsec_skb_cb(skb)->req);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	rcu_read_lock_bh();
5648c2ecf20Sopenharmony_ci	macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa);
5658c2ecf20Sopenharmony_ci	/* packet is encrypted/protected so tx_bytes must be calculated */
5668c2ecf20Sopenharmony_ci	len = macsec_msdu_len(skb) + 2 * ETH_ALEN;
5678c2ecf20Sopenharmony_ci	macsec_encrypt_finish(skb, dev);
5688c2ecf20Sopenharmony_ci	ret = dev_queue_xmit(skb);
5698c2ecf20Sopenharmony_ci	count_tx(dev, ret, len);
5708c2ecf20Sopenharmony_ci	rcu_read_unlock_bh();
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	macsec_txsa_put(sa);
5738c2ecf20Sopenharmony_ci	dev_put(dev);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic struct aead_request *macsec_alloc_req(struct crypto_aead *tfm,
5778c2ecf20Sopenharmony_ci					     unsigned char **iv,
5788c2ecf20Sopenharmony_ci					     struct scatterlist **sg,
5798c2ecf20Sopenharmony_ci					     int num_frags)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	size_t size, iv_offset, sg_offset;
5828c2ecf20Sopenharmony_ci	struct aead_request *req;
5838c2ecf20Sopenharmony_ci	void *tmp;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	size = sizeof(struct aead_request) + crypto_aead_reqsize(tfm);
5868c2ecf20Sopenharmony_ci	iv_offset = size;
5878c2ecf20Sopenharmony_ci	size += GCM_AES_IV_LEN;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	size = ALIGN(size, __alignof__(struct scatterlist));
5908c2ecf20Sopenharmony_ci	sg_offset = size;
5918c2ecf20Sopenharmony_ci	size += sizeof(struct scatterlist) * num_frags;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	tmp = kmalloc(size, GFP_ATOMIC);
5948c2ecf20Sopenharmony_ci	if (!tmp)
5958c2ecf20Sopenharmony_ci		return NULL;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	*iv = (unsigned char *)(tmp + iv_offset);
5988c2ecf20Sopenharmony_ci	*sg = (struct scatterlist *)(tmp + sg_offset);
5998c2ecf20Sopenharmony_ci	req = tmp;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	aead_request_set_tfm(req, tfm);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return req;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic struct sk_buff *macsec_encrypt(struct sk_buff *skb,
6078c2ecf20Sopenharmony_ci				      struct net_device *dev)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	int ret;
6108c2ecf20Sopenharmony_ci	struct scatterlist *sg;
6118c2ecf20Sopenharmony_ci	struct sk_buff *trailer;
6128c2ecf20Sopenharmony_ci	unsigned char *iv;
6138c2ecf20Sopenharmony_ci	struct ethhdr *eth;
6148c2ecf20Sopenharmony_ci	struct macsec_eth_header *hh;
6158c2ecf20Sopenharmony_ci	size_t unprotected_len;
6168c2ecf20Sopenharmony_ci	struct aead_request *req;
6178c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
6188c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc;
6198c2ecf20Sopenharmony_ci	struct macsec_tx_sa *tx_sa;
6208c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
6218c2ecf20Sopenharmony_ci	bool sci_present;
6228c2ecf20Sopenharmony_ci	pn_t pn;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	secy = &macsec->secy;
6258c2ecf20Sopenharmony_ci	tx_sc = &secy->tx_sc;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* 10.5.1 TX SA assignment */
6288c2ecf20Sopenharmony_ci	tx_sa = macsec_txsa_get(tx_sc->sa[tx_sc->encoding_sa]);
6298c2ecf20Sopenharmony_ci	if (!tx_sa) {
6308c2ecf20Sopenharmony_ci		secy->operational = false;
6318c2ecf20Sopenharmony_ci		kfree_skb(skb);
6328c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (unlikely(skb_headroom(skb) < MACSEC_NEEDED_HEADROOM ||
6368c2ecf20Sopenharmony_ci		     skb_tailroom(skb) < MACSEC_NEEDED_TAILROOM)) {
6378c2ecf20Sopenharmony_ci		struct sk_buff *nskb = skb_copy_expand(skb,
6388c2ecf20Sopenharmony_ci						       MACSEC_NEEDED_HEADROOM,
6398c2ecf20Sopenharmony_ci						       MACSEC_NEEDED_TAILROOM,
6408c2ecf20Sopenharmony_ci						       GFP_ATOMIC);
6418c2ecf20Sopenharmony_ci		if (likely(nskb)) {
6428c2ecf20Sopenharmony_ci			consume_skb(skb);
6438c2ecf20Sopenharmony_ci			skb = nskb;
6448c2ecf20Sopenharmony_ci		} else {
6458c2ecf20Sopenharmony_ci			macsec_txsa_put(tx_sa);
6468c2ecf20Sopenharmony_ci			kfree_skb(skb);
6478c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
6488c2ecf20Sopenharmony_ci		}
6498c2ecf20Sopenharmony_ci	} else {
6508c2ecf20Sopenharmony_ci		skb = skb_unshare(skb, GFP_ATOMIC);
6518c2ecf20Sopenharmony_ci		if (!skb) {
6528c2ecf20Sopenharmony_ci			macsec_txsa_put(tx_sa);
6538c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
6548c2ecf20Sopenharmony_ci		}
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	unprotected_len = skb->len;
6588c2ecf20Sopenharmony_ci	eth = eth_hdr(skb);
6598c2ecf20Sopenharmony_ci	sci_present = send_sci(secy);
6608c2ecf20Sopenharmony_ci	hh = skb_push(skb, macsec_extra_len(sci_present));
6618c2ecf20Sopenharmony_ci	memmove(hh, eth, 2 * ETH_ALEN);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	pn = tx_sa_update_pn(tx_sa, secy);
6648c2ecf20Sopenharmony_ci	if (pn.full64 == 0) {
6658c2ecf20Sopenharmony_ci		macsec_txsa_put(tx_sa);
6668c2ecf20Sopenharmony_ci		kfree_skb(skb);
6678c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOLINK);
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci	macsec_fill_sectag(hh, secy, pn.lower, sci_present);
6708c2ecf20Sopenharmony_ci	macsec_set_shortlen(hh, unprotected_len - 2 * ETH_ALEN);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	skb_put(skb, secy->icv_len);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	if (skb->len - ETH_HLEN > macsec_priv(dev)->real_dev->mtu) {
6758c2ecf20Sopenharmony_ci		struct pcpu_secy_stats *secy_stats = this_cpu_ptr(macsec->stats);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		u64_stats_update_begin(&secy_stats->syncp);
6788c2ecf20Sopenharmony_ci		secy_stats->stats.OutPktsTooLong++;
6798c2ecf20Sopenharmony_ci		u64_stats_update_end(&secy_stats->syncp);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		macsec_txsa_put(tx_sa);
6828c2ecf20Sopenharmony_ci		kfree_skb(skb);
6838c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	ret = skb_cow_data(skb, 0, &trailer);
6878c2ecf20Sopenharmony_ci	if (unlikely(ret < 0)) {
6888c2ecf20Sopenharmony_ci		macsec_txsa_put(tx_sa);
6898c2ecf20Sopenharmony_ci		kfree_skb(skb);
6908c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	req = macsec_alloc_req(tx_sa->key.tfm, &iv, &sg, ret);
6948c2ecf20Sopenharmony_ci	if (!req) {
6958c2ecf20Sopenharmony_ci		macsec_txsa_put(tx_sa);
6968c2ecf20Sopenharmony_ci		kfree_skb(skb);
6978c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (secy->xpn)
7018c2ecf20Sopenharmony_ci		macsec_fill_iv_xpn(iv, tx_sa->ssci, pn.full64, tx_sa->key.salt);
7028c2ecf20Sopenharmony_ci	else
7038c2ecf20Sopenharmony_ci		macsec_fill_iv(iv, secy->sci, pn.lower);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	sg_init_table(sg, ret);
7068c2ecf20Sopenharmony_ci	ret = skb_to_sgvec(skb, sg, 0, skb->len);
7078c2ecf20Sopenharmony_ci	if (unlikely(ret < 0)) {
7088c2ecf20Sopenharmony_ci		aead_request_free(req);
7098c2ecf20Sopenharmony_ci		macsec_txsa_put(tx_sa);
7108c2ecf20Sopenharmony_ci		kfree_skb(skb);
7118c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (tx_sc->encrypt) {
7158c2ecf20Sopenharmony_ci		int len = skb->len - macsec_hdr_len(sci_present) -
7168c2ecf20Sopenharmony_ci			  secy->icv_len;
7178c2ecf20Sopenharmony_ci		aead_request_set_crypt(req, sg, sg, len, iv);
7188c2ecf20Sopenharmony_ci		aead_request_set_ad(req, macsec_hdr_len(sci_present));
7198c2ecf20Sopenharmony_ci	} else {
7208c2ecf20Sopenharmony_ci		aead_request_set_crypt(req, sg, sg, 0, iv);
7218c2ecf20Sopenharmony_ci		aead_request_set_ad(req, skb->len - secy->icv_len);
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	macsec_skb_cb(skb)->req = req;
7258c2ecf20Sopenharmony_ci	macsec_skb_cb(skb)->tx_sa = tx_sa;
7268c2ecf20Sopenharmony_ci	macsec_skb_cb(skb)->has_sci = sci_present;
7278c2ecf20Sopenharmony_ci	aead_request_set_callback(req, 0, macsec_encrypt_done, skb);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	dev_hold(skb->dev);
7308c2ecf20Sopenharmony_ci	ret = crypto_aead_encrypt(req);
7318c2ecf20Sopenharmony_ci	if (ret == -EINPROGRESS) {
7328c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
7338c2ecf20Sopenharmony_ci	} else if (ret != 0) {
7348c2ecf20Sopenharmony_ci		dev_put(skb->dev);
7358c2ecf20Sopenharmony_ci		kfree_skb(skb);
7368c2ecf20Sopenharmony_ci		aead_request_free(req);
7378c2ecf20Sopenharmony_ci		macsec_txsa_put(tx_sa);
7388c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	dev_put(skb->dev);
7428c2ecf20Sopenharmony_ci	aead_request_free(req);
7438c2ecf20Sopenharmony_ci	macsec_txsa_put(tx_sa);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	return skb;
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_cistatic bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u32 pn)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	struct macsec_rx_sa *rx_sa = macsec_skb_cb(skb)->rx_sa;
7518c2ecf20Sopenharmony_ci	struct pcpu_rx_sc_stats *rxsc_stats = this_cpu_ptr(rx_sa->sc->stats);
7528c2ecf20Sopenharmony_ci	struct macsec_eth_header *hdr = macsec_ethhdr(skb);
7538c2ecf20Sopenharmony_ci	u32 lowest_pn = 0;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	spin_lock(&rx_sa->lock);
7568c2ecf20Sopenharmony_ci	if (rx_sa->next_pn_halves.lower >= secy->replay_window)
7578c2ecf20Sopenharmony_ci		lowest_pn = rx_sa->next_pn_halves.lower - secy->replay_window;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* Now perform replay protection check again
7608c2ecf20Sopenharmony_ci	 * (see IEEE 802.1AE-2006 figure 10-5)
7618c2ecf20Sopenharmony_ci	 */
7628c2ecf20Sopenharmony_ci	if (secy->replay_protect && pn < lowest_pn &&
7638c2ecf20Sopenharmony_ci	    (!secy->xpn || pn_same_half(pn, lowest_pn))) {
7648c2ecf20Sopenharmony_ci		spin_unlock(&rx_sa->lock);
7658c2ecf20Sopenharmony_ci		u64_stats_update_begin(&rxsc_stats->syncp);
7668c2ecf20Sopenharmony_ci		rxsc_stats->stats.InPktsLate++;
7678c2ecf20Sopenharmony_ci		u64_stats_update_end(&rxsc_stats->syncp);
7688c2ecf20Sopenharmony_ci		DEV_STATS_INC(secy->netdev, rx_dropped);
7698c2ecf20Sopenharmony_ci		return false;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	if (secy->validate_frames != MACSEC_VALIDATE_DISABLED) {
7738c2ecf20Sopenharmony_ci		unsigned int msdu_len = macsec_msdu_len(skb);
7748c2ecf20Sopenharmony_ci		u64_stats_update_begin(&rxsc_stats->syncp);
7758c2ecf20Sopenharmony_ci		if (hdr->tci_an & MACSEC_TCI_E)
7768c2ecf20Sopenharmony_ci			rxsc_stats->stats.InOctetsDecrypted += msdu_len;
7778c2ecf20Sopenharmony_ci		else
7788c2ecf20Sopenharmony_ci			rxsc_stats->stats.InOctetsValidated += msdu_len;
7798c2ecf20Sopenharmony_ci		u64_stats_update_end(&rxsc_stats->syncp);
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (!macsec_skb_cb(skb)->valid) {
7838c2ecf20Sopenharmony_ci		spin_unlock(&rx_sa->lock);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		/* 10.6.5 */
7868c2ecf20Sopenharmony_ci		if (hdr->tci_an & MACSEC_TCI_C ||
7878c2ecf20Sopenharmony_ci		    secy->validate_frames == MACSEC_VALIDATE_STRICT) {
7888c2ecf20Sopenharmony_ci			u64_stats_update_begin(&rxsc_stats->syncp);
7898c2ecf20Sopenharmony_ci			rxsc_stats->stats.InPktsNotValid++;
7908c2ecf20Sopenharmony_ci			u64_stats_update_end(&rxsc_stats->syncp);
7918c2ecf20Sopenharmony_ci			this_cpu_inc(rx_sa->stats->InPktsNotValid);
7928c2ecf20Sopenharmony_ci			DEV_STATS_INC(secy->netdev, rx_errors);
7938c2ecf20Sopenharmony_ci			return false;
7948c2ecf20Sopenharmony_ci		}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci		u64_stats_update_begin(&rxsc_stats->syncp);
7978c2ecf20Sopenharmony_ci		if (secy->validate_frames == MACSEC_VALIDATE_CHECK) {
7988c2ecf20Sopenharmony_ci			rxsc_stats->stats.InPktsInvalid++;
7998c2ecf20Sopenharmony_ci			this_cpu_inc(rx_sa->stats->InPktsInvalid);
8008c2ecf20Sopenharmony_ci		} else if (pn < lowest_pn) {
8018c2ecf20Sopenharmony_ci			rxsc_stats->stats.InPktsDelayed++;
8028c2ecf20Sopenharmony_ci		} else {
8038c2ecf20Sopenharmony_ci			rxsc_stats->stats.InPktsUnchecked++;
8048c2ecf20Sopenharmony_ci		}
8058c2ecf20Sopenharmony_ci		u64_stats_update_end(&rxsc_stats->syncp);
8068c2ecf20Sopenharmony_ci	} else {
8078c2ecf20Sopenharmony_ci		u64_stats_update_begin(&rxsc_stats->syncp);
8088c2ecf20Sopenharmony_ci		if (pn < lowest_pn) {
8098c2ecf20Sopenharmony_ci			rxsc_stats->stats.InPktsDelayed++;
8108c2ecf20Sopenharmony_ci		} else {
8118c2ecf20Sopenharmony_ci			rxsc_stats->stats.InPktsOK++;
8128c2ecf20Sopenharmony_ci			this_cpu_inc(rx_sa->stats->InPktsOK);
8138c2ecf20Sopenharmony_ci		}
8148c2ecf20Sopenharmony_ci		u64_stats_update_end(&rxsc_stats->syncp);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci		// Instead of "pn >=" - to support pn overflow in xpn
8178c2ecf20Sopenharmony_ci		if (pn + 1 > rx_sa->next_pn_halves.lower) {
8188c2ecf20Sopenharmony_ci			rx_sa->next_pn_halves.lower = pn + 1;
8198c2ecf20Sopenharmony_ci		} else if (secy->xpn &&
8208c2ecf20Sopenharmony_ci			   !pn_same_half(pn, rx_sa->next_pn_halves.lower)) {
8218c2ecf20Sopenharmony_ci			rx_sa->next_pn_halves.upper++;
8228c2ecf20Sopenharmony_ci			rx_sa->next_pn_halves.lower = pn + 1;
8238c2ecf20Sopenharmony_ci		}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci		spin_unlock(&rx_sa->lock);
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	return true;
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cistatic void macsec_reset_skb(struct sk_buff *skb, struct net_device *dev)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	skb->pkt_type = PACKET_HOST;
8348c2ecf20Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
8378c2ecf20Sopenharmony_ci	if (!skb_transport_header_was_set(skb))
8388c2ecf20Sopenharmony_ci		skb_reset_transport_header(skb);
8398c2ecf20Sopenharmony_ci	skb_reset_mac_len(skb);
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic void macsec_finalize_skb(struct sk_buff *skb, u8 icv_len, u8 hdr_len)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
8458c2ecf20Sopenharmony_ci	memmove(skb->data + hdr_len, skb->data, 2 * ETH_ALEN);
8468c2ecf20Sopenharmony_ci	skb_pull(skb, hdr_len);
8478c2ecf20Sopenharmony_ci	pskb_trim_unique(skb, skb->len - icv_len);
8488c2ecf20Sopenharmony_ci}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_cistatic void count_rx(struct net_device *dev, int len)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	struct pcpu_sw_netstats *stats = this_cpu_ptr(dev->tstats);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	u64_stats_update_begin(&stats->syncp);
8558c2ecf20Sopenharmony_ci	stats->rx_packets++;
8568c2ecf20Sopenharmony_ci	stats->rx_bytes += len;
8578c2ecf20Sopenharmony_ci	u64_stats_update_end(&stats->syncp);
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic void macsec_decrypt_done(struct crypto_async_request *base, int err)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct sk_buff *skb = base->data;
8638c2ecf20Sopenharmony_ci	struct net_device *dev = skb->dev;
8648c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
8658c2ecf20Sopenharmony_ci	struct macsec_rx_sa *rx_sa = macsec_skb_cb(skb)->rx_sa;
8668c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc = rx_sa->sc;
8678c2ecf20Sopenharmony_ci	int len;
8688c2ecf20Sopenharmony_ci	u32 pn;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	aead_request_free(macsec_skb_cb(skb)->req);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	if (!err)
8738c2ecf20Sopenharmony_ci		macsec_skb_cb(skb)->valid = true;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	rcu_read_lock_bh();
8768c2ecf20Sopenharmony_ci	pn = ntohl(macsec_ethhdr(skb)->packet_number);
8778c2ecf20Sopenharmony_ci	if (!macsec_post_decrypt(skb, &macsec->secy, pn)) {
8788c2ecf20Sopenharmony_ci		rcu_read_unlock_bh();
8798c2ecf20Sopenharmony_ci		kfree_skb(skb);
8808c2ecf20Sopenharmony_ci		goto out;
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	macsec_finalize_skb(skb, macsec->secy.icv_len,
8848c2ecf20Sopenharmony_ci			    macsec_extra_len(macsec_skb_cb(skb)->has_sci));
8858c2ecf20Sopenharmony_ci	len = skb->len;
8868c2ecf20Sopenharmony_ci	macsec_reset_skb(skb, macsec->secy.netdev);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	if (gro_cells_receive(&macsec->gro_cells, skb) == NET_RX_SUCCESS)
8898c2ecf20Sopenharmony_ci		count_rx(dev, len);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	rcu_read_unlock_bh();
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ciout:
8948c2ecf20Sopenharmony_ci	macsec_rxsa_put(rx_sa);
8958c2ecf20Sopenharmony_ci	macsec_rxsc_put(rx_sc);
8968c2ecf20Sopenharmony_ci	dev_put(dev);
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_cistatic struct sk_buff *macsec_decrypt(struct sk_buff *skb,
9008c2ecf20Sopenharmony_ci				      struct net_device *dev,
9018c2ecf20Sopenharmony_ci				      struct macsec_rx_sa *rx_sa,
9028c2ecf20Sopenharmony_ci				      sci_t sci,
9038c2ecf20Sopenharmony_ci				      struct macsec_secy *secy)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	int ret;
9068c2ecf20Sopenharmony_ci	struct scatterlist *sg;
9078c2ecf20Sopenharmony_ci	struct sk_buff *trailer;
9088c2ecf20Sopenharmony_ci	unsigned char *iv;
9098c2ecf20Sopenharmony_ci	struct aead_request *req;
9108c2ecf20Sopenharmony_ci	struct macsec_eth_header *hdr;
9118c2ecf20Sopenharmony_ci	u32 hdr_pn;
9128c2ecf20Sopenharmony_ci	u16 icv_len = secy->icv_len;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	macsec_skb_cb(skb)->valid = false;
9158c2ecf20Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
9168c2ecf20Sopenharmony_ci	if (!skb)
9178c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	ret = skb_cow_data(skb, 0, &trailer);
9208c2ecf20Sopenharmony_ci	if (unlikely(ret < 0)) {
9218c2ecf20Sopenharmony_ci		kfree_skb(skb);
9228c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci	req = macsec_alloc_req(rx_sa->key.tfm, &iv, &sg, ret);
9258c2ecf20Sopenharmony_ci	if (!req) {
9268c2ecf20Sopenharmony_ci		kfree_skb(skb);
9278c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9288c2ecf20Sopenharmony_ci	}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	hdr = (struct macsec_eth_header *)skb->data;
9318c2ecf20Sopenharmony_ci	hdr_pn = ntohl(hdr->packet_number);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	if (secy->xpn) {
9348c2ecf20Sopenharmony_ci		pn_t recovered_pn = rx_sa->next_pn_halves;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci		recovered_pn.lower = hdr_pn;
9378c2ecf20Sopenharmony_ci		if (hdr_pn < rx_sa->next_pn_halves.lower &&
9388c2ecf20Sopenharmony_ci		    !pn_same_half(hdr_pn, rx_sa->next_pn_halves.lower))
9398c2ecf20Sopenharmony_ci			recovered_pn.upper++;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci		macsec_fill_iv_xpn(iv, rx_sa->ssci, recovered_pn.full64,
9428c2ecf20Sopenharmony_ci				   rx_sa->key.salt);
9438c2ecf20Sopenharmony_ci	} else {
9448c2ecf20Sopenharmony_ci		macsec_fill_iv(iv, sci, hdr_pn);
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	sg_init_table(sg, ret);
9488c2ecf20Sopenharmony_ci	ret = skb_to_sgvec(skb, sg, 0, skb->len);
9498c2ecf20Sopenharmony_ci	if (unlikely(ret < 0)) {
9508c2ecf20Sopenharmony_ci		aead_request_free(req);
9518c2ecf20Sopenharmony_ci		kfree_skb(skb);
9528c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	if (hdr->tci_an & MACSEC_TCI_E) {
9568c2ecf20Sopenharmony_ci		/* confidentiality: ethernet + macsec header
9578c2ecf20Sopenharmony_ci		 * authenticated, encrypted payload
9588c2ecf20Sopenharmony_ci		 */
9598c2ecf20Sopenharmony_ci		int len = skb->len - macsec_hdr_len(macsec_skb_cb(skb)->has_sci);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci		aead_request_set_crypt(req, sg, sg, len, iv);
9628c2ecf20Sopenharmony_ci		aead_request_set_ad(req, macsec_hdr_len(macsec_skb_cb(skb)->has_sci));
9638c2ecf20Sopenharmony_ci		skb = skb_unshare(skb, GFP_ATOMIC);
9648c2ecf20Sopenharmony_ci		if (!skb) {
9658c2ecf20Sopenharmony_ci			aead_request_free(req);
9668c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
9678c2ecf20Sopenharmony_ci		}
9688c2ecf20Sopenharmony_ci	} else {
9698c2ecf20Sopenharmony_ci		/* integrity only: all headers + data authenticated */
9708c2ecf20Sopenharmony_ci		aead_request_set_crypt(req, sg, sg, icv_len, iv);
9718c2ecf20Sopenharmony_ci		aead_request_set_ad(req, skb->len - icv_len);
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	macsec_skb_cb(skb)->req = req;
9758c2ecf20Sopenharmony_ci	skb->dev = dev;
9768c2ecf20Sopenharmony_ci	aead_request_set_callback(req, 0, macsec_decrypt_done, skb);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	dev_hold(dev);
9798c2ecf20Sopenharmony_ci	ret = crypto_aead_decrypt(req);
9808c2ecf20Sopenharmony_ci	if (ret == -EINPROGRESS) {
9818c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
9828c2ecf20Sopenharmony_ci	} else if (ret != 0) {
9838c2ecf20Sopenharmony_ci		/* decryption/authentication failed
9848c2ecf20Sopenharmony_ci		 * 10.6 if validateFrames is disabled, deliver anyway
9858c2ecf20Sopenharmony_ci		 */
9868c2ecf20Sopenharmony_ci		if (ret != -EBADMSG) {
9878c2ecf20Sopenharmony_ci			kfree_skb(skb);
9888c2ecf20Sopenharmony_ci			skb = ERR_PTR(ret);
9898c2ecf20Sopenharmony_ci		}
9908c2ecf20Sopenharmony_ci	} else {
9918c2ecf20Sopenharmony_ci		macsec_skb_cb(skb)->valid = true;
9928c2ecf20Sopenharmony_ci	}
9938c2ecf20Sopenharmony_ci	dev_put(dev);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	aead_request_free(req);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	return skb;
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_cistatic struct macsec_rx_sc *find_rx_sc(struct macsec_secy *secy, sci_t sci)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	for_each_rxsc(secy, rx_sc) {
10058c2ecf20Sopenharmony_ci		if (rx_sc->sci == sci)
10068c2ecf20Sopenharmony_ci			return rx_sc;
10078c2ecf20Sopenharmony_ci	}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	return NULL;
10108c2ecf20Sopenharmony_ci}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_cistatic struct macsec_rx_sc *find_rx_sc_rtnl(struct macsec_secy *secy, sci_t sci)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	for_each_rxsc_rtnl(secy, rx_sc) {
10178c2ecf20Sopenharmony_ci		if (rx_sc->sci == sci)
10188c2ecf20Sopenharmony_ci			return rx_sc;
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	return NULL;
10228c2ecf20Sopenharmony_ci}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_cistatic enum rx_handler_result handle_not_macsec(struct sk_buff *skb)
10258c2ecf20Sopenharmony_ci{
10268c2ecf20Sopenharmony_ci	/* Deliver to the uncontrolled port by default */
10278c2ecf20Sopenharmony_ci	enum rx_handler_result ret = RX_HANDLER_PASS;
10288c2ecf20Sopenharmony_ci	struct ethhdr *hdr = eth_hdr(skb);
10298c2ecf20Sopenharmony_ci	struct macsec_rxh_data *rxd;
10308c2ecf20Sopenharmony_ci	struct macsec_dev *macsec;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	rcu_read_lock();
10338c2ecf20Sopenharmony_ci	rxd = macsec_data_rcu(skb->dev);
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(macsec, &rxd->secys, secys) {
10368c2ecf20Sopenharmony_ci		struct sk_buff *nskb;
10378c2ecf20Sopenharmony_ci		struct pcpu_secy_stats *secy_stats = this_cpu_ptr(macsec->stats);
10388c2ecf20Sopenharmony_ci		struct net_device *ndev = macsec->secy.netdev;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci		/* If h/w offloading is enabled, HW decodes frames and strips
10418c2ecf20Sopenharmony_ci		 * the SecTAG, so we have to deduce which port to deliver to.
10428c2ecf20Sopenharmony_ci		 */
10438c2ecf20Sopenharmony_ci		if (macsec_is_offloaded(macsec) && netif_running(ndev)) {
10448c2ecf20Sopenharmony_ci			if (ether_addr_equal_64bits(hdr->h_dest,
10458c2ecf20Sopenharmony_ci						    ndev->dev_addr)) {
10468c2ecf20Sopenharmony_ci				/* exact match, divert skb to this port */
10478c2ecf20Sopenharmony_ci				skb->dev = ndev;
10488c2ecf20Sopenharmony_ci				skb->pkt_type = PACKET_HOST;
10498c2ecf20Sopenharmony_ci				ret = RX_HANDLER_ANOTHER;
10508c2ecf20Sopenharmony_ci				goto out;
10518c2ecf20Sopenharmony_ci			} else if (is_multicast_ether_addr_64bits(
10528c2ecf20Sopenharmony_ci					   hdr->h_dest)) {
10538c2ecf20Sopenharmony_ci				/* multicast frame, deliver on this port too */
10548c2ecf20Sopenharmony_ci				nskb = skb_clone(skb, GFP_ATOMIC);
10558c2ecf20Sopenharmony_ci				if (!nskb)
10568c2ecf20Sopenharmony_ci					break;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci				nskb->dev = ndev;
10598c2ecf20Sopenharmony_ci				if (ether_addr_equal_64bits(hdr->h_dest,
10608c2ecf20Sopenharmony_ci							    ndev->broadcast))
10618c2ecf20Sopenharmony_ci					nskb->pkt_type = PACKET_BROADCAST;
10628c2ecf20Sopenharmony_ci				else
10638c2ecf20Sopenharmony_ci					nskb->pkt_type = PACKET_MULTICAST;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci				netif_rx(nskb);
10668c2ecf20Sopenharmony_ci			}
10678c2ecf20Sopenharmony_ci			continue;
10688c2ecf20Sopenharmony_ci		}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci		/* 10.6 If the management control validateFrames is not
10718c2ecf20Sopenharmony_ci		 * Strict, frames without a SecTAG are received, counted, and
10728c2ecf20Sopenharmony_ci		 * delivered to the Controlled Port
10738c2ecf20Sopenharmony_ci		 */
10748c2ecf20Sopenharmony_ci		if (macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) {
10758c2ecf20Sopenharmony_ci			u64_stats_update_begin(&secy_stats->syncp);
10768c2ecf20Sopenharmony_ci			secy_stats->stats.InPktsNoTag++;
10778c2ecf20Sopenharmony_ci			u64_stats_update_end(&secy_stats->syncp);
10788c2ecf20Sopenharmony_ci			DEV_STATS_INC(macsec->secy.netdev, rx_dropped);
10798c2ecf20Sopenharmony_ci			continue;
10808c2ecf20Sopenharmony_ci		}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci		/* deliver on this port */
10838c2ecf20Sopenharmony_ci		nskb = skb_clone(skb, GFP_ATOMIC);
10848c2ecf20Sopenharmony_ci		if (!nskb)
10858c2ecf20Sopenharmony_ci			break;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci		nskb->dev = ndev;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci		if (netif_rx(nskb) == NET_RX_SUCCESS) {
10908c2ecf20Sopenharmony_ci			u64_stats_update_begin(&secy_stats->syncp);
10918c2ecf20Sopenharmony_ci			secy_stats->stats.InPktsUntagged++;
10928c2ecf20Sopenharmony_ci			u64_stats_update_end(&secy_stats->syncp);
10938c2ecf20Sopenharmony_ci		}
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ciout:
10978c2ecf20Sopenharmony_ci	rcu_read_unlock();
10988c2ecf20Sopenharmony_ci	return ret;
10998c2ecf20Sopenharmony_ci}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_cistatic rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb)
11028c2ecf20Sopenharmony_ci{
11038c2ecf20Sopenharmony_ci	struct sk_buff *skb = *pskb;
11048c2ecf20Sopenharmony_ci	struct net_device *dev = skb->dev;
11058c2ecf20Sopenharmony_ci	struct macsec_eth_header *hdr;
11068c2ecf20Sopenharmony_ci	struct macsec_secy *secy = NULL;
11078c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
11088c2ecf20Sopenharmony_ci	struct macsec_rx_sa *rx_sa;
11098c2ecf20Sopenharmony_ci	struct macsec_rxh_data *rxd;
11108c2ecf20Sopenharmony_ci	struct macsec_dev *macsec;
11118c2ecf20Sopenharmony_ci	unsigned int len;
11128c2ecf20Sopenharmony_ci	sci_t sci;
11138c2ecf20Sopenharmony_ci	u32 hdr_pn;
11148c2ecf20Sopenharmony_ci	bool cbit;
11158c2ecf20Sopenharmony_ci	struct pcpu_rx_sc_stats *rxsc_stats;
11168c2ecf20Sopenharmony_ci	struct pcpu_secy_stats *secy_stats;
11178c2ecf20Sopenharmony_ci	bool pulled_sci;
11188c2ecf20Sopenharmony_ci	int ret;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (skb_headroom(skb) < ETH_HLEN)
11218c2ecf20Sopenharmony_ci		goto drop_direct;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	hdr = macsec_ethhdr(skb);
11248c2ecf20Sopenharmony_ci	if (hdr->eth.h_proto != htons(ETH_P_MACSEC))
11258c2ecf20Sopenharmony_ci		return handle_not_macsec(skb);
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	skb = skb_unshare(skb, GFP_ATOMIC);
11288c2ecf20Sopenharmony_ci	*pskb = skb;
11298c2ecf20Sopenharmony_ci	if (!skb)
11308c2ecf20Sopenharmony_ci		return RX_HANDLER_CONSUMED;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	pulled_sci = pskb_may_pull(skb, macsec_extra_len(true));
11338c2ecf20Sopenharmony_ci	if (!pulled_sci) {
11348c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, macsec_extra_len(false)))
11358c2ecf20Sopenharmony_ci			goto drop_direct;
11368c2ecf20Sopenharmony_ci	}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	hdr = macsec_ethhdr(skb);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	/* Frames with a SecTAG that has the TCI E bit set but the C
11418c2ecf20Sopenharmony_ci	 * bit clear are discarded, as this reserved encoding is used
11428c2ecf20Sopenharmony_ci	 * to identify frames with a SecTAG that are not to be
11438c2ecf20Sopenharmony_ci	 * delivered to the Controlled Port.
11448c2ecf20Sopenharmony_ci	 */
11458c2ecf20Sopenharmony_ci	if ((hdr->tci_an & (MACSEC_TCI_C | MACSEC_TCI_E)) == MACSEC_TCI_E)
11468c2ecf20Sopenharmony_ci		return RX_HANDLER_PASS;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	/* now, pull the extra length */
11498c2ecf20Sopenharmony_ci	if (hdr->tci_an & MACSEC_TCI_SC) {
11508c2ecf20Sopenharmony_ci		if (!pulled_sci)
11518c2ecf20Sopenharmony_ci			goto drop_direct;
11528c2ecf20Sopenharmony_ci	}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	/* ethernet header is part of crypto processing */
11558c2ecf20Sopenharmony_ci	skb_push(skb, ETH_HLEN);
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	macsec_skb_cb(skb)->has_sci = !!(hdr->tci_an & MACSEC_TCI_SC);
11588c2ecf20Sopenharmony_ci	macsec_skb_cb(skb)->assoc_num = hdr->tci_an & MACSEC_AN_MASK;
11598c2ecf20Sopenharmony_ci	sci = macsec_frame_sci(hdr, macsec_skb_cb(skb)->has_sci);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	rcu_read_lock();
11628c2ecf20Sopenharmony_ci	rxd = macsec_data_rcu(skb->dev);
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(macsec, &rxd->secys, secys) {
11658c2ecf20Sopenharmony_ci		struct macsec_rx_sc *sc = find_rx_sc(&macsec->secy, sci);
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci		sc = sc ? macsec_rxsc_get(sc) : NULL;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci		if (sc) {
11708c2ecf20Sopenharmony_ci			secy = &macsec->secy;
11718c2ecf20Sopenharmony_ci			rx_sc = sc;
11728c2ecf20Sopenharmony_ci			break;
11738c2ecf20Sopenharmony_ci		}
11748c2ecf20Sopenharmony_ci	}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	if (!secy)
11778c2ecf20Sopenharmony_ci		goto nosci;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	dev = secy->netdev;
11808c2ecf20Sopenharmony_ci	macsec = macsec_priv(dev);
11818c2ecf20Sopenharmony_ci	secy_stats = this_cpu_ptr(macsec->stats);
11828c2ecf20Sopenharmony_ci	rxsc_stats = this_cpu_ptr(rx_sc->stats);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	if (!macsec_validate_skb(skb, secy->icv_len, secy->xpn)) {
11858c2ecf20Sopenharmony_ci		u64_stats_update_begin(&secy_stats->syncp);
11868c2ecf20Sopenharmony_ci		secy_stats->stats.InPktsBadTag++;
11878c2ecf20Sopenharmony_ci		u64_stats_update_end(&secy_stats->syncp);
11888c2ecf20Sopenharmony_ci		DEV_STATS_INC(secy->netdev, rx_errors);
11898c2ecf20Sopenharmony_ci		goto drop_nosa;
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	rx_sa = macsec_rxsa_get(rx_sc->sa[macsec_skb_cb(skb)->assoc_num]);
11938c2ecf20Sopenharmony_ci	if (!rx_sa) {
11948c2ecf20Sopenharmony_ci		/* 10.6.1 if the SA is not in use */
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci		/* If validateFrames is Strict or the C bit in the
11978c2ecf20Sopenharmony_ci		 * SecTAG is set, discard
11988c2ecf20Sopenharmony_ci		 */
11998c2ecf20Sopenharmony_ci		struct macsec_rx_sa *active_rx_sa = macsec_active_rxsa_get(rx_sc);
12008c2ecf20Sopenharmony_ci		if (hdr->tci_an & MACSEC_TCI_C ||
12018c2ecf20Sopenharmony_ci		    secy->validate_frames == MACSEC_VALIDATE_STRICT) {
12028c2ecf20Sopenharmony_ci			u64_stats_update_begin(&rxsc_stats->syncp);
12038c2ecf20Sopenharmony_ci			rxsc_stats->stats.InPktsNotUsingSA++;
12048c2ecf20Sopenharmony_ci			u64_stats_update_end(&rxsc_stats->syncp);
12058c2ecf20Sopenharmony_ci			DEV_STATS_INC(secy->netdev, rx_errors);
12068c2ecf20Sopenharmony_ci			if (active_rx_sa)
12078c2ecf20Sopenharmony_ci				this_cpu_inc(active_rx_sa->stats->InPktsNotUsingSA);
12088c2ecf20Sopenharmony_ci			goto drop_nosa;
12098c2ecf20Sopenharmony_ci		}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci		/* not Strict, the frame (with the SecTAG and ICV
12128c2ecf20Sopenharmony_ci		 * removed) is delivered to the Controlled Port.
12138c2ecf20Sopenharmony_ci		 */
12148c2ecf20Sopenharmony_ci		u64_stats_update_begin(&rxsc_stats->syncp);
12158c2ecf20Sopenharmony_ci		rxsc_stats->stats.InPktsUnusedSA++;
12168c2ecf20Sopenharmony_ci		u64_stats_update_end(&rxsc_stats->syncp);
12178c2ecf20Sopenharmony_ci		if (active_rx_sa)
12188c2ecf20Sopenharmony_ci			this_cpu_inc(active_rx_sa->stats->InPktsUnusedSA);
12198c2ecf20Sopenharmony_ci		goto deliver;
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	/* First, PN check to avoid decrypting obviously wrong packets */
12238c2ecf20Sopenharmony_ci	hdr_pn = ntohl(hdr->packet_number);
12248c2ecf20Sopenharmony_ci	if (secy->replay_protect) {
12258c2ecf20Sopenharmony_ci		bool late;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci		spin_lock(&rx_sa->lock);
12288c2ecf20Sopenharmony_ci		late = rx_sa->next_pn_halves.lower >= secy->replay_window &&
12298c2ecf20Sopenharmony_ci		       hdr_pn < (rx_sa->next_pn_halves.lower - secy->replay_window);
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci		if (secy->xpn)
12328c2ecf20Sopenharmony_ci			late = late && pn_same_half(rx_sa->next_pn_halves.lower, hdr_pn);
12338c2ecf20Sopenharmony_ci		spin_unlock(&rx_sa->lock);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci		if (late) {
12368c2ecf20Sopenharmony_ci			u64_stats_update_begin(&rxsc_stats->syncp);
12378c2ecf20Sopenharmony_ci			rxsc_stats->stats.InPktsLate++;
12388c2ecf20Sopenharmony_ci			u64_stats_update_end(&rxsc_stats->syncp);
12398c2ecf20Sopenharmony_ci			DEV_STATS_INC(macsec->secy.netdev, rx_dropped);
12408c2ecf20Sopenharmony_ci			goto drop;
12418c2ecf20Sopenharmony_ci		}
12428c2ecf20Sopenharmony_ci	}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	macsec_skb_cb(skb)->rx_sa = rx_sa;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	/* Disabled && !changed text => skip validation */
12478c2ecf20Sopenharmony_ci	if (hdr->tci_an & MACSEC_TCI_C ||
12488c2ecf20Sopenharmony_ci	    secy->validate_frames != MACSEC_VALIDATE_DISABLED)
12498c2ecf20Sopenharmony_ci		skb = macsec_decrypt(skb, dev, rx_sa, sci, secy);
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	if (IS_ERR(skb)) {
12528c2ecf20Sopenharmony_ci		/* the decrypt callback needs the reference */
12538c2ecf20Sopenharmony_ci		if (PTR_ERR(skb) != -EINPROGRESS) {
12548c2ecf20Sopenharmony_ci			macsec_rxsa_put(rx_sa);
12558c2ecf20Sopenharmony_ci			macsec_rxsc_put(rx_sc);
12568c2ecf20Sopenharmony_ci		}
12578c2ecf20Sopenharmony_ci		rcu_read_unlock();
12588c2ecf20Sopenharmony_ci		*pskb = NULL;
12598c2ecf20Sopenharmony_ci		return RX_HANDLER_CONSUMED;
12608c2ecf20Sopenharmony_ci	}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	if (!macsec_post_decrypt(skb, secy, hdr_pn))
12638c2ecf20Sopenharmony_ci		goto drop;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cideliver:
12668c2ecf20Sopenharmony_ci	macsec_finalize_skb(skb, secy->icv_len,
12678c2ecf20Sopenharmony_ci			    macsec_extra_len(macsec_skb_cb(skb)->has_sci));
12688c2ecf20Sopenharmony_ci	len = skb->len;
12698c2ecf20Sopenharmony_ci	macsec_reset_skb(skb, secy->netdev);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	if (rx_sa)
12728c2ecf20Sopenharmony_ci		macsec_rxsa_put(rx_sa);
12738c2ecf20Sopenharmony_ci	macsec_rxsc_put(rx_sc);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	skb_orphan(skb);
12768c2ecf20Sopenharmony_ci	ret = gro_cells_receive(&macsec->gro_cells, skb);
12778c2ecf20Sopenharmony_ci	if (ret == NET_RX_SUCCESS)
12788c2ecf20Sopenharmony_ci		count_rx(dev, len);
12798c2ecf20Sopenharmony_ci	else
12808c2ecf20Sopenharmony_ci		DEV_STATS_INC(macsec->secy.netdev, rx_dropped);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	rcu_read_unlock();
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	*pskb = NULL;
12858c2ecf20Sopenharmony_ci	return RX_HANDLER_CONSUMED;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cidrop:
12888c2ecf20Sopenharmony_ci	macsec_rxsa_put(rx_sa);
12898c2ecf20Sopenharmony_cidrop_nosa:
12908c2ecf20Sopenharmony_ci	macsec_rxsc_put(rx_sc);
12918c2ecf20Sopenharmony_ci	rcu_read_unlock();
12928c2ecf20Sopenharmony_cidrop_direct:
12938c2ecf20Sopenharmony_ci	kfree_skb(skb);
12948c2ecf20Sopenharmony_ci	*pskb = NULL;
12958c2ecf20Sopenharmony_ci	return RX_HANDLER_CONSUMED;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_cinosci:
12988c2ecf20Sopenharmony_ci	/* 10.6.1 if the SC is not found */
12998c2ecf20Sopenharmony_ci	cbit = !!(hdr->tci_an & MACSEC_TCI_C);
13008c2ecf20Sopenharmony_ci	if (!cbit)
13018c2ecf20Sopenharmony_ci		macsec_finalize_skb(skb, DEFAULT_ICV_LEN,
13028c2ecf20Sopenharmony_ci				    macsec_extra_len(macsec_skb_cb(skb)->has_sci));
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(macsec, &rxd->secys, secys) {
13058c2ecf20Sopenharmony_ci		struct sk_buff *nskb;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci		secy_stats = this_cpu_ptr(macsec->stats);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci		/* If validateFrames is Strict or the C bit in the
13108c2ecf20Sopenharmony_ci		 * SecTAG is set, discard
13118c2ecf20Sopenharmony_ci		 */
13128c2ecf20Sopenharmony_ci		if (cbit ||
13138c2ecf20Sopenharmony_ci		    macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) {
13148c2ecf20Sopenharmony_ci			u64_stats_update_begin(&secy_stats->syncp);
13158c2ecf20Sopenharmony_ci			secy_stats->stats.InPktsNoSCI++;
13168c2ecf20Sopenharmony_ci			u64_stats_update_end(&secy_stats->syncp);
13178c2ecf20Sopenharmony_ci			DEV_STATS_INC(macsec->secy.netdev, rx_errors);
13188c2ecf20Sopenharmony_ci			continue;
13198c2ecf20Sopenharmony_ci		}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci		/* not strict, the frame (with the SecTAG and ICV
13228c2ecf20Sopenharmony_ci		 * removed) is delivered to the Controlled Port.
13238c2ecf20Sopenharmony_ci		 */
13248c2ecf20Sopenharmony_ci		nskb = skb_clone(skb, GFP_ATOMIC);
13258c2ecf20Sopenharmony_ci		if (!nskb)
13268c2ecf20Sopenharmony_ci			break;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci		macsec_reset_skb(nskb, macsec->secy.netdev);
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci		ret = netif_rx(nskb);
13318c2ecf20Sopenharmony_ci		if (ret == NET_RX_SUCCESS) {
13328c2ecf20Sopenharmony_ci			u64_stats_update_begin(&secy_stats->syncp);
13338c2ecf20Sopenharmony_ci			secy_stats->stats.InPktsUnknownSCI++;
13348c2ecf20Sopenharmony_ci			u64_stats_update_end(&secy_stats->syncp);
13358c2ecf20Sopenharmony_ci		} else {
13368c2ecf20Sopenharmony_ci			DEV_STATS_INC(macsec->secy.netdev, rx_dropped);
13378c2ecf20Sopenharmony_ci		}
13388c2ecf20Sopenharmony_ci	}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	rcu_read_unlock();
13418c2ecf20Sopenharmony_ci	*pskb = skb;
13428c2ecf20Sopenharmony_ci	return RX_HANDLER_PASS;
13438c2ecf20Sopenharmony_ci}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_cistatic struct crypto_aead *macsec_alloc_tfm(char *key, int key_len, int icv_len)
13468c2ecf20Sopenharmony_ci{
13478c2ecf20Sopenharmony_ci	struct crypto_aead *tfm;
13488c2ecf20Sopenharmony_ci	int ret;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	if (IS_ERR(tfm))
13538c2ecf20Sopenharmony_ci		return tfm;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	ret = crypto_aead_setkey(tfm, key, key_len);
13568c2ecf20Sopenharmony_ci	if (ret < 0)
13578c2ecf20Sopenharmony_ci		goto fail;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	ret = crypto_aead_setauthsize(tfm, icv_len);
13608c2ecf20Sopenharmony_ci	if (ret < 0)
13618c2ecf20Sopenharmony_ci		goto fail;
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	return tfm;
13648c2ecf20Sopenharmony_cifail:
13658c2ecf20Sopenharmony_ci	crypto_free_aead(tfm);
13668c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
13678c2ecf20Sopenharmony_ci}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_cistatic int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len,
13708c2ecf20Sopenharmony_ci		      int icv_len)
13718c2ecf20Sopenharmony_ci{
13728c2ecf20Sopenharmony_ci	rx_sa->stats = alloc_percpu(struct macsec_rx_sa_stats);
13738c2ecf20Sopenharmony_ci	if (!rx_sa->stats)
13748c2ecf20Sopenharmony_ci		return -ENOMEM;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	rx_sa->key.tfm = macsec_alloc_tfm(sak, key_len, icv_len);
13778c2ecf20Sopenharmony_ci	if (IS_ERR(rx_sa->key.tfm)) {
13788c2ecf20Sopenharmony_ci		free_percpu(rx_sa->stats);
13798c2ecf20Sopenharmony_ci		return PTR_ERR(rx_sa->key.tfm);
13808c2ecf20Sopenharmony_ci	}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	rx_sa->ssci = MACSEC_UNDEF_SSCI;
13838c2ecf20Sopenharmony_ci	rx_sa->active = false;
13848c2ecf20Sopenharmony_ci	rx_sa->next_pn = 1;
13858c2ecf20Sopenharmony_ci	refcount_set(&rx_sa->refcnt, 1);
13868c2ecf20Sopenharmony_ci	spin_lock_init(&rx_sa->lock);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	return 0;
13898c2ecf20Sopenharmony_ci}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_cistatic void clear_rx_sa(struct macsec_rx_sa *rx_sa)
13928c2ecf20Sopenharmony_ci{
13938c2ecf20Sopenharmony_ci	rx_sa->active = false;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	macsec_rxsa_put(rx_sa);
13968c2ecf20Sopenharmony_ci}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_cistatic void free_rx_sc(struct macsec_rx_sc *rx_sc)
13998c2ecf20Sopenharmony_ci{
14008c2ecf20Sopenharmony_ci	int i;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	for (i = 0; i < MACSEC_NUM_AN; i++) {
14038c2ecf20Sopenharmony_ci		struct macsec_rx_sa *sa = rtnl_dereference(rx_sc->sa[i]);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(rx_sc->sa[i], NULL);
14068c2ecf20Sopenharmony_ci		if (sa)
14078c2ecf20Sopenharmony_ci			clear_rx_sa(sa);
14088c2ecf20Sopenharmony_ci	}
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	macsec_rxsc_put(rx_sc);
14118c2ecf20Sopenharmony_ci}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cistatic struct macsec_rx_sc *del_rx_sc(struct macsec_secy *secy, sci_t sci)
14148c2ecf20Sopenharmony_ci{
14158c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc, __rcu **rx_scp;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	for (rx_scp = &secy->rx_sc, rx_sc = rtnl_dereference(*rx_scp);
14188c2ecf20Sopenharmony_ci	     rx_sc;
14198c2ecf20Sopenharmony_ci	     rx_scp = &rx_sc->next, rx_sc = rtnl_dereference(*rx_scp)) {
14208c2ecf20Sopenharmony_ci		if (rx_sc->sci == sci) {
14218c2ecf20Sopenharmony_ci			if (rx_sc->active)
14228c2ecf20Sopenharmony_ci				secy->n_rx_sc--;
14238c2ecf20Sopenharmony_ci			rcu_assign_pointer(*rx_scp, rx_sc->next);
14248c2ecf20Sopenharmony_ci			return rx_sc;
14258c2ecf20Sopenharmony_ci		}
14268c2ecf20Sopenharmony_ci	}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	return NULL;
14298c2ecf20Sopenharmony_ci}
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_cistatic struct macsec_rx_sc *create_rx_sc(struct net_device *dev, sci_t sci,
14328c2ecf20Sopenharmony_ci					 bool active)
14338c2ecf20Sopenharmony_ci{
14348c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
14358c2ecf20Sopenharmony_ci	struct macsec_dev *macsec;
14368c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec_priv(dev)->real_dev;
14378c2ecf20Sopenharmony_ci	struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
14388c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	list_for_each_entry(macsec, &rxd->secys, secys) {
14418c2ecf20Sopenharmony_ci		if (find_rx_sc_rtnl(&macsec->secy, sci))
14428c2ecf20Sopenharmony_ci			return ERR_PTR(-EEXIST);
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	rx_sc = kzalloc(sizeof(*rx_sc), GFP_KERNEL);
14468c2ecf20Sopenharmony_ci	if (!rx_sc)
14478c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	rx_sc->stats = netdev_alloc_pcpu_stats(struct pcpu_rx_sc_stats);
14508c2ecf20Sopenharmony_ci	if (!rx_sc->stats) {
14518c2ecf20Sopenharmony_ci		kfree(rx_sc);
14528c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
14538c2ecf20Sopenharmony_ci	}
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	rx_sc->sci = sci;
14568c2ecf20Sopenharmony_ci	rx_sc->active = active;
14578c2ecf20Sopenharmony_ci	refcount_set(&rx_sc->refcnt, 1);
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	secy = &macsec_priv(dev)->secy;
14608c2ecf20Sopenharmony_ci	rcu_assign_pointer(rx_sc->next, secy->rx_sc);
14618c2ecf20Sopenharmony_ci	rcu_assign_pointer(secy->rx_sc, rx_sc);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	if (rx_sc->active)
14648c2ecf20Sopenharmony_ci		secy->n_rx_sc++;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	return rx_sc;
14678c2ecf20Sopenharmony_ci}
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_cistatic int init_tx_sa(struct macsec_tx_sa *tx_sa, char *sak, int key_len,
14708c2ecf20Sopenharmony_ci		      int icv_len)
14718c2ecf20Sopenharmony_ci{
14728c2ecf20Sopenharmony_ci	tx_sa->stats = alloc_percpu(struct macsec_tx_sa_stats);
14738c2ecf20Sopenharmony_ci	if (!tx_sa->stats)
14748c2ecf20Sopenharmony_ci		return -ENOMEM;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	tx_sa->key.tfm = macsec_alloc_tfm(sak, key_len, icv_len);
14778c2ecf20Sopenharmony_ci	if (IS_ERR(tx_sa->key.tfm)) {
14788c2ecf20Sopenharmony_ci		free_percpu(tx_sa->stats);
14798c2ecf20Sopenharmony_ci		return PTR_ERR(tx_sa->key.tfm);
14808c2ecf20Sopenharmony_ci	}
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	tx_sa->ssci = MACSEC_UNDEF_SSCI;
14838c2ecf20Sopenharmony_ci	tx_sa->active = false;
14848c2ecf20Sopenharmony_ci	refcount_set(&tx_sa->refcnt, 1);
14858c2ecf20Sopenharmony_ci	spin_lock_init(&tx_sa->lock);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	return 0;
14888c2ecf20Sopenharmony_ci}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_cistatic void clear_tx_sa(struct macsec_tx_sa *tx_sa)
14918c2ecf20Sopenharmony_ci{
14928c2ecf20Sopenharmony_ci	tx_sa->active = false;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	macsec_txsa_put(tx_sa);
14958c2ecf20Sopenharmony_ci}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_cistatic struct genl_family macsec_fam;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_cistatic struct net_device *get_dev_from_nl(struct net *net,
15008c2ecf20Sopenharmony_ci					  struct nlattr **attrs)
15018c2ecf20Sopenharmony_ci{
15028c2ecf20Sopenharmony_ci	int ifindex = nla_get_u32(attrs[MACSEC_ATTR_IFINDEX]);
15038c2ecf20Sopenharmony_ci	struct net_device *dev;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	dev = __dev_get_by_index(net, ifindex);
15068c2ecf20Sopenharmony_ci	if (!dev)
15078c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (!netif_is_macsec(dev))
15108c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	return dev;
15138c2ecf20Sopenharmony_ci}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_cistatic enum macsec_offload nla_get_offload(const struct nlattr *nla)
15168c2ecf20Sopenharmony_ci{
15178c2ecf20Sopenharmony_ci	return (__force enum macsec_offload)nla_get_u8(nla);
15188c2ecf20Sopenharmony_ci}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_cistatic sci_t nla_get_sci(const struct nlattr *nla)
15218c2ecf20Sopenharmony_ci{
15228c2ecf20Sopenharmony_ci	return (__force sci_t)nla_get_u64(nla);
15238c2ecf20Sopenharmony_ci}
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_cistatic int nla_put_sci(struct sk_buff *skb, int attrtype, sci_t value,
15268c2ecf20Sopenharmony_ci		       int padattr)
15278c2ecf20Sopenharmony_ci{
15288c2ecf20Sopenharmony_ci	return nla_put_u64_64bit(skb, attrtype, (__force u64)value, padattr);
15298c2ecf20Sopenharmony_ci}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_cistatic ssci_t nla_get_ssci(const struct nlattr *nla)
15328c2ecf20Sopenharmony_ci{
15338c2ecf20Sopenharmony_ci	return (__force ssci_t)nla_get_u32(nla);
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_cistatic int nla_put_ssci(struct sk_buff *skb, int attrtype, ssci_t value)
15378c2ecf20Sopenharmony_ci{
15388c2ecf20Sopenharmony_ci	return nla_put_u32(skb, attrtype, (__force u64)value);
15398c2ecf20Sopenharmony_ci}
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_cistatic struct macsec_tx_sa *get_txsa_from_nl(struct net *net,
15428c2ecf20Sopenharmony_ci					     struct nlattr **attrs,
15438c2ecf20Sopenharmony_ci					     struct nlattr **tb_sa,
15448c2ecf20Sopenharmony_ci					     struct net_device **devp,
15458c2ecf20Sopenharmony_ci					     struct macsec_secy **secyp,
15468c2ecf20Sopenharmony_ci					     struct macsec_tx_sc **scp,
15478c2ecf20Sopenharmony_ci					     u8 *assoc_num)
15488c2ecf20Sopenharmony_ci{
15498c2ecf20Sopenharmony_ci	struct net_device *dev;
15508c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
15518c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc;
15528c2ecf20Sopenharmony_ci	struct macsec_tx_sa *tx_sa;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	if (!tb_sa[MACSEC_SA_ATTR_AN])
15558c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	*assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	dev = get_dev_from_nl(net, attrs);
15608c2ecf20Sopenharmony_ci	if (IS_ERR(dev))
15618c2ecf20Sopenharmony_ci		return ERR_CAST(dev);
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	if (*assoc_num >= MACSEC_NUM_AN)
15648c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	secy = &macsec_priv(dev)->secy;
15678c2ecf20Sopenharmony_ci	tx_sc = &secy->tx_sc;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	tx_sa = rtnl_dereference(tx_sc->sa[*assoc_num]);
15708c2ecf20Sopenharmony_ci	if (!tx_sa)
15718c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	*devp = dev;
15748c2ecf20Sopenharmony_ci	*scp = tx_sc;
15758c2ecf20Sopenharmony_ci	*secyp = secy;
15768c2ecf20Sopenharmony_ci	return tx_sa;
15778c2ecf20Sopenharmony_ci}
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_cistatic struct macsec_rx_sc *get_rxsc_from_nl(struct net *net,
15808c2ecf20Sopenharmony_ci					     struct nlattr **attrs,
15818c2ecf20Sopenharmony_ci					     struct nlattr **tb_rxsc,
15828c2ecf20Sopenharmony_ci					     struct net_device **devp,
15838c2ecf20Sopenharmony_ci					     struct macsec_secy **secyp)
15848c2ecf20Sopenharmony_ci{
15858c2ecf20Sopenharmony_ci	struct net_device *dev;
15868c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
15878c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
15888c2ecf20Sopenharmony_ci	sci_t sci;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	dev = get_dev_from_nl(net, attrs);
15918c2ecf20Sopenharmony_ci	if (IS_ERR(dev))
15928c2ecf20Sopenharmony_ci		return ERR_CAST(dev);
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	secy = &macsec_priv(dev)->secy;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	if (!tb_rxsc[MACSEC_RXSC_ATTR_SCI])
15978c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]);
16008c2ecf20Sopenharmony_ci	rx_sc = find_rx_sc_rtnl(secy, sci);
16018c2ecf20Sopenharmony_ci	if (!rx_sc)
16028c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	*secyp = secy;
16058c2ecf20Sopenharmony_ci	*devp = dev;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	return rx_sc;
16088c2ecf20Sopenharmony_ci}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic struct macsec_rx_sa *get_rxsa_from_nl(struct net *net,
16118c2ecf20Sopenharmony_ci					     struct nlattr **attrs,
16128c2ecf20Sopenharmony_ci					     struct nlattr **tb_rxsc,
16138c2ecf20Sopenharmony_ci					     struct nlattr **tb_sa,
16148c2ecf20Sopenharmony_ci					     struct net_device **devp,
16158c2ecf20Sopenharmony_ci					     struct macsec_secy **secyp,
16168c2ecf20Sopenharmony_ci					     struct macsec_rx_sc **scp,
16178c2ecf20Sopenharmony_ci					     u8 *assoc_num)
16188c2ecf20Sopenharmony_ci{
16198c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
16208c2ecf20Sopenharmony_ci	struct macsec_rx_sa *rx_sa;
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci	if (!tb_sa[MACSEC_SA_ATTR_AN])
16238c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	*assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
16268c2ecf20Sopenharmony_ci	if (*assoc_num >= MACSEC_NUM_AN)
16278c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	rx_sc = get_rxsc_from_nl(net, attrs, tb_rxsc, devp, secyp);
16308c2ecf20Sopenharmony_ci	if (IS_ERR(rx_sc))
16318c2ecf20Sopenharmony_ci		return ERR_CAST(rx_sc);
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	rx_sa = rtnl_dereference(rx_sc->sa[*assoc_num]);
16348c2ecf20Sopenharmony_ci	if (!rx_sa)
16358c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	*scp = rx_sc;
16388c2ecf20Sopenharmony_ci	return rx_sa;
16398c2ecf20Sopenharmony_ci}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_cistatic const struct nla_policy macsec_genl_policy[NUM_MACSEC_ATTR] = {
16428c2ecf20Sopenharmony_ci	[MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
16438c2ecf20Sopenharmony_ci	[MACSEC_ATTR_RXSC_CONFIG] = { .type = NLA_NESTED },
16448c2ecf20Sopenharmony_ci	[MACSEC_ATTR_SA_CONFIG] = { .type = NLA_NESTED },
16458c2ecf20Sopenharmony_ci	[MACSEC_ATTR_OFFLOAD] = { .type = NLA_NESTED },
16468c2ecf20Sopenharmony_ci};
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_cistatic const struct nla_policy macsec_genl_rxsc_policy[NUM_MACSEC_RXSC_ATTR] = {
16498c2ecf20Sopenharmony_ci	[MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
16508c2ecf20Sopenharmony_ci	[MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
16518c2ecf20Sopenharmony_ci};
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_cistatic const struct nla_policy macsec_genl_sa_policy[NUM_MACSEC_SA_ATTR] = {
16548c2ecf20Sopenharmony_ci	[MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
16558c2ecf20Sopenharmony_ci	[MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
16568c2ecf20Sopenharmony_ci	[MACSEC_SA_ATTR_PN] = NLA_POLICY_MIN_LEN(4),
16578c2ecf20Sopenharmony_ci	[MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY,
16588c2ecf20Sopenharmony_ci				   .len = MACSEC_KEYID_LEN, },
16598c2ecf20Sopenharmony_ci	[MACSEC_SA_ATTR_KEY] = { .type = NLA_BINARY,
16608c2ecf20Sopenharmony_ci				 .len = MACSEC_MAX_KEY_LEN, },
16618c2ecf20Sopenharmony_ci	[MACSEC_SA_ATTR_SSCI] = { .type = NLA_U32 },
16628c2ecf20Sopenharmony_ci	[MACSEC_SA_ATTR_SALT] = { .type = NLA_BINARY,
16638c2ecf20Sopenharmony_ci				  .len = MACSEC_SALT_LEN, },
16648c2ecf20Sopenharmony_ci};
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_cistatic const struct nla_policy macsec_genl_offload_policy[NUM_MACSEC_OFFLOAD_ATTR] = {
16678c2ecf20Sopenharmony_ci	[MACSEC_OFFLOAD_ATTR_TYPE] = { .type = NLA_U8 },
16688c2ecf20Sopenharmony_ci};
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci/* Offloads an operation to a device driver */
16718c2ecf20Sopenharmony_cistatic int macsec_offload(int (* const func)(struct macsec_context *),
16728c2ecf20Sopenharmony_ci			  struct macsec_context *ctx)
16738c2ecf20Sopenharmony_ci{
16748c2ecf20Sopenharmony_ci	int ret;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	if (unlikely(!func))
16778c2ecf20Sopenharmony_ci		return 0;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	if (ctx->offload == MACSEC_OFFLOAD_PHY)
16808c2ecf20Sopenharmony_ci		mutex_lock(&ctx->phydev->lock);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	/* Phase I: prepare. The drive should fail here if there are going to be
16838c2ecf20Sopenharmony_ci	 * issues in the commit phase.
16848c2ecf20Sopenharmony_ci	 */
16858c2ecf20Sopenharmony_ci	ctx->prepare = true;
16868c2ecf20Sopenharmony_ci	ret = (*func)(ctx);
16878c2ecf20Sopenharmony_ci	if (ret)
16888c2ecf20Sopenharmony_ci		goto phy_unlock;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	/* Phase II: commit. This step cannot fail. */
16918c2ecf20Sopenharmony_ci	ctx->prepare = false;
16928c2ecf20Sopenharmony_ci	ret = (*func)(ctx);
16938c2ecf20Sopenharmony_ci	/* This should never happen: commit is not allowed to fail */
16948c2ecf20Sopenharmony_ci	if (unlikely(ret))
16958c2ecf20Sopenharmony_ci		WARN(1, "MACsec offloading commit failed (%d)\n", ret);
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ciphy_unlock:
16988c2ecf20Sopenharmony_ci	if (ctx->offload == MACSEC_OFFLOAD_PHY)
16998c2ecf20Sopenharmony_ci		mutex_unlock(&ctx->phydev->lock);
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	return ret;
17028c2ecf20Sopenharmony_ci}
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_cistatic int parse_sa_config(struct nlattr **attrs, struct nlattr **tb_sa)
17058c2ecf20Sopenharmony_ci{
17068c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_SA_CONFIG])
17078c2ecf20Sopenharmony_ci		return -EINVAL;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	if (nla_parse_nested_deprecated(tb_sa, MACSEC_SA_ATTR_MAX, attrs[MACSEC_ATTR_SA_CONFIG], macsec_genl_sa_policy, NULL))
17108c2ecf20Sopenharmony_ci		return -EINVAL;
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	return 0;
17138c2ecf20Sopenharmony_ci}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_cistatic int parse_rxsc_config(struct nlattr **attrs, struct nlattr **tb_rxsc)
17168c2ecf20Sopenharmony_ci{
17178c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_RXSC_CONFIG])
17188c2ecf20Sopenharmony_ci		return -EINVAL;
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	if (nla_parse_nested_deprecated(tb_rxsc, MACSEC_RXSC_ATTR_MAX, attrs[MACSEC_ATTR_RXSC_CONFIG], macsec_genl_rxsc_policy, NULL))
17218c2ecf20Sopenharmony_ci		return -EINVAL;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	return 0;
17248c2ecf20Sopenharmony_ci}
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_cistatic bool validate_add_rxsa(struct nlattr **attrs)
17278c2ecf20Sopenharmony_ci{
17288c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_SA_ATTR_AN] ||
17298c2ecf20Sopenharmony_ci	    !attrs[MACSEC_SA_ATTR_KEY] ||
17308c2ecf20Sopenharmony_ci	    !attrs[MACSEC_SA_ATTR_KEYID])
17318c2ecf20Sopenharmony_ci		return false;
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN)
17348c2ecf20Sopenharmony_ci		return false;
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	if (attrs[MACSEC_SA_ATTR_PN] &&
17378c2ecf20Sopenharmony_ci	    nla_get_u64(attrs[MACSEC_SA_ATTR_PN]) == 0)
17388c2ecf20Sopenharmony_ci		return false;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	if (attrs[MACSEC_SA_ATTR_ACTIVE]) {
17418c2ecf20Sopenharmony_ci		if (nla_get_u8(attrs[MACSEC_SA_ATTR_ACTIVE]) > 1)
17428c2ecf20Sopenharmony_ci			return false;
17438c2ecf20Sopenharmony_ci	}
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	if (nla_len(attrs[MACSEC_SA_ATTR_KEYID]) != MACSEC_KEYID_LEN)
17468c2ecf20Sopenharmony_ci		return false;
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	return true;
17498c2ecf20Sopenharmony_ci}
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_cistatic int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info)
17528c2ecf20Sopenharmony_ci{
17538c2ecf20Sopenharmony_ci	struct net_device *dev;
17548c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
17558c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
17568c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
17578c2ecf20Sopenharmony_ci	struct macsec_rx_sa *rx_sa;
17588c2ecf20Sopenharmony_ci	unsigned char assoc_num;
17598c2ecf20Sopenharmony_ci	int pn_len;
17608c2ecf20Sopenharmony_ci	struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
17618c2ecf20Sopenharmony_ci	struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
17628c2ecf20Sopenharmony_ci	int err;
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
17658c2ecf20Sopenharmony_ci		return -EINVAL;
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	if (parse_sa_config(attrs, tb_sa))
17688c2ecf20Sopenharmony_ci		return -EINVAL;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	if (parse_rxsc_config(attrs, tb_rxsc))
17718c2ecf20Sopenharmony_ci		return -EINVAL;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	if (!validate_add_rxsa(tb_sa))
17748c2ecf20Sopenharmony_ci		return -EINVAL;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	rtnl_lock();
17778c2ecf20Sopenharmony_ci	rx_sc = get_rxsc_from_nl(genl_info_net(info), attrs, tb_rxsc, &dev, &secy);
17788c2ecf20Sopenharmony_ci	if (IS_ERR(rx_sc)) {
17798c2ecf20Sopenharmony_ci		rtnl_unlock();
17808c2ecf20Sopenharmony_ci		return PTR_ERR(rx_sc);
17818c2ecf20Sopenharmony_ci	}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	if (nla_len(tb_sa[MACSEC_SA_ATTR_KEY]) != secy->key_len) {
17868c2ecf20Sopenharmony_ci		pr_notice("macsec: nl: add_rxsa: bad key length: %d != %d\n",
17878c2ecf20Sopenharmony_ci			  nla_len(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len);
17888c2ecf20Sopenharmony_ci		rtnl_unlock();
17898c2ecf20Sopenharmony_ci		return -EINVAL;
17908c2ecf20Sopenharmony_ci	}
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	pn_len = secy->xpn ? MACSEC_XPN_PN_LEN : MACSEC_DEFAULT_PN_LEN;
17938c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_PN] &&
17948c2ecf20Sopenharmony_ci	    nla_len(tb_sa[MACSEC_SA_ATTR_PN]) != pn_len) {
17958c2ecf20Sopenharmony_ci		pr_notice("macsec: nl: add_rxsa: bad pn length: %d != %d\n",
17968c2ecf20Sopenharmony_ci			  nla_len(tb_sa[MACSEC_SA_ATTR_PN]), pn_len);
17978c2ecf20Sopenharmony_ci		rtnl_unlock();
17988c2ecf20Sopenharmony_ci		return -EINVAL;
17998c2ecf20Sopenharmony_ci	}
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	if (secy->xpn) {
18028c2ecf20Sopenharmony_ci		if (!tb_sa[MACSEC_SA_ATTR_SSCI] || !tb_sa[MACSEC_SA_ATTR_SALT]) {
18038c2ecf20Sopenharmony_ci			rtnl_unlock();
18048c2ecf20Sopenharmony_ci			return -EINVAL;
18058c2ecf20Sopenharmony_ci		}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci		if (nla_len(tb_sa[MACSEC_SA_ATTR_SALT]) != MACSEC_SALT_LEN) {
18088c2ecf20Sopenharmony_ci			pr_notice("macsec: nl: add_rxsa: bad salt length: %d != %d\n",
18098c2ecf20Sopenharmony_ci				  nla_len(tb_sa[MACSEC_SA_ATTR_SALT]),
18108c2ecf20Sopenharmony_ci				  MACSEC_SALT_LEN);
18118c2ecf20Sopenharmony_ci			rtnl_unlock();
18128c2ecf20Sopenharmony_ci			return -EINVAL;
18138c2ecf20Sopenharmony_ci		}
18148c2ecf20Sopenharmony_ci	}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	rx_sa = rtnl_dereference(rx_sc->sa[assoc_num]);
18178c2ecf20Sopenharmony_ci	if (rx_sa) {
18188c2ecf20Sopenharmony_ci		rtnl_unlock();
18198c2ecf20Sopenharmony_ci		return -EBUSY;
18208c2ecf20Sopenharmony_ci	}
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	rx_sa = kmalloc(sizeof(*rx_sa), GFP_KERNEL);
18238c2ecf20Sopenharmony_ci	if (!rx_sa) {
18248c2ecf20Sopenharmony_ci		rtnl_unlock();
18258c2ecf20Sopenharmony_ci		return -ENOMEM;
18268c2ecf20Sopenharmony_ci	}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	err = init_rx_sa(rx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
18298c2ecf20Sopenharmony_ci			 secy->key_len, secy->icv_len);
18308c2ecf20Sopenharmony_ci	if (err < 0) {
18318c2ecf20Sopenharmony_ci		kfree(rx_sa);
18328c2ecf20Sopenharmony_ci		rtnl_unlock();
18338c2ecf20Sopenharmony_ci		return err;
18348c2ecf20Sopenharmony_ci	}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_PN]) {
18378c2ecf20Sopenharmony_ci		spin_lock_bh(&rx_sa->lock);
18388c2ecf20Sopenharmony_ci		rx_sa->next_pn = nla_get_u64(tb_sa[MACSEC_SA_ATTR_PN]);
18398c2ecf20Sopenharmony_ci		spin_unlock_bh(&rx_sa->lock);
18408c2ecf20Sopenharmony_ci	}
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
18438c2ecf20Sopenharmony_ci		rx_sa->active = !!nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	rx_sa->sc = rx_sc;
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
18488c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
18498c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
18508c2ecf20Sopenharmony_ci		struct macsec_context ctx;
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
18538c2ecf20Sopenharmony_ci		if (!ops) {
18548c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
18558c2ecf20Sopenharmony_ci			goto cleanup;
18568c2ecf20Sopenharmony_ci		}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci		ctx.sa.assoc_num = assoc_num;
18598c2ecf20Sopenharmony_ci		ctx.sa.rx_sa = rx_sa;
18608c2ecf20Sopenharmony_ci		ctx.secy = secy;
18618c2ecf20Sopenharmony_ci		memcpy(ctx.sa.key, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
18628c2ecf20Sopenharmony_ci		       secy->key_len);
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci		err = macsec_offload(ops->mdo_add_rxsa, &ctx);
18658c2ecf20Sopenharmony_ci		memzero_explicit(ctx.sa.key, secy->key_len);
18668c2ecf20Sopenharmony_ci		if (err)
18678c2ecf20Sopenharmony_ci			goto cleanup;
18688c2ecf20Sopenharmony_ci	}
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	if (secy->xpn) {
18718c2ecf20Sopenharmony_ci		rx_sa->ssci = nla_get_ssci(tb_sa[MACSEC_SA_ATTR_SSCI]);
18728c2ecf20Sopenharmony_ci		nla_memcpy(rx_sa->key.salt.bytes, tb_sa[MACSEC_SA_ATTR_SALT],
18738c2ecf20Sopenharmony_ci			   MACSEC_SALT_LEN);
18748c2ecf20Sopenharmony_ci	}
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	nla_memcpy(rx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN);
18778c2ecf20Sopenharmony_ci	rcu_assign_pointer(rx_sc->sa[assoc_num], rx_sa);
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	rtnl_unlock();
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	return 0;
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_cicleanup:
18848c2ecf20Sopenharmony_ci	macsec_rxsa_put(rx_sa);
18858c2ecf20Sopenharmony_ci	rtnl_unlock();
18868c2ecf20Sopenharmony_ci	return err;
18878c2ecf20Sopenharmony_ci}
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_cistatic bool validate_add_rxsc(struct nlattr **attrs)
18908c2ecf20Sopenharmony_ci{
18918c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_RXSC_ATTR_SCI])
18928c2ecf20Sopenharmony_ci		return false;
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	if (attrs[MACSEC_RXSC_ATTR_ACTIVE]) {
18958c2ecf20Sopenharmony_ci		if (nla_get_u8(attrs[MACSEC_RXSC_ATTR_ACTIVE]) > 1)
18968c2ecf20Sopenharmony_ci			return false;
18978c2ecf20Sopenharmony_ci	}
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	return true;
19008c2ecf20Sopenharmony_ci}
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_cistatic int macsec_add_rxsc(struct sk_buff *skb, struct genl_info *info)
19038c2ecf20Sopenharmony_ci{
19048c2ecf20Sopenharmony_ci	struct net_device *dev;
19058c2ecf20Sopenharmony_ci	sci_t sci = MACSEC_UNDEF_SCI;
19068c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
19078c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
19088c2ecf20Sopenharmony_ci	struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
19098c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
19108c2ecf20Sopenharmony_ci	bool active = true;
19118c2ecf20Sopenharmony_ci	int ret;
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
19148c2ecf20Sopenharmony_ci		return -EINVAL;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	if (parse_rxsc_config(attrs, tb_rxsc))
19178c2ecf20Sopenharmony_ci		return -EINVAL;
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	if (!validate_add_rxsc(tb_rxsc))
19208c2ecf20Sopenharmony_ci		return -EINVAL;
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	rtnl_lock();
19238c2ecf20Sopenharmony_ci	dev = get_dev_from_nl(genl_info_net(info), attrs);
19248c2ecf20Sopenharmony_ci	if (IS_ERR(dev)) {
19258c2ecf20Sopenharmony_ci		rtnl_unlock();
19268c2ecf20Sopenharmony_ci		return PTR_ERR(dev);
19278c2ecf20Sopenharmony_ci	}
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci	secy = &macsec_priv(dev)->secy;
19308c2ecf20Sopenharmony_ci	sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]);
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci	if (tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE])
19338c2ecf20Sopenharmony_ci		active = nla_get_u8(tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]);
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	rx_sc = create_rx_sc(dev, sci, active);
19368c2ecf20Sopenharmony_ci	if (IS_ERR(rx_sc)) {
19378c2ecf20Sopenharmony_ci		rtnl_unlock();
19388c2ecf20Sopenharmony_ci		return PTR_ERR(rx_sc);
19398c2ecf20Sopenharmony_ci	}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
19428c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
19438c2ecf20Sopenharmony_ci		struct macsec_context ctx;
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
19468c2ecf20Sopenharmony_ci		if (!ops) {
19478c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
19488c2ecf20Sopenharmony_ci			goto cleanup;
19498c2ecf20Sopenharmony_ci		}
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci		ctx.rx_sc = rx_sc;
19528c2ecf20Sopenharmony_ci		ctx.secy = secy;
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci		ret = macsec_offload(ops->mdo_add_rxsc, &ctx);
19558c2ecf20Sopenharmony_ci		if (ret)
19568c2ecf20Sopenharmony_ci			goto cleanup;
19578c2ecf20Sopenharmony_ci	}
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	rtnl_unlock();
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	return 0;
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_cicleanup:
19648c2ecf20Sopenharmony_ci	del_rx_sc(secy, sci);
19658c2ecf20Sopenharmony_ci	free_rx_sc(rx_sc);
19668c2ecf20Sopenharmony_ci	rtnl_unlock();
19678c2ecf20Sopenharmony_ci	return ret;
19688c2ecf20Sopenharmony_ci}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_cistatic bool validate_add_txsa(struct nlattr **attrs)
19718c2ecf20Sopenharmony_ci{
19728c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_SA_ATTR_AN] ||
19738c2ecf20Sopenharmony_ci	    !attrs[MACSEC_SA_ATTR_PN] ||
19748c2ecf20Sopenharmony_ci	    !attrs[MACSEC_SA_ATTR_KEY] ||
19758c2ecf20Sopenharmony_ci	    !attrs[MACSEC_SA_ATTR_KEYID])
19768c2ecf20Sopenharmony_ci		return false;
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_ci	if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN)
19798c2ecf20Sopenharmony_ci		return false;
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci	if (nla_get_u64(attrs[MACSEC_SA_ATTR_PN]) == 0)
19828c2ecf20Sopenharmony_ci		return false;
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	if (attrs[MACSEC_SA_ATTR_ACTIVE]) {
19858c2ecf20Sopenharmony_ci		if (nla_get_u8(attrs[MACSEC_SA_ATTR_ACTIVE]) > 1)
19868c2ecf20Sopenharmony_ci			return false;
19878c2ecf20Sopenharmony_ci	}
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	if (nla_len(attrs[MACSEC_SA_ATTR_KEYID]) != MACSEC_KEYID_LEN)
19908c2ecf20Sopenharmony_ci		return false;
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	return true;
19938c2ecf20Sopenharmony_ci}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_cistatic int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info)
19968c2ecf20Sopenharmony_ci{
19978c2ecf20Sopenharmony_ci	struct net_device *dev;
19988c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
19998c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
20008c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc;
20018c2ecf20Sopenharmony_ci	struct macsec_tx_sa *tx_sa;
20028c2ecf20Sopenharmony_ci	unsigned char assoc_num;
20038c2ecf20Sopenharmony_ci	int pn_len;
20048c2ecf20Sopenharmony_ci	struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
20058c2ecf20Sopenharmony_ci	bool was_operational;
20068c2ecf20Sopenharmony_ci	int err;
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
20098c2ecf20Sopenharmony_ci		return -EINVAL;
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci	if (parse_sa_config(attrs, tb_sa))
20128c2ecf20Sopenharmony_ci		return -EINVAL;
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	if (!validate_add_txsa(tb_sa))
20158c2ecf20Sopenharmony_ci		return -EINVAL;
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	rtnl_lock();
20188c2ecf20Sopenharmony_ci	dev = get_dev_from_nl(genl_info_net(info), attrs);
20198c2ecf20Sopenharmony_ci	if (IS_ERR(dev)) {
20208c2ecf20Sopenharmony_ci		rtnl_unlock();
20218c2ecf20Sopenharmony_ci		return PTR_ERR(dev);
20228c2ecf20Sopenharmony_ci	}
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	secy = &macsec_priv(dev)->secy;
20258c2ecf20Sopenharmony_ci	tx_sc = &secy->tx_sc;
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	if (nla_len(tb_sa[MACSEC_SA_ATTR_KEY]) != secy->key_len) {
20308c2ecf20Sopenharmony_ci		pr_notice("macsec: nl: add_txsa: bad key length: %d != %d\n",
20318c2ecf20Sopenharmony_ci			  nla_len(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len);
20328c2ecf20Sopenharmony_ci		rtnl_unlock();
20338c2ecf20Sopenharmony_ci		return -EINVAL;
20348c2ecf20Sopenharmony_ci	}
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci	pn_len = secy->xpn ? MACSEC_XPN_PN_LEN : MACSEC_DEFAULT_PN_LEN;
20378c2ecf20Sopenharmony_ci	if (nla_len(tb_sa[MACSEC_SA_ATTR_PN]) != pn_len) {
20388c2ecf20Sopenharmony_ci		pr_notice("macsec: nl: add_txsa: bad pn length: %d != %d\n",
20398c2ecf20Sopenharmony_ci			  nla_len(tb_sa[MACSEC_SA_ATTR_PN]), pn_len);
20408c2ecf20Sopenharmony_ci		rtnl_unlock();
20418c2ecf20Sopenharmony_ci		return -EINVAL;
20428c2ecf20Sopenharmony_ci	}
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	if (secy->xpn) {
20458c2ecf20Sopenharmony_ci		if (!tb_sa[MACSEC_SA_ATTR_SSCI] || !tb_sa[MACSEC_SA_ATTR_SALT]) {
20468c2ecf20Sopenharmony_ci			rtnl_unlock();
20478c2ecf20Sopenharmony_ci			return -EINVAL;
20488c2ecf20Sopenharmony_ci		}
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci		if (nla_len(tb_sa[MACSEC_SA_ATTR_SALT]) != MACSEC_SALT_LEN) {
20518c2ecf20Sopenharmony_ci			pr_notice("macsec: nl: add_txsa: bad salt length: %d != %d\n",
20528c2ecf20Sopenharmony_ci				  nla_len(tb_sa[MACSEC_SA_ATTR_SALT]),
20538c2ecf20Sopenharmony_ci				  MACSEC_SALT_LEN);
20548c2ecf20Sopenharmony_ci			rtnl_unlock();
20558c2ecf20Sopenharmony_ci			return -EINVAL;
20568c2ecf20Sopenharmony_ci		}
20578c2ecf20Sopenharmony_ci	}
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci	tx_sa = rtnl_dereference(tx_sc->sa[assoc_num]);
20608c2ecf20Sopenharmony_ci	if (tx_sa) {
20618c2ecf20Sopenharmony_ci		rtnl_unlock();
20628c2ecf20Sopenharmony_ci		return -EBUSY;
20638c2ecf20Sopenharmony_ci	}
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	tx_sa = kmalloc(sizeof(*tx_sa), GFP_KERNEL);
20668c2ecf20Sopenharmony_ci	if (!tx_sa) {
20678c2ecf20Sopenharmony_ci		rtnl_unlock();
20688c2ecf20Sopenharmony_ci		return -ENOMEM;
20698c2ecf20Sopenharmony_ci	}
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci	err = init_tx_sa(tx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
20728c2ecf20Sopenharmony_ci			 secy->key_len, secy->icv_len);
20738c2ecf20Sopenharmony_ci	if (err < 0) {
20748c2ecf20Sopenharmony_ci		kfree(tx_sa);
20758c2ecf20Sopenharmony_ci		rtnl_unlock();
20768c2ecf20Sopenharmony_ci		return err;
20778c2ecf20Sopenharmony_ci	}
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci	spin_lock_bh(&tx_sa->lock);
20808c2ecf20Sopenharmony_ci	tx_sa->next_pn = nla_get_u64(tb_sa[MACSEC_SA_ATTR_PN]);
20818c2ecf20Sopenharmony_ci	spin_unlock_bh(&tx_sa->lock);
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
20848c2ecf20Sopenharmony_ci		tx_sa->active = !!nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	was_operational = secy->operational;
20878c2ecf20Sopenharmony_ci	if (assoc_num == tx_sc->encoding_sa && tx_sa->active)
20888c2ecf20Sopenharmony_ci		secy->operational = true;
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
20918c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
20928c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
20938c2ecf20Sopenharmony_ci		struct macsec_context ctx;
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
20968c2ecf20Sopenharmony_ci		if (!ops) {
20978c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
20988c2ecf20Sopenharmony_ci			goto cleanup;
20998c2ecf20Sopenharmony_ci		}
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci		ctx.sa.assoc_num = assoc_num;
21028c2ecf20Sopenharmony_ci		ctx.sa.tx_sa = tx_sa;
21038c2ecf20Sopenharmony_ci		ctx.secy = secy;
21048c2ecf20Sopenharmony_ci		memcpy(ctx.sa.key, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
21058c2ecf20Sopenharmony_ci		       secy->key_len);
21068c2ecf20Sopenharmony_ci
21078c2ecf20Sopenharmony_ci		err = macsec_offload(ops->mdo_add_txsa, &ctx);
21088c2ecf20Sopenharmony_ci		memzero_explicit(ctx.sa.key, secy->key_len);
21098c2ecf20Sopenharmony_ci		if (err)
21108c2ecf20Sopenharmony_ci			goto cleanup;
21118c2ecf20Sopenharmony_ci	}
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	if (secy->xpn) {
21148c2ecf20Sopenharmony_ci		tx_sa->ssci = nla_get_ssci(tb_sa[MACSEC_SA_ATTR_SSCI]);
21158c2ecf20Sopenharmony_ci		nla_memcpy(tx_sa->key.salt.bytes, tb_sa[MACSEC_SA_ATTR_SALT],
21168c2ecf20Sopenharmony_ci			   MACSEC_SALT_LEN);
21178c2ecf20Sopenharmony_ci	}
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	nla_memcpy(tx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN);
21208c2ecf20Sopenharmony_ci	rcu_assign_pointer(tx_sc->sa[assoc_num], tx_sa);
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci	rtnl_unlock();
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	return 0;
21258c2ecf20Sopenharmony_ci
21268c2ecf20Sopenharmony_cicleanup:
21278c2ecf20Sopenharmony_ci	secy->operational = was_operational;
21288c2ecf20Sopenharmony_ci	macsec_txsa_put(tx_sa);
21298c2ecf20Sopenharmony_ci	rtnl_unlock();
21308c2ecf20Sopenharmony_ci	return err;
21318c2ecf20Sopenharmony_ci}
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_cistatic int macsec_del_rxsa(struct sk_buff *skb, struct genl_info *info)
21348c2ecf20Sopenharmony_ci{
21358c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
21368c2ecf20Sopenharmony_ci	struct net_device *dev;
21378c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
21388c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
21398c2ecf20Sopenharmony_ci	struct macsec_rx_sa *rx_sa;
21408c2ecf20Sopenharmony_ci	u8 assoc_num;
21418c2ecf20Sopenharmony_ci	struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
21428c2ecf20Sopenharmony_ci	struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
21438c2ecf20Sopenharmony_ci	int ret;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
21468c2ecf20Sopenharmony_ci		return -EINVAL;
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	if (parse_sa_config(attrs, tb_sa))
21498c2ecf20Sopenharmony_ci		return -EINVAL;
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_ci	if (parse_rxsc_config(attrs, tb_rxsc))
21528c2ecf20Sopenharmony_ci		return -EINVAL;
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci	rtnl_lock();
21558c2ecf20Sopenharmony_ci	rx_sa = get_rxsa_from_nl(genl_info_net(info), attrs, tb_rxsc, tb_sa,
21568c2ecf20Sopenharmony_ci				 &dev, &secy, &rx_sc, &assoc_num);
21578c2ecf20Sopenharmony_ci	if (IS_ERR(rx_sa)) {
21588c2ecf20Sopenharmony_ci		rtnl_unlock();
21598c2ecf20Sopenharmony_ci		return PTR_ERR(rx_sa);
21608c2ecf20Sopenharmony_ci	}
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_ci	if (rx_sa->active) {
21638c2ecf20Sopenharmony_ci		rtnl_unlock();
21648c2ecf20Sopenharmony_ci		return -EBUSY;
21658c2ecf20Sopenharmony_ci	}
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
21688c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
21698c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
21708c2ecf20Sopenharmony_ci		struct macsec_context ctx;
21718c2ecf20Sopenharmony_ci
21728c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
21738c2ecf20Sopenharmony_ci		if (!ops) {
21748c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
21758c2ecf20Sopenharmony_ci			goto cleanup;
21768c2ecf20Sopenharmony_ci		}
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_ci		ctx.sa.assoc_num = assoc_num;
21798c2ecf20Sopenharmony_ci		ctx.sa.rx_sa = rx_sa;
21808c2ecf20Sopenharmony_ci		ctx.secy = secy;
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci		ret = macsec_offload(ops->mdo_del_rxsa, &ctx);
21838c2ecf20Sopenharmony_ci		if (ret)
21848c2ecf20Sopenharmony_ci			goto cleanup;
21858c2ecf20Sopenharmony_ci	}
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(rx_sc->sa[assoc_num], NULL);
21888c2ecf20Sopenharmony_ci	clear_rx_sa(rx_sa);
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_ci	rtnl_unlock();
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	return 0;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_cicleanup:
21958c2ecf20Sopenharmony_ci	rtnl_unlock();
21968c2ecf20Sopenharmony_ci	return ret;
21978c2ecf20Sopenharmony_ci}
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_cistatic int macsec_del_rxsc(struct sk_buff *skb, struct genl_info *info)
22008c2ecf20Sopenharmony_ci{
22018c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
22028c2ecf20Sopenharmony_ci	struct net_device *dev;
22038c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
22048c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
22058c2ecf20Sopenharmony_ci	sci_t sci;
22068c2ecf20Sopenharmony_ci	struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
22078c2ecf20Sopenharmony_ci	int ret;
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
22108c2ecf20Sopenharmony_ci		return -EINVAL;
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	if (parse_rxsc_config(attrs, tb_rxsc))
22138c2ecf20Sopenharmony_ci		return -EINVAL;
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci	if (!tb_rxsc[MACSEC_RXSC_ATTR_SCI])
22168c2ecf20Sopenharmony_ci		return -EINVAL;
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_ci	rtnl_lock();
22198c2ecf20Sopenharmony_ci	dev = get_dev_from_nl(genl_info_net(info), info->attrs);
22208c2ecf20Sopenharmony_ci	if (IS_ERR(dev)) {
22218c2ecf20Sopenharmony_ci		rtnl_unlock();
22228c2ecf20Sopenharmony_ci		return PTR_ERR(dev);
22238c2ecf20Sopenharmony_ci	}
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	secy = &macsec_priv(dev)->secy;
22268c2ecf20Sopenharmony_ci	sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]);
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	rx_sc = del_rx_sc(secy, sci);
22298c2ecf20Sopenharmony_ci	if (!rx_sc) {
22308c2ecf20Sopenharmony_ci		rtnl_unlock();
22318c2ecf20Sopenharmony_ci		return -ENODEV;
22328c2ecf20Sopenharmony_ci	}
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
22358c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
22368c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
22378c2ecf20Sopenharmony_ci		struct macsec_context ctx;
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
22408c2ecf20Sopenharmony_ci		if (!ops) {
22418c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
22428c2ecf20Sopenharmony_ci			goto cleanup;
22438c2ecf20Sopenharmony_ci		}
22448c2ecf20Sopenharmony_ci
22458c2ecf20Sopenharmony_ci		ctx.rx_sc = rx_sc;
22468c2ecf20Sopenharmony_ci		ctx.secy = secy;
22478c2ecf20Sopenharmony_ci		ret = macsec_offload(ops->mdo_del_rxsc, &ctx);
22488c2ecf20Sopenharmony_ci		if (ret)
22498c2ecf20Sopenharmony_ci			goto cleanup;
22508c2ecf20Sopenharmony_ci	}
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	free_rx_sc(rx_sc);
22538c2ecf20Sopenharmony_ci	rtnl_unlock();
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci	return 0;
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_cicleanup:
22588c2ecf20Sopenharmony_ci	rtnl_unlock();
22598c2ecf20Sopenharmony_ci	return ret;
22608c2ecf20Sopenharmony_ci}
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_cistatic int macsec_del_txsa(struct sk_buff *skb, struct genl_info *info)
22638c2ecf20Sopenharmony_ci{
22648c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
22658c2ecf20Sopenharmony_ci	struct net_device *dev;
22668c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
22678c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc;
22688c2ecf20Sopenharmony_ci	struct macsec_tx_sa *tx_sa;
22698c2ecf20Sopenharmony_ci	u8 assoc_num;
22708c2ecf20Sopenharmony_ci	struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
22718c2ecf20Sopenharmony_ci	int ret;
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
22748c2ecf20Sopenharmony_ci		return -EINVAL;
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	if (parse_sa_config(attrs, tb_sa))
22778c2ecf20Sopenharmony_ci		return -EINVAL;
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	rtnl_lock();
22808c2ecf20Sopenharmony_ci	tx_sa = get_txsa_from_nl(genl_info_net(info), attrs, tb_sa,
22818c2ecf20Sopenharmony_ci				 &dev, &secy, &tx_sc, &assoc_num);
22828c2ecf20Sopenharmony_ci	if (IS_ERR(tx_sa)) {
22838c2ecf20Sopenharmony_ci		rtnl_unlock();
22848c2ecf20Sopenharmony_ci		return PTR_ERR(tx_sa);
22858c2ecf20Sopenharmony_ci	}
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci	if (tx_sa->active) {
22888c2ecf20Sopenharmony_ci		rtnl_unlock();
22898c2ecf20Sopenharmony_ci		return -EBUSY;
22908c2ecf20Sopenharmony_ci	}
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
22938c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
22948c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
22958c2ecf20Sopenharmony_ci		struct macsec_context ctx;
22968c2ecf20Sopenharmony_ci
22978c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
22988c2ecf20Sopenharmony_ci		if (!ops) {
22998c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
23008c2ecf20Sopenharmony_ci			goto cleanup;
23018c2ecf20Sopenharmony_ci		}
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci		ctx.sa.assoc_num = assoc_num;
23048c2ecf20Sopenharmony_ci		ctx.sa.tx_sa = tx_sa;
23058c2ecf20Sopenharmony_ci		ctx.secy = secy;
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci		ret = macsec_offload(ops->mdo_del_txsa, &ctx);
23088c2ecf20Sopenharmony_ci		if (ret)
23098c2ecf20Sopenharmony_ci			goto cleanup;
23108c2ecf20Sopenharmony_ci	}
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(tx_sc->sa[assoc_num], NULL);
23138c2ecf20Sopenharmony_ci	clear_tx_sa(tx_sa);
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_ci	rtnl_unlock();
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci	return 0;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_cicleanup:
23208c2ecf20Sopenharmony_ci	rtnl_unlock();
23218c2ecf20Sopenharmony_ci	return ret;
23228c2ecf20Sopenharmony_ci}
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_cistatic bool validate_upd_sa(struct nlattr **attrs)
23258c2ecf20Sopenharmony_ci{
23268c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_SA_ATTR_AN] ||
23278c2ecf20Sopenharmony_ci	    attrs[MACSEC_SA_ATTR_KEY] ||
23288c2ecf20Sopenharmony_ci	    attrs[MACSEC_SA_ATTR_KEYID] ||
23298c2ecf20Sopenharmony_ci	    attrs[MACSEC_SA_ATTR_SSCI] ||
23308c2ecf20Sopenharmony_ci	    attrs[MACSEC_SA_ATTR_SALT])
23318c2ecf20Sopenharmony_ci		return false;
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN)
23348c2ecf20Sopenharmony_ci		return false;
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	if (attrs[MACSEC_SA_ATTR_PN] && nla_get_u64(attrs[MACSEC_SA_ATTR_PN]) == 0)
23378c2ecf20Sopenharmony_ci		return false;
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci	if (attrs[MACSEC_SA_ATTR_ACTIVE]) {
23408c2ecf20Sopenharmony_ci		if (nla_get_u8(attrs[MACSEC_SA_ATTR_ACTIVE]) > 1)
23418c2ecf20Sopenharmony_ci			return false;
23428c2ecf20Sopenharmony_ci	}
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci	return true;
23458c2ecf20Sopenharmony_ci}
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_cistatic int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info)
23488c2ecf20Sopenharmony_ci{
23498c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
23508c2ecf20Sopenharmony_ci	struct net_device *dev;
23518c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
23528c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc;
23538c2ecf20Sopenharmony_ci	struct macsec_tx_sa *tx_sa;
23548c2ecf20Sopenharmony_ci	u8 assoc_num;
23558c2ecf20Sopenharmony_ci	struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
23568c2ecf20Sopenharmony_ci	bool was_operational, was_active;
23578c2ecf20Sopenharmony_ci	pn_t prev_pn;
23588c2ecf20Sopenharmony_ci	int ret = 0;
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_ci	prev_pn.full64 = 0;
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
23638c2ecf20Sopenharmony_ci		return -EINVAL;
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci	if (parse_sa_config(attrs, tb_sa))
23668c2ecf20Sopenharmony_ci		return -EINVAL;
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_ci	if (!validate_upd_sa(tb_sa))
23698c2ecf20Sopenharmony_ci		return -EINVAL;
23708c2ecf20Sopenharmony_ci
23718c2ecf20Sopenharmony_ci	rtnl_lock();
23728c2ecf20Sopenharmony_ci	tx_sa = get_txsa_from_nl(genl_info_net(info), attrs, tb_sa,
23738c2ecf20Sopenharmony_ci				 &dev, &secy, &tx_sc, &assoc_num);
23748c2ecf20Sopenharmony_ci	if (IS_ERR(tx_sa)) {
23758c2ecf20Sopenharmony_ci		rtnl_unlock();
23768c2ecf20Sopenharmony_ci		return PTR_ERR(tx_sa);
23778c2ecf20Sopenharmony_ci	}
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_PN]) {
23808c2ecf20Sopenharmony_ci		int pn_len;
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci		pn_len = secy->xpn ? MACSEC_XPN_PN_LEN : MACSEC_DEFAULT_PN_LEN;
23838c2ecf20Sopenharmony_ci		if (nla_len(tb_sa[MACSEC_SA_ATTR_PN]) != pn_len) {
23848c2ecf20Sopenharmony_ci			pr_notice("macsec: nl: upd_txsa: bad pn length: %d != %d\n",
23858c2ecf20Sopenharmony_ci				  nla_len(tb_sa[MACSEC_SA_ATTR_PN]), pn_len);
23868c2ecf20Sopenharmony_ci			rtnl_unlock();
23878c2ecf20Sopenharmony_ci			return -EINVAL;
23888c2ecf20Sopenharmony_ci		}
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci		spin_lock_bh(&tx_sa->lock);
23918c2ecf20Sopenharmony_ci		prev_pn = tx_sa->next_pn_halves;
23928c2ecf20Sopenharmony_ci		tx_sa->next_pn = nla_get_u64(tb_sa[MACSEC_SA_ATTR_PN]);
23938c2ecf20Sopenharmony_ci		spin_unlock_bh(&tx_sa->lock);
23948c2ecf20Sopenharmony_ci	}
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci	was_active = tx_sa->active;
23978c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
23988c2ecf20Sopenharmony_ci		tx_sa->active = nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci	was_operational = secy->operational;
24018c2ecf20Sopenharmony_ci	if (assoc_num == tx_sc->encoding_sa)
24028c2ecf20Sopenharmony_ci		secy->operational = tx_sa->active;
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
24058c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
24068c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
24078c2ecf20Sopenharmony_ci		struct macsec_context ctx;
24088c2ecf20Sopenharmony_ci
24098c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
24108c2ecf20Sopenharmony_ci		if (!ops) {
24118c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
24128c2ecf20Sopenharmony_ci			goto cleanup;
24138c2ecf20Sopenharmony_ci		}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_ci		ctx.sa.assoc_num = assoc_num;
24168c2ecf20Sopenharmony_ci		ctx.sa.tx_sa = tx_sa;
24178c2ecf20Sopenharmony_ci		ctx.sa.update_pn = !!prev_pn.full64;
24188c2ecf20Sopenharmony_ci		ctx.secy = secy;
24198c2ecf20Sopenharmony_ci
24208c2ecf20Sopenharmony_ci		ret = macsec_offload(ops->mdo_upd_txsa, &ctx);
24218c2ecf20Sopenharmony_ci		if (ret)
24228c2ecf20Sopenharmony_ci			goto cleanup;
24238c2ecf20Sopenharmony_ci	}
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_ci	rtnl_unlock();
24268c2ecf20Sopenharmony_ci
24278c2ecf20Sopenharmony_ci	return 0;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_cicleanup:
24308c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_PN]) {
24318c2ecf20Sopenharmony_ci		spin_lock_bh(&tx_sa->lock);
24328c2ecf20Sopenharmony_ci		tx_sa->next_pn_halves = prev_pn;
24338c2ecf20Sopenharmony_ci		spin_unlock_bh(&tx_sa->lock);
24348c2ecf20Sopenharmony_ci	}
24358c2ecf20Sopenharmony_ci	tx_sa->active = was_active;
24368c2ecf20Sopenharmony_ci	secy->operational = was_operational;
24378c2ecf20Sopenharmony_ci	rtnl_unlock();
24388c2ecf20Sopenharmony_ci	return ret;
24398c2ecf20Sopenharmony_ci}
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_cistatic int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info)
24428c2ecf20Sopenharmony_ci{
24438c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
24448c2ecf20Sopenharmony_ci	struct net_device *dev;
24458c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
24468c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
24478c2ecf20Sopenharmony_ci	struct macsec_rx_sa *rx_sa;
24488c2ecf20Sopenharmony_ci	u8 assoc_num;
24498c2ecf20Sopenharmony_ci	struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
24508c2ecf20Sopenharmony_ci	struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
24518c2ecf20Sopenharmony_ci	bool was_active;
24528c2ecf20Sopenharmony_ci	pn_t prev_pn;
24538c2ecf20Sopenharmony_ci	int ret = 0;
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci	prev_pn.full64 = 0;
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
24588c2ecf20Sopenharmony_ci		return -EINVAL;
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	if (parse_rxsc_config(attrs, tb_rxsc))
24618c2ecf20Sopenharmony_ci		return -EINVAL;
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci	if (parse_sa_config(attrs, tb_sa))
24648c2ecf20Sopenharmony_ci		return -EINVAL;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci	if (!validate_upd_sa(tb_sa))
24678c2ecf20Sopenharmony_ci		return -EINVAL;
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_ci	rtnl_lock();
24708c2ecf20Sopenharmony_ci	rx_sa = get_rxsa_from_nl(genl_info_net(info), attrs, tb_rxsc, tb_sa,
24718c2ecf20Sopenharmony_ci				 &dev, &secy, &rx_sc, &assoc_num);
24728c2ecf20Sopenharmony_ci	if (IS_ERR(rx_sa)) {
24738c2ecf20Sopenharmony_ci		rtnl_unlock();
24748c2ecf20Sopenharmony_ci		return PTR_ERR(rx_sa);
24758c2ecf20Sopenharmony_ci	}
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_PN]) {
24788c2ecf20Sopenharmony_ci		int pn_len;
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_ci		pn_len = secy->xpn ? MACSEC_XPN_PN_LEN : MACSEC_DEFAULT_PN_LEN;
24818c2ecf20Sopenharmony_ci		if (nla_len(tb_sa[MACSEC_SA_ATTR_PN]) != pn_len) {
24828c2ecf20Sopenharmony_ci			pr_notice("macsec: nl: upd_rxsa: bad pn length: %d != %d\n",
24838c2ecf20Sopenharmony_ci				  nla_len(tb_sa[MACSEC_SA_ATTR_PN]), pn_len);
24848c2ecf20Sopenharmony_ci			rtnl_unlock();
24858c2ecf20Sopenharmony_ci			return -EINVAL;
24868c2ecf20Sopenharmony_ci		}
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci		spin_lock_bh(&rx_sa->lock);
24898c2ecf20Sopenharmony_ci		prev_pn = rx_sa->next_pn_halves;
24908c2ecf20Sopenharmony_ci		rx_sa->next_pn = nla_get_u64(tb_sa[MACSEC_SA_ATTR_PN]);
24918c2ecf20Sopenharmony_ci		spin_unlock_bh(&rx_sa->lock);
24928c2ecf20Sopenharmony_ci	}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	was_active = rx_sa->active;
24958c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
24968c2ecf20Sopenharmony_ci		rx_sa->active = nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
24978c2ecf20Sopenharmony_ci
24988c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
24998c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
25008c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
25018c2ecf20Sopenharmony_ci		struct macsec_context ctx;
25028c2ecf20Sopenharmony_ci
25038c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
25048c2ecf20Sopenharmony_ci		if (!ops) {
25058c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
25068c2ecf20Sopenharmony_ci			goto cleanup;
25078c2ecf20Sopenharmony_ci		}
25088c2ecf20Sopenharmony_ci
25098c2ecf20Sopenharmony_ci		ctx.sa.assoc_num = assoc_num;
25108c2ecf20Sopenharmony_ci		ctx.sa.rx_sa = rx_sa;
25118c2ecf20Sopenharmony_ci		ctx.sa.update_pn = !!prev_pn.full64;
25128c2ecf20Sopenharmony_ci		ctx.secy = secy;
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_ci		ret = macsec_offload(ops->mdo_upd_rxsa, &ctx);
25158c2ecf20Sopenharmony_ci		if (ret)
25168c2ecf20Sopenharmony_ci			goto cleanup;
25178c2ecf20Sopenharmony_ci	}
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_ci	rtnl_unlock();
25208c2ecf20Sopenharmony_ci	return 0;
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_cicleanup:
25238c2ecf20Sopenharmony_ci	if (tb_sa[MACSEC_SA_ATTR_PN]) {
25248c2ecf20Sopenharmony_ci		spin_lock_bh(&rx_sa->lock);
25258c2ecf20Sopenharmony_ci		rx_sa->next_pn_halves = prev_pn;
25268c2ecf20Sopenharmony_ci		spin_unlock_bh(&rx_sa->lock);
25278c2ecf20Sopenharmony_ci	}
25288c2ecf20Sopenharmony_ci	rx_sa->active = was_active;
25298c2ecf20Sopenharmony_ci	rtnl_unlock();
25308c2ecf20Sopenharmony_ci	return ret;
25318c2ecf20Sopenharmony_ci}
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_cistatic int macsec_upd_rxsc(struct sk_buff *skb, struct genl_info *info)
25348c2ecf20Sopenharmony_ci{
25358c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
25368c2ecf20Sopenharmony_ci	struct net_device *dev;
25378c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
25388c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
25398c2ecf20Sopenharmony_ci	struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
25408c2ecf20Sopenharmony_ci	unsigned int prev_n_rx_sc;
25418c2ecf20Sopenharmony_ci	bool was_active;
25428c2ecf20Sopenharmony_ci	int ret;
25438c2ecf20Sopenharmony_ci
25448c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
25458c2ecf20Sopenharmony_ci		return -EINVAL;
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_ci	if (parse_rxsc_config(attrs, tb_rxsc))
25488c2ecf20Sopenharmony_ci		return -EINVAL;
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_ci	if (!validate_add_rxsc(tb_rxsc))
25518c2ecf20Sopenharmony_ci		return -EINVAL;
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci	rtnl_lock();
25548c2ecf20Sopenharmony_ci	rx_sc = get_rxsc_from_nl(genl_info_net(info), attrs, tb_rxsc, &dev, &secy);
25558c2ecf20Sopenharmony_ci	if (IS_ERR(rx_sc)) {
25568c2ecf20Sopenharmony_ci		rtnl_unlock();
25578c2ecf20Sopenharmony_ci		return PTR_ERR(rx_sc);
25588c2ecf20Sopenharmony_ci	}
25598c2ecf20Sopenharmony_ci
25608c2ecf20Sopenharmony_ci	was_active = rx_sc->active;
25618c2ecf20Sopenharmony_ci	prev_n_rx_sc = secy->n_rx_sc;
25628c2ecf20Sopenharmony_ci	if (tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]) {
25638c2ecf20Sopenharmony_ci		bool new = !!nla_get_u8(tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]);
25648c2ecf20Sopenharmony_ci
25658c2ecf20Sopenharmony_ci		if (rx_sc->active != new)
25668c2ecf20Sopenharmony_ci			secy->n_rx_sc += new ? 1 : -1;
25678c2ecf20Sopenharmony_ci
25688c2ecf20Sopenharmony_ci		rx_sc->active = new;
25698c2ecf20Sopenharmony_ci	}
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
25728c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
25738c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
25748c2ecf20Sopenharmony_ci		struct macsec_context ctx;
25758c2ecf20Sopenharmony_ci
25768c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
25778c2ecf20Sopenharmony_ci		if (!ops) {
25788c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
25798c2ecf20Sopenharmony_ci			goto cleanup;
25808c2ecf20Sopenharmony_ci		}
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_ci		ctx.rx_sc = rx_sc;
25838c2ecf20Sopenharmony_ci		ctx.secy = secy;
25848c2ecf20Sopenharmony_ci
25858c2ecf20Sopenharmony_ci		ret = macsec_offload(ops->mdo_upd_rxsc, &ctx);
25868c2ecf20Sopenharmony_ci		if (ret)
25878c2ecf20Sopenharmony_ci			goto cleanup;
25888c2ecf20Sopenharmony_ci	}
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_ci	rtnl_unlock();
25918c2ecf20Sopenharmony_ci
25928c2ecf20Sopenharmony_ci	return 0;
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_cicleanup:
25958c2ecf20Sopenharmony_ci	secy->n_rx_sc = prev_n_rx_sc;
25968c2ecf20Sopenharmony_ci	rx_sc->active = was_active;
25978c2ecf20Sopenharmony_ci	rtnl_unlock();
25988c2ecf20Sopenharmony_ci	return ret;
25998c2ecf20Sopenharmony_ci}
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_cistatic bool macsec_is_configured(struct macsec_dev *macsec)
26028c2ecf20Sopenharmony_ci{
26038c2ecf20Sopenharmony_ci	struct macsec_secy *secy = &macsec->secy;
26048c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc = &secy->tx_sc;
26058c2ecf20Sopenharmony_ci	int i;
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_ci	if (secy->rx_sc)
26088c2ecf20Sopenharmony_ci		return true;
26098c2ecf20Sopenharmony_ci
26108c2ecf20Sopenharmony_ci	for (i = 0; i < MACSEC_NUM_AN; i++)
26118c2ecf20Sopenharmony_ci		if (tx_sc->sa[i])
26128c2ecf20Sopenharmony_ci			return true;
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_ci	return false;
26158c2ecf20Sopenharmony_ci}
26168c2ecf20Sopenharmony_ci
26178c2ecf20Sopenharmony_cistatic int macsec_upd_offload(struct sk_buff *skb, struct genl_info *info)
26188c2ecf20Sopenharmony_ci{
26198c2ecf20Sopenharmony_ci	struct nlattr *tb_offload[MACSEC_OFFLOAD_ATTR_MAX + 1];
26208c2ecf20Sopenharmony_ci	enum macsec_offload offload, prev_offload;
26218c2ecf20Sopenharmony_ci	int (*func)(struct macsec_context *ctx);
26228c2ecf20Sopenharmony_ci	struct nlattr **attrs = info->attrs;
26238c2ecf20Sopenharmony_ci	struct net_device *dev;
26248c2ecf20Sopenharmony_ci	const struct macsec_ops *ops;
26258c2ecf20Sopenharmony_ci	struct macsec_context ctx;
26268c2ecf20Sopenharmony_ci	struct macsec_dev *macsec;
26278c2ecf20Sopenharmony_ci	int ret = 0;
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_IFINDEX])
26308c2ecf20Sopenharmony_ci		return -EINVAL;
26318c2ecf20Sopenharmony_ci
26328c2ecf20Sopenharmony_ci	if (!attrs[MACSEC_ATTR_OFFLOAD])
26338c2ecf20Sopenharmony_ci		return -EINVAL;
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci	if (nla_parse_nested_deprecated(tb_offload, MACSEC_OFFLOAD_ATTR_MAX,
26368c2ecf20Sopenharmony_ci					attrs[MACSEC_ATTR_OFFLOAD],
26378c2ecf20Sopenharmony_ci					macsec_genl_offload_policy, NULL))
26388c2ecf20Sopenharmony_ci		return -EINVAL;
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	rtnl_lock();
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	dev = get_dev_from_nl(genl_info_net(info), attrs);
26438c2ecf20Sopenharmony_ci	if (IS_ERR(dev)) {
26448c2ecf20Sopenharmony_ci		ret = PTR_ERR(dev);
26458c2ecf20Sopenharmony_ci		goto out;
26468c2ecf20Sopenharmony_ci	}
26478c2ecf20Sopenharmony_ci	macsec = macsec_priv(dev);
26488c2ecf20Sopenharmony_ci
26498c2ecf20Sopenharmony_ci	if (!tb_offload[MACSEC_OFFLOAD_ATTR_TYPE]) {
26508c2ecf20Sopenharmony_ci		ret = -EINVAL;
26518c2ecf20Sopenharmony_ci		goto out;
26528c2ecf20Sopenharmony_ci	}
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_ci	offload = nla_get_u8(tb_offload[MACSEC_OFFLOAD_ATTR_TYPE]);
26558c2ecf20Sopenharmony_ci	if (macsec->offload == offload)
26568c2ecf20Sopenharmony_ci		goto out;
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_ci	/* Check if the offloading mode is supported by the underlying layers */
26598c2ecf20Sopenharmony_ci	if (offload != MACSEC_OFFLOAD_OFF &&
26608c2ecf20Sopenharmony_ci	    !macsec_check_offload(offload, macsec)) {
26618c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
26628c2ecf20Sopenharmony_ci		goto out;
26638c2ecf20Sopenharmony_ci	}
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	/* Check if the net device is busy. */
26668c2ecf20Sopenharmony_ci	if (netif_running(dev)) {
26678c2ecf20Sopenharmony_ci		ret = -EBUSY;
26688c2ecf20Sopenharmony_ci		goto out;
26698c2ecf20Sopenharmony_ci	}
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci	prev_offload = macsec->offload;
26728c2ecf20Sopenharmony_ci	macsec->offload = offload;
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	/* Check if the device already has rules configured: we do not support
26758c2ecf20Sopenharmony_ci	 * rules migration.
26768c2ecf20Sopenharmony_ci	 */
26778c2ecf20Sopenharmony_ci	if (macsec_is_configured(macsec)) {
26788c2ecf20Sopenharmony_ci		ret = -EBUSY;
26798c2ecf20Sopenharmony_ci		goto rollback;
26808c2ecf20Sopenharmony_ci	}
26818c2ecf20Sopenharmony_ci
26828c2ecf20Sopenharmony_ci	ops = __macsec_get_ops(offload == MACSEC_OFFLOAD_OFF ? prev_offload : offload,
26838c2ecf20Sopenharmony_ci			       macsec, &ctx);
26848c2ecf20Sopenharmony_ci	if (!ops) {
26858c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
26868c2ecf20Sopenharmony_ci		goto rollback;
26878c2ecf20Sopenharmony_ci	}
26888c2ecf20Sopenharmony_ci
26898c2ecf20Sopenharmony_ci	if (prev_offload == MACSEC_OFFLOAD_OFF)
26908c2ecf20Sopenharmony_ci		func = ops->mdo_add_secy;
26918c2ecf20Sopenharmony_ci	else
26928c2ecf20Sopenharmony_ci		func = ops->mdo_del_secy;
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_ci	ctx.secy = &macsec->secy;
26958c2ecf20Sopenharmony_ci	ret = macsec_offload(func, &ctx);
26968c2ecf20Sopenharmony_ci	if (ret)
26978c2ecf20Sopenharmony_ci		goto rollback;
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_ci	rtnl_unlock();
27008c2ecf20Sopenharmony_ci	return 0;
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_cirollback:
27038c2ecf20Sopenharmony_ci	macsec->offload = prev_offload;
27048c2ecf20Sopenharmony_ciout:
27058c2ecf20Sopenharmony_ci	rtnl_unlock();
27068c2ecf20Sopenharmony_ci	return ret;
27078c2ecf20Sopenharmony_ci}
27088c2ecf20Sopenharmony_ci
27098c2ecf20Sopenharmony_cistatic void get_tx_sa_stats(struct net_device *dev, int an,
27108c2ecf20Sopenharmony_ci			    struct macsec_tx_sa *tx_sa,
27118c2ecf20Sopenharmony_ci			    struct macsec_tx_sa_stats *sum)
27128c2ecf20Sopenharmony_ci{
27138c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
27148c2ecf20Sopenharmony_ci	int cpu;
27158c2ecf20Sopenharmony_ci
27168c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
27178c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
27188c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
27198c2ecf20Sopenharmony_ci		struct macsec_context ctx;
27208c2ecf20Sopenharmony_ci
27218c2ecf20Sopenharmony_ci		ops = macsec_get_ops(macsec, &ctx);
27228c2ecf20Sopenharmony_ci		if (ops) {
27238c2ecf20Sopenharmony_ci			ctx.sa.assoc_num = an;
27248c2ecf20Sopenharmony_ci			ctx.sa.tx_sa = tx_sa;
27258c2ecf20Sopenharmony_ci			ctx.stats.tx_sa_stats = sum;
27268c2ecf20Sopenharmony_ci			ctx.secy = &macsec_priv(dev)->secy;
27278c2ecf20Sopenharmony_ci			macsec_offload(ops->mdo_get_tx_sa_stats, &ctx);
27288c2ecf20Sopenharmony_ci		}
27298c2ecf20Sopenharmony_ci		return;
27308c2ecf20Sopenharmony_ci	}
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
27338c2ecf20Sopenharmony_ci		const struct macsec_tx_sa_stats *stats =
27348c2ecf20Sopenharmony_ci			per_cpu_ptr(tx_sa->stats, cpu);
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_ci		sum->OutPktsProtected += stats->OutPktsProtected;
27378c2ecf20Sopenharmony_ci		sum->OutPktsEncrypted += stats->OutPktsEncrypted;
27388c2ecf20Sopenharmony_ci	}
27398c2ecf20Sopenharmony_ci}
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_cistatic int copy_tx_sa_stats(struct sk_buff *skb, struct macsec_tx_sa_stats *sum)
27428c2ecf20Sopenharmony_ci{
27438c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED,
27448c2ecf20Sopenharmony_ci			sum->OutPktsProtected) ||
27458c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED,
27468c2ecf20Sopenharmony_ci			sum->OutPktsEncrypted))
27478c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27488c2ecf20Sopenharmony_ci
27498c2ecf20Sopenharmony_ci	return 0;
27508c2ecf20Sopenharmony_ci}
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_cistatic void get_rx_sa_stats(struct net_device *dev,
27538c2ecf20Sopenharmony_ci			    struct macsec_rx_sc *rx_sc, int an,
27548c2ecf20Sopenharmony_ci			    struct macsec_rx_sa *rx_sa,
27558c2ecf20Sopenharmony_ci			    struct macsec_rx_sa_stats *sum)
27568c2ecf20Sopenharmony_ci{
27578c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
27588c2ecf20Sopenharmony_ci	int cpu;
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
27618c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
27628c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
27638c2ecf20Sopenharmony_ci		struct macsec_context ctx;
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_ci		ops = macsec_get_ops(macsec, &ctx);
27668c2ecf20Sopenharmony_ci		if (ops) {
27678c2ecf20Sopenharmony_ci			ctx.sa.assoc_num = an;
27688c2ecf20Sopenharmony_ci			ctx.sa.rx_sa = rx_sa;
27698c2ecf20Sopenharmony_ci			ctx.stats.rx_sa_stats = sum;
27708c2ecf20Sopenharmony_ci			ctx.secy = &macsec_priv(dev)->secy;
27718c2ecf20Sopenharmony_ci			ctx.rx_sc = rx_sc;
27728c2ecf20Sopenharmony_ci			macsec_offload(ops->mdo_get_rx_sa_stats, &ctx);
27738c2ecf20Sopenharmony_ci		}
27748c2ecf20Sopenharmony_ci		return;
27758c2ecf20Sopenharmony_ci	}
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
27788c2ecf20Sopenharmony_ci		const struct macsec_rx_sa_stats *stats =
27798c2ecf20Sopenharmony_ci			per_cpu_ptr(rx_sa->stats, cpu);
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci		sum->InPktsOK         += stats->InPktsOK;
27828c2ecf20Sopenharmony_ci		sum->InPktsInvalid    += stats->InPktsInvalid;
27838c2ecf20Sopenharmony_ci		sum->InPktsNotValid   += stats->InPktsNotValid;
27848c2ecf20Sopenharmony_ci		sum->InPktsNotUsingSA += stats->InPktsNotUsingSA;
27858c2ecf20Sopenharmony_ci		sum->InPktsUnusedSA   += stats->InPktsUnusedSA;
27868c2ecf20Sopenharmony_ci	}
27878c2ecf20Sopenharmony_ci}
27888c2ecf20Sopenharmony_ci
27898c2ecf20Sopenharmony_cistatic int copy_rx_sa_stats(struct sk_buff *skb,
27908c2ecf20Sopenharmony_ci			    struct macsec_rx_sa_stats *sum)
27918c2ecf20Sopenharmony_ci{
27928c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_OK, sum->InPktsOK) ||
27938c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID,
27948c2ecf20Sopenharmony_ci			sum->InPktsInvalid) ||
27958c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID,
27968c2ecf20Sopenharmony_ci			sum->InPktsNotValid) ||
27978c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA,
27988c2ecf20Sopenharmony_ci			sum->InPktsNotUsingSA) ||
27998c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA,
28008c2ecf20Sopenharmony_ci			sum->InPktsUnusedSA))
28018c2ecf20Sopenharmony_ci		return -EMSGSIZE;
28028c2ecf20Sopenharmony_ci
28038c2ecf20Sopenharmony_ci	return 0;
28048c2ecf20Sopenharmony_ci}
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_cistatic void get_rx_sc_stats(struct net_device *dev,
28078c2ecf20Sopenharmony_ci			    struct macsec_rx_sc *rx_sc,
28088c2ecf20Sopenharmony_ci			    struct macsec_rx_sc_stats *sum)
28098c2ecf20Sopenharmony_ci{
28108c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
28118c2ecf20Sopenharmony_ci	int cpu;
28128c2ecf20Sopenharmony_ci
28138c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
28148c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
28158c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
28168c2ecf20Sopenharmony_ci		struct macsec_context ctx;
28178c2ecf20Sopenharmony_ci
28188c2ecf20Sopenharmony_ci		ops = macsec_get_ops(macsec, &ctx);
28198c2ecf20Sopenharmony_ci		if (ops) {
28208c2ecf20Sopenharmony_ci			ctx.stats.rx_sc_stats = sum;
28218c2ecf20Sopenharmony_ci			ctx.secy = &macsec_priv(dev)->secy;
28228c2ecf20Sopenharmony_ci			ctx.rx_sc = rx_sc;
28238c2ecf20Sopenharmony_ci			macsec_offload(ops->mdo_get_rx_sc_stats, &ctx);
28248c2ecf20Sopenharmony_ci		}
28258c2ecf20Sopenharmony_ci		return;
28268c2ecf20Sopenharmony_ci	}
28278c2ecf20Sopenharmony_ci
28288c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
28298c2ecf20Sopenharmony_ci		const struct pcpu_rx_sc_stats *stats;
28308c2ecf20Sopenharmony_ci		struct macsec_rx_sc_stats tmp;
28318c2ecf20Sopenharmony_ci		unsigned int start;
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci		stats = per_cpu_ptr(rx_sc->stats, cpu);
28348c2ecf20Sopenharmony_ci		do {
28358c2ecf20Sopenharmony_ci			start = u64_stats_fetch_begin_irq(&stats->syncp);
28368c2ecf20Sopenharmony_ci			memcpy(&tmp, &stats->stats, sizeof(tmp));
28378c2ecf20Sopenharmony_ci		} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_ci		sum->InOctetsValidated += tmp.InOctetsValidated;
28408c2ecf20Sopenharmony_ci		sum->InOctetsDecrypted += tmp.InOctetsDecrypted;
28418c2ecf20Sopenharmony_ci		sum->InPktsUnchecked   += tmp.InPktsUnchecked;
28428c2ecf20Sopenharmony_ci		sum->InPktsDelayed     += tmp.InPktsDelayed;
28438c2ecf20Sopenharmony_ci		sum->InPktsOK          += tmp.InPktsOK;
28448c2ecf20Sopenharmony_ci		sum->InPktsInvalid     += tmp.InPktsInvalid;
28458c2ecf20Sopenharmony_ci		sum->InPktsLate        += tmp.InPktsLate;
28468c2ecf20Sopenharmony_ci		sum->InPktsNotValid    += tmp.InPktsNotValid;
28478c2ecf20Sopenharmony_ci		sum->InPktsNotUsingSA  += tmp.InPktsNotUsingSA;
28488c2ecf20Sopenharmony_ci		sum->InPktsUnusedSA    += tmp.InPktsUnusedSA;
28498c2ecf20Sopenharmony_ci	}
28508c2ecf20Sopenharmony_ci}
28518c2ecf20Sopenharmony_ci
28528c2ecf20Sopenharmony_cistatic int copy_rx_sc_stats(struct sk_buff *skb, struct macsec_rx_sc_stats *sum)
28538c2ecf20Sopenharmony_ci{
28548c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED,
28558c2ecf20Sopenharmony_ci			      sum->InOctetsValidated,
28568c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD) ||
28578c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED,
28588c2ecf20Sopenharmony_ci			      sum->InOctetsDecrypted,
28598c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD) ||
28608c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED,
28618c2ecf20Sopenharmony_ci			      sum->InPktsUnchecked,
28628c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD) ||
28638c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED,
28648c2ecf20Sopenharmony_ci			      sum->InPktsDelayed,
28658c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD) ||
28668c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK,
28678c2ecf20Sopenharmony_ci			      sum->InPktsOK,
28688c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD) ||
28698c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID,
28708c2ecf20Sopenharmony_ci			      sum->InPktsInvalid,
28718c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD) ||
28728c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE,
28738c2ecf20Sopenharmony_ci			      sum->InPktsLate,
28748c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD) ||
28758c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID,
28768c2ecf20Sopenharmony_ci			      sum->InPktsNotValid,
28778c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD) ||
28788c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA,
28798c2ecf20Sopenharmony_ci			      sum->InPktsNotUsingSA,
28808c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD) ||
28818c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA,
28828c2ecf20Sopenharmony_ci			      sum->InPktsUnusedSA,
28838c2ecf20Sopenharmony_ci			      MACSEC_RXSC_STATS_ATTR_PAD))
28848c2ecf20Sopenharmony_ci		return -EMSGSIZE;
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci	return 0;
28878c2ecf20Sopenharmony_ci}
28888c2ecf20Sopenharmony_ci
28898c2ecf20Sopenharmony_cistatic void get_tx_sc_stats(struct net_device *dev,
28908c2ecf20Sopenharmony_ci			    struct macsec_tx_sc_stats *sum)
28918c2ecf20Sopenharmony_ci{
28928c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
28938c2ecf20Sopenharmony_ci	int cpu;
28948c2ecf20Sopenharmony_ci
28958c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
28968c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
28978c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
28988c2ecf20Sopenharmony_ci		struct macsec_context ctx;
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_ci		ops = macsec_get_ops(macsec, &ctx);
29018c2ecf20Sopenharmony_ci		if (ops) {
29028c2ecf20Sopenharmony_ci			ctx.stats.tx_sc_stats = sum;
29038c2ecf20Sopenharmony_ci			ctx.secy = &macsec_priv(dev)->secy;
29048c2ecf20Sopenharmony_ci			macsec_offload(ops->mdo_get_tx_sc_stats, &ctx);
29058c2ecf20Sopenharmony_ci		}
29068c2ecf20Sopenharmony_ci		return;
29078c2ecf20Sopenharmony_ci	}
29088c2ecf20Sopenharmony_ci
29098c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
29108c2ecf20Sopenharmony_ci		const struct pcpu_tx_sc_stats *stats;
29118c2ecf20Sopenharmony_ci		struct macsec_tx_sc_stats tmp;
29128c2ecf20Sopenharmony_ci		unsigned int start;
29138c2ecf20Sopenharmony_ci
29148c2ecf20Sopenharmony_ci		stats = per_cpu_ptr(macsec_priv(dev)->secy.tx_sc.stats, cpu);
29158c2ecf20Sopenharmony_ci		do {
29168c2ecf20Sopenharmony_ci			start = u64_stats_fetch_begin_irq(&stats->syncp);
29178c2ecf20Sopenharmony_ci			memcpy(&tmp, &stats->stats, sizeof(tmp));
29188c2ecf20Sopenharmony_ci		} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ci		sum->OutPktsProtected   += tmp.OutPktsProtected;
29218c2ecf20Sopenharmony_ci		sum->OutPktsEncrypted   += tmp.OutPktsEncrypted;
29228c2ecf20Sopenharmony_ci		sum->OutOctetsProtected += tmp.OutOctetsProtected;
29238c2ecf20Sopenharmony_ci		sum->OutOctetsEncrypted += tmp.OutOctetsEncrypted;
29248c2ecf20Sopenharmony_ci	}
29258c2ecf20Sopenharmony_ci}
29268c2ecf20Sopenharmony_ci
29278c2ecf20Sopenharmony_cistatic int copy_tx_sc_stats(struct sk_buff *skb, struct macsec_tx_sc_stats *sum)
29288c2ecf20Sopenharmony_ci{
29298c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED,
29308c2ecf20Sopenharmony_ci			      sum->OutPktsProtected,
29318c2ecf20Sopenharmony_ci			      MACSEC_TXSC_STATS_ATTR_PAD) ||
29328c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED,
29338c2ecf20Sopenharmony_ci			      sum->OutPktsEncrypted,
29348c2ecf20Sopenharmony_ci			      MACSEC_TXSC_STATS_ATTR_PAD) ||
29358c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED,
29368c2ecf20Sopenharmony_ci			      sum->OutOctetsProtected,
29378c2ecf20Sopenharmony_ci			      MACSEC_TXSC_STATS_ATTR_PAD) ||
29388c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED,
29398c2ecf20Sopenharmony_ci			      sum->OutOctetsEncrypted,
29408c2ecf20Sopenharmony_ci			      MACSEC_TXSC_STATS_ATTR_PAD))
29418c2ecf20Sopenharmony_ci		return -EMSGSIZE;
29428c2ecf20Sopenharmony_ci
29438c2ecf20Sopenharmony_ci	return 0;
29448c2ecf20Sopenharmony_ci}
29458c2ecf20Sopenharmony_ci
29468c2ecf20Sopenharmony_cistatic void get_secy_stats(struct net_device *dev, struct macsec_dev_stats *sum)
29478c2ecf20Sopenharmony_ci{
29488c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
29498c2ecf20Sopenharmony_ci	int cpu;
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
29528c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
29538c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
29548c2ecf20Sopenharmony_ci		struct macsec_context ctx;
29558c2ecf20Sopenharmony_ci
29568c2ecf20Sopenharmony_ci		ops = macsec_get_ops(macsec, &ctx);
29578c2ecf20Sopenharmony_ci		if (ops) {
29588c2ecf20Sopenharmony_ci			ctx.stats.dev_stats = sum;
29598c2ecf20Sopenharmony_ci			ctx.secy = &macsec_priv(dev)->secy;
29608c2ecf20Sopenharmony_ci			macsec_offload(ops->mdo_get_dev_stats, &ctx);
29618c2ecf20Sopenharmony_ci		}
29628c2ecf20Sopenharmony_ci		return;
29638c2ecf20Sopenharmony_ci	}
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
29668c2ecf20Sopenharmony_ci		const struct pcpu_secy_stats *stats;
29678c2ecf20Sopenharmony_ci		struct macsec_dev_stats tmp;
29688c2ecf20Sopenharmony_ci		unsigned int start;
29698c2ecf20Sopenharmony_ci
29708c2ecf20Sopenharmony_ci		stats = per_cpu_ptr(macsec_priv(dev)->stats, cpu);
29718c2ecf20Sopenharmony_ci		do {
29728c2ecf20Sopenharmony_ci			start = u64_stats_fetch_begin_irq(&stats->syncp);
29738c2ecf20Sopenharmony_ci			memcpy(&tmp, &stats->stats, sizeof(tmp));
29748c2ecf20Sopenharmony_ci		} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
29758c2ecf20Sopenharmony_ci
29768c2ecf20Sopenharmony_ci		sum->OutPktsUntagged  += tmp.OutPktsUntagged;
29778c2ecf20Sopenharmony_ci		sum->InPktsUntagged   += tmp.InPktsUntagged;
29788c2ecf20Sopenharmony_ci		sum->OutPktsTooLong   += tmp.OutPktsTooLong;
29798c2ecf20Sopenharmony_ci		sum->InPktsNoTag      += tmp.InPktsNoTag;
29808c2ecf20Sopenharmony_ci		sum->InPktsBadTag     += tmp.InPktsBadTag;
29818c2ecf20Sopenharmony_ci		sum->InPktsUnknownSCI += tmp.InPktsUnknownSCI;
29828c2ecf20Sopenharmony_ci		sum->InPktsNoSCI      += tmp.InPktsNoSCI;
29838c2ecf20Sopenharmony_ci		sum->InPktsOverrun    += tmp.InPktsOverrun;
29848c2ecf20Sopenharmony_ci	}
29858c2ecf20Sopenharmony_ci}
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_cistatic int copy_secy_stats(struct sk_buff *skb, struct macsec_dev_stats *sum)
29888c2ecf20Sopenharmony_ci{
29898c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED,
29908c2ecf20Sopenharmony_ci			      sum->OutPktsUntagged,
29918c2ecf20Sopenharmony_ci			      MACSEC_SECY_STATS_ATTR_PAD) ||
29928c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED,
29938c2ecf20Sopenharmony_ci			      sum->InPktsUntagged,
29948c2ecf20Sopenharmony_ci			      MACSEC_SECY_STATS_ATTR_PAD) ||
29958c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG,
29968c2ecf20Sopenharmony_ci			      sum->OutPktsTooLong,
29978c2ecf20Sopenharmony_ci			      MACSEC_SECY_STATS_ATTR_PAD) ||
29988c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG,
29998c2ecf20Sopenharmony_ci			      sum->InPktsNoTag,
30008c2ecf20Sopenharmony_ci			      MACSEC_SECY_STATS_ATTR_PAD) ||
30018c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG,
30028c2ecf20Sopenharmony_ci			      sum->InPktsBadTag,
30038c2ecf20Sopenharmony_ci			      MACSEC_SECY_STATS_ATTR_PAD) ||
30048c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI,
30058c2ecf20Sopenharmony_ci			      sum->InPktsUnknownSCI,
30068c2ecf20Sopenharmony_ci			      MACSEC_SECY_STATS_ATTR_PAD) ||
30078c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI,
30088c2ecf20Sopenharmony_ci			      sum->InPktsNoSCI,
30098c2ecf20Sopenharmony_ci			      MACSEC_SECY_STATS_ATTR_PAD) ||
30108c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN,
30118c2ecf20Sopenharmony_ci			      sum->InPktsOverrun,
30128c2ecf20Sopenharmony_ci			      MACSEC_SECY_STATS_ATTR_PAD))
30138c2ecf20Sopenharmony_ci		return -EMSGSIZE;
30148c2ecf20Sopenharmony_ci
30158c2ecf20Sopenharmony_ci	return 0;
30168c2ecf20Sopenharmony_ci}
30178c2ecf20Sopenharmony_ci
30188c2ecf20Sopenharmony_cistatic int nla_put_secy(struct macsec_secy *secy, struct sk_buff *skb)
30198c2ecf20Sopenharmony_ci{
30208c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc = &secy->tx_sc;
30218c2ecf20Sopenharmony_ci	struct nlattr *secy_nest = nla_nest_start_noflag(skb,
30228c2ecf20Sopenharmony_ci							 MACSEC_ATTR_SECY);
30238c2ecf20Sopenharmony_ci	u64 csid;
30248c2ecf20Sopenharmony_ci
30258c2ecf20Sopenharmony_ci	if (!secy_nest)
30268c2ecf20Sopenharmony_ci		return 1;
30278c2ecf20Sopenharmony_ci
30288c2ecf20Sopenharmony_ci	switch (secy->key_len) {
30298c2ecf20Sopenharmony_ci	case MACSEC_GCM_AES_128_SAK_LEN:
30308c2ecf20Sopenharmony_ci		csid = secy->xpn ? MACSEC_CIPHER_ID_GCM_AES_XPN_128 : MACSEC_DEFAULT_CIPHER_ID;
30318c2ecf20Sopenharmony_ci		break;
30328c2ecf20Sopenharmony_ci	case MACSEC_GCM_AES_256_SAK_LEN:
30338c2ecf20Sopenharmony_ci		csid = secy->xpn ? MACSEC_CIPHER_ID_GCM_AES_XPN_256 : MACSEC_CIPHER_ID_GCM_AES_256;
30348c2ecf20Sopenharmony_ci		break;
30358c2ecf20Sopenharmony_ci	default:
30368c2ecf20Sopenharmony_ci		goto cancel;
30378c2ecf20Sopenharmony_ci	}
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_ci	if (nla_put_sci(skb, MACSEC_SECY_ATTR_SCI, secy->sci,
30408c2ecf20Sopenharmony_ci			MACSEC_SECY_ATTR_PAD) ||
30418c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MACSEC_SECY_ATTR_CIPHER_SUITE,
30428c2ecf20Sopenharmony_ci			      csid, MACSEC_SECY_ATTR_PAD) ||
30438c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_ICV_LEN, secy->icv_len) ||
30448c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_OPER, secy->operational) ||
30458c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_PROTECT, secy->protect_frames) ||
30468c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_REPLAY, secy->replay_protect) ||
30478c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_VALIDATE, secy->validate_frames) ||
30488c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_ENCRYPT, tx_sc->encrypt) ||
30498c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_INC_SCI, tx_sc->send_sci) ||
30508c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_ES, tx_sc->end_station) ||
30518c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_SCB, tx_sc->scb) ||
30528c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MACSEC_SECY_ATTR_ENCODING_SA, tx_sc->encoding_sa))
30538c2ecf20Sopenharmony_ci		goto cancel;
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_ci	if (secy->replay_protect) {
30568c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, MACSEC_SECY_ATTR_WINDOW, secy->replay_window))
30578c2ecf20Sopenharmony_ci			goto cancel;
30588c2ecf20Sopenharmony_ci	}
30598c2ecf20Sopenharmony_ci
30608c2ecf20Sopenharmony_ci	nla_nest_end(skb, secy_nest);
30618c2ecf20Sopenharmony_ci	return 0;
30628c2ecf20Sopenharmony_ci
30638c2ecf20Sopenharmony_cicancel:
30648c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, secy_nest);
30658c2ecf20Sopenharmony_ci	return 1;
30668c2ecf20Sopenharmony_ci}
30678c2ecf20Sopenharmony_ci
30688c2ecf20Sopenharmony_cistatic noinline_for_stack int
30698c2ecf20Sopenharmony_cidump_secy(struct macsec_secy *secy, struct net_device *dev,
30708c2ecf20Sopenharmony_ci	  struct sk_buff *skb, struct netlink_callback *cb)
30718c2ecf20Sopenharmony_ci{
30728c2ecf20Sopenharmony_ci	struct macsec_tx_sc_stats tx_sc_stats = {0, };
30738c2ecf20Sopenharmony_ci	struct macsec_tx_sa_stats tx_sa_stats = {0, };
30748c2ecf20Sopenharmony_ci	struct macsec_rx_sc_stats rx_sc_stats = {0, };
30758c2ecf20Sopenharmony_ci	struct macsec_rx_sa_stats rx_sa_stats = {0, };
30768c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = netdev_priv(dev);
30778c2ecf20Sopenharmony_ci	struct macsec_dev_stats dev_stats = {0, };
30788c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc = &secy->tx_sc;
30798c2ecf20Sopenharmony_ci	struct nlattr *txsa_list, *rxsc_list;
30808c2ecf20Sopenharmony_ci	struct macsec_rx_sc *rx_sc;
30818c2ecf20Sopenharmony_ci	struct nlattr *attr;
30828c2ecf20Sopenharmony_ci	void *hdr;
30838c2ecf20Sopenharmony_ci	int i, j;
30848c2ecf20Sopenharmony_ci
30858c2ecf20Sopenharmony_ci	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
30868c2ecf20Sopenharmony_ci			  &macsec_fam, NLM_F_MULTI, MACSEC_CMD_GET_TXSC);
30878c2ecf20Sopenharmony_ci	if (!hdr)
30888c2ecf20Sopenharmony_ci		return -EMSGSIZE;
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_ci	genl_dump_check_consistent(cb, hdr);
30918c2ecf20Sopenharmony_ci
30928c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, MACSEC_ATTR_IFINDEX, dev->ifindex))
30938c2ecf20Sopenharmony_ci		goto nla_put_failure;
30948c2ecf20Sopenharmony_ci
30958c2ecf20Sopenharmony_ci	attr = nla_nest_start_noflag(skb, MACSEC_ATTR_OFFLOAD);
30968c2ecf20Sopenharmony_ci	if (!attr)
30978c2ecf20Sopenharmony_ci		goto nla_put_failure;
30988c2ecf20Sopenharmony_ci	if (nla_put_u8(skb, MACSEC_OFFLOAD_ATTR_TYPE, macsec->offload))
30998c2ecf20Sopenharmony_ci		goto nla_put_failure;
31008c2ecf20Sopenharmony_ci	nla_nest_end(skb, attr);
31018c2ecf20Sopenharmony_ci
31028c2ecf20Sopenharmony_ci	if (nla_put_secy(secy, skb))
31038c2ecf20Sopenharmony_ci		goto nla_put_failure;
31048c2ecf20Sopenharmony_ci
31058c2ecf20Sopenharmony_ci	attr = nla_nest_start_noflag(skb, MACSEC_ATTR_TXSC_STATS);
31068c2ecf20Sopenharmony_ci	if (!attr)
31078c2ecf20Sopenharmony_ci		goto nla_put_failure;
31088c2ecf20Sopenharmony_ci
31098c2ecf20Sopenharmony_ci	get_tx_sc_stats(dev, &tx_sc_stats);
31108c2ecf20Sopenharmony_ci	if (copy_tx_sc_stats(skb, &tx_sc_stats)) {
31118c2ecf20Sopenharmony_ci		nla_nest_cancel(skb, attr);
31128c2ecf20Sopenharmony_ci		goto nla_put_failure;
31138c2ecf20Sopenharmony_ci	}
31148c2ecf20Sopenharmony_ci	nla_nest_end(skb, attr);
31158c2ecf20Sopenharmony_ci
31168c2ecf20Sopenharmony_ci	attr = nla_nest_start_noflag(skb, MACSEC_ATTR_SECY_STATS);
31178c2ecf20Sopenharmony_ci	if (!attr)
31188c2ecf20Sopenharmony_ci		goto nla_put_failure;
31198c2ecf20Sopenharmony_ci	get_secy_stats(dev, &dev_stats);
31208c2ecf20Sopenharmony_ci	if (copy_secy_stats(skb, &dev_stats)) {
31218c2ecf20Sopenharmony_ci		nla_nest_cancel(skb, attr);
31228c2ecf20Sopenharmony_ci		goto nla_put_failure;
31238c2ecf20Sopenharmony_ci	}
31248c2ecf20Sopenharmony_ci	nla_nest_end(skb, attr);
31258c2ecf20Sopenharmony_ci
31268c2ecf20Sopenharmony_ci	txsa_list = nla_nest_start_noflag(skb, MACSEC_ATTR_TXSA_LIST);
31278c2ecf20Sopenharmony_ci	if (!txsa_list)
31288c2ecf20Sopenharmony_ci		goto nla_put_failure;
31298c2ecf20Sopenharmony_ci	for (i = 0, j = 1; i < MACSEC_NUM_AN; i++) {
31308c2ecf20Sopenharmony_ci		struct macsec_tx_sa *tx_sa = rtnl_dereference(tx_sc->sa[i]);
31318c2ecf20Sopenharmony_ci		struct nlattr *txsa_nest;
31328c2ecf20Sopenharmony_ci		u64 pn;
31338c2ecf20Sopenharmony_ci		int pn_len;
31348c2ecf20Sopenharmony_ci
31358c2ecf20Sopenharmony_ci		if (!tx_sa)
31368c2ecf20Sopenharmony_ci			continue;
31378c2ecf20Sopenharmony_ci
31388c2ecf20Sopenharmony_ci		txsa_nest = nla_nest_start_noflag(skb, j++);
31398c2ecf20Sopenharmony_ci		if (!txsa_nest) {
31408c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, txsa_list);
31418c2ecf20Sopenharmony_ci			goto nla_put_failure;
31428c2ecf20Sopenharmony_ci		}
31438c2ecf20Sopenharmony_ci
31448c2ecf20Sopenharmony_ci		attr = nla_nest_start_noflag(skb, MACSEC_SA_ATTR_STATS);
31458c2ecf20Sopenharmony_ci		if (!attr) {
31468c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, txsa_nest);
31478c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, txsa_list);
31488c2ecf20Sopenharmony_ci			goto nla_put_failure;
31498c2ecf20Sopenharmony_ci		}
31508c2ecf20Sopenharmony_ci		memset(&tx_sa_stats, 0, sizeof(tx_sa_stats));
31518c2ecf20Sopenharmony_ci		get_tx_sa_stats(dev, i, tx_sa, &tx_sa_stats);
31528c2ecf20Sopenharmony_ci		if (copy_tx_sa_stats(skb, &tx_sa_stats)) {
31538c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, attr);
31548c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, txsa_nest);
31558c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, txsa_list);
31568c2ecf20Sopenharmony_ci			goto nla_put_failure;
31578c2ecf20Sopenharmony_ci		}
31588c2ecf20Sopenharmony_ci		nla_nest_end(skb, attr);
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_ci		if (secy->xpn) {
31618c2ecf20Sopenharmony_ci			pn = tx_sa->next_pn;
31628c2ecf20Sopenharmony_ci			pn_len = MACSEC_XPN_PN_LEN;
31638c2ecf20Sopenharmony_ci		} else {
31648c2ecf20Sopenharmony_ci			pn = tx_sa->next_pn_halves.lower;
31658c2ecf20Sopenharmony_ci			pn_len = MACSEC_DEFAULT_PN_LEN;
31668c2ecf20Sopenharmony_ci		}
31678c2ecf20Sopenharmony_ci
31688c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) ||
31698c2ecf20Sopenharmony_ci		    nla_put(skb, MACSEC_SA_ATTR_PN, pn_len, &pn) ||
31708c2ecf20Sopenharmony_ci		    nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, tx_sa->key.id) ||
31718c2ecf20Sopenharmony_ci		    (secy->xpn && nla_put_ssci(skb, MACSEC_SA_ATTR_SSCI, tx_sa->ssci)) ||
31728c2ecf20Sopenharmony_ci		    nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, tx_sa->active)) {
31738c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, txsa_nest);
31748c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, txsa_list);
31758c2ecf20Sopenharmony_ci			goto nla_put_failure;
31768c2ecf20Sopenharmony_ci		}
31778c2ecf20Sopenharmony_ci
31788c2ecf20Sopenharmony_ci		nla_nest_end(skb, txsa_nest);
31798c2ecf20Sopenharmony_ci	}
31808c2ecf20Sopenharmony_ci	nla_nest_end(skb, txsa_list);
31818c2ecf20Sopenharmony_ci
31828c2ecf20Sopenharmony_ci	rxsc_list = nla_nest_start_noflag(skb, MACSEC_ATTR_RXSC_LIST);
31838c2ecf20Sopenharmony_ci	if (!rxsc_list)
31848c2ecf20Sopenharmony_ci		goto nla_put_failure;
31858c2ecf20Sopenharmony_ci
31868c2ecf20Sopenharmony_ci	j = 1;
31878c2ecf20Sopenharmony_ci	for_each_rxsc_rtnl(secy, rx_sc) {
31888c2ecf20Sopenharmony_ci		int k;
31898c2ecf20Sopenharmony_ci		struct nlattr *rxsa_list;
31908c2ecf20Sopenharmony_ci		struct nlattr *rxsc_nest = nla_nest_start_noflag(skb, j++);
31918c2ecf20Sopenharmony_ci
31928c2ecf20Sopenharmony_ci		if (!rxsc_nest) {
31938c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, rxsc_list);
31948c2ecf20Sopenharmony_ci			goto nla_put_failure;
31958c2ecf20Sopenharmony_ci		}
31968c2ecf20Sopenharmony_ci
31978c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, MACSEC_RXSC_ATTR_ACTIVE, rx_sc->active) ||
31988c2ecf20Sopenharmony_ci		    nla_put_sci(skb, MACSEC_RXSC_ATTR_SCI, rx_sc->sci,
31998c2ecf20Sopenharmony_ci				MACSEC_RXSC_ATTR_PAD)) {
32008c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, rxsc_nest);
32018c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, rxsc_list);
32028c2ecf20Sopenharmony_ci			goto nla_put_failure;
32038c2ecf20Sopenharmony_ci		}
32048c2ecf20Sopenharmony_ci
32058c2ecf20Sopenharmony_ci		attr = nla_nest_start_noflag(skb, MACSEC_RXSC_ATTR_STATS);
32068c2ecf20Sopenharmony_ci		if (!attr) {
32078c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, rxsc_nest);
32088c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, rxsc_list);
32098c2ecf20Sopenharmony_ci			goto nla_put_failure;
32108c2ecf20Sopenharmony_ci		}
32118c2ecf20Sopenharmony_ci		memset(&rx_sc_stats, 0, sizeof(rx_sc_stats));
32128c2ecf20Sopenharmony_ci		get_rx_sc_stats(dev, rx_sc, &rx_sc_stats);
32138c2ecf20Sopenharmony_ci		if (copy_rx_sc_stats(skb, &rx_sc_stats)) {
32148c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, attr);
32158c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, rxsc_nest);
32168c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, rxsc_list);
32178c2ecf20Sopenharmony_ci			goto nla_put_failure;
32188c2ecf20Sopenharmony_ci		}
32198c2ecf20Sopenharmony_ci		nla_nest_end(skb, attr);
32208c2ecf20Sopenharmony_ci
32218c2ecf20Sopenharmony_ci		rxsa_list = nla_nest_start_noflag(skb,
32228c2ecf20Sopenharmony_ci						  MACSEC_RXSC_ATTR_SA_LIST);
32238c2ecf20Sopenharmony_ci		if (!rxsa_list) {
32248c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, rxsc_nest);
32258c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, rxsc_list);
32268c2ecf20Sopenharmony_ci			goto nla_put_failure;
32278c2ecf20Sopenharmony_ci		}
32288c2ecf20Sopenharmony_ci
32298c2ecf20Sopenharmony_ci		for (i = 0, k = 1; i < MACSEC_NUM_AN; i++) {
32308c2ecf20Sopenharmony_ci			struct macsec_rx_sa *rx_sa = rtnl_dereference(rx_sc->sa[i]);
32318c2ecf20Sopenharmony_ci			struct nlattr *rxsa_nest;
32328c2ecf20Sopenharmony_ci			u64 pn;
32338c2ecf20Sopenharmony_ci			int pn_len;
32348c2ecf20Sopenharmony_ci
32358c2ecf20Sopenharmony_ci			if (!rx_sa)
32368c2ecf20Sopenharmony_ci				continue;
32378c2ecf20Sopenharmony_ci
32388c2ecf20Sopenharmony_ci			rxsa_nest = nla_nest_start_noflag(skb, k++);
32398c2ecf20Sopenharmony_ci			if (!rxsa_nest) {
32408c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsa_list);
32418c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsc_nest);
32428c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsc_list);
32438c2ecf20Sopenharmony_ci				goto nla_put_failure;
32448c2ecf20Sopenharmony_ci			}
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_ci			attr = nla_nest_start_noflag(skb,
32478c2ecf20Sopenharmony_ci						     MACSEC_SA_ATTR_STATS);
32488c2ecf20Sopenharmony_ci			if (!attr) {
32498c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsa_list);
32508c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsc_nest);
32518c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsc_list);
32528c2ecf20Sopenharmony_ci				goto nla_put_failure;
32538c2ecf20Sopenharmony_ci			}
32548c2ecf20Sopenharmony_ci			memset(&rx_sa_stats, 0, sizeof(rx_sa_stats));
32558c2ecf20Sopenharmony_ci			get_rx_sa_stats(dev, rx_sc, i, rx_sa, &rx_sa_stats);
32568c2ecf20Sopenharmony_ci			if (copy_rx_sa_stats(skb, &rx_sa_stats)) {
32578c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, attr);
32588c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsa_list);
32598c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsc_nest);
32608c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsc_list);
32618c2ecf20Sopenharmony_ci				goto nla_put_failure;
32628c2ecf20Sopenharmony_ci			}
32638c2ecf20Sopenharmony_ci			nla_nest_end(skb, attr);
32648c2ecf20Sopenharmony_ci
32658c2ecf20Sopenharmony_ci			if (secy->xpn) {
32668c2ecf20Sopenharmony_ci				pn = rx_sa->next_pn;
32678c2ecf20Sopenharmony_ci				pn_len = MACSEC_XPN_PN_LEN;
32688c2ecf20Sopenharmony_ci			} else {
32698c2ecf20Sopenharmony_ci				pn = rx_sa->next_pn_halves.lower;
32708c2ecf20Sopenharmony_ci				pn_len = MACSEC_DEFAULT_PN_LEN;
32718c2ecf20Sopenharmony_ci			}
32728c2ecf20Sopenharmony_ci
32738c2ecf20Sopenharmony_ci			if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) ||
32748c2ecf20Sopenharmony_ci			    nla_put(skb, MACSEC_SA_ATTR_PN, pn_len, &pn) ||
32758c2ecf20Sopenharmony_ci			    nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, rx_sa->key.id) ||
32768c2ecf20Sopenharmony_ci			    (secy->xpn && nla_put_ssci(skb, MACSEC_SA_ATTR_SSCI, rx_sa->ssci)) ||
32778c2ecf20Sopenharmony_ci			    nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, rx_sa->active)) {
32788c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsa_nest);
32798c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsc_nest);
32808c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, rxsc_list);
32818c2ecf20Sopenharmony_ci				goto nla_put_failure;
32828c2ecf20Sopenharmony_ci			}
32838c2ecf20Sopenharmony_ci			nla_nest_end(skb, rxsa_nest);
32848c2ecf20Sopenharmony_ci		}
32858c2ecf20Sopenharmony_ci
32868c2ecf20Sopenharmony_ci		nla_nest_end(skb, rxsa_list);
32878c2ecf20Sopenharmony_ci		nla_nest_end(skb, rxsc_nest);
32888c2ecf20Sopenharmony_ci	}
32898c2ecf20Sopenharmony_ci
32908c2ecf20Sopenharmony_ci	nla_nest_end(skb, rxsc_list);
32918c2ecf20Sopenharmony_ci
32928c2ecf20Sopenharmony_ci	genlmsg_end(skb, hdr);
32938c2ecf20Sopenharmony_ci
32948c2ecf20Sopenharmony_ci	return 0;
32958c2ecf20Sopenharmony_ci
32968c2ecf20Sopenharmony_cinla_put_failure:
32978c2ecf20Sopenharmony_ci	genlmsg_cancel(skb, hdr);
32988c2ecf20Sopenharmony_ci	return -EMSGSIZE;
32998c2ecf20Sopenharmony_ci}
33008c2ecf20Sopenharmony_ci
33018c2ecf20Sopenharmony_cistatic int macsec_generation = 1; /* protected by RTNL */
33028c2ecf20Sopenharmony_ci
33038c2ecf20Sopenharmony_cistatic int macsec_dump_txsc(struct sk_buff *skb, struct netlink_callback *cb)
33048c2ecf20Sopenharmony_ci{
33058c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
33068c2ecf20Sopenharmony_ci	struct net_device *dev;
33078c2ecf20Sopenharmony_ci	int dev_idx, d;
33088c2ecf20Sopenharmony_ci
33098c2ecf20Sopenharmony_ci	dev_idx = cb->args[0];
33108c2ecf20Sopenharmony_ci
33118c2ecf20Sopenharmony_ci	d = 0;
33128c2ecf20Sopenharmony_ci	rtnl_lock();
33138c2ecf20Sopenharmony_ci
33148c2ecf20Sopenharmony_ci	cb->seq = macsec_generation;
33158c2ecf20Sopenharmony_ci
33168c2ecf20Sopenharmony_ci	for_each_netdev(net, dev) {
33178c2ecf20Sopenharmony_ci		struct macsec_secy *secy;
33188c2ecf20Sopenharmony_ci
33198c2ecf20Sopenharmony_ci		if (d < dev_idx)
33208c2ecf20Sopenharmony_ci			goto next;
33218c2ecf20Sopenharmony_ci
33228c2ecf20Sopenharmony_ci		if (!netif_is_macsec(dev))
33238c2ecf20Sopenharmony_ci			goto next;
33248c2ecf20Sopenharmony_ci
33258c2ecf20Sopenharmony_ci		secy = &macsec_priv(dev)->secy;
33268c2ecf20Sopenharmony_ci		if (dump_secy(secy, dev, skb, cb) < 0)
33278c2ecf20Sopenharmony_ci			goto done;
33288c2ecf20Sopenharmony_cinext:
33298c2ecf20Sopenharmony_ci		d++;
33308c2ecf20Sopenharmony_ci	}
33318c2ecf20Sopenharmony_ci
33328c2ecf20Sopenharmony_cidone:
33338c2ecf20Sopenharmony_ci	rtnl_unlock();
33348c2ecf20Sopenharmony_ci	cb->args[0] = d;
33358c2ecf20Sopenharmony_ci	return skb->len;
33368c2ecf20Sopenharmony_ci}
33378c2ecf20Sopenharmony_ci
33388c2ecf20Sopenharmony_cistatic const struct genl_small_ops macsec_genl_ops[] = {
33398c2ecf20Sopenharmony_ci	{
33408c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_GET_TXSC,
33418c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33428c2ecf20Sopenharmony_ci		.dumpit = macsec_dump_txsc,
33438c2ecf20Sopenharmony_ci	},
33448c2ecf20Sopenharmony_ci	{
33458c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_ADD_RXSC,
33468c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33478c2ecf20Sopenharmony_ci		.doit = macsec_add_rxsc,
33488c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
33498c2ecf20Sopenharmony_ci	},
33508c2ecf20Sopenharmony_ci	{
33518c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_DEL_RXSC,
33528c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33538c2ecf20Sopenharmony_ci		.doit = macsec_del_rxsc,
33548c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
33558c2ecf20Sopenharmony_ci	},
33568c2ecf20Sopenharmony_ci	{
33578c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_UPD_RXSC,
33588c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33598c2ecf20Sopenharmony_ci		.doit = macsec_upd_rxsc,
33608c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
33618c2ecf20Sopenharmony_ci	},
33628c2ecf20Sopenharmony_ci	{
33638c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_ADD_TXSA,
33648c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33658c2ecf20Sopenharmony_ci		.doit = macsec_add_txsa,
33668c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
33678c2ecf20Sopenharmony_ci	},
33688c2ecf20Sopenharmony_ci	{
33698c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_DEL_TXSA,
33708c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33718c2ecf20Sopenharmony_ci		.doit = macsec_del_txsa,
33728c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
33738c2ecf20Sopenharmony_ci	},
33748c2ecf20Sopenharmony_ci	{
33758c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_UPD_TXSA,
33768c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33778c2ecf20Sopenharmony_ci		.doit = macsec_upd_txsa,
33788c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
33798c2ecf20Sopenharmony_ci	},
33808c2ecf20Sopenharmony_ci	{
33818c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_ADD_RXSA,
33828c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33838c2ecf20Sopenharmony_ci		.doit = macsec_add_rxsa,
33848c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
33858c2ecf20Sopenharmony_ci	},
33868c2ecf20Sopenharmony_ci	{
33878c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_DEL_RXSA,
33888c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33898c2ecf20Sopenharmony_ci		.doit = macsec_del_rxsa,
33908c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
33918c2ecf20Sopenharmony_ci	},
33928c2ecf20Sopenharmony_ci	{
33938c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_UPD_RXSA,
33948c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
33958c2ecf20Sopenharmony_ci		.doit = macsec_upd_rxsa,
33968c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
33978c2ecf20Sopenharmony_ci	},
33988c2ecf20Sopenharmony_ci	{
33998c2ecf20Sopenharmony_ci		.cmd = MACSEC_CMD_UPD_OFFLOAD,
34008c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
34018c2ecf20Sopenharmony_ci		.doit = macsec_upd_offload,
34028c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
34038c2ecf20Sopenharmony_ci	},
34048c2ecf20Sopenharmony_ci};
34058c2ecf20Sopenharmony_ci
34068c2ecf20Sopenharmony_cistatic struct genl_family macsec_fam __ro_after_init = {
34078c2ecf20Sopenharmony_ci	.name		= MACSEC_GENL_NAME,
34088c2ecf20Sopenharmony_ci	.hdrsize	= 0,
34098c2ecf20Sopenharmony_ci	.version	= MACSEC_GENL_VERSION,
34108c2ecf20Sopenharmony_ci	.maxattr	= MACSEC_ATTR_MAX,
34118c2ecf20Sopenharmony_ci	.policy = macsec_genl_policy,
34128c2ecf20Sopenharmony_ci	.netnsok	= true,
34138c2ecf20Sopenharmony_ci	.module		= THIS_MODULE,
34148c2ecf20Sopenharmony_ci	.small_ops	= macsec_genl_ops,
34158c2ecf20Sopenharmony_ci	.n_small_ops	= ARRAY_SIZE(macsec_genl_ops),
34168c2ecf20Sopenharmony_ci};
34178c2ecf20Sopenharmony_ci
34188c2ecf20Sopenharmony_cistatic netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
34198c2ecf20Sopenharmony_ci				     struct net_device *dev)
34208c2ecf20Sopenharmony_ci{
34218c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = netdev_priv(dev);
34228c2ecf20Sopenharmony_ci	struct macsec_secy *secy = &macsec->secy;
34238c2ecf20Sopenharmony_ci	struct pcpu_secy_stats *secy_stats;
34248c2ecf20Sopenharmony_ci	int ret, len;
34258c2ecf20Sopenharmony_ci
34268c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(netdev_priv(dev))) {
34278c2ecf20Sopenharmony_ci		skb->dev = macsec->real_dev;
34288c2ecf20Sopenharmony_ci		return dev_queue_xmit(skb);
34298c2ecf20Sopenharmony_ci	}
34308c2ecf20Sopenharmony_ci
34318c2ecf20Sopenharmony_ci	/* 10.5 */
34328c2ecf20Sopenharmony_ci	if (!secy->protect_frames) {
34338c2ecf20Sopenharmony_ci		secy_stats = this_cpu_ptr(macsec->stats);
34348c2ecf20Sopenharmony_ci		u64_stats_update_begin(&secy_stats->syncp);
34358c2ecf20Sopenharmony_ci		secy_stats->stats.OutPktsUntagged++;
34368c2ecf20Sopenharmony_ci		u64_stats_update_end(&secy_stats->syncp);
34378c2ecf20Sopenharmony_ci		skb->dev = macsec->real_dev;
34388c2ecf20Sopenharmony_ci		len = skb->len;
34398c2ecf20Sopenharmony_ci		ret = dev_queue_xmit(skb);
34408c2ecf20Sopenharmony_ci		count_tx(dev, ret, len);
34418c2ecf20Sopenharmony_ci		return ret;
34428c2ecf20Sopenharmony_ci	}
34438c2ecf20Sopenharmony_ci
34448c2ecf20Sopenharmony_ci	if (!secy->operational) {
34458c2ecf20Sopenharmony_ci		kfree_skb(skb);
34468c2ecf20Sopenharmony_ci		DEV_STATS_INC(dev, tx_dropped);
34478c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
34488c2ecf20Sopenharmony_ci	}
34498c2ecf20Sopenharmony_ci
34508c2ecf20Sopenharmony_ci	len = skb->len;
34518c2ecf20Sopenharmony_ci	skb = macsec_encrypt(skb, dev);
34528c2ecf20Sopenharmony_ci	if (IS_ERR(skb)) {
34538c2ecf20Sopenharmony_ci		if (PTR_ERR(skb) != -EINPROGRESS)
34548c2ecf20Sopenharmony_ci			DEV_STATS_INC(dev, tx_dropped);
34558c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
34568c2ecf20Sopenharmony_ci	}
34578c2ecf20Sopenharmony_ci
34588c2ecf20Sopenharmony_ci	macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa);
34598c2ecf20Sopenharmony_ci
34608c2ecf20Sopenharmony_ci	macsec_encrypt_finish(skb, dev);
34618c2ecf20Sopenharmony_ci	ret = dev_queue_xmit(skb);
34628c2ecf20Sopenharmony_ci	count_tx(dev, ret, len);
34638c2ecf20Sopenharmony_ci	return ret;
34648c2ecf20Sopenharmony_ci}
34658c2ecf20Sopenharmony_ci
34668c2ecf20Sopenharmony_ci#define MACSEC_FEATURES \
34678c2ecf20Sopenharmony_ci	(NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST)
34688c2ecf20Sopenharmony_ci
34698c2ecf20Sopenharmony_cistatic int macsec_dev_init(struct net_device *dev)
34708c2ecf20Sopenharmony_ci{
34718c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
34728c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec->real_dev;
34738c2ecf20Sopenharmony_ci	int err;
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
34768c2ecf20Sopenharmony_ci	if (!dev->tstats)
34778c2ecf20Sopenharmony_ci		return -ENOMEM;
34788c2ecf20Sopenharmony_ci
34798c2ecf20Sopenharmony_ci	err = gro_cells_init(&macsec->gro_cells, dev);
34808c2ecf20Sopenharmony_ci	if (err) {
34818c2ecf20Sopenharmony_ci		free_percpu(dev->tstats);
34828c2ecf20Sopenharmony_ci		return err;
34838c2ecf20Sopenharmony_ci	}
34848c2ecf20Sopenharmony_ci
34858c2ecf20Sopenharmony_ci	dev->features = real_dev->features & MACSEC_FEATURES;
34868c2ecf20Sopenharmony_ci	dev->features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE;
34878c2ecf20Sopenharmony_ci
34888c2ecf20Sopenharmony_ci	dev->needed_headroom = real_dev->needed_headroom +
34898c2ecf20Sopenharmony_ci			       MACSEC_NEEDED_HEADROOM;
34908c2ecf20Sopenharmony_ci	dev->needed_tailroom = real_dev->needed_tailroom +
34918c2ecf20Sopenharmony_ci			       MACSEC_NEEDED_TAILROOM;
34928c2ecf20Sopenharmony_ci
34938c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(dev->dev_addr))
34948c2ecf20Sopenharmony_ci		eth_hw_addr_inherit(dev, real_dev);
34958c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(dev->broadcast))
34968c2ecf20Sopenharmony_ci		memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len);
34978c2ecf20Sopenharmony_ci
34988c2ecf20Sopenharmony_ci	return 0;
34998c2ecf20Sopenharmony_ci}
35008c2ecf20Sopenharmony_ci
35018c2ecf20Sopenharmony_cistatic void macsec_dev_uninit(struct net_device *dev)
35028c2ecf20Sopenharmony_ci{
35038c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
35048c2ecf20Sopenharmony_ci
35058c2ecf20Sopenharmony_ci	gro_cells_destroy(&macsec->gro_cells);
35068c2ecf20Sopenharmony_ci	free_percpu(dev->tstats);
35078c2ecf20Sopenharmony_ci}
35088c2ecf20Sopenharmony_ci
35098c2ecf20Sopenharmony_cistatic netdev_features_t macsec_fix_features(struct net_device *dev,
35108c2ecf20Sopenharmony_ci					     netdev_features_t features)
35118c2ecf20Sopenharmony_ci{
35128c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
35138c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec->real_dev;
35148c2ecf20Sopenharmony_ci
35158c2ecf20Sopenharmony_ci	features &= (real_dev->features & MACSEC_FEATURES) |
35168c2ecf20Sopenharmony_ci		    NETIF_F_GSO_SOFTWARE | NETIF_F_SOFT_FEATURES;
35178c2ecf20Sopenharmony_ci	features |= NETIF_F_LLTX;
35188c2ecf20Sopenharmony_ci
35198c2ecf20Sopenharmony_ci	return features;
35208c2ecf20Sopenharmony_ci}
35218c2ecf20Sopenharmony_ci
35228c2ecf20Sopenharmony_cistatic int macsec_dev_open(struct net_device *dev)
35238c2ecf20Sopenharmony_ci{
35248c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
35258c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec->real_dev;
35268c2ecf20Sopenharmony_ci	int err;
35278c2ecf20Sopenharmony_ci
35288c2ecf20Sopenharmony_ci	err = dev_uc_add(real_dev, dev->dev_addr);
35298c2ecf20Sopenharmony_ci	if (err < 0)
35308c2ecf20Sopenharmony_ci		return err;
35318c2ecf20Sopenharmony_ci
35328c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
35338c2ecf20Sopenharmony_ci		err = dev_set_allmulti(real_dev, 1);
35348c2ecf20Sopenharmony_ci		if (err < 0)
35358c2ecf20Sopenharmony_ci			goto del_unicast;
35368c2ecf20Sopenharmony_ci	}
35378c2ecf20Sopenharmony_ci
35388c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
35398c2ecf20Sopenharmony_ci		err = dev_set_promiscuity(real_dev, 1);
35408c2ecf20Sopenharmony_ci		if (err < 0)
35418c2ecf20Sopenharmony_ci			goto clear_allmulti;
35428c2ecf20Sopenharmony_ci	}
35438c2ecf20Sopenharmony_ci
35448c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
35458c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
35468c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
35478c2ecf20Sopenharmony_ci		struct macsec_context ctx;
35488c2ecf20Sopenharmony_ci
35498c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
35508c2ecf20Sopenharmony_ci		if (!ops) {
35518c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
35528c2ecf20Sopenharmony_ci			goto clear_allmulti;
35538c2ecf20Sopenharmony_ci		}
35548c2ecf20Sopenharmony_ci
35558c2ecf20Sopenharmony_ci		ctx.secy = &macsec->secy;
35568c2ecf20Sopenharmony_ci		err = macsec_offload(ops->mdo_dev_open, &ctx);
35578c2ecf20Sopenharmony_ci		if (err)
35588c2ecf20Sopenharmony_ci			goto clear_allmulti;
35598c2ecf20Sopenharmony_ci	}
35608c2ecf20Sopenharmony_ci
35618c2ecf20Sopenharmony_ci	if (netif_carrier_ok(real_dev))
35628c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
35638c2ecf20Sopenharmony_ci
35648c2ecf20Sopenharmony_ci	return 0;
35658c2ecf20Sopenharmony_ciclear_allmulti:
35668c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI)
35678c2ecf20Sopenharmony_ci		dev_set_allmulti(real_dev, -1);
35688c2ecf20Sopenharmony_cidel_unicast:
35698c2ecf20Sopenharmony_ci	dev_uc_del(real_dev, dev->dev_addr);
35708c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
35718c2ecf20Sopenharmony_ci	return err;
35728c2ecf20Sopenharmony_ci}
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_cistatic int macsec_dev_stop(struct net_device *dev)
35758c2ecf20Sopenharmony_ci{
35768c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
35778c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec->real_dev;
35788c2ecf20Sopenharmony_ci
35798c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
35808c2ecf20Sopenharmony_ci
35818c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
35828c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
35838c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
35848c2ecf20Sopenharmony_ci		struct macsec_context ctx;
35858c2ecf20Sopenharmony_ci
35868c2ecf20Sopenharmony_ci		ops = macsec_get_ops(macsec, &ctx);
35878c2ecf20Sopenharmony_ci		if (ops) {
35888c2ecf20Sopenharmony_ci			ctx.secy = &macsec->secy;
35898c2ecf20Sopenharmony_ci			macsec_offload(ops->mdo_dev_stop, &ctx);
35908c2ecf20Sopenharmony_ci		}
35918c2ecf20Sopenharmony_ci	}
35928c2ecf20Sopenharmony_ci
35938c2ecf20Sopenharmony_ci	dev_mc_unsync(real_dev, dev);
35948c2ecf20Sopenharmony_ci	dev_uc_unsync(real_dev, dev);
35958c2ecf20Sopenharmony_ci
35968c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI)
35978c2ecf20Sopenharmony_ci		dev_set_allmulti(real_dev, -1);
35988c2ecf20Sopenharmony_ci
35998c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC)
36008c2ecf20Sopenharmony_ci		dev_set_promiscuity(real_dev, -1);
36018c2ecf20Sopenharmony_ci
36028c2ecf20Sopenharmony_ci	dev_uc_del(real_dev, dev->dev_addr);
36038c2ecf20Sopenharmony_ci
36048c2ecf20Sopenharmony_ci	return 0;
36058c2ecf20Sopenharmony_ci}
36068c2ecf20Sopenharmony_ci
36078c2ecf20Sopenharmony_cistatic void macsec_dev_change_rx_flags(struct net_device *dev, int change)
36088c2ecf20Sopenharmony_ci{
36098c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec_priv(dev)->real_dev;
36108c2ecf20Sopenharmony_ci
36118c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_UP))
36128c2ecf20Sopenharmony_ci		return;
36138c2ecf20Sopenharmony_ci
36148c2ecf20Sopenharmony_ci	if (change & IFF_ALLMULTI)
36158c2ecf20Sopenharmony_ci		dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1);
36168c2ecf20Sopenharmony_ci
36178c2ecf20Sopenharmony_ci	if (change & IFF_PROMISC)
36188c2ecf20Sopenharmony_ci		dev_set_promiscuity(real_dev,
36198c2ecf20Sopenharmony_ci				    dev->flags & IFF_PROMISC ? 1 : -1);
36208c2ecf20Sopenharmony_ci}
36218c2ecf20Sopenharmony_ci
36228c2ecf20Sopenharmony_cistatic void macsec_dev_set_rx_mode(struct net_device *dev)
36238c2ecf20Sopenharmony_ci{
36248c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec_priv(dev)->real_dev;
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_ci	dev_mc_sync(real_dev, dev);
36278c2ecf20Sopenharmony_ci	dev_uc_sync(real_dev, dev);
36288c2ecf20Sopenharmony_ci}
36298c2ecf20Sopenharmony_ci
36308c2ecf20Sopenharmony_cistatic int macsec_set_mac_address(struct net_device *dev, void *p)
36318c2ecf20Sopenharmony_ci{
36328c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
36338c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec->real_dev;
36348c2ecf20Sopenharmony_ci	struct sockaddr *addr = p;
36358c2ecf20Sopenharmony_ci	int err;
36368c2ecf20Sopenharmony_ci
36378c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
36388c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
36398c2ecf20Sopenharmony_ci
36408c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_UP))
36418c2ecf20Sopenharmony_ci		goto out;
36428c2ecf20Sopenharmony_ci
36438c2ecf20Sopenharmony_ci	err = dev_uc_add(real_dev, addr->sa_data);
36448c2ecf20Sopenharmony_ci	if (err < 0)
36458c2ecf20Sopenharmony_ci		return err;
36468c2ecf20Sopenharmony_ci
36478c2ecf20Sopenharmony_ci	dev_uc_del(real_dev, dev->dev_addr);
36488c2ecf20Sopenharmony_ci
36498c2ecf20Sopenharmony_ciout:
36508c2ecf20Sopenharmony_ci	ether_addr_copy(dev->dev_addr, addr->sa_data);
36518c2ecf20Sopenharmony_ci	macsec->secy.sci = dev_to_sci(dev, MACSEC_PORT_ES);
36528c2ecf20Sopenharmony_ci
36538c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
36548c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
36558c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
36568c2ecf20Sopenharmony_ci		struct macsec_context ctx;
36578c2ecf20Sopenharmony_ci
36588c2ecf20Sopenharmony_ci		ops = macsec_get_ops(macsec, &ctx);
36598c2ecf20Sopenharmony_ci		if (ops) {
36608c2ecf20Sopenharmony_ci			ctx.secy = &macsec->secy;
36618c2ecf20Sopenharmony_ci			macsec_offload(ops->mdo_upd_secy, &ctx);
36628c2ecf20Sopenharmony_ci		}
36638c2ecf20Sopenharmony_ci	}
36648c2ecf20Sopenharmony_ci
36658c2ecf20Sopenharmony_ci	return 0;
36668c2ecf20Sopenharmony_ci}
36678c2ecf20Sopenharmony_ci
36688c2ecf20Sopenharmony_cistatic int macsec_change_mtu(struct net_device *dev, int new_mtu)
36698c2ecf20Sopenharmony_ci{
36708c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
36718c2ecf20Sopenharmony_ci	unsigned int extra = macsec->secy.icv_len + macsec_extra_len(true);
36728c2ecf20Sopenharmony_ci
36738c2ecf20Sopenharmony_ci	if (macsec->real_dev->mtu - extra < new_mtu)
36748c2ecf20Sopenharmony_ci		return -ERANGE;
36758c2ecf20Sopenharmony_ci
36768c2ecf20Sopenharmony_ci	dev->mtu = new_mtu;
36778c2ecf20Sopenharmony_ci
36788c2ecf20Sopenharmony_ci	return 0;
36798c2ecf20Sopenharmony_ci}
36808c2ecf20Sopenharmony_ci
36818c2ecf20Sopenharmony_cistatic void macsec_get_stats64(struct net_device *dev,
36828c2ecf20Sopenharmony_ci			       struct rtnl_link_stats64 *s)
36838c2ecf20Sopenharmony_ci{
36848c2ecf20Sopenharmony_ci	if (!dev->tstats)
36858c2ecf20Sopenharmony_ci		return;
36868c2ecf20Sopenharmony_ci
36878c2ecf20Sopenharmony_ci	dev_fetch_sw_netstats(s, dev->tstats);
36888c2ecf20Sopenharmony_ci
36898c2ecf20Sopenharmony_ci	s->rx_dropped = DEV_STATS_READ(dev, rx_dropped);
36908c2ecf20Sopenharmony_ci	s->tx_dropped = DEV_STATS_READ(dev, tx_dropped);
36918c2ecf20Sopenharmony_ci	s->rx_errors = DEV_STATS_READ(dev, rx_errors);
36928c2ecf20Sopenharmony_ci}
36938c2ecf20Sopenharmony_ci
36948c2ecf20Sopenharmony_cistatic int macsec_get_iflink(const struct net_device *dev)
36958c2ecf20Sopenharmony_ci{
36968c2ecf20Sopenharmony_ci	return macsec_priv(dev)->real_dev->ifindex;
36978c2ecf20Sopenharmony_ci}
36988c2ecf20Sopenharmony_ci
36998c2ecf20Sopenharmony_cistatic const struct net_device_ops macsec_netdev_ops = {
37008c2ecf20Sopenharmony_ci	.ndo_init		= macsec_dev_init,
37018c2ecf20Sopenharmony_ci	.ndo_uninit		= macsec_dev_uninit,
37028c2ecf20Sopenharmony_ci	.ndo_open		= macsec_dev_open,
37038c2ecf20Sopenharmony_ci	.ndo_stop		= macsec_dev_stop,
37048c2ecf20Sopenharmony_ci	.ndo_fix_features	= macsec_fix_features,
37058c2ecf20Sopenharmony_ci	.ndo_change_mtu		= macsec_change_mtu,
37068c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= macsec_dev_set_rx_mode,
37078c2ecf20Sopenharmony_ci	.ndo_change_rx_flags	= macsec_dev_change_rx_flags,
37088c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= macsec_set_mac_address,
37098c2ecf20Sopenharmony_ci	.ndo_start_xmit		= macsec_start_xmit,
37108c2ecf20Sopenharmony_ci	.ndo_get_stats64	= macsec_get_stats64,
37118c2ecf20Sopenharmony_ci	.ndo_get_iflink		= macsec_get_iflink,
37128c2ecf20Sopenharmony_ci};
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_cistatic const struct device_type macsec_type = {
37158c2ecf20Sopenharmony_ci	.name = "macsec",
37168c2ecf20Sopenharmony_ci};
37178c2ecf20Sopenharmony_ci
37188c2ecf20Sopenharmony_cistatic const struct nla_policy macsec_rtnl_policy[IFLA_MACSEC_MAX + 1] = {
37198c2ecf20Sopenharmony_ci	[IFLA_MACSEC_SCI] = { .type = NLA_U64 },
37208c2ecf20Sopenharmony_ci	[IFLA_MACSEC_PORT] = { .type = NLA_U16 },
37218c2ecf20Sopenharmony_ci	[IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 },
37228c2ecf20Sopenharmony_ci	[IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 },
37238c2ecf20Sopenharmony_ci	[IFLA_MACSEC_WINDOW] = { .type = NLA_U32 },
37248c2ecf20Sopenharmony_ci	[IFLA_MACSEC_ENCODING_SA] = { .type = NLA_U8 },
37258c2ecf20Sopenharmony_ci	[IFLA_MACSEC_ENCRYPT] = { .type = NLA_U8 },
37268c2ecf20Sopenharmony_ci	[IFLA_MACSEC_PROTECT] = { .type = NLA_U8 },
37278c2ecf20Sopenharmony_ci	[IFLA_MACSEC_INC_SCI] = { .type = NLA_U8 },
37288c2ecf20Sopenharmony_ci	[IFLA_MACSEC_ES] = { .type = NLA_U8 },
37298c2ecf20Sopenharmony_ci	[IFLA_MACSEC_SCB] = { .type = NLA_U8 },
37308c2ecf20Sopenharmony_ci	[IFLA_MACSEC_REPLAY_PROTECT] = { .type = NLA_U8 },
37318c2ecf20Sopenharmony_ci	[IFLA_MACSEC_VALIDATION] = { .type = NLA_U8 },
37328c2ecf20Sopenharmony_ci	[IFLA_MACSEC_OFFLOAD] = { .type = NLA_U8 },
37338c2ecf20Sopenharmony_ci};
37348c2ecf20Sopenharmony_ci
37358c2ecf20Sopenharmony_cistatic void macsec_free_netdev(struct net_device *dev)
37368c2ecf20Sopenharmony_ci{
37378c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
37388c2ecf20Sopenharmony_ci
37398c2ecf20Sopenharmony_ci	free_percpu(macsec->stats);
37408c2ecf20Sopenharmony_ci	free_percpu(macsec->secy.tx_sc.stats);
37418c2ecf20Sopenharmony_ci
37428c2ecf20Sopenharmony_ci}
37438c2ecf20Sopenharmony_ci
37448c2ecf20Sopenharmony_cistatic void macsec_setup(struct net_device *dev)
37458c2ecf20Sopenharmony_ci{
37468c2ecf20Sopenharmony_ci	ether_setup(dev);
37478c2ecf20Sopenharmony_ci	dev->min_mtu = 0;
37488c2ecf20Sopenharmony_ci	dev->max_mtu = ETH_MAX_MTU;
37498c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_NO_QUEUE;
37508c2ecf20Sopenharmony_ci	dev->netdev_ops = &macsec_netdev_ops;
37518c2ecf20Sopenharmony_ci	dev->needs_free_netdev = true;
37528c2ecf20Sopenharmony_ci	dev->priv_destructor = macsec_free_netdev;
37538c2ecf20Sopenharmony_ci	SET_NETDEV_DEVTYPE(dev, &macsec_type);
37548c2ecf20Sopenharmony_ci
37558c2ecf20Sopenharmony_ci	eth_zero_addr(dev->broadcast);
37568c2ecf20Sopenharmony_ci}
37578c2ecf20Sopenharmony_ci
37588c2ecf20Sopenharmony_cistatic int macsec_changelink_common(struct net_device *dev,
37598c2ecf20Sopenharmony_ci				    struct nlattr *data[])
37608c2ecf20Sopenharmony_ci{
37618c2ecf20Sopenharmony_ci	struct macsec_secy *secy;
37628c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc;
37638c2ecf20Sopenharmony_ci
37648c2ecf20Sopenharmony_ci	secy = &macsec_priv(dev)->secy;
37658c2ecf20Sopenharmony_ci	tx_sc = &secy->tx_sc;
37668c2ecf20Sopenharmony_ci
37678c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_ENCODING_SA]) {
37688c2ecf20Sopenharmony_ci		struct macsec_tx_sa *tx_sa;
37698c2ecf20Sopenharmony_ci
37708c2ecf20Sopenharmony_ci		tx_sc->encoding_sa = nla_get_u8(data[IFLA_MACSEC_ENCODING_SA]);
37718c2ecf20Sopenharmony_ci		tx_sa = rtnl_dereference(tx_sc->sa[tx_sc->encoding_sa]);
37728c2ecf20Sopenharmony_ci
37738c2ecf20Sopenharmony_ci		secy->operational = tx_sa && tx_sa->active;
37748c2ecf20Sopenharmony_ci	}
37758c2ecf20Sopenharmony_ci
37768c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_ENCRYPT])
37778c2ecf20Sopenharmony_ci		tx_sc->encrypt = !!nla_get_u8(data[IFLA_MACSEC_ENCRYPT]);
37788c2ecf20Sopenharmony_ci
37798c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_PROTECT])
37808c2ecf20Sopenharmony_ci		secy->protect_frames = !!nla_get_u8(data[IFLA_MACSEC_PROTECT]);
37818c2ecf20Sopenharmony_ci
37828c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_INC_SCI])
37838c2ecf20Sopenharmony_ci		tx_sc->send_sci = !!nla_get_u8(data[IFLA_MACSEC_INC_SCI]);
37848c2ecf20Sopenharmony_ci
37858c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_ES])
37868c2ecf20Sopenharmony_ci		tx_sc->end_station = !!nla_get_u8(data[IFLA_MACSEC_ES]);
37878c2ecf20Sopenharmony_ci
37888c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_SCB])
37898c2ecf20Sopenharmony_ci		tx_sc->scb = !!nla_get_u8(data[IFLA_MACSEC_SCB]);
37908c2ecf20Sopenharmony_ci
37918c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_REPLAY_PROTECT])
37928c2ecf20Sopenharmony_ci		secy->replay_protect = !!nla_get_u8(data[IFLA_MACSEC_REPLAY_PROTECT]);
37938c2ecf20Sopenharmony_ci
37948c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_VALIDATION])
37958c2ecf20Sopenharmony_ci		secy->validate_frames = nla_get_u8(data[IFLA_MACSEC_VALIDATION]);
37968c2ecf20Sopenharmony_ci
37978c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_CIPHER_SUITE]) {
37988c2ecf20Sopenharmony_ci		switch (nla_get_u64(data[IFLA_MACSEC_CIPHER_SUITE])) {
37998c2ecf20Sopenharmony_ci		case MACSEC_CIPHER_ID_GCM_AES_128:
38008c2ecf20Sopenharmony_ci		case MACSEC_DEFAULT_CIPHER_ID:
38018c2ecf20Sopenharmony_ci			secy->key_len = MACSEC_GCM_AES_128_SAK_LEN;
38028c2ecf20Sopenharmony_ci			secy->xpn = false;
38038c2ecf20Sopenharmony_ci			break;
38048c2ecf20Sopenharmony_ci		case MACSEC_CIPHER_ID_GCM_AES_256:
38058c2ecf20Sopenharmony_ci			secy->key_len = MACSEC_GCM_AES_256_SAK_LEN;
38068c2ecf20Sopenharmony_ci			secy->xpn = false;
38078c2ecf20Sopenharmony_ci			break;
38088c2ecf20Sopenharmony_ci		case MACSEC_CIPHER_ID_GCM_AES_XPN_128:
38098c2ecf20Sopenharmony_ci			secy->key_len = MACSEC_GCM_AES_128_SAK_LEN;
38108c2ecf20Sopenharmony_ci			secy->xpn = true;
38118c2ecf20Sopenharmony_ci			break;
38128c2ecf20Sopenharmony_ci		case MACSEC_CIPHER_ID_GCM_AES_XPN_256:
38138c2ecf20Sopenharmony_ci			secy->key_len = MACSEC_GCM_AES_256_SAK_LEN;
38148c2ecf20Sopenharmony_ci			secy->xpn = true;
38158c2ecf20Sopenharmony_ci			break;
38168c2ecf20Sopenharmony_ci		default:
38178c2ecf20Sopenharmony_ci			return -EINVAL;
38188c2ecf20Sopenharmony_ci		}
38198c2ecf20Sopenharmony_ci	}
38208c2ecf20Sopenharmony_ci
38218c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_WINDOW]) {
38228c2ecf20Sopenharmony_ci		secy->replay_window = nla_get_u32(data[IFLA_MACSEC_WINDOW]);
38238c2ecf20Sopenharmony_ci
38248c2ecf20Sopenharmony_ci		/* IEEE 802.1AEbw-2013 10.7.8 - maximum replay window
38258c2ecf20Sopenharmony_ci		 * for XPN cipher suites */
38268c2ecf20Sopenharmony_ci		if (secy->xpn &&
38278c2ecf20Sopenharmony_ci		    secy->replay_window > MACSEC_XPN_MAX_REPLAY_WINDOW)
38288c2ecf20Sopenharmony_ci			return -EINVAL;
38298c2ecf20Sopenharmony_ci	}
38308c2ecf20Sopenharmony_ci
38318c2ecf20Sopenharmony_ci	return 0;
38328c2ecf20Sopenharmony_ci}
38338c2ecf20Sopenharmony_ci
38348c2ecf20Sopenharmony_cistatic int macsec_changelink(struct net_device *dev, struct nlattr *tb[],
38358c2ecf20Sopenharmony_ci			     struct nlattr *data[],
38368c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
38378c2ecf20Sopenharmony_ci{
38388c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
38398c2ecf20Sopenharmony_ci	struct macsec_tx_sc tx_sc;
38408c2ecf20Sopenharmony_ci	struct macsec_secy secy;
38418c2ecf20Sopenharmony_ci	int ret;
38428c2ecf20Sopenharmony_ci
38438c2ecf20Sopenharmony_ci	if (!data)
38448c2ecf20Sopenharmony_ci		return 0;
38458c2ecf20Sopenharmony_ci
38468c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_CIPHER_SUITE] ||
38478c2ecf20Sopenharmony_ci	    data[IFLA_MACSEC_ICV_LEN] ||
38488c2ecf20Sopenharmony_ci	    data[IFLA_MACSEC_SCI] ||
38498c2ecf20Sopenharmony_ci	    data[IFLA_MACSEC_PORT])
38508c2ecf20Sopenharmony_ci		return -EINVAL;
38518c2ecf20Sopenharmony_ci
38528c2ecf20Sopenharmony_ci	/* Keep a copy of unmodified secy and tx_sc, in case the offload
38538c2ecf20Sopenharmony_ci	 * propagation fails, to revert macsec_changelink_common.
38548c2ecf20Sopenharmony_ci	 */
38558c2ecf20Sopenharmony_ci	memcpy(&secy, &macsec->secy, sizeof(secy));
38568c2ecf20Sopenharmony_ci	memcpy(&tx_sc, &macsec->secy.tx_sc, sizeof(tx_sc));
38578c2ecf20Sopenharmony_ci
38588c2ecf20Sopenharmony_ci	ret = macsec_changelink_common(dev, data);
38598c2ecf20Sopenharmony_ci	if (ret)
38608c2ecf20Sopenharmony_ci		goto cleanup;
38618c2ecf20Sopenharmony_ci
38628c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
38638c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
38648c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
38658c2ecf20Sopenharmony_ci		struct macsec_context ctx;
38668c2ecf20Sopenharmony_ci
38678c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
38688c2ecf20Sopenharmony_ci		if (!ops) {
38698c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
38708c2ecf20Sopenharmony_ci			goto cleanup;
38718c2ecf20Sopenharmony_ci		}
38728c2ecf20Sopenharmony_ci
38738c2ecf20Sopenharmony_ci		ctx.secy = &macsec->secy;
38748c2ecf20Sopenharmony_ci		ret = macsec_offload(ops->mdo_upd_secy, &ctx);
38758c2ecf20Sopenharmony_ci		if (ret)
38768c2ecf20Sopenharmony_ci			goto cleanup;
38778c2ecf20Sopenharmony_ci	}
38788c2ecf20Sopenharmony_ci
38798c2ecf20Sopenharmony_ci	return 0;
38808c2ecf20Sopenharmony_ci
38818c2ecf20Sopenharmony_cicleanup:
38828c2ecf20Sopenharmony_ci	memcpy(&macsec->secy.tx_sc, &tx_sc, sizeof(tx_sc));
38838c2ecf20Sopenharmony_ci	memcpy(&macsec->secy, &secy, sizeof(secy));
38848c2ecf20Sopenharmony_ci
38858c2ecf20Sopenharmony_ci	return ret;
38868c2ecf20Sopenharmony_ci}
38878c2ecf20Sopenharmony_ci
38888c2ecf20Sopenharmony_cistatic void macsec_del_dev(struct macsec_dev *macsec)
38898c2ecf20Sopenharmony_ci{
38908c2ecf20Sopenharmony_ci	int i;
38918c2ecf20Sopenharmony_ci
38928c2ecf20Sopenharmony_ci	while (macsec->secy.rx_sc) {
38938c2ecf20Sopenharmony_ci		struct macsec_rx_sc *rx_sc = rtnl_dereference(macsec->secy.rx_sc);
38948c2ecf20Sopenharmony_ci
38958c2ecf20Sopenharmony_ci		rcu_assign_pointer(macsec->secy.rx_sc, rx_sc->next);
38968c2ecf20Sopenharmony_ci		free_rx_sc(rx_sc);
38978c2ecf20Sopenharmony_ci	}
38988c2ecf20Sopenharmony_ci
38998c2ecf20Sopenharmony_ci	for (i = 0; i < MACSEC_NUM_AN; i++) {
39008c2ecf20Sopenharmony_ci		struct macsec_tx_sa *sa = rtnl_dereference(macsec->secy.tx_sc.sa[i]);
39018c2ecf20Sopenharmony_ci
39028c2ecf20Sopenharmony_ci		if (sa) {
39038c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(macsec->secy.tx_sc.sa[i], NULL);
39048c2ecf20Sopenharmony_ci			clear_tx_sa(sa);
39058c2ecf20Sopenharmony_ci		}
39068c2ecf20Sopenharmony_ci	}
39078c2ecf20Sopenharmony_ci}
39088c2ecf20Sopenharmony_ci
39098c2ecf20Sopenharmony_cistatic void macsec_common_dellink(struct net_device *dev, struct list_head *head)
39108c2ecf20Sopenharmony_ci{
39118c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
39128c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec->real_dev;
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
39158c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
39168c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
39178c2ecf20Sopenharmony_ci		struct macsec_context ctx;
39188c2ecf20Sopenharmony_ci
39198c2ecf20Sopenharmony_ci		ops = macsec_get_ops(netdev_priv(dev), &ctx);
39208c2ecf20Sopenharmony_ci		if (ops) {
39218c2ecf20Sopenharmony_ci			ctx.secy = &macsec->secy;
39228c2ecf20Sopenharmony_ci			macsec_offload(ops->mdo_del_secy, &ctx);
39238c2ecf20Sopenharmony_ci		}
39248c2ecf20Sopenharmony_ci	}
39258c2ecf20Sopenharmony_ci
39268c2ecf20Sopenharmony_ci	unregister_netdevice_queue(dev, head);
39278c2ecf20Sopenharmony_ci	list_del_rcu(&macsec->secys);
39288c2ecf20Sopenharmony_ci	macsec_del_dev(macsec);
39298c2ecf20Sopenharmony_ci	netdev_upper_dev_unlink(real_dev, dev);
39308c2ecf20Sopenharmony_ci
39318c2ecf20Sopenharmony_ci	macsec_generation++;
39328c2ecf20Sopenharmony_ci}
39338c2ecf20Sopenharmony_ci
39348c2ecf20Sopenharmony_cistatic void macsec_dellink(struct net_device *dev, struct list_head *head)
39358c2ecf20Sopenharmony_ci{
39368c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
39378c2ecf20Sopenharmony_ci	struct net_device *real_dev = macsec->real_dev;
39388c2ecf20Sopenharmony_ci	struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
39398c2ecf20Sopenharmony_ci
39408c2ecf20Sopenharmony_ci	macsec_common_dellink(dev, head);
39418c2ecf20Sopenharmony_ci
39428c2ecf20Sopenharmony_ci	if (list_empty(&rxd->secys)) {
39438c2ecf20Sopenharmony_ci		netdev_rx_handler_unregister(real_dev);
39448c2ecf20Sopenharmony_ci		kfree(rxd);
39458c2ecf20Sopenharmony_ci	}
39468c2ecf20Sopenharmony_ci}
39478c2ecf20Sopenharmony_ci
39488c2ecf20Sopenharmony_cistatic int register_macsec_dev(struct net_device *real_dev,
39498c2ecf20Sopenharmony_ci			       struct net_device *dev)
39508c2ecf20Sopenharmony_ci{
39518c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
39528c2ecf20Sopenharmony_ci	struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
39538c2ecf20Sopenharmony_ci
39548c2ecf20Sopenharmony_ci	if (!rxd) {
39558c2ecf20Sopenharmony_ci		int err;
39568c2ecf20Sopenharmony_ci
39578c2ecf20Sopenharmony_ci		rxd = kmalloc(sizeof(*rxd), GFP_KERNEL);
39588c2ecf20Sopenharmony_ci		if (!rxd)
39598c2ecf20Sopenharmony_ci			return -ENOMEM;
39608c2ecf20Sopenharmony_ci
39618c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&rxd->secys);
39628c2ecf20Sopenharmony_ci
39638c2ecf20Sopenharmony_ci		err = netdev_rx_handler_register(real_dev, macsec_handle_frame,
39648c2ecf20Sopenharmony_ci						 rxd);
39658c2ecf20Sopenharmony_ci		if (err < 0) {
39668c2ecf20Sopenharmony_ci			kfree(rxd);
39678c2ecf20Sopenharmony_ci			return err;
39688c2ecf20Sopenharmony_ci		}
39698c2ecf20Sopenharmony_ci	}
39708c2ecf20Sopenharmony_ci
39718c2ecf20Sopenharmony_ci	list_add_tail_rcu(&macsec->secys, &rxd->secys);
39728c2ecf20Sopenharmony_ci	return 0;
39738c2ecf20Sopenharmony_ci}
39748c2ecf20Sopenharmony_ci
39758c2ecf20Sopenharmony_cistatic bool sci_exists(struct net_device *dev, sci_t sci)
39768c2ecf20Sopenharmony_ci{
39778c2ecf20Sopenharmony_ci	struct macsec_rxh_data *rxd = macsec_data_rtnl(dev);
39788c2ecf20Sopenharmony_ci	struct macsec_dev *macsec;
39798c2ecf20Sopenharmony_ci
39808c2ecf20Sopenharmony_ci	list_for_each_entry(macsec, &rxd->secys, secys) {
39818c2ecf20Sopenharmony_ci		if (macsec->secy.sci == sci)
39828c2ecf20Sopenharmony_ci			return true;
39838c2ecf20Sopenharmony_ci	}
39848c2ecf20Sopenharmony_ci
39858c2ecf20Sopenharmony_ci	return false;
39868c2ecf20Sopenharmony_ci}
39878c2ecf20Sopenharmony_ci
39888c2ecf20Sopenharmony_cistatic int macsec_add_dev(struct net_device *dev, sci_t sci, u8 icv_len)
39898c2ecf20Sopenharmony_ci{
39908c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
39918c2ecf20Sopenharmony_ci	struct macsec_secy *secy = &macsec->secy;
39928c2ecf20Sopenharmony_ci
39938c2ecf20Sopenharmony_ci	macsec->stats = netdev_alloc_pcpu_stats(struct pcpu_secy_stats);
39948c2ecf20Sopenharmony_ci	if (!macsec->stats)
39958c2ecf20Sopenharmony_ci		return -ENOMEM;
39968c2ecf20Sopenharmony_ci
39978c2ecf20Sopenharmony_ci	secy->tx_sc.stats = netdev_alloc_pcpu_stats(struct pcpu_tx_sc_stats);
39988c2ecf20Sopenharmony_ci	if (!secy->tx_sc.stats) {
39998c2ecf20Sopenharmony_ci		free_percpu(macsec->stats);
40008c2ecf20Sopenharmony_ci		return -ENOMEM;
40018c2ecf20Sopenharmony_ci	}
40028c2ecf20Sopenharmony_ci
40038c2ecf20Sopenharmony_ci	if (sci == MACSEC_UNDEF_SCI)
40048c2ecf20Sopenharmony_ci		sci = dev_to_sci(dev, MACSEC_PORT_ES);
40058c2ecf20Sopenharmony_ci
40068c2ecf20Sopenharmony_ci	secy->netdev = dev;
40078c2ecf20Sopenharmony_ci	secy->operational = true;
40088c2ecf20Sopenharmony_ci	secy->key_len = DEFAULT_SAK_LEN;
40098c2ecf20Sopenharmony_ci	secy->icv_len = icv_len;
40108c2ecf20Sopenharmony_ci	secy->validate_frames = MACSEC_VALIDATE_DEFAULT;
40118c2ecf20Sopenharmony_ci	secy->protect_frames = true;
40128c2ecf20Sopenharmony_ci	secy->replay_protect = false;
40138c2ecf20Sopenharmony_ci	secy->xpn = DEFAULT_XPN;
40148c2ecf20Sopenharmony_ci
40158c2ecf20Sopenharmony_ci	secy->sci = sci;
40168c2ecf20Sopenharmony_ci	secy->tx_sc.active = true;
40178c2ecf20Sopenharmony_ci	secy->tx_sc.encoding_sa = DEFAULT_ENCODING_SA;
40188c2ecf20Sopenharmony_ci	secy->tx_sc.encrypt = DEFAULT_ENCRYPT;
40198c2ecf20Sopenharmony_ci	secy->tx_sc.send_sci = DEFAULT_SEND_SCI;
40208c2ecf20Sopenharmony_ci	secy->tx_sc.end_station = false;
40218c2ecf20Sopenharmony_ci	secy->tx_sc.scb = false;
40228c2ecf20Sopenharmony_ci
40238c2ecf20Sopenharmony_ci	return 0;
40248c2ecf20Sopenharmony_ci}
40258c2ecf20Sopenharmony_ci
40268c2ecf20Sopenharmony_cistatic struct lock_class_key macsec_netdev_addr_lock_key;
40278c2ecf20Sopenharmony_ci
40288c2ecf20Sopenharmony_cistatic int macsec_newlink(struct net *net, struct net_device *dev,
40298c2ecf20Sopenharmony_ci			  struct nlattr *tb[], struct nlattr *data[],
40308c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
40318c2ecf20Sopenharmony_ci{
40328c2ecf20Sopenharmony_ci	struct macsec_dev *macsec = macsec_priv(dev);
40338c2ecf20Sopenharmony_ci	rx_handler_func_t *rx_handler;
40348c2ecf20Sopenharmony_ci	u8 icv_len = DEFAULT_ICV_LEN;
40358c2ecf20Sopenharmony_ci	struct net_device *real_dev;
40368c2ecf20Sopenharmony_ci	int err, mtu;
40378c2ecf20Sopenharmony_ci	sci_t sci;
40388c2ecf20Sopenharmony_ci
40398c2ecf20Sopenharmony_ci	if (!tb[IFLA_LINK])
40408c2ecf20Sopenharmony_ci		return -EINVAL;
40418c2ecf20Sopenharmony_ci	real_dev = __dev_get_by_index(net, nla_get_u32(tb[IFLA_LINK]));
40428c2ecf20Sopenharmony_ci	if (!real_dev)
40438c2ecf20Sopenharmony_ci		return -ENODEV;
40448c2ecf20Sopenharmony_ci	if (real_dev->type != ARPHRD_ETHER)
40458c2ecf20Sopenharmony_ci		return -EINVAL;
40468c2ecf20Sopenharmony_ci
40478c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_MACSEC;
40488c2ecf20Sopenharmony_ci
40498c2ecf20Sopenharmony_ci	macsec->real_dev = real_dev;
40508c2ecf20Sopenharmony_ci
40518c2ecf20Sopenharmony_ci	if (data && data[IFLA_MACSEC_OFFLOAD])
40528c2ecf20Sopenharmony_ci		macsec->offload = nla_get_offload(data[IFLA_MACSEC_OFFLOAD]);
40538c2ecf20Sopenharmony_ci	else
40548c2ecf20Sopenharmony_ci		/* MACsec offloading is off by default */
40558c2ecf20Sopenharmony_ci		macsec->offload = MACSEC_OFFLOAD_OFF;
40568c2ecf20Sopenharmony_ci
40578c2ecf20Sopenharmony_ci	/* Check if the offloading mode is supported by the underlying layers */
40588c2ecf20Sopenharmony_ci	if (macsec->offload != MACSEC_OFFLOAD_OFF &&
40598c2ecf20Sopenharmony_ci	    !macsec_check_offload(macsec->offload, macsec))
40608c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
40618c2ecf20Sopenharmony_ci
40628c2ecf20Sopenharmony_ci	/* send_sci must be set to true when transmit sci explicitly is set */
40638c2ecf20Sopenharmony_ci	if ((data && data[IFLA_MACSEC_SCI]) &&
40648c2ecf20Sopenharmony_ci	    (data && data[IFLA_MACSEC_INC_SCI])) {
40658c2ecf20Sopenharmony_ci		u8 send_sci = !!nla_get_u8(data[IFLA_MACSEC_INC_SCI]);
40668c2ecf20Sopenharmony_ci
40678c2ecf20Sopenharmony_ci		if (!send_sci)
40688c2ecf20Sopenharmony_ci			return -EINVAL;
40698c2ecf20Sopenharmony_ci	}
40708c2ecf20Sopenharmony_ci
40718c2ecf20Sopenharmony_ci	if (data && data[IFLA_MACSEC_ICV_LEN])
40728c2ecf20Sopenharmony_ci		icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]);
40738c2ecf20Sopenharmony_ci	mtu = real_dev->mtu - icv_len - macsec_extra_len(true);
40748c2ecf20Sopenharmony_ci	if (mtu < 0)
40758c2ecf20Sopenharmony_ci		dev->mtu = 0;
40768c2ecf20Sopenharmony_ci	else
40778c2ecf20Sopenharmony_ci		dev->mtu = mtu;
40788c2ecf20Sopenharmony_ci
40798c2ecf20Sopenharmony_ci	rx_handler = rtnl_dereference(real_dev->rx_handler);
40808c2ecf20Sopenharmony_ci	if (rx_handler && rx_handler != macsec_handle_frame)
40818c2ecf20Sopenharmony_ci		return -EBUSY;
40828c2ecf20Sopenharmony_ci
40838c2ecf20Sopenharmony_ci	err = register_netdevice(dev);
40848c2ecf20Sopenharmony_ci	if (err < 0)
40858c2ecf20Sopenharmony_ci		return err;
40868c2ecf20Sopenharmony_ci
40878c2ecf20Sopenharmony_ci	netdev_lockdep_set_classes(dev);
40888c2ecf20Sopenharmony_ci	lockdep_set_class(&dev->addr_list_lock,
40898c2ecf20Sopenharmony_ci			  &macsec_netdev_addr_lock_key);
40908c2ecf20Sopenharmony_ci
40918c2ecf20Sopenharmony_ci	err = netdev_upper_dev_link(real_dev, dev, extack);
40928c2ecf20Sopenharmony_ci	if (err < 0)
40938c2ecf20Sopenharmony_ci		goto unregister;
40948c2ecf20Sopenharmony_ci
40958c2ecf20Sopenharmony_ci	/* need to be already registered so that ->init has run and
40968c2ecf20Sopenharmony_ci	 * the MAC addr is set
40978c2ecf20Sopenharmony_ci	 */
40988c2ecf20Sopenharmony_ci	if (data && data[IFLA_MACSEC_SCI])
40998c2ecf20Sopenharmony_ci		sci = nla_get_sci(data[IFLA_MACSEC_SCI]);
41008c2ecf20Sopenharmony_ci	else if (data && data[IFLA_MACSEC_PORT])
41018c2ecf20Sopenharmony_ci		sci = dev_to_sci(dev, nla_get_be16(data[IFLA_MACSEC_PORT]));
41028c2ecf20Sopenharmony_ci	else
41038c2ecf20Sopenharmony_ci		sci = dev_to_sci(dev, MACSEC_PORT_ES);
41048c2ecf20Sopenharmony_ci
41058c2ecf20Sopenharmony_ci	if (rx_handler && sci_exists(real_dev, sci)) {
41068c2ecf20Sopenharmony_ci		err = -EBUSY;
41078c2ecf20Sopenharmony_ci		goto unlink;
41088c2ecf20Sopenharmony_ci	}
41098c2ecf20Sopenharmony_ci
41108c2ecf20Sopenharmony_ci	err = macsec_add_dev(dev, sci, icv_len);
41118c2ecf20Sopenharmony_ci	if (err)
41128c2ecf20Sopenharmony_ci		goto unlink;
41138c2ecf20Sopenharmony_ci
41148c2ecf20Sopenharmony_ci	if (data) {
41158c2ecf20Sopenharmony_ci		err = macsec_changelink_common(dev, data);
41168c2ecf20Sopenharmony_ci		if (err)
41178c2ecf20Sopenharmony_ci			goto del_dev;
41188c2ecf20Sopenharmony_ci	}
41198c2ecf20Sopenharmony_ci
41208c2ecf20Sopenharmony_ci	/* If h/w offloading is available, propagate to the device */
41218c2ecf20Sopenharmony_ci	if (macsec_is_offloaded(macsec)) {
41228c2ecf20Sopenharmony_ci		const struct macsec_ops *ops;
41238c2ecf20Sopenharmony_ci		struct macsec_context ctx;
41248c2ecf20Sopenharmony_ci
41258c2ecf20Sopenharmony_ci		ops = macsec_get_ops(macsec, &ctx);
41268c2ecf20Sopenharmony_ci		if (ops) {
41278c2ecf20Sopenharmony_ci			ctx.secy = &macsec->secy;
41288c2ecf20Sopenharmony_ci			err = macsec_offload(ops->mdo_add_secy, &ctx);
41298c2ecf20Sopenharmony_ci			if (err)
41308c2ecf20Sopenharmony_ci				goto del_dev;
41318c2ecf20Sopenharmony_ci		}
41328c2ecf20Sopenharmony_ci	}
41338c2ecf20Sopenharmony_ci
41348c2ecf20Sopenharmony_ci	err = register_macsec_dev(real_dev, dev);
41358c2ecf20Sopenharmony_ci	if (err < 0)
41368c2ecf20Sopenharmony_ci		goto del_dev;
41378c2ecf20Sopenharmony_ci
41388c2ecf20Sopenharmony_ci	netif_stacked_transfer_operstate(real_dev, dev);
41398c2ecf20Sopenharmony_ci	linkwatch_fire_event(dev);
41408c2ecf20Sopenharmony_ci
41418c2ecf20Sopenharmony_ci	macsec_generation++;
41428c2ecf20Sopenharmony_ci
41438c2ecf20Sopenharmony_ci	return 0;
41448c2ecf20Sopenharmony_ci
41458c2ecf20Sopenharmony_cidel_dev:
41468c2ecf20Sopenharmony_ci	macsec_del_dev(macsec);
41478c2ecf20Sopenharmony_ciunlink:
41488c2ecf20Sopenharmony_ci	netdev_upper_dev_unlink(real_dev, dev);
41498c2ecf20Sopenharmony_ciunregister:
41508c2ecf20Sopenharmony_ci	unregister_netdevice(dev);
41518c2ecf20Sopenharmony_ci	return err;
41528c2ecf20Sopenharmony_ci}
41538c2ecf20Sopenharmony_ci
41548c2ecf20Sopenharmony_cistatic int macsec_validate_attr(struct nlattr *tb[], struct nlattr *data[],
41558c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
41568c2ecf20Sopenharmony_ci{
41578c2ecf20Sopenharmony_ci	u64 csid = MACSEC_DEFAULT_CIPHER_ID;
41588c2ecf20Sopenharmony_ci	u8 icv_len = DEFAULT_ICV_LEN;
41598c2ecf20Sopenharmony_ci	int flag;
41608c2ecf20Sopenharmony_ci	bool es, scb, sci;
41618c2ecf20Sopenharmony_ci
41628c2ecf20Sopenharmony_ci	if (!data)
41638c2ecf20Sopenharmony_ci		return 0;
41648c2ecf20Sopenharmony_ci
41658c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_CIPHER_SUITE])
41668c2ecf20Sopenharmony_ci		csid = nla_get_u64(data[IFLA_MACSEC_CIPHER_SUITE]);
41678c2ecf20Sopenharmony_ci
41688c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_ICV_LEN]) {
41698c2ecf20Sopenharmony_ci		icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]);
41708c2ecf20Sopenharmony_ci		if (icv_len != DEFAULT_ICV_LEN) {
41718c2ecf20Sopenharmony_ci			char dummy_key[DEFAULT_SAK_LEN] = { 0 };
41728c2ecf20Sopenharmony_ci			struct crypto_aead *dummy_tfm;
41738c2ecf20Sopenharmony_ci
41748c2ecf20Sopenharmony_ci			dummy_tfm = macsec_alloc_tfm(dummy_key,
41758c2ecf20Sopenharmony_ci						     DEFAULT_SAK_LEN,
41768c2ecf20Sopenharmony_ci						     icv_len);
41778c2ecf20Sopenharmony_ci			if (IS_ERR(dummy_tfm))
41788c2ecf20Sopenharmony_ci				return PTR_ERR(dummy_tfm);
41798c2ecf20Sopenharmony_ci			crypto_free_aead(dummy_tfm);
41808c2ecf20Sopenharmony_ci		}
41818c2ecf20Sopenharmony_ci	}
41828c2ecf20Sopenharmony_ci
41838c2ecf20Sopenharmony_ci	switch (csid) {
41848c2ecf20Sopenharmony_ci	case MACSEC_CIPHER_ID_GCM_AES_128:
41858c2ecf20Sopenharmony_ci	case MACSEC_CIPHER_ID_GCM_AES_256:
41868c2ecf20Sopenharmony_ci	case MACSEC_CIPHER_ID_GCM_AES_XPN_128:
41878c2ecf20Sopenharmony_ci	case MACSEC_CIPHER_ID_GCM_AES_XPN_256:
41888c2ecf20Sopenharmony_ci	case MACSEC_DEFAULT_CIPHER_ID:
41898c2ecf20Sopenharmony_ci		if (icv_len < MACSEC_MIN_ICV_LEN ||
41908c2ecf20Sopenharmony_ci		    icv_len > MACSEC_STD_ICV_LEN)
41918c2ecf20Sopenharmony_ci			return -EINVAL;
41928c2ecf20Sopenharmony_ci		break;
41938c2ecf20Sopenharmony_ci	default:
41948c2ecf20Sopenharmony_ci		return -EINVAL;
41958c2ecf20Sopenharmony_ci	}
41968c2ecf20Sopenharmony_ci
41978c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_ENCODING_SA]) {
41988c2ecf20Sopenharmony_ci		if (nla_get_u8(data[IFLA_MACSEC_ENCODING_SA]) >= MACSEC_NUM_AN)
41998c2ecf20Sopenharmony_ci			return -EINVAL;
42008c2ecf20Sopenharmony_ci	}
42018c2ecf20Sopenharmony_ci
42028c2ecf20Sopenharmony_ci	for (flag = IFLA_MACSEC_ENCODING_SA + 1;
42038c2ecf20Sopenharmony_ci	     flag < IFLA_MACSEC_VALIDATION;
42048c2ecf20Sopenharmony_ci	     flag++) {
42058c2ecf20Sopenharmony_ci		if (data[flag]) {
42068c2ecf20Sopenharmony_ci			if (nla_get_u8(data[flag]) > 1)
42078c2ecf20Sopenharmony_ci				return -EINVAL;
42088c2ecf20Sopenharmony_ci		}
42098c2ecf20Sopenharmony_ci	}
42108c2ecf20Sopenharmony_ci
42118c2ecf20Sopenharmony_ci	es  = data[IFLA_MACSEC_ES] ? nla_get_u8(data[IFLA_MACSEC_ES]) : false;
42128c2ecf20Sopenharmony_ci	sci = data[IFLA_MACSEC_INC_SCI] ? nla_get_u8(data[IFLA_MACSEC_INC_SCI]) : false;
42138c2ecf20Sopenharmony_ci	scb = data[IFLA_MACSEC_SCB] ? nla_get_u8(data[IFLA_MACSEC_SCB]) : false;
42148c2ecf20Sopenharmony_ci
42158c2ecf20Sopenharmony_ci	if ((sci && (scb || es)) || (scb && es))
42168c2ecf20Sopenharmony_ci		return -EINVAL;
42178c2ecf20Sopenharmony_ci
42188c2ecf20Sopenharmony_ci	if (data[IFLA_MACSEC_VALIDATION] &&
42198c2ecf20Sopenharmony_ci	    nla_get_u8(data[IFLA_MACSEC_VALIDATION]) > MACSEC_VALIDATE_MAX)
42208c2ecf20Sopenharmony_ci		return -EINVAL;
42218c2ecf20Sopenharmony_ci
42228c2ecf20Sopenharmony_ci	if ((data[IFLA_MACSEC_REPLAY_PROTECT] &&
42238c2ecf20Sopenharmony_ci	     nla_get_u8(data[IFLA_MACSEC_REPLAY_PROTECT])) &&
42248c2ecf20Sopenharmony_ci	    !data[IFLA_MACSEC_WINDOW])
42258c2ecf20Sopenharmony_ci		return -EINVAL;
42268c2ecf20Sopenharmony_ci
42278c2ecf20Sopenharmony_ci	return 0;
42288c2ecf20Sopenharmony_ci}
42298c2ecf20Sopenharmony_ci
42308c2ecf20Sopenharmony_cistatic struct net *macsec_get_link_net(const struct net_device *dev)
42318c2ecf20Sopenharmony_ci{
42328c2ecf20Sopenharmony_ci	return dev_net(macsec_priv(dev)->real_dev);
42338c2ecf20Sopenharmony_ci}
42348c2ecf20Sopenharmony_ci
42358c2ecf20Sopenharmony_cistatic size_t macsec_get_size(const struct net_device *dev)
42368c2ecf20Sopenharmony_ci{
42378c2ecf20Sopenharmony_ci	return  nla_total_size_64bit(8) + /* IFLA_MACSEC_SCI */
42388c2ecf20Sopenharmony_ci		nla_total_size(1) + /* IFLA_MACSEC_ICV_LEN */
42398c2ecf20Sopenharmony_ci		nla_total_size_64bit(8) + /* IFLA_MACSEC_CIPHER_SUITE */
42408c2ecf20Sopenharmony_ci		nla_total_size(4) + /* IFLA_MACSEC_WINDOW */
42418c2ecf20Sopenharmony_ci		nla_total_size(1) + /* IFLA_MACSEC_ENCODING_SA */
42428c2ecf20Sopenharmony_ci		nla_total_size(1) + /* IFLA_MACSEC_ENCRYPT */
42438c2ecf20Sopenharmony_ci		nla_total_size(1) + /* IFLA_MACSEC_PROTECT */
42448c2ecf20Sopenharmony_ci		nla_total_size(1) + /* IFLA_MACSEC_INC_SCI */
42458c2ecf20Sopenharmony_ci		nla_total_size(1) + /* IFLA_MACSEC_ES */
42468c2ecf20Sopenharmony_ci		nla_total_size(1) + /* IFLA_MACSEC_SCB */
42478c2ecf20Sopenharmony_ci		nla_total_size(1) + /* IFLA_MACSEC_REPLAY_PROTECT */
42488c2ecf20Sopenharmony_ci		nla_total_size(1) + /* IFLA_MACSEC_VALIDATION */
42498c2ecf20Sopenharmony_ci		0;
42508c2ecf20Sopenharmony_ci}
42518c2ecf20Sopenharmony_ci
42528c2ecf20Sopenharmony_cistatic int macsec_fill_info(struct sk_buff *skb,
42538c2ecf20Sopenharmony_ci			    const struct net_device *dev)
42548c2ecf20Sopenharmony_ci{
42558c2ecf20Sopenharmony_ci	struct macsec_secy *secy = &macsec_priv(dev)->secy;
42568c2ecf20Sopenharmony_ci	struct macsec_tx_sc *tx_sc = &secy->tx_sc;
42578c2ecf20Sopenharmony_ci	u64 csid;
42588c2ecf20Sopenharmony_ci
42598c2ecf20Sopenharmony_ci	switch (secy->key_len) {
42608c2ecf20Sopenharmony_ci	case MACSEC_GCM_AES_128_SAK_LEN:
42618c2ecf20Sopenharmony_ci		csid = secy->xpn ? MACSEC_CIPHER_ID_GCM_AES_XPN_128 : MACSEC_DEFAULT_CIPHER_ID;
42628c2ecf20Sopenharmony_ci		break;
42638c2ecf20Sopenharmony_ci	case MACSEC_GCM_AES_256_SAK_LEN:
42648c2ecf20Sopenharmony_ci		csid = secy->xpn ? MACSEC_CIPHER_ID_GCM_AES_XPN_256 : MACSEC_CIPHER_ID_GCM_AES_256;
42658c2ecf20Sopenharmony_ci		break;
42668c2ecf20Sopenharmony_ci	default:
42678c2ecf20Sopenharmony_ci		goto nla_put_failure;
42688c2ecf20Sopenharmony_ci	}
42698c2ecf20Sopenharmony_ci
42708c2ecf20Sopenharmony_ci	if (nla_put_sci(skb, IFLA_MACSEC_SCI, secy->sci,
42718c2ecf20Sopenharmony_ci			IFLA_MACSEC_PAD) ||
42728c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_MACSEC_ICV_LEN, secy->icv_len) ||
42738c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, IFLA_MACSEC_CIPHER_SUITE,
42748c2ecf20Sopenharmony_ci			      csid, IFLA_MACSEC_PAD) ||
42758c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_MACSEC_ENCODING_SA, tx_sc->encoding_sa) ||
42768c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_MACSEC_ENCRYPT, tx_sc->encrypt) ||
42778c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_MACSEC_PROTECT, secy->protect_frames) ||
42788c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_MACSEC_INC_SCI, tx_sc->send_sci) ||
42798c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_MACSEC_ES, tx_sc->end_station) ||
42808c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_MACSEC_SCB, tx_sc->scb) ||
42818c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_MACSEC_REPLAY_PROTECT, secy->replay_protect) ||
42828c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_MACSEC_VALIDATION, secy->validate_frames) ||
42838c2ecf20Sopenharmony_ci	    0)
42848c2ecf20Sopenharmony_ci		goto nla_put_failure;
42858c2ecf20Sopenharmony_ci
42868c2ecf20Sopenharmony_ci	if (secy->replay_protect) {
42878c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, IFLA_MACSEC_WINDOW, secy->replay_window))
42888c2ecf20Sopenharmony_ci			goto nla_put_failure;
42898c2ecf20Sopenharmony_ci	}
42908c2ecf20Sopenharmony_ci
42918c2ecf20Sopenharmony_ci	return 0;
42928c2ecf20Sopenharmony_ci
42938c2ecf20Sopenharmony_cinla_put_failure:
42948c2ecf20Sopenharmony_ci	return -EMSGSIZE;
42958c2ecf20Sopenharmony_ci}
42968c2ecf20Sopenharmony_ci
42978c2ecf20Sopenharmony_cistatic struct rtnl_link_ops macsec_link_ops __read_mostly = {
42988c2ecf20Sopenharmony_ci	.kind		= "macsec",
42998c2ecf20Sopenharmony_ci	.priv_size	= sizeof(struct macsec_dev),
43008c2ecf20Sopenharmony_ci	.maxtype	= IFLA_MACSEC_MAX,
43018c2ecf20Sopenharmony_ci	.policy		= macsec_rtnl_policy,
43028c2ecf20Sopenharmony_ci	.setup		= macsec_setup,
43038c2ecf20Sopenharmony_ci	.validate	= macsec_validate_attr,
43048c2ecf20Sopenharmony_ci	.newlink	= macsec_newlink,
43058c2ecf20Sopenharmony_ci	.changelink	= macsec_changelink,
43068c2ecf20Sopenharmony_ci	.dellink	= macsec_dellink,
43078c2ecf20Sopenharmony_ci	.get_size	= macsec_get_size,
43088c2ecf20Sopenharmony_ci	.fill_info	= macsec_fill_info,
43098c2ecf20Sopenharmony_ci	.get_link_net	= macsec_get_link_net,
43108c2ecf20Sopenharmony_ci};
43118c2ecf20Sopenharmony_ci
43128c2ecf20Sopenharmony_cistatic bool is_macsec_master(struct net_device *dev)
43138c2ecf20Sopenharmony_ci{
43148c2ecf20Sopenharmony_ci	return rcu_access_pointer(dev->rx_handler) == macsec_handle_frame;
43158c2ecf20Sopenharmony_ci}
43168c2ecf20Sopenharmony_ci
43178c2ecf20Sopenharmony_cistatic int macsec_notify(struct notifier_block *this, unsigned long event,
43188c2ecf20Sopenharmony_ci			 void *ptr)
43198c2ecf20Sopenharmony_ci{
43208c2ecf20Sopenharmony_ci	struct net_device *real_dev = netdev_notifier_info_to_dev(ptr);
43218c2ecf20Sopenharmony_ci	LIST_HEAD(head);
43228c2ecf20Sopenharmony_ci
43238c2ecf20Sopenharmony_ci	if (!is_macsec_master(real_dev))
43248c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
43258c2ecf20Sopenharmony_ci
43268c2ecf20Sopenharmony_ci	switch (event) {
43278c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
43288c2ecf20Sopenharmony_ci	case NETDEV_UP:
43298c2ecf20Sopenharmony_ci	case NETDEV_CHANGE: {
43308c2ecf20Sopenharmony_ci		struct macsec_dev *m, *n;
43318c2ecf20Sopenharmony_ci		struct macsec_rxh_data *rxd;
43328c2ecf20Sopenharmony_ci
43338c2ecf20Sopenharmony_ci		rxd = macsec_data_rtnl(real_dev);
43348c2ecf20Sopenharmony_ci		list_for_each_entry_safe(m, n, &rxd->secys, secys) {
43358c2ecf20Sopenharmony_ci			struct net_device *dev = m->secy.netdev;
43368c2ecf20Sopenharmony_ci
43378c2ecf20Sopenharmony_ci			netif_stacked_transfer_operstate(real_dev, dev);
43388c2ecf20Sopenharmony_ci		}
43398c2ecf20Sopenharmony_ci		break;
43408c2ecf20Sopenharmony_ci	}
43418c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER: {
43428c2ecf20Sopenharmony_ci		struct macsec_dev *m, *n;
43438c2ecf20Sopenharmony_ci		struct macsec_rxh_data *rxd;
43448c2ecf20Sopenharmony_ci
43458c2ecf20Sopenharmony_ci		rxd = macsec_data_rtnl(real_dev);
43468c2ecf20Sopenharmony_ci		list_for_each_entry_safe(m, n, &rxd->secys, secys) {
43478c2ecf20Sopenharmony_ci			macsec_common_dellink(m->secy.netdev, &head);
43488c2ecf20Sopenharmony_ci		}
43498c2ecf20Sopenharmony_ci
43508c2ecf20Sopenharmony_ci		netdev_rx_handler_unregister(real_dev);
43518c2ecf20Sopenharmony_ci		kfree(rxd);
43528c2ecf20Sopenharmony_ci
43538c2ecf20Sopenharmony_ci		unregister_netdevice_many(&head);
43548c2ecf20Sopenharmony_ci		break;
43558c2ecf20Sopenharmony_ci	}
43568c2ecf20Sopenharmony_ci	case NETDEV_CHANGEMTU: {
43578c2ecf20Sopenharmony_ci		struct macsec_dev *m;
43588c2ecf20Sopenharmony_ci		struct macsec_rxh_data *rxd;
43598c2ecf20Sopenharmony_ci
43608c2ecf20Sopenharmony_ci		rxd = macsec_data_rtnl(real_dev);
43618c2ecf20Sopenharmony_ci		list_for_each_entry(m, &rxd->secys, secys) {
43628c2ecf20Sopenharmony_ci			struct net_device *dev = m->secy.netdev;
43638c2ecf20Sopenharmony_ci			unsigned int mtu = real_dev->mtu - (m->secy.icv_len +
43648c2ecf20Sopenharmony_ci							    macsec_extra_len(true));
43658c2ecf20Sopenharmony_ci
43668c2ecf20Sopenharmony_ci			if (dev->mtu > mtu)
43678c2ecf20Sopenharmony_ci				dev_set_mtu(dev, mtu);
43688c2ecf20Sopenharmony_ci		}
43698c2ecf20Sopenharmony_ci	}
43708c2ecf20Sopenharmony_ci	}
43718c2ecf20Sopenharmony_ci
43728c2ecf20Sopenharmony_ci	return NOTIFY_OK;
43738c2ecf20Sopenharmony_ci}
43748c2ecf20Sopenharmony_ci
43758c2ecf20Sopenharmony_cistatic struct notifier_block macsec_notifier = {
43768c2ecf20Sopenharmony_ci	.notifier_call = macsec_notify,
43778c2ecf20Sopenharmony_ci};
43788c2ecf20Sopenharmony_ci
43798c2ecf20Sopenharmony_cistatic int __init macsec_init(void)
43808c2ecf20Sopenharmony_ci{
43818c2ecf20Sopenharmony_ci	int err;
43828c2ecf20Sopenharmony_ci
43838c2ecf20Sopenharmony_ci	pr_info("MACsec IEEE 802.1AE\n");
43848c2ecf20Sopenharmony_ci	err = register_netdevice_notifier(&macsec_notifier);
43858c2ecf20Sopenharmony_ci	if (err)
43868c2ecf20Sopenharmony_ci		return err;
43878c2ecf20Sopenharmony_ci
43888c2ecf20Sopenharmony_ci	err = rtnl_link_register(&macsec_link_ops);
43898c2ecf20Sopenharmony_ci	if (err)
43908c2ecf20Sopenharmony_ci		goto notifier;
43918c2ecf20Sopenharmony_ci
43928c2ecf20Sopenharmony_ci	err = genl_register_family(&macsec_fam);
43938c2ecf20Sopenharmony_ci	if (err)
43948c2ecf20Sopenharmony_ci		goto rtnl;
43958c2ecf20Sopenharmony_ci
43968c2ecf20Sopenharmony_ci	return 0;
43978c2ecf20Sopenharmony_ci
43988c2ecf20Sopenharmony_cirtnl:
43998c2ecf20Sopenharmony_ci	rtnl_link_unregister(&macsec_link_ops);
44008c2ecf20Sopenharmony_cinotifier:
44018c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&macsec_notifier);
44028c2ecf20Sopenharmony_ci	return err;
44038c2ecf20Sopenharmony_ci}
44048c2ecf20Sopenharmony_ci
44058c2ecf20Sopenharmony_cistatic void __exit macsec_exit(void)
44068c2ecf20Sopenharmony_ci{
44078c2ecf20Sopenharmony_ci	genl_unregister_family(&macsec_fam);
44088c2ecf20Sopenharmony_ci	rtnl_link_unregister(&macsec_link_ops);
44098c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&macsec_notifier);
44108c2ecf20Sopenharmony_ci	rcu_barrier();
44118c2ecf20Sopenharmony_ci}
44128c2ecf20Sopenharmony_ci
44138c2ecf20Sopenharmony_cimodule_init(macsec_init);
44148c2ecf20Sopenharmony_cimodule_exit(macsec_exit);
44158c2ecf20Sopenharmony_ci
44168c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("macsec");
44178c2ecf20Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY("macsec");
44188c2ecf20Sopenharmony_ci
44198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MACsec IEEE 802.1AE");
44208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
4421