+++ /dev/null
-package edu.uci.iotproject;
-
-import org.jgrapht.Graphs;
-import org.jgrapht.graph.DefaultEdge;
-import org.jgrapht.graph.SimpleDirectedGraph;
-import org.pcap4j.core.PcapPacket;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * TODO add class documentation.
- *
- * @author Janus Varmarken
- */
-public class StateMachine {
-
-
- private final SimpleDirectedGraph<Vertex, DefaultEdge> mGraph = new SimpleDirectedGraph<>(DefaultEdge.class);
-
-
- public StateMachine(List<List<PcapPacket>> subCluster) {
-
- for (List<PcapPacket> seqVariation : subCluster) {
-
- Vertex currVtx;
- Vertex prevVtx = null;
-
- for (int i = 0; i < seqVariation.size(); i++) {
- // Create new vertex corresponding to this packet of the sequence
- PcapPacket currPkt = seqVariation.get(i);
- currVtx = new Vertex(currPkt.getOriginalLength(), i);
-
-
-
-
-
- mGraph.addVertex(currVtx);
-
-
-
- if (prevVtx != null) {
- // Link vertex representing previous packet of sequence to this vertex.
- mGraph.addEdge(prevVtx, currVtx);
-
- }
-
- // Current vertex becomes previous vertex for next iteration.
- prevVtx = currVtx;
- }
- }
-
- }
-
-
- private Vertex mCurrentState;
-
-// @Override
-// public void gotPacket(PcapPacket packet) {
-// // Generate a vertex corresponding to the received packet.
-// // We expect a packet at the layer that follows the current state's layer.
-// Vertex pktVtx = new Vertex(packet.getOriginalLength(), mCurrentState.mLayer + 1);
-// // Check if such a vertex is present as a successor of the current state
-// Optional<Vertex> match = Graphs.successorListOf(mGraph, mCurrentState).stream().
-// filter(v -> v.equals(pktVtx)).findFirst();
-// // If yes, we move to that new state (new vertex).
-// match.ifPresent(v -> mCurrentState = v);
-// // TODO buffer the packets that got us here
-// // TODO check if we've reached the final layer...
-//
-// }
-
-
- /**
- * Attempts to use {@code packet} to advance this state machine.
- * @param packet
- * @return {@code true} if this state machine could progress by consuming {@code packet}, {@code false} otherwise.
- */
- public boolean attemptAdvance(PcapPacket packet) {
- // Generate a vertex corresponding to the received packet.
- // We expect a packet at the layer that follows the current state's layer.
- Vertex pktVtx = new Vertex(packet.getOriginalLength(), mCurrentState.mLayer + 1);
- // Check if such a vertex is present as a successor of the current state
- Optional<Vertex> match = Graphs.successorListOf(mGraph, mCurrentState).stream().
- filter(v -> v.equals(pktVtx)).findFirst();
- if (match.isPresent()) {
- // If yes, we move to that new state (new vertex).
- mCurrentState = match.get();
- // TODO buffer the packet to keep track of what packets got us here (keep track of the match)
- // TODO check if we've reached the final layer...
-
- return true;
- }
- return false;
- }
-
- private static class Vertex {
-
- // TODO how to include direction of packets here...
-
- private final int mPktLength;
- private final int mLayer;
-
-
- private Vertex(int pktLength, int layer) {
- mPktLength = pktLength;
- mLayer = layer;
- }
-
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof Vertex)) return false;
- Vertex that = (Vertex) obj;
- return that.mPktLength == this.mPktLength && that.mLayer == this.mLayer;
- }
-
- @Override
- public int hashCode() {
-// return Integer.hashCode(mPktLength);
- // Hack: use string's hashCode implementation.
- return (Integer.toString(mPktLength) + " " + Integer.toString(mLayer)).hashCode();
- }
-
- }
-}
+++ /dev/null
-package edu.uci.iotproject.detection;
-
-import edu.uci.iotproject.Layer2Flow;
-import edu.uci.iotproject.L2FlowReassembler;
-import edu.uci.iotproject.StateMachine;
-import edu.uci.iotproject.util.PcapPacketUtils;
-import org.pcap4j.core.PacketListener;
-import org.pcap4j.core.PcapPacket;
-import org.pcap4j.util.MacAddress;
-
-import java.util.*;
-
-/**
- * Layer 2 cluster matcher.
- *
- * @author Janus Varmarken {@literal <jvarmark@uci.edu>}
- * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
- */
-public class L2ClusterMatcher extends AbstractClusterMatcher implements PacketListener {
-
- private final MacAddress mRouterMac = null;
- private final MacAddress mPhoneMac = null;
- private final MacAddress mDeviceMac = null;
-
- /**
- * Reassembles traffic flows.
- */
- private final L2FlowReassembler mFlowReassembler = new L2FlowReassembler();
-
- /**
- * Each inner set holds the possible packet lengths for the packet at the corresponding index in a sequemce, taken
- * across all sequences in {@link #mCluster}. For example, if the cluster is comprised of the sequences [112, 115]
- * and [112, 116], the set at index 0 will be {112}, and the set at index 1 will be {115, 116}.
- */
- private final List<Set<Integer>> mValidPktLengths;
-
-
-
- // Maintain one state machine for each layer...?
- private final StateMachine[] seqMatchers;
-
- public L2ClusterMatcher(List<List<PcapPacket>> cluster) {
- super(cluster);
-
- mValidPktLengths = new ArrayList<>();
- for (int i = 0; i < mCluster.get(0).size(); i++) {
- mValidPktLengths.add(new HashSet<>());
- }
- for (List<PcapPacket> seqVariation : mCluster) {
- for (int i = 0; i < seqVariation.size(); i++) {
- mValidPktLengths.get(i).add(seqVariation.get(i).getOriginalLength());
- }
- }
-
- seqMatchers = new StateMachine[mValidPktLengths.size()];
- }
-
- @Override
- protected List<List<PcapPacket>> pruneCluster(List<List<PcapPacket>> cluster) {
- return null;
- }
-
-
- @Override
- public void gotPacket(PcapPacket packet) {
- for (int i = 0; i < seqMatchers.length; i++) {
- StateMachine sm = seqMatchers[i];
- if (sm.attemptAdvance(packet)) {
-
- }
-
- }
-
-
-
-
-
-
-
- for (int i = 0; i < mValidPktLengths.size(); i++) {
- if (mValidPktLengths.get(i).contains(packet.getOriginalLength())) {
- // This packet length is potentially of interest to state machines that currently expect the i'th packet
- // of the searched sequence
-
- }
- }
-
-
-
-
- // Forward to flow reassembler
- mFlowReassembler.gotPacket(packet);
-
-
-
-
- }
-
-
- public void performDetection() {
- for (Layer2Flow flow : mFlowReassembler.getFlows()) {
- List<PcapPacket> flowPkts = flow.getPackets();
-
- for (List<PcapPacket> signatureSequence : mCluster) {
-
- }
- }
- }
-
-/*
- private Optional<List<PcapPacket>> findSubsequenceInSequence(List<PcapPacket> subsequence,
- List<PcapPacket> sequence,
- boolean[] subsequenceDirections) {
- if (sequence.size() < subsequence.size()) {
- // If subsequence is longer, it cannot be contained in sequence.
- return Optional.empty();
- }
- // If packet directions have not been precomputed by calling code, we need to construct them.
- if (subsequenceDirections == null) {
- subsequenceDirections = getPacketDirections(subsequence);
- }
-
-
-
-
-
-
-// if (sequenceDirections == null) {
-// sequenceDirections = getPacketDirections(sequence);
-// }
-
-
- boolean[] sequenceDirections;
-
- int subseqIdx = 0;
- int seqIdx = 0;
- while (seqIdx < sequence.size()) {
- if (subseqIdx == 0) {
- // Every time we (re-)start matching (i.e., when we consider the first element of subsequence), we must
- // recompute the directions array for the subsequence.size() next elements of sequence so that we can
- // perform index-wise comparisons of the individual elements of the two direction arrays. If we compute
- // the directions array for the entire sequence in one go, we may end up with a reversed representation
- // of the packet directions (i.e. one in which all boolean values in the array are flipped to be the
- // opposite of what is the expected order) for a subsection of sequence that actually obeys the expected
- // directions (as defined by the directions array corresponding to subsequence), depending on the packets
- // that come earlier (as we always use 'true' for the first packet direction of a sequence).
- int toIndex = Integer.min(seqIdx + subsequence.size(), sequence.size());
- sequenceDirections = getPacketDirections(sequence.subList(seqIdx, toIndex));
- }
-
-
- PcapPacket subseqPkt = subsequence.get(subseqIdx);
- PcapPacket seqPkt = sequence.get(seqIdx);
- // We only have a match if packet lengths and directions match.
- if (subseqPkt.getOriginalLength() == seqPkt.getOriginalLength() &&
- subsequenceDirections[subseqIdx] == sequenceDirections[subseqIdx]) {
- if (subseqIdx > 0) {
-
- }
- }
- }
- }
- */
-
- /**
- * Returns a boolean array {@code b} such that each entry in {@code b} indicates the direction of the packet at the
- * corresponding index in {@code pktSequence}. As there is no notion of client and server, we model the
- * packet directions as simple binary values. The direction of the first packet in {@code pktSequence} (and all
- * subsequent packets going in the same direction) is denoted using a value of {@code true}, and all packets going
- * in the opposite direction are denoted using a value of {@code false}.
- *
- * @param pktSequence A sequence of packets exchanged between two hosts for which packet directions are to be
- * extracted.
- * @return The packet directions for {@code pktSequence}.
- */
- private boolean[] getPacketDirections(List<PcapPacket> pktSequence) {
- boolean[] directions = new boolean[pktSequence.size()];
- for (int i = 0; i < pktSequence.size(); i++) {
- if (i == 0) {
- // Special case for first packet: no previous packet to compare against.
- directions[i] = true;
- } else {
- PcapPacket currPkt = pktSequence.get(i);
- PcapPacket prevPkt = pktSequence.get(i-1);
- if (PcapPacketUtils.getEthSrcAddr(currPkt).equals(PcapPacketUtils.getEthSrcAddr(prevPkt))) {
- // Same direction as previous packet.
- directions[i] = directions[i-1];
- } else {
- // Opposite direction of previous packet.
- directions[i] = !directions[i-1];
- }
- }
- }
- return directions;
- }
-}