# Introduction

Pcap provides a packet processing library for rapid development on top of JVM languages.

## Hello Pcap!

"Hello pcap!" implementation in different JVM languages.

{% tabs %}
{% tab title="Java" %}

```
var service = Service.Creator.create("PcapService");
try (var pcap = service.live(service.interfaces(), new DefaultLiveOptions())) {
  pcap.loop(10, (args, header, buffer) -> {
    System.out.println("Args     : " + args);
    System.out.println("Header   : " + header);
    System.out.println("Packet   : " + buffer);
  }, "Hello pcap!");
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
val service = Service.Creator.create("PcapService")
val pcap = service.live(service.interfaces(), DefaultLiveOptions())
pcap.loop(10, { args, header, buffer ->
  println("Args     : $args")
  println("Header   : $header")
  println("Packet   : $buffer")
}, "Hello pcap!")
pcap.close()
```

{% endtab %}

{% tab title="Scala" %}

```
val service = Service.Creator.create("PcapService")
val pcap = service.live(service.interfaces(), new DefaultLiveOptions())
pcap.loop(10, (args: String, header: PacketHeader, buffer: PacketBuffer) => {
  println("Args     : ", args)
  println("Header   : ", header)
  println("Packet   : ", buffer)
}, "Hello pcap!")
pcap.close()
```

{% endtab %}

{% tab title="Groovy" %}

```
def service = Service.Creator.create("PcapService")
def pcap = service.live(service.interfaces(), new DefaultLiveOptions())
pcap.loop(10, { args, header, buffer ->
  println("Args     : " + args)
  println("Header   : " + header)
  println("Packet   : " + buffer)
}, "Hello pcap!");
pcap.close()
```

{% endtab %}

{% tab title="Clojure" %}

```
(def service (. Service$Creator (create "PcapService")))
(def pcap (.live service (.interfaces service) (DefaultLiveOptions.)))
(def handler (reify PacketHandler (gotPacket [this args header buffer]
  (println "Args     : " args)
  (println "Header   : " header)
  (println "Buffer    : " buffer))))
(.loop pcap 10 handler "Hello pcap!")
(.close pcap))
```

{% endtab %}

{% tab title="JRuby" %}

```
class Callback
  def gotPacket(args, header, buffer)
    puts "Args     : " + args
    puts "Header   : " + header.toString
    puts "Buffer    : " + buffer.toString
  end
end
service = Service::Creator.create('PcapService')
pcap = service.live(service.interfaces(), DefaultLiveOptions.new)
pcap.loop(10, Callback.new, "Hello pcap!")
pcap.close()
```

{% endtab %}

{% tab title="Jython" %}

```
class Callback(PacketHandler):
  def gotPacket(self, args, hdr, buf):
    print("Args     : ", args)
    print("Header   : ", hdr)
    print("Buffer    : ", buf)
service = DefaultService()
pcap = service.live(service.interfaces(), DefaultLiveOptions())
pcap.loop(10, Callback(), "Hello pcap!")
pcap.close()
```

{% endtab %}
{% endtabs %}

## Version History

