All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.sidnlabs.pcap.PcapReader Maven / Gradle / Ivy

There is a newer version: 0.2.24
Show newest version
/*
 * ENTRADA, a big data platform for network data analytics
 *
 * Copyright (C) 2016 SIDN [https://www.sidn.nl]
 * 
 * This file is part of ENTRADA.
 * 
 * ENTRADA is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 * 
 * ENTRADA is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with ENTRADA. If
 * not, see [ stream() {
    Iterable valueIterable = PacketIterator::new;
    return StreamSupport.stream(valueIterable.spliterator(), false);
  }


  /**
   * Clear expired cache entries in order to avoid memory problems
   * 
   * @param tcpCacheTTL timeout for tcp flows
   * @param ipCacheTTL timeout for IP fragments
   */
  public void clearCache(int tcpCacheTTL, int ipCacheTTL) {
    ipDecoder.clearCache(ipCacheTTL);
    tcpDecoder.clearCache(tcpCacheTTL);
  }

  public void close() {
    try {
      is.close();
    } catch (IOException e) {
      log.error("Error closing PCAP data inputstream", e);
    }
  }

  private Packet nextPacket() {
    byte[] pcapPacketHeader = new byte[PACKET_HEADER_SIZE];
    if (!readBytes(pcapPacketHeader)) {
      // no more data left
      log.info("Reached end of file, or zero-length file?");
      return null;
    }

    long packetSize =
        PcapReaderUtil.convertInt(pcapPacketHeader, CAP_LEN_OFFSET, reverseHeaderByteOrder);
    byte[] packetData = new byte[(int) packetSize];

    if (!readBytes(packetData)) {
      return Packet.NULL;
    }

    // find the start pos of the ip packet in the pcap frame
    int ipStart = findIPStart(packetData);

    if (ipStart == -1) {
      if (log.isDebugEnabled()) {
        log.debug("Invalid IP packet: {}", Hex.encodeHexString(packetData));
      }
      return Packet.NULL;
    }

    // the pcap header for each packet contains a timestamp with the capture time of the packet
    long packetTimestampSecs =
        PcapReaderUtil.convertInt(pcapPacketHeader, TIMESTAMP_OFFSET, reverseHeaderByteOrder);
    long packetTimestampMicros = PcapReaderUtil
        .convertInt(pcapPacketHeader, TIMESTAMP_MICROS_OFFSET, reverseHeaderByteOrder);

    // decode the packet bytes
    Packet decodedPacket =
        ipDecoder.decode(packetData, ipStart, packetTimestampSecs, packetTimestampMicros);

    packetCounter++;
    return decodedPacket;
  }

  protected boolean validateMagicNumber(byte[] pcapHeader) {
    if (PcapReaderUtil.convertInt(pcapHeader) == MAGIC_NUMBER) {
      return true;
    } else if (PcapReaderUtil.convertInt(pcapHeader, true) == MAGIC_NUMBER) {
      reverseHeaderByteOrder = true;
      return true;
    } else {
      return false;
    }
  }

  protected enum LinkType {
    NULL, EN10MB, RAW, LOOP, LINUX_SLL
  }

  protected LinkType getLinkType(long linkTypeVal) {
    switch ((int) linkTypeVal) {
      case 0:
        return LinkType.NULL;
      case 1:
        return LinkType.EN10MB;
      case 101:
        return LinkType.RAW;
      case 108:
        return LinkType.LOOP;
      case 113:
        return LinkType.LINUX_SLL;
      default:
        return null;
    }
  }

  protected int findIPStart(byte[] packet) {
    int start = -1;
    switch (linkType) {
      case NULL:
        return 4;
      case EN10MB:
        start = ETHERNET_HEADER_SIZE;
        int etherType = PcapReaderUtil.convertShort(packet, ETHERNET_TYPE_OFFSET);
        if (etherType == ETHERNET_TYPE_8021Q) {
          etherType = PcapReaderUtil.convertShort(packet, ETHERNET_TYPE_OFFSET + 4);
          start += 4;
        }
        if (etherType == ETHERNET_TYPE_IP || etherType == ETHERNET_TYPE_IPV6)
          return start;
        break;
      case RAW:
        return 0;
      case LOOP:
        return 4;
      case LINUX_SLL:
        return SLL_HEADER_BASE_SIZE
            + PcapReaderUtil.convertShort(packet, SLL_ADDRESS_LENGTH_OFFSET);
    }
    return -1;
  }



  protected boolean readBytes(byte[] buf) {
    try {
      is.readFully(buf);
    } catch (EOFException e) {
      // Reached the end of the stream
      caughtEOF = true;
      return false;
    } catch (IOException e) {
      log.error("Error while reading " + buf.length + " bytes from buffer");
      return false;
    }

    return true;
  }

  public Map getFlows() {
    return tcpDecoder.getFlows();
  }

  public void setFlows(Map flows) {
    tcpDecoder.setFlows(flows);
  }

  private class PacketIterator implements Iterator {
    private Packet next;

    private void fetchNext() {
      if (next == null) {
        // 1st check if reassembled response is available
        // if (tcpDecoder.hasReassembledPackets()) {
        // next = tcpDecoder.getNextReassmbledPacket();
        // reassembledPacketCounter++;
        // return;
        // }

        // skip fragmented packets until they are assembled
        do {
          try {
            next = nextPacket();
          } catch (Exception e) {
            log.error("PCAP decode error: ", e);
            next = Packet.NULL;
          }
        } while (next == Packet.NULL);
      }
    }

    @Override
    public boolean hasNext() {
      // fetchNext will keep result in "next" var so that when next() is
      // called the data does not have to be parsed a 2nd time
      fetchNext();
      if (next != null)
        return true;

      // // there might still be a reassembled packet in the tcpdecoder waiting to
      // // be fetched.
      // if (tcpDecoder.hasReassembledPackets()) {
      // return true;
      // }

      // no more data left
      int remainingFlows = tcpDecoder.getFlows().size() + ipDecoder.getDatagrams().size();
      if (remainingFlows > 0) {
        log.warn("Still " + remainingFlows + " flows queued. Missing packets to finish assembly?");
        log.warn("Packets processed: " + packetCounter);
        log.warn("Reassembled response packets: " + reassembledPacketCounter);
      }

      return false;
    }

    @Override
    public Packet next() {
      fetchNext();

      if (next == null) {
        throw new NoSuchElementException("No more packets to decode");
      }

      try {
        return next;
      } finally {
        // make sure to set next to null so the next packet is read from the stream
        next = null;
      }
    }

    @Override
    public void remove() {
      // Not supported
    }
  }

  public Multimap getDatagrams() {
    return ipDecoder.getDatagrams();
  }

  public void setDatagrams(Multimap map) {
    ipDecoder.setDatagrams(map);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy