/*******************************************************************************
* Copyright (c) 2011, 2016 Eurotech and/or its affiliates and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eurotech
* Benjamin Cabé - fix for GH issue #299
*******************************************************************************/
package org.eclipse.kura.net.admin.visitor.linux;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
import org.eclipse.kura.KuraErrorCode;
import org.eclipse.kura.KuraException;
import org.eclipse.kura.core.net.NetInterfaceAddressConfigImpl;
import org.eclipse.kura.core.net.NetworkConfiguration;
import org.eclipse.kura.core.net.NetworkConfigurationVisitor;
import org.eclipse.kura.core.net.WifiInterfaceAddressConfigImpl;
import org.eclipse.kura.core.net.util.NetworkUtil;
import org.eclipse.kura.linux.net.dns.LinuxDns;
import org.eclipse.kura.linux.net.util.KuraConstants;
import org.eclipse.kura.net.IP4Address;
import org.eclipse.kura.net.IPAddress;
import org.eclipse.kura.net.NetConfig;
import org.eclipse.kura.net.NetConfigIP4;
import org.eclipse.kura.net.NetInterfaceAddressConfig;
import org.eclipse.kura.net.NetInterfaceConfig;
import org.eclipse.kura.net.NetInterfaceStatus;
import org.eclipse.kura.net.NetInterfaceType;
import org.eclipse.kura.net.admin.visitor.linux.util.KuranetConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class IfcfgConfigReader implements NetworkConfigurationVisitor {
private static final Logger s_logger = LoggerFactory.getLogger(IfcfgConfigReader.class);
private static final String OS_VERSION = System.getProperty("kura.os.version");
private static final String REDHAT_NET_CONFIGURATION_DIRECTORY = "/etc/sysconfig/network-scripts/";
private static final String DEBIAN_NET_CONFIGURATION_DIRECTORY = "/etc/network/";
private static IfcfgConfigReader s_instance;
public static IfcfgConfigReader getInstance() {
if (s_instance == null) {
s_instance = new IfcfgConfigReader();
}
return s_instance;
}
@Override
public void visit(NetworkConfiguration config) throws KuraException {
List<NetInterfaceConfig<? extends NetInterfaceAddressConfig>> netInterfaceConfigs = config
.getNetInterfaceConfigs();
Properties kuraExtendedProps = KuranetConfig.getProperties();
for (NetInterfaceConfig<? extends NetInterfaceAddressConfig> netInterfaceConfig : netInterfaceConfigs) {
getConfig(netInterfaceConfig, kuraExtendedProps);
}
}
private void getConfig(NetInterfaceConfig<? extends NetInterfaceAddressConfig> netInterfaceConfig,
Properties kuraExtendedProps) throws KuraException {
String interfaceName = netInterfaceConfig.getName();
s_logger.debug("Getting config for {}", interfaceName);
NetInterfaceType type = netInterfaceConfig.getType();
if (type == NetInterfaceType.ETHERNET || type == NetInterfaceType.WIFI || type == NetInterfaceType.LOOPBACK) {
NetInterfaceStatus netInterfaceStatus = null;
StringBuilder sb = new StringBuilder().append("net.interface.").append(netInterfaceConfig.getName())
.append(".config.ip4.status");
if (kuraExtendedProps != null && kuraExtendedProps.getProperty(sb.toString()) != null) {
netInterfaceStatus = NetInterfaceStatus.valueOf(kuraExtendedProps.getProperty(sb.toString()));
} else {
netInterfaceStatus = NetInterfaceStatus.netIPv4StatusDisabled;
}
s_logger.debug(
"Setting NetInterfaceStatus to " + netInterfaceStatus + " for " + netInterfaceConfig.getName());
boolean autoConnect = false;
// int mtu = -1; // MTU is not currently used
boolean dhcp = false;
IP4Address address = null;
String ipAddress = null;
String prefixString = null;
String netmask = null;
String gateway = null;
File ifcfgFile = null;
if (OS_VERSION
.equals(KuraConstants.Mini_Gateway.getImageName() + "_"
+ KuraConstants.Mini_Gateway.getImageVersion())
|| OS_VERSION.equals(KuraConstants.Raspberry_Pi.getImageName())
|| OS_VERSION.equals(KuraConstants.BeagleBone.getImageName())
|| OS_VERSION.equals(KuraConstants.Intel_Edison.getImageName() + "_"
+ KuraConstants.Intel_Edison.getImageVersion() + "_"
+ KuraConstants.Intel_Edison.getTargetName())
|| OS_VERSION.equals(KuraConstants.ReliaGATE_50_21_Ubuntu.getImageName() + "_"
+ KuraConstants.ReliaGATE_50_21_Ubuntu.getImageVersion())) {
ifcfgFile = new File(DEBIAN_NET_CONFIGURATION_DIRECTORY + "interfaces");
} else {
ifcfgFile = new File(REDHAT_NET_CONFIGURATION_DIRECTORY + "ifcfg-" + interfaceName);
}
if (ifcfgFile.exists()) {
Properties kuraProps;
// found our match so load the properties
if (OS_VERSION
.equals(KuraConstants.Mini_Gateway.getImageName() + "_"
+ KuraConstants.Mini_Gateway.getImageVersion())
|| OS_VERSION.equals(KuraConstants.Raspberry_Pi.getImageName())
|| OS_VERSION.equals(KuraConstants.BeagleBone.getImageName())
|| OS_VERSION.equals(KuraConstants.Intel_Edison.getImageName() + "_"
+ KuraConstants.Intel_Edison.getImageVersion() + "_"
+ KuraConstants.Intel_Edison.getTargetName())
|| OS_VERSION.equals(KuraConstants.ReliaGATE_50_21_Ubuntu.getImageName() + "_"
+ KuraConstants.ReliaGATE_50_21_Ubuntu.getImageVersion())) {
kuraProps = parseDebianConfigFile(ifcfgFile, interfaceName);
} else {
kuraProps = parseRedhatConfigFile(ifcfgFile, interfaceName);
}
if (kuraProps != null) {
String onBoot = kuraProps.getProperty("ONBOOT");
if ("yes".equals(onBoot)) {
s_logger.debug("Setting autoConnect to true");
autoConnect = true;
} else {
s_logger.debug("Setting autoConnect to false");
autoConnect = false;
}
// override MTU with what is in config if it is present
/*
* IAB: MTU is not currently used
* String stringMtu = kuraProps.getProperty("MTU");
* if (stringMtu == null) {
* try {
* mtu = LinuxNetworkUtil.getCurrentMtu(interfaceName);
* } catch (KuraException e) {
* // just assume ???
* if (interfaceName.equals("lo")) {
* mtu = 16436;
* } else {
* mtu = 1500;
* }
* }
* } else {
* mtu = Short.parseShort(stringMtu);
* }
*/
// get the bootproto
String bootproto = kuraProps.getProperty("BOOTPROTO");
if (bootproto == null) {
bootproto = "static";
}
// get the defroute
String defroute = kuraProps.getProperty("DEFROUTE");
if (defroute == null) {
defroute = "no";
}
// correct the status if needed by validating against the
// actual properties
if (netInterfaceStatus == NetInterfaceStatus.netIPv4StatusDisabled) {
if (autoConnect) {
if (defroute.equals("no")) {
netInterfaceStatus = NetInterfaceStatus.netIPv4StatusEnabledLAN;
} else {
netInterfaceStatus = NetInterfaceStatus.netIPv4StatusEnabledWAN;
}
}
}
// check for dhcp or static configuration
try {
ipAddress = kuraProps.getProperty("IPADDR");
prefixString = kuraProps.getProperty("PREFIX");
netmask = kuraProps.getProperty("NETMASK");
kuraProps.getProperty("BROADCAST");
try {
gateway = kuraProps.getProperty("GATEWAY");
s_logger.debug("got gateway for " + interfaceName + ": " + gateway);
} catch (Exception e) {
s_logger.warn("missing gateway stanza for " + interfaceName);
}
if (bootproto.equals("dhcp")) {
s_logger.debug("currently set for DHCP");
dhcp = true;
ipAddress = null;
netmask = null;
} else {
s_logger.debug("currently set for static address");
dhcp = false;
}
} catch (Exception e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR,
"malformatted config file: " + ifcfgFile.toString(), e);
}
if (ipAddress != null && !ipAddress.isEmpty()) {
try {
address = (IP4Address) IPAddress.parseHostAddress(ipAddress);
} catch (UnknownHostException e) {
s_logger.warn("Error parsing address: " + ipAddress, e);
}
}
// make sure at least prefix or netmask is present if static
if (autoConnect && !dhcp && prefixString == null && netmask == null) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, "malformatted config file: "
+ ifcfgFile.toString() + " must contain NETMASK and/or PREFIX");
}
}
List<? extends NetInterfaceAddressConfig> netInterfaceAddressConfigs = netInterfaceConfig
.getNetInterfaceAddresses();
if (netInterfaceAddressConfigs == null) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, "InterfaceAddressConfig list is null");
} else if (netInterfaceAddressConfigs.size() == 0) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, "InterfaceAddressConfig list has no entries");
}
for (NetInterfaceAddressConfig netInterfaceAddressConfig : netInterfaceAddressConfigs) {
List<NetConfig> netConfigs = netInterfaceAddressConfig.getConfigs();
if (netConfigs == null) {
netConfigs = new ArrayList<NetConfig>();
if (netInterfaceAddressConfig instanceof NetInterfaceAddressConfigImpl) {
((NetInterfaceAddressConfigImpl) netInterfaceAddressConfig).setNetConfigs(netConfigs);
if (dhcp) {
// Replace with DNS provided by DHCP server
// (displayed as read-only in Denali)
List<? extends IPAddress> dhcpDnsServers = getDhcpDnsServers(interfaceName,
netInterfaceAddressConfig.getAddress());
if (dhcpDnsServers != null) {
((NetInterfaceAddressConfigImpl) netInterfaceAddressConfig)
.setDnsServers(dhcpDnsServers);
}
}
} else if (netInterfaceAddressConfig instanceof WifiInterfaceAddressConfigImpl) {
((WifiInterfaceAddressConfigImpl) netInterfaceAddressConfig).setNetConfigs(netConfigs);
if (dhcp) {
// Replace with DNS provided by DHCP server
// (displayed as read-only in Denali)
List<? extends IPAddress> dhcpDnsServers = getDhcpDnsServers(interfaceName,
netInterfaceAddressConfig.getAddress());
if (dhcpDnsServers != null) {
((WifiInterfaceAddressConfigImpl) netInterfaceAddressConfig)
.setDnsServers(dhcpDnsServers);
}
}
}
}
NetConfigIP4 netConfig = new NetConfigIP4(netInterfaceStatus, autoConnect);
setNetConfigIP4(netConfig, autoConnect, dhcp, address, gateway, prefixString, netmask, kuraProps);
s_logger.debug("NetConfig: {}", netConfig);
netConfigs.add(netConfig);
}
}
}
}
private Properties parseRedhatConfigFile(File ifcfgFile, String interfaceName) {
Properties kuraProps = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(ifcfgFile);
kuraProps.load(fis);
// Values in the config file may be surrounded with double quotes or single quotes.
for (String key : kuraProps.stringPropertyNames()) {
String value = kuraProps.getProperty(key);
if (value.length() >= 2 && (value.startsWith("'") && value.endsWith("'")
|| value.startsWith("\"") && value.endsWith("\""))) {
value = value.substring(1, value.length() - 1);
kuraProps.put(key, value);
}
}
} catch (Exception e) {
s_logger.error("Could not get configuration for " + interfaceName, e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException ex) {
s_logger.error("I/O Exception while closing BufferedReader!");
}
}
}
return kuraProps;
}
static Properties parseDebianConfigFile(File ifcfgFile, String interfaceName) throws KuraException {
Properties kuraProps = new Properties();
Scanner scanner = null;
try {
scanner = new Scanner(new FileInputStream(ifcfgFile));
// Debian specific routine to create Properties object
kuraProps.setProperty("ONBOOT", "no");
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
// ignore comments and blank lines
if (!line.isEmpty()) {
if (line.startsWith("#!kura!")) {
line = line.substring("#!kura!".length());
}
if (!line.startsWith("#")) {
String[] args = line.split("\\s+");
try {
// must be a line stating that interface starts on
// boot
if (args[0].equals("auto") && args[1].equals(interfaceName)) {
s_logger.debug("Setting ONBOOT to yes for " + interfaceName);
kuraProps.setProperty("ONBOOT", "yes");
}
// once the correct interface is found, read all
// configuration information
else if (args[0].equals("iface") && args[1].equals(interfaceName)) {
kuraProps.setProperty("BOOTPROTO", args[3]);
if (args[3].equals("dhcp")) {
kuraProps.setProperty("DEFROUTE", "yes");
}
while (scanner.hasNextLine()) {
line = scanner.nextLine().trim();
if (line != null && !line.isEmpty()) {
if (line.startsWith("auto") || line.startsWith("iface")) {
break;
}
args = line.trim().split("\\s+");
if (args[0].equals("mtu")) {
kuraProps.setProperty("mtu", args[1]);
} else if (args[0].equals("address")) {
kuraProps.setProperty("IPADDR", args[1]);
} else if (args[0].equals("netmask")) {
kuraProps.setProperty("NETMASK", args[1]);
} else if (args[0].equals("gateway")) {
kuraProps.setProperty("GATEWAY", args[1]);
kuraProps.setProperty("DEFROUTE", "yes");
} else if (args[0].equals("#dns-nameservers")) {
/*
* IAB:
* If DNS servers are listed,
* those entries will be appended to
* the /etc/resolv.conf file on
* every ifdown/ifup sequence
* resulting in multiple entries for
* the same servers. (Tested on
* 10-20, 10-10, and Raspberry Pi).
* Commenting out dns-nameservers in
* the /etc/network interfaces file
* allows DNS servers to be picked
* up by the IfcfgConfigReader and
* be displayed on the Web UI but
* the /etc/resolv.conf file will
* only be updated by Kura.
*/
if (args.length > 1) {
for (int i = 1; i < args.length; i++) {
kuraProps.setProperty("DNS" + Integer.toString(i), args[i]);
}
}
} else if (args[0].equals("post-up")) {
StringBuffer sb = new StringBuffer();
for (int i = 1; i < args.length; i++) {
sb.append(args[i]);
sb.append(' ');
}
if (sb.toString().trim().equals("route del default dev " + interfaceName)) {
kuraProps.setProperty("DEFROUTE", "no");
}
}
}
}
// Debian makes assumptions about lo, handle
// those here
if (interfaceName.equals("lo") && kuraProps.getProperty("IPADDR") == null
&& kuraProps.getProperty("NETMASK") == null) {
kuraProps.setProperty("IPADDR", "127.0.0.1");
kuraProps.setProperty("NETMASK", "255.0.0.0");
}
break;
}
} catch (Exception e) {
s_logger.warn("Possible malformed configuration file for " + interfaceName, e);
}
}
}
}
} catch (FileNotFoundException err) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, err);
} finally {
if (scanner != null) {
scanner.close();
}
}
return kuraProps;
}
private static void setNetConfigIP4(NetConfigIP4 netConfig, boolean autoConnect, boolean dhcp, IP4Address address,
String gateway, String prefixString, String netmask, Properties kuraProps) throws KuraException {
netConfig.setDhcp(dhcp);
if (kuraProps != null) {
// get the DNS
List<IP4Address> dnsServers = new ArrayList<IP4Address>();
int count = 1;
while (true) {
String dns = null;
if ((dns = kuraProps.getProperty("DNS" + count)) != null) {
try {
dnsServers.add((IP4Address) IPAddress.parseHostAddress(dns));
} catch (UnknownHostException e) {
s_logger.error("Could not parse address: " + dns, e);
}
count++;
} else {
break;
}
}
netConfig.setDnsServers(dnsServers);
if (!dhcp) {
netConfig.setAddress(address);
// TODO ((NetConfigIP4)netConfig).setDomains(domains);
if (gateway != null && !gateway.isEmpty()) {
try {
netConfig.setGateway((IP4Address) IPAddress.parseHostAddress(gateway));
} catch (UnknownHostException e) {
s_logger.error("Could not parse address: " + gateway, e);
}
}
if (prefixString != null) {
short prefix = Short.parseShort(prefixString);
netConfig.setNetworkPrefixLength(prefix);
}
if (netmask != null) {
netConfig.setNetworkPrefixLength(NetworkUtil.getNetmaskShortForm(netmask));
}
// TODO netConfig.setWinsServers(winsServers);
}
}
}
private static List<? extends IPAddress> getDhcpDnsServers(String interfaceName, IPAddress address) {
List<IPAddress> dnsServers = null;
if (address != null) {
LinuxDns linuxDns = LinuxDns.getInstance();
try {
dnsServers = linuxDns.getDhcpDnsServers(interfaceName, address);
} catch (KuraException e) {
s_logger.error("Error getting DHCP DNS servers", e);
}
}
return dnsServers;
}
}