Pcap is using semantic versioning: [https://semver.org](https://semver.org/)

| Version | Latest Version | Release Date | Latest Release |
| ------- | -------------- | ------------ | -------------- |
| 1.0.0   | 1.0.7          | 2020-12-24   | 2021-03-12     |
| 1.2.0   | 1.2.2          | 2021-02-01   | 2021-03-20     |
| 1.3.0   | 1.3.4          | 2021-05-02   | 2021-11-15     |
| 1.4.0   | 1.4.2          | 2021-12-25   | 2022-09-13     |

## Project Status

{% embed url="<https://github.com/users/ardikars/projects/1>" %}

## Supported platforms

* Linux (Tested)
* Mac OS (Tested)
* Windows (Tested)
* FreeBSD (Tested)
* Other OSes that supported by both Libpcap and JNA (Untested).

## Requirements

* Java 7+
* Libpcap for Unix or Npcap for Windows

## Java module system with java 9+

Pcap can be used in modular java 9+ applications with specific module name listed below:

* pcap-spi: pcap.spi
* pcap-jdk7: pcap.jdk7
* pcap-common: pcap.common
* pcap-codec: pcap.codec

{% hint style="warning" %}

## **Disclimers**

* Every types, methods, or fields annotated with @Incubating might be changed or removed at any time.
* Every types, methods, or fields annotated with @Incubating does not imply that the functionality being tested.
* Calling the @Restricted method might crash your app if you use it incorrectly. Make sure you know what you're doing with it.
  {% endhint %}


# Getting Started


# Dependency management

Pcap dependency management.

{% tabs %}
{% tab title="Meven" %}

```
<dependencies>
  <dependency>
    <groupId>com.ardikars.pcap</groupId>
    <artifactId>pcap-jdk7</artifactId>
  </dependency>
</dependencies>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.ardikars.pcap</groupId>
      <artifactId>pcap</artifactId>
      <version>${PCAP-LATEST-VERSION}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
```

{% endtab %}

{% tab title="Gradle" %}

```
dependencies {
  implementation ("com.ardikars.pcap:pcap-jdk7")
}

dependencyManagement {
  imports { mavenBom("com.ardikars.pcap:pcap:${PCAP-LATEST-VERSION}") }
}
```

{% endtab %}

{% tab title="SBT" %}

```
libraryDependencies ++= Seq(
  "com.ardikars.pcap" % "pcap-jdk7" % "${PCAP-LATEST-VERSION}"
)
```

{% endtab %}

{% tab title="Leiningen" %}

```
[com.ardikars.pcap/pcap-jdk7 "${PCAP-LATEST-VERSION}"]
```

{% endtab %}

{% tab title="Grape" %}

```
@Grapes(
  @Grab(group='com.ardikars.pcap', module='pcap-jdk7', version='${PCAP_LATEST_VERSION}')
)
```

{% endtab %}
{% endtabs %}


# Installing Libpcap or Npcap

Installing required third party software.

## On Windows

1. Download Npcap installer from [https://nmap.org/npcap](https://nmap.org/npcap/).
2. Install Npcap in "Winpcap Compatible Mode"

Other installation methods:

{% tabs %}
{% tab title="Powershell" %}

```
$urlPath = "https://nmap.org/npcap/dist/npcap-0.96.exe"
$checksum = "83667e1306fdcf7f9967c10277b36b87e50ee8812e1ee2bb9443bdd065dc04a1"

# Download the file
echo "Downloading... ($urlPath)"
wget $urlPath -UseBasicParsing -OutFile $PSScriptRoot"\npcap.exe"

# Now let's check its checksum
$_chksum = $(CertUtil -hashfile $PSScriptRoot"\npcap.exe" SHA256)[1] -replace " ",""
if ($_chksum -ne $checksum){
    echo "Hashes NOT match."
    exit
} else {
    echo "Hashes match."
    echo "Installing Npcap..."
}
# Run installer
Start-Process $PSScriptRoot"\npcap.exe" -ArgumentList "/S /npf_startup=yes /loopback_support=yes /dlt_null=no /admin_only=no /dot11_support=no /vlan_support=no /winpcap_mode=yes" -wait
echo "Npcap has been installed."
```

{% endtab %}

{% tab title="Chocolatey" %}

```
choco install nmap -y
```

{% endtab %}
{% endtabs %}

## On Linux

{% tabs %}
{% tab title="Debian/Ubuntu" %}

```
sudo apt install libpcap0.8
```

{% endtab %}

{% tab title="CentOS" %}

```
yum install libpcap
```

{% endtab %}
{% endtabs %}

## On Mac OS X

Libpcap should be installed on Mac OS X by default.

Using latest version of libpcap:

1. Download libpcap source code from [https://www.tcpdump.org](https://www.tcpdump.org/)
2. tar -xzvf libpcap\*.tar.gz
3. cd libpcap
4. ./configure --prefix=/usr/local
5. make
6. sudo make install
7. Run your application with custom native library path jna.library.path=/usr/local/lib


# Run as non root user

Operating System might require the root user to perform some operation.

{% tabs %}
{% tab title="Linux" %}

```
sudo setcap cap_net_raw,cap_net_admin=eip $PATH_TO_YOUR_JDK/bin/java
```

{% endtab %}

{% tab title="Mac" %}

```
sudo chown $(whoami):admin /dev/bpf*
```

{% endtab %}
{% endtabs %}


# Logging

Logging

Currently pcap-common logging only support Slf4j and Log4j2.

{% tabs %}
{% tab title="Logback" %}

```
<!-- Add requires org.slf4j; on module-info.java -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
</dependency>

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>x.y.z</version>
</dependency>
```

{% endtab %}

{% tab title="Log4j2" %}

```
<!-- Add requires org.apache.logging.log4j; on module-info.java -->

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>x.y.z</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>x.y.z</version>
</dependency>
```

{% endtab %}
{% endtabs %}

Sample logging configurations.

{% tabs %}
{% tab title="logback.xml" %}

```
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
```

{% endtab %}

{% tab title="log4j2.xml" %}

```
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>
```

{% endtab %}
{% endtabs %}


# Obtaining the device list

The following code retrieves the list of network interfaces that can be opened with Pcap.live(Interface, LiveOptions()).

Note: There may be network devices that cannot be opened by the process calling Service.interfaces(), because, for example, that process does not have sufficient privileges to open them for capturing; if so, those devices will not appear on the list.

Docs[: libpcap](https://www.tcpdump.org/manpages/pcap.3pcap.html), [javadoc](https://javadoc.io/doc/com.ardikars.pcap/pcap-spi/latest/pcap/spi/Service.html).

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] _args) throws ErrorException {
  var service = Service.Creator.create("PcapService");
  Iterator<Interface> devices = service.interfaces().iterator();
  while (devices.hasNext()) {
    Interface device = devices.next();
    System.out.println("Name             : " + device.name());
    System.out.println("Description      : " + device.description());
    System.out.println("Flags            : " + device.flags());
    if (device.addresses() != null) {
      System.out.println("Addresses        : ");
      Iterator<Address> addresses = device.addresses().iterator();
      while (addresses.hasNext()) {
        Address address = addresses.next();
        System.out.println("\tAddress      : " + address.address());
        System.out.println("\tNetmask      : " + address.netmask());
        System.out.println("\tBroadcast    : " + address.broadcast());
        System.out.println("\tDestination  : " + address.destination());
      }
    }
    System.out.println();
  }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
fun main() {
  val service = Service.Creator.create("PcapService")
  val devices: Iterator<Interface> = service.interfaces().iterator()
  while (devices.hasNext()) {
    val device = devices.next()
    println("Name             : " + device.name())
    println("Description      : " + device.description())
    println("Flags            : " + device.flags())
    if (device.addresses() != null) {
      println("Addresses        : ")
      val addresses: Iterator<Address> = device.addresses().iterator()
      while (addresses.hasNext()) {
        val address: Address = addresses.next()
        System.out.println("\tAddress      : " + address.address())
        System.out.println("\tNetmask      : " + address.netmask())
        System.out.println("\tBroadcast    : " + address.broadcast())
        System.out.println("\tDestination  : " + address.destination())
      }
    }
    println()
  }
}
```

{% endtab %}

{% tab title="Scala" %}

```
def main(args: Array[String]): Unit = {
  val service = Service.Creator.create("PcapService")
  val devices = service.interfaces.iterator
  while ( {
    devices.hasNext
  }) {
    val device = devices.next
    System.out.println("Name             : " + device.name)
    System.out.println("Description      : " + device.description)
    System.out.println("Flags            : " + device.flags)
    if (device.addresses != null) {
      System.out.println("Addresses        : ")
      val addresses = device.addresses.iterator
      while ( {
        addresses.hasNext
      }) {
        val address = addresses.next
        System.out.println("\tAddress      : " + address.address)
        System.out.println("\tNetmask      : " + address.netmask)
        System.out.println("\tBroadcast    : " + address.broadcast)
        System.out.println("\tDestination  : " + address.destination)
      }
    }
    System.out.println()
  }
}
```

{% endtab %}

{% tab title="Clojure" %}

```
(defn print_addresses [addresses]
  (println "Addresses        :")
  (while (.hasNext addresses)
    (def address (.next addresses))
    (println "\tAddreess      :" (.address address))
    (println "\tNetmask       :" (.netmask address))
    (println "\tBroadcast     :" (.broadcast address))
    (println "\tDestination   :" (.destination address))))

(defn -main
  [& args]
  (def service (. Service$Creator (create "PcapService")))
  (def devices (.iterator (.interfaces service)))
  (while (.hasNext devices)
    (def device (.next devices))
    (println "Name             :" (.name device))
    (println "Description      :" (.description device))
    (println "Flags            :" (.flags device))
    (if (some? (.addresses device))
      (print_addresses (.iterator (.addresses device))))))
```

{% endtab %}
{% endtabs %}


# Opening an adapter and capturing the packets

Obtain a packet capture handle (Pcap) with a specific network interface (adapter) to look at packets on the network and process it with Pcap.loop(int, PacketHandler, T args).

Once the adapter is opened, the capture can be started with Pcap.dispatch(int, PacketHandler, T args) or Pcap.dispatch(int, PacketHandler, T args). These two functions are very similar, the difference is that Pcap.dispatch(int, PacketHandler, T args) returns (although not guaranteed) when the timeout expires while Pcap.loop(int, PacketHandler, T args) doesn't return until maximum packets have been captured, so it can block for an arbitrary period on an under-utilized network.

Docs[: libpcap](https://www.tcpdump.org/manpages/pcap.3pcap.html), [javadoc](https://javadoc.io/doc/com.ardikars.pcap/pcap-spi/latest/pcap/spi/Pcap.html).

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] _args)
    throws ErrorException, PermissionDeniedException, PromiscuousModePermissionDeniedException,
        TimestampPrecisionNotSupportedException, RadioFrequencyModeNotSupportedException,
        NoSuchDeviceException, ActivatedException, InterfaceNotUpException,
        InterfaceNotSupportTimestampTypeException {
  var service = Service.Creator.create("PcapService");
  try (var pcap = service.live(service.interfaces(), new DefaultLiveOptions())) {
    try {
      pcap.loop(
          10,
          (args, header, buffer) -> {
            System.out.println("Args     : " + args);
            System.out.println("Header   : " + header);
            System.out.println("Packet   : " + buffer);
          },
          "Hello pcap!");
    } catch (BreakException e) {
      System.err.println(e.getMessage());
    } catch (ErrorException e) {
      System.err.println(e.getMessage());
    }
  }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
fun main() {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), DefaultLiveOptions())
  try {
    pcap.loop(
      10,
      { args: String, header: PacketHeader, buffer: PacketBuffer ->
        println("Args     : $args")
        println("Header   : $header")
        println("Packet   : $buffer")
      },
      "Hello pcap!"
    )
  } catch (e: BreakException) {
    System.err.println(e.message)
  } catch (e: ErrorException) {
    System.err.println(e.message)
  }
  pcap.close()
}
```

{% endtab %}

{% tab title="Scala" %}

```
def main(_args: Array[String]): Unit = {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces().next(), new DefaultLiveOptions())
  try pcap.loop(10, (args: String, header: PacketHeader, buffer: PacketBuffer) => {
    println("Args     : ", args)
    println("Header   : ", header)
    println("Packet   : ", buffer)
  }, "Hello pcap!")
  catch {
    case e: BreakException =>
      System.err.println(e.getMessage)
    case e: ErrorException =>
      System.err.println(e.getMessage)
  }
  pcap.close()
}
```

{% endtab %}
{% endtabs %}


# Capturing the packets without the callback

Handling a callback is sometimes not practical, it often makes the program more complex. So we need an alternative method to capture the packets without a callback.

Note: Both "PacketHeader" and "PacketBuffer" is not freed by the caller, and not guaranteed to be valid after the next call. If you needs it to remain valid, you must make a copy of it.

Docs[: libpcap](https://www.tcpdump.org/manpages/pcap.3pcap.html), [javadoc](https://javadoc.io/doc/com.ardikars.pcap/pcap-spi/latest/pcap/spi/Pcap.html).

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] _args)
    throws ErrorException, PermissionDeniedException, PromiscuousModePermissionDeniedException,
        TimestampPrecisionNotSupportedException, RadioFrequencyModeNotSupportedException,
        NoSuchDeviceException, ActivatedException, InterfaceNotUpException,
        InterfaceNotSupportTimestampTypeException {
  var service = Service.Creator.create("PcapService");
  try (var pcap = service.live(service.interfaces(), new DefaultLiveOptions())) {
    PacketHeader header = pcap.allocate(PacketHeader.class);
    PacketBuffer buffer = pcap.allocate(PacketBuffer.class);
    for (int i = 0; i < 10; i++) {
      try {
        pcap.nextEx(header, buffer);
        System.out.println("Header   : " + header);
        System.out.println("Packet   : " + buffer);
      } catch (BreakException e) {
        System.err.println(e.getMessage());
      } catch (TimeoutException e) {
        System.err.println(e.getMessage());
      }
    }
  }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
fun main() {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), DefaultLiveOptions())
  pcap.setFilter("icmp", true)
  val header = pcap.allocate(PacketHeader::class.java)
  val buffer = pcap.allocate(PacketBuffer::class.java)
  for (i in 0..9) {
    try {
      pcap.nextEx(header, buffer)
      println("Header   : $header")
      println("Packet   : $buffer")
    } catch (e: BreakException) {
      System.err.println(e.message)
    } catch (e: TimeoutException) {
      System.err.println(e.message)
    }
  }
  pcap.close()
}
```

{% endtab %}

{% tab title="Scala" %}

```
def main(_args: Array[String]): Unit = {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), new DefaultLiveOptions())
  val header = pcap.allocate(classOf[PacketHeader])
  val buffer = pcap.allocate(classOf[PacketBuffer])
  for (i <- 0 until 10) {
    try {
      pcap.nextEx(header, buffer)
      System.out.println("Header   : " + header)
      System.out.println("Packet   : " + buffer)
    } catch {
      case e: BreakException =>
        System.err.println(e.getMessage)
      case e: TimeoutException =>
        System.err.println(e.getMessage)
      case e: ErrorException =>
        System.err.println(e.getMessage)
    }
  }
  pcap.close()
}
```

{% endtab %}

{% tab title="Clojure" %}

```
(defn -main
  [& args]
  (def service (. Service$Creator (create "PcapService")))
  (def pcap (.live service (.interfaces service) (DefaultLiveOptions.)))
  (def header (.allocate pcap pcap.spi.PacketHeader))
  (def buffer (.allocate pcap pcap.spi.PacketBuffer))
  (dotimes [n 10]
    (.nextEx pcap header buffer)
    (println header)
    (println buffer))
  (.close pcap))
