0adecd879affb0f5f6520eac4e574040890796e9
[pingpong.git] / Code / Projects / PacketLevelSignatureExtractor / src / main / java / edu / uci / iotproject / analysis / TriggerTrafficExtractor.java
1 package edu.uci.iotproject.analysis;
2
3 import edu.uci.iotproject.io.PcapHandleReader;
4 import org.pcap4j.core.*;
5
6 import java.time.Instant;
7 import java.util.Collections;
8 import java.util.List;
9 import java.util.concurrent.TimeoutException;
10
11 /**
12  * TODO add class documentation.
13  *
14  * @author Janus Varmarken
15  */
16 public class TriggerTrafficExtractor implements PcapPacketFilter {
17
18     private final String mPcapFilePath;
19     private final List<Instant> mTriggerTimes;
20     private final String mDeviceIp;
21
22     private int mTriggerIndex = 0;
23
24     /**
25      * The total number of packets marked for inclusion during one run of {@link #performExtraction(PacketListener...)}.
26      */
27     private long mIncludedPackets = 0;
28
29     public static final int INCLUSION_WINDOW_MILLIS = 15_000;
30     //public static final int INCLUSION_WINDOW_MILLIS = 30_000;
31
32     public TriggerTrafficExtractor(String pcapFilePath, List<Instant> triggerTimes, String deviceIp) throws PcapNativeException, NotOpenException {
33         mPcapFilePath = pcapFilePath;
34         // Ensure that trigger times are sorted in ascending as we rely on this fact in the logic that works out if a
35         // packet is related to a trigger.
36         Collections.sort(triggerTimes, (i1, i2) -> {
37             if (i1.isBefore(i2)) return -1;
38             else if (i2.isBefore(i1)) return 1;
39             else return 0;
40         });
41         mTriggerTimes = Collections.unmodifiableList(triggerTimes);
42         mDeviceIp = deviceIp;
43     }
44
45
46     public void performExtraction(PacketListener... extractedPacketsConsumers) throws PcapNativeException, NotOpenException, TimeoutException {
47         // Reset trigger index and packet counter in case client code chooses to rerun the extraction.
48         mTriggerIndex = 0;
49         mIncludedPackets = 0;
50         PcapHandle handle;
51         try {
52             handle = Pcaps.openOffline(mPcapFilePath, PcapHandle.TimestampPrecision.NANO);
53         } catch (PcapNativeException pne) {
54             handle = Pcaps.openOffline(mPcapFilePath);
55         }
56         // Use the native support for BPF to immediately filter irrelevant traffic.
57         handle.setFilter("ip host " + mDeviceIp, BpfProgram.BpfCompileMode.OPTIMIZE);
58         PcapHandleReader pcapReader = new PcapHandleReader(handle, this, extractedPacketsConsumers);
59         pcapReader.readFromHandle();
60
61     }
62
63     /**
64      * Return the number of extracted packets (i.e., packets selected for inclusion) as a result of the most recent call
65      * to {@link #performExtraction(PacketListener...)}.
66      *
67      * @return the number of extracted packets (i.e., packets selected for inclusion) as a result of the most recent
68      *         call to {@link #performExtraction(PacketListener...)}.
69      */
70     public long getPacketsIncludedCount() {
71         return mIncludedPackets;
72     }
73
74     @Override
75     public boolean shouldIncludePacket(PcapPacket packet) {
76         // New version. Simpler, but slower: the later a packet arrives, the more elements of mTriggerTimes will need to
77         // be traversed.
78         boolean include = mTriggerTimes.stream().anyMatch(
79                 trigger -> trigger.isBefore(packet.getTimestamp()) &&
80                         packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS))
81         );
82         if (include) {
83             mIncludedPackets++;
84         }
85         return include;
86
87         /*
88         // Old version. Faster, but more complex - is it correct?
89         if (mTriggerIndex >= mTriggerTimes.size()) {
90             // Don't include packet if we've exhausted the list of trigger times.
91             return false;
92         }
93
94         // TODO hmm, is this correct?
95         Instant trigger = mTriggerTimes.get(mTriggerIndex);
96         if (trigger.isBefore(packet.getTimestamp()) &&
97                 packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS))) {
98             // Packet lies within INCLUSION_WINDOW_MILLIS after currently considered trigger, include it.
99             return true;
100         } else {
101             if (!trigger.isBefore(packet.getTimestamp())) {
102                 // Packet is before currently considered trigger, so it shouldn't be included
103                 return false;
104             } else {
105                 // Packet is >= INCLUSION_WINDOW_MILLIS after currently considered trigger.
106                 // Proceed to next trigger to see if it lies in range of that.
107                 // Note that there's an assumption here that no two trigger intervals don't overlap!
108                 mTriggerIndex++;
109                 return shouldIncludePacket(packet);
110             }
111         }
112         */
113     }
114
115 }