Skip to content

Commit 27b8caa

Browse files
feat: WPA3 personal support (#5586)
* Removed filter for WPA3 security Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> * First implementation of WPA3 personal station mode Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> * Removed OWE filtering Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> * Added support for wpa2/wpa3 Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> * Added tests for WPA3 configuration Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> * Added experimetal tag to WPA3 feature Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> * Fixed wpa2/wpa3 selection Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> * Added experimetal tag to GwtWifiSecurity Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> * Fixed indentation Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> * Update kura/org.eclipse.kura.web2/src/main/resources/org/eclipse/kura/web/client/messages/Messages.properties Co-authored-by: Mattia Dal Ben <mattdibi@users.noreply.github.com> --------- Signed-off-by: pierantoniomerlino <pierantonio.merlino@eurotech.com> Co-authored-by: Mattia Dal Ben <mattdibi@users.noreply.github.com>
1 parent eba94ed commit 27b8caa

11 files changed

Lines changed: 185 additions & 41 deletions

File tree

kura/org.eclipse.kura.api/src/main/java/org/eclipse/kura/net/wifi/WifiSecurity.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2011, 2020 Eurotech and/or its affiliates and others
2+
* Copyright (c) 2011, 2024 Eurotech and/or its affiliates and others
33
*
44
* This program and the accompanying materials are made
55
* available under the terms of the Eclipse Public License 2.0
@@ -50,7 +50,17 @@ public enum WifiSecurity {
5050
/** Supports WPA and WPA2 encryption. */
5151
SECURITY_WPA_WPA2(0x4000),
5252
/** Supports WPA2 WPA3 enterprise. */
53-
SECURITY_WPA2_WPA3_ENTERPRISE(0x8000);
53+
SECURITY_WPA2_WPA3_ENTERPRISE(0x8000),
54+
/**
55+
* @since 3.0
56+
* Supports WPA3 encryption.
57+
*/
58+
SECURITY_WPA3(0x10000),
59+
/**
60+
* @since 3.0
61+
* Supports WPA2 and WPA3 encryption.
62+
*/
63+
SECURITY_WPA2_WPA3(0x20000);
5464

5565
private int code;
5666

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2023 Eurotech and/or its affiliates and others
2+
* Copyright (c) 2023, 2024 Eurotech and/or its affiliates and others
33
*
44
* This program and the accompanying materials are made
55
* available under the terms of the Eclipse Public License 2.0
@@ -18,29 +18,35 @@ public enum KuraWifiSecurityType {
1818
SECURITY_WEP,
1919
SECURITY_WPA,
2020
SECURITY_WPA2,
21+
SECURITY_WPA3,
2122
SECURITY_WPA2_WPA3_ENTERPRISE,
22-
SECURITY_WPA_WPA2;
23+
SECURITY_WPA_WPA2,
24+
SECURITY_WPA2_WPA3;
2325

2426
public static KuraWifiSecurityType fromString(String securityType) {
2527
if (securityType == null || securityType.isEmpty()) {
2628
throw new IllegalArgumentException("Invalid security type: null or empty string are not supported");
2729
}
2830

2931
switch (securityType) {
30-
case "NONE":
31-
return KuraWifiSecurityType.SECURITY_NONE;
32-
case "SECURITY_WEP":
33-
return KuraWifiSecurityType.SECURITY_WEP;
34-
case "SECURITY_WPA":
35-
return KuraWifiSecurityType.SECURITY_WPA;
36-
case "SECURITY_WPA2":
37-
return KuraWifiSecurityType.SECURITY_WPA2;
38-
case "SECURITY_WPA2_WPA3_ENTERPRISE":
39-
return KuraWifiSecurityType.SECURITY_WPA2_WPA3_ENTERPRISE;
40-
case "SECURITY_WPA_WPA2":
41-
return KuraWifiSecurityType.SECURITY_WPA_WPA2;
42-
default:
43-
throw new IllegalArgumentException("Invalid security type: " + securityType);
32+
case "NONE":
33+
return KuraWifiSecurityType.SECURITY_NONE;
34+
case "SECURITY_WEP":
35+
return KuraWifiSecurityType.SECURITY_WEP;
36+
case "SECURITY_WPA":
37+
return KuraWifiSecurityType.SECURITY_WPA;
38+
case "SECURITY_WPA2":
39+
return KuraWifiSecurityType.SECURITY_WPA2;
40+
case "SECURITY_WPA3":
41+
return KuraWifiSecurityType.SECURITY_WPA3;
42+
case "SECURITY_WPA2_WPA3_ENTERPRISE":
43+
return KuraWifiSecurityType.SECURITY_WPA2_WPA3_ENTERPRISE;
44+
case "SECURITY_WPA_WPA2":
45+
return KuraWifiSecurityType.SECURITY_WPA_WPA2;
46+
case "SECURITY_WPA2_WPA3":
47+
return KuraWifiSecurityType.SECURITY_WPA2_WPA3;
48+
default:
49+
throw new IllegalArgumentException("Invalid security type: " + securityType);
4450
}
4551
}
4652
}

kura/org.eclipse.kura.nm/src/main/java/org/eclipse/kura/nm/configuration/NMSettingsConverter.java

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,7 @@ public static Map<String, Variant<?>> build8021xSettings(NetworkProperties props
158158
create8021xTls(props, deviceId, settings);
159159
break;
160160
default:
161-
throw new IllegalArgumentException(
162-
String.format("Security type 802-1x EAP \"%s\" is not supported.", eap));
161+
throw new IllegalArgumentException(String.format("Security type 802-1x EAP \"%s\" is not supported.", eap));
163162
}
164163

165164
if (!phase2.isPresent()) {
@@ -174,7 +173,7 @@ public static Map<String, Variant<?>> build8021xSettings(NetworkProperties props
174173
break;
175174
default:
176175
throw new IllegalArgumentException(
177-
String.format("Security type 802-1x InnerAuth (Phase2) \"%s\" is not supported.", phase2));
176+
String.format("Security type 802-1x InnerAuth (Phase2) \"%s\" is not supported.", phase2));
178177
}
179178

180179
return settings;
@@ -386,8 +385,7 @@ private static void configureIp6Mtu(NetworkProperties props, String deviceId, Se
386385
}
387386

388387
private static void configureIp6Wan(NetworkProperties props, String deviceId, Map<String, Variant<?>> settings) {
389-
Optional<List<String>> dnsServers = props.getOptStringList("net.interface.%s.config.ip6.dnsServers",
390-
deviceId);
388+
Optional<List<String>> dnsServers = props.getOptStringList("net.interface.%s.config.ip6.dnsServers", deviceId);
391389

392390
dnsServers.ifPresent(value -> {
393391
settings.put("dns", new Variant<>(convertIp6(value), "aay"));
@@ -429,14 +427,14 @@ private static void configureIp6MethodAuto(NetworkProperties props, String devic
429427
Map<String, Variant<?>> settings) {
430428
settings.put(NM_SETTINGS_IPV6_METHOD, new Variant<>("auto"));
431429

432-
Optional<String> addressGenerationMode = props.getOpt(String.class,
433-
"net.interface.%s.config.ip6.addr.gen.mode", deviceId);
430+
Optional<String> addressGenerationMode = props.getOpt(String.class, "net.interface.%s.config.ip6.addr.gen.mode",
431+
deviceId);
434432

435433
addressGenerationMode.ifPresent(value -> {
436434
KuraIp6AddressGenerationMode ipv6AddressGenerationMode = KuraIp6AddressGenerationMode
437435
.fromString(addressGenerationMode.get());
438-
settings.put("addr-gen-mode", new Variant<>(KuraIp6AddressGenerationMode
439-
.toNMSettingIP6ConfigAddrGenMode(ipv6AddressGenerationMode).toInt32()));
436+
settings.put("addr-gen-mode", new Variant<>(
437+
KuraIp6AddressGenerationMode.toNMSettingIP6ConfigAddrGenMode(ipv6AddressGenerationMode).toInt32()));
440438
});
441439

442440
Optional<String> privacy = props.getOpt(String.class, "net.interface.%s.config.ip6.privacy", deviceId);
@@ -454,8 +452,7 @@ private static KuraIp6ConfigurationMethod getIp6ConfigMethod(NetworkProperties p
454452
ip6ConfigMethod = KuraIp6ConfigurationMethod
455453
.fromString(props.get(String.class, "net.interface.%s.config.ip6.address.method", deviceId));
456454
} catch (NoSuchElementException e) {
457-
logger.warn("IPv6 address method property not found. Using default value: {}",
458-
ip6ConfigMethod);
455+
logger.warn("IPv6 address method property not found. Using default value: {}", ip6ConfigMethod);
459456
}
460457
return ip6ConfigMethod;
461458
}
@@ -502,11 +499,14 @@ public static Map<String, Variant<?>> build80211WirelessSecuritySettings(Network
502499
case SECURITY_WPA2:
503500
case SECURITY_WPA_WPA2:
504501
return createWPAWPA2Settings(props, deviceId, propMode);
502+
case SECURITY_WPA3:
503+
return createWPA3Settings(props, deviceId, propMode);
504+
case SECURITY_WPA2_WPA3:
505+
return createWPA2WPA3Settings(props, deviceId, propMode);
505506
case SECURITY_WPA2_WPA3_ENTERPRISE:
506507
return createWPA2WPA3EnterpriseSettings();
507508
default:
508-
throw new IllegalArgumentException(
509-
String.format("Security type \"%s\" is not supported.", securityType));
509+
throw new IllegalArgumentException(String.format("Security type \"%s\" is not supported.", securityType));
510510
}
511511
}
512512

@@ -527,9 +527,38 @@ private static Map<String, Variant<?>> createWEPSettings(NetworkProperties props
527527

528528
private static Map<String, Variant<?>> createWPAWPA2Settings(NetworkProperties props, String deviceId,
529529
String propMode) {
530-
Map<String, Variant<?>> settings = new HashMap<>();
530+
Map<String, Variant<?>> settings = createWifiSettings(props, deviceId, propMode);
531+
532+
settings.put(NM_SETTINGS_80211_KEY_MANAGEMENT, new Variant<>("wpa-psk"));
533+
534+
return settings;
535+
}
536+
537+
private static Map<String, Variant<?>> createWPA3Settings(NetworkProperties props, String deviceId,
538+
String propMode) {
539+
Map<String, Variant<?>> settings = createWifiSettings(props, deviceId, propMode);
540+
541+
settings.put(NM_SETTINGS_80211_KEY_MANAGEMENT, new Variant<>("sae"));
542+
// Set PMF (Protected Management Frames) as required
543+
settings.put("pmf", new Variant<>(new UInt32(3)));
544+
545+
return settings;
546+
}
547+
548+
private static Map<String, Variant<?>> createWPA2WPA3Settings(NetworkProperties props, String deviceId,
549+
String propMode) {
550+
Map<String, Variant<?>> settings = createWifiSettings(props, deviceId, propMode);
531551

532552
settings.put(NM_SETTINGS_80211_KEY_MANAGEMENT, new Variant<>("wpa-psk"));
553+
// Set PMF (Protected Management Frames) as optional
554+
settings.put("pmf", new Variant<>(new UInt32(2)));
555+
556+
return settings;
557+
}
558+
559+
private static Map<String, Variant<?>> createWifiSettings(NetworkProperties props, String deviceId,
560+
String propMode) {
561+
Map<String, Variant<?>> settings = new HashMap<>();
533562

534563
String psk = props
535564
.get(Password.class, "net.interface.%s.config.wifi.%s.passphrase", deviceId, propMode.toLowerCase())
@@ -808,6 +837,8 @@ private static List<String> wifiProtoConvert(KuraWifiSecurityType securityType)
808837
case SECURITY_WPA:
809838
return Arrays.asList("wpa");
810839
case SECURITY_WPA2:
840+
case SECURITY_WPA2_WPA3:
841+
case SECURITY_WPA3:
811842
return Arrays.asList("rsn");
812843
case SECURITY_WPA_WPA2:
813844
return Arrays.asList();
@@ -829,8 +860,7 @@ private static String connectionTypeConvert(NMDeviceType deviceType) {
829860
// ... WIP
830861
default:
831862
throw new IllegalArgumentException(String
832-
.format("Unsupported connection type conversion from NMDeviceType \"%s\"",
833-
deviceType.toString()));
863+
.format("Unsupported connection type conversion from NMDeviceType \"%s\"", deviceType.toString()));
834864
}
835865
}
836866

kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/network/TabWirelessUi.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,20 @@ public class TabWirelessUi extends Composite implements NetworkTab {
9696
private static final String WIFI_MODE_AP = GwtWifiWirelessMode.netWifiWirelessModeAccessPoint.name();
9797
private static final String WIFI_MODE_STATION_MESSAGE = MessageUtils.get(WIFI_MODE_STATION);
9898
private static final String WIFI_MODE_ACCESS_POINT_MESSAGE = MessageUtils.get(WIFI_MODE_AP);
99+
private static final String WIFI_SECURITY_NONE_MESSAGE = MessageUtils
100+
.get(GwtWifiSecurity.netWifiSecurityNONE.name());
99101
private static final String WIFI_SECURITY_WEP_MESSAGE = MessageUtils.get(GwtWifiSecurity.netWifiSecurityWEP.name());
100102
private static final String WIFI_SECURITY_WPA_MESSAGE = MessageUtils.get(GwtWifiSecurity.netWifiSecurityWPA.name());
101103
private static final String WIFI_SECURITY_WPA2_MESSAGE = MessageUtils
102104
.get(GwtWifiSecurity.netWifiSecurityWPA2.name());
105+
private static final String WIFI_SECURITY_WPA3_MESSAGE = MessageUtils
106+
.get(GwtWifiSecurity.netWifiSecurityWPA3.name());
103107
private static final String WIFI_SECURITY_WPA2_WPA3_ENTERPRISE_MESSAGE = MessageUtils
104108
.get(GwtWifiSecurity.netWifiSecurityWPA2WPA3Enterprise.name());
105109
private static final String WIFI_SECURITY_WPA_WPA2_MESSAGE = MessageUtils
106110
.get(GwtWifiSecurity.netWifiSecurityWPA_WPA2.name());
111+
private static final String WIFI_SECURITY_WPA2_WPA3_MESSAGE = MessageUtils
112+
.get(GwtWifiSecurity.netWifiSecurityWPA2_WPA3.name());
107113
private static final String WIFI_BGSCAN_NONE_MESSAGE = MessageUtils
108114
.get(GwtWifiBgscanModule.netWifiBgscanMode_NONE.name());
109115
private static final String WIFI_CIPHERS_CCMP_TKIP_MESSAGE = MessageUtils
@@ -121,8 +127,6 @@ public class TabWirelessUi extends Composite implements NetworkTab {
121127
private static final String WIFI_BAND_2GHZ_MESSAGE = MessageUtils.get("netWifiBand2Ghz");
122128
private static final String WIFI_BAND_BOTH_MESSAGE = MessageUtils.get("netWifiBandBoth");
123129

124-
private static final String WIFI_SECURITY_NONE_MESSAGE = MessageUtils
125-
.get(GwtWifiSecurity.netWifiSecurityNONE.name());
126130
private static final String IPV4_STATUS_WAN_MESSAGE = MessageUtils
127131
.get(GwtNetIfStatus.netIPv4StatusEnabledWAN.name());
128132

@@ -715,8 +719,11 @@ private void refreshForm() {
715719

716720
if (this.security.getSelectedItemText().equals(WIFI_SECURITY_WPA2_MESSAGE)
717721
|| this.security.getSelectedItemText().equals(WIFI_SECURITY_WPA_MESSAGE)
722+
|| this.security.getSelectedItemText().equals(WIFI_SECURITY_WPA3_MESSAGE)
723+
|| this.security.getSelectedItemText()
724+
.equals(MessageUtils.get(GwtWifiSecurity.netWifiSecurityWPA_WPA2.name()))
718725
|| this.security.getSelectedItemText()
719-
.equals(MessageUtils.get(GwtWifiSecurity.netWifiSecurityWPA_WPA2.name()))) {
726+
.equals(MessageUtils.get(GwtWifiSecurity.netWifiSecurityWPA2_WPA3.name()))) {
720727
if (WIFI_MODE_STATION_MESSAGE.equals(this.wireless.getSelectedItemText())) {
721728
this.pairwise.setEnabled(true);
722729
this.group.setEnabled(true);
@@ -1282,7 +1289,9 @@ private void setPasswordValidation() {
12821289

12831290
if (this.security != null && (this.security.getSelectedItemText().equals(WIFI_SECURITY_WPA_MESSAGE)
12841291
|| this.security.getSelectedItemText().equals(WIFI_SECURITY_WPA2_MESSAGE)
1285-
|| this.security.getSelectedItemText().contentEquals(WIFI_SECURITY_WPA_WPA2_MESSAGE))) {
1292+
|| this.security.getSelectedItemText().equals(WIFI_SECURITY_WPA3_MESSAGE)
1293+
|| this.security.getSelectedItemText().contentEquals(WIFI_SECURITY_WPA_WPA2_MESSAGE)
1294+
|| this.security.getSelectedItemText().contentEquals(WIFI_SECURITY_WPA2_WPA3_MESSAGE))) {
12861295

12871296
this.password.setValidatorsFrom(configUserOptions);
12881297
configUserOptions.setPasswordMinimumLength(Math.min(configUserOptions.getPasswordMinimumLength(), 63));

kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/server/net2/status/NetworkStatusServiceAdapter.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,12 +393,18 @@ private void parseAndSetWifiSecurity(GwtWifiHotspotEntry entryToModify, WifiAcce
393393
} else if (isSecurityWPA(rsnSecurity, wpaSecurity, flags) && isSecurityWPA2(rsnSecurity, wpaSecurity, flags)) {
394394
entryToModify.setSecurity(GwtWifiSecurity.netWifiSecurityWPA_WPA2.value());
395395
setCiphers(entryToModify, rsnSecurity);
396+
} else if (isSecurityWPA2(rsnSecurity, wpaSecurity, flags) && isSecurityWPA3(rsnSecurity, wpaSecurity, flags)) {
397+
entryToModify.setSecurity(GwtWifiSecurity.netWifiSecurityWPA2_WPA3.value());
398+
setCiphers(entryToModify, rsnSecurity);
396399
} else if (isSecurityWPA(rsnSecurity, wpaSecurity, flags)) {
397400
entryToModify.setSecurity(GwtWifiSecurity.netWifiSecurityWPA.value());
398401
setCiphers(entryToModify, wpaSecurity);
399402
} else if (isSecurityWPA2(rsnSecurity, wpaSecurity, flags)) {
400403
entryToModify.setSecurity(GwtWifiSecurity.netWifiSecurityWPA2.value());
401404
setCiphers(entryToModify, rsnSecurity);
405+
} else if (isSecurityWPA3(rsnSecurity, wpaSecurity, flags)) {
406+
entryToModify.setSecurity(GwtWifiSecurity.netWifiSecurityWPA3.value());
407+
setCiphers(entryToModify, rsnSecurity);
402408
} else if (isSecurityWPA2WPA3Enterprise(rsnSecurity, wpaSecurity, flags)) {
403409
entryToModify.setSecurity(GwtWifiSecurity.netWifiSecurityWPA2WPA3Enterprise.value());
404410
setCiphers(entryToModify, rsnSecurity);
@@ -430,6 +436,10 @@ private boolean isSecurityWPA2WPA3Enterprise(Set<WifiSecurity> rsnSecurity, Set<
430436
return flags.contains(WifiFlag.PRIVACY) && (rsnSecurity.contains(WifiSecurity.KEY_MGMT_802_1X));
431437
}
432438

439+
private boolean isSecurityWPA3(Set<WifiSecurity> rsnSecurity, Set<WifiSecurity> wpaSecurity, Set<WifiFlag> flags) {
440+
return flags.contains(WifiFlag.PRIVACY) && (rsnSecurity.contains(WifiSecurity.KEY_MGMT_SAE));
441+
}
442+
433443
private void setCiphers(GwtWifiHotspotEntry entryToModify, Set<WifiSecurity> wifiSecurity) {
434444
boolean isGroupCCMP = wifiSecurity.contains(WifiSecurity.GROUP_CCMP);
435445
boolean isGroupTKIP = wifiSecurity.contains(WifiSecurity.GROUP_TKIP);

kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/server/net2/utils/EnumsParser.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,18 @@ public static String getGwtWifiSecurity(Optional<String> wifiSecurity) {
161161
return GwtWifiSecurity.netWifiSecurityWPA2.name();
162162
}
163163

164+
if (wifiSecurity.get().equals(WifiSecurity.SECURITY_WPA3.name())) {
165+
return GwtWifiSecurity.netWifiSecurityWPA3.name();
166+
}
167+
164168
if (wifiSecurity.get().equals(WifiSecurity.SECURITY_WPA_WPA2.name())) {
165169
return GwtWifiSecurity.netWifiSecurityWPA_WPA2.name();
166170
}
167171

172+
if (wifiSecurity.get().equals(WifiSecurity.SECURITY_WPA2_WPA3.name())) {
173+
return GwtWifiSecurity.netWifiSecurityWPA2_WPA3.name();
174+
}
175+
168176
if (wifiSecurity.get().equals(WifiSecurity.SECURITY_WPA2_WPA3_ENTERPRISE.name())) {
169177
return GwtWifiSecurity.netWifiSecurityWPA2WPA3Enterprise.name();
170178
}
@@ -191,10 +199,18 @@ public static String getWifiSecurity(Optional<String> gwtWifiSecurity) {
191199
return WifiSecurity.SECURITY_WPA2.name();
192200
}
193201

202+
if (gwtWifiSecurity.get().equals(GwtWifiSecurity.netWifiSecurityWPA3.name())) {
203+
return WifiSecurity.SECURITY_WPA3.name();
204+
}
205+
194206
if (gwtWifiSecurity.get().equals(GwtWifiSecurity.netWifiSecurityWPA_WPA2.name())) {
195207
return WifiSecurity.SECURITY_WPA_WPA2.name();
196208
}
197209

210+
if (gwtWifiSecurity.get().equals(GwtWifiSecurity.netWifiSecurityWPA2_WPA3.name())) {
211+
return WifiSecurity.SECURITY_WPA2_WPA3.name();
212+
}
213+
198214
if (gwtWifiSecurity.get().equals(GwtWifiSecurity.netWifiSecurityWPA2WPA3Enterprise.name())) {
199215
return WifiSecurity.SECURITY_WPA2_WPA3_ENTERPRISE.name();
200216
}

kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/model/GwtWifiSecurity.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ public enum GwtWifiSecurity {
1919
netWifiSecurityWPA("WPA"),
2020
netWifiSecurityWPA2("WPA2"),
2121
netWifiSecurityWPA2WPA3Enterprise("WPA2/WPA3-Enterprise"),
22-
netWifiSecurityWPA_WPA2("WPA/WPA2");
22+
netWifiSecurityWPA_WPA2("WPA/WPA2"),
23+
netWifiSecurityWPA3("WPA3 - Experimental"),
24+
netWifiSecurityWPA2_WPA3("WPA2/WPA3 - Experimental");
2325

2426
String value;
2527

0 commit comments

Comments
 (0)