Renaming root project name.
[pingpong.git] / Code / Projects / SmartPlugDetector / src / main / java / edu / uci / iotproject / maclayer / MacLayerFlowPatternFinder.java
1 package edu.uci.iotproject.maclayer;
2
3 import edu.uci.iotproject.FlowPattern;
4 import org.pcap4j.core.NotOpenException;
5 import org.pcap4j.core.PcapHandle;
6 import org.pcap4j.core.PcapNativeException;
7 import org.pcap4j.core.PcapPacket;
8 import org.pcap4j.packet.RadiotapPacket;
9
10 import java.io.EOFException;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.List;
14 import java.util.Objects;
15 import java.util.concurrent.TimeoutException;
16
17 /**
18  * Performs a search for {@link FlowPattern}
19  * TODO: May want to create an abstract FlowPatternFinder and then derive MacLayer, TcpipLayer FlowPatternFinders from that one.
20  *
21  * @author Janus Varmarken
22  */
23 public class MacLayerFlowPatternFinder {
24
25     private final MacLayerFlowPattern mPattern;
26     private final PcapHandle mPcap;
27
28     public MacLayerFlowPatternFinder(PcapHandle pcap, MacLayerFlowPattern pattern) {
29         this.mPcap = Objects.requireNonNull(pcap,
30                 String.format("Argument of type '%s' cannot be null", PcapHandle.class.getSimpleName()));
31         this.mPattern = Objects.requireNonNull(pattern,
32                 String.format("Argument of type '%s' cannot be null", FlowPattern.class.getSimpleName()));
33     }
34
35     public void findFlowPattern() {
36         PcapPacket packet;
37         try {
38             // Packets matched to flow pattern searched for.
39             List<PcapPacket> patternPackets = new ArrayList<>();
40             while ((packet = mPcap.getNextPacketEx()) != null) {
41                 RadiotapPacket radiotapPacket;
42                 try {
43                     // Some packets throw an IAE with message "msi must be between 0 and 6 but is actually: 7"
44                     // when accessing the RadiotapPacket.
45                     radiotapPacket = packet.get(RadiotapPacket.class);
46                 } catch (IllegalArgumentException iae) {
47                     System.out.println(iae.getMessage());
48                     continue;
49                 }
50                 if (radiotapPacket == null) {
51                     continue;
52                 }
53                 // Restart search if pattern not found within reasonable time frame (hardcoded for now).
54                 if (patternPackets.size() > 0 && packet.getTimestamp().getEpochSecond() -
55                             patternPackets.get(patternPackets.size()-1).getTimestamp().getEpochSecond() > 2) {
56                     patternPackets = new ArrayList<>();
57                 }
58
59                 byte[] rawData = radiotapPacket.getPayload().getRawData();
60                 // Search rawData for MAC of FlowPattern in sender/receiver section
61                 // [TODO needs verification that this section is actually the sender/receiver section]
62                 if (rawData.length < 16) {
63                     continue;
64                 }
65                 int prefixLength = mPattern.getMacPrefixRawBytes().length;
66                 byte[] mac1 = Arrays.copyOfRange(rawData, 4, prefixLength < 6 ? 4 + prefixLength : 10);
67                 byte[] mac2 = Arrays.copyOfRange(rawData, 10, prefixLength < 6 ? 10 + prefixLength : 16);
68                 if (!Arrays.equals(mac1, mPattern.getMacPrefixRawBytes()) && !Arrays.equals(mac2, mPattern.getMacPrefixRawBytes())) {
69                     // MAC prefix not present in raw data.
70                     continue;
71                 }
72                 // Packet related to device associated with the pattern we are looking for.
73                 int expectedLength = mPattern.getPacketLengthSequence().get(patternPackets.size());
74                 if (packet.length() == expectedLength) {
75                     patternPackets.add(packet);
76                     if (patternPackets.size() == mPattern.getLength()) {
77                         // Full pattern found, declare success if packets are within some reasonable amount of time of
78                         // one another.
79                         // For now, we use a hardcoded value.
80                         if (patternPackets.get(patternPackets.size()-1).getTimestamp().getEpochSecond() -
81                                 patternPackets.get(0).getTimestamp().getEpochSecond() < 5) {
82                             System.out.println(String.format("[ find ] Detected a COMPLETE MATCH of pattern '%s' at %s!",
83                                     mPattern.getPatternId(), patternPackets.get(0).getTimestamp().toString()));
84                         }
85                         // Reset search by resetting list.
86                         patternPackets = new ArrayList<>();
87                     }
88                 } else {
89                     // Discard packet, not relevant to pattern.
90                     continue;
91                 }
92             }
93         } catch (EOFException e) {
94             // TODO wait for, and print, results.
95         } catch (PcapNativeException|TimeoutException|NotOpenException e) {
96             e.printStackTrace();
97         }
98     }
99
100 }
101
102