public void handleIPv4Packet(IPv4 ipv4, IPacketModuleContext ctx) {
    if (ipv4.getNextProtocol() != NetworkConstants.IPPROTO_TCP) {
      return;
    }

    TCP tcp = (TCP) ipv4.findHeader(TCP.class);

    if (tcp == null) {
      return;
    }
    if (foundAddresses.contains(ipv4.getSourceAddress())) {
      return;
    }

    Signature s = handleTCP(ipv4, tcp, ctx);
    if (s == null) return;

    log(
        ctx,
        "match! ["
            + ipv4.getSourceAddress()
            + "] --> "
            + ipv4.getDestinationAddress()
            + " is "
            + s.getOSGenre()
            + " "
            + s.getOSVersion());
    foundAddresses.add(ipv4.getSourceAddress());

    INetworkEntityFactory factory = Activator.getInstance().getNetworkEntityFactory();
    factory.setOperatingSystem(
        ctx.getRealm(),
        ctx.getSpaceId(),
        ipv4.getSourceAddress(),
        s.getOSGenre() + " " + s.getOSVersion());
  }
  private Signature matchPacket(IPv4 ip, TCP tcp, int mode, IPacketModuleContext ctx) {
    int quirks = 0;

    if (ip.getHeaderLength32() > 5) {
      quirks |= Signature.QUIRK_IPOPT;
    }

    if (mode == MODE_RST && tcp.getACK()) {
      quirks |= Signature.QUIRK_RSTACK;
    }

    if (tcp.sequence().equals(tcp.ackSequence())) {
      quirks |= Signature.QUIRK_SEQEQ;
    }

    if (tcp.sequence().toInteger() == 0) {
      quirks |= Signature.QUIRK_SEQ0;
    }

    if (mode == MODE_OPEN) {
      if (tcp.getURG() || tcp.getFIN()) {
        quirks |= Signature.QUIRK_FLAGS;
      }

    } else {
      if (tcp.getPSH() || tcp.getURG() || tcp.getFIN()) {
        quirks |= Signature.QUIRK_FLAGS;
      }
    }

    if (tcp.getNextHeader() != null) {
      quirks |= Signature.QUIRK_DATA;
    }

    ByteBuffer options = ByteBuffer.wrap(tcp.getOptionsBuffer());
    int mss = 0;
    int wsc = 0;
    int tstamp = 0;
    byte[] op = new byte[Signature.MAXOPT];
    int opCount = 0;
    boolean done = false;

    while (!done && options.hasRemaining()) {

      int currentOp = options.get() & 0xFF;
      switch (currentOp) {
        case TCP.OPT_EOL:
          op[opCount++] = TCP.OPT_EOL;
          if (options.hasRemaining()) {
            quirks |= Signature.QUIRK_PAST;
            done = true;
          }
          break;
        case TCP.OPT_NOP:
          op[opCount++] = TCP.OPT_NOP;
          break;
        case TCP.OPT_SACKOK:
          op[opCount++] = TCP.OPT_SACKOK;
          options.get(); // length byte
          break;
        case TCP.OPT_MAXSEG:
          if (options.remaining() < 3) {
            quirks |= Signature.QUIRK_BROKEN;
            done = true;
            break;
          }
          op[opCount++] = TCP.OPT_MAXSEG;
          options.get(); // length byte
          mss = options.getShort() & 0xFFFF;
          break;

        case TCP.OPT_WSCALE:
          if (options.remaining() < 2) {
            quirks |= Signature.QUIRK_BROKEN;
            done = true;
            break;
          }
          op[opCount++] = TCP.OPT_WSCALE;
          options.get(); // length byte
          wsc = options.get() & 0xFF;
          break;
        case TCP.OPT_TIMESTAMP:
          if (options.remaining() < 9) {
            quirks |= Signature.QUIRK_BROKEN;
            done = true;
            break;
          }
          op[opCount++] = TCP.OPT_TIMESTAMP;
          options.get(); // length byte
          tstamp = options.getInt();
          if (options.getInt() != 0) {
            quirks |= Signature.QUIRK_T2;
          }
          break;

        default:
          if (!options.hasRemaining()) {
            quirks |= Signature.QUIRK_BROKEN;
            done = true;
            break;
          }
          op[opCount++] = (byte) currentOp;
          int olen = options.get() & 0xFF;
          if (olen > 32 || olen > options.remaining()) {
            // p0f has a bug and won't set the QUIRK flag
            // in the second case.
            quirks |= Signature.QUIRK_BROKEN;
            done = true;
            break;
          }

          options.position(options.position() + olen);
          if (opCount >= Signature.MAXOPT) {
            quirks |= Signature.QUIRK_BROKEN;
            done = true;
          }
      }
    }

    if (tcp.getACK()) quirks |= Signature.QUIRK_ACK;
    if (tcp.getURG()) quirks |= Signature.QUIRK_URG;
    if (tcp.getReserved() != 0) quirks |= Signature.QUIRK_X2;
    if (ip.getIdentification() == 0) quirks |= Signature.QUIRK_ZEROID;

    Signature res =
        sigSet.match(
            ip.getTotalLength(),
            ip.getDF(),
            ip.getTimeToLive(),
            tcp.getWindow(),
            op,
            opCount,
            mss,
            wsc,
            tstamp,
            ip.getTypeOfService(),
            quirks,
            mode);
    if (res == null) {
      log(
          ctx,
          "Unmatched ["
              + ip.getSourceAddress()
              + "] --> "
              + ip.getDestinationAddress()
              + " has signature "
              + sigSet.printSignature(
                  ip.getTotalLength(),
                  ip.getDF(),
                  ip.getTimeToLive(),
                  tcp.getWindow(),
                  op,
                  opCount,
                  mss,
                  wsc,
                  tstamp,
                  ip.getTypeOfService(),
                  quirks,
                  mode));
    }

    return res;
  }