```

{% endtab %}
{% endtabs %}


# Capture statistics

Getting statistics about packets received and dropped in a live capture.

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] _args)
    throws ErrorException, PermissionDeniedException, PromiscuousModePermissionDeniedException,
        TimestampPrecisionNotSupportedException, RadioFrequencyModeNotSupportedException,
        NoSuchDeviceException, ActivatedException, InterfaceNotUpException,
        InterfaceNotSupportTimestampTypeException {
  var service = Service.Creator.create("PcapService");
  try (var pcap = service.live(service.interfaces(), new DefaultLiveOptions())) {
    PacketHeader header = pcap.allocate(PacketHeader.class);
    PacketBuffer buffer = pcap.allocate(PacketBuffer.class);
    for (int i = 0; i < 10; i++) {
      try {
        pcap.nextEx(header, buffer);
        System.out.println("Header   : " + header);
        System.out.println("Packet   : " + buffer);
        val stats = pcap.stats();
        System.out.println("Stats    : " + stats);
      } catch (BreakException e) {
        System.err.println(e.getMessage());
      } catch (TimeoutException e) {
        System.err.println(e.getMessage());
      }
    }
  }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
fun main() {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), DefaultLiveOptions())
  pcap.setFilter("icmp", true)
  val header = pcap.allocate(PacketHeader::class.java)
  val buffer = pcap.allocate(PacketBuffer::class.java)
  for (i in 0..9) {
    try {
      pcap.nextEx(header, buffer)
      println("Header   : $header")
      println("Packet   : $buffer")
      val stats = pcap.stats()
      println("Stats    : $stats")
    } catch (e: BreakException) {
      System.err.println(e.message)
    } catch (e: TimeoutException) {
      System.err.println(e.message)
    }
  }
  pcap.close()
}
```

