Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
540 lines (467 sloc) 30.1 KB
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,900%7CRoboto+Mono" rel="stylesheet">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.13/clipboard.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/prism.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/plugins/toolbar/prism-toolbar.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-java.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-scala.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism-okaidia.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
<style type="text/css">
body {
font-family: 'Roboto', sans-serif;
line-height:1.6;
color:rgb(5,5,5);
background:rgb(255,255,255);
max-width:60em;
}
code {
font-family: 'Roboto Mono', monospace;
}
header pre {
border:2px solid;
border-color: firebrick;
text-align: center;
padding:0.6em;
overflow-x:scroll;
}
header code {
font-size:1.6em;
font-weight:bold;
}
ul {
list-style-type: square;
}
a {
color:darkslategrey;
}
a:hover {
color:darkslateblue;
}
.token.operator {
background:none;
}
nav button {
box-shadow: 0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;
font-family: inherit;
font-size: 100%;
padding: .5em 1em;
color: #444;
border: 1px solid #999;
border: 0 rgba(0,0,0,0);
background-color: #E6E6E6;
text-decoration: none;
border-radius: 2px;
}
nav.hide ul {
display:none;
}
nav ul {
list-style-type: none;
}
nav li:before {
content: "- ";
}
figure {
display:inline-block;
text-align: center;
font-style: italic;
font-size: smaller;
text-indent: 0;
border: thin silver solid;
box-shadow: 1px 1px 3px black;
margin: 0.5em;
padding: 0.5em;
}
.clear { clear:both;}
</style>
<title>Capturing Packets with Scala Native and libpcap</title>
<meta name="twitter:title" content="Capturing Packets with Scala Native and libpcap">
<meta property="og:title" content="Capturing Packets with Scala Native and libpcap">
<meta property="og:url" content="/scala-native-libpcap/">
<link rel="canonical" href="/scala-native-libpcap/">
<!-- http://ogp.me/ -->
<meta property="og:type" content="article">
<meta property="article:published_time" content="2017-03-26">
<meta property="article:modified_time" content="2017-03-26">
<meta property="og:description" content="William Narmontas takes you through how to use Scala Native and libpcap to capture and analyse network packets. Scala Native compiles Scala to native code.">
<meta itemprop="description" content="William Narmontas takes you through how to use Scala Native and libpcap to capture and analyse network packets. Scala Native compiles Scala to native code.">
<meta name="description" content="William Narmontas takes you through how to use Scala Native and libpcap to capture and analyse network packets. Scala Native compiles Scala to native code.">
<meta name="twitter:description" content="William Narmontas takes you through how to use Scala Native and libpcap to capture and analyse network packets. Scala Native compiles Scala to native code.">
<meta property="og:site_name" content="Scala William">
<link rel="author" href="https://plus.google.com/u/0/103489630517643950426/">
<link rel="publisher" href="https://plus.google.com/u/0/103489630517643950426/">
<meta itemprop="name" content="The most important Streaming abstraction">
<meta property="og:image" content="https://avatars2.githubusercontent.com/u/2464813">
<meta itemprop="image" content="https://avatars2.githubusercontent.com/u/2464813">
<meta name="twitter:image" content="https://avatars2.githubusercontent.com/u/2464813">
<meta name="author" content="William Narmontas">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@ScalaWilliam">
</head>
<body class="h-entry">
<header>
<h1 class="p-name">Capturing Packets with Scala Native and libpcap</h1>
<h2>By <a href="/" class="u-author">William Narmontas</a>, <a href="/scala-native-libpcap/" class="u-url"><time class="dt-published" datetime="2017-03-26">March 2017</time></a></h2>
<p>For the source code, see the Scala application (updated July 2017, March 2018): <a href="https://github.com/ScalaWilliam/scala-native-libpcap">ScalaWilliam/scala-native-libpcap @ GitHub</a> (<a href="https://github.com/ScalaWilliam/scala-native-libpcap/blob/master/src/main/scala/PcapExample.scala">PcapExample.scala</a> in particular).
This page can be <a href="https://github.com/ScalaWilliam/ScalaWilliam.com/blob/master/scala-native-libpcap/index.html">edited on GitHub</a>.</p>
</header>
<nav id="contents">
<button onclick="toggle_contents();">Toggle Index / Table of Contents</button>
<ul>
<li> Test 1</li>
</ul>
</nav>
<article class="e-content">
<section>
<h2>Background</h2>
<section style="column-count:2">
<p><a href="https://en.wikipedia.org/wiki/Pcap">libcap</a> (also: <a href="http://www.tcpdump.org/pcap.html">pcap</a>) is a <a href="https://en.wikipedia.org/wiki/Packet_analyzer">network traffic packet capture library</a> that enables real-time and offline packet capture and analysis. Packet capture and analysis has <a href="https://en.wikipedia.org/wiki/Packet_analyzer#Uses">many use cases</a>.</p>
<p><a href="http://www.scala-native.org/">Scala Native</a> is an ahead-of-time compiler for <a href="http://www.scala-lang.org/">Scala</a> targeting <a href="http://llvm.org/">LLVM</a> and so capable of producing native binaries. This brings the promise of high performance coding using existing Scala skills and high quality tooling such as <a href="https://www.scalawilliam.com/essential-sbt/">SBT (Scala Build Tool)</a> and <a href="http://www.scalatest.org/">ScalaTest</a> as well as availability of patterns like <a href="http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html">type classes</a>.</p>
<p>Scala, which runs on the JVM, can interact with native libraries in two ways: <a href="https://github.com/java-native-access/jna/blob/master/README.md">JNA (Java Native Access)</a>
and <a href="https://en.wikipedia.org/wiki/Java_Native_Interface">JNI (Java Native Interface)</a>. <a href="http://stackoverflow.com/a/22289269/2789308">JNA slower but easier than JNI</a>.
For JNA you need nothing more <a href="http://mvnrepository.com/artifact/net.java.dev.jna/jna"> than a dependency</a> but for JNI you need <a href="https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html">to write native code</a>. When doing in Scala, you can benefit from the <a href="https://github.com/jodersky/sbt-jni">sbt-jni plugin</a> that automates this compilation. Scala Native's interop is similar to JNA.</p>
<p><a href="https://www.pcap4j.org/">Pcap4j</a> is an actively maintained library that wraps libpcap using JNA. Then there is <a href="http://jnetpcap.com/">jNetPcap</a> which as a project appears inactive and <a href="http://jnetpcap.com/docs/javadoc/jnetpcap-1.3/index.html">uses JNI</a> and <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html">ByteBuffer</a>. And a last way would be to use JNI with <a href="http://www.javaworld.com/article/2952869/java-platform/understanding-sun-misc-unsafe.html">Unsafe</a> <a href="https://vilasathavale.wordpress.com/2015/05/10/what-is-better-unsafe-bytebuffer-or-direct-bytebuffer/">for the highest performance</a>. The performance is difference is huge. There may be other even higher performance ways, but this is beyond the scope - if you have ideas do let me know <a href="https://twitter.com/ScalaWilliam">on Twitter</a>.</p>
<p>Packets can be captured in live mode using <a href="https://danielmiessler.com/study/tcpdump/">tcpdump</a>, replayed with <a href="http://tcpreplay.appneta.com/">tcpreplay</a> and visually analysed with <a href="https://www.wireshark.org/">Wireshark</a>. libpcap supports live capture and reading from files.</p>
<figure>
<p><img src="pcap-flow.svg"></p>
<figcaption>libpcap flow involving data copy from kernel to user space</figcaption>
</figure>
<p>In live capture mode, the Kernel will look for the next packet at the pcap_next call, pass through any defined filters, and then copy the data into user-space.</p>
<p>There are <a href="https://en.wikipedia.org/wiki/Netsniff-ng">solutions</a> <a href="http://www.ntop.org/products/packet-capture/pf_ring/pf_ring-zc-zero-copy/">for pure zero-copy</a> approach but it's beyond the scope of this article.</p>
</section>
<h2>Why this interests me</h2>
<ol>
<li>A client needed a high performance online packet analyser for the binary-encoded <a href="https://en.wikipedia.org/wiki/GPRS_Tunnelling_Protocol">GPRS Tunnelling Protocol (GTP)</a> which runs over UDP (see the <a href="http://www.etsi.org/deliver/etsi_ts/129200_129299/129274/12.06.00_60/ts_129274v120600p.pdf">GTPv2 specification</a> &#8212; large PDF!) and contains cell tower identifiers for 4G mobile subscribers. I implemented one solution with Scala &amp; libpcap.</li>
<li>I worked on Scala <a href="https://github.com/ScalaWilliam/duel.gg">projects</a> <a href="https://github.com/ScalaWilliam/ActionFPS">of mine</a> needed a native (JNA) interface layer to <a href="http://enet.bespin.org/">ENet, "Reliable UDP networking library"</a> and lots of binary parsing.</li>
<li>I was researching ways of achieving high performance data processing, including not only JVM Unsafe but also memory mapping and ring buffers.</li>
<li>I'm a big Scala fan, having worked on it professionally and non-commercially since 2013.</li>
</ol>
</section>
<section>
<h2>Developing a program</h2>
<p>For showcasing what's possible - we're going to go through developing a simple libpcap program using Scala Native.</p>
<p>Goal of the program would be to output packet information from files and also from a live interface. Minimal packet information would be:
Timestamp, source IP, destination IP, packet length, a few bytes of the packet in hexadecimal.</p>
<p>Note that functional and pure programming are not in scope of this article.</p>
<section>
<h3>Resources</h3>
<ul>
<li>Scala Native <a href="http://www.scala-native.org/en/latest/user/interop.html">The documentation</a> (<a href="https://media.readthedocs.org/pdf/scala-native/latest/scala-native.pdf">PDF</a>)</li>
<li>libpcap manpages, <a href="https://linux.die.net/man/3/pcap_open_live">linux.die.net</a> and <a href="http://www.tcpdump.org/manpages/pcap_open_live.3pcap.html">tcpdump.org</a></li>
<li><a href="https://fossies.org/dox/libpcap-1.8.1/pcap_2pcap_8h_source.html#l00163">libpcap sources</a></li>
</ul>
</section>
<section>
<h3>Preparing some reference data</h3>
<p>Which is basically a pcap file. Using tcpdump:</p>
<pre class="language-bash"><code>$ tcpdump -i [interface] -w sample.pcap</code></pre>
<p>Capture some packets, maybe do a <a href="http://www.speedtest.net/">speed test</a>, then terminate the app and now you have a pcap file which you can consume later.</p>
<p>Use <code>capinfos</code> to get basic information about your pcap file. You can also look at it visually with Wireshark.</p>
<figure>
<p><img src="wireshark.png"></p>
<figcaption>How a packet from sample.pcap looks in Wireshark</figcaption>
</figure>
</section>
<section>
<h3>Tools</h3>
<p>I use SBT to <a href="https://www.scalawilliam.com/essential-sbt/#run-continuously">continuously run the program</a> using <a href="http://www.scala-sbt.org/0.13/docs/Triggered-Execution.html">Triggered Execution</a>.</p>
<p>I use Docker and <a href="https://github.com/ScalaWilliam/scala-native-sbt-docker">ScalaWilliam/scala-native-sbt Docker image</a> to get an isolated Linux execution environment:</p>
<pre class="language-bash"><code>$ docker run -v $PWD:/workspace -w /workspace -it <a href="https://github.com/ScalaWilliam/scala-native-sbt-docker">scalawilliam/scala-native-sbt</a>
root@0c957f870d61:/workspace# apt-get -y install libpcap-dev</code></pre>
<p>And of course IntelliJ IDEA for its <a href="https://www.jetbrains.com/idea/whatsnew/#v2017-1-scala">excellent Scala support</a>.</p>
</section>
<section>
<h3>Minimal required application flow</h3>
<h4>Packet-reading flows</h4>
<figure style="float:right">
<p><img src="pcap-offline-app-flow.svg"></p>
<figcaption>libpcap offline flow</figcaption>
</figure>
<figure style="float:right">
<p><img src="pcap-live-app-flow.svg"></p>
<figcaption>libpcap live flow</figcaption>
</figure>
<p>We shall combine two flows into one app: Live and Offline (File).</p>
<h4>Processing the packet</h4>
<p><code>tshark</code> can give you this output already, but we're not interested in replacing <code>tshark</code>.</p>
<ol>
<li>Read timestamp</li>
<li>Read packet length</li>
<li>Determine whether packet is IPv4</li>
<li>Read source IP</li>
<li>Read destination IP</li>
<li>Read some bytes of data</li>
<li>... and at every step of the way, bound-check</li>
</ol>
<div class="clear"></div>
</section>
<section>
<h3>Minimal native mapping</h3>
<figure style="float:right">
<p><img src="pcap-header.svg"></p>
<figcaption>It's the same in memory and in storage</figcaption>
</figure>
<p>In order to call native methods we need some sort of interface definition. It's similar to defining a C header file which then is then <code>#import</code>'ed.</p>
<p>This was not particularly difficult achieve with the resources. Will be obvious to anyone who's done some C.</p>
<div class="clear"></div>
<pre class="language-scala"><code data-source="src/main/scala/pcap.scala" data-from="9" data-to="36">@native.link("pcap")
@native.extern
object pcap {
/** This is just a pointer for us, we don't care what is inside **/
type pcap_handle = native.Ptr[Unit]
type pcap_pkthdr = native.CStruct4[native.CUnsignedLong,
native.CUnsignedLong,
native.CUnsignedInt,
native.CUnsignedInt]
def pcap_open_live(deviceName: CString,
snapLen: CInt,
promisc: CInt,
to_ms: CInt,
errbuf: CString): pcap_handle =
native.extern
def pcap_open_offline(fname: CString, errbuf: CString): pcap_handle =
native.extern
def pcap_next(p: native.Ptr[Unit],
h: native.Ptr[pcap_pkthdr]): native.CString = native.extern
def pcap_close(p: native.Ptr[Unit]): Unit = native.extern
}</code></pre>
</section>
<section>
<h3>Minimal code for opening a pcap handle</h3>
<p>Here we'll capture from <code>any</code> interface by default.</p>
<pre class="language-scala"><code data-source="src/main/scala/PcapExample.scala" data-from="82" data-to="93">val pcapHandle = if (live) {
pcap.pcap_open_live(
deviceName = c"any",
snapLen = Short.MaxValue,
promisc = 0,
to_ms = 10,
errbuf = errorBuffer
)
} else {
pcap.pcap_open_offline(fname = toCString(args.last),
errbuf = errorBuffer)
}</code></pre>
</section>
<section>
<h3>Minimal code for continuously reading the handle</h3>
<p>At this point, I was getting closer to pointers and the like and if I did something wrong, I'd get a segfault with exit code 139. Still dislike Java exceptions and verbose stack traces?</p>
<pre class="language-scala"><code data-source="src/main/scala/PcapExample.scala" data-from="98" data-to="116">val packetHeaderPointer: native.Ptr[pcap.pcap_pkthdr] =
native.stackalloc[pcap.pcap_pkthdr]
var packetReadData = pcap.pcap_next(pcapHandle, packetHeaderPointer)
var continue = true
while (continue) {
if (packetReadData != null) {
process_packet(
epochSecond = (!packetHeaderPointer._1).toLong,
dataLength = (!packetHeaderPointer._3).toInt,
data = packetReadData,
cooked = cooked
)
} else if (!live) {
continue = false
}
if (continue) {
packetReadData = pcap.pcap_next(pcapHandle, packetHeaderPointer)
}
}</code></pre>
</section>
<section>
<h3>Processing an individual packet</h3>
<p>So at this point we have extracted the key information and pass a C-style string (pointer) to the method.</p>
<p>Note that this C-string is NOT a <a href="https://en.wikipedia.org/wiki/Null-terminated_string">null-terminated string</a> because packets may contain the byte 0x00 anywhere.
So you have to rely on input length to manipulate the incoming data.</p>
<pre class="language-scala"><code data-source="src/main/scala/PcapExample.scala" data-from="25" data-to="34">/**
* We have a separate processing function to separate out the plumbing.
*
* @param data remember this is a pointer! But note that it may contain byte 0x00
* which is typically a string termination character - so we must pass dataLength explicitly.
*/
def process_packet(epochSecond: Long,
dataLength: Int,
data: CString,
cooked: Boolean): Unit = {</code></pre>
</section>
<section>
<h3>Linux Cooked Capture</h3>
<p>IF we're capturing on Linux, consider <a href="https://wiki.wireshark.org/SLL">Linux cooked-mode capture (SLL)</a> which <a href="https://ask.wireshark.org/questions/21562/editcap-from-linux-cooked-capture-to-ethernet-packet">can be</a> <a href="https://www.wireshark.org/lists/ethereal-users/200412/msg00314.html">confusing</a>.</p>
<p>When we're in Cooked mode, there are 2 extra bytes at the front of the packet.</p>
<pre class="language-scala"><code data-source="src/main/scala/PcapExample.scala" data-from="36" data-to="36">val offsetBytes = if (cooked) 2 else 0</code></pre>
</section>
<section>
<h3>Check bounds and verify it's IPv4</h3>
<p>Dealing with IPv6 and and others is a different matter. Here we start incrementing pointers, in a pure manner, mind you.</p>
<pre class="language-scala"><code data-source="src/main/scala/PcapExample.scala" data-from="38" data-to="43">val hasEnoughData = dataLength &gt; (offsetBytes + PcapDestinationIpv4AddressOffset + 4)
if (!hasEnoughData) return
/** IP version is stored in the first nibble of the target byte **/
val isIpv4 = (!(data + IpVersionByteOffset + offsetBytes) &gt;&gt; 4) == 4
if (!isIpv4) return</code></pre>
</section>
<section>
<h3>Read source and destination IPs</h3>
<pre class="language-scala"><code data-source="src/main/scala/PcapExample.scala" data-from="51" data-to="52">val ip = !(data + PcapSourceIpv4AddressOffset + offsetBytes)
.cast[Ptr[CUnsignedInt]]</code></pre>
</section>
<section>
<h3>Make an IP human readable</h3>
<p>This was one thing that was easier in native land than in JVM land. As far as I'm aware there is no <a href="https://linux.die.net/man/3/inet_ntoa"><code>inet_ntoa</code></a> in the JVM to convert an IP address from Int into text form. This was easily achievable by native binding.
</p>
<pre class="language-scala"><code data-source="src/main/scala/inet.scala" data-from="6" data-to="15">/**
* We use this to avoid our own byte manipulation.
* Ironically I have to do this with bytes in Java, so scala-native is already proving itself!
*/
@native.extern
object inet {
def inet_ntoa(input: CUnsignedInt): native.CString = native.extern
}</code></pre>
<p>The usage is super simple:</p>
<pre class="language-scala"><code data-source="src/main/scala/PcapExample.scala" data-from="50" data-to="54">val sourceIp = {
val ip = !(data + PcapSourceIpv4AddressOffset + offsetBytes)
.cast[Ptr[CUnsignedInt]]
fromCString(inet.inet_ntoa(ip))
}</code></pre>
</section>
<section>
<h3>Printing the packet summary</h3>
<p>One line, one packet - with some data bytes in hex.</p>
<pre class="language-scala"><code data-source="src/main/scala/PcapExample.scala" data-from="60" data-to="68">print(s"Time: $epochSecond, $sourceIp --&gt; $destIp, $dataLength bytes: [")
(0 to Math.min(dataLength, 12))
.map { n =&gt;
!(data + offsetBytes + n)
}
.foreach { v =&gt;
native.stdio.printf(c"%02X", v)
}
println("...]")</code></pre>
</section>
</section>
<section>
<h2>Running for yourself</h2>
<p>...so you can reproduce this yourself.</p>
<p>Let's assume you've already started the Docker container as earlier in the article, and produced a sample pcap file.
After cloning the <a href="https://github.com/ScalaWilliam/scala-native-libpcap"><code>ScalaWilliam/scala-native-libpcap</code></a> repository,
do:
</p><pre class="language-bash"><code>root@0c957f870d61:/workspace/scala-native-libpcap# sbt clean 'show nativeLink'
...
[info] /workspace/scala-native-libpcap/target/scala-2.11/scala-native-libpcap-out
[success] Total time: 37 s, completed Mar 26, 2017 3:49:38 AM
root@0c957f870d61:/workspace/scala-native-libpcap# /workspace/scala-native-libpcap/target/scala-2.11/scala-native-libpcap-out /workspace/sample.pcap |head
Time: 1490492402, 192.168.1.53 --&gt; 74.125.68.149, 790 bytes: [1C872...] &lt;-- Google
Time: 1490492402, 74.125.68.149 --&gt; 192.168.1.53, 579 bytes: [38C9E...] &lt;-- Google
Time: 1490492403, 192.168.1.53 --&gt; 104.16.59.5, 54 bytes: [1C872...] &lt;-- CloudFare
Time: 1490492403, 104.16.59.5 --&gt; 192.168.1.53, 64 bytes: [38C98...] &lt;-- CloudFare
...</code></pre>
<h3>Running live</h3>
<p>Exercise for the reader... just read the source :-)</p>
</section>
<section>
<h2>Impact</h2>
<p>scala-native opens up a plethora of integration opportunities: you are no longer restricted to using
JVM-only libraries or waiting for those wrappers, no longer restricted to using C++/C for lower level or high performance programming. You can now rapidly iterate and test your code in JVM mode and port it to native easily.</p>
<p>This opens up the possibility of native interoperation with <a href="https://docs.python.org/3/extending/extending.html">Python</a> and <a href="http://www.lua.org/manual/5.1/manual.html#3">Lua</a> - and from JVM via <a href="http://www.luaj.org/luaj/3.0/README.html">Luaj</a>, <a href="https://github.com/mrj0/jep">jep</a> and <a href="http://www.jython.org/">Jython</a>. Good article: <a href="https://sushant-hiray.me/posts/python-in-scala-stack/">Integrating Python into Scala Stack</a>.</p>
<p>You can write your application in the JVM first knowing you can potentially scale it out later, should the JVM be the pain point. Though in my experience it really isn't, but still worth having that possibility.</p>
<p>Of course there will be many other use cases, and I'd like to mention them here as well - so why not <a href="https://twitter.com/ScalaWilliam">Tweet me</a> about them?</p>
<p>Make sure to watch <a href="https://twitter.com/den_sh">Denys Shabalin</a>'s <a href="http://scaladays.org/">Scala Days</a> talk <a href="https://www.youtube.com/watch?v=ArWWlwQl37A">"Scala Goes Native"</a> (<a href="https://github.com/densh/talks/blob/517b20c30dd4aaf390785039cdd002f623eaa91e/2016-05-11-scala-goes-native.pdf">slides</a>).</p>
</section>
<section>
<h2>Conclusion</h2>
<p>We managed to read offline and online packets using a native packet capture library and Scala.</p>
<p>There were no major difficulties while doing so, proving scala-native is a viable platform for native applications for an existing JVM development team who should strongly consider Scala.</p>
<p>I also came across some interesting (<a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.194.18&amp;rep=rep1&amp;type=pdf">PDF</a>) reading about packet capture. And learned that <a href="https://linuxconfig.org/how-to-install-missing-ifconfig-command-on-debian-linux">ifconfig is missing from latest Debian</a>!</p>
</section>
<section id="copy">
<p>Big thanks for free graphing software <a href="https://www.draw.io">draw.io</a>.</p>
<p>&#169; William Narmontas.</p>
</section>
</article>
<section>
<h2>Social media</h2>
<h3>Share on Twitter</h3>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">New! Capturing Packets with <a href="https://twitter.com/scala_native">@scala_native</a> and <a href="https://twitter.com/hashtag/libpcap?src=hash">#libpcap</a>.<br>See: <a href="https://t.co/F4A5Y4CBlz">https://t.co/F4A5Y4CBlz</a> <a href="https://twitter.com/scala_lang">@scala_lang</a> <a href="https://twitter.com/hashtag/scala?src=hash">#scala</a></p>&#8212; William Narmontas (@ScalaWilliam) <a href="https://twitter.com/ScalaWilliam/status/845994368892747777">March 26, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h3>Share</h3>
<div class="addthis_inline_share_toolbox"></div>
<h3>Follow</h3>
<style type="text/css">
#atftbx > p > span { display:none; }
</style>
<div class="addthis_inline_follow_toolbox"></div>
<h3>My other articles</h3>
<ul>
<li><a href="www.scalawilliam.com/most-important-streaming-abstraction/">Most important streaming abstraction</a></li>
<li><a href="https://medium.com/@ScalaWilliam/limit-degrees-of-freedom-in-development-4c543bb6f806#.xmrcpl8rg">Limit degrees of freedom in development
</a></li>
<li><a href="https://www.scalawilliam.com/essential-sbt/">Essential SBT</a></li>
<li><a href="https://hackernoon.com/feature-switches-inheritance-and-agile-with-scala-jmx-on-the-jvm-140b4bf94d9f?gi=d8324d17dca0#.vj0o8770w" target="_blank">Feature Switches, Inheritance and Agile with Scala &amp; JMX on the JVM</a></li>
</ul>
<!-- Go to www.addthis.com/dashboard to customize your tools --> <script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-584b716cc10a0e3c"></script>
<script type="text/javascript">
var ul = document.querySelector("#contents ul");
while ( ul.firstChild ) {
ul.removeChild(ul.firstChild);
};
[].forEach.call(document.querySelectorAll("article h2, article h3"),function(item) {
var title = item.textContent;
var id = item.getAttribute("id");
if ( !id ) {
id = title.toLowerCase()
.replace(/[^a-z]/g, "-")
.replace(/-+/g,"-");
item.setAttribute("id", id);
}
var li = document.createElement("li");
var a = document.createElement("a");
a.appendChild(document.createTextNode(title));
a.setAttribute("href", "#" + id);
li.appendChild(a);
ul.appendChild(li);
var a2 = document.createElement("a");
a2.setAttribute("href", "#" + id);
a2.appendChild(document.createTextNode(title));
while(item.firstChild) {
item.removeChild(item.firstChild);
}
item.appendChild(a2);
});
</script>
<script type="text/javascript">
function toggle(cnt){
if ( cnt.classList.contains("hide") ) {
cnt.classList.remove("hide");
} else {
cnt.classList.add("hide");
}
}
function toggle_contents(){
return toggle(document.querySelector("#contents"));
}
toggle_contents();
</script>
</section>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://scala-william.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</body></html>