Skip to content

Commit

Permalink
tracker channel designator
Browse files Browse the repository at this point in the history
  • Loading branch information
lennartkoopmann committed Sep 27, 2020
1 parent 2a925ec commit ac9e372
Show file tree
Hide file tree
Showing 17 changed files with 271 additions and 82 deletions.
2 changes: 1 addition & 1 deletion src/main/java/horse/wtf/nzyme/NzymeLeaderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ private void initializeProbes() {
configuration.graylogUplinks(),
getNodeID(),
td.device(),
td.channels(),
ImmutableList.copyOf(td.channels()),
td.channelHopInterval(),
td.channelHopCommand(),
configuration.dot11Networks(),
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/horse/wtf/nzyme/NzymeTrackerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import horse.wtf.nzyme.bandits.trackers.hid.TextGUIHID;
import horse.wtf.nzyme.bandits.trackers.hid.TrackerHID;
import horse.wtf.nzyme.bandits.trackers.protobuf.TrackerMessage;
import horse.wtf.nzyme.bandits.trackers.trackerlogic.ChannelDesignator;
import horse.wtf.nzyme.bandits.trackers.trackerlogic.TrackerBanditManager;
import horse.wtf.nzyme.bandits.trackers.GroundStation;
import horse.wtf.nzyme.bandits.trackers.trackerlogic.TrackerStateWatchdog;
Expand Down Expand Up @@ -158,7 +159,13 @@ public void initialize() {
}
});