{% endtab %}

{% tab title="Scala" %}

```
def main(_args: Array[String]): Unit = {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), new DefaultLiveOptions())
  val header = pcap.allocate(classOf[PacketHeader])
  val buffer = pcap.allocate(classOf[PacketBuffer])
  for (i <- 0 until 10) {
    try {
      pcap.nextEx(header, buffer)
      System.out.println("Header   : " + header)
      System.out.println("Packet   : " + buffer)
      val stats = pcap.stats()
      System.out.println("Stats    : " + stats)
    } catch {
      case e: BreakException =>
        System.err.println(e.getMessage)
      case e: TimeoutException =>
        System.err.println(e.getMessage)
      case e: ErrorException =>
        System.err.println(e.getMessage)
    }
  }
  pcap.close()
}
```

{% endtab %}

{% tab title="Groovy" %}

```
static void main(String[] usrArgs) {
  def service = Service.Creator.create("PcapService")
  def pcap = service.live(service.interfaces(), new DefaultLiveOptions())
  def header = pcap.allocate(PacketHeader.class)
  def buffer = pcap.allocate(PacketBuffer.class)
  try {
    pcap.nextEx(header, buffer)
    println("Header  : " + header)
    println("Packet  : " + buffer)
    Statistics stats = pcap.stats();
    println("Stats   : " + stats)
  } catch (BreakException e) {
    System.err.println(e.getMessage())
  } catch (TimeoutException e) {
    System.err.println(e.getMessage())
  } catch (ErrorException e) {
    System.err.println(e.getMessage())
  }
  pcap.close()
}
```

{% endtab %}
{% endtabs %}


# Filtering the traffic

In order to cause only certain packets to be returned when reading packets, a filter can be set on a pcap handle.

