- // Buffer for new sequence matchers that will take over the job of observing for the first packet when a
- // sequence matcher advances beyond the first packet.
- List<Layer2SequenceMatcher> newSeqMatchers = new ArrayList<>();
- // Buffer for sequence matchers that have terminated and are to be removed from mPerFlowSeqMatchers.
- List<Layer2SequenceMatcher> terminatedSeqMatchers = new ArrayList<>();
- // Present the new packet to all sequence matchers
- for (Layer2SequenceMatcher sm : mPerFlowSeqMatchers.get(flow)) {
- boolean matched = sm.matchPacket(newPacket);
- if (matched && sm.getMatchedPacketsCount() == 1) {
- // Setup a new sequence matcher that matches from the beginning of the sequence so as to keep
- // progressing in the sequence matcher that just matched the current packet, while still allowing
- // for matches of the full sequence in later traffic. This is to accommodate the case where the
- // first packet of a sequence is detected in an early packet, but where the remaining packets of
- // that sequence do not appear until way later in time (e.g., if the first packet of the sequence
- // by chance is generated from traffic unrelated to the trigger traffic).
- // Note that we must store the new sequence matcher in a buffer and add it outside the loop in order to
- // prevent concurrent modification exceptions.
- newSeqMatchers.add(new Layer2SequenceMatcher(sm.getTargetSequence()));
- }
- if (matched && sm.getMatchedPacketsCount() == sm.getTargetSequencePacketCount()) {
- // This sequence matcher has a match of the sequence it was searching for
- // TODO report it.... for now just do a dummy printout.
- mObservers.forEach(o -> o.onMatch(this, sm.getMatchedPackets()));
-// System.out.println("SEQUENCE MATCHER HAS A MATCH AT " + sm.getMatchedPackets().get(0).getTimestamp());
- // Mark the sequence matcher for removal. No need to create a replacement one as we do that whenever the
- // first packet of the sequence is matched (see above).
- terminatedSeqMatchers.add(sm);
+ // Fetch table that contains sequence matchers for this flow.
+ Layer2SequenceMatcher[][] matchers = mPerFlowSeqMatchers.get(flow);
+ // Present the packet to all sequence matchers.
+ for (int i = 0; i < matchers.length; i++) {
+ // Present packet to the sequence matchers that has advanced the most first. This is to prevent discarding
+ // the sequence matchers that have advanced the most in the special case where the searched sequence
+ // contains two packets of the same length going in the same direction.
+ for (int j = matchers[i].length - 1; j >= 0 ; j--) {
+ Layer2SequenceMatcher sm = matchers[i][j];
+ if (sm == null) {
+ // There is currently no sequence matcher that has managed to match j packets.
+ continue;
+ }
+ boolean matched = sm.matchPacket(newPacket);
+ if (matched) {
+ if (sm.getMatchedPacketsCount() == sm.getTargetSequencePacketCount()) {
+ // Sequence matcher has a match. Report it to observers.
+ mObservers.forEach(o -> o.onMatch(this, sm.getMatchedPackets()));
+ // Remove the now terminated sequence matcher.
+ matchers[i][j] = null;
+ } else {
+ // Sequence matcher advanced one step, so move it to its corresponding new position iff the
+ // packet that advanced it has a later timestamp than that of the last matched packet of the
+ // sequence matcher at the new index, if any. In most traces, a small amount of the packets
+ // appear out of order (with regards to their timestamp), which is why this check is required.
+ // Obviously it would not be needed if packets where guaranteed to be processed in timestamp
+ // order here.
+ if (matchers[i][j+1] == null ||
+ newPacket.getTimestamp().isAfter(matchers[i][j+1].getLastPacket().getTimestamp())) {
+ matchers[i][j+1] = sm;
+ }
+ // We always want to have a sequence matcher in state 0, regardless of if the one that advanced
+ // from state zero replaced a different one in state 1 or not.
+ if (sm.getMatchedPacketsCount() == 1) {
+ matchers[i][j] = new Layer2SequenceMatcher(sm.getTargetSequence());
+ }
+ }
+ }