/*******************************************************************************
* Copyright (c) 2011, 2016 Eurotech and/or its affiliates
*
* 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
*******************************************************************************/
package org.eclipse.kura.net.admin.visitor.linux;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.eclipse.kura.KuraErrorCode;
import org.eclipse.kura.KuraException;
import org.eclipse.kura.core.net.NetworkConfiguration;
import org.eclipse.kura.core.net.NetworkConfigurationVisitor;
import org.eclipse.kura.core.net.modem.ModemInterfaceAddressConfigImpl;
import org.eclipse.kura.core.net.modem.ModemInterfaceConfigImpl;
import org.eclipse.kura.linux.net.dns.LinuxDns;
import org.eclipse.kura.linux.net.modem.SupportedSerialModemInfo;
import org.eclipse.kura.linux.net.modem.SupportedSerialModemsInfo;
import org.eclipse.kura.linux.net.modem.SupportedUsbModemInfo;
import org.eclipse.kura.linux.net.modem.SupportedUsbModemsInfo;
import org.eclipse.kura.linux.net.util.LinuxNetworkUtil;
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.NetworkConfigurationServiceImpl;
import org.eclipse.kura.net.admin.visitor.linux.util.ChapLinux;
import org.eclipse.kura.net.admin.visitor.linux.util.KuranetConfig;
import org.eclipse.kura.net.admin.visitor.linux.util.ModemXchangePair;
import org.eclipse.kura.net.admin.visitor.linux.util.ModemXchangeScript;
import org.eclipse.kura.net.admin.visitor.linux.util.PapLinux;
import org.eclipse.kura.net.admin.visitor.linux.util.PppUtil;
import org.eclipse.kura.net.modem.ModemConfig;
import org.eclipse.kura.net.modem.ModemConfig.AuthType;
import org.eclipse.kura.net.modem.ModemConfig.PdpType;
import org.eclipse.kura.net.modem.ModemTechnologyType;
import org.eclipse.kura.usb.UsbDevice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PppConfigReader implements NetworkConfigurationVisitor {
private static class FileFilter implements FilenameFilter {
@Override
public boolean accept(File directory, String filename) {
return filename.startsWith("ppp") ? true : false;
}
}
private static final Logger s_logger = LoggerFactory.getLogger(PppConfigReader.class);
public static final String PEERS_DIRECTORY = "/etc/ppp/peers/";
public static final String SCRIPTS_DIRECTORY = "/etc/ppp/scripts/";
private static PppConfigReader s_instance;
public static PppConfigReader getInstance() {
if (s_instance == null) {
s_instance = new PppConfigReader();
}
return s_instance;
}
@Override
public void visit(NetworkConfiguration config) throws KuraException {
List<NetInterfaceConfig<? extends NetInterfaceAddressConfig>> netInterfaceConfigs = config
.getNetInterfaceConfigs();
for (NetInterfaceConfig<? extends NetInterfaceAddressConfig> netInterfaceConfig : netInterfaceConfigs) {
if (netInterfaceConfig.getType() == NetInterfaceType.MODEM) {
getConfig(netInterfaceConfig);
}
}
}
private void getConfig(NetInterfaceConfig<? extends NetInterfaceAddressConfig> netInterfaceConfig)
throws KuraException {
String interfaceName = netInterfaceConfig.getName();
s_logger.debug("Getting ppp config for {}", interfaceName);
if (netInterfaceConfig instanceof ModemInterfaceConfigImpl) {
StringBuilder key = new StringBuilder(
"net.interface." + netInterfaceConfig.getName() + ".modem.identifier");
String modemId = KuranetConfig.getProperty(key.toString());
s_logger.debug("Getting modem identifier using key " + key + ": " + modemId);
if (modemId != null) {
((ModemInterfaceConfigImpl) netInterfaceConfig).setModemIdentifier(modemId);
}
}
List<? extends NetInterfaceAddressConfig> netInterfaceAddressConfigs = netInterfaceConfig
.getNetInterfaceAddresses();
for (NetInterfaceAddressConfig netInterfaceAddressConfig : netInterfaceAddressConfigs) {
if (netInterfaceAddressConfig instanceof ModemInterfaceAddressConfigImpl) {
List<NetConfig> netConfigs = netInterfaceAddressConfig.getConfigs();
if (netConfigs == null) {
netConfigs = new ArrayList<NetConfig>();
((ModemInterfaceAddressConfigImpl) netInterfaceAddressConfig).setNetConfigs(netConfigs);
}
// Create a ModemConfig
ModemConfig modemConfig = getModemConfig(interfaceName, netInterfaceConfig.getUsbDevice());
if (modemConfig != null) {
netConfigs.add(modemConfig);
}
// Create a NetConfigIP4
netConfigs.add(getNetConfigIP4(interfaceName));
// Populate with DNS provided by PPP (displayed as read-only in Denali)
if (LinuxNetworkUtil.hasAddress("ppp" + modemConfig.getPppNumber())) {
List<? extends IPAddress> pppDnsServers = LinuxDns.getInstance().getPppDnServers();
if (pppDnsServers != null) {
((ModemInterfaceAddressConfigImpl) netInterfaceAddressConfig).setDnsServers(pppDnsServers);
}
}
}
}
}
private static ModemConfig getModemConfig(String ifaceName, UsbDevice usbDevice) throws KuraException {
s_logger.debug("parsePppPeerConfig()");
boolean isGsmGprsUmtsHspa = false;
List<ModemTechnologyType> technologyTypes = null;
if (usbDevice != null) {
SupportedUsbModemInfo usbModemInfo = SupportedUsbModemsInfo.getModem(usbDevice);
if (usbModemInfo != null) {
technologyTypes = usbModemInfo.getTechnologyTypes();
}
} else {
SupportedSerialModemInfo serialModemInfo = SupportedSerialModemsInfo.getModem();
if (serialModemInfo != null) {
technologyTypes = serialModemInfo.getTechnologyTypes();
}
}
if (technologyTypes != null) {
for (ModemTechnologyType technologyType : technologyTypes) {
if (technologyType == ModemTechnologyType.GSM_GPRS || technologyType == ModemTechnologyType.UMTS
|| technologyType == ModemTechnologyType.HSDPA || technologyType == ModemTechnologyType.HSPA) {
isGsmGprsUmtsHspa = true;
break;
}
}
}
boolean enabled = true;
int unitNum = getUnitNum(ifaceName);
String apn = "";
String pdpType = "UNKNOWN";
String dialString = "";
String username = "";
String password = "";
String model = "";
AuthType authType = AuthType.NONE;
boolean persist = false;
int maxFail = 0;
int idle = 0;
String activeFilter = "";
int lcpEchoInterval = 0;
int lcpEchoFailure = 0;
String peerFilename = getPeerFilename(ifaceName, usbDevice);
File peerFile = new File(peerFilename);
if (!peerFile.exists()) {
persist = true;
maxFail = 5;
idle = 95;
activeFilter = "inbound";
s_logger.warn("getModemConfig() :: PPPD peer file does not exist - {}", peerFilename);
} else {
s_logger.debug("getModemConfig() :: PPPD peer file exists - {}", peerFilename);
// Check if peer file is a symlink. If so, get information from the linked filename.
try {
if (!peerFile.getCanonicalPath().equals(peerFile.getAbsolutePath())) {
Map<String, String> fileInfo = PppUtil.parsePeerFilename(peerFile.getCanonicalFile().getName());
fileInfo.get("technology");
model = fileInfo.get("model");
fileInfo.get("serialNum");
fileInfo.get("modemId");
}
} catch (IOException e) {
s_logger.error("Error checking for symlink", e);
}
Properties props = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(peerFilename);
props.load(fis);
} catch (Exception e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, "Error getting modem config", e);
} finally {
if (null != fis) {
try {
fis.close();
} catch (IOException e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, "Error getting modem config", e);
}
}
}
s_logger.debug("peer properties: {}", props);
if (props.getProperty("unit") != null) {
unitNum = Integer.parseInt(props.getProperty("unit"));
}
if (props.getProperty("user") != null) {
username = removeQuotes(props.getProperty("user"));
}
if (props.getProperty("persist") != null) {
persist = true;
}
if (props.getProperty("maxfail") != null) {
maxFail = Integer.parseInt(props.getProperty("maxfail"));
}
if (props.getProperty("idle") != null) {
idle = Integer.parseInt(props.getProperty("idle"));
}
if (props.getProperty("active-filter") != null) {
activeFilter = removeQuotes(props.getProperty("active-filter"));
}
if (props.getProperty("lcp-echo-interval") != null) {
lcpEchoInterval = Integer.parseInt(props.getProperty("lcp-echo-interval"));
}
if (props.getProperty("lcp-echo-failure") != null) {
lcpEchoFailure = Integer.parseInt(props.getProperty("lcp-echo-failure"));
}
String chatFilename = "";
String connectProperty = removeQuotes(props.getProperty("connect"));
String[] args = connectProperty.split("\\s+");
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-f") && args.length > i + 1) {
chatFilename = args[i + 1];
break;
}
}
// String disconnectFilename = "";
// String disconnectProperty = removeQuotes(props.getProperty("disconnect"));
// args = disconnectProperty.split("\\s+");
// for(int i=0; i<args.length; i++) {
// if(args[i].equals("-f") && args.length > i+1) {
// disconnectFilename = args[i+1];
// break;
// }
// }
// Parse the connect script
ModemXchangeScript connectScript = null;
try {
connectScript = ModemXchangeScript.parseFile(chatFilename);
} catch (Exception e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, "Error parsing " + chatFilename, e);
}
if (connectScript != null) {
ModemXchangePair modemXchangePair = connectScript.getFirstModemXchangePair();
ModemXchangePair prevXchangePair = null;
String expectedStr, sendStr;
while (modemXchangePair != null) {
expectedStr = modemXchangePair.getExpectString();
sendStr = removeQuotes(modemXchangePair.getSendString());
if (expectedStr.equals("OK")) {
// apn
if (sendStr.contains(",")) {
String[] sendArgs = sendStr.split(",");
if (sendArgs.length == 3 && isGsmGprsUmtsHspa) {
pdpType = removeQuotes(sendArgs[1]);
apn = removeQuotes(sendArgs[2]);
}
}
// } else if(expectedStr.equals("\"\"")) {
} else if (expectedStr.equals("CONNECT")) {
// dial string
if (prevXchangePair != null) {
dialString = removeQuotes(prevXchangePair.getSendString());
}
}
prevXchangePair = modemXchangePair;
modemXchangePair = connectScript.getNextModemXchangePair();
}
}
s_logger.debug("* Enabled: {}", enabled);
s_logger.debug("* CHAT file: {}", chatFilename);
s_logger.debug("* UnitNum: {}", unitNum);
s_logger.debug("* dial string: {}", dialString);
s_logger.debug("* persist: {}", persist);
s_logger.debug("* maxfail: {}", maxFail);
s_logger.debug("* idle: {}", idle);
s_logger.debug("* active-filter: {}", activeFilter);
s_logger.debug("* LCP Echo Interval: {}", lcpEchoInterval);
s_logger.debug("* LCP Echo Failure: {}", lcpEchoFailure);
// Get the auth type and credentials
// pppd will use CHAP if available, else PAP
password = "";
if (isGsmGprsUmtsHspa) {
String chapSecret = ChapLinux.getInstance().getSecret(model, username, "*", "*");
String papSecret = PapLinux.getInstance().getSecret(model, username, "*", "*");
if (chapSecret != null && papSecret != null && chapSecret.equals(papSecret)) {
authType = AuthType.AUTO;
password = chapSecret;
} else if (chapSecret != null) {
authType = AuthType.CHAP;
password = chapSecret;
} else if (papSecret != null) {
authType = AuthType.PAP;
password = papSecret;
}
s_logger.debug("* APN: {}", apn);
s_logger.debug("* auth: {}", authType);
s_logger.debug("* username: {}", username);
s_logger.debug("* password: {}", password);
}
}
boolean gpsEnabled = false;
StringBuilder key = new StringBuilder().append("net.interface.").append(ifaceName).append(".config.gpsEnabled");
String statusString = KuranetConfig.getProperty(key.toString());
if (statusString != null && !statusString.isEmpty()) {
gpsEnabled = Boolean.parseBoolean(statusString);
}
int resetTout = 5;
key = new StringBuilder().append("net.interface.").append(ifaceName).append(".config.resetTimeout");
statusString = KuranetConfig.getProperty(key.toString());
if (statusString != null && !statusString.isEmpty()) {
resetTout = Integer.parseInt(statusString);
}
// Populate the modem config
ModemConfig modemConfig = new ModemConfig();
modemConfig.setPppNumber(unitNum);
modemConfig.setPersist(persist);
modemConfig.setMaxFail(maxFail);
modemConfig.setIdle(idle);
modemConfig.setActiveFilter(activeFilter);
modemConfig.setLcpEchoInterval(lcpEchoInterval);
modemConfig.setLcpEchoFailure(lcpEchoFailure);
modemConfig.setEnabled(enabled); // TODO - from self configuring properties
modemConfig.setDataCompression(0); // FIXME
modemConfig.setDialString(dialString);
modemConfig.setHeaderCompression(0); // FIXME
modemConfig.setGpsEnabled(gpsEnabled);
modemConfig.setResetTimeout(resetTout);
if (isGsmGprsUmtsHspa) {
modemConfig.setApn(apn);
modemConfig.setAuthType(authType);
modemConfig.setPassword(password);
modemConfig.setPdpType(PdpType.valueOf(pdpType.toUpperCase()));
modemConfig.setUsername(username);
}
return modemConfig;
}
private static NetConfigIP4 getNetConfigIP4(String interfaceName) throws KuraException {
NetConfigIP4 netConfigIP4 = null;
NetInterfaceStatus netInterfaceStatus = NetInterfaceStatus.netIPv4StatusDisabled;
StringBuilder key = new StringBuilder().append("net.interface.").append(interfaceName)
.append(".config.ip4.status");
String statusString = KuranetConfig.getProperty(key.toString());
if (statusString != null && !statusString.isEmpty()) {
netInterfaceStatus = NetInterfaceStatus.valueOf(statusString);
}
s_logger.debug("Setting NetInterfaceStatus to " + netInterfaceStatus + " for " + interfaceName);
netConfigIP4 = new NetConfigIP4(netInterfaceStatus, true, true);
key = new StringBuilder("net.interface.").append(interfaceName).append(".config.dnsServers");
String dnsServersStr = KuranetConfig.getProperty(key.toString());
List<IP4Address> dnsServersList = new ArrayList<IP4Address>();
if (dnsServersStr != null && !dnsServersStr.isEmpty()) {
String[] serversArr = dnsServersStr.split(PppConfigWriter.DNS_DELIM);
for (String server : serversArr) {
try {
dnsServersList.add((IP4Address) IPAddress.parseHostAddress(server));
} catch (UnknownHostException e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
}
}
netConfigIP4.setDnsServers(dnsServersList);
}
return netConfigIP4;
}
private static String getPeerFilename(String interfaceName, UsbDevice usbDevice) {
String filename = null;
// if interfaceName is a usb port address
if (interfaceName.matches(NetworkConfigurationServiceImpl.UNCONFIGURED_MODEM_REGEX)) {
filename = PppConfigWriter.formPeerFilename(usbDevice);
} else {
StringBuilder sb = new StringBuilder();
sb.append(PEERS_DIRECTORY).append(interfaceName);
filename = sb.toString();
}
return filename;
}
/**
* Remove quotes if they exist at the start and end of a string.
* Otherwise, return the string with no changes.
*
* @param line
* @return line without surrounding quotes
*/
private static String removeQuotes(String line) {
if (line != null) {
if (line.startsWith("'") && line.endsWith("'") // remove surrounding quotes
|| line.startsWith("\"") && line.endsWith("\"")) {
return line.substring(1, line.length() - 1);
}
}
return line;
}
private static int getUnitNum(String interfaceName) {
int unitNum = -1;
if (interfaceName.matches("(?i)ppp\\d+")) {
unitNum = Integer.parseInt(interfaceName.substring(3));
} else {
unitNum = getUnitNum();
}
return unitNum;
}
private static int getUnitNum() {
int unit = 0;
try {
File peersFolder = new File(PEERS_DIRECTORY).getCanonicalFile();
String[] files = peersFolder.list(new FileFilter());
if (files != null && files.length > 0) {
int[] pppUnitNumbers = new int[files.length];
for (int i = 0; i < pppUnitNumbers.length; i++) {
pppUnitNumbers[i] = Integer.parseInt(files[i].substring(3));
}
Arrays.sort(pppUnitNumbers);
boolean found = false;
for (int i = 0; i < pppUnitNumbers.length; i++) {
if (i < pppUnitNumbers[i]) {
found = true;
unit = i;
break;
}
}
if (!found) {
unit = pppUnitNumbers.length;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return unit;
}
}