Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nm): implementation of IPv6 configuration #4786

Merged
merged 12 commits into from
Jul 28, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*******************************************************************************
* Copyright (c) 2023 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eurotech
*******************************************************************************/
package org.eclipse.kura.nm;
mattdibi marked this conversation as resolved.
Show resolved Hide resolved

import org.eclipse.kura.nm.enums.NMSettingIP6ConfigAddrGenMode;

public enum KuraIp6AddressGenerationMode {

EUI64,
STABLE_PRIVACY;

public static KuraIp6AddressGenerationMode fromString(String status) {
switch (status) {
case "netIPv6AddressGenModeEUI64":
return KuraIp6AddressGenerationMode.EUI64;
case "netIPv6AddressGenModeStablePrivacy":
return KuraIp6AddressGenerationMode.STABLE_PRIVACY;
default:
throw new IllegalArgumentException(
String.format("Unsupported IPv6 address generation mode: \"%s\"", status));
}
}

public static NMSettingIP6ConfigAddrGenMode toNMSettingIP6ConfigAddrGenMode(
KuraIp6AddressGenerationMode privacyValue) {
switch (privacyValue) {
case EUI64:
return NMSettingIP6ConfigAddrGenMode.NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64;
case STABLE_PRIVACY:
return NMSettingIP6ConfigAddrGenMode.NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY;
default:
throw new IllegalArgumentException(
String.format("Unsupported IPv6 address generation mode: \"%s\"", privacyValue));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2023 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eurotech
*******************************************************************************/

package org.eclipse.kura.nm;

public enum KuraIp6ConfigurationMethod {

AUTO,
DHCP,
MANUAL;

public static KuraIp6ConfigurationMethod fromString(String status) {
switch (status) {
case "netIPv6MethodAuto":
return KuraIp6ConfigurationMethod.AUTO;
case "netIPv6MethodDhcp":
return KuraIp6ConfigurationMethod.DHCP;
case "netIPv6MethodManual":
return KuraIp6ConfigurationMethod.MANUAL;
default:
throw new IllegalArgumentException(String.format("Unsupported IPv6 configuration method: \"%s\"", status));

}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2023 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eurotech
*******************************************************************************/
package org.eclipse.kura.nm;
mattdibi marked this conversation as resolved.
Show resolved Hide resolved

import org.eclipse.kura.nm.enums.NMSettingIP6ConfigPrivacy;

public enum KuraIp6Privacy {

UNKNOWN,
DISABLED,
ENABLED_PUBLIC_ADD,
ENABLED_TEMP_ADD;

public static KuraIp6Privacy fromString(String status) {
switch (status) {
case "netIPv6PrivacyUnknown":
return KuraIp6Privacy.UNKNOWN;
case "netIPv6PrivacyDisabled":
return KuraIp6Privacy.DISABLED;
case "netIPv6PrivacyEnabledPubAdd":
return KuraIp6Privacy.ENABLED_PUBLIC_ADD;
case "netIPv6PrivacyEnabledTempAdd":
return KuraIp6Privacy.ENABLED_TEMP_ADD;
default:
throw new IllegalArgumentException(String.format("Unsupported IPv6 privacy value: \"%s\"", status));
}
}
mattdibi marked this conversation as resolved.
Show resolved Hide resolved

public static NMSettingIP6ConfigPrivacy toNMSettingIP6ConfigPrivacy(KuraIp6Privacy privacyValue) {
switch (privacyValue) {
case UNKNOWN:
return NMSettingIP6ConfigPrivacy.NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
case DISABLED:
return NMSettingIP6ConfigPrivacy.NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED;
case ENABLED_PUBLIC_ADD:
return NMSettingIP6ConfigPrivacy.NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR;
case ENABLED_TEMP_ADD:
return NMSettingIP6ConfigPrivacy.NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR;
default:
return NMSettingIP6ConfigPrivacy.NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public enum KuraIpStatus {

Expand Down Expand Up @@ -55,4 +56,34 @@ public static KuraIpStatus fromString(String status) {

}

public static Optional<KuraIpStatus> fromString(Optional<String> status) {

if (status.isPresent()) {
switch (status.get()) {
case "netIPv4StatusDisabled":
case "netIPv6StatusDisabled":
return Optional.of(KuraIpStatus.DISABLED);
case "netIPv4StatusUnmanaged":
case "netIPv6StatusUnmanaged":
return Optional.of(KuraIpStatus.UNMANAGED);
case "netIPv4StatusL2Only":
case "netIPv6StatusL2Only":
return Optional.of(KuraIpStatus.L2ONLY);
case "netIPv4StatusEnabledLAN":
case "netIPv6StatusEnabledLAN":
return Optional.of(KuraIpStatus.ENABLEDLAN);
case "netIPv4StatusEnabledWAN":
case "netIPv6StatusEnabledWAN":
return Optional.of(KuraIpStatus.ENABLEDWAN);
default:
return Optional.of(KuraIpStatus.UNKNOWN);

}

} else {
return Optional.empty();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,17 @@ private synchronized void manageConfiguredInterface(Device device, String device

KuraIpStatus ip4Status = KuraIpStatus
.fromString(properties.get(String.class, "net.interface.%s.config.ip4.status", deviceId));
// Temporary solution while we wait to add complete IPv6 support
KuraIpStatus ip6Status = ip4Status == KuraIpStatus.UNMANAGED ? KuraIpStatus.UNMANAGED : KuraIpStatus.DISABLED;

Optional<KuraIpStatus> ip6OptStatus = KuraIpStatus
.fromString(properties.getOpt(String.class, "net.interface.%s.config.ip6.status", deviceId));
KuraIpStatus ip6Status;

if (!ip6OptStatus.isPresent()) {
ip6Status = ip4Status == KuraIpStatus.UNMANAGED ? KuraIpStatus.UNMANAGED : KuraIpStatus.DISABLED;
} else {
ip6Status = ip6OptStatus.get();
}

KuraInterfaceStatus interfaceStatus = KuraInterfaceStatus.fromKuraIpStatus(ip4Status, ip6Status);

if (!CONFIGURATION_SUPPORTED_DEVICE_TYPES.contains(deviceType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
import java.util.Optional;

import org.eclipse.kura.configuration.Password;
import org.eclipse.kura.nm.KuraIp6AddressGenerationMode;
import org.eclipse.kura.nm.KuraIp6Privacy;
import org.eclipse.kura.nm.KuraIpStatus;
import org.eclipse.kura.nm.KuraIp6ConfigurationMethod;
import org.eclipse.kura.nm.KuraWifiSecurityType;
import org.eclipse.kura.nm.NetworkProperties;
import org.eclipse.kura.nm.enums.NMDeviceType;
Expand All @@ -40,6 +43,8 @@ public class NMSettingsConverter {
private static final String NM_SETTINGS_CONNECTION = "connection";
private static final String NM_SETTINGS_IPV4_METHOD = "method";
private static final String NM_SETTINGS_IPV6_METHOD = "method";
private static final String NM_SETTINGS_IPV4_IGNORE_AUTO_DNS = "ignore-auto-dns";
private static final String NM_SETTINGS_IPV6_IGNORE_AUTO_DNS = "ignore-auto-dns";

private static final String PPP_REFUSE_EAP = "refuse-eap";
private static final String PPP_REFUSE_CHAP = "refuse-chap";
Expand Down Expand Up @@ -123,14 +128,14 @@ public static Map<String, Variant<?>> buildIpv4Settings(NetworkProperties props,
}

if (ip4Status.equals(KuraIpStatus.ENABLEDLAN)) {
settings.put("ignore-auto-dns", new Variant<>(true));
settings.put(NM_SETTINGS_IPV4_IGNORE_AUTO_DNS, new Variant<>(true));
settings.put("ignore-auto-routes", new Variant<>(true));
} else if (ip4Status.equals(KuraIpStatus.ENABLEDWAN)) {
Optional<List<String>> dnsServers = props.getOptStringList("net.interface.%s.config.ip4.dnsServers",
deviceId);
if (dnsServers.isPresent()) {
settings.put("dns", new Variant<>(convertIp4(dnsServers.get()), "au"));
settings.put("ignore-auto-dns", new Variant<>(true));
settings.put(NM_SETTINGS_IPV4_IGNORE_AUTO_DNS, new Variant<>(true));
}

Optional<Integer> wanPriority = props.getOpt(Integer.class, "net.interface.%s.config.ip4.wan.priority",
Expand All @@ -147,10 +152,100 @@ public static Map<String, Variant<?>> buildIpv4Settings(NetworkProperties props,
}

public static Map<String, Variant<?>> buildIpv6Settings(NetworkProperties props, String deviceId) {

// buildIpv6Settings doesn't support Unmanaged status. Therefore if ip6.status property is not set, it assumes
// it is disabled.

Optional<KuraIpStatus> ip6OptStatus = KuraIpStatus
.fromString(props.getOpt(String.class, "net.interface.%s.config.ip6.status", deviceId));

KuraIpStatus ip6Status = ip6OptStatus.isPresent() ? ip6OptStatus.get() : KuraIpStatus.DISABLED;

if (ip6Status == KuraIpStatus.UNMANAGED || ip6Status == KuraIpStatus.UNKNOWN) {
throw new IllegalArgumentException("IPv6 status is not supported: " + ip6Status
+ ". Build settings should be called only for managed interfaces.");
}

Map<String, Variant<?>> settings = new HashMap<>();

// Disabled for now
settings.put(NM_SETTINGS_IPV6_METHOD, new Variant<>("disabled"));
if (ip6Status == KuraIpStatus.DISABLED) {
settings.put(NM_SETTINGS_IPV6_METHOD, new Variant<>("disabled"));
return settings;
}

KuraIp6ConfigurationMethod ip6ConfigMethod = KuraIp6ConfigurationMethod
.fromString(props.get(String.class, "net.interface.%s.config.ip6.address.method", deviceId));

if (ip6ConfigMethod.equals(KuraIp6ConfigurationMethod.AUTO)) {

settings.put(NM_SETTINGS_IPV6_METHOD, new Variant<>("auto"));

Optional<String> addressGenerationMode = props.getOpt(String.class,
"net.interface.%s.config.ip6.addr.gen.mode", deviceId);

addressGenerationMode.ifPresent(value -> {
KuraIp6AddressGenerationMode ipv6AddressGenerationMode = KuraIp6AddressGenerationMode
.fromString(addressGenerationMode.get());
settings.put("addr-gen-mode", new Variant<>(KuraIp6AddressGenerationMode
.toNMSettingIP6ConfigAddrGenMode(ipv6AddressGenerationMode).toInt32()));
});

Optional<String> privacy = props.getOpt(String.class, "net.interface.%s.config.ip6.privacy", deviceId);
privacy.ifPresent(value -> {
KuraIp6Privacy ip6Privacy = KuraIp6Privacy.fromString(privacy.get());
settings.put("ip6-privacy",
new Variant<>(KuraIp6Privacy.toNMSettingIP6ConfigPrivacy(ip6Privacy).toInt32()));
});

} else if (ip6ConfigMethod.equals(KuraIp6ConfigurationMethod.DHCP)) {

settings.put(NM_SETTINGS_IPV6_METHOD, new Variant<>("dhcp"));

} else if (ip6ConfigMethod.equals(KuraIp6ConfigurationMethod.MANUAL)) {

settings.put(NM_SETTINGS_IPV6_METHOD, new Variant<>("manual"));

String address = props.get(String.class, "net.interface.%s.config.ip6.address", deviceId);
Short prefix = props.get(Short.class, "net.interface.%s.config.ip6.prefix", deviceId);

Map<String, Variant<?>> addressEntry = new HashMap<>();
addressEntry.put("address", new Variant<>(address));
addressEntry.put("prefix", new Variant<>(new UInt32(prefix)));

if (ip6Status.equals(KuraIpStatus.ENABLEDWAN)) {
Optional<String> gateway = props.getOpt(String.class, "net.interface.%s.config.ip6.gateway", deviceId);
gateway.ifPresent(gatewayAddress -> settings.put("gateway", new Variant<>(gatewayAddress)));
}

List<Map<String, Variant<?>>> addressData = Arrays.asList(addressEntry);
settings.put("address-data", new Variant<>(addressData, "aa{sv}"));

} else {
throw new IllegalArgumentException(
String.format("Unsupported IPv6 address generation mode: \"%s\"", ip6ConfigMethod));
}

if (ip6Status.equals(KuraIpStatus.ENABLEDLAN)) {
settings.put(NM_SETTINGS_IPV6_IGNORE_AUTO_DNS, new Variant<>(true));
settings.put("ignore-auto-routes", new Variant<>(true));

} else if (ip6Status.equals(KuraIpStatus.ENABLEDWAN)) {
Optional<List<String>> dnsServers = props.getOptStringList("net.interface.%s.config.ip6.dnsServers",
deviceId);

dnsServers.ifPresent(value -> {
settings.put("dns", new Variant<>(convertIp6(value), "aay"));
settings.put(NM_SETTINGS_IPV6_IGNORE_AUTO_DNS, new Variant<>(true));
});

Optional<Integer> wanPriority = props.getOpt(Integer.class, "net.interface.%s.config.ip6.wan.priority",
deviceId);

wanPriority.ifPresent(value -> settings.put("route-metric", new Variant<>(value.longValue())));

} else {
logger.warn("Unexpected ip status received: \"{}\". Ignoring", ip6Status);
}

return settings;
}
Expand Down Expand Up @@ -366,6 +461,31 @@ private static UInt32 convertIp4(String ipAddrString) throws UnknownHostExceptio
return new UInt32(result);
}

private static List<List<Byte>> convertIp6(List<String> ipAddrList) {
List<List<Byte>> uint32Addresses = new ArrayList<>();
for (String address : ipAddrList) {
try {
uint32Addresses.add(convertIp6(address));
} catch (UnknownHostException e) {
logger.warn("Cannot convert ip address \"{}\" because: ", address, e);
}
}
return uint32Addresses;
}

private static List<Byte> convertIp6(String ipAddrString) throws UnknownHostException {
InetAddress address = InetAddress.getByName(ipAddrString);

byte[] dnsByteArray = address.getAddress();

List<Byte> dnsAddress = new ArrayList<>();
for (int i = 0; i < dnsByteArray.length; i++) {
dnsAddress.add(dnsByteArray[i]);
}

return dnsAddress;
}

private static String wifiModeConvert(String kuraMode) {
switch (kuraMode) {
case "INFRA":
Expand Down
Loading