1 package edu.uci.iotproject.analysis;
3 import org.pcap4j.core.*;
5 import java.time.Instant;
6 import java.util.Collections;
8 import java.util.concurrent.TimeoutException;
11 * TODO add class documentation.
13 * @author Janus Varmarken
15 public class TriggerTrafficExtractor implements PcapPacketFilter {
17 private final String mPcapFilePath;
18 private final List<Instant> mTriggerTimes;
19 private final String mDeviceIp;
21 private int mTriggerIndex = 0;
24 private static final int INCLUSION_WINDOW_MILLIS = 3_000;
26 public TriggerTrafficExtractor(String pcapFilePath, List<Instant> triggerTimes, String deviceIp) throws PcapNativeException, NotOpenException {
27 mPcapFilePath = pcapFilePath;
28 // Ensure that trigger times are sorted in ascending as we rely on this fact in the logic that works out if a
29 // packet is related to a trigger.
30 Collections.sort(triggerTimes, (i1, i2) -> {
31 if (i1.isBefore(i2)) return -1;
32 else if (i2.isBefore(i1)) return 1;
35 mTriggerTimes = Collections.unmodifiableList(triggerTimes);
40 public void performExtraction(PacketListener... extractedPacketsConsumers) throws PcapNativeException, NotOpenException, TimeoutException {
43 handle = Pcaps.openOffline(mPcapFilePath, PcapHandle.TimestampPrecision.NANO);
44 } catch (PcapNativeException pne) {
45 handle = Pcaps.openOffline(mPcapFilePath);
47 // Use the native support for BPF to immediately filter irrelevant traffic.
48 handle.setFilter("ip host " + mDeviceIp, BpfProgram.BpfCompileMode.OPTIMIZE);
49 PcapHandleReader pcapReader = new PcapHandleReader(handle, this, extractedPacketsConsumers);
50 pcapReader.readFromHandle();
51 // Reset trigger index (in case client code chooses to rerun the extraction)
55 // public TriggerTrafficExtractor(String deviceIp, PcapHandle handle, List<Instant> triggerTimes) throws PcapNativeException, NotOpenException {
56 // mDeviceIp = deviceIp;
58 // mHandle.setFilter("ip host " + deviceIp, BpfProgram.BpfCompileMode.OPTIMIZE);
59 // // Sort in ascending order.
60 // Collections.sort(triggerTimes, (i1, i2) -> {
61 // if (i1.isBefore(i2)) return -1;
62 // else if (i2.isBefore(i1)) return 1;
65 // mTriggerTimes = Collections.unmodifiableList(triggerTimes);
70 // private void process() {
72 // PcapPacket prevPacket = null;
74 // while ((packet = mHandle.getNextPacketEx()) != null) {
75 // if (prevPacket != null && packet.getTimestamp().isBefore(prevPacket.getTimestamp())) {
76 // // Fail early if assumption doesn't hold.
77 // throw new RuntimeException("Packets not in ascending temporal order");
79 // if (shouldIncludePacket(packet)) {
80 // // TODO output packet
82 // prevPacket = packet;
84 // } catch (PcapNativeException | TimeoutException | NotOpenException e) {
85 // e.printStackTrace();
86 // } catch (EOFException e) {
87 // System.out.println("Reached end of pcap file");
92 public boolean shouldIncludePacket(PcapPacket packet) {
93 // TODO hmm, is this correct?
94 Instant trigger = mTriggerTimes.get(mTriggerIndex);
95 if (trigger.isBefore(packet.getTimestamp()) &&
96 packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS))) {
97 // Packet lies within INCLUSION_WINDOW_MILLIS after currently considered trigger, include it.
100 if (!trigger.isBefore(packet.getTimestamp())) {
101 // Packet is before currently considered trigger, so it shouldn't be included
104 // Packet is >= INCLUSION_WINDOW_MILLIS after currently considered trigger.
105 // Proceed to next trigger to see if it lies in range of that.
106 // Note that there's an assumption here that no two trigger intervals don't overlap!
108 return shouldIncludePacket(packet);
118 if (mTriggerIndex >= mTriggerTimes.size()) {
119 // Don't include packet if we've exhausted the list of trigger timestamps.
122 Instant trigger = mTriggerTimes.get(mTriggerIndex);
123 if (trigger.isBefore(packet.getTimestamp()) &&
124 packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS))) {
125 // Packet lies within INCLUSION_WINDOW_MILLIS after currently considered trigger, include it.
128 if (mTriggerIndex >= mTriggerTimes.size()-1) {
129 // No additional triggers left to be considered.
132 trigger = mTriggerTimes.get(mTriggerIndex + 1);
133 if (packet.getTimestamp().isBefore(trigger)) {
137 return includePacket(packet);
142 // else if (trigger.isBefore(packet.getTimestamp()) &&
143 // !packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS)) {
149 private Instant findTriggerTime(PcapPacket packet) {
150 mTriggerTimes.stream().filter(i ->
151 i.isBefore(packet.getTimestamp()) && packet.getTimestamp().isBefore(i.plusMillis(3000)));
153 while (mTriggerIndex < mTriggerTimes.size() &&
154 !(mTriggerTimes.get(mTriggerIndex).isBefore(packet.getTimestamp()) &&
155 packet.getTimestamp().isBefore(mTriggerTimes.get(mTriggerIndex).plusMillis(3_000)))
159 return mTriggerIndex < mTriggerTimes.size() ? mTriggerTimes.get(mTriggerIndex) : null;
161 // Trigger time must logically be BEFORE packet timestamp, so advance
162 while (mTriggerIndex < mTriggerTimes.size() &&
163 mTriggerTimes.get(mTriggerIndex).isAfter(packet.getTimestamp())) {
166 return mTriggerIndex < mTriggerTimes.size() ? mTriggerTimes.get(mTriggerIndex) : null;