Filter syntax: <https://www.tcpdump.org/manpages/pcap-filter.7.html>

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] _args)
    throws ErrorException, PermissionDeniedException, PromiscuousModePermissionDeniedException,
        TimestampPrecisionNotSupportedException, RadioFrequencyModeNotSupportedException,
        NoSuchDeviceException, ActivatedException, InterfaceNotUpException,
        InterfaceNotSupportTimestampTypeException {
  var service = Service.Creator.create("PcapService");
  try (var pcap = service.live(service.interfaces(), new DefaultLiveOptions())) {
    pcap.setFilter("icmp", true);
    try {
      pcap.loop(
          10,
          (args, header, buffer) -> {
            System.out.println("Args     : " + args);
            System.out.println("Header   : " + header);
            System.out.println("Packet   : " + buffer);
          },
          "Hello pcap!");
    } catch (BreakException e) {
      System.err.println(e.getMessage());
    } catch (ErrorException e) {
      System.err.println(e.getMessage());
    }
  }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
fun main() {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), DefaultLiveOptions())
  pcap.setFilter("icmp", true)
  try {
    pcap.loop(
      10,
      { args: String, header: PacketHeader, buffer: PacketBuffer ->
        println("Args     : $args")
        println("Header   : $header")
        println("Packet   : $buffer")
      },
      "Hello pcap!"
    )
  } catch (e: BreakException) {
    System.err.println(e.message)
  } catch (e: ErrorException) {
    System.err.println(e.message)
  }
  pcap.close()
}
```

{% endtab %}

{% tab title="Scala" %}

```
def main(_args: Array[String]): Unit = {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), new DefaultLiveOptions())
  pcap.setFilter("icmp", true)
  try pcap.loop(10, (args: String, header: PacketHeader, buffer: PacketBuffer) => {
    println("Args     : ", args)
    println("Header   : ", header)
    println("Packet   : ", buffer)
  }, "Hello pcap!")
  catch {
    case e: BreakException =>
      System.err.println(e.getMessage)
    case e: ErrorException =>
      System.err.println(e.getMessage)
  }
  pcap.close()
}
```

{% endtab %}
{% endtabs %}


# Write the packets to a capture file

Capture the packets and write them to a capture file.

Note: Pcap.dumpOpen(String) will override if the "savefile" already exists; If you need to add a captured packet to the existing "savefile" you can use Pcap.dumpOpenAppend(String) instead.

Docs[: libpcap](https://www.tcpdump.org/manpages/pcap.3pcap.html), [javadoc](https://javadoc.io/doc/com.ardikars.pcap/pcap-spi/latest/pcap/spi/Dumper.html).

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] _args)
    throws ErrorException, PermissionDeniedException, PromiscuousModePermissionDeniedException,
        TimestampPrecisionNotSupportedException, RadioFrequencyModeNotSupportedException,
        NoSuchDeviceException, ActivatedException, InterfaceNotUpException,
        InterfaceNotSupportTimestampTypeException {
  var service = Service.Creator.create("PcapService");
  try (var pcap = service.live(service.interfaces(), new DefaultLiveOptions())) {
    try (var dumper = pcap.dumpOpen("savefile.pcap")) {
      try {
        pcap.loop(
            10,
            (args, header, buffer) -> {
              System.out.println("Args     : " + args);
              System.out.println("Header   : " + header);
              System.out.println("Packet   : " + buffer);
              dumper.dump(header, buffer);
            },
            "Hello pcap!");
      } catch (BreakException e) {
        System.err.println(e.getMessage());
      } catch (ErrorException e) {
        System.err.println(e.getMessage());
      }
    }
  }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
fun main() {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), DefaultLiveOptions())
  val dumper = pcap.dumpOpen("savefile.pcap")
  try {
    pcap.loop(
      10,
      { args: String, header: PacketHeader, buffer: PacketBuffer ->
        println("Args     : $args")
        println("Header   : $header")
        println("Packet   : $buffer")
        dumper.dump(header, buffer)
      },
      "Hello pcap!"
    )
  } catch (e: BreakException) {
    System.err.println(e.message)
  } catch (e: ErrorException) {
    System.err.println(e.message)
  }
  dumper.close()
  pcap.close()
}
```

{% endtab %}

{% tab title="Scala" %}

```
def main(_args: Array[String]): Unit = {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), new DefaultLiveOptions())
  val dumper = pcap.dumpOpen("savefile.pcap");
  try pcap.loop(10, (args: String, header: PacketHeader, buffer: PacketBuffer) => {
    println("Args     : ", args)
    println("Header   : ", header)
    println("Packet   : ", buffer)
    dumper.dump(header, buffer)
  }, "Hello pcap!")
  catch {
    case e: BreakException =>
      System.err.println(e.getMessage)
    case e: ErrorException =>
      System.err.println(e.getMessage)
  }
  dumper.close()
  pcap.close()
}
```

{% endtab %}
{% endtabs %}


# Read packets from a capture file.

Read packets from the capture file that was created from the previous section.

