]>
Commit | Line | Data |
---|---|---|
8d0dd4ee HW |
1 | /* |
2 | * Wired Ethernet driver interface for QCA MACsec driver | |
3 | * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> | |
4 | * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> | |
5 | * Copyright (c) 2013-2014, Qualcomm Atheros, Inc. | |
6 | * | |
7 | * This software may be distributed under the terms of the BSD license. | |
8 | * See README for more details. | |
9 | */ | |
10 | ||
11 | #include "includes.h" | |
12 | #include <sys/ioctl.h> | |
13 | #include <net/if.h> | |
07a6bfe1 | 14 | #include <inttypes.h> |
8d0dd4ee HW |
15 | #ifdef __linux__ |
16 | #include <netpacket/packet.h> | |
17 | #include <net/if_arp.h> | |
18 | #include <net/if.h> | |
19 | #endif /* __linux__ */ | |
20 | #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) | |
21 | #include <net/if_dl.h> | |
22 | #include <net/if_media.h> | |
23 | #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ | |
24 | #ifdef __sun__ | |
25 | #include <sys/sockio.h> | |
26 | #endif /* __sun__ */ | |
27 | ||
28 | #include "utils/common.h" | |
29 | #include "utils/eloop.h" | |
30 | #include "common/defs.h" | |
31 | #include "common/ieee802_1x_defs.h" | |
7fa5eff8 | 32 | #include "pae/ieee802_1x_kay.h" |
8d0dd4ee HW |
33 | #include "driver.h" |
34 | ||
35 | #include "nss_macsec_secy.h" | |
36 | #include "nss_macsec_secy_rx.h" | |
37 | #include "nss_macsec_secy_tx.h" | |
38 | ||
39 | #define MAXSC 16 | |
40 | ||
41 | /* TCI field definition */ | |
42 | #define TCI_ES 0x40 | |
43 | #define TCI_SC 0x20 | |
44 | #define TCI_SCB 0x10 | |
45 | #define TCI_E 0x08 | |
46 | #define TCI_C 0x04 | |
47 | ||
48 | #ifdef _MSC_VER | |
49 | #pragma pack(push, 1) | |
50 | #endif /* _MSC_VER */ | |
51 | ||
52 | #ifdef _MSC_VER | |
53 | #pragma pack(pop) | |
54 | #endif /* _MSC_VER */ | |
55 | ||
56 | static const u8 pae_group_addr[ETH_ALEN] = | |
57 | { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; | |
58 | ||
59 | struct macsec_qca_data { | |
60 | char ifname[IFNAMSIZ + 1]; | |
61 | u32 secy_id; | |
62 | void *ctx; | |
63 | ||
64 | int sock; /* raw packet socket for driver access */ | |
65 | int pf_sock; | |
66 | int membership, multi, iff_allmulti, iff_up; | |
67 | ||
68 | /* shadow */ | |
69 | Boolean always_include_sci; | |
70 | Boolean use_es; | |
71 | Boolean use_scb; | |
72 | Boolean protect_frames; | |
73 | Boolean replay_protect; | |
74 | u32 replay_window; | |
75 | }; | |
76 | ||
77 | ||
78 | static int macsec_qca_multicast_membership(int sock, int ifindex, | |
79 | const u8 *addr, int add) | |
80 | { | |
81 | #ifdef __linux__ | |
82 | struct packet_mreq mreq; | |
83 | ||
84 | if (sock < 0) | |
85 | return -1; | |
86 | ||
87 | os_memset(&mreq, 0, sizeof(mreq)); | |
88 | mreq.mr_ifindex = ifindex; | |
89 | mreq.mr_type = PACKET_MR_MULTICAST; | |
90 | mreq.mr_alen = ETH_ALEN; | |
91 | os_memcpy(mreq.mr_address, addr, ETH_ALEN); | |
92 | ||
93 | if (setsockopt(sock, SOL_PACKET, | |
94 | add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, | |
95 | &mreq, sizeof(mreq)) < 0) { | |
a193231d | 96 | wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); |
8d0dd4ee HW |
97 | return -1; |
98 | } | |
99 | return 0; | |
100 | #else /* __linux__ */ | |
101 | return -1; | |
102 | #endif /* __linux__ */ | |
103 | } | |
104 | ||
105 | ||
106 | static int macsec_qca_get_ssid(void *priv, u8 *ssid) | |
107 | { | |
108 | ssid[0] = 0; | |
109 | return 0; | |
110 | } | |
111 | ||
112 | ||
113 | static int macsec_qca_get_bssid(void *priv, u8 *bssid) | |
114 | { | |
115 | /* Report PAE group address as the "BSSID" for macsec connection. */ | |
116 | os_memcpy(bssid, pae_group_addr, ETH_ALEN); | |
117 | return 0; | |
118 | } | |
119 | ||
120 | ||
121 | static int macsec_qca_get_capa(void *priv, struct wpa_driver_capa *capa) | |
122 | { | |
123 | os_memset(capa, 0, sizeof(*capa)); | |
124 | capa->flags = WPA_DRIVER_FLAGS_WIRED; | |
125 | return 0; | |
126 | } | |
127 | ||
128 | ||
129 | static int macsec_qca_get_ifflags(const char *ifname, int *flags) | |
130 | { | |
131 | struct ifreq ifr; | |
132 | int s; | |
133 | ||
134 | s = socket(PF_INET, SOCK_DGRAM, 0); | |
135 | if (s < 0) { | |
a193231d | 136 | wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); |
8d0dd4ee HW |
137 | return -1; |
138 | } | |
139 | ||
140 | os_memset(&ifr, 0, sizeof(ifr)); | |
141 | os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
142 | if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { | |
a193231d JM |
143 | wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", |
144 | strerror(errno)); | |
8d0dd4ee HW |
145 | close(s); |
146 | return -1; | |
147 | } | |
148 | close(s); | |
149 | *flags = ifr.ifr_flags & 0xffff; | |
150 | return 0; | |
151 | } | |
152 | ||
153 | ||
154 | static int macsec_qca_set_ifflags(const char *ifname, int flags) | |
155 | { | |
156 | struct ifreq ifr; | |
157 | int s; | |
158 | ||
159 | s = socket(PF_INET, SOCK_DGRAM, 0); | |
160 | if (s < 0) { | |
a193231d | 161 | wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); |
8d0dd4ee HW |
162 | return -1; |
163 | } | |
164 | ||
165 | os_memset(&ifr, 0, sizeof(ifr)); | |
166 | os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
167 | ifr.ifr_flags = flags & 0xffff; | |
168 | if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { | |
a193231d JM |
169 | wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", |
170 | strerror(errno)); | |
8d0dd4ee HW |
171 | close(s); |
172 | return -1; | |
173 | } | |
174 | close(s); | |
175 | return 0; | |
176 | } | |
177 | ||
178 | ||
179 | #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) | |
180 | static int macsec_qca_get_ifstatus(const char *ifname, int *status) | |
181 | { | |
182 | struct ifmediareq ifmr; | |
183 | int s; | |
184 | ||
185 | s = socket(PF_INET, SOCK_DGRAM, 0); | |
186 | if (s < 0) { | |
a193231d | 187 | wpa_print(MSG_ERROR, "socket: %s", strerror(errno)); |
8d0dd4ee HW |
188 | return -1; |
189 | } | |
190 | ||
191 | os_memset(&ifmr, 0, sizeof(ifmr)); | |
192 | os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); | |
193 | if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { | |
a193231d JM |
194 | wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", |
195 | strerror(errno)); | |
8d0dd4ee HW |
196 | close(s); |
197 | return -1; | |
198 | } | |
199 | close(s); | |
200 | *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == | |
201 | (IFM_ACTIVE | IFM_AVALID); | |
202 | ||
203 | return 0; | |
204 | } | |
205 | #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ | |
206 | ||
207 | ||
208 | static int macsec_qca_multi(const char *ifname, const u8 *addr, int add) | |
209 | { | |
210 | struct ifreq ifr; | |
211 | int s; | |
212 | ||
213 | #ifdef __sun__ | |
214 | return -1; | |
215 | #endif /* __sun__ */ | |
216 | ||
217 | s = socket(PF_INET, SOCK_DGRAM, 0); | |
218 | if (s < 0) { | |
a193231d | 219 | wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); |
8d0dd4ee HW |
220 | return -1; |
221 | } | |
222 | ||
223 | os_memset(&ifr, 0, sizeof(ifr)); | |
224 | os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
225 | #ifdef __linux__ | |
226 | ifr.ifr_hwaddr.sa_family = AF_UNSPEC; | |
227 | os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); | |
228 | #endif /* __linux__ */ | |
229 | #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) | |
230 | { | |
231 | struct sockaddr_dl *dlp; | |
232 | dlp = (struct sockaddr_dl *) &ifr.ifr_addr; | |
233 | dlp->sdl_len = sizeof(struct sockaddr_dl); | |
234 | dlp->sdl_family = AF_LINK; | |
235 | dlp->sdl_index = 0; | |
236 | dlp->sdl_nlen = 0; | |
237 | dlp->sdl_alen = ETH_ALEN; | |
238 | dlp->sdl_slen = 0; | |
239 | os_memcpy(LLADDR(dlp), addr, ETH_ALEN); | |
240 | } | |
241 | #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ | |
242 | #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) | |
243 | { | |
244 | struct sockaddr *sap; | |
245 | sap = (struct sockaddr *) &ifr.ifr_addr; | |
246 | sap->sa_len = sizeof(struct sockaddr); | |
247 | sap->sa_family = AF_UNSPEC; | |
248 | os_memcpy(sap->sa_data, addr, ETH_ALEN); | |
249 | } | |
250 | #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ | |
251 | ||
252 | if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { | |
a193231d JM |
253 | wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", |
254 | strerror(errno)); | |
8d0dd4ee HW |
255 | close(s); |
256 | return -1; | |
257 | } | |
258 | close(s); | |
259 | return 0; | |
260 | } | |
261 | ||
262 | ||
263 | static void __macsec_drv_init(struct macsec_qca_data *drv) | |
264 | { | |
265 | int ret = 0; | |
266 | fal_rx_ctl_filt_t rx_ctl_filt; | |
267 | fal_tx_ctl_filt_t tx_ctl_filt; | |
268 | ||
269 | wpa_printf(MSG_INFO, "%s: secy_id=%d", __func__, drv->secy_id); | |
270 | ||
271 | /* Enable Secy and Let EAPoL bypass */ | |
272 | ret = nss_macsec_secy_en_set(drv->secy_id, TRUE); | |
273 | if (ret) | |
274 | wpa_printf(MSG_ERROR, "nss_macsec_secy_en_set: FAIL"); | |
275 | ||
276 | ret = nss_macsec_secy_sc_sa_mapping_mode_set(drv->secy_id, | |
277 | FAL_SC_SA_MAP_1_4); | |
278 | if (ret) | |
279 | wpa_printf(MSG_ERROR, | |
280 | "nss_macsec_secy_sc_sa_mapping_mode_set: FAIL"); | |
281 | ||
282 | os_memset(&rx_ctl_filt, 0, sizeof(rx_ctl_filt)); | |
283 | rx_ctl_filt.bypass = 1; | |
284 | rx_ctl_filt.match_type = IG_CTL_COMPARE_ETHER_TYPE; | |
285 | rx_ctl_filt.match_mask = 0xffff; | |
286 | rx_ctl_filt.ether_type_da_range = 0x888e; | |
287 | ret = nss_macsec_secy_rx_ctl_filt_set(drv->secy_id, 0, &rx_ctl_filt); | |
288 | if (ret) | |
289 | wpa_printf(MSG_ERROR, "nss_macsec_secy_rx_ctl_filt_set: FAIL"); | |
290 | ||
291 | os_memset(&tx_ctl_filt, 0, sizeof(tx_ctl_filt)); | |
292 | tx_ctl_filt.bypass = 1; | |
293 | tx_ctl_filt.match_type = EG_CTL_COMPARE_ETHER_TYPE; | |
294 | tx_ctl_filt.match_mask = 0xffff; | |
295 | tx_ctl_filt.ether_type_da_range = 0x888e; | |
296 | ret = nss_macsec_secy_tx_ctl_filt_set(drv->secy_id, 0, &tx_ctl_filt); | |
297 | if (ret) | |
298 | wpa_printf(MSG_ERROR, "nss_macsec_secy_tx_ctl_filt_set: FAIL"); | |
299 | } | |
300 | ||
301 | ||
302 | static void __macsec_drv_deinit(struct macsec_qca_data *drv) | |
303 | { | |
304 | nss_macsec_secy_en_set(drv->secy_id, FALSE); | |
305 | nss_macsec_secy_rx_sc_del_all(drv->secy_id); | |
306 | nss_macsec_secy_tx_sc_del_all(drv->secy_id); | |
307 | } | |
308 | ||
309 | ||
310 | static void * macsec_qca_init(void *ctx, const char *ifname) | |
311 | { | |
312 | struct macsec_qca_data *drv; | |
313 | int flags; | |
314 | ||
315 | drv = os_zalloc(sizeof(*drv)); | |
316 | if (drv == NULL) | |
317 | return NULL; | |
318 | os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); | |
319 | drv->ctx = ctx; | |
320 | ||
321 | /* Board specific settings */ | |
322 | if (os_memcmp("eth2", drv->ifname, 4) == 0) | |
323 | drv->secy_id = 1; | |
324 | else if (os_memcmp("eth3", drv->ifname, 4) == 0) | |
325 | drv->secy_id = 2; | |
326 | else | |
327 | drv->secy_id = -1; | |
328 | ||
329 | #ifdef __linux__ | |
330 | drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); | |
331 | if (drv->pf_sock < 0) | |
a193231d | 332 | wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); |
8d0dd4ee HW |
333 | #else /* __linux__ */ |
334 | drv->pf_sock = -1; | |
335 | #endif /* __linux__ */ | |
336 | ||
337 | if (macsec_qca_get_ifflags(ifname, &flags) == 0 && | |
338 | !(flags & IFF_UP) && | |
339 | macsec_qca_set_ifflags(ifname, flags | IFF_UP) == 0) { | |
340 | drv->iff_up = 1; | |
341 | } | |
342 | ||
343 | if (macsec_qca_multicast_membership(drv->pf_sock, | |
344 | if_nametoindex(drv->ifname), | |
345 | pae_group_addr, 1) == 0) { | |
346 | wpa_printf(MSG_DEBUG, | |
347 | "%s: Added multicast membership with packet socket", | |
348 | __func__); | |
349 | drv->membership = 1; | |
350 | } else if (macsec_qca_multi(ifname, pae_group_addr, 1) == 0) { | |
351 | wpa_printf(MSG_DEBUG, | |
352 | "%s: Added multicast membership with SIOCADDMULTI", | |
353 | __func__); | |
354 | drv->multi = 1; | |
355 | } else if (macsec_qca_get_ifflags(ifname, &flags) < 0) { | |
356 | wpa_printf(MSG_INFO, "%s: Could not get interface flags", | |
357 | __func__); | |
358 | os_free(drv); | |
359 | return NULL; | |
360 | } else if (flags & IFF_ALLMULTI) { | |
361 | wpa_printf(MSG_DEBUG, | |
362 | "%s: Interface is already configured for multicast", | |
363 | __func__); | |
364 | } else if (macsec_qca_set_ifflags(ifname, flags | IFF_ALLMULTI) < 0) { | |
365 | wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", | |
366 | __func__); | |
367 | os_free(drv); | |
368 | return NULL; | |
369 | } else { | |
370 | wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__); | |
371 | drv->iff_allmulti = 1; | |
372 | } | |
373 | #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) | |
374 | { | |
375 | int status; | |
376 | wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", | |
377 | __func__); | |
378 | while (macsec_qca_get_ifstatus(ifname, &status) == 0 && | |
379 | status == 0) | |
380 | sleep(1); | |
381 | } | |
382 | #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ | |
383 | ||
384 | return drv; | |
385 | } | |
386 | ||
387 | ||
388 | static void macsec_qca_deinit(void *priv) | |
389 | { | |
390 | struct macsec_qca_data *drv = priv; | |
391 | int flags; | |
392 | ||
393 | if (drv->membership && | |
394 | macsec_qca_multicast_membership(drv->pf_sock, | |
395 | if_nametoindex(drv->ifname), | |
396 | pae_group_addr, 0) < 0) { | |
397 | wpa_printf(MSG_DEBUG, | |
398 | "%s: Failed to remove PAE multicast group (PACKET)", | |
399 | __func__); | |
400 | } | |
401 | ||
402 | if (drv->multi && | |
403 | macsec_qca_multi(drv->ifname, pae_group_addr, 0) < 0) { | |
404 | wpa_printf(MSG_DEBUG, | |
405 | "%s: Failed to remove PAE multicast group (SIOCDELMULTI)", | |
406 | __func__); | |
407 | } | |
408 | ||
409 | if (drv->iff_allmulti && | |
410 | (macsec_qca_get_ifflags(drv->ifname, &flags) < 0 || | |
411 | macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) { | |
412 | wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", | |
413 | __func__); | |
414 | } | |
415 | ||
416 | if (drv->iff_up && | |
417 | macsec_qca_get_ifflags(drv->ifname, &flags) == 0 && | |
418 | (flags & IFF_UP) && | |
419 | macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { | |
420 | wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", | |
421 | __func__); | |
422 | } | |
423 | ||
424 | if (drv->pf_sock != -1) | |
425 | close(drv->pf_sock); | |
426 | ||
427 | os_free(drv); | |
428 | } | |
429 | ||
430 | ||
431 | static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params) | |
432 | { | |
433 | struct macsec_qca_data *drv = priv; | |
434 | ||
435 | drv->always_include_sci = params->always_include_sci; | |
436 | drv->use_es = params->use_es; | |
437 | drv->use_scb = params->use_scb; | |
438 | ||
439 | wpa_printf(MSG_DEBUG, "%s: es=%d, scb=%d, sci=%d", | |
440 | __func__, drv->use_es, drv->use_scb, | |
441 | drv->always_include_sci); | |
442 | ||
443 | __macsec_drv_init(drv); | |
444 | ||
445 | return 0; | |
446 | } | |
447 | ||
448 | ||
449 | static int macsec_qca_macsec_deinit(void *priv) | |
450 | { | |
451 | struct macsec_qca_data *drv = priv; | |
452 | ||
453 | wpa_printf(MSG_DEBUG, "%s", __func__); | |
454 | ||
455 | __macsec_drv_deinit(drv); | |
456 | ||
457 | return 0; | |
458 | } | |
459 | ||
460 | ||
a25e4efc SD |
461 | static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap) |
462 | { | |
463 | wpa_printf(MSG_DEBUG, "%s", __func__); | |
464 | ||
465 | *cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50; | |
466 | ||
467 | return 0; | |
468 | } | |
469 | ||
470 | ||
8d0dd4ee HW |
471 | static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled) |
472 | { | |
473 | struct macsec_qca_data *drv = priv; | |
474 | int ret = 0; | |
475 | ||
476 | wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); | |
477 | ||
478 | drv->protect_frames = enabled; | |
479 | ||
480 | return ret; | |
481 | } | |
482 | ||
483 | ||
484 | static int macsec_qca_set_replay_protect(void *priv, Boolean enabled, | |
485 | unsigned int window) | |
486 | { | |
487 | struct macsec_qca_data *drv = priv; | |
488 | int ret = 0; | |
489 | ||
490 | wpa_printf(MSG_DEBUG, "%s: enabled=%d, win=%u", | |
491 | __func__, enabled, window); | |
492 | ||
493 | drv->replay_protect = enabled; | |
494 | drv->replay_window = window; | |
495 | ||
496 | return ret; | |
497 | } | |
498 | ||
499 | ||
07a6bfe1 | 500 | static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs) |
8d0dd4ee | 501 | { |
07a6bfe1 SD |
502 | if (cs != CS_ID_GCM_AES_128) { |
503 | wpa_printf(MSG_ERROR, | |
504 | "%s: NOT supported CipherSuite: %016" PRIx64, | |
505 | __func__, cs); | |
8d0dd4ee HW |
506 | return -1; |
507 | } | |
508 | ||
509 | /* Support default Cipher Suite 0080020001000001 (GCM-AES-128) */ | |
510 | wpa_printf(MSG_DEBUG, "%s: default support aes-gcm-128", __func__); | |
511 | ||
512 | return 0; | |
513 | } | |
514 | ||
515 | ||
516 | static int macsec_qca_enable_controlled_port(void *priv, Boolean enabled) | |
517 | { | |
518 | struct macsec_qca_data *drv = priv; | |
519 | int ret = 0; | |
520 | ||
521 | wpa_printf(MSG_DEBUG, "%s: enable=%d", __func__, enabled); | |
522 | ||
523 | ret += nss_macsec_secy_controlled_port_en_set(drv->secy_id, enabled); | |
524 | ||
525 | return ret; | |
526 | } | |
527 | ||
528 | ||
7fa5eff8 | 529 | static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa) |
8d0dd4ee HW |
530 | { |
531 | struct macsec_qca_data *drv = priv; | |
532 | int ret = 0; | |
533 | u32 next_pn = 0; | |
534 | bool enabled = FALSE; | |
535 | u32 win; | |
7fa5eff8 | 536 | u32 channel = sa->sc->channel; |
8d0dd4ee | 537 | |
7fa5eff8 | 538 | ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an, |
8d0dd4ee HW |
539 | &next_pn); |
540 | ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel, | |
541 | &enabled); | |
542 | ret += nss_macsec_secy_rx_sc_anti_replay_window_get(drv->secy_id, | |
543 | channel, &win); | |
544 | ||
545 | if (enabled) | |
7fa5eff8 | 546 | sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1; |
8d0dd4ee | 547 | else |
7fa5eff8 | 548 | sa->lowest_pn = next_pn; |
8d0dd4ee | 549 | |
7fa5eff8 | 550 | wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn); |
8d0dd4ee HW |
551 | |
552 | return ret; | |
553 | } | |
554 | ||
555 | ||
7fa5eff8 | 556 | static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa) |
8d0dd4ee HW |
557 | { |
558 | struct macsec_qca_data *drv = priv; | |
559 | int ret = 0; | |
7fa5eff8 | 560 | u32 channel = sa->sc->channel; |
8d0dd4ee | 561 | |
7fa5eff8 SD |
562 | ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an, |
563 | &sa->next_pn); | |
8d0dd4ee | 564 | |
7fa5eff8 | 565 | wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn); |
8d0dd4ee HW |
566 | |
567 | return ret; | |
568 | } | |
569 | ||
570 | ||
7fa5eff8 | 571 | int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa) |
8d0dd4ee HW |
572 | { |
573 | struct macsec_qca_data *drv = priv; | |
574 | int ret = 0; | |
7fa5eff8 | 575 | u32 channel = sa->sc->channel; |
8d0dd4ee | 576 | |
8d0dd4ee | 577 | |
7fa5eff8 SD |
578 | ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an, |
579 | sa->next_pn); | |
580 | ||
581 | wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn); | |
8d0dd4ee HW |
582 | |
583 | return ret; | |
584 | } | |
585 | ||
586 | ||
587 | static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel) | |
588 | { | |
589 | struct macsec_qca_data *drv = priv; | |
590 | int ret = 0; | |
591 | u32 sc_ch = 0; | |
592 | bool in_use = FALSE; | |
593 | ||
594 | for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) { | |
595 | ret = nss_macsec_secy_rx_sc_in_used_get(drv->secy_id, sc_ch, | |
596 | &in_use); | |
597 | if (ret) | |
598 | continue; | |
599 | ||
600 | if (!in_use) { | |
601 | *channel = sc_ch; | |
602 | wpa_printf(MSG_DEBUG, "%s: channel=%d", | |
603 | __func__, *channel); | |
604 | return 0; | |
605 | } | |
606 | } | |
607 | ||
608 | wpa_printf(MSG_DEBUG, "%s: no available channel", __func__); | |
609 | ||
610 | return -1; | |
611 | } | |
612 | ||
613 | ||
5f5ca284 | 614 | static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc, |
8d0dd4ee HW |
615 | unsigned int conf_offset, |
616 | int validation) | |
617 | { | |
618 | struct macsec_qca_data *drv = priv; | |
619 | int ret = 0; | |
620 | fal_rx_prc_lut_t entry; | |
621 | fal_rx_sc_validate_frame_e vf; | |
622 | enum validate_frames validate_frames = validation; | |
5f5ca284 SD |
623 | u32 channel = sc->channel; |
624 | const u8 *sci_addr = sc->sci.addr; | |
625 | u16 sci_port = be_to_host16(sc->sci.port); | |
8d0dd4ee HW |
626 | |
627 | wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); | |
628 | ||
629 | /* rx prc lut */ | |
630 | os_memset(&entry, 0, sizeof(entry)); | |
631 | ||
632 | os_memcpy(entry.sci, sci_addr, ETH_ALEN); | |
633 | entry.sci[6] = (sci_port >> 8) & 0xf; | |
634 | entry.sci[7] = sci_port & 0xf; | |
635 | entry.sci_mask = 0xf; | |
636 | ||
637 | entry.valid = 1; | |
638 | entry.channel = channel; | |
639 | entry.action = FAL_RX_PRC_ACTION_PROCESS; | |
640 | entry.offset = conf_offset; | |
641 | ||
642 | /* rx validate frame */ | |
643 | if (validate_frames == Strict) | |
644 | vf = FAL_RX_SC_VALIDATE_FRAME_STRICT; | |
645 | else if (validate_frames == Checked) | |
646 | vf = FAL_RX_SC_VALIDATE_FRAME_CHECK; | |
647 | else | |
648 | vf = FAL_RX_SC_VALIDATE_FRAME_DISABLED; | |
649 | ||
650 | ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry); | |
651 | ret += nss_macsec_secy_rx_sc_create(drv->secy_id, channel); | |
652 | ret += nss_macsec_secy_rx_sc_validate_frame_set(drv->secy_id, channel, | |
653 | vf); | |
654 | ret += nss_macsec_secy_rx_sc_replay_protect_set(drv->secy_id, channel, | |
655 | drv->replay_protect); | |
656 | ret += nss_macsec_secy_rx_sc_anti_replay_window_set(drv->secy_id, | |
657 | channel, | |
658 | drv->replay_window); | |
659 | ||
660 | return ret; | |
661 | } | |
662 | ||
663 | ||
5f5ca284 | 664 | static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc) |
8d0dd4ee HW |
665 | { |
666 | struct macsec_qca_data *drv = priv; | |
667 | int ret = 0; | |
668 | fal_rx_prc_lut_t entry; | |
5f5ca284 | 669 | u32 channel = sc->channel; |
8d0dd4ee HW |
670 | |
671 | wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); | |
672 | ||
673 | /* rx prc lut */ | |
674 | os_memset(&entry, 0, sizeof(entry)); | |
675 | ||
676 | ret += nss_macsec_secy_rx_sc_del(drv->secy_id, channel); | |
677 | ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry); | |
678 | ||
679 | return ret; | |
680 | } | |
681 | ||
682 | ||
cecdecdb | 683 | static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa) |
8d0dd4ee HW |
684 | { |
685 | struct macsec_qca_data *drv = priv; | |
686 | int ret = 0; | |
687 | fal_rx_sak_t rx_sak; | |
688 | int i = 0; | |
cecdecdb | 689 | u32 channel = sa->sc->channel; |
8d0dd4ee HW |
690 | |
691 | wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x", | |
cecdecdb | 692 | __func__, channel, sa->an, sa->lowest_pn); |
8d0dd4ee HW |
693 | |
694 | os_memset(&rx_sak, 0, sizeof(rx_sak)); | |
695 | for (i = 0; i < 16; i++) | |
cecdecdb | 696 | rx_sak.sak[i] = sa->pkey->key[15 - i]; |
8d0dd4ee | 697 | |
cecdecdb SD |
698 | ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an); |
699 | ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an, | |
700 | &rx_sak); | |
8d0dd4ee HW |
701 | |
702 | return ret; | |
703 | } | |
704 | ||
705 | ||
cecdecdb | 706 | static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa) |
8d0dd4ee HW |
707 | { |
708 | struct macsec_qca_data *drv = priv; | |
709 | int ret = 0; | |
cecdecdb | 710 | u32 channel = sa->sc->channel; |
8d0dd4ee | 711 | |
8d0dd4ee | 712 | |
cecdecdb SD |
713 | wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, |
714 | sa->an); | |
715 | ||
716 | ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an, | |
717 | TRUE); | |
8d0dd4ee HW |
718 | |
719 | return ret; | |
720 | } | |
721 | ||
722 | ||
cecdecdb | 723 | static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa) |
8d0dd4ee HW |
724 | { |
725 | struct macsec_qca_data *drv = priv; | |
726 | int ret = 0; | |
cecdecdb | 727 | u32 channel = sa->sc->channel; |
8d0dd4ee | 728 | |
cecdecdb SD |
729 | wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, |
730 | sa->an); | |
8d0dd4ee | 731 | |
cecdecdb SD |
732 | ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an, |
733 | FALSE); | |
8d0dd4ee HW |
734 | |
735 | return ret; | |
736 | } | |
737 | ||
738 | ||
739 | static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel) | |
740 | { | |
741 | struct macsec_qca_data *drv = priv; | |
742 | int ret = 0; | |
743 | u32 sc_ch = 0; | |
744 | bool in_use = FALSE; | |
745 | ||
746 | for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) { | |
747 | ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch, | |
748 | &in_use); | |
749 | if (ret) | |
750 | continue; | |
751 | ||
752 | if (!in_use) { | |
753 | *channel = sc_ch; | |
754 | wpa_printf(MSG_DEBUG, "%s: channel=%d", | |
755 | __func__, *channel); | |
756 | return 0; | |
757 | } | |
758 | } | |
759 | ||
760 | wpa_printf(MSG_DEBUG, "%s: no avaiable channel", __func__); | |
761 | ||
762 | return -1; | |
763 | } | |
764 | ||
765 | ||
8ebfc7c2 | 766 | static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc, |
8d0dd4ee HW |
767 | unsigned int conf_offset) |
768 | { | |
769 | struct macsec_qca_data *drv = priv; | |
770 | int ret = 0; | |
771 | fal_tx_class_lut_t entry; | |
772 | u8 psci[ETH_ALEN + 2]; | |
8ebfc7c2 | 773 | u32 channel = sc->channel; |
8d0dd4ee HW |
774 | |
775 | wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); | |
776 | ||
777 | /* class lut */ | |
778 | os_memset(&entry, 0, sizeof(entry)); | |
779 | ||
780 | entry.valid = 1; | |
781 | entry.action = FAL_TX_CLASS_ACTION_FORWARD; | |
782 | entry.channel = channel; | |
783 | ||
8ebfc7c2 SD |
784 | os_memcpy(psci, sc->sci.addr, ETH_ALEN); |
785 | psci[6] = (sc->sci.port >> 8) & 0xf; | |
786 | psci[7] = sc->sci.port & 0xf; | |
8d0dd4ee HW |
787 | |
788 | ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry); | |
789 | ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8); | |
790 | ret += nss_macsec_secy_tx_sc_protect_set(drv->secy_id, channel, | |
791 | drv->protect_frames); | |
792 | ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id, | |
793 | channel, | |
794 | conf_offset); | |
795 | ||
796 | return ret; | |
797 | } | |
798 | ||
799 | ||
8ebfc7c2 | 800 | static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc) |
8d0dd4ee HW |
801 | { |
802 | struct macsec_qca_data *drv = priv; | |
803 | int ret = 0; | |
804 | fal_tx_class_lut_t entry; | |
8ebfc7c2 | 805 | u32 channel = sc->channel; |
8d0dd4ee HW |
806 | |
807 | wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); | |
808 | ||
809 | /* class lut */ | |
810 | os_memset(&entry, 0, sizeof(entry)); | |
811 | ||
812 | ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry); | |
813 | ret += nss_macsec_secy_tx_sc_del(drv->secy_id, channel); | |
814 | ||
815 | return ret; | |
816 | } | |
817 | ||
818 | ||
909c1b98 | 819 | static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa) |
8d0dd4ee HW |
820 | { |
821 | struct macsec_qca_data *drv = priv; | |
822 | int ret = 0; | |
823 | u8 tci = 0; | |
824 | fal_tx_sak_t tx_sak; | |
825 | int i; | |
909c1b98 | 826 | u32 channel = sa->sc->channel; |
8d0dd4ee HW |
827 | |
828 | wpa_printf(MSG_DEBUG, | |
829 | "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d", | |
909c1b98 | 830 | __func__, channel, sa->an, sa->next_pn, sa->confidentiality); |
8d0dd4ee HW |
831 | |
832 | if (drv->always_include_sci) | |
833 | tci |= TCI_SC; | |
834 | else if (drv->use_es) | |
835 | tci |= TCI_ES; | |
836 | else if (drv->use_scb) | |
837 | tci |= TCI_SCB; | |
838 | ||
909c1b98 | 839 | if (sa->confidentiality) |
8d0dd4ee HW |
840 | tci |= TCI_E | TCI_C; |
841 | ||
842 | os_memset(&tx_sak, 0, sizeof(tx_sak)); | |
843 | for (i = 0; i < 16; i++) | |
909c1b98 | 844 | tx_sak.sak[i] = sa->pkey->key[15 - i]; |
8d0dd4ee | 845 | |
909c1b98 SD |
846 | ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an, |
847 | sa->next_pn); | |
848 | ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an, | |
849 | &tx_sak); | |
8d0dd4ee HW |
850 | ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel, |
851 | (tci >> 2)); | |
909c1b98 | 852 | ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an); |
8d0dd4ee HW |
853 | |
854 | return ret; | |
855 | } | |
856 | ||
857 | ||
909c1b98 | 858 | static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa) |
8d0dd4ee HW |
859 | { |
860 | struct macsec_qca_data *drv = priv; | |
861 | int ret = 0; | |
909c1b98 | 862 | u32 channel = sa->sc->channel; |
8d0dd4ee | 863 | |
8d0dd4ee | 864 | |
909c1b98 SD |
865 | wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, |
866 | sa->an); | |
867 | ||
868 | ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an, | |
869 | TRUE); | |
8d0dd4ee HW |
870 | |
871 | return ret; | |
872 | } | |
873 | ||
874 | ||
909c1b98 | 875 | static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa) |
8d0dd4ee HW |
876 | { |
877 | struct macsec_qca_data *drv = priv; | |
878 | int ret = 0; | |
909c1b98 | 879 | u32 channel = sa->sc->channel; |
8d0dd4ee | 880 | |
909c1b98 SD |
881 | wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, |
882 | sa->an); | |
8d0dd4ee | 883 | |
909c1b98 SD |
884 | ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an, |
885 | FALSE); | |
8d0dd4ee HW |
886 | |
887 | return ret; | |
888 | } | |
889 | ||
890 | ||
891 | const struct wpa_driver_ops wpa_driver_macsec_qca_ops = { | |
892 | .name = "macsec_qca", | |
893 | .desc = "QCA MACsec Ethernet driver", | |
894 | .get_ssid = macsec_qca_get_ssid, | |
895 | .get_bssid = macsec_qca_get_bssid, | |
896 | .get_capa = macsec_qca_get_capa, | |
897 | .init = macsec_qca_init, | |
898 | .deinit = macsec_qca_deinit, | |
899 | ||
900 | .macsec_init = macsec_qca_macsec_init, | |
901 | .macsec_deinit = macsec_qca_macsec_deinit, | |
a25e4efc | 902 | .macsec_get_capability = macsec_qca_get_capability, |
8d0dd4ee HW |
903 | .enable_protect_frames = macsec_qca_enable_protect_frames, |
904 | .set_replay_protect = macsec_qca_set_replay_protect, | |
905 | .set_current_cipher_suite = macsec_qca_set_current_cipher_suite, | |
906 | .enable_controlled_port = macsec_qca_enable_controlled_port, | |
907 | .get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn, | |
908 | .get_transmit_next_pn = macsec_qca_get_transmit_next_pn, | |
909 | .set_transmit_next_pn = macsec_qca_set_transmit_next_pn, | |
910 | .get_available_receive_sc = macsec_qca_get_available_receive_sc, | |
911 | .create_receive_sc = macsec_qca_create_receive_sc, | |
912 | .delete_receive_sc = macsec_qca_delete_receive_sc, | |
913 | .create_receive_sa = macsec_qca_create_receive_sa, | |
914 | .enable_receive_sa = macsec_qca_enable_receive_sa, | |
915 | .disable_receive_sa = macsec_qca_disable_receive_sa, | |
916 | .get_available_transmit_sc = macsec_qca_get_available_transmit_sc, | |
917 | .create_transmit_sc = macsec_qca_create_transmit_sc, | |
918 | .delete_transmit_sc = macsec_qca_delete_transmit_sc, | |
919 | .create_transmit_sa = macsec_qca_create_transmit_sa, | |
920 | .enable_transmit_sa = macsec_qca_enable_transmit_sa, | |
921 | .disable_transmit_sa = macsec_qca_disable_transmit_sa, | |
922 | }; |