First work on layer 2 sequence matching. Basic functionality seems to work. Cleanup...
[pingpong.git] / Code / Projects / SmartPlugDetector / src / main / java / edu / uci / iotproject / detection / L2ClusterMatcher.java
1 package edu.uci.iotproject.detection;
2
3 import edu.uci.iotproject.Layer2Flow;
4 import edu.uci.iotproject.L2FlowReassembler;
5 import edu.uci.iotproject.StateMachine;
6 import edu.uci.iotproject.util.PcapPacketUtils;
7 import org.pcap4j.core.PacketListener;
8 import org.pcap4j.core.PcapPacket;
9 import org.pcap4j.util.MacAddress;
10
11 import java.util.*;
12
13 /**
14  * Layer 2 cluster matcher.
15  *
16  * @author Janus Varmarken {@literal <jvarmark@uci.edu>}
17  * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
18  */
19 public class L2ClusterMatcher extends AbstractClusterMatcher implements PacketListener {
20
21     private final MacAddress mRouterMac = null;
22     private final MacAddress mPhoneMac = null;
23     private final MacAddress mDeviceMac = null;
24
25     /**
26      * Reassembles traffic flows.
27      */
28     private final L2FlowReassembler mFlowReassembler = new L2FlowReassembler();
29
30     /**
31      * Each inner set holds the possible packet lengths for the packet at the corresponding index in a sequemce, taken
32      * across all sequences in {@link #mCluster}. For example, if the cluster is comprised of the sequences [112, 115]
33      * and [112, 116], the set at index 0 will be {112}, and the set at index 1 will be {115, 116}.
34      */
35     private final List<Set<Integer>> mValidPktLengths;
36
37
38
39     // Maintain one state machine for each layer...?
40     private final StateMachine[] seqMatchers;
41
42     public L2ClusterMatcher(List<List<PcapPacket>> cluster) {
43         super(cluster);
44
45         mValidPktLengths = new ArrayList<>();
46         for (int i = 0; i < mCluster.get(0).size(); i++) {
47             mValidPktLengths.add(new HashSet<>());
48         }
49         for (List<PcapPacket> seqVariation : mCluster) {
50             for (int i = 0; i < seqVariation.size(); i++) {
51                 mValidPktLengths.get(i).add(seqVariation.get(i).getOriginalLength());
52             }
53         }
54
55         seqMatchers = new StateMachine[mValidPktLengths.size()];
56     }
57
58     @Override
59     protected List<List<PcapPacket>> pruneCluster(List<List<PcapPacket>> cluster) {
60         return null;
61     }
62
63
64     @Override
65     public void gotPacket(PcapPacket packet) {
66         for (int i = 0; i < seqMatchers.length; i++) {
67             StateMachine sm = seqMatchers[i];
68             if (sm.attemptAdvance(packet)) {
69
70             }
71
72         }
73
74
75
76
77
78
79
80         for (int i = 0; i < mValidPktLengths.size(); i++) {
81             if (mValidPktLengths.get(i).contains(packet.getOriginalLength())) {
82                 // This packet length is potentially of interest to state machines that currently expect the i'th packet
83                 // of the searched sequence
84
85             }
86         }
87
88
89
90
91         // Forward to flow reassembler
92         mFlowReassembler.gotPacket(packet);
93
94
95
96
97     }
98
99
100     public void performDetection() {
101         for (Layer2Flow flow : mFlowReassembler.getFlows()) {
102             List<PcapPacket> flowPkts = flow.getPackets();
103
104             for (List<PcapPacket> signatureSequence : mCluster) {
105
106             }
107         }
108     }
109
110 /*
111     private Optional<List<PcapPacket>> findSubsequenceInSequence(List<PcapPacket> subsequence,
112                                                                  List<PcapPacket> sequence,
113                                                                  boolean[] subsequenceDirections) {
114         if (sequence.size() < subsequence.size()) {
115             // If subsequence is longer, it cannot be contained in sequence.
116             return Optional.empty();
117         }
118         // If packet directions have not been precomputed by calling code, we need to construct them.
119         if (subsequenceDirections == null) {
120             subsequenceDirections = getPacketDirections(subsequence);
121         }
122
123
124
125
126
127
128 //        if (sequenceDirections == null) {
129 //            sequenceDirections = getPacketDirections(sequence);
130 //        }
131
132
133         boolean[] sequenceDirections;
134
135         int subseqIdx = 0;
136         int seqIdx = 0;
137         while (seqIdx < sequence.size()) {
138             if (subseqIdx == 0) {
139                 // Every time we (re-)start matching (i.e., when we consider the first element of subsequence), we must
140                 // recompute the directions array for the subsequence.size() next elements of sequence so that we can
141                 // perform index-wise comparisons of the individual elements of the two direction arrays. If we compute
142                 // the directions array for the entire sequence in one go, we may end up with a reversed representation
143                 // of the packet directions (i.e. one in which all boolean values in the array are flipped to be the
144                 // opposite of what is the expected order) for a subsection of sequence that actually obeys the expected
145                 // directions (as defined by the directions array corresponding to subsequence), depending on the packets
146                 // that come earlier (as we always use 'true' for the first packet direction of a sequence).
147                 int toIndex = Integer.min(seqIdx + subsequence.size(), sequence.size());
148                 sequenceDirections = getPacketDirections(sequence.subList(seqIdx, toIndex));
149             }
150
151
152             PcapPacket subseqPkt = subsequence.get(subseqIdx);
153             PcapPacket seqPkt = sequence.get(seqIdx);
154             // We only have a match if packet lengths and directions match.
155             if (subseqPkt.getOriginalLength() == seqPkt.getOriginalLength() &&
156                     subsequenceDirections[subseqIdx] == sequenceDirections[subseqIdx]) {
157                 if (subseqIdx > 0) {
158
159                 }
160             }
161         }
162     }
163     */
164
165     /**
166      * Returns a boolean array {@code b} such that each entry in {@code b} indicates the direction of the packet at the
167      * corresponding index in {@code pktSequence}. As there is no notion of client and server, we model the
168      * packet directions as simple binary values. The direction of the first packet in {@code pktSequence} (and all
169      * subsequent packets going in the same direction) is denoted using a value of {@code true}, and all packets going
170      * in the opposite direction are denoted using a value of {@code false}.
171      *
172      * @param pktSequence A sequence of packets exchanged between two hosts for which packet directions are to be
173      *                    extracted.
174      * @return The packet directions for {@code pktSequence}.
175      */
176     private boolean[] getPacketDirections(List<PcapPacket> pktSequence) {
177         boolean[] directions = new boolean[pktSequence.size()];
178         for (int i = 0; i < pktSequence.size(); i++) {
179             if (i == 0) {
180                 // Special case for first packet: no previous packet to compare against.
181                 directions[i] = true;
182             } else {
183                 PcapPacket currPkt = pktSequence.get(i);
184                 PcapPacket prevPkt = pktSequence.get(i-1);
185                 if (PcapPacketUtils.getEthSrcAddr(currPkt).equals(PcapPacketUtils.getEthSrcAddr(prevPkt))) {
186                     // Same direction as previous packet.
187                     directions[i] = directions[i-1];
188                 } else {
189                     // Opposite direction of previous packet.
190                     directions[i] = !directions[i-1];
191                 }
192             }
193         }
194         return directions;
195     }
196 }