Docs[: libpcap](https://www.tcpdump.org/manpages/pcap.3pcap.html), [javadoc](https://javadoc.io/doc/com.ardikars.pcap/pcap-spi/latest/pcap/spi/Pcap.html).

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] _args) throws ErrorException {
  var service = Service.Creator.create("PcapService");
  try (var pcap = service.offline("savefile.pcap", new DefaultOfflineOptions())) {
    try {
      pcap.loop(
          10,
          (args, header, buffer) -> {
            System.out.println("Args     : " + args);
            System.out.println("Header   : " + header);
            System.out.println("Packet   : " + buffer);
          },
          "Hello pcap!");
    } catch (BreakException e) {
      System.err.println(e.getMessage());
    } catch (ErrorException e) {
      System.err.println(e.getMessage());
    }
  }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
fun main() {
  val service = Service.Creator.create("PcapService")
  val pcap = service.offline("savefile.pcap", DefaultOfflineOptions())
  try {
    pcap.loop(
      10,
      { args: String, header: PacketHeader, buffer: PacketBuffer ->
        println("Args     : $args")
        println("Header   : $header")
        println("Packet   : $buffer")
      },
      "Hello pcap!"
    )
  } catch (e: BreakException) {
    System.err.println(e.message)
  } catch (e: ErrorException) {
    System.err.println(e.message)
  }
  pcap.close()
}
```

{% endtab %}

{% tab title="Scala" %}

```
def main(_args: Array[String]): Unit = {
  val service = Service.Creator.create("PcapService")
  val pcap = service.offline("savefile.pcap", new DefaultOfflineOptions())
  try pcap.loop(10, (args: String, header: PacketHeader, buffer: PacketBuffer) => {
    println("Args     : ", args)
    println("Header   : ", header)
    println("Packet   : ", buffer)
  }, "Hello pcap!")
  catch {
    case e: BreakException =>
      System.err.println(e.getMessage)
    case e: ErrorException =>
      System.err.println(e.getMessage)
  }
  pcap.close()
}
```

{% endtab %}
{% endtabs %}


# Sending packets

Send a hand-crafted packet to the network.

Docs[: libpcap](https://www.tcpdump.org/manpages/pcap.3pcap.html), [javadoc](https://javadoc.io/doc/com.ardikars.pcap/pcap-spi/latest/pcap/spi/Pcap.html).

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] _args)
    throws ErrorException, PermissionDeniedException, PromiscuousModePermissionDeniedException,
        TimestampPrecisionNotSupportedException, RadioFrequencyModeNotSupportedException,
        NoSuchDeviceException, ActivatedException, InterfaceNotUpException,
        InterfaceNotSupportTimestampTypeException {
  var service = Service.Creator.create("PcapService");
  try (var pcap = service.live(service.interfaces(), new DefaultLiveOptions())) {
    PacketBuffer buffer = pcap.allocate(PacketBuffer.class).capacity(14);
    buffer.writeBytes(
        new byte[] {(byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255});
    buffer.writeBytes(
        new byte[] {(byte) 222, (byte) 173, (byte) 190, (byte) 239, (byte) 192, (byte) 254});
    buffer.writeShortRE(0x0806);
    try {
      pcap.sendPacket(buffer);
    } catch (ErrorException e) {
      System.err.println(e.getMessage());
    }
    buffer.release();
  }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
fun main() {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), DefaultLiveOptions())
  val buffer = pcap.allocate(PacketBuffer::class.java).capacity(14)
  buffer.writeBytes(byteArrayOf(255.toByte(), 255.toByte(), 255.toByte(), 255.toByte(), 255.toByte(), 255.toByte()))
  buffer.writeBytes(byteArrayOf(222.toByte(), 173.toByte(), 190.toByte(), 239.toByte(), 192.toByte(), 254.toByte()))
  buffer.writeShortRE(0x0806)
  try {
    pcap.sendPacket(buffer)
  } catch (e: ErrorException) {
    System.err.println(e.message)
  }
  buffer.release()
  pcap.close()
}
```

{% endtab %}

{% tab title="Scala" %}

```
def main(_args: Array[String]): Unit = {
  val service = Service.Creator.create("PcapService")
  val pcap = service.live(service.interfaces(), new DefaultLiveOptions())
  val buffer = pcap.allocate(classOf[PacketBuffer]).capacity(14)
  buffer.writeBytes(Array[Byte](255.toByte, 255.toByte, 255.toByte, 255.toByte, 255.toByte, 255.toByte))
  buffer.writeBytes(Array[Byte](222.toByte, 173.toByte, 190.toByte, 239.toByte, 192.toByte, 254.toByte))
  buffer.writeShortRE(0x0806)
  try pcap.sendPacket(buffer)
  catch {
    case e: ErrorException =>
      System.err.println(e.getMessage)
  }
  buffer.release()
  pcap.close()
}
```

{% endtab %}
{% endtabs %}


# I/O Multiplexing

Capturing on multiple interfaces with single thread.

Minimum version: 1.1.x or above.

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] _args) throws Exception {
  var service = Service.Creator.create("PcapService");
  var dev1 = service.interfaces();
  var dev2 = dev1.next();
  var pcap1 = service.live(dev1, new DefaultLiveOptions());
  var pcap2 = service.live(dev2, new DefaultLiveOptions());
  PacketHandler<Pcap> handler =
      (args, header, buffer) -> {
        System.out.println("Args     : " + args);
        System.out.println("Header   : " + header);
        System.out.println("Packet   : " + buffer);
      };
  var selector = service.selector();
  selector.register(pcap1);
  selector.register(pcap2);
  var timeout = new DefaultTimeout(1000000L, Timeout.Precision.MICRO);
  try {
    var selectables = selector.select(timeout);
    var iterator = selectables.iterator();
    while (iterator.hasNext()) {
      var next = iterator.next();
      var pcap = (Pcap) next;
      try {
        pcap.dispatch(1, handler, pcap);
      } catch (BreakException e) {
        System.err.println(e);
      }
    }
  } catch (TimeoutException e) {
    System.err.println(e);
  }
  selector.close();
  pcap1.close();
  pcap2.close();
}
```

{% endtab %}

{% tab title="Kotlin" %}

```
fun main(args: Array<String>) {
  val service = Service.Creator.create("PcapService")
  val dev1 = service.interfaces()
  val dev2 = dev1.next()
  val pcap1 = service.live(dev1, DefaultLiveOptions())
  val pcap2 = service.live(dev2, DefaultLiveOptions())
  val handler = PacketHandler { args: Pcap, header: PacketHeader, buffer: PacketBuffer ->
    println("Args     : $args")
    println("Header   : $header")
    println("Packet   : $buffer")
  }
  val selector = service.selector()
  selector.register(pcap1)
  selector.register(pcap2)
  val timeout: Timeout = DefaultTimeout(1000000L, Timeout.Precision.MICRO)
  val selectables = selector.select(timeout)
  val iterator: Iterator<Selectable> = selectables.iterator()
  while (iterator.hasNext()) {
    val next = iterator.next()
    val pcap = next as Pcap
    pcap.dispatch(1, handler, pcap)
  }
  selector.close()
  pcap1.close()
  pcap2.close()
}
```

{% endtab %}

{% tab title="Scala" %}

```
def main(_args: Array[String]): Unit = {
  val service = Service.Creator.create("PcapService")
  val dev1 = service.interfaces()
  val dev2 = dev1.next()
  val pcap1 = service.live(dev1, new DefaultLiveOptions())
  val pcap2 = service.live(dev2, new DefaultLiveOptions())
  val handler = new PacketHandler[(Pcap)] {
    override def gotPacket(args: Pcap, header: PacketHeader, buffer: PacketBuffer): Unit = {
      println("Args     : ", args)
      println("Header   : ", header)
      println("Packet   : ", buffer)
    }
  }
  val selector = service.selector()
  selector.register(pcap1)
  selector.register(pcap2)
  val timeout = new DefaultTimeout(1000000L, Timeout.Precision.MICRO)
  val selectables = selector.select(timeout)
  val iterator = selectables.iterator()
  while (iterator.hasNext) {
    val selectable = iterator.next()
    val pcap = selectable.asInstanceOf[Pcap]
    pcap.dispatch(1, handler, pcap)
  }
  selector.close()
  pcap1.close()
  pcap2.close()
}
```

{% endtab %}

{% tab title="Groovy" %}

```
static void main(String[] _args) {
  def service = Service.Creator.create("PcapService")
  def dev1 = service.interfaces()
  def dev2 = dev1.next()
  def pcap1 = service.live(dev1, new DefaultLiveOptions())
  def pcap2 = service.live(dev2, new DefaultLiveOptions())
  PacketHandler<Pcap> handler = new PacketHandler<Pcap>() {
    @Override
    void gotPacket(Pcap args, PacketHeader header, PacketBuffer buffer) {
      println("Args     : " + args)
      println("Header   : " + header)
      println("Packet   : " + buffer)
    }
  }
  def selector = service.selector()
  selector.register(pcap1)
  selector.register(pcap2)
  def timeout = new DefaultTimeout(1000000L, Timeout.Precision.MICRO)
  try {
    Iterable<Selectable> selectables = selector.select(timeout)
    Iterator<Selectable> iterator = selectables.iterator()
    while (iterator.hasNext()) {
      Selectable next = iterator.next()
      Pcap pcap = (Pcap) next
      try {
        pcap.dispatch(1, handler, pcap)
      } catch (BreakException e) {
        System.err.println(e)
      }
    }
  } catch (TimeoutException e) {
    System.err.println(e)
  }
  selector.close()
  pcap1.close()
  pcap2.close()
}
```

{% endtab %}
{% endtabs %}

Since 1.4.0

{% tabs %}
{% tab title="Java" %}

```
public static void main(String[] args) throws Exception {
  final Service service = Service.Creator.create("PcapService");
  final Interface interfaces = service.interfaces();
  final Selector selector = service.selector();
  final Iterator<Interface> iterator = interfaces.iterator();
  while (iterator.hasNext()) {
    final Interface source = iterator.next();
    if ((source.flags() & Interface.PCAP_IF_UP) != 0) {
      final Pcap live = service.live(source, new DefaultLiveOptions());
      final Selection selection = live.register(selector, Selection.OPERATION_READ, null);
      assert selection.interestOperations() == Selection.OPERATION_READ;
    }
  }
  final Timeout timeout = new DefaultTimeout(1000000L, Timeout.Precision.MICRO);
  for (int i = 0; i < 100; i++) {
    try {
      final int nSelected =
          selector.select(
              new Consumer<Selection>() {
                @Override
                public void accept(Selection selection) {
                  if (selection.isReadable()) {
                    final Pcap pcap = (Pcap) selection.selectable();
                    try {
                      pcap.dispatch(
                          1,
                          new PacketHandler<String>() {
                            @Override
                            public void gotPacket(
                                String s, PacketHeader packetHeader, PacketBuffer packetBuffer) {
                              System.out.println("Header : " + packetHeader);
                            }
                          },
                          "");
                      selection.interestOperations(Selection.OPERATION_WRITE);
                    } catch (BreakException e) {
                      System.err.println(e);
                    } catch (ErrorException e) {
                      System.err.println(e);
                    } catch (TimeoutException e) {
                      System.err.println(e);
                    }
                  } else if (selection.isWritable()) {
                    selection.interestOperations(Selection.OPERATION_READ);
                  }
                }
              },
              timeout);
      System.out.println("N selected : " + nSelected);
    } catch (TimeoutException e) {
      //
    }
  }
}
```

{% endtab %}

{% tab title="Second Tab" %}
Soon
{% endtab %}
{% endtabs %}


# Restricted Method

Restricted method

Access to method annotated with @Restricted is disabled by default; to enabled access to restricted method, the Pcap property 'pcap.restricted' must be set to a value other then deny. The possible values for this property are:

* deny: issues a runtime exception on each restricted call. This is the default value.
* permit: allows restricted calls.
* warn: like permit, but also prints a one-line warning on each restricted call.


# Developer Guide

Developer guideline.


# Branches to look

Git branches.

Development of all versions takes place in each branch whose name is identical to `<majorVersion>.<minorVersion>`. The `development` and `production` branch is for internal usage.


# Build from Source

Build jars from source

You require the following to build Pcap:

* Latest stable JDK 1.7 and JDK 11
* Latest stable Apache Maven, or you can use Maven Wrapper instead.

{% tabs %}
{% tab title="Unix" %}

```
export JAVA_HOME="PATH_TO_YOUR_JDK_11"
export JAVA_HOME_JDK7="PATH_TO_YOUR_JDK_7"
./mvnw -t .mvn/toolchains.xml package -Plegacy-support -Dmaven.test.skip=true

