diff --git a/extension/dhcpdump/dhcpdump.js b/extension/dhcpdump/dhcpdump.js index 39e4063c10..c5a5e63ece 100644 --- a/extension/dhcpdump/dhcpdump.js +++ b/extension/dhcpdump/dhcpdump.js @@ -1,4 +1,4 @@ -/* Copyright 2016 Firewalla LLC +/* Copyright 2016-2019 Firewalla Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -17,16 +17,8 @@ let instance = null; let log = null; -let fs = require('fs'); -let util = require('util'); -let jsonfile = require('jsonfile'); - -let f = require('../../net2/Firewalla.js'); -let fHome = f.getFirewallaHome(); - -let userID = f.getUserID(); -let dhcpdumpSpawn = null; -let pid = null; +const util = require('util'); +const networkTool = require('../../net2/NetworkTool')(); module.exports = class { constructor(loglevel) { @@ -152,32 +144,34 @@ OPTION: 12 ( 12) Host name Great-Room-3 } } - rawStart(callback) { - callback = callback || function() {} - - - let spawn = require('child_process').spawn; - let dhcpdumpSpawn = spawn('sudo', ['dhcpdump', '-i', 'eth0']); - let pid = dhcpdumpSpawn.pid; - let StringDecoder = require('string_decoder').StringDecoder; - let decoder = new StringDecoder('utf8'); - - log.info("DHCPDump started with PID: ", pid); - - dhcpdumpSpawn.stdout.on('data', (data) => { - log.debug("Found a dhcpdiscover request"); - let message = decoder.write(data); - - this.parseEvents(message).map(e => callback(e)) - }); - - dhcpdumpSpawn.stderr.on('data', (data) => { - log.error("Got error when running dhcp: ", data); - }); - - dhcpdumpSpawn.on('close', (code) => { - log.info("DHCPDump exited with error code: ", code); - }); + async rawStart(callback) { + callback = callback || function () { } + const interfaces = await networkTool.getLocalNetworkInterface(); + for (const intf of interfaces) { + if(!intf.name) continue; + let spawn = require('child_process').spawn; + let dhcpdumpSpawn = spawn('sudo', ['dhcpdump', '-i', intf.name]); + let pid = dhcpdumpSpawn.pid; + let StringDecoder = require('string_decoder').StringDecoder; + let decoder = new StringDecoder('utf8'); + + log.info("DHCPDump started with PID: ", pid); + + dhcpdumpSpawn.stdout.on('data', (data) => { + log.debug("Found a dhcpdiscover request"); + let message = decoder.write(data); + + this.parseEvents(message).map(e => callback(e)) + }); + + dhcpdumpSpawn.stderr.on('data', (data) => { + log.error("Got error when running dhcp: ", data); + }); + + dhcpdumpSpawn.on('close', (code) => { + log.info("DHCPDump exited with error code: ", code); + }); + } } rawStop(callback) { diff --git a/net2/NetworkTool.js b/net2/NetworkTool.js index 9140e27bca..ca220b72f6 100644 --- a/net2/NetworkTool.js +++ b/net2/NetworkTool.js @@ -116,6 +116,10 @@ class NetworkTool { // same as listInterfaces() but filters out non-local interfaces async getLocalNetworkInterface() { + //todo + //get gold networkInterface from firerouter api + //simple mode br0 + //router mode enabled wans let intfs = fConfig.discovery && fConfig.discovery.networkInterfaces; if (!intfs) { return null; diff --git a/sensor/BonjourSensor.js b/sensor/BonjourSensor.js index ff6bec17ac..94af69eb4a 100644 --- a/sensor/BonjourSensor.js +++ b/sensor/BonjourSensor.js @@ -1,4 +1,4 @@ -/* Copyright 2016 Firewalla LLC +/* Copyright 2016-2019 Firewalla Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -26,6 +26,7 @@ const Promise = require('bluebird'); const SysManager = require('../net2/SysManager.js') const sysManager = new SysManager('info') +const networkTool = require('../net2/NetworkTool')(); const Nmap = require('../net2/Nmap.js'); const nmap = new Nmap(); const l2 = require('../util/Layer2.js'); @@ -40,16 +41,16 @@ const ipMacCache = {}; class BonjourSensor extends Sensor { constructor() { super(); - + this.interfaces = null; this.hostCache = {}; bonjour._server.mdns.on('warning', (err) => log.warn("Warning on mdns server", err)) bonjour._server.mdns.on('error', (err) => log.error("Error on mdns server", err)) } - run() { + async run() { log.info("Bonjour Watch Starting"); - + this.interfaces = await networkTool.getLocalNetworkInterface(); if (this.bonjourBrowserTCP == null) { this.bonjourBrowserTCP = bonjour.find({ protocol: 'tcp' @@ -132,13 +133,16 @@ class BonjourSensor extends Sensor { resolve(null); } else { if (!mac) { - if (ipAddr === sysManager.myIp()) { - resolve(sysManager.myMAC()); - } else if (ipAddr === sysManager.myWifiIp()) { - resolve(sysManager.myWifiMAC()); - } else { - log.error("Not able to find mac address for host:", ipAddr, mac); - resolve(null); + for (const intf of this.interfaces) { + const intfName = intf.name; + if (ipAddr === sysManager.myIp(intfName)) { + resolve(sysManager.myMAC(intfName)); + } else if (ipAddr === sysManager.myWifiIp(intfName)) { + resolve(sysManager.myWifiMAC(intfName)); + } else { + log.error("Not able to find mac address for host:", ipAddr, mac); + resolve(null); + } } } else { ipMacCache[ipAddr] = { mac: mac, lastSeen: Date.now() / 1000 }; @@ -153,8 +157,11 @@ class BonjourSensor extends Sensor { return null; }) if (!mac) { - if (sysManager.myIp6() && sysManager.myIp6().includes(ipAddr)) { - mac = sysManager.myMAC(); + for (const intf of this.interfaces) { + const intfName = intf.name; + if (sysManager.myIp6(intfName) && sysManager.myIp6(intfName).includes(ipAddr)) { + mac = sysManager.myMAC(intfName); + } } } else { ipMacCache[ipAddr] = { mac: mac, lastSeen: Date.now() / 1000 }; diff --git a/sensor/ICMP6Sensor.js b/sensor/ICMP6Sensor.js index 1836fb4a03..1eddc40b8d 100644 --- a/sensor/ICMP6Sensor.js +++ b/sensor/ICMP6Sensor.js @@ -1,4 +1,4 @@ -/* Copyright 2019 Firewalla LLC +/* Copyright 2019 Firewalla Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -25,7 +25,7 @@ const sem = require('../sensor/SensorEventManager.js').getInstance(); const Sensor = require('./Sensor.js').Sensor; const SysManager = require('../net2/SysManager.js'); const Discovery = require('../net2//Discovery.js'); - +const networkTool = require('../net2/NetworkTool')(); const cp = require('child_process'); const execAsync = util.promisify(cp.exec); const spawn = cp.spawn; @@ -34,47 +34,35 @@ class ICMP6Sensor extends Sensor { constructor() { super(); this.myMac = null; + this.interfaces = null; } run() { (async () => { - try { - const result = await execAsync("cat /sys/class/net/eth0/address"); - this.myMac = result.stdout.trim().toUpperCase(); - } catch (err) { - log.warn("Failed to get self MAC address from /sys/class/net/eth0/address"); - } - if (!this.myMac) { - const d = new Discovery("ICMP6Sensor", fConfig, "info", false); - await d.discoverInterfacesAsync(); - const sysManager = new SysManager(); - await sysManager.updateAsync(); - this.myMac = sysManager.myMAC().toUpperCase(); + this.interfaces = await networkTool.getLocalNetworkInterface(); + for (const intf of this.interfaces) { + if (!intf.name || !intf.mac_address) continue; + // listen on icmp6 neighbor-advertisement which is not sent from firewalla + const tcpdumpSpawn = spawn('sudo', ['tcpdump', '-i', intf.name, '-en', `!(ether src ${intf.mac_address}) && icmp6 && ip6[40] == 136`]); + const pid = tcpdumpSpawn.pid; + log.info("TCPDump icmp6 started with PID: ", pid); + const reader = readline.createInterface({ + input: tcpdumpSpawn.stdout + }); + reader.on('line', (line) => { + this.processNeighborAdvertisement(line); + }); + tcpdumpSpawn.on('close', (code) => { + log.info("TCPDump icmp6 exited with code: ", code); + }) } - - if (!this.myMac) { + if (!this.interfaces) { setTimeout(() => { log.info("Failed to get self MAC address from sysManager, retry in 60 seconds."); this.run(); }, 60000); return; } - - // listen on icmp6 neighbor-advertisement which is not sent from firewalla - const tcpdumpSpawn = spawn('sudo', ['tcpdump', '-i', 'eth0', '-en', `!(ether src ${this.myMac}) && icmp6 && ip6[40] == 136`]); - const pid = tcpdumpSpawn.pid; - log.info("TCPDump icmp6 started with PID: ", pid); - - const reader = readline.createInterface({ - input: tcpdumpSpawn.stdout - }); - reader.on('line', (line) => { - this.processNeighborAdvertisement(line); - }); - - tcpdumpSpawn.on('close', (code) => { - log.info("TCPDump icmp6 exited with code: ", code); - }) })().catch((err) => { log.error("Failed to run ICMP6Sensor", err); }); diff --git a/sensor/IPv6DiscoverySensor.js b/sensor/IPv6DiscoverySensor.js index c381c9cb5a..7e71cd0a61 100644 --- a/sensor/IPv6DiscoverySensor.js +++ b/sensor/IPv6DiscoverySensor.js @@ -1,4 +1,4 @@ -/* Copyright 2016 Firewalla LLC +/* Copyright 2016-2019 Firewalla Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -34,11 +34,9 @@ const execAsync = require('child-process-promise').exec class IPv6DiscoverySensor extends Sensor { constructor() { super(); - this.networkInterface = networkTool.getLocalNetworkInterface(); this.enabled = true; // very basic feature, always enabled let p = require('../net2/MessageBus.js'); this.publisher = new p('info','Scan:Done', 10); - log.debug("Starting IPv6DiscoverySensor Interfaces [",this.networkInterface,"]"); } run() { @@ -70,9 +68,9 @@ class IPv6DiscoverySensor extends Sensor { } async ping6ForDiscovery(intf, obj) { - await execAsync("ping6 -c2 -I eth0 ff02::1") + await execAsync(`ping6 -c2 -I ${intf} ff02::1`) return asyncNative.eachLimit(obj.ip6_addresses, 5, async (o) => { - let pcmd = "ping6 -B -c 2 -I eth0 -I " + o + " ff02::1"; + let pcmd = `ping6 -B -c 2 -I ${intf} -I ${o} ff02::1`; log.info("Discovery:v6Neighbor:Ping6", pcmd); return execAsync(pcmd) }) diff --git a/sensor/NmapSensor.js b/sensor/NmapSensor.js index d7ce8b3d9f..e0d526639a 100644 --- a/sensor/NmapSensor.js +++ b/sensor/NmapSensor.js @@ -1,4 +1,4 @@ -/* Copyright 2016 Firewalla LLC +/* Copyright 2016-2019 Firewalla Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -35,7 +35,7 @@ const sysManager = new SysManager('info') class NmapSensor extends Sensor { constructor() { super(); - + this.interfaces = null; this.enabled = true; // very basic feature, always enabled let p = require('../net2/MessageBus.js'); @@ -166,13 +166,12 @@ class NmapSensor extends Sensor { return host; } - async getNetworkRanges() { - let results = await networkTool.getLocalNetworkInterface() - return results && - results.map((x) => networkTool.capSubnet(x.subnet)) + getNetworkRanges() { + return this.interfaces && this.interfaces.map((x) => networkTool.capSubnet(x.subnet)) } - run() { + async run() { + this.interfaces = await networkTool.getLocalNetworkInterface(); process.nextTick(() => { this.checkAndRunOnce(false); }); @@ -184,9 +183,9 @@ class NmapSensor extends Sensor { }, 1000 * 60 * 5); // every 5 minutes, fast scan } - async checkAndRunOnce(fastMode) { + checkAndRunOnce(fastMode) { if (this.isSensorEnabled()) { - let range = await this.getNetworkRanges() + let range = this.getNetworkRanges() return this.runOnce(fastMode, range) } } @@ -246,15 +245,18 @@ class NmapSensor extends Sensor { _processHost(host) { if(!host.mac) { - if(host.ipv4Addr && host.ipv4Addr === sysManager.myIp()) { - host.mac = sysManager.myMAC() - } else if (host.ipv4Addr && host.ipv4Addr === sysManager.myWifiIp()) { - host.mac = sysManager.myWifiMAC(); - } else if(host.ipv4Addr && host.ipv4Addr === sysManager.myIp2()) { - return // do nothing on secondary ip - } else { - log.error("Invalid MAC Address for host", host); - return + for(const intf of this.interfaces){ + const intfName = intf.name; + if(host.ipv4Addr && host.ipv4Addr === sysManager.myIp(intfName)) { + host.mac = sysManager.myMAC(intfName) + } else if (host.ipv4Addr && host.ipv4Addr === sysManager.myWifiIp(intfName)) { + host.mac = sysManager.myWifiMAC(intfName); + } else if(host.ipv4Addr && host.ipv4Addr === sysManager.myIp2(intfName)) { + return // do nothing on secondary ip + } else { + log.error("Invalid MAC Address for host", host); + return + } } } diff --git a/sensor/UPNPSensor.js b/sensor/UPNPSensor.js index efeb96f522..94261d2a06 100644 --- a/sensor/UPNPSensor.js +++ b/sensor/UPNPSensor.js @@ -1,4 +1,4 @@ -/* Copyright 2016 Firewalla LLC +/* Copyright 2016-2019 Firewalla Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -44,7 +44,7 @@ const cfg = require('../net2/config.js'); const SysManager = require('../net2/SysManager.js'); const sysManager = new SysManager('info'); - +const networkTool = require('../net2/NetworkTool')(); const Alarm = require('../alarm/Alarm.js'); const AM2 = require('../alarm/AlarmManager2.js'); const am2 = new AM2(); @@ -55,16 +55,17 @@ const ALARM_UPNP = 'alarm_upnp'; function compareUpnp(a, b) { return a.public && b.public && - a.private && b.private && - a.public.port == b.public.port && - a.private.host == b.private.host && - a.private.port == b.private.port && - a.protocol == b.protocol; + a.private && b.private && + a.public.port == b.public.port && + a.private.host == b.private.host && + a.private.port == b.private.port && + a.protocol == b.protocol; } class UPNPSensor extends Sensor { constructor() { super(); + this.interfaces = null; } isExpired(mapping) { @@ -86,7 +87,8 @@ class UPNPSensor extends Sensor { .filter((mapping) => !this.isExpired(mapping)); } - run() { + async run() { + this.interfaces = await networkTool.getLocalNetworkInterface(); setInterval(() => { upnp.getPortMappingsUPNP(async (err, results) => { if (err) { @@ -108,10 +110,11 @@ class UPNPSensor extends Sensor { if (cfg.isFeatureOn(ALARM_UPNP)) { for (let current of mergedResults) { - let firewallaRegistered = - current.private.host == sysManager.myIp() && - upnp.getRegisteredUpnpMappings().some( m => upnp.mappingCompare(current, m) ); - + let firewallaRegistered = upnp.getRegisteredUpnpMappings().some(m => upnp.mappingCompare(current, m)); + for (const intf of this.interfaces) { + firewallaRegistered && current.private.host == sysManager.myIp(intf.name) + if (firewallaRegistered) break; + } if ( !firewallaRegistered && !preMappings.some(pre => compareUpnp(current, pre)) @@ -122,15 +125,15 @@ class UPNPSensor extends Sensor { { 'p.source': 'UPNPSensor', 'p.device.ip': current.private.host, - 'p.upnp.public.host' : current.public.host, - 'p.upnp.public.port' : current.public.port.toString(), - 'p.upnp.private.host' : current.private.host, - 'p.upnp.private.port' : current.private.port.toString(), - 'p.upnp.protocol' : current.protocol, - 'p.upnp.enabled' : current.enabled.toString(), - 'p.upnp.description' : current.description, - 'p.upnp.ttl' : current.ttl.toString(), - 'p.upnp.local' : current.local.toString(), + 'p.upnp.public.host': current.public.host, + 'p.upnp.public.port': current.public.port.toString(), + 'p.upnp.private.host': current.private.host, + 'p.upnp.private.port': current.private.port.toString(), + 'p.upnp.protocol': current.protocol, + 'p.upnp.enabled': current.enabled.toString(), + 'p.upnp.description': current.description, + 'p.upnp.ttl': current.ttl.toString(), + 'p.upnp.local': current.local.toString(), 'p.device.port': current.private.port.toString(), 'p.protocol': current.protocol } @@ -141,10 +144,10 @@ class UPNPSensor extends Sensor { } } - if (await rclient.hmsetAsync(key, {upnp: JSON.stringify(mergedResults)} )) + if (await rclient.hmsetAsync(key, { upnp: JSON.stringify(mergedResults) })) log.info("UPNP mapping is updated,", mergedResults.length, "entries"); - } catch(err) { + } catch (err) { log.error("Failed to scan upnp mapping: " + err); } });