Some work-in-progress code for extracting trigger traffic
[pingpong.git] / Code / Projects / SmartPlugDetector / src / main / java / edu / uci / iotproject / analysis / TriggerTrafficExtractor.java
1 package edu.uci.iotproject.analysis;
2
3 import org.pcap4j.core.*;
4
5 import java.time.Instant;
6 import java.util.Collections;
7 import java.util.List;
8 import java.util.concurrent.TimeoutException;
9
10 /**
11  * TODO add class documentation.
12  *
13  * @author Janus Varmarken
14  */
15 public class TriggerTrafficExtractor implements PcapPacketFilter {
16
17     private final String mPcapFilePath;
18     private final List<Instant> mTriggerTimes;
19     private final String mDeviceIp;
20
21     private int mTriggerIndex = 0;
22
23
24     private static final int INCLUSION_WINDOW_MILLIS = 3_000;
25
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;
33             else return 0;
34         });
35         mTriggerTimes = Collections.unmodifiableList(triggerTimes);
36         mDeviceIp = deviceIp;
37     }
38
39
40     public void performExtraction(PacketListener... extractedPacketsConsumers) throws PcapNativeException, NotOpenException, TimeoutException {
41         PcapHandle handle;
42         try {
43             handle = Pcaps.openOffline(mPcapFilePath, PcapHandle.TimestampPrecision.NANO);
44         } catch (PcapNativeException pne) {
45             handle = Pcaps.openOffline(mPcapFilePath);
46         }
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)
52         mTriggerIndex = 0;
53     }
54
55 //    public TriggerTrafficExtractor(String deviceIp, PcapHandle handle, List<Instant> triggerTimes) throws PcapNativeException, NotOpenException {
56 //        mDeviceIp = deviceIp;
57 //        mHandle = handle;
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;
63 //            else return 0;
64 //        });
65 //        mTriggerTimes = Collections.unmodifiableList(triggerTimes);
66 //    }
67
68
69
70     //    private void process() {
71 //        try {
72 //            PcapPacket prevPacket = null;
73 //            PcapPacket packet;
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");
78 //                }
79 //                if (shouldIncludePacket(packet)) {
80 //                    // TODO output packet
81 //                }
82 //                prevPacket = packet;
83 //            }
84 //        } catch (PcapNativeException | TimeoutException | NotOpenException e) {
85 //            e.printStackTrace();
86 //        } catch (EOFException e) {
87 //            System.out.println("Reached end of pcap file");
88 //        }
89 //    }
90
91     @Override
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.
98             return true;
99         } else {
100             if (!trigger.isBefore(packet.getTimestamp())) {
101                 // Packet is before currently considered trigger, so it shouldn't be included
102                 return false;
103             } else {
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!
107                 mTriggerIndex++;
108                 return shouldIncludePacket(packet);
109             }
110         }
111
112
113
114
115
116
117         /*
118         if (mTriggerIndex >= mTriggerTimes.size()) {
119             // Don't include packet if we've exhausted the list of trigger timestamps.
120             return false;
121         }
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.
126             return true;
127         }
128         if (mTriggerIndex >= mTriggerTimes.size()-1) {
129             // No additional triggers left to be considered.
130             return false;
131         }
132         trigger = mTriggerTimes.get(mTriggerIndex + 1);
133         if (packet.getTimestamp().isBefore(trigger)) {
134             return false;
135         } else {
136             mTriggerIndex++;
137             return includePacket(packet);
138         }
139         */
140
141
142 //        else if (trigger.isBefore(packet.getTimestamp()) &&
143 //                !packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS)) {
144 //
145 //        }
146     }
147
148
149     private Instant findTriggerTime(PcapPacket packet) {
150         mTriggerTimes.stream().filter(i ->
151                 i.isBefore(packet.getTimestamp()) && packet.getTimestamp().isBefore(i.plusMillis(3000)));
152
153         while (mTriggerIndex < mTriggerTimes.size() &&
154                 !(mTriggerTimes.get(mTriggerIndex).isBefore(packet.getTimestamp()) &&
155                         packet.getTimestamp().isBefore(mTriggerTimes.get(mTriggerIndex).plusMillis(3_000)))
156                 ) {
157             mTriggerIndex++;
158         }
159         return mTriggerIndex < mTriggerTimes.size() ? mTriggerTimes.get(mTriggerIndex) : null;
160         /*
161         // Trigger time must logically be BEFORE packet timestamp, so advance
162         while (mTriggerIndex < mTriggerTimes.size() &&
163                 mTriggerTimes.get(mTriggerIndex).isAfter(packet.getTimestamp())) {
164             mTriggerIndex++;
165         }
166         return mTriggerIndex < mTriggerTimes.size() ? mTriggerTimes.get(mTriggerIndex) : null;
167         */
168     }
169 }