# Run with test and code coverage requires at least JDK-8
export JAVA_HOME_JDK7="PATH_TO_YOUR_JDK_8"
./mvnw -t .mvn/toolchains.xml package jacoco:report-aggregate -Pcoverage -Plegacy-support
```

{% endtab %}

{% tab title="Windows" %}

```
set JAVA_HOME="PATH_TO_YOUR_JDK_11"
set JAVA_HOME_JDK7="PATH_TO_YOUR_JDK_7"
mvnw.bat -t .mvn\toolchains.xml package -Plegacy-support -Dmaven.test.skip=true

;; Run with test and code coverage requires at least JDK-8
export JAVA_HOME_JDK7="PATH_TO_YOUR_JDK_8"
mvnw.bat -t .mvn/toolchains.xml package jacoco:report-aggregate -Pcoverage -Plegacy-support
```

{% endtab %}
{% endtabs %}


# Notes

Important notes for developers.

Commits:

* Commit [#212ab](https://github.com/ardikars/pcap/commit/212ab1bd759c0d9e1eca5a392e6d51f8ac530d88) is rewriting (amend) from previous commit, it is a dependency update.


# Packet Structure


# Packet Header

Generic per-packet information.

This packet header structure contains:

| Field Name     | Description                                                      |
| -------------- | ---------------------------------------------------------------- |
| Timestamp      | Specify a time interval (elapsed time)                           |
| Capture Length | Length of portion present (must be less then or equal to Length) |
| Length         | Length of current received packet (off wire)                     |

{% hint style="warning" %}
This header are not freed by the caller, and not guaranteed to be valid after the next call to Pcap.next(PacketHeader), Pcap.nextEx(PacketHeader, PacketBuffer), Pcap.loop(int cnt, PacketHandler, \<T>), and Pcap.dispatch(int cnt, PacketHandler, \<T>).
{% endhint %}


# Packet Buffer

Used to wrap low-lavel memory address (native buffer/off-heap buffer).

This buffer use network-byte-order (big endian) by default.

#### Using Packet Buffer.

* Allocate and Deallocate buffer

```java
final Pcap pcap = ..;
// allocate 8 bytes memory block.
final PacketBuffer buffer = pcap.allocate(PacketBuffer.class).capacity(8);

