diff --git a/.eslintrc.json b/.eslintrc.json index f6e685bf1a..1696ec3804 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,7 +5,7 @@ }, "rules": { "indent": [ - "error", + "off", 2 ], "linebreak-style": [ diff --git a/.gitignore b/.gitignore index ca4b400d32..294ba2acfb 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ coverage build/Release # Dependency directories -node_modules +node_modules* jspm_packages # Optional npm cache directory @@ -105,6 +105,9 @@ api/db/token.json .idea/ .idea/workspace.xml + +.vscode/ + /test /.gitmodules /extension/frp/frpc.ini diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 7a73a41bfd..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/README.md b/README.md index 0b7688fcdf..cd95aed1f0 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ follow direction to flash this to a card - Boot Pi and update ``` sudo apt-get update -sudo apt-get -y dist-upgrade +sudo apt-get -y dist-upgrade sudo apt-get -y install git ``` diff --git a/alarm/PolicyManager2.js b/alarm/PolicyManager2.js index f8d21cb2e9..fbfb64270f 100644 --- a/alarm/PolicyManager2.js +++ b/alarm/PolicyManager2.js @@ -969,7 +969,8 @@ class PolicyManager2 { return domainBlock.blockDomain(policy.target, { exactMatch: policy.domainExactMatch, blockSet: Block.getDstSet(policy.pid), - no_dnsmasq_entry: true + no_dnsmasq_entry: true, + no_dnsmasq_reload: true }) } else { return domainBlock.blockDomain(policy.target, {exactMatch: policy.domainExactMatch}) @@ -992,7 +993,8 @@ class PolicyManager2 { return categoryBlock.blockCategory(policy.target, { blockSet: Block.getDstSet(policy.pid), macSet: Block.getMacSet(policy.pid), - no_dnsmasq_entry: true + no_dnsmasq_entry: true, + no_dnsmasq_reload: true }) } else { return categoryBlock.blockCategory(policy.target) @@ -1093,12 +1095,13 @@ class PolicyManager2 { case "dns": return async(() => { if(scope) { - await (Block.advancedUnblock(policy.pid, scope, [])) - return domainBlock.unblockDomain(policy.target, { + await (domainBlock.unblockDomain(policy.target, { exactMatch: policy.domainExactMatch, blockSet: Block.getDstSet(policy.pid), - no_dnsmasq_entry: true - }) + no_dnsmasq_entry: true, + no_dnsmasq_reload: true + })) + return Block.advancedUnblock(policy.pid, scope, []) } else { return domainBlock.unblockDomain(policy.target, {exactMatch: policy.domainExactMatch}) } @@ -1120,7 +1123,8 @@ class PolicyManager2 { blockSet: Block.getDstSet(policy.pid), macSet: Block.getMacSet(policy.pid), ignoreUnapplyBlock: true, - no_dnsmasq_entry: true + no_dnsmasq_entry: true, + no_dnsmasq_reload: true })) return Block.advancedUnblock(policy.pid, scope, []) } else { diff --git a/etc/brotab b/etc/brotab index b1e8a4d6b4..a4ad32988d 100644 --- a/etc/brotab +++ b/etc/brotab @@ -13,3 +13,4 @@ SHELL=/bin/bash */5 * * * * logger "Firewalla checkpoint every 5 mins" &>/dev/null */30 * * * * /home/pi/firewalla/scripts/free-memory &>/dev/null 0 * * * * /home/pi/firewalla/scripts/health_check.sh &> /tmp/health_check.log +0 0 * * * timeout 30 /home/pi/firewalla/scripts/diag_hello.sh &>/dev/null diff --git a/extension/dnsmasq/dnsmasq.js b/extension/dnsmasq/dnsmasq.js index 464dca57d8..5b8ad8bb46 100644 --- a/extension/dnsmasq/dnsmasq.js +++ b/extension/dnsmasq/dnsmasq.js @@ -691,7 +691,7 @@ module.exports = class DNSMASQ { onSpoofChanged() { if (this.dhcpMode) { this.needWriteHostsFile = true; - log.info("Spoof status changed, set need write hosts file to be true"); + log.debug("Spoof status changed, set need write hosts file to be true"); } } diff --git a/extension/install/diag.js b/extension/install/diag.js index 1cd5a5178d..50bc612795 100644 --- a/extension/install/diag.js +++ b/extension/install/diag.js @@ -3,17 +3,16 @@ let instance = null; const exec = require('child-process-promise').exec; -const network = require('network'); -const Promise = require('bluebird'); const fConfig = require('../../net2/config.js').getConfig(); const log = require('../../net2/logger.js')(__filename); -const rclient = require('../../util/redis_manager.js').getRedisClient(); -const get_interfaces_list_async = Promise.promisify(network.get_interfaces_list); +const get_interfaces_list_async = require('bluebird').promisify(require('network').get_interfaces_list); const activeInterface = fConfig.monitoringInterface || "eth0"; const platformLoader = require('../../platform/PlatformLoader.js'); const platform = platformLoader.getPlatform(); +const model = platform.getName(); +const serial = platform.getBoardSerial(); const rp = require('request-promise'); @@ -58,6 +57,25 @@ class FWDiag { return name.replace(/\n$/, '') } + async getBranchInfo() { + const result = await exec("git rev-parse --abbrev-ref HEAD"); + return result && result.stdout && result.stdout.replace(/\n$/, '') + } + + getVersion() { + return fConfig.version; + } + + async getLongVersion() { + const result = await exec("git describe --tags"); + return result && result.stdout && result.stdout.replace(/\n$/, '') + } + + async getTotalMemory() { + const result = await exec("free -m | awk '/Mem:/ {print $2}'"); + return result && result.stdout && result.stdout.replace(/\n$/, '') + } + async prepareData(payload) { const inter = await this.getNetworkInfo(); @@ -77,13 +95,15 @@ class FWDiag { ts: ts, gw_mac: gatewayMac, gw_name: gatewayName, - model: platform.getName() + model: model }); } async submitInfo(payload) { const data = await this.prepareData(payload); if(data.gw) { + const rclient = require('../../util/redis_manager.js').getRedisClient(); + const options = { uri: `${fConfig.firewallaDiagServerURL}/${data.gw}` || `https://api.firewalla.com/diag/api/v1/device/${data.gw}`, method: 'POST', @@ -101,6 +121,46 @@ class FWDiag { log.info("submitted info to diag server successfully with result", result); } } + + async prepareHelloData() { + const inter = await this.getNetworkInfo(); + + const firewallaIP = inter.ip_address; + const mac = inter.mac_address; + const gateway = inter.gateway_ip; + + const version = this.getVersion(); + + const [gatewayMac, branch, longVersion, memory] = await require('bluebird').all([ + this.getGatewayMac(gateway), + this.getBranchInfo(), + this.getLongVersion(), + this.getTotalMemory() + ]); + + return { + mac, + firewallaIP, + gatewayMac, + branch, + version, + longVersion, + memory, + model, + serial + }; + } + + async sayHello() { + const data = await this.prepareHelloData(); + const options = { + uri: `${fConfig.firewallaDiagServerURL}/hello` || `https://api.firewalla.com/diag/api/v1/device/hello`, + method: 'POST', + json: data + } + await rp(options); + log.info("said hello to Firewalla Cloud"); + } } module.exports = new FWDiag(); \ No newline at end of file diff --git a/extension/upnp/upnp.js b/extension/upnp/upnp.js index 877a7faafe..3cedd86117 100644 --- a/extension/upnp/upnp.js +++ b/extension/upnp/upnp.js @@ -24,13 +24,14 @@ */ 'use strict'; - + let instance = null; let log = null; -let fs = require('fs'); let util = require('util'); +let _ = require('lodash'); + let f = require('../../net2/Firewalla.js'); let natpmp = require('./nat-pmp'); @@ -38,16 +39,59 @@ let natupnp = require('./nat-upnp'); let upnpClient = natupnp.createClient(); //upnpClient.timeout = 10000; // set timeout to 10 seconds to avoid timeout too often -let natpmpTimeout = 86400; +let natpmpTimeout = 86400; // 1 day = 24 * 60 * 60 seconds + +let upnpIntervalHandler = null; +let upnpMappings = []; +const upnpCheckInterval = 15 * 60 * 1000 // 15 mins + +function mappingCompare(natUpnpMapping, localMapping) { + // "==" is used instead of "===" with intention here to enable comparison between number and string + return natUpnpMapping.public.port == localMapping.externalPort && + natUpnpMapping.private.port == localMapping.localPort && + natUpnpMapping.protocol === localMapping.protocol; +} module.exports = class { - constructor(loglevel,gw) { + constructor(loglevel, gw) { if (instance == null) { log = require("../../net2/logger.js")("upnp.js", loglevel || "info"); this.gw = gw; instance = this; this.refreshTimers = {}; + + upnpIntervalHandler = setInterval( + () => { + log.info("UPnP periodical check starts") + if (upnpMappings.isEmpty) { + log.info("No mapping registered.") + return; + } + upnpClient.getMappings((err, results) => { + if (err) { + log.error("Failed to get current mappings", err); + return; + } + log.info("Current mappings: ", results); + + upnpMappings.forEach((check) => { + log.info("Checking registered mapping:", check); + if (_.isEmpty( + results.find((m) => mappingCompare(m, check)) + )) { + log.info("Mapping no longer exists, adding back to router...") + let { protocol, localPort, externalPort, description } = check; + this.addPortMappingUPNP(protocol, localPort, externalPort, description) + } else { + log.info("Mapping still exists") + } + }) + }) + }, + upnpCheckInterval + ) } + return instance; } @@ -57,53 +101,53 @@ module.exports = class { this._natpmpClient = natpmp.connect(this.gw); } return this._natpmpClient; - } catch(e) { - log.error("UPNP:natpmpClient Unable to initalize", e,{}); + } catch (e) { + log.error("UPNP:natpmpClient Unable to initalize", e, {}); } } - + /* return if NATPMP or UPNP * */ getCapability(callback) { - try { - upnpClient.externalIp((err,ip)=>{ - if (err !=null || ip == null) { - this.upnpEnabled = false; - if (this.natpmpClient()) { - - let timeout = true; - setTimeout(() => { - if(timeout) { - callback(null, this.upnpEnabled, false) - } - }, 5 * 1000) - - this.natpmpClient().externalIp((err, info)=> { - if (err == null && info!=null) { - this.natpmpIP = info.ip.join('.'); - this.natpmpEnabled = true; - } else { - this.natpmpEnabled = false; - } - timeout = false - callback(null, this.upnpEnabled, this.natpmpEnabled); - }); + try { + upnpClient.externalIp((err, ip) => { + if (err != null || ip == null) { + this.upnpEnabled = false; + if (this.natpmpClient()) { + + let timeout = true; + setTimeout(() => { + if (timeout) { + callback(null, this.upnpEnabled, false) + } + }, 5 * 1000) + + this.natpmpClient().externalIp((err, info) => { + if (err == null && info != null) { + this.natpmpIP = info.ip.join('.'); + this.natpmpEnabled = true; + } else { + this.natpmpEnabled = false; + } + timeout = false + callback(null, this.upnpEnabled, this.natpmpEnabled); + }); + } + } else { + this.upnpIP = ip; + this.upnpEnabled = true; + callback(null, this.upnpEnabled, this.natpmpEnabled); } - } else { - this.upnpIP = ip; - this.upnpEnabled = true; - callback(null, this.upnpEnabled, this.natpmpEnabled); - } - }); - } catch(e) { - log.error("UPNP.getCapability exception ", e,{}); - } - - } + }); + } catch (e) { + log.error("UPNP.getCapability exception ", e, {}); + } + + } addPortMapping(protocol, localPort, externalPort, description, callback) { - this.getCapability(()=>{ + this.getCapability(() => { try { if (this.upnpEnabled == true) { return this.addPortMappingUPNP(protocol, localPort, externalPort, description, callback); @@ -112,14 +156,15 @@ module.exports = class { } else { callback(new Error("no upnp/natpmp")); } - } catch(e) { - log.error("UPNP.addPortMapping exception",e,{}); + } catch (e) { + log.error("UPNP.addPortMapping exception", e, {}); } }); } addPortMappingUPNP(protocol, localPort, externalPort, description, callback) { - callback = callback || function() {}; + callback = callback || function () { }; + upnpClient.portMapping({ type: protocol, protocol: protocol, @@ -128,64 +173,72 @@ module.exports = class { ttl: 0, // set ttl to 0 for better compatibility description: description }, (err) => { - if(err) { - log.error("Failed to add port mapping ", description, " :", err); - if(callback) { - callback(err); - } - return; - } - log.info(util.format("Port mapping [%s, %s, %s] is added successfully.", - protocol, localPort, externalPort)); - - if(callback) { - callback(); - } + if (err) { + log.error("Failed to add port mapping ", description, " :", err); + callback(err); + return; + } + log.info(util.format("Port mapping [%s, %s, %s] is added successfully.", + protocol, localPort, externalPort)); + + let mappingObj = { protocol, localPort, externalPort, description }; + // check if mapping registered + if (_.isEmpty(upnpMappings.find((m) => + m.localPort == localPort && + m.externalPort == externalPort && + m.protocol === protocol + ))) { + upnpMappings.push(mappingObj); + } else { + log.info("Mapping handler already exists"); + } + + callback(err); }); } - + addPortMappingNATPMP(protocol, localPort, externalPort, description, callback) { - callback = callback || function() {}; + callback = callback || function () { }; if (this.natpmpClient() == null) { - callback(new Error("natpmpClient null"),null); + callback(new Error("natpmpClient null"), null); return; } - this.natpmpClient().portMapping({type:protocol, private: localPort, public: externalPort, ttl: natpmpTimeout}, (err, info)=> { + this.natpmpClient().portMapping({ type: protocol, private: localPort, public: externalPort, ttl: natpmpTimeout }, (err, info) => { if (err == null) { - this.refreshTimers[localPort+":"+externalPort] = setTimeout(()=>{ - this.addPortMappingNATPMP(protocol,localPort, externalPort, description,()=>{ - }); - }, natpmpTimeout/2*1000); + this.refreshTimers[localPort + ":" + externalPort] = setTimeout(() => { + this.addPortMappingNATPMP(protocol, localPort, externalPort, description, () => { + }); + }, natpmpTimeout / 2 * 1000); } - callback(err,info); + callback(err, info); }); } removePortMappingNATPMP(protocol, localPort, externalPort, callback) { - callback = callback || function() {}; - let timer = this.refreshTimers[localPort+":"+externalPort]; + callback = callback || function () { }; + let timer = this.refreshTimers[localPort + ":" + externalPort]; if (this.natpmpClient() == null) { - callback(new Error("natpmpClient null"),null); + callback(new Error("natpmpClient null"), null); return; } if (timer) { clearTimeout(timer); } - this.natpmpClient().portUnmapping({ type:protocol, private: localPort, public: externalPort, ttl: 0}, (err, info)=> { + this.natpmpClient().portUnmapping({ type: protocol, private: localPort, public: externalPort, ttl: 0 }, (err, info) => { if (err) { - log.error("UPNP.removePortMappingNATPMP",err,{}); + log.error("UPNP.removePortMappingNATPMP", err, {}); } - callback(err,info); + callback(err, info); }); } removePortMapping(protocol, localPort, externalPort, callback) { - callback = callback || function() {} - this.getCapability(()=>{ + callback = callback || function () { } + this.getCapability(() => { try { if (this.upnpEnabled == true) { - return this.removePortMappingUPNP(protocol, localPort, externalPort,callback); + return this.removePortMappingUPNP(protocol, localPort, externalPort, callback); } else if (this.natpmpEnabled == true) { return this.removePortMappingNATPMP(protocol, localPort, externalPort, callback); } else { @@ -193,32 +246,38 @@ module.exports = class { callback(new Error("no upnp/natpmp")); } } - } catch(e) { - log.error("UPNP.removePortMapping Exception",e,{}); + } catch (e) { + log.error("UPNP.removePortMapping Exception", e, {}); } }); } removePortMappingUPNP(protocol, localPort, externalPort, callback) { - callback = callback || function() {}; - + callback = callback || function () { }; + upnpClient.portUnmapping({ protocol: protocol, private: localPort, public: externalPort }, (err) => { - if(err) { + if (err) { log.error("UPNP Failed to remove port mapping: " + err); - if(callback) { + if (callback) { callback(err); } return; } + upnpMappings = _.reject(upnpMappings, (m) => + m.localPort == localPort && + m.externalPort == externalPort && + m.protocol === protocol + ); + log.info(util.format("Port mapping [%s, %s, %s] is removed successfully" , protocol, localPort, externalPort)); - if(callback) { + if (callback) { callback(); } }); @@ -238,29 +297,18 @@ module.exports = class { // local: true // description: description }, (err, results) => { - if(err) { + if (err) { log.error("Failed to get upnp mappings"); callback(err); return; - } - console.log(util.inspect(results)); - let matches = results.filter((r) => { - console.log(r); - return r.public.port === externalPort && - r.private.port === localPort && - r.protocol === protocol && - r.description === description; - }); + } + log.debug(util.inspect(results)); + let matches = results.find((r) => mappingCompare(r, {protocol, localPort, externalPort})); console.log(util.inspect(matches)); - if(matches.length > 0) { - callback(null, true); - } else { - callback(null, false); - } + + callback(null, !matches.isEmpty) }); - } + } } - - diff --git a/lib/Bone.js b/lib/Bone.js index ddd6ea6940..7d9210be64 100644 --- a/lib/Bone.js +++ b/lib/Bone.js @@ -306,7 +306,7 @@ exports.checkin = function(config, license, info, callback) { redis: JSON.stringify({ 'memory': rclient.server_info.used_memory }), - cpuid: utils.getCpuId(), + cpuid: platform.getBoardSerial() || "0", mac: info.mac, gid: gid, sysinfo: JSON.stringify(info), diff --git a/monitor/MonitorMain.js b/monitor/MonitorMain.js index ef00dbfb15..c82c2d5ea5 100644 --- a/monitor/MonitorMain.js +++ b/monitor/MonitorMain.js @@ -244,4 +244,4 @@ sem.on("ChangeLogLevel", (event) => { require('../net2/LoggerManager.js').setLogLevel(event.name, event.level); } } -}); \ No newline at end of file +}); diff --git a/net2/DNSTool.js b/net2/DNSTool.js index b6018b3c62..732656cc0a 100644 --- a/net2/DNSTool.js +++ b/net2/DNSTool.js @@ -103,16 +103,18 @@ class DNSTool { const existing = await this.reverseDNSKeyExists(dns) let updated = false + const validAddresses = []; for (let i = 0; i < addresses.length; i++) { const addr = addresses[i]; if(iptool.isV4Format(addr) || iptool.isV6Format(addr)) { await rclient.zaddAsync(key, new Date() / 1000, addr) - await domainUpdater.updateDomainMapping(dns, addresses); + validAddresses.push(addr); updated = true } } + await domainUpdater.updateDomainMapping(dns, validAddresses); if(updated === false && existing === false) { await rclient.zaddAsync(key, new Date() / 1000, RED_HOLE_IP); // red hole is a placeholder ip for non-existing domain diff --git a/net2/Discovery.js b/net2/Discovery.js index 6bfddaa3d0..caf713a58b 100644 --- a/net2/Discovery.js +++ b/net2/Discovery.js @@ -283,6 +283,7 @@ module.exports = class { mac:list[i].mac_address.toUpperCase(), ipv4Addr:list[i].ip_address, ipv6Addr:list[i].ip6_addresses || JSON.stringify([]), + macVendor:"Firewalla" }; this.processHost(host); } diff --git a/net2/HostManager.js b/net2/HostManager.js index 4fae8d2168..e82e501d87 100644 --- a/net2/HostManager.js +++ b/net2/HostManager.js @@ -1470,7 +1470,7 @@ module.exports = class HostManager { json.network.ip_address = fConfig.docker.hostIP; } - json.cpuid = utils.getCpuId(); + json.cpuid = platform.getBoardSerial(); json.uptime = process.uptime() if(sysManager.language) { @@ -1505,7 +1505,7 @@ module.exports = class HostManager { json.isBeta = false } - json.cpuid = utils.getCpuId() + json.cpuid = platform.getBoardSerial(); json.updateTime = Date.now(); if (sysManager.sshPassword) { json.ssh = sysManager.sshPassword; diff --git a/net2/PolicyManager.js b/net2/PolicyManager.js index fd8ed5f5cc..60ef0014d7 100644 --- a/net2/PolicyManager.js +++ b/net2/PolicyManager.js @@ -424,7 +424,7 @@ module.exports = class { return; // doesn't support per-device policy } - let vpnManager = new VpnManager('info'); + let vpnManager = new VpnManager(); vpnManager.configure(config, false, (err) => { if (err != null) { log.error("PolicyManager:VPN", "Failed to configure vpn"); diff --git a/net2/config.js b/net2/config.js index 35aa4e3cc1..bcf4fe6b04 100644 --- a/net2/config.js +++ b/net2/config.js @@ -5,14 +5,10 @@ let log = require("./logger.js")(__filename, "info"); let fs = require('fs'); let f = require('./Firewalla.js'); -const redis = require('redis') const rclient = require('../util/redis_manager.js').getRedisClient() const sclient = require('../util/redis_manager.js').getSubscriptionClient() const pclient = require('../util/redis_manager.js').getPublishClient() -const async = require('asyncawait/async'); -const await = require('asyncawait/await'); - const dynamicConfigKey = "sys:features" var dynamicConfigs = {} @@ -130,39 +126,31 @@ function isFeatureOn(featureName) { } } -function syncDynamicFeaturesConfigs() { - return async(() => { - let configs = await (rclient.hgetallAsync(dynamicConfigKey)) - if(configs) { - dynamicConfigs = configs - } else { - dynamicConfigs = {} - } - })() +async function syncDynamicFeaturesConfigs() { + let configs = await rclient.hgetallAsync(dynamicConfigKey); + if(configs) { + dynamicConfigs = configs + } else { + dynamicConfigs = {} + } } -function enableDynamicFeature(featureName) { - return async(() => { - await (rclient.hsetAsync(dynamicConfigKey, featureName, '1')) - pclient.publish("config:feature:dynamic:enable", featureName) - dynamicConfigs[featureName] = '1' - })() +async function enableDynamicFeature(featureName) { + await rclient.hsetAsync(dynamicConfigKey, featureName, '1'); + pclient.publish("config:feature:dynamic:enable", featureName) + dynamicConfigs[featureName] = '1' } -function disableDynamicFeature(featureName) { - return async(() => { - await (rclient.hsetAsync(dynamicConfigKey, featureName, '0')) - pclient.publish("config:feature:dynamic:disable", featureName) - dynamicConfigs[featureName] = '0' - })() +async function disableDynamicFeature(featureName) { + await rclient.hsetAsync(dynamicConfigKey, featureName, '0'); + pclient.publish("config:feature:dynamic:disable", featureName) + dynamicConfigs[featureName] = '0' } -function clearDynamicFeature(featureName) { - return async(() => { - await (rclient.hdel(dynamicConfigKey, featureName)) - pclient.publish("config:feature:dynamic:clear", featureName) - delete dynamicConfigs[featureName] - })() +async function clearDynamicFeature(featureName) { + await rclient.hdel(dynamicConfigKey, featureName); + pclient.publish("config:feature:dynamic:clear", featureName) + delete dynamicConfigs[featureName] } function getDynamicConfigs() { diff --git a/net2/config.json b/net2/config.json index a438a4a8a9..ffe98d9dbf 100644 --- a/net2/config.json +++ b/net2/config.json @@ -289,5 +289,4 @@ "pubKeys": [ ] - } diff --git a/net2/main.js b/net2/main.js index bcbbfa1a5a..4c2aa3bc84 100644 --- a/net2/main.js +++ b/net2/main.js @@ -340,7 +340,7 @@ function run() { this will kick off vpnManager, and later policy manager should stop the VpnManager if needed */ setTimeout(()=>{ - var vpnManager = new VpnManager('info'); + var vpnManager = new VpnManager(); hostManager.loadPolicy((err, data) => { if (err != null) { log.error("Failed to load system policy for VPN", err); @@ -421,4 +421,4 @@ sem.on("ChangeLogLevel", (event) => { require('./LoggerManager.js').setLogLevel(event.name, event.level); } } -}); \ No newline at end of file +}); diff --git a/platform/PlatformLoader.js b/platform/PlatformLoader.js index c712c45a65..09bfbe444b 100644 --- a/platform/PlatformLoader.js +++ b/platform/PlatformLoader.js @@ -17,10 +17,6 @@ let instance = null; -const RedPlatform = require('./red/RedPlatform.js'); -const BluePlatform = require('./blue/BluePlatform.js'); -const DockerPlatform = require('./docker/DockerPlatform.js'); - const execSync = require('child_process').execSync; class PlatformLoader { @@ -49,16 +45,19 @@ class PlatformLoader { switch (uname) { case "aarch64": + const BluePlatform = require('./blue/BluePlatform.js'); this.platform = new BluePlatform(); break; case "armv7l": + const RedPlatform = require('./red/RedPlatform.js'); this.platform = new RedPlatform(); break; case "x86_64": + const DockerPlatform = require('./docker/DockerPlatform.js'); this.platform = new DockerPlatform(); + break; default: return null; - break; } return this.platform; diff --git a/scripts/bootstrap.sha256sum b/scripts/bootstrap.sha256sum index a659a9c8be..80ba532212 100644 --- a/scripts/bootstrap.sha256sum +++ b/scripts/bootstrap.sha256sum @@ -3,4 +3,4 @@ 39539f51a04e9e699bfae4886b137496112ca0dff4faae78ed2fb0234491c7e6 check_reset.sh 3257a1b5c7736488bf8a8e9cef22eef90f718dad8daccff956dd3f80286da32f ../etc/fireupgrade2.service 365eae38c6addb87a4a92ffae5461762fbe7827e12fa045d63679e94f789ad86 mgit -6eaa1a5675aa6cd01386f15b0a61f1d4e8a481d1795f6a57af4083f99d267f15 fire-ping.sh +e5c2e534ceefcd216473f223fadec9badd7d8b58ac589c2f608b6a89a804833d fire-ping.sh diff --git a/scripts/diag_hello.js b/scripts/diag_hello.js new file mode 100644 index 0000000000..b9ae63be4b --- /dev/null +++ b/scripts/diag_hello.js @@ -0,0 +1,12 @@ +'use strict' + +const fwDiag = require("../extension/install/diag.js"); + +(async () => { + try { + await fwDiag.sayHello(); + } catch(err) { + console.log("Failed to say hello:", err.statusCode); + } + process.exit(0); +})(); diff --git a/scripts/diag_hello.sh b/scripts/diag_hello.sh new file mode 100755 index 0000000000..a55f35a5b8 --- /dev/null +++ b/scripts/diag_hello.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +: ${FIREWALLA_HOME:=/home/pi/firewalla} + +cd $FIREWALLA_HOME + +$FIREWALLA_HOME/bin/node scripts/diag_hello.js \ No newline at end of file diff --git a/scripts/fire-ping.sh b/scripts/fire-ping.sh index 92c321440a..23bf0c2a40 100755 --- a/scripts/fire-ping.sh +++ b/scripts/fire-ping.sh @@ -46,9 +46,15 @@ for i in `seq 1 10`; do then exit 0 else - /home/pi/firewalla/scripts/firelog -t debug -m "FIREWALLA PING NO Local Network $DEFAULT_ROUTE" - sleep 1 - touch /tmp/watchdog + echo "Ping backup domain $BACKUP_DOMAIN failed. Trying curl instead ..." + if curl -s "https://$BACKUP_DOMAIN" &> /dev/null + then + exit 0 + else + /home/pi/firewalla/scripts/firelog -t debug -m "FIREWALLA PING NO Local Network $DEFAULT_ROUTE" + sleep 1 + touch /tmp/watchdog + fi fi fi done diff --git a/scripts/main-start b/scripts/main-start index 3506f8d547..4a10f57973 100755 --- a/scripts/main-start +++ b/scripts/main-start @@ -68,6 +68,8 @@ echo $branch > /tmp/REPO_BRANCH node $HOME/firewalla/scripts/diag_status.js --event "MAIN_START" --message "Main starting $branch $current_tag " &>/dev/null & +node $HOME/firewalla/scripts/diag_hello.js &>/dev/null & + function pre_main() { source $FIREWALLA_HOME/scripts/utils.sh setup_folders diff --git a/sensor/DNSMASQSensor.js b/sensor/DNSMASQSensor.js index 47459a6eae..0fbf530c95 100644 --- a/sensor/DNSMASQSensor.js +++ b/sensor/DNSMASQSensor.js @@ -31,6 +31,8 @@ class DNSMASQSensor extends Sensor { this.dhcpMode = false; this.registered = false; + this.started = false; + this.eventBuffer = []; } _start() { @@ -64,6 +66,19 @@ class DNSMASQSensor extends Sensor { }) } + _bufferEvent(event) { + log.info("Buffering event: " + event.type); + this.eventBuffer.push(event); + } + + _emitBufferedEvent() { + if (this.eventBuffer && this.eventBuffer.length > 0) { + this.eventBuffer.forEach((event) => { + sem.emitEvent(event); + }); + } + } + _run() { // always start dnsmasq return Mode.getSetupMode() @@ -72,47 +87,65 @@ class DNSMASQSensor extends Sensor { dnsmasq.setDhcpMode(true); } + if(!this.registered) { + log.info("Registering dnsmasq events listeners"); + + sem.on("StartDNS", (event) => { + // NO NEED TO RELOAD DNSMASQ if it's gone, it's going to be managed by systemctl + // dnsmasq.checkStatus((status) => { + // if(!status) { + // this.reload(); + // } + // }) + }); + + sem.on("StopDNS", (event) => { + // ignore StopDNS, as now it will always start as daemon process + }); + + sem.on("StartDHCP", (event) => { + if (!this.started) { + this._bufferEvent(event); + } else { + log.info("Starting DHCP") + dnsmasq.enableDHCP(); + } + }); + + sem.on("StopDHCP", (event) => { + if (!this.started) { + this._bufferEvent(event); + } else { + dnsmasq.disableDHCP(); + } + }); + + sem.on("ReloadDNSRule", (event) => { + if (!this.started) { + this._bufferEvent(event); + } else { + this.reload(); + } + }); + + sem.on("VPNSubnetChanged", (event) => { + if (!this.started) { + this._bufferEvent(event); + } else { + const subnet = event.vpnSubnet; + if (subnet) { + dnsmasq.updateVpnIptablesRules(subnet); + } + } + }); + + this.registered = true; + } + return this._start() .then(() => { - if(!this.registered) { - log.info("Registering dnsmasq events listeners"); - - sem.on("StartDNS", (event) => { - // NO NEED TO RELOAD DNSMASQ if it's gone, it's going to be managed by systemctl - // dnsmasq.checkStatus((status) => { - // if(!status) { - // this.reload(); - // } - // }) - }); - - sem.on("StopDNS", (event) => { - // ignore StopDNS, as now it will always start as daemon process - }); - - sem.on("StartDHCP", (event) => { - log.info("Starting DHCP") - dnsmasq.enableDHCP(); - }); - - sem.on("StopDHCP", (event) => { - dnsmasq.disableDHCP(); - }); - - sem.on("ReloadDNSRule", (event) => { - this.reload(); - }); - - sem.on("VPNSubnetChanged", (event) => { - const subnet = event.vpnSubnet; - if (subnet) { - dnsmasq.updateVpnIptablesRules(subnet); - } - }); - - - this.registered = true; - } + this.started = true; + this._emitBufferedEvent(); }) }) } diff --git a/vpn/VpnManager.js b/vpn/VpnManager.js index e92a4a41e4..86265e6401 100644 --- a/vpn/VpnManager.js +++ b/vpn/VpnManager.js @@ -26,11 +26,6 @@ var later = require('later'); var publicIp = require('public-ip'); var fs = require('fs'); -var network = require('network'); -var natpmp = require('nat-pmp'); -var natupnp = require('nat-upnp'); -var ipTool = require('ip'); -var async = require('async'); const sem = require('../sensor/SensorEventManager.js').getInstance(); var util = require('util'); @@ -38,67 +33,22 @@ var util = require('util'); var linux = require('../util/linux'); var UPNP = require('../extension/upnp/upnp.js'); -var ttlExpire = 12*60*60; +var ttlExpire = 12 * 60 * 60; module.exports = class { constructor() { if (instance == null) { - this.upnp = new UPNP("info",sysManager.myGateway()); + this.upnp = new UPNP("info", sysManager.myGateway()); instance = this; } return instance; } - punchNat(opts, success, error) { - opts = opts || {} - opts.timeout = opts.timeout || 5000 - - log.info("VpnManager:PunchNat",opts); - - let replied = false; - // returns the IP of the gateway that your active network interface is linked to - linux.gateway_ip((err, gateway) => { - if (err) return error(err) - log.info("VpnManager:PublicGateway Checking",gateway,gateway.trim(),gateway.length); - if (ipTool.isPublic(gateway.trim())==true) { - log.info("VpnManager:PublicGateway True",gateway); - return success(gateway) - } - - if (this.upnpClient == null) { - this.upnpClient = natupnp.createClient(); - } - this.upnpClient.portMapping(opts, (err) => { - if (err == null) { - log.info("VpnManager:NatUPNP Success"); - if (replied == false) { - replied = true; - success(null, "success", null); - } - } else { - if (replied == false) { - log.error("VpnManager:NatUPNP failed",err); - replied = true; - error(err); - } - } - setTimeout(() => { - if (this.upnpClient) { - this.upnpClient.close(); - this.upnpClient = null; - } else { - log.error("VpnManager:NatUPNP resetupnp client null"); - } - },1000); - }); - }); - } - setIptables(callback) { const serverNetwork = this.serverNetwork; const localIp = sysManager.myIp(); log.info("VpnManager:SetIptables", serverNetwork, localIp); - const commands =[ + const commands = [ `sudo iptables -w -t nat -C POSTROUTING -s ${serverNetwork}/24 -o eth0 -j SNAT --to-source ${localIp} &>/dev/null && (sudo iptables -w -t nat -D POSTROUTING -s ${serverNetwork}/24 -o eth0 -j SNAT --to-source ${localIp} || false)|| true`, `sudo iptables -w -t nat -I POSTROUTING 2 -s ${serverNetwork}/24 -o eth0 -j SNAT --to-source ${localIp}` // insert this rule next to first rule of POSTROUTING ]; @@ -109,7 +59,7 @@ module.exports = class { const serverNetwork = this.serverNetwork; const localIp = sysManager.myIp(); log.info("VpnManager:UnsetIptables", serverNetwork, localIp); - const commands =[ + const commands = [ `sudo iptables -w -t nat -C POSTROUTING -s ${serverNetwork}/24 -o eth0 -j SNAT --to-source ${localIp} &>/dev/null && (sudo iptables -w -t nat -D POSTROUTING -s ${serverNetwork}/24 -o eth0 -j SNAT --to-source ${localIp} || false)|| true`, ]; iptable.run(commands, callback); @@ -117,7 +67,7 @@ module.exports = class { unpunchNat(opts, callback) { log.info("VpnManager:UnpunchNat", opts); - this.upnp.removePortMapping(opts.protocol, opts.private,opts.public,(err)=>{ + this.upnp.removePortMapping(opts.protocol, opts.private, opts.public, (err) => { if (callback) { callback(err); } @@ -125,14 +75,14 @@ module.exports = class { } install(instance, callback) { - let install1_cmd = util.format('cd %s/vpn; sudo -E ./install1.sh %s', fHome, instance); + let install1_cmd = util.format('cd %s/vpn; sudo -E ./install1.sh %s', fHome, instance); this.install1 = require('child_process').exec(install1_cmd, (err, out, code) => { if (err) { - log.error("VPNManager:INSTALL:Error", "Unable to install1.sh for " + instance, err); + log.error("VPNManager:INSTALL:Error", "Unable to install1.sh for " + instance, err); } if (err == null) { // !! Pay attention to the parameter "-E" which is used to preserve the - // enviornment valueables when running sudo commands + // enviornment variables when running sudo commands let install2_cmd = util.format("cd %s/vpn; sudo -E ./install2.sh %s", fHome, instance); log.info("VPNManager:INSTALL:cmd", install2_cmd); this.install2 = require('child_process').exec(install2_cmd, (err, out, code) => { @@ -149,7 +99,7 @@ module.exports = class { callback(null, null); }); } else { - if (callback) + if (callback) callback(err, null); } }); @@ -176,7 +126,7 @@ module.exports = class { if (needRestart === true) { this.needRestart = true; } - var mydns = sysManager.myDNS()[0]; + var mydns = sysManager.myDNS()[0]; if (mydns == null) { mydns = "8.8.8.8"; // use google DNS as default } @@ -216,46 +166,11 @@ module.exports = class { }); } - setNat(callback) { - if (this.started == false) { - if (callback) - callback(null, null, null); - return; - } - this.punchNat({ - type: 'udp', - protocol: 'udp', - private: this.localPort, - public: this.localPort, - ttl: 0, - description: "Firewalla VPN" - }, (external) => { - log.info("VpnManager:Start:portMap", external); - setTimeout(() => { - log.info("VpnManager:Restart:portMap"); - this.setNat(null) - }, ttlExpire/3*1000); - if (callback) { - this.portmapped = true; - callback(null, external, this.localPort); - } - }, (err) => { - log.info("VpnManager:Start:portMap:Failed: " + err); - setTimeout(() => { - log.info("VpnManager:Restart:portMap"); - this.setNat(null) - }, ttlExpire/3*1000); - if (callback) { - callback(null, null, null); - } - }) - } - start(callback) { if (this.started && !this.needRestart) { log.info("VpnManager::StartedAlready"); if (callback) - callback(null, this.portmapped, this.portmapped, this.serverNetwork, this.localPort); + callback(null, this.portmapped, this.portmapped, this.serverNetwork, this.localPort); return; } @@ -265,12 +180,12 @@ module.exports = class { } this.upnp.gw = sysManager.myGateway(); - + this.unpunchNat({ protocol: 'udp', private: this.localPort, public: this.localPort - },(err)=>{ + }, (err) => { let op = "start"; if (this.needRestart) { op = "restart"; @@ -291,19 +206,19 @@ module.exports = class { this.stop(); if (callback) { callback(err); - } + } } else { - this.upnp.addPortMapping("udp",this.localPort,this.localPort,"Firewalla OpenVPN",(err)=>{ // public port and private port is equivalent by default + this.upnp.addPortMapping("udp", this.localPort, this.localPort, "Firewalla OpenVPN", (err) => { // public port and private port is equivalent by default log.info("VpnManager:UPNP:SetDone", err); sem.emitEvent({ type: "VPNSubnetChanged", message: "VPN subnet is updated", - vpnSubnet: this.serverNetwork, + vpnSubnet: this.serverNetwork + "/24", toProcess: "FireMain" }); if (err) { if (callback) { - callback(null, null, null, this.serverNetwork, this.localPort); + callback(null, null, null, this.serverNetwork, this.localPort); } } else { this.portmapped = true; @@ -313,7 +228,7 @@ module.exports = class { } }); } - }); + }); }); }); } @@ -341,8 +256,8 @@ module.exports = class { if (compressAlg == null) compressAlg = ""; - log.info("Reading ovpn file", ovpn_file,ovpn_password,regenerate); - + log.info("Reading ovpn file", ovpn_file, ovpn_password, regenerate); + fs.readFile(ovpn_file, 'utf8', (err, ovpn) => { if (ovpn != null && regenerate == false) { let password = fs.readFileSync(ovpn_password, 'utf8'); @@ -367,14 +282,14 @@ module.exports = class { ip = sysManager.publicIp; } - var mydns = sysManager.myDNS()[0]; + var mydns = sysManager.myDNS()[0]; if (mydns == null) { mydns = "8.8.8.8"; // use google DNS as default } - + const vpnLockFile = "/dev/shm/vpn_gen_lock_file"; - let cmd = util.format("cd %s/vpn; flock -n %s -c 'sudo -E ./ovpngen.sh %s %s %s %s %s %s'; sync", + let cmd = util.format("cd %s/vpn; flock -n %s -c 'sudo -E ./ovpngen.sh %s %s %s %s %s %s'; sync", fHome, vpnLockFile, clientname, password, ip, this.localPort, originalName, compressAlg); log.info("VPNManager:GEN", cmd); this.getovpn = require('child_process').exec(cmd, (err, out, code) => {