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