// deallocate ( free buffer).
assert true == buffer.release();
// double free is not allowed.
assert false == buffer.release();
```

* Slice and Unslice buffer

```java
// allocate 8 bytes memory block.
final PacketBuffer buffer = pcap.allocate(PacketBuffer.class).capacity(8);
final PacketBuffer sliced = buffer.slice();
final PacketBuffer unSliced = ((PacketBuffer.Sliced) sliced).unSlice();
assert buffer != sliced;
assert buffer == unSliced;
// Whatever you do with either sliced or unSliced buffer will affect the original buffer.
// for example when calling release() on the sliced buffer.
assert true == sliced.release();
assert false == buffer.release();
assert false == unSliced.release();
```

**What happen if I forget to call release() before it's garbage collected?**

PacketBuffer uses phantom reference for handling that case. Off-heap (native) buffer will be freed automatically when the object that wraps the off-heap buffer is garbage collected. Instead of waiting for unused buffer object garbage collected, you can free the off-heap buffer immediately by calling release() to avoid a large amount of unused off-heap memory.

You can enable memory leak detection by setting the properties "pcap.leakDetection" to "true". It can be useful to find where is the buffer that you forgot to release.

**What happen when I read/write (accessing) freed/closed buffer?**

MemoryAccessException will be thrown when you are trying to access the freed/closed buffer.

* Set/Get value from the buffer

```java
final Pcap pcap = ..;
// allocate 8 bytes memory block.
final PacketBuffer buffer = pcap.allocate(PacketBuffer.class).capacity(8);

buffer.setInt(0, 10);
buffet.setInt(4, 20);

assert 10 == buffer.getInt(0);
assert 20 == buffer.getInt(4);

assert true = buffer.release();
```

* Write/Read value from the buffer

```java
final Pcap pcap = ..;
// allocate 8 bytes memory block.
final PacketBuffer buffer = pcap.allocate(PacketBuffer.class).capacity(8);
assert 0 == buffer.readerIndex();
assert 0 == buffer.writerIndex();

buffer.writeInt(10);
assert 0 == buffer.readerIndex();
assert 4 == buffer.writerIndex();

buffer.writeInt(20);
assert 0 == buffer.readerIndex();
assert 8 == buffer.writerIndex();

assert 10 == buffer.readInt();
assert 4 == buffer.readerIndex();
assert 8 == buffer.writerIndex();

assert 20 == buffer.readInt();
assert 8 == buffer.readerIndex();
assert 8 == buffer.writerIndex();

assert true = buffer.release();
```

* Using PacketBuffer for nextEx call

```java
final Pcap pcap = ..;
final PacketHeader header = pcap.allocate(PacketHeader.class);
// no need to call buffer.capacity(..); no malloc needed.
final PacketBuffer buffer = pcap.allocate(PacketBuffer.class);

pcap.nextEx(header, buffer);

// buffer is not guaranteed to be valid after the next call
// copy the buffer if necessary
final copied = buffer.copy();
assert true == copied.release();

// no need to call buffer.release();
```


# Packet Codec

Crafting packet.

{% hint style="warning" %}
This feature is incubating
{% endhint %}


# Using packet codec

How to use packet codec

#### Example of working with packet codec.

```java
PacketBuffer packetBuffer = ..;

Ethernet ethernet = packetBuffer.cast(Ethernet.class);
System.out.println(ethernet);
if (ethernet.type() == Ip4.TYPE) {
  Ip4 ip4 = packetBuffer.readerIndex(ethernet.size()).cast(Ip4.class);
  System.out.println(ip4);
  if (ip4.protocol() == Tcp.TYPE) {
    Tcp tcp = packetBuffer.readerIndex(ethernet.size() + ip4.size()).cast(Tcp.class);
    System.out.println(tcp);
  } else if (ip4.protocol() == Udp.TYPE) {
    Udp udp = packetBuffer.readerIndex(ethernet.size() + ip4.size()).cast(Udp.class);
    System.out.println(udp);
  }
} else if (ethernet.type() == Ip6.TYPE) {
  Ip6 ip6 = packetBuffer.readerIndex(ethernet.size()).cast(Ip6.class);
  System.out.println(ip6);
  if (ip6.nextHeader() == Tcp.TYPE) {
    Tcp tcp = packetBuffer.readerIndex(ethernet.size() + ip6.size()).cast(Tcp.class);
    System.out.println(tcp);
  } else if (ip6.nextHeader() == Udp.TYPE) {
    Udp udp = packetBuffer.readerIndex(ethernet.size() + ip6.size()).cast(Udp.class);
    System.out.println(udp);
  }
}
```


# Adding protocol support

Adding protocol support.

You can extends you class with AbstractPacket inside pcap-codec module.

See [Ethernet](https://github.com/ardikars/pcap/blob/development/codec/src/main/java/pcap/codec/ethernet/Ethernet.java) and [Ip](https://github.com/ardikars/pcap/blob/development/codec/src/main/java/pcap/codec/ip/Ip4.java) class for example.


# Thanks to

Another project that inspiring this library.

* Jpcap and Jnetpcap
* Onos Project and Pcap4j
* Netty Project
* Slf4j
* Log4j

Development Tools:

* OpenJDK
* Intelij IDEA
* Maven and maven-plugins
* Junit5
* fmt-maven-plugin
* jacoco-maven-plugin
* nexus
* sonar
* Github (Sonar cloud, Dependabot, Stale, and DeepCode CI Bot)