this.banditManager.onBanditTrace((bandit,rssi) -> {
this.banditManager.onBanditTrace((bandit,rssi,channel) -> {
for (Dot11Probe probe : getProbes()) {
if (probe instanceof Dot11MonitorProbe) {
((Dot11MonitorProbe) probe).getChannelDesignator().onBanditTrace(channel);
}
}

for (TrackerHID hid : hids) {
hid.onBanditTrace(bandit,rssi);
}
Expand Down
33 changes: 31 additions & 2 deletions src/main/java/horse/wtf/nzyme/bandits/trackers/hid/TextGUIHID.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import horse.wtf.nzyme.bandits.trackers.TrackerState;
import horse.wtf.nzyme.bandits.trackers.TrackerTrackSummary;
import horse.wtf.nzyme.bandits.trackers.protobuf.TrackerMessage;
import horse.wtf.nzyme.bandits.trackers.trackerlogic.ChannelDesignator;
import horse.wtf.nzyme.dot11.probes.Dot11MonitorProbe;
import horse.wtf.nzyme.dot11.probes.Dot11Probe;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand All @@ -60,6 +62,7 @@ public class TextGUIHID implements TrackerHID {
private final Label labelTrackerStatus = new Label("");
private final Label labelWiFiStatus = new Label("");
private final Label labelWiFiChannels = new Label("").setForegroundColor(TextColor.ANSI.WHITE);
private final Label labelDesignator = new Label("").setForegroundColor(TextColor.ANSI.WHITE);

private final Label labelTask = new Label("");
private final Label labelBandit = new Label("");
Expand Down Expand Up @@ -103,13 +106,38 @@ public void initialize() {
labelWiFiChannels.setText(wifiChannels());

boolean allProbesLive = true;
Dot11MonitorProbe firstMonitor = null;
for (Dot11Probe probe : nzyme.getProbes()) {
if (probe instanceof Dot11MonitorProbe) {
firstMonitor = (Dot11MonitorProbe) probe;
}

if (!probe.isInLoop() || !probe.isActive()) {
allProbesLive = false;
break;
}
}

if (firstMonitor != null) {
switch (firstMonitor.getChannelDesignator().getStatus()) {
case LOCKED:
labelDesignator.setText(" LOCK ");
labelDesignator.setForegroundColor(TextColor.ANSI.BLACK);
labelDesignator.setBackgroundColor(TextColor.ANSI.GREEN);
break;
case UNLOCKED:
labelDesignator.setText("UNLOCKED");
labelDesignator.setForegroundColor(TextColor.ANSI.WHITE);
labelDesignator.setBackgroundColor(TextColor.ANSI.BLACK);
break;
case SWEEPING:
labelDesignator.setText("SWEEPING");
labelDesignator.setForegroundColor(TextColor.ANSI.BLACK);
labelDesignator.setBackgroundColor(TextColor.ANSI.YELLOW);
break;
}
}

if (allProbesLive) {
labelWiFiStatus.setText(" ONLINE");
labelWiFiStatus.setForegroundColor(TextColor.ANSI.GREEN);
Expand Down Expand Up @@ -204,13 +232,14 @@ private void initializeGUI() throws IOException {
Panel mainPanel = new Panel();

// Top status.
Panel statusPanel = new Panel(new GridLayout(7));
Panel statusPanel = new Panel(new GridLayout(8));
statusPanel.addComponent(labelConnection.withBorder(Borders.singleLine("CONN")));
statusPanel.addComponent(labelSignal.withBorder(Borders.singleLine("SIG")));
statusPanel.addComponent(labelTrackerStatus.withBorder(Borders.singleLine("LINK DVC")));
statusPanel.addComponent(labelWiFiStatus.withBorder(Borders.singleLine("802.11")));
statusPanel.addComponent(labelWiFiChannels.withBorder(Borders.singleLine("CHANNEL")));
statusPanel.addComponent(new EmptySpace(new TerminalSize(12, 3)));
statusPanel.addComponent(labelDesignator.withBorder(Borders.singleLine("DSGNTR")));
statusPanel.addComponent(new EmptySpace(new TerminalSize(3, 3)));
statusPanel.addComponent(labelTime.withBorder(Borders.singleLine("CLOCK")));
mainPanel.addComponent(statusPanel);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* This file is part of nzyme.
*
* nzyme is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nzyme is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with nzyme. If not, see <http://www.gnu.org/licenses/>.
*/

package horse.wtf.nzyme.bandits.trackers.trackerlogic;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import horse.wtf.nzyme.dot11.probes.Dot11MonitorProbe;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

public class ChannelDesignator {

private static final Logger LOG = LogManager.getLogger(ChannelDesignator.class);

private static final short SWEEP_BREAK = 5;

public enum DESIGNATION_STATUS {
LOCKED,
UNLOCKED,
SWEEPING
}

private final Dot11MonitorProbe probe;

private final AtomicReference<List<Integer>> designatedChannels;

private final AtomicBoolean contactDuringCycle;
private final AtomicReference<DESIGNATION_STATUS> status;

private short loopCount;

public ChannelDesignator(Dot11MonitorProbe probe) {
this.probe = probe;
this.contactDuringCycle = new AtomicBoolean(false);
this.status = new AtomicReference<>(DESIGNATION_STATUS.UNLOCKED);

// Start with the default/configured channels of the underlying probe.
this.designatedChannels = new AtomicReference<>(new ArrayList<>(probe.getConfiguration().channels()));

// Periodically designate channels for ChannelHopper.
Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder()
.setDaemon(true)
.setNameFormat("channel-designator-%d")
.build())
.scheduleAtFixedRate(this::designate, 0, 30, TimeUnit.SECONDS);

LOG.info("Channel Designator is online.");
}

public void onBanditTrace(int channel) {
this.contactDuringCycle.set(true);
if (!this.designatedChannels.get().contains(channel)) {
this.designatedChannels.get().add(channel);
}
}

public DESIGNATION_STATUS getStatus() {
return status.get();
}

private void designate() {
boolean skip = false;

// Is this a sweep run?
if (loopCount == SWEEP_BREAK-1) {
loopCount = 0;

if (this.contactDuringCycle.get()) {
LOG.info("Starting designator sweep.");
this.probe.getChannelHopper().setChannels(probe.getConfiguration().channels());
this.status.set(DESIGNATION_STATUS.SWEEPING);

skip = true;
}
}

if (!skip) {
if (contactDuringCycle.get()) {
// Contacts recorded during cycle. Designate all channels that a contact was made on.
LOG.info("Contacts recorded during cycle. Designating channels [{}].", Joiner.on(",").join(designatedChannels.get()));
this.probe.getChannelHopper().setChannels(intersectChannels(new ArrayList<>(designatedChannels.get()), probe.getConfiguration().channels()));

this.status.set(DESIGNATION_STATUS.LOCKED);
} else {
// No contacts during cycle. Designate all configured channels.
LOG.info("No contacts during cycle. Resuming operations on configured probe channels.");
this.probe.getChannelHopper().setChannels(probe.getConfiguration().channels());

this.status.set(DESIGNATION_STATUS.UNLOCKED);
}
}

// Reset for next cycle.
this.contactDuringCycle.set(false);
this.designatedChannels.get().clear();
loopCount++;
}

private List<Integer> intersectChannels(List<Integer> partial, List<Integer> complete) {
ImmutableList.Builder<Integer> result = new ImmutableList.Builder<>();
for (Integer x : partial) {
if (complete.contains(x)) {
result.add(x);
}
}

return result.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public void identify(Dot11Frame frame) {
currentTrackLastContact = DateTime.now();

for (BanditTraceHandler handler : traceHandlers) {
handler.handle(bandit, frame.meta().getAntennaSignal());
handler.handle(bandit, frame.meta().getAntennaSignal(), frame.meta().getChannel());
}
}
}
Expand Down Expand Up @@ -213,6 +213,6 @@ public interface InitialTrackHandler {
}

public interface BanditTraceHandler {
void handle(Bandit bandit, int rssi);
void handle(Bandit bandit, int rssi, int channel);
}
}
23 changes: 21 additions & 2 deletions src/main/java/horse/wtf/nzyme/channels/ChannelHopper.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package horse.wtf.nzyme.channels;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.io.CharStreams;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
Expand All @@ -27,6 +28,7 @@
import org.apache.logging.log4j.Logger;

import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
Expand All @@ -40,6 +42,8 @@ public class ChannelHopper {

private final List<ChannelSwitchHandler> channelSwitchHandlers;

private List<Integer> configuredChannels;

private int currentChannel = 0;
private int currentChannelIndex = 0;

Expand All @@ -48,6 +52,7 @@ public ChannelHopper(Dot11Probe probe, Dot11ProbeConfiguration probeConfiguratio
throw new RuntimeException("Channels empty or NULL. You need to configure at least one channel.");
}

this.configuredChannels = probeConfiguration.channels();
this.channelSwitchHandlers = Lists.newArrayList();

this.probe = probe;
Expand All @@ -61,19 +66,20 @@ public void initialize() {
.build()
).scheduleWithFixedDelay(() -> {
try {
List<Integer> channels = new ArrayList<>(configuredChannels);
if (!this.probe.isInLoop()) {
LOG.debug("Not hopping channel. Probe [{}] not in loop.", probeConfiguration.networkInterfaceName());
return;
}

// Check if we reached end of channel list and recycle to 0 in that case.
if(this.currentChannelIndex >= probeConfiguration.channels().size()-1) {
if(this.currentChannelIndex >= channels.size()-1) {
this.currentChannelIndex = 0;
} else {
this.currentChannelIndex++;
}

int channel = probeConfiguration.channels().get(this.currentChannelIndex);
int channel = channels.get(this.currentChannelIndex);

LOG.debug("Configuring [{}] to use channel <{}>", probeConfiguration.networkInterfaceName(), channel);

Expand Down Expand Up @@ -117,6 +123,19 @@ private void changeToChannel(Integer channel) {
}
}

public void setChannels(List<Integer> channels) {
if (channels.equals(this.configuredChannels)) {
// No need to update if channels did not change.
return;
}

LOG.info("Updating list of channels to <{}>.", Joiner.on(",").join(channels));
this.configuredChannels = channels;

this.currentChannel = 0;
this.currentChannelIndex = 0;
}

public Integer getCurrentChannel() {
return currentChannel;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public BaseDot11ConfigurationLoader(Config root) {
this.root = root;
}

public List<Dot11MonitorDefinition> parseDot11Monitors() {
public ImmutableList<Dot11MonitorDefinition> parseDot11Monitors() {
ImmutableList.Builder<Dot11MonitorDefinition> result = new ImmutableList.Builder<>();

for (Config config : root.getConfigList(ConfigurationKeys.DOT11_MONITORS)) {
Expand All @@ -48,7 +48,7 @@ public List<Dot11MonitorDefinition> parseDot11Monitors() {

result.add(Dot11MonitorDefinition.create(
config.getString(ConfigurationKeys.DEVICE),
config.getIntList(ConfigurationKeys.CHANNELS),
ImmutableList.copyOf(config.getIntList(ConfigurationKeys.CHANNELS)),
config.getString(ConfigurationKeys.HOP_COMMAND),
config.getInt(ConfigurationKeys.HOP_INTERVAL)
));
Expand All @@ -57,7 +57,7 @@ public List<Dot11MonitorDefinition> parseDot11Monitors() {
return result.build();
}

public List<Dot11NetworkDefinition> parseDot11Networks() {
public ImmutableList<Dot11NetworkDefinition> parseDot11Networks() {
ImmutableList.Builder<Dot11NetworkDefinition> result = new ImmutableList.Builder<>();

for (Config config : root.getConfigList(ConfigurationKeys.DOT11_NETWORKS)) {
Expand Down Expand Up @@ -112,7 +112,7 @@ public void validateMonitors() throws IncompleteConfigurationException, InvalidC
for (Config c : root.getConfigList(ConfigurationKeys.DOT11_MONITORS)) {
String where = ConfigurationKeys.DOT11_MONITORS + "." + "#" + i;
ConfigurationValidator.expect(c, ConfigurationKeys.DEVICE, where, String.class);
ConfigurationValidator.expect(c, ConfigurationKeys.CHANNELS, where, List.class);
ConfigurationValidator.expect(c, ConfigurationKeys.CHANNELS, where, ImmutableList.class);
ConfigurationValidator.expect(c, ConfigurationKeys.HOP_COMMAND, where, String.class);
ConfigurationValidator.expect(c, ConfigurationKeys.HOP_INTERVAL, where, Integer.class);
i++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package horse.wtf.nzyme.configuration;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigException;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -46,7 +47,7 @@ public static void expect(Config c, String key, String where, Class clazz) throw
}

// List
if (clazz.equals(List.class)) {
if (clazz.equals(ImmutableList.class)) {
c.getList(key);
}

Expand Down
Loading

0 comments on commit ac9e372

Please sign in to comment.