1 package edu.uci.iotproject.analysis;
3 import edu.uci.iotproject.io.PcapHandleReader;
4 import org.pcap4j.core.*;
6 import java.time.Instant;
7 import java.util.Collections;
9 import java.util.concurrent.TimeoutException;
12 * TODO add class documentation.
14 * @author Janus Varmarken
16 public class TriggerTrafficExtractor implements PcapPacketFilter {
18 private final String mPcapFilePath;
19 private final List<Instant> mTriggerTimes;
20 private final String mDeviceIp;
22 private int mTriggerIndex = 0;
25 * The total number of packets marked for inclusion during one run of {@link #performExtraction(PacketListener...)}.
27 private long mIncludedPackets = 0;
29 public static final int INCLUSION_WINDOW_MILLIS = 15_000;
30 // TODO: Relax the inclusion time if needed
31 // public static final int INCLUSION_WINDOW_MILLIS = 30_000;
33 public TriggerTrafficExtractor(String pcapFilePath, List<Instant> triggerTimes, String deviceIp) throws PcapNativeException, NotOpenException {
34 mPcapFilePath = pcapFilePath;
35 // Ensure that trigger times are sorted in ascending as we rely on this fact in the logic that works out if a
36 // packet is related to a trigger.
37 Collections.sort(triggerTimes, (i1, i2) -> {
38 if (i1.isBefore(i2)) return -1;
39 else if (i2.isBefore(i1)) return 1;
42 mTriggerTimes = Collections.unmodifiableList(triggerTimes);
47 public void performExtraction(PacketListener... extractedPacketsConsumers) throws PcapNativeException, NotOpenException, TimeoutException {
48 // Reset trigger index and packet counter in case client code chooses to rerun the extraction.
53 handle = Pcaps.openOffline(mPcapFilePath, PcapHandle.TimestampPrecision.NANO);
54 } catch (PcapNativeException pne) {
55 handle = Pcaps.openOffline(mPcapFilePath);
57 // Use the native support for BPF to immediately filter irrelevant traffic.
58 handle.setFilter("ip host " + mDeviceIp, BpfProgram.BpfCompileMode.OPTIMIZE);
59 PcapHandleReader pcapReader = new PcapHandleReader(handle, this, extractedPacketsConsumers);
60 pcapReader.readFromHandle();
65 * Return the number of extracted packets (i.e., packets selected for inclusion) as a result of the most recent call
66 * to {@link #performExtraction(PacketListener...)}.
68 * @return the number of extracted packets (i.e., packets selected for inclusion) as a result of the most recent
69 * call to {@link #performExtraction(PacketListener...)}.
71 public long getPacketsIncludedCount() {
72 return mIncludedPackets;
76 public boolean shouldIncludePacket(PcapPacket packet) {
77 // New version. Simpler, but slower: the later a packet arrives, the more elements of mTriggerTimes will need to
79 boolean include = mTriggerTimes.stream().anyMatch(
80 trigger -> trigger.isBefore(packet.getTimestamp()) &&
81 packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS))
89 // Old version. Faster, but more complex - is it correct?
90 if (mTriggerIndex >= mTriggerTimes.size()) {
91 // Don't include packet if we've exhausted the list of trigger times.
95 // TODO hmm, is this correct?
96 Instant trigger = mTriggerTimes.get(mTriggerIndex);
97 if (trigger.isBefore(packet.getTimestamp()) &&
98 packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS))) {
99 // Packet lies within INCLUSION_WINDOW_MILLIS after currently considered trigger, include it.
102 if (!trigger.isBefore(packet.getTimestamp())) {
103 // Packet is before currently considered trigger, so it shouldn't be included
106 // Packet is >= INCLUSION_WINDOW_MILLIS after currently considered trigger.
107 // Proceed to next trigger to see if it lies in range of that.
108 // Note that there's an assumption here that no two trigger intervals don't overlap!
110 return shouldIncludePacket(packet);