xref: /freebsd/contrib/wpa/src/drivers/driver_macsec_qca.c (revision 206b73d0429edb7c49b612537544e677fa568e83)
15b9c547cSRui Paulo /*
25b9c547cSRui Paulo  * Wired Ethernet driver interface for QCA MACsec driver
35b9c547cSRui Paulo  * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
45b9c547cSRui Paulo  * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
55b9c547cSRui Paulo  * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
6*206b73d0SCy Schubert  * Copyright (c) 2019, The Linux Foundation
75b9c547cSRui Paulo  *
85b9c547cSRui Paulo  * This software may be distributed under the terms of the BSD license.
95b9c547cSRui Paulo  * See README for more details.
105b9c547cSRui Paulo  */
115b9c547cSRui Paulo 
125b9c547cSRui Paulo #include "includes.h"
135b9c547cSRui Paulo #include <sys/ioctl.h>
145b9c547cSRui Paulo #include <net/if.h>
15780fb4a2SCy Schubert #include <inttypes.h>
165b9c547cSRui Paulo #ifdef __linux__
175b9c547cSRui Paulo #include <netpacket/packet.h>
185b9c547cSRui Paulo #include <net/if_arp.h>
195b9c547cSRui Paulo #include <net/if.h>
205b9c547cSRui Paulo #endif /* __linux__ */
215b9c547cSRui Paulo #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
225b9c547cSRui Paulo #include <net/if_dl.h>
235b9c547cSRui Paulo #include <net/if_media.h>
245b9c547cSRui Paulo #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
255b9c547cSRui Paulo #ifdef __sun__
265b9c547cSRui Paulo #include <sys/sockio.h>
275b9c547cSRui Paulo #endif /* __sun__ */
285b9c547cSRui Paulo 
295b9c547cSRui Paulo #include "utils/common.h"
305b9c547cSRui Paulo #include "utils/eloop.h"
315b9c547cSRui Paulo #include "common/defs.h"
325b9c547cSRui Paulo #include "common/ieee802_1x_defs.h"
33*206b73d0SCy Schubert #include "common/eapol_common.h"
3485732ac8SCy Schubert #include "pae/ieee802_1x_kay.h"
355b9c547cSRui Paulo #include "driver.h"
3685732ac8SCy Schubert #include "driver_wired_common.h"
375b9c547cSRui Paulo 
385b9c547cSRui Paulo #include "nss_macsec_secy.h"
395b9c547cSRui Paulo #include "nss_macsec_secy_rx.h"
405b9c547cSRui Paulo #include "nss_macsec_secy_tx.h"
415b9c547cSRui Paulo 
425b9c547cSRui Paulo #define MAXSC 16
435b9c547cSRui Paulo 
4485732ac8SCy Schubert #define SAK_128_LEN	16
4585732ac8SCy Schubert #define SAK_256_LEN	32
4685732ac8SCy Schubert 
475b9c547cSRui Paulo /* TCI field definition */
485b9c547cSRui Paulo #define TCI_ES                0x40
495b9c547cSRui Paulo #define TCI_SC                0x20
505b9c547cSRui Paulo #define TCI_SCB               0x10
515b9c547cSRui Paulo #define TCI_E                 0x08
525b9c547cSRui Paulo #define TCI_C                 0x04
535b9c547cSRui Paulo 
545b9c547cSRui Paulo #ifdef _MSC_VER
555b9c547cSRui Paulo #pragma pack(push, 1)
565b9c547cSRui Paulo #endif /* _MSC_VER */
575b9c547cSRui Paulo 
585b9c547cSRui Paulo #ifdef _MSC_VER
595b9c547cSRui Paulo #pragma pack(pop)
605b9c547cSRui Paulo #endif /* _MSC_VER */
615b9c547cSRui Paulo 
6285732ac8SCy Schubert struct channel_map {
6385732ac8SCy Schubert 	struct ieee802_1x_mka_sci sci;
6485732ac8SCy Schubert };
655b9c547cSRui Paulo 
665b9c547cSRui Paulo struct macsec_qca_data {
6785732ac8SCy Schubert 	struct driver_wired_common_data common;
685b9c547cSRui Paulo 
69*206b73d0SCy Schubert 	int use_pae_group_addr;
7085732ac8SCy Schubert 	u32 secy_id;
715b9c547cSRui Paulo 
725b9c547cSRui Paulo 	/* shadow */
735b9c547cSRui Paulo 	Boolean always_include_sci;
745b9c547cSRui Paulo 	Boolean use_es;
755b9c547cSRui Paulo 	Boolean use_scb;
765b9c547cSRui Paulo 	Boolean protect_frames;
775b9c547cSRui Paulo 	Boolean replay_protect;
785b9c547cSRui Paulo 	u32 replay_window;
7985732ac8SCy Schubert 
8085732ac8SCy Schubert 	struct channel_map receive_channel_map[MAXSC];
8185732ac8SCy Schubert 	struct channel_map transmit_channel_map[MAXSC];
825b9c547cSRui Paulo };
835b9c547cSRui Paulo 
845b9c547cSRui Paulo 
855b9c547cSRui Paulo static void __macsec_drv_init(struct macsec_qca_data *drv)
865b9c547cSRui Paulo {
875b9c547cSRui Paulo 	int ret = 0;
885b9c547cSRui Paulo 	fal_rx_ctl_filt_t rx_ctl_filt;
895b9c547cSRui Paulo 	fal_tx_ctl_filt_t tx_ctl_filt;
905b9c547cSRui Paulo 
915b9c547cSRui Paulo 	wpa_printf(MSG_INFO, "%s: secy_id=%d", __func__, drv->secy_id);
925b9c547cSRui Paulo 
935b9c547cSRui Paulo 	/* Enable Secy and Let EAPoL bypass */
945b9c547cSRui Paulo 	ret = nss_macsec_secy_en_set(drv->secy_id, TRUE);
955b9c547cSRui Paulo 	if (ret)
965b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "nss_macsec_secy_en_set: FAIL");
975b9c547cSRui Paulo 
985b9c547cSRui Paulo 	ret = nss_macsec_secy_sc_sa_mapping_mode_set(drv->secy_id,
995b9c547cSRui Paulo 						     FAL_SC_SA_MAP_1_4);
1005b9c547cSRui Paulo 	if (ret)
1015b9c547cSRui Paulo 		wpa_printf(MSG_ERROR,
1025b9c547cSRui Paulo 			   "nss_macsec_secy_sc_sa_mapping_mode_set: FAIL");
1035b9c547cSRui Paulo 
1045b9c547cSRui Paulo 	os_memset(&rx_ctl_filt, 0, sizeof(rx_ctl_filt));
1055b9c547cSRui Paulo 	rx_ctl_filt.bypass = 1;
1065b9c547cSRui Paulo 	rx_ctl_filt.match_type = IG_CTL_COMPARE_ETHER_TYPE;
1075b9c547cSRui Paulo 	rx_ctl_filt.match_mask = 0xffff;
1085b9c547cSRui Paulo 	rx_ctl_filt.ether_type_da_range = 0x888e;
1095b9c547cSRui Paulo 	ret = nss_macsec_secy_rx_ctl_filt_set(drv->secy_id, 0, &rx_ctl_filt);
1105b9c547cSRui Paulo 	if (ret)
1115b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "nss_macsec_secy_rx_ctl_filt_set: FAIL");
1125b9c547cSRui Paulo 
1135b9c547cSRui Paulo 	os_memset(&tx_ctl_filt, 0, sizeof(tx_ctl_filt));
1145b9c547cSRui Paulo 	tx_ctl_filt.bypass = 1;
1155b9c547cSRui Paulo 	tx_ctl_filt.match_type = EG_CTL_COMPARE_ETHER_TYPE;
1165b9c547cSRui Paulo 	tx_ctl_filt.match_mask = 0xffff;
1175b9c547cSRui Paulo 	tx_ctl_filt.ether_type_da_range = 0x888e;
1185b9c547cSRui Paulo 	ret = nss_macsec_secy_tx_ctl_filt_set(drv->secy_id, 0, &tx_ctl_filt);
1195b9c547cSRui Paulo 	if (ret)
1205b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "nss_macsec_secy_tx_ctl_filt_set: FAIL");
1215b9c547cSRui Paulo }
1225b9c547cSRui Paulo 
1235b9c547cSRui Paulo 
1245b9c547cSRui Paulo static void __macsec_drv_deinit(struct macsec_qca_data *drv)
1255b9c547cSRui Paulo {
1265b9c547cSRui Paulo 	nss_macsec_secy_en_set(drv->secy_id, FALSE);
1275b9c547cSRui Paulo 	nss_macsec_secy_rx_sc_del_all(drv->secy_id);
1285b9c547cSRui Paulo 	nss_macsec_secy_tx_sc_del_all(drv->secy_id);
1295b9c547cSRui Paulo }
1305b9c547cSRui Paulo 
1315b9c547cSRui Paulo 
132*206b73d0SCy Schubert #ifdef __linux__
133*206b73d0SCy Schubert 
134*206b73d0SCy Schubert static void macsec_qca_handle_data(void *ctx, unsigned char *buf, size_t len)
135*206b73d0SCy Schubert {
136*206b73d0SCy Schubert #ifdef HOSTAPD
137*206b73d0SCy Schubert 	struct ieee8023_hdr *hdr;
138*206b73d0SCy Schubert 	u8 *pos, *sa;
139*206b73d0SCy Schubert 	size_t left;
140*206b73d0SCy Schubert 	union wpa_event_data event;
141*206b73d0SCy Schubert 
142*206b73d0SCy Schubert 	/* at least 6 bytes src macaddress, 6 bytes dst macaddress
143*206b73d0SCy Schubert 	 * and 2 bytes ethertype
144*206b73d0SCy Schubert 	*/
145*206b73d0SCy Schubert 	if (len < 14) {
146*206b73d0SCy Schubert 		wpa_printf(MSG_MSGDUMP,
147*206b73d0SCy Schubert 			   "macsec_qca_handle_data: too short (%lu)",
148*206b73d0SCy Schubert 			   (unsigned long) len);
149*206b73d0SCy Schubert 		return;
150*206b73d0SCy Schubert 	}
151*206b73d0SCy Schubert 	hdr = (struct ieee8023_hdr *) buf;
152*206b73d0SCy Schubert 
153*206b73d0SCy Schubert 	switch (ntohs(hdr->ethertype)) {
154*206b73d0SCy Schubert 	case ETH_P_PAE:
155*206b73d0SCy Schubert 		wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
156*206b73d0SCy Schubert 		sa = hdr->src;
157*206b73d0SCy Schubert 		os_memset(&event, 0, sizeof(event));
158*206b73d0SCy Schubert 		event.new_sta.addr = sa;
159*206b73d0SCy Schubert 		wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
160*206b73d0SCy Schubert 
161*206b73d0SCy Schubert 		pos = (u8 *) (hdr + 1);
162*206b73d0SCy Schubert 		left = len - sizeof(*hdr);
163*206b73d0SCy Schubert 		drv_event_eapol_rx(ctx, sa, pos, left);
164*206b73d0SCy Schubert 		break;
165*206b73d0SCy Schubert 	default:
166*206b73d0SCy Schubert 		wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
167*206b73d0SCy Schubert 			   ntohs(hdr->ethertype));
168*206b73d0SCy Schubert 		break;
169*206b73d0SCy Schubert 	}
170*206b73d0SCy Schubert #endif /* HOSTAPD */
171*206b73d0SCy Schubert }
172*206b73d0SCy Schubert 
173*206b73d0SCy Schubert 
174*206b73d0SCy Schubert static void macsec_qca_handle_read(int sock, void *eloop_ctx, void *sock_ctx)
175*206b73d0SCy Schubert {
176*206b73d0SCy Schubert 	int len;
177*206b73d0SCy Schubert 	unsigned char buf[3000];
178*206b73d0SCy Schubert 
179*206b73d0SCy Schubert 	len = recv(sock, buf, sizeof(buf), 0);
180*206b73d0SCy Schubert 	if (len < 0) {
181*206b73d0SCy Schubert 		wpa_printf(MSG_ERROR, "macsec_qca: recv: %s", strerror(errno));
182*206b73d0SCy Schubert 		return;
183*206b73d0SCy Schubert 	}
184*206b73d0SCy Schubert 
185*206b73d0SCy Schubert 	macsec_qca_handle_data(eloop_ctx, buf, len);
186*206b73d0SCy Schubert }
187*206b73d0SCy Schubert 
188*206b73d0SCy Schubert #endif /* __linux__ */
189*206b73d0SCy Schubert 
190*206b73d0SCy Schubert 
191*206b73d0SCy Schubert static int macsec_qca_init_sockets(struct macsec_qca_data *drv, u8 *own_addr)
192*206b73d0SCy Schubert {
193*206b73d0SCy Schubert #ifdef __linux__
194*206b73d0SCy Schubert 	struct ifreq ifr;
195*206b73d0SCy Schubert 	struct sockaddr_ll addr;
196*206b73d0SCy Schubert 
197*206b73d0SCy Schubert 	drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
198*206b73d0SCy Schubert 	if (drv->common.sock < 0) {
199*206b73d0SCy Schubert 		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
200*206b73d0SCy Schubert 			   strerror(errno));
201*206b73d0SCy Schubert 		return -1;
202*206b73d0SCy Schubert 	}
203*206b73d0SCy Schubert 
204*206b73d0SCy Schubert 	if (eloop_register_read_sock(drv->common.sock, macsec_qca_handle_read,
205*206b73d0SCy Schubert 				     drv->common.ctx, NULL)) {
206*206b73d0SCy Schubert 		wpa_printf(MSG_INFO, "Could not register read socket");
207*206b73d0SCy Schubert 		return -1;
208*206b73d0SCy Schubert 	}
209*206b73d0SCy Schubert 
210*206b73d0SCy Schubert 	os_memset(&ifr, 0, sizeof(ifr));
211*206b73d0SCy Schubert 	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
212*206b73d0SCy Schubert 	if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
213*206b73d0SCy Schubert 		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
214*206b73d0SCy Schubert 			   strerror(errno));
215*206b73d0SCy Schubert 		return -1;
216*206b73d0SCy Schubert 	}
217*206b73d0SCy Schubert 
218*206b73d0SCy Schubert 	os_memset(&addr, 0, sizeof(addr));
219*206b73d0SCy Schubert 	addr.sll_family = AF_PACKET;
220*206b73d0SCy Schubert 	addr.sll_ifindex = ifr.ifr_ifindex;
221*206b73d0SCy Schubert 	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
222*206b73d0SCy Schubert 		   addr.sll_ifindex);
223*206b73d0SCy Schubert 
224*206b73d0SCy Schubert 	if (bind(drv->common.sock, (struct sockaddr *) &addr,
225*206b73d0SCy Schubert 		 sizeof(addr)) < 0) {
226*206b73d0SCy Schubert 		wpa_printf(MSG_ERROR, "macsec_qca: bind: %s", strerror(errno));
227*206b73d0SCy Schubert 		return -1;
228*206b73d0SCy Schubert 	}
229*206b73d0SCy Schubert 
230*206b73d0SCy Schubert 	/* filter multicast address */
231*206b73d0SCy Schubert 	if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
232*206b73d0SCy Schubert 				       pae_group_addr, 1) < 0) {
233*206b73d0SCy Schubert 		wpa_printf(MSG_ERROR,
234*206b73d0SCy Schubert 			"macsec_qca_init_sockets: Failed to add multicast group membership");
235*206b73d0SCy Schubert 		return -1;
236*206b73d0SCy Schubert 	}
237*206b73d0SCy Schubert 
238*206b73d0SCy Schubert 	os_memset(&ifr, 0, sizeof(ifr));
239*206b73d0SCy Schubert 	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
240*206b73d0SCy Schubert 	if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
241*206b73d0SCy Schubert 		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
242*206b73d0SCy Schubert 			   strerror(errno));
243*206b73d0SCy Schubert 		return -1;
244*206b73d0SCy Schubert 	}
245*206b73d0SCy Schubert 
246*206b73d0SCy Schubert 	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
247*206b73d0SCy Schubert 		wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
248*206b73d0SCy Schubert 			   ifr.ifr_hwaddr.sa_family);
249*206b73d0SCy Schubert 		return -1;
250*206b73d0SCy Schubert 	}
251*206b73d0SCy Schubert 	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
252*206b73d0SCy Schubert 
253*206b73d0SCy Schubert 	return 0;
254*206b73d0SCy Schubert #else /* __linux__ */
255*206b73d0SCy Schubert 	return -1;
256*206b73d0SCy Schubert #endif /* __linux__ */
257*206b73d0SCy Schubert }
258*206b73d0SCy Schubert 
259*206b73d0SCy Schubert 
2605b9c547cSRui Paulo static void * macsec_qca_init(void *ctx, const char *ifname)
2615b9c547cSRui Paulo {
2625b9c547cSRui Paulo 	struct macsec_qca_data *drv;
2635b9c547cSRui Paulo 
2645b9c547cSRui Paulo 	drv = os_zalloc(sizeof(*drv));
2655b9c547cSRui Paulo 	if (drv == NULL)
2665b9c547cSRui Paulo 		return NULL;
2675b9c547cSRui Paulo 
2685b9c547cSRui Paulo 	/* Board specific settings */
26985732ac8SCy Schubert 	if (os_memcmp("eth2", ifname, 4) == 0)
2705b9c547cSRui Paulo 		drv->secy_id = 1;
27185732ac8SCy Schubert 	else if (os_memcmp("eth3", ifname, 4) == 0)
2725b9c547cSRui Paulo 		drv->secy_id = 2;
2735b9c547cSRui Paulo 	else
2745b9c547cSRui Paulo 		drv->secy_id = -1;
2755b9c547cSRui Paulo 
27685732ac8SCy Schubert 	if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
2775b9c547cSRui Paulo 		os_free(drv);
2785b9c547cSRui Paulo 		return NULL;
2795b9c547cSRui Paulo 	}
2805b9c547cSRui Paulo 
2815b9c547cSRui Paulo 	return drv;
2825b9c547cSRui Paulo }
2835b9c547cSRui Paulo 
2845b9c547cSRui Paulo 
2855b9c547cSRui Paulo static void macsec_qca_deinit(void *priv)
2865b9c547cSRui Paulo {
2875b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
2885b9c547cSRui Paulo 
28985732ac8SCy Schubert 	driver_wired_deinit_common(&drv->common);
2905b9c547cSRui Paulo 	os_free(drv);
2915b9c547cSRui Paulo }
2925b9c547cSRui Paulo 
2935b9c547cSRui Paulo 
294*206b73d0SCy Schubert static void * macsec_qca_hapd_init(struct hostapd_data *hapd,
295*206b73d0SCy Schubert 				   struct wpa_init_params *params)
296*206b73d0SCy Schubert {
297*206b73d0SCy Schubert 	struct macsec_qca_data *drv;
298*206b73d0SCy Schubert 
299*206b73d0SCy Schubert 	drv = os_zalloc(sizeof(struct macsec_qca_data));
300*206b73d0SCy Schubert 	if (!drv) {
301*206b73d0SCy Schubert 		wpa_printf(MSG_INFO,
302*206b73d0SCy Schubert 			   "Could not allocate memory for macsec_qca driver data");
303*206b73d0SCy Schubert 		return NULL;
304*206b73d0SCy Schubert 	}
305*206b73d0SCy Schubert 
306*206b73d0SCy Schubert 	/* Board specific settings */
307*206b73d0SCy Schubert 	if (os_memcmp("eth2", params->ifname, 4) == 0)
308*206b73d0SCy Schubert 		drv->secy_id = 1;
309*206b73d0SCy Schubert 	else if (os_memcmp("eth3", params->ifname, 4) == 0)
310*206b73d0SCy Schubert 		drv->secy_id = 2;
311*206b73d0SCy Schubert 	else if (os_memcmp("eth4", params->ifname, 4) == 0)
312*206b73d0SCy Schubert 		drv->secy_id = 0;
313*206b73d0SCy Schubert 	else if (os_memcmp("eth5", params->ifname, 4) == 0)
314*206b73d0SCy Schubert 		drv->secy_id = 1;
315*206b73d0SCy Schubert 	else
316*206b73d0SCy Schubert 		drv->secy_id = -1;
317*206b73d0SCy Schubert 
318*206b73d0SCy Schubert 	drv->common.ctx = hapd;
319*206b73d0SCy Schubert 	os_strlcpy(drv->common.ifname, params->ifname,
320*206b73d0SCy Schubert 		   sizeof(drv->common.ifname));
321*206b73d0SCy Schubert 	drv->use_pae_group_addr = params->use_pae_group_addr;
322*206b73d0SCy Schubert 
323*206b73d0SCy Schubert 	if (macsec_qca_init_sockets(drv, params->own_addr)) {
324*206b73d0SCy Schubert 		os_free(drv);
325*206b73d0SCy Schubert 		return NULL;
326*206b73d0SCy Schubert 	}
327*206b73d0SCy Schubert 
328*206b73d0SCy Schubert 	return drv;
329*206b73d0SCy Schubert }
330*206b73d0SCy Schubert 
331*206b73d0SCy Schubert 
332*206b73d0SCy Schubert static void macsec_qca_hapd_deinit(void *priv)
333*206b73d0SCy Schubert {
334*206b73d0SCy Schubert 	struct macsec_qca_data *drv = priv;
335*206b73d0SCy Schubert 
336*206b73d0SCy Schubert 	if (drv->common.sock >= 0) {
337*206b73d0SCy Schubert 		eloop_unregister_read_sock(drv->common.sock);
338*206b73d0SCy Schubert 		close(drv->common.sock);
339*206b73d0SCy Schubert 	}
340*206b73d0SCy Schubert 
341*206b73d0SCy Schubert 	os_free(drv);
342*206b73d0SCy Schubert }
343*206b73d0SCy Schubert 
344*206b73d0SCy Schubert 
345*206b73d0SCy Schubert static int macsec_qca_send_eapol(void *priv, const u8 *addr,
346*206b73d0SCy Schubert 				 const u8 *data, size_t data_len, int encrypt,
347*206b73d0SCy Schubert 				 const u8 *own_addr, u32 flags)
348*206b73d0SCy Schubert {
349*206b73d0SCy Schubert 	struct macsec_qca_data *drv = priv;
350*206b73d0SCy Schubert 	struct ieee8023_hdr *hdr;
351*206b73d0SCy Schubert 	size_t len;
352*206b73d0SCy Schubert 	u8 *pos;
353*206b73d0SCy Schubert 	int res;
354*206b73d0SCy Schubert 
355*206b73d0SCy Schubert 	len = sizeof(*hdr) + data_len;
356*206b73d0SCy Schubert 	hdr = os_zalloc(len);
357*206b73d0SCy Schubert 	if (!hdr) {
358*206b73d0SCy Schubert 		wpa_printf(MSG_INFO,
359*206b73d0SCy Schubert 			   "malloc() failed for macsec_qca_send_eapol(len=%lu)",
360*206b73d0SCy Schubert 			   (unsigned long) len);
361*206b73d0SCy Schubert 		return -1;
362*206b73d0SCy Schubert 	}
363*206b73d0SCy Schubert 
364*206b73d0SCy Schubert 	os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
365*206b73d0SCy Schubert 		  ETH_ALEN);
366*206b73d0SCy Schubert 	os_memcpy(hdr->src, own_addr, ETH_ALEN);
367*206b73d0SCy Schubert 	hdr->ethertype = htons(ETH_P_PAE);
368*206b73d0SCy Schubert 
369*206b73d0SCy Schubert 	pos = (u8 *) (hdr + 1);
370*206b73d0SCy Schubert 	os_memcpy(pos, data, data_len);
371*206b73d0SCy Schubert 
372*206b73d0SCy Schubert 	res = send(drv->common.sock, (u8 *) hdr, len, 0);
373*206b73d0SCy Schubert 	os_free(hdr);
374*206b73d0SCy Schubert 
375*206b73d0SCy Schubert 	if (res < 0) {
376*206b73d0SCy Schubert 		wpa_printf(MSG_ERROR,
377*206b73d0SCy Schubert 			   "macsec_qca_send_eapol - packet len: %lu - failed: send: %s",
378*206b73d0SCy Schubert 			   (unsigned long) len, strerror(errno));
379*206b73d0SCy Schubert 	}
380*206b73d0SCy Schubert 
381*206b73d0SCy Schubert 	return res;
382*206b73d0SCy Schubert }
383*206b73d0SCy Schubert 
384*206b73d0SCy Schubert 
3855b9c547cSRui Paulo static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params)
3865b9c547cSRui Paulo {
3875b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
3885b9c547cSRui Paulo 
3895b9c547cSRui Paulo 	drv->always_include_sci = params->always_include_sci;
3905b9c547cSRui Paulo 	drv->use_es = params->use_es;
3915b9c547cSRui Paulo 	drv->use_scb = params->use_scb;
3925b9c547cSRui Paulo 
3935b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: es=%d, scb=%d, sci=%d",
3945b9c547cSRui Paulo 		   __func__, drv->use_es, drv->use_scb,
3955b9c547cSRui Paulo 		   drv->always_include_sci);
3965b9c547cSRui Paulo 
3975b9c547cSRui Paulo 	__macsec_drv_init(drv);
3985b9c547cSRui Paulo 
3995b9c547cSRui Paulo 	return 0;
4005b9c547cSRui Paulo }
4015b9c547cSRui Paulo 
4025b9c547cSRui Paulo 
4035b9c547cSRui Paulo static int macsec_qca_macsec_deinit(void *priv)
4045b9c547cSRui Paulo {
4055b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
4065b9c547cSRui Paulo 
4075b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s", __func__);
4085b9c547cSRui Paulo 
4095b9c547cSRui Paulo 	__macsec_drv_deinit(drv);
4105b9c547cSRui Paulo 
4115b9c547cSRui Paulo 	return 0;
4125b9c547cSRui Paulo }
4135b9c547cSRui Paulo 
4145b9c547cSRui Paulo 
41585732ac8SCy Schubert static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap)
41685732ac8SCy Schubert {
41785732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "%s", __func__);
41885732ac8SCy Schubert 
41985732ac8SCy Schubert 	*cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
42085732ac8SCy Schubert 
42185732ac8SCy Schubert 	return 0;
42285732ac8SCy Schubert }
42385732ac8SCy Schubert 
42485732ac8SCy Schubert 
4255b9c547cSRui Paulo static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled)
4265b9c547cSRui Paulo {
4275b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
4285b9c547cSRui Paulo 	int ret = 0;
4295b9c547cSRui Paulo 
4305b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
4315b9c547cSRui Paulo 
4325b9c547cSRui Paulo 	drv->protect_frames = enabled;
4335b9c547cSRui Paulo 
4345b9c547cSRui Paulo 	return ret;
4355b9c547cSRui Paulo }
4365b9c547cSRui Paulo 
4375b9c547cSRui Paulo 
4385b9c547cSRui Paulo static int macsec_qca_set_replay_protect(void *priv, Boolean enabled,
4395b9c547cSRui Paulo 					 unsigned int window)
4405b9c547cSRui Paulo {
4415b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
4425b9c547cSRui Paulo 	int ret = 0;
4435b9c547cSRui Paulo 
4445b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: enabled=%d, win=%u",
4455b9c547cSRui Paulo 		   __func__, enabled, window);
4465b9c547cSRui Paulo 
4475b9c547cSRui Paulo 	drv->replay_protect = enabled;
4485b9c547cSRui Paulo 	drv->replay_window = window;
4495b9c547cSRui Paulo 
4505b9c547cSRui Paulo 	return ret;
4515b9c547cSRui Paulo }
4525b9c547cSRui Paulo 
4535b9c547cSRui Paulo 
45485732ac8SCy Schubert static fal_cipher_suite_e macsec_qca_cs_type_get(u64 cs)
45585732ac8SCy Schubert {
45685732ac8SCy Schubert 	if (cs == CS_ID_GCM_AES_128)
45785732ac8SCy Schubert 		return FAL_CIPHER_SUITE_AES_GCM_128;
45885732ac8SCy Schubert 	if (cs == CS_ID_GCM_AES_256)
45985732ac8SCy Schubert 		return FAL_CIPHER_SUITE_AES_GCM_256;
46085732ac8SCy Schubert 	return FAL_CIPHER_SUITE_MAX;
46185732ac8SCy Schubert }
46285732ac8SCy Schubert 
46385732ac8SCy Schubert 
464780fb4a2SCy Schubert static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs)
4655b9c547cSRui Paulo {
46685732ac8SCy Schubert 	struct macsec_qca_data *drv = priv;
46785732ac8SCy Schubert 	fal_cipher_suite_e cs_type;
46885732ac8SCy Schubert 
46985732ac8SCy Schubert 	if (cs != CS_ID_GCM_AES_128 && cs != CS_ID_GCM_AES_256) {
470780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
471780fb4a2SCy Schubert 			   "%s: NOT supported CipherSuite: %016" PRIx64,
472780fb4a2SCy Schubert 			   __func__, cs);
4735b9c547cSRui Paulo 		return -1;
4745b9c547cSRui Paulo 	}
4755b9c547cSRui Paulo 
47685732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "%s: CipherSuite: %016" PRIx64, __func__, cs);
4775b9c547cSRui Paulo 
47885732ac8SCy Schubert 	cs_type = macsec_qca_cs_type_get(cs);
47985732ac8SCy Schubert 	return nss_macsec_secy_cipher_suite_set(drv->secy_id, cs_type);
4805b9c547cSRui Paulo }
4815b9c547cSRui Paulo 
4825b9c547cSRui Paulo 
4835b9c547cSRui Paulo static int macsec_qca_enable_controlled_port(void *priv, Boolean enabled)
4845b9c547cSRui Paulo {
4855b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
4865b9c547cSRui Paulo 	int ret = 0;
4875b9c547cSRui Paulo 
4885b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: enable=%d", __func__, enabled);
4895b9c547cSRui Paulo 
4905b9c547cSRui Paulo 	ret += nss_macsec_secy_controlled_port_en_set(drv->secy_id, enabled);
4915b9c547cSRui Paulo 
4925b9c547cSRui Paulo 	return ret;
4935b9c547cSRui Paulo }
4945b9c547cSRui Paulo 
4955b9c547cSRui Paulo 
49685732ac8SCy Schubert static int macsec_qca_lookup_channel(struct channel_map *map,
49785732ac8SCy Schubert 				     struct ieee802_1x_mka_sci *sci,
49885732ac8SCy Schubert 				     u32 *channel)
49985732ac8SCy Schubert {
50085732ac8SCy Schubert 	u32 i;
50185732ac8SCy Schubert 
50285732ac8SCy Schubert 	for (i = 0; i < MAXSC; i++) {
50385732ac8SCy Schubert 		if (os_memcmp(&map[i].sci, sci,
50485732ac8SCy Schubert 			      sizeof(struct ieee802_1x_mka_sci)) == 0) {
50585732ac8SCy Schubert 			*channel = i;
50685732ac8SCy Schubert 			return 0;
50785732ac8SCy Schubert 		}
50885732ac8SCy Schubert 	}
50985732ac8SCy Schubert 
51085732ac8SCy Schubert 	return -1;
51185732ac8SCy Schubert }
51285732ac8SCy Schubert 
51385732ac8SCy Schubert 
51485732ac8SCy Schubert static void macsec_qca_register_channel(struct channel_map *map,
51585732ac8SCy Schubert 					struct ieee802_1x_mka_sci *sci,
51685732ac8SCy Schubert 					u32 channel)
51785732ac8SCy Schubert {
51885732ac8SCy Schubert 	os_memcpy(&map[channel].sci, sci, sizeof(struct ieee802_1x_mka_sci));
51985732ac8SCy Schubert }
52085732ac8SCy Schubert 
52185732ac8SCy Schubert 
52285732ac8SCy Schubert static int macsec_qca_lookup_receive_channel(struct macsec_qca_data *drv,
52385732ac8SCy Schubert 					     struct receive_sc *sc,
52485732ac8SCy Schubert 					     u32 *channel)
52585732ac8SCy Schubert {
52685732ac8SCy Schubert 	return macsec_qca_lookup_channel(drv->receive_channel_map, &sc->sci,
52785732ac8SCy Schubert 					 channel);
52885732ac8SCy Schubert }
52985732ac8SCy Schubert 
53085732ac8SCy Schubert 
53185732ac8SCy Schubert static void macsec_qca_register_receive_channel(struct macsec_qca_data *drv,
53285732ac8SCy Schubert 						struct receive_sc *sc,
53385732ac8SCy Schubert 						u32 channel)
53485732ac8SCy Schubert {
53585732ac8SCy Schubert 	macsec_qca_register_channel(drv->receive_channel_map, &sc->sci,
53685732ac8SCy Schubert 				    channel);
53785732ac8SCy Schubert }
53885732ac8SCy Schubert 
53985732ac8SCy Schubert 
54085732ac8SCy Schubert static int macsec_qca_lookup_transmit_channel(struct macsec_qca_data *drv,
54185732ac8SCy Schubert 					      struct transmit_sc *sc,
54285732ac8SCy Schubert 					      u32 *channel)
54385732ac8SCy Schubert {
54485732ac8SCy Schubert 	return macsec_qca_lookup_channel(drv->transmit_channel_map, &sc->sci,
54585732ac8SCy Schubert 					 channel);
54685732ac8SCy Schubert }
54785732ac8SCy Schubert 
54885732ac8SCy Schubert 
54985732ac8SCy Schubert static void macsec_qca_register_transmit_channel(struct macsec_qca_data *drv,
55085732ac8SCy Schubert 						 struct transmit_sc *sc,
55185732ac8SCy Schubert 						 u32 channel)
55285732ac8SCy Schubert {
55385732ac8SCy Schubert 	macsec_qca_register_channel(drv->transmit_channel_map, &sc->sci,
55485732ac8SCy Schubert 				    channel);
55585732ac8SCy Schubert }
55685732ac8SCy Schubert 
55785732ac8SCy Schubert 
55885732ac8SCy Schubert static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
5595b9c547cSRui Paulo {
5605b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
5615b9c547cSRui Paulo 	int ret = 0;
5625b9c547cSRui Paulo 	u32 next_pn = 0;
5635b9c547cSRui Paulo 	bool enabled = FALSE;
5645b9c547cSRui Paulo 	u32 win;
56585732ac8SCy Schubert 	u32 channel;
5665b9c547cSRui Paulo 
56785732ac8SCy Schubert 	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
56885732ac8SCy Schubert 	if (ret != 0)
56985732ac8SCy Schubert 		return ret;
57085732ac8SCy Schubert 
57185732ac8SCy Schubert 	ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an,
5725b9c547cSRui Paulo 						 &next_pn);
5735b9c547cSRui Paulo 	ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
5745b9c547cSRui Paulo 							&enabled);
5755b9c547cSRui Paulo 	ret += nss_macsec_secy_rx_sc_anti_replay_window_get(drv->secy_id,
5765b9c547cSRui Paulo 							    channel, &win);
5775b9c547cSRui Paulo 
5785b9c547cSRui Paulo 	if (enabled)
57985732ac8SCy Schubert 		sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
5805b9c547cSRui Paulo 	else
58185732ac8SCy Schubert 		sa->lowest_pn = next_pn;
5825b9c547cSRui Paulo 
58385732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn);
5845b9c547cSRui Paulo 
5855b9c547cSRui Paulo 	return ret;
5865b9c547cSRui Paulo }
5875b9c547cSRui Paulo 
5885b9c547cSRui Paulo 
58985732ac8SCy Schubert static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
5905b9c547cSRui Paulo {
5915b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
5925b9c547cSRui Paulo 	int ret = 0;
59385732ac8SCy Schubert 	u32 channel;
5945b9c547cSRui Paulo 
59585732ac8SCy Schubert 	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
59685732ac8SCy Schubert 	if (ret != 0)
59785732ac8SCy Schubert 		return ret;
5985b9c547cSRui Paulo 
59985732ac8SCy Schubert 	ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an,
60085732ac8SCy Schubert 						 &sa->next_pn);
60185732ac8SCy Schubert 
60285732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn);
6035b9c547cSRui Paulo 
6045b9c547cSRui Paulo 	return ret;
6055b9c547cSRui Paulo }
6065b9c547cSRui Paulo 
6075b9c547cSRui Paulo 
60885732ac8SCy Schubert static int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
6095b9c547cSRui Paulo {
6105b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
6115b9c547cSRui Paulo 	int ret = 0;
61285732ac8SCy Schubert 	u32 channel;
6135b9c547cSRui Paulo 
61485732ac8SCy Schubert 	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
61585732ac8SCy Schubert 	if (ret != 0)
61685732ac8SCy Schubert 		return ret;
6175b9c547cSRui Paulo 
61885732ac8SCy Schubert 	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
61985732ac8SCy Schubert 						 sa->next_pn);
62085732ac8SCy Schubert 
62185732ac8SCy Schubert 	wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn);
6225b9c547cSRui Paulo 
6235b9c547cSRui Paulo 	return ret;
6245b9c547cSRui Paulo }
6255b9c547cSRui Paulo 
6265b9c547cSRui Paulo 
6275b9c547cSRui Paulo static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel)
6285b9c547cSRui Paulo {
6295b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
6305b9c547cSRui Paulo 	int ret = 0;
6315b9c547cSRui Paulo 	u32 sc_ch = 0;
6325b9c547cSRui Paulo 	bool in_use = FALSE;
6335b9c547cSRui Paulo 
6345b9c547cSRui Paulo 	for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
6355b9c547cSRui Paulo 		ret = nss_macsec_secy_rx_sc_in_used_get(drv->secy_id, sc_ch,
6365b9c547cSRui Paulo 							&in_use);
6375b9c547cSRui Paulo 		if (ret)
6385b9c547cSRui Paulo 			continue;
6395b9c547cSRui Paulo 
6405b9c547cSRui Paulo 		if (!in_use) {
6415b9c547cSRui Paulo 			*channel = sc_ch;
6425b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "%s: channel=%d",
6435b9c547cSRui Paulo 				   __func__, *channel);
6445b9c547cSRui Paulo 			return 0;
6455b9c547cSRui Paulo 		}
6465b9c547cSRui Paulo 	}
6475b9c547cSRui Paulo 
6485b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
6495b9c547cSRui Paulo 
6505b9c547cSRui Paulo 	return -1;
6515b9c547cSRui Paulo }
6525b9c547cSRui Paulo 
6535b9c547cSRui Paulo 
65485732ac8SCy Schubert static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc,
6555b9c547cSRui Paulo 					unsigned int conf_offset,
6565b9c547cSRui Paulo 					int validation)
6575b9c547cSRui Paulo {
6585b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
6595b9c547cSRui Paulo 	int ret = 0;
6605b9c547cSRui Paulo 	fal_rx_prc_lut_t entry;
6615b9c547cSRui Paulo 	fal_rx_sc_validate_frame_e vf;
6625b9c547cSRui Paulo 	enum validate_frames validate_frames = validation;
66385732ac8SCy Schubert 	u32 channel;
66485732ac8SCy Schubert 	const u8 *sci_addr = sc->sci.addr;
66585732ac8SCy Schubert 	u16 sci_port = be_to_host16(sc->sci.port);
66685732ac8SCy Schubert 
66785732ac8SCy Schubert 	ret = macsec_qca_get_available_receive_sc(priv, &channel);
66885732ac8SCy Schubert 	if (ret != 0)
66985732ac8SCy Schubert 		return ret;
6705b9c547cSRui Paulo 
6715b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
6725b9c547cSRui Paulo 
6735b9c547cSRui Paulo 	/* rx prc lut */
6745b9c547cSRui Paulo 	os_memset(&entry, 0, sizeof(entry));
6755b9c547cSRui Paulo 
6765b9c547cSRui Paulo 	os_memcpy(entry.sci, sci_addr, ETH_ALEN);
67785732ac8SCy Schubert 	entry.sci[6] = (sci_port >> 8) & 0xff;
67885732ac8SCy Schubert 	entry.sci[7] = sci_port & 0xff;
6795b9c547cSRui Paulo 	entry.sci_mask = 0xf;
6805b9c547cSRui Paulo 
6815b9c547cSRui Paulo 	entry.valid = 1;
6825b9c547cSRui Paulo 	entry.channel = channel;
6835b9c547cSRui Paulo 	entry.action = FAL_RX_PRC_ACTION_PROCESS;
6845b9c547cSRui Paulo 	entry.offset = conf_offset;
6855b9c547cSRui Paulo 
6865b9c547cSRui Paulo 	/* rx validate frame  */
6875b9c547cSRui Paulo 	if (validate_frames == Strict)
6885b9c547cSRui Paulo 		vf = FAL_RX_SC_VALIDATE_FRAME_STRICT;
6895b9c547cSRui Paulo 	else if (validate_frames == Checked)
6905b9c547cSRui Paulo 		vf = FAL_RX_SC_VALIDATE_FRAME_CHECK;
6915b9c547cSRui Paulo 	else
6925b9c547cSRui Paulo 		vf = FAL_RX_SC_VALIDATE_FRAME_DISABLED;
6935b9c547cSRui Paulo 
6945b9c547cSRui Paulo 	ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
6955b9c547cSRui Paulo 	ret += nss_macsec_secy_rx_sc_create(drv->secy_id, channel);
6965b9c547cSRui Paulo 	ret += nss_macsec_secy_rx_sc_validate_frame_set(drv->secy_id, channel,
6975b9c547cSRui Paulo 							vf);
6985b9c547cSRui Paulo 	ret += nss_macsec_secy_rx_sc_replay_protect_set(drv->secy_id, channel,
6995b9c547cSRui Paulo 							drv->replay_protect);
7005b9c547cSRui Paulo 	ret += nss_macsec_secy_rx_sc_anti_replay_window_set(drv->secy_id,
7015b9c547cSRui Paulo 							    channel,
7025b9c547cSRui Paulo 							    drv->replay_window);
7035b9c547cSRui Paulo 
70485732ac8SCy Schubert 	macsec_qca_register_receive_channel(drv, sc, channel);
70585732ac8SCy Schubert 
7065b9c547cSRui Paulo 	return ret;
7075b9c547cSRui Paulo }
7085b9c547cSRui Paulo 
7095b9c547cSRui Paulo 
71085732ac8SCy Schubert static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc)
7115b9c547cSRui Paulo {
7125b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
71385732ac8SCy Schubert 	int ret;
7145b9c547cSRui Paulo 	fal_rx_prc_lut_t entry;
71585732ac8SCy Schubert 	u32 channel;
71685732ac8SCy Schubert 
71785732ac8SCy Schubert 	ret = macsec_qca_lookup_receive_channel(priv, sc, &channel);
71885732ac8SCy Schubert 	if (ret != 0)
71985732ac8SCy Schubert 		return ret;
7205b9c547cSRui Paulo 
7215b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
7225b9c547cSRui Paulo 
7235b9c547cSRui Paulo 	/* rx prc lut */
7245b9c547cSRui Paulo 	os_memset(&entry, 0, sizeof(entry));
7255b9c547cSRui Paulo 
7265b9c547cSRui Paulo 	ret += nss_macsec_secy_rx_sc_del(drv->secy_id, channel);
7275b9c547cSRui Paulo 	ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
7285b9c547cSRui Paulo 
7295b9c547cSRui Paulo 	return ret;
7305b9c547cSRui Paulo }
7315b9c547cSRui Paulo 
7325b9c547cSRui Paulo 
73385732ac8SCy Schubert static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa)
7345b9c547cSRui Paulo {
7355b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
73685732ac8SCy Schubert 	int ret;
7375b9c547cSRui Paulo 	fal_rx_sak_t rx_sak;
7385b9c547cSRui Paulo 	int i = 0;
73985732ac8SCy Schubert 	u32 channel;
74085732ac8SCy Schubert 	fal_rx_prc_lut_t entry;
74185732ac8SCy Schubert 	u32 offset;
74285732ac8SCy Schubert 
74385732ac8SCy Schubert 	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
74485732ac8SCy Schubert 	if (ret != 0)
74585732ac8SCy Schubert 		return ret;
7465b9c547cSRui Paulo 
7475b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
74885732ac8SCy Schubert 		   __func__, channel, sa->an, sa->lowest_pn);
7495b9c547cSRui Paulo 
7505b9c547cSRui Paulo 	os_memset(&rx_sak, 0, sizeof(rx_sak));
75185732ac8SCy Schubert 	rx_sak.sak_len = sa->pkey->key_len;
75285732ac8SCy Schubert 	if (sa->pkey->key_len == SAK_128_LEN) {
7535b9c547cSRui Paulo 		for (i = 0; i < 16; i++)
75485732ac8SCy Schubert 			rx_sak.sak[i] = sa->pkey->key[15 - i];
75585732ac8SCy Schubert 	} else if (sa->pkey->key_len == SAK_256_LEN) {
75685732ac8SCy Schubert 		for (i = 0; i < 16; i++) {
75785732ac8SCy Schubert 			rx_sak.sak1[i] = sa->pkey->key[15 - i];
75885732ac8SCy Schubert 			rx_sak.sak[i] = sa->pkey->key[31 - i];
75985732ac8SCy Schubert 		}
76085732ac8SCy Schubert 	} else {
76185732ac8SCy Schubert 		return -1;
76285732ac8SCy Schubert 	}
7635b9c547cSRui Paulo 
76485732ac8SCy Schubert 	if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
76585732ac8SCy Schubert 		offset = 0;
76685732ac8SCy Schubert 	else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
76785732ac8SCy Schubert 		offset = 30;
76885732ac8SCy Schubert 	else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
76985732ac8SCy Schubert 		offset = 50;
77085732ac8SCy Schubert 	else
77185732ac8SCy Schubert 		return -1;
77285732ac8SCy Schubert 	ret += nss_macsec_secy_rx_prc_lut_get(drv->secy_id, channel, &entry);
77385732ac8SCy Schubert 	entry.offset = offset;
77485732ac8SCy Schubert 	ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
77585732ac8SCy Schubert 	ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an);
77685732ac8SCy Schubert 	ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an,
77785732ac8SCy Schubert 					  &rx_sak);
7785b9c547cSRui Paulo 
7795b9c547cSRui Paulo 	return ret;
7805b9c547cSRui Paulo }
7815b9c547cSRui Paulo 
7825b9c547cSRui Paulo 
78385732ac8SCy Schubert static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa)
7845b9c547cSRui Paulo {
7855b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
78685732ac8SCy Schubert 	int ret;
78785732ac8SCy Schubert 	u32 channel;
7885b9c547cSRui Paulo 
78985732ac8SCy Schubert 	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
79085732ac8SCy Schubert 	if (ret != 0)
79185732ac8SCy Schubert 		return ret;
7925b9c547cSRui Paulo 
79385732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
79485732ac8SCy Schubert 		   sa->an);
79585732ac8SCy Schubert 
79685732ac8SCy Schubert 	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
79785732ac8SCy Schubert 					    TRUE);
7985b9c547cSRui Paulo 
7995b9c547cSRui Paulo 	return ret;
8005b9c547cSRui Paulo }
8015b9c547cSRui Paulo 
8025b9c547cSRui Paulo 
80385732ac8SCy Schubert static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa)
8045b9c547cSRui Paulo {
8055b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
80685732ac8SCy Schubert 	int ret;
80785732ac8SCy Schubert 	u32 channel;
8085b9c547cSRui Paulo 
80985732ac8SCy Schubert 	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
81085732ac8SCy Schubert 	if (ret != 0)
81185732ac8SCy Schubert 		return ret;
8125b9c547cSRui Paulo 
81385732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
81485732ac8SCy Schubert 		   sa->an);
81585732ac8SCy Schubert 
81685732ac8SCy Schubert 	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
81785732ac8SCy Schubert 					    FALSE);
8185b9c547cSRui Paulo 
8195b9c547cSRui Paulo 	return ret;
8205b9c547cSRui Paulo }
8215b9c547cSRui Paulo 
8225b9c547cSRui Paulo 
8235b9c547cSRui Paulo static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
8245b9c547cSRui Paulo {
8255b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
8265b9c547cSRui Paulo 	u32 sc_ch = 0;
8275b9c547cSRui Paulo 	bool in_use = FALSE;
8285b9c547cSRui Paulo 
8295b9c547cSRui Paulo 	for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
83085732ac8SCy Schubert 		if (nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
83185732ac8SCy Schubert 						      &in_use))
8325b9c547cSRui Paulo 			continue;
8335b9c547cSRui Paulo 
8345b9c547cSRui Paulo 		if (!in_use) {
8355b9c547cSRui Paulo 			*channel = sc_ch;
8365b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "%s: channel=%d",
8375b9c547cSRui Paulo 				   __func__, *channel);
8385b9c547cSRui Paulo 			return 0;
8395b9c547cSRui Paulo 		}
8405b9c547cSRui Paulo 	}
8415b9c547cSRui Paulo 
8425b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: no avaiable channel", __func__);
8435b9c547cSRui Paulo 
8445b9c547cSRui Paulo 	return -1;
8455b9c547cSRui Paulo }
8465b9c547cSRui Paulo 
8475b9c547cSRui Paulo 
84885732ac8SCy Schubert static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc,
8495b9c547cSRui Paulo 					 unsigned int conf_offset)
8505b9c547cSRui Paulo {
8515b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
85285732ac8SCy Schubert 	int ret;
8535b9c547cSRui Paulo 	fal_tx_class_lut_t entry;
8545b9c547cSRui Paulo 	u8 psci[ETH_ALEN + 2];
85585732ac8SCy Schubert 	u32 channel;
85685732ac8SCy Schubert 	u16 sci_port = be_to_host16(sc->sci.port);
85785732ac8SCy Schubert 
85885732ac8SCy Schubert 	ret = macsec_qca_get_available_transmit_sc(priv, &channel);
85985732ac8SCy Schubert 	if (ret != 0)
86085732ac8SCy Schubert 		return ret;
8615b9c547cSRui Paulo 
8625b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
8635b9c547cSRui Paulo 
8645b9c547cSRui Paulo 	/* class lut */
8655b9c547cSRui Paulo 	os_memset(&entry, 0, sizeof(entry));
8665b9c547cSRui Paulo 
8675b9c547cSRui Paulo 	entry.valid = 1;
8685b9c547cSRui Paulo 	entry.action = FAL_TX_CLASS_ACTION_FORWARD;
8695b9c547cSRui Paulo 	entry.channel = channel;
8705b9c547cSRui Paulo 
87185732ac8SCy Schubert 	os_memcpy(psci, sc->sci.addr, ETH_ALEN);
87285732ac8SCy Schubert 	psci[6] = (sci_port >> 8) & 0xff;
87385732ac8SCy Schubert 	psci[7] = sci_port & 0xff;
8745b9c547cSRui Paulo 
8755b9c547cSRui Paulo 	ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
8765b9c547cSRui Paulo 	ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
8775b9c547cSRui Paulo 	ret += nss_macsec_secy_tx_sc_protect_set(drv->secy_id, channel,
8785b9c547cSRui Paulo 						 drv->protect_frames);
8795b9c547cSRui Paulo 	ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
8805b9c547cSRui Paulo 								channel,
8815b9c547cSRui Paulo 								conf_offset);
8825b9c547cSRui Paulo 
88385732ac8SCy Schubert 	macsec_qca_register_transmit_channel(drv, sc, channel);
88485732ac8SCy Schubert 
8855b9c547cSRui Paulo 	return ret;
8865b9c547cSRui Paulo }
8875b9c547cSRui Paulo 
8885b9c547cSRui Paulo 
88985732ac8SCy Schubert static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc)
8905b9c547cSRui Paulo {
8915b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
89285732ac8SCy Schubert 	int ret;
8935b9c547cSRui Paulo 	fal_tx_class_lut_t entry;
89485732ac8SCy Schubert 	u32 channel;
89585732ac8SCy Schubert 
89685732ac8SCy Schubert 	ret = macsec_qca_lookup_transmit_channel(priv, sc, &channel);
89785732ac8SCy Schubert 	if (ret != 0)
89885732ac8SCy Schubert 		return ret;
8995b9c547cSRui Paulo 
9005b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
9015b9c547cSRui Paulo 
9025b9c547cSRui Paulo 	/* class lut */
9035b9c547cSRui Paulo 	os_memset(&entry, 0, sizeof(entry));
9045b9c547cSRui Paulo 
9055b9c547cSRui Paulo 	ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
9065b9c547cSRui Paulo 	ret += nss_macsec_secy_tx_sc_del(drv->secy_id, channel);
9075b9c547cSRui Paulo 
9085b9c547cSRui Paulo 	return ret;
9095b9c547cSRui Paulo }
9105b9c547cSRui Paulo 
9115b9c547cSRui Paulo 
91285732ac8SCy Schubert static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa)
9135b9c547cSRui Paulo {
9145b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
91585732ac8SCy Schubert 	int ret;
9165b9c547cSRui Paulo 	u8 tci = 0;
9175b9c547cSRui Paulo 	fal_tx_sak_t tx_sak;
9185b9c547cSRui Paulo 	int i;
91985732ac8SCy Schubert 	u32 channel;
92085732ac8SCy Schubert 	u32 offset;
92185732ac8SCy Schubert 
92285732ac8SCy Schubert 	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
92385732ac8SCy Schubert 	if (ret != 0)
92485732ac8SCy Schubert 		return ret;
9255b9c547cSRui Paulo 
9265b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG,
9275b9c547cSRui Paulo 		   "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
92885732ac8SCy Schubert 		   __func__, channel, sa->an, sa->next_pn, sa->confidentiality);
9295b9c547cSRui Paulo 
9305b9c547cSRui Paulo 	if (drv->always_include_sci)
9315b9c547cSRui Paulo 		tci |= TCI_SC;
9325b9c547cSRui Paulo 	else if (drv->use_es)
9335b9c547cSRui Paulo 		tci |= TCI_ES;
9345b9c547cSRui Paulo 	else if (drv->use_scb)
9355b9c547cSRui Paulo 		tci |= TCI_SCB;
9365b9c547cSRui Paulo 
93785732ac8SCy Schubert 	if (sa->confidentiality)
9385b9c547cSRui Paulo 		tci |= TCI_E | TCI_C;
9395b9c547cSRui Paulo 
9405b9c547cSRui Paulo 	os_memset(&tx_sak, 0, sizeof(tx_sak));
94185732ac8SCy Schubert 	tx_sak.sak_len = sa->pkey->key_len;
94285732ac8SCy Schubert 	if (sa->pkey->key_len == SAK_128_LEN) {
9435b9c547cSRui Paulo 		for (i = 0; i < 16; i++)
94485732ac8SCy Schubert 			tx_sak.sak[i] = sa->pkey->key[15 - i];
94585732ac8SCy Schubert 	} else if (sa->pkey->key_len == SAK_256_LEN) {
94685732ac8SCy Schubert 		for (i = 0; i < 16; i++) {
94785732ac8SCy Schubert 			tx_sak.sak1[i] = sa->pkey->key[15 - i];
94885732ac8SCy Schubert 			tx_sak.sak[i] = sa->pkey->key[31 - i];
94985732ac8SCy Schubert 		}
95085732ac8SCy Schubert 	} else {
95185732ac8SCy Schubert 		return -1;
95285732ac8SCy Schubert 	}
9535b9c547cSRui Paulo 
95485732ac8SCy Schubert 	if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
95585732ac8SCy Schubert 		offset = 0;
95685732ac8SCy Schubert 	else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
95785732ac8SCy Schubert 		offset = 30;
95885732ac8SCy Schubert 	else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
95985732ac8SCy Schubert 		offset = 50;
96085732ac8SCy Schubert 	else
96185732ac8SCy Schubert 		return -1;
96285732ac8SCy Schubert 	ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
96385732ac8SCy Schubert 								channel,
96485732ac8SCy Schubert 								offset);
96585732ac8SCy Schubert 	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
96685732ac8SCy Schubert 						 sa->next_pn);
96785732ac8SCy Schubert 	ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an,
96885732ac8SCy Schubert 					  &tx_sak);
9695b9c547cSRui Paulo 	ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
9705b9c547cSRui Paulo 						 (tci >> 2));
97185732ac8SCy Schubert 	ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an);
9725b9c547cSRui Paulo 
9735b9c547cSRui Paulo 	return ret;
9745b9c547cSRui Paulo }
9755b9c547cSRui Paulo 
9765b9c547cSRui Paulo 
97785732ac8SCy Schubert static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa)
9785b9c547cSRui Paulo {
9795b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
98085732ac8SCy Schubert 	int ret;
98185732ac8SCy Schubert 	u32 channel;
9825b9c547cSRui Paulo 
98385732ac8SCy Schubert 	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
98485732ac8SCy Schubert 	if (ret != 0)
98585732ac8SCy Schubert 		return ret;
9865b9c547cSRui Paulo 
98785732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
98885732ac8SCy Schubert 		   sa->an);
98985732ac8SCy Schubert 
99085732ac8SCy Schubert 	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
99185732ac8SCy Schubert 					    TRUE);
9925b9c547cSRui Paulo 
9935b9c547cSRui Paulo 	return ret;
9945b9c547cSRui Paulo }
9955b9c547cSRui Paulo 
9965b9c547cSRui Paulo 
99785732ac8SCy Schubert static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa)
9985b9c547cSRui Paulo {
9995b9c547cSRui Paulo 	struct macsec_qca_data *drv = priv;
100085732ac8SCy Schubert 	int ret;
100185732ac8SCy Schubert 	u32 channel;
10025b9c547cSRui Paulo 
100385732ac8SCy Schubert 	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
100485732ac8SCy Schubert 	if (ret != 0)
100585732ac8SCy Schubert 		return ret;
10065b9c547cSRui Paulo 
100785732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
100885732ac8SCy Schubert 		   sa->an);
100985732ac8SCy Schubert 
101085732ac8SCy Schubert 	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
101185732ac8SCy Schubert 					    FALSE);
10125b9c547cSRui Paulo 
10135b9c547cSRui Paulo 	return ret;
10145b9c547cSRui Paulo }
10155b9c547cSRui Paulo 
10165b9c547cSRui Paulo 
10175b9c547cSRui Paulo const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
10185b9c547cSRui Paulo 	.name = "macsec_qca",
10195b9c547cSRui Paulo 	.desc = "QCA MACsec Ethernet driver",
102085732ac8SCy Schubert 	.get_ssid = driver_wired_get_ssid,
102185732ac8SCy Schubert 	.get_bssid = driver_wired_get_bssid,
102285732ac8SCy Schubert 	.get_capa = driver_wired_get_capa,
10235b9c547cSRui Paulo 	.init = macsec_qca_init,
10245b9c547cSRui Paulo 	.deinit = macsec_qca_deinit,
1025*206b73d0SCy Schubert 	.hapd_init = macsec_qca_hapd_init,
1026*206b73d0SCy Schubert 	.hapd_deinit = macsec_qca_hapd_deinit,
1027*206b73d0SCy Schubert 	.hapd_send_eapol = macsec_qca_send_eapol,
10285b9c547cSRui Paulo 
10295b9c547cSRui Paulo 	.macsec_init = macsec_qca_macsec_init,
10305b9c547cSRui Paulo 	.macsec_deinit = macsec_qca_macsec_deinit,
103185732ac8SCy Schubert 	.macsec_get_capability = macsec_qca_get_capability,
10325b9c547cSRui Paulo 	.enable_protect_frames = macsec_qca_enable_protect_frames,
10335b9c547cSRui Paulo 	.set_replay_protect = macsec_qca_set_replay_protect,
10345b9c547cSRui Paulo 	.set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
10355b9c547cSRui Paulo 	.enable_controlled_port = macsec_qca_enable_controlled_port,
10365b9c547cSRui Paulo 	.get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
10375b9c547cSRui Paulo 	.get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
10385b9c547cSRui Paulo 	.set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
10395b9c547cSRui Paulo 	.create_receive_sc = macsec_qca_create_receive_sc,
10405b9c547cSRui Paulo 	.delete_receive_sc = macsec_qca_delete_receive_sc,
10415b9c547cSRui Paulo 	.create_receive_sa = macsec_qca_create_receive_sa,
10425b9c547cSRui Paulo 	.enable_receive_sa = macsec_qca_enable_receive_sa,
10435b9c547cSRui Paulo 	.disable_receive_sa = macsec_qca_disable_receive_sa,
10445b9c547cSRui Paulo 	.create_transmit_sc = macsec_qca_create_transmit_sc,
10455b9c547cSRui Paulo 	.delete_transmit_sc = macsec_qca_delete_transmit_sc,
10465b9c547cSRui Paulo 	.create_transmit_sa = macsec_qca_create_transmit_sa,
10475b9c547cSRui Paulo 	.enable_transmit_sa = macsec_qca_enable_transmit_sa,
10485b9c547cSRui Paulo 	.disable_transmit_sa = macsec_qca_disable_transmit_sa,
10495b9c547cSRui Paulo };
1050