Implementing relaxed matching for layer 2 and layer 3.
[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     // TODO: Relax the inclusion time if needed
31 //    public static final int INCLUSION_WINDOW_MILLIS = 30_000;
32
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;
40             else return 0;
41         });
42         mTriggerTimes = Collections.unmodifiableList(triggerTimes);
43         mDeviceIp = deviceIp;
44     }
45
46
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.
49         mTriggerIndex = 0;
50         mIncludedPackets = 0;
51         PcapHandle handle;
52         try {
53             handle = Pcaps.openOffline(mPcapFilePath, PcapHandle.TimestampPrecision.NANO);
54         } catch (PcapNativeException pne) {
55             handle = Pcaps.openOffline(mPcapFilePath);
56         }
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();
61
62     }
63
64     /**
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...)}.
67      *
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...)}.
70      */
71     public long getPacketsIncludedCount() {
72         return mIncludedPackets;
73     }
74
75     @Override
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
78         // be traversed.
79         boolean include = mTriggerTimes.stream().anyMatch(
80                 trigger -> trigger.isBefore(packet.getTimestamp()) &&
81                         packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS))
82         );
83         if (include) {
84             mIncludedPackets++;
85         }
86         return include;
87
88         /*
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.
92             return false;
93         }
94
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.
100             return true;
101         } else {
102             if (!trigger.isBefore(packet.getTimestamp())) {
103                 // Packet is before currently considered trigger, so it shouldn't be included
104                 return false;
105             } else {
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!
109                 mTriggerIndex++;
110                 return shouldIncludePacket(packet);
111             }
112         }
113         */
114     }